From 70886d98479a28cd9d7dedc7a9cfbfd393bb6f4f Mon Sep 17 00:00:00 2001 From: YoVinchen Date: Mon, 6 Apr 2026 11:12:41 +0800 Subject: [PATCH] feat(ui): add Gemini Native provider preset and api format option - Add gemini_native to ClaudeApiFormat type and ProviderMeta.apiFormat - Add "Gemini Native" provider preset with default Google AI endpoints - Show Gemini-specific endpoint hints and full-URL mode guidance - Add gemini_native option to API format selector in ClaudeFormFields - Add i18n strings for zh/en/ja --- .../providers/forms/ClaudeFormFields.tsx | 16 +++++++++-- .../providers/forms/shared/EndpointField.tsx | 5 +++- src/config/claudeProviderPresets.ts | 28 ++++++++++++++++++- src/i18n/locales/en.json | 3 ++ src/i18n/locales/ja.json | 3 ++ src/i18n/locales/zh.json | 3 ++ src/types.ts | 14 ++++++++-- 7 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/components/providers/forms/ClaudeFormFields.tsx b/src/components/providers/forms/ClaudeFormFields.tsx index 033a0874c..3351ccedf 100644 --- a/src/components/providers/forms/ClaudeFormFields.tsx +++ b/src/components/providers/forms/ClaudeFormFields.tsx @@ -106,7 +106,7 @@ interface ClaudeFormFieldsProps { // Speed Test Endpoints speedTestEndpoints: EndpointCandidate[]; - // API Format (for third-party providers that use OpenAI Chat Completions format) + // API Format (for Claude-compatible providers that need request/response conversion) apiFormat: ClaudeApiFormat; onApiFormatChange: (format: ClaudeApiFormat) => void; @@ -418,7 +418,14 @@ export function ClaudeFormFields({ ? t("providerForm.apiHintResponses") : apiFormat === "openai_chat" ? t("providerForm.apiHintOAI") - : t("providerForm.apiHint") + : apiFormat === "gemini_native" + ? t("providerForm.apiHintGeminiNative") + : t("providerForm.apiHint") + } + fullUrlHint={ + apiFormat === "gemini_native" + ? t("providerForm.fullUrlHintGeminiNative") + : undefined } onManageClick={() => onEndpointModalToggle(true)} showFullUrlToggle={true} @@ -493,6 +500,11 @@ export function ClaudeFormFields({ defaultValue: "OpenAI Responses API (需转换)", })} + + {t("providerForm.apiFormatGeminiNative", { + defaultValue: "Gemini Native generateContent (需转换)", + })} +

diff --git a/src/components/providers/forms/shared/EndpointField.tsx b/src/components/providers/forms/shared/EndpointField.tsx index bf33afc92..4d5f53f1e 100644 --- a/src/components/providers/forms/shared/EndpointField.tsx +++ b/src/components/providers/forms/shared/EndpointField.tsx @@ -11,6 +11,7 @@ interface EndpointFieldProps { onChange: (value: string) => void; placeholder: string; hint?: string; + fullUrlHint?: string; showManageButton?: boolean; onManageClick?: () => void; manageButtonLabel?: string; @@ -26,6 +27,7 @@ export function EndpointField({ onChange, placeholder, hint, + fullUrlHint, showManageButton = true, onManageClick, manageButtonLabel, @@ -40,7 +42,8 @@ export function EndpointField({ }); const effectiveHint = showFullUrlToggle && isFullUrl - ? t("providerForm.fullUrlHint", { + ? fullUrlHint || + t("providerForm.fullUrlHint", { defaultValue: "💡 请填写完整请求 URL,并且必须开启代理后使用;代理将直接使用此 URL,不拼接路径", }) diff --git a/src/config/claudeProviderPresets.ts b/src/config/claudeProviderPresets.ts index 2ed5dcd88..b62fe74ec 100644 --- a/src/config/claudeProviderPresets.ts +++ b/src/config/claudeProviderPresets.ts @@ -49,7 +49,12 @@ export interface ProviderPreset { // - "anthropic" (默认): Anthropic Messages API 格式,直接透传 // - "openai_chat": OpenAI Chat Completions 格式,需要格式转换 // - "openai_responses": OpenAI Responses API 格式,需要格式转换 - apiFormat?: "anthropic" | "openai_chat" | "openai_responses"; + // - "gemini_native": Gemini Native generateContent API 格式,需要格式转换 + apiFormat?: + | "anthropic" + | "openai_chat" + | "openai_responses" + | "gemini_native"; // 供应商类型标识(用于特殊供应商检测) // - "github_copilot": GitHub Copilot 供应商(需要 OAuth 认证) @@ -79,6 +84,27 @@ export const providerPresets: ProviderPreset[] = [ icon: "anthropic", iconColor: "#D4915D", }, + { + name: "Gemini Native", + websiteUrl: "https://ai.google.dev/gemini-api", + apiKeyUrl: "https://aistudio.google.com/app/apikey", + apiKeyField: "ANTHROPIC_API_KEY", + settingsConfig: { + env: { + ANTHROPIC_BASE_URL: "https://generativelanguage.googleapis.com", + ANTHROPIC_API_KEY: "", + ANTHROPIC_MODEL: "gemini-2.5-pro", + ANTHROPIC_DEFAULT_HAIKU_MODEL: "gemini-2.5-flash", + ANTHROPIC_DEFAULT_SONNET_MODEL: "gemini-2.5-pro", + ANTHROPIC_DEFAULT_OPUS_MODEL: "gemini-2.5-pro", + }, + }, + category: "third_party", + apiFormat: "gemini_native", + endpointCandidates: ["https://generativelanguage.googleapis.com"], + icon: "gemini", + iconColor: "#4285F4", + }, { name: "DeepSeek", websiteUrl: "https://platform.deepseek.com", diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 09f421fce..6d232fbb5 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -746,6 +746,7 @@ "modelHint": "💡 Leave blank to use provider's default model", "apiHint": "💡 Fill in Claude API compatible service endpoint, avoid trailing slash", "apiHintOAI": "💡 Fill in OpenAI Chat Completions compatible service endpoint, avoid trailing slash", + "apiHintGeminiNative": "💡 Prefer a Gemini Native base URL such as https://generativelanguage.googleapis.com or https://generativelanguage.googleapis.com/v1beta; the proxy will append models/*:generateContent automatically", "codexApiHint": "💡 Fill in service endpoint compatible with OpenAI Response format", "fillSupplierName": "Please fill in provider name", "fillConfigContent": "Please fill in configuration content", @@ -768,9 +769,11 @@ "fullUrlEnabled": "Full URL Mode", "fullUrlDisabled": "Mark as Full URL", "fullUrlHint": "💡 Enter the full request URL. This mode requires the proxy to be enabled, and the proxy will use the URL as-is without appending a path", + "fullUrlHintGeminiNative": "💡 In Gemini Native full URL mode, two inputs are supported: 1. official/structured Gemini URLs, which will still be normalized to the requested model and streaming method; 2. opaque custom relay URLs, which will be used mostly as-is with only query parameters appended", "apiFormatAnthropic": "Anthropic Messages (Native)", "apiFormatOpenAIChat": "OpenAI Chat Completions (Requires proxy)", "apiFormatOpenAIResponses": "OpenAI Responses API (Requires proxy)", + "apiFormatGeminiNative": "Gemini Native generateContent (Requires proxy)", "authField": "Auth Field", "authFieldAuthToken": "ANTHROPIC_AUTH_TOKEN (Default)", "authFieldApiKey": "ANTHROPIC_API_KEY", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 145355ef3..cf14cb15f 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -746,6 +746,7 @@ "modelHint": "💡 空欄ならプロバイダーのデフォルトモデルを使用します", "apiHint": "💡 Claude API 互換サービスのエンドポイントを入力してください。末尾にスラッシュを付けないでください", "apiHintOAI": "💡 OpenAI Chat Completions 互換サービスのエンドポイントを入力してください。末尾にスラッシュを付けないでください", + "apiHintGeminiNative": "💡 Gemini Native では https://generativelanguage.googleapis.com または https://generativelanguage.googleapis.com/v1beta のような base URL を推奨します。プロキシが models/*:generateContent を自動補完します", "codexApiHint": "💡 OpenAI Response 互換のサービスエンドポイントを入力してください", "fillSupplierName": "プロバイダー名を入力してください", "fillConfigContent": "設定内容を入力してください", @@ -768,9 +769,11 @@ "fullUrlEnabled": "フル URL モード", "fullUrlDisabled": "フル URL として設定", "fullUrlHint": "💡 完全なリクエスト URL を入力してください。このモードはプロキシを有効にして使用する必要があり、プロキシはこの URL をそのまま使用し、パスを追加しません", + "fullUrlHintGeminiNative": "💡 Gemini Native のフル URL モードでは 2 種類の入力を扱えます。1. 公式/構造化された Gemini URL は、要求されたモデルやストリーミング方式に合わせて正規化されます。2. カスタム relay の opaque な完全 URL は、主にそのまま使用され、必要なクエリだけ追加されます", "apiFormatAnthropic": "Anthropic Messages(ネイティブ)", "apiFormatOpenAIChat": "OpenAI Chat Completions(プロキシが必要)", "apiFormatOpenAIResponses": "OpenAI Responses API(プロキシが必要)", + "apiFormatGeminiNative": "Gemini Native generateContent(プロキシが必要)", "authField": "認証フィールド", "authFieldAuthToken": "ANTHROPIC_AUTH_TOKEN(デフォルト)", "authFieldApiKey": "ANTHROPIC_API_KEY", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 75fc9232a..f6745a8d0 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -746,6 +746,7 @@ "modelHint": "💡 留空将使用供应商的默认模型", "apiHint": "💡 填写兼容 Claude API 的服务端点地址,不要以斜杠结尾", "apiHintOAI": "💡 填写兼容 OpenAI Chat Completions 的服务端点地址,不要以斜杠结尾", + "apiHintGeminiNative": "💡 建议填写 Gemini Native 的 base URL,例如 https://generativelanguage.googleapis.com 或 https://generativelanguage.googleapis.com/v1beta;代理会自动补全 models/*:generateContent", "codexApiHint": "💡 填写兼容 OpenAI Response 格式的服务端点地址", "fillSupplierName": "请填写供应商名称", "fillConfigContent": "请填写配置内容", @@ -768,9 +769,11 @@ "fullUrlEnabled": "完整 URL 模式", "fullUrlDisabled": "标记为完整 URL", "fullUrlHint": "💡 请填写完整请求 URL,并且必须开启代理后使用;代理将直接使用此 URL,不拼接路径", + "fullUrlHintGeminiNative": "💡 Gemini Native 下,完整 URL 模式同时兼容两类地址:1. 官方/标准 Gemini URL,代理会按模型和流式参数自动归一化;2. 自定义 relay 的完整 URL,代理会尽量原样使用,只补查询参数,不再强行追加 models 路径", "apiFormatAnthropic": "Anthropic Messages (原生)", "apiFormatOpenAIChat": "OpenAI Chat Completions (需开启代理)", "apiFormatOpenAIResponses": "OpenAI Responses API (需开启代理)", + "apiFormatGeminiNative": "Gemini Native generateContent (需开启代理)", "authField": "认证字段", "authFieldAuthToken": "ANTHROPIC_AUTH_TOKEN(默认)", "authFieldApiKey": "ANTHROPIC_API_KEY", diff --git a/src/types.ts b/src/types.ts index 868a05cc6..6f9f866d0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -159,7 +159,12 @@ export interface ProviderMeta { // - "anthropic": 原生 Anthropic Messages API 格式,直接透传 // - "openai_chat": OpenAI Chat Completions 格式,需要格式转换 // - "openai_responses": OpenAI Responses API 格式,需要格式转换 - apiFormat?: "anthropic" | "openai_chat" | "openai_responses"; + // - "gemini_native": Gemini Native generateContent API 格式,需要格式转换 + apiFormat?: + | "anthropic" + | "openai_chat" + | "openai_responses" + | "gemini_native"; // 通用认证绑定 authBinding?: AuthBinding; // Claude 认证字段名 @@ -181,7 +186,12 @@ export type SkillSyncMethod = "auto" | "symlink" | "copy"; // - "anthropic": 原生 Anthropic Messages API 格式,直接透传 // - "openai_chat": OpenAI Chat Completions 格式,需要格式转换 // - "openai_responses": OpenAI Responses API 格式,需要格式转换 -export type ClaudeApiFormat = "anthropic" | "openai_chat" | "openai_responses"; +// - "gemini_native": Gemini Native generateContent API 格式,需要格式转换 +export type ClaudeApiFormat = + | "anthropic" + | "openai_chat" + | "openai_responses" + | "gemini_native"; // Claude 认证字段类型 export type ClaudeApiKeyField = "ANTHROPIC_AUTH_TOKEN" | "ANTHROPIC_API_KEY";