Skip to content

Parse function_call-wrapped and bare JSON tool calls#317

Closed
chubes4 wants to merge 1 commit into
mainfrom
fix/json-tool-call-function-call-shape
Closed

Parse function_call-wrapped and bare JSON tool calls#317
chubes4 wants to merge 1 commit into
mainfrom
fix/json-tool-call-function-call-shape

Conversation

@chubes4
Copy link
Copy Markdown
Contributor

@chubes4 chubes4 commented Jun 8, 2026

Summary

WP_Agent_Provider_Turn_Result's text tool-call fallback dropped tool calls in two shapes that real providers emit, so the model's tool call silently never executed:

  1. Bare JSON object — a tool call emitted as the entire message with no <tool_call> tag and no ```json fence. extract_json_tool_calls() only collected payloads from tags/fences, so bare JSON was never parsed.
  2. function_call wrapper{"type":"function_call","function_call":{"name":...,"arguments":{...}}}. The parser read name/function/arguments at the top level and never unwrapped the inner function_call object, yielding an empty call.

Both produced zero extracted tool calls.

Changes

  • extract_json_tool_calls(): when no tag/fence payloads are found, treat a trimmed message that is a single JSON object ({...}) as a payload candidate.
  • tool_calls_from_json_payloads(): unwrap an inner function_call object before reading the tool name and arguments.
  • Added smoke coverage in tests/provider-turn-adapter-smoke.php for the function_call wrapper shape.

Testing

Context

Discovered while landing the ai-provider-for-claude-code carried provider (Extra-Chill/wp-coding-agents#197) and the writable-workspace-tools fix (Extra-Chill/homeboy-extensions#1190). With those in place, Claude Opus 4.8 correctly emitted a workspace_write tool call, but it was dropped here because of the unrecognized shape. This is the final link making real Codebox coding tasks work.

AI assistance

  • AI assistance: Yes
  • Tool(s): OpenCode (openai/gpt-5.5)
  • Used for: Root-caused the dropped tool-call shapes from live transcripts, implemented the parser fix, added smoke coverage, and ran the full suite plus the end-to-end canary; Chris directed the work and remains responsible for review/testing.

The text tool-call fallback in WP_Agent_Provider_Turn_Result dropped tool
calls emitted as a bare JSON object (no <tool_call> tag or ```json fence)
and did not recognize the `{"type":"function_call","function_call":{...}}`
wrapper that some providers emit. Both shapes resulted in zero extracted
tool calls, so the model's tool call never executed.

Treat a trimmed message that is a single JSON object as a payload
candidate, and unwrap an inner `function_call` object before reading the
tool name and arguments. Adds smoke coverage for the wrapper shape.
@chubes4
Copy link
Copy Markdown
Contributor Author

chubes4 commented Jun 8, 2026

Closing without merge.

This PR widened the text tool-call scraper to absorb more malformed shapes (bare JSON objects and {"type":"function_call","function_call":{...}} wrappers). That is a paper-over: the canonical contract is that provider plugins return structured tool calls via wp-ai-client FunctionCall parts, which extract_tool_calls() resolves at extract_structured_tool_calls() and returns early — before any text scraping runs.

The real fix landed in the Claude Code provider (Extra-Chill/wp-coding-agents#197), which now declares Anthropic tools and parses tool_use blocks into structured FunctionCall parts. With that, the text fallback is never reached for the provider that motivated this change, so growing the scraper here is unnecessary and would calcify acceptance of non-canonical provider output.

If we want agents-api to actively enforce the contract, the better follow-up is a _doing_it_wrong() warning when a provider only yields tool calls via text scraping — tracked separately rather than by extending the scraper.

@chubes4 chubes4 closed this Jun 8, 2026
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