fix(ai): propagate validate skipped reason into chat status line

Why: every time the vision validation loop returned skipped:true the
chat panel logged the same hardcoded "(timeout or provider error)"
string regardless of the actual cause — provider mismatch, HTTP error,
upstream config issue. Now that the server (validate.ts) returns
explicit skip reasons (e.g. "Vision validation is not supported for
builtin providers"), the UI should surface them so the user can fix
the right thing instead of guessing it's a timeout.

What: ValidationResult gains an optional `skippedReason` field.
validateDesignScreenshot fills it from response.json's `error` (or
the HTTP status text on a non-OK response) and propagates it through
the loop. The chat-panel status line now reads
"[error] Analysis skipped (<reason>)" with the server-provided
message clipped to 120 chars; falls back to the legacy string when no
reason is present.

1070 / 1070 AI tests still pass; no test depended on the literal
"timeout or provider error" string.
This commit is contained in:
Fini 2026-05-09 21:21:00 +08:00
parent 84fc0ffddb
commit 7cb28b52b6
2 changed files with 28 additions and 4 deletions

View file

@ -53,6 +53,11 @@ export interface ValidationResult {
structuralFixes: StructuralFix[];
qualityScore: number;
skipped?: boolean;
/** Human-readable reason when `skipped` — surfaced in the chat panel
* status line so users can tell "no vision provider" apart from
* "request timed out". Empty when the skip is transient.
*/
skippedReason?: string;
}
// ---------------------------------------------------------------------------

View file

@ -173,7 +173,14 @@ Cross-reference visual issues with the node IDs above. Return JSON fixes using r
if (!response.ok) {
console.warn(`[Validation] HTTP ${response.status}: ${response.statusText}`);
return { issues: [], fixes: [], structuralFixes: [], qualityScore: 0, skipped: true };
return {
issues: [],
fixes: [],
structuralFixes: [],
qualityScore: 0,
skipped: true,
skippedReason: `HTTP ${response.status} ${response.statusText}`,
};
}
const data = (await response.json()) as { text?: string; skipped?: boolean; error?: string };
@ -186,7 +193,14 @@ Cross-reference visual issues with the node IDs above. Return JSON fixes using r
provider,
model,
});
return { issues: [], fixes: [], structuralFixes: [], qualityScore: 0, skipped: true };
return {
issues: [],
fixes: [],
structuralFixes: [],
qualityScore: 0,
skipped: true,
skippedReason: data.error,
};
}
const parsed = parseValidationResponse(data.text);
@ -381,8 +395,13 @@ export async function runPostGenerationValidation(options?: {
console.log(
`[Validation] Round ${round}: skipped (see warnings above for details; provider=${options?.provider}, model=${options?.model})`,
);
// Replace "Analyzing..." with skipped reason
log[log.length - 1] = '[error] Analysis skipped (timeout or provider error)';
// Replace "Analyzing..." with skipped reason. Prefer the
// server-provided explanation when present so the chat shows
// "(no vision provider)" instead of the generic timeout text.
const reasonShort = result.skippedReason
? result.skippedReason.slice(0, 120)
: 'timeout or provider error';
log[log.length - 1] = `[error] Analysis skipped (${reasonShort})`;
if (isFirstRound) {
clearVisualReference();
emit('done');