fix: harden reviewer workflow and memory handling

This commit is contained in:
2026-07-23 12:34:23 +02:00
parent 00761ae2ca
commit 694b7dd21f
23 changed files with 728 additions and 75 deletions
+39 -17
View File
@@ -115,14 +115,6 @@ def delete_record(path: Path, mem_id: str) -> MemoryRecord:
raise MemoryNotFound(mem_id)
def _default_tables(decision: DecisionRecord) -> list[str]:
if decision.type in ("table_promoted", "table_excluded"):
return [decision.subject]
if decision.type in ("column_corrected", "join_modified") and "." in decision.subject:
return [decision.subject.split(".")[0]]
return []
def _default_concepts(decision: DecisionRecord) -> list[str]:
if decision.type == "concept_clarified":
return [decision.subject]
@@ -139,9 +131,9 @@ def _next_id_num(existing: list[MemoryRecord]) -> int:
return (max(nums) + 1) if nums else 1
# Solo questi tipi di decisione sono concetti riusabili in altre generazioni
# (scelta reviewer): il resto e' query-specifico e non va proposto in promozione.
REUSABLE_TYPES = frozenset({"concept_clarified", "table_promoted", "table_excluded"})
# Solo i concetti chiariti sono riusabili tra domande. Le scelte sulle tabelle
# dipendono dallo schema-linking della singola domanda e non sono memory.
REUSABLE_TYPES = frozenset({"concept_clarified"})
MAX_PROMOTION_CANDIDATES = 5
@@ -159,6 +151,8 @@ def _compute_promotions(
context = question_context(decisions, manifest)
out: list[MemoryRecord] = []
for d in selected:
if d.type not in REUSABLE_TYPES:
continue
if (manifest.id, d.seq) in already:
continue
out.append(
@@ -166,7 +160,7 @@ def _compute_promotions(
id=f"mem-{n:04d}", ts=datetime.now(UTC), session_id=manifest.id,
decision_seq=d.seq, type=d.type, subject=d.subject, detail=d.detail,
rationale=d.rationale, question_context=context,
tables=_default_tables(d), concepts=_default_concepts(d),
tables=[], concepts=_default_concepts(d),
)
)
n += 1
@@ -206,9 +200,10 @@ def reusable_promotions(
session_dir, manifest, seqs=None, existing=load_registry(registry_path)
)
declined = declined_promotion_seqs(effective_decisions(session_dir))
return [
reusable = [
c for c in cand if c.type in REUSABLE_TYPES and c.decision_seq not in declined
]
return _dedupe_reusable_promotions(reusable)
def reusable_promotions_snapshot(snapshot, registry_path: Path) -> list[MemoryRecord]:
@@ -216,7 +211,30 @@ def reusable_promotions_snapshot(snapshot, registry_path: Path) -> list[MemoryRe
cand = _compute_promotions(snapshot, snapshot.manifest, seqs=None, existing=load_registry(registry_path))
declined = declined_promotion_seqs(effective_decisions(snapshot))
return [c for c in cand if c.type in REUSABLE_TYPES and c.decision_seq not in declined]
reusable = [c for c in cand if c.type in REUSABLE_TYPES and c.decision_seq not in declined]
return _dedupe_reusable_promotions(reusable)
def _dedupe_reusable_promotions(records: list[MemoryRecord]) -> list[MemoryRecord]:
"""Keep the first proposal for identical reviewer-visible memory content."""
seen: set[tuple[str, str, str, str, str]] = set()
out: list[MemoryRecord] = []
for record in records:
key = tuple(
" ".join(value.split()).casefold()
for value in (
record.type,
record.subject,
record.detail,
record.rationale,
record.question_context,
)
)
if key in seen:
continue
seen.add(key)
out.append(record)
return out
def preview_promotions(
@@ -235,6 +253,8 @@ def preview_promotions_snapshot(snapshot, registry_path: Path) -> list[MemoryRec
def memory_vector_records(records: list[MemoryRecord]) -> list[VectorRecord]:
out: list[VectorRecord] = []
for r in records:
if r.type not in REUSABLE_TYPES:
continue
lines = [
f"Decisione {r.type}: {r.subject}",
r.detail,
@@ -268,10 +288,12 @@ def memory_vector_record_for_decision(
D11 save-one builds only this one record (not the full memory_vector_records
list) so the remote upsert is a single row.
"""
match = [r for r in records if r.decision_seq == decision_seq]
if not match:
vectors = memory_vector_records(
[r for r in records if r.decision_seq == decision_seq]
)
if not vectors:
return None
return memory_vector_records(match)[0]
return vectors[0]
def save_one_memory(