Skip to content

Comments

fix: Coerce tool call arguments to match schema types#95

Open
janhilgard wants to merge 1 commit intowaybarrios:mainfrom
janhilgard:fix/coerce-tool-arguments
Open

fix: Coerce tool call arguments to match schema types#95
janhilgard wants to merge 1 commit intowaybarrios:mainfrom
janhilgard:fix/coerce-tool-arguments

Conversation

@janhilgard
Copy link
Collaborator

Summary

  • Add _coerce_tool_arguments() that auto-stringifies object/array values when the tool schema expects a string
  • Fixes a common LLM failure mode where models output raw JSON objects instead of JSON strings (e.g. when writing package.json, the model puts content as an object instead of a string)
  • Applied to all tool call emission paths (non-streaming, streaming, streaming fallback)

Problem

When using tool calling with coding assistants (e.g. OpenCoder), models sometimes generate:

{"file_path": "package.json", "content": {"name": "app", "version": "1.0.0"}}

instead of:

{"file_path": "package.json", "content": "{\"name\": \"app\", \"version\": \"1.0.0\"}"}

The client validates against the tool schema (content: string) and fails with:
expected string, received object

Fix

Server-side coercion: after parsing tool call arguments, check each field against the tool schema. If schema says type: "string" but the value is an object/array, json.dumps() it automatically.

Test plan

  • Verify tool calls with string content pass through unchanged
  • Verify tool calls with object content are auto-stringified
  • Verify tool calls with array content are auto-stringified
  • Verify no-op when tools schema is not provided
  • Verify streaming and non-streaming paths both coerce

🤖 Generated with Claude Code

Add _coerce_tool_arguments() that auto-stringifies object/array values
when the tool schema expects a string. Fixes a common LLM failure mode
where models output raw JSON objects instead of JSON strings (e.g. when
writing package.json, the model puts content as an object instead of a
string). Applied to all 4 tool call emission paths (non-streaming,
streaming with reasoning, streaming without reasoning, streaming fallback).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant