| package service |
|
|
| import ( |
| "fmt" |
| "net/http" |
| "net/http/httptest" |
| "strings" |
| "testing" |
| "time" |
|
|
| relaycommon "github.com/QuantumNous/new-api/relay/common" |
| "github.com/QuantumNous/new-api/setting/operation_setting" |
| "github.com/gin-gonic/gin" |
| "github.com/stretchr/testify/require" |
| ) |
|
|
| func buildChannelAffinityTemplateContextForTest(meta channelAffinityMeta) *gin.Context { |
| rec := httptest.NewRecorder() |
| ctx, _ := gin.CreateTestContext(rec) |
| setChannelAffinityContext(ctx, meta) |
| return ctx |
| } |
|
|
| func TestApplyChannelAffinityOverrideTemplate_NoTemplate(t *testing.T) { |
| ctx := buildChannelAffinityTemplateContextForTest(channelAffinityMeta{ |
| RuleName: "rule-no-template", |
| }) |
| base := map[string]interface{}{ |
| "temperature": 0.7, |
| } |
|
|
| merged, applied := ApplyChannelAffinityOverrideTemplate(ctx, base) |
| require.False(t, applied) |
| require.Equal(t, base, merged) |
| } |
|
|
| func TestApplyChannelAffinityOverrideTemplate_MergeTemplate(t *testing.T) { |
| ctx := buildChannelAffinityTemplateContextForTest(channelAffinityMeta{ |
| RuleName: "rule-with-template", |
| ParamTemplate: map[string]interface{}{ |
| "temperature": 0.2, |
| "top_p": 0.95, |
| }, |
| UsingGroup: "default", |
| ModelName: "gpt-4.1", |
| RequestPath: "/v1/responses", |
| KeySourceType: "gjson", |
| KeySourcePath: "prompt_cache_key", |
| KeyHint: "abcd...wxyz", |
| KeyFingerprint: "abcd1234", |
| }) |
| base := map[string]interface{}{ |
| "temperature": 0.7, |
| "max_tokens": 2000, |
| } |
|
|
| merged, applied := ApplyChannelAffinityOverrideTemplate(ctx, base) |
| require.True(t, applied) |
| require.Equal(t, 0.7, merged["temperature"]) |
| require.Equal(t, 0.95, merged["top_p"]) |
| require.Equal(t, 2000, merged["max_tokens"]) |
| require.Equal(t, 0.7, base["temperature"]) |
|
|
| anyInfo, ok := ctx.Get(ginKeyChannelAffinityLogInfo) |
| require.True(t, ok) |
| info, ok := anyInfo.(map[string]interface{}) |
| require.True(t, ok) |
| overrideInfoAny, ok := info["override_template"] |
| require.True(t, ok) |
| overrideInfo, ok := overrideInfoAny.(map[string]interface{}) |
| require.True(t, ok) |
| require.Equal(t, true, overrideInfo["applied"]) |
| require.Equal(t, "rule-with-template", overrideInfo["rule_name"]) |
| require.EqualValues(t, 2, overrideInfo["param_override_keys"]) |
| } |
|
|
| func TestApplyChannelAffinityOverrideTemplate_MergeOperations(t *testing.T) { |
| ctx := buildChannelAffinityTemplateContextForTest(channelAffinityMeta{ |
| RuleName: "rule-with-ops-template", |
| ParamTemplate: map[string]interface{}{ |
| "operations": []map[string]interface{}{ |
| { |
| "mode": "pass_headers", |
| "value": []string{"Originator"}, |
| }, |
| }, |
| }, |
| }) |
| base := map[string]interface{}{ |
| "temperature": 0.7, |
| "operations": []map[string]interface{}{ |
| { |
| "path": "model", |
| "mode": "trim_prefix", |
| "value": "openai/", |
| }, |
| }, |
| } |
|
|
| merged, applied := ApplyChannelAffinityOverrideTemplate(ctx, base) |
| require.True(t, applied) |
| require.Equal(t, 0.7, merged["temperature"]) |
|
|
| opsAny, ok := merged["operations"] |
| require.True(t, ok) |
| ops, ok := opsAny.([]interface{}) |
| require.True(t, ok) |
| require.Len(t, ops, 2) |
|
|
| firstOp, ok := ops[0].(map[string]interface{}) |
| require.True(t, ok) |
| require.Equal(t, "pass_headers", firstOp["mode"]) |
|
|
| secondOp, ok := ops[1].(map[string]interface{}) |
| require.True(t, ok) |
| require.Equal(t, "trim_prefix", secondOp["mode"]) |
| } |
|
|
| func TestChannelAffinityHitCodexTemplatePassHeadersEffective(t *testing.T) { |
| gin.SetMode(gin.TestMode) |
|
|
| setting := operation_setting.GetChannelAffinitySetting() |
| require.NotNil(t, setting) |
|
|
| var codexRule *operation_setting.ChannelAffinityRule |
| for i := range setting.Rules { |
| rule := &setting.Rules[i] |
| if strings.EqualFold(strings.TrimSpace(rule.Name), "codex cli trace") { |
| codexRule = rule |
| break |
| } |
| } |
| require.NotNil(t, codexRule) |
|
|
| affinityValue := fmt.Sprintf("pc-hit-%d", time.Now().UnixNano()) |
| cacheKeySuffix := buildChannelAffinityCacheKeySuffix(*codexRule, "default", affinityValue) |
|
|
| cache := getChannelAffinityCache() |
| require.NoError(t, cache.SetWithTTL(cacheKeySuffix, 9527, time.Minute)) |
| t.Cleanup(func() { |
| _, _ = cache.DeleteMany([]string{cacheKeySuffix}) |
| }) |
|
|
| rec := httptest.NewRecorder() |
| ctx, _ := gin.CreateTestContext(rec) |
| ctx.Request = httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(fmt.Sprintf(`{"prompt_cache_key":"%s"}`, affinityValue))) |
| ctx.Request.Header.Set("Content-Type", "application/json") |
|
|
| channelID, found := GetPreferredChannelByAffinity(ctx, "gpt-5", "default") |
| require.True(t, found) |
| require.Equal(t, 9527, channelID) |
|
|
| baseOverride := map[string]interface{}{ |
| "temperature": 0.2, |
| } |
| mergedOverride, applied := ApplyChannelAffinityOverrideTemplate(ctx, baseOverride) |
| require.True(t, applied) |
| require.Equal(t, 0.2, mergedOverride["temperature"]) |
|
|
| info := &relaycommon.RelayInfo{ |
| RequestHeaders: map[string]string{ |
| "Originator": "Codex CLI", |
| "Session_id": "sess-123", |
| "User-Agent": "codex-cli-test", |
| }, |
| ChannelMeta: &relaycommon.ChannelMeta{ |
| ParamOverride: mergedOverride, |
| HeadersOverride: map[string]interface{}{ |
| "X-Static": "legacy-static", |
| }, |
| }, |
| } |
|
|
| _, err := relaycommon.ApplyParamOverrideWithRelayInfo([]byte(`{"model":"gpt-5"}`), info) |
| require.NoError(t, err) |
| require.True(t, info.UseRuntimeHeadersOverride) |
|
|
| require.Equal(t, "legacy-static", info.RuntimeHeadersOverride["x-static"]) |
| require.Equal(t, "Codex CLI", info.RuntimeHeadersOverride["originator"]) |
| require.Equal(t, "sess-123", info.RuntimeHeadersOverride["session_id"]) |
| require.Equal(t, "codex-cli-test", info.RuntimeHeadersOverride["user-agent"]) |
|
|
| _, exists := info.RuntimeHeadersOverride["x-codex-beta-features"] |
| require.False(t, exists) |
| _, exists = info.RuntimeHeadersOverride["x-codex-turn-metadata"] |
| require.False(t, exists) |
| } |
|
|