/** * /api/chunk?doc=&chunk= * * Returns one chunk's payload (page, type, bbox, content_en, content_pt, * classification, anomaly flags). Used by client-side InlineCitation * component to expand `[[doc/p007#c0042]]` links into rich cards. * * Tries DB first (with embeddings already indexed), falls back to filesystem * (raw/--subagent/chunks/cXXXX.md) so the UX works even before the * indexer has run on a particular doc. */ import { NextRequest } from "next/server"; import { getChunk } from "@/lib/retrieval/hybrid"; import { readChunk as readChunkFs } from "@/lib/chunks"; export const runtime = "nodejs"; export const dynamic = "force-dynamic"; function json(data: unknown, status = 200) { return new Response(JSON.stringify(data), { status, headers: { "content-type": "application/json", "cache-control": "public, max-age=300", }, }); } export async function GET(req: NextRequest) { const u = new URL(req.url); const doc = u.searchParams.get("doc")?.trim(); const chunk = u.searchParams.get("chunk")?.trim(); if (!doc || !chunk) return json({ error: "doc and chunk required" }, 400); // 1. Try DB try { const c = await getChunk(doc, chunk); if (c) { return json({ source: "db", chunk_id: c.chunk_id, doc_id: c.doc_id, page: c.page, type: c.type, bbox: c.bbox, classification: c.classification, content_en: c.content_en, content_pt: c.content_pt, }); } } catch { // db unavailable → fall through to fs } // 2. Filesystem fallback const fs = await readChunkFs(doc, chunk); if (!fs) return json({ error: "not_found", doc_id: doc, chunk_id: chunk }, 404); return json({ source: "fs", chunk_id: fs.fm.chunk_id, doc_id: doc, page: fs.fm.page, type: fs.fm.type, bbox: fs.fm.bbox, classification: fs.fm.classification, content_en: fs.content_en, content_pt: fs.content_pt, }); }