Skip to content

fix: handle missing </tool_call> closing tag in parse_tool_call#121

Merged
dzorlu merged 1 commit intomainfrom
fix/tool-call-parsing
Feb 5, 2026
Merged

fix: handle missing </tool_call> closing tag in parse_tool_call#121
dzorlu merged 1 commit intomainfrom
fix/tool-call-parsing

Conversation

@dzorlu
Copy link
Collaborator

@dzorlu dzorlu commented Feb 5, 2026

Summary

  • Fix two critical bugs in parse_tool_call() causing training crashes and 0% reward

Bug 1: Missing closing tag (stop string issue)

When using </tool_call> as the stop string:

generator.sampling_params.stop='["</tool_call>"]'

vLLM stops generation before outputting the closing tag. Model generates:

<tool_call>{"name": "func", "arguments": {...}}<|im_end|>

But regex required both tags → every tool call rejected as "No tool call found" → 0% reward.

Evidence: Run fleet_tool_use_024ac38b - all trajectories show model generating valid tool calls but environment rejecting them.

Bug 2: Non-dict JSON type crash

json.loads() can return any valid JSON type (string, list, number, etc). If model generates:

<tool_call>"some_string"</tool_call>

Then json.loads() returns a Python string, and parsed.get("name") crashes:

AttributeError: 'str' object has no attribute 'get'

Evidence: Run fleet_tool_use_287cf214 crashed at step 10 with this exact error during training rollout.

Fix

# Handle missing closing tag
match = re.search(rf"<{tag}>(.*?)</{tag}>", action, re.DOTALL)
if not match:
    match = re.search(rf"<{tag}>(.*?)(?:<\||\Z)", action, re.DOTALL)

# Check parsed is dict before calling .get()
parsed = json.loads(match.group(1).strip())
if not isinstance(parsed, dict):
    continue

Test plan

  • Unit test showing both fixes work correctly
  • Run training to verify no crashes and non-zero rewards

🤖 Generated with Claude Code

Two fixes:
1. Handle missing </tool_call> closing tag when it's used as stop string
2. Check that json.loads returns dict before calling .get()

Bug 1: When stop=['</tool_call>'], vLLM stops before outputting the closing
tag. The regex required both tags, so valid tool calls were rejected.

Bug 2: json.loads() can return any JSON type (string, list, number, etc).
If model generates <tool_call>"string"</tool_call>, json.loads returns a
Python string, and parsed.get("name") crashes with AttributeError.

Evidence:
- Run fleet_tool_use_287cf214 crashed at step 10 with:
  AttributeError: 'str' object has no attribute 'get'
- Run fleet_tool_use_024ac38b had 0% reward because all tool calls
  rejected with "No tool call found"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@dzorlu dzorlu force-pushed the fix/tool-call-parsing branch from be82abf to a403810 Compare February 5, 2026 20:21
@dzorlu dzorlu merged commit 0a56e36 into main Feb 5, 2026
3 checks passed
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