mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-23 11:06:42 +02:00
refactor: restructure
monorepo with apps/ and services/ directories
This commit is contained in:
parent
25824ed0ac
commit
ff80aeec1f
4062 changed files with 2592 additions and 1278 deletions
633
apps/chat/apps/mobile/services/api.ts
Normal file
633
apps/chat/apps/mobile/services/api.ts
Normal 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;
|
||||
},
|
||||
};
|
||||
550
apps/chat/apps/mobile/services/conversation.ts
Normal file
550
apps/chat/apps/mobile/services/conversation.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
161
apps/chat/apps/mobile/services/document.ts
Normal file
161
apps/chat/apps/mobile/services/document.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
64
apps/chat/apps/mobile/services/modelService.ts
Normal file
64
apps/chat/apps/mobile/services/modelService.ts
Normal 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
|
||||
}
|
||||
}
|
||||
200
apps/chat/apps/mobile/services/openai.ts
Normal file
200
apps/chat/apps/mobile/services/openai.ts
Normal 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'}`;
|
||||
}
|
||||
}
|
||||
253
apps/chat/apps/mobile/services/space.ts
Normal file
253
apps/chat/apps/mobile/services/space.ts
Normal 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 [];
|
||||
}
|
||||
}
|
||||
163
apps/chat/apps/mobile/services/template.ts
Normal file
163
apps/chat/apps/mobile/services/template.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue