Skip to content

feat(claude): support strict tool use via ToolInfo.Extra#750

Open
m11y wants to merge 4 commits into
cloudwego:mainfrom
m11y:feat/claude-strict-tool-use
Open

feat(claude): support strict tool use via ToolInfo.Extra#750
m11y wants to merge 4 commits into
cloudwego:mainfrom
m11y:feat/claude-strict-tool-use

Conversation

@m11y

@m11y m11y commented Mar 30, 2026

Copy link
Copy Markdown

Summary

  • When ToolInfo.Extra["strict"] is set to true, the Claude adapter now passes strict: true on the ToolParam
  • This enables Anthropic's strict structured output mode which guarantees tool call inputs conform to the declared JSON Schema exactly
  • Prevents issues like array fields being returned as serialized strings, which can cause downstream JSON parsing failures

Changes

  • toAnthropicToolParam(): check tool.Extra["strict"] and set ToolParam.Strict accordingly
  • Added TestToAnthropicToolParam_Strict test

Motivation

Without strict: true, Claude may return a string for a field declared as "type": "array" in the tool schema. The string contains a serialized JSON array, but with improper escaping of nested quotes, making it unparseable. With strict mode enabled, the API guarantees schema conformance.

@CLAassistant

CLAassistant commented Mar 30, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@m11y m11y force-pushed the feat/claude-strict-tool-use branch from 9a65785 to a6cb781 Compare April 15, 2026 08:21
@m11y m11y force-pushed the feat/claude-strict-tool-use branch from 288a031 to be4d750 Compare May 2, 2026 04:08
@tangchaojun-bytedance

Copy link
Copy Markdown
Collaborator

@m11y thanks for your pr. you add strict tool use, but changed openai's tool choice, could you explain this? I think this changes the semantics for ToolChoiceForced with one AllowedToolNames entry. A single allowed tool is not the same as a single request tool: req.Tools may still contain multiple tools. Returning "required" only forces the model to call some tool, but it drops the allowed-tool restriction and may allow other tools in req.Tools to be called. We should keep the allowed_tools payload whenever allowedToolNames is non-empty, and only use plain "required" when there is no allowed subset and the request truly has one tool.

@m11y

m11y commented Jun 2, 2026

Copy link
Copy Markdown
Author

Thanks for catching this. You are right: my compatibility shortcut changed the semantics for ToolChoiceForced when AllowedToolNames has one entry but req.Tools still contains multiple tools. In that case plain "required" only requires some tool call and loses the allowed subset restriction.

I pushed 7ba58b1 to fix this: allowed_tools is now preserved whenever allowedToolNames is non-empty, with mode: "required" for forced tool choice. The single-request-tool compatibility shortcut is only used when there is no allowed subset and the request itself has exactly one tool, where "required" remains semantically equivalent to forcing that only tool. The existing no-subset/multiple-tools behavior remains unchanged ("required", i.e. force some tool).

Validated with:

cd libs/acl/openai && GOTOOLCHAIN=go1.25.5 go test -gcflags="all=-N -l" .

@tangchaojun-bytedance

Copy link
Copy Markdown
Collaborator

@m11y 针对ark, 当前 PR 虽然会把 ToolInfo.Extra["strict"] 读到内部的 functionDefinition.Strict 里,但在构造 Ark SDK 请求时,只把 NameDescriptionParameters 映射到了 model.FunctionDefinition,没有把 Strict 放进最终请求体里。而且当前项目 pinned 的 volcengine-go-sdk v1.2.27 里的 FunctionDefinition 本身也没有 Strict 字段,所以 Ark 请求实际不会带出 "strict": true

@m11y

m11y commented Jun 2, 2026

Copy link
Copy Markdown
Author

@m11y 针对ark, 当前 PR 虽然会把 ToolInfo.Extra["strict"] 读到内部的 functionDefinition.Strict 里,但在构造 Ark SDK 请求时,只把 NameDescriptionParameters 映射到了 model.FunctionDefinition,没有把 Strict 放进最终请求体里。而且当前项目 pinned 的 volcengine-go-sdk v1.2.27 里的 FunctionDefinition 本身也没有 Strict 字段,所以 Ark 请求实际不会带出 "strict": true

所以,当前 PR 让 ark 具体 strict 调用能力,等后续 volcengine-go-sdk v1.2.27 支持了,就可以?还是有其他修改建议?

@tangchaojun-bytedance

Copy link
Copy Markdown
Collaborator

@m11y 针对ark, 当前 PR 虽然会把 ToolInfo.Extra["strict"] 读到内部的 functionDefinition.Strict 里,但在构造 Ark SDK 请求时,只把 NameDescriptionParameters 映射到了 model.FunctionDefinition,没有把 Strict 放进最终请求体里。而且当前项目 pinned 的 volcengine-go-sdk v1.2.27 里的 FunctionDefinition 本身也没有 Strict 字段,所以 Ark 请求实际不会带出 "strict": true

所以,当前 PR 让 ark 具体 strict 调用能力,等后续 volcengine-go-sdk v1.2.27 支持了,就可以?还是有其他修改建议?

那针对ark的修改,暂且就是无效的吧

@m11y

m11y commented Jun 3, 2026

Copy link
Copy Markdown
Author

You are right about Ark. The previous Ark change only stored ToolInfo.Extra["strict"] in the adapter-local functionDefinition, but it was not carried into the final Ark SDK request body, so it was effectively a no-op.

I pushed 67fdfca to remove the Ark-related change from this PR. The PR diff now only keeps the Claude strict-tool change and the OpenAI strict/tool-choice changes discussed above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants