Skip to content

Commit ad86692

Browse files
committed
feat: enhance ensureDataURIPrefix to detect MIME types and trim whitespace
1 parent 20d4c29 commit ad86692

File tree

2 files changed

+71
-5
lines changed

2 files changed

+71
-5
lines changed

pkg/ollama/http_handler.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -724,17 +724,33 @@ func (h *HTTPHandler) mapOllamaOptionsToOpenAI(ollamaOpts map[string]interface{}
724724
// OpenWebUI may send raw base64 data without prefix, but llama.cpp requires it.
725725
// This function:
726726
// - Returns data as-is if it already starts with "data:", "http://", or "https://"
727-
// - Prepends "data:image/jpeg;base64," to raw base64 strings
727+
// - Detects MIME type from base64 prefix and prepends appropriate data URI
728728
func ensureDataURIPrefix(imageData string) string {
729+
// Trim whitespace that might come from UIs
730+
imageData = strings.TrimSpace(imageData)
731+
729732
// Check if already has a URI scheme
730733
if strings.HasPrefix(imageData, "data:") ||
731734
strings.HasPrefix(imageData, "http://") ||
732735
strings.HasPrefix(imageData, "https://") {
733736
return imageData
734737
}
735738

736-
// Assume raw base64 data - add data URI prefix
737-
return "data:image/jpeg;base64," + imageData
739+
// Detect MIME type from base64 prefix
740+
var mimeType string
741+
if strings.HasPrefix(imageData, "/9j/") {
742+
mimeType = "image/jpeg"
743+
} else if strings.HasPrefix(imageData, "iVBOR") {
744+
mimeType = "image/png"
745+
} else if strings.HasPrefix(imageData, "R0lG") {
746+
mimeType = "image/gif"
747+
} else {
748+
// Default to jpeg for unknown formats
749+
mimeType = "image/jpeg"
750+
}
751+
752+
// Assume raw base64 data - add data URI prefix with detected MIME type
753+
return "data:" + mimeType + ";base64," + imageData
738754
}
739755

740756
// convertMessages converts Ollama messages to OpenAI format
@@ -748,7 +764,11 @@ func convertMessages(messages []Message) []map[string]interface{} {
748764
// Handle multimodal content (text + images)
749765
if len(msg.Images) > 0 {
750766
// Convert to OpenAI multimodal format: content is an array of content objects
751-
var contentArray []map[string]interface{}
767+
contentArraySize := len(msg.Images)
768+
if msg.Content != "" {
769+
contentArraySize++
770+
}
771+
contentArray := make([]map[string]interface{}, 0, contentArraySize)
752772

753773
// Add text content if present
754774
if msg.Content != "" {

pkg/ollama/http_handler_test.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,32 @@ func TestConvertMessages_Multimodal(t *testing.T) {
9191
// Note: JSON field order follows struct definition
9292
expected: `[{"content":"Let me call a function","role":"assistant","tool_calls":[{"id":"call_123","type":"function","function":{"name":"get_weather","arguments":"{\"location\":\"San Francisco\"}"}}]}]`,
9393
},
94+
{
95+
name: "tool result message with tool_call_id",
96+
messages: []Message{
97+
{
98+
Role: "tool",
99+
Content: "The weather in San Francisco is sunny, 72°F",
100+
ToolCallID: "call_123",
101+
},
102+
},
103+
expected: `[{"content":"The weather in San Francisco is sunny, 72°F","role":"tool","tool_call_id":"call_123"}]`,
104+
},
105+
{
106+
name: "multiple raw base64 images without prefix",
107+
messages: []Message{
108+
{
109+
Role: "user",
110+
Content: "Compare these two images",
111+
Images: []string{
112+
"/9j/4AAQSkZJRgABAQEBLA...",
113+
"iVBORw0KGgoAAAANSUhEUgAAA...",
114+
},
115+
},
116+
},
117+
// Should auto-detect MIME types and add appropriate prefixes
118+
expected: `[{"content":[{"text":"Compare these two images","type":"text"},{"image_url":{"url":"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLA..."},"type":"image_url"},{"image_url":{"url":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA..."},"type":"image_url"}],"role":"user"}]`,
119+
},
94120
}
95121

96122
for _, tt := range tests {
@@ -118,10 +144,20 @@ func TestEnsureDataURIPrefix(t *testing.T) {
118144
expected string
119145
}{
120146
{
121-
name: "raw base64 without prefix",
147+
name: "raw JPEG base64 without prefix",
122148
input: "/9j/4AAQSkZJRgABAQEBLA...",
123149
expected: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLA...",
124150
},
151+
{
152+
name: "raw PNG base64 without prefix",
153+
input: "iVBORw0KGgoAAAANSUhEUgAAA...",
154+
expected: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA...",
155+
},
156+
{
157+
name: "raw GIF base64 without prefix",
158+
input: "R0lGODlhAQABAIAAAAAAAP...",
159+
expected: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP...",
160+
},
125161
{
126162
name: "already has data URI prefix",
127163
input: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLA...",
@@ -142,6 +178,16 @@ func TestEnsureDataURIPrefix(t *testing.T) {
142178
input: "https://example.com/image.jpg",
143179
expected: "https://example.com/image.jpg",
144180
},
181+
{
182+
name: "empty string",
183+
input: "",
184+
expected: "data:image/jpeg;base64,",
185+
},
186+
{
187+
name: "whitespace with base64",
188+
input: " /9j/4AAQSkZJRgABAQEBLA... ",
189+
expected: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLA...",
190+
},
145191
}
146192

147193
for _, tt := range tests {

0 commit comments

Comments
 (0)