From c51cd23d5ebbf38e2745df868bc39364851ea9a5 Mon Sep 17 00:00:00 2001 From: robin Date: Sat, 27 Dec 2025 00:10:55 +0100 Subject: [PATCH] feat: enhanced AI usage logging (model, provider, response) and fixed build blockers --- logging_enhancements.sql | 17 ++++++++ src/app/actions/enrich-data.ts | 16 ++++--- src/app/actions/gemini-vision.ts | 16 ++++--- src/app/actions/scan-label.ts | 14 +++++-- src/app/admin/page.tsx | 58 +++++++++++++++++++------- src/services/admin-credit-service.ts | 8 +++- src/services/analyze-bottle-mistral.ts | 9 +++- src/services/analyze-bottle.ts | 14 +++++-- src/services/track-api-usage.ts | 6 +++ supa_schema.sql | 3 ++ 10 files changed, 127 insertions(+), 34 deletions(-) create mode 100644 logging_enhancements.sql diff --git a/logging_enhancements.sql b/logging_enhancements.sql new file mode 100644 index 0000000..1ce76ec --- /dev/null +++ b/logging_enhancements.sql @@ -0,0 +1,17 @@ +-- AI Logging Enhancements +-- Add model, provider and response_text to api_usage table + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'api_usage' AND COLUMN_NAME = 'model') THEN + ALTER TABLE api_usage ADD COLUMN model TEXT; + END IF; + + IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'api_usage' AND COLUMN_NAME = 'provider') THEN + ALTER TABLE api_usage ADD COLUMN provider TEXT; + END IF; + + IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'api_usage' AND COLUMN_NAME = 'response_text') THEN + ALTER TABLE api_usage ADD COLUMN response_text TEXT; + END IF; +END $$; diff --git a/src/app/actions/enrich-data.ts b/src/app/actions/enrich-data.ts index b2ad023..a33b8d4 100644 --- a/src/app/actions/enrich-data.ts +++ b/src/app/actions/enrich-data.ts @@ -46,7 +46,7 @@ function sleep(ms: number): Promise { /** * Enrich with OpenRouter */ -async function enrichWithOpenRouter(instruction: string): Promise<{ data: any; apiTime: number }> { +async function enrichWithOpenRouter(instruction: string): Promise<{ data: any; apiTime: number; responseText: string }> { const client = getOpenRouterClient(); const startApi = performance.now(); const maxRetries = 3; @@ -86,6 +86,7 @@ async function enrichWithOpenRouter(instruction: string): Promise<{ data: any; a return { data: JSON.parse(jsonStr), apiTime: endApi - startApi, + responseText: content }; } catch (error: any) { @@ -109,7 +110,7 @@ async function enrichWithOpenRouter(instruction: string): Promise<{ data: any; a /** * Enrich with Gemini */ -async function enrichWithGemini(instruction: string): Promise<{ data: any; apiTime: number }> { +async function enrichWithGemini(instruction: string): Promise<{ data: any; apiTime: number; responseText: string }> { const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!); const model = genAI.getGenerativeModel({ model: 'gemini-2.5-flash', @@ -130,9 +131,11 @@ async function enrichWithGemini(instruction: string): Promise<{ data: any; apiTi const result = await model.generateContent(instruction); const endApi = performance.now(); + const responseText = result.response.text(); return { - data: JSON.parse(result.response.text()), + data: JSON.parse(responseText), apiTime: endApi - startApi, + responseText: responseText }; } @@ -200,7 +203,7 @@ Instructions: 3. Provide a clean "search_string" for Whiskybase (e.g., "Distillery Name Age").`; console.log(`[EnrichData] Using provider: ${provider}`); - let result: { data: any; apiTime: number }; + let result: { data: any; apiTime: number; responseText: string }; if (provider === 'openrouter') { result = await enrichWithOpenRouter(instruction); @@ -224,7 +227,10 @@ Instructions: userId: userId, apiType: 'gemini_ai', endpoint: `enrichData_${provider}`, - success: true + success: true, + provider: provider, + model: provider === 'openrouter' ? ENRICHMENT_MODEL : 'gemini-2.5-flash', + responseText: result.responseText }); await deductCredits(userId, 'gemini_ai', `Data enrichment (${provider})`); diff --git a/src/app/actions/gemini-vision.ts b/src/app/actions/gemini-vision.ts index c1ef253..d151b8c 100644 --- a/src/app/actions/gemini-vision.ts +++ b/src/app/actions/gemini-vision.ts @@ -83,7 +83,7 @@ function sleep(ms: number): Promise { * Analyze whisky label with OpenRouter (Gemma 3 27B) * Includes retry logic for 429 rate limit errors */ -async function analyzeWithOpenRouter(base64Data: string, mimeType: string): Promise<{ data: any; apiTime: number }> { +async function analyzeWithOpenRouter(base64Data: string, mimeType: string): Promise<{ data: any; apiTime: number; responseText: string }> { const client = getOpenRouterClient(); const startApi = performance.now(); const maxRetries = 3; @@ -129,6 +129,7 @@ async function analyzeWithOpenRouter(base64Data: string, mimeType: string): Prom return { data: JSON.parse(jsonStr), apiTime: endApi - startApi, + responseText: content }; } catch (error: any) { @@ -155,7 +156,7 @@ async function analyzeWithOpenRouter(base64Data: string, mimeType: string): Prom /** * Analyze whisky label with Gemini */ -async function analyzeWithGemini(base64Data: string, mimeType: string): Promise<{ data: any; apiTime: number }> { +async function analyzeWithGemini(base64Data: string, mimeType: string): Promise<{ data: any; apiTime: number; responseText: string }> { const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!); const model = genAI.getGenerativeModel({ model: 'gemini-2.5-flash', @@ -179,9 +180,11 @@ async function analyzeWithGemini(base64Data: string, mimeType: string): Promise< ]); const endApi = performance.now(); + const responseText = result.response.text(); return { - data: JSON.parse(result.response.text()), + data: JSON.parse(responseText), apiTime: endApi - startApi, + responseText: responseText }; } @@ -243,7 +246,7 @@ export async function analyzeLabelWithGemini(imageBase64: string): Promise { userId: userId, apiType: 'gemini_ai', endpoint: 'scanLabel_openrouter', - success: true + success: true, + provider: 'openrouter', + model: 'google/gemma-3-27b-it', + responseText: content }); await deductCredits(userId, 'gemini_ai', 'Bottle OCR scan (OpenRouter)'); @@ -248,7 +251,10 @@ export async function scanLabel(input: any): Promise { userId: userId, apiType: 'gemini_ai', endpoint: 'scanLabel_gemini', - success: true + success: true, + provider: 'google', + model: 'gemini-2.5-flash', + responseText: result.response.text() }); await deductCredits(userId, 'gemini_ai', 'Bottle OCR scan (Gemini)'); @@ -279,7 +285,9 @@ export async function scanLabel(input: any): Promise { apiType: 'gemini_ai', endpoint: `scanLabel_${provider}`, success: false, - errorMessage: aiError.message + errorMessage: aiError.message, + provider: provider, + model: provider === 'openrouter' ? 'google/gemma-3-27b-it' : 'gemini-2.5-flash' }); return { diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index d4adff5..c54bb32 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -219,36 +219,66 @@ export default async function AdminPage() { Time User - API Type + API/Provider + Model Endpoint Status {recentUsage.map((call: any) => ( - - - {new Date(call.created_at).toLocaleString('de-DE')} + + + {new Date(call.created_at).toLocaleString('de-DE', { hour: '2-digit', minute: '2-digit', second: '2-digit', day: '2-digit', month: '2-digit' })} - + {call.profiles?.username || 'Unknown'} - - {call.api_type === 'google_search' ? 'Google Search' : 'Gemini AI'} +
+ + {call.api_type === 'google_search' ? 'Google' : 'AI'} + + {call.provider && ( + + via {call.provider} + + )} +
+ + + + {call.model || '-'} - - {call.endpoint} + +
+
{call.endpoint}
+ {call.response_text && ( +
+ Response +
+                                                                {call.response_text}
+                                                            
+
+ )} +
{call.success ? ( - + OK ) : ( - +
+ ERR + {call.error_message && ( +
+ {call.error_message} +
+ )} +
)} diff --git a/src/services/admin-credit-service.ts b/src/services/admin-credit-service.ts index 06900cc..595a9bf 100644 --- a/src/services/admin-credit-service.ts +++ b/src/services/admin-credit-service.ts @@ -109,7 +109,7 @@ export async function updateUserCredits( // Get current credits - if not found, create entry first let currentCredits = await getUserCredits(validated.userId); - + if (!currentCredits) { // Create credits entry for user who doesn't have one console.log(`[updateUserCredits] Creating credits entry for user ${validated.userId}`); @@ -128,10 +128,14 @@ export async function updateUserCredits( console.error('Error creating user credits:', insertError); return { success: false, error: 'Failed to create user credits' }; } - + currentCredits = newCredits; } + if (!currentCredits) { + return { success: false, error: 'Konto konnte nicht erstellt werden' }; + } + const difference = validated.newBalance - currentCredits.balance; // Use addCredits which handles the transaction logging diff --git a/src/services/analyze-bottle-mistral.ts b/src/services/analyze-bottle-mistral.ts index e76765e..53da401 100644 --- a/src/services/analyze-bottle-mistral.ts +++ b/src/services/analyze-bottle-mistral.ts @@ -135,7 +135,10 @@ export async function analyzeBottleMistral(input: any): Promise { userId: userId, apiType: 'gemini_ai', endpoint: 'analyzeBottle_openrouter', - success: true + success: true, + provider: 'openrouter', + model: 'google/gemma-3-27b-it', + responseText: content }); await deductCredits(userId, 'gemini_ai', 'Bottle analysis (OpenRouter)'); @@ -196,7 +199,10 @@ export async function analyzeBottle(input: any): Promise { userId: userId, apiType: 'gemini_ai', endpoint: 'analyzeBottle_gemini', - success: true + success: true, + provider: 'google', + model: 'gemini-2.5-flash', + responseText: responseText }); await deductCredits(userId, 'gemini_ai', 'Bottle analysis (Gemini)'); @@ -225,7 +231,9 @@ export async function analyzeBottle(input: any): Promise { apiType: 'gemini_ai', endpoint: `analyzeBottle_${provider}`, success: false, - errorMessage: aiError.message + errorMessage: aiError.message, + provider: provider, + model: provider === 'openrouter' ? 'google/gemma-3-27b-it' : 'gemini-2.5-flash' }); return { diff --git a/src/services/track-api-usage.ts b/src/services/track-api-usage.ts index 201d63d..f4980da 100644 --- a/src/services/track-api-usage.ts +++ b/src/services/track-api-usage.ts @@ -8,6 +8,9 @@ interface TrackApiUsageParams { endpoint?: string; success: boolean; errorMessage?: string; + model?: string; + provider?: string; + responseText?: string; } interface ApiStats { @@ -49,6 +52,9 @@ export async function trackApiUsage(params: TrackApiUsageParams): Promise<{ succ endpoint: params.endpoint, success: params.success, error_message: params.errorMessage, + model: params.model, + provider: params.provider, + response_text: params.responseText }); if (error) { diff --git a/supa_schema.sql b/supa_schema.sql index bbfa0b9..16d5bfd 100644 --- a/supa_schema.sql +++ b/supa_schema.sql @@ -238,6 +238,9 @@ CREATE TABLE IF NOT EXISTS api_usage ( endpoint TEXT, success BOOLEAN DEFAULT true, error_message TEXT, + model TEXT, + provider TEXT, + response_text TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('Europe/Berlin'::text, now()) );