Skip to content

Commit 738a84a

Browse files
committed
fix: update Ollama provider to use official npm package API correctly
1 parent 056347f commit 738a84a

File tree

1 file changed

+76
-64
lines changed
  • packages/agent/src/core/llm/providers

1 file changed

+76
-64
lines changed
Lines changed: 76 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
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';
56
import { TokenUsage } from '../../tokens.js';
67
import { LLMProvider } from '../provider.js';
78
import {
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
*/
2426
export 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

Comments
 (0)