fix(moderation): audit fixes — media/vision timeout 120s, Qdrant retry w/ backoff, JSON repair in LLM caller
- AI_LLM_MEDIA_ANALYSIS_TIMEOUT_MS 60s→120s + vision 60s→120s: vision model via router regularly exceeded 60s, dropping media batches into the individual-fallback chain then exhausting into ai_status=error. - Qdrant upserts: retryWithRetry() wraps PUT /points with exponential backoff (3 attempts, jitter) for transient 408/abort/ECONNRESET — the 41 six-hour 'Qdrant upsert failed — semantic entry skipped' warnings were single-hop timeouts on a healthy-but-loaded Qdrant. - LLM caller: on parse failure, attempt extractJson() structural repair of the raw content (models with thinking disabled sometimes emit JSON as plain text) before giving up and re-requesting.
This commit is contained in:
1 parent
7d4009da9f
commit
f85af3952a
4 files changed
+103
-7
No files matched your search
@@ -17,7 +17,10 @@ import { config } from "../../shared/config/config.js";
|
||||
import { incrementCounterBy } from "../gateway-metrics/index.js";
|
||||
import type { AnalysisResult } from "../message-capture/types.js";
|
||||
import { llmChat } from "./llmClient.js";
|
||||
import { parseModerationResponse } from "./moderationResponseParser.js";
|
||||
import {
|
||||
extractJson,
|
||||
parseModerationResponse,
|
||||
} from "./moderationResponseParser.js";
|
||||
import { logModerationError } from "./responseLogger.js";
|
||||
|
||||
const log = createChildLogger("llm-caller");
|
||||
@@ -129,6 +132,35 @@ export async function callModerationLLM(
|
||||
},
|
||||
`Failed to parse moderation response (${label})`,
|
||||
);
|
||||
// Attempt cheap structural repair before re-throwing: models with
|
||||
// thinking disabled sometimes emit the JSON object as plain text
|
||||
// (no code fence, outer prose). extractJson walks the raw content
|
||||
// for a balanced {…} / […]. If that yields a parseable object,
|
||||
// use it — the parse is data-driven, never injected into the
|
||||
// prompt, so it cannot leak into future calls.
|
||||
try {
|
||||
const repaired = extractJson(rawContent);
|
||||
if (repaired) {
|
||||
return {
|
||||
parsed: parseModerationResponse(
|
||||
JSON.stringify(repaired),
|
||||
targetIds,
|
||||
),
|
||||
result: completion,
|
||||
};
|
||||
}
|
||||
} catch (repairError: any) {
|
||||
log.warn(
|
||||
{
|
||||
error:
|
||||
repairError instanceof Error
|
||||
? repairError.message
|
||||
: String(repairError),
|
||||
label,
|
||||
},
|
||||
"JSON repair attempt failed — falling through to retry",
|
||||
);
|
||||
}
|
||||
throw parseError;
|
||||
}
|
||||
} catch (apiError: any) {
|
||||
|
||||
Reference in new issue
Block a user