mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-07 13:08:03 +08:00
fix(frontend): clarify OpenAI Fast/Flex policy rules
This commit is contained in:
@@ -27,4 +27,30 @@ describe('OpenAI Fast/Flex policy locale keys', () => {
|
||||
removeUser: 'Remove user'
|
||||
})
|
||||
})
|
||||
|
||||
it('describes target and other-model actions without whitelist terminology', () => {
|
||||
expect(zh.admin.settings.openaiFastPolicy).toMatchObject({
|
||||
tierAll: '全部 tier 值',
|
||||
modelWhitelist: '目标模型',
|
||||
fallbackAction: '其他模型处理方式',
|
||||
summaryTargetModels: '目标模型',
|
||||
summaryOtherModels: '其他模型'
|
||||
})
|
||||
expect(zh.admin.settings.openaiFastPolicy.modelWhitelistHint).toContain(
|
||||
'留空时“处理方式”应用于全部模型'
|
||||
)
|
||||
expect(zh.admin.settings.openaiFastPolicy.modelWhitelistHint).not.toContain('白名单')
|
||||
|
||||
expect(en.admin.settings.openaiFastPolicy).toMatchObject({
|
||||
tierAll: 'All tier values',
|
||||
modelWhitelist: 'Target models',
|
||||
fallbackAction: 'Other models action',
|
||||
summaryTargetModels: 'Target models',
|
||||
summaryOtherModels: 'Other models'
|
||||
})
|
||||
expect(en.admin.settings.openaiFastPolicy.modelWhitelistHint).toContain(
|
||||
'Leave empty to apply Action to all models'
|
||||
)
|
||||
expect(en.admin.settings.openaiFastPolicy.modelWhitelistHint).not.toContain('whitelist')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1173,7 +1173,7 @@ export default {
|
||||
addRule: 'Add rule',
|
||||
saveHint: 'Saved together with system settings (click the global Save button at the bottom of the page).',
|
||||
serviceTier: 'service_tier match',
|
||||
tierAll: 'All tiers',
|
||||
tierAll: 'All tier values',
|
||||
tierPriority: 'priority (fast)',
|
||||
tierFlex: 'flex',
|
||||
action: 'Action',
|
||||
@@ -1196,13 +1196,22 @@ export default {
|
||||
errorMessage: 'Error message',
|
||||
errorMessagePlaceholder: 'Custom error message when blocked',
|
||||
errorMessageHint: 'Leave empty for the default message.',
|
||||
modelWhitelist: 'Model whitelist',
|
||||
modelWhitelistHint: 'Leave empty to apply to all models. Supports exact match and wildcard prefix (e.g., gpt-5.5*).',
|
||||
modelPatternPlaceholder: 'e.g., gpt-5.5 or gpt-5.5*',
|
||||
addModelPattern: 'Add model pattern',
|
||||
fallbackAction: 'Fallback action',
|
||||
fallbackActionHint: 'Action for models not matching the whitelist.',
|
||||
fallbackErrorMessagePlaceholder: 'Custom error message when non-whitelisted models are blocked'
|
||||
modelWhitelist: 'Target models',
|
||||
modelWhitelistHint: 'Models in this list use Action; models outside it use Other models action. Leave empty to apply Action to all models. Supports exact matches and wildcard prefixes (e.g., gpt-5.6*).',
|
||||
modelPatternPlaceholder: 'e.g., gpt-5.6-sol or gpt-5.6*',
|
||||
addModelPattern: 'Add target model',
|
||||
fallbackAction: 'Other models action',
|
||||
fallbackActionHint: 'Applies only to models outside the target list.',
|
||||
fallbackErrorMessagePlaceholder: 'Custom error message when other models are blocked',
|
||||
summaryTargetModels: 'Target models',
|
||||
summaryAllModels: 'All models',
|
||||
summaryOtherModels: 'Other models',
|
||||
summaryAction: {
|
||||
pass: 'Pass',
|
||||
filter: 'Filter',
|
||||
block: 'Block',
|
||||
force_priority: 'Force priority'
|
||||
}
|
||||
},
|
||||
wechatConnect: {
|
||||
title: 'WeChat Connect',
|
||||
|
||||
@@ -1167,7 +1167,7 @@ export default {
|
||||
addRule: '新增规则',
|
||||
saveHint: '保存时随系统设置一起提交(点击页面底部「保存」按钮)。',
|
||||
serviceTier: 'service_tier 匹配',
|
||||
tierAll: '全部 tier',
|
||||
tierAll: '全部 tier 值',
|
||||
tierPriority: 'priority(fast)',
|
||||
tierFlex: 'flex',
|
||||
action: '处理方式',
|
||||
@@ -1190,13 +1190,22 @@ export default {
|
||||
errorMessage: '错误消息',
|
||||
errorMessagePlaceholder: '拦截时返回的自定义错误消息',
|
||||
errorMessageHint: '留空则使用默认错误消息。',
|
||||
modelWhitelist: '模型白名单',
|
||||
modelWhitelistHint: '留空表示对所有模型生效;支持精确匹配与通配符(如 gpt-5.5*)。',
|
||||
modelPatternPlaceholder: '例如: gpt-5.5 或 gpt-5.5*',
|
||||
addModelPattern: '添加模型规则',
|
||||
fallbackAction: '未匹配模型处理方式',
|
||||
fallbackActionHint: '当请求模型不在白名单中时的处理方式。',
|
||||
fallbackErrorMessagePlaceholder: '未匹配模型被拦截时返回的自定义错误消息'
|
||||
modelWhitelist: '目标模型',
|
||||
modelWhitelistHint: '列表内模型使用“处理方式”,列表外模型使用“其他模型处理方式”。留空时“处理方式”应用于全部模型。支持精确匹配与前缀通配符(如 gpt-5.6*)。',
|
||||
modelPatternPlaceholder: '例如: gpt-5.6-sol 或 gpt-5.6*',
|
||||
addModelPattern: '添加目标模型',
|
||||
fallbackAction: '其他模型处理方式',
|
||||
fallbackActionHint: '仅应用于目标列表之外的模型。',
|
||||
fallbackErrorMessagePlaceholder: '其他模型被拦截时返回的自定义错误消息',
|
||||
summaryTargetModels: '目标模型',
|
||||
summaryAllModels: '全部模型',
|
||||
summaryOtherModels: '其他模型',
|
||||
summaryAction: {
|
||||
pass: '透传',
|
||||
filter: '过滤',
|
||||
block: '拦截',
|
||||
force_priority: '强制 priority'
|
||||
}
|
||||
},
|
||||
wechatConnect: {
|
||||
title: '微信登录',
|
||||
|
||||
@@ -1128,6 +1128,47 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mb-4 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-gray-500 dark:text-gray-400"
|
||||
:data-testid="`openai-fast-policy-summary-${ruleIndex}`"
|
||||
>
|
||||
<span class="font-medium text-gray-700 dark:text-gray-300">
|
||||
{{
|
||||
t(
|
||||
hasOpenAIFastPolicyTargetModels(rule)
|
||||
? "admin.settings.openaiFastPolicy.summaryTargetModels"
|
||||
: "admin.settings.openaiFastPolicy.summaryAllModels",
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
<span aria-hidden="true">→</span>
|
||||
<span
|
||||
class="inline-flex items-center rounded bg-primary-50 px-2 py-0.5 font-medium text-primary-700 dark:bg-primary-900/30 dark:text-primary-300"
|
||||
>
|
||||
{{ openaiFastPolicyActionSummary(rule.action) }}
|
||||
</span>
|
||||
<template v-if="hasOpenAIFastPolicyTargetModels(rule)">
|
||||
<span aria-hidden="true">·</span>
|
||||
<span class="font-medium text-gray-700 dark:text-gray-300">
|
||||
{{
|
||||
t(
|
||||
"admin.settings.openaiFastPolicy.summaryOtherModels",
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
<span aria-hidden="true">→</span>
|
||||
<span
|
||||
class="inline-flex items-center rounded bg-gray-100 px-2 py-0.5 font-medium text-gray-700 dark:bg-dark-600 dark:text-gray-300"
|
||||
>
|
||||
{{
|
||||
openaiFastPolicyActionSummary(
|
||||
rule.fallback_action || "pass",
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-3">
|
||||
<!-- Service Tier -->
|
||||
<div>
|
||||
@@ -1227,14 +1268,23 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Model Whitelist -->
|
||||
<div class="mt-3">
|
||||
<!-- Target Models -->
|
||||
<div
|
||||
class="mt-3"
|
||||
role="group"
|
||||
:aria-labelledby="`openai-fast-policy-models-label-${ruleIndex}`"
|
||||
:aria-describedby="`openai-fast-policy-models-hint-${ruleIndex}`"
|
||||
>
|
||||
<label
|
||||
:id="`openai-fast-policy-models-label-${ruleIndex}`"
|
||||
class="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400"
|
||||
>
|
||||
{{ t("admin.settings.openaiFastPolicy.modelWhitelist") }}
|
||||
</label>
|
||||
<p class="mb-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
<p
|
||||
:id="`openai-fast-policy-models-hint-${ruleIndex}`"
|
||||
class="mb-2 text-xs text-gray-400 dark:text-gray-500"
|
||||
>
|
||||
{{
|
||||
t("admin.settings.openaiFastPolicy.modelWhitelistHint")
|
||||
}}
|
||||
@@ -1298,11 +1348,9 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Fallback Action (only when model_whitelist is non-empty) -->
|
||||
<!-- Other Models Action (only when target models are non-empty) -->
|
||||
<div
|
||||
v-if="
|
||||
rule.model_whitelist && rule.model_whitelist.length > 0
|
||||
"
|
||||
v-if="hasOpenAIFastPolicyTargetModels(rule)"
|
||||
class="mt-3"
|
||||
>
|
||||
<label
|
||||
@@ -11975,6 +12023,16 @@ const openaiFastPolicyActionOptions = computed(() => [
|
||||
{ value: "block", label: t("admin.settings.openaiFastPolicy.actionBlock") },
|
||||
]);
|
||||
|
||||
function openaiFastPolicyActionSummary(
|
||||
action: OpenAIFastPolicyRule["action"],
|
||||
) {
|
||||
return t(`admin.settings.openaiFastPolicy.summaryAction.${action}`);
|
||||
}
|
||||
|
||||
function hasOpenAIFastPolicyTargetModels(rule: OpenAIFastPolicyRule) {
|
||||
return Boolean(rule.model_whitelist?.some((pattern) => pattern.trim() !== ""));
|
||||
}
|
||||
|
||||
const openaiFastPolicyScopeOptions = computed(() => [
|
||||
{ value: "all", label: t("admin.settings.openaiFastPolicy.scopeAll") },
|
||||
{ value: "oauth", label: t("admin.settings.openaiFastPolicy.scopeOAuth") },
|
||||
|
||||
@@ -229,6 +229,11 @@ vi.mock("vue-i18n", async () => {
|
||||
"admin.settings.upstreamBillingProbe.intervalHint": "范围 5–1440 分钟。",
|
||||
"admin.settings.upstreamBillingProbe.saved": "上游倍率自动探测设置已保存",
|
||||
"admin.settings.upstreamBillingProbe.saveFailed": "保存上游倍率自动探测设置失败",
|
||||
"admin.settings.openaiFastPolicy.summaryTargetModels": "目标模型",
|
||||
"admin.settings.openaiFastPolicy.summaryAllModels": "全部模型",
|
||||
"admin.settings.openaiFastPolicy.summaryOtherModels": "其他模型",
|
||||
"admin.settings.openaiFastPolicy.summaryAction.filter": "过滤",
|
||||
"admin.settings.openaiFastPolicy.summaryAction.pass": "透传",
|
||||
"admin.settings.security.passkeyDeploymentHint":
|
||||
"请由服务器运维在部署配置中将 webauthn.enabled 设为 true,填写 webauthn.rp_id(仅域名)与 webauthn.rp_origins(完整 HTTPS 来源),然后重启服务。",
|
||||
"admin.settings.site.uploadImage": "上传图片",
|
||||
@@ -1261,6 +1266,43 @@ describe("admin SettingsView payment visible method controls", () => {
|
||||
expect(wrapper.text()).not.toContain("OpenAI 高级调度器");
|
||||
});
|
||||
|
||||
it("summarizes target and other-model actions, then switches to all models", async () => {
|
||||
getSettings.mockResolvedValueOnce({
|
||||
...baseSettingsResponse,
|
||||
openai_fast_policy_settings: {
|
||||
rules: [
|
||||
{
|
||||
service_tier: "all",
|
||||
action: "filter",
|
||||
scope: "all",
|
||||
model_whitelist: ["gpt-5.6-sol"],
|
||||
fallback_action: "pass",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const wrapper = mountView();
|
||||
|
||||
await flushPromises();
|
||||
await openGatewayTab(wrapper);
|
||||
|
||||
const summary = wrapper.get('[data-testid="openai-fast-policy-summary-0"]');
|
||||
expect(summary.text()).toContain("目标模型");
|
||||
expect(summary.text()).toContain("过滤");
|
||||
expect(summary.text()).toContain("其他模型");
|
||||
expect(summary.text()).toContain("透传");
|
||||
|
||||
await wrapper
|
||||
.get(
|
||||
'[role="group"][aria-labelledby="openai-fast-policy-models-label-0"] input[type="text"]',
|
||||
)
|
||||
.setValue("");
|
||||
expect(summary.text()).toContain("全部模型");
|
||||
expect(summary.text()).toContain("过滤");
|
||||
expect(summary.text()).not.toContain("其他模型");
|
||||
expect(summary.text()).not.toContain("透传");
|
||||
});
|
||||
|
||||
it("loads and saves upstream billing probe settings from the gateway tab", async () => {
|
||||
getUpstreamBillingProbeSettings.mockResolvedValueOnce({
|
||||
enabled: false,
|
||||
|
||||
Reference in New Issue
Block a user