feat(ai): reichhaltigeres Logging fuer AI_FAILED-Diagnose
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 2m15s
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 2m15s
Der bisherige Log "[extract-from-photo] AI_FAILED after 43165ms, 385807 bytes" verriet nicht, ob es JSON-Parse, Schema-Validierung oder ein SDK-Fehler war. Endpoint haengt jetzt e.message an; gemini-client loggt den First-Attempt-Fehler vor dem Retry und packt bei AI_FAILED beide Messages in den finalen Error. Keine Prompt-/Response-Inhalte werden geloggt -- nur unsere eigenen GeminiError-Messages (Zod-Pfade, "non-JSON output", SDK-toString). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -114,6 +114,7 @@ export async function extractRecipeFromImage(
|
||||
imageBuffer: Buffer,
|
||||
mimeType: string
|
||||
): Promise<ExtractionResponse> {
|
||||
let firstMsg: string | null = null;
|
||||
try {
|
||||
return await callGemini(imageBuffer, mimeType);
|
||||
} catch (e) {
|
||||
@@ -132,6 +133,9 @@ export async function extractRecipeFromImage(
|
||||
: new GeminiError('AI_FAILED', String(e));
|
||||
}
|
||||
|
||||
firstMsg = e instanceof Error ? e.message : String(e);
|
||||
console.warn(`[gemini-client] first attempt failed, retrying: ${firstMsg}`);
|
||||
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
try {
|
||||
return await callGemini(
|
||||
@@ -140,11 +144,23 @@ export async function extractRecipeFromImage(
|
||||
'Dein vorheriger Output war ungültig. Bitte antworte ausschließlich mit JSON gemäß Schema.'
|
||||
);
|
||||
} catch (retryErr) {
|
||||
if (retryErr instanceof GeminiError) throw retryErr;
|
||||
const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
|
||||
if (retryErr instanceof GeminiError) {
|
||||
if (retryErr.code === 'AI_FAILED') {
|
||||
throw new GeminiError(
|
||||
'AI_FAILED',
|
||||
`retry failed: ${retryMsg} (first: ${firstMsg})`
|
||||
);
|
||||
}
|
||||
throw retryErr;
|
||||
}
|
||||
const retryStatus = getStatus(retryErr);
|
||||
if (retryStatus === 429)
|
||||
throw new GeminiError('AI_RATE_LIMITED', 'Gemini rate limit on retry');
|
||||
throw new GeminiError('AI_FAILED', String(retryErr));
|
||||
throw new GeminiError(
|
||||
'AI_FAILED',
|
||||
`retry failed: ${retryMsg} (first: ${firstMsg})`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,9 +121,11 @@ export const POST: RequestHandler = async ({ request, getClientAddress }) => {
|
||||
: e.code === 'AI_NOT_CONFIGURED'
|
||||
? 503
|
||||
: 503;
|
||||
// Nur Code + Meta loggen, niemals Prompt/Response-Inhalt.
|
||||
// Nur Code + Meta + Error-Message loggen, niemals Prompt/Response-Inhalt.
|
||||
// e.message enthaelt z.B. Zod-Validierungspfade oder "non-JSON output" --
|
||||
// kein AI-Content, aber die Diagnose-Info, warum AI_FAILED kam.
|
||||
console.warn(
|
||||
`[extract-from-photo] ${e.code} after ${Date.now() - startedAt}ms, ${preprocessed.buffer.byteLength} bytes`
|
||||
`[extract-from-photo] ${e.code} after ${Date.now() - startedAt}ms, ${preprocessed.buffer.byteLength} bytes: ${e.message}`
|
||||
);
|
||||
return errJson(status, e.code, 'Die Bild-Analyse ist fehlgeschlagen.');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user