feat(confidence-engine): preserve initial reconstruction relationships
This commit is contained in:
+42
-9
@@ -22,6 +22,12 @@ export function buildInitialGraph(analysisData) {
|
||||
}
|
||||
|
||||
const nodeMap = new Map(); // label -> node
|
||||
const sourceIdToNodeId = new Map();
|
||||
|
||||
function mapSourceId(sourceId, node) {
|
||||
if (sourceId) sourceIdToNodeId.set(sourceId, node.id);
|
||||
return node;
|
||||
}
|
||||
|
||||
// ── Helper: register or get a node by label ────────────
|
||||
|
||||
@@ -97,14 +103,17 @@ export function buildInitialGraph(analysisData) {
|
||||
obs.confidence || "medium",
|
||||
);
|
||||
|
||||
if (obs.id) node.evidenceIds.push(obs.id);
|
||||
if (obs.id) {
|
||||
node.evidenceIds.push(obs.id);
|
||||
mapSourceId(obs.id, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actors as states/nodes
|
||||
if (reconstruction.actors) {
|
||||
for (const actor of reconstruction.actors) {
|
||||
ensureNode(
|
||||
mapSourceId(actor.id, ensureNode(
|
||||
actor.description || actor.label,
|
||||
"observation",
|
||||
"supported",
|
||||
@@ -112,13 +121,13 @@ export function buildInitialGraph(analysisData) {
|
||||
null,
|
||||
null,
|
||||
actor.confidence || "medium",
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (reconstruction.systemsOrObjects) {
|
||||
for (const sys of reconstruction.systemsOrObjects) {
|
||||
ensureNode(
|
||||
mapSourceId(sys.id, ensureNode(
|
||||
sys.description || sys.label,
|
||||
"metric",
|
||||
"known",
|
||||
@@ -126,7 +135,7 @@ export function buildInitialGraph(analysisData) {
|
||||
null,
|
||||
null,
|
||||
sys.confidence || "medium",
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +151,7 @@ export function buildInitialGraph(analysisData) {
|
||||
null,
|
||||
diff.confidence || "medium",
|
||||
);
|
||||
mapSourceId(diff.id, node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,6 +167,7 @@ export function buildInitialGraph(analysisData) {
|
||||
null,
|
||||
c.confidence || "medium",
|
||||
);
|
||||
mapSourceId(c.id, node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,6 +184,7 @@ export function buildInitialGraph(analysisData) {
|
||||
null,
|
||||
unk.confidence || "low",
|
||||
);
|
||||
mapSourceId(unk.id, node);
|
||||
unknownNodes.push(node);
|
||||
}
|
||||
}
|
||||
@@ -180,7 +192,7 @@ export function buildInitialGraph(analysisData) {
|
||||
// Plausible interpretations
|
||||
if (reconstruction.plausibleInterpretations) {
|
||||
for (const interp of reconstruction.plausibleInterpretations) {
|
||||
ensureNode(
|
||||
mapSourceId(interp.id, ensureNode(
|
||||
interp.description || interp.label,
|
||||
"assumption",
|
||||
"provisional",
|
||||
@@ -188,14 +200,14 @@ export function buildInitialGraph(analysisData) {
|
||||
null,
|
||||
null,
|
||||
interp.confidence || "low",
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Known transitions
|
||||
if (reconstruction.knownTransitions) {
|
||||
for (const trans of reconstruction.knownTransitions) {
|
||||
ensureNode(
|
||||
mapSourceId(trans.id, ensureNode(
|
||||
`${trans.entity}: ${trans.previousState} → ${trans.currentState}`,
|
||||
"transition",
|
||||
trans.explanationStatus === "confirmed" ? "known" : "provisional",
|
||||
@@ -204,7 +216,7 @@ export function buildInitialGraph(analysisData) {
|
||||
null,
|
||||
null,
|
||||
trans.confidence || "medium",
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,6 +259,27 @@ export function buildInitialGraph(analysisData) {
|
||||
}
|
||||
}
|
||||
|
||||
const edgeIds = new Set(edges.map((edge) => edge.id));
|
||||
for (const relationship of reconstruction.relationships ?? []) {
|
||||
const fromNodeId = sourceIdToNodeId.get(relationship.fromId);
|
||||
const toNodeId = sourceIdToNodeId.get(relationship.toId);
|
||||
const id = `e-rel-${relationship.id}`;
|
||||
|
||||
if (!fromNodeId || !toNodeId || edgeIds.has(id)) continue;
|
||||
|
||||
edges.push(
|
||||
situationEdgeSchema.parse({
|
||||
id,
|
||||
fromNodeId,
|
||||
toNodeId,
|
||||
relationship: relationship.relationship,
|
||||
confidence: relationship.confidence,
|
||||
description: relationship.description,
|
||||
}),
|
||||
);
|
||||
edgeIds.add(id);
|
||||
}
|
||||
|
||||
return { nodes: nodeArr, edges };
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@ const __dirname = dirname(__filename);
|
||||
const PROMPTS_DIR = join(__dirname, "../../prompts");
|
||||
|
||||
/** Available prompt versions */
|
||||
export const PROMPT_VERSIONS = ["v0.1", "v0.2", "v0.3", "v0.4"];
|
||||
export const PROMPT_VERSIONS = ["v0.1", "v0.2", "v0.3", "v0.4", "v0.5"];
|
||||
|
||||
/** Default prompt version (override via RECONSTRUCTION_PROMPT_VERSION env var) */
|
||||
const defaultVersionFromEnv = process.env.RECONSTRUCTION_PROMPT_VERSION;
|
||||
export const DEFAULT_PROMPT_VERSION =
|
||||
defaultVersionFromEnv && PROMPT_VERSIONS.includes(defaultVersionFromEnv)
|
||||
? defaultVersionFromEnv
|
||||
: "v0.4";
|
||||
: "v0.5";
|
||||
|
||||
/** Build a v0.1 (extraction-only) prompt inline for backward compatibility */
|
||||
function buildV1Prompt(scenario) {
|
||||
@@ -91,15 +91,29 @@ async function buildV4Prompt(scenario) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Load a versioned prompt from disk and substitute {{SCENARIO}} */
|
||||
async function buildV5Prompt(scenario) {
|
||||
try {
|
||||
const content = await fs.readFile(
|
||||
join(PROMPTS_DIR, "reconstruct-v0.5.md"),
|
||||
"utf-8",
|
||||
);
|
||||
return content.replace("{{SCENARIO}}", scenario);
|
||||
} catch {
|
||||
// Fall back to v0.4 prompt if v0.5 file is missing
|
||||
return buildV4Prompt(scenario);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an analysis prompt for the given version.
|
||||
* @param {string} scenario - The scenario text
|
||||
* @param {"v0.1" | "v0.2" | "v0.3" | "v0.4"} [version="v0.4"] - Prompt version
|
||||
* @param {"v0.1" | "v0.2" | "v0.3" | "v0.4" | "v0.5"} [version="v0.5"] - Prompt version
|
||||
* @param {object} [opts] - Optional experimental parameters
|
||||
* @param {string} [opts.experimentInstruction] - Bounded experimental instruction block appended to the base prompt (production prompt is never replaced)
|
||||
* @returns {Promise<{prompt: string, version: string}>}
|
||||
*/
|
||||
export async function buildPrompt(scenario, version = "v0.4", opts = {}) {
|
||||
export async function buildPrompt(scenario, version = "v0.5", opts = {}) {
|
||||
let prompt;
|
||||
switch (version) {
|
||||
case "v0.1":
|
||||
@@ -111,6 +125,9 @@ export async function buildPrompt(scenario, version = "v0.4", opts = {}) {
|
||||
case "v0.4":
|
||||
prompt = await buildV4Prompt(scenario);
|
||||
break;
|
||||
case "v0.5":
|
||||
prompt = await buildV5Prompt(scenario);
|
||||
break;
|
||||
default: // v0.3
|
||||
prompt = await buildV3Prompt(scenario);
|
||||
break;
|
||||
|
||||
@@ -126,6 +126,24 @@ const evidenceRecordSchema = z.object({
|
||||
importance: importanceEnum,
|
||||
});
|
||||
|
||||
const reconstructionRelationshipSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
fromId: z.string().min(1),
|
||||
toId: z.string().min(1),
|
||||
relationship: z.enum([
|
||||
"supports",
|
||||
"weakens",
|
||||
"contradicts",
|
||||
"depends_on",
|
||||
"causes",
|
||||
"may_cause",
|
||||
"compares_with",
|
||||
"other",
|
||||
]),
|
||||
description: z.string().min(1),
|
||||
confidence: confidenceEnum,
|
||||
});
|
||||
|
||||
const reconstructionSchemaV2 = z.object({
|
||||
summary: z.string().min(1),
|
||||
actors: z.array(itemSchemaV1),
|
||||
@@ -159,6 +177,7 @@ const reconstructionSchemaV2 = z.object({
|
||||
confidence: confidenceEnum,
|
||||
}),
|
||||
),
|
||||
relationships: z.array(reconstructionRelationshipSchema).optional().default([]),
|
||||
});
|
||||
|
||||
const inputClassificationSchema = z.object({
|
||||
|
||||
Reference in New Issue
Block a user