Skip to content

Commit fa9aecd

Browse files
authored
Merge pull request #81 from drivecore/feature/add-openai-models
Add support for OpenAI models (o3 mini and GPT-4o)
2 parents ca44fe7 + 9f5a14a commit fa9aecd

File tree

8 files changed

+99
-7
lines changed

8 files changed

+99
-7
lines changed

.changeset/openai-models.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'mycoder': minor
3+
'mycoder-agent': minor
4+
---
5+
6+
Add support for OpenAI models (o3 mini and GPT-4o) via Vercel AI SDK

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,34 @@
11
import { execSync } from 'child_process';
22

33
import { anthropic } from '@ai-sdk/anthropic';
4+
import { openai } from '@ai-sdk/openai';
5+
6+
/**
7+
* Available model providers
8+
*/
9+
export type ModelProvider = 'anthropic' | 'openai';
10+
11+
/**
12+
* Available models by provider
13+
*/
14+
export const AVAILABLE_MODELS = {
15+
anthropic: ['claude-3-7-sonnet-20250219', 'claude-3-opus-20240229'],
16+
openai: ['gpt-4o-2024-05-13', 'o3-mini-2024-07-18'],
17+
};
18+
19+
/**
20+
* Get the model instance based on provider and model name
21+
*/
22+
export function getModel(provider: ModelProvider, modelName: string) {
23+
switch (provider) {
24+
case 'anthropic':
25+
return anthropic(modelName);
26+
case 'openai':
27+
return openai(modelName);
28+
default:
29+
throw new Error(`Unknown model provider: ${provider}`);
30+
}
31+
}
432

533
/**
634
* Default configuration for the tool agent

packages/agent/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export * from './tools/interaction/userPrompt.js';
2626
export * from './core/executeToolCall.js';
2727
export * from './core/types.js';
2828
export * from './core/toolAgent.js';
29+
export * from './core/toolAgent/config.js';
2930

3031
// Utils
3132
export * from './tools/getTools.js';

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { anthropic } from '@ai-sdk/anthropic';
21
import { z } from 'zod';
32
import { zodToJsonSchema } from 'zod-to-json-schema';
43

4+
import { getModel } from '../../core/toolAgent/config.js';
55
import { toolAgent } from '../../core/toolAgent.js';
66
import { Tool } from '../../core/types.js';
77
import { getTools } from '../getTools.js';
@@ -50,7 +50,7 @@ type ReturnType = z.infer<typeof returnSchema>;
5050
// Sub-agent specific configuration
5151
const subAgentConfig = {
5252
maxIterations: 50,
53-
model: anthropic('claude-3-7-sonnet-20250219'),
53+
model: getModel('anthropic', 'claude-3-7-sonnet-20250219'),
5454
maxTokens: 4096,
5555
temperature: 0.7,
5656
getSystemPrompt: () => {

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

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ import {
1111
LogLevel,
1212
subAgentTool,
1313
errorToString,
14+
getModel,
15+
AVAILABLE_MODELS,
16+
DEFAULT_CONFIG,
1417
} from 'mycoder-agent';
1518
import { TokenTracker } from 'mycoder-agent/dist/core/tokens.js';
1619

1720
import { SharedOptions } from '../options.js';
1821
import { initSentry, captureException } from '../sentry/index.js';
22+
import { getConfig } from '../settings/config.js';
1923
import { hasUserConsented, saveUserConsent } from '../settings/settings.js';
2024
import { nameToLogIndex } from '../utils/nameToLogIndex.js';
2125
import { checkForUpdates, getPackageInfo } from '../utils/versionCheck.js';
@@ -86,10 +90,33 @@ export const command: CommandModule<SharedOptions, DefaultArgs> = {
8690
);
8791

8892
try {
89-
// Early API key check
90-
if (!process.env.ANTHROPIC_API_KEY) {
93+
// Get configuration for model provider and name
94+
const userConfig = getConfig();
95+
const userModelProvider = argv.modelProvider || userConfig.modelProvider;
96+
const userModelName = argv.modelName || userConfig.modelName;
97+
98+
// Early API key check based on model provider
99+
if (userModelProvider === 'anthropic' && !process.env.ANTHROPIC_API_KEY) {
91100
logger.error(getAnthropicApiKeyError());
92101
throw new Error('Anthropic API key not found');
102+
} else if (
103+
userModelProvider === 'openai' &&
104+
!process.env.OPENAI_API_KEY
105+
) {
106+
logger.error(
107+
'No OpenAI API key found. Please set the OPENAI_API_KEY environment variable.',
108+
'You can get an API key from https://platform.openai.com/api-keys',
109+
);
110+
throw new Error('OpenAI API key not found');
111+
}
112+
113+
// Validate model name
114+
if (!AVAILABLE_MODELS[userModelProvider].includes(userModelName)) {
115+
logger.error(
116+
`Invalid model name: ${userModelName} for provider ${userModelProvider}`,
117+
`Available models for ${userModelProvider}: ${AVAILABLE_MODELS[userModelProvider].join(', ')}`,
118+
);
119+
throw new Error(`Invalid model name: ${userModelName}`);
93120
}
94121

95122
let prompt: string | undefined;
@@ -134,7 +161,16 @@ export const command: CommandModule<SharedOptions, DefaultArgs> = {
134161
process.exit(0);
135162
});
136163

137-
const result = await toolAgent(prompt, tools, undefined, {
164+
// Create a config with the selected model
165+
const agentConfig = {
166+
...DEFAULT_CONFIG,
167+
model: getModel(
168+
userModelProvider as 'anthropic' | 'openai',
169+
userModelName,
170+
),
171+
};
172+
173+
const result = await toolAgent(prompt, tools, agentConfig, {
138174
logger,
139175
headless: argv.headless ?? true,
140176
userSession: argv.userSession ?? false,

packages/cli/src/options.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export type SharedOptions = {
77
readonly userSession?: boolean;
88
readonly pageFilter?: 'simple' | 'none' | 'readability';
99
readonly sentryDsn?: string;
10+
readonly modelProvider?: string;
11+
readonly modelName?: string;
1012
};
1113

1214
export const sharedOptions = {
@@ -17,6 +19,15 @@ export const sharedOptions = {
1719
default: 'info',
1820
choices: ['debug', 'verbose', 'info', 'warn', 'error'],
1921
} as const,
22+
modelProvider: {
23+
type: 'string',
24+
description: 'AI model provider to use',
25+
choices: ['anthropic', 'openai'],
26+
} as const,
27+
modelName: {
28+
type: 'string',
29+
description: 'AI model name to use',
30+
} as const,
2031
interactive: {
2132
type: 'boolean',
2233
alias: 'i',

packages/cli/src/settings/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const configFile = path.join(getSettingsDir(), 'config.json');
99
const defaultConfig = {
1010
// Add default configuration values here
1111
githubMode: false,
12+
modelProvider: 'anthropic',
13+
modelName: 'claude-3-7-sonnet-20250219',
1214
};
1315

1416
export type Config = typeof defaultConfig;

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ describe('Config', () => {
3636

3737
const config = getConfig();
3838

39-
expect(config).toEqual({ githubMode: false });
39+
expect(config).toEqual({
40+
githubMode: false,
41+
modelProvider: 'anthropic',
42+
modelName: 'claude-3-7-sonnet-20250219',
43+
});
4044
expect(fs.existsSync).toHaveBeenCalledWith(mockConfigFile);
4145
});
4246

@@ -60,7 +64,11 @@ describe('Config', () => {
6064

6165
const config = getConfig();
6266

63-
expect(config).toEqual({ githubMode: false });
67+
expect(config).toEqual({
68+
githubMode: false,
69+
modelProvider: 'anthropic',
70+
modelName: 'claude-3-7-sonnet-20250219',
71+
});
6472
});
6573
});
6674

0 commit comments

Comments
 (0)