feat: 添加gemini协议适配 (#125)

* feat: 添加gemini协议适配

* Remove unrelated local files from Gemini PR
This commit is contained in:
SaltedFish555
2026-04-06 09:55:20 +08:00
committed by GitHub
parent 27825293bb
commit 0da5ec09e8
24 changed files with 2257 additions and 38 deletions

View File

@@ -1,8 +1,18 @@
import { describe, expect, test, beforeEach, afterEach } from "bun:test";
import { getAPIProvider, isFirstPartyAnthropicBaseUrl } from "../providers";
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
let mockedModelType: "gemini" | undefined;
mock.module("../../settings/settings.js", () => ({
getInitialSettings: () =>
mockedModelType ? { modelType: mockedModelType } : {},
}));
const { getAPIProvider, isFirstPartyAnthropicBaseUrl } =
await import("../providers");
describe("getAPIProvider", () => {
const envKeys = [
"CLAUDE_CODE_USE_GEMINI",
"CLAUDE_CODE_USE_BEDROCK",
"CLAUDE_CODE_USE_VERTEX",
"CLAUDE_CODE_USE_FOUNDRY",
@@ -10,10 +20,15 @@ describe("getAPIProvider", () => {
const savedEnv: Record<string, string | undefined> = {};
beforeEach(() => {
for (const key of envKeys) savedEnv[key] = process.env[key];
mockedModelType = undefined;
for (const key of envKeys) {
savedEnv[key] = process.env[key];
delete process.env[key];
}
});
afterEach(() => {
mockedModelType = undefined;
for (const key of envKeys) {
if (savedEnv[key] !== undefined) {
process.env[key] = savedEnv[key];
@@ -24,12 +39,25 @@ describe("getAPIProvider", () => {
});
test('returns "firstParty" by default', () => {
delete process.env.CLAUDE_CODE_USE_BEDROCK;
delete process.env.CLAUDE_CODE_USE_VERTEX;
delete process.env.CLAUDE_CODE_USE_FOUNDRY;
expect(getAPIProvider()).toBe("firstParty");
});
test('returns "gemini" when modelType is gemini', () => {
mockedModelType = "gemini";
expect(getAPIProvider()).toBe("gemini");
});
test("modelType takes precedence over environment variables", () => {
mockedModelType = "gemini";
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
expect(getAPIProvider()).toBe("gemini");
});
test('returns "gemini" when CLAUDE_CODE_USE_GEMINI is set', () => {
process.env.CLAUDE_CODE_USE_GEMINI = "1";
expect(getAPIProvider()).toBe("gemini");
});
test('returns "bedrock" when CLAUDE_CODE_USE_BEDROCK is set', () => {
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
expect(getAPIProvider()).toBe("bedrock");
@@ -45,6 +73,12 @@ describe("getAPIProvider", () => {
expect(getAPIProvider()).toBe("foundry");
});
test("bedrock takes precedence over gemini", () => {
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
process.env.CLAUDE_CODE_USE_GEMINI = "1";
expect(getAPIProvider()).toBe("bedrock");
});
test("bedrock takes precedence over vertex", () => {
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
process.env.CLAUDE_CODE_USE_VERTEX = "1";

View File

@@ -12,6 +12,7 @@ export const CLAUDE_3_7_SONNET_CONFIG = {
vertex: 'claude-3-7-sonnet@20250219',
foundry: 'claude-3-7-sonnet',
openai: 'claude-3-7-sonnet-20250219',
gemini: 'claude-3-7-sonnet-20250219',
} as const satisfies ModelConfig
export const CLAUDE_3_5_V2_SONNET_CONFIG = {
@@ -20,6 +21,7 @@ export const CLAUDE_3_5_V2_SONNET_CONFIG = {
vertex: 'claude-3-5-sonnet-v2@20241022',
foundry: 'claude-3-5-sonnet',
openai: 'claude-3-5-sonnet-20241022',
gemini: 'claude-3-5-sonnet-20241022',
} as const satisfies ModelConfig
export const CLAUDE_3_5_HAIKU_CONFIG = {
@@ -28,6 +30,7 @@ export const CLAUDE_3_5_HAIKU_CONFIG = {
vertex: 'claude-3-5-haiku@20241022',
foundry: 'claude-3-5-haiku',
openai: 'claude-3-5-haiku-20241022',
gemini: 'claude-3-5-haiku-20241022',
} as const satisfies ModelConfig
export const CLAUDE_HAIKU_4_5_CONFIG = {
@@ -36,6 +39,7 @@ export const CLAUDE_HAIKU_4_5_CONFIG = {
vertex: 'claude-haiku-4-5@20251001',
foundry: 'claude-haiku-4-5',
openai: 'claude-haiku-4-5-20251001',
gemini: 'claude-haiku-4-5-20251001',
} as const satisfies ModelConfig
export const CLAUDE_SONNET_4_CONFIG = {
@@ -44,6 +48,7 @@ export const CLAUDE_SONNET_4_CONFIG = {
vertex: 'claude-sonnet-4@20250514',
foundry: 'claude-sonnet-4',
openai: 'claude-sonnet-4-20250514',
gemini: 'claude-sonnet-4-20250514',
} as const satisfies ModelConfig
export const CLAUDE_SONNET_4_5_CONFIG = {
@@ -52,6 +57,7 @@ export const CLAUDE_SONNET_4_5_CONFIG = {
vertex: 'claude-sonnet-4-5@20250929',
foundry: 'claude-sonnet-4-5',
openai: 'claude-sonnet-4-5-20250929',
gemini: 'claude-sonnet-4-5-20250929',
} as const satisfies ModelConfig
export const CLAUDE_OPUS_4_CONFIG = {
@@ -60,6 +66,7 @@ export const CLAUDE_OPUS_4_CONFIG = {
vertex: 'claude-opus-4@20250514',
foundry: 'claude-opus-4',
openai: 'claude-opus-4-20250514',
gemini: 'claude-opus-4-20250514',
} as const satisfies ModelConfig
export const CLAUDE_OPUS_4_1_CONFIG = {
@@ -68,6 +75,7 @@ export const CLAUDE_OPUS_4_1_CONFIG = {
vertex: 'claude-opus-4-1@20250805',
foundry: 'claude-opus-4-1',
openai: 'claude-opus-4-1-20250805',
gemini: 'claude-opus-4-1-20250805',
} as const satisfies ModelConfig
export const CLAUDE_OPUS_4_5_CONFIG = {
@@ -76,6 +84,7 @@ export const CLAUDE_OPUS_4_5_CONFIG = {
vertex: 'claude-opus-4-5@20251101',
foundry: 'claude-opus-4-5',
openai: 'claude-opus-4-5-20251101',
gemini: 'claude-opus-4-5-20251101',
} as const satisfies ModelConfig
export const CLAUDE_OPUS_4_6_CONFIG = {
@@ -84,6 +93,7 @@ export const CLAUDE_OPUS_4_6_CONFIG = {
vertex: 'claude-opus-4-6',
foundry: 'claude-opus-4-6',
openai: 'claude-opus-4-6',
gemini: 'claude-opus-4-6',
} as const satisfies ModelConfig
export const CLAUDE_SONNET_4_6_CONFIG = {
@@ -92,6 +102,7 @@ export const CLAUDE_SONNET_4_6_CONFIG = {
vertex: 'claude-sonnet-4-6',
foundry: 'claude-sonnet-4-6',
openai: 'claude-sonnet-4-6',
gemini: 'claude-sonnet-4-6',
} as const satisfies ModelConfig
// @[MODEL LAUNCH]: Register the new config here.

View File

@@ -2,23 +2,32 @@ import type { AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS } from
import { getInitialSettings } from '../settings/settings.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 {
// 1. Check settings.json modelType field (highest priority)
const modelType = getInitialSettings().modelType
if (modelType === 'openai') return 'openai'
if (modelType === 'gemini') return 'gemini'
// 2. Check environment variables (backward compatibility)
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_BEDROCK)
? 'bedrock'
: isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX)
? 'vertex'
: isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY)
? 'foundry'
: isEnvTruthy(process.env.CLAUDE_CODE_USE_OPENAI)
? 'openai'
: isEnvTruthy(process.env.CLAUDE_CODE_USE_GEMINI)
? 'gemini'
: 'firstParty'
}
export function getAPIProviderForStatsig(): AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS {