Skip to content

fix(tracing): mark deferred tool responses as pending on tool spans - #1210

Open
gitwillsky wants to merge 1 commit into
volcengine:mainfrom
gitwillsky:fix/tool-span-pending-output
Open

gitwillsky wants to merge 1 commit into
volcengine:mainfrom
gitwillsky:fix/tool-span-pending-output

Conversation

@gitwillsky

Copy link
Copy Markdown

Problem

A tool whose response arrives after its call returns finishes its execute_tool span without a function response:

  • ADK's task-delegation tool (_TaskAgentTool, _defers_response=True) returns None; ADK synthesizes the response once the sub-agent finishes (google/adk/flows/llm_flows/functions.py, the is_long_running or _defers_response branch).
  • A long-running tool (is_long_running=True) that has not responded yet behaves the same way.

record_tool_execution then calls the patched trace_tool_call with function_response_event=None, and tool_gen_ai_tool_output writes <unknown_tool_output> to gen_ai.output (APMPlus), gen_ai.tool.output (TLS) and cozeloop.output (CozeLoop). On APMPlus a delegated or long-running call therefore looks like a missing or broken output. Callers cannot work around it: returning a value from an after-tool callback turns the tool into an immediate response and breaks the delegation.

Fix

In tool_gen_ai_tool_output, when there is no function response and the tool is long-running or defers its response, record {"status": "pending"}. Every other tool without a response keeps <unknown_tool_output>. This matches what the Codex runtime already returns to the model in the same situation ({"status": "pending", "call_id": ...} in veadk/runtime/codex/tools_bridge.py); the call id is not available to the extractor, so it is omitted.

Compatibility

Only the recorded output value of these spans changes; no public API, configuration or event contract changes. Content tracing (OBSERVABILITY_OPENTELEMETRY_TRACE_CONTENT) still gates these attributes as before.

Verification (2026-11-05, against main @ e5bfe04)

  • New tests in tests/test_tracing_content.py: a deferred-response tool and a long-running tool record {"status": "pending"} on all three attributes (fail on main, pass here); a plain tool without a response still records <unknown_tool_output>.
  • pytest -c pytest.ini tests/test_tracing_content.py tests/test_tracing.py tests/test_portal_metrics.py tests/test_inmemory_exporter.py tests/test_llm_attributes_extractors.py: 85 passed.
  • ruff@0.11.12 format --check and ruff check on the changed files: pass.
  • Environment note: run with Python 3.12 against an installed VeADK 1.1.24 whose affected files are byte-identical to main, overlaid with this change, not a full uv sync checkout. Not run: pre-commit run --all-files (including gitleaks) and the full parallel regression.

Found while tracing ADK task delegation to a remote A2A sub-agent: the delegation's execute_tool span showed <unknown_tool_output> even though the delegation worked.

This PR was prepared with an AI coding agent; I authorized the submission.

🤖 Generated with Claude Code

A long-running tool, or ADK's task-delegation tool (`_defers_response`),
can finish its call without a response: ADK synthesizes it later. The
tool span then recorded `<unknown_tool_output>` for gen_ai.output,
gen_ai.tool.output and cozeloop.output, indistinguishable from a missing
output. Record `{"status": "pending"}` for these tools instead; other
tools without a response keep `<unknown_tool_output>`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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