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) => {