Skip to content

Commit babee52

Browse files
committed
fix(consolidation): make the dedup cache run-level (persist across fetch iterations)
The per-scope dedup cache was created inside _process_tag_group, but that closure is redefined each fetch iteration of the consolidation loop — so the cache reset every ~100 memories and cross-fetch-iteration near-duplicates (the same topic recurring across a long document) slipped through (residual ~4-6% @0.97). Move the cache to a run-level dict keyed by tag group, persisting across all fetch iterations. A scope's batches run serially across iterations so the per-scope list stays race-free.
1 parent 9839caf commit babee52

1 file changed

Lines changed: 19 additions & 7 deletions

File tree

hindsight-api-slim/hindsight_api/engine/consolidation/consolidator.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,12 @@ async def _run_consolidation_job(
650650
# the per-batch log so it can still report processed/total under parallelism.
651651
# Mutable container so the inner closure can update without a `nonlocal`.
652652
cumulative_progress = {"processed": 0}
653+
# Semantic-dedup caches, keyed by tag group (scope), persisting across ALL fetch
654+
# iterations of this run — so a later create dedups against an earlier same-run create
655+
# even when they land in different fetch batches (the DB probe misses same-run rows).
656+
# A scope's batches run serially across iterations, so a per-scope list is race-free;
657+
# distinct scopes get distinct lists (and may run in parallel) so there's no shared state.
658+
run_dedup_caches: dict[tuple[str, ...], list[_RunObs]] = {}
653659
while True:
654660
# Cap fetch size by remaining round budget
655661
fetch_limit = (
@@ -926,15 +932,18 @@ async def _process_one_llm_batch(
926932
llm_batch_num += 1
927933
numbered.append((b, llm_batch_num))
928934
numbered_groups.append(numbered)
935+
# Aligned with numbered_groups (both derive from tag_groups in insertion order).
936+
group_keys = list(tag_groups.keys())
929937

930938
async def _process_tag_group(
931939
group_batches: list[tuple[list[dict[str, Any]], int]],
940+
group_key: tuple[str, ...],
932941
) -> list[_BatchDeltas]:
933942
# Batches within a group share a tag set and observation scope, so
934943
# they MUST run serially. Stop early if the op was cancelled mid-group.
935-
# One semantic-dedup cache per group: its batches run serially (no race) and
936-
# share a scope, so a create can dedup against earlier same-run creates.
937-
group_dedup_cache: list[_RunObs] = []
944+
# The semantic-dedup cache is per scope and persists across fetch iterations
945+
# (run_dedup_caches), so creates dedup against earlier same-run creates.
946+
group_dedup_cache = run_dedup_caches.setdefault(group_key, [])
938947
deltas: list[_BatchDeltas] = []
939948
for b, n in group_batches:
940949
d = await _process_one_llm_batch(b, n, group_dedup_cache)
@@ -958,21 +967,24 @@ async def _process_tag_group(
958967
async def _run_group(
959968
group_batches: list[tuple[list[dict[str, Any]], int]],
960969
scopes: list[frozenset[str]],
970+
group_key: tuple[str, ...],
961971
) -> list[_BatchDeltas]:
962972
async with sem:
963973
async with AsyncExitStack() as stack:
964974
for s in scopes:
965975
await stack.enter_async_context(scope_locks[s])
966-
return await _process_tag_group(group_batches)
976+
return await _process_tag_group(group_batches, group_key)
967977

968-
group_results = await asyncio.gather(*(_run_group(g, s) for g, s in zip(numbered_groups, group_scopes)))
978+
group_results = await asyncio.gather(
979+
*(_run_group(g, s, k) for g, s, k in zip(numbered_groups, group_scopes, group_keys))
980+
)
969981
batch_results: list[_BatchDeltas] = [d for gd in group_results for d in gd]
970982
any_cancelled = any(d.cancelled for d in batch_results)
971983
else:
972984
batch_results = []
973985
any_cancelled = False
974-
for g in numbered_groups:
975-
group_deltas = await _process_tag_group(g)
986+
for g, k in zip(numbered_groups, group_keys):
987+
group_deltas = await _process_tag_group(g, k)
976988
batch_results.extend(group_deltas)
977989
if any(d.cancelled for d in group_deltas):
978990
any_cancelled = True

0 commit comments

Comments
 (0)