From eb2e73df3df09a2edc77c1fce2d0018e6160b8d7 Mon Sep 17 00:00:00 2001 From: IanShaw027 Date: Fri, 7 Aug 2026 14:22:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(grok):=20=E8=A1=A5=E9=BD=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E7=AB=AF=E6=A8=A1=E5=9E=8B=E6=98=A0=E5=B0=84=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/admin/settings.ts | 4 ++ .../src/i18n/locales/en/admin/settings.ts | 4 ++ .../src/i18n/locales/zh/admin/settings.ts | 4 ++ frontend/src/views/admin/SettingsView.vue | 48 +++++++++++++++++++ .../admin/__tests__/SettingsView.spec.ts | 30 ++++++++++++ 5 files changed, 90 insertions(+) diff --git a/frontend/src/api/admin/settings.ts b/frontend/src/api/admin/settings.ts index c32e0a5d6..d825aac26 100644 --- a/frontend/src/api/admin/settings.ts +++ b/frontend/src/api/admin/settings.ts @@ -556,6 +556,8 @@ export interface SystemSettings { fallback_model_openai: string; fallback_model_gemini: string; fallback_model_antigravity: string; + grok_default_text_model: string; + grok_cross_client_model_map_enabled: boolean; // Identity patch configuration (Claude -> Gemini) enable_identity_patch: boolean; @@ -872,6 +874,8 @@ export interface UpdateSettingsRequest { fallback_model_openai?: string; fallback_model_gemini?: string; fallback_model_antigravity?: string; + grok_default_text_model?: string; + grok_cross_client_model_map_enabled?: boolean; enable_identity_patch?: boolean; identity_patch_prompt?: string; ops_monitoring_enabled?: boolean; diff --git a/frontend/src/i18n/locales/en/admin/settings.ts b/frontend/src/i18n/locales/en/admin/settings.ts index 60630aaf3..fb00d9319 100644 --- a/frontend/src/i18n/locales/en/admin/settings.ts +++ b/frontend/src/i18n/locales/en/admin/settings.ts @@ -436,6 +436,10 @@ export default { gatewayForwarding: { title: 'Request Forwarding', description: 'Control how requests are forwarded to upstream OAuth accounts', + grokDefaultTextModel: 'Default Grok text model', + grokDefaultTextModelHint: 'Used for empty model values and, only when the switch is enabled, requests from other client model namespaces. Custom Grok model IDs are accepted.', + grokCrossClientMap: 'Map other clients to Grok', + grokCrossClientMapHint: 'Disabled by default. When enabled, GPT, Codex, o-series, and Claude model IDs are routed to the default Grok text model above.', fingerprintUnification: 'Fingerprint Unification', fingerprintUnificationHint: 'Unify X-Stainless-* headers across users sharing the same OAuth account. Disabling passes through each client\'s original headers.', metadataPassthrough: 'Metadata Passthrough', diff --git a/frontend/src/i18n/locales/zh/admin/settings.ts b/frontend/src/i18n/locales/zh/admin/settings.ts index 952c7b605..a23f744aa 100644 --- a/frontend/src/i18n/locales/zh/admin/settings.ts +++ b/frontend/src/i18n/locales/zh/admin/settings.ts @@ -429,6 +429,10 @@ export default { gatewayForwarding: { title: '请求转发行为', description: '控制请求转发到上游 OAuth 账号时的行为', + grokDefaultTextModel: '默认 Grok 文本模型', + grokDefaultTextModelHint: '用于空模型值;仅在右侧开关开启时也用于其他客户端模型命名空间。允许填写自定义 Grok 模型 ID。', + grokCrossClientMap: '映射其他客户端模型到 Grok', + grokCrossClientMapHint: '默认关闭。开启后,GPT、Codex、o 系列和 Claude 模型 ID 会路由到左侧默认 Grok 文本模型。', fingerprintUnification: '指纹统一化', fingerprintUnificationHint: '统一共享同一 OAuth 账号的用户的 X-Stainless-* 请求头。关闭后透传客户端原始请求头。', metadataPassthrough: 'Metadata 透传', diff --git a/frontend/src/views/admin/SettingsView.vue b/frontend/src/views/admin/SettingsView.vue index 9904b0376..80f0f53fc 100644 --- a/frontend/src/views/admin/SettingsView.vue +++ b/frontend/src/views/admin/SettingsView.vue @@ -5066,6 +5066,48 @@

+
+
+ + + + +

+ {{ t("admin.settings.gatewayForwarding.grokDefaultTextModelHint") }} +

+
+
+
+ +

+ {{ t("admin.settings.gatewayForwarding.grokCrossClientMapHint") }} +

+
+ +
+
+
@@ -9397,6 +9439,8 @@ const form = reactive({ fallback_model_openai: "gpt-4o", fallback_model_gemini: "gemini-2.5-pro", fallback_model_antigravity: "gemini-2.5-pro", + grok_default_text_model: "grok-4.5", + grok_cross_client_model_map_enabled: false, // Identity patch (Claude -> Gemini) enable_identity_patch: true, identity_patch_prompt: "", @@ -10972,6 +11016,10 @@ async function saveSettings() { fallback_model_openai: form.fallback_model_openai, fallback_model_gemini: form.fallback_model_gemini, fallback_model_antigravity: form.fallback_model_antigravity, + grok_default_text_model: + form.grok_default_text_model.trim() || "grok-4.5", + grok_cross_client_model_map_enabled: + form.grok_cross_client_model_map_enabled, enable_identity_patch: form.enable_identity_patch, identity_patch_prompt: form.identity_patch_prompt, min_claude_code_version: form.min_claude_code_version, diff --git a/frontend/src/views/admin/__tests__/SettingsView.spec.ts b/frontend/src/views/admin/__tests__/SettingsView.spec.ts index edcd879a9..75c36bac1 100644 --- a/frontend/src/views/admin/__tests__/SettingsView.spec.ts +++ b/frontend/src/views/admin/__tests__/SettingsView.spec.ts @@ -444,6 +444,8 @@ const baseSettingsResponse = { fallback_model_openai: "", fallback_model_gemini: "", fallback_model_antigravity: "", + grok_default_text_model: "grok-4.5", + grok_cross_client_model_map_enabled: false, enable_identity_patch: false, identity_patch_prompt: "", ops_monitoring_enabled: false, @@ -1265,6 +1267,34 @@ describe("admin SettingsView payment visible method controls", () => { expect(showSuccess).toHaveBeenCalledWith("上游倍率自动探测设置已保存"); }); + it("loads and saves configurable Grok cross-client model mapping", async () => { + getSettings.mockResolvedValueOnce({ + ...baseSettingsResponse, + grok_default_text_model: "grok-4.1-fast", + grok_cross_client_model_map_enabled: true, + }); + const wrapper = mountView(); + + await flushPromises(); + await openGatewayTab(wrapper); + + const modelInput = wrapper.get('[data-testid="grok-default-text-model"]'); + const mappingToggle = wrapper.get( + '[data-testid="grok-cross-client-model-map-toggle"]', + ); + expect((modelInput.element as HTMLInputElement).value).toBe("grok-4.1-fast"); + expect((mappingToggle.element as HTMLInputElement).checked).toBe(true); + + await modelInput.setValue("grok-custom-text"); + await mappingToggle.setValue(false); + await wrapper.find("form").trigger("submit.prevent"); + await flushPromises(); + + const payload = updateSettings.mock.calls.at(-1)?.[0] as Record; + expect(payload.grok_default_text_model).toBe("grok-custom-text"); + expect(payload.grok_cross_client_model_map_enabled).toBe(false); + }); + it("loads fail-safe-off Ollama Cloud usage refresh settings and saves an explicit opt-in", async () => { const wrapper = mountView();