her / engine /core /rereads.py
geekwrestler's picture
Squash history (purge pre-scrub demo session blobs)
5f43c7d
Raw
History Blame Contribute Delete
1.83 kB
"""rereads.py — re-read detection within a turn.
Pattern: the same Read `file_path` opened >= 3x WITHIN a single turn → (file, count).
Counted on the full path; the reported `file` is the basename for display, but the
count is over identical full paths (so two different files with the same basename
do not merge). Pure code, NO model.
On the fixture this fires exactly once: turn 12 → apply.js x4.
"""
from __future__ import annotations
import os
from collections import Counter
from dataclasses import dataclass
from typing import Any
_REREAD_MIN = 3
@dataclass
class Reread:
turn: int
file_path: str # the full path that was re-read
file: str # basename, for display
count: int
def _read_path(tc: Any):
if tc.name != "Read":
return None
inp = tc.input if isinstance(tc.input, dict) else {}
p = inp.get("file_path")
return p if isinstance(p, str) and p else None
def detect_rereads(turn) -> list[Reread]:
"""Return re-read patterns (>= 3 reads of the same path) for one turn."""
counts: Counter = Counter()
for tc in turn.tools:
p = _read_path(tc)
if p is not None:
counts[p] += 1
out: list[Reread] = []
for path, n in counts.items():
if n >= _REREAD_MIN:
out.append(
Reread(turn=turn.i, file_path=path, file=os.path.basename(path), count=n)
)
# deterministic order: highest count first, then basename
out.sort(key=lambda r: (-r.count, r.file))
return out
def detect_all_rereads(turns) -> dict[int, list[Reread]]:
"""Map turn index → re-read patterns, only for turns where the pattern fires."""
result: dict[int, list[Reread]] = {}
for t in turns:
rr = detect_rereads(t)
if rr:
result[t.i] = rr
return result