Skip to content

fix(agno): split tool-call loops into react steps#240

Merged
sipercai merged 3 commits into
mainfrom
liuyu/fix/agno-react-loop-token
Jul 9, 2026
Merged

fix(agno): split tool-call loops into react steps#240
sipercai merged 3 commits into
mainfrom
liuyu/fix/agno-react-loop-token

Conversation

@sipercai

@sipercai sipercai commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Description

This fixes Agno 2.x tool-call loop instrumentation so every real model provider request is emitted as its own chat span under a ReAct step, instead of wrapping the whole Agno model response loop as one LLM span.

The change keeps direct Model.response usage covered through a fallback wrapper, but uses Agno's per-request processing hooks when they are available. Streaming agent token totals now fall back to the sum of child LLM spans when Agno does not expose final run metrics.

Fixes no existing issue.

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration

  • tox -c tox-loongsuite.ini -e py311-test-loongsuite-instrumentation-agno -- -q
  • tox -c tox-loongsuite.ini -e py39-test-loongsuite-instrumentation-agno,py313-test-loongsuite-instrumentation-agno -- -q
  • tox -c tox-loongsuite.ini -e lint-loongsuite-instrumentation-agno
  • tox -e precommit
  • python "$PIPELINE_SKILL_DIR/scripts/check_loongsuite_pr_readiness.py" --repo .

Validation Evidence

Spec and Scope

  • Linked issue/spec: none
  • Approved spec/comment: requested Agno tool-call loop / ReAct span repair
  • Changed surface: loongsuite-instrumentation-agno instrumentation hooks, span/token aggregation logic, and focused Agno tests

Local Checks

Check Command Result Notes
Static readiness python "$PIPELINE_SKILL_DIR/scripts/check_loongsuite_pr_readiness.py" --repo . pass
Precommit tox -e precommit pass
Focused tests tox -c tox-loongsuite.ini -e py311-test-loongsuite-instrumentation-agno -- -q pass 36 tests
Version edge tests tox -c tox-loongsuite.ini -e py39-test-loongsuite-instrumentation-agno,py313-test-loongsuite-instrumentation-agno -- -q pass 36 tests on each env
Focused lint tox -c tox-loongsuite.ini -e lint-loongsuite-instrumentation-agno pass
Privacy scan changed-file scan for secrets and private trace material pass no hits

Real E2E Matrix

Scenario Status Command or Demo Evidence
non-streaming pass deterministic Agno 2.x latency tool-call smoke with OTLP export ARMS/CMS cloud readback verified; LLM/tool durations match injected provider/tool latency
streaming pass deterministic Agno 2.x streaming latency tool-call smoke with OTLP export agent and child LLM token totals verified in ARMS/CMS; streaming LLM span covers chunk interval
concurrency pass concurrent Agent.arun(stream=True) smoke no round/token/context leakage observed
agent/tool/ReAct pass deterministic two-round tool-call loop tree verified as Agent -> Step -> LLM/Tool -> Step -> LLM
tool-heavy N/A not part of this bug surface focused fix covers one tool-call batch per round
error path pass focused unit coverage for stream close/cancel cleanup ContextVar cleanup verified

Telemetry and Weaver

Check Status Command or Artifact Notes
Span tree / span kinds pass local in-memory spans plus ARMS/CMS cloud readback tool spans are siblings of LLM spans under ReAct step
Span timing pass local in-memory spans plus ARMS/CMS cloud readback LLM spans cover provider latency; tool spans cover tool execution latency
Token totals pass local in-memory spans plus ARMS/CMS cloud readback agent total equals sum of child LLM totals
Concurrency isolation pass concurrent async streaming smoke per-run ReAct rounds and token aggregation remain isolated
Weaver live-check pass weaver registry live-check --advice-profile loongsuite-genai ... no violations

CI

  • GitHub checks: not run yet
  • Known unrelated failures: none known
  • Follow-up needed: inspect GitHub Actions after PR creation

Does This PR Require a Core Repo Change?

  • No.

Checklist:

See contributing.md for styleguide, changelog guidelines, and more.

  • Followed the style guidelines of this project
  • Changelogs have been updated
  • Unit tests have been added
  • Documentation has been updated

@ralf0131 ralf0131 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Refactors Agno instrumentation to split tool-call loops into individual ReAct step spans (one LLM span per provider request), with contextvar-based run-state tracking and token-usage fallback from child LLM spans. Well-structured, follows existing patterns, and includes comprehensive test coverage (sync/async, stream/non-stream, direct model call).

Findings

  • [Info] _wrapper.py:643 — Dead code in _response_for_llm_finish: the if condition is redundant since both branches return assistant_message. See inline comment.

Suggestions

The dead conditional in _response_for_llm_finish can be simplified. If the original intent was to validate assistant_message attributes before returning it, consider returning None in the else branch or removing the conditional entirely.


Automated review by github-manager-bot

@ralf0131 ralf0131 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Re-review after new commit fbc32a18 (latency test coverage). The added latency tests correctly validate that LLM spans cover provider latency (≥25ms for 30ms delay) and that direct Model.response calls without agent context do not emit ReAct step spans. The 5-layer span tree assertion (session > turn > step > {llm, tool}) is well-structured.

The previously noted [Info] finding in _response_for_llm_finish (redundant conditional — both branches return assistant_message) is still present. This is non-blocking; recommend cleaning up in a follow-up.

Overall the implementation is solid: ContextVar-based state coordination is clean, the fallback to outer Model.response wrappers for older Agno versions is well-handled, and test coverage is comprehensive.


Automated review by github-manager-bot

@ralf0131 ralf0131 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Refactors Agno 2.x tool-call loop instrumentation to emit one chat span per provider request under a ReAct step, instead of wrapping the entire model response loop as a single LLM span. Uses Agno's per-request processing hooks (_process_model_response, process_response_stream, etc.) when available, with a clean fallback to outer Model.response wrappers for older versions. Streaming agent token totals now fall back to summing child LLM spans when Agno doesn't expose final run metrics.

Findings

No issues found. The implementation is solid:

  • _wrap_model_methods feature detection cleanly prefers inner hooks, falling back with a warning when unavailable
  • _AgnoRunState + ContextVar properly tracks react round, active step, and accumulated token usage per run
  • State lifecycle is correctly managed in try/except/finally across sync, async, and streaming paths
  • _is_stream_close check correctly handles the edge case where stream closure triggers finally without a real error
  • Token fallback (_apply_agent_token_fallback) only activates when Agno metrics are None, avoiding double-counting
  • _uninstrument properly unwraps both inner and fallback wrappers via _unwrap_if_present
  • Excellent test coverage: sync/async/stream/async-stream tool-call loops, latency coverage verification, direct model call (no react step), token aggregation from child spans

CI: all checks passing. Changelog updated. CLA: signed.


Automated review by github-manager-bot

@sipercai sipercai merged commit 9e4d1c8 into main Jul 9, 2026
20 of 25 checks passed
@sipercai sipercai deleted the liuyu/fix/agno-react-loop-token branch July 9, 2026 05:56
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.

4 participants