Conversation
…ousResearch#23724) On the modern Hindsight API (>=0.5.0) sync_turn() uses update_mode='append' against a stable session-scoped document_id, so the server preserves prior content across retains. However, _session_turns was never cleared after a successful enqueue, so every subsequent retain re-shipped the full growing transcript. With retain_every_n_turns=N the server ended up with each turn appended ceil(total_turns/N) times, producing duplicate chunks and ~80% unnecessary extraction tokens. Clear _session_turns after building content only when update_mode='append'. On the legacy path (update_mode is None, per-process document_id, replace semantics) the full session must keep being resent, so the buffer stays intact in that case. The session-switch flush path (on_session_switch) already snapshots and clears explicitly, so it is unaffected.
This was referenced May 11, 2026
This was referenced May 24, 2026
Contributor
Author
|
Friendly bump 🙂 — this one's been quiet for ~3 weeks but is still mergeable with no conflicts. The fix is small (+81 LoC, no deletions) and self-contained to the hindsight delta path. Happy to rebase or add tests if useful. |
Contributor
Author
|
Closing to keep our open PR count manageable per the repo contribution guidelines. The underlying issue (#23686 / append-mode delta) remains open; happy to revive this PR if a maintainer wants to pick it up. Thanks! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #23724.
On the modern Hindsight API (≥ 0.5.0)
sync_turn()usesupdate_mode='append'against a stable session-scoped
document_id, so the server preserves priordocument content across retains. However,
_session_turnswas never clearedafter a successful retain enqueue, so every subsequent retain re-shipped the
full growing transcript instead of just the new turns.
With
retain_every_n_turns=N, retain #k sends turns 1..k·N, so turniendsup appended to the document
⌈(total_turns − i + 1)/N⌉times. The downstreamchunker/extractor then produces 3-4× duplicate facts per session and burns
~80% extra extraction tokens (see issue for measurements on a live document).
Fix
After building
contentinsync_turn(), clear_session_turnsiff we usedupdate_mode='append':That's the entire behavior change. The next retain starts with an empty buffer
and ships only the turns accumulated since the last successful enqueue.
Why conditional on
update_mode == "append"_resolve_retain_targetreturnsupdate_mode=Noneon the legacy path(Hindsight < 0.5.0). There the per-process
document_idis unique perprocess lifecycle and each retain replaces the document (preserving the
#6654resume-overwrite fix). On that path we MUST keep resending the fullsession — clearing the buffer would make the next retain overwrite the
document with only the new turns and silently lose earlier content.
The session-switch flush path (
on_session_switch,c38dac74) alreadysnapshots
_session_turnsand clears it explicitly after enqueueing theflush, so it's unaffected.
Relationship to #20664
I'm aware #20664 is open and addresses the same bug. Its approach is to
switch the modern path from
update_mode='append'on the session documentback to
update_mode='replace'on the per-process document.This PR takes the opposite, minimal approach: keep append semantics (which
is the documented direction of travel — see
3082fa0andvectorize-io/hindsight#932 / #1303), and instead fix the client to actually
honor them by only sending deltas.
Reasons to prefer this approach:
3082fa0, no changes to capability probing, no changes todocument_idselection.
update_mode='append'on a stable session-scoped
document_idwas introduced to enable. Goingback to per-process
replacedocuments loses that.(
retain_every_n_turnsturns), not the entire growing session. This isthe original motivation for batching.
exactly this: build content from the buffered turns, then clear the
buffer.
sync_turn()was the asymmetric outlier.Happy to defer to #20664 if maintainers prefer the replace-semantics
direction; flagging here so the choice is explicit.
Testing
Two new tests added under
TestUpdateModeAppendCapability:test_modern_api_sends_only_new_turns_on_subsequent_retain— with the/version probe stubbed to
0.5.6, verifies that the second retain batchcontains turns 3-4 only, NOT turns 1-2 (which the server already has).
test_legacy_api_still_sends_full_session_on_each_retain— with /versionstubbed to
None(legacy server), verifies that the second retain stillcontains the full session 1-4 AND has no
update_modekey, so theresume-overwrite fix keeps working on older Hindsight deployments.
The existing
test_sync_turn_accumulates_full_sessionstill passes — itruns without monkeypatching the probe, falls back to legacy mode, and
exercises the same full-session-resend behavior the new legacy-API test
codifies explicitly.
Full memory plugin suite and session-switch tests also green:
Tested on macOS 14 / Python 3.11.
Risk
update_mode='append') path. Legacyservers are untouched and covered by the new explicit test.
on_session_switchalready uses the same content/clear pattern, so thesemantics are now symmetric across the two retain entry points instead of
asymmetric.