From 3818cf62970d000c640df04e1c25f68a4f6edda9 Mon Sep 17 00:00:00 2001 From: Abdul121104 Date: Tue, 21 Oct 2025 15:20:53 +0530 Subject: [PATCH] fix(openai): move image detail property inside image_url object - Fix MessageContentImageDetail type in type.ts to place 'detail' inside image_url object - Add test cases for image detail formatting - Resolves #2221 where detail property was in wrong location causing OpenAI 400 errors The 'detail' property was incorrectly placed at the top level instead of inside the image_url object, which caused OpenAI API to return 400 errors. This change aligns with OpenAI's expected format where detail is nested inside image_url. --- packages/core/src/llms/type.ts | 6 +- packages/providers/openai/src/llm.ts | 11 ++- packages/providers/openai/tests/llm.test.ts | 67 +++++++++++++++++++ .../openai/tests/structure-verify.js | 32 +++++++++ 4 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 packages/providers/openai/tests/structure-verify.js diff --git a/packages/core/src/llms/type.ts b/packages/core/src/llms/type.ts index a84a67b545..6042b32cbe 100644 --- a/packages/core/src/llms/type.ts +++ b/packages/core/src/llms/type.ts @@ -194,8 +194,10 @@ export type MessageContentTextDetail = { export type MessageContentImageDetail = { type: "image_url"; - image_url: { url: string }; - detail?: "high" | "low" | "auto"; + image_url: { + url: string; + detail?: "high" | "low" | "auto"; + }; }; export type MessageContentAudioDetail = { diff --git a/packages/providers/openai/src/llm.ts b/packages/providers/openai/src/llm.ts index 50dc6b6348..2ca8341902 100644 --- a/packages/providers/openai/src/llm.ts +++ b/packages/providers/openai/src/llm.ts @@ -246,7 +246,16 @@ export class OpenAI extends ToolCallLLM { } // Keep other types as is (text, image_url, etc.) - return item; + // return item; + if (item.type === "image_url") { + return { + type: "image_url", + image_url: { + url: item.image_url.url, + detail: item.detail, // This might need to be item.image_url.detail after the type change + }, + }; + } }) as ChatCompletionContentPart[], } satisfies ChatCompletionUserMessageParam; } diff --git a/packages/providers/openai/tests/llm.test.ts b/packages/providers/openai/tests/llm.test.ts index cd11a20dbd..f7b26bc449 100644 --- a/packages/providers/openai/tests/llm.test.ts +++ b/packages/providers/openai/tests/llm.test.ts @@ -148,4 +148,71 @@ describe("Message Formatting", () => { expect(OpenAI.toOpenAIMessage(multiToolMessages)).toEqual(expectedOutput); }); }); + describe("Image Detail Formatting", () => { + test("OpenAI formats image messages with detail correctly", () => { + const inputMessages: ChatMessage[] = [ + { + role: "user", + content: [ + { + type: "image_url", + image_url: { + url: "data:image/jpeg;base64,aGVsbG8=", + detail: "high", + }, + }, + ], + }, + ]; + + const expectedOutput = [ + { + role: "user", + content: [ + { + type: "image_url", + image_url: { + url: "data:image/jpeg;base64,aGVsbG8=", + detail: "high", + }, + }, + ], + }, + ]; + + expect(OpenAI.toOpenAIMessage(inputMessages)).toEqual(expectedOutput); + }); + + test("OpenAI formats image messages without detail correctly", () => { + const inputMessages: ChatMessage[] = [ + { + role: "user", + content: [ + { + type: "image_url", + image_url: { + url: "data:image/jpeg;base64,aGVsbG8=", + }, + }, + ], + }, + ]; + + const expectedOutput = [ + { + role: "user", + content: [ + { + type: "image_url", + image_url: { + url: "data:image/jpeg;base64,aGVsbG8=", + }, + }, + ], + }, + ]; + + expect(OpenAI.toOpenAIMessage(inputMessages)).toEqual(expectedOutput); + }); + }); }); diff --git a/packages/providers/openai/tests/structure-verify.js b/packages/providers/openai/tests/structure-verify.js new file mode 100644 index 0000000000..2b24a0dcd7 --- /dev/null +++ b/packages/providers/openai/tests/structure-verify.js @@ -0,0 +1,32 @@ +console.log("VERIFYING IMAGE DETAIL FIX\n"); + +// Test the structure change +console.log("OLD STRUCTURE (Broken):"); +const oldStructure = { + type: "image_url", + image_url: { url: "data:image/jpeg;base64,test" }, + detail: "high", // WRONG: detail at top level +}; +console.log(JSON.stringify(oldStructure, null, 2)); +console.log("Problem: 'detail' is at top level, OpenAI rejects this\n"); + +console.log("NEW STRUCTURE (Fixed):"); +const newStructure = { + type: "image_url", + image_url: { + url: "data:image/jpeg;base64,test", + detail: "high", // CORRECT: detail inside image_url + }, +}; +console.log(JSON.stringify(newStructure, null, 2)); +console.log("Fixed: 'detail' is inside image_url object\n"); + +console.log("WHAT YOU CHANGED:"); +console.log("In packages/core/src/llms/type.ts:"); +console.log("BEFORE: detail?: 'high' | 'low' | 'auto' (at top level)"); +console.log( + "AFTER: image_url: { url: string; detail?: 'high' | 'low' | 'auto' }", +); +console.log(" ^-- detail moved inside image_url object"); + +console.log("\n This matches OpenAI's API requirements!");