diff --git a/internal/provider/openai/openai.go b/internal/provider/openai/openai.go index 7a1de930b..8885f8b42 100644 --- a/internal/provider/openai/openai.go +++ b/internal/provider/openai/openai.go @@ -588,8 +588,8 @@ type chatTool struct { type chatFunction struct { Name string `json:"name"` - Description string `json:"description"` - Parameters json.RawMessage `json:"parameters"` + Description string `json:"description,omitempty"` + Parameters json.RawMessage `json:"parameters,omitempty"` } type chatToolCall struct { diff --git a/internal/provider/openai/openai_test.go b/internal/provider/openai/openai_test.go index e517accd8..3c4ded774 100644 --- a/internal/provider/openai/openai_test.go +++ b/internal/provider/openai/openai_test.go @@ -613,3 +613,35 @@ func TestBuildRequestContentNullForAssistantToolCalls(t *testing.T) { t.Errorf("no-param tool should serialize a valid empty-object schema: %s", s) } } + +func TestBuildRequestOmitsEmptyToolDescriptionAndParameters(t *testing.T) { + c := &client{name: "x", model: "m", baseURL: "https://api.example.com/v1"} + req := provider.Request{ + Tools: []provider.ToolSchema{{Name: "noargs"}}, + } + body, err := json.Marshal(c.buildRequest(req)) + if err != nil { + t.Fatalf("marshal: %v", err) + } + var wire struct { + Tools []struct { + Function map[string]json.RawMessage `json:"function"` + } `json:"tools"` + } + if err := json.Unmarshal(body, &wire); err != nil { + t.Fatalf("unmarshal request: %v\n%s", err, body) + } + if len(wire.Tools) != 1 { + t.Fatalf("tools = %d, want 1: %s", len(wire.Tools), body) + } + fn := wire.Tools[0].Function + if string(fn["name"]) != `"noargs"` { + t.Fatalf("function name = %s, want noargs", fn["name"]) + } + if _, ok := fn["description"]; ok { + t.Fatalf("empty description should be omitted: %s", body) + } + if _, ok := fn["parameters"]; ok { + t.Fatalf("nil parameters should be omitted: %s", body) + } +}