OpenClawDashboard/bridge/src/routes/knowledge.ts

126 lines
No EOL
4 KiB
TypeScript

/**
* knowledge.ts — Knowledge Graph endpoints
* Uses raw SQL execution to create tables if not exist
*/
import { Router, Request, Response } from "express";
import { randomUUID } from "crypto";
import db from "../db.js";
const router = Router();
// Ensure tables exist
const initTables = () => {
try {
db.exec(`
CREATE TABLE IF NOT EXISTS knowledge_nodes (
id TEXT PRIMARY KEY,
type TEXT NOT NULL,
name TEXT NOT NULL,
description TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
`);
db.exec(`
CREATE TABLE IF NOT EXISTS knowledge_edges (
id TEXT PRIMARY KEY,
from_node TEXT NOT NULL,
to_node TEXT NOT NULL,
relationship TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now'))
);
`);
} catch (e) { /* tables may exist */ }
};
initTables();
// List all nodes
router.get("/", async (req: Request, res: Response) => {
const { q, limit = 50 } = req.query;
try {
let sql = "SELECT * FROM knowledge_nodes";
const params: string[] = [];
if (q) {
sql += " WHERE name LIKE ? OR description LIKE ?";
params.push(`%${q}%`, `%${q}%`);
}
sql += " ORDER BY created_at DESC LIMIT ?";
params.push(String(limit));
const nodes = db.prepare(sql).all(...params);
res.json({ nodes });
} catch (err: any) {
res.status(500).json({ error: err.message });
}
});
// Get node with connections
router.get("/:id", async (req: Request, res: Response) => {
const { id } = req.params;
try {
const node = db.prepare("SELECT * FROM knowledge_nodes WHERE id = ?").get(id);
if (!node) return res.status(404).json({ error: "Not found" });
// Get all edges for graph
const edges = db.prepare(`
SELECT ke.from_node, ke.to_node, ke.relationship, kn.name as to_name
FROM knowledge_edges ke
JOIN knowledge_nodes kn ON ke.to_node = kn.id
`).all();
// Get all nodes for graph
const allNodes = db.prepare("SELECT * FROM knowledge_nodes").all();
res.json({ node, connections: edges, allNodes });
} catch (err: any) {
res.status(500).json({ error: err.message });
}
});
// Create node
router.post("/", async (req: Request, res: Response) => {
const { type, name, description } = req.body;
if (!type || !name) return res.status(400).json({ error: "type, name required" });
const id = randomUUID();
try {
db.prepare("INSERT INTO knowledge_nodes (id, type, name, description) VALUES (?, ?, ?, ?)")
.run(id, type, name, description || "");
res.json({ ok: true, id });
} catch (err: any) {
res.status(500).json({ error: err.message });
}
});
// Create connection
router.post("/connect", async (req: Request, res: Response) => {
const { from, to, relationship } = req.body;
if (!from || !to || !relationship) {
return res.status(400).json({ error: "from, to, relationship required" });
}
try {
const id = randomUUID();
db.prepare("INSERT INTO knowledge_edges (id, from_node, to_node, relationship) VALUES (?, ?, ?, ?)")
.run(id, from, to, relationship);
res.json({ ok: true, id });
} catch (err: any) {
res.status(500).json({ error: err.message });
}
});
// Seed initial knowledge
router.post("/seed", async (req: Request, res: Response) => {
const nodes = [
{ t: "person", n: "Manohar", d: "IIT Roorkee, IIM Rohtak, works at Renew Power" },
{ t: "company", n: "Renew Power", d: "India renewable energy, NYSE: RNW" },
{ t: "company", n: "Adani Green", d: "Competitor in renewables" },
{ t: "concept", n: "PE/VC", d: "Career path interest" },
{ t: "concept", n: "Option Trading", d: "Nifty options selling" },
];
try {
for (const x of nodes) {
db.prepare("INSERT OR IGNORE INTO knowledge_nodes (id, type, name, description) VALUES (?, ?, ?, ?)")
.run(randomUUID(), x.t, x.n, x.d);
}
res.json({ ok: true, count: nodes.length });
} catch (err: any) {
res.status(500).json({ error: err.message });
}
});
export default router;