Files
kbdb-graph-plugin/src/lib/templates.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

77 lines
3.1 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.
// 插件用到的基本盤 template 定義(= 替代建表)。
// 鐵律:插件新類型只能建 template,不建表。這裡集中宣告 slot schema
// 任何寫入前先 client.ensureTemplate 確保存在。
import type { KbdbClient } from './kbdb-client';
import type { Triplet, Entity } from '../types';
import type { BaseRecord } from './kbdb-client';
export const TPL_TRIPLET = 'triplet';
export const TPL_ENTITY = 'entity';
export const TPL_ENTITY_PENDING = 'entity_pending';
export const TRIPLET_SLOTS = [
'subject', 'predicate', 'object', 'source_block_id',
'confidence', 'clusters_json', 'bridge_score',
'subject_entity_type', 'object_entity_type',
// 取代/快照(T3.2):status=active|deprecatedsuperseded_by=取代它的新 record id
// source_uri+content_hash 承載 ingest idempotency(按 source_uri 分組 deprecate)。
// source_anchor 供 get_source 精準回跳原文(T3.7)。
'status', 'superseded_by', 'source_uri', 'content_hash', 'source_anchor',
];
// glossT3.2b):一句話描述,供「詞+gloss」語義 normalize 的 embedding 對象。
export const ENTITY_SLOTS = ['canonical', 'aliases_json', 'entity_type', 'owner', 'gloss'];
export const ENTITY_PENDING_SLOTS = [
'raw_name', 'candidate_entity_id', 'candidate_canonical', 'similarity',
];
/** 確保插件三個 template 存在(idempotent,走 API)。 */
export async function ensurePluginTemplates(client: KbdbClient): Promise<void> {
await client.ensureTemplate(TPL_TRIPLET, TRIPLET_SLOTS, 'knowledge graph triplet (S-P-O)');
await client.ensureTemplate(TPL_ENTITY, ENTITY_SLOTS, 'normalized entity (canonical + aliases)');
await client.ensureTemplate(TPL_ENTITY_PENDING, ENTITY_PENDING_SLOTS, 'pending entity alias for review');
}
/** 基本盤 record → 插件 Triplet 型別。 */
export function recordToTriplet(rec: BaseRecord): Triplet {
const v = rec.values;
return {
id: rec.record_id,
subject: v.subject ?? '',
predicate: v.predicate ?? '',
object: v.object ?? '',
source_block_id: v.source_block_id ?? null,
confidence: parseFloat(v.confidence ?? '1.0'),
clusters: safeArr(v.clusters_json),
bridge_score: parseInt(v.bridge_score ?? '0', 10),
subject_entity_type: (v.subject_entity_type as Triplet['subject_entity_type']) || null,
object_entity_type: (v.object_entity_type as Triplet['object_entity_type']) || null,
// 缺省視為 active(相容尚無 status slot 的舊資料)。
status: v.status === 'deprecated' ? 'deprecated' : 'active',
superseded_by: v.superseded_by || null,
source_uri: v.source_uri || null,
content_hash: v.content_hash || null,
source_anchor: v.source_anchor || null,
created_at: 0,
updated_at: 0,
};
}
/** 基本盤 record → 插件 Entity 型別。 */
export function recordToEntity(rec: BaseRecord): Entity {
return {
id: rec.record_id,
canonical: rec.values.canonical ?? '',
aliases: safeArr(rec.values.aliases_json),
};
}
function safeArr(json?: string): string[] {
try {
const p = JSON.parse(json || '[]');
return Array.isArray(p) ? p : [];
} catch {
return [];
}
}