mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 19:21:10 +02:00
make auth working
This commit is contained in:
parent
7a1f1e9aef
commit
25824ed0ac
73 changed files with 9093 additions and 3877 deletions
|
|
@ -1,16 +1,29 @@
|
|||
import { supabase } from '../utils/supabase';
|
||||
import { sendChatRequest, ChatMessage, logTokenUsage, ChatRequestResult } from './openai';
|
||||
/**
|
||||
* Conversation Service - CRUD operations via Backend API
|
||||
*/
|
||||
import {
|
||||
conversationApi,
|
||||
chatApi,
|
||||
modelApi,
|
||||
templateApi,
|
||||
usageApi,
|
||||
type Conversation as ApiConversation,
|
||||
type Message as ApiMessage,
|
||||
type ChatMessage,
|
||||
type TokenUsage,
|
||||
} from './api';
|
||||
|
||||
// Typdefinitionen für Konversationen und Nachrichten
|
||||
// Re-export types with backwards-compatible naming (snake_case for mobile)
|
||||
export type Conversation = {
|
||||
id: string; // UUID
|
||||
user_id: string; // UUID des Benutzers (auth.uid)
|
||||
model_id: string; // UUID
|
||||
template_id?: string; // UUID, optional
|
||||
id: string;
|
||||
user_id: string;
|
||||
model_id: string;
|
||||
template_id?: string;
|
||||
space_id?: string;
|
||||
conversation_mode: 'free' | 'guided' | 'template';
|
||||
document_mode: boolean; // Gibt an, ob der Dokumentmodus aktiviert ist
|
||||
title?: string; // Titel der Konversation
|
||||
is_archived: boolean; // Gibt an, ob die Konversation archiviert wurde
|
||||
document_mode: boolean;
|
||||
title?: string;
|
||||
is_archived: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
|
@ -37,6 +50,34 @@ export type TokenUsageType = {
|
|||
created_at: string;
|
||||
};
|
||||
|
||||
// Helper to convert API response to local format
|
||||
function toLocalConversation(conv: ApiConversation): Conversation {
|
||||
return {
|
||||
id: conv.id,
|
||||
user_id: conv.userId,
|
||||
model_id: conv.modelId,
|
||||
template_id: conv.templateId,
|
||||
space_id: conv.spaceId,
|
||||
conversation_mode: conv.conversationMode,
|
||||
document_mode: conv.documentMode,
|
||||
title: conv.title,
|
||||
is_archived: conv.isArchived,
|
||||
created_at: conv.createdAt,
|
||||
updated_at: conv.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
function toLocalMessage(msg: ApiMessage): Message {
|
||||
return {
|
||||
id: msg.id,
|
||||
conversation_id: msg.conversationId,
|
||||
sender: msg.sender,
|
||||
message_text: msg.messageText,
|
||||
created_at: msg.createdAt,
|
||||
updated_at: msg.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt eine neue Konversation in der Datenbank
|
||||
*/
|
||||
|
|
@ -49,28 +90,22 @@ export async function createConversation(
|
|||
spaceId?: string
|
||||
): Promise<string | null> {
|
||||
try {
|
||||
console.log("🔵 Erstelle Konversation mit Space ID:", spaceId || "keine");
|
||||
|
||||
// Erstelle einen neuen Eintrag in der Conversations-Tabelle
|
||||
const { data, error } = await supabase
|
||||
.from('conversations')
|
||||
.insert({
|
||||
user_id: userId,
|
||||
model_id: modelId,
|
||||
template_id: templateId,
|
||||
conversation_mode: mode,
|
||||
document_mode: documentMode,
|
||||
space_id: spaceId,
|
||||
})
|
||||
.select('id')
|
||||
.single();
|
||||
console.log('🔵 Erstelle Konversation mit Space ID:', spaceId || 'keine');
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Erstellen der Konversation:', error);
|
||||
const conversation = await conversationApi.createConversation({
|
||||
modelId,
|
||||
conversationMode: mode,
|
||||
templateId,
|
||||
documentMode,
|
||||
spaceId,
|
||||
});
|
||||
|
||||
if (!conversation) {
|
||||
console.error('Fehler beim Erstellen der Konversation');
|
||||
return null;
|
||||
}
|
||||
|
||||
return data.id;
|
||||
return conversation.id;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Erstellen der Konversation:', error);
|
||||
return null;
|
||||
|
|
@ -86,35 +121,23 @@ export async function addMessage(
|
|||
messageText: string
|
||||
): Promise<string | null> {
|
||||
try {
|
||||
// Führe eine Prüfung und Validierung des Senders durch
|
||||
// Validate sender
|
||||
let validSender = sender;
|
||||
|
||||
// Stelle sicher, dass der Sender den zulässigen Werten entspricht
|
||||
// Das scheint das Problem zu sein - die Datenbank akzeptiert nur bestimmte Werte
|
||||
if (!['user', 'assistant', 'system'].includes(validSender)) {
|
||||
console.error('Ungültiger Sender-Wert:', sender);
|
||||
validSender = 'user'; // Fallback auf 'user'
|
||||
validSender = 'user';
|
||||
}
|
||||
|
||||
console.log('Füge Nachricht hinzu mit Sender:', validSender);
|
||||
|
||||
// Füge eine neue Nachricht in die Messages-Tabelle ein
|
||||
const { data, error } = await supabase
|
||||
.from('messages')
|
||||
.insert({
|
||||
conversation_id: conversationId,
|
||||
sender: validSender,
|
||||
message_text: messageText,
|
||||
})
|
||||
.select('id')
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Hinzufügen der Nachricht:', error);
|
||||
console.log('Füge Nachricht hinzu mit Sender:', validSender);
|
||||
|
||||
const message = await conversationApi.addMessage(conversationId, validSender, messageText);
|
||||
|
||||
if (!message) {
|
||||
console.error('Fehler beim Hinzufügen der Nachricht');
|
||||
return null;
|
||||
}
|
||||
|
||||
return data.id;
|
||||
return message.id;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Hinzufügen der Nachricht:', error);
|
||||
return null;
|
||||
|
|
@ -126,18 +149,8 @@ export async function addMessage(
|
|||
*/
|
||||
export async function getMessages(conversationId: string): Promise<Message[]> {
|
||||
try {
|
||||
const { data, error } = await supabase
|
||||
.from('messages')
|
||||
.select('*')
|
||||
.eq('conversation_id', conversationId)
|
||||
.order('created_at', { ascending: true });
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Laden der Nachrichten:', error);
|
||||
return [];
|
||||
}
|
||||
|
||||
return data as Message[];
|
||||
const messages = await conversationApi.getMessages(conversationId);
|
||||
return messages.map(toLocalMessage);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Nachrichten:', error);
|
||||
return [];
|
||||
|
|
@ -146,52 +159,41 @@ export async function getMessages(conversationId: string): Promise<Message[]> {
|
|||
|
||||
/**
|
||||
* Generiert einen Titel für die Konversation basierend auf der ersten Benutzeranfrage
|
||||
* @param userQuestion Die erste Frage des Benutzers
|
||||
* @returns Generierter Titel
|
||||
*/
|
||||
export async function generateConversationTitle(userQuestion: string): Promise<string> {
|
||||
try {
|
||||
console.log('Generiere Titel für Konversation basierend auf:',
|
||||
userQuestion.substring(0, 50) + (userQuestion.length > 50 ? '...' : ''));
|
||||
|
||||
// Verwende speziell GPT-4o-Mini für die Titelerstellung
|
||||
console.log(
|
||||
'Generiere Titel für Konversation basierend auf:',
|
||||
userQuestion.substring(0, 50) + (userQuestion.length > 50 ? '...' : '')
|
||||
);
|
||||
|
||||
const titlePrompt = `Schreibe eine kurze, prägnante Überschrift (maximal 5 Wörter) für unseren Chat mit dieser Frage: "${userQuestion}"`;
|
||||
|
||||
// Manuell 4o-mini-Modell festlegen
|
||||
const chatMessages: ChatMessage[] = [
|
||||
{
|
||||
role: 'system',
|
||||
content: 'MODEL:gpt-4o-mini-se'
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: titlePrompt
|
||||
}
|
||||
];
|
||||
|
||||
// Sende die Anfrage mit niedrigerer Temperatur für zuverlässigere Ergebnisse
|
||||
const titleResponse = await sendChatRequest(chatMessages, 0.3, 50);
|
||||
|
||||
// Extrahiere den Text aus der Antwort
|
||||
const responseText = typeof titleResponse === 'string'
|
||||
? titleResponse
|
||||
: titleResponse.content;
|
||||
const response = await chatApi.createCompletion({
|
||||
messages: [{ role: 'user', content: titlePrompt }],
|
||||
modelId: '550e8400-e29b-41d4-a716-446655440004', // GPT-4o-Mini
|
||||
temperature: 0.3,
|
||||
maxTokens: 50,
|
||||
});
|
||||
|
||||
if (!response) {
|
||||
return 'Neue Konversation';
|
||||
}
|
||||
|
||||
// Clean up title
|
||||
let cleanTitle = response.content
|
||||
.trim()
|
||||
.replace(/^["']|["']$/g, '')
|
||||
.replace(/\.$/g, '');
|
||||
|
||||
// Entferne Anführungszeichen und Punkt am Ende, falls vorhanden
|
||||
let cleanTitle = responseText.trim()
|
||||
.replace(/^["']|["']$/g, '') // Entferne Anführungszeichen am Anfang und Ende
|
||||
.replace(/\.$/g, ''); // Entferne Punkt am Ende
|
||||
|
||||
// Begrenze die Länge auf 100 Zeichen (für Datenbank)
|
||||
if (cleanTitle.length > 100) {
|
||||
cleanTitle = cleanTitle.substring(0, 97) + '...';
|
||||
}
|
||||
|
||||
|
||||
console.log('Generierter Titel:', cleanTitle);
|
||||
return cleanTitle;
|
||||
} catch (error) {
|
||||
console.error('Fehler bei der Titelgenerierung:', error);
|
||||
// Fallback-Titel bei Fehler
|
||||
return 'Neue Konversation';
|
||||
}
|
||||
}
|
||||
|
|
@ -199,18 +201,18 @@ export async function generateConversationTitle(userQuestion: string): Promise<s
|
|||
/**
|
||||
* Aktualisiert den Titel einer Konversation
|
||||
*/
|
||||
export async function updateConversationTitle(conversationId: string, title: string): Promise<boolean> {
|
||||
export async function updateConversationTitle(
|
||||
conversationId: string,
|
||||
title: string
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const { error } = await supabase
|
||||
.from('conversations')
|
||||
.update({ title })
|
||||
.eq('id', conversationId);
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Aktualisieren des Konversationstitels:', error);
|
||||
const success = await conversationApi.updateTitle(conversationId, title);
|
||||
|
||||
if (!success) {
|
||||
console.error('Fehler beim Aktualisieren des Konversationstitels');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Aktualisieren des Konversationstitels:', error);
|
||||
|
|
@ -220,23 +222,17 @@ export async function updateConversationTitle(conversationId: string, title: str
|
|||
|
||||
/**
|
||||
* Lädt einen System-Prompt aus einer Vorlage
|
||||
* @param templateId Die ID der Vorlage
|
||||
* @returns Der System-Prompt der Vorlage oder null
|
||||
*/
|
||||
export async function getSystemPromptFromTemplate(templateId: string): Promise<string | null> {
|
||||
try {
|
||||
const { data, error } = await supabase
|
||||
.from('templates')
|
||||
.select('system_prompt')
|
||||
.eq('id', templateId)
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Laden der Vorlage:', error);
|
||||
const template = await templateApi.getTemplate(templateId);
|
||||
|
||||
if (!template) {
|
||||
console.error('Fehler beim Laden der Vorlage');
|
||||
return null;
|
||||
}
|
||||
|
||||
return data.system_prompt;
|
||||
|
||||
return template.systemPrompt;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Vorlage:', error);
|
||||
return null;
|
||||
|
|
@ -252,70 +248,52 @@ export async function sendMessageAndGetResponse(
|
|||
modelId: string,
|
||||
templateId?: string,
|
||||
documentMode: boolean = false
|
||||
): Promise<{ userMessageId: string | null; assistantMessageId: string | null; assistantResponse: string; title?: string; documentContent?: string }> {
|
||||
): Promise<{
|
||||
userMessageId: string | null;
|
||||
assistantMessageId: string | null;
|
||||
assistantResponse: string;
|
||||
title?: string;
|
||||
documentContent?: string;
|
||||
}> {
|
||||
try {
|
||||
console.log('Starte sendMessageAndGetResponse mit:', {
|
||||
conversationId,
|
||||
userMessage: userMessage.substring(0, 50) + (userMessage.length > 50 ? '...' : ''),
|
||||
modelId
|
||||
modelId,
|
||||
});
|
||||
|
||||
// Lade das Modell aus der Datenbank oder verwende Fallback
|
||||
const { data: modelData, error: modelError } = await supabase
|
||||
.from('models')
|
||||
.select('*')
|
||||
.eq('id', modelId)
|
||||
.single();
|
||||
|
||||
if (modelError) {
|
||||
console.error('Fehler beim Laden des Modells:', modelError);
|
||||
|
||||
// Load the model from API
|
||||
const modelData = await modelApi.getModel(modelId);
|
||||
|
||||
if (!modelData) {
|
||||
console.log('Verwende Standard-Parameter, da Modell nicht geladen werden konnte');
|
||||
} else {
|
||||
console.log('Modell geladen:', {
|
||||
id: modelData.id,
|
||||
name: modelData.name,
|
||||
parameters: modelData.parameters,
|
||||
deployment: modelData.parameters?.deployment
|
||||
deployment: modelData.parameters?.deployment,
|
||||
});
|
||||
}
|
||||
|
||||
// Variable für die Benutzer-Nachricht-ID
|
||||
let userMessageId: string | null = null;
|
||||
|
||||
// Überprüfe, ob die Nachricht bereits in der Datenbank existiert
|
||||
const { data: existingMessages } = await supabase
|
||||
.from('messages')
|
||||
.select('id')
|
||||
.eq('conversation_id', conversationId)
|
||||
.eq('sender', 'user')
|
||||
.eq('message_text', userMessage)
|
||||
.limit(1);
|
||||
|
||||
if (existingMessages && existingMessages.length > 0) {
|
||||
// Wenn die Nachricht bereits existiert, verwende diese ID
|
||||
userMessageId = existingMessages[0].id;
|
||||
console.log('Bestehende Benutzernachricht gefunden mit ID:', userMessageId);
|
||||
} else {
|
||||
// Speichere die Benutzernachricht nur, wenn sie nicht bereits existiert
|
||||
userMessageId = await addMessage(conversationId, 'user', userMessage);
|
||||
console.log('Neue Benutzernachricht gespeichert mit ID:', userMessageId);
|
||||
}
|
||||
// Save the user message
|
||||
const userMessageId = await addMessage(conversationId, 'user', userMessage);
|
||||
console.log('Benutzernachricht gespeichert mit ID:', userMessageId);
|
||||
|
||||
// Lade alle bisherigen Nachrichten für Kontext
|
||||
// Load all messages for context
|
||||
const messages = await getMessages(conversationId);
|
||||
console.log(`${messages.length} Nachrichten für Kontext geladen`);
|
||||
|
||||
// Konvertiere die Nachrichten in das Format für die OpenAI-API
|
||||
// Build chat messages for API
|
||||
const chatMessages: ChatMessage[] = [];
|
||||
|
||||
// Lade den System-Prompt aus der Vorlage, falls template_id vorhanden
|
||||
|
||||
// Load system prompt from template if available
|
||||
let systemPrompt: string | null = null;
|
||||
if (templateId) {
|
||||
systemPrompt = await getSystemPromptFromTemplate(templateId);
|
||||
if (systemPrompt) {
|
||||
console.log('System-Prompt aus Vorlage geladen');
|
||||
|
||||
// Wenn Dokumentmodus aktiv ist, füge spezielle Anweisungen hinzu
|
||||
|
||||
if (documentMode) {
|
||||
const documentModePrompt = `
|
||||
${systemPrompt}
|
||||
|
|
@ -333,23 +311,13 @@ CHAT: Hier antwortest du auf die Frage oder das Feedback des Nutzers.
|
|||
DOKUMENT:
|
||||
Hier steht dein Vorschlag für das Dokument in Markdown-Format, ohne Codeblock-Markierungen.
|
||||
`;
|
||||
// Ersetze den Original-Prompt durch den Dokumentmodus-Prompt
|
||||
chatMessages.push({
|
||||
role: 'system',
|
||||
content: documentModePrompt
|
||||
});
|
||||
|
||||
chatMessages.push({ role: 'system', content: documentModePrompt });
|
||||
console.log('Dokumentmodus-Prompt hinzugefügt');
|
||||
} else {
|
||||
// Standard-Prompt ohne Dokumentmodus
|
||||
chatMessages.push({
|
||||
role: 'system',
|
||||
content: systemPrompt
|
||||
});
|
||||
chatMessages.push({ role: 'system', content: systemPrompt });
|
||||
}
|
||||
}
|
||||
} else if (documentMode) {
|
||||
// Wenn kein Template, aber Dokumentmodus aktiv ist
|
||||
const documentModePrompt = `
|
||||
Du befindest dich im Dokumentmodus. Deine Aufgabe ist es, dem Benutzer zu helfen, ein Dokument zu erstellen und zu verbessern.
|
||||
|
||||
|
|
@ -364,225 +332,168 @@ CHAT: Hier antwortest du auf die Frage oder das Feedback des Nutzers.
|
|||
DOKUMENT:
|
||||
Hier steht dein Vorschlag für das Dokument in Markdown-Format, ohne Codeblock-Markierungen.
|
||||
`;
|
||||
chatMessages.push({
|
||||
role: 'system',
|
||||
content: documentModePrompt
|
||||
});
|
||||
|
||||
chatMessages.push({ role: 'system', content: documentModePrompt });
|
||||
console.log('Standard-Dokumentmodus-Prompt hinzugefügt');
|
||||
}
|
||||
|
||||
// Füge eine System-Nachricht mit der Modell-ID hinzu, falls ein Modell geladen wurde
|
||||
if (modelData && modelData.parameters && modelData.parameters.deployment) {
|
||||
console.log(`Nutze deployment '${modelData.parameters.deployment}' für Modell ${modelData.name}`);
|
||||
// Stelle die Modell-Identifikation ganz am Anfang ein
|
||||
chatMessages.unshift({
|
||||
role: 'system',
|
||||
content: `MODEL:${modelData.parameters.deployment}`
|
||||
});
|
||||
} else {
|
||||
// Versuche, das Deployment über die Modell-ID zu finden
|
||||
console.warn('Kein Modell-Deployment in Modell-Daten gefunden, suche in verfügbaren Modellen');
|
||||
// Lade dynamisch die Modelle
|
||||
try {
|
||||
const { data: availableModels } = await supabase
|
||||
.from('models')
|
||||
.select('id, parameters, name');
|
||||
|
||||
const matchingModel = availableModels?.find(m => m.id === modelId);
|
||||
|
||||
if (matchingModel && matchingModel.parameters && matchingModel.parameters.deployment) {
|
||||
console.log(`Nutze deployment '${matchingModel.parameters.deployment}' für Modell ${matchingModel.name}`);
|
||||
chatMessages.unshift({
|
||||
role: 'system',
|
||||
content: `MODEL:${matchingModel.parameters.deployment}`
|
||||
});
|
||||
} else {
|
||||
console.warn('Kein passendes Modell-Deployment gefunden, verwende Standard-Deployment');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der verfügbaren Modelle:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Füge alle normalen Nachrichten hinzu
|
||||
chatMessages.push(...messages.map(msg => ({
|
||||
role: msg.sender === 'user' ? 'user' : msg.sender === 'assistant' ? 'assistant' : 'system',
|
||||
content: msg.message_text
|
||||
})));
|
||||
|
||||
|
||||
// Add all conversation messages
|
||||
chatMessages.push(
|
||||
...messages.map((msg) => ({
|
||||
role: msg.sender === 'user' ? 'user' : msg.sender === 'assistant' ? 'assistant' : 'system',
|
||||
content: msg.message_text,
|
||||
})) as ChatMessage[]
|
||||
);
|
||||
|
||||
console.log('Nachrichten für OpenAI konvertiert:', chatMessages.length, 'Nachrichten');
|
||||
|
||||
// Sende die Anfrage an das LLM-Modell
|
||||
// Send request to AI
|
||||
console.log('Sende Anfrage an LLM-Modell mit Parametern:', {
|
||||
temperature: modelData?.parameters?.temperature || 0.7,
|
||||
maxTokens: modelData?.parameters?.max_tokens || 800
|
||||
maxTokens: modelData?.parameters?.max_tokens || 800,
|
||||
});
|
||||
|
||||
const result = await sendChatRequest(
|
||||
chatMessages,
|
||||
modelData?.parameters?.temperature || 0.7,
|
||||
modelData?.parameters?.max_tokens || 800
|
||||
);
|
||||
|
||||
// Extrahiere die Antwort und Token-Nutzung aus dem Ergebnis
|
||||
|
||||
const result = await chatApi.createCompletion({
|
||||
messages: chatMessages,
|
||||
modelId,
|
||||
temperature: modelData?.parameters?.temperature || 0.7,
|
||||
maxTokens: modelData?.parameters?.max_tokens || 800,
|
||||
});
|
||||
|
||||
let assistantResponse: string;
|
||||
let tokenUsage;
|
||||
|
||||
if (typeof result === 'string') {
|
||||
// Falls nur ein String zurückgegeben wurde (Fehlerfall)
|
||||
assistantResponse = result;
|
||||
console.log('Einfache String-Antwort vom LLM-Modell erhalten (kein Tokennutzungs-Tracking):', {
|
||||
length: assistantResponse.length,
|
||||
preview: assistantResponse.substring(0, 50) + (assistantResponse.length > 50 ? '...' : '')
|
||||
});
|
||||
let tokenUsage: TokenUsage | undefined;
|
||||
|
||||
if (!result) {
|
||||
assistantResponse =
|
||||
'Es konnte keine Antwort generiert werden. Bitte stelle sicher, dass das Backend läuft.';
|
||||
} else {
|
||||
// Bei vollständigem Ergebnis mit Token-Nutzung
|
||||
assistantResponse = result.content;
|
||||
tokenUsage = result.usage;
|
||||
|
||||
|
||||
console.log('Antwort vom LLM-Modell erhalten:', {
|
||||
length: assistantResponse.length,
|
||||
preview: assistantResponse.substring(0, 50) + (assistantResponse.length > 50 ? '...' : ''),
|
||||
tokenUsage
|
||||
tokenUsage,
|
||||
});
|
||||
}
|
||||
|
||||
// Dokumentinhalt extrahieren, wenn im Dokumentmodus
|
||||
// Extract document content if in document mode
|
||||
let documentContent: string | undefined;
|
||||
let chatResponse = assistantResponse;
|
||||
|
||||
|
||||
if (documentMode) {
|
||||
// Nach dem Format "CHAT: ... DOKUMENT: ```markdown ... ```" suchen
|
||||
console.log("Analysiere LLM-Antwort für Dokumentextraktion:", assistantResponse.substring(0, 200) + "...");
|
||||
|
||||
console.log(
|
||||
'Analysiere LLM-Antwort für Dokumentextraktion:',
|
||||
assistantResponse.substring(0, 200) + '...'
|
||||
);
|
||||
|
||||
const chatMatch = assistantResponse.match(/CHAT:(.*?)(?=DOKUMENT:|$)/s);
|
||||
const documentMatch = assistantResponse.match(/DOKUMENT:[\s\n]*(```markdown|```|`markdown)?([^`].*?)(?:```|`+)?$/s);
|
||||
|
||||
console.log("Dokument-Regex Match:", documentMatch ? "Ja" : "Nein");
|
||||
|
||||
const documentMatch = assistantResponse.match(
|
||||
/DOKUMENT:[\s\n]*(```markdown|```|`markdown)?([^`].*?)(?:```|`+)?$/s
|
||||
);
|
||||
|
||||
console.log('Dokument-Regex Match:', documentMatch ? 'Ja' : 'Nein');
|
||||
|
||||
if (chatMatch && chatMatch[1]) {
|
||||
chatResponse = chatMatch[1].trim();
|
||||
console.log('Chat-Antwort extrahiert:', chatResponse.substring(0, 50) + '...');
|
||||
}
|
||||
|
||||
|
||||
if (documentMatch && documentMatch[2]) {
|
||||
documentContent = documentMatch[2].trim();
|
||||
console.log('Dokument-Inhalt extrahiert:', (documentContent?.substring(0, 50) || '') + '...');
|
||||
} else if (documentMatch) {
|
||||
console.log('Dokument-Match gefunden, aber kein Inhalt in Gruppe 2');
|
||||
console.log('Dokument-Match-Gruppen:', documentMatch.length);
|
||||
for (let i = 0; i < documentMatch.length; i++) {
|
||||
console.log(`Gruppe ${i}:`, documentMatch[i]?.substring(0, 30));
|
||||
}
|
||||
console.log(
|
||||
'Dokument-Inhalt extrahiert:',
|
||||
(documentContent?.substring(0, 50) || '') + '...'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Speichere die Antwort des Assistenten
|
||||
// Save assistant message
|
||||
const assistantMessageId = await addMessage(conversationId, 'assistant', chatResponse);
|
||||
console.log('Assistentenantwort gespeichert mit ID:', assistantMessageId);
|
||||
|
||||
// Token-Nutzung loggen, falls verfügbar
|
||||
if (tokenUsage && assistantMessageId && userMessageId && modelData) {
|
||||
|
||||
// Log token usage if available
|
||||
if (tokenUsage && assistantMessageId && userMessageId) {
|
||||
try {
|
||||
// Lade die Konversation, um die user_id zu erhalten
|
||||
const { data: conversationData, error: convError } = await supabase
|
||||
.from('conversations')
|
||||
.select('user_id')
|
||||
.eq('id', conversationId)
|
||||
.single();
|
||||
|
||||
if (convError || !conversationData) {
|
||||
console.error('Fehler beim Laden der Konversation für Token-Logging:', convError);
|
||||
} else {
|
||||
const userId = conversationData.user_id;
|
||||
|
||||
// Logge die Token-Nutzung
|
||||
await logTokenUsage(
|
||||
tokenUsage,
|
||||
conversationId,
|
||||
assistantMessageId,
|
||||
userId,
|
||||
modelId
|
||||
);
|
||||
console.log('Token-Nutzung erfolgreich geloggt');
|
||||
}
|
||||
const estimatedCost = calculateTokenCost(
|
||||
tokenUsage.prompt_tokens,
|
||||
tokenUsage.completion_tokens,
|
||||
modelData?.costSettings
|
||||
);
|
||||
|
||||
await usageApi.logTokenUsage({
|
||||
conversationId,
|
||||
messageId: assistantMessageId,
|
||||
modelId,
|
||||
promptTokens: tokenUsage.prompt_tokens,
|
||||
completionTokens: tokenUsage.completion_tokens,
|
||||
totalTokens: tokenUsage.total_tokens,
|
||||
estimatedCost,
|
||||
});
|
||||
console.log('Token-Nutzung erfolgreich geloggt');
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Loggen der Token-Nutzung:', error);
|
||||
// Wir werfen keinen Fehler, da das Token-Logging nicht kritisch ist
|
||||
}
|
||||
}
|
||||
|
||||
// Prüfe, ob dies die erste Nachricht in der Konversation ist
|
||||
// Wenn ja, generiere einen Titel und aktualisiere die Konversation
|
||||
const { count } = await supabase
|
||||
.from('messages')
|
||||
.select('id', { count: 'exact', head: true })
|
||||
.eq('conversation_id', conversationId);
|
||||
|
||||
let title;
|
||||
// Nur für die erste oder zweite Nachricht (die erste könnte eine System-Nachricht sein)
|
||||
if (count === 1 || count === 2) {
|
||||
// Generiere einen Titel basierend auf der Benutzernachricht
|
||||
// Generate title for new conversations
|
||||
const allMessages = await getMessages(conversationId);
|
||||
let title: string | undefined;
|
||||
|
||||
if (allMessages.length <= 2) {
|
||||
title = await generateConversationTitle(userMessage);
|
||||
|
||||
// Aktualisiere den Titel in der Datenbank
|
||||
const success = await updateConversationTitle(conversationId, title);
|
||||
console.log('Konversationstitel aktualisiert:', success ? 'erfolgreich' : 'fehlgeschlagen');
|
||||
|
||||
if (title) {
|
||||
const success = await updateConversationTitle(conversationId, title);
|
||||
console.log('Konversationstitel aktualisiert:', success ? 'erfolgreich' : 'fehlgeschlagen');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
userMessageId,
|
||||
assistantMessageId,
|
||||
assistantResponse: chatResponse,
|
||||
title,
|
||||
documentContent
|
||||
documentContent,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Senden der Nachricht:', error);
|
||||
|
||||
// Detaillierte Fehlerinformationen ausgeben
|
||||
|
||||
if (error instanceof Error) {
|
||||
console.error('Fehlerdetails:', {
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
stack: error.stack
|
||||
stack: error.stack,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
userMessageId: null,
|
||||
assistantMessageId: null,
|
||||
assistantResponse: `Es ist ein Fehler aufgetreten: ${error instanceof Error ? error.message : 'Unbekannter Fehler'}. Bitte versuche es später erneut.`
|
||||
assistantResponse: `Es ist ein Fehler aufgetreten: ${error instanceof Error ? error.message : 'Unbekannter Fehler'}. Bitte versuche es später erneut.`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to calculate token cost
|
||||
function calculateTokenCost(
|
||||
promptTokens: number,
|
||||
completionTokens: number,
|
||||
costSettings?: { prompt_per_1k_tokens?: number; completion_per_1k_tokens?: number }
|
||||
): number {
|
||||
const promptCost = costSettings?.prompt_per_1k_tokens || 0.0001;
|
||||
const completionCost = costSettings?.completion_per_1k_tokens || 0.0002;
|
||||
|
||||
const cost = (promptTokens * promptCost + completionTokens * completionCost) / 1000;
|
||||
return Number(cost.toFixed(6));
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt alle aktiven (nicht archivierten) Konversationen eines Benutzers
|
||||
* Optional: Mit spaceId werden nur Konversationen aus diesem Space geladen
|
||||
*/
|
||||
export async function getConversations(userId: string, spaceId?: string): Promise<Conversation[]> {
|
||||
try {
|
||||
let query = supabase
|
||||
.from('conversations')
|
||||
.select('*')
|
||||
.eq('user_id', userId)
|
||||
.eq('is_archived', false);
|
||||
|
||||
// Wenn eine Space-ID angegeben wurde, filtere nach diesem Space
|
||||
if (spaceId) {
|
||||
query = query.eq('space_id', spaceId);
|
||||
}
|
||||
|
||||
const { data, error } = await query.order('updated_at', { ascending: false });
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Laden der Konversationen:', error);
|
||||
return [];
|
||||
}
|
||||
|
||||
return data as Conversation[];
|
||||
const conversations = await conversationApi.getConversations(spaceId);
|
||||
return conversations.map(toLocalConversation);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Konversationen:', error);
|
||||
return [];
|
||||
|
|
@ -594,19 +505,8 @@ export async function getConversations(userId: string, spaceId?: string): Promis
|
|||
*/
|
||||
export async function getArchivedConversations(userId: string): Promise<Conversation[]> {
|
||||
try {
|
||||
const { data, error } = await supabase
|
||||
.from('conversations')
|
||||
.select('*')
|
||||
.eq('user_id', userId)
|
||||
.eq('is_archived', true)
|
||||
.order('updated_at', { ascending: false });
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Laden der archivierten Konversationen:', error);
|
||||
return [];
|
||||
}
|
||||
|
||||
return data as Conversation[];
|
||||
const conversations = await conversationApi.getArchivedConversations();
|
||||
return conversations.map(toLocalConversation);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der archivierten Konversationen:', error);
|
||||
return [];
|
||||
|
|
@ -618,17 +518,7 @@ export async function getArchivedConversations(userId: string): Promise<Conversa
|
|||
*/
|
||||
export async function archiveConversation(conversationId: string): Promise<boolean> {
|
||||
try {
|
||||
const { error } = await supabase
|
||||
.from('conversations')
|
||||
.update({ is_archived: true })
|
||||
.eq('id', conversationId);
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Archivieren der Konversation:', error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return await conversationApi.archiveConversation(conversationId);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Archivieren der Konversation:', error);
|
||||
return false;
|
||||
|
|
@ -640,17 +530,7 @@ export async function archiveConversation(conversationId: string): Promise<boole
|
|||
*/
|
||||
export async function unarchiveConversation(conversationId: string): Promise<boolean> {
|
||||
try {
|
||||
const { error } = await supabase
|
||||
.from('conversations')
|
||||
.update({ is_archived: false })
|
||||
.eq('id', conversationId);
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Wiederherstellen der Konversation:', error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return await conversationApi.unarchiveConversation(conversationId);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Wiederherstellen der Konversation:', error);
|
||||
return false;
|
||||
|
|
@ -662,29 +542,7 @@ export async function unarchiveConversation(conversationId: string): Promise<boo
|
|||
*/
|
||||
export async function deleteConversation(conversationId: string): Promise<boolean> {
|
||||
try {
|
||||
// Lösche zuerst alle zugehörigen Nachrichten
|
||||
const { error: messagesError } = await supabase
|
||||
.from('messages')
|
||||
.delete()
|
||||
.eq('conversation_id', conversationId);
|
||||
|
||||
if (messagesError) {
|
||||
console.error('Fehler beim Löschen der Nachrichten:', messagesError);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lösche dann die Konversation selbst
|
||||
const { error: conversationError } = await supabase
|
||||
.from('conversations')
|
||||
.delete()
|
||||
.eq('id', conversationId);
|
||||
|
||||
if (conversationError) {
|
||||
console.error('Fehler beim Löschen der Konversation:', conversationError);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return await conversationApi.deleteConversation(conversationId);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Löschen der Konversation:', error);
|
||||
return false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue