@@ -205,10 +205,37 @@ export class NativeOllamaHandler extends BaseProvider implements SingleCompletio
205205 messages : Anthropic . Messages . MessageParam [ ] ,
206206 metadata ?: ApiHandlerCreateMessageMetadata ,
207207 ) : ApiStream {
208- const client = this . ensureClient ( )
209208 const { id : modelId } = await this . fetchModel ( )
210209 const useR1Format = modelId . toLowerCase ( ) . includes ( "deepseek-r1" )
211210
211+ // Create a per-request Ollama client since the SDK doesn't support abortSignal in options.
212+ // We listen to metadata.abortSignal and call client.abort() when it fires.
213+ const requestClient = new Ollama ( {
214+ host : this . options . ollamaBaseUrl || "http://localhost:11434" ,
215+ } )
216+
217+ // Add API key if provided
218+ if ( this . options . ollamaApiKey ) {
219+ ; ( requestClient as any ) . config = {
220+ ...( ( requestClient as any ) . config ?? { } ) ,
221+ headers : { Authorization : `Bearer ${ this . options . ollamaApiKey } ` } ,
222+ }
223+ }
224+
225+ // Wire external abort signal to per-request client's abort() method
226+ const externalAbortSignal = metadata ?. abortSignal
227+ if ( externalAbortSignal ) {
228+ if ( externalAbortSignal . aborted ) {
229+ requestClient . abort ( )
230+ } else {
231+ const abortListener = ( ) => {
232+ requestClient . abort ( )
233+ externalAbortSignal . removeEventListener ( "abort" , abortListener )
234+ }
235+ externalAbortSignal . addEventListener ( "abort" , abortListener , { once : true } )
236+ }
237+ }
238+
212239 const ollamaMessages : Message [ ] = [
213240 { role : "system" , content : systemPrompt } ,
214241 ...convertToOllamaMessages ( messages ) ,
@@ -234,14 +261,13 @@ export class NativeOllamaHandler extends BaseProvider implements SingleCompletio
234261 chatOptions . num_ctx = this . options . ollamaNumCtx
235262 }
236263
237- // Create the actual API request promise
238- const stream = await client . chat ( {
264+ // Create the actual API request promise (use per-request client, not signal in options)
265+ const stream = await requestClient . chat ( {
239266 model : modelId ,
240267 messages : ollamaMessages ,
241268 stream : true ,
242269 options : chatOptions ,
243270 tools : this . convertToolsToOllama ( metadata ?. tools ) ,
244- signal : metadata ?. abortSignal ,
245271 } as any )
246272
247273 let totalInputTokens = 0
@@ -346,27 +372,48 @@ export class NativeOllamaHandler extends BaseProvider implements SingleCompletio
346372 }
347373
348374 async completePrompt ( prompt : string , metadata ?: ApiHandlerCreateMessageMetadata ) : Promise < string > {
375+ // Create a per-request Ollama client since the SDK doesn't support abortSignal in options.
376+ const requestClient = new Ollama ( {
377+ host : this . options . ollamaBaseUrl || "http://localhost:11434" ,
378+ } )
379+
380+ if ( this . options . ollamaApiKey ) {
381+ ; ( requestClient as any ) . config = {
382+ headers : { Authorization : `Bearer ${ this . options . ollamaApiKey } ` } ,
383+ }
384+ }
385+
386+ // Wire external abort signal to per-request client's abort() method
387+ const externalAbortSignal = metadata ?. abortSignal
388+ if ( externalAbortSignal ) {
389+ if ( externalAbortSignal . aborted ) {
390+ requestClient . abort ( )
391+ } else {
392+ const abortListener = ( ) => {
393+ requestClient . abort ( )
394+ externalAbortSignal . removeEventListener ( "abort" , abortListener )
395+ }
396+ externalAbortSignal . addEventListener ( "abort" , abortListener , { once : true } )
397+ }
398+ }
399+
349400 try {
350- const client = this . ensureClient ( )
351401 const { id : modelId } = await this . fetchModel ( )
352402 const useR1Format = modelId . toLowerCase ( ) . includes ( "deepseek-r1" )
353403
354- // Build options object conditionally
355404 const chatOptions : OllamaChatOptions = {
356405 temperature : this . options . modelTemperature ?? ( useR1Format ? DEEP_SEEK_DEFAULT_TEMPERATURE : 0 ) ,
357406 }
358407
359- // Only include num_ctx if explicitly set via ollamaNumCtx
360408 if ( this . options . ollamaNumCtx !== undefined ) {
361409 chatOptions . num_ctx = this . options . ollamaNumCtx
362410 }
363411
364- const response = await client . chat ( {
412+ const response = await requestClient . chat ( {
365413 model : modelId ,
366414 messages : [ { role : "user" , content : prompt } ] ,
367415 stream : false ,
368416 options : chatOptions ,
369- signal : metadata ?. abortSignal ,
370417 } as any )
371418
372419 return ( ( response as any ) . message ?. content as string ) || ""
@@ -375,6 +422,8 @@ export class NativeOllamaHandler extends BaseProvider implements SingleCompletio
375422 throw new Error ( `Ollama completion error: ${ error . message } ` )
376423 }
377424 throw error
425+ } finally {
426+ requestClient . abort ( )
378427 }
379428 }
380429}
0 commit comments