feat: add native Gemini provider for Google AI models
Adds Google Gemini as a first-class provider using Gemini's OpenAI-compatible endpoint, supporting gemini-2.0-flash, gemini-2.5-pro, and gemini-2.0-flash-lite across all three model tiers (opus/sonnet/haiku). - Add 'gemini' to APIProvider type with CLAUDE_CODE_USE_GEMINI env detection - Map all 11 model configs to appropriate Gemini models per tier - Route Gemini through existing OpenAI shim (generativelanguage.googleapis.com) - Support GEMINI_API_KEY and GOOGLE_API_KEY for authentication - Fix model display name to show actual Gemini model instead of Claude fallback - Add Gemini support to provider-launch, provider-bootstrap, system-check scripts - Add dev:gemini npm script for local development Bootstrap: bun run profile:init -- --provider gemini --api-key <key> Launch: bun run dev:gemini Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
"dev:profile:fast": "bun run scripts/provider-launch.ts auto --fast --bare",
|
||||
"dev:codex": "bun run scripts/provider-launch.ts codex",
|
||||
"dev:openai": "bun run scripts/provider-launch.ts openai",
|
||||
"dev:gemini": "bun run scripts/provider-launch.ts gemini",
|
||||
"dev:ollama": "bun run scripts/provider-launch.ts ollama",
|
||||
"dev:ollama:fast": "bun run scripts/provider-launch.ts ollama --fast --bare",
|
||||
"profile:init": "bun run scripts/provider-bootstrap.ts",
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
resolveCodexApiCredentials,
|
||||
} from '../src/services/api/providerConfig.js'
|
||||
|
||||
type ProviderProfile = 'openai' | 'ollama' | 'codex'
|
||||
type ProviderProfile = 'openai' | 'ollama' | 'codex' | 'gemini'
|
||||
|
||||
type ProfileFile = {
|
||||
profile: ProviderProfile
|
||||
@@ -15,6 +15,9 @@ type ProfileFile = {
|
||||
OPENAI_MODEL?: string
|
||||
OPENAI_API_KEY?: string
|
||||
CODEX_API_KEY?: string
|
||||
GEMINI_API_KEY?: string
|
||||
GEMINI_MODEL?: string
|
||||
GEMINI_BASE_URL?: string
|
||||
}
|
||||
createdAt: string
|
||||
}
|
||||
@@ -28,7 +31,7 @@ function parseArg(name: string): string | null {
|
||||
|
||||
function parseProviderArg(): ProviderProfile | 'auto' {
|
||||
const p = parseArg('--provider')?.toLowerCase()
|
||||
if (p === 'openai' || p === 'ollama' || p === 'codex') return p
|
||||
if (p === 'openai' || p === 'ollama' || p === 'codex' || p === 'gemini') return p
|
||||
return 'auto'
|
||||
}
|
||||
|
||||
@@ -69,7 +72,18 @@ async function main(): Promise<void> {
|
||||
}
|
||||
|
||||
const env: ProfileFile['env'] = {}
|
||||
if (selected === 'ollama') {
|
||||
|
||||
if (selected === 'gemini') {
|
||||
env.GEMINI_MODEL = argModel || process.env.GEMINI_MODEL || 'gemini-2.0-flash'
|
||||
const key = sanitizeApiKey(argApiKey || process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY || null)
|
||||
if (!key) {
|
||||
console.error('Gemini profile requires an API key. Use --api-key or set GEMINI_API_KEY.')
|
||||
console.error('Get a free key at: https://aistudio.google.com/apikey')
|
||||
process.exit(1)
|
||||
}
|
||||
env.GEMINI_API_KEY = key
|
||||
if (argBaseUrl) env.GEMINI_BASE_URL = argBaseUrl
|
||||
} else if (selected === 'ollama') {
|
||||
env.OPENAI_BASE_URL = argBaseUrl || 'http://localhost:11434/v1'
|
||||
env.OPENAI_MODEL = argModel || process.env.OPENAI_MODEL || 'llama3.1:8b'
|
||||
const key = sanitizeApiKey(argApiKey || process.env.OPENAI_API_KEY || null)
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
resolveCodexApiCredentials,
|
||||
} from '../src/services/api/providerConfig.js'
|
||||
|
||||
type ProviderProfile = 'openai' | 'ollama' | 'codex'
|
||||
type ProviderProfile = 'openai' | 'ollama' | 'codex' | 'gemini'
|
||||
|
||||
type ProfileFile = {
|
||||
profile: ProviderProfile
|
||||
@@ -16,6 +16,9 @@ type ProfileFile = {
|
||||
OPENAI_MODEL?: string
|
||||
OPENAI_API_KEY?: string
|
||||
CODEX_API_KEY?: string
|
||||
GEMINI_API_KEY?: string
|
||||
GEMINI_MODEL?: string
|
||||
GEMINI_BASE_URL?: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +40,7 @@ function parseLaunchOptions(argv: string[]): LaunchOptions {
|
||||
continue
|
||||
}
|
||||
|
||||
if ((lower === 'auto' || lower === 'openai' || lower === 'ollama' || lower === 'codex') && requestedProfile === 'auto') {
|
||||
if ((lower === 'auto' || lower === 'openai' || lower === 'ollama' || lower === 'codex' || lower === 'gemini') && requestedProfile === 'auto') {
|
||||
requestedProfile = lower as ProviderProfile | 'auto'
|
||||
continue
|
||||
}
|
||||
@@ -67,7 +70,7 @@ function loadPersistedProfile(): ProfileFile | null {
|
||||
if (!existsSync(path)) return null
|
||||
try {
|
||||
const parsed = JSON.parse(readFileSync(path, 'utf8')) as ProfileFile
|
||||
if (parsed.profile === 'openai' || parsed.profile === 'ollama' || parsed.profile === 'codex') {
|
||||
if (parsed.profile === 'openai' || parsed.profile === 'ollama' || parsed.profile === 'codex' || parsed.profile === 'gemini') {
|
||||
return parsed
|
||||
}
|
||||
return null
|
||||
@@ -106,6 +109,21 @@ function runCommand(command: string, env: NodeJS.ProcessEnv): Promise<number> {
|
||||
|
||||
function buildEnv(profile: ProviderProfile, persisted: ProfileFile | null): NodeJS.ProcessEnv {
|
||||
const persistedEnv = persisted?.env ?? {}
|
||||
|
||||
if (profile === 'gemini') {
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
...process.env,
|
||||
CLAUDE_CODE_USE_GEMINI: '1',
|
||||
}
|
||||
delete env.CLAUDE_CODE_USE_OPENAI
|
||||
env.GEMINI_MODEL = process.env.GEMINI_MODEL || persistedEnv.GEMINI_MODEL || 'gemini-2.0-flash'
|
||||
env.GEMINI_API_KEY = process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY || persistedEnv.GEMINI_API_KEY
|
||||
if (persistedEnv.GEMINI_BASE_URL || process.env.GEMINI_BASE_URL) {
|
||||
env.GEMINI_BASE_URL = process.env.GEMINI_BASE_URL || persistedEnv.GEMINI_BASE_URL
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
...process.env,
|
||||
CLAUDE_CODE_USE_OPENAI: '1',
|
||||
@@ -156,22 +174,26 @@ function quoteArg(arg: string): string {
|
||||
}
|
||||
|
||||
function printSummary(profile: ProviderProfile, env: NodeJS.ProcessEnv): void {
|
||||
const keySet = profile === 'codex'
|
||||
? Boolean(resolveCodexApiCredentials(env).apiKey)
|
||||
: Boolean(env.OPENAI_API_KEY)
|
||||
console.log(`Launching profile: ${profile}`)
|
||||
console.log(`OPENAI_BASE_URL=${env.OPENAI_BASE_URL}`)
|
||||
console.log(`OPENAI_MODEL=${env.OPENAI_MODEL}`)
|
||||
console.log(
|
||||
`${profile === 'codex' ? 'CODEX_API_KEY_SET' : 'OPENAI_API_KEY_SET'}=${keySet}`,
|
||||
)
|
||||
if (profile === 'gemini') {
|
||||
console.log(`GEMINI_MODEL=${env.GEMINI_MODEL}`)
|
||||
console.log(`GEMINI_API_KEY_SET=${Boolean(env.GEMINI_API_KEY)}`)
|
||||
} else if (profile === 'codex') {
|
||||
console.log(`OPENAI_BASE_URL=${env.OPENAI_BASE_URL}`)
|
||||
console.log(`OPENAI_MODEL=${env.OPENAI_MODEL}`)
|
||||
console.log(`CODEX_API_KEY_SET=${Boolean(resolveCodexApiCredentials(env).apiKey)}`)
|
||||
} else {
|
||||
console.log(`OPENAI_BASE_URL=${env.OPENAI_BASE_URL}`)
|
||||
console.log(`OPENAI_MODEL=${env.OPENAI_MODEL}`)
|
||||
console.log(`OPENAI_API_KEY_SET=${Boolean(env.OPENAI_API_KEY)}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const options = parseLaunchOptions(process.argv.slice(2))
|
||||
const requestedProfile = options.requestedProfile
|
||||
if (!requestedProfile) {
|
||||
console.error('Usage: bun run scripts/provider-launch.ts [openai|ollama|codex|auto] [--fast] [-- <cli args>]')
|
||||
console.error('Usage: bun run scripts/provider-launch.ts [openai|ollama|codex|gemini|auto] [--fast] [-- <cli args>]')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
@@ -193,6 +215,11 @@ async function main(): Promise<void> {
|
||||
applyFastFlags(env)
|
||||
}
|
||||
|
||||
if (profile === 'gemini' && !env.GEMINI_API_KEY) {
|
||||
console.error('GEMINI_API_KEY is required for gemini profile. Run: bun run profile:init -- --provider gemini --api-key <key>')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (profile === 'openai' && (!env.OPENAI_API_KEY || env.OPENAI_API_KEY === 'SUA_CHAVE')) {
|
||||
console.error('OPENAI_API_KEY is required for openai profile and cannot be SUA_CHAVE. Run: bun run profile:init -- --provider openai --api-key <key>')
|
||||
process.exit(1)
|
||||
|
||||
@@ -92,14 +92,49 @@ function isLocalBaseUrl(baseUrl: string): boolean {
|
||||
return isProviderLocalUrl(baseUrl)
|
||||
}
|
||||
|
||||
const GEMINI_DEFAULT_BASE_URL = 'https://generativelanguage.googleapis.com/v1beta/openai'
|
||||
|
||||
function currentBaseUrl(): string {
|
||||
if (isTruthy(process.env.CLAUDE_CODE_USE_GEMINI)) {
|
||||
return process.env.GEMINI_BASE_URL ?? GEMINI_DEFAULT_BASE_URL
|
||||
}
|
||||
return process.env.OPENAI_BASE_URL ?? 'https://api.openai.com/v1'
|
||||
}
|
||||
|
||||
function checkGeminiEnv(): CheckResult[] {
|
||||
const results: CheckResult[] = []
|
||||
const model = process.env.GEMINI_MODEL
|
||||
const key = process.env.GEMINI_API_KEY ?? process.env.GOOGLE_API_KEY
|
||||
const baseUrl = process.env.GEMINI_BASE_URL ?? GEMINI_DEFAULT_BASE_URL
|
||||
|
||||
results.push(pass('Provider mode', 'Google Gemini provider enabled.'))
|
||||
|
||||
if (!model) {
|
||||
results.push(pass('GEMINI_MODEL', 'Not set. Default gemini-2.0-flash will be used.'))
|
||||
} else {
|
||||
results.push(pass('GEMINI_MODEL', model))
|
||||
}
|
||||
|
||||
results.push(pass('GEMINI_BASE_URL', baseUrl))
|
||||
|
||||
if (!key) {
|
||||
results.push(fail('GEMINI_API_KEY', 'Missing. Set GEMINI_API_KEY or GOOGLE_API_KEY.'))
|
||||
} else {
|
||||
results.push(pass('GEMINI_API_KEY', 'Configured.'))
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
function checkOpenAIEnv(): CheckResult[] {
|
||||
const results: CheckResult[] = []
|
||||
const useGemini = isTruthy(process.env.CLAUDE_CODE_USE_GEMINI)
|
||||
const useOpenAI = isTruthy(process.env.CLAUDE_CODE_USE_OPENAI)
|
||||
|
||||
if (useGemini) {
|
||||
return checkGeminiEnv()
|
||||
}
|
||||
|
||||
if (!useOpenAI) {
|
||||
results.push(pass('Provider mode', 'Anthropic login flow enabled (CLAUDE_CODE_USE_OPENAI is off).'))
|
||||
return results
|
||||
@@ -160,13 +195,20 @@ function checkOpenAIEnv(): CheckResult[] {
|
||||
}
|
||||
|
||||
async function checkBaseUrlReachability(): Promise<CheckResult> {
|
||||
if (!isTruthy(process.env.CLAUDE_CODE_USE_OPENAI)) {
|
||||
const useGemini = isTruthy(process.env.CLAUDE_CODE_USE_GEMINI)
|
||||
const useOpenAI = isTruthy(process.env.CLAUDE_CODE_USE_OPENAI)
|
||||
|
||||
if (!useGemini && !useOpenAI) {
|
||||
return pass('Provider reachability', 'Skipped (OpenAI-compatible mode disabled).')
|
||||
}
|
||||
|
||||
const geminiBaseUrl = 'https://generativelanguage.googleapis.com/v1beta/openai'
|
||||
const resolvedBaseUrl = useGemini
|
||||
? (process.env.GEMINI_BASE_URL ?? geminiBaseUrl)
|
||||
: undefined
|
||||
const request = resolveProviderRequest({
|
||||
model: process.env.OPENAI_MODEL,
|
||||
baseUrl: process.env.OPENAI_BASE_URL,
|
||||
baseUrl: resolvedBaseUrl ?? process.env.OPENAI_BASE_URL,
|
||||
})
|
||||
const endpoint = request.transport === 'codex_responses'
|
||||
? `${request.baseUrl}/responses`
|
||||
@@ -203,6 +245,8 @@ async function checkBaseUrlReachability(): Promise<CheckResult> {
|
||||
store: false,
|
||||
stream: true,
|
||||
})
|
||||
} else if (useGemini && (process.env.GEMINI_API_KEY ?? process.env.GOOGLE_API_KEY)) {
|
||||
headers.Authorization = `Bearer ${process.env.GEMINI_API_KEY ?? process.env.GOOGLE_API_KEY}`
|
||||
} else if (process.env.OPENAI_API_KEY) {
|
||||
headers.Authorization = `Bearer ${process.env.OPENAI_API_KEY}`
|
||||
}
|
||||
@@ -228,7 +272,7 @@ async function checkBaseUrlReachability(): Promise<CheckResult> {
|
||||
}
|
||||
|
||||
function checkOllamaProcessorMode(): CheckResult {
|
||||
if (!isTruthy(process.env.CLAUDE_CODE_USE_OPENAI)) {
|
||||
if (!isTruthy(process.env.CLAUDE_CODE_USE_OPENAI) || isTruthy(process.env.CLAUDE_CODE_USE_GEMINI)) {
|
||||
return pass('Ollama processor mode', 'Skipped (OpenAI-compatible mode disabled).')
|
||||
}
|
||||
|
||||
@@ -267,6 +311,14 @@ function checkOllamaProcessorMode(): CheckResult {
|
||||
}
|
||||
|
||||
function serializeSafeEnvSummary(): Record<string, string | boolean> {
|
||||
if (isTruthy(process.env.CLAUDE_CODE_USE_GEMINI)) {
|
||||
return {
|
||||
CLAUDE_CODE_USE_GEMINI: true,
|
||||
GEMINI_MODEL: process.env.GEMINI_MODEL ?? '(unset, default: gemini-2.0-flash)',
|
||||
GEMINI_BASE_URL: process.env.GEMINI_BASE_URL ?? 'https://generativelanguage.googleapis.com/v1beta/openai',
|
||||
GEMINI_API_KEY_SET: Boolean(process.env.GEMINI_API_KEY ?? process.env.GOOGLE_API_KEY),
|
||||
}
|
||||
}
|
||||
const request = resolveProviderRequest({
|
||||
model: process.env.OPENAI_MODEL,
|
||||
baseUrl: process.env.OPENAI_BASE_URL,
|
||||
|
||||
@@ -741,6 +741,22 @@ export function createOpenAIShimClient(options: {
|
||||
maxRetries?: number
|
||||
timeout?: number
|
||||
}): unknown {
|
||||
// When Gemini provider is active, map Gemini env vars to OpenAI-compatible ones
|
||||
// so the existing providerConfig.ts infrastructure picks them up correctly.
|
||||
if (
|
||||
process.env.CLAUDE_CODE_USE_GEMINI === '1' ||
|
||||
process.env.CLAUDE_CODE_USE_GEMINI === 'true'
|
||||
) {
|
||||
process.env.OPENAI_BASE_URL ??=
|
||||
process.env.GEMINI_BASE_URL ??
|
||||
'https://generativelanguage.googleapis.com/v1beta/openai'
|
||||
process.env.OPENAI_API_KEY ??=
|
||||
process.env.GEMINI_API_KEY ?? process.env.GOOGLE_API_KEY ?? ''
|
||||
if (process.env.GEMINI_MODEL && !process.env.OPENAI_MODEL) {
|
||||
process.env.OPENAI_MODEL = process.env.GEMINI_MODEL
|
||||
}
|
||||
}
|
||||
|
||||
const beta = new OpenAIShimBeta({
|
||||
...(options.defaultHeaders ?? {}),
|
||||
})
|
||||
|
||||
@@ -14,6 +14,17 @@ export const OPENAI_MODEL_DEFAULTS = {
|
||||
haiku: 'gpt-4o-mini', // fast & cheap
|
||||
} as const
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Gemini model mappings
|
||||
// Maps Claude model tiers to Google Gemini equivalents.
|
||||
// Override with GEMINI_MODEL env var.
|
||||
// ---------------------------------------------------------------------------
|
||||
export const GEMINI_MODEL_DEFAULTS = {
|
||||
opus: 'gemini-2.5-pro-preview-03-25', // most capable
|
||||
sonnet: 'gemini-2.0-flash', // balanced
|
||||
haiku: 'gemini-2.0-flash-lite', // fast & cheap
|
||||
} as const
|
||||
|
||||
// @[MODEL LAUNCH]: Add a new CLAUDE_*_CONFIG constant here. Double check the correct model strings
|
||||
// here since the pattern may change.
|
||||
|
||||
@@ -23,6 +34,7 @@ export const CLAUDE_3_7_SONNET_CONFIG = {
|
||||
vertex: 'claude-3-7-sonnet@20250219',
|
||||
foundry: 'claude-3-7-sonnet',
|
||||
openai: 'gpt-4o-mini',
|
||||
gemini: 'gemini-2.0-flash',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_3_5_V2_SONNET_CONFIG = {
|
||||
@@ -31,6 +43,7 @@ export const CLAUDE_3_5_V2_SONNET_CONFIG = {
|
||||
vertex: 'claude-3-5-sonnet-v2@20241022',
|
||||
foundry: 'claude-3-5-sonnet',
|
||||
openai: 'gpt-4o-mini',
|
||||
gemini: 'gemini-2.0-flash',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_3_5_HAIKU_CONFIG = {
|
||||
@@ -39,6 +52,7 @@ export const CLAUDE_3_5_HAIKU_CONFIG = {
|
||||
vertex: 'claude-3-5-haiku@20241022',
|
||||
foundry: 'claude-3-5-haiku',
|
||||
openai: 'gpt-4o-mini',
|
||||
gemini: 'gemini-2.0-flash-lite',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_HAIKU_4_5_CONFIG = {
|
||||
@@ -47,6 +61,7 @@ export const CLAUDE_HAIKU_4_5_CONFIG = {
|
||||
vertex: 'claude-haiku-4-5@20251001',
|
||||
foundry: 'claude-haiku-4-5',
|
||||
openai: 'gpt-4o-mini',
|
||||
gemini: 'gemini-2.0-flash-lite',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_SONNET_4_CONFIG = {
|
||||
@@ -55,6 +70,7 @@ export const CLAUDE_SONNET_4_CONFIG = {
|
||||
vertex: 'claude-sonnet-4@20250514',
|
||||
foundry: 'claude-sonnet-4',
|
||||
openai: 'gpt-4o-mini',
|
||||
gemini: 'gemini-2.0-flash',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_SONNET_4_5_CONFIG = {
|
||||
@@ -63,6 +79,7 @@ export const CLAUDE_SONNET_4_5_CONFIG = {
|
||||
vertex: 'claude-sonnet-4-5@20250929',
|
||||
foundry: 'claude-sonnet-4-5',
|
||||
openai: 'gpt-4o',
|
||||
gemini: 'gemini-2.0-flash',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_OPUS_4_CONFIG = {
|
||||
@@ -71,6 +88,7 @@ export const CLAUDE_OPUS_4_CONFIG = {
|
||||
vertex: 'claude-opus-4@20250514',
|
||||
foundry: 'claude-opus-4',
|
||||
openai: 'gpt-4o',
|
||||
gemini: 'gemini-2.5-pro-preview-03-25',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_OPUS_4_1_CONFIG = {
|
||||
@@ -79,6 +97,7 @@ export const CLAUDE_OPUS_4_1_CONFIG = {
|
||||
vertex: 'claude-opus-4-1@20250805',
|
||||
foundry: 'claude-opus-4-1',
|
||||
openai: 'gpt-4o',
|
||||
gemini: 'gemini-2.5-pro-preview-03-25',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_OPUS_4_5_CONFIG = {
|
||||
@@ -87,6 +106,7 @@ export const CLAUDE_OPUS_4_5_CONFIG = {
|
||||
vertex: 'claude-opus-4-5@20251101',
|
||||
foundry: 'claude-opus-4-5',
|
||||
openai: 'gpt-4o',
|
||||
gemini: 'gemini-2.5-pro-preview-03-25',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_OPUS_4_6_CONFIG = {
|
||||
@@ -95,6 +115,7 @@ export const CLAUDE_OPUS_4_6_CONFIG = {
|
||||
vertex: 'claude-opus-4-6',
|
||||
foundry: 'claude-opus-4-6',
|
||||
openai: 'gpt-4o',
|
||||
gemini: 'gemini-2.5-pro-preview-03-25',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
export const CLAUDE_SONNET_4_6_CONFIG = {
|
||||
@@ -103,6 +124,7 @@ export const CLAUDE_SONNET_4_6_CONFIG = {
|
||||
vertex: 'claude-sonnet-4-6',
|
||||
foundry: 'claude-sonnet-4-6',
|
||||
openai: 'gpt-4o',
|
||||
gemini: 'gemini-2.0-flash',
|
||||
} as const satisfies ModelConfig
|
||||
|
||||
// @[MODEL LAUNCH]: Register the new config here.
|
||||
|
||||
@@ -35,6 +35,10 @@ export type ModelSetting = ModelName | ModelAlias | null
|
||||
|
||||
export function getSmallFastModel(): ModelName {
|
||||
if (process.env.ANTHROPIC_SMALL_FAST_MODEL) return process.env.ANTHROPIC_SMALL_FAST_MODEL
|
||||
// For Gemini provider, use a fast model
|
||||
if (getAPIProvider() === 'gemini') {
|
||||
return process.env.GEMINI_MODEL || 'gemini-2.0-flash-lite'
|
||||
}
|
||||
// For OpenAI provider, use OPENAI_MODEL or a sensible default
|
||||
if (getAPIProvider() === 'openai') {
|
||||
return process.env.OPENAI_MODEL || 'gpt-4o-mini'
|
||||
@@ -71,7 +75,7 @@ export function getUserSpecifiedModelSetting(): ModelSetting | undefined {
|
||||
specifiedModel = modelOverride
|
||||
} else {
|
||||
const settings = getSettings_DEPRECATED() || {}
|
||||
specifiedModel = process.env.ANTHROPIC_MODEL || process.env.OPENAI_MODEL || settings.model || undefined
|
||||
specifiedModel = process.env.ANTHROPIC_MODEL || process.env.GEMINI_MODEL || process.env.OPENAI_MODEL || settings.model || undefined
|
||||
}
|
||||
|
||||
// Ignore the user-specified model if it's not in the availableModels allowlist.
|
||||
@@ -111,6 +115,10 @@ export function getDefaultOpusModel(): ModelName {
|
||||
if (process.env.ANTHROPIC_DEFAULT_OPUS_MODEL) {
|
||||
return process.env.ANTHROPIC_DEFAULT_OPUS_MODEL
|
||||
}
|
||||
// Gemini provider
|
||||
if (getAPIProvider() === 'gemini') {
|
||||
return process.env.GEMINI_MODEL || 'gemini-2.5-pro-preview-03-25'
|
||||
}
|
||||
// OpenAI provider: use user-specified model or default
|
||||
if (getAPIProvider() === 'openai') {
|
||||
return process.env.OPENAI_MODEL || 'gpt-4o'
|
||||
@@ -129,6 +137,10 @@ export function getDefaultSonnetModel(): ModelName {
|
||||
if (process.env.ANTHROPIC_DEFAULT_SONNET_MODEL) {
|
||||
return process.env.ANTHROPIC_DEFAULT_SONNET_MODEL
|
||||
}
|
||||
// Gemini provider
|
||||
if (getAPIProvider() === 'gemini') {
|
||||
return process.env.GEMINI_MODEL || 'gemini-2.0-flash'
|
||||
}
|
||||
// OpenAI provider
|
||||
if (getAPIProvider() === 'openai') {
|
||||
return process.env.OPENAI_MODEL || 'gpt-4o'
|
||||
@@ -145,6 +157,10 @@ export function getDefaultHaikuModel(): ModelName {
|
||||
if (process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL) {
|
||||
return process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL
|
||||
}
|
||||
// Gemini provider
|
||||
if (getAPIProvider() === 'gemini') {
|
||||
return process.env.GEMINI_MODEL || 'gemini-2.0-flash-lite'
|
||||
}
|
||||
// OpenAI provider
|
||||
if (getAPIProvider() === 'openai') {
|
||||
return process.env.OPENAI_MODEL || 'gpt-4o-mini'
|
||||
@@ -193,6 +209,10 @@ export function getRuntimeMainLoopModel(params: {
|
||||
* @returns The default model setting to use
|
||||
*/
|
||||
export function getDefaultMainLoopModelSetting(): ModelName | ModelAlias {
|
||||
// Gemini provider: always use the configured Gemini model
|
||||
if (getAPIProvider() === 'gemini') {
|
||||
return process.env.GEMINI_MODEL || 'gemini-2.0-flash'
|
||||
}
|
||||
// OpenAI provider: always use the configured OpenAI model
|
||||
if (getAPIProvider() === 'openai') {
|
||||
return process.env.OPENAI_MODEL || 'gpt-4o'
|
||||
@@ -381,8 +401,8 @@ export function renderModelSetting(setting: ModelName | ModelAlias): string {
|
||||
* if the model is not recognized as a public model.
|
||||
*/
|
||||
export function getPublicModelDisplayName(model: ModelName): string | null {
|
||||
// For OpenAI provider, show the actual model name (e.g. 'gpt-4o') not a Claude alias
|
||||
if (getAPIProvider() === 'openai') {
|
||||
// For OpenAI/Gemini providers, show the actual model name not a Claude alias
|
||||
if (getAPIProvider() === 'openai' || getAPIProvider() === 'gemini') {
|
||||
return null
|
||||
}
|
||||
switch (model) {
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import type { AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS } from '../../services/analytics/index.js'
|
||||
import { isEnvTruthy } from '../envUtils.js'
|
||||
|
||||
export type APIProvider = 'firstParty' | 'bedrock' | 'vertex' | 'foundry' | 'openai'
|
||||
export type APIProvider = 'firstParty' | 'bedrock' | 'vertex' | 'foundry' | 'openai' | 'gemini'
|
||||
|
||||
export function getAPIProvider(): APIProvider {
|
||||
return isEnvTruthy(process.env.CLAUDE_CODE_USE_OPENAI)
|
||||
? 'openai'
|
||||
: isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK)
|
||||
? 'bedrock'
|
||||
: isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX)
|
||||
? 'vertex'
|
||||
: isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY)
|
||||
? 'foundry'
|
||||
: 'firstParty'
|
||||
return isEnvTruthy(process.env.CLAUDE_CODE_USE_GEMINI)
|
||||
? 'gemini'
|
||||
: isEnvTruthy(process.env.CLAUDE_CODE_USE_OPENAI)
|
||||
? 'openai'
|
||||
: isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK)
|
||||
? 'bedrock'
|
||||
: isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX)
|
||||
? 'vertex'
|
||||
: isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY)
|
||||
? 'foundry'
|
||||
: 'firstParty'
|
||||
}
|
||||
|
||||
export function getAPIProviderForStatsig(): AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS {
|
||||
|
||||
Reference in New Issue
Block a user