fix(provider): use message-array input for Responses probes (#605)

This commit is contained in:
Snowyy authored and GitHub committed 2026-10-03 03:02:46 +08:00
1 parent 20c5fa6fd9
commit 8c7791c071
2 files changed
+76 -1

No files matched your search

+75
View File
@@ -133,6 +133,81 @@ func TestDetectProtocols(t *testing.T) {
}
}
// Some Responses relays reject the string shorthand and require message arrays.
func TestResponsesProbesUseArrayInput(t *testing.T) {
detectHome(t)
const model = "gpt-test"
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.Method + " " + r.URL.Path {
case "GET /v1/models":
io.WriteString(w, `{"data":[{"id":"`+model+`"}]}`)
case "POST /v1/responses":
var in struct {
Model string `json:"model"`
Input []struct {
Type string `json:"type"`
Role string `json:"role"`
Content []struct {
Type string `json:"type"`
Text string `json:"text"`
} `json:"content"`
} `json:"input"`
}
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
http.Error(w, `{"error":{"message":"input must be an array"}}`, http.StatusBadRequest)
return
}
if in.Model != model || len(in.Input) != 1 || in.Input[0].Type != "message" ||
in.Input[0].Role != "user" || len(in.Input[0].Content) != 1 ||
in.Input[0].Content[0].Type != "input_text" || in.Input[0].Content[0].Text != "hi" {
http.Error(w, `{"error":{"message":"expected a user text message"}}`, http.StatusBadRequest)
return
}
io.WriteString(w, `{"object":"response","status":"completed","output":[]}`)
default:
http.NotFound(w, r)
}
}))
defer srv.Close()
p := Provider{ID: "relay", Key: "sk-relay", Responses: srv.URL + "/v1", Models: []string{model}}
ctx := context.Background()
check := func(t *testing.T, r Result) {
t.Helper()
if !r.OK || r.Status != http.StatusOK || r.Protocol != Responses || r.Model != model {
t.Fatalf("Responses probe: %+v", r)
}
}
t.Run("Detect", func(t *testing.T) {
rs, err := p.Detect(ctx, srv.URL, model)
if err != nil || len(rs) != len(Protocols) {
t.Fatalf("Detect: %v, %+v", err, rs)
}
check(t, rs[1].Result)
})
t.Run("DetectModels", func(t *testing.T) {
each, sum, err := p.DetectModels(ctx, srv.URL, []string{model})
if err != nil || len(each) != 1 || len(each[0].Results) != len(Protocols) || len(sum) != len(Protocols) {
t.Fatalf("DetectModels: %v, %+v, %+v", err, each, sum)
}
check(t, each[0].Results[1].Result)
check(t, sum[1].Result)
})
t.Run("Test", func(t *testing.T) {
rs := p.Test(ctx)
if len(rs) != 1 {
t.Fatalf("Test: %+v", rs)
}
check(t, rs[0])
})
t.Run("TestModels", func(t *testing.T) {
rs := p.TestModels(ctx, []string{model})
if len(rs) != 1 {
t.Fatalf("TestModels: %+v", rs)
}
check(t, rs[0])
})
}
// A model typed is the one asked on all three, and the vendor's list isn't
// asked for; a URL the provider has for an API is asked rather than the
// one typed; no URL at all says so.
+1 -1
View File
@@ -124,7 +124,7 @@ func tinyBody(q Provider, proto Protocol, model string) (url, body string) {
}
return q.Chat + "/chat/completions", fmt.Sprintf(`{"model":%q,"messages":[{"role":"user","content":"hi"}],"max_tokens":16}`, model)
case Responses:
return q.Responses + "/responses", fmt.Sprintf(`{"model":%q,"input":"hi","max_output_tokens":16}`, model)
return q.Responses + "/responses", fmt.Sprintf(`{"model":%q,"input":[{"type":"message","role":"user","content":[{"type":"input_text","text":"hi"}]}],"max_output_tokens":16}`, model)
case Anthropic:
return q.Anthropic + "/v1/messages", fmt.Sprintf(`{"model":%q,"max_tokens":16,"messages":[{"role":"user","content":"hi"}]}`, model)
}