chore: preserve initial reconstruction prototype

This commit is contained in:
2026-07-31 18:51:38 +01:00
commit a2f9e472ea
26 changed files with 8874 additions and 0 deletions
+101
View File
@@ -0,0 +1,101 @@
import { getConfig } from "@/lib/config";
import { getProvider } from "@/lib/llm/provider";
import { reconstructionSchema } from "@/lib/reconstruction/schema";
const MAX_SCENARIO_LENGTH = 10000;
export async function POST(request) {
const startTime = Date.now();
let rawResponse = null;
try {
const body = await request.json();
if (!body.scenario || typeof body.scenario !== "string") {
return Response.json(
{ error: "Request must include a 'scenario' string field" },
{ status: 400 }
);
}
const trimmed = body.scenario.trim();
if (trimmed.length === 0) {
return Response.json(
{ error: "Scenario cannot be empty" },
{ status: 400 }
);
}
if (trimmed.length > MAX_SCENARIO_LENGTH) {
return Response.json(
{ error: `Scenario must be under ${MAX_SCENARIO_LENGTH} characters` },
{ status: 400 }
);
}
const configResult = getConfig();
if (!configResult.ok) {
return Response.json(
{ error: "Invalid server configuration" },
{ status: 500 }
);
}
const { OLLAMA_BASE_URL, OLLAMA_MODEL } = configResult.config;
const provider = getProvider();
// Attempt parse to capture raw for debugging
let reconstruction;
try {
reconstruction = await provider.generateReconstruction(trimmed, OLLAMA_MODEL);
} catch (e) {
return Response.json(
{
error: e.message || "Unknown server error",
responseDurationMs: Date.now() - startTime,
modelName: OLLAMA_MODEL,
validationStatus: "invalid",
},
{ status: 500 }
);
}
// Try to stringify for rawResponse display (safe even if it's already an object)
try {
rawResponse = JSON.stringify(reconstruction);
} catch {
rawResponse = String(reconstruction).slice(0, 2000);
}
const duration = Date.now() - startTime;
// Validate with Zod schema
const validationResult = reconstructionSchema.safeParse(reconstruction);
if (!validationResult.success) {
return Response.json({
reconstruction: null,
modelName: OLLAMA_MODEL,
responseDurationMs: duration,
validationStatus: "invalid",
rawResponse: rawResponse?.slice(0, 2000),
errors: validationResult.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`),
});
}
return Response.json({
reconstruction: validationResult.data,
modelName: OLLAMA_MODEL,
responseDurationMs: duration,
validationStatus: "valid",
rawResponse: rawResponse?.slice(0, 2000),
});
} catch (e) {
const duration = Date.now() - startTime;
return Response.json(
{ error: e.message || "Unknown server error", responseDurationMs: duration },
{ status: 500 }
);
}
}
+49
View File
@@ -0,0 +1,49 @@
import { getConfig } from "@/lib/config";
export async function GET() {
try {
const result = getConfig();
if (!result.ok) {
return Response.json({
configPresent: false,
baseUrl: null,
model: null,
reachable: false,
error: "Missing or invalid environment configuration",
}, { status: 500 });
}
const { OLLAMA_BASE_URL, OLLAMA_MODEL } = result.config;
// Test reachability with a short timeout
let reachable = false;
let reachError = null;
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 3000);
const res = await fetch(`${OLLAMA_BASE_URL}/api/tags`, {
signal: controller.signal
});
clearTimeout(timeout);
reachable = res.ok;
} catch (e) {
reachError = e.message || "Connection failed";
}
return Response.json({
configPresent: true,
baseUrl: OLLAMA_BASE_URL,
model: OLLAMA_MODEL,
reachable,
error: reachable ? null : (`Could not reach Ollama at ${OLLAMA_BASE_URL}: ${reachError || "timeout"}`),
});
} catch (e) {
return Response.json(
{ configPresent: false, error: e.message },
{ status: 500 }
);
}
}
+3
View File
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
+16
View File
@@ -0,0 +1,16 @@
import "./globals.css";
export const metadata = {
title: "Confidence Engine",
description: "Experimental evidence-based situation reconstruction prototype",
};
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className="min-h-screen bg-gray-50 text-gray-900">
{children}
</body>
</html>
);
}
+15
View File
@@ -0,0 +1,15 @@
import ScenarioForm from "@/components/scenario-form";
export default function Home() {
return (
<main className="mx-auto max-w-2xl px-6 py-12">
<h1 className="mb-2 text-3xl font-bold tracking-tight">Confidence Engine</h1>
<p className="mb-8 text-sm text-gray-500">
Experimental prototype: enter a scenario and send it to a local LLM for
evidence-based structured reconstruction. This is a technical vertical
slice not a production system.
</p>
<ScenarioForm />
</main>
);
}