155 lines
5.0 KiB
JavaScript
155 lines
5.0 KiB
JavaScript
import { getProvider, getProviderModelName } from "@/lib/llm/provider";
|
|
import {
|
|
buildFocusedDeconstructPrompt,
|
|
focusedDeconstructJsonSchema,
|
|
validateFocusedDeconstructSchema,
|
|
} from "@/lib/graph/focused-investigation";
|
|
|
|
export async function POST(request) {
|
|
let targetNodeId = null;
|
|
let startedAt = null;
|
|
try {
|
|
const body = await request.json();
|
|
targetNodeId = body.targetNodeId ?? null;
|
|
|
|
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 modelName = getProviderModelName();
|
|
startedAt = Date.now();
|
|
console.info("[api/focused-investigation/deconstruct] start", {
|
|
targetNodeId,
|
|
modelName,
|
|
providerMode: process.env.CONFIDENCE_ENGINE_EXPERIMENT_PROVIDER ?? "ollama",
|
|
startedAt,
|
|
});
|
|
const wrapper = await provider.generateReconstruction(
|
|
prompt,
|
|
modelName,
|
|
focusedDeconstructJsonSchema,
|
|
);
|
|
const elapsedMs = Date.now() - startedAt;
|
|
|
|
// Unwrap the semantic deconstruction from the provider envelope.
|
|
const deconstruction = wrapper.response;
|
|
console.info("[api/focused-investigation/deconstruct] provider success", {
|
|
targetNodeId,
|
|
elapsedMs,
|
|
providerApiPath: wrapper.providerApiPath ?? null,
|
|
responsePresent: Boolean(deconstruction),
|
|
responseKeys: deconstruction && typeof deconstruction === "object"
|
|
? Object.keys(deconstruction)
|
|
: [],
|
|
});
|
|
|
|
// Validate schema (required fields present, no graph-mutation fields)
|
|
const validationErrors = validateFocusedDeconstructSchema(deconstruction);
|
|
console.info("[api/focused-investigation/deconstruct] validation", {
|
|
targetNodeId,
|
|
schemaValid: validationErrors.length === 0,
|
|
responseKeys: deconstruction && typeof deconstruction === "object"
|
|
? Object.keys(deconstruction)
|
|
: [],
|
|
validationErrors,
|
|
});
|
|
if (validationErrors.length > 0) {
|
|
console.info("[api/focused-investigation/deconstruct] end", {
|
|
targetNodeId,
|
|
status: 502,
|
|
elapsedMs,
|
|
});
|
|
return Response.json(
|
|
{
|
|
success: false,
|
|
error: "Focused deconstruction result did not match expected schema",
|
|
validationErrors,
|
|
targetNodeId: body.targetNodeId,
|
|
elapsedMs,
|
|
},
|
|
{ status: 502 },
|
|
);
|
|
}
|
|
|
|
const response = Response.json({
|
|
success: true,
|
|
targetNodeId: body.targetNodeId,
|
|
observations: deconstruction.observations,
|
|
uncertainties: deconstruction.uncertainties,
|
|
assumptions: deconstruction.assumptions,
|
|
relationships: deconstruction.relationships,
|
|
possibleFollowUpQuestions: deconstruction.possibleFollowUpQuestions,
|
|
elapsedMs,
|
|
});
|
|
console.info("[api/focused-investigation/deconstruct] end", {
|
|
targetNodeId,
|
|
status: 200,
|
|
elapsedMs,
|
|
});
|
|
return response;
|
|
} catch (e) {
|
|
const elapsedMs = startedAt == null ? null : Date.now() - startedAt;
|
|
console.error("[api/focused-investigation/deconstruct] provider failure", {
|
|
targetNodeId,
|
|
elapsedMs,
|
|
errorName: e?.name ?? "Error",
|
|
errorMessage: e?.message ?? "Unknown server error",
|
|
statusCode: e?.statusCode ?? e?.status ?? null,
|
|
providerApiPath: e?.providerApiPath ?? null,
|
|
errorCode: e?.code ?? null,
|
|
errorParam: e?.param ?? null,
|
|
});
|
|
console.info("[api/focused-investigation/deconstruct] end", {
|
|
targetNodeId,
|
|
status: 500,
|
|
elapsedMs,
|
|
});
|
|
return Response.json(
|
|
{ error: e.message || "Unknown server error" },
|
|
{ status: 500 },
|
|
);
|
|
}
|
|
}
|