Intentional changes in this checkpoint: - Deconstruct route: use body.targetNodeId (client identity) over raw.model-invented ID - ThreadContributionsBadge: compact per-thread contribution indicator with expandable history - Reopen continuation: resume from accumulated contributions instead of reformulating - showEvidenceLimit gate: hide evidence-limit card during active investigation paths - Evidence-limit visibility correction in rendering pipeline - Section ordering: assumptions and connections after 'Still unclear' in focused result - Prompt v0.3: preserve user-stated alternatives as separate unknowns; no count inflation - 3 durable regression tests (target identity, contribution persistence, reopen state) - evidence-limit card visibility gate test suite Temporary residue removed: - test-analysis.mjs (scratch diagnostic) - 5 diagnostic console.log blocks from reasoning-workspace.jsx
90 lines
2.8 KiB
JavaScript
90 lines
2.8 KiB
JavaScript
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: body.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 },
|
|
);
|
|
}
|
|
}
|