diff --git a/server/src/llm/structuredInvoke.ts b/server/src/llm/structuredInvoke.ts index 267187d19..a17833289 100644 --- a/server/src/llm/structuredInvoke.ts +++ b/server/src/llm/structuredInvoke.ts @@ -108,10 +108,17 @@ function tryFixTruncatedJson(raw: string): string { return fixed; } +function unwrapSingletonArray(val: unknown): unknown { + if (Array.isArray(val) && val.length === 1) { + return val[0]; + } + return val; +} + function tryParseStructuredJsonValue(source: string): { parsed: unknown } | { error: string } { try { return { - parsed: JSON.parse(extractJSONValue(source)) as unknown, + parsed: unwrapSingletonArray(JSON.parse(extractJSONValue(source))), }; } catch (error) { const fixed = tryFixTruncatedJson(source); @@ -126,7 +133,7 @@ function tryParseStructuredJsonValue(source: string): { parsed: unknown } | { er try { return { - parsed: JSON.parse(extractJSONValue(fixed)) as unknown, + parsed: unwrapSingletonArray(JSON.parse(extractJSONValue(fixed))), }; } catch (fixedError) { return { diff --git a/server/src/llm/structuredOutput.ts b/server/src/llm/structuredOutput.ts index a5323a1f4..eca9c523e 100644 --- a/server/src/llm/structuredOutput.ts +++ b/server/src/llm/structuredOutput.ts @@ -355,6 +355,24 @@ export function classifyStructuredOutputFailure(input: { if (haystack.includes("zod") || haystack.includes("schema") || haystack.includes("校验错误")) { return "schema_mismatch"; } + // Relay/proxy returned HTML instead of JSON (e.g. rate limit page, auth error page) + const lowerHaystack = haystack.toLowerCase(); + if ( + lowerHaystack.includes("") || + lowerHaystack.includes("") || + lowerHaystack.includes("rate limit") || + lowerHaystack.includes("429") || + lowerHaystack.includes("error") || + lowerHaystack.includes("<title>403") || + lowerHaystack.includes("<title>404") || + lowerHaystack.includes("<title>502") || + lowerHaystack.includes("<title>503") + ) { + return "transport_error"; + } return "transport_error"; }