@@ -5,7 +5,6 @@ import { ApiStream } from "../transform/stream"
55import { BaseProvider } from "./base-provider"
66import type { ApiHandlerOptions } from "../../shared/api"
77import { getOllamaModels } from "./fetchers/ollama"
8- import { getApiRequestTimeout } from "./utils/timeout-config"
98import { XmlMatcher } from "../../utils/xml-matcher"
109import type { SingleCompletionHandler , ApiHandlerCreateMessageMetadata } from "../index"
1110
@@ -49,10 +48,9 @@ function convertToOllamaMessages(anthropicMessages: Anthropic.Messages.MessagePa
4948 ?. map ( ( part ) => {
5049 if ( part . type === "image" ) {
5150 // Handle base64 images only (Anthropic SDK uses base64)
51+ // Ollama expects raw base64 strings, not data URLs
5252 if ( "source" in part && part . source . type === "base64" ) {
53- toolResultImages . push (
54- `data:${ part . source . media_type } ;base64,${ part . source . data } ` ,
55- )
53+ toolResultImages . push ( part . source . data )
5654 }
5755 return "(see following user message for image)"
5856 }
@@ -69,20 +67,24 @@ function convertToOllamaMessages(anthropicMessages: Anthropic.Messages.MessagePa
6967
7068 // Process non-tool messages
7169 if ( nonToolMessages . length > 0 ) {
70+ // Separate text and images for Ollama
71+ const textContent = nonToolMessages
72+ . filter ( ( part ) => part . type === "text" )
73+ . map ( ( part ) => part . text )
74+ . join ( "\n" )
75+
76+ const imageData : string [ ] = [ ]
77+ nonToolMessages . forEach ( ( part ) => {
78+ if ( part . type === "image" && "source" in part && part . source . type === "base64" ) {
79+ // Ollama expects raw base64 strings, not data URLs
80+ imageData . push ( part . source . data )
81+ }
82+ } )
83+
7284 ollamaMessages . push ( {
7385 role : "user" ,
74- content : nonToolMessages
75- . map ( ( part ) => {
76- if ( part . type === "image" ) {
77- // Handle base64 images only (Anthropic SDK uses base64)
78- if ( "source" in part && part . source . type === "base64" ) {
79- return `data:${ part . source . media_type } ;base64,${ part . source . data } `
80- }
81- return ""
82- }
83- return part . text
84- } )
85- . join ( "\n" ) ,
86+ content : textContent ,
87+ images : imageData . length > 0 ? imageData : undefined ,
8688 } )
8789 }
8890 } else if ( anthropicMessage . role === "assistant" ) {
@@ -125,8 +127,6 @@ function convertToOllamaMessages(anthropicMessages: Anthropic.Messages.MessagePa
125127 return ollamaMessages
126128}
127129
128- const OLLAMA_TIMEOUT_MS = 3_600_000
129-
130130export class NativeOllamaHandler extends BaseProvider implements SingleCompletionHandler {
131131 protected options : ApiHandlerOptions
132132 private client : Ollama | undefined
@@ -157,7 +157,7 @@ export class NativeOllamaHandler extends BaseProvider implements SingleCompletio
157157 metadata ?: ApiHandlerCreateMessageMetadata ,
158158 ) : ApiStream {
159159 const client = this . ensureClient ( )
160- const modelId = this . getModel ( ) . id
160+ const { id : modelId , info : modelInfo } = await this . fetchModel ( )
161161 const useR1Format = modelId . toLowerCase ( ) . includes ( "deepseek-r1" )
162162
163163 const ollamaMessages : Message [ ] = [
@@ -176,13 +176,12 @@ export class NativeOllamaHandler extends BaseProvider implements SingleCompletio
176176
177177 try {
178178 // Create the actual API request promise
179- const model = this . getModel ( )
180179 const stream = await client . chat ( {
181- model : model . id ,
180+ model : modelId ,
182181 messages : ollamaMessages ,
183182 stream : true ,
184183 options : {
185- num_ctx : model . info . contextWindow ,
184+ num_ctx : modelInfo . contextWindow ,
186185 temperature : this . options . modelTemperature ?? ( useR1Format ? DEEP_SEEK_DEFAULT_TEMPERATURE : 0 ) ,
187186 } ,
188187 } )
@@ -263,7 +262,7 @@ export class NativeOllamaHandler extends BaseProvider implements SingleCompletio
263262 async completePrompt ( prompt : string ) : Promise < string > {
264263 try {
265264 const client = this . ensureClient ( )
266- const modelId = this . getModel ( ) . id
265+ const { id : modelId } = await this . fetchModel ( )
267266 const useR1Format = modelId . toLowerCase ( ) . includes ( "deepseek-r1" )
268267
269268 const response = await client . chat ( {
0 commit comments