refactor: restructure

monorepo with apps/ and services/
  directories
This commit is contained in:
Wuesteon 2025-11-26 03:03:24 +01:00
parent 25824ed0ac
commit ff80aeec1f
4062 changed files with 2592 additions and 1278 deletions

View file

@ -0,0 +1,633 @@
/**
* API Client for Chat Mobile App
* Handles all communication with the NestJS backend
*/
import * as SecureStore from 'expo-secure-store';
const BACKEND_URL = process.env.EXPO_PUBLIC_BACKEND_URL || 'http://localhost:3001';
// Token storage key (must match what @manacore/shared-auth uses)
const APP_TOKEN_KEY = '@manacore/app_token';
// ============================================================================
// Types
// ============================================================================
export type Conversation = {
id: string;
userId: string;
modelId: string;
templateId?: string;
spaceId?: string;
conversationMode: 'free' | 'guided' | 'template';
documentMode: boolean;
title?: string;
isArchived: boolean;
createdAt: string;
updatedAt: string;
};
export type Message = {
id: string;
conversationId: string;
sender: 'user' | 'assistant' | 'system';
messageText: string;
createdAt: string;
updatedAt: string;
};
export type Template = {
id: string;
userId: string;
name: string;
description?: string;
systemPrompt: string;
initialQuestion?: string;
modelId?: string;
color: string;
isDefault: boolean;
documentMode: boolean;
createdAt: string;
updatedAt: string;
};
export type Space = {
id: string;
name: string;
description?: string;
ownerId: string;
isArchived: boolean;
createdAt: string;
updatedAt: string;
};
export type SpaceMember = {
id: string;
spaceId: string;
userId: string;
role: 'owner' | 'admin' | 'member' | 'viewer';
invitationStatus: 'pending' | 'accepted' | 'declined';
invitedBy?: string;
invitedAt: string;
joinedAt?: string;
createdAt: string;
updatedAt: string;
};
export type Document = {
id: string;
conversationId: string;
version: number;
content: string;
createdAt: string;
updatedAt: string;
};
export type AIModel = {
id: string;
name: string;
description?: string;
parameters: {
temperature?: number;
max_tokens?: number;
provider?: string;
deployment?: string;
endpoint?: string;
api_version?: string;
};
costSettings?: {
prompt_per_1k_tokens?: number;
completion_per_1k_tokens?: number;
};
isActive: boolean;
createdAt: string;
updatedAt: string;
};
export type ChatMessage = {
role: 'system' | 'user' | 'assistant';
content: string;
};
export type TokenUsage = {
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
};
export type ChatCompletionResponse = {
content: string;
usage: TokenUsage;
};
// ============================================================================
// Base API Functions
// ============================================================================
async function getAuthToken(): Promise<string | null> {
try {
return await SecureStore.getItemAsync(APP_TOKEN_KEY);
} catch {
return null;
}
}
async function apiRequest<T>(
endpoint: string,
options: RequestInit = {}
): Promise<{ data: T | null; error: string | null }> {
try {
const token = await getAuthToken();
const headers: HeadersInit = {
'Content-Type': 'application/json',
...(options.headers || {}),
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const response = await fetch(`${BACKEND_URL}${endpoint}`, {
...options,
headers,
});
if (!response.ok) {
const errorText = await response.text();
console.error(`API Error [${response.status}]: ${errorText}`);
return { data: null, error: `API Error: ${response.status}` };
}
// Handle empty responses
const text = await response.text();
if (!text) {
return { data: null, error: null };
}
const data = JSON.parse(text);
return { data, error: null };
} catch (error) {
console.error('API Request failed:', error);
return { data: null, error: error instanceof Error ? error.message : 'Unknown error' };
}
}
// ============================================================================
// Conversation API
// ============================================================================
export const conversationApi = {
async getConversations(spaceId?: string): Promise<Conversation[]> {
const params = spaceId ? `?spaceId=${spaceId}` : '';
const { data, error } = await apiRequest<Conversation[]>(`/api/conversations${params}`);
if (error) {
console.error('Failed to fetch conversations:', error);
return [];
}
return data || [];
},
async getArchivedConversations(): Promise<Conversation[]> {
const { data, error } = await apiRequest<Conversation[]>('/api/conversations/archived');
if (error) {
console.error('Failed to fetch archived conversations:', error);
return [];
}
return data || [];
},
async getConversation(id: string): Promise<Conversation | null> {
const { data, error } = await apiRequest<Conversation>(`/api/conversations/${id}`);
if (error) {
console.error('Failed to fetch conversation:', error);
return null;
}
return data;
},
async getMessages(conversationId: string): Promise<Message[]> {
const { data, error } = await apiRequest<Message[]>(`/api/conversations/${conversationId}/messages`);
if (error) {
console.error('Failed to fetch messages:', error);
return [];
}
return data || [];
},
async createConversation(params: {
modelId: string;
conversationMode?: 'free' | 'guided' | 'template';
templateId?: string;
documentMode?: boolean;
spaceId?: string;
}): Promise<Conversation | null> {
const { data, error } = await apiRequest<Conversation>('/api/conversations', {
method: 'POST',
body: JSON.stringify(params),
});
if (error) {
console.error('Failed to create conversation:', error);
return null;
}
return data;
},
async addMessage(
conversationId: string,
sender: 'user' | 'assistant' | 'system',
messageText: string
): Promise<Message | null> {
const { data, error } = await apiRequest<Message>(`/api/conversations/${conversationId}/messages`, {
method: 'POST',
body: JSON.stringify({ sender, messageText }),
});
if (error) {
console.error('Failed to add message:', error);
return null;
}
return data;
},
async updateTitle(conversationId: string, title: string): Promise<boolean> {
const { error } = await apiRequest(`/api/conversations/${conversationId}/title`, {
method: 'PATCH',
body: JSON.stringify({ title }),
});
return !error;
},
async archiveConversation(conversationId: string): Promise<boolean> {
const { error } = await apiRequest(`/api/conversations/${conversationId}/archive`, {
method: 'POST',
});
return !error;
},
async unarchiveConversation(conversationId: string): Promise<boolean> {
const { error } = await apiRequest(`/api/conversations/${conversationId}/unarchive`, {
method: 'POST',
});
return !error;
},
async deleteConversation(conversationId: string): Promise<boolean> {
const { error } = await apiRequest(`/api/conversations/${conversationId}`, {
method: 'DELETE',
});
return !error;
},
};
// ============================================================================
// Template API
// ============================================================================
export const templateApi = {
async getTemplates(): Promise<Template[]> {
const { data, error } = await apiRequest<Template[]>('/api/templates');
if (error) {
console.error('Failed to fetch templates:', error);
return [];
}
return data || [];
},
async getTemplate(id: string): Promise<Template | null> {
const { data, error } = await apiRequest<Template>(`/api/templates/${id}`);
if (error) {
console.error('Failed to fetch template:', error);
return null;
}
return data;
},
async getDefaultTemplate(): Promise<Template | null> {
const { data, error } = await apiRequest<Template>('/api/templates/default');
if (error) {
// Not finding a default template is not an error
return null;
}
return data;
},
async createTemplate(template: {
name: string;
description?: string;
systemPrompt: string;
initialQuestion?: string;
modelId?: string;
color?: string;
documentMode?: boolean;
}): Promise<Template | null> {
const { data, error } = await apiRequest<Template>('/api/templates', {
method: 'POST',
body: JSON.stringify(template),
});
if (error) {
console.error('Failed to create template:', error);
return null;
}
return data;
},
async updateTemplate(
id: string,
updates: Partial<{
name: string;
description: string;
systemPrompt: string;
initialQuestion: string;
modelId: string;
color: string;
documentMode: boolean;
}>
): Promise<boolean> {
const { error } = await apiRequest(`/api/templates/${id}`, {
method: 'PATCH',
body: JSON.stringify(updates),
});
return !error;
},
async setDefaultTemplate(id: string): Promise<boolean> {
const { error } = await apiRequest(`/api/templates/${id}/default`, {
method: 'POST',
});
return !error;
},
async deleteTemplate(id: string): Promise<boolean> {
const { error } = await apiRequest(`/api/templates/${id}`, {
method: 'DELETE',
});
return !error;
},
};
// ============================================================================
// Space API
// ============================================================================
export const spaceApi = {
async getUserSpaces(): Promise<Space[]> {
const { data, error } = await apiRequest<Space[]>('/api/spaces');
if (error) {
console.error('Failed to fetch spaces:', error);
return [];
}
return data || [];
},
async getOwnedSpaces(): Promise<Space[]> {
const { data, error } = await apiRequest<Space[]>('/api/spaces/owned');
if (error) {
console.error('Failed to fetch owned spaces:', error);
return [];
}
return data || [];
},
async getSpace(id: string): Promise<Space | null> {
const { data, error } = await apiRequest<Space>(`/api/spaces/${id}`);
if (error) {
console.error('Failed to fetch space:', error);
return null;
}
return data;
},
async getSpaceMembers(spaceId: string): Promise<SpaceMember[]> {
const { data, error } = await apiRequest<SpaceMember[]>(`/api/spaces/${spaceId}/members`);
if (error) {
console.error('Failed to fetch space members:', error);
return [];
}
return data || [];
},
async getUserRoleInSpace(spaceId: string): Promise<'owner' | 'admin' | 'member' | 'viewer' | null> {
const { data, error } = await apiRequest<{ role: 'owner' | 'admin' | 'member' | 'viewer' }>(`/api/spaces/${spaceId}/role`);
if (error) {
return null;
}
return data?.role || null;
},
async getPendingInvitations(): Promise<Array<{ invitation: SpaceMember; space: Space }>> {
const { data, error } = await apiRequest<Array<{ invitation: SpaceMember; space: Space }>>('/api/spaces/invitations/pending');
if (error) {
console.error('Failed to fetch pending invitations:', error);
return [];
}
return data || [];
},
async createSpace(name: string, description?: string): Promise<Space | null> {
const { data, error } = await apiRequest<Space>('/api/spaces', {
method: 'POST',
body: JSON.stringify({ name, description }),
});
if (error) {
console.error('Failed to create space:', error);
return null;
}
return data;
},
async updateSpace(
id: string,
updates: { name?: string; description?: string; isArchived?: boolean }
): Promise<boolean> {
const { error } = await apiRequest(`/api/spaces/${id}`, {
method: 'PATCH',
body: JSON.stringify(updates),
});
return !error;
},
async deleteSpace(id: string): Promise<boolean> {
const { error } = await apiRequest(`/api/spaces/${id}`, {
method: 'DELETE',
});
return !error;
},
async inviteUser(
spaceId: string,
userId: string,
role: 'admin' | 'member' | 'viewer' = 'member'
): Promise<boolean> {
const { error } = await apiRequest(`/api/spaces/${spaceId}/members`, {
method: 'POST',
body: JSON.stringify({ userId, role }),
});
return !error;
},
async respondToInvitation(
spaceId: string,
status: 'accepted' | 'declined'
): Promise<boolean> {
const { error } = await apiRequest(`/api/spaces/${spaceId}/invitation`, {
method: 'POST',
body: JSON.stringify({ status }),
});
return !error;
},
async removeMember(spaceId: string, userId: string): Promise<boolean> {
const { error } = await apiRequest(`/api/spaces/${spaceId}/members/${userId}`, {
method: 'DELETE',
});
return !error;
},
async changeMemberRole(
spaceId: string,
userId: string,
newRole: 'admin' | 'member' | 'viewer'
): Promise<boolean> {
const { error } = await apiRequest(`/api/spaces/${spaceId}/members/${userId}/role`, {
method: 'PATCH',
body: JSON.stringify({ role: newRole }),
});
return !error;
},
};
// ============================================================================
// Document API
// ============================================================================
export const documentApi = {
async getLatestDocument(conversationId: string): Promise<Document | null> {
const { data, error } = await apiRequest<Document>(`/api/documents/conversation/${conversationId}/latest`);
if (error) {
return null;
}
return data;
},
async getAllDocumentVersions(conversationId: string): Promise<Document[]> {
const { data, error } = await apiRequest<Document[]>(`/api/documents/conversation/${conversationId}`);
if (error) {
console.error('Failed to fetch document versions:', error);
return [];
}
return data || [];
},
async hasDocument(conversationId: string): Promise<boolean> {
const { data, error } = await apiRequest<{ exists: boolean }>(`/api/documents/conversation/${conversationId}/exists`);
if (error) {
return false;
}
return data?.exists || false;
},
async createDocument(conversationId: string, content: string): Promise<Document | null> {
const { data, error } = await apiRequest<Document>('/api/documents', {
method: 'POST',
body: JSON.stringify({ conversationId, content }),
});
if (error) {
console.error('Failed to create document:', error);
return null;
}
return data;
},
async createDocumentVersion(conversationId: string, content: string): Promise<Document | null> {
const { data, error } = await apiRequest<Document>(`/api/documents/conversation/${conversationId}/version`, {
method: 'POST',
body: JSON.stringify({ content }),
});
if (error) {
console.error('Failed to create document version:', error);
return null;
}
return data;
},
async deleteDocumentVersion(documentId: string): Promise<boolean> {
const { error } = await apiRequest(`/api/documents/${documentId}`, {
method: 'DELETE',
});
return !error;
},
};
// ============================================================================
// Model API
// ============================================================================
export const modelApi = {
async getModels(): Promise<AIModel[]> {
const { data, error } = await apiRequest<AIModel[]>('/api/chat/models');
if (error) {
console.error('Failed to fetch models:', error);
return [];
}
return data || [];
},
async getModel(id: string): Promise<AIModel | null> {
const { data, error } = await apiRequest<AIModel>(`/api/models/${id}`);
if (error) {
console.error('Failed to fetch model:', error);
return null;
}
return data;
},
};
// ============================================================================
// Chat API
// ============================================================================
export const chatApi = {
async createCompletion(params: {
messages: ChatMessage[];
modelId: string;
temperature?: number;
maxTokens?: number;
}): Promise<ChatCompletionResponse | null> {
const { data, error } = await apiRequest<ChatCompletionResponse>('/api/chat/completions', {
method: 'POST',
body: JSON.stringify({
messages: params.messages,
modelId: params.modelId,
temperature: params.temperature ?? 0.7,
maxTokens: params.maxTokens ?? 1000,
}),
});
if (error) {
console.error('Failed to create completion:', error);
return null;
}
return data;
},
};
// ============================================================================
// Usage Log API
// ============================================================================
export const usageApi = {
async logTokenUsage(params: {
conversationId: string;
messageId: string;
modelId: string;
promptTokens: number;
completionTokens: number;
totalTokens: number;
estimatedCost: number;
}): Promise<boolean> {
const { error } = await apiRequest('/api/usage-logs', {
method: 'POST',
body: JSON.stringify(params),
});
return !error;
},
};

View file

@ -0,0 +1,550 @@
/**
* 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';
// Re-export types with backwards-compatible naming (snake_case for mobile)
export type Conversation = {
id: string;
user_id: string;
model_id: string;
template_id?: string;
space_id?: string;
conversation_mode: 'free' | 'guided' | 'template';
document_mode: boolean;
title?: string;
is_archived: boolean;
created_at: string;
updated_at: string;
};
export type Message = {
id: string;
conversation_id: string;
sender: 'user' | 'assistant' | 'system';
message_text: string;
created_at: string;
updated_at: string;
};
export type TokenUsageType = {
id: string;
conversation_id: string;
message_id: string;
user_id: string;
model_id: string;
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
estimated_cost: number;
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
*/
export async function createConversation(
userId: string,
modelId: string,
mode: 'free' | 'guided' | 'template' = 'free',
templateId?: string,
documentMode: boolean = false,
spaceId?: string
): Promise<string | null> {
try {
console.log('🔵 Erstelle Konversation mit Space ID:', spaceId || 'keine');
const conversation = await conversationApi.createConversation({
modelId,
conversationMode: mode,
templateId,
documentMode,
spaceId,
});
if (!conversation) {
console.error('Fehler beim Erstellen der Konversation');
return null;
}
return conversation.id;
} catch (error) {
console.error('Fehler beim Erstellen der Konversation:', error);
return null;
}
}
/**
* Fügt eine neue Nachricht zur Konversation hinzu
*/
export async function addMessage(
conversationId: string,
sender: 'user' | 'assistant' | 'system',
messageText: string
): Promise<string | null> {
try {
// Validate sender
let validSender = sender;
if (!['user', 'assistant', 'system'].includes(validSender)) {
console.error('Ungültiger Sender-Wert:', sender);
validSender = 'user';
}
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 message.id;
} catch (error) {
console.error('Fehler beim Hinzufügen der Nachricht:', error);
return null;
}
}
/**
* Lädt alle Nachrichten einer Konversation
*/
export async function getMessages(conversationId: string): Promise<Message[]> {
try {
const messages = await conversationApi.getMessages(conversationId);
return messages.map(toLocalMessage);
} catch (error) {
console.error('Fehler beim Laden der Nachrichten:', error);
return [];
}
}
/**
* Generiert einen Titel für die Konversation basierend auf der ersten Benutzeranfrage
*/
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 ? '...' : '')
);
const titlePrompt = `Schreibe eine kurze, prägnante Überschrift (maximal 5 Wörter) für unseren Chat mit dieser Frage: "${userQuestion}"`;
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, '');
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);
return 'Neue Konversation';
}
}
/**
* Aktualisiert den Titel einer Konversation
*/
export async function updateConversationTitle(
conversationId: string,
title: string
): Promise<boolean> {
try {
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);
return false;
}
}
/**
* Lädt einen System-Prompt aus einer Vorlage
*/
export async function getSystemPromptFromTemplate(templateId: string): Promise<string | null> {
try {
const template = await templateApi.getTemplate(templateId);
if (!template) {
console.error('Fehler beim Laden der Vorlage');
return null;
}
return template.systemPrompt;
} catch (error) {
console.error('Fehler beim Laden der Vorlage:', error);
return null;
}
}
/**
* Sendet eine Benutzeranfrage an das LLM-Modell und speichert die Antwort
*/
export async function sendMessageAndGetResponse(
conversationId: string,
userMessage: string,
modelId: string,
templateId?: string,
documentMode: boolean = false
): 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,
});
// 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,
});
}
// Save the user message
const userMessageId = await addMessage(conversationId, 'user', userMessage);
console.log('Benutzernachricht gespeichert mit ID:', userMessageId);
// Load all messages for context
const messages = await getMessages(conversationId);
console.log(`${messages.length} Nachrichten für Kontext geladen`);
// Build chat messages for API
const chatMessages: ChatMessage[] = [];
// 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');
if (documentMode) {
const documentModePrompt = `
${systemPrompt}
WICHTIG: Du befindest dich im Dokumentmodus. Deine Aufgabe ist es, dem Benutzer zu helfen, ein Dokument zu erstellen und zu verbessern.
1. Das Dokument wird in einem separaten Bereich neben dem Chat angezeigt.
2. Wenn der Benutzer Feedback zu dem Dokument gibt, sollst du eine Überarbeitung des Dokuments vorschlagen.
3. Formatiere deine Vorschläge für das Dokument in gut strukturiertem Markdown-Format.
4. Verwende bei längeren Dokumenten Überschriften, Listen und andere Markdown-Elemente zur besseren Gliederung.
5. Antworte IMMER in diesem Format:
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 });
console.log('Dokumentmodus-Prompt hinzugefügt');
} else {
chatMessages.push({ role: 'system', content: systemPrompt });
}
}
} else if (documentMode) {
const documentModePrompt = `
Du befindest dich im Dokumentmodus. Deine Aufgabe ist es, dem Benutzer zu helfen, ein Dokument zu erstellen und zu verbessern.
1. Das Dokument wird in einem separaten Bereich neben dem Chat angezeigt.
2. Wenn der Benutzer Feedback zu dem Dokument gibt, sollst du eine Überarbeitung des Dokuments vorschlagen.
3. Formatiere deine Vorschläge für das Dokument in gut strukturiertem Markdown-Format.
4. Verwende bei längeren Dokumenten Überschriften, Listen und andere Markdown-Elemente zur besseren Gliederung.
5. Antworte IMMER in diesem Format:
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 });
console.log('Standard-Dokumentmodus-Prompt hinzugefügt');
}
// 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');
// 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,
});
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: TokenUsage | undefined;
if (!result) {
assistantResponse =
'Es konnte keine Antwort generiert werden. Bitte stelle sicher, dass das Backend läuft.';
} else {
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,
});
}
// Extract document content if in document mode
let documentContent: string | undefined;
let chatResponse = assistantResponse;
if (documentMode) {
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');
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) || '') + '...'
);
}
}
// Save assistant message
const assistantMessageId = await addMessage(conversationId, 'assistant', chatResponse);
console.log('Assistentenantwort gespeichert mit ID:', assistantMessageId);
// Log token usage if available
if (tokenUsage && assistantMessageId && userMessageId) {
try {
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);
}
}
// Generate title for new conversations
const allMessages = await getMessages(conversationId);
let title: string | undefined;
if (allMessages.length <= 2) {
title = await generateConversationTitle(userMessage);
if (title) {
const success = await updateConversationTitle(conversationId, title);
console.log('Konversationstitel aktualisiert:', success ? 'erfolgreich' : 'fehlgeschlagen');
}
}
return {
userMessageId,
assistantMessageId,
assistantResponse: chatResponse,
title,
documentContent,
};
} catch (error) {
console.error('Fehler beim Senden der Nachricht:', error);
if (error instanceof Error) {
console.error('Fehlerdetails:', {
name: error.name,
message: error.message,
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.`,
};
}
}
// 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
*/
export async function getConversations(userId: string, spaceId?: string): Promise<Conversation[]> {
try {
const conversations = await conversationApi.getConversations(spaceId);
return conversations.map(toLocalConversation);
} catch (error) {
console.error('Fehler beim Laden der Konversationen:', error);
return [];
}
}
/**
* Lädt alle archivierten Konversationen eines Benutzers
*/
export async function getArchivedConversations(userId: string): Promise<Conversation[]> {
try {
const conversations = await conversationApi.getArchivedConversations();
return conversations.map(toLocalConversation);
} catch (error) {
console.error('Fehler beim Laden der archivierten Konversationen:', error);
return [];
}
}
/**
* Archiviert eine Konversation
*/
export async function archiveConversation(conversationId: string): Promise<boolean> {
try {
return await conversationApi.archiveConversation(conversationId);
} catch (error) {
console.error('Fehler beim Archivieren der Konversation:', error);
return false;
}
}
/**
* Stellt eine archivierte Konversation wieder her
*/
export async function unarchiveConversation(conversationId: string): Promise<boolean> {
try {
return await conversationApi.unarchiveConversation(conversationId);
} catch (error) {
console.error('Fehler beim Wiederherstellen der Konversation:', error);
return false;
}
}
/**
* Löscht eine Konversation dauerhaft
*/
export async function deleteConversation(conversationId: string): Promise<boolean> {
try {
return await conversationApi.deleteConversation(conversationId);
} catch (error) {
console.error('Fehler beim Löschen der Konversation:', error);
return false;
}
}

View file

@ -0,0 +1,161 @@
/**
* Document Service - CRUD operations via Backend API
*/
import { documentApi, type Document as ApiDocument } from './api';
// Re-export type with backwards-compatible naming (snake_case for mobile)
export interface Document {
id: string;
conversation_id: string;
version: number;
content: string;
created_at: string;
updated_at: string;
}
// Helper to convert API response to local format
function toLocalDocument(doc: ApiDocument): Document {
return {
id: doc.id,
conversation_id: doc.conversationId,
version: doc.version,
content: doc.content,
created_at: doc.createdAt,
updated_at: doc.updatedAt,
};
}
/**
* Erstellt ein neues Dokument in einer Konversation
*/
export async function createDocument(
conversationId: string,
content: string
): Promise<Document | null> {
try {
console.log(
`Erstelle Dokument für Konversation ${conversationId} mit Inhalt: ${content.substring(0, 50)}...`
);
const document = await documentApi.createDocument(conversationId, content);
if (!document) {
console.error('Fehler beim Erstellen des Dokuments');
return null;
}
console.log('Dokument erfolgreich erstellt:', document);
return toLocalDocument(document);
} catch (error) {
console.error('Fehler beim Erstellen des Dokuments:', error);
if (error instanceof Error) {
console.error('Error details:', error.message, error.stack);
}
return null;
}
}
/**
* Erstellt eine neue Version eines Dokuments
*/
export async function createDocumentVersion(
conversationId: string,
content: string
): Promise<Document | null> {
try {
const document = await documentApi.createDocumentVersion(conversationId, content);
if (!document) {
console.error('Fehler beim Erstellen der neuen Dokumentversion');
return null;
}
return toLocalDocument(document);
} catch (error) {
console.error('Fehler beim Erstellen der neuen Dokumentversion:', error);
return null;
}
}
/**
* Holt die aktuellste Version eines Dokuments für eine Konversation
*/
export async function getLatestDocument(conversationId: string): Promise<Document | null> {
try {
console.log(`Lade neuestes Dokument für Konversation ${conversationId}`);
const document = await documentApi.getLatestDocument(conversationId);
if (!document) {
console.log('Kein Dokument gefunden');
return null;
}
console.log(`Neuestes Dokument gefunden: Version ${document.version}, ID ${document.id}`);
return toLocalDocument(document);
} catch (error) {
console.error('Fehler beim Laden des aktuellen Dokuments:', error);
return null;
}
}
/**
* Lädt alle Versionen eines Dokuments für eine Konversation
*/
export async function getAllDocumentVersions(conversationId: string): Promise<Document[]> {
try {
console.log(`Lade alle Dokumentversionen für Konversation ${conversationId}`);
const documents = await documentApi.getAllDocumentVersions(conversationId);
console.log(`${documents.length} Dokumentversionen geladen`);
if (documents.length > 0) {
console.log(`Erstes Dokument: ID=${documents[0].id}, Version=${documents[0].version}`);
} else {
console.log('Keine Dokumente gefunden');
}
return documents.map(toLocalDocument);
} catch (error) {
console.error('Fehler beim Laden der Dokumentversionen:', error);
return [];
}
}
/**
* Prüft, ob für eine Konversation ein Dokument existiert
*/
export async function hasDocument(conversationId: string): Promise<boolean> {
try {
return await documentApi.hasDocument(conversationId);
} catch (error) {
console.error('Fehler beim Prüfen auf Dokument:', error);
return false;
}
}
/**
* Löscht eine spezifische Dokumentversion
*/
export async function deleteDocumentVersion(documentId: string): Promise<boolean> {
try {
console.log(`=== LÖSCH-OPERATION GESTARTET FÜR DOKUMENT ID ${documentId} ===`);
const success = await documentApi.deleteDocumentVersion(documentId);
if (success) {
console.log(`=== DOKUMENT ${documentId} ERFOLGREICH GELÖSCHT ===`);
} else {
console.error('Fehler beim Löschen der Dokumentversion');
}
return success;
} catch (error) {
console.error('Unerwarteter Fehler beim Löschen der Dokumentversion:', error);
if (error instanceof Error) {
console.error('Fehlerstack:', error.stack);
}
return false;
}
}

View file

@ -0,0 +1,64 @@
/**
* ModelService - Dienst zum Abrufen von KI-Modellen
*/
import { availableModels } from '../config/azure';
// Typendefinition für ein KI-Modell
export interface Model {
id: string;
name: string;
description: string;
parameters?: {
temperature?: number;
max_tokens?: number;
provider?: string;
deployment?: string;
endpoint?: string;
api_version?: string;
};
}
/**
* Ruft alle verfügbaren KI-Modelle ab
* @returns Eine Liste von verfügbaren Modellen
*/
export async function getModels(): Promise<Model[]> {
try {
// In einer echten Anwendung würde hier eine API-Anfrage erfolgen
// Für jetzt verwenden wir die Fallback-Modelle aus der Konfiguration
return availableModels;
} catch (error) {
console.error('Fehler beim Abrufen der Modelle:', error);
return availableModels; // Fallback auf lokale Modelle
}
}
/**
* Ruft ein bestimmtes Modell anhand seiner ID ab
* @param id Die ID des gesuchten Modells
* @returns Das Modell oder undefined, wenn nicht gefunden
*/
export async function getModelById(id: string): Promise<Model | undefined> {
try {
const models = await getModels();
return models.find(model => model.id === id);
} catch (error) {
console.error('Fehler beim Abrufen des Modells:', error);
// Fallback: Suche in lokalen Modellen
return availableModels.find(model => model.id === id);
}
}
/**
* Gibt das Standard-Modell zurück
* @returns Das Standard-Modell
*/
export async function getDefaultModel(): Promise<Model> {
try {
const models = await getModels();
return models[0]; // Das erste Modell in der Liste als Standard
} catch (error) {
console.error('Fehler beim Abrufen des Standard-Modells:', error);
return availableModels[0]; // Fallback auf lokales Standard-Modell
}
}

View file

@ -0,0 +1,200 @@
/**
* Chat Service - AI API calls via Backend
* This service wraps the backend API for AI completions
*/
import { availableModels } from '../config/azure';
import { chatApi, modelApi, usageApi, type ChatMessage, type TokenUsage } from './api';
// Re-export types for backward compatibility
export type { ChatMessage };
// Re-export TokenUsage
export type { TokenUsage };
// Chat response type (kept for compatibility)
export type ChatResponse = {
id: string;
choices: {
content_filter_results?: any;
finish_reason: string;
index: number;
logprobs: any;
message?: {
content: string;
refusal?: any;
role: string;
};
}[];
created: number;
model: string;
object: string;
prompt_filter_results?: any[];
system_fingerprint?: string;
usage: {
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
completion_tokens_details?: any;
prompt_tokens_details?: any;
};
};
// Return type for chat request
export type ChatRequestResult = {
content: string;
usage: TokenUsage;
};
// Logging configuration
console.log('Chat Service Konfiguration:', {
backendUrl: process.env.EXPO_PUBLIC_BACKEND_URL || 'http://localhost:3001',
availableModels: availableModels.length,
});
/**
* Calculates estimated cost for an LLM request
*/
export async function calculateTokenCost(
promptTokens: number,
completionTokens: number,
modelId: string
): Promise<number> {
try {
// Get cost settings from model
const modelData = await modelApi.getModel(modelId);
if (!modelData || !modelData.costSettings) {
console.warn('Fehler beim Laden der Kosteninformationen, verwende Standardwerte');
const promptCost = 0.0001;
const completionCost = 0.0002;
const cost = (promptTokens * promptCost + completionTokens * completionCost) / 1000;
return Number(cost.toFixed(6));
}
const promptCost = modelData.costSettings.prompt_per_1k_tokens || 0.0001;
const completionCost = modelData.costSettings.completion_per_1k_tokens || 0.0002;
const cost = (promptTokens * promptCost + completionTokens * completionCost) / 1000;
return Number(cost.toFixed(6));
} catch (error) {
console.error('Fehler bei der Kostenberechnung:', error);
return Number(((promptTokens * 0.0001 + completionTokens * 0.0002) / 1000).toFixed(6));
}
}
/**
* Logs token usage to the database
*/
export async function logTokenUsage(
usage: TokenUsage,
conversationId: string,
messageId: string,
userId: string,
modelId: string
): Promise<void> {
try {
const estimatedCost = await calculateTokenCost(
usage.prompt_tokens,
usage.completion_tokens,
modelId
);
const success = await usageApi.logTokenUsage({
conversationId,
messageId,
modelId,
promptTokens: usage.prompt_tokens,
completionTokens: usage.completion_tokens,
totalTokens: usage.total_tokens,
estimatedCost,
});
if (success) {
console.log('Token-Nutzung erfolgreich gespeichert:', {
conversationId,
messageId,
totalTokens: usage.total_tokens,
estimatedCost,
});
} else {
console.error('Fehler beim Speichern der Token-Nutzung');
}
} catch (error) {
console.error('Fehler beim Loggen der Token-Nutzung:', error);
}
}
/**
* Sends a chat request via the backend
*/
export async function sendChatRequest(
messages: ChatMessage[],
temperature: number = 0.7,
maxTokens: number = 800
): Promise<string | ChatRequestResult> {
console.log('sendChatRequest gestartet mit:', {
messagesCount: messages.length,
maxTokens,
});
try {
// Find model deployment from system message
let modelId = '550e8400-e29b-41d4-a716-446655440000'; // Default to GPT-O3-Mini
const systemMessage = messages.find(
(msg) => msg.role === 'system' && msg.content.startsWith('MODEL:')
);
if (systemMessage) {
const deployment = systemMessage.content.split(':')[1].trim();
console.log('Modell in system Nachricht erkannt:', deployment);
// Map deployment to model ID
const deploymentToModelId: Record<string, string> = {
'gpt-o3-mini-se': '550e8400-e29b-41d4-a716-446655440000',
'gpt-4o-mini-se': '550e8400-e29b-41d4-a716-446655440004',
'gpt-4o-se': '550e8400-e29b-41d4-a716-446655440005',
};
modelId = deploymentToModelId[deployment] || modelId;
} else {
console.warn('Keine System-Nachricht mit MODEL-Präfix gefunden!');
}
console.log('Verwende Model ID:', modelId);
// Filter out MODEL: system messages before sending to API
const filteredMessages = messages.filter(
(msg) => !(msg.role === 'system' && msg.content.startsWith('MODEL:'))
);
// Send request to backend
const result = await chatApi.createCompletion({
messages: filteredMessages,
modelId,
temperature,
maxTokens,
});
if (!result) {
return 'Es tut mir leid, aber ich konnte keine Antwort generieren. Bitte stelle sicher, dass das Backend läuft.';
}
return {
content: result.content,
usage: result.usage,
};
} catch (error) {
console.error('Fehler bei der Chat-Anfrage:', error);
if (error instanceof Error) {
console.error('Fehlerdetails:', {
name: error.name,
message: error.message,
stack: error.stack,
});
}
return `Es tut mir leid, aber ich konnte keine Antwort generieren. Bitte stelle sicher, dass das Backend läuft. Fehlerdetails: ${error instanceof Error ? error.message : 'Unbekannter Fehler'}`;
}
}

View file

@ -0,0 +1,253 @@
/**
* Space Service - CRUD operations via Backend API
*/
import { spaceApi, type Space as ApiSpace, type SpaceMember as ApiSpaceMember } from './api';
// Re-export types with backwards-compatible naming (snake_case for mobile)
export type Space = {
id: string;
name: string;
description?: string;
owner_id: string;
created_at: string;
updated_at: string;
is_archived: boolean;
};
export type SpaceMember = {
id: string;
space_id: string;
user_id: string;
role: 'owner' | 'admin' | 'member' | 'viewer';
invitation_status: 'pending' | 'accepted' | 'declined';
invited_by?: string;
invited_at: string;
joined_at?: string;
created_at: string;
updated_at: string;
};
// Helper to convert API response to local format
function toLocalSpace(space: ApiSpace): Space {
return {
id: space.id,
name: space.name,
description: space.description,
owner_id: space.ownerId,
created_at: space.createdAt,
updated_at: space.updatedAt,
is_archived: space.isArchived,
};
}
function toLocalSpaceMember(member: ApiSpaceMember): SpaceMember {
return {
id: member.id,
space_id: member.spaceId,
user_id: member.userId,
role: member.role,
invitation_status: member.invitationStatus,
invited_by: member.invitedBy,
invited_at: member.invitedAt,
joined_at: member.joinedAt,
created_at: member.createdAt,
updated_at: member.updatedAt,
};
}
/**
* Get all spaces for a user (both owned and member of)
*/
export async function getUserSpaces(userId: string): Promise<Space[]> {
try {
const spaces = await spaceApi.getUserSpaces();
return spaces.map(toLocalSpace);
} catch (error) {
console.error('Error in getUserSpaces:', error);
return [];
}
}
/**
* Get spaces the user owns
*/
export async function getOwnedSpaces(userId: string): Promise<Space[]> {
try {
const spaces = await spaceApi.getOwnedSpaces();
return spaces.map(toLocalSpace);
} catch (error) {
console.error('Error in getOwnedSpaces:', error);
return [];
}
}
/**
* Get a single space by ID
*/
export async function getSpace(spaceId: string): Promise<Space | null> {
try {
const space = await spaceApi.getSpace(spaceId);
if (!space) {
return null;
}
return toLocalSpace(space);
} catch (error) {
console.error('Error in getSpace:', error);
return null;
}
}
/**
* Create a new space
*/
export async function createSpace(
userId: string,
name: string,
description?: string
): Promise<string | null> {
try {
const space = await spaceApi.createSpace(name, description);
return space?.id || null;
} catch (error) {
console.error('Error in createSpace:', error);
return null;
}
}
/**
* Update a space
*/
export async function updateSpace(
spaceId: string,
updates: { name?: string; description?: string; is_archived?: boolean }
): Promise<boolean> {
try {
return await spaceApi.updateSpace(spaceId, {
name: updates.name,
description: updates.description,
isArchived: updates.is_archived,
});
} catch (error) {
console.error('Error in updateSpace:', error);
return false;
}
}
/**
* Delete a space
*/
export async function deleteSpace(spaceId: string): Promise<boolean> {
try {
return await spaceApi.deleteSpace(spaceId);
} catch (error) {
console.error('Error in deleteSpace:', error);
return false;
}
}
/**
* Get members of a space
*/
export async function getSpaceMembers(spaceId: string): Promise<SpaceMember[]> {
try {
const members = await spaceApi.getSpaceMembers(spaceId);
return members.map(toLocalSpaceMember);
} catch (error) {
console.error('Error in getSpaceMembers:', error);
return [];
}
}
/**
* Add a member to a space (invite)
*/
export async function inviteUserToSpace(
spaceId: string,
userId: string,
invitedByUserId: string,
role: 'admin' | 'member' | 'viewer' = 'member'
): Promise<boolean> {
try {
return await spaceApi.inviteUser(spaceId, userId, role);
} catch (error) {
console.error('Error in inviteUserToSpace:', error);
return false;
}
}
/**
* Accept or decline a space invitation
*/
export async function respondToInvitation(
spaceId: string,
userId: string,
status: 'accepted' | 'declined'
): Promise<boolean> {
try {
return await spaceApi.respondToInvitation(spaceId, status);
} catch (error) {
console.error('Error in respondToInvitation:', error);
return false;
}
}
/**
* Remove a member from a space
*/
export async function removeMember(spaceId: string, userId: string): Promise<boolean> {
try {
return await spaceApi.removeMember(spaceId, userId);
} catch (error) {
console.error('Error in removeMember:', error);
return false;
}
}
/**
* Change a member's role
*/
export async function changeMemberRole(
spaceId: string,
userId: string,
newRole: 'admin' | 'member' | 'viewer'
): Promise<boolean> {
try {
return await spaceApi.changeMemberRole(spaceId, userId, newRole);
} catch (error) {
console.error('Error in changeMemberRole:', error);
return false;
}
}
/**
* Get user's role in a space
*/
export async function getUserRoleInSpace(
spaceId: string,
userId: string
): Promise<'owner' | 'admin' | 'member' | 'viewer' | null> {
try {
return await spaceApi.getUserRoleInSpace(spaceId);
} catch (error) {
console.error('Error in getUserRoleInSpace:', error);
return null;
}
}
/**
* Get pending space invitations for a user
*/
export async function getPendingInvitations(
userId: string
): Promise<Array<{ invitation: SpaceMember; space: Space }>> {
try {
const invitations = await spaceApi.getPendingInvitations();
return invitations.map((inv) => ({
invitation: toLocalSpaceMember(inv.invitation),
space: toLocalSpace(inv.space),
}));
} catch (error) {
console.error('Error in getPendingInvitations:', error);
return [];
}
}

View file

@ -0,0 +1,163 @@
/**
* Template Service - CRUD operations via Backend API
*/
import { templateApi, type Template as ApiTemplate } from './api';
// Re-export type with backwards-compatible naming (snake_case for mobile)
export interface Template {
id: string;
user_id: string;
name: string;
description: string | null;
system_prompt: string;
initial_question: string | null;
model_id: string | null;
color: string;
is_default: boolean;
document_mode: boolean;
created_at: string;
updated_at: string;
}
// Helper to convert API response to local format
function toLocalTemplate(template: ApiTemplate): Template {
return {
id: template.id,
user_id: template.userId,
name: template.name,
description: template.description || null,
system_prompt: template.systemPrompt,
initial_question: template.initialQuestion || null,
model_id: template.modelId || null,
color: template.color,
is_default: template.isDefault,
document_mode: template.documentMode,
created_at: template.createdAt,
updated_at: template.updatedAt,
};
}
/**
* Lädt alle Vorlagen eines Benutzers
*/
export async function getTemplates(userId: string): Promise<Template[]> {
try {
const templates = await templateApi.getTemplates();
return templates.map(toLocalTemplate);
} catch (error) {
console.error('Fehler beim Laden der Vorlagen:', error);
return [];
}
}
/**
* Lädt eine bestimmte Vorlage anhand ihrer ID
*/
export async function getTemplateById(templateId: string): Promise<Template | null> {
try {
const template = await templateApi.getTemplate(templateId);
if (!template) {
return null;
}
return toLocalTemplate(template);
} catch (error) {
console.error('Fehler beim Laden der Vorlage:', error);
return null;
}
}
/**
* Erstellt eine neue Vorlage
*/
export async function createTemplate(
template: Omit<Template, 'id' | 'created_at' | 'updated_at'>
): Promise<Template | null> {
try {
const result = await templateApi.createTemplate({
name: template.name,
description: template.description || undefined,
systemPrompt: template.system_prompt,
initialQuestion: template.initial_question || undefined,
modelId: template.model_id || undefined,
color: template.color,
documentMode: template.document_mode,
});
if (!result) {
console.error('Fehler beim Erstellen der Vorlage');
return null;
}
return toLocalTemplate(result);
} catch (error) {
console.error('Fehler beim Erstellen der Vorlage:', error);
return null;
}
}
/**
* Aktualisiert eine bestehende Vorlage
*/
export async function updateTemplate(
templateId: string,
updates: Partial<Omit<Template, 'id' | 'user_id' | 'created_at' | 'updated_at'>>
): Promise<boolean> {
try {
const apiUpdates: Parameters<typeof templateApi.updateTemplate>[1] = {};
if (updates.name !== undefined) apiUpdates.name = updates.name;
if (updates.description !== undefined)
apiUpdates.description = updates.description || undefined;
if (updates.system_prompt !== undefined) apiUpdates.systemPrompt = updates.system_prompt;
if (updates.initial_question !== undefined)
apiUpdates.initialQuestion = updates.initial_question || undefined;
if (updates.model_id !== undefined) apiUpdates.modelId = updates.model_id || undefined;
if (updates.color !== undefined) apiUpdates.color = updates.color;
if (updates.document_mode !== undefined) apiUpdates.documentMode = updates.document_mode;
return await templateApi.updateTemplate(templateId, apiUpdates);
} catch (error) {
console.error('Fehler beim Aktualisieren der Vorlage:', error);
return false;
}
}
/**
* Löscht eine Vorlage
*/
export async function deleteTemplate(templateId: string): Promise<boolean> {
try {
return await templateApi.deleteTemplate(templateId);
} catch (error) {
console.error('Fehler beim Löschen der Vorlage:', error);
return false;
}
}
/**
* Setzt eine Vorlage als Standard
*/
export async function setDefaultTemplate(templateId: string, userId: string): Promise<boolean> {
try {
return await templateApi.setDefaultTemplate(templateId);
} catch (error) {
console.error('Fehler beim Setzen der Standard-Vorlage:', error);
return false;
}
}
/**
* Holt die Standard-Vorlage des Benutzers
*/
export async function getDefaultTemplate(userId: string): Promise<Template | null> {
try {
const template = await templateApi.getDefaultTemplate();
if (!template) {
return null;
}
return toLocalTemplate(template);
} catch (error) {
console.error('Fehler beim Laden der Standard-Vorlage:', error);
return null;
}
}