feat(grok): 补齐管理端模型映射设置
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 透传',
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user