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:
gnanam1990
2026-04-01 14:46:04 +05:30
parent 6b6407018d
commit a3d8ab0fec
8 changed files with 185 additions and 31 deletions

View File

@@ -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 ?? {}),
})

View File

@@ -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.

View File

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

View File

@@ -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 {