-
Notifications
You must be signed in to change notification settings - Fork 16.4k
provider (api) 指令查询/切换模型api类型 分离OpenAl和Anthropic模型的环境变量 分离 Gemini和Anthropic环境变量 通过github action更新contributors #129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 17 commits
c9f95fc
fa5329d
21b2854
dfe25b1
affc826
4fa7582
7631c0f
3ae973c
e548369
ad10444
b5d1dbc
0c9fd37
c33d5dc
ec5dfed
eb6fbe5
a0141c1
1e53943
282f2f4
41f733a
a50971f
2f95d1a
ecac0ab
1f8f90e
26245e0
a7a9659
6f80e96
14dc54a
81ecd82
eb62b47
a15340f
5bf3c93
fec8ec6
7d88b4d
767d6fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| import type { Command } from '../commands.js' | ||
| import type { LocalCommandCall } from '../types/command.js' | ||
| import { getAPIProvider } from '../utils/model/providers.js' | ||
| import { updateSettingsForSource } from '../utils/settings/settings.js' | ||
| import { getSettings_DEPRECATED } from '../utils/settings/settings.js' | ||
| import { applyConfigEnvironmentVariables } from '../utils/managedEnv.js' | ||
|
|
||
| function getEnvVarForProvider(provider: string): string { | ||
| switch (provider) { | ||
| case 'bedrock': | ||
| return 'CLAUDE_CODE_USE_BEDROCK' | ||
| case 'vertex': | ||
| return 'CLAUDE_CODE_USE_VERTEX' | ||
| case 'foundry': | ||
| return 'CLAUDE_CODE_USE_FOUNDRY' | ||
| default: | ||
| throw new Error(`Unknown provider: ${provider}`) | ||
| } | ||
| } | ||
|
|
||
| // Get merged env: process.env + settings.env (from userSettings) | ||
| function getMergedEnv(): Record<string, string> { | ||
| const settings = getSettings_DEPRECATED() | ||
| const merged = { ...process.env } | ||
| if (settings?.env) { | ||
| Object.assign(merged, settings.env) | ||
| } | ||
| return merged | ||
| } | ||
|
|
||
| const call: LocalCommandCall = async (args, context) => { | ||
| const arg = args.trim().toLowerCase() | ||
|
|
||
| // No argument: show current provider | ||
| if (!arg) { | ||
| const current = getAPIProvider() | ||
| return { type: 'text', value: `Current API provider: ${current}` } | ||
| } | ||
|
|
||
| // unset - clear settings, fallback to env vars | ||
| if (arg === 'unset') { | ||
| updateSettingsForSource('userSettings', { modelType: undefined }) | ||
| // Also clear all provider-specific env vars to prevent conflicts | ||
| delete process.env.CLAUDE_CODE_USE_BEDROCK | ||
| delete process.env.CLAUDE_CODE_USE_VERTEX | ||
| delete process.env.CLAUDE_CODE_USE_FOUNDRY | ||
| delete process.env.CLAUDE_CODE_USE_OPENAI | ||
| return { type: 'text', value: 'API provider cleared (will use environment variables).' } | ||
| } | ||
|
|
||
| // Validate provider | ||
| const validProviders = ['anthropic', 'openai', 'bedrock', 'vertex', 'foundry'] | ||
| if (!validProviders.includes(arg)) { | ||
| return { type: 'text', value: `Invalid provider: ${arg}\nValid: ${validProviders.join(', ')}` } | ||
| } | ||
|
|
||
| // Check env vars when switching to openai (including settings.env) | ||
| if (arg === 'openai') { | ||
| const mergedEnv = getMergedEnv() | ||
| const hasKey = !!mergedEnv.OPENAI_API_KEY | ||
| const hasUrl = !!mergedEnv.OPENAI_BASE_URL | ||
| if (!hasKey || !hasUrl) { | ||
| updateSettingsForSource('userSettings', { modelType: 'openai' }) | ||
| const missing = [] | ||
| if (!hasKey) missing.push('OPENAI_API_KEY') | ||
| if (!hasUrl) missing.push('OPENAI_BASE_URL') | ||
| return { | ||
| type: 'text', | ||
| value: `Switched to OpenAI provider.\nWarning: Missing env vars: ${missing.join(', ')}\nConfigure them via /login or set manually.`, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Handle different provider types | ||
| // - 'anthropic' and 'openai' are stored in settings.json (persistent) | ||
| // - 'bedrock', 'vertex', 'foundry' are env-only (do NOT touch settings.json) | ||
| if (arg === 'anthropic' || arg === 'openai') { | ||
| // Clear any cloud provider env vars to avoid conflicts | ||
| delete process.env.CLAUDE_CODE_USE_BEDROCK | ||
| delete process.env.CLAUDE_CODE_USE_VERTEX | ||
| delete process.env.CLAUDE_CODE_USE_FOUNDRY | ||
| // Update settings.json | ||
| updateSettingsForSource('userSettings', { modelType: arg }) | ||
| // Ensure settings.env gets applied to process.env | ||
| applyConfigEnvironmentVariables() | ||
| return { type: 'text', value: `API provider set to ${arg}.` } | ||
| } else { | ||
| // Cloud providers: set env vars only, do NOT touch settings.modelType | ||
| delete process.env.CLAUDE_CODE_USE_OPENAI | ||
| delete process.env.OPENAI_API_KEY | ||
| delete process.env.OPENAI_BASE_URL | ||
| process.env[getEnvVarForProvider(arg)] = '1' | ||
| // Do not modify settings.json - cloud providers controlled solely by env vars | ||
| applyConfigEnvironmentVariables() | ||
| return { type: 'text', value: `API provider set to ${arg} (via environment variable).` } | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
|
|
||
| const provider = { | ||
| type: 'local', | ||
| name: 'provider', | ||
| description: 'Switch API provider (anthropic/openai/bedrock/vertex/foundry)', | ||
| aliases: ['api'], | ||
| argumentHint: '[anthropic|openai|bedrock|vertex|foundry|unset]', | ||
| supportsNonInteractive: true, | ||
| load: () => Promise.resolve({ call }), | ||
| } satisfies Command | ||
|
|
||
| export default provider | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -303,17 +303,10 @@ export function ConsoleOAuthFlow({ | |||||||||||||||||||||||||||||||
| !pendingOAuthStartRef.current | ||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||
| pendingOAuthStartRef.current = true | ||||||||||||||||||||||||||||||||
| process.nextTick( | ||||||||||||||||||||||||||||||||
| ( | ||||||||||||||||||||||||||||||||
| startOAuth: () => Promise<void>, | ||||||||||||||||||||||||||||||||
| pendingOAuthStartRef: React.MutableRefObject<boolean>, | ||||||||||||||||||||||||||||||||
| ) => { | ||||||||||||||||||||||||||||||||
| void startOAuth() | ||||||||||||||||||||||||||||||||
| pendingOAuthStartRef.current = false | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| startOAuth, | ||||||||||||||||||||||||||||||||
| pendingOAuthStartRef, | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| // Start OAuth flow and reset the pending flag when complete | ||||||||||||||||||||||||||||||||
| void startOAuth().finally(() => { | ||||||||||||||||||||||||||||||||
| pendingOAuthStartRef.current = false | ||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| }, [oauthStatus.state, startOAuth]) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
@@ -538,9 +531,9 @@ function OAuthStatusMessage({ | |||||||||||||||||||||||||||||||
| state: 'openai_chat_api', | ||||||||||||||||||||||||||||||||
| baseUrl: process.env.OPENAI_BASE_URL ?? '', | ||||||||||||||||||||||||||||||||
| apiKey: process.env.OPENAI_API_KEY ?? '', | ||||||||||||||||||||||||||||||||
| haikuModel: process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL ?? '', | ||||||||||||||||||||||||||||||||
| sonnetModel: process.env.ANTHROPIC_DEFAULT_SONNET_MODEL ?? '', | ||||||||||||||||||||||||||||||||
| opusModel: process.env.ANTHROPIC_DEFAULT_OPUS_MODEL ?? '', | ||||||||||||||||||||||||||||||||
| haikuModel: process.env.OPENAI_DEFAULT_HAIKU_MODEL ?? '', | ||||||||||||||||||||||||||||||||
| sonnetModel: process.env.OPENAI_DEFAULT_SONNET_MODEL ?? '', | ||||||||||||||||||||||||||||||||
| opusModel: process.env.OPENAI_DEFAULT_OPUS_MODEL ?? '', | ||||||||||||||||||||||||||||||||
|
Comment on lines
+553
to
+555
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mirror runtime fallback when prefilling OpenAI model fields. The runtime still falls back from 💡 Suggested fix- haikuModel: process.env.OPENAI_DEFAULT_HAIKU_MODEL ?? '',
- sonnetModel: process.env.OPENAI_DEFAULT_SONNET_MODEL ?? '',
- opusModel: process.env.OPENAI_DEFAULT_OPUS_MODEL ?? '',
+ haikuModel:
+ process.env.OPENAI_DEFAULT_HAIKU_MODEL ||
+ process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL ||
+ '',
+ sonnetModel:
+ process.env.OPENAI_DEFAULT_SONNET_MODEL ||
+ process.env.ANTHROPIC_DEFAULT_SONNET_MODEL ||
+ '',
+ opusModel:
+ process.env.OPENAI_DEFAULT_OPUS_MODEL ||
+ process.env.ANTHROPIC_DEFAULT_OPUS_MODEL ||
+ '',📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
| activeField: 'base_url', | ||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||
| } else if (value === 'platform') { | ||||||||||||||||||||||||||||||||
|
|
@@ -640,7 +633,7 @@ function OAuthStatusMessage({ | |||||||||||||||||||||||||||||||
| if (error) { | ||||||||||||||||||||||||||||||||
| setOAuthStatus({ | ||||||||||||||||||||||||||||||||
| state: 'error', | ||||||||||||||||||||||||||||||||
| message: `Failed to save: ${error.message}`, | ||||||||||||||||||||||||||||||||
| message: 'Failed to save settings. Please try again.', | ||||||||||||||||||||||||||||||||
| toRetry: { | ||||||||||||||||||||||||||||||||
| state: 'custom_platform', | ||||||||||||||||||||||||||||||||
| baseUrl: '', | ||||||||||||||||||||||||||||||||
|
|
@@ -654,6 +647,7 @@ function OAuthStatusMessage({ | |||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||
| for (const [k, v] of Object.entries(env)) process.env[k] = v | ||||||||||||||||||||||||||||||||
| setOAuthStatus({ state: 'success' }) | ||||||||||||||||||||||||||||||||
| // Schedule onDone after state update to avoid potential state update on unmounted component | ||||||||||||||||||||||||||||||||
| void onDone() | ||||||||||||||||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| }, [activeField, inputValue, displayValues, setOAuthStatus, onDone]) | ||||||||||||||||||||||||||||||||
|
|
@@ -826,17 +820,17 @@ function OAuthStatusMessage({ | |||||||||||||||||||||||||||||||
| const env: Record<string, string> = {} | ||||||||||||||||||||||||||||||||
| if (finalVals.base_url) env.OPENAI_BASE_URL = finalVals.base_url | ||||||||||||||||||||||||||||||||
| if (finalVals.api_key) env.OPENAI_API_KEY = finalVals.api_key | ||||||||||||||||||||||||||||||||
| if (finalVals.haiku_model) env.ANTHROPIC_DEFAULT_HAIKU_MODEL = finalVals.haiku_model | ||||||||||||||||||||||||||||||||
| if (finalVals.sonnet_model) env.ANTHROPIC_DEFAULT_SONNET_MODEL = finalVals.sonnet_model | ||||||||||||||||||||||||||||||||
| if (finalVals.opus_model) env.ANTHROPIC_DEFAULT_OPUS_MODEL = finalVals.opus_model | ||||||||||||||||||||||||||||||||
| if (finalVals.haiku_model) env.OPENAI_DEFAULT_HAIKU_MODEL = finalVals.haiku_model | ||||||||||||||||||||||||||||||||
| if (finalVals.sonnet_model) env.OPENAI_DEFAULT_SONNET_MODEL = finalVals.sonnet_model | ||||||||||||||||||||||||||||||||
| if (finalVals.opus_model) env.OPENAI_DEFAULT_OPUS_MODEL = finalVals.opus_model | ||||||||||||||||||||||||||||||||
| const { error } = updateSettingsForSource('userSettings', { | ||||||||||||||||||||||||||||||||
| modelType: 'openai' as any, | ||||||||||||||||||||||||||||||||
| env, | ||||||||||||||||||||||||||||||||
| } as any) | ||||||||||||||||||||||||||||||||
| if (error) { | ||||||||||||||||||||||||||||||||
| setOAuthStatus({ | ||||||||||||||||||||||||||||||||
| state: 'error', | ||||||||||||||||||||||||||||||||
| message: `Failed to save: ${error.message}`, | ||||||||||||||||||||||||||||||||
| message: 'Failed to save settings. Please try again.', | ||||||||||||||||||||||||||||||||
| toRetry: { | ||||||||||||||||||||||||||||||||
| state: 'openai_chat_api', | ||||||||||||||||||||||||||||||||
| baseUrl: '', | ||||||||||||||||||||||||||||||||
|
|
@@ -850,6 +844,7 @@ function OAuthStatusMessage({ | |||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||
| for (const [k, v] of Object.entries(env)) process.env[k] = v | ||||||||||||||||||||||||||||||||
| setOAuthStatus({ state: 'success' }) | ||||||||||||||||||||||||||||||||
| // Schedule onDone after state update to avoid potential state update on unmounted component | ||||||||||||||||||||||||||||||||
| void onDone() | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| }, [activeField, openaiInputValue, openaiDisplayValues, setOAuthStatus, onDone]) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.