@zabaca/lattice 1.0.4 → 1.0.6

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.
Files changed (2) hide show
  1. package/dist/main.js +281 -35
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -711,7 +711,14 @@ class VoyageEmbeddingProvider {
711
711
  const embeddings = await this.generateEmbeddings([text]);
712
712
  return embeddings[0];
713
713
  }
714
+ async generateQueryEmbedding(text) {
715
+ const embeddings = await this.generateEmbeddingsWithType([text], "query");
716
+ return embeddings[0];
717
+ }
714
718
  async generateEmbeddings(texts) {
719
+ return this.generateEmbeddingsWithType(texts, this.inputType);
720
+ }
721
+ async generateEmbeddingsWithType(texts, inputType) {
715
722
  if (!texts || texts.length === 0) {
716
723
  return [];
717
724
  }
@@ -726,7 +733,7 @@ class VoyageEmbeddingProvider {
726
733
  model: this.model,
727
734
  input: texts,
728
735
  output_dimension: this.dimensions,
729
- input_type: this.inputType
736
+ input_type: inputType
730
737
  })
731
738
  });
732
739
  if (!response.ok) {
@@ -813,6 +820,15 @@ class EmbeddingService {
813
820
  }
814
821
  return this.provider.generateEmbedding(text);
815
822
  }
823
+ async generateQueryEmbedding(text) {
824
+ if (!text || text.trim().length === 0) {
825
+ throw new Error("Cannot generate embedding for empty text");
826
+ }
827
+ if (this.provider.generateQueryEmbedding) {
828
+ return this.provider.generateQueryEmbedding(text);
829
+ }
830
+ return this.provider.generateEmbedding(text);
831
+ }
816
832
  async generateEmbeddings(texts) {
817
833
  const validTexts = texts.filter((t) => t && t.trim().length > 0);
818
834
  if (validTexts.length === 0) {
@@ -1234,7 +1250,7 @@ class SearchCommand extends CommandRunner3 {
1234
1250
  const query = inputs[0];
1235
1251
  const limit = Math.min(parseInt(options.limit || "20", 10), 100);
1236
1252
  try {
1237
- const queryEmbedding = await this.embeddingService.generateEmbedding(query);
1253
+ const queryEmbedding = await this.embeddingService.generateQueryEmbedding(query);
1238
1254
  let results;
1239
1255
  if (options.label) {
1240
1256
  const labelResults = await this.graphService.vectorSearch(options.label, queryEmbedding, limit);
@@ -1570,6 +1586,46 @@ function collectUniqueEntities(docs) {
1570
1586
  // src/pure/validation.ts
1571
1587
  function validateDocuments(docs) {
1572
1588
  const errors = [];
1589
+ for (const doc of docs) {
1590
+ if (!doc.title || doc.title.trim() === "") {
1591
+ errors.push({
1592
+ path: doc.path,
1593
+ error: "Missing required field: title"
1594
+ });
1595
+ }
1596
+ if (!doc.summary || doc.summary.trim() === "") {
1597
+ errors.push({
1598
+ path: doc.path,
1599
+ error: "Missing required field: summary"
1600
+ });
1601
+ }
1602
+ if (!doc.created) {
1603
+ errors.push({
1604
+ path: doc.path,
1605
+ error: "Missing required field: created"
1606
+ });
1607
+ }
1608
+ if (!doc.updated) {
1609
+ errors.push({
1610
+ path: doc.path,
1611
+ error: "Missing required field: updated"
1612
+ });
1613
+ }
1614
+ if (!doc.status) {
1615
+ errors.push({
1616
+ path: doc.path,
1617
+ error: "Missing required field: status"
1618
+ });
1619
+ }
1620
+ for (const entity of doc.entities) {
1621
+ if (!entity.description || entity.description.trim() === "") {
1622
+ errors.push({
1623
+ path: doc.path,
1624
+ error: `Entity "${entity.name}" (${entity.type}) missing required field: description`
1625
+ });
1626
+ }
1627
+ }
1628
+ }
1573
1629
  const entityIndex = new Map;
1574
1630
  for (const doc of docs) {
1575
1631
  for (const entity of doc.entities) {
@@ -2485,15 +2541,175 @@ StatusCommand = __legacyDecorateClassTS([
2485
2541
  // src/commands/sync.command.ts
2486
2542
  import { watch } from "fs";
2487
2543
  import { join as join3 } from "path";
2488
- import { Injectable as Injectable13 } from "@nestjs/common";
2544
+ import { Injectable as Injectable14 } from "@nestjs/common";
2489
2545
  import { Command as Command5, CommandRunner as CommandRunner5, Option as Option4 } from "nest-commander";
2546
+
2547
+ // src/sync/graph-validator.service.ts
2548
+ import { Injectable as Injectable13, Logger as Logger6 } from "@nestjs/common";
2549
+ class GraphValidatorService {
2550
+ graph;
2551
+ logger = new Logger6(GraphValidatorService.name);
2552
+ constructor(graph) {
2553
+ this.graph = graph;
2554
+ }
2555
+ async validateGraph() {
2556
+ const issues = [];
2557
+ let totalNodes = 0;
2558
+ let documentsChecked = 0;
2559
+ let entitiesChecked = 0;
2560
+ try {
2561
+ const result = await this.graph.query("SELECT label, name, properties FROM nodes");
2562
+ totalNodes = result.resultSet.length;
2563
+ for (const row of result.resultSet) {
2564
+ const label = row[0];
2565
+ const name = row[1];
2566
+ const propertiesJson = row[2];
2567
+ let properties;
2568
+ try {
2569
+ properties = typeof propertiesJson === "string" ? JSON.parse(propertiesJson) : propertiesJson;
2570
+ } catch (_error) {
2571
+ issues.push({
2572
+ type: "error",
2573
+ nodeLabel: label,
2574
+ nodeName: name,
2575
+ field: "properties",
2576
+ message: "Invalid JSON in properties field",
2577
+ suggestion: "Re-sync this document to fix corrupted data"
2578
+ });
2579
+ continue;
2580
+ }
2581
+ if (label === "Document") {
2582
+ documentsChecked++;
2583
+ this.validateDocumentNode(name, properties, issues);
2584
+ } else {
2585
+ entitiesChecked++;
2586
+ this.validateEntityNode(label, name, properties, issues);
2587
+ }
2588
+ }
2589
+ } catch (error) {
2590
+ this.logger.error(`Graph validation failed: ${error instanceof Error ? error.message : String(error)}`);
2591
+ throw error;
2592
+ }
2593
+ const errorsFound = issues.filter((i) => i.type === "error").length;
2594
+ const warningsFound = issues.filter((i) => i.type === "warning").length;
2595
+ return {
2596
+ valid: errorsFound === 0,
2597
+ issues,
2598
+ stats: {
2599
+ totalNodes,
2600
+ documentsChecked,
2601
+ entitiesChecked,
2602
+ errorsFound,
2603
+ warningsFound
2604
+ }
2605
+ };
2606
+ }
2607
+ validateDocumentNode(name, properties, issues) {
2608
+ if ("name" in properties) {
2609
+ issues.push({
2610
+ type: "warning",
2611
+ nodeLabel: "Document",
2612
+ nodeName: name,
2613
+ field: "name",
2614
+ message: "Duplicate 'name' field in properties (already in column)",
2615
+ suggestion: "Re-sync to remove duplicate"
2616
+ });
2617
+ }
2618
+ const requiredFields = ["title", "contentHash"];
2619
+ for (const field of requiredFields) {
2620
+ if (!(field in properties)) {
2621
+ issues.push({
2622
+ type: "error",
2623
+ nodeLabel: "Document",
2624
+ nodeName: name,
2625
+ field,
2626
+ message: `Missing required field: ${field}`,
2627
+ suggestion: "Re-sync this document to populate required fields"
2628
+ });
2629
+ }
2630
+ }
2631
+ const recommendedFields = ["summary", "created", "updated", "status"];
2632
+ for (const field of recommendedFields) {
2633
+ if (!(field in properties)) {
2634
+ issues.push({
2635
+ type: "warning",
2636
+ nodeLabel: "Document",
2637
+ nodeName: name,
2638
+ field,
2639
+ message: `Missing recommended field: ${field}`,
2640
+ suggestion: `Add '${field}' to document frontmatter`
2641
+ });
2642
+ }
2643
+ }
2644
+ }
2645
+ validateEntityNode(label, name, properties, issues) {
2646
+ if ("name" in properties) {
2647
+ issues.push({
2648
+ type: "warning",
2649
+ nodeLabel: label,
2650
+ nodeName: name,
2651
+ field: "name",
2652
+ message: "Duplicate 'name' field in properties (already in column)",
2653
+ suggestion: "Re-sync to remove duplicate"
2654
+ });
2655
+ }
2656
+ if (!("description" in properties) || !properties.description) {
2657
+ issues.push({
2658
+ type: "error",
2659
+ nodeLabel: label,
2660
+ nodeName: name,
2661
+ field: "description",
2662
+ message: "Missing required field: description",
2663
+ suggestion: `Add description to ${label} entity in frontmatter`
2664
+ });
2665
+ }
2666
+ }
2667
+ async validateDocument(path2) {
2668
+ const issues = [];
2669
+ try {
2670
+ const result = await this.graph.query(`SELECT label, name, properties FROM nodes WHERE label = 'Document' AND name = '${this.escape(path2)}'`);
2671
+ if (result.resultSet.length === 0) {
2672
+ issues.push({
2673
+ type: "error",
2674
+ nodeLabel: "Document",
2675
+ nodeName: path2,
2676
+ field: "node",
2677
+ message: "Document not found in graph",
2678
+ suggestion: "Run 'lattice sync' to add this document"
2679
+ });
2680
+ return issues;
2681
+ }
2682
+ const row = result.resultSet[0];
2683
+ const propertiesJson = row[2];
2684
+ const properties = typeof propertiesJson === "string" ? JSON.parse(propertiesJson) : propertiesJson;
2685
+ this.validateDocumentNode(path2, properties, issues);
2686
+ } catch (error) {
2687
+ this.logger.error(`Failed to validate document ${path2}: ${error instanceof Error ? error.message : String(error)}`);
2688
+ throw error;
2689
+ }
2690
+ return issues;
2691
+ }
2692
+ escape(value) {
2693
+ return value.replace(/'/g, "''");
2694
+ }
2695
+ }
2696
+ GraphValidatorService = __legacyDecorateClassTS([
2697
+ Injectable13(),
2698
+ __legacyMetadataTS("design:paramtypes", [
2699
+ typeof GraphService === "undefined" ? Object : GraphService
2700
+ ])
2701
+ ], GraphValidatorService);
2702
+
2703
+ // src/commands/sync.command.ts
2490
2704
  class SyncCommand extends CommandRunner5 {
2491
2705
  syncService;
2706
+ _graphValidator;
2492
2707
  watcher = null;
2493
2708
  isShuttingDown = false;
2494
- constructor(syncService) {
2709
+ constructor(syncService, _graphValidator) {
2495
2710
  super();
2496
2711
  this.syncService = syncService;
2712
+ this._graphValidator = _graphValidator;
2497
2713
  }
2498
2714
  async run(paths, options) {
2499
2715
  if (options.watch && options.dryRun) {
@@ -2651,22 +2867,25 @@ class SyncCommand extends CommandRunner5 {
2651
2867
  });
2652
2868
  }
2653
2869
  if (result.changes && result.changes.length > 0) {
2654
- console.log(`
2870
+ const actualChanges = result.changes.filter((c) => c.changeType !== "unchanged");
2871
+ if (actualChanges.length > 0) {
2872
+ console.log(`
2655
2873
  \uD83D\uDCDD Changes:
2656
2874
  `);
2657
- const icons = {
2658
- new: "\u2795",
2659
- updated: "\uD83D\uDD04",
2660
- deleted: "\uD83D\uDDD1\uFE0F",
2661
- unchanged: "\u23ED\uFE0F"
2662
- };
2663
- result.changes.forEach((c) => {
2664
- const icon = icons[c.changeType];
2665
- console.log(` ${icon} ${c.changeType}: ${c.path}`);
2666
- if (c.reason) {
2667
- console.log(` ${c.reason}`);
2668
- }
2669
- });
2875
+ const icons = {
2876
+ new: "\u2795",
2877
+ updated: "\uD83D\uDD04",
2878
+ deleted: "\uD83D\uDDD1\uFE0F",
2879
+ unchanged: "\u23ED\uFE0F"
2880
+ };
2881
+ actualChanges.forEach((c) => {
2882
+ const icon = icons[c.changeType];
2883
+ console.log(` ${icon} ${c.changeType}: ${c.path}`);
2884
+ if (c.reason) {
2885
+ console.log(` ${c.reason}`);
2886
+ }
2887
+ });
2888
+ }
2670
2889
  }
2671
2890
  if (result.cascadeWarnings && result.cascadeWarnings.length > 0) {
2672
2891
  console.log(`
@@ -2795,28 +3014,31 @@ __legacyDecorateClassTS([
2795
3014
  __legacyMetadataTS("design:returntype", Boolean)
2796
3015
  ], SyncCommand.prototype, "parseNoEmbeddings", null);
2797
3016
  SyncCommand = __legacyDecorateClassTS([
2798
- Injectable13(),
3017
+ Injectable14(),
2799
3018
  Command5({
2800
3019
  name: "sync",
2801
3020
  arguments: "[paths...]",
2802
3021
  description: "Synchronize documents to the knowledge graph"
2803
3022
  }),
2804
3023
  __legacyMetadataTS("design:paramtypes", [
2805
- typeof SyncService === "undefined" ? Object : SyncService
3024
+ typeof SyncService === "undefined" ? Object : SyncService,
3025
+ typeof GraphValidatorService === "undefined" ? Object : GraphValidatorService
2806
3026
  ])
2807
3027
  ], SyncCommand);
2808
3028
  // src/commands/validate.command.ts
2809
- import { Injectable as Injectable14 } from "@nestjs/common";
3029
+ import { Injectable as Injectable15 } from "@nestjs/common";
2810
3030
  import { Command as Command6, CommandRunner as CommandRunner6, Option as Option5 } from "nest-commander";
2811
3031
  class ValidateCommand extends CommandRunner6 {
2812
3032
  parserService;
2813
- constructor(parserService) {
3033
+ _graphValidator;
3034
+ constructor(parserService, _graphValidator) {
2814
3035
  super();
2815
3036
  this.parserService = parserService;
3037
+ this._graphValidator = _graphValidator;
2816
3038
  }
2817
3039
  async run(_inputs, options) {
2818
3040
  try {
2819
- console.log(`Validating entities and relationships...
3041
+ console.log(`=== Document Validation ===
2820
3042
  `);
2821
3043
  const { docs, errors: schemaErrors } = await this.parserService.parseAllDocumentsWithErrors();
2822
3044
  const issues = [];
@@ -2851,7 +3073,7 @@ class ValidateCommand extends CommandRunner6 {
2851
3073
  console.log(`Found ${entityIndex.size} unique entities
2852
3074
  `);
2853
3075
  if (issues.length > 0) {
2854
- console.log(`Errors (${issues.length}):
3076
+ console.log(`Document Errors (${issues.length}):
2855
3077
  `);
2856
3078
  issues.forEach((i) => {
2857
3079
  console.log(` ${i.path}`);
@@ -2861,11 +3083,32 @@ class ValidateCommand extends CommandRunner6 {
2861
3083
  }
2862
3084
  console.log("");
2863
3085
  });
3086
+ } else {
3087
+ console.log(`\u2713 Markdown files valid (schema + relationships)
3088
+ `);
2864
3089
  }
2865
- if (issues.length === 0) {
2866
- console.log("All validations passed!");
2867
- }
2868
- process.exit(issues.length > 0 ? 1 : 0);
3090
+ const graphResult = {
3091
+ valid: true,
3092
+ issues: [],
3093
+ stats: {
3094
+ totalNodes: 0,
3095
+ documentsChecked: 0,
3096
+ entitiesChecked: 0,
3097
+ errorsFound: 0,
3098
+ warningsFound: 0
3099
+ }
3100
+ };
3101
+ const totalErrors = issues.length + graphResult.stats.errorsFound;
3102
+ const totalWarnings = graphResult.stats.warningsFound;
3103
+ console.log(`
3104
+ === Validation Summary ===`);
3105
+ console.log(`Markdown files: ${issues.length === 0 ? "\u2713 PASSED" : `\u2717 ${issues.length} errors`}`);
3106
+ console.log(`Graph database: ${graphResult.stats.errorsFound === 0 ? "\u2713 PASSED" : `\u2717 ${graphResult.stats.errorsFound} errors`}`);
3107
+ console.log(`Warnings: ${totalWarnings}`);
3108
+ console.log(`
3109
+ Overall: ${totalErrors === 0 ? "\u2713 PASSED" : "\u2717 FAILED"}${totalWarnings > 0 ? ` (${totalWarnings} warnings)` : ""}
3110
+ `);
3111
+ process.exit(totalErrors > 0 ? 1 : 0);
2869
3112
  } catch (error) {
2870
3113
  console.error("Validation failed:", error instanceof Error ? error.message : String(error));
2871
3114
  process.exit(1);
@@ -2885,13 +3128,14 @@ __legacyDecorateClassTS([
2885
3128
  __legacyMetadataTS("design:returntype", Boolean)
2886
3129
  ], ValidateCommand.prototype, "parseFix", null);
2887
3130
  ValidateCommand = __legacyDecorateClassTS([
2888
- Injectable14(),
3131
+ Injectable15(),
2889
3132
  Command6({
2890
3133
  name: "validate",
2891
3134
  description: "Validate entity references and relationships across documents"
2892
3135
  }),
2893
3136
  __legacyMetadataTS("design:paramtypes", [
2894
- typeof DocumentParserService === "undefined" ? Object : DocumentParserService
3137
+ typeof DocumentParserService === "undefined" ? Object : DocumentParserService,
3138
+ typeof GraphValidatorService === "undefined" ? Object : GraphValidatorService
2895
3139
  ])
2896
3140
  ], ValidateCommand);
2897
3141
  // src/embedding/embedding.module.ts
@@ -2922,10 +3166,10 @@ GraphModule = __legacyDecorateClassTS([
2922
3166
  import { Module as Module3 } from "@nestjs/common";
2923
3167
 
2924
3168
  // src/query/query.service.ts
2925
- import { Injectable as Injectable15, Logger as Logger6 } from "@nestjs/common";
3169
+ import { Injectable as Injectable16, Logger as Logger7 } from "@nestjs/common";
2926
3170
  class QueryService {
2927
3171
  graphService;
2928
- logger = new Logger6(QueryService.name);
3172
+ logger = new Logger7(QueryService.name);
2929
3173
  constructor(graphService) {
2930
3174
  this.graphService = graphService;
2931
3175
  }
@@ -2935,7 +3179,7 @@ class QueryService {
2935
3179
  }
2936
3180
  }
2937
3181
  QueryService = __legacyDecorateClassTS([
2938
- Injectable15(),
3182
+ Injectable16(),
2939
3183
  __legacyMetadataTS("design:paramtypes", [
2940
3184
  typeof GraphService === "undefined" ? Object : GraphService
2941
3185
  ])
@@ -2965,7 +3209,8 @@ SyncModule = __legacyDecorateClassTS([
2965
3209
  DocumentParserService,
2966
3210
  OntologyService,
2967
3211
  CascadeService,
2968
- PathResolverService
3212
+ PathResolverService,
3213
+ GraphValidatorService
2969
3214
  ],
2970
3215
  exports: [
2971
3216
  SyncService,
@@ -2973,7 +3218,8 @@ SyncModule = __legacyDecorateClassTS([
2973
3218
  DocumentParserService,
2974
3219
  OntologyService,
2975
3220
  CascadeService,
2976
- PathResolverService
3221
+ PathResolverService,
3222
+ GraphValidatorService
2977
3223
  ]
2978
3224
  })
2979
3225
  ], SyncModule);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zabaca/lattice",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Human-initiated, AI-powered knowledge graph for markdown documentation",
5
5
  "type": "module",
6
6
  "bin": {