From f18b27bef9c3f3a20d8ab02f23278adbcc1c0f5c Mon Sep 17 00:00:00 2001 From: piratf Date: Sun, 26 Apr 2026 12:00:38 +0800 Subject: [PATCH] feat(openai): support Strict parameter for tool function definitions Allow setting strict mode on tool functions via ToolInfo.Extra["strict"]. This enables OpenAI Structured Outputs for function calling, ensuring the model generates exact JSON Schema compliant arguments. --- libs/acl/openai/chat_model.go | 9 ++++ libs/acl/openai/chat_model_test.go | 79 ++++++++++++++++++++++++++++++ libs/acl/openai/tool.go | 1 + 3 files changed, 89 insertions(+) diff --git a/libs/acl/openai/chat_model.go b/libs/acl/openai/chat_model.go index 9c48a7c8e..2b3fd656b 100644 --- a/libs/acl/openai/chat_model.go +++ b/libs/acl/openai/chat_model.go @@ -658,6 +658,7 @@ func (c *Client) genRequest(ctx context.Context, in []*schema.Message, opts ...m Name: t.Function.Name, Description: t.Function.Description, Parameters: t.Function.Parameters, + Strict: t.Function.Strict, }, } } @@ -1291,11 +1292,19 @@ func toTools(tis []*schema.ToolInfo) ([]tool, error) { sortArrayFields(paramsJSONSchema) + var strict bool + if v, ok := ti.Extra["strict"]; ok { + if b, ok := v.(bool); ok { + strict = b + } + } + tools[i] = tool{ Function: &functionDefinition{ Name: ti.Name, Description: ti.Desc, Parameters: paramsJSONSchema, + Strict: strict, }, } } diff --git a/libs/acl/openai/chat_model_test.go b/libs/acl/openai/chat_model_test.go index 9c169909d..8a4c73e96 100644 --- a/libs/acl/openai/chat_model_test.go +++ b/libs/acl/openai/chat_model_test.go @@ -373,6 +373,85 @@ func TestToTools(t *testing.T) { }) } +func TestToToolsStrict(t *testing.T) { + mockey.PatchConvey("strict via Extra", t, func() { + mockTools := []*schema.ToolInfo{ + { + Name: "strict_tool", + Desc: "a tool with strict mode", + ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ + "query": { + Type: schema.String, + Required: true, + }, + }), + Extra: map[string]any{"strict": true}, + }, + { + Name: "non_strict_tool", + Desc: "a tool without strict mode", + ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ + "query": { + Type: schema.String, + Required: true, + }, + }), + }, + { + Name: "invalid_strict_tool", + Desc: "a tool with invalid strict value", + ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ + "query": { + Type: schema.String, + Required: true, + }, + }), + Extra: map[string]any{"strict": "yes"}, + }, + } + + tools, err := toTools(mockTools) + assert.Nil(t, err) + assert.Len(t, tools, 3) + + assert.True(t, tools[0].Function.Strict, "strict_tool should have Strict=true") + assert.False(t, tools[1].Function.Strict, "non_strict_tool should have Strict=false") + assert.False(t, tools[2].Function.Strict, "invalid strict value should default to false") + }) + + mockey.PatchConvey("strict propagated to genRequest", t, func() { + ctx := context.Background() + strictTools := []*schema.ToolInfo{ + { + Name: "strict_tool", + Desc: "a tool with strict mode", + ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{ + "queries": { + Type: schema.Array, + Required: true, + }, + }), + Extra: map[string]any{"strict": true}, + }, + } + + c := &Client{ + config: &Config{Model: "test-model"}, + } + + nc, err := c.WithToolsForClient(strictTools) + assert.Nil(t, err) + + req, _, _, _, err := nc.genRequest(ctx, []*schema.Message{ + {Role: schema.User, Content: "hello"}, + }) + assert.Nil(t, err) + assert.Len(t, req.Tools, 1) + assert.True(t, req.Tools[0].Function.Strict, "FunctionDefinition.Strict should be true") + assert.Equal(t, "strict_tool", req.Tools[0].Function.Name) + }) +} + func TestBuildMessages(t *testing.T) { t.Run("buildMessageFromAssistantGenMultiContent", func(t *testing.T) { t.Run("success with audio", func(t *testing.T) { diff --git a/libs/acl/openai/tool.go b/libs/acl/openai/tool.go index 38b9dc9b2..cc5fa765f 100644 --- a/libs/acl/openai/tool.go +++ b/libs/acl/openai/tool.go @@ -28,4 +28,5 @@ type functionDefinition struct { Name string `json:"name"` Description string `json:"description,omitempty"` Parameters *jsonschema.Schema `json:"parameters"` + Strict bool `json:"strict,omitempty"` }