feat(history): bound memory + O(turns) detail with turn pagination - #795
feat(history): bound memory + O(turns) detail with turn pagination#795sjawhar wants to merge 1 commit into
Conversation
Fixes admin dashboard slowness/OOM on large sessions reported by Sami. - List reads session_summaries (no full-payload scan); one-time preview backfill. - Detail/exports stream bounded-memory; O(turns) single-anchor request reconstruction (cumulative payloads parsed once, deltas sliced via raw->parsed prefix map); fast path with slow fallback for request-modified/non-monotonic. - Conversation-detail turn pagination (offset/limit window) so each request is bounded — this is the conversation-page lazy-loading deferred by LuthienResearch#752. - Frontend renders server-provided per-turn deltas directly; newest page loads first, scroll-up loads older; whole-session stats invariant across pages. Relates to LuthienResearch#752 (list cursor pagination) / LuthienResearch#753 (perf harness).
3106cbd to
553a135
Compare
|
Claude-generated merge-queue triage of all open Luthien PRs, requested by Scott (Jul 7, 2026). Advisory only; Scott has not yet acted on these recommendations. Recommendation: merge; top of the review queue. This is the most complete fix available for the reported dashboard slowness: it bounds server compute on the detail path (single-anchor reconstruction instead of re-parsing every cumulative payload), adds turn pagination, and moves the session list onto precomputed summaries, with no schema migration and strong test discipline (byte-identity tests of windowed vs. full builds, Postgres/SQLite parity). It was also verified live at the scale that hurts. Suggested path to merge:
Overlap note: #803 touches the same files with a smaller payload-focused fix; the plan is to resolve in favor of this PR (see the comment there). |
Summary
Makes the conversation-history detail path fast and bounded-memory for very large sessions (the admin-dashboard slowness/OOM Sami reported). Complements #752 (session-list cursor pagination) and #753 (perf harness): #752 explicitly defers conversation-page turn pagination to a follow-up — this PR is that follow-up, plus the underlying O(turns) read fix.
Problem
Request payloads are cumulative — each
transaction.request_recordedevent stores the entire conversation so far. The detail endpoint read+parsed every turn's full cumulative payload, so a full-session read was O(turns²). A 1,771-turn session returned 69MB and did not finish in 60s. In Postgres this is worsened by TOAST: reading any field detoasts the whole cumulative blob per row.Changes
Bounded memory
session_summaries(no full-payload scan) with a one-time preview backfill.O(turns) detail
ConversationMessages). Fast path with a correctness fallback for request-modified / non-monotonic sessions. Output is byte-identical to the per-turn build.Turn pagination (the piece #752 deferred)
GET /api/history/sessions/{id}takesoffset/limitover turns (omitoffset⇒ newest page); only the window's payloads are read, so each request is bounded.total_turns/ interventions / models are computed cheaply and stay constant across pages.Result
1,771-turn detail: ~110–217s → ~3s per page, bounded memory, rendered conversation unchanged.
Tests
Full suite 3101 passed, ruff + pyright clean. Coverage includes windowed-vs-full byte-identity at non-zero offset, boundary-delta correctness, whole-session stats invariance across windows, an O(window) boundedness guard, and Postgres+SQLite parity. Verified live in a real browser against the deployed build.