feat(grok): 补齐管理端模型映射设置

This commit is contained in:
IanShaw027
2026-08-07 14:22:15 +08:00
parent ff4bb3bd2c
commit eb2e73df3d
5 changed files with 90 additions and 0 deletions
+48
View File
@@ -5066,6 +5066,48 @@
</p>
</div>
<div class="space-y-5 p-6">
<div class="grid gap-5 border-b border-gray-100 pb-5 dark:border-dark-700 md:grid-cols-[minmax(0,1fr)_auto] md:items-end">
<div>
<label
for="grok-default-text-model"
class="text-sm font-medium text-gray-700 dark:text-gray-300"
>
{{ t("admin.settings.gatewayForwarding.grokDefaultTextModel") }}
</label>
<input
id="grok-default-text-model"
v-model.trim="form.grok_default_text_model"
type="text"
class="input mt-2 w-full"
list="grok-default-text-model-options"
data-testid="grok-default-text-model"
placeholder="grok-4.5"
/>
<datalist id="grok-default-text-model-options">
<option value="grok-4.5" />
<option value="grok-4.1-fast" />
<option value="grok-4" />
</datalist>
<p class="mt-1.5 text-xs text-gray-500 dark:text-gray-400">
{{ t("admin.settings.gatewayForwarding.grokDefaultTextModelHint") }}
</p>
</div>
<div class="flex items-center justify-between gap-5 md:min-w-72">
<div>
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">
{{ t("admin.settings.gatewayForwarding.grokCrossClientMap") }}
</label>
<p class="mt-0.5 max-w-sm text-xs text-gray-500 dark:text-gray-400">
{{ t("admin.settings.gatewayForwarding.grokCrossClientMapHint") }}
</p>
</div>
<Toggle
v-model="form.grok_cross_client_model_map_enabled"
data-testid="grok-cross-client-model-map-toggle"
/>
</div>
</div>
<!-- Fingerprint Unification -->
<div class="flex items-center justify-between">
<div>
@@ -9397,6 +9439,8 @@ const form = reactive<SettingsForm>({
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,
@@ -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<string, unknown>;
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();