cozo-memory 1.0.7 → 1.0.8

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/index.js CHANGED
@@ -361,6 +361,124 @@ class MemoryServer {
361
361
  }
362
362
  return result.rows.map((r) => ({ entity_id: String(r[0]), community_id: String(r[1]) }));
363
363
  }
364
+ async summarizeCommunities(args) {
365
+ await this.initPromise;
366
+ const minSize = Math.max(2, Math.floor(args?.min_community_size ?? 3));
367
+ const model = args?.model ?? "demyagent-4b-i1:Q6_K";
368
+ console.error("[GraphRAG] Recomputing communities before summarization...");
369
+ const commResult = await this.recomputeCommunities();
370
+ if (commResult.length === 0) {
371
+ return { status: "no_data", generated_summaries: 0 };
372
+ }
373
+ // Group entities by community
374
+ const byCommunity = new Map();
375
+ for (const row of commResult) {
376
+ const arr = byCommunity.get(row.community_id) ?? [];
377
+ arr.push(row.entity_id);
378
+ byCommunity.set(row.community_id, arr);
379
+ }
380
+ let generatedSummaries = 0;
381
+ const results = [];
382
+ for (const [communityId, entityIds] of byCommunity.entries()) {
383
+ if (entityIds.length < minSize)
384
+ continue;
385
+ console.error(`[GraphRAG] Summarizing community ${communityId} with ${entityIds.length} members...`);
386
+ // Fetch details for all entities in this community
387
+ const entityInfos = [];
388
+ for (const eId of entityIds) {
389
+ try {
390
+ // get entity
391
+ const entRes = await this.db.run('?[name, type] := *entity{id: $id, name, type, @ "NOW"}', { id: eId });
392
+ if (entRes.rows.length > 0) {
393
+ const name = entRes.rows[0][0];
394
+ const type = entRes.rows[0][1];
395
+ // get top observations for this entity
396
+ const obsRes = await this.db.run('?[text, created_at] := *observation{entity_id: $id, text, created_at, @ "NOW"} :limit 3', { id: eId });
397
+ const obs = obsRes.rows.map((r) => r[0]);
398
+ entityInfos.push(`- ${name} (${type})` + (obs.length > 0 ? `\n Notes: ${obs.join("; ")}` : ""));
399
+ }
400
+ }
401
+ catch (e) {
402
+ console.warn(`[GraphRAG] Error fetching info for ${eId}: ${e.message}`);
403
+ }
404
+ }
405
+ if (entityInfos.length === 0)
406
+ continue;
407
+ const systemPrompt = "You are an expert analyst. Summarize the following community of related entities into a single, cohesive 'Community Report'. Identify the overarching themes, topics, and connections between these entities. Respond ONLY with the finalized summary text.";
408
+ const userPrompt = `Community Size: ${entityIds.length}\n\nMembers & Notes:\n${entityInfos.join("\n")}`;
409
+ let summaryText = "";
410
+ try {
411
+ const ollamaMod = await import("ollama");
412
+ const ollamaClient = ollamaMod?.default ?? ollamaMod;
413
+ const response = await ollamaClient.chat({
414
+ model,
415
+ messages: [
416
+ { role: "system", content: systemPrompt },
417
+ { role: "user", content: userPrompt },
418
+ ],
419
+ });
420
+ summaryText = response?.message?.content?.trim?.() ?? "";
421
+ }
422
+ catch (e) {
423
+ console.warn(`[GraphRAG] Ollama error for community ${communityId}: ${e.message}`);
424
+ }
425
+ if (!summaryText || summaryText === "") {
426
+ console.warn(`[GraphRAG] Could not generate summary for community ${communityId}, skipping.`);
427
+ continue;
428
+ }
429
+ // Create new CommunitySummary entity
430
+ const nowIso = new Date().toISOString();
431
+ const summaryName = `Community Summary ${communityId.slice(0, 8)} (${nowIso.slice(0, 10)})`;
432
+ const sumEntity = await this.createEntity({
433
+ name: summaryName,
434
+ type: "CommunitySummary",
435
+ metadata: {
436
+ graphrag: {
437
+ kind: "community_summary",
438
+ community_id: communityId,
439
+ member_count: entityIds.length,
440
+ member_ids: entityIds,
441
+ model,
442
+ summarized_at: nowIso
443
+ }
444
+ }
445
+ });
446
+ const summaryEntityId = sumEntity?.id;
447
+ if (summaryEntityId) {
448
+ await this.addObservation({
449
+ entity_id: summaryEntityId,
450
+ text: summaryText,
451
+ metadata: {
452
+ graphrag: {
453
+ kind: "community_summary",
454
+ community_id: communityId,
455
+ }
456
+ }
457
+ });
458
+ // Link the summary to all members
459
+ for (const memberId of entityIds) {
460
+ await this.createRelation({
461
+ from_id: summaryEntityId,
462
+ to_id: memberId,
463
+ relation_type: "summary_of",
464
+ strength: 1.0,
465
+ metadata: { community_id: communityId }
466
+ });
467
+ }
468
+ generatedSummaries++;
469
+ results.push({
470
+ community_id: communityId,
471
+ summary_entity_id: summaryEntityId,
472
+ member_count: entityIds.length
473
+ });
474
+ }
475
+ }
476
+ return {
477
+ status: "completed",
478
+ generated_summaries: generatedSummaries,
479
+ results
480
+ };
481
+ }
364
482
  async recomputeBetweennessCentrality() {
365
483
  await this.initPromise;
366
484
  const edgeCheckRes = await this.db.run(`?[from_id] := *relationship{from_id, @ "NOW"} :limit 1`);
@@ -1482,6 +1600,7 @@ ids[id] <- $ids
1482
1600
  async reflectMemory(args) {
1483
1601
  await this.initPromise;
1484
1602
  const model = args.model ?? "demyagent-4b-i1:Q6_K";
1603
+ const mode = args.mode ?? "summary";
1485
1604
  const targetEntityId = args.entity_id;
1486
1605
  let entitiesToReflect = [];
1487
1606
  if (targetEntityId) {
@@ -1504,47 +1623,114 @@ ids[id] <- $ids
1504
1623
  }
1505
1624
  const results = [];
1506
1625
  for (const entity of entitiesToReflect) {
1507
- const obsRes = await this.db.run('?[text, ts] := *observation{entity_id: $id, text, created_at, @ "NOW"}, ts = to_int(created_at) :order ts', {
1508
- id: entity.id,
1509
- });
1510
- if (obsRes.rows.length < 2) {
1511
- results.push({ entity_id: entity.id, status: "skipped", reason: "Too few observations for reflection" });
1512
- continue;
1513
- }
1514
- const observations = obsRes.rows.map((r) => `- [${new Date(Number(r[1]) / 1000).toISOString()}] ${r[0]}`);
1515
- const systemPrompt = `You are an analytical memory module. Analyze the following observations about an entity.
1626
+ if (mode === "summary") {
1627
+ const obsRes = await this.db.run('?[text, ts] := *observation{entity_id: $id, text, created_at, @ "NOW"}, ts = to_int(created_at) :order ts', {
1628
+ id: entity.id,
1629
+ });
1630
+ if (obsRes.rows.length < 2) {
1631
+ results.push({ entity_id: entity.id, status: "skipped", reason: "Too few observations for reflection" });
1632
+ continue;
1633
+ }
1634
+ const observations = obsRes.rows.map((r) => `- [${new Date(Number(r[1]) / 1000).toISOString()}] ${r[0]}`);
1635
+ const systemPrompt = `You are an analytical memory module. Analyze the following observations about an entity.
1516
1636
  Look for contradictions, temporal developments, behavioral patterns, or deeper insights.
1517
1637
  Formulate a concise reflection (max. 3-4 sentences) that helps the user understand the current state or evolution.
1518
1638
  If there are contradictory statements, name them explicitly.
1519
1639
  If no special patterns are recognizable, answer with "No new insights".`;
1520
- const userPrompt = `Entity: ${entity.name} (${entity.type})\n\nObservations:\n${observations.join("\n")}`;
1521
- let reflectionText;
1522
- try {
1523
- const ollamaMod = await import("ollama");
1524
- const ollamaClient = ollamaMod?.default ?? ollamaMod;
1525
- const response = await ollamaClient.chat({
1526
- model,
1527
- messages: [
1528
- { role: "system", content: systemPrompt },
1529
- { role: "user", content: userPrompt },
1530
- ],
1531
- });
1532
- reflectionText = response?.message?.content?.trim?.() ?? "";
1533
- }
1534
- catch (e) {
1535
- console.error(`[Reflect] Ollama error for ${entity.name}:`, e);
1536
- reflectionText = "";
1537
- }
1538
- if (reflectionText && reflectionText !== "No new insights" && !reflectionText.includes("No new insights")) {
1539
- await this.addObservation({
1540
- entity_id: entity.id,
1541
- text: `Reflexive insight: ${reflectionText}`,
1542
- metadata: { kind: "reflection", model, generated_at: Date.now() },
1543
- });
1544
- results.push({ entity_id: entity.id, status: "reflected", insight: reflectionText });
1545
- }
1546
- else {
1547
- results.push({ entity_id: entity.id, status: "no_insight_found" });
1640
+ const userPrompt = `Entity: ${entity.name} (${entity.type})\n\nObservations:\n${observations.join("\n")}`;
1641
+ let reflectionText;
1642
+ try {
1643
+ const ollamaMod = await import("ollama");
1644
+ const ollamaClient = ollamaMod?.default ?? ollamaMod;
1645
+ const response = await ollamaClient.chat({
1646
+ model,
1647
+ messages: [
1648
+ { role: "system", content: systemPrompt },
1649
+ { role: "user", content: userPrompt },
1650
+ ],
1651
+ });
1652
+ reflectionText = response?.message?.content?.trim?.() ?? "";
1653
+ }
1654
+ catch (e) {
1655
+ console.error(`[Reflect] Ollama error for ${entity.name}:`, e);
1656
+ reflectionText = "";
1657
+ }
1658
+ if (reflectionText && reflectionText !== "No new insights" && !reflectionText.includes("No new insights")) {
1659
+ await this.addObservation({
1660
+ entity_id: entity.id,
1661
+ text: `Reflexive insight: ${reflectionText}`,
1662
+ metadata: { kind: "reflection", model, generated_at: Date.now() },
1663
+ });
1664
+ results.push({ entity_id: entity.id, status: "reflected", insight: reflectionText });
1665
+ }
1666
+ else {
1667
+ results.push({ entity_id: entity.id, status: "no_insight_found" });
1668
+ }
1669
+ }
1670
+ else if (mode === "discovery") {
1671
+ // Discovery Mode: Find and validate new relationships
1672
+ const candidates = await this.inferenceEngine.getRefinementCandidates(entity.id);
1673
+ const discoveryResults = [];
1674
+ const suggestions = [];
1675
+ for (const candidate of candidates) {
1676
+ // Check if relationship already exists
1677
+ const existing = await this.db.run(`
1678
+ ?[count(from_id)] := *relationship{from_id, to_id, relation_type, @ "NOW"},
1679
+ from_id = $from, to_id = $to, relation_type = $rel
1680
+ `, { from: candidate.from_id, to: candidate.to_id, rel: candidate.relation_type });
1681
+ if (Number(existing.rows[0][0]) > 0)
1682
+ continue;
1683
+ // Load target entity details for context
1684
+ const targetRes = await this.db.run('?[name, type] := *entity{id, name, type, @ "NOW"}, id = $id', { id: candidate.to_id });
1685
+ const targetName = String(targetRes.rows[0][0]);
1686
+ const targetType = String(targetRes.rows[0][1]);
1687
+ const validatePrompt = `You are a Knowledge Graph specialized AI. Evaluate if the following suggested relationship is logically sound based on the reason provided.
1688
+ Source Entity: ${entity.name} (${entity.type})
1689
+ Target Entity: ${targetName} (${targetType})
1690
+ Suggested Relationship: ${candidate.relation_type}
1691
+ Reason: ${candidate.reason}
1692
+
1693
+ Respond with a JSON object:
1694
+ {
1695
+ "confidence": <0.0 to 1.0>,
1696
+ "reasoning": "<short explanation>",
1697
+ "verdict": "create" | "suggest" | "reject"
1698
+ }
1699
+ Assign "create" if confidence > 0.8, "suggest" if confidence > 0.5, else "reject".`;
1700
+ try {
1701
+ const ollamaMod = await import("ollama");
1702
+ const ollamaClient = ollamaMod?.default ?? ollamaMod;
1703
+ const response = await ollamaClient.chat({
1704
+ model,
1705
+ messages: [{ role: "user", content: validatePrompt }],
1706
+ format: "json"
1707
+ });
1708
+ const validation = JSON.parse(response?.message?.content ?? "{}");
1709
+ if (validation.verdict === "create" && validation.confidence > 0.8) {
1710
+ await this.createRelation({
1711
+ from_id: candidate.from_id,
1712
+ to_id: candidate.to_id,
1713
+ relation_type: candidate.relation_type,
1714
+ strength: validation.confidence,
1715
+ metadata: { source: "reflection", reasoning: validation.reasoning, model }
1716
+ });
1717
+ discoveryResults.push({ target: targetName, type: candidate.relation_type, status: "created" });
1718
+ }
1719
+ else if (validation.verdict === "suggest" || validation.confidence > 0.5) {
1720
+ suggestions.push({
1721
+ from_id: candidate.from_id,
1722
+ to_id: candidate.to_id,
1723
+ relation_type: candidate.relation_type,
1724
+ confidence: validation.confidence,
1725
+ reason: validation.reasoning
1726
+ });
1727
+ }
1728
+ }
1729
+ catch (e) {
1730
+ console.error(`[Discovery] Validation failed for ${targetName}:`, e);
1731
+ }
1732
+ }
1733
+ results.push({ entity_id: entity.id, status: "discovery_completed", created: discoveryResults, suggestions });
1548
1734
  }
1549
1735
  }
1550
1736
  return { status: "completed", results };
@@ -2501,12 +2687,17 @@ Validation: Invalid syntax or missing columns in inference rules will result in
2501
2687
  max_depth: zod_1.z.number().min(1).max(5).optional().default(3).describe("Maximum walking depth"),
2502
2688
  limit: zod_1.z.number().optional().default(5).describe("Number of results"),
2503
2689
  }),
2690
+ zod_1.z.object({
2691
+ action: zod_1.z.literal("agentic_search"),
2692
+ query: zod_1.z.string().describe("Context query for agentic routing"),
2693
+ limit: zod_1.z.number().optional().default(10).describe("Maximum number of results"),
2694
+ }),
2504
2695
  ]);
2505
2696
  const QueryMemoryParameters = zod_1.z.object({
2506
2697
  action: zod_1.z
2507
- .enum(["search", "advancedSearch", "context", "entity_details", "history", "graph_rag", "graph_walking"])
2698
+ .enum(["search", "advancedSearch", "context", "entity_details", "history", "graph_rag", "graph_walking", "agentic_search"])
2508
2699
  .describe("Action (determines which fields are required)"),
2509
- query: zod_1.z.string().optional().describe("Required for search/advancedSearch/context/graph_rag/graph_walking"),
2700
+ query: zod_1.z.string().optional().describe("Required for search/advancedSearch/context/graph_rag/graph_walking/agentic_search"),
2510
2701
  limit: zod_1.z.number().optional().describe("Only for search/advancedSearch/graph_rag/graph_walking"),
2511
2702
  filters: zod_1.z.any().optional().describe("Only for advancedSearch"),
2512
2703
  graphConstraints: zod_1.z.any().optional().describe("Only for advancedSearch"),
@@ -2534,8 +2725,9 @@ Supported actions:
2534
2725
  - 'history': Retrieve historical evolution of an entity. Params: { entity_id: string }.
2535
2726
  - 'graph_rag': Graph-based reasoning (Hybrid RAG). Finds semantic vector seeds first, then expands via graph traversals. Ideal for multi-hop reasoning. Params: { query: string, max_depth?: number, limit?: number }.
2536
2727
  - 'graph_walking': Recursive semantic graph search. Starts at vector seeds or an entity and follows relationships to other semantically relevant entities. Params: { query: string, start_entity_id?: string, max_depth?: number, limit?: number }.
2728
+ - 'agentic_search': Auto-Routing Search. Uses local LLM to analyze intent and routes the query automatically to the best strategy (Vector, Graph, or Community Summaries). Params: { query: string, limit?: number }.
2537
2729
 
2538
- Notes: 'context' is ideal for exploratory questions. 'search' and 'advancedSearch' are better for targeted fact retrieval.`,
2730
+ Notes: 'agentic_search' is the most powerful and adaptable, 'context' is ideal for exploratory questions. 'search' and 'advancedSearch' are better for targeted fact retrieval.`,
2539
2731
  parameters: QueryMemoryParameters,
2540
2732
  execute: async (args) => {
2541
2733
  await this.initPromise;
@@ -2689,6 +2881,16 @@ Notes: 'context' is ideal for exploratory questions. 'search' and 'advancedSearc
2689
2881
  };
2690
2882
  return JSON.stringify(context);
2691
2883
  }
2884
+ if (input.action === "agentic_search") {
2885
+ if (!input.query || input.query.trim().length === 0) {
2886
+ return JSON.stringify({ error: "Search query must not be empty." });
2887
+ }
2888
+ const results = await this.hybridSearch.agenticRetrieve({
2889
+ query: input.query,
2890
+ limit: input.limit,
2891
+ });
2892
+ return JSON.stringify(results);
2893
+ }
2692
2894
  if (input.action === "graph_rag") {
2693
2895
  if (!input.query || input.query.trim().length === 0) {
2694
2896
  return JSON.stringify({ error: "Search query must not be empty." });
@@ -3165,15 +3367,21 @@ Supported actions:
3165
3367
  action: zod_1.z.literal("reflect"),
3166
3368
  entity_id: zod_1.z.string().optional().describe("Optional entity ID for targeted reflection"),
3167
3369
  model: zod_1.z.string().optional().default("demyagent-4b-i1:Q6_K"),
3370
+ mode: zod_1.z.enum(["summary", "discovery"]).optional().default("summary").describe("Reflection mode: 'summary' for insights, 'discovery' for new links"),
3168
3371
  }),
3169
3372
  zod_1.z.object({
3170
3373
  action: zod_1.z.literal("clear_memory"),
3171
3374
  confirm: zod_1.z.boolean().describe("Must be true to confirm deletion"),
3172
3375
  }),
3376
+ zod_1.z.object({
3377
+ action: zod_1.z.literal("summarize_communities"),
3378
+ model: zod_1.z.string().optional().default("demyagent-4b-i1:Q6_K"),
3379
+ min_community_size: zod_1.z.number().min(2).max(100).optional().default(3),
3380
+ }),
3173
3381
  ]);
3174
3382
  const ManageSystemParameters = zod_1.z.object({
3175
3383
  action: zod_1.z
3176
- .enum(["health", "metrics", "export_memory", "import_memory", "snapshot_create", "snapshot_list", "snapshot_diff", "cleanup", "reflect", "clear_memory"])
3384
+ .enum(["health", "metrics", "export_memory", "import_memory", "snapshot_create", "snapshot_list", "snapshot_diff", "cleanup", "reflect", "clear_memory", "summarize_communities"])
3177
3385
  .describe("Action (determines which fields are required)"),
3178
3386
  format: zod_1.z.enum(["json", "markdown", "obsidian"]).optional().describe("Export format (for export_memory)"),
3179
3387
  includeMetadata: zod_1.z.boolean().optional().describe("Include metadata (for export_memory)"),
@@ -3192,8 +3400,10 @@ Supported actions:
3192
3400
  older_than_days: zod_1.z.number().optional().describe("Optional for cleanup"),
3193
3401
  max_observations: zod_1.z.number().optional().describe("Optional for cleanup"),
3194
3402
  min_entity_degree: zod_1.z.number().optional().describe("Optional for cleanup"),
3195
- model: zod_1.z.string().optional().describe("Optional for cleanup/reflect"),
3403
+ model: zod_1.z.string().optional().describe("Optional for cleanup/reflect/summarize_communities"),
3196
3404
  entity_id: zod_1.z.string().optional().describe("Optional for reflect"),
3405
+ min_community_size: zod_1.z.number().optional().describe("Optional for summarize_communities"),
3406
+ mode: zod_1.z.enum(["summary", "discovery"]).optional().describe("Optional for reflect"),
3197
3407
  });
3198
3408
  this.mcp.addTool({
3199
3409
  name: "manage_system",
@@ -3217,7 +3427,8 @@ Supported actions:
3217
3427
  * With confirm=false: Dry-Run (shows candidates).
3218
3428
  * With confirm=true: Merges old/isolated fragments using LLM (Executive Summary) and removes noise.
3219
3429
  - 'reflect': Reflection service. Analyzes memory for contradictions and insights. Params: { entity_id?: string, model?: string }.
3220
- - 'clear_memory': Resets the entire database. Params: { confirm: boolean (must be true) }.`,
3430
+ - 'clear_memory': Resets the entire database. Params: { confirm: boolean (must be true) }.
3431
+ - 'summarize_communities': Hierarchical GraphRAG. Generates summaries for entity clusters. Params: { model?: string, min_community_size?: number }.`,
3221
3432
  parameters: ManageSystemParameters,
3222
3433
  execute: async (args) => {
3223
3434
  await this.initPromise;
@@ -3410,6 +3621,7 @@ Supported actions:
3410
3621
  const result = await this.reflectMemory({
3411
3622
  entity_id: input.entity_id,
3412
3623
  model: input.model,
3624
+ mode: input.mode,
3413
3625
  });
3414
3626
  return JSON.stringify(result);
3415
3627
  }
@@ -3417,6 +3629,18 @@ Supported actions:
3417
3629
  return JSON.stringify({ error: error.message || "Error during reflection" });
3418
3630
  }
3419
3631
  }
3632
+ if (input.action === "summarize_communities") {
3633
+ try {
3634
+ const result = await this.summarizeCommunities({
3635
+ model: input.model,
3636
+ min_community_size: input.min_community_size,
3637
+ });
3638
+ return JSON.stringify(result);
3639
+ }
3640
+ catch (error) {
3641
+ return JSON.stringify({ error: error.message || "Error during summarize_communities" });
3642
+ }
3643
+ }
3420
3644
  if (input.action === "clear_memory") {
3421
3645
  if (!input.confirm) {
3422
3646
  return JSON.stringify({ error: "Deletion not confirmed. Set 'confirm' to true." });
@@ -28,6 +28,13 @@ class InferenceEngine {
28
28
  results.push(...custom);
29
29
  return results;
30
30
  }
31
+ /**
32
+ * Aggregates potential links from all strategies for the "discovery" mode of reflection.
33
+ */
34
+ async getRefinementCandidates(entityId) {
35
+ // This is essentially the same as inferRelations but explicitly for refinement
36
+ return this.inferRelations(entityId);
37
+ }
31
38
  async inferImplicitRelations(entityId) {
32
39
  const expertise = await this.findTransitiveExpertise(entityId);
33
40
  const custom = await this.applyCustomRules(entityId);
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const index_1 = require("./index");
4
+ const uuid_1 = require("uuid");
5
+ async function run() {
6
+ const server = new index_1.MemoryServer();
7
+ await server.initPromise;
8
+ console.log("Directly inserting old entity and observations via CozoDB...");
9
+ const fortyDaysAgo = Math.floor(Date.now() - 40 * 24 * 60 * 60 * 1000) * 1000;
10
+ const entityId = (0, uuid_1.v4)();
11
+ const name = "Very Old Project";
12
+ const type = "Project";
13
+ const metadata = { purpose: "testing janitor" };
14
+ const zeroVec = new Array(1024).fill(0);
15
+ try {
16
+ await server.db.run(`
17
+ ?[id, name, type, embedding, name_embedding, metadata, created_at] <- [[$id, $name, $type, $embedding, $name_embedding, $metadata, [$fortyDaysAgo, true]]]
18
+ :insert entity {id, name, type, embedding, name_embedding, metadata, created_at}
19
+ `, {
20
+ id: entityId,
21
+ name,
22
+ type,
23
+ embedding: zeroVec,
24
+ name_embedding: zeroVec,
25
+ metadata,
26
+ fortyDaysAgo
27
+ });
28
+ console.log("Old entity inserted: " + entityId);
29
+ const obsTexts = [
30
+ "This is a really old architecture note.",
31
+ "We decided to use subversion for version control.",
32
+ "The server is a physical machine in the basement.",
33
+ "We wrote our own ORM from scratch.",
34
+ "Deployment takes 3 days and a lot of manual steps."
35
+ ];
36
+ for (const text of obsTexts) {
37
+ const obsId = (0, uuid_1.v4)();
38
+ await server.db.run(`
39
+ ?[id, entity_id, text, embedding, metadata, created_at] <- [[$id, $entity_id, $text, $embedding, $metadata, [$fortyDaysAgo, true]]]
40
+ :insert observation {id, entity_id, text, embedding, metadata, created_at}
41
+ `, {
42
+ id: obsId,
43
+ entity_id: entityId,
44
+ text,
45
+ embedding: zeroVec,
46
+ metadata: {},
47
+ fortyDaysAgo
48
+ });
49
+ console.log("Old observation inserted: " + obsId);
50
+ }
51
+ console.log("Successfully inserted old data!");
52
+ }
53
+ catch (e) {
54
+ console.error("DB error:", e.message);
55
+ }
56
+ process.exit(0);
57
+ }
58
+ run().catch(console.error);
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const index_1 = require("./index");
4
+ async function run() {
5
+ const server = new index_1.MemoryServer();
6
+ await server.initPromise;
7
+ console.log("Testing Agentic Retrieval Routing Logic...");
8
+ // Expose the protected hybridSearch for testing (TypeScript hack)
9
+ const hybridSearch = server.hybridSearch;
10
+ const queries = [
11
+ { text: "Welches Datenbank-System nutzt das Backend Project B?", expected: ["vector_search", "hybrid"] },
12
+ { text: "Wer arbeitet alles mit ReactJS oder was nutzt ReactJS?", expected: ["graph_walk", "hybrid"] },
13
+ { text: "Wie ist der generelle Status aller Frontend-Projekte?", expected: ["community_summary"] }
14
+ ];
15
+ for (const q of queries) {
16
+ console.log(`\n\n--- Query: "${q.text}" ---`);
17
+ console.log(`Expected Route: ${q.expected.join(" or ")}`);
18
+ const results = await hybridSearch.agenticRetrieve({ query: q.text, limit: 1 });
19
+ if (results.length > 0) {
20
+ console.log(`-> LLM Routed to: ${results[0].metadata?.agentic_routing}`);
21
+ }
22
+ else {
23
+ console.log(`\nNo results found. LLM might have routed to an empty strategy.`);
24
+ }
25
+ }
26
+ process.exit(0);
27
+ }
28
+ run().catch(console.error);
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const index_1 = require("../src/index");
7
+ const path_1 = __importDefault(require("path"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ async function runTest() {
10
+ const testDbPath = path_1.default.resolve(__dirname, "discovery_test.cozo");
11
+ // Cleanup previous test DB
12
+ if (fs_1.default.existsSync(testDbPath + ".db"))
13
+ fs_1.default.unlinkSync(testDbPath + ".db");
14
+ const server = new index_1.MemoryServer(testDbPath);
15
+ await server.initPromise;
16
+ console.log("1. Creating test entities...");
17
+ const aliceRes = await server.createEntity({
18
+ name: "Alice",
19
+ type: "Person",
20
+ metadata: { role: "Developer" }
21
+ });
22
+ const aliceId = aliceRes.id;
23
+ const phoenixRes = await server.createEntity({
24
+ name: "Project Phoenix",
25
+ type: "Project",
26
+ metadata: { status: "active" }
27
+ });
28
+ const phoenixId = phoenixRes.id;
29
+ console.log("2. Adding overlapping observations...");
30
+ await server.addObservation({
31
+ entity_id: aliceId,
32
+ text: "Alice is currently focusing all her time on Project Phoenix."
33
+ });
34
+ console.log("3. Running reflection in 'discovery' mode...");
35
+ // We specify the model to ensure it uses one that supports JSON mode if possible
36
+ const reflectRes = await server.reflectMemory({
37
+ entity_id: aliceId,
38
+ mode: "discovery"
39
+ });
40
+ console.log("Result:", JSON.stringify(reflectRes, null, 2));
41
+ console.log("4. Verifying relationship creation...");
42
+ const relationsRes = await server.db.run(`
43
+ ?[to_name, rel_type] := *relationship{from_id, to_id, relation_type: rel_type, @ "NOW"},
44
+ from_id = $alice,
45
+ *entity{id: to_id, name: to_name, @ "NOW"}
46
+ `, { alice: aliceId });
47
+ console.log("Relationships from Alice:", relationsRes.rows);
48
+ // Cleanup
49
+ if (fs_1.default.existsSync(testDbPath + ".db"))
50
+ fs_1.default.unlinkSync(testDbPath + ".db");
51
+ process.exit(0);
52
+ }
53
+ runTest().catch(console.error);
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const index_1 = require("./index");
4
+ async function run() {
5
+ const server = new index_1.MemoryServer();
6
+ await server.initPromise;
7
+ console.log("Setting up Test Clusters for GraphRAG Community Summaries...");
8
+ // Cluster 1: Frontend
9
+ const fe1 = await server.createEntity({ name: "ReactJS", type: "technology", metadata: {} });
10
+ const fe2 = await server.createEntity({ name: "Redux", type: "technology", metadata: {} });
11
+ const fe3 = await server.createEntity({ name: "Frontend Project A", type: "project", metadata: {} });
12
+ await server.addObservation({ entity_id: fe1.id, text: "ReactJS is used for building UIs." });
13
+ await server.addObservation({ entity_id: fe2.id, text: "Redux is used for state management in React." });
14
+ await server.addObservation({ entity_id: fe3.id, text: "Project A is a heavy frontend SPA using React and Redux." });
15
+ await server.createRelation({ from_id: fe3.id, to_id: fe1.id, relation_type: "uses", strength: 1.0 });
16
+ await server.createRelation({ from_id: fe3.id, to_id: fe2.id, relation_type: "uses", strength: 1.0 });
17
+ await server.createRelation({ from_id: fe1.id, to_id: fe2.id, relation_type: "integrates_with", strength: 1.0 });
18
+ console.log("Created Frontend Cluster");
19
+ // Cluster 2: Backend
20
+ const be1 = await server.createEntity({ name: "PostgreSQL", type: "database", metadata: {} });
21
+ const be2 = await server.createEntity({ name: "CozoDB", type: "database", metadata: {} });
22
+ const be3 = await server.createEntity({ name: "Backend Project B", type: "project", metadata: {} });
23
+ await server.addObservation({ entity_id: be1.id, text: "PostgreSQL is a robust relational database." });
24
+ await server.addObservation({ entity_id: be2.id, text: "CozoDB is a graph database with Datalog." });
25
+ await server.addObservation({ entity_id: be3.id, text: "Project B relies heavily on complex queries across PG and CozoDB." });
26
+ await server.createRelation({ from_id: be3.id, to_id: be1.id, relation_type: "uses", strength: 1.0 });
27
+ await server.createRelation({ from_id: be3.id, to_id: be2.id, relation_type: "uses", strength: 1.0 });
28
+ await server.createRelation({ from_id: be1.id, to_id: be2.id, relation_type: "migrating_to", strength: 1.0 });
29
+ console.log("Created Backend Cluster");
30
+ console.log("Initiating Community Summarization...");
31
+ const result = await server.summarizeCommunities({ min_community_size: 3 });
32
+ console.log("Community Summarization Result:", JSON.stringify(result, null, 2));
33
+ process.exit(0);
34
+ }
35
+ run().catch(console.error);