feat(experiment): checkpoint focused investigation boundaries

This commit is contained in:
2026-08-19 05:41:32 +01:00
parent c0b963973f
commit 7add85d8d2
4 changed files with 466 additions and 0 deletions
@@ -0,0 +1,89 @@
import { getProvider } from "@/lib/llm/provider";
import { buildFocusedDeconstructPrompt, validateFocusedDeconstructSchema } from "@/lib/graph/focused-investigation";
export async function POST(request) {
try {
const body = await request.json();
if (!body.targetNodeId || typeof body.targetNodeId !== "string") {
return Response.json(
{ error: "Request must include a 'targetNodeId' string field" },
{ status: 400 },
);
}
if (!body.targetLabel || typeof body.targetLabel !== "string") {
return Response.json(
{ error: "Request must include a 'targetLabel' string field" },
{ status: 400 },
);
}
if (!body.targetDescription || typeof body.targetDescription !== "string") {
return Response.json(
{ error: "Request must include a 'targetDescription' string field" },
{ status: 400 },
);
}
if (!body.centralStatement || typeof body.centralStatement !== "string") {
return Response.json(
{ error: "Request must include a 'centralStatement' string field" },
{ status: 400 },
);
}
if (!body.question || typeof body.question !== "string") {
return Response.json(
{ error: "Request must include a 'question' string field" },
{ status: 400 },
);
}
if (!body.answer || typeof body.answer !== "string") {
return Response.json(
{ error: "Request must include an 'answer' string field" },
{ status: 400 },
);
}
const prompt = buildFocusedDeconstructPrompt({
targetLabel: body.targetLabel,
targetDescription: body.targetDescription,
centralStatement: body.centralStatement,
question: body.question,
answer: body.answer,
});
const provider = getProvider();
const startedAt = Date.now();
const raw = await provider.generateReconstruction(prompt, process.env.OLLAMA_MODEL);
const elapsedMs = Date.now() - startedAt;
// Validate schema (required fields present, no graph-mutation fields)
const validationErrors = validateFocusedDeconstructSchema(raw);
if (validationErrors.length > 0) {
return Response.json(
{
success: false,
error: "Focused deconstruction result did not match expected schema",
validationErrors,
targetNodeId: body.targetNodeId,
elapsedMs,
},
{ status: 502 },
);
}
return Response.json({
success: true,
targetNodeId: raw.targetNodeId,
observations: raw.observations,
uncertainties: raw.uncertainties,
assumptions: raw.assumptions,
relationships: raw.relationships,
possibleFollowUpQuestions: raw.possibleFollowUpQuestions,
elapsedMs,
});
} catch (e) {
return Response.json(
{ error: e.message || "Unknown server error" },
{ status: 500 },
);
}
}
@@ -0,0 +1,52 @@
import { formulateQuestionForTarget } from "@/lib/graph/focused-investigation";
export async function POST(request) {
try {
const body = await request.json();
if (!body.targetNodeId || typeof body.targetNodeId !== "string") {
return Response.json(
{ error: "Request must include a 'targetNodeId' string field" },
{ status: 400 },
);
}
if (!body.situationGraph || typeof body.situationGraph !== "object") {
return Response.json(
{ error: "Request must include a 'situationGraph' object field" },
{ status: 400 },
);
}
const result = formulateQuestionForTarget({
situationGraph: body.situationGraph,
targetNodeId: body.targetNodeId,
});
if (!result.success) {
return Response.json(
{ success: false, error: result.error },
{ status: 400 },
);
}
return Response.json({
success: true,
targetNodeId: result.targetNodeId,
question: result.question,
strategy: result.strategy,
reasoningPattern: result.reasoningPattern,
reasoningPatternReason: result.reasoningPatternReason,
reason: result.reason,
questionFamily: result.questionFamily,
selectedQuestionTemplate: result.selectedQuestionTemplate,
allowedQuestionFamilies: result.allowedQuestionFamilies,
rejectedQuestionFamilies: result.rejectedQuestionFamilies,
});
} catch (e) {
return Response.json(
{ error: e.message || "Unknown server error" },
{ status: 500 },
);
}
}