Skip to content

Commit b23dd28

Browse files
committed
Add Ollama support via the Vercel AI SDK (#85)
1 parent 35cfaa4 commit b23dd28

File tree

6 files changed

+56
-38
lines changed

6 files changed

+56
-38
lines changed

packages/agent/src/core/toolAgent/config.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,42 @@ import { execSync } from 'child_process';
22

33
import { anthropic } from '@ai-sdk/anthropic';
44
import { openai } from '@ai-sdk/openai';
5+
import { createOllama, ollama } from 'ollama-ai-provider';
56

67
/**
78
* Available model providers
89
*/
9-
export type ModelProvider = 'anthropic' | 'openai';
10+
export type ModelProvider = 'anthropic' | 'openai' | 'ollama';
1011

1112
/**
1213
* Available models by provider
1314
*/
1415
export const AVAILABLE_MODELS = {
1516
anthropic: ['claude-3-7-sonnet-20250219', 'claude-3-opus-20240229'],
1617
openai: ['gpt-4o-2024-05-13', 'o3-mini-2024-07-18'],
18+
ollama: ['llama3-groq-tool-use'],
1719
};
1820

1921
/**
2022
* Get the model instance based on provider and model name
2123
*/
22-
export function getModel(provider: ModelProvider, modelName: string) {
24+
export function getModel(
25+
provider: ModelProvider,
26+
modelName: string,
27+
options?: { ollamaBaseUrl?: string },
28+
) {
2329
switch (provider) {
2430
case 'anthropic':
2531
return anthropic(modelName);
2632
case 'openai':
2733
return openai(modelName);
34+
case 'ollama':
35+
if (options?.ollamaBaseUrl) {
36+
return createOllama({
37+
baseURL: options.ollamaBaseUrl,
38+
})(modelName);
39+
}
40+
return ollama(modelName);
2841
default:
2942
throw new Error(`Unknown model provider: ${provider}`);
3043
}

packages/agent/src/tools/interaction/subAgent.ts

Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { z } from 'zod';
22
import { zodToJsonSchema } from 'zod-to-json-schema';
33

4-
import { getDefaultSystemPrompt } from '../../core/toolAgent/index.js';
54
import { getModel } from '../../core/toolAgent/config.js';
5+
import { getDefaultSystemPrompt } from '../../core/toolAgent/index.js';
66
import { toolAgent } from '../../core/toolAgent.js';
77
import { Tool, ToolContext } from '../../core/types.js';
88
import { getTools } from '../getTools.js';
@@ -18,23 +18,14 @@ const parameterSchema = z.object({
1818
projectContext: z
1919
.string()
2020
.describe('Context about the problem or environment'),
21-
fileContext: z
22-
.object({
23-
workingDirectory: z
24-
.string()
25-
.optional()
26-
.describe('The directory where the sub-agent should operate'),
27-
relevantFiles: z
28-
.string()
29-
.optional()
30-
.describe(
31-
'A list of files, which may include ** or * wildcard characters',
32-
),
33-
})
34-
.describe(
35-
'When working with files and directories, it is best to be very specific to avoid sub-agents making incorrect assumptions',
36-
)
37-
.optional(),
21+
workingDirectory: z
22+
.string()
23+
.optional()
24+
.describe('The directory where the sub-agent should operate'),
25+
relevantFilesDirectories: z
26+
.string()
27+
.optional()
28+
.describe('A list of files, which may include ** or * wildcard characters'),
3829
});
3930

4031
const returnSchema = z.object({
@@ -77,25 +68,22 @@ export const subAgentTool: Tool<Parameters, ReturnType> = {
7768
returnsJsonSchema: zodToJsonSchema(returnSchema),
7869
execute: async (params, context) => {
7970
// Validate parameters
80-
const { description, goal, projectContext, fileContext } =
81-
parameterSchema.parse(params);
71+
const {
72+
description,
73+
goal,
74+
projectContext,
75+
workingDirectory,
76+
relevantFilesDirectories,
77+
} = parameterSchema.parse(params);
8278

8379
// Construct a well-structured prompt
8480
const prompt = [
8581
`Description: ${description}`,
8682
`Goal: ${goal}`,
8783
`Project Context: ${projectContext}`,
88-
fileContext
89-
? `\nContext:\n${[
90-
fileContext.workingDirectory
91-
? `- Working Directory: ${fileContext.workingDirectory}`
92-
: '',
93-
fileContext.relevantFiles
94-
? `- Relevant Files:\n ${fileContext.relevantFiles}`
95-
: '',
96-
]
97-
.filter(Boolean)
98-
.join('\n')}`
84+
workingDirectory ? `Working Directory: ${workingDirectory}` : '',
85+
relevantFilesDirectories
86+
? `Relevant Files:\n ${relevantFilesDirectories}`
9987
: '',
10088
]
10189
.filter(Boolean)
@@ -110,8 +98,7 @@ export const subAgentTool: Tool<Parameters, ReturnType> = {
11098

11199
const result = await toolAgent(prompt, tools, config, {
112100
...context,
113-
workingDirectory:
114-
fileContext?.workingDirectory ?? context.workingDirectory,
101+
workingDirectory: workingDirectory ?? context.workingDirectory,
115102
});
116103
return { response: result.result };
117104
},

packages/cli/README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Command-line interface for AI-powered coding tasks.
44

55
## Features
66

7-
- 🤖 **AI-Powered**: Leverages Anthropic's Claude and OpenAI models for intelligent coding assistance
7+
- 🤖 **AI-Powered**: Leverages Anthropic's Claude, OpenAI models, and Ollama for intelligent coding assistance
88
- 🛠️ **Extensible Tool System**: Modular architecture with various tool categories
99
- 🔄 **Parallel Execution**: Ability to spawn sub-agents for concurrent task processing
1010
- 📝 **Self-Modification**: Can modify code, it was built and tested by writing itself
@@ -82,7 +82,7 @@ mycoder config set modelName gpt-4o-2024-05-13
8282

8383
### Model Selection
8484

85-
MyCoder supports both Anthropic and OpenAI models. You can configure which model to use with the following commands:
85+
MyCoder supports Anthropic, OpenAI, and Ollama models. You can configure which model to use with the following commands:
8686

8787
```bash
8888
# Use OpenAI's GPT-4o model
@@ -100,6 +100,13 @@ mycoder config set modelName claude-3-7-sonnet-20250219
100100
# Use Anthropic's Claude 3 Opus model
101101
mycoder config set modelProvider anthropic
102102
mycoder config set modelName claude-3-opus-20240229
103+
104+
# Use Ollama's llama3-groq-tool-use model (local)
105+
mycoder config set modelProvider ollama
106+
mycoder config set modelName llama3-groq-tool-use
107+
108+
# Configure custom Ollama server URL (default is http://localhost:11434/api)
109+
mycoder config set ollamaBaseUrl http://your-ollama-server:11434/api
103110
```
104111

105112
You can also specify the model provider and name directly when running a command:
@@ -114,6 +121,7 @@ mycoder --modelProvider openai --modelName gpt-4o-2024-05-13 "Your prompt here"
114121
- `headless`: Run browser in headless mode with no UI showing (default: `true`)
115122
- `userSession`: Use user's existing browser session instead of sandboxed session (default: `false`)
116123
- `pageFilter`: Method to process webpage content: 'simple', 'none', or 'readability' (default: `none`)
124+
- `ollamaBaseUrl`: Base URL for Ollama API (default: `http://localhost:11434/api`)
117125

118126
Example:
119127

@@ -126,13 +134,18 @@ mycoder config set userSession true
126134

127135
# Use readability for webpage processing
128136
mycoder config set pageFilter readability
137+
138+
# Set custom Ollama server URL
139+
mycoder config set ollamaBaseUrl http://your-ollama-server:11434/api
129140
```
130141

131142
## Environment Variables
132143

133144
- `ANTHROPIC_API_KEY`: Your Anthropic API key (required when using Anthropic models)
134145
- `OPENAI_API_KEY`: Your OpenAI API key (required when using OpenAI models)
135146

147+
Note: Ollama models do not require an API key as they run locally or on a specified server.
148+
136149
## Development
137150

138151
```bash

packages/cli/src/commands/$default.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ export const command: CommandModule<SharedOptions, DefaultArgs> = {
109109
);
110110
throw new Error('OpenAI API key not found');
111111
}
112+
// No API key check needed for Ollama as it uses a local server
112113

113114
// Validate model name
114115
if (!AVAILABLE_MODELS[userModelProvider].includes(userModelName)) {
@@ -166,8 +167,9 @@ export const command: CommandModule<SharedOptions, DefaultArgs> = {
166167
const agentConfig = {
167168
...DEFAULT_CONFIG,
168169
model: getModel(
169-
userModelProvider as 'anthropic' | 'openai',
170+
userModelProvider as 'anthropic' | 'openai' | 'ollama',
170171
userModelName,
172+
{ ollamaBaseUrl: config.ollamaBaseUrl },
171173
),
172174
};
173175

packages/cli/src/settings/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const defaultConfig = {
1414
pageFilter: 'none' as 'simple' | 'none' | 'readability',
1515
modelProvider: 'anthropic',
1616
modelName: 'claude-3-7-sonnet-20250219',
17+
ollamaBaseUrl: 'http://localhost:11434/api',
1718
};
1819

1920
export type Config = typeof defaultConfig;

packages/cli/tests/settings/config.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ describe('Config', () => {
4343
pageFilter: 'none',
4444
modelProvider: 'anthropic',
4545
modelName: 'claude-3-7-sonnet-20250219',
46+
ollamaBaseUrl: 'http://localhost:11434/api',
4647
});
4748
expect(fs.existsSync).toHaveBeenCalledWith(mockConfigFile);
4849
});
@@ -74,6 +75,7 @@ describe('Config', () => {
7475
pageFilter: 'none',
7576
modelProvider: 'anthropic',
7677
modelName: 'claude-3-7-sonnet-20250219',
78+
ollamaBaseUrl: 'http://localhost:11434/api',
7779
});
7880
});
7981
});

0 commit comments

Comments
 (0)