Support Gradio 6 call_v2 protocol with named parameters and fallback

This commit is contained in:
Luxferre
2026-09-07 14:10:18 +03:00
parent 03825fecfd
commit defc727c95
2 changed files with 198 additions and 11 deletions
+126
View File
@@ -2196,6 +2196,132 @@ func TestExecuteCallCompletionSessionHashAndTrailingStateTrim(t *testing.T) {
}
}
// TestGradio6CallV2Protocol verifies that Gradio 6 spaces resolve protocol call_v2,
// submit named JSON parameters to /call/v2/{endpoint}, and stream from /call/{endpoint}/{event_id}.
func TestGradio6CallV2Protocol(t *testing.T) {
var receivedBody map[string]interface{}
var callEndpointReached string
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/gradio_api/info" {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"named_endpoints": map[string]interface{}{
"/answer": map[string]interface{}{
"parameters": []map[string]interface{}{
{
"parameter_name": "prompt",
"component": "Textbox",
"label": "Prompt",
},
},
"code_snippets": map[string]interface{}{
"bash": "curl -X POST http://localhost:7860/gradio_api/call/v2/answer -s -H \"Content-Type: application/json\" -d '{\"prompt\": \"Hello!!\"}' | awk -F'\"' '{ print $4}' | read EVENT_ID; curl -N http://localhost:7860/gradio_api/call/answer/$EVENT_ID",
},
},
},
})
return
}
if r.URL.Path == "/config" {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"version": "6.13.0",
"components": []map[string]interface{}{
{"id": 1, "type": "textbox", "props": map[string]interface{}{"label": "Prompt"}},
},
"dependencies": []map[string]interface{}{
{
"id": 0,
"api_name": "answer",
"inputs": []int{1},
"outputs": []int{1},
"types": map[string]interface{}{"generator": true},
},
},
})
return
}
if r.URL.Path == "/gradio_api/call/v2/answer" {
callEndpointReached = r.URL.Path
if err := json.NewDecoder(r.Body).Decode(&receivedBody); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"event_id": "evt-v2-777",
})
return
}
if r.URL.Path == "/gradio_api/call/answer/evt-v2-777" {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.WriteHeader(http.StatusOK)
flusher, _ := w.(http.Flusher)
fmt.Fprintf(w, "event: complete\ndata: [\"Hello from Gradio 6 v2!\"]\n\n")
if flusher != nil {
flusher.Flush()
}
return
}
http.NotFound(w, r)
}))
defer ts.Close()
gw := NewGradioGateway(ts.URL, "", 10*time.Second)
// 1. Verify inspection resolved call_v2 protocol
disc, err := InspectSpace(gw.client, ts.URL, DefaultUserAgent)
if err != nil {
t.Fatalf("InspectSpace failed: %v", err)
}
if disc.Protocol != "call_v2" {
t.Errorf("expected Protocol call_v2, got %s", disc.Protocol)
}
if disc.Endpoint != "/answer" {
t.Errorf("expected Endpoint /answer, got %s", disc.Endpoint)
}
// 2. Execute chat completion
req := ChatCompletionRequest{
Model: "default",
Messages: []ChatMessage{
{Role: "user", Content: "Hello test"},
},
}
httpReq := httptest.NewRequest("POST", "/v1/chat/completions", nil)
rec := httptest.NewRecorder()
err = gw.ExecuteChatCompletion(rec, httpReq, req)
if err != nil {
t.Fatalf("ExecuteChatCompletion failed: %v", err)
}
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d: %s", rec.Code, rec.Body.String())
}
if callEndpointReached != "/gradio_api/call/v2/answer" {
t.Errorf("expected call to /gradio_api/call/v2/answer, got %s", callEndpointReached)
}
if promptVal, ok := receivedBody["prompt"].(string); !ok || promptVal != "Hello test" {
t.Errorf("expected named param 'prompt' with value 'Hello test', got %v", receivedBody)
}
var resp ChatCompletionResponse
if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil {
t.Fatalf("failed to decode response: %v", err)
}
if len(resp.Choices) == 0 || resp.Choices[0].Message.Content != "Hello from Gradio 6 v2!" {
t.Errorf("unexpected content: %v", resp.Choices[0].Message.Content)
}
}