Skip to content

Commit b5d1dbc

Browse files
author
HitMargin
committed
provider指令切换模型类型
1 parent ad10444 commit b5d1dbc

3 files changed

Lines changed: 116 additions & 0 deletions

File tree

src/commands.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ import sandboxToggle from './commands/sandbox-toggle/index.js'
150150
import chrome from './commands/chrome/index.js'
151151
import stickers from './commands/stickers/index.js'
152152
import advisor from './commands/advisor.js'
153+
import provider from './commands/provider.js'
153154
import { logError } from './utils/log.js'
154155
import { toError } from './utils/errors.js'
155156
import { logForDebugging } from './utils/debug.js'
@@ -258,6 +259,7 @@ export const INTERNAL_ONLY_COMMANDS = [
258259
const COMMANDS = memoize((): Command[] => [
259260
addDir,
260261
advisor,
262+
provider,
261263
agents,
262264
branch,
263265
btw,

src/commands/provider.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import type { Command } from '../commands.js'
2+
import type { LocalCommandCall } from '../types/command.js'
3+
import { getAPIProvider } from '../utils/model/providers.js'
4+
import { updateSettingsForSource } from '../utils/settings/settings.js'
5+
import { getSettings_DEPRECATED } from '../utils/settings/settings.js'
6+
import { applyConfigEnvironmentVariables } from '../utils/managedEnv.js'
7+
8+
function getEnvVarForProvider(provider: string): string {
9+
switch (provider) {
10+
case 'bedrock':
11+
return 'CLAUDE_CODE_USE_BEDROCK'
12+
case 'vertex':
13+
return 'CLAUDE_CODE_USE_VERTEX'
14+
case 'foundry':
15+
return 'CLAUDE_CODE_USE_FOUNDRY'
16+
default:
17+
throw new Error(`Unknown provider: ${provider}`)
18+
}
19+
}
20+
21+
// Get merged env: process.env + settings.env (from userSettings)
22+
function getMergedEnv(): Record<string, string> {
23+
const settings = getSettings_DEPRECATED()
24+
const merged = { ...process.env }
25+
if (settings?.env) {
26+
Object.assign(merged, settings.env)
27+
}
28+
return merged
29+
}
30+
31+
const call: LocalCommandCall = async (args, context) => {
32+
const arg = args.trim().toLowerCase()
33+
34+
// No argument: show current provider
35+
if (!arg) {
36+
const current = getAPIProvider()
37+
return { type: 'text', value: `Current API provider: ${current}` }
38+
}
39+
40+
// unset - clear settings, fallback to env vars
41+
if (arg === 'unset') {
42+
updateSettingsForSource('userSettings', { modelType: undefined })
43+
// Also clear all provider-specific env vars to prevent conflicts
44+
delete process.env.CLAUDE_CODE_USE_BEDROCK
45+
delete process.env.CLAUDE_CODE_USE_VERTEX
46+
delete process.env.CLAUDE_CODE_USE_FOUNDRY
47+
delete process.env.CLAUDE_CODE_USE_OPENAI
48+
return { type: 'text', value: 'API provider cleared (will use environment variables).' }
49+
}
50+
51+
// Validate provider
52+
const validProviders = ['anthropic', 'openai', 'bedrock', 'vertex', 'foundry']
53+
if (!validProviders.includes(arg)) {
54+
return { type: 'text', value: `Invalid provider: ${arg}\nValid: ${validProviders.join(', ')}` }
55+
}
56+
57+
// Check env vars when switching to openai (including settings.env)
58+
if (arg === 'openai') {
59+
const mergedEnv = getMergedEnv()
60+
const hasKey = !!mergedEnv.OPENAI_API_KEY
61+
const hasUrl = !!mergedEnv.OPENAI_BASE_URL
62+
if (!hasKey || !hasUrl) {
63+
updateSettingsForSource('userSettings', { modelType: 'openai' })
64+
const missing = []
65+
if (!hasKey) missing.push('OPENAI_API_KEY')
66+
if (!hasUrl) missing.push('OPENAI_BASE_URL')
67+
return {
68+
type: 'text',
69+
value: `Switched to OpenAI provider.\nWarning: Missing env vars: ${missing.join(', ')}\nConfigure them via /login or set manually.`,
70+
}
71+
}
72+
}
73+
74+
// Handle different provider types
75+
// - 'anthropic' and 'openai' are stored in settings.json (persistent)
76+
// - 'bedrock', 'vertex', 'foundry' are env-only (do NOT touch settings.json)
77+
if (arg === 'anthropic' || arg === 'openai') {
78+
// Clear any cloud provider env vars to avoid conflicts
79+
delete process.env.CLAUDE_CODE_USE_BEDROCK
80+
delete process.env.CLAUDE_CODE_USE_VERTEX
81+
delete process.env.CLAUDE_CODE_USE_FOUNDRY
82+
// Update settings.json
83+
updateSettingsForSource('userSettings', { modelType: arg })
84+
// Ensure settings.env gets applied to process.env
85+
applyConfigEnvironmentVariables()
86+
return { type: 'text', value: `API provider set to ${arg}.` }
87+
} else {
88+
// Cloud providers: set env vars only, clear settings.modelType
89+
delete process.env.CLAUDE_CODE_USE_OPENAI
90+
delete process.env.OPENAI_API_KEY
91+
delete process.env.OPENAI_BASE_URL
92+
process.env[getEnvVarForProvider(arg)] = '1'
93+
// Clear settings.json to 'anthropic' to avoid validation errors
94+
updateSettingsForSource('userSettings', { modelType: 'anthropic' })
95+
// Ensure settings.env gets applied to process.env (in case it contains provider-specific creds)
96+
applyConfigEnvironmentVariables()
97+
return { type: 'text', value: `API provider set to ${arg} (via environment variable).` }
98+
}
99+
}
100+
101+
const provider = {
102+
type: 'local',
103+
name: 'provider',
104+
description: 'Switch API provider (anthropic/openai/bedrock/vertex/foundry)',
105+
aliases: ['api'],
106+
argumentHint: '[anthropic|openai|bedrock|vertex|foundry|unset]',
107+
supportsNonInteractive: true,
108+
load: () => Promise.resolve({ call }),
109+
} satisfies Command
110+
111+
export default provider

src/utils/model/providers.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ export function getAPIProvider(): APIProvider {
88
// 1. Check settings.json modelType field (highest priority)
99
const modelType = getInitialSettings().modelType
1010
if (modelType === 'openai') return 'openai'
11+
if (modelType === 'bedrock') return 'bedrock'
12+
if (modelType === 'vertex') return 'vertex'
13+
if (modelType === 'foundry') return 'foundry'
1114

1215
// 2. Check environment variables (backward compatibility)
1316
return isEnvTruthy(process.env.CLAUDE_CODE_USE_OPENAI)

0 commit comments

Comments
 (0)