Skip to content

feat(agent-sessions): page a session's spans and summarise it in the warehouse - #740

Open
JeremyFunk wants to merge 2 commits into
feat/agent-sessions-lean-span-projectionfrom
feat/agent-sessions-paged-spans
Open

feat(agent-sessions): page a session's spans and summarise it in the warehouse#740
JeremyFunk wants to merge 2 commits into
feat/agent-sessions-lean-span-projectionfrom
feat/agent-sessions-paged-spans

Conversation

@JeremyFunk

@JeremyFunk JeremyFunk commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #739 (the projection change); merge that first.

Why

The Agent Sessions detail page read a session in one response capped at 2,000 spans, and reported anything past that as truncated. The largest session in the warehouse has ~209k spans in a single trace, of which ~19.5k are the agent's own and the rest the app's SQL/HTTP work. One page showed its first minute. No page size fixes that — the page has to load what the reader needs first and the rest on demand, and the totals it prints have to come from the warehouse rather than from the spans in hand.

What

API (/internal/ai-sessions)

  • spans pages by a keyset cursor: after: {timestamp, spanId} on the read's own (timestamp, spanId) order, with nextCursor in the response replacing truncated. The timestamp is the warehouse literal at nanosecond precision, so the resume point is exact.
  • scope: all | ai | app — every span, the vendor-stamped agent spans alone, or the app's own spans alone.
  • traceIds reads named traces without session detection — a turn's worth, capped at 100, validated as 32-hex.
  • New summary: whole-session totals (spans, agent spans, traces, bounds, model/tool calls, errors, tokens, cost, models, agents) plus per-turn rows aggregated in ClickHouse; the totals come from a separate ungrouped read so a session with more turns than one response carries still reports exact totals. The turn key is the conversation id under every vendor spelling the mapper reads, falling back to the trace. Usage is summed over all spans and over model-call spans; the handler keeps the model-call figures when any exist (per-call) and the plain sum otherwise (roll-up), which is the page's deepest-reporter rule at turn granularity. A child span that does not carry the id lands in its trace's row — the rows partition the session exactly, but a turn row may hold fewer spans than the page's turn; documented on the schema.
  • The operation vocabulary (AI_INFERENCE_OPERATIONS etc.) moves to @maple/domain/gen-ai so the summary's "llm call" is the page's.

Web

  • useSessionSpans: the first page is the session's opening, every span of it — a session that fits is complete after one read, exactly as before. When a cursor comes back the session is partial: further pages fetch the agent's spans alone (a tenth of a large session's rows), and each turn's app spans are fetched from the Trace view's turn header by the turn's traces and bounds. Pages are merged and deduplicated; state is keyed by the first-page input so a window change drops them and a late response is discarded.
  • Detail page: banner with "showing N of M spans · Load more"; the Overview leads with a "Whole session" block from the summary when partial; the transcript ends on a load-more divider instead of a truncation notice; the URL's t/end stamp waits for the summary's bounds so a deep link never gets a cut-short window written into it.

Verification

  • Query, route, and web unit tests green (integrations 61, route 19, web agent-sessions 312 incl. new hook and component tests); CLICKHOUSE_E2E=1 catalog sweep passes 259 shapes; scoped typecheck green.
  • Against Tinybird local with a synthetic 6,000-span session (3 traces, 29 turns, 199 agent spans): the summary reports 6,000 / 199 / 85 model calls / 85 tool calls / 1 error with consistent tokens; page 1 (2,001 rows) then agent-scope pages after the cursor return all 199 agent spans with zero overlap; a turn's app read by its traces returns exactly its 1,930 app spans; the projected attribute map on an SQL span keeps only server.address.
  • Nanosecond cursor comparison verified against a real ClickHouse (DateTime64(9) vs string literal: strict, exact at the boundary).
  • Not verified in a browser: the local dev sign-in needs a password entered, which I leave to you. The worktree stack is up on localhost:3471 / 3472 with the synthetic session ingested if you want to click through.

🤖 Generated with Claude Code


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

…warehouse

The detail page read a session in one response capped at 2,000 spans and
called anything past that truncated. The largest session in the warehouse
has ~209k spans in one trace, a tenth of them the agent's own; one page
showed its first minute.

The `spans` read now pages by a keyset cursor on its own (timestamp, spanId)
order — `nextCursor` replaces `truncated` — and takes a `scope` (all, the
agent's spans, or the app's), `traceIds` for a turn's traces without session
detection, and a `limit`. A new `summary` read returns the whole session's
totals from an ungrouped aggregate, with per-turn rows grouped by
conversation id (falling back to the trace) beside it; usage is summed over
all spans and over model calls alone so the handler can apply the page's
deepest-reporter rule.

The page loads the opening whole — a session that fits is complete after one
read, as before — and continues with the agent's spans alone, fetching a
turn's app spans from the Trace view's header on demand. The Overview leads
with the warehouse totals when the session is only partly loaded, the
transcript ends on a load-more divider, and a deep link's bounds are stamped
from the totals so a cut-short window is never written into the URL.
…nt-sessions-paged-spans

# Conflicts:
#	apps/web/src/api/warehouse/ai-sessions.ts
#	apps/web/src/components/agent-sessions/session-detail/session-transcript.tsx
#	apps/web/src/components/agent-sessions/session-detail/session-views.tsx
#	apps/web/src/lib/agent-sessions/session-transcript.ts
#	packages/query-engine-integrations/src/ai/ai-sessions.ts
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