feat(hierarchy): supporto metadati strutturati e gerarchici su Qdrant (parent_id, level, topic, links)
This commit is contained in:
+68
-5
@@ -207,6 +207,23 @@ export default function qmemExtension(pi: ExtensionAPI) {
|
||||
),
|
||||
supersedes_id: Type.Optional(Type.String({ description: "UUID del record da supersedere (correzione): il nuovo record diventa la versione attiva, il vecchio resta in archivio marcato superseded." })),
|
||||
supersede_reason: Type.Optional(Type.String({ description: "Motivo della correzione (visibile in audit e sul vecchio record)." })),
|
||||
parent_id: Type.Optional(Type.String({ description: "UUID del record genitore per organizzazione gerarchica/subtopic." })),
|
||||
level: Type.Optional(
|
||||
Type.Union([Type.Literal("L1_ROOT"), Type.Literal("L2_SUBTOPIC"), Type.Literal("L3_DETAIL")], {
|
||||
description: "Livello gerarchico (L1_ROOT = indice macro-topic, L2_SUBTOPIC = dettaglio specialistico, L3_DETAIL).",
|
||||
}),
|
||||
),
|
||||
topic: Type.Optional(Type.String({ description: "Topic ID gerarchico (es. ALFA-ROMEO-GT-1300-JUNIOR/SPECS)." })),
|
||||
links: Type.Optional(
|
||||
Type.Array(
|
||||
Type.Object({
|
||||
target_id: Type.String({ description: "UUID del record target collegato." }),
|
||||
predicate: Type.Optional(Type.String({ description: "Tipo di relazione (parent_of, part_of, relates_to, supersedes...)." })),
|
||||
weight: Type.Optional(Type.Number({ description: "Peso della relazione (default: 1.0)." })),
|
||||
}),
|
||||
{ description: "Collegamenti relazionali espliciti verso altri record." },
|
||||
),
|
||||
),
|
||||
}),
|
||||
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
||||
const cfg = loadConfig();
|
||||
@@ -245,6 +262,10 @@ export default function qmemExtension(pi: ExtensionAPI) {
|
||||
expires_at: p.expires_at,
|
||||
supersedes_id: p.supersedes_id,
|
||||
supersede_reason: p.supersede_reason,
|
||||
parent_id: p.parent_id,
|
||||
level: p.level,
|
||||
topic: p.topic,
|
||||
links: p.links,
|
||||
},
|
||||
signal,
|
||||
idemKey,
|
||||
@@ -261,9 +282,10 @@ export default function qmemExtension(pi: ExtensionAPI) {
|
||||
dupes.map((d: any) => ` - ${d.memory_id} (score ${d.score}): ${String(d.text).slice(0, 100)}`).join("\n") +
|
||||
"\nValuta se il nuovo record è davvero necessario o se conviene qmem_correct sul duplicato."
|
||||
: "";
|
||||
const extra = `${p.level ? `, level ${p.level}` : ""}${p.topic ? `, topic ${p.topic}` : ""}${p.parent_id ? ` — parent ${p.parent_id}` : ""}`;
|
||||
return {
|
||||
content: [{ type: "text", text: `Memoria salvata: ${data.memory_id} (${p.kind ?? "fact"}, scope ${p.scope ?? "agent"})${p.supersedes_id ? ` — supersede ${p.supersedes_id}` : ""}${dupWarning}` }],
|
||||
details: { memory_id: data.memory_id, created_at: data.created_at, duplicates: dupes.length },
|
||||
content: [{ type: "text", text: `Memoria salvata: ${data.memory_id} (${p.kind ?? "fact"}, scope ${p.scope ?? "agent"}${extra})${p.supersedes_id ? ` — supersede ${p.supersedes_id}` : ""}${dupWarning}` }],
|
||||
details: { memory_id: data.memory_id, created_at: data.created_at, duplicates: dupes.length, parent_id: p.parent_id, level: p.level, topic: p.topic },
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -318,6 +340,13 @@ export default function qmemExtension(pi: ExtensionAPI) {
|
||||
"non come similarità. min_score resta applicato al ramo vettoriale (anti-rumore).",
|
||||
}),
|
||||
),
|
||||
parent_id: Type.Optional(Type.String({ description: "Filtra per UUID del record genitore." })),
|
||||
level: Type.Optional(
|
||||
Type.Union([Type.Literal("L1_ROOT"), Type.Literal("L2_SUBTOPIC"), Type.Literal("L3_DETAIL")], {
|
||||
description: "Filtra per livello gerarchico.",
|
||||
}),
|
||||
),
|
||||
topic: Type.Optional(Type.String({ description: "Filtra per topic esatto." })),
|
||||
}),
|
||||
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
||||
const cfg = loadConfig();
|
||||
@@ -342,6 +371,9 @@ export default function qmemExtension(pi: ExtensionAPI) {
|
||||
min_score: p.min_score ?? 0.45,
|
||||
top_k: p.top_k ?? 5,
|
||||
hybrid: p.hybrid ?? false,
|
||||
parent_id: p.parent_id,
|
||||
level: p.level,
|
||||
topic: p.topic,
|
||||
},
|
||||
signal,
|
||||
);
|
||||
@@ -364,8 +396,13 @@ export default function qmemExtension(pi: ExtensionAPI) {
|
||||
};
|
||||
}
|
||||
const lines = results.map(
|
||||
(r: any, i: number) =>
|
||||
`${i + 1}. [${r.kind}/${r.scope} score=${r.score}${r.score < 0.6 ? " ⚠️" : ""}${r.confidence ? ` conf=${r.confidence}` : ""}] ${r.text}\n (id: ${r.memory_id}, agente: ${r.agent_id ?? "?"}, creato: ${r.created_at ?? "?"}${r.source ? `, fonte: ${r.source}` : ""}${r.supersedes_id ? `, supersede ${r.supersedes_id}` : ""}${r.superseded_by ? `, ⚠️ superseduto da ${r.superseded_by}` : ""})`,
|
||||
(r: any, i: number) => {
|
||||
const lvl = r.level ? ` [${r.level}]` : "";
|
||||
const top = r.topic ? ` (${r.topic})` : "";
|
||||
const parent = r.parent_id ? `, parent: ${r.parent_id}` : "";
|
||||
const links = r.links && r.links.length > 0 ? `, links: ${r.links.length}` : "";
|
||||
return `${i + 1}. [${r.kind}/${r.scope}${lvl}${top} score=${r.score}${r.score < 0.6 ? " ⚠️" : ""}${r.confidence ? ` conf=${r.confidence}` : ""}] ${r.text}\n (id: ${r.memory_id}${parent}${links}, agente: ${r.agent_id ?? "?"}, creato: ${r.created_at ?? "?"}${r.source ? `, fonte: ${r.source}` : ""}${r.supersedes_id ? `, supersede ${r.supersedes_id}` : ""}${r.superseded_by ? `, ⚠️ superseduto da ${r.superseded_by}` : ""})`;
|
||||
},
|
||||
);
|
||||
return {
|
||||
content: [{ type: "text", text: lines.join("\n") }],
|
||||
@@ -410,6 +447,23 @@ export default function qmemExtension(pi: ExtensionAPI) {
|
||||
}),
|
||||
),
|
||||
agent_id: Type.Optional(Type.String({ description: "Nome dell'agente che corregge (solo provenienza)." })),
|
||||
parent_id: Type.Optional(Type.String({ description: "UUID del record genitore (default: eredita dal record superseduto se presente)." })),
|
||||
level: Type.Optional(
|
||||
Type.Union([Type.Literal("L1_ROOT"), Type.Literal("L2_SUBTOPIC"), Type.Literal("L3_DETAIL")], {
|
||||
description: "Livello gerarchico del nuovo record.",
|
||||
}),
|
||||
),
|
||||
topic: Type.Optional(Type.String({ description: "Topic ID gerarchico del nuovo record." })),
|
||||
links: Type.Optional(
|
||||
Type.Array(
|
||||
Type.Object({
|
||||
target_id: Type.String(),
|
||||
predicate: Type.Optional(Type.String()),
|
||||
weight: Type.Optional(Type.Number()),
|
||||
}),
|
||||
{ description: "Collegamenti relazionali espliciti." },
|
||||
),
|
||||
),
|
||||
}),
|
||||
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
||||
const cfg = loadConfig();
|
||||
@@ -496,6 +550,10 @@ export default function qmemExtension(pi: ExtensionAPI) {
|
||||
source: "qmem_correct",
|
||||
supersedes_id: memoryId,
|
||||
supersede_reason: p.reason,
|
||||
parent_id: p.parent_id ?? orig.parent_id,
|
||||
level: p.level ?? orig.level,
|
||||
topic: p.topic ?? orig.topic,
|
||||
links: p.links ?? orig.links,
|
||||
},
|
||||
signal,
|
||||
idemKey,
|
||||
@@ -610,10 +668,12 @@ export default function qmemExtension(pi: ExtensionAPI) {
|
||||
}
|
||||
const r = data ?? {};
|
||||
const meta = [
|
||||
`[${r.kind ?? "?"}/${r.scope ?? "?"}${r.confidence ? ` conf=${r.confidence}` : ""}]`,
|
||||
`[${r.kind ?? "?"}/${r.scope ?? "?"}${r.level ? ` ${r.level}` : ""}${r.topic ? ` (${r.topic})` : ""}${r.confidence ? ` conf=${r.confidence}` : ""}]`,
|
||||
`project: ${r.project_id ?? "?"}`,
|
||||
`agente: ${r.agent_id ?? "?"}, creato: ${r.created_at ?? "?"}${r.source ? `, fonte: ${r.source}` : ""}`,
|
||||
];
|
||||
if (r.parent_id) meta.push(`parent: ${r.parent_id}`);
|
||||
if (r.links && r.links.length > 0) meta.push(`links: ${r.links.length}`);
|
||||
if (r.supersedes_id) meta.push(`supersede ${r.supersedes_id}`);
|
||||
if (r.superseded_by) meta.push(`⚠️ SUPERSEDUTO da ${r.superseded_by}`);
|
||||
if (r.expires_at) meta.push(`scade: ${r.expires_at}`);
|
||||
@@ -625,6 +685,9 @@ export default function qmemExtension(pi: ExtensionAPI) {
|
||||
kind: r.kind,
|
||||
scope: r.scope,
|
||||
project_id: r.project_id,
|
||||
parent_id: r.parent_id,
|
||||
level: r.level,
|
||||
topic: r.topic,
|
||||
superseded_by: r.superseded_by ?? null,
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user