Files
confidence-engine/app/api/focused-investigation/deconstruct/route.js
T

154 lines
4.7 KiB
JavaScript

import { getProvider, getProviderModelName } from "@/lib/llm/provider";
import { focusedDeconstruct } from "@/lib/graph/focused-investigation";
import { withAuthenticatedApi } from "@/lib/supabase/api-auth.js";
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 REQUEST_LENGTH_LIMITS = {
answer: 10000,
question: 2048,
centralStatement: 2048,
targetLabel: 2048,
targetDescription: 2048,
};
for (const [field, limit] of Object.entries(REQUEST_LENGTH_LIMITS)) {
if (body[field] && body[field].length > limit) {
return Response.json(
{ error: `Request field "${field}" exceeds maximum length of ${limit} characters` },
{ status: 400 },
);
}
}
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 result = await focusedDeconstruct({
provider,
modelName,
targetNodeId,
targetLabel: body.targetLabel,
targetDescription: body.targetDescription,
centralStatement: body.centralStatement,
question: body.question,
answer: body.answer,
});
const elapsedMs = Date.now() - startedAt;
if (!result.success) {
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: result.errors,
targetNodeId: body.targetNodeId,
elapsedMs,
},
{ status: 502 },
);
}
const response = Response.json({
success: true,
targetNodeId: body.targetNodeId,
observations: result.observations,
uncertainties: result.uncertainties,
assumptions: result.assumptions,
relationships: result.relationships,
possibleFollowUpQuestions: result.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: e?.code === "PROVIDER_UNAVAILABLE" ? 503 : 500,
elapsedMs,
});
if (e?.code === "PROVIDER_UNAVAILABLE") {
return Response.json(
{ error: "Reasoning service is temporarily unavailable." },
{ status: 503 },
);
}
return Response.json(
{ error: "Reasoning request could not be completed." },
{ status: 500 },
);
}
}
export const POST = withAuthenticatedApi(post);