fix: harden reviewer workflow and memory handling
This commit is contained in:
+39
-17
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user