mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-23 00:06:42 +02:00
feat(chat): integrate chat project into monorepo with full app structure
- Restructure chat as apps/mobile, apps/web, apps/landing, backend - Add NestJS backend for secure Azure OpenAI API calls - Remove exposed API key from mobile app (security fix) - Add shared chat-types package - Create SvelteKit web app scaffold - Create Astro landing page scaffold - Update pnpm workspace configuration - Add project-level CLAUDE.md documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
fcf3a344b1
commit
c638a7ffee
155 changed files with 22622 additions and 348 deletions
148
chat/apps/mobile/app/api/models+api.ts
Normal file
148
chat/apps/mobile/app/api/models+api.ts
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
import { supabase } from '../../utils/supabase';
|
||||
|
||||
// Definiere den Typ für ein Modell
|
||||
export type Model = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
parameters?: Record<string, any>;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
};
|
||||
|
||||
// Fallback-Modelle, falls keine aus der Datenbank geladen werden können
|
||||
const FALLBACK_MODELS: Model[] = [
|
||||
{
|
||||
id: '550e8400-e29b-41d4-a716-446655440000',
|
||||
name: 'GPT-O3-Mini',
|
||||
description: 'Azure OpenAI O3-Mini: Effizientes Modell für schnelle Antworten.',
|
||||
parameters: {
|
||||
temperature: 0.7,
|
||||
max_tokens: 800,
|
||||
provider: 'azure',
|
||||
deployment: 'gpt-o3-mini-se',
|
||||
endpoint: 'https://memoroseopenai.openai.azure.com',
|
||||
api_version: '2024-12-01-preview'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '550e8400-e29b-41d4-a716-446655440004',
|
||||
name: 'GPT-4o-Mini',
|
||||
description: 'Azure OpenAI GPT-4o-Mini: Kompaktes, leistungsstarkes KI-Modell.',
|
||||
parameters: {
|
||||
temperature: 0.7,
|
||||
max_tokens: 1000,
|
||||
provider: 'azure',
|
||||
deployment: 'gpt-4o-mini-se',
|
||||
endpoint: 'https://memoroseopenai.openai.azure.com',
|
||||
api_version: '2024-12-01-preview'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '550e8400-e29b-41d4-a716-446655440005',
|
||||
name: 'GPT-4o',
|
||||
description: 'Azure OpenAI GPT-4o: Das fortschrittlichste multimodale KI-Modell.',
|
||||
parameters: {
|
||||
temperature: 0.7,
|
||||
max_tokens: 1200,
|
||||
provider: 'azure',
|
||||
deployment: 'gpt-4o-se',
|
||||
endpoint: 'https://memoroseopenai.openai.azure.com',
|
||||
api_version: '2024-12-01-preview'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// GET-Handler für Modelle
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
// Versuche, Modelle aus der Supabase-Datenbank zu laden
|
||||
let models: Model[] = FALLBACK_MODELS;
|
||||
|
||||
// Wenn Supabase konfiguriert ist, versuche die Modelle von dort zu laden
|
||||
try {
|
||||
if (supabase) {
|
||||
const { data, error } = await supabase
|
||||
.from('models')
|
||||
.select('*');
|
||||
// Entfernt: .order('created_at', { ascending: false })
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Laden der Modelle aus Supabase:', error);
|
||||
} else if (data && data.length > 0) {
|
||||
models = data as Model[];
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Fehler bei der Supabase-Verbindung:', e);
|
||||
// Fallback zu den vordefinierten Modellen
|
||||
}
|
||||
|
||||
return Response.json(models);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Verarbeiten der Anfrage:', error);
|
||||
return new Response(JSON.stringify({ error: 'Interner Serverfehler' }), {
|
||||
status: 500,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// POST-Handler zum Erstellen eines neuen Modells
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
|
||||
// Validiere die Eingabedaten
|
||||
if (!body.name || !body.description) {
|
||||
return new Response(JSON.stringify({ error: 'Name und Beschreibung sind erforderlich' }), {
|
||||
status: 400,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Erstelle ein neues Modell in der Datenbank
|
||||
if (supabase) {
|
||||
const { data, error } = await supabase
|
||||
.from('models')
|
||||
.insert([{
|
||||
name: body.name,
|
||||
description: body.description,
|
||||
parameters: body.parameters || {},
|
||||
}])
|
||||
.select();
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Erstellen des Modells:', error);
|
||||
return new Response(JSON.stringify({ error: 'Fehler beim Erstellen des Modells' }), {
|
||||
status: 500,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return Response.json(data[0]);
|
||||
} else {
|
||||
// Wenn Supabase nicht verfügbar ist, gib einen Fehler zurück
|
||||
return new Response(JSON.stringify({ error: 'Datenbank nicht verfügbar' }), {
|
||||
status: 503,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Verarbeiten der Anfrage:', error);
|
||||
return new Response(JSON.stringify({ error: 'Interner Serverfehler' }), {
|
||||
status: 500,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
137
chat/apps/mobile/app/api/usage+api.ts
Normal file
137
chat/apps/mobile/app/api/usage+api.ts
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
import { supabase } from '../../utils/supabase';
|
||||
|
||||
// Typ für die Token-Nutzung pro Modell
|
||||
export type ModelUsage = {
|
||||
model_id: string;
|
||||
model_name: string;
|
||||
total_prompt_tokens: number;
|
||||
total_completion_tokens: number;
|
||||
total_tokens: number;
|
||||
total_cost: number;
|
||||
};
|
||||
|
||||
// Typ für die Token-Nutzung nach Zeitraum
|
||||
export type UsageByPeriod = {
|
||||
time_period: string;
|
||||
total_tokens: number;
|
||||
total_cost: number;
|
||||
};
|
||||
|
||||
// Typ für die Token-Nutzung einer Konversation
|
||||
export type ConversationUsage = {
|
||||
message_id: string;
|
||||
created_at: string;
|
||||
prompt_tokens: number;
|
||||
completion_tokens: number;
|
||||
total_tokens: number;
|
||||
estimated_cost: number;
|
||||
};
|
||||
|
||||
// Handler für GET /api/usage
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const userId = url.searchParams.get('userId');
|
||||
const period = url.searchParams.get('period') || 'month';
|
||||
|
||||
if (!userId) {
|
||||
return new Response(JSON.stringify({ error: 'User ID ist erforderlich' }), {
|
||||
status: 400,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
// Lade die Tokennutzung nach Modell
|
||||
const { data: modelUsage, error: modelError } = await supabase
|
||||
.rpc('get_user_model_usage', { user_id: userId });
|
||||
|
||||
if (modelError) {
|
||||
console.error('Fehler beim Laden der Modellnutzung:', modelError);
|
||||
return new Response(JSON.stringify({ error: 'Fehler beim Laden der Modellnutzung' }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
// Lade die Tokennutzung nach Zeitraum
|
||||
const { data: periodUsage, error: periodError } = await supabase
|
||||
.rpc('get_user_usage_by_period', {
|
||||
user_id: userId,
|
||||
period: period
|
||||
});
|
||||
|
||||
if (periodError) {
|
||||
console.error('Fehler beim Laden der Zeitraumnutzung:', periodError);
|
||||
return new Response(JSON.stringify({ error: 'Fehler beim Laden der Zeitraumnutzung' }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
// Berechne Gesamtkosten und Token
|
||||
const totalCost = (modelUsage as ModelUsage[]).reduce((sum, model) => sum + model.total_cost, 0);
|
||||
const totalTokens = (modelUsage as ModelUsage[]).reduce((sum, model) => sum + model.total_tokens, 0);
|
||||
|
||||
return Response.json({
|
||||
modelUsage,
|
||||
periodUsage,
|
||||
summary: {
|
||||
totalCost,
|
||||
totalTokens
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Verarbeiten der Anfrage:', error);
|
||||
return new Response(JSON.stringify({ error: 'Interner Serverfehler' }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Handler für GET /api/usage/conversation
|
||||
export async function GET_conversation(request: Request) {
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const conversationId = url.searchParams.get('conversationId');
|
||||
|
||||
if (!conversationId) {
|
||||
return new Response(JSON.stringify({ error: 'Conversation ID ist erforderlich' }), {
|
||||
status: 400,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
// Lade die Tokennutzung für die Konversation
|
||||
const { data: conversationUsage, error } = await supabase
|
||||
.rpc('get_conversation_usage', { conversation_id: conversationId });
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Laden der Konversationsnutzung:', error);
|
||||
return new Response(JSON.stringify({ error: 'Fehler beim Laden der Konversationsnutzung' }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
// Berechne Gesamtkosten und Token für diese Konversation
|
||||
const usage = conversationUsage as ConversationUsage[];
|
||||
const totalCost = usage.reduce((sum, item) => sum + item.estimated_cost, 0);
|
||||
const totalTokens = usage.reduce((sum, item) => sum + item.total_tokens, 0);
|
||||
|
||||
return Response.json({
|
||||
conversationUsage,
|
||||
summary: {
|
||||
totalCost,
|
||||
totalTokens,
|
||||
messageCount: usage.length
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Verarbeiten der Anfrage:', error);
|
||||
return new Response(JSON.stringify({ error: 'Interner Serverfehler' }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue