-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Overview
A review of the claude_agent_sdk integration against the Claude Agent SDK Python docs found several missing pieces. Tracking them here in priority order.
1. ThinkingBlock not serialized with a type field
BlockClassName and SERIALIZED_CONTENT_TYPE_BY_BLOCK_CLASS in _constants.py only cover TextBlock, ToolUseBlock, and ToolResultBlock. When extended thinking is enabled, ThinkingBlock objects are serialized via dataclasses.asdict() (producing {"thinking": "...", "signature": "..."}), but no "type": "thinking" key is added — unlike every other block type.
Fix: Add THINKING = "ThinkingBlock" to BlockClassName and a "ThinkingBlock" → "thinking" entry to SERIALIZED_CONTENT_TYPE_BY_BLOCK_CLASS.
2. Top-level query() function not instrumented
patchers.py patches ClaudeSDKClient and SdkMcpTool but not the standalone claude_agent_sdk.query() function (the one-shot API). Users calling await query(prompt=...) directly get zero tracing.
Fix: Add a patcher for claude_agent_sdk.query that wraps it with similar span lifecycle logic to WrappedClaudeSDKClient.query + receive_response.
3. ResultMessage.result not captured as root span output
_handle_result reads usage, num_turns, and session_id, but ignores the result field — the canonical final text answer from the agent. The root span output currently comes from the last accumulated assistant message in _final_results, which may differ.
Fix: If message.result is set, use it as (or log it alongside) the root span output.
4. ResultMessage.is_error not handled
When an agent run fails, ResultMessage.is_error=True, but _handle_result never checks this, so the root span won't reflect the error state.
Fix: Check getattr(message, "is_error", None) in _handle_result and call self._root_span.log(error=...) when true.
5. Useful ResultMessage fields not captured
Several fields are silently dropped:
| Field | Value |
|---|---|
stop_reason |
Why the agent stopped |
total_cost_usd |
Cost of the run |
duration_ms |
Wall-clock latency |
duration_api_ms |
API latency |
Fix: Include these in result_metadata logged to the root span in _handle_result.
6. AssistantMessage.error not logged
The SDK docs show AssistantMessage has an error field. _handle_assistant never checks it, so model-returned errors in assistant messages are silently discarded.
Fix: Check getattr(message, "error", None) in _handle_assistant and log it on the LLM span.
7. receive_messages() not instrumented (lower priority)
Wrapper.__getattr__ proxies receive_messages() directly to the underlying client without tracing. This is lower priority since receive_response() is the recommended path, but it's a gap if users use the raw event stream.
Not gaps (intentional)
StreamEvent— raw per-token streaming events; intentionally ignored inContextTracker.add()interrupt()— control method; no tracing needed- All hook types (PreToolUse, PostToolUse, SubagentStart, SubagentStop, etc.) — handled correctly via the generic
_hook_event_name()path