feat(opencode): drop bedrock defaults

This commit is contained in:
DhanushSantosh
2026-01-12 22:47:25 +05:30
parent 6184440441
commit 9ce3cfee7d
7 changed files with 69 additions and 289 deletions

View File

@@ -51,63 +51,38 @@ describe('opencode-provider.ts', () => {
});
describe('getAvailableModels', () => {
it('should return 10 models', () => {
it('should return 5 models', () => {
const models = provider.getAvailableModels();
expect(models).toHaveLength(10);
expect(models).toHaveLength(5);
});
it('should include Claude Sonnet 4.5 (Bedrock) as default', () => {
const models = provider.getAvailableModels();
const sonnet = models.find(
(m) => m.id === 'amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0'
);
expect(sonnet).toBeDefined();
expect(sonnet?.name).toBe('Claude Sonnet 4.5 (Bedrock)');
expect(sonnet?.provider).toBe('opencode');
expect(sonnet?.default).toBe(true);
expect(sonnet?.modelString).toBe('amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0');
});
it('should include Claude Opus 4.5 (Bedrock)', () => {
const models = provider.getAvailableModels();
const opus = models.find(
(m) => m.id === 'amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0'
);
expect(opus).toBeDefined();
expect(opus?.name).toBe('Claude Opus 4.5 (Bedrock)');
expect(opus?.modelString).toBe('amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0');
});
it('should include Claude Haiku 4.5 (Bedrock)', () => {
const models = provider.getAvailableModels();
const haiku = models.find(
(m) => m.id === 'amazon-bedrock/anthropic.claude-haiku-4-5-20251001-v1:0'
);
expect(haiku).toBeDefined();
expect(haiku?.name).toBe('Claude Haiku 4.5 (Bedrock)');
expect(haiku?.tier).toBe('standard');
});
it('should include free tier Big Pickle model', () => {
it('should include Big Pickle as default', () => {
const models = provider.getAvailableModels();
const bigPickle = models.find((m) => m.id === 'opencode/big-pickle');
expect(bigPickle).toBeDefined();
expect(bigPickle?.name).toBe('Big Pickle (Free)');
expect(bigPickle?.provider).toBe('opencode');
expect(bigPickle?.default).toBe(true);
expect(bigPickle?.modelString).toBe('opencode/big-pickle');
expect(bigPickle?.tier).toBe('basic');
});
it('should include DeepSeek R1 (Bedrock)', () => {
it('should include free tier GLM model', () => {
const models = provider.getAvailableModels();
const deepseek = models.find((m) => m.id === 'amazon-bedrock/deepseek.r1-v1:0');
const glm = models.find((m) => m.id === 'opencode/glm-4.7-free');
expect(deepseek).toBeDefined();
expect(deepseek?.name).toBe('DeepSeek R1 (Bedrock)');
expect(deepseek?.tier).toBe('premium');
expect(glm).toBeDefined();
expect(glm?.name).toBe('GLM 4.7 Free');
expect(glm?.tier).toBe('basic');
});
it('should include free tier MiniMax model', () => {
const models = provider.getAvailableModels();
const minimax = models.find((m) => m.id === 'opencode/minimax-m2.1-free');
expect(minimax).toBeDefined();
expect(minimax?.name).toBe('MiniMax M2.1 Free');
expect(minimax?.tier).toBe('basic');
});
it('should have all models support tools', () => {
@@ -130,18 +105,14 @@ describe('opencode-provider.ts', () => {
describe('parseModelsOutput', () => {
it('should parse nested provider model IDs', () => {
const output = [
'openrouter/anthropic/claude-3.5-sonnet',
'openai/gpt-4o',
'amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0',
].join('\n');
const output = ['openrouter/anthropic/claude-3.5-sonnet', 'openai/gpt-4o'].join('\n');
const parseModelsOutput = (
provider as unknown as { parseModelsOutput: (output: string) => ModelDefinition[] }
).parseModelsOutput;
const models = parseModelsOutput(output);
expect(models).toHaveLength(3);
expect(models).toHaveLength(2);
const openrouterModel = models.find((model) => model.id.startsWith('openrouter/'));
expect(openrouterModel).toBeDefined();
@@ -1265,7 +1236,7 @@ describe('opencode-provider.ts', () => {
const defaultModels = models.filter((m) => m.default === true);
expect(defaultModels).toHaveLength(1);
expect(defaultModels[0].id).toBe('amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0');
expect(defaultModels[0].id).toBe('opencode/big-pickle');
});
it('should have valid tier values for all models', () => {

View File

@@ -11,12 +11,8 @@ import type { OpencodeAuthStatus, OpenCodeProviderInfo } from '../cli-status/ope
const logger = createLogger('OpencodeSettings');
const OPENCODE_PROVIDER_ID = 'opencode';
const OPENCODE_BEDROCK_PROVIDER_ID = 'amazon-bedrock';
const OPENCODE_PROVIDER_SIGNATURE_SEPARATOR = '|';
const OPENCODE_STATIC_MODEL_PROVIDERS = new Set([
OPENCODE_PROVIDER_ID,
OPENCODE_BEDROCK_PROVIDER_ID,
]);
const OPENCODE_STATIC_MODEL_PROVIDERS = new Set([OPENCODE_PROVIDER_ID]);
export function OpencodeSettingsTab() {
const {

View File

@@ -182,7 +182,7 @@ export function OpencodeSetupStep({ onNext, onBack, onSkip }: OpencodeSetupStepP
<div>
<p className="font-medium text-foreground">This step is optional</p>
<p className="text-sm text-muted-foreground mt-1">
Configure OpenCode CLI for access to free tier models and AWS Bedrock models. You
Configure OpenCode CLI for access to free tier models and connected providers. You
can skip this and use other providers, or configure it later in Settings.
</p>
</div>
@@ -241,7 +241,7 @@ export function OpencodeSetupStep({ onNext, onBack, onSkip }: OpencodeSetupStepP
<div className="flex-1">
<p className="font-medium text-foreground">OpenCode CLI not found</p>
<p className="text-sm text-muted-foreground mt-1">
Install the OpenCode CLI to use free tier and AWS Bedrock models.
Install the OpenCode CLI to use free tier models and connected providers.
</p>
</div>
</div>

View File

@@ -1119,7 +1119,7 @@ function OpencodeContent() {
<div className="flex-1">
<p className="font-medium text-foreground">OpenCode CLI not found</p>
<p className="text-sm text-muted-foreground mt-1">
Install the OpenCode CLI for free tier and AWS Bedrock models.
Install the OpenCode CLI for free tier models and connected providers.
</p>
</div>
</div>

View File

@@ -28,7 +28,11 @@ import { getHttpApiClient, waitForApiKeyInit } from '@/lib/http-api-client';
import { getItem, setItem } from '@/lib/storage';
import { useAppStore, THEME_STORAGE_KEY } from '@/store/app-store';
import { useSetupStore } from '@/store/setup-store';
import type { GlobalSettings } from '@automaker/types';
import {
DEFAULT_OPENCODE_MODEL,
getAllOpencodeModelIds,
type GlobalSettings,
} from '@automaker/types';
const logger = createLogger('SettingsMigration');
@@ -497,6 +501,21 @@ export function useSettingsMigration(): MigrationState {
*/
export function hydrateStoreFromSettings(settings: GlobalSettings): void {
const current = useAppStore.getState();
const validOpencodeModelIds = new Set(getAllOpencodeModelIds());
const incomingEnabledOpencodeModels =
settings.enabledOpencodeModels ?? current.enabledOpencodeModels;
const sanitizedOpencodeDefaultModel = validOpencodeModelIds.has(
settings.opencodeDefaultModel ?? current.opencodeDefaultModel
)
? (settings.opencodeDefaultModel ?? current.opencodeDefaultModel)
: DEFAULT_OPENCODE_MODEL;
const sanitizedEnabledOpencodeModels = Array.from(
new Set(incomingEnabledOpencodeModels.filter((modelId) => validOpencodeModelIds.has(modelId)))
);
if (!sanitizedEnabledOpencodeModels.includes(sanitizedOpencodeDefaultModel)) {
sanitizedEnabledOpencodeModels.push(sanitizedOpencodeDefaultModel);
}
// Convert ProjectRef[] to Project[] (minimal data, features will be loaded separately)
const projects = (settings.projects ?? []).map((ref) => ({
@@ -541,8 +560,8 @@ export function hydrateStoreFromSettings(settings: GlobalSettings): void {
phaseModels: settings.phaseModels ?? current.phaseModels,
enabledCursorModels: settings.enabledCursorModels ?? current.enabledCursorModels,
cursorDefaultModel: settings.cursorDefaultModel ?? 'auto',
enabledOpencodeModels: settings.enabledOpencodeModels ?? current.enabledOpencodeModels,
opencodeDefaultModel: settings.opencodeDefaultModel ?? current.opencodeDefaultModel,
enabledOpencodeModels: sanitizedEnabledOpencodeModels,
opencodeDefaultModel: sanitizedOpencodeDefaultModel,
autoLoadClaudeMd: settings.autoLoadClaudeMd ?? false,
skipSandboxWarning: settings.skipSandboxWarning ?? false,
keyboardShortcuts: {

View File

@@ -34,6 +34,8 @@ import {
} from '@automaker/types';
const logger = createLogger('AppStore');
const OPENCODE_BEDROCK_PROVIDER_ID = 'amazon-bedrock';
const OPENCODE_BEDROCK_MODEL_PREFIX = `${OPENCODE_BEDROCK_PROVIDER_ID}/`;
// Re-export types for convenience
export type {
@@ -1237,7 +1239,7 @@ const initialState: AppState = {
codexEnableWebSearch: false, // Default to disabled
codexEnableImages: false, // Default to disabled
enabledOpencodeModels: getAllOpencodeModelIds(), // All OpenCode models enabled by default
opencodeDefaultModel: DEFAULT_OPENCODE_MODEL, // Default to Claude Sonnet 4.5
opencodeDefaultModel: DEFAULT_OPENCODE_MODEL, // Default to OpenCode free tier
dynamicOpencodeModels: [], // Empty until fetched from OpenCode CLI
enabledDynamicModelIds: [], // All dynamic models enabled by default (populated when models are fetched)
cachedOpencodeProviders: [], // Empty until fetched from OpenCode CLI
@@ -2042,15 +2044,20 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
// Dynamic models are session-only (not persisted to server) because they depend on
// current CLI authentication state and are re-discovered each session
// When setting dynamic models, auto-enable all of them if enabledDynamicModelIds is empty
const filteredModels = models.filter(
(model) =>
model.provider !== OPENCODE_BEDROCK_PROVIDER_ID &&
!model.id.startsWith(OPENCODE_BEDROCK_MODEL_PREFIX)
);
const currentEnabled = get().enabledDynamicModelIds;
const newModelIds = models.map((m) => m.id);
const newModelIds = filteredModels.map((m) => m.id);
// If no models were previously enabled, enable all new ones
if (currentEnabled.length === 0) {
set({ dynamicOpencodeModels: models, enabledDynamicModelIds: newModelIds });
set({ dynamicOpencodeModels: filteredModels, enabledDynamicModelIds: newModelIds });
} else {
// Keep existing enabled state, just update the models list
set({ dynamicOpencodeModels: models });
set({ dynamicOpencodeModels: filteredModels });
}
},
setEnabledDynamicModelIds: (ids) => set({ enabledDynamicModelIds: ids }),
@@ -2060,7 +2067,12 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
? [...state.enabledDynamicModelIds, modelId]
: state.enabledDynamicModelIds.filter((id) => id !== modelId),
})),
setCachedOpencodeProviders: (providers) => set({ cachedOpencodeProviders: providers }),
setCachedOpencodeProviders: (providers) =>
set({
cachedOpencodeProviders: providers.filter(
(provider) => provider.id !== OPENCODE_BEDROCK_PROVIDER_ID
),
}),
// Claude Agent SDK Settings actions
setAutoLoadClaudeMd: async (enabled) => {

View File

@@ -8,43 +8,12 @@ export type OpencodeModelId =
| 'opencode/glm-4.7-free'
| 'opencode/gpt-5-nano'
| 'opencode/grok-code'
| 'opencode/minimax-m2.1-free'
// Amazon Bedrock - Claude Models
| 'amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0'
| 'amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0'
| 'amazon-bedrock/anthropic.claude-haiku-4-5-20251001-v1:0'
| 'amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0'
| 'amazon-bedrock/anthropic.claude-opus-4-20250514-v1:0'
| 'amazon-bedrock/anthropic.claude-3-7-sonnet-20250219-v1:0'
| 'amazon-bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0'
| 'amazon-bedrock/anthropic.claude-3-opus-20240229-v1:0'
// Amazon Bedrock - DeepSeek Models
| 'amazon-bedrock/deepseek.r1-v1:0'
| 'amazon-bedrock/deepseek.v3-v1:0'
// Amazon Bedrock - Amazon Nova Models
| 'amazon-bedrock/amazon.nova-premier-v1:0'
| 'amazon-bedrock/amazon.nova-pro-v1:0'
| 'amazon-bedrock/amazon.nova-lite-v1:0'
// Amazon Bedrock - Meta Llama Models
| 'amazon-bedrock/meta.llama4-maverick-17b-instruct-v1:0'
| 'amazon-bedrock/meta.llama3-3-70b-instruct-v1:0'
// Amazon Bedrock - Mistral Models
| 'amazon-bedrock/mistral.mistral-large-2402-v1:0'
// Amazon Bedrock - Qwen Models
| 'amazon-bedrock/qwen.qwen3-coder-480b-a35b-v1:0'
| 'amazon-bedrock/qwen.qwen3-235b-a22b-2507-v1:0';
| 'opencode/minimax-m2.1-free';
/**
* Provider type for OpenCode models
*/
export type OpencodeProvider =
| 'opencode'
| 'amazon-bedrock-anthropic'
| 'amazon-bedrock-deepseek'
| 'amazon-bedrock-amazon'
| 'amazon-bedrock-meta'
| 'amazon-bedrock-mistral'
| 'amazon-bedrock-qwen';
export type OpencodeProvider = 'opencode';
/**
* Friendly aliases mapped to full model IDs
@@ -59,36 +28,6 @@ export const OPENCODE_MODEL_MAP: Record<string, OpencodeModelId> = {
'grok-code': 'opencode/grok-code',
grok: 'opencode/grok-code',
minimax: 'opencode/minimax-m2.1-free',
// Claude aliases (via Bedrock)
'claude-sonnet-4.5': 'amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0',
'sonnet-4.5': 'amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0',
sonnet: 'amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0',
'claude-opus-4.5': 'amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0',
'opus-4.5': 'amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0',
opus: 'amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0',
'claude-haiku-4.5': 'amazon-bedrock/anthropic.claude-haiku-4-5-20251001-v1:0',
'haiku-4.5': 'amazon-bedrock/anthropic.claude-haiku-4-5-20251001-v1:0',
haiku: 'amazon-bedrock/anthropic.claude-haiku-4-5-20251001-v1:0',
// DeepSeek aliases
'deepseek-r1': 'amazon-bedrock/deepseek.r1-v1:0',
r1: 'amazon-bedrock/deepseek.r1-v1:0',
'deepseek-v3': 'amazon-bedrock/deepseek.v3-v1:0',
// Nova aliases
'nova-premier': 'amazon-bedrock/amazon.nova-premier-v1:0',
'nova-pro': 'amazon-bedrock/amazon.nova-pro-v1:0',
nova: 'amazon-bedrock/amazon.nova-pro-v1:0',
// Llama aliases
llama4: 'amazon-bedrock/meta.llama4-maverick-17b-instruct-v1:0',
'llama-4': 'amazon-bedrock/meta.llama4-maverick-17b-instruct-v1:0',
llama3: 'amazon-bedrock/meta.llama3-3-70b-instruct-v1:0',
// Qwen aliases
qwen: 'amazon-bedrock/qwen.qwen3-coder-480b-a35b-v1:0',
'qwen-coder': 'amazon-bedrock/qwen.qwen3-coder-480b-a35b-v1:0',
} as const;
/**
@@ -148,162 +87,6 @@ export const OPENCODE_MODELS: OpencodeModelConfig[] = [
provider: 'opencode',
tier: 'free',
},
// Amazon Bedrock - Claude Models
{
id: 'amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0',
label: 'Claude Sonnet 4.5 (Bedrock)',
description: 'Latest Claude Sonnet via AWS Bedrock - fast and intelligent (default)',
supportsVision: true,
provider: 'amazon-bedrock-anthropic',
tier: 'premium',
},
{
id: 'amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0',
label: 'Claude Opus 4.5 (Bedrock)',
description: 'Most capable Claude model via AWS Bedrock',
supportsVision: true,
provider: 'amazon-bedrock-anthropic',
tier: 'premium',
},
{
id: 'amazon-bedrock/anthropic.claude-haiku-4-5-20251001-v1:0',
label: 'Claude Haiku 4.5 (Bedrock)',
description: 'Fastest Claude model via AWS Bedrock',
supportsVision: true,
provider: 'amazon-bedrock-anthropic',
tier: 'standard',
},
{
id: 'amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0',
label: 'Claude Sonnet 4 (Bedrock)',
description: 'Claude Sonnet 4 via AWS Bedrock',
supportsVision: true,
provider: 'amazon-bedrock-anthropic',
tier: 'premium',
},
{
id: 'amazon-bedrock/anthropic.claude-opus-4-20250514-v1:0',
label: 'Claude Opus 4 (Bedrock)',
description: 'Claude Opus 4 via AWS Bedrock',
supportsVision: true,
provider: 'amazon-bedrock-anthropic',
tier: 'premium',
},
{
id: 'amazon-bedrock/anthropic.claude-3-7-sonnet-20250219-v1:0',
label: 'Claude 3.7 Sonnet (Bedrock)',
description: 'Claude 3.7 Sonnet via AWS Bedrock',
supportsVision: true,
provider: 'amazon-bedrock-anthropic',
tier: 'standard',
},
{
id: 'amazon-bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0',
label: 'Claude 3.5 Sonnet (Bedrock)',
description: 'Claude 3.5 Sonnet v2 via AWS Bedrock',
supportsVision: true,
provider: 'amazon-bedrock-anthropic',
tier: 'standard',
},
{
id: 'amazon-bedrock/anthropic.claude-3-opus-20240229-v1:0',
label: 'Claude 3 Opus (Bedrock)',
description: 'Claude 3 Opus via AWS Bedrock',
supportsVision: true,
provider: 'amazon-bedrock-anthropic',
tier: 'premium',
},
// Amazon Bedrock - DeepSeek Models
{
id: 'amazon-bedrock/deepseek.r1-v1:0',
label: 'DeepSeek R1 (Bedrock)',
description: 'DeepSeek R1 reasoning model via AWS Bedrock - excellent for coding',
supportsVision: false,
provider: 'amazon-bedrock-deepseek',
tier: 'premium',
},
{
id: 'amazon-bedrock/deepseek.v3-v1:0',
label: 'DeepSeek V3 (Bedrock)',
description: 'DeepSeek V3 via AWS Bedrock',
supportsVision: false,
provider: 'amazon-bedrock-deepseek',
tier: 'standard',
},
// Amazon Bedrock - Amazon Nova Models
{
id: 'amazon-bedrock/amazon.nova-premier-v1:0',
label: 'Amazon Nova Premier (Bedrock)',
description: 'Amazon Nova Premier - most capable Nova model',
supportsVision: true,
provider: 'amazon-bedrock-amazon',
tier: 'premium',
},
{
id: 'amazon-bedrock/amazon.nova-pro-v1:0',
label: 'Amazon Nova Pro (Bedrock)',
description: 'Amazon Nova Pro - balanced performance',
supportsVision: true,
provider: 'amazon-bedrock-amazon',
tier: 'standard',
},
{
id: 'amazon-bedrock/amazon.nova-lite-v1:0',
label: 'Amazon Nova Lite (Bedrock)',
description: 'Amazon Nova Lite - fast and efficient',
supportsVision: true,
provider: 'amazon-bedrock-amazon',
tier: 'standard',
},
// Amazon Bedrock - Meta Llama Models
{
id: 'amazon-bedrock/meta.llama4-maverick-17b-instruct-v1:0',
label: 'Llama 4 Maverick 17B (Bedrock)',
description: 'Meta Llama 4 Maverick via AWS Bedrock',
supportsVision: false,
provider: 'amazon-bedrock-meta',
tier: 'standard',
},
{
id: 'amazon-bedrock/meta.llama3-3-70b-instruct-v1:0',
label: 'Llama 3.3 70B (Bedrock)',
description: 'Meta Llama 3.3 70B via AWS Bedrock',
supportsVision: false,
provider: 'amazon-bedrock-meta',
tier: 'standard',
},
// Amazon Bedrock - Mistral Models
{
id: 'amazon-bedrock/mistral.mistral-large-2402-v1:0',
label: 'Mistral Large (Bedrock)',
description: 'Mistral Large via AWS Bedrock',
supportsVision: false,
provider: 'amazon-bedrock-mistral',
tier: 'standard',
},
// Amazon Bedrock - Qwen Models
{
id: 'amazon-bedrock/qwen.qwen3-coder-480b-a35b-v1:0',
label: 'Qwen3 Coder 480B (Bedrock)',
description: 'Qwen3 Coder 480B via AWS Bedrock - excellent for coding',
supportsVision: false,
provider: 'amazon-bedrock-qwen',
tier: 'premium',
},
{
id: 'amazon-bedrock/qwen.qwen3-235b-a22b-2507-v1:0',
label: 'Qwen3 235B (Bedrock)',
description: 'Qwen3 235B via AWS Bedrock',
supportsVision: false,
provider: 'amazon-bedrock-qwen',
tier: 'premium',
},
];
/**
@@ -319,10 +102,9 @@ export const OPENCODE_MODEL_CONFIG_MAP: Record<OpencodeModelId, OpencodeModelCon
);
/**
* Default OpenCode model - Claude Sonnet 4.5 via Bedrock
* Default OpenCode model - OpenCode free tier
*/
export const DEFAULT_OPENCODE_MODEL: OpencodeModelId =
'amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0';
export const DEFAULT_OPENCODE_MODEL: OpencodeModelId = 'opencode/big-pickle';
/**
* Helper: Get display name for model