fix: implement json_schema fallback for OpenAI-compatible providers (#1343)

When json_schema response_format is sent to models that do not natively
support Structured Output (e.g. DeepSeek/Ollama/local LLMs via
openai-compatible-* providers), they often return empty or malformed
content.

Detect response_format.type === "json_schema", inject the schema into the
system prompt, and downgrade response_format to json_object so Structured
Output works transparently across openai-compatible providers.

Gated to provider.startsWith("openai-compatible-") so providers with
native Structured Output support are not downgraded.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
mustafabozkaya
2026-05-26 11:22:31 +07:00
committed by decolua
parent a648a42bdb
commit f3176b4b23

View File

@@ -12,7 +12,28 @@ export class DefaultExecutor extends BaseExecutor {
}
transformRequest(model, body) {
return injectReasoningContent({ provider: this.provider, model, body });
const transformed = this.applyJsonSchemaFallback(body);
return injectReasoningContent({ provider: this.provider, model, body: transformed });
}
// Fallback json_schema → json_object for openai-compatible providers without native Structured Output.
applyJsonSchemaFallback(body) {
if (!this.provider?.startsWith?.("openai-compatible-")) return body;
const rf = body?.response_format;
if (rf?.type !== "json_schema" || !rf.json_schema?.schema) return body;
const schemaJson = JSON.stringify(rf.json_schema.schema, null, 2);
const prompt = `You must respond with valid JSON that strictly follows this JSON schema:\n\`\`\`json\n${schemaJson}\n\`\`\`\nRespond ONLY with the JSON object, no other text.`;
const messages = Array.isArray(body.messages) ? body.messages.map(m => ({ ...m })) : [];
const sys = messages.find(m => m.role === "system");
if (sys) {
if (typeof sys.content === "string") sys.content = `${sys.content}\n\n${prompt}`;
else if (Array.isArray(sys.content)) sys.content.push({ type: "text", text: `\n\n${prompt}` });
} else {
messages.unshift({ role: "system", content: prompt });
}
return { ...body, messages, response_format: { type: "json_object" } };
}
buildUrl(model, stream, urlIndex = 0, credentials = null) {