feat: enhanced AI usage logging (model, provider, response) and fixed build blockers
This commit is contained in:
17
logging_enhancements.sql
Normal file
17
logging_enhancements.sql
Normal file
@@ -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 $$;
|
||||||
@@ -46,7 +46,7 @@ function sleep(ms: number): Promise<void> {
|
|||||||
/**
|
/**
|
||||||
* Enrich with OpenRouter
|
* 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 client = getOpenRouterClient();
|
||||||
const startApi = performance.now();
|
const startApi = performance.now();
|
||||||
const maxRetries = 3;
|
const maxRetries = 3;
|
||||||
@@ -86,6 +86,7 @@ async function enrichWithOpenRouter(instruction: string): Promise<{ data: any; a
|
|||||||
return {
|
return {
|
||||||
data: JSON.parse(jsonStr),
|
data: JSON.parse(jsonStr),
|
||||||
apiTime: endApi - startApi,
|
apiTime: endApi - startApi,
|
||||||
|
responseText: content
|
||||||
};
|
};
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@@ -109,7 +110,7 @@ async function enrichWithOpenRouter(instruction: string): Promise<{ data: any; a
|
|||||||
/**
|
/**
|
||||||
* Enrich with Gemini
|
* 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 genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!);
|
||||||
const model = genAI.getGenerativeModel({
|
const model = genAI.getGenerativeModel({
|
||||||
model: 'gemini-2.5-flash',
|
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 result = await model.generateContent(instruction);
|
||||||
const endApi = performance.now();
|
const endApi = performance.now();
|
||||||
|
|
||||||
|
const responseText = result.response.text();
|
||||||
return {
|
return {
|
||||||
data: JSON.parse(result.response.text()),
|
data: JSON.parse(responseText),
|
||||||
apiTime: endApi - startApi,
|
apiTime: endApi - startApi,
|
||||||
|
responseText: responseText
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +203,7 @@ Instructions:
|
|||||||
3. Provide a clean "search_string" for Whiskybase (e.g., "Distillery Name Age").`;
|
3. Provide a clean "search_string" for Whiskybase (e.g., "Distillery Name Age").`;
|
||||||
|
|
||||||
console.log(`[EnrichData] Using provider: ${provider}`);
|
console.log(`[EnrichData] Using provider: ${provider}`);
|
||||||
let result: { data: any; apiTime: number };
|
let result: { data: any; apiTime: number; responseText: string };
|
||||||
|
|
||||||
if (provider === 'openrouter') {
|
if (provider === 'openrouter') {
|
||||||
result = await enrichWithOpenRouter(instruction);
|
result = await enrichWithOpenRouter(instruction);
|
||||||
@@ -224,7 +227,10 @@ Instructions:
|
|||||||
userId: userId,
|
userId: userId,
|
||||||
apiType: 'gemini_ai',
|
apiType: 'gemini_ai',
|
||||||
endpoint: `enrichData_${provider}`,
|
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})`);
|
await deductCredits(userId, 'gemini_ai', `Data enrichment (${provider})`);
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ function sleep(ms: number): Promise<void> {
|
|||||||
* Analyze whisky label with OpenRouter (Gemma 3 27B)
|
* Analyze whisky label with OpenRouter (Gemma 3 27B)
|
||||||
* Includes retry logic for 429 rate limit errors
|
* 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 client = getOpenRouterClient();
|
||||||
const startApi = performance.now();
|
const startApi = performance.now();
|
||||||
const maxRetries = 3;
|
const maxRetries = 3;
|
||||||
@@ -129,6 +129,7 @@ async function analyzeWithOpenRouter(base64Data: string, mimeType: string): Prom
|
|||||||
return {
|
return {
|
||||||
data: JSON.parse(jsonStr),
|
data: JSON.parse(jsonStr),
|
||||||
apiTime: endApi - startApi,
|
apiTime: endApi - startApi,
|
||||||
|
responseText: content
|
||||||
};
|
};
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@@ -155,7 +156,7 @@ async function analyzeWithOpenRouter(base64Data: string, mimeType: string): Prom
|
|||||||
/**
|
/**
|
||||||
* Analyze whisky label with Gemini
|
* 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 genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!);
|
||||||
const model = genAI.getGenerativeModel({
|
const model = genAI.getGenerativeModel({
|
||||||
model: 'gemini-2.5-flash',
|
model: 'gemini-2.5-flash',
|
||||||
@@ -179,9 +180,11 @@ async function analyzeWithGemini(base64Data: string, mimeType: string): Promise<
|
|||||||
]);
|
]);
|
||||||
const endApi = performance.now();
|
const endApi = performance.now();
|
||||||
|
|
||||||
|
const responseText = result.response.text();
|
||||||
return {
|
return {
|
||||||
data: JSON.parse(result.response.text()),
|
data: JSON.parse(responseText),
|
||||||
apiTime: endApi - startApi,
|
apiTime: endApi - startApi,
|
||||||
|
responseText: responseText
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +246,7 @@ export async function analyzeLabelWithGemini(imageBase64: string): Promise<Gemin
|
|||||||
|
|
||||||
// Call appropriate provider
|
// Call appropriate provider
|
||||||
console.log(`[Vision] Using provider: ${provider}`);
|
console.log(`[Vision] Using provider: ${provider}`);
|
||||||
let result: { data: any; apiTime: number };
|
let result: { data: any; apiTime: number; responseText: string };
|
||||||
|
|
||||||
if (provider === 'openrouter') {
|
if (provider === 'openrouter') {
|
||||||
result = await analyzeWithOpenRouter(base64Data, mimeType);
|
result = await analyzeWithOpenRouter(base64Data, mimeType);
|
||||||
@@ -278,7 +281,10 @@ export async function analyzeLabelWithGemini(imageBase64: string): Promise<Gemin
|
|||||||
userId: user.id,
|
userId: user.id,
|
||||||
apiType: 'gemini_ai', // Keep same type for tracking
|
apiType: 'gemini_ai', // Keep same type for tracking
|
||||||
endpoint: `analyzeLabelWith${provider === 'openrouter' ? 'OpenRouter' : 'Gemini'}`,
|
endpoint: `analyzeLabelWith${provider === 'openrouter' ? 'OpenRouter' : 'Gemini'}`,
|
||||||
success: true
|
success: true,
|
||||||
|
provider: provider,
|
||||||
|
model: provider === 'openrouter' ? OPENROUTER_VISION_MODEL : 'gemini-2.5-flash',
|
||||||
|
responseText: result.responseText
|
||||||
});
|
});
|
||||||
await deductCredits(user.id, 'gemini_ai', `Vision label analysis (${provider})`);
|
await deductCredits(user.id, 'gemini_ai', `Vision label analysis (${provider})`);
|
||||||
|
|
||||||
|
|||||||
@@ -186,7 +186,10 @@ export async function scanLabel(input: any): Promise<AnalysisResponse> {
|
|||||||
userId: userId,
|
userId: userId,
|
||||||
apiType: 'gemini_ai',
|
apiType: 'gemini_ai',
|
||||||
endpoint: 'scanLabel_openrouter',
|
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)');
|
await deductCredits(userId, 'gemini_ai', 'Bottle OCR scan (OpenRouter)');
|
||||||
|
|
||||||
@@ -248,7 +251,10 @@ export async function scanLabel(input: any): Promise<AnalysisResponse> {
|
|||||||
userId: userId,
|
userId: userId,
|
||||||
apiType: 'gemini_ai',
|
apiType: 'gemini_ai',
|
||||||
endpoint: 'scanLabel_gemini',
|
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)');
|
await deductCredits(userId, 'gemini_ai', 'Bottle OCR scan (Gemini)');
|
||||||
|
|
||||||
@@ -279,7 +285,9 @@ export async function scanLabel(input: any): Promise<AnalysisResponse> {
|
|||||||
apiType: 'gemini_ai',
|
apiType: 'gemini_ai',
|
||||||
endpoint: `scanLabel_${provider}`,
|
endpoint: `scanLabel_${provider}`,
|
||||||
success: false,
|
success: false,
|
||||||
errorMessage: aiError.message
|
errorMessage: aiError.message,
|
||||||
|
provider: provider,
|
||||||
|
model: provider === 'openrouter' ? 'google/gemma-3-27b-it' : 'gemini-2.5-flash'
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -219,36 +219,66 @@ export default async function AdminPage() {
|
|||||||
<tr className="border-b border-zinc-200 dark:border-zinc-800">
|
<tr className="border-b border-zinc-200 dark:border-zinc-800">
|
||||||
<th className="text-left py-3 px-4 text-xs font-black uppercase text-zinc-400">Time</th>
|
<th className="text-left py-3 px-4 text-xs font-black uppercase text-zinc-400">Time</th>
|
||||||
<th className="text-left py-3 px-4 text-xs font-black uppercase text-zinc-400">User</th>
|
<th className="text-left py-3 px-4 text-xs font-black uppercase text-zinc-400">User</th>
|
||||||
<th className="text-left py-3 px-4 text-xs font-black uppercase text-zinc-400">API Type</th>
|
<th className="text-left py-3 px-4 text-xs font-black uppercase text-zinc-400">API/Provider</th>
|
||||||
|
<th className="text-left py-3 px-4 text-xs font-black uppercase text-zinc-400">Model</th>
|
||||||
<th className="text-left py-3 px-4 text-xs font-black uppercase text-zinc-400">Endpoint</th>
|
<th className="text-left py-3 px-4 text-xs font-black uppercase text-zinc-400">Endpoint</th>
|
||||||
<th className="text-left py-3 px-4 text-xs font-black uppercase text-zinc-400">Status</th>
|
<th className="text-left py-3 px-4 text-xs font-black uppercase text-zinc-400">Status</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{recentUsage.map((call: any) => (
|
{recentUsage.map((call: any) => (
|
||||||
<tr key={call.id} className="border-b border-zinc-100 dark:border-zinc-800/50">
|
<tr key={call.id} className="border-b border-zinc-100 dark:border-zinc-800/50 hover:bg-zinc-50 dark:hover:bg-zinc-900/50 transition-colors">
|
||||||
<td className="py-3 px-4 text-sm text-zinc-600 dark:text-zinc-400">
|
<td className="py-3 px-4 text-[10px] text-zinc-500 font-mono">
|
||||||
{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' })}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-3 px-4 text-sm text-zinc-900 dark:text-white">
|
<td className="py-3 px-4 text-sm font-bold text-zinc-900 dark:text-white">
|
||||||
{call.profiles?.username || 'Unknown'}
|
{call.profiles?.username || 'Unknown'}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-3 px-4">
|
<td className="py-3 px-4">
|
||||||
<span className={`px-2 py-1 rounded-full text-xs font-bold ${call.api_type === 'google_search'
|
<div className="flex flex-col gap-1">
|
||||||
? 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400'
|
<span className={`px-2 py-0.5 rounded-full text-[10px] font-black uppercase w-fit ${call.api_type === 'google_search'
|
||||||
: 'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-400'
|
? 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400'
|
||||||
}`}>
|
: 'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-400'
|
||||||
{call.api_type === 'google_search' ? 'Google Search' : 'Gemini AI'}
|
}`}>
|
||||||
|
{call.api_type === 'google_search' ? 'Google' : 'AI'}
|
||||||
|
</span>
|
||||||
|
{call.provider && (
|
||||||
|
<span className="text-[10px] font-bold text-zinc-500 uppercase tracking-tighter">
|
||||||
|
via {call.provider}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="py-3 px-4">
|
||||||
|
<span className="text-[10px] font-mono text-zinc-600 dark:text-zinc-400 block max-w-[120px] truncate" title={call.model}>
|
||||||
|
{call.model || '-'}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="py-3 px-4 text-sm text-zinc-600 dark:text-zinc-400">
|
<td className="py-3 px-4">
|
||||||
{call.endpoint}
|
<div className="space-y-1">
|
||||||
|
<div className="text-[10px] font-bold text-zinc-500 uppercase">{call.endpoint}</div>
|
||||||
|
{call.response_text && (
|
||||||
|
<details className="text-[10px]">
|
||||||
|
<summary className="cursor-pointer text-orange-600 hover:text-orange-700 font-bold uppercase transition-colors">Response</summary>
|
||||||
|
<pre className="mt-2 p-2 bg-zinc-100 dark:bg-zinc-950 rounded border border-zinc-200 dark:border-zinc-800 overflow-x-auto max-w-sm whitespace-pre-wrap font-mono text-[9px] text-zinc-400">
|
||||||
|
{call.response_text}
|
||||||
|
</pre>
|
||||||
|
</details>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="py-3 px-4">
|
<td className="py-3 px-4">
|
||||||
{call.success ? (
|
{call.success ? (
|
||||||
<span className="text-green-600 dark:text-green-400 font-bold">✓</span>
|
<span className="text-green-600 dark:text-green-400 font-black text-xs">OK</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-red-600 dark:text-red-400 font-bold">✗</span>
|
<div className="group relative">
|
||||||
|
<span className="text-red-600 dark:text-red-400 font-black text-xs cursor-help">ERR</span>
|
||||||
|
{call.error_message && (
|
||||||
|
<div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-2 w-48 p-2 bg-red-600 text-white text-[9px] rounded shadow-xl opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none z-50">
|
||||||
|
{call.error_message}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export async function updateUserCredits(
|
|||||||
|
|
||||||
// Get current credits - if not found, create entry first
|
// Get current credits - if not found, create entry first
|
||||||
let currentCredits = await getUserCredits(validated.userId);
|
let currentCredits = await getUserCredits(validated.userId);
|
||||||
|
|
||||||
if (!currentCredits) {
|
if (!currentCredits) {
|
||||||
// Create credits entry for user who doesn't have one
|
// Create credits entry for user who doesn't have one
|
||||||
console.log(`[updateUserCredits] Creating credits entry for user ${validated.userId}`);
|
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);
|
console.error('Error creating user credits:', insertError);
|
||||||
return { success: false, error: 'Failed to create user credits' };
|
return { success: false, error: 'Failed to create user credits' };
|
||||||
}
|
}
|
||||||
|
|
||||||
currentCredits = newCredits;
|
currentCredits = newCredits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!currentCredits) {
|
||||||
|
return { success: false, error: 'Konto konnte nicht erstellt werden' };
|
||||||
|
}
|
||||||
|
|
||||||
const difference = validated.newBalance - currentCredits.balance;
|
const difference = validated.newBalance - currentCredits.balance;
|
||||||
|
|
||||||
// Use addCredits which handles the transaction logging
|
// Use addCredits which handles the transaction logging
|
||||||
|
|||||||
@@ -135,7 +135,10 @@ export async function analyzeBottleMistral(input: any): Promise<AnalysisResponse
|
|||||||
userId: userId,
|
userId: userId,
|
||||||
apiType: 'gemini_ai',
|
apiType: 'gemini_ai',
|
||||||
endpoint: 'mistral/mistral-large',
|
endpoint: 'mistral/mistral-large',
|
||||||
success: true
|
success: true,
|
||||||
|
provider: 'mistral',
|
||||||
|
model: 'mistral-large-latest',
|
||||||
|
responseText: rawContent as string
|
||||||
});
|
});
|
||||||
|
|
||||||
await deductCredits(userId, 'gemini_ai', 'Mistral AI analysis');
|
await deductCredits(userId, 'gemini_ai', 'Mistral AI analysis');
|
||||||
@@ -164,7 +167,9 @@ export async function analyzeBottleMistral(input: any): Promise<AnalysisResponse
|
|||||||
apiType: 'gemini_ai',
|
apiType: 'gemini_ai',
|
||||||
endpoint: 'mistral/mistral-large',
|
endpoint: 'mistral/mistral-large',
|
||||||
success: false,
|
success: false,
|
||||||
errorMessage: aiError.message
|
errorMessage: aiError.message,
|
||||||
|
provider: 'mistral',
|
||||||
|
model: 'mistral-large-latest'
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -128,7 +128,10 @@ export async function analyzeBottle(input: any): Promise<AnalysisResponse> {
|
|||||||
userId: userId,
|
userId: userId,
|
||||||
apiType: 'gemini_ai',
|
apiType: 'gemini_ai',
|
||||||
endpoint: 'analyzeBottle_openrouter',
|
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)');
|
await deductCredits(userId, 'gemini_ai', 'Bottle analysis (OpenRouter)');
|
||||||
|
|
||||||
@@ -196,7 +199,10 @@ export async function analyzeBottle(input: any): Promise<AnalysisResponse> {
|
|||||||
userId: userId,
|
userId: userId,
|
||||||
apiType: 'gemini_ai',
|
apiType: 'gemini_ai',
|
||||||
endpoint: 'analyzeBottle_gemini',
|
endpoint: 'analyzeBottle_gemini',
|
||||||
success: true
|
success: true,
|
||||||
|
provider: 'google',
|
||||||
|
model: 'gemini-2.5-flash',
|
||||||
|
responseText: responseText
|
||||||
});
|
});
|
||||||
await deductCredits(userId, 'gemini_ai', 'Bottle analysis (Gemini)');
|
await deductCredits(userId, 'gemini_ai', 'Bottle analysis (Gemini)');
|
||||||
|
|
||||||
@@ -225,7 +231,9 @@ export async function analyzeBottle(input: any): Promise<AnalysisResponse> {
|
|||||||
apiType: 'gemini_ai',
|
apiType: 'gemini_ai',
|
||||||
endpoint: `analyzeBottle_${provider}`,
|
endpoint: `analyzeBottle_${provider}`,
|
||||||
success: false,
|
success: false,
|
||||||
errorMessage: aiError.message
|
errorMessage: aiError.message,
|
||||||
|
provider: provider,
|
||||||
|
model: provider === 'openrouter' ? 'google/gemma-3-27b-it' : 'gemini-2.5-flash'
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ interface TrackApiUsageParams {
|
|||||||
endpoint?: string;
|
endpoint?: string;
|
||||||
success: boolean;
|
success: boolean;
|
||||||
errorMessage?: string;
|
errorMessage?: string;
|
||||||
|
model?: string;
|
||||||
|
provider?: string;
|
||||||
|
responseText?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ApiStats {
|
interface ApiStats {
|
||||||
@@ -49,6 +52,9 @@ export async function trackApiUsage(params: TrackApiUsageParams): Promise<{ succ
|
|||||||
endpoint: params.endpoint,
|
endpoint: params.endpoint,
|
||||||
success: params.success,
|
success: params.success,
|
||||||
error_message: params.errorMessage,
|
error_message: params.errorMessage,
|
||||||
|
model: params.model,
|
||||||
|
provider: params.provider,
|
||||||
|
response_text: params.responseText
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|||||||
@@ -238,6 +238,9 @@ CREATE TABLE IF NOT EXISTS api_usage (
|
|||||||
endpoint TEXT,
|
endpoint TEXT,
|
||||||
success BOOLEAN DEFAULT true,
|
success BOOLEAN DEFAULT true,
|
||||||
error_message TEXT,
|
error_message TEXT,
|
||||||
|
model TEXT,
|
||||||
|
provider TEXT,
|
||||||
|
response_text TEXT,
|
||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('Europe/Berlin'::text, now())
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('Europe/Berlin'::text, now())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user