feat(reparenting): supersede di un record ri-parenta automaticamente i figli attivi
- gateway: nel blocco supersede, i figli attivi (parent_id == vecchio UUID, superseded_by vuoto) vengono ri-parentati al nuovo UUID; audit action 'reparent'; risposta con campo 'reparented'. Un solo livello: i nipoti puntano agli UUID dei figli, invariati. I figli superseduti restano storici ancorati alla vecchia lineage. - estensione: qmem_tree con fallback lineage (cerca figli anche per supersedes_id del root) per gli orfani pre-fix; qmem_correct riporta il numero di figli ri-parentati. - test: 2 nuovi (ri-parenta figli attivi; ri-parenta solo attivi con figlio già superseduto). 34 pass. - deploy su brain (10.8.0.3): main.py aggiornato, container ricreato, smoke test end-to-end OK (reparented=1).
This commit is contained in:
+32
-1
@@ -593,6 +593,7 @@ async def add_memory(
|
||||
)
|
||||
_invalidate_meta()
|
||||
|
||||
reparented_count = 0
|
||||
if superseded_id:
|
||||
qdrant.set_payload(
|
||||
collection_name=COLLECTION,
|
||||
@@ -604,6 +605,31 @@ async def add_memory(
|
||||
points=[superseded_id],
|
||||
)
|
||||
_audit(key, "supersede", old_id=superseded_id, new_id=memory_id, kind=body.kind, agent_id=payload["agent_id"])
|
||||
|
||||
# REPARENTING: i figli attivi del record superseduto seguono il nuovo UUID.
|
||||
# Solo i figli attivi (superseded_by vuoto): le versioni storiche restano
|
||||
# ancorate alla vecchia lineage; la loro versione attiva ha già ereditato
|
||||
# il parent_id (qmem_correct) e viene ri-parentata qui. Un solo livello:
|
||||
# i nipoti puntano agli UUID dei figli, che non cambiano.
|
||||
children, _ = qdrant.scroll(
|
||||
collection_name=COLLECTION,
|
||||
scroll_filter=qm.Filter(
|
||||
must=[
|
||||
qm.FieldCondition(key="parent_id", match=qm.MatchValue(value=superseded_id)),
|
||||
qm.IsEmptyCondition(is_empty=qm.PayloadField(key="superseded_by")),
|
||||
]
|
||||
),
|
||||
limit=1000,
|
||||
with_payload=False,
|
||||
)
|
||||
if children:
|
||||
qdrant.set_payload(
|
||||
collection_name=COLLECTION,
|
||||
payload={"parent_id": memory_id},
|
||||
points=[p.id for p in children],
|
||||
)
|
||||
reparented_count = len(children)
|
||||
_audit(key, "reparent", old_id=superseded_id, new_id=memory_id, count=reparented_count)
|
||||
else:
|
||||
_audit(
|
||||
key,
|
||||
@@ -613,7 +639,12 @@ async def add_memory(
|
||||
agent_id=payload["agent_id"],
|
||||
guardrail=payload.get("guardrail", {}).get("decision", "ALLOW"),
|
||||
)
|
||||
response = {"memory_id": memory_id, "created_at": payload["created_at"], "supersedes_id": superseded_id}
|
||||
response = {
|
||||
"memory_id": memory_id,
|
||||
"created_at": payload["created_at"],
|
||||
"supersedes_id": superseded_id,
|
||||
"reparented": reparented_count,
|
||||
}
|
||||
if idem_key:
|
||||
_idempotency[idem_key] = {"hash": _payload_hash(body), "response": response, "ts": time.time()}
|
||||
return response
|
||||
|
||||
Reference in New Issue
Block a user