fix(tools): eliminate zero-token suppression and propagate upstream errors cleanly
This commit is contained in:
+129
-1
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user