Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-output-object-json-mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ai-sdk/openai-compatible': patch
---

fix(openai-compatible): inject JSON schema instruction when structuredOutputs is disabled. Improves reliability of generateText + Output.object() with OpenAI-compatible providers that don't support json_schema response format.
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,7 @@ describe('doGenerate', () => {
`);
});

it('should forward json response format as "json_object" and omit schema when structuredOutputs are disabled', async () => {
it('should forward json response format as "json_object" and inject schema instruction when structuredOutputs are disabled', async () => {
prepareJsonResponse({ content: '{"value":"Spark"}' });

const model = new OpenAICompatibleChatLanguageModel('gpt-4o-2024-08-06', {
Expand All @@ -862,6 +862,12 @@ describe('doGenerate', () => {
expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
{
"messages": [
{
"content": "JSON schema:
{"type":"object","properties":{"value":{"type":"string"}},"required":["value"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}
You MUST answer with a JSON object that matches the JSON schema above.",
"role": "system",
},
{
"content": "Hello",
"role": "user",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
createJsonResponseHandler,
FetchFunction,
generateId,
injectJsonInstructionIntoMessages,
isParsableJson,
parseProviderOptions,
ParseResult,
Expand Down Expand Up @@ -162,11 +163,15 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
warnings.push({ type: 'unsupported', feature: 'topK' });
}

if (
// When structured outputs are not supported but a schema is provided,
// inject the JSON schema as a system instruction so the model knows
// what structure to produce (since the schema is dropped from response_format).
const shouldInjectSchemaInstruction =
responseFormat?.type === 'json' &&
responseFormat.schema != null &&
!this.supportsStructuredOutputs
) {
!this.supportsStructuredOutputs;

if (shouldInjectSchemaInstruction) {
warnings.push({
type: 'unsupported',
feature: 'responseFormat',
Expand All @@ -175,6 +180,13 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
});
}

const messagesPrompt = shouldInjectSchemaInstruction
? injectJsonInstructionIntoMessages({
messages: prompt,
schema: responseFormat.schema,
})
: prompt;

const {
tools: openaiTools,
toolChoice: openaiToolChoice,
Expand Down Expand Up @@ -231,7 +243,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
verbosity: compatibleOptions.textVerbosity,

// messages:
messages: convertToOpenAICompatibleChatMessages(prompt),
messages: convertToOpenAICompatibleChatMessages(messagesPrompt),

// tools:
tools: openaiTools,
Expand Down