debug(ai): add verbose logging and improved error visibility in batch analyzer

This commit is contained in:
Michael committed 2026-02-21 21:45:58 +01:00
1 parent c6148b58a4
commit 1cda9cef9d
2 files changed
+37 -15

No files matched your search

+21 -4
View File
@@ -81,13 +81,30 @@ Format attendu:
} }
const data = await response.json(); const data = await response.json();
const content = data.choices[0].message.content; const content: string = data.choices?.[0]?.message?.content ?? "";
// Clean up markdown markers if the model ignored response_format console.log("[batch-analyze] Raw LLM response:", content.slice(0, 500));
const jsonMatch = content.match(/```json\n([\s\S]*?)\n```/) || content.match(/{[\s\S]*}/);
const jsonStr = jsonMatch ? jsonMatch[0].replace(/```json\n|\n```/g, '') : content; if (!content) {
return NextResponse.json({ error: "Réponse vide du modèle." }, { status: 500 });
}
// Try to extract JSON block from the response (handles markdown code blocks and raw JSON)
let jsonStr = content;
const jsonBlock = content.match(/```json\s*([\s\S]*?)\s*```/);
if (jsonBlock) {
jsonStr = jsonBlock[1];
} else {
// Look for first { ... } block
const start = content.indexOf("{");
const end = content.lastIndexOf("}");
if (start !== -1 && end !== -1) {
jsonStr = content.slice(start, end + 1);
}
}
const resultJson = JSON.parse(jsonStr); const resultJson = JSON.parse(jsonStr);
console.log("[batch-analyze] Parsed results count:", resultJson?.results?.length ?? "N/A");
return NextResponse.json(resultJson); return NextResponse.json(resultJson);
@@ -8,13 +8,13 @@ import { ProductRow } from "@/types/grid";
export function BulkAiAnalyzer() { export function BulkAiAnalyzer() {
const [isAnalyzing, setIsAnalyzing] = useState(false); const [isAnalyzing, setIsAnalyzing] = useState(false);
const [progress, setProgress] = useState({ current: 0, total: 0, message: "" }); const [progress, setProgress] = useState({ current: 0, total: 0, message: "", errors: 0 });
const setDraftGamme = useGridStore(state => state.setDraftGamme); const setDraftGamme = useGridStore(state => state.setDraftGamme);
const setInsight = useAiCopilotStore(state => state.setInsight); const setInsight = useAiCopilotStore(state => state.setInsight);
const handleAnalyze = async () => { const handleAnalyze = async () => {
const { rows } = useGridStore.getState(); const { rows } = useGridStore.getState();
if (rows.length === 0) return; console.log("[BulkAiAnalyzer] Starting analysis on", rows.length, "rows");
setIsAnalyzing(true); setIsAnalyzing(true);
@@ -40,12 +40,12 @@ export function BulkAiAnalyzer() {
} }
} }
setProgress({ current: 0, total: chunks.length, message: "Initialisation..." }); setProgress({ current: 0, total: chunks.length, message: "Initialisation...", errors: 0 });
// Process sequentially // Process sequentially
let completed = 0; let completed = 0;
for (const chunk of chunks) { for (const chunk of chunks) {
setProgress({ current: completed, total: chunks.length, message: `Analyse du rayon: ${chunk.rayon}` }); setProgress(prev => ({ ...prev, current: completed, total: chunks.length, message: `Analyse du rayon: ${chunk.rayon}` }));
// Simplify payload to save tokens // Simplify payload to save tokens
const payloadProducts = chunk.items.map(r => ({ const payloadProducts = chunk.items.map(r => ({
@@ -66,35 +66,40 @@ export function BulkAiAnalyzer() {
}); });
if (!res.ok) { if (!res.ok) {
console.error(`Erreur sur le lot ${chunk.rayon}`); const errText = await res.text();
console.error(`[BulkAiAnalyzer] Erreur HTTP ${res.status} sur le lot ${chunk.rayon}:`, errText);
setProgress(prev => ({ ...prev, errors: prev.errors + 1 }));
completed++; completed++;
continue; // Skip the failed chunk but continue processing continue;
} }
try { try {
const data = await res.json(); const data = await res.json();
console.log(`[BulkAiAnalyzer] Response for ${chunk.rayon}:`, data);
// data.results should be an array
if (data && Array.isArray(data.results)) { if (data && Array.isArray(data.results)) {
let applied = 0;
data.results.forEach((reco: any) => { data.results.forEach((reco: any) => {
if (reco.codein && reco.recommandationGamme) { if (reco.codein && reco.recommandationGamme) {
// Update the gamme draft
setDraftGamme(reco.codein, reco.recommandationGamme); setDraftGamme(reco.codein, reco.recommandationGamme);
// Inject the justification into the AI insight column (with gamme letter prefix)
const baseJustification = reco.justificationCourte || "Aucune explication."; const baseJustification = reco.justificationCourte || "Aucune explication.";
const justification = `Gamme ${reco.recommandationGamme} — ${baseJustification}`; const justification = `Gamme ${reco.recommandationGamme} — ${baseJustification}`;
setInsight(reco.codein, justification, reco.isDuplicate ?? false); setInsight(reco.codein, justification, reco.isDuplicate ?? false);
applied++;
} }
}); });
console.log(`[BulkAiAnalyzer] Applied ${applied} recommendations for ${chunk.rayon}`);
} else {
console.warn(`[BulkAiAnalyzer] Unexpected response shape for ${chunk.rayon}:`, data);
} }
} catch (e) { } catch (e) {
console.error("Failed to parse JSON for chunk", chunk.rayon, e); console.error(`[BulkAiAnalyzer] Failed to parse JSON for chunk ${chunk.rayon}:`, e);
} }
completed++; completed++;
} }
setProgress({ current: completed, total: chunks.length, message: "Analyse terminée avec succès !" }); setProgress(prev => ({ ...prev, current: completed, total: chunks.length, message: `Analyse terminée ! (${prev.errors > 0 ? prev.errors + " erreurs" : "succès"})` }));
setTimeout(() => { setTimeout(() => {
setIsAnalyzing(false); setIsAnalyzing(false);
}, 3000); }, 3000);