Skip to content

fix(ingest): count TypeScript ADK's call_llm as the model call - #1170

Merged
JeremyFunk merged 1 commit into
mainfrom
fix/ingest-adk-ts-call-llm-model-call
Sep 30, 2026
Merged

JeremyFunk merged 1 commit into
mainfrom
fix/ingest-adk-ts-call-llm-model-call

Conversation

@JeremyFunk

@JeremyFunk JeremyFunk commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Regression from #1143 in apps/ingest/src/ai_session/usage.rs (is_model_call).

  • The arm "google_adk" if span_name == "call_llm" => false ran before the inference-op check, so no ADK call_llm span was ever marked as the model call.
  • Python ADK: call_llm is a wrapper with no gen_ai.operation.name, and its generate_content child carries the same figures. Every call_llm in the trace-capture ADK recordings (docs_google-adk_a/b, google_adk_agents, google_adk_agents_probe, google_adk_hitl_probe, google_adk_user, smoke-adk) has no operation and no openinference.span.kind.
  • TypeScript ADK has no generate_content span. The AdkSpanProcessor from the google-adk setup guide stamps gen_ai.operation.name=chat on call_llm, which makes that span the model call. With the arm in place, TS ADK sessions got no LLM calls and no tokens. EU prod shows this on the synthetic span from verify2-adk-ts-synthetic (maple_ai.llm_call=0).
  • Fix: drop the arm and let the operation decide, with no cross-span state. A Python call_llm has no operation, so it misses INFERENCE_OPS and falls through to false like before, because google_adk is not an unknown: vendor. A TS call_llm has op chat and counts as the call. This is the same as the suggested op.is_empty() guard, one line shorter.
  • facts.rs has no call_llm or span-name rule of this kind.

Tests:

  • The existing google_adk_generate_content_owns_usage test covers Python: call_llm + generate_content gives one call, and the wrapper gets llm_call=0 and no buckets.
  • New google_adk_ts_call_llm_owns_usage test covers TS: call_llm with op chat and usage gives one call, with buckets [600, 400, 0, 100, 0].
  • cargo test --lib ai_session: 79 passed.

Out of scope, noted here: for Gemini, output_tokens excludes thinking. Usage::read clamps reasoning to the completion count, so a Gemini call that thinks more than it writes (the synthetic span had output 100 and reasoning 300) is bucketed as output 0 and reasoning 100. The new test leaves reasoning out so it does not lock that behaviour in.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Summary by CodeRabbit

  • Bug Fixes
    • Google ADK usage reporting now recognizes call_llm as the model call when no generate_content call is present, and separates cached from uncached prompt usage.
  • Documentation
    • Clarified how Python ADK reports wrapper calls and their associated model calls.

#1143 excluded every google_adk call_llm span before the inference-op check.
Python ADK's call_llm is a wrapper with no operation over its generate_content
child, but TypeScript ADK has no generate_content span: the Maple span
processor stamps op chat on call_llm, so those sessions lost every LLM call and
their tokens. Without the arm, the op decides: Python's opless wrapper still
falls through to false, TypeScript's chat span is the call.
@maple-review-bot

maple-review-bot Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Maple review

🟢 Confidence 4/5 · likely safe to merge
Contained to one vendor's stamping, and the Python and TypeScript paths each have a test that FAILs against the old guard.
quality 100/100 · no findings · tests covered · risk medium

Drops the google_adk + call_llm early-out in is_model_call so the operation decides, letting TypeScript ADK's call_llm (stamped chat) be the model call while Python's wrapper still is not. Safe to merge; both dialects are covered by tests.

  • is_model_call no longer short-circuits on google_adk spans named call_llm
  • ADK call_llm with gen_ai.operation.name=chat now stamps buckets and maple_ai.llm_call=1
  • New google_adk_ts_call_llm_owns_usage test pins the TypeScript path
What was checked
  • Python call_llm has no op and no openinference.span.kind, so it still misses INFERENCE_OPS and google_adk does not start with unknown: (usage.rs:194, facts.rs:251)
  • The removed arm sat before only the generic op check, so litellm, semantic_kernel and vercel_ai_sdk ordering is unchanged
  • TS buckets [600,400,0,100,0] follow from input_excludes_cache false for gemini-2.5-flash (usage.rs:223), matching the new assertion

9a516c1 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 4a352327-fec4-45be-a791-c5bcf25ef6d2

📥 Commits

Reviewing files that changed from the base of the PR and between 671e80a and 9a516c1.

📒 Files selected for processing (1)
  • apps/ingest/src/ai_session/usage.rs

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.


📝 Walkthrough

Walkthrough

Google ADK call_llm spans now use the general inference-operation rule for model-call classification. The Python test comment clarifies the generate_content child’s role. A TypeScript test verifies input and cache-read bucket values.

Changes

Google ADK usage classification

Layer / File(s) Summary
Classify call_llm spans and verify usage buckets
apps/ingest/src/ai_session/usage.rs
The call_llm exclusion is removed. The Python test comment clarifies that the generate_content child names an operation. A TypeScript test checks the resulting usage buckets for input 1000 and cache-read count 400.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 9a516

The change enables inference-tagged Google ADK call_llm spans to count as model calls. The checked-in evidence does not establish production TypeScript span stamping, but identifies no concrete merge-blocking failure.

Architecture Summary

Architecture risk: 🔵 Low · up to 9a516

The change affects 1 system.

Changed systems: apps/ingest

Architecture concerns
No architecture-level concerns identified.

Review details

Systems and components

  • observed — apps/ingest (service) was modified; 1 changed file maps to changed impact.

Before / after behavior

  • observed — Modified behavior in apps/ingest/src/ai_session/usage.rs: Removed the Google ADK special case that excluded call_llm spans from model-call classification. Such spans now follow the general operation check, so call_llm is a model call when its operation is an inference operation.
  • observed — Modified behavior in apps/ingest/src/ai_session/usage.rs: Updated the Python ADK test comment to specify that only the generate_content child names an operation; the test assertions are unchanged.
  • observed — Modified behavior in apps/ingest/src/ai_session/usage.rs: Added a TypeScript ADK test where call_llm has operation chat, model gemini-2.5-flash, input 1000, and cache-read count 400; it expects buckets [600, 400, 0, 100, 0].
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the main change: counting TypeScript ADK's call_llm span as the model call.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 1 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR

Warning

Some tools did not complete. Review the errors below.

🔧 Clippy (1.98.1)

Clippy execution failed


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@JeremyFunk
JeremyFunk merged commit affb30b into main Sep 30, 2026
35 of 38 checks passed
@JeremyFunk
JeremyFunk deleted the fix/ingest-adk-ts-call-llm-model-call branch September 30, 2026 11:34
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