11/**
2- * Ollama provider implementation
2+ * Ollama provider implementation using the official Ollama npm package
33 */
44
5+ import ollama , { Ollama , ChatResponse , Tool } from 'ollama' ;
56import { TokenUsage } from '../../tokens.js' ;
67import { LLMProvider } from '../provider.js' ;
78import {
9+ FunctionDefinition ,
810 GenerateOptions ,
911 LLMResponse ,
1012 Message ,
@@ -19,29 +21,26 @@ export interface OllamaOptions extends ProviderOptions {
1921}
2022
2123/**
22- * Ollama provider implementation
24+ * Ollama provider implementation using the official Ollama npm package
2325 */
2426export class OllamaProvider implements LLMProvider {
2527 name : string = 'ollama' ;
2628 provider : string = 'ollama.chat' ;
2729 model : string ;
28- private baseUrl : string ;
30+ private client : Ollama ;
2931
3032 constructor ( model : string , options : OllamaOptions = { } ) {
3133 this . model = model ;
32- this . baseUrl =
33- options . baseUrl ||
34- process . env . OLLAMA_BASE_URL ||
34+ const baseUrl =
35+ options . baseUrl ||
36+ process . env . OLLAMA_BASE_URL ||
3537 'http://localhost:11434' ;
3638
37- // Ensure baseUrl doesn't end with a slash
38- if ( this . baseUrl . endsWith ( '/' ) ) {
39- this . baseUrl = this . baseUrl . slice ( 0 , - 1 ) ;
40- }
39+ this . client = new Ollama ( { host : baseUrl } ) ;
4140 }
4241
4342 /**
44- * Generate text using Ollama API
43+ * Generate text using Ollama API via the official npm package
4544 */
4645 async generateText ( options : GenerateOptions ) : Promise < LLMResponse > {
4746 const {
@@ -52,75 +51,55 @@ export class OllamaProvider implements LLMProvider {
5251 topP,
5352 frequencyPenalty,
5453 presencePenalty,
54+ stopSequences,
5555 } = options ;
5656
5757 // Format messages for Ollama API
5858 const formattedMessages = this . formatMessages ( messages ) ;
5959
6060 try {
61- // Prepare request options
62- const requestOptions : any = {
61+ // Prepare chat options
62+ const ollamaOptions : Record < string , any > = {
63+ temperature,
64+ } ;
65+
66+ // Add optional parameters if provided
67+ if ( topP !== undefined ) ollamaOptions . top_p = topP ;
68+ if ( frequencyPenalty !== undefined ) ollamaOptions . frequency_penalty = frequencyPenalty ;
69+ if ( presencePenalty !== undefined ) ollamaOptions . presence_penalty = presencePenalty ;
70+ if ( maxTokens !== undefined ) ollamaOptions . num_predict = maxTokens ;
71+ if ( stopSequences && stopSequences . length > 0 ) ollamaOptions . stop = stopSequences ;
72+
73+ // Prepare request parameters
74+ const requestParams : any = {
6375 model : this . model ,
6476 messages : formattedMessages ,
6577 stream : false ,
66- options : {
67- temperature : temperature ,
68- // Ollama uses top_k instead of top_p, but we'll include top_p if provided
69- ...( topP !== undefined && { top_p : topP } ) ,
70- ...( frequencyPenalty !== undefined && {
71- frequency_penalty : frequencyPenalty ,
72- } ) ,
73- ...( presencePenalty !== undefined && {
74- presence_penalty : presencePenalty ,
75- } ) ,
76- } ,
78+ options : ollamaOptions ,
7779 } ;
7880
79- // Add max_tokens if provided
80- if ( maxTokens !== undefined ) {
81- requestOptions . options . num_predict = maxTokens ;
82- }
83-
8481 // Add functions/tools if provided
8582 if ( functions && functions . length > 0 ) {
86- requestOptions . tools = functions . map ( ( fn ) => ( {
87- name : fn . name ,
88- description : fn . description ,
89- parameters : fn . parameters ,
90- } ) ) ;
91- }
92-
93- // Make the API request
94- const response = await fetch ( `${ this . baseUrl } /api/chat` , {
95- method : 'POST' ,
96- headers : {
97- 'Content-Type' : 'application/json' ,
98- } ,
99- body : JSON . stringify ( requestOptions ) ,
100- } ) ;
101-
102- if ( ! response . ok ) {
103- const errorText = await response . text ( ) ;
104- throw new Error ( `Ollama API error: ${ response . status } ${ errorText } ` ) ;
83+ requestParams . tools = this . convertFunctionsToTools ( functions ) ;
10584 }
10685
107- const data = await response . json ( ) ;
86+ // Make the API request using the Ollama client
87+ const response = await this . client . chat ( requestParams ) ;
10888
109- // Extract content and tool calls
110- const content = data . message ?. content || '' ;
111- const toolCalls =
112- data . message ?. tool_calls ?. map ( ( toolCall : any ) => ( {
113- id :
114- toolCall . id ||
115- `tool-${ Date . now ( ) } -${ Math . random ( ) . toString ( 36 ) . substring ( 2 , 11 ) } ` ,
116- name : toolCall . name ,
117- content : JSON . stringify ( toolCall . args || toolCall . arguments || { } ) ,
118- } ) ) || [ ] ;
89+ // Extract content from response
90+ const content = response . message ?. content || '' ;
91+
92+ // Process tool calls if present
93+ const toolCalls = this . processToolCalls ( response ) ;
11994
12095 // Create token usage from response data
12196 const tokenUsage = new TokenUsage ( ) ;
122- tokenUsage . input = data . prompt_eval_count || 0 ;
123- tokenUsage . output = data . eval_count || 0 ;
97+ if ( response . prompt_eval_count ) {
98+ tokenUsage . input = response . prompt_eval_count ;
99+ }
100+ if ( response . eval_count ) {
101+ tokenUsage . output = response . eval_count ;
102+ }
124103
125104 return {
126105 text : content ,
@@ -132,6 +111,37 @@ export class OllamaProvider implements LLMProvider {
132111 }
133112 }
134113
114+ /**
115+ * Convert our FunctionDefinition format to Ollama's Tool format
116+ */
117+ private convertFunctionsToTools ( functions : FunctionDefinition [ ] ) : Tool [ ] {
118+ return functions . map ( ( fn ) => ( {
119+ type : 'function' ,
120+ function : {
121+ name : fn . name ,
122+ description : fn . description ,
123+ parameters : fn . parameters ,
124+ }
125+ } ) ) ;
126+ }
127+
128+ /**
129+ * Process tool calls from the Ollama response
130+ */
131+ private processToolCalls ( response : ChatResponse ) : any [ ] {
132+ if ( ! response . message ?. tool_calls || response . message . tool_calls . length === 0 ) {
133+ return [ ] ;
134+ }
135+
136+ return response . message . tool_calls . map ( ( toolCall ) => ( {
137+ id : toolCall . function ?. name
138+ ? `tool-${ Date . now ( ) } -${ Math . random ( ) . toString ( 36 ) . substring ( 2 , 11 ) } `
139+ : toolCall . id ,
140+ name : toolCall . function ?. name ,
141+ content : JSON . stringify ( toolCall . function ?. arguments || { } ) ,
142+ } ) ) ;
143+ }
144+
135145 /**
136146 * Format messages for Ollama API
137147 */
@@ -161,8 +171,10 @@ export class OllamaProvider implements LLMProvider {
161171 tool_calls : [
162172 {
163173 id : msg . id ,
164- name : msg . name ,
165- arguments : msg . content ,
174+ function : {
175+ name : msg . name ,
176+ arguments : msg . content ,
177+ }
166178 } ,
167179 ] ,
168180 } ;
@@ -174,4 +186,4 @@ export class OllamaProvider implements LLMProvider {
174186 } ;
175187 } ) ;
176188 }
177- }
189+ }
0 commit comments