Files
kbdb-graph-plugin/src/routes/graph.ts
T
Leo 613071f41d feat(graph): get_source + refresh 端點 + keyword 收斂 (T3.6-3.7)
對應 issue #1 T3 C 段(圖工具 HTTP API 備好,MCP 註冊薄殼待 arcrun)。

- get_source (3.7): graph-source.ts + GET /graph/source/:name —
  回節點的 active triplet 來源指標(uri/anchor/block_id/content_hash),去重。
  連帶加 source_anchor slot,ingest 從 source.anchor 帶入
- refresh (3.6/3.6b): graph-refresh.ts + POST /graph/refresh —
  純被動代轉 ingest(KBDB_INGEST_URL),只人發起、無排程/webhook(fan-out 紅線)。
  未設 URL → 誠實 forwarded:false,不假綠
- 3.6d: POST /search 移除公開 keyword 模式(重複 KBDB MCP),收斂 suggest-only;
  keywordSearch helper 留作 suggest 內部建構塊
- 3 新測試(get_source uri+anchor / active-only / refresh 未就緒誠實回報)

gates: vitest 19 passed / zero SQL / 無新綁定 / dry-run bundle 乾淨
待接:MCP 註冊薄殼併 arcrun u6u-mcp-server;refresh 端到端待 ingest(T4) 部署

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 18:24:04 +08:00

69 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 圖遍歷路由入口
// 僅驗證參數,呼叫 actionsactions 走基本盤 API,圖在插件層組裝)
import { Hono } from 'hono';
import type { Bindings } from '../types';
import { traverseGraph, queryRelation } from '../actions/graph-traverse';
import { getNodeEdges, getNeighbors } from '../actions/graph-nodes';
import { findShortestPath } from '../actions/graph-path';
import { getSource } from '../actions/graph-source';
import { refreshSource } from '../actions/graph-refresh';
import { makeKbdbClient } from '../lib/kbdb-client';
const graphRoutes = new Hono<{ Bindings: Bindings }>();
graphRoutes.get('/traverse', async (c) => {
const start = c.req.query('start');
if (!start) return c.json({ error: 'start parameter required' }, 400);
const depth = Number(c.req.query('depth') ?? 2);
const results = await traverseGraph(makeKbdbClient(c.env), start, depth);
return c.json({ start, depth, nodes: results, nodeCount: results.length });
});
graphRoutes.get('/relation', async (c) => {
const from = c.req.query('from');
const to = c.req.query('to');
if (!from || !to) return c.json({ error: 'from and to parameters required' }, 400);
const relations = await queryRelation(makeKbdbClient(c.env), from, to);
return c.json({ from, to, relations, count: relations.length });
});
// GET /graph/neighbors/:name — 合併原 nodes/:name 的 edges + neighbors
graphRoutes.get('/neighbors/:name', async (c) => {
const name = decodeURIComponent(c.req.param('name'));
const client = makeKbdbClient(c.env);
const [edges, neighbors] = await Promise.all([
getNodeEdges(client, name),
getNeighbors(client, name),
]);
return c.json({ node: name, edges, neighbors, edgeCount: edges.length, neighborCount: neighbors.length });
});
// GET /graph/path — 取代 /shortest-path,行為不變
graphRoutes.get('/path', async (c) => {
const from = c.req.query('from');
const to = c.req.query('to');
if (!from || !to) return c.json({ error: 'from and to parameters required' }, 400);
const result = await findShortestPath(makeKbdbClient(c.env), from, to);
return c.json({ from, to, ...result });
});
// GET /graph/source/:name — get_sourceT3.7):回節點的原文來源指標(uri+anchor)
graphRoutes.get('/source/:name', async (c) => {
const name = decodeURIComponent(c.req.param('name'));
const sources = await getSource(makeKbdbClient(c.env), name);
return c.json({ node: name, sources, count: sources.length });
});
// POST /graph/refresh — 代轉 ingest 重抓+萃(T3.6/3.6b
// 🚫 只人發起 MCP 調用觸發,禁排程/webhook 自動(fan-out 紅線)。
graphRoutes.post('/refresh', async (c) => {
const body = await c.req.json().catch(() => ({}));
const uri = body?.uri;
if (!uri) return c.json({ error: 'uri required' }, 400);
const result = await refreshSource({ uri, owner_id: body?.owner_id }, c.env.KBDB_INGEST_URL);
return c.json(result);
});
export { graphRoutes };