/** * EntityGraphMini — sidebar widget with co-mentioned entities (neighbors) * and a sample of chunks where they co-occur. Lives next to the entity body. * * Gracefully degrades to empty state when entity_mentions is unpopulated * or the DB is unreachable. */ import Link from "next/link"; import { findEntity, getNeighbors } from "@/lib/retrieval/graph"; const CLASS_COLOR: Record = { person: "text-[#ff6ec7] border-[#ff6ec7]", organization: "text-[#ff8a4d] border-[#ff8a4d]", location: "text-[#3fde6a] border-[#3fde6a]", event: "text-[#ffa500] border-[#ffa500]", uap_object: "text-[#ff3344] border-[#ff3344]", vehicle: "text-[#5b9bd5] border-[#5b9bd5]", operation: "text-[#9b5de5] border-[#9b5de5]", concept: "text-[#06d6a0] border-[#06d6a0]", }; const CLASS_FOLDER: Record = { person: "people", organization: "organizations", location: "locations", event: "events", uap_object: "uap-objects", vehicle: "vehicles", operation: "operations", concept: "concepts", }; export async function EntityGraphMini({ entityClassSingular, entityId, }: { entityClassSingular: string; entityId: string; }) { let neighbors: Awaited> = []; let totalMentions = 0; try { const ent = await findEntity(entityClassSingular, entityId); if (ent) { totalMentions = ent.total_mentions ?? 0; neighbors = await getNeighbors(ent.entity_pk, { limit: 25 }); } } catch { return null; } if (neighbors.length === 0) { return null; } return (

🕸 co-mentioned · top {neighbors.length} via {totalMentions} mentions

    {neighbors.map((n) => { const folder = CLASS_FOLDER[n.entity_class] ?? n.entity_class; const color = CLASS_COLOR[n.entity_class] ?? "text-[#7fdbff] border-[#7fdbff]"; return (
  • {n.entity_class.slice(0, 3)} {n.canonical_name} ×{n.weight}
  • ); })}
); }