Skip to content

fix(streaming): tool calls and race conditions for SDK mode#31

Merged
CaddyGlow merged 2 commits intoCaddyGlow:dev/v0.2from
saxyguy81:fix/tool-call-streaming-dev-v0.2
Jan 4, 2026
Merged

fix(streaming): tool calls and race conditions for SDK mode#31
CaddyGlow merged 2 commits intoCaddyGlow:dev/v0.2from
saxyguy81:fix/tool-call-streaming-dev-v0.2

Conversation

@saxyguy81
Copy link

Summary

Ports critical bug fixes to dev/v0.2 architecture:

  1. Tool calls SSE-only bug - Tool calls now work in both SSE and dict (SDK) formats
  2. Non-atomic broadcast race - Fixes message loss with fast STDIO tools
  3. Listener pre-registration race - Fixes first-message loss with rapid tool responses
  4. Orphaned tool_result sanitization - Prevents "unexpected tool_use_id" API errors

Test plan

  • 21 new tests added and passing
  • Linting passes
  • Manual test with Claude Code SDK mode

Files Changed

  • ccproxy/llms/streaming/processors.py - Remove SSE-only condition
  • ccproxy/plugins/claude_sdk/stream_worker.py - Atomic broadcast
  • ccproxy/plugins/claude_sdk/stream_handle.py - Pre-register listener
  • ccproxy/llms/formatters/openai_to_anthropic/requests.py - Add sanitization

🤖 Generated with Claude Code

smhanan and others added 2 commits January 3, 2026 18:37
This commit ports critical bug fixes to the dev/v0.2 architecture:

1. **Tool calls SSE-only bug** (processors.py)
   - Removed `output_format == "sse"` condition that excluded dict format
   - Tool calls now work in both SSE and dict (SDK) output formats
   - Added proper tool call indexing with enumerate()
   - Added debug logging for tool call yielding

2. **Non-atomic broadcast race condition** (stream_worker.py)
   - Removed racy has_listeners() check before broadcast()
   - Now always broadcasts and checks delivered count afterward
   - Fixes message loss with fast STDIO tools like filesystem

3. **Listener pre-registration race** (stream_handle.py)
   - Worker is now created but not started until listener is registered
   - Prevents messages from being lost before listener is ready
   - Fixes first-message loss with rapid tool responses

4. **Orphaned tool_result sanitization** (requests.py)
   - Added _sanitize_tool_results() function
   - Removes tool_result blocks that lack matching tool_use blocks
   - Converts orphaned results to text to preserve information
   - Prevents "unexpected tool_use_id" API errors

Tests added:
- test_tool_call_streaming.py: 4 tests for tool call streaming
- test_tool_result_sanitization.py: 17 tests for sanitization

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove unused _ensure_worker_created method (dead code)
- Change tool call logging from debug to trace level for consistency

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

Co-Authored-By: Claude Opus 4.5 <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.

2 participants