fix(provider): condense upstream error bodies before they reach clients

api error strings embedded raw upstream response bodies, so JSON quota
payloads and WAF HTML pages leaked through to clients (and through the
per-tier chain summary). shortAPIError extracts the envelope reason
(error.message / message / msg), collapses HTML blocklist pages to a
marker, and caps everything at one line.
This commit is contained in:
JianFeeeee
2026-08-24 18:56:06 +08:00
parent e3a36bc7ca
commit 6eac80bc6c
2 changed files with 81 additions and 4 deletions

View File

@ -538,3 +538,30 @@ func TestStandardSSEChunkFinishReason(t *testing.T) {
t.Fatalf("empty-string finish reason is not a finish signal: %s", out)
}
}
func TestShortAPIError(t *testing.T) {
cases := []struct{ name, body, want string }{
{"openai-style envelope", `{"error":{"message":"Allocated quota exceeded","type":"invalid_request_error","code":"insufficient_quota"}}`, "api error 429: Allocated quota exceeded"},
{"nested console envelope", `{"error":{"type":"server_error","message":"Error from provider (Console): Upstream request failed: Endpoint is unavailable."}}`, "api error 503: Error from provider (Console): Upstream request failed: Endpoint is unavailable."},
{"string error", `{"error":"boom"}`, "api error 500: boom"},
{"html waf page", "<!doctypehtml><html lang=\"zh-cn\"><title>405</title></html>", "api error 405: upstream returned an HTML error page"},
{"plain text body", "service unavailable", "api error 503: service unavailable"},
}
for _, c := range cases {
if got := shortAPIError(statusFor(c.want), c.body); got != c.want {
t.Fatalf("%s: got %q want %q", c.name, got, c.want)
}
}
}
func statusFor(want string) int {
switch {
case strings.Contains(want, "429"):
return 429
case strings.Contains(want, "503"):
return 503
case strings.Contains(want, "405"):
return 405
}
return 500
}