Skip to content

Commit 629830d

Browse files
committed
fix: send base64 images without data URL prefix to LM Studio
LM Studio expects raw base64 encoded image data without the data: URL prefix. This fix adds an optional lmStudioFormat parameter to convertToOpenAiMessages that strips the prefix when sending images to LM Studio. Fixes #8827
1 parent 98b8d5b commit 629830d

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

src/api/providers/lm-studio.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export class LmStudioHandler extends BaseProvider implements SingleCompletionHan
4343
): ApiStream {
4444
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
4545
{ role: "system", content: systemPrompt },
46-
...convertToOpenAiMessages(messages),
46+
...convertToOpenAiMessages(messages, { lmStudioFormat: true }),
4747
]
4848

4949
// -------------------------

src/api/transform/__tests__/openai-format.spec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,52 @@ describe("convertToOpenAiMessages", () => {
7070
})
7171
})
7272

73+
it("should handle images with LM Studio format", () => {
74+
const anthropicMessages: Anthropic.Messages.MessageParam[] = [
75+
{
76+
role: "user",
77+
content: [
78+
{
79+
type: "text",
80+
text: "Analyze this image",
81+
},
82+
{
83+
type: "image",
84+
source: {
85+
type: "base64",
86+
media_type: "image/png",
87+
data: "base64imagedata",
88+
},
89+
},
90+
],
91+
},
92+
]
93+
94+
// Test default format (with data URL prefix)
95+
const openAiMessages = convertToOpenAiMessages(anthropicMessages)
96+
const content = openAiMessages[0].content as Array<{
97+
type: string
98+
text?: string
99+
image_url?: { url: string }
100+
}>
101+
expect(content[1]).toEqual({
102+
type: "image_url",
103+
image_url: { url: "data:image/png;base64,base64imagedata" },
104+
})
105+
106+
// Test LM Studio format (without data URL prefix)
107+
const lmStudioMessages = convertToOpenAiMessages(anthropicMessages, { lmStudioFormat: true })
108+
const lmStudioContent = lmStudioMessages[0].content as Array<{
109+
type: string
110+
text?: string
111+
image_url?: { url: string }
112+
}>
113+
expect(lmStudioContent[1]).toEqual({
114+
type: "image_url",
115+
image_url: { url: "base64imagedata" },
116+
})
117+
})
118+
73119
it("should handle assistant messages with tool use", () => {
74120
const anthropicMessages: Anthropic.Messages.MessageParam[] = [
75121
{

src/api/transform/openai-format.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import OpenAI from "openai"
33

44
export function convertToOpenAiMessages(
55
anthropicMessages: Anthropic.Messages.MessageParam[],
6+
options?: { lmStudioFormat?: boolean },
67
): OpenAI.Chat.ChatCompletionMessageParam[] {
78
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = []
89

@@ -83,9 +84,13 @@ export function convertToOpenAiMessages(
8384
role: "user",
8485
content: nonToolMessages.map((part) => {
8586
if (part.type === "image") {
87+
// LM Studio expects just the base64 data without the data URL prefix
88+
const imageUrl = options?.lmStudioFormat
89+
? part.source.data
90+
: `data:${part.source.media_type};base64,${part.source.data}`
8691
return {
8792
type: "image_url",
88-
image_url: { url: `data:${part.source.media_type};base64,${part.source.data}` },
93+
image_url: { url: imageUrl },
8994
}
9095
}
9196
return { type: "text", text: part.text }

0 commit comments

Comments
 (0)