hippo-memory 1.19.0 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +133 -3
- package/dist/cli.js.map +1 -1
- package/dist/graph-stream.d.ts +71 -0
- package/dist/graph-stream.d.ts.map +1 -0
- package/dist/graph-stream.js +170 -0
- package/dist/graph-stream.js.map +1 -0
- package/dist/graph-view.d.ts +72 -0
- package/dist/graph-view.d.ts.map +1 -0
- package/dist/graph-view.js +310 -0
- package/dist/graph-view.js.map +1 -0
- package/dist/graph.d.ts +30 -4
- package/dist/graph.d.ts.map +1 -1
- package/dist/graph.js +104 -11
- package/dist/graph.js.map +1 -1
- package/dist/search.d.ts +16 -0
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +31 -1
- package/dist/search.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +19 -0
- package/dist/server.js.map +1 -1
- package/dist/src/cli.js +133 -3
- package/dist/src/cli.js.map +1 -1
- package/dist/src/graph-stream.js +170 -0
- package/dist/src/graph-stream.js.map +1 -0
- package/dist/src/graph-view.js +310 -0
- package/dist/src/graph-view.js.map +1 -0
- package/dist/src/graph.js +104 -11
- package/dist/src/graph.js.map +1 -1
- package/dist/src/search.js +31 -1
- package/dist/src/search.js.map +1 -1
- package/dist/src/server.js +19 -0
- package/dist/src/server.js.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -66,6 +66,7 @@ import * as skillsModule from './skills.js';
|
|
|
66
66
|
import * as briefsModule from './project-briefs.js';
|
|
67
67
|
import * as customerNotesModule from './customer-notes.js';
|
|
68
68
|
import { extractGraph } from './graph-extract.js';
|
|
69
|
+
import { buildGraphModel, renderGraphHtml, renderGraphCanvas, DEFAULT_VIEW_LIMIT } from './graph-view.js';
|
|
69
70
|
import { createHash } from 'node:crypto';
|
|
70
71
|
import { detectAnchoring, hashQueryText, buildSessionKey, getOrCreateRing, appendRecall, snapshotRing, } from './recall-history.js';
|
|
71
72
|
import { detectAvailabilityBias } from './availability.js';
|
|
@@ -100,6 +101,7 @@ import { refineStore } from './refine-llm.js';
|
|
|
100
101
|
import { wmPush, wmRead, wmClear, wmFlush } from './working-memory.js';
|
|
101
102
|
import { multihopSearch } from './multihop.js';
|
|
102
103
|
import { graphExpandRecall, MAX_HOPS, DEFAULT_MAX_NEIGHBORS } from './graph-recall.js';
|
|
104
|
+
import { DEFAULT_GRAPH_STREAM_WEIGHT } from './graph-stream.js';
|
|
103
105
|
import { getReranker } from './rerankers/index.js';
|
|
104
106
|
import { computeSalience } from './salience.js';
|
|
105
107
|
import { computeAmbientState, renderAmbientSummary } from './ambient.js';
|
|
@@ -779,8 +781,59 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
779
781
|
const recallExplicitScope = flags['scope'] !== undefined ? String(flags['scope']).trim() : null;
|
|
780
782
|
const recallActiveScope = recallExplicitScope || detectScope();
|
|
781
783
|
const useMultihop = flags['multihop'] === true || config.multihop.enabled;
|
|
784
|
+
// L1 — graph-retrieval stream. `--graph-stream` forces rrf fusion + the graph stream
|
|
785
|
+
// (see src/graph-stream.ts). NOTE: it implies `scoring:'rrf'` (production default is
|
|
786
|
+
// 'blend'), so the flag bundles two behaviours by design. CLI surface is local-store
|
|
787
|
+
// only for now; the library API supports a globalRoot for callers who need it.
|
|
788
|
+
const useGraphStream = flags['graph-stream'] === true;
|
|
789
|
+
let graphStreamHops;
|
|
790
|
+
if (useGraphStream && flags['graph-hops'] !== undefined) {
|
|
791
|
+
if (typeof flags['graph-hops'] === 'boolean') {
|
|
792
|
+
console.error(`--graph-hops requires an integer value 1..${MAX_HOPS} (e.g. --graph-hops 2).`);
|
|
793
|
+
process.exit(1);
|
|
794
|
+
}
|
|
795
|
+
const h = Number(flags['graph-hops']);
|
|
796
|
+
if (!Number.isInteger(h) || h < 1 || h > MAX_HOPS) {
|
|
797
|
+
console.error(`Invalid --graph-hops: "${String(flags['graph-hops'])}". Must be an integer 1..${MAX_HOPS}.`);
|
|
798
|
+
process.exit(1);
|
|
799
|
+
}
|
|
800
|
+
graphStreamHops = h;
|
|
801
|
+
}
|
|
802
|
+
let graphStreamSeeds;
|
|
803
|
+
if (useGraphStream && flags['graph-seeds'] !== undefined) {
|
|
804
|
+
if (typeof flags['graph-seeds'] === 'boolean') {
|
|
805
|
+
console.error('--graph-seeds requires a positive integer value (e.g. --graph-seeds 10).');
|
|
806
|
+
process.exit(1);
|
|
807
|
+
}
|
|
808
|
+
const s = Number(flags['graph-seeds']);
|
|
809
|
+
if (!Number.isInteger(s) || s < 1) {
|
|
810
|
+
console.error(`Invalid --graph-seeds: "${String(flags['graph-seeds'])}". Must be a positive integer.`);
|
|
811
|
+
process.exit(1);
|
|
812
|
+
}
|
|
813
|
+
graphStreamSeeds = s;
|
|
814
|
+
}
|
|
782
815
|
let results;
|
|
783
|
-
if (
|
|
816
|
+
if (useGraphStream) {
|
|
817
|
+
if (!isEmbeddingAvailable()) {
|
|
818
|
+
// The graph stream fuses inside the rrf path, which only runs with embeddings; without
|
|
819
|
+
// them hybridSearch falls back to BM25-only and the stream is inert. Say so plainly
|
|
820
|
+
// rather than silently no-op.
|
|
821
|
+
console.error('[note] --graph-stream needs embeddings (rrf fusion); none available, so the graph stream is inert. Run `hippo embed` first.');
|
|
822
|
+
}
|
|
823
|
+
if (hasGlobal) {
|
|
824
|
+
console.error('[note] --graph-stream searches the local store only; global graph fusion is a follow-up.');
|
|
825
|
+
}
|
|
826
|
+
// The stream anchors on the top-`seedCount` lexical hits and re-ranks the rank>seedCount
|
|
827
|
+
// tail; on a pool with <= seedCount candidates EVERY candidate is a seed and the stream
|
|
828
|
+
// is inert (it degrades to the 2-list fusion). Tune the anchor count with --graph-seeds.
|
|
829
|
+
results = await hybridSearch(query, localEntries, {
|
|
830
|
+
budget, hippoRoot, mmr: mmrEnabled, mmrLambda, minResults, scope: recallActiveScope,
|
|
831
|
+
includeSuperseded, asOf,
|
|
832
|
+
scoring: 'rrf',
|
|
833
|
+
graphStream: { weight: DEFAULT_GRAPH_STREAM_WEIGHT, tenantId, hops: graphStreamHops, seedCount: graphStreamSeeds },
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
else if (useMultihop) {
|
|
784
837
|
const allEntries = [...localEntries, ...globalEntries];
|
|
785
838
|
results = multihopSearch(query, allEntries, {
|
|
786
839
|
budget,
|
|
@@ -4522,7 +4575,7 @@ function noteUsage() {
|
|
|
4522
4575
|
console.error(' hippo note supersede <id> --text "<note>" [--change "<summary>"]');
|
|
4523
4576
|
console.error(' hippo note close <id>');
|
|
4524
4577
|
}
|
|
4525
|
-
function cmdGraph(hippoRoot, args,
|
|
4578
|
+
function cmdGraph(hippoRoot, args, flags) {
|
|
4526
4579
|
requireInit(hippoRoot);
|
|
4527
4580
|
const tenantId = resolveTenantId({});
|
|
4528
4581
|
const subcommand = args[0] ?? '';
|
|
@@ -4538,7 +4591,75 @@ function cmdGraph(hippoRoot, args, _flags) {
|
|
|
4538
4591
|
}
|
|
4539
4592
|
return;
|
|
4540
4593
|
}
|
|
4541
|
-
|
|
4594
|
+
const entity = typeof flags['entity'] === 'string' ? flags['entity'] : undefined;
|
|
4595
|
+
if (subcommand === 'show') {
|
|
4596
|
+
const model = buildGraphModel(hippoRoot, tenantId, { entity, limit: DEFAULT_VIEW_LIMIT });
|
|
4597
|
+
if (flags['json']) {
|
|
4598
|
+
console.log(JSON.stringify(model, null, 2));
|
|
4599
|
+
return;
|
|
4600
|
+
}
|
|
4601
|
+
if (model.nodes.length === 0) {
|
|
4602
|
+
console.log(entity ? `No entity named "${entity}".` : 'Graph is empty. Run `hippo graph extract` first.');
|
|
4603
|
+
return;
|
|
4604
|
+
}
|
|
4605
|
+
console.log(`Graph: ${model.nodes.length} entities, ${model.edges.length} relations${model.truncated ? ' (truncated)' : ''}`);
|
|
4606
|
+
const byType = new Map();
|
|
4607
|
+
for (const n of model.nodes) {
|
|
4608
|
+
const arr = byType.get(n.type) ?? [];
|
|
4609
|
+
arr.push({ id: n.id, name: n.name });
|
|
4610
|
+
byType.set(n.type, arr);
|
|
4611
|
+
}
|
|
4612
|
+
for (const [type, ns] of byType) {
|
|
4613
|
+
console.log(`\n${type} (${ns.length}):`);
|
|
4614
|
+
for (const n of ns)
|
|
4615
|
+
console.log(` [${n.id}] ${n.name}`);
|
|
4616
|
+
}
|
|
4617
|
+
if (model.edges.length > 0) {
|
|
4618
|
+
const nameById = new Map(model.nodes.map((n) => [n.id, n.name]));
|
|
4619
|
+
console.log('\nrelations:');
|
|
4620
|
+
for (const e of model.edges) {
|
|
4621
|
+
console.log(` ${nameById.get(e.from)} --${e.relType}--> ${nameById.get(e.to)}`);
|
|
4622
|
+
}
|
|
4623
|
+
}
|
|
4624
|
+
return;
|
|
4625
|
+
}
|
|
4626
|
+
if (subcommand === 'view') {
|
|
4627
|
+
const format = typeof flags['format'] === 'string' ? flags['format'] : 'html';
|
|
4628
|
+
if (format !== 'html' && format !== 'canvas') {
|
|
4629
|
+
console.error("graph view: --format must be 'html' or 'canvas'");
|
|
4630
|
+
process.exit(1);
|
|
4631
|
+
}
|
|
4632
|
+
const model = buildGraphModel(hippoRoot, tenantId, { entity, limit: DEFAULT_VIEW_LIMIT });
|
|
4633
|
+
const content = format === 'canvas' ? renderGraphCanvas(model) : renderGraphHtml(model);
|
|
4634
|
+
const defaultOut = format === 'canvas' ? 'hippo-graph.canvas' : 'hippo-graph.html';
|
|
4635
|
+
const out = typeof flags['out'] === 'string' ? flags['out'] : defaultOut;
|
|
4636
|
+
fs.writeFileSync(out, content, 'utf8');
|
|
4637
|
+
console.log(`Wrote ${model.nodes.length} entities + ${model.edges.length} relations to ${out}${model.truncated ? ' (truncated)' : ''}`);
|
|
4638
|
+
if (flags['open'] && format === 'html') {
|
|
4639
|
+
// Best-effort browser launch; never fail the command if it doesn't work.
|
|
4640
|
+
try {
|
|
4641
|
+
const [cmd, cmdArgs] = process.platform === 'win32'
|
|
4642
|
+
? ['cmd', ['/c', 'start', '', out]]
|
|
4643
|
+
: process.platform === 'darwin'
|
|
4644
|
+
? ['open', [out]]
|
|
4645
|
+
: ['xdg-open', [out]];
|
|
4646
|
+
const child = spawn(cmd, cmdArgs, { detached: true, stdio: 'ignore' });
|
|
4647
|
+
// A missing launcher (e.g. xdg-open absent) emits 'error' asynchronously;
|
|
4648
|
+
// an unhandled 'error' event would throw, so swallow it — the file is
|
|
4649
|
+
// already written and its path printed above.
|
|
4650
|
+
child.on('error', () => { });
|
|
4651
|
+
child.unref();
|
|
4652
|
+
}
|
|
4653
|
+
catch {
|
|
4654
|
+
/* ignore — the file is written; the path is printed above */
|
|
4655
|
+
}
|
|
4656
|
+
}
|
|
4657
|
+
return;
|
|
4658
|
+
}
|
|
4659
|
+
console.error('Usage:\n' +
|
|
4660
|
+
' hippo graph extract Rebuild the entity/relation graph from consolidated objects\n' +
|
|
4661
|
+
' hippo graph show [--entity NAME] [--json] Inspect entities + their edges (text or JSON)\n' +
|
|
4662
|
+
' hippo graph view [--out FILE] [--open] [--format html|canvas] [--entity NAME] Generate an interactive node-link diagram');
|
|
4542
4663
|
process.exit(1);
|
|
4543
4664
|
}
|
|
4544
4665
|
function cmdCustomerNote(hippoRoot, args, flags) {
|
|
@@ -6675,6 +6796,15 @@ Commands:
|
|
|
6675
6796
|
graph holds supersedes edges (E3.1); cross-object edges
|
|
6676
6797
|
light up the same traversal once extracted.
|
|
6677
6798
|
--max-neighbors <n> Per-hop fanout cap for --hops (1..200, default 25).
|
|
6799
|
+
--graph-stream L1: fuse a graph-retrieval stream into RRF, re-ranking
|
|
6800
|
+
in-pool results by graph proximity to the strong lexical
|
|
6801
|
+
seeds. Implies rrf scoring (default is blend). Local store
|
|
6802
|
+
only. Distinct from --hops (which injects out-of-pool
|
|
6803
|
+
neighbours); this re-ranks within the candidate pool.
|
|
6804
|
+
--graph-hops <n> Hops for --graph-stream (1..3, default 2).
|
|
6805
|
+
--graph-seeds <n> Lexical anchors for --graph-stream (default 10). The stream
|
|
6806
|
+
re-ranks the rank>seeds tail; on a pool with <= n candidates
|
|
6807
|
+
every candidate is a seed and the stream is inert.
|
|
6678
6808
|
--no-mmr Disable MMR diversity re-ranking
|
|
6679
6809
|
--mmr-lambda <f> MMR balance 0..1 (default: 0.7, 1.0 = pure relevance)
|
|
6680
6810
|
--evc-adaptive ACC-style: when top-K shows high inter-item overlap
|