diff --git a/src/jobs/analyze-voice-experience.ts b/src/jobs/analyze-voice-experience.ts index 2d83aab..b1a0bb8 100644 --- a/src/jobs/analyze-voice-experience.ts +++ b/src/jobs/analyze-voice-experience.ts @@ -4,10 +4,14 @@ import { prisma } from '../prisma.js'; type OpenRouterResponse = { choices?: Array<{ + finish_reason?: string; message?: { content?: string; }; }>; + error?: { + message?: string; + }; }; type PlaceAnalysis = { @@ -84,11 +88,12 @@ async function buildPlaceAnalysis(input: { model: config.openRouterModel, temperature: 0.1, max_tokens: 900, + response_format: { type: 'json_object' }, messages: [ { role: 'system', content: - 'You classify voice reviews about places. Return only valid JSON. Use only allowed ontology tags.', + 'You classify voice reviews about places. Return one valid JSON object in message.content. Do not return markdown. Use only allowed ontology tags.', }, { role: 'user', @@ -118,13 +123,19 @@ async function buildPlaceAnalysis(input: { }); if (!response.ok) { - throw new Error(`OpenRouter analysis failed with ${response.status}.`); + const errorBody = await response.text(); + throw new Error( + `OpenRouter analysis failed with ${response.status}: ${errorBody.slice(0, 500)}`, + ); } const payload = (await response.json()) as OpenRouterResponse; - const content = payload.choices?.[0]?.message?.content; + const choice = payload.choices?.[0]; + const content = choice?.message?.content?.trim(); if (!content) { - throw new Error('OpenRouter returned an empty analysis.'); + throw new Error( + `OpenRouter returned an empty analysis for ${config.openRouterModel}; finish_reason=${choice?.finish_reason ?? 'missing'}.`, + ); } const analysis = JSON.parse(content) as PlaceAnalysis;