Files
Krystie dc5dc94d79 Initial commit: Adaptive Recall sidecar for UMP (Phase 5)
Multi-channel retrieval sidecar over Universal Memory Protocol:
- 3-channel RRF (UMP FTS5 + Qdrant vector + knowledge graph)
- ACT-R re-ranking (Anderson 1983) with access tracking
- Co-occurrence graph edges (Phase 6) for dense traversal
- Memory lifecycle decay (Phase 4) with per-kind confidence
- MCP shim routes recall through sidecar, falls back to canonical UMP

Architecture:
- src/server.js      HTTP sidecar on port 4380
- src/graph.js       2592-node / 111-edge graph from UMP (or +cooccur: 13k+)
- src/actr.js        A_i = -d*ln(age) + beta*log1p(freq) + epsilon*conf
- src/access_log.js  per-URN counter + last_accessed_at
- src/ump-recall-mcp.js  MCP shim (recall via sidecar, others passthrough)

Eval results (851-record UMP corpus):
- 2ch RRF over baseline: +50pp recall@10
- 3ch RRF (+graph): +60pp, 12 unique wins
- ACT-R re-rank: 4/20 #1 changes, 84% top-5 retention

Tests: 76/76 passing across graph (27), actr (27), access_log (28),
decay (20), mcp-shim (sidecar + fallback). Run with: npm test

Inspired by AIAppsAPI/adaptive-recall but built from scratch against
existing DNS2 infrastructure (UMP at :4317, Qdrant at :6333,
Ollama at :11434). No paid SaaS, MIT-licensed.
2026-07-12 19:24:47 -07:00

54 lines
1.5 KiB
JavaScript

// Phase 1B entity extractor test suite.
// Run: node test/test-entities.js
// Exits 0 on full pass, 1 on any failure.
//
// Expected order: first occurrence in the source text (deduped). Matches
// the task brief.
import { extractEntities } from "../src/entities.js";
const cases = [
{
name: "machine + product + tailscale ip",
in: "DNS2 runs Triangles daemon on 100.121.150.22",
want: ["DNS2", "Triangles", "100.121.150.22"],
},
{
name: "PR code + Phase code",
in: "PR-30 fixed Phase 4 of the plan",
want: ["PR-30", "Phase 4"],
},
{
name: "explicit arrow relations yield source+target entities",
in: "DashCaddy → Caddy → nftables",
want: ["DashCaddy", "Caddy", "nftables"],
},
{
name: "Hermes + krystie (profile) + MCP all-caps token",
in: "Hermes (krystie profile) uses MCP stdio",
want: ["Hermes", "krystie", "MCP"],
},
];
let pass = 0;
let fail = 0;
for (const c of cases) {
const got = extractEntities(c.in);
const ok = JSON.stringify(got) === JSON.stringify(c.want);
if (ok) {
pass++;
console.log(`PASS ${c.name}`);
console.log(` in=${JSON.stringify(c.in)}`);
console.log(` out=${JSON.stringify(got)}`);
} else {
fail++;
console.log(`FAIL ${c.name}`);
console.log(` in=${JSON.stringify(c.in)}`);
console.log(` got=${JSON.stringify(got)}`);
console.log(` want=${JSON.stringify(c.want)}`);
}
}
console.log(`\n${pass}/${pass + fail} passed`);
if (fail > 0) process.exit(1);