feat: publish sanitized custom.97 source

This commit is contained in:
cneicy committed 2026-10-27 20:43:39 +08:00
1 parent 8e6ed0317b
commit 86c0276156
1022 files changed
+69903 -19207

No files matched your search

+137 -10
View File
@@ -12,12 +12,14 @@ import (
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/constant"
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/relay/channel"
"github.com/QuantumNous/new-api/relay/channel/claude"
"github.com/QuantumNous/new-api/relay/channel/gemini"
"github.com/QuantumNous/new-api/relay/channel/openai"
relaycommon "github.com/QuantumNous/new-api/relay/common"
relayconstant "github.com/QuantumNous/new-api/relay/constant"
"github.com/QuantumNous/new-api/relay/helper"
"github.com/QuantumNous/new-api/relaykit/dto"
"github.com/QuantumNous/new-api/relaykit/types"
"github.com/QuantumNous/new-api/service"
@@ -242,7 +244,14 @@ func TestResponseModelComparisonAndLog(t *testing.T) {
{name: "mapped case differs", models: []string{"MAPPED"}, returned: "MAPPED"},
{name: "mapped prefix", models: []string{"mapped-2026-09-01"}, returned: "mapped-2026-09-01"},
{name: "reverse prefix still warns", models: []string{"request"}, returned: "request", mismatch: true},
{name: "substring still warns", models: []string{"other-requested"}, returned: "other-requested", mismatch: true},
{name: "suffix", models: []string{"other-requested"}, returned: "other-requested"},
{name: "provider path", models: []string{"vendor/requested"}, returned: "vendor/requested"},
{name: "provider path case differs", models: []string{"vendor/REQUESTED"}, returned: "vendor/REQUESTED"},
{name: "provider path with dated mapped model", models: []string{"vendor/mapped-2026-09-01"}, returned: "vendor/mapped-2026-09-01", mismatch: true},
{name: "nested provider path", models: []string{"accounts/vendor/models/requested"}, returned: "accounts/vendor/models/requested"},
{name: "provider path reverse prefix still warns", models: []string{"vendor/request"}, returned: "vendor/request", mismatch: true},
{name: "provider path other model still warns", models: []string{"vendor/other"}, returned: "vendor/other", mismatch: true},
{name: "provider path only still warns", models: []string{"vendor/"}, returned: "vendor/", mismatch: true},
{name: "compatible difference survives matching frames", models: []string{"mapped", "Requested", "", "requested"}, returned: "Requested"},
{name: "warning supersedes compatible difference", models: []string{"Requested", "other"}, returned: "other", mismatch: true},
{name: "compatible difference cannot erase warning", models: []string{"other", "Requested"}, returned: "other", mismatch: true},
@@ -268,8 +277,10 @@ func TestResponseModelComparisonAndLog(t *testing.T) {
}
require.NotNil(t, stored.ResponseModel)
assert.Equal(t, &relaycommon.ResponseModel{
RequestedModel: "requested", UpstreamModel: "mapped", ReturnedModel: tc.returned, Mismatch: tc.mismatch,
RequestedModel: "requested", UpstreamModel: "mapped", ReturnedModel: tc.returned,
}, stored.ResponseModel)
assert.NotContains(t, other.JSONString(), "mismatch")
assert.Equal(t, tc.mismatch, stored.ResponseModel.Mismatch())
assert.Equal(t, "mapped", info.UpstreamModelName)
})
}
@@ -321,7 +332,33 @@ func TestResponseModelEmptyExpectedNamesDoNotMatchEveryPrefix(t *testing.T) {
}
info.ObserveResponseModel("other")
require.NotNil(t, info.ResponseModel)
assert.True(t, info.ResponseModel.Mismatch)
assert.True(t, info.ResponseModel.Mismatch())
})
}
}
func TestResponseModelExpectedProviderPath(t *testing.T) {
for _, tc := range []struct {
name string
requested string
upstream string
returned string
mismatch bool
}{
{name: "requested carries provider path", requested: "vendor/requested", returned: "requested", mismatch: true},
{name: "same provider path on both sides", requested: "vendor/requested", returned: "vendor/requested-2026-09-01"},
{name: "provider path alone does not match", requested: "vendor/requested", returned: "vendor", mismatch: true},
{name: "different model behind same provider still warns", requested: "vendor/requested", returned: "vendor/other", mismatch: true},
} {
t.Run(tc.name, func(t *testing.T) {
info := &relaycommon.RelayInfo{
OriginModelName: tc.requested,
ChannelMeta: &relaycommon.ChannelMeta{UpstreamModelName: tc.upstream},
}
info.ObserveResponseModel(tc.returned)
require.NotNil(t, info.ResponseModel)
assert.Equal(t, tc.returned, info.ResponseModel.ReturnedModel)
assert.Equal(t, tc.mismatch, info.ResponseModel.Mismatch())
})
}
}
@@ -332,11 +369,11 @@ func TestResponseModelRetryResetsObservation(t *testing.T) {
info := &relaycommon.RelayInfo{OriginModelName: "requested"}
info.InitChannelMeta(c)
info.ObserveResponseModel("other")
require.True(t, info.ResponseModel.Mismatch)
require.True(t, info.ResponseModel.Mismatch())
info.InitChannelMeta(c)
assert.Nil(t, info.ResponseModel)
info.ObserveResponseModel("requested")
assert.False(t, info.ResponseModel.Mismatch)
assert.False(t, info.ResponseModel.Mismatch())
}
func TestResponseModelHandlersCaptureBeforeConversion(t *testing.T) {
@@ -397,7 +434,8 @@ func TestResponseModelHandlersCaptureBeforeConversion(t *testing.T) {
require.NotNil(t, usage)
assert.Equal(t, 5, usage.TotalTokens)
require.NotNil(t, info.ResponseModel)
assert.Equal(t, &relaycommon.ResponseModel{RequestedModel: "requested", UpstreamModel: "mapped", ReturnedModel: "returned", Mismatch: true}, info.ResponseModel)
assert.Equal(t, &relaycommon.ResponseModel{RequestedModel: "requested", UpstreamModel: "mapped", ReturnedModel: "returned"}, info.ResponseModel)
assert.True(t, info.ResponseModel.Mismatch())
other := service.GenerateTextOtherInfo(c, info, 1, 1, 1, 0, 0, 0, 1)
assert.Equal(t, *info.ResponseModel, other.Snapshot()["response_model"])
})
@@ -409,11 +447,11 @@ func TestResponseModelSharedResponsesAccumulator(t *testing.T) {
// terminal must not erase the model declared before an interrupted stream.
info := &relaycommon.RelayInfo{OriginModelName: "requested", ChannelMeta: &relaycommon.ChannelMeta{UpstreamModelName: "mapped"}}
accumulator := service.NewResponsesUsageAccumulator(info)
accumulator.Observe(&dto.ResponsesStreamResponse{Type: "response.created", Response: &dto.OpenAIResponsesResponse{Model: "returned"}})
accumulator.Observe(&dto.ResponsesStreamResponse{Type: "response.failed", Response: &dto.OpenAIResponsesResponse{Usage: &dto.Usage{InputTokens: 2, OutputTokens: 3}}})
accumulator.Observe(&dto.ResponsesStreamResponse{Type: "response.created", Response: &dto.OpenAIResponsesResponse{Model: "returned"}}, nil)
accumulator.Observe(&dto.ResponsesStreamResponse{Type: "response.failed", Response: &dto.OpenAIResponsesResponse{Usage: &dto.Usage{InputTokens: 2, OutputTokens: 3}}}, nil)
assert.Equal(t, 5, accumulator.Finish().TotalTokens)
require.NotNil(t, info.ResponseModel)
assert.True(t, info.ResponseModel.Mismatch)
assert.True(t, info.ResponseModel.Mismatch())
assert.Equal(t, "returned", info.ResponseModel.ReturnedModel)
}
@@ -440,8 +478,97 @@ func TestAstraDetectionCanBeDisabledIndependently(t *testing.T) {
require.False(t, shouldEnableAstraResponseGuard("/v1/responses", info))
} else {
require.NotNil(t, info.ResponseModel)
require.True(t, info.ResponseModel.Mismatch)
require.True(t, info.ResponseModel.Mismatch())
require.True(t, shouldEnableAstraResponseGuard("/v1/responses", info))
}
}
}
func TestUpstreamTimingResponsesGuardAndScope(t *testing.T) {
service.InitHttpClient()
previous := constant.StreamingTimeout
constant.StreamingTimeout = 30
t.Cleanup(func() { constant.StreamingTimeout = previous })
for _, tc := range []struct {
name, group, model string
guard bool
}{
{"guard accepted", "gpt-s-unstable", "gpt-6-astra", true},
{"guard rejected", "gpt-s-unstable", "gpt-5.6-luna", true},
{"without guard", "gpt-s-unstable", "gpt-6-astra", false},
{"other group", "gpt-v-pro", "gpt-6-astra", true},
} {
t.Run(tc.name, func(t *testing.T) {
payload := ": keepalive\n\ndata: {\"type\":\"response.in_progress\",\"response\":{}}\n\n" +
"data: {\"type\":\"response.created\",\"response\":{\"model\":\"" + tc.model + "\"}}\n\n"
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = io.Copy(io.Discard, r.Body)
w.Header().Set("Content-Type", "text/event-stream")
_, _ = io.WriteString(w, payload)
}))
defer server.Close()
writer := httptest.NewRecorder()
c, _ := gin.CreateTestContext(writer)
c.Request = httptest.NewRequest("POST", "/v1/responses", strings.NewReader("{}"))
info := &relaycommon.RelayInfo{
StartTime: time.Now(), UsingGroup: tc.group, AstraAutoSwitch: tc.guard,
ChannelMeta: &relaycommon.ChannelMeta{ChannelId: 1}, IsStream: true,
}
if tc.guard {
info.ResponseGuard = &relaycommon.ResponseModelGuard{}
}
req, err := http.NewRequest("POST", server.URL, strings.NewReader("{}"))
require.NoError(t, err)
resp, err := channel.DoRequest(c, req, info)
assert.False(t, c.Writer.Written(), "model confirmation must not deliver buffered data")
if tc.guard && tc.model != "gpt-6-astra" {
require.Error(t, err)
require.Nil(t, resp)
snapshot := info.UpstreamTiming.Snapshot()
events := snapshot["events_ms"].(map[string]float64)
assert.Contains(t, events, "first_sse_data")
assert.NotContains(t, events, "model_confirmed")
assert.NotContains(t, events, "first_downstream_flush")
return
}
require.NoError(t, err)
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
assert.Equal(t, payload, string(body), "instrumentation preserves every upstream byte")
resp.Body = io.NopCloser(strings.NewReader(string(body)))
helper.StreamScannerHandler(c, resp, info, func(data string, sr *helper.StreamResult) {
assert.NoError(t, helper.ResponseChunkData(c, dto.ResponsesStreamResponse{Type: "response.created"}, data))
})
assert.Contains(t, writer.Body.String(), tc.model)
other := model.NewLogOther()
service.AppendRelayLogAdminInfo(c, info, other)
logData := other.Snapshot()
assert.NotContains(t, logData, "upstream_timing")
admin := logData["admin_info"].(map[string]any)
if tc.group != "gpt-s-unstable" {
assert.Nil(t, info.UpstreamTiming)
assert.NotContains(t, admin, "upstream_timing")
return
}
require.NotNil(t, info.UpstreamTiming)
snapshot := admin["upstream_timing"].(map[string]any)
events := snapshot["events_ms"].(map[string]float64)
ordered := []string{"upstream_start", "get_connection", "connection_ready", "request_sent", "response_first_byte", "response_headers", "first_sse_data"}
if tc.guard {
ordered = append(ordered, "model_confirmed")
}
ordered = append(ordered, "first_downstream_flush")
for i, key := range ordered {
require.Contains(t, events, key)
if i > 0 {
assert.GreaterOrEqual(t, events[key], events[ordered[i-1]], key)
}
}
assert.EqualValues(t, 2, snapshot["request_body_bytes"])
assert.Equal(t, "HTTP/1.1", snapshot["protocol"])
assert.Equal(t, 1, snapshot["write_count"])
assert.Equal(t, false, snapshot["write_failed"])
})
}
}