fix: handle OpenAI-style router responses + log unparseable model output
The router's response shape varies by upstream: some backends return .message.content, some choices[0].message.content, some only top-level .content. Prefer them in that order. Record the actually-served model (_shre.model) in the ledger and log a snippet of unparseable output for diagnosis. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e3ad9c25fc
commit
74222a1631
+10
-6
@@ -55,18 +55,22 @@ export async function runReview({ cfg, gitea, db, job, withGlobal, log }) {
|
||||
const messages = buildReviewPrompt({
|
||||
repo: fullRepo, ref: job.ref, sha, prTitle: job.prTitle, conventions, diff, truncated
|
||||
});
|
||||
let review = null, llmError = null;
|
||||
let review = null, llmError = null, servedModel = null;
|
||||
await withGlobal(async () => {
|
||||
for (let attempt = 1; attempt <= 2; attempt++) {
|
||||
try {
|
||||
const content = await chat({
|
||||
const out = await chat({
|
||||
routerUrl: cfg.routerUrl, model: cfg.model, messages,
|
||||
agentId: 'code-reviewer',
|
||||
sessionId: `review-${owner}-${repo}-${sha.slice(0, 12)}`,
|
||||
sessionId: `review-${owner}-${repo}-${sha.slice(0, 12)}-a${attempt}`,
|
||||
tenantId: cfg.tenantId, timeoutMs: cfg.routerTimeoutMs
|
||||
});
|
||||
const raw = extractJson(content);
|
||||
if (!raw) throw new Error(`no JSON object in model output (attempt ${attempt})`);
|
||||
servedModel = out.servedModel;
|
||||
const raw = extractJson(out.content);
|
||||
if (!raw) {
|
||||
log(`[${fullRepo}@${sha.slice(0, 8)}] unparseable model output (served=${servedModel}): ${JSON.stringify(out.content.slice(0, 400))}`);
|
||||
throw new Error(`no JSON object in model output (attempt ${attempt})`);
|
||||
}
|
||||
review = normalizeReview(raw);
|
||||
if (!review) throw new Error('normalization failed');
|
||||
llmError = null;
|
||||
@@ -111,7 +115,7 @@ export async function runReview({ cfg, gitea, db, job, withGlobal, log }) {
|
||||
axes: review.axes, overall: review.overall, grade: review.grade,
|
||||
verdict: review.verdict, findings: review.findings,
|
||||
statusState: state, error: null, diffBytes, truncated,
|
||||
model: cfg.model, durationMs: Date.now() - t0
|
||||
model: servedModel || cfg.model, durationMs: Date.now() - t0
|
||||
});
|
||||
log(`[${fullRepo}@${sha.slice(0, 8)}] reviewed: ${review.verdict} grade=${review.grade} overall=${review.overall} findings=${review.findings.length} in ${Date.now() - t0}ms`);
|
||||
return { ok: true, verdict: review.verdict };
|
||||
|
||||
+7
-2
@@ -14,9 +14,14 @@ export async function chat({ routerUrl, model, messages, agentId, sessionId, ten
|
||||
throw new Error(`router -> ${res.status}: ${text.slice(0, 300)}`);
|
||||
}
|
||||
const data = await res.json();
|
||||
const content = data?.message?.content ?? data?.content;
|
||||
// Response shape varies by upstream: prefer .message.content, then
|
||||
// OpenAI-style choices, then top-level .content (which can carry an
|
||||
// upstream-down apology — hence last).
|
||||
const content = data?.message?.content
|
||||
?? data?.choices?.[0]?.message?.content
|
||||
?? data?.content;
|
||||
if (typeof content !== 'string' || !content.trim()) {
|
||||
throw new Error('router returned empty content');
|
||||
}
|
||||
return content;
|
||||
return { content, servedModel: data?._shre?.model ?? null };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user