fix(tools): eliminate zero-token suppression and propagate upstream errors cleanly

This commit is contained in:
Luxferre
2026-09-07 09:56:48 +03:00
parent de19bab4eb
commit 9eaa1dd25c
2 changed files with 249 additions and 16 deletions
+129 -1
View File
@@ -755,7 +755,7 @@ func TestBuildGradioPayloadGenericSpaces(t *testing.T) {
if !ok {
t.Fatalf("expected string transcript, got %T", data3[0])
}
if !strings.Contains(transcript, "System: ") || !strings.Contains(transcript, "User: What is 10+10?") || !strings.Contains(transcript, "Assistant: <tool_call>") {
if !strings.Contains(transcript, "# Instructions") || !strings.Contains(transcript, "User: What is 10+10?") || !strings.Contains(transcript, "Assistant: <tool_call>") || !strings.Contains(transcript, "# Current Request") {
t.Errorf("unexpected single-input transcript: %s", transcript)
}
}
@@ -896,4 +896,132 @@ func TestGenericSpaceMockServerToolCalling(t *testing.T) {
}
}
func TestExtractGradioErrorMessage(t *testing.T) {
cases := []struct {
input string
expected string
}{
{
input: `{"error": "Client error '402 Payment Required'"}`,
expected: "Client error '402 Payment Required'",
},
{
input: `{"message": "Rate limit exceeded"}`,
expected: "Rate limit exceeded",
},
{
input: `{"error": null, "title": "Validation Error"}`,
expected: "Validation Error",
},
{
input: `{"error": null}`,
expected: "internal space error (check Gradio inputs/types)",
},
{
input: `raw server failure`,
expected: "raw server failure",
},
}
for _, c := range cases {
got := extractGradioErrorMessage(c.input)
if got != c.expected {
t.Errorf("extractGradioErrorMessage(%q) = %q, expected %q", c.input, got, c.expected)
}
}
}
func TestStreamerLazyStartAndErrors(t *testing.T) {
rec := httptest.NewRecorder()
s := NewStreamer(rec, nil, "cmpl-1", 12345, "test-model")
if s.started {
t.Errorf("expected streamer to start as not started")
}
// Ensure Role/headers only sent on first write
s.Content("Hello")
if !s.started {
t.Errorf("expected streamer to be started after Content")
}
s.Finish("stop")
s.Done()
body := rec.Body.String()
if !strings.Contains(body, `"role":"assistant"`) {
t.Errorf("expected role chunk in body: %s", body)
}
if !strings.Contains(body, `"content":"Hello"`) {
t.Errorf("expected content chunk in body: %s", body)
}
if !strings.Contains(body, "[DONE]") {
t.Errorf("expected [DONE] in body: %s", body)
}
}
func TestUpstreamGradioErrorPropagation(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/gradio_api/info" {
resp := GradioAPIInfoResponse{
NamedEndpoints: map[string]GradioEndpointInfo{
"/chat_fn": {
Parameters: []GradioParamInfo{
{ParameterName: "message", Component: "Textbox"},
},
},
},
}
json.NewEncoder(w).Encode(resp)
return
}
if r.URL.Path == "/gradio_api/call/chat_fn" {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(GradioJoinResponse{EventID: "err-event"})
return
}
if r.URL.Path == "/gradio_api/call/chat_fn/err-event" {
w.Header().Set("Content-Type", "text/event-stream")
flusher := w.(http.Flusher)
fmt.Fprintf(w, "event: error\ndata: {\"error\": \"Quota exceeded: 402 Payment Required\"}\n\n")
flusher.Flush()
return
}
http.NotFound(w, r)
}))
defer ts.Close()
gw := NewGradioGateway(ts.URL, "", 10*time.Second)
// 1. Non-streaming error should return error with upstream message
recNonStream := httptest.NewRecorder()
httpReqNonStream := httptest.NewRequest("POST", "/v1/chat/completions", nil)
err := gw.ExecuteChatCompletion(recNonStream, httpReqNonStream, ChatCompletionRequest{
Messages: []ChatMessage{{Role: "user", Content: "Hello"}},
Stream: false,
})
if err == nil {
t.Fatalf("expected error from non-streaming upstream failure, got nil")
}
if !strings.Contains(err.Error(), "402 Payment Required") {
t.Errorf("expected error to mention 402 Payment Required, got %v", err)
}
// 2. Streaming error before start should return error with upstream message
recStream := httptest.NewRecorder()
httpReqStream := httptest.NewRequest("POST", "/v1/chat/completions", nil)
err = gw.ExecuteChatCompletion(recStream, httpReqStream, ChatCompletionRequest{
Messages: []ChatMessage{{Role: "user", Content: "Hello"}},
Stream: true,
})
if err == nil {
t.Fatalf("expected error from streaming upstream failure, got nil")
}
if !strings.Contains(err.Error(), "402 Payment Required") {
t.Errorf("expected error to mention 402 Payment Required, got %v", err)
}
// And nothing should have been written to body
if strings.Contains(recStream.Body.String(), "[DONE]") {
t.Errorf("did not expect [DONE] on upstream error: %s", recStream.Body.String())
}
}