mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-09-24 16:05:44 +08:00
add force priority fast policy action
This commit is contained in:
@@ -179,6 +179,26 @@ func TestApplyOpenAIFastPolicyToBody_ExplicitFilterRemovesField(t *testing.T) {
|
||||
require.NotContains(t, string(updated), `"service_tier"`)
|
||||
}
|
||||
|
||||
func TestApplyOpenAIFastPolicyToBody_ForcePriorityRewritesKnownTier(t *testing.T) {
|
||||
settings := &OpenAIFastPolicySettings{
|
||||
Rules: []OpenAIFastPolicyRule{{
|
||||
ServiceTier: OpenAIFastTierAny,
|
||||
Action: OpenAIFastPolicyActionForcePriority,
|
||||
Scope: BetaPolicyScopeAll,
|
||||
}},
|
||||
}
|
||||
svc := newOpenAIGatewayServiceWithSettings(t, settings)
|
||||
account := &Account{Platform: PlatformOpenAI, Type: AccountTypeAPIKey}
|
||||
|
||||
for _, tier := range []string{"flex", "auto", "default", "scale", "fast", "priority"} {
|
||||
body := []byte(`{"model":"gpt-5.5","service_tier":"` + tier + `"}`)
|
||||
updated, err := svc.applyOpenAIFastPolicyToBody(context.Background(), account, "gpt-5.5", body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, OpenAIFastTierPriority, gjson.GetBytes(updated, "service_tier").String(),
|
||||
"tier %q should be forced to priority", tier)
|
||||
}
|
||||
}
|
||||
|
||||
// TestApplyOpenAIFastPolicyToBody_OfficialTiersBypassDefaultRule 验证默认配置
|
||||
// 下客户端显式发送的 OpenAI 官方合法 tier 能透传到上游而不被静默剥离。
|
||||
func TestApplyOpenAIFastPolicyToBody_OfficialTiersBypassDefaultRule(t *testing.T) {
|
||||
@@ -293,7 +313,7 @@ func TestSetOpenAIFastPolicySettings_Validation(t *testing.T) {
|
||||
err = svc.SetOpenAIFastPolicySettings(context.Background(), &OpenAIFastPolicySettings{
|
||||
Rules: []OpenAIFastPolicyRule{{
|
||||
ServiceTier: OpenAIFastTierPriority,
|
||||
Action: BetaPolicyActionFilter,
|
||||
Action: OpenAIFastPolicyActionForcePriority,
|
||||
Scope: BetaPolicyScopeAll,
|
||||
}},
|
||||
})
|
||||
@@ -303,4 +323,5 @@ func TestSetOpenAIFastPolicySettings_Validation(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, got.Rules, 1)
|
||||
require.Equal(t, OpenAIFastTierPriority, got.Rules[0].ServiceTier)
|
||||
require.Equal(t, OpenAIFastPolicyActionForcePriority, got.Rules[0].Action)
|
||||
}
|
||||
|
||||
@@ -67,6 +67,27 @@ func TestWSResponseCreate_ExplicitFilterStripsServiceTier(t *testing.T) {
|
||||
require.NotContains(t, string(updated), `"service_tier"`)
|
||||
}
|
||||
|
||||
func TestWSResponseCreate_ForcePriorityRewritesKnownTier(t *testing.T) {
|
||||
settings := &OpenAIFastPolicySettings{
|
||||
Rules: []OpenAIFastPolicyRule{{
|
||||
ServiceTier: OpenAIFastTierAny,
|
||||
Action: OpenAIFastPolicyActionForcePriority,
|
||||
Scope: BetaPolicyScopeAll,
|
||||
}},
|
||||
}
|
||||
svc := newOpenAIGatewayServiceWithSettings(t, settings)
|
||||
account := &Account{Platform: PlatformOpenAI, Type: AccountTypeAPIKey}
|
||||
|
||||
for _, tier := range []string{"flex", "auto", "default", "scale", "fast", "priority"} {
|
||||
frame := []byte(`{"type":"response.create","model":"gpt-5.5","service_tier":"` + tier + `"}`)
|
||||
updated, blocked, err := svc.applyOpenAIFastPolicyToWSResponseCreate(context.Background(), account, "gpt-5.5", frame)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, blocked)
|
||||
require.Equal(t, OpenAIFastTierPriority, gjson.GetBytes(updated, "service_tier").String(),
|
||||
"tier %q should be forced to priority", tier)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWSResponseCreate_FlexPassThrough(t *testing.T) {
|
||||
svc := newOpenAIGatewayServiceWithSettings(t, DefaultOpenAIFastPolicySettings())
|
||||
account := &Account{Platform: PlatformOpenAI, Type: AccountTypeAPIKey}
|
||||
|
||||
@@ -2973,6 +2973,10 @@ func (s *OpenAIGatewayService) Forward(ctx context.Context, c *gin.Context, acco
|
||||
return nil, blocked
|
||||
case BetaPolicyActionFilter:
|
||||
markPatchDelete("service_tier")
|
||||
case OpenAIFastPolicyActionForcePriority:
|
||||
if rawTier != OpenAIFastTierPriority {
|
||||
markPatchSet("service_tier", OpenAIFastTierPriority)
|
||||
}
|
||||
default:
|
||||
if normTier != rawTier {
|
||||
markPatchSet("service_tier", normTier)
|
||||
@@ -7365,8 +7369,8 @@ func openAIFastPolicySettingsFromContext(ctx context.Context) *OpenAIFastPolicyS
|
||||
// applyOpenAIFastPolicyToBody applies the OpenAI fast policy to a raw request
|
||||
// body. When action=filter it removes the service_tier field; when
|
||||
// action=block it returns (body, *OpenAIFastBlockedError). On pass it
|
||||
// normalizes the service_tier value (e.g. client alias "fast" → "priority"),
|
||||
// rewriting the body so the upstream receives a slug it recognizes.
|
||||
// normalizes the service_tier value (e.g. client alias "fast" → "priority").
|
||||
// action=force_priority rewrites any matched known tier to "priority".
|
||||
//
|
||||
// Rationale for normalize-on-pass: chat-completions / messages 入口在调用本
|
||||
// 函数之前已经通过 normalizeResponsesBodyServiceTier 把 service_tier 归一化
|
||||
@@ -7399,6 +7403,12 @@ func (s *OpenAIGatewayService) applyOpenAIFastPolicyToBody(ctx context.Context,
|
||||
return body, fmt.Errorf("strip service_tier from body: %w", err)
|
||||
}
|
||||
return trimmed, nil
|
||||
case OpenAIFastPolicyActionForcePriority:
|
||||
updated, err := sjson.SetBytes(body, "service_tier", OpenAIFastTierPriority)
|
||||
if err != nil {
|
||||
return body, fmt.Errorf("force service_tier priority on body: %w", err)
|
||||
}
|
||||
return updated, nil
|
||||
default:
|
||||
// pass:把别名(如 "fast")写回为规范值("priority")。
|
||||
if normTier == rawTier {
|
||||
@@ -7435,6 +7445,7 @@ func writeOpenAIFastPolicyBlockedResponse(c *gin.Context, err *OpenAIFastBlocked
|
||||
//
|
||||
// - pass: keeps service_tier, normalizing aliases such as "fast" to "priority"
|
||||
// - filter: returns a copy with top-level service_tier removed
|
||||
// - force_priority: keeps service_tier and rewrites it to "priority"
|
||||
// - block: returns (frame, *OpenAIFastBlockedError)
|
||||
//
|
||||
// Only frames whose "type" field strictly equals "response.create" are
|
||||
@@ -7496,6 +7507,12 @@ func (s *OpenAIGatewayService) applyOpenAIFastPolicyToWSResponseCreate(
|
||||
return frame, nil, fmt.Errorf("strip service_tier from ws frame: %w", err)
|
||||
}
|
||||
return trimmed, nil, nil
|
||||
case OpenAIFastPolicyActionForcePriority:
|
||||
updated, err := sjson.SetBytes(frame, "service_tier", OpenAIFastTierPriority)
|
||||
if err != nil {
|
||||
return frame, nil, fmt.Errorf("force service_tier priority in ws frame: %w", err)
|
||||
}
|
||||
return updated, nil, nil
|
||||
default:
|
||||
if normTier == rawTier {
|
||||
return frame, nil, nil
|
||||
|
||||
@@ -5336,6 +5336,7 @@ func (s *SettingService) SetOpenAIFastPolicySettings(ctx context.Context, settin
|
||||
|
||||
validActions := map[string]bool{
|
||||
BetaPolicyActionPass: true, BetaPolicyActionFilter: true, BetaPolicyActionBlock: true,
|
||||
OpenAIFastPolicyActionForcePriority: true,
|
||||
}
|
||||
validScopes := map[string]bool{
|
||||
BetaPolicyScopeAll: true, BetaPolicyScopeOAuth: true, BetaPolicyScopeAPIKey: true, BetaPolicyScopeBedrock: true,
|
||||
|
||||
@@ -575,12 +575,16 @@ const (
|
||||
OpenAIFastTierAny = "all" // 匹配任意已识别的 service_tier
|
||||
OpenAIFastTierPriority = "priority" // 仅匹配 fast(priority)
|
||||
OpenAIFastTierFlex = "flex" // 仅匹配 flex
|
||||
|
||||
// OpenAIFastPolicyActionForcePriority 会保留 service_tier 字段并强制写成
|
||||
// priority,用于把 flex/auto/default/scale 等已识别 tier 收敛为 fast。
|
||||
OpenAIFastPolicyActionForcePriority = "force_priority"
|
||||
)
|
||||
|
||||
// OpenAIFastPolicyRule 单条 OpenAI fast/flex 策略规则
|
||||
type OpenAIFastPolicyRule struct {
|
||||
ServiceTier string `json:"service_tier"` // "priority" | "flex" | "auto" | "default" | "scale" | "all"
|
||||
Action string `json:"action"` // "pass" | "filter" | "block"
|
||||
Action string `json:"action"` // "pass" | "filter" | "block" | "force_priority"
|
||||
Scope string `json:"scope"` // "all" | "oauth" | "apikey" | "bedrock"
|
||||
ErrorMessage string `json:"error_message,omitempty"` // 自定义错误消息 (action=block 时生效)
|
||||
ModelWhitelist []string `json:"model_whitelist,omitempty"` // 模型匹配模式列表(为空=对所有模型生效)
|
||||
|
||||
@@ -1273,11 +1273,11 @@ export async function updateRectifierSettings(
|
||||
*/
|
||||
export interface OpenAIFastPolicyRule {
|
||||
service_tier: "all" | "priority" | "flex";
|
||||
action: "pass" | "filter" | "block";
|
||||
action: "pass" | "filter" | "block" | "force_priority";
|
||||
scope: "all" | "oauth" | "apikey" | "bedrock";
|
||||
error_message?: string;
|
||||
model_whitelist?: string[];
|
||||
fallback_action?: "pass" | "filter" | "block";
|
||||
fallback_action?: "pass" | "filter" | "block" | "force_priority";
|
||||
fallback_error_message?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -6686,6 +6686,7 @@ export default {
|
||||
action: 'Action',
|
||||
actionPass: 'Pass (keep service_tier)',
|
||||
actionFilter: 'Filter (remove service_tier)',
|
||||
actionForcePriority: 'Force priority (fast)',
|
||||
actionBlock: 'Block (reject request)',
|
||||
scope: 'Scope',
|
||||
scopeAll: 'All accounts',
|
||||
|
||||
@@ -6840,6 +6840,7 @@ export default {
|
||||
action: '处理方式',
|
||||
actionPass: '透传(保留 service_tier)',
|
||||
actionFilter: '过滤(移除 service_tier)',
|
||||
actionForcePriority: '强制设置 priority(fast)',
|
||||
actionBlock: '拦截(拒绝请求)',
|
||||
scope: '生效范围',
|
||||
scopeAll: '全部账号',
|
||||
|
||||
@@ -1158,7 +1158,11 @@
|
||||
<Select
|
||||
:modelValue="rule.action"
|
||||
@update:modelValue="
|
||||
rule.action = $event as 'pass' | 'filter' | 'block'
|
||||
rule.action = $event as
|
||||
| 'pass'
|
||||
| 'filter'
|
||||
| 'block'
|
||||
| 'force_priority'
|
||||
"
|
||||
:options="openaiFastPolicyActionOptions"
|
||||
/>
|
||||
@@ -1297,6 +1301,7 @@
|
||||
| 'pass'
|
||||
| 'filter'
|
||||
| 'block'
|
||||
| 'force_priority'
|
||||
"
|
||||
:options="openaiFastPolicyActionOptions"
|
||||
/>
|
||||
@@ -10115,6 +10120,10 @@ const openaiFastPolicyTierOptions = computed(() => [
|
||||
const openaiFastPolicyActionOptions = computed(() => [
|
||||
{ value: "pass", label: t("admin.settings.openaiFastPolicy.actionPass") },
|
||||
{ value: "filter", label: t("admin.settings.openaiFastPolicy.actionFilter") },
|
||||
{
|
||||
value: "force_priority",
|
||||
label: t("admin.settings.openaiFastPolicy.actionForcePriority"),
|
||||
},
|
||||
{ value: "block", label: t("admin.settings.openaiFastPolicy.actionBlock") },
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user