hana-kgvector 0.2.10 → 0.2.12

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/README.md CHANGED
@@ -274,6 +274,7 @@ Retrieve relevant context from the graph.
274
274
  | `graphName` | `string` | Required | RDF named graph identifier (e.g., `"my_knowledge_graph"`) |
275
275
  | `vectorTableName` | `string` | Auto-generated | Custom table name for vector storage |
276
276
  | `documentNodesTableName` | `string` | Auto-generated | Custom table name for document nodes |
277
+ | `sqlBatchSize` | `number` | `500` | Prepared statement batch size for vector/document table writes. Can also be set with `HANA_KGVECTOR_SQL_BATCH_SIZE` |
277
278
  | `resetTables` | `boolean` | `false` | Drop and recreate tables on init (dev/test only) |
278
279
 
279
280
  ### Graph Discovery
@@ -439,14 +440,31 @@ await index.insert([
439
440
  { id: "doc_2", text: "SAP is headquartered in Walldorf.", metadata: {} },
440
441
  ]);
441
442
 
442
- // Query via vector similarity
443
+ // Query via vector similarity — returns matching document chunks directly
443
444
  const results = await index.query("Where is SAP located?");
445
+ for (const r of results) {
446
+ console.log(`[${r.score.toFixed(3)}] ${r.node.text}`);
447
+ }
444
448
 
445
449
  // Entities/relations can be inserted separately via the store
446
450
  await graphStore.upsertNodes([...]);
447
451
  await graphStore.upsertRelations([...]);
448
452
  ```
449
453
 
454
+ When `kgExtractors` is empty, `query()` returns document chunks ranked by vector similarity. When entities and relations are also present, `query()` returns both document matches and graph-expanded triplets, merged and sorted by score.
455
+
456
+ ## Storage Architecture
457
+
458
+ Each graph creates three storage areas:
459
+
460
+ | Storage | Table | Contents |
461
+ |---------|-------|----------|
462
+ | **`_VECTORS`** | `{graphName}_VECTORS` | Unified query table. Stores entities (`NODE_TYPE='entity'`), document vectors (`NODE_TYPE='document'`), and relations (`NODE_TYPE='relation'` with indexed `source_id`/`target_id` columns). |
463
+ | **`_NODES`** | `{graphName}_NODES` | Document node storage with full text, metadata, and content hashes for deduplication. |
464
+ | **RDF graph** | SPARQL workspace | SPARQL-accessible triples (backward compat). Relations are also written here when HANA KG Engine is available. |
465
+
466
+ `vectorQuery()` searches `_VECTORS` for rows with non-null embeddings (both entities and documents). `getRelMap()` queries `_VECTORS` for `NODE_TYPE='relation'` rows using indexed `source_id`/`target_id` lookups, with a SPARQL fallback for backward compatibility.
467
+
450
468
  ## Multi-Tenancy
451
469
 
452
470
  Isolate data for different domains using separate graph names:
package/dist/index.d.ts CHANGED
@@ -187,6 +187,8 @@ interface HanaPropertyGraphStoreOptions {
187
187
  documentNodesTableName?: string;
188
188
  /** Optional, unused for dimensionless REAL_VECTOR columns (kept for backward compat logging). */
189
189
  vectorDimension?: number;
190
+ /** SQL execBatch chunk size for vector/document table writes. Defaults to HANA_KGVECTOR_SQL_BATCH_SIZE or 500. */
191
+ sqlBatchSize?: number;
190
192
  /** If true, will DROP and recreate tables on init (intended for tests/dev only). */
191
193
  resetTables?: boolean;
192
194
  }
@@ -195,13 +197,18 @@ declare class HanaPropertyGraphStore implements PropertyGraphStore {
195
197
  private readonly graphName;
196
198
  private readonly vectorTableName;
197
199
  private readonly documentNodesTableName;
200
+ private readonly sqlBatchSize;
198
201
  private readonly resetTables;
199
202
  private initialized;
200
203
  readonly supportsVectorQueries = true;
201
204
  readonly supportsStructuredQueries = true;
202
205
  constructor(conn: HanaConnection, options: HanaPropertyGraphStoreOptions);
206
+ private resolveSqlBatchSize;
203
207
  private ensureInitialized;
204
208
  private exec;
209
+ private execBatch;
210
+ private uniqueBy;
211
+ private sanitizeRecord;
205
212
  private sparqlExecute;
206
213
  private entityToUri;
207
214
  private relationToTriples;
package/dist/index.js CHANGED
@@ -222,6 +222,7 @@ var HanaPropertyGraphStore = class {
222
222
  graphName;
223
223
  vectorTableName;
224
224
  documentNodesTableName;
225
+ sqlBatchSize;
225
226
  resetTables;
226
227
  initialized = false;
227
228
  supportsVectorQueries = true;
@@ -231,8 +232,14 @@ var HanaPropertyGraphStore = class {
231
232
  this.graphName = options.graphName;
232
233
  this.vectorTableName = options.vectorTableName ?? `${options.graphName.replace(/[^a-zA-Z0-9]/g, "_")}_VECTORS`;
233
234
  this.documentNodesTableName = options.documentNodesTableName ?? `${options.graphName.replace(/[^a-zA-Z0-9]/g, "_")}_NODES`;
235
+ this.sqlBatchSize = this.resolveSqlBatchSize(options.sqlBatchSize);
234
236
  this.resetTables = options.resetTables ?? false;
235
237
  }
238
+ resolveSqlBatchSize(configured) {
239
+ const raw = configured ?? Number(process.env.HANA_KGVECTOR_SQL_BATCH_SIZE);
240
+ if (!Number.isFinite(raw) || raw <= 0) return 500;
241
+ return Math.floor(raw);
242
+ }
236
243
  async ensureInitialized() {
237
244
  if (this.initialized) return;
238
245
  if (this.resetTables) {
@@ -250,6 +257,8 @@ var HanaPropertyGraphStore = class {
250
257
  label NVARCHAR(128),
251
258
  name NVARCHAR(512),
252
259
  properties NCLOB,
260
+ source_id NVARCHAR(512),
261
+ target_id NVARCHAR(512),
253
262
  embedding REAL_VECTOR
254
263
  )
255
264
  `;
@@ -259,6 +268,12 @@ var HanaPropertyGraphStore = class {
259
268
  throw err;
260
269
  }
261
270
  });
271
+ await this.exec(`ALTER TABLE ${this.vectorTableName} ADD (source_id NVARCHAR(512), target_id NVARCHAR(512))`).catch((err) => {
272
+ const message = String(err?.message ?? "");
273
+ if (!/duplicate column|already exists/i.test(message)) {
274
+ throw err;
275
+ }
276
+ });
262
277
  const createDocumentTable = `
263
278
  CREATE COLUMN TABLE ${this.documentNodesTableName} (
264
279
  id NVARCHAR(512) PRIMARY KEY,
@@ -291,6 +306,56 @@ var HanaPropertyGraphStore = class {
291
306
  }
292
307
  });
293
308
  }
309
+ async execBatch(sql, paramRows) {
310
+ if (paramRows.length === 0) return;
311
+ if (typeof this.conn.prepare !== "function") {
312
+ for (const params of paramRows) {
313
+ await this.exec(sql, params);
314
+ }
315
+ return;
316
+ }
317
+ for (let i = 0; i < paramRows.length; i += this.sqlBatchSize) {
318
+ const chunk = paramRows.slice(i, i + this.sqlBatchSize);
319
+ const stmt = await new Promise((resolve, reject) => {
320
+ this.conn.prepare(sql, (err, prepared) => {
321
+ if (err) reject(err);
322
+ else resolve(prepared);
323
+ });
324
+ });
325
+ try {
326
+ if (typeof stmt.execBatch !== "function") {
327
+ for (const params of chunk) {
328
+ await this.exec(sql, params);
329
+ }
330
+ continue;
331
+ }
332
+ await new Promise((resolve, reject) => {
333
+ stmt.execBatch(chunk, (err) => {
334
+ if (err) reject(err);
335
+ else resolve();
336
+ });
337
+ });
338
+ } finally {
339
+ try {
340
+ stmt.drop?.();
341
+ } catch {
342
+ }
343
+ }
344
+ }
345
+ }
346
+ uniqueBy(items, getId) {
347
+ const byId = /* @__PURE__ */ new Map();
348
+ for (const item of items) {
349
+ byId.set(getId(item), item);
350
+ }
351
+ return Array.from(byId.values());
352
+ }
353
+ sanitizeRecord(record) {
354
+ const safeRecord = { ...record ?? {} };
355
+ delete safeRecord.kg_nodes;
356
+ delete safeRecord.kg_relations;
357
+ return safeRecord;
358
+ }
294
359
  async sparqlExecute(sparql, headers = "") {
295
360
  try {
296
361
  const streamMod = await import("@sap/hana-client/extension/Stream");
@@ -348,46 +413,54 @@ var HanaPropertyGraphStore = class {
348
413
  async upsertNodes(nodes) {
349
414
  await this.ensureInitialized();
350
415
  if (nodes.length === 0) return;
351
- const triples = nodes.map((n) => this.entityToTriples(n)).join("\n");
416
+ const uniqueNodes = this.uniqueBy(nodes, (node) => node.id);
417
+ const triples = uniqueNodes.map((n) => this.entityToTriples(n)).join("\n");
352
418
  const sparql = `INSERT DATA { GRAPH <${this.graphName}> { ${triples} } }`;
353
419
  await this.sparqlExecute(sparql);
354
- for (const node of nodes) {
355
- const safeProps = { ...node.properties ?? {} };
356
- delete safeProps.kg_nodes;
357
- delete safeProps.kg_relations;
358
- const sql = node.embedding ? `
359
- UPSERT ${this.vectorTableName} (id, node_type, label, name, properties, embedding)
360
- VALUES (?, 'entity', ?, ?, ?, TO_REAL_VECTOR(?))
361
- WITH PRIMARY KEY
362
- ` : `
363
- UPSERT ${this.vectorTableName} (id, node_type, label, name, properties)
364
- VALUES (?, 'entity', ?, ?, ?)
365
- WITH PRIMARY KEY
366
- `;
367
- const params = [
368
- node.id,
369
- node.label,
370
- node.name,
371
- JSON.stringify(safeProps)
372
- ];
420
+ const rowsWithEmbedding = [];
421
+ const rowsWithoutEmbedding = [];
422
+ for (const node of uniqueNodes) {
423
+ const safeProps = this.sanitizeRecord(node.properties);
424
+ const row = [node.id, node.label, node.name, JSON.stringify(safeProps)];
373
425
  if (node.embedding) {
374
- params.push(JSON.stringify(node.embedding));
426
+ rowsWithEmbedding.push([...row, JSON.stringify(node.embedding)]);
427
+ } else {
428
+ rowsWithoutEmbedding.push(row);
375
429
  }
376
- await this.exec(sql, params);
377
430
  }
431
+ await this.execBatch(
432
+ `
433
+ UPSERT ${this.vectorTableName} (id, node_type, label, name, properties)
434
+ VALUES (?, 'entity', ?, ?, ?)
435
+ WITH PRIMARY KEY
436
+ `,
437
+ rowsWithoutEmbedding
438
+ );
439
+ await this.execBatch(
440
+ `
441
+ UPSERT ${this.vectorTableName} (id, node_type, label, name, properties, embedding)
442
+ VALUES (?, 'entity', ?, ?, ?, TO_REAL_VECTOR(?))
443
+ WITH PRIMARY KEY
444
+ `,
445
+ rowsWithEmbedding
446
+ );
378
447
  }
379
448
  async upsertRelations(relations) {
380
449
  await this.ensureInitialized();
381
450
  if (relations.length === 0) return;
451
+ const uniqueRelations = this.uniqueBy(
452
+ relations,
453
+ (rel) => rel.id ?? `${rel.sourceId}_${rel.label}_${rel.targetId}`
454
+ );
382
455
  const nodeIds = /* @__PURE__ */ new Set();
383
- for (const rel of relations) {
456
+ for (const rel of uniqueRelations) {
384
457
  nodeIds.add(rel.sourceId);
385
458
  nodeIds.add(rel.targetId);
386
459
  }
387
460
  const existingNodes = await this.get({ ids: Array.from(nodeIds) });
388
461
  const nodeMap = new Map(existingNodes.map((n) => [n.id, n]));
389
462
  const lines = [];
390
- for (const rel of relations) {
463
+ for (const rel of uniqueRelations) {
391
464
  const subj = nodeMap.get(rel.sourceId);
392
465
  const obj = nodeMap.get(rel.targetId);
393
466
  if (subj && obj) {
@@ -396,8 +469,37 @@ var HanaPropertyGraphStore = class {
396
469
  }
397
470
  if (lines.length > 0) {
398
471
  const sparql = `INSERT DATA { GRAPH <${this.graphName}> { ${lines.join("\n")} } }`;
399
- await this.sparqlExecute(sparql);
472
+ await this.sparqlExecute(sparql).catch((err) => {
473
+ console.warn(`[HanaPropertyGraphStore] SPARQL INSERT for relations failed (non-fatal):`, err?.message ?? err);
474
+ });
475
+ }
476
+ const relationRows = [];
477
+ for (const rel of uniqueRelations) {
478
+ const relId = rel.id ?? `${rel.sourceId}_${rel.label}_${rel.targetId}`;
479
+ const safeProps = {
480
+ sourceId: rel.sourceId,
481
+ targetId: rel.targetId,
482
+ ...rel.properties ?? {}
483
+ };
484
+ delete safeProps.kg_nodes;
485
+ delete safeProps.kg_relations;
486
+ relationRows.push([
487
+ relId,
488
+ rel.label,
489
+ `${rel.sourceId} -> ${rel.targetId}`,
490
+ JSON.stringify(safeProps),
491
+ rel.sourceId,
492
+ rel.targetId
493
+ ]);
400
494
  }
495
+ await this.execBatch(
496
+ `
497
+ UPSERT ${this.vectorTableName} (id, node_type, label, name, properties, source_id, target_id)
498
+ VALUES (?, 'relation', ?, ?, ?, ?, ?)
499
+ WITH PRIMARY KEY
500
+ `,
501
+ relationRows
502
+ );
401
503
  }
402
504
  async get(opts) {
403
505
  await this.ensureInitialized();
@@ -420,45 +522,93 @@ var HanaPropertyGraphStore = class {
420
522
  const depth = opts.depth ?? 1;
421
523
  const limit = opts.limit ?? 100;
422
524
  const ignoreRels = opts.ignoreRels ?? [KG_SOURCE_REL];
423
- const filterClause = nodeIds.map((id) => `CONTAINS(STR(?s), "${encodeURIComponent(id)}") || CONTAINS(STR(?o), "${encodeURIComponent(id)}")`).join(" || ");
424
- const ignoreFilter = ignoreRels.length > 0 ? `FILTER(${ignoreRels.map((r) => `!CONTAINS(STR(?p), "${r}")`).join(" && ")})` : "";
425
- const sparql = `
426
- SELECT ?s ?p ?o
427
- FROM <${this.graphName}>
428
- WHERE {
429
- ?s ?p ?o .
430
- FILTER(${filterClause})
431
- ${ignoreFilter}
432
- }
433
- LIMIT ${limit}
434
- `;
435
- const result = await this.exec(
436
- `SELECT * FROM SPARQL_TABLE('${sparql.replace(/'/g, "''")}')`
437
- );
525
+ const ignoreRelSet = new Set(ignoreRels);
438
526
  const triplets = [];
527
+ const seenKeys = /* @__PURE__ */ new Set();
439
528
  const nodeMap = new Map(opts.nodes.map((n) => [n.id, n]));
440
- for (const row of result ?? []) {
441
- const sUri = row.S ?? row.s;
442
- const pUri = row.P ?? row.p;
443
- const oUri = row.O ?? row.o;
444
- const sId = this.extractIdFromUri(sUri);
445
- const oId = this.extractIdFromUri(oUri);
446
- const relLabel = this.extractRelLabelFromUri(pUri);
447
- let subj = nodeMap.get(sId);
448
- let obj = nodeMap.get(oId);
449
- if (!subj) {
450
- subj = { id: sId, label: "UNKNOWN", name: sId, properties: {} };
529
+ const placeholders = nodeIds.map(() => "?").join(",");
530
+ const sqlRelQuery = `
531
+ SELECT id, label, name, properties, source_id, target_id
532
+ FROM ${this.vectorTableName}
533
+ WHERE node_type = 'relation'
534
+ AND (source_id IN (${placeholders}) OR target_id IN (${placeholders}))
535
+ LIMIT ?
536
+ `;
537
+ const sqlParams = [...nodeIds, ...nodeIds, limit];
538
+ try {
539
+ const sqlRows = await this.exec(sqlRelQuery, sqlParams);
540
+ for (const row of sqlRows ?? []) {
541
+ const relLabel = row.LABEL ?? row.label;
542
+ const sourceId = row.SOURCE_ID ?? row.source_id;
543
+ const targetId = row.TARGET_ID ?? row.target_id;
544
+ if (ignoreRelSet.has(relLabel)) continue;
545
+ const key = `${sourceId}|${relLabel}|${targetId}`;
546
+ if (seenKeys.has(key)) continue;
547
+ seenKeys.add(key);
548
+ let subj = nodeMap.get(sourceId);
549
+ let obj = nodeMap.get(targetId);
550
+ if (!subj) {
551
+ subj = { id: sourceId, label: "UNKNOWN", name: sourceId, properties: {} };
552
+ }
553
+ if (!obj) {
554
+ obj = { id: targetId, label: "UNKNOWN", name: targetId, properties: {} };
555
+ }
556
+ const rel = {
557
+ id: row.ID ?? row.id,
558
+ label: relLabel,
559
+ sourceId,
560
+ targetId,
561
+ properties: JSON.parse(row.PROPERTIES ?? row.properties ?? "{}")
562
+ };
563
+ triplets.push([subj, rel, obj]);
451
564
  }
452
- if (!obj) {
453
- obj = { id: oId, label: "UNKNOWN", name: oId, properties: {} };
565
+ } catch (err) {
566
+ console.warn(`[HanaPropertyGraphStore] SQL relation query failed (falling back to SPARQL only):`, err?.message ?? err);
567
+ }
568
+ try {
569
+ const filterClause = nodeIds.map((id) => `CONTAINS(STR(?s), "${encodeURIComponent(id)}") || CONTAINS(STR(?o), "${encodeURIComponent(id)}")`).join(" || ");
570
+ const ignoreFilter = ignoreRels.length > 0 ? `FILTER(${ignoreRels.map((r) => `!CONTAINS(STR(?p), "${r}")`).join(" && ")})` : "";
571
+ const sparql = `
572
+ SELECT ?s ?p ?o
573
+ FROM <${this.graphName}>
574
+ WHERE {
575
+ ?s ?p ?o .
576
+ FILTER(${filterClause})
577
+ ${ignoreFilter}
578
+ }
579
+ LIMIT ${limit}
580
+ `;
581
+ const result = await this.exec(
582
+ `SELECT * FROM SPARQL_TABLE('${sparql.replace(/'/g, "''")}')`
583
+ );
584
+ for (const row of result ?? []) {
585
+ const sUri = row.S ?? row.s;
586
+ const pUri = row.P ?? row.p;
587
+ const oUri = row.O ?? row.o;
588
+ const sId = this.extractIdFromUri(sUri);
589
+ const oId = this.extractIdFromUri(oUri);
590
+ const relLabel = this.extractRelLabelFromUri(pUri);
591
+ const key = `${sId}|${relLabel}|${oId}`;
592
+ if (seenKeys.has(key)) continue;
593
+ seenKeys.add(key);
594
+ let subj = nodeMap.get(sId);
595
+ let obj = nodeMap.get(oId);
596
+ if (!subj) {
597
+ subj = { id: sId, label: "UNKNOWN", name: sId, properties: {} };
598
+ }
599
+ if (!obj) {
600
+ obj = { id: oId, label: "UNKNOWN", name: oId, properties: {} };
601
+ }
602
+ const rel = {
603
+ label: relLabel,
604
+ sourceId: sId,
605
+ targetId: oId,
606
+ properties: {}
607
+ };
608
+ triplets.push([subj, rel, obj]);
454
609
  }
455
- const rel = {
456
- label: relLabel,
457
- sourceId: sId,
458
- targetId: oId,
459
- properties: {}
460
- };
461
- triplets.push([subj, rel, obj]);
610
+ } catch (err) {
611
+ console.warn(`[HanaPropertyGraphStore] SPARQL relation query failed (non-fatal):`, err?.message ?? err);
462
612
  }
463
613
  return triplets;
464
614
  }
@@ -510,41 +660,58 @@ var HanaPropertyGraphStore = class {
510
660
  }
511
661
  async upsertDocumentNodes(nodes) {
512
662
  await this.ensureInitialized();
513
- for (const node of nodes) {
514
- const { metadata, ...rest } = node;
515
- const safeMetadata = { ...metadata };
516
- delete safeMetadata.kg_nodes;
517
- delete safeMetadata.kg_relations;
518
- const docSql = `
519
- UPSERT ${this.documentNodesTableName} (id, text, metadata, hash, embedding)
520
- VALUES (?, ?, ?, ?, ${node.embedding ? "TO_REAL_VECTOR(?)" : "NULL"})
521
- WITH PRIMARY KEY
522
- `;
523
- const docParams = [
524
- node.id,
525
- node.text,
526
- JSON.stringify(safeMetadata ?? {}),
527
- node.hash ?? null
528
- ];
529
- if (node.embedding) {
530
- docParams.push(JSON.stringify(node.embedding));
531
- }
532
- await this.exec(docSql, docParams);
533
- const vecSql = node.embedding ? `
534
- UPSERT ${this.vectorTableName} (id, node_type, label, name, properties, embedding)
535
- VALUES (?, 'document', 'DOCUMENT', ?, ?, TO_REAL_VECTOR(?))
536
- WITH PRIMARY KEY
537
- ` : `
538
- UPSERT ${this.vectorTableName} (id, node_type, label, name, properties)
539
- VALUES (?, 'document', 'DOCUMENT', ?, ?)
540
- WITH PRIMARY KEY
541
- `;
542
- const vecParams = [node.id, node.id, JSON.stringify(safeMetadata ?? {})];
663
+ if (nodes.length === 0) return;
664
+ const uniqueNodes = this.uniqueBy(nodes, (node) => node.id);
665
+ const docRowsWithEmbedding = [];
666
+ const docRowsWithoutEmbedding = [];
667
+ const vectorRowsWithEmbedding = [];
668
+ const vectorRowsWithoutEmbedding = [];
669
+ for (const node of uniqueNodes) {
670
+ const safeMetadata = this.sanitizeRecord(node.metadata);
671
+ const metadataJson = JSON.stringify(safeMetadata ?? {});
672
+ const docRow = [node.id, node.text, metadataJson, node.hash ?? null];
673
+ const vectorRow = [node.id, node.id, metadataJson];
543
674
  if (node.embedding) {
544
- vecParams.push(JSON.stringify(node.embedding));
675
+ const embeddingJson = JSON.stringify(node.embedding);
676
+ docRowsWithEmbedding.push([...docRow, embeddingJson]);
677
+ vectorRowsWithEmbedding.push([...vectorRow, embeddingJson]);
678
+ } else {
679
+ docRowsWithoutEmbedding.push(docRow);
680
+ vectorRowsWithoutEmbedding.push(vectorRow);
545
681
  }
546
- await this.exec(vecSql, vecParams);
547
682
  }
683
+ await this.execBatch(
684
+ `
685
+ UPSERT ${this.documentNodesTableName} (id, text, metadata, hash, embedding)
686
+ VALUES (?, ?, ?, ?, TO_REAL_VECTOR(?))
687
+ WITH PRIMARY KEY
688
+ `,
689
+ docRowsWithEmbedding
690
+ );
691
+ await this.execBatch(
692
+ `
693
+ UPSERT ${this.documentNodesTableName} (id, text, metadata, hash, embedding)
694
+ VALUES (?, ?, ?, ?, NULL)
695
+ WITH PRIMARY KEY
696
+ `,
697
+ docRowsWithoutEmbedding
698
+ );
699
+ await this.execBatch(
700
+ `
701
+ UPSERT ${this.vectorTableName} (id, node_type, label, name, properties, embedding)
702
+ VALUES (?, 'document', 'DOCUMENT', ?, ?, TO_REAL_VECTOR(?))
703
+ WITH PRIMARY KEY
704
+ `,
705
+ vectorRowsWithEmbedding
706
+ );
707
+ await this.execBatch(
708
+ `
709
+ UPSERT ${this.vectorTableName} (id, node_type, label, name, properties)
710
+ VALUES (?, 'document', 'DOCUMENT', ?, ?)
711
+ WITH PRIMARY KEY
712
+ `,
713
+ vectorRowsWithoutEmbedding
714
+ );
548
715
  }
549
716
  async getDocumentNodes(ids) {
550
717
  await this.ensureInitialized();
@@ -721,86 +888,114 @@ var VectorContextRetriever = class extends BasePGRetriever {
721
888
  if (kgNodes.length === 0) {
722
889
  return [];
723
890
  }
724
- const kgIds = kgNodes.map((n) => n.id);
725
- const provenanceSet = /* @__PURE__ */ new Set();
726
- if (this.crossCheckBoost) {
727
- for (const node of kgNodes) {
728
- provenanceSet.add(node.id.toLowerCase());
729
- provenanceSet.add(node.name.toLowerCase());
730
- const props = node.properties ?? {};
731
- if (props.documentId) {
732
- provenanceSet.add(String(props.documentId).toLowerCase());
733
- }
734
- if (props.sourceChunk) {
735
- provenanceSet.add(String(props.sourceChunk).toLowerCase());
736
- }
891
+ const entityNodes = [];
892
+ const entityScores = [];
893
+ const documentResults = [];
894
+ for (let i = 0; i < kgNodes.length; i++) {
895
+ const node = kgNodes[i];
896
+ if (node.label === "DOCUMENT") {
897
+ documentResults.push({
898
+ node: {
899
+ id: node.id,
900
+ text: `Document: ${node.name}`,
901
+ metadata: node.properties ?? {},
902
+ refDocId: node.id
903
+ },
904
+ score: scores[i]
905
+ });
906
+ } else {
907
+ entityNodes.push(node);
908
+ entityScores.push(scores[i]);
737
909
  }
738
910
  }
739
- let triplets = await this.graphStore.getRelMap({
740
- nodes: kgNodes,
741
- depth: this.pathDepth,
742
- limit: this.limit,
743
- ignoreRels: [KG_SOURCE_REL]
744
- });
745
- if (this.includeStructuralEdges) {
746
- const chunkIds = /* @__PURE__ */ new Set();
747
- for (const node of kgNodes) {
748
- const sourceId = node.properties?.[TRIPLET_SOURCE_KEY];
749
- if (sourceId) {
750
- chunkIds.add(`CHUNK_${String(sourceId).replace(/\s+/g, "_").toUpperCase()}`);
751
- }
752
- if (node.label === "CHUNK") {
753
- chunkIds.add(node.id);
911
+ let tripletResults = [];
912
+ if (entityNodes.length > 0) {
913
+ const kgIds = entityNodes.map((n) => n.id);
914
+ const provenanceSet = /* @__PURE__ */ new Set();
915
+ if (this.crossCheckBoost) {
916
+ for (const node of entityNodes) {
917
+ provenanceSet.add(node.id.toLowerCase());
918
+ provenanceSet.add(node.name.toLowerCase());
919
+ const props = node.properties ?? {};
920
+ if (props.documentId) {
921
+ provenanceSet.add(String(props.documentId).toLowerCase());
922
+ }
923
+ if (props.sourceChunk) {
924
+ provenanceSet.add(String(props.sourceChunk).toLowerCase());
925
+ }
754
926
  }
755
927
  }
756
- if (chunkIds.size > 0) {
757
- const chunkNodes = await this.graphStore.get({ ids: Array.from(chunkIds) });
758
- if (chunkNodes.length > 0) {
759
- const structuralTriplets = await this.graphStore.getRelMap({
760
- nodes: chunkNodes,
761
- depth: this.structuralDepth,
762
- limit: this.limit,
763
- ignoreRels: [KG_SOURCE_REL]
764
- });
765
- const structuralRels = [STRUCT_SAME_PAGE, STRUCT_ADJACENT, STRUCT_CONTAINS];
766
- const structuralOnly = structuralTriplets.filter(
767
- ([_s, r, _o]) => structuralRels.includes(r.label)
768
- );
769
- const existingKeys = new Set(triplets.map(([s, r, o]) => `${s.id}|${r.label}|${o.id}`));
770
- for (const triplet of structuralOnly) {
771
- const key = `${triplet[0].id}|${triplet[1].label}|${triplet[2].id}`;
772
- if (!existingKeys.has(key)) {
773
- triplets.push(triplet);
774
- existingKeys.add(key);
928
+ let triplets = await this.graphStore.getRelMap({
929
+ nodes: entityNodes,
930
+ depth: this.pathDepth,
931
+ limit: this.limit,
932
+ ignoreRels: [KG_SOURCE_REL]
933
+ });
934
+ if (this.includeStructuralEdges) {
935
+ const chunkIds = /* @__PURE__ */ new Set();
936
+ for (const node of entityNodes) {
937
+ const sourceId = node.properties?.[TRIPLET_SOURCE_KEY];
938
+ if (sourceId) {
939
+ chunkIds.add(`CHUNK_${String(sourceId).replace(/\s+/g, "_").toUpperCase()}`);
940
+ }
941
+ if (node.label === "CHUNK") {
942
+ chunkIds.add(node.id);
943
+ }
944
+ }
945
+ if (chunkIds.size > 0) {
946
+ const chunkNodes = await this.graphStore.get({ ids: Array.from(chunkIds) });
947
+ if (chunkNodes.length > 0) {
948
+ const structuralTriplets = await this.graphStore.getRelMap({
949
+ nodes: chunkNodes,
950
+ depth: this.structuralDepth,
951
+ limit: this.limit,
952
+ ignoreRels: [KG_SOURCE_REL]
953
+ });
954
+ const structuralRels = [STRUCT_SAME_PAGE, STRUCT_ADJACENT, STRUCT_CONTAINS];
955
+ const structuralOnly = structuralTriplets.filter(
956
+ ([_s, r, _o]) => structuralRels.includes(r.label)
957
+ );
958
+ const existingKeys = new Set(triplets.map(([s, r, o]) => `${s.id}|${r.label}|${o.id}`));
959
+ for (const triplet of structuralOnly) {
960
+ const key = `${triplet[0].id}|${triplet[1].label}|${triplet[2].id}`;
961
+ if (!existingKeys.has(key)) {
962
+ triplets.push(triplet);
963
+ existingKeys.add(key);
964
+ }
775
965
  }
776
966
  }
777
967
  }
778
968
  }
779
- }
780
- const newScores = [];
781
- for (const triplet of triplets) {
782
- const idx1 = kgIds.indexOf(triplet[0].id);
783
- const idx2 = kgIds.indexOf(triplet[2].id);
784
- const score1 = idx1 >= 0 ? scores[idx1] : 0;
785
- const score2 = idx2 >= 0 ? scores[idx2] : 0;
786
- let baseScore = Math.max(score1, score2);
787
- if (this.crossCheckBoost && baseScore > 0) {
788
- const shouldBoost = this.checkProvenance(triplet[0], provenanceSet) || this.checkProvenance(triplet[2], provenanceSet);
789
- if (shouldBoost) {
790
- baseScore = Math.min(1, baseScore * this.crossCheckBoostFactor);
969
+ const newScores = [];
970
+ for (const triplet of triplets) {
971
+ const idx1 = kgIds.indexOf(triplet[0].id);
972
+ const idx2 = kgIds.indexOf(triplet[2].id);
973
+ const score1 = idx1 >= 0 ? entityScores[idx1] : 0;
974
+ const score2 = idx2 >= 0 ? entityScores[idx2] : 0;
975
+ let baseScore = Math.max(score1, score2);
976
+ if (this.crossCheckBoost && baseScore > 0) {
977
+ const shouldBoost = this.checkProvenance(triplet[0], provenanceSet) || this.checkProvenance(triplet[2], provenanceSet);
978
+ if (shouldBoost) {
979
+ baseScore = Math.min(1, baseScore * this.crossCheckBoostFactor);
980
+ }
791
981
  }
982
+ newScores.push(baseScore);
792
983
  }
793
- newScores.push(baseScore);
984
+ let results = triplets.map((t, i) => ({ triplet: t, score: newScores[i] }));
985
+ if (this.similarityScore !== void 0) {
986
+ results = results.filter((r) => r.score >= this.similarityScore);
987
+ }
988
+ results.sort((a, b) => b.score - a.score);
989
+ tripletResults = this.getNodesWithScore(
990
+ results.map((r) => r.triplet),
991
+ results.map((r) => r.score)
992
+ );
794
993
  }
795
- let results = triplets.map((t, i) => ({ triplet: t, score: newScores[i] }));
994
+ const allResults = [...documentResults, ...tripletResults];
796
995
  if (this.similarityScore !== void 0) {
797
- results = results.filter((r) => r.score >= this.similarityScore);
996
+ return allResults.filter((r) => r.score >= this.similarityScore).sort((a, b) => b.score - a.score);
798
997
  }
799
- results.sort((a, b) => b.score - a.score);
800
- return this.getNodesWithScore(
801
- results.map((r) => r.triplet),
802
- results.map((r) => r.score)
803
- );
998
+ return allResults.sort((a, b) => b.score - a.score);
804
999
  }
805
1000
  /**
806
1001
  * Check if a node has provenance linking to the vector-matched nodes.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hana/parse-host-port.ts","../src/hana/connection.ts","../src/hana/sparql-store.ts","../src/hana/graph-discovery.ts","../src/graph/types.ts","../src/graph/hana-property-graph-store.ts","../src/graph/pg-retriever.ts","../src/graph/retrievers/base.ts","../src/graph/retrievers/vector-context.ts","../src/graph/extractors/implicit.ts","../src/graph/property-graph-index.ts","../src/graph/extractors/schema-llm.ts","../src/graph/extractors/adjacency-linker.ts"],"sourcesContent":["export function parseHostPort(\n host: string,\n fallbackPort: number\n): { host: string; port: number } {\n const idx = host.lastIndexOf(\":\");\n if (idx > -1 && idx < host.length - 1) {\n const maybePort = Number(host.slice(idx + 1));\n if (!Number.isNaN(maybePort) && maybePort > 0) {\n return { host: host.slice(0, idx), port: maybePort };\n }\n }\n\n return { host, port: fallbackPort };\n}\n","import { parseHostPort } from \"./parse-host-port\";\nimport type { HanaConnection, HanaConnectionConfig } from \"./types\";\n\nexport async function createHanaConnection(\n config: HanaConnectionConfig\n): Promise<HanaConnection> {\n let hana: any;\n try {\n const hanaModule = await import(\"@sap/hana-client\");\n hana = (hanaModule as any).default || hanaModule;\n } catch {\n throw new Error(\n \"@sap/hana-client is not available. Ensure it is installed and pnpm build scripts are approved (pnpm approve-builds).\"\n );\n }\n\n const fallbackPort = config.port ?? 443;\n const { host, port } = parseHostPort(config.host, fallbackPort);\n\n const conn: HanaConnection = hana.createConnection();\n\n await new Promise<void>((resolve, reject) => {\n conn.connect(\n {\n serverNode: `${host}:${port}`,\n uid: config.user,\n pwd: config.password,\n encrypt: config.encrypt ?? true,\n sslValidateCertificate: config.sslValidateCertificate ?? true\n },\n (err?: unknown) => (err ? reject(err) : resolve())\n );\n });\n\n return conn;\n}\n\nexport async function hanaExec<T = unknown>(\n conn: HanaConnection,\n sql: string\n): Promise<T> {\n return await new Promise<T>((resolve, reject) => {\n conn.exec(sql, (err: unknown, result: unknown) => {\n if (err) return reject(err);\n resolve(result as T);\n });\n });\n}\n","import type { HanaConnection } from \"./types\";\n\nexport type SparqlExecuteResult = unknown;\n\nexport class HanaSparqlStore {\n private readonly conn: HanaConnection;\n\n constructor(conn: HanaConnection) {\n this.conn = conn;\n }\n\n async execute(options: {\n sparql: string;\n headers?: string;\n defaultGraphUri?: string;\n }): Promise<SparqlExecuteResult> {\n const headerLines: string[] = [];\n\n if (options.defaultGraphUri) {\n headerLines.push(`rqx-default-graph-uri: ${options.defaultGraphUri}`);\n }\n\n if (options.headers) {\n headerLines.push(options.headers.trim());\n }\n\n const hdrs = headerLines.length ? `${headerLines.join(\"\\r\\n\")}\\r\\n` : \"\";\n\n const sql = \"CALL SPARQL_EXECUTE(?, ?, ?, ?)\";\n\n return await new Promise((resolve, reject) => {\n // @sap/hana-client supports conn.exec with parameter arrays.\n // We intentionally keep the result as unknown; callers can parse the XML/JSON depending on HANA return mode.\n (this.conn as any).exec(sql, [options.sparql, hdrs, \"\", null], (err: unknown, result: unknown) => {\n if (err) return reject(err);\n resolve(result);\n });\n });\n }\n\n async loadTurtle(options: {\n turtle: string;\n graphName: string;\n filename?: string;\n }): Promise<SparqlExecuteResult> {\n const requestHdrs = [\n \"rqx-load-protocol: true\",\n options.filename ? `rqx-load-filename: ${options.filename}` : undefined,\n `rqx-load-graphname: ${options.graphName}`\n ]\n .filter(Boolean)\n .join(\"\\r\\n\");\n\n return await this.execute({ sparql: options.turtle, headers: requestHdrs });\n }\n}\n","import type { HanaConnection } from \"./types\";\nimport { hanaExec } from \"./connection\";\n\nexport type HanaKgGraphTableSuffix = \"VECTORS\" | \"NODES\" | \"IMAGES\";\n\nexport type GraphTables = {\n vectorsTable: string;\n nodesTable: string;\n imagesTable: string;\n};\n\nexport type HanaKgGraphInfo = {\n graphName: string;\n hasVectors: boolean;\n hasNodes: boolean;\n hasImages: boolean;\n vectorsCount?: number;\n nodesCount?: number;\n imagesCount?: number;\n};\n\nexport function getGraphTables(graphName: string): GraphTables {\n return {\n vectorsTable: `${graphName}_VECTORS`,\n nodesTable: `${graphName}_NODES`,\n imagesTable: `${graphName}_IMAGES`,\n };\n}\n\nfunction escapeSqlStringLiteral(value: string): string {\n return value.replace(/'/g, \"''\");\n}\n\nfunction normalizeRequirement(req: HanaKgGraphTableSuffix): HanaKgGraphTableSuffix {\n return req.toUpperCase() as HanaKgGraphTableSuffix;\n}\n\nexport async function listGraphs(\n conn: HanaConnection,\n opts?: {\n schema?: string;\n require?: HanaKgGraphTableSuffix[];\n includeCounts?: boolean;\n limit?: number;\n }\n): Promise<HanaKgGraphInfo[]> {\n const schemaFilter = opts?.schema ? ` = '${escapeSqlStringLiteral(opts.schema)}'` : \" = CURRENT_SCHEMA\";\n\n // Discover by our naming convention in a single pass.\n // Note: `_` is a wildcard in LIKE, so we must escape it.\n const rows = (await hanaExec<any[]>(\n conn,\n `SELECT TABLE_NAME\n FROM SYS.TABLES\n WHERE SCHEMA_NAME${schemaFilter}\n AND (\n TABLE_NAME LIKE '%\\\\_VECTORS' ESCAPE '\\\\'\n OR TABLE_NAME LIKE '%\\\\_NODES' ESCAPE '\\\\'\n OR TABLE_NAME LIKE '%\\\\_IMAGES' ESCAPE '\\\\'\n )`\n )) as Array<{ TABLE_NAME?: string; table_name?: string }>;\n\n const tableNames = new Set(\n (rows ?? []).map((r) => String(r.TABLE_NAME ?? r.table_name ?? \"\").toUpperCase()).filter(Boolean)\n );\n\n const stems = new Set<string>();\n for (const t of tableNames) {\n if (t.endsWith(\"_VECTORS\")) {\n stems.add(t.slice(0, -\"_VECTORS\".length));\n }\n }\n\n const infos: HanaKgGraphInfo[] = [];\n for (const stem of stems) {\n const { vectorsTable, nodesTable, imagesTable } = getGraphTables(stem);\n infos.push({\n graphName: stem,\n hasVectors: tableNames.has(vectorsTable),\n hasNodes: tableNames.has(nodesTable),\n hasImages: tableNames.has(imagesTable),\n });\n }\n\n if (opts?.includeCounts) {\n // Only count tables that exist; use quoted identifiers to be robust.\n // NOTE: Table names come from SYS.TABLES results, so this is not user input.\n const schemaName = opts?.schema ? String(opts.schema).toUpperCase() : undefined;\n\n const countTable = async (tableName: string): Promise<number> => {\n const fq = schemaName\n ? `\"${schemaName.replace(/\"/g, '\"\"')}\".\"${tableName.replace(/\"/g, '\"\"')}\"`\n : `\"${tableName.replace(/\"/g, '\"\"')}\"`;\n const res = (await hanaExec<any[]>(conn, `SELECT COUNT(*) AS CNT FROM ${fq}`)) as any[];\n const row = res?.[0];\n const cnt = row?.CNT ?? row?.cnt ?? Object.values(row ?? {})[0];\n return Number(cnt ?? 0);\n };\n\n for (const info of infos) {\n const { vectorsTable, nodesTable, imagesTable } = getGraphTables(info.graphName);\n\n if (info.hasVectors) {\n info.vectorsCount = await countTable(vectorsTable);\n }\n if (info.hasNodes) {\n info.nodesCount = await countTable(nodesTable);\n }\n if (info.hasImages) {\n info.imagesCount = await countTable(imagesTable);\n }\n }\n }\n\n const requireList = (opts?.require ?? []).map(normalizeRequirement);\n const filtered = requireList.length\n ? infos.filter((i) =>\n requireList.every((req) => {\n if (req === \"VECTORS\") return i.hasVectors;\n if (req === \"NODES\") return i.hasNodes;\n if (req === \"IMAGES\") return i.hasImages;\n return false;\n })\n )\n : infos;\n\n filtered.sort((a, b) => a.graphName.localeCompare(b.graphName));\n\n if (opts?.limit !== undefined) {\n return filtered.slice(0, Math.max(0, opts.limit));\n }\n\n return filtered;\n}\n","import { z } from \"zod\";\n\nexport const TRIPLET_SOURCE_KEY = \"triplet_source_id\";\nexport const KG_NODES_KEY = \"kg_nodes\";\nexport const KG_RELATIONS_KEY = \"kg_relations\";\nexport const VECTOR_SOURCE_KEY = \"vector_source_id\";\nexport const KG_SOURCE_REL = \"HAS_SOURCE\";\n\n// Structural adjacency relations (not semantic)\nexport const STRUCT_SAME_PAGE = \"ON_SAME_PAGE\";\nexport const STRUCT_ADJACENT = \"ADJACENT_TO\";\nexport const STRUCT_CONTAINS = \"CONTAINS\";\n\nexport const EntityNodeSchema = z.object({\n id: z.string(),\n label: z.string(),\n name: z.string(),\n properties: z.record(z.unknown()).default({}),\n embedding: z.array(z.number()).optional(),\n});\n\nexport type EntityNode = z.infer<typeof EntityNodeSchema>;\n\nexport const RelationSchema = z.object({\n id: z.string().optional(),\n label: z.string(),\n sourceId: z.string(),\n targetId: z.string(),\n properties: z.record(z.unknown()).default({}),\n});\n\nexport type Relation = z.infer<typeof RelationSchema>;\n\nexport type Triplet = [EntityNode, Relation, EntityNode];\n\nexport type LabelledNode = EntityNode;\n\nexport interface NodeWithScore {\n node: {\n id: string;\n text: string;\n metadata: Record<string, unknown>;\n refDocId?: string;\n };\n score: number;\n}\n\nexport interface QueryBundle {\n queryStr: string;\n embedding?: number[];\n embeddingStrs?: string[];\n}\n\nexport function createEntityNode(opts: {\n label: string;\n name: string;\n properties?: Record<string, unknown>;\n embedding?: number[];\n}): EntityNode {\n const id = `${opts.label.toUpperCase()}_${opts.name.replace(/\\s+/g, \"_\").toUpperCase()}`;\n return {\n id,\n label: opts.label,\n name: opts.name,\n properties: opts.properties ?? {},\n embedding: opts.embedding,\n };\n}\n\nexport function createRelation(opts: {\n label: string;\n sourceId: string;\n targetId: string;\n properties?: Record<string, unknown>;\n}): Relation {\n return {\n id: `${opts.sourceId}_${opts.label}_${opts.targetId}`,\n label: opts.label,\n sourceId: opts.sourceId,\n targetId: opts.targetId,\n properties: opts.properties ?? {},\n };\n}\n\nexport function tripletToString(triplet: Triplet, includeProperties = false): string {\n const [subj, rel, obj] = triplet;\n if (includeProperties) {\n return `${subj.name} (${subj.label}) -[${rel.label}]-> ${obj.name} (${obj.label})`;\n }\n return `${subj.id} -> ${rel.label} -> ${obj.id}`;\n}\n","import type { HanaConnection } from \"../hana/types\";\nimport type { PropertyGraphStore, VectorStoreQuery } from \"./property-graph-store\";\nimport type { EntityNode, Relation, Triplet, LabelledNode } from \"./types\";\nimport { TRIPLET_SOURCE_KEY, KG_SOURCE_REL } from \"./types\";\n\ntype DocumentNode = {\n id: string;\n text: string;\n metadata: Record<string, unknown>;\n embedding?: number[];\n hash?: string;\n};\n\nexport interface HanaPropertyGraphStoreOptions {\n graphName: string;\n vectorTableName?: string;\n documentNodesTableName?: string;\n /** Optional, unused for dimensionless REAL_VECTOR columns (kept for backward compat logging). */\n vectorDimension?: number;\n /** If true, will DROP and recreate tables on init (intended for tests/dev only). */\n resetTables?: boolean;\n}\n\nexport class HanaPropertyGraphStore implements PropertyGraphStore {\n private readonly conn: HanaConnection;\n private readonly graphName: string;\n private readonly vectorTableName: string;\n private readonly documentNodesTableName: string;\n private readonly resetTables: boolean;\n private initialized = false;\n\n readonly supportsVectorQueries = true;\n readonly supportsStructuredQueries = true;\n\n constructor(conn: HanaConnection, options: HanaPropertyGraphStoreOptions) {\n this.conn = conn;\n this.graphName = options.graphName;\n this.vectorTableName = options.vectorTableName ?? `${options.graphName.replace(/[^a-zA-Z0-9]/g, \"_\")}_VECTORS`;\n this.documentNodesTableName = options.documentNodesTableName ?? `${options.graphName.replace(/[^a-zA-Z0-9]/g, \"_\")}_NODES`;\n this.resetTables = options.resetTables ?? false;\n }\n\n private async ensureInitialized(): Promise<void> {\n if (this.initialized) return;\n\n // Optional destructive reset intended for tests/dev. This is OFF by default.\n if (this.resetTables) {\n await this.exec(`DROP TABLE ${this.vectorTableName}`).catch(() => {});\n await this.exec(`DROP TABLE ${this.documentNodesTableName}`).catch(() => {});\n\n // Also clear the RDF named graph to avoid accumulating triples across test runs.\n // (Tables are recreated, but the KG graph would otherwise persist.)\n await this.sparqlExecute(`CLEAR GRAPH <${this.graphName}>`).catch(() => {});\n }\n\n // HANA does not support \"IF NOT EXISTS\" for CREATE TABLE. We try to create and ignore the\n // \"name exists\" error, but surface any other errors.\n const createVectorTable = `\n CREATE COLUMN TABLE ${this.vectorTableName} (\n id NVARCHAR(512) PRIMARY KEY,\n node_type NVARCHAR(64),\n label NVARCHAR(128),\n name NVARCHAR(512),\n properties NCLOB,\n embedding REAL_VECTOR\n )\n `;\n await this.exec(createVectorTable).catch((err: any) => {\n const message = String(err?.message ?? \"\");\n // Handle \"duplicate table name\" or \"already exists\"\n if (!/exists|duplicate table name/i.test(message)) {\n throw err;\n }\n });\n\n const createDocumentTable = `\n CREATE COLUMN TABLE ${this.documentNodesTableName} (\n id NVARCHAR(512) PRIMARY KEY,\n text NCLOB,\n metadata NCLOB,\n hash NVARCHAR(64),\n embedding REAL_VECTOR\n )\n `;\n await this.exec(createDocumentTable).catch((err: any) => {\n const message = String(err?.message ?? \"\");\n if (!/exists|duplicate table name/i.test(message)) {\n throw err;\n }\n });\n\n this.initialized = true;\n }\n\n private async exec(sql: string, params?: unknown[]): Promise<unknown[]> {\n return new Promise((resolve, reject) => {\n if (params && params.length > 0) {\n (this.conn as any).exec(sql, params, (err: unknown, result: unknown) => {\n if (err) reject(err);\n else resolve(result as unknown[]);\n });\n } else {\n this.conn.exec(sql, (err: unknown, result: unknown) => {\n if (err) reject(err);\n else resolve(result as unknown[]);\n });\n }\n });\n }\n\n private async sparqlExecute(sparql: string, headers = \"\"): Promise<unknown> {\n // Some HANA environments require OUT parameters (e.g. RESPONSE) to be bound.\n // The @sap/hana-client driver provides a proc statement helper which correctly\n // handles OUT params *without* requiring you to pass them in the params array.\n try {\n const streamMod = await import(\"@sap/hana-client/extension/Stream\");\n const Stream = (streamMod as any).default ?? streamMod;\n\n return await new Promise<unknown>((resolve, reject) => {\n Stream.createProcStatement(this.conn as any, \"CALL SPARQL_EXECUTE(?, ?, ?, ?)\", (err: any, stmt: any) => {\n if (err) return reject(err);\n stmt.exec([sparql, headers], (err2: any, scalarParams: any) => {\n // scalarParams contains OUT params by name (e.g. RESPONSE)\n if (err2) return reject(err2);\n resolve(scalarParams);\n });\n });\n });\n } catch {\n // Fallback (may fail on systems requiring bound OUT params)\n return await new Promise<unknown>((resolve, reject) => {\n (this.conn as any).exec(\n \"CALL SPARQL_EXECUTE(?, ?, ?, ?)\",\n // Important: only pass IN params; OUT params are placeholders in SQL.\n [sparql, headers],\n (err: unknown, result: unknown) => {\n if (err) reject(err);\n else resolve(result);\n }\n );\n });\n }\n }\n\n private entityToUri(node: EntityNode): string {\n return `<urn:hkv:${this.graphName}:${node.label}:${encodeURIComponent(node.id)}>`;\n }\n\n private relationToTriples(rel: Relation, subj: EntityNode, obj: EntityNode): string {\n const subjUri = this.entityToUri(subj);\n const objUri = this.entityToUri(obj);\n const predUri = `<urn:hkv:rel:${rel.label}>`;\n return `${subjUri} ${predUri} ${objUri} .`;\n }\n\n private entityToTriples(node: EntityNode): string {\n const uri = this.entityToUri(node);\n const lines: string[] = [];\n lines.push(`${uri} a <urn:hkv:type:${node.label}> .`);\n lines.push(`${uri} <urn:hkv:prop:name> \"${this.escapeLiteral(node.name)}\" .`);\n\n for (const [key, value] of Object.entries(node.properties)) {\n if (value !== undefined && value !== null) {\n const strVal = typeof value === \"string\" ? value : String(value);\n // Skip overly large properties in RDF — they are already stored in the vectors table\n if (strVal.length > 5000) continue;\n const escaped = this.escapeLiteral(strVal);\n lines.push(`${uri} <urn:hkv:prop:${key}> \"${escaped}\" .`);\n }\n }\n\n return lines.join(\"\\n\");\n }\n\n private escapeLiteral(s: string): string {\n return s\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, \"\\\\n\")\n .replace(/\\r/g, \"\\\\r\")\n .replace(/\\t/g, \"\\\\t\");\n }\n\n async upsertNodes(nodes: EntityNode[]): Promise<void> {\n await this.ensureInitialized();\n if (nodes.length === 0) return;\n\n const triples = nodes.map((n) => this.entityToTriples(n)).join(\"\\n\");\n const sparql = `INSERT DATA { GRAPH <${this.graphName}> { ${triples} } }`;\n await this.sparqlExecute(sparql);\n\n for (const node of nodes) {\n // Defensive: prevent circular references from being serialized into NCLOB\n const safeProps: Record<string, unknown> = { ...(node.properties ?? {}) };\n delete (safeProps as any).kg_nodes;\n delete (safeProps as any).kg_relations;\n\n const sql = node.embedding\n ? `\n UPSERT ${this.vectorTableName} (id, node_type, label, name, properties, embedding)\n VALUES (?, 'entity', ?, ?, ?, TO_REAL_VECTOR(?))\n WITH PRIMARY KEY\n `\n : `\n UPSERT ${this.vectorTableName} (id, node_type, label, name, properties)\n VALUES (?, 'entity', ?, ?, ?)\n WITH PRIMARY KEY\n `;\n const params: unknown[] = [\n node.id,\n node.label,\n node.name,\n JSON.stringify(safeProps),\n ];\n if (node.embedding) {\n params.push(JSON.stringify(node.embedding));\n }\n await this.exec(sql, params);\n }\n }\n\n async upsertRelations(relations: Relation[]): Promise<void> {\n await this.ensureInitialized();\n if (relations.length === 0) return;\n\n const nodeIds = new Set<string>();\n for (const rel of relations) {\n nodeIds.add(rel.sourceId);\n nodeIds.add(rel.targetId);\n }\n\n const existingNodes = await this.get({ ids: Array.from(nodeIds) });\n const nodeMap = new Map(existingNodes.map((n) => [n.id, n]));\n\n const lines: string[] = [];\n for (const rel of relations) {\n const subj = nodeMap.get(rel.sourceId);\n const obj = nodeMap.get(rel.targetId);\n if (subj && obj) {\n lines.push(this.relationToTriples(rel, subj, obj));\n }\n }\n\n if (lines.length > 0) {\n const sparql = `INSERT DATA { GRAPH <${this.graphName}> { ${lines.join(\"\\n\")} } }`;\n await this.sparqlExecute(sparql);\n }\n }\n\n async get(opts: { ids: string[] }): Promise<LabelledNode[]> {\n await this.ensureInitialized();\n if (opts.ids.length === 0) return [];\n\n const rows = (await this.exec(\n `SELECT id, label, name, properties FROM ${this.vectorTableName} WHERE id IN (${opts.ids.map(() => \"?\").join(\",\")})`,\n opts.ids\n )) as any[];\n\n return (rows ?? []).map((r) => ({\n id: r.ID ?? r.id,\n label: r.LABEL ?? r.label,\n name: r.NAME ?? r.name,\n properties: JSON.parse(r.PROPERTIES ?? r.properties ?? \"{}\"),\n }));\n }\n\n async getRelMap(opts: {\n nodes: LabelledNode[];\n depth?: number;\n limit?: number;\n ignoreRels?: string[];\n }): Promise<Triplet[]> {\n await this.ensureInitialized();\n if (opts.nodes.length === 0) return [];\n\n const nodeIds = opts.nodes.map((n) => n.id);\n const depth = opts.depth ?? 1;\n const limit = opts.limit ?? 100;\n const ignoreRels = opts.ignoreRels ?? [KG_SOURCE_REL];\n\n const filterClause = nodeIds\n .map((id) => `CONTAINS(STR(?s), \"${encodeURIComponent(id)}\") || CONTAINS(STR(?o), \"${encodeURIComponent(id)}\")`)\n .join(\" || \");\n\n const ignoreFilter = ignoreRels.length > 0\n ? `FILTER(${ignoreRels.map((r) => `!CONTAINS(STR(?p), \"${r}\")`).join(\" && \")})`\n : \"\";\n\n const sparql = `\n SELECT ?s ?p ?o\n FROM <${this.graphName}>\n WHERE {\n ?s ?p ?o .\n FILTER(${filterClause})\n ${ignoreFilter}\n }\n LIMIT ${limit}\n `;\n\n const result = await this.exec(\n `SELECT * FROM SPARQL_TABLE('${sparql.replace(/'/g, \"''\")}')`\n ) as any[];\n\n const triplets: Triplet[] = [];\n const nodeMap = new Map(opts.nodes.map((n) => [n.id, n]));\n\n for (const row of result ?? []) {\n const sUri = row.S ?? row.s;\n const pUri = row.P ?? row.p;\n const oUri = row.O ?? row.o;\n\n const sId = this.extractIdFromUri(sUri);\n const oId = this.extractIdFromUri(oUri);\n const relLabel = this.extractRelLabelFromUri(pUri);\n\n let subj = nodeMap.get(sId);\n let obj = nodeMap.get(oId);\n\n if (!subj) {\n subj = { id: sId, label: \"UNKNOWN\", name: sId, properties: {} };\n }\n if (!obj) {\n obj = { id: oId, label: \"UNKNOWN\", name: oId, properties: {} };\n }\n\n const rel: Relation = {\n label: relLabel,\n sourceId: sId,\n targetId: oId,\n properties: {},\n };\n\n triplets.push([subj, rel, obj]);\n }\n\n return triplets;\n }\n\n private extractIdFromUri(uri: string): string {\n // URI format: <urn:hkv:graphName:label:id> or just the content without angle brackets\n // The ID is the last segment after the label (second-to-last segment)\n const cleanUri = uri.replace(/^<|>$/g, \"\");\n const parts = cleanUri.split(\":\");\n // ID is the last part, decode it\n if (parts.length >= 2) {\n return decodeURIComponent(parts[parts.length - 1]);\n }\n return uri;\n }\n\n private extractRelLabelFromUri(uri: string): string {\n const match = uri.match(/urn:hkv:rel:(.+)>?$/);\n return match ? match[1].replace(/>$/, \"\") : uri;\n }\n\n async delete(opts: { ids: string[] }): Promise<void> {\n await this.ensureInitialized();\n if (opts.ids.length === 0) return;\n\n for (const id of opts.ids) {\n await this.exec(`DELETE FROM ${this.vectorTableName} WHERE id = ?`, [id]);\n }\n }\n\n async vectorQuery(query: VectorStoreQuery): Promise<[LabelledNode[], number[]]> {\n await this.ensureInitialized();\n\n const sql = `\n SELECT id, label, name, properties,\n COSINE_SIMILARITY(embedding, TO_REAL_VECTOR(?)) AS score\n FROM ${this.vectorTableName}\n WHERE embedding IS NOT NULL\n ORDER BY score DESC\n LIMIT ?\n `;\n\n const rows = (await this.exec(sql, [\n JSON.stringify(query.queryEmbedding),\n query.similarityTopK,\n ])) as any[];\n\n const nodes: LabelledNode[] = [];\n const scores: number[] = [];\n\n for (const row of rows ?? []) {\n nodes.push({\n id: row.ID ?? row.id,\n label: row.LABEL ?? row.label,\n name: row.NAME ?? row.name,\n properties: JSON.parse(row.PROPERTIES ?? row.properties ?? \"{}\"),\n });\n scores.push(row.SCORE ?? row.score ?? 0);\n }\n\n return [nodes, scores];\n }\n\n async upsertDocumentNodes(nodes: DocumentNode[]): Promise<void> {\n await this.ensureInitialized();\n for (const node of nodes) {\n // Remove circular references from metadata before serialization\n const { metadata, ...rest } = node;\n const safeMetadata = { ...metadata };\n delete (safeMetadata as any).kg_nodes;\n delete (safeMetadata as any).kg_relations;\n\n // Write to _NODES table (text + metadata storage)\n const docSql = `\n UPSERT ${this.documentNodesTableName} (id, text, metadata, hash, embedding)\n VALUES (?, ?, ?, ?, ${node.embedding ? \"TO_REAL_VECTOR(?)\" : \"NULL\"})\n WITH PRIMARY KEY\n `;\n const docParams: unknown[] = [\n node.id,\n node.text,\n JSON.stringify(safeMetadata ?? {}),\n node.hash ?? null,\n ];\n if (node.embedding) {\n docParams.push(JSON.stringify(node.embedding));\n }\n await this.exec(docSql, docParams);\n\n // Also write to _VECTORS table so vectorQuery() can find document nodes.\n // NODE_TYPE='document' distinguishes them from entity nodes.\n const vecSql = node.embedding\n ? `\n UPSERT ${this.vectorTableName} (id, node_type, label, name, properties, embedding)\n VALUES (?, 'document', 'DOCUMENT', ?, ?, TO_REAL_VECTOR(?))\n WITH PRIMARY KEY\n `\n : `\n UPSERT ${this.vectorTableName} (id, node_type, label, name, properties)\n VALUES (?, 'document', 'DOCUMENT', ?, ?)\n WITH PRIMARY KEY\n `;\n const vecParams: unknown[] = [node.id, node.id, JSON.stringify(safeMetadata ?? {})];\n if (node.embedding) {\n vecParams.push(JSON.stringify(node.embedding));\n }\n await this.exec(vecSql, vecParams);\n }\n }\n\n async getDocumentNodes(ids: string[]): Promise<DocumentNode[]> {\n await this.ensureInitialized();\n if (ids.length === 0) return [];\n\n const rows = (await this.exec(\n `SELECT id, text, metadata, hash FROM ${this.documentNodesTableName} WHERE id IN (${ids.map(() => \"?\").join(\",\")})`,\n ids\n )) as any[];\n\n return (rows ?? []).map((r) => ({\n id: r.ID ?? r.id,\n text: r.TEXT ?? r.text,\n metadata: JSON.parse(r.METADATA ?? r.metadata ?? \"{}\"),\n hash: r.HASH ?? r.hash,\n }));\n }\n}\n","import type { QueryBundle, NodeWithScore } from \"./types\";\nimport type { BasePGRetriever } from \"./retrievers/base\";\n\nexport interface PGRetrieverOptions {\n subRetrievers: BasePGRetriever[];\n showProgress?: boolean;\n}\n\nexport class PGRetriever {\n private readonly subRetrievers: BasePGRetriever[];\n private readonly showProgress: boolean;\n\n constructor(options: PGRetrieverOptions) {\n this.subRetrievers = options.subRetrievers;\n this.showProgress = options.showProgress ?? false;\n }\n\n private deduplicate(nodes: NodeWithScore[]): NodeWithScore[] {\n const seen = new Set<string>();\n const deduped: NodeWithScore[] = [];\n\n for (const node of nodes) {\n if (!seen.has(node.node.text)) {\n deduped.push(node);\n seen.add(node.node.text);\n }\n }\n\n return deduped;\n }\n\n async retrieve(queryBundle: QueryBundle): Promise<NodeWithScore[]> {\n const allResults: NodeWithScore[] = [];\n\n const promises = this.subRetrievers.map((r) => r.retrieve(queryBundle));\n const results = await Promise.all(promises);\n\n for (const result of results) {\n allResults.push(...result);\n }\n\n return this.deduplicate(allResults);\n }\n}\n","import type { PropertyGraphStore } from \"../property-graph-store\";\nimport type { Triplet, NodeWithScore, QueryBundle, LabelledNode } from \"../types\";\nimport { TRIPLET_SOURCE_KEY, tripletToString } from \"../types\";\n\nexport const DEFAULT_PREAMBLE = \"Here are some facts extracted from the provided text:\\n\\n\";\n\nexport interface BasePGRetrieverOptions {\n graphStore: PropertyGraphStore;\n includeText?: boolean;\n includeTextPreamble?: string;\n includeProperties?: boolean;\n}\n\nexport abstract class BasePGRetriever {\n protected readonly graphStore: PropertyGraphStore;\n protected readonly includeText: boolean;\n protected readonly includeTextPreamble: string;\n protected readonly includeProperties: boolean;\n\n constructor(options: BasePGRetrieverOptions) {\n this.graphStore = options.graphStore;\n this.includeText = options.includeText ?? true;\n this.includeTextPreamble = options.includeTextPreamble ?? DEFAULT_PREAMBLE;\n this.includeProperties = options.includeProperties ?? false;\n }\n\n protected getNodesWithScore(triplets: Triplet[], scores?: number[]): NodeWithScore[] {\n const results: NodeWithScore[] = [];\n\n for (let i = 0; i < triplets.length; i++) {\n const triplet = triplets[i];\n const sourceId = triplet[0].properties[TRIPLET_SOURCE_KEY] as string | undefined;\n\n const text = tripletToString(triplet, this.includeProperties);\n\n results.push({\n node: {\n id: `triplet_${i}`,\n text,\n metadata: {},\n refDocId: sourceId,\n },\n score: scores?.[i] ?? 1.0,\n });\n }\n\n return results;\n }\n\n protected async addSourceText(nodes: NodeWithScore[]): Promise<NodeWithScore[]> {\n if (!this.graphStore.getDocumentNodes) {\n return nodes;\n }\n\n const refDocIds = nodes\n .map((n) => n.node.refDocId)\n .filter((id): id is string => id !== undefined);\n\n if (refDocIds.length === 0) return nodes;\n\n const ogNodes = await this.graphStore.getDocumentNodes(refDocIds);\n const nodeMap = new Map(ogNodes.map((n) => [n.id, n]));\n\n const graphNodeMap = new Map<string, string[]>();\n for (const node of nodes) {\n const refDocId = node.node.refDocId ?? \"\";\n if (!graphNodeMap.has(refDocId)) {\n graphNodeMap.set(refDocId, []);\n }\n graphNodeMap.get(refDocId)!.push(node.node.text);\n }\n\n const resultNodes: NodeWithScore[] = [];\n for (const nodeWithScore of nodes) {\n const mappedNode = nodeMap.get(nodeWithScore.node.refDocId ?? \"\");\n\n if (mappedNode) {\n const graphContent = graphNodeMap.get(mappedNode.id) ?? [];\n if (graphContent.length > 0) {\n const graphContentStr = graphContent.join(\"\\n\");\n const newContent = this.includeTextPreamble + graphContentStr + \"\\n\\n\" + mappedNode.text;\n resultNodes.push({\n node: {\n id: mappedNode.id,\n text: newContent,\n metadata: mappedNode.metadata,\n refDocId: nodeWithScore.node.refDocId,\n },\n score: nodeWithScore.score,\n });\n } else {\n resultNodes.push({\n node: {\n id: mappedNode.id,\n text: mappedNode.text,\n metadata: mappedNode.metadata,\n refDocId: nodeWithScore.node.refDocId,\n },\n score: nodeWithScore.score,\n });\n }\n } else {\n resultNodes.push(nodeWithScore);\n }\n }\n\n return resultNodes;\n }\n\n async retrieve(queryBundle: QueryBundle): Promise<NodeWithScore[]> {\n let nodes = await this.retrieveFromGraph(queryBundle);\n if (this.includeText && nodes.length > 0) {\n nodes = await this.addSourceText(nodes);\n }\n return nodes;\n }\n\n abstract retrieveFromGraph(queryBundle: QueryBundle): Promise<NodeWithScore[]>;\n}\n","import type { PropertyGraphStore, VectorStoreQuery } from \"../property-graph-store\";\nimport type { QueryBundle, NodeWithScore, LabelledNode } from \"../types\";\nimport { KG_SOURCE_REL, TRIPLET_SOURCE_KEY, STRUCT_SAME_PAGE, STRUCT_ADJACENT, STRUCT_CONTAINS } from \"../types\";\nimport { BasePGRetriever, type BasePGRetrieverOptions } from \"./base\";\n\nexport interface EmbedModel {\n getTextEmbedding(text: string): Promise<number[]>;\n getTextEmbeddingBatch(texts: string[]): Promise<number[][]>;\n}\n\nexport interface VectorContextRetrieverOptions extends BasePGRetrieverOptions {\n embedModel: EmbedModel;\n similarityTopK?: number;\n pathDepth?: number;\n limit?: number;\n similarityScore?: number;\n /** Enable cross-check boosting: boost scores when vector-matched entities link to graph facts (default: true) */\n crossCheckBoost?: boolean;\n /** Multiplier for cross-check boost (default: 1.25 = 25% boost) */\n crossCheckBoostFactor?: number;\n /** Include structural adjacency relations (ON_SAME_PAGE, ADJACENT_TO) in graph expansion (default: true) */\n includeStructuralEdges?: boolean;\n /** Depth for structural edge traversal (default: 1) */\n structuralDepth?: number;\n}\n\nexport class VectorContextRetriever extends BasePGRetriever {\n private readonly embedModel: EmbedModel;\n private readonly similarityTopK: number;\n private readonly pathDepth: number;\n private readonly limit: number;\n private readonly similarityScore?: number;\n private readonly crossCheckBoost: boolean;\n private readonly crossCheckBoostFactor: number;\n private readonly includeStructuralEdges: boolean;\n private readonly structuralDepth: number;\n\n constructor(options: VectorContextRetrieverOptions) {\n super(options);\n this.embedModel = options.embedModel;\n this.similarityTopK = options.similarityTopK ?? 4;\n this.pathDepth = options.pathDepth ?? 1;\n this.limit = options.limit ?? 30;\n this.similarityScore = options.similarityScore;\n this.crossCheckBoost = options.crossCheckBoost ?? true;\n this.crossCheckBoostFactor = options.crossCheckBoostFactor ?? 1.25;\n this.includeStructuralEdges = options.includeStructuralEdges ?? true;\n this.structuralDepth = options.structuralDepth ?? 1;\n }\n\n async retrieveFromGraph(queryBundle: QueryBundle): Promise<NodeWithScore[]> {\n let embedding = queryBundle.embedding;\n\n if (!embedding) {\n embedding = await this.embedModel.getTextEmbedding(queryBundle.queryStr);\n }\n\n const vectorQuery: VectorStoreQuery = {\n queryEmbedding: embedding,\n similarityTopK: this.similarityTopK,\n };\n\n if (!this.graphStore.supportsVectorQueries || !this.graphStore.vectorQuery) {\n throw new Error(\"Graph store does not support vector queries\");\n }\n\n const [kgNodes, scores] = await this.graphStore.vectorQuery(vectorQuery);\n\n if (kgNodes.length === 0) {\n return [];\n }\n\n const kgIds = kgNodes.map((n) => n.id);\n\n // Build provenance set from vector-matched nodes for cross-check boosting\n // This includes document IDs, chunk IDs, and other source identifiers\n const provenanceSet = new Set<string>();\n if (this.crossCheckBoost) {\n for (const node of kgNodes) {\n // Add node ID itself (for CHUNK nodes)\n provenanceSet.add(node.id.toLowerCase());\n provenanceSet.add(node.name.toLowerCase());\n \n // Add documentId from properties if present\n const props = node.properties ?? {};\n if (props.documentId) {\n provenanceSet.add(String(props.documentId).toLowerCase());\n }\n if (props.sourceChunk) {\n provenanceSet.add(String(props.sourceChunk).toLowerCase());\n }\n }\n }\n\n let triplets = await this.graphStore.getRelMap({\n nodes: kgNodes,\n depth: this.pathDepth,\n limit: this.limit,\n ignoreRels: [KG_SOURCE_REL],\n });\n\n // Structural expansion: traverse ON_SAME_PAGE, ADJACENT_TO edges from source chunks\n if (this.includeStructuralEdges) {\n // Get CHUNK node IDs linked to matched entities via TRIPLET_SOURCE_KEY\n const chunkIds = new Set<string>();\n for (const node of kgNodes) {\n const sourceId = node.properties?.[TRIPLET_SOURCE_KEY];\n if (sourceId) {\n chunkIds.add(`CHUNK_${String(sourceId).replace(/\\s+/g, \"_\").toUpperCase()}`);\n }\n // Also include CHUNK nodes that were directly matched\n if (node.label === \"CHUNK\") {\n chunkIds.add(node.id);\n }\n }\n\n if (chunkIds.size > 0) {\n const chunkNodes = await this.graphStore.get({ ids: Array.from(chunkIds) });\n if (chunkNodes.length > 0) {\n const structuralTriplets = await this.graphStore.getRelMap({\n nodes: chunkNodes,\n depth: this.structuralDepth,\n limit: this.limit,\n ignoreRels: [KG_SOURCE_REL],\n });\n\n // Filter to only structural relations\n const structuralRels = [STRUCT_SAME_PAGE, STRUCT_ADJACENT, STRUCT_CONTAINS];\n const structuralOnly = structuralTriplets.filter(([_s, r, _o]) =>\n structuralRels.includes(r.label)\n );\n\n // Add structural triplets to results (avoid duplicates)\n const existingKeys = new Set(triplets.map(([s, r, o]) => `${s.id}|${r.label}|${o.id}`));\n for (const triplet of structuralOnly) {\n const key = `${triplet[0].id}|${triplet[1].label}|${triplet[2].id}`;\n if (!existingKeys.has(key)) {\n triplets.push(triplet);\n existingKeys.add(key);\n }\n }\n }\n }\n }\n\n const newScores: number[] = [];\n for (const triplet of triplets) {\n const idx1 = kgIds.indexOf(triplet[0].id);\n const idx2 = kgIds.indexOf(triplet[2].id);\n const score1 = idx1 >= 0 ? scores[idx1] : 0;\n const score2 = idx2 >= 0 ? scores[idx2] : 0;\n let baseScore = Math.max(score1, score2);\n\n // Cross-check boosting: if triplet entities have provenance linking back to\n // vector-matched nodes, boost the score. This rewards facts that are both\n // semantically similar AND have explicit graph connections.\n if (this.crossCheckBoost && baseScore > 0) {\n const shouldBoost = this.checkProvenance(triplet[0], provenanceSet) ||\n this.checkProvenance(triplet[2], provenanceSet);\n if (shouldBoost) {\n baseScore = Math.min(1.0, baseScore * this.crossCheckBoostFactor);\n }\n }\n\n newScores.push(baseScore);\n }\n\n let results = triplets.map((t, i) => ({ triplet: t, score: newScores[i] }));\n\n if (this.similarityScore !== undefined) {\n results = results.filter((r) => r.score >= this.similarityScore!);\n }\n\n results.sort((a, b) => b.score - a.score);\n\n return this.getNodesWithScore(\n results.map((r) => r.triplet),\n results.map((r) => r.score)\n );\n }\n\n /**\n * Check if a node has provenance linking to the vector-matched nodes.\n * Returns true if the node's properties contain a documentId or sourceChunk\n * that matches something in the provenance set.\n */\n private checkProvenance(node: LabelledNode, provenanceSet: Set<string>): boolean {\n if (provenanceSet.size === 0) return false;\n\n const props = node.properties ?? {};\n \n // Check documentId property\n if (props.documentId && provenanceSet.has(String(props.documentId).toLowerCase())) {\n return true;\n }\n \n // Check sourceChunk property\n if (props.sourceChunk && provenanceSet.has(String(props.sourceChunk).toLowerCase())) {\n return true;\n }\n \n // Check if this node itself is in the provenance set (e.g., a CHUNK node)\n if (provenanceSet.has(node.id.toLowerCase()) || provenanceSet.has(node.name.toLowerCase())) {\n return true;\n }\n\n return false;\n }\n}\n","import type { TransformComponent, TextNode } from \"./types\";\nimport type { EntityNode, Relation } from \"../types\";\nimport { KG_NODES_KEY, KG_RELATIONS_KEY, createEntityNode, createRelation } from \"../types\";\n\nexport class ImplicitPathExtractor implements TransformComponent {\n async transform(nodes: TextNode[], options?: { showProgress?: boolean }): Promise<TextNode[]> {\n const results: TextNode[] = [];\n\n for (const node of nodes) {\n const existingNodes = (node.metadata[KG_NODES_KEY] as EntityNode[]) ?? [];\n const existingRelations = (node.metadata[KG_RELATIONS_KEY] as Relation[]) ?? [];\n\n const safeMetadata: Record<string, unknown> = { ...node.metadata };\n delete safeMetadata[KG_NODES_KEY];\n delete safeMetadata[KG_RELATIONS_KEY];\n\n const chunkNode = createEntityNode({\n label: \"CHUNK\",\n name: node.id,\n properties: {\n text: node.text.slice(0, 500),\n ...safeMetadata,\n },\n });\n\n if (node.metadata.documentId) {\n const docNode = createEntityNode({\n label: \"DOCUMENT\",\n name: String(node.metadata.documentId),\n properties: {},\n });\n\n const rel = createRelation({\n label: \"FROM_DOCUMENT\",\n sourceId: chunkNode.id,\n targetId: docNode.id,\n });\n\n existingNodes.push(docNode);\n existingRelations.push(rel);\n }\n\n existingNodes.push(chunkNode);\n\n results.push({\n ...node,\n metadata: {\n ...node.metadata,\n [KG_NODES_KEY]: existingNodes,\n [KG_RELATIONS_KEY]: existingRelations,\n },\n });\n }\n\n return results;\n }\n}\n","import type { PropertyGraphStore } from \"./property-graph-store\";\nimport type { TransformComponent, TextNode } from \"./extractors/types\";\nimport type { EntityNode, Relation, NodeWithScore, QueryBundle } from \"./types\";\nimport { KG_NODES_KEY, KG_RELATIONS_KEY, TRIPLET_SOURCE_KEY } from \"./types\";\nimport { PGRetriever } from \"./pg-retriever\";\nimport { VectorContextRetriever, type EmbedModel } from \"./retrievers/vector-context\";\nimport { ImplicitPathExtractor } from \"./extractors/implicit\";\nimport { createHash } from \"crypto\";\nimport { get_encoding } from \"tiktoken\";\n\n/** Default token limit with safety buffer for text-embedding-3-small (8 192 token context). */\nconst DEFAULT_MAX_EMBED_TOKENS = 8_000;\n\nexport interface PropertyGraphIndexOptions {\n propertyGraphStore: PropertyGraphStore;\n kgExtractors?: TransformComponent[];\n embedModel?: EmbedModel;\n embedKgNodes?: boolean;\n showProgress?: boolean;\n /** Maximum tokens to send to the embedding model per text.\n * Texts exceeding this limit are truncated using tiktoken (cl100k_base encoding).\n * Default: 8 000 (safe for 8 192-token models like text-embedding-3-small). */\n maxEmbedTokens?: number;\n}\n\nexport class PropertyGraphIndex {\n private readonly propertyGraphStore: PropertyGraphStore;\n private readonly kgExtractors: TransformComponent[];\n private readonly embedModel?: EmbedModel;\n private readonly embedKgNodes: boolean;\n private readonly showProgress: boolean;\n private readonly maxEmbedTokens: number;\n\n constructor(options: PropertyGraphIndexOptions) {\n this.propertyGraphStore = options.propertyGraphStore;\n this.kgExtractors = options.kgExtractors ?? [new ImplicitPathExtractor()];\n this.embedModel = options.embedModel;\n this.embedKgNodes = options.embedKgNodes ?? true;\n this.showProgress = options.showProgress ?? false;\n this.maxEmbedTokens = options.maxEmbedTokens ?? DEFAULT_MAX_EMBED_TOKENS;\n }\n\n get graphStore(): PropertyGraphStore {\n return this.propertyGraphStore;\n }\n\n static fromExisting(options: PropertyGraphIndexOptions): PropertyGraphIndex {\n return new PropertyGraphIndex(options);\n }\n\n private computeHash(text: string): string {\n return createHash(\"md5\").update(text).digest(\"hex\");\n }\n\n private truncateForEmbedding(text: string): string {\n const enc = get_encoding(\"cl100k_base\");\n try {\n const tokens = enc.encode(text);\n if (tokens.length <= this.maxEmbedTokens) return text;\n const truncated = tokens.slice(0, this.maxEmbedTokens);\n return new TextDecoder().decode(enc.decode(truncated));\n } finally {\n enc.free();\n }\n }\n\n async insert(nodes: TextNode[]): Promise<TextNode[]> {\n if (nodes.length === 0) return [];\n\n let processedNodes = nodes;\n for (const extractor of this.kgExtractors) {\n processedNodes = await extractor.transform(processedNodes, {\n showProgress: this.showProgress,\n });\n }\n\n const hasExtractors = this.kgExtractors.length > 0;\n\n const kgNodesToInsert: EntityNode[] = [];\n const kgRelsToInsert: Relation[] = [];\n let newKgNodes: EntityNode[] = [];\n\n if (hasExtractors) {\n for (const node of processedNodes) {\n if (!node.metadata[KG_NODES_KEY] && !node.metadata[KG_RELATIONS_KEY]) {\n throw new Error(`Node ${node.id} has no KG_NODES_KEY or KG_RELATIONS_KEY after extraction`);\n }\n }\n\n for (const node of processedNodes) {\n const kgNodes = (node.metadata[KG_NODES_KEY] as EntityNode[]) ?? [];\n const kgRels = (node.metadata[KG_RELATIONS_KEY] as Relation[]) ?? [];\n\n for (const kgNode of kgNodes) {\n kgNode.properties[TRIPLET_SOURCE_KEY] = node.id;\n }\n for (const kgRel of kgRels) {\n kgRel.properties[TRIPLET_SOURCE_KEY] = node.id;\n }\n\n kgNodesToInsert.push(...kgNodes);\n kgRelsToInsert.push(...kgRels);\n }\n\n const kgNodeIds = [...new Set(kgNodesToInsert.map((n) => n.id))];\n const existingKgNodes = await this.propertyGraphStore.get({ ids: kgNodeIds });\n const existingKgNodeIds = new Set(existingKgNodes.map((n) => n.id));\n newKgNodes = kgNodesToInsert.filter((n) => !existingKgNodeIds.has(n.id));\n }\n\n if (this.propertyGraphStore.getDocumentNodes) {\n const existingDocumentNodes = await this.propertyGraphStore.getDocumentNodes(\n processedNodes.map((n) => n.id)\n );\n const existingHashes = new Set(existingDocumentNodes.map((n) => n.hash));\n processedNodes = processedNodes.filter((n) => {\n const hash = this.computeHash(n.text);\n n.hash = hash;\n return !existingHashes.has(hash);\n });\n }\n\n if (this.embedModel && processedNodes.length > 0) {\n const nodeTexts = processedNodes.map((n) => this.truncateForEmbedding(n.text));\n const embeddings = await this.embedModel.getTextEmbeddingBatch(nodeTexts);\n for (let i = 0; i < processedNodes.length; i++) {\n processedNodes[i].embedding = embeddings[i];\n }\n }\n\n if (this.embedKgNodes && this.embedModel && newKgNodes.length > 0) {\n const kgNodeTexts = newKgNodes.map((n) => `${n.label}: ${n.name}`);\n const kgEmbeddings = await this.embedModel.getTextEmbeddingBatch(kgNodeTexts);\n for (let i = 0; i < newKgNodes.length; i++) {\n newKgNodes[i].embedding = kgEmbeddings[i];\n }\n }\n\n if (this.propertyGraphStore.upsertDocumentNodes && processedNodes.length > 0) {\n await this.propertyGraphStore.upsertDocumentNodes(\n processedNodes.map((n) => ({\n id: n.id,\n text: n.text,\n metadata: n.metadata,\n embedding: n.embedding,\n hash: n.hash,\n }))\n );\n }\n\n if (newKgNodes.length > 0) {\n await this.propertyGraphStore.upsertNodes(newKgNodes);\n }\n\n if (kgRelsToInsert.length > 0) {\n await this.propertyGraphStore.upsertRelations(kgRelsToInsert);\n }\n\n return processedNodes;\n }\n\n async delete(nodeIds: string[]): Promise<void> {\n await this.propertyGraphStore.delete({ ids: nodeIds });\n }\n\n asRetriever(options?: {\n subRetrievers?: Array<{ retrieve(query: QueryBundle): Promise<NodeWithScore[]> }>;\n includeText?: boolean;\n /** Number of top similar nodes to retrieve via vector search (default: 4) */\n similarityTopK?: number;\n /** Graph traversal depth from matched nodes (default: 1) */\n pathDepth?: number;\n /** Maximum number of triplets to return after expansion (default: 30) */\n limit?: number;\n /** Minimum similarity score threshold to include results (default: no threshold) */\n similarityScore?: number;\n /** Enable cross-check boosting: boost scores when vector-matched entities link to graph facts (default: true) */\n crossCheckBoost?: boolean;\n /** Multiplier for cross-check boost (default: 1.25 = 25% boost) */\n crossCheckBoostFactor?: number;\n /** Include structural adjacency relations (ON_SAME_PAGE, ADJACENT_TO) in graph expansion (default: true) */\n includeStructuralEdges?: boolean;\n /** Depth for structural edge traversal (default: 1) */\n structuralDepth?: number;\n }): PGRetriever {\n let subRetrievers = options?.subRetrievers as any[];\n\n if (!subRetrievers && this.embedModel && this.propertyGraphStore.supportsVectorQueries) {\n subRetrievers = [\n new VectorContextRetriever({\n graphStore: this.propertyGraphStore,\n embedModel: this.embedModel,\n includeText: options?.includeText ?? true,\n similarityTopK: options?.similarityTopK,\n pathDepth: options?.pathDepth,\n limit: options?.limit,\n similarityScore: options?.similarityScore,\n crossCheckBoost: options?.crossCheckBoost,\n crossCheckBoostFactor: options?.crossCheckBoostFactor,\n includeStructuralEdges: options?.includeStructuralEdges,\n structuralDepth: options?.structuralDepth,\n }),\n ];\n }\n\n if (!subRetrievers) {\n subRetrievers = [];\n }\n\n return new PGRetriever({ subRetrievers });\n }\n\n async query(queryStr: string, options?: {\n /** Number of top similar nodes to retrieve via vector search (default: 4) */\n similarityTopK?: number;\n /** Graph traversal depth from matched nodes (default: 1) */\n pathDepth?: number;\n /** Maximum number of triplets to return after expansion (default: 30) */\n limit?: number;\n /** Minimum similarity score threshold to include results (default: no threshold) */\n similarityScore?: number;\n /** Enable cross-check boosting: boost scores when vector-matched entities link to graph facts (default: true) */\n crossCheckBoost?: boolean;\n /** Multiplier for cross-check boost (default: 1.25 = 25% boost) */\n crossCheckBoostFactor?: number;\n /** Include structural adjacency relations (ON_SAME_PAGE, ADJACENT_TO) in graph expansion (default: true) */\n includeStructuralEdges?: boolean;\n /** Depth for structural edge traversal (default: 1) */\n structuralDepth?: number;\n }): Promise<NodeWithScore[]> {\n const retriever = this.asRetriever(options);\n return retriever.retrieve({ queryStr });\n }\n}\n","import { z } from \"zod\";\nimport type { TransformComponent, TextNode, SchemaDefinition } from \"./types\";\nimport type { EntityNode, Relation, Triplet } from \"../types\";\nimport { KG_NODES_KEY, KG_RELATIONS_KEY, createEntityNode, createRelation } from \"../types\";\n\n/** Minimal contract for a schema — compatible with Zod 3, Zod 4, or any parse-capable object. */\nexport interface Parseable<T> {\n parse(data: unknown): T;\n}\n\nexport interface LLMClient {\n structuredPredict<T>(schema: Parseable<T>, prompt: string): Promise<T>;\n}\n\nexport interface SchemaLLMPathExtractorOptions {\n llm: LLMClient;\n schema: SchemaDefinition;\n maxTripletsPerChunk?: number;\n strict?: boolean;\n extractPromptTemplate?: string;\n}\n\nconst DEFAULT_EXTRACT_PROMPT = `Given the following text, extract a knowledge graph according to the provided schema.\nExtract up to {maxTriplets} triplets. If fewer are present in the text, return fewer. Do not invent or pad triplets.\n\nSchema:\n- Entity types: {entityTypes}\n- Relation types: {relationTypes}\n- Valid relationships: {validationSchema}\n\nText:\n-------\n{text}\n-------\n\nExtract entities and relationships from the text above.\nReturn your answer as a JSON object with a \"triplets\" array. Each triplet should have:\n- \"subject\": { \"name\": string, \"type\": one of [{entityTypes}] }\n- \"relation\": { \"type\": one of [{relationTypes}] }\n- \"object\": { \"name\": string, \"type\": one of [{entityTypes}] }\n\nExample output format:\n{\n \"triplets\": [\n {\n \"subject\": { \"name\": \"John\", \"type\": \"PERSON\" },\n \"relation\": { \"type\": \"WORKS_AT\" },\n \"object\": { \"name\": \"Acme Corp\", \"type\": \"ORGANIZATION\" }\n }\n ]\n}`;\n\nexport class SchemaLLMPathExtractor implements TransformComponent {\n private readonly llm: LLMClient;\n private readonly schema: SchemaDefinition;\n private readonly maxTripletsPerChunk: number;\n private readonly strict: boolean;\n private readonly extractPromptTemplate: string;\n private readonly tripletSchema: z.ZodType<any>;\n private readonly rawTripletSchema: z.ZodType<any>;\n\n constructor(options: SchemaLLMPathExtractorOptions) {\n this.llm = options.llm;\n this.schema = options.schema;\n this.maxTripletsPerChunk = options.maxTripletsPerChunk ?? 10;\n this.strict = options.strict ?? true;\n this.extractPromptTemplate = options.extractPromptTemplate ?? DEFAULT_EXTRACT_PROMPT;\n\n const entityTypeEnum = z.enum(this.schema.entityTypes as [string, ...string[]]);\n const relationTypeEnum = z.enum(this.schema.relationTypes as [string, ...string[]]);\n\n const entitySchema = z.object({\n name: z.string(),\n type: entityTypeEnum,\n properties: z.record(z.unknown()).optional(),\n });\n\n const relationSchema = z.object({\n type: relationTypeEnum,\n properties: z.record(z.unknown()).optional(),\n });\n\n const tripletSchema = z.object({\n subject: entitySchema,\n relation: relationSchema,\n object: entitySchema,\n });\n\n this.tripletSchema = z.object({\n triplets: z.array(tripletSchema),\n });\n\n this.rawTripletSchema = z.object({\n triplets: z.array(z.any()),\n });\n }\n\n private normalizeType(type: string): string {\n return String(type).toUpperCase().replace(/\\s+/g, \"_\");\n }\n\n private buildPrompt(text: string): string {\n const validationSchemaStr = this.schema.validationSchema\n ?.map(([s, r, o]) => `(${s})-[${r}]->(${o})`)\n .join(\", \") ?? \"Any valid combination\";\n\n return this.extractPromptTemplate\n .replace(\"{maxTriplets}\", String(this.maxTripletsPerChunk))\n .replace(\"{entityTypes}\", this.schema.entityTypes.join(\", \"))\n .replace(\"{relationTypes}\", this.schema.relationTypes.join(\", \"))\n .replace(\"{validationSchema}\", validationSchemaStr)\n .replace(\"{text}\", text);\n }\n\n private isValidTriplet(subjectType: string, relationType: string, objectType: string): boolean {\n if (!this.strict || !this.schema.validationSchema) {\n return true;\n }\n\n const subj = this.normalizeType(subjectType);\n const rel = this.normalizeType(relationType);\n const obj = this.normalizeType(objectType);\n\n return this.schema.validationSchema.some(\n ([s, r, o]) =>\n this.normalizeType(s) === subj &&\n this.normalizeType(r) === rel &&\n this.normalizeType(o) === obj\n );\n }\n\n private pruneInvalidTriplets(\n extracted: { triplets: Array<{ subject: any; relation: any; object: any }> }\n ): Triplet[] {\n const validTriplets: Triplet[] = [];\n\n const allowedEntityTypes = new Set(this.schema.entityTypes.map((t) => this.normalizeType(t)));\n const allowedRelationTypes = new Set(this.schema.relationTypes.map((t) => this.normalizeType(t)));\n\n for (const triplet of extracted.triplets) {\n const subject = (triplet as any)?.subject;\n const relation = (triplet as any)?.relation;\n const object = (triplet as any)?.object;\n\n if (!subject || !relation || !object) continue;\n if (typeof subject.type !== \"string\" || typeof subject.name !== \"string\") continue;\n if (typeof relation.type !== \"string\") continue;\n if (typeof object.type !== \"string\" || typeof object.name !== \"string\") continue;\n\n const subjectType = this.normalizeType(subject.type);\n const relationType = this.normalizeType(relation.type);\n const objectType = this.normalizeType(object.type);\n\n if (this.strict) {\n if (!allowedEntityTypes.has(subjectType)) continue;\n if (!allowedRelationTypes.has(relationType)) continue;\n if (!allowedEntityTypes.has(objectType)) continue;\n }\n\n if (!this.isValidTriplet(subjectType, relationType, objectType)) {\n continue;\n }\n\n const subjectName = String(triplet.subject.name).trim();\n const objectName = String(triplet.object.name).trim();\n\n if (subjectName.toLowerCase() === objectName.toLowerCase()) {\n continue;\n }\n\n const subj = createEntityNode({\n label: subjectType,\n name: subjectName,\n properties: triplet.subject.properties ?? {},\n });\n\n const obj = createEntityNode({\n label: objectType,\n name: objectName,\n properties: triplet.object.properties ?? {},\n });\n\n const rel = createRelation({\n label: relationType,\n sourceId: subj.id,\n targetId: obj.id,\n properties: triplet.relation.properties ?? {},\n });\n\n validTriplets.push([subj, rel, obj]);\n }\n\n return validTriplets;\n }\n\n private async extractFromNode(node: TextNode): Promise<TextNode> {\n const prompt = this.buildPrompt(node.text);\n\n let triplets: Triplet[] = [];\n\n try {\n const result = await this.llm.structuredPredict(this.rawTripletSchema, prompt);\n triplets = this.pruneInvalidTriplets(result);\n\n if (this.maxTripletsPerChunk > 0 && triplets.length > this.maxTripletsPerChunk) {\n triplets = triplets.slice(0, this.maxTripletsPerChunk);\n }\n } catch (err) {\n // Log extraction errors for debugging\n console.warn(`[SchemaLLMPathExtractor] Failed to extract from node ${node.id}:`, err);\n triplets = [];\n }\n\n const existingNodes = (node.metadata[KG_NODES_KEY] as EntityNode[]) ?? [];\n const existingRelations = (node.metadata[KG_RELATIONS_KEY] as Relation[]) ?? [];\n\n // Avoid copying kg_nodes/kg_relations into node properties (can create circular refs)\n const safeMetadata: Record<string, unknown> = { ...node.metadata };\n delete safeMetadata[KG_NODES_KEY];\n delete safeMetadata[KG_RELATIONS_KEY];\n\n for (const [subj, rel, obj] of triplets) {\n subj.properties = { ...subj.properties, ...safeMetadata };\n obj.properties = { ...obj.properties, ...safeMetadata };\n rel.properties = { ...rel.properties, ...safeMetadata };\n\n existingNodes.push(subj);\n existingNodes.push(obj);\n existingRelations.push(rel);\n }\n\n return {\n ...node,\n metadata: {\n ...node.metadata,\n [KG_NODES_KEY]: existingNodes,\n [KG_RELATIONS_KEY]: existingRelations,\n },\n };\n }\n\n async transform(nodes: TextNode[], options?: { showProgress?: boolean }): Promise<TextNode[]> {\n const results: TextNode[] = [];\n\n for (const node of nodes) {\n const processed = await this.extractFromNode(node);\n results.push(processed);\n }\n\n return results;\n }\n}\n","import type { TransformComponent, TextNode } from \"./types\";\nimport type { Relation } from \"../types\";\nimport { KG_RELATIONS_KEY, STRUCT_SAME_PAGE, STRUCT_ADJACENT } from \"../types\";\n\nexport interface AdjacencyLinkerOptions {\n /**\n * Link chunks that share the same pageNumber in metadata.\n * Default: true\n */\n linkSamePage?: boolean;\n\n /**\n * Link chunks that are adjacent by chunkIndex in metadata.\n * Default: true\n */\n linkAdjacent?: boolean;\n\n /**\n * Maximum chunk index distance for ADJACENT_TO links.\n * Default: 1 (immediate neighbors only)\n */\n adjacentDistance?: number;\n\n /**\n * Only create cross-type links (e.g., text↔image), not text↔text.\n * Default: false (link all)\n */\n crossTypeOnly?: boolean;\n}\n\nexport class AdjacencyLinker implements TransformComponent {\n private linkSamePage: boolean;\n private linkAdjacent: boolean;\n private adjacentDistance: number;\n private crossTypeOnly: boolean;\n\n constructor(options: AdjacencyLinkerOptions = {}) {\n this.linkSamePage = options.linkSamePage ?? true;\n this.linkAdjacent = options.linkAdjacent ?? true;\n this.adjacentDistance = options.adjacentDistance ?? 1;\n this.crossTypeOnly = options.crossTypeOnly ?? false;\n }\n\n /**\n * Transform is called AFTER other extractors (SchemaLLMPathExtractor, ImplicitPathExtractor).\n * It adds structural relations between chunks based on metadata.\n */\n async transform(nodes: TextNode[], options?: { showProgress?: boolean }): Promise<TextNode[]> {\n if (nodes.length < 2) return nodes;\n\n // Group nodes by documentId\n const byDocument = new Map<string, TextNode[]>();\n for (const node of nodes) {\n const docId = String(node.metadata.documentId ?? \"unknown\");\n if (!byDocument.has(docId)) byDocument.set(docId, []);\n byDocument.get(docId)!.push(node);\n }\n\n // For each document, create structural links\n // Map from node.id to relations that should be added to that node\n const nodeRelationsMap = new Map<string, Relation[]>();\n\n for (const [docId, docNodes] of byDocument) {\n // Group by page\n const byPage = new Map<number, TextNode[]>();\n for (const node of docNodes) {\n const page = Number(node.metadata.pageNumber ?? 0);\n if (!byPage.has(page)) byPage.set(page, []);\n byPage.get(page)!.push(node);\n }\n\n // Same-page links\n if (this.linkSamePage) {\n for (const [page, pageNodes] of byPage) {\n if (pageNodes.length < 2) continue;\n for (let i = 0; i < pageNodes.length; i++) {\n for (let j = i + 1; j < pageNodes.length; j++) {\n const a = pageNodes[i];\n const b = pageNodes[j];\n if (this.shouldLink(a, b)) {\n this.addRelation(nodeRelationsMap, a.id, b.id, STRUCT_SAME_PAGE, { pageNumber: page, documentId: docId });\n }\n }\n }\n }\n }\n\n // Adjacent chunk links (by chunkIndex)\n if (this.linkAdjacent) {\n // Sort by chunkIndex\n const sorted = [...docNodes].sort((a, b) => {\n const idxA = Number(a.metadata.chunkIndex ?? 0);\n const idxB = Number(b.metadata.chunkIndex ?? 0);\n return idxA - idxB;\n });\n\n for (let i = 0; i < sorted.length; i++) {\n for (let d = 1; d <= this.adjacentDistance && i + d < sorted.length; d++) {\n const a = sorted[i];\n const b = sorted[i + d];\n if (this.shouldLink(a, b)) {\n this.addRelation(nodeRelationsMap, a.id, b.id, STRUCT_ADJACENT, { distance: d, documentId: docId });\n }\n }\n }\n }\n }\n\n // Merge relations into node metadata\n return nodes.map((node) => {\n const existingRelations = (node.metadata[KG_RELATIONS_KEY] as Relation[]) ?? [];\n const newRelations = nodeRelationsMap.get(node.id) ?? [];\n\n return {\n ...node,\n metadata: {\n ...node.metadata,\n [KG_RELATIONS_KEY]: [...existingRelations, ...newRelations],\n },\n };\n });\n }\n\n private shouldLink(a: TextNode, b: TextNode): boolean {\n if (!this.crossTypeOnly) return true;\n const typeA = String(a.metadata.contentType ?? \"text\");\n const typeB = String(b.metadata.contentType ?? \"text\");\n return typeA !== typeB;\n }\n\n private addRelation(\n map: Map<string, Relation[]>,\n sourceNodeId: string,\n targetNodeId: string,\n label: string,\n properties: Record<string, unknown>\n ): void {\n // Create CHUNK entity IDs (matching ImplicitPathExtractor format)\n const sourceId = `CHUNK_${sourceNodeId.replace(/\\s+/g, \"_\").toUpperCase()}`;\n const targetId = `CHUNK_${targetNodeId.replace(/\\s+/g, \"_\").toUpperCase()}`;\n\n const rel: Relation = {\n id: `${sourceId}_${label}_${targetId}`,\n label,\n sourceId,\n targetId,\n properties,\n };\n\n // Add to source node\n if (!map.has(sourceNodeId)) map.set(sourceNodeId, []);\n map.get(sourceNodeId)!.push(rel);\n\n // Bidirectional: also add reverse to target node\n const reverseRel: Relation = {\n id: `${targetId}_${label}_${sourceId}`,\n label,\n sourceId: targetId,\n targetId: sourceId,\n properties,\n };\n\n if (!map.has(targetNodeId)) map.set(targetNodeId, []);\n map.get(targetNodeId)!.push(reverseRel);\n }\n}\n"],"mappings":"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","names":["z","z"]}
1
+ {"version":3,"sources":["../src/hana/parse-host-port.ts","../src/hana/connection.ts","../src/hana/sparql-store.ts","../src/hana/graph-discovery.ts","../src/graph/types.ts","../src/graph/hana-property-graph-store.ts","../src/graph/pg-retriever.ts","../src/graph/retrievers/base.ts","../src/graph/retrievers/vector-context.ts","../src/graph/extractors/implicit.ts","../src/graph/property-graph-index.ts","../src/graph/extractors/schema-llm.ts","../src/graph/extractors/adjacency-linker.ts"],"sourcesContent":["export function parseHostPort(\n host: string,\n fallbackPort: number\n): { host: string; port: number } {\n const idx = host.lastIndexOf(\":\");\n if (idx > -1 && idx < host.length - 1) {\n const maybePort = Number(host.slice(idx + 1));\n if (!Number.isNaN(maybePort) && maybePort > 0) {\n return { host: host.slice(0, idx), port: maybePort };\n }\n }\n\n return { host, port: fallbackPort };\n}\n","import { parseHostPort } from \"./parse-host-port\";\nimport type { HanaConnection, HanaConnectionConfig } from \"./types\";\n\nexport async function createHanaConnection(\n config: HanaConnectionConfig\n): Promise<HanaConnection> {\n let hana: any;\n try {\n const hanaModule = await import(\"@sap/hana-client\");\n hana = (hanaModule as any).default || hanaModule;\n } catch {\n throw new Error(\n \"@sap/hana-client is not available. Ensure it is installed and pnpm build scripts are approved (pnpm approve-builds).\"\n );\n }\n\n const fallbackPort = config.port ?? 443;\n const { host, port } = parseHostPort(config.host, fallbackPort);\n\n const conn: HanaConnection = hana.createConnection();\n\n await new Promise<void>((resolve, reject) => {\n conn.connect(\n {\n serverNode: `${host}:${port}`,\n uid: config.user,\n pwd: config.password,\n encrypt: config.encrypt ?? true,\n sslValidateCertificate: config.sslValidateCertificate ?? true\n },\n (err?: unknown) => (err ? reject(err) : resolve())\n );\n });\n\n return conn;\n}\n\nexport async function hanaExec<T = unknown>(\n conn: HanaConnection,\n sql: string\n): Promise<T> {\n return await new Promise<T>((resolve, reject) => {\n conn.exec(sql, (err: unknown, result: unknown) => {\n if (err) return reject(err);\n resolve(result as T);\n });\n });\n}\n","import type { HanaConnection } from \"./types\";\n\nexport type SparqlExecuteResult = unknown;\n\nexport class HanaSparqlStore {\n private readonly conn: HanaConnection;\n\n constructor(conn: HanaConnection) {\n this.conn = conn;\n }\n\n async execute(options: {\n sparql: string;\n headers?: string;\n defaultGraphUri?: string;\n }): Promise<SparqlExecuteResult> {\n const headerLines: string[] = [];\n\n if (options.defaultGraphUri) {\n headerLines.push(`rqx-default-graph-uri: ${options.defaultGraphUri}`);\n }\n\n if (options.headers) {\n headerLines.push(options.headers.trim());\n }\n\n const hdrs = headerLines.length ? `${headerLines.join(\"\\r\\n\")}\\r\\n` : \"\";\n\n const sql = \"CALL SPARQL_EXECUTE(?, ?, ?, ?)\";\n\n return await new Promise((resolve, reject) => {\n // @sap/hana-client supports conn.exec with parameter arrays.\n // We intentionally keep the result as unknown; callers can parse the XML/JSON depending on HANA return mode.\n (this.conn as any).exec(sql, [options.sparql, hdrs, \"\", null], (err: unknown, result: unknown) => {\n if (err) return reject(err);\n resolve(result);\n });\n });\n }\n\n async loadTurtle(options: {\n turtle: string;\n graphName: string;\n filename?: string;\n }): Promise<SparqlExecuteResult> {\n const requestHdrs = [\n \"rqx-load-protocol: true\",\n options.filename ? `rqx-load-filename: ${options.filename}` : undefined,\n `rqx-load-graphname: ${options.graphName}`\n ]\n .filter(Boolean)\n .join(\"\\r\\n\");\n\n return await this.execute({ sparql: options.turtle, headers: requestHdrs });\n }\n}\n","import type { HanaConnection } from \"./types\";\nimport { hanaExec } from \"./connection\";\n\nexport type HanaKgGraphTableSuffix = \"VECTORS\" | \"NODES\" | \"IMAGES\";\n\nexport type GraphTables = {\n vectorsTable: string;\n nodesTable: string;\n imagesTable: string;\n};\n\nexport type HanaKgGraphInfo = {\n graphName: string;\n hasVectors: boolean;\n hasNodes: boolean;\n hasImages: boolean;\n vectorsCount?: number;\n nodesCount?: number;\n imagesCount?: number;\n};\n\nexport function getGraphTables(graphName: string): GraphTables {\n return {\n vectorsTable: `${graphName}_VECTORS`,\n nodesTable: `${graphName}_NODES`,\n imagesTable: `${graphName}_IMAGES`,\n };\n}\n\nfunction escapeSqlStringLiteral(value: string): string {\n return value.replace(/'/g, \"''\");\n}\n\nfunction normalizeRequirement(req: HanaKgGraphTableSuffix): HanaKgGraphTableSuffix {\n return req.toUpperCase() as HanaKgGraphTableSuffix;\n}\n\nexport async function listGraphs(\n conn: HanaConnection,\n opts?: {\n schema?: string;\n require?: HanaKgGraphTableSuffix[];\n includeCounts?: boolean;\n limit?: number;\n }\n): Promise<HanaKgGraphInfo[]> {\n const schemaFilter = opts?.schema ? ` = '${escapeSqlStringLiteral(opts.schema)}'` : \" = CURRENT_SCHEMA\";\n\n // Discover by our naming convention in a single pass.\n // Note: `_` is a wildcard in LIKE, so we must escape it.\n const rows = (await hanaExec<any[]>(\n conn,\n `SELECT TABLE_NAME\n FROM SYS.TABLES\n WHERE SCHEMA_NAME${schemaFilter}\n AND (\n TABLE_NAME LIKE '%\\\\_VECTORS' ESCAPE '\\\\'\n OR TABLE_NAME LIKE '%\\\\_NODES' ESCAPE '\\\\'\n OR TABLE_NAME LIKE '%\\\\_IMAGES' ESCAPE '\\\\'\n )`\n )) as Array<{ TABLE_NAME?: string; table_name?: string }>;\n\n const tableNames = new Set(\n (rows ?? []).map((r) => String(r.TABLE_NAME ?? r.table_name ?? \"\").toUpperCase()).filter(Boolean)\n );\n\n const stems = new Set<string>();\n for (const t of tableNames) {\n if (t.endsWith(\"_VECTORS\")) {\n stems.add(t.slice(0, -\"_VECTORS\".length));\n }\n }\n\n const infos: HanaKgGraphInfo[] = [];\n for (const stem of stems) {\n const { vectorsTable, nodesTable, imagesTable } = getGraphTables(stem);\n infos.push({\n graphName: stem,\n hasVectors: tableNames.has(vectorsTable),\n hasNodes: tableNames.has(nodesTable),\n hasImages: tableNames.has(imagesTable),\n });\n }\n\n if (opts?.includeCounts) {\n // Only count tables that exist; use quoted identifiers to be robust.\n // NOTE: Table names come from SYS.TABLES results, so this is not user input.\n const schemaName = opts?.schema ? String(opts.schema).toUpperCase() : undefined;\n\n const countTable = async (tableName: string): Promise<number> => {\n const fq = schemaName\n ? `\"${schemaName.replace(/\"/g, '\"\"')}\".\"${tableName.replace(/\"/g, '\"\"')}\"`\n : `\"${tableName.replace(/\"/g, '\"\"')}\"`;\n const res = (await hanaExec<any[]>(conn, `SELECT COUNT(*) AS CNT FROM ${fq}`)) as any[];\n const row = res?.[0];\n const cnt = row?.CNT ?? row?.cnt ?? Object.values(row ?? {})[0];\n return Number(cnt ?? 0);\n };\n\n for (const info of infos) {\n const { vectorsTable, nodesTable, imagesTable } = getGraphTables(info.graphName);\n\n if (info.hasVectors) {\n info.vectorsCount = await countTable(vectorsTable);\n }\n if (info.hasNodes) {\n info.nodesCount = await countTable(nodesTable);\n }\n if (info.hasImages) {\n info.imagesCount = await countTable(imagesTable);\n }\n }\n }\n\n const requireList = (opts?.require ?? []).map(normalizeRequirement);\n const filtered = requireList.length\n ? infos.filter((i) =>\n requireList.every((req) => {\n if (req === \"VECTORS\") return i.hasVectors;\n if (req === \"NODES\") return i.hasNodes;\n if (req === \"IMAGES\") return i.hasImages;\n return false;\n })\n )\n : infos;\n\n filtered.sort((a, b) => a.graphName.localeCompare(b.graphName));\n\n if (opts?.limit !== undefined) {\n return filtered.slice(0, Math.max(0, opts.limit));\n }\n\n return filtered;\n}\n","import { z } from \"zod\";\n\nexport const TRIPLET_SOURCE_KEY = \"triplet_source_id\";\nexport const KG_NODES_KEY = \"kg_nodes\";\nexport const KG_RELATIONS_KEY = \"kg_relations\";\nexport const VECTOR_SOURCE_KEY = \"vector_source_id\";\nexport const KG_SOURCE_REL = \"HAS_SOURCE\";\n\n// Structural adjacency relations (not semantic)\nexport const STRUCT_SAME_PAGE = \"ON_SAME_PAGE\";\nexport const STRUCT_ADJACENT = \"ADJACENT_TO\";\nexport const STRUCT_CONTAINS = \"CONTAINS\";\n\nexport const EntityNodeSchema = z.object({\n id: z.string(),\n label: z.string(),\n name: z.string(),\n properties: z.record(z.unknown()).default({}),\n embedding: z.array(z.number()).optional(),\n});\n\nexport type EntityNode = z.infer<typeof EntityNodeSchema>;\n\nexport const RelationSchema = z.object({\n id: z.string().optional(),\n label: z.string(),\n sourceId: z.string(),\n targetId: z.string(),\n properties: z.record(z.unknown()).default({}),\n});\n\nexport type Relation = z.infer<typeof RelationSchema>;\n\nexport type Triplet = [EntityNode, Relation, EntityNode];\n\nexport type LabelledNode = EntityNode;\n\nexport interface NodeWithScore {\n node: {\n id: string;\n text: string;\n metadata: Record<string, unknown>;\n refDocId?: string;\n };\n score: number;\n}\n\nexport interface QueryBundle {\n queryStr: string;\n embedding?: number[];\n embeddingStrs?: string[];\n}\n\nexport function createEntityNode(opts: {\n label: string;\n name: string;\n properties?: Record<string, unknown>;\n embedding?: number[];\n}): EntityNode {\n const id = `${opts.label.toUpperCase()}_${opts.name.replace(/\\s+/g, \"_\").toUpperCase()}`;\n return {\n id,\n label: opts.label,\n name: opts.name,\n properties: opts.properties ?? {},\n embedding: opts.embedding,\n };\n}\n\nexport function createRelation(opts: {\n label: string;\n sourceId: string;\n targetId: string;\n properties?: Record<string, unknown>;\n}): Relation {\n return {\n id: `${opts.sourceId}_${opts.label}_${opts.targetId}`,\n label: opts.label,\n sourceId: opts.sourceId,\n targetId: opts.targetId,\n properties: opts.properties ?? {},\n };\n}\n\nexport function tripletToString(triplet: Triplet, includeProperties = false): string {\n const [subj, rel, obj] = triplet;\n if (includeProperties) {\n return `${subj.name} (${subj.label}) -[${rel.label}]-> ${obj.name} (${obj.label})`;\n }\n return `${subj.id} -> ${rel.label} -> ${obj.id}`;\n}\n","import type { HanaConnection } from \"../hana/types\";\nimport type { PropertyGraphStore, VectorStoreQuery } from \"./property-graph-store\";\nimport type { EntityNode, Relation, Triplet, LabelledNode } from \"./types\";\nimport { TRIPLET_SOURCE_KEY, KG_SOURCE_REL } from \"./types\";\n\ntype DocumentNode = {\n id: string;\n text: string;\n metadata: Record<string, unknown>;\n embedding?: number[];\n hash?: string;\n};\n\nexport interface HanaPropertyGraphStoreOptions {\n graphName: string;\n vectorTableName?: string;\n documentNodesTableName?: string;\n /** Optional, unused for dimensionless REAL_VECTOR columns (kept for backward compat logging). */\n vectorDimension?: number;\n /** SQL execBatch chunk size for vector/document table writes. Defaults to HANA_KGVECTOR_SQL_BATCH_SIZE or 500. */\n sqlBatchSize?: number;\n /** If true, will DROP and recreate tables on init (intended for tests/dev only). */\n resetTables?: boolean;\n}\n\nexport class HanaPropertyGraphStore implements PropertyGraphStore {\n private readonly conn: HanaConnection;\n private readonly graphName: string;\n private readonly vectorTableName: string;\n private readonly documentNodesTableName: string;\n private readonly sqlBatchSize: number;\n private readonly resetTables: boolean;\n private initialized = false;\n\n readonly supportsVectorQueries = true;\n readonly supportsStructuredQueries = true;\n\n constructor(conn: HanaConnection, options: HanaPropertyGraphStoreOptions) {\n this.conn = conn;\n this.graphName = options.graphName;\n this.vectorTableName = options.vectorTableName ?? `${options.graphName.replace(/[^a-zA-Z0-9]/g, \"_\")}_VECTORS`;\n this.documentNodesTableName = options.documentNodesTableName ?? `${options.graphName.replace(/[^a-zA-Z0-9]/g, \"_\")}_NODES`;\n this.sqlBatchSize = this.resolveSqlBatchSize(options.sqlBatchSize);\n this.resetTables = options.resetTables ?? false;\n }\n\n private resolveSqlBatchSize(configured?: number): number {\n const raw = configured ?? Number(process.env.HANA_KGVECTOR_SQL_BATCH_SIZE);\n if (!Number.isFinite(raw) || raw <= 0) return 500;\n return Math.floor(raw);\n }\n\n private async ensureInitialized(): Promise<void> {\n if (this.initialized) return;\n\n // Optional destructive reset intended for tests/dev. This is OFF by default.\n if (this.resetTables) {\n await this.exec(`DROP TABLE ${this.vectorTableName}`).catch(() => {});\n await this.exec(`DROP TABLE ${this.documentNodesTableName}`).catch(() => {});\n\n // Also clear the RDF named graph to avoid accumulating triples across test runs.\n // (Tables are recreated, but the KG graph would otherwise persist.)\n await this.sparqlExecute(`CLEAR GRAPH <${this.graphName}>`).catch(() => {});\n }\n\n // HANA does not support \"IF NOT EXISTS\" for CREATE TABLE. We try to create and ignore the\n // \"name exists\" error, but surface any other errors.\n const createVectorTable = `\n CREATE COLUMN TABLE ${this.vectorTableName} (\n id NVARCHAR(512) PRIMARY KEY,\n node_type NVARCHAR(64),\n label NVARCHAR(128),\n name NVARCHAR(512),\n properties NCLOB,\n source_id NVARCHAR(512),\n target_id NVARCHAR(512),\n embedding REAL_VECTOR\n )\n `;\n await this.exec(createVectorTable).catch((err: any) => {\n const message = String(err?.message ?? \"\");\n // Handle \"duplicate table name\" or \"already exists\"\n if (!/exists|duplicate table name/i.test(message)) {\n throw err;\n }\n });\n\n // Migrate existing tables that lack the source_id / target_id columns.\n await this.exec(`ALTER TABLE ${this.vectorTableName} ADD (source_id NVARCHAR(512), target_id NVARCHAR(512))`).catch((err: any) => {\n const message = String(err?.message ?? \"\");\n // Ignore if columns already exist\n if (!/duplicate column|already exists/i.test(message)) {\n throw err;\n }\n });\n\n const createDocumentTable = `\n CREATE COLUMN TABLE ${this.documentNodesTableName} (\n id NVARCHAR(512) PRIMARY KEY,\n text NCLOB,\n metadata NCLOB,\n hash NVARCHAR(64),\n embedding REAL_VECTOR\n )\n `;\n await this.exec(createDocumentTable).catch((err: any) => {\n const message = String(err?.message ?? \"\");\n if (!/exists|duplicate table name/i.test(message)) {\n throw err;\n }\n });\n\n this.initialized = true;\n }\n\n private async exec(sql: string, params?: unknown[]): Promise<unknown[]> {\n return new Promise((resolve, reject) => {\n if (params && params.length > 0) {\n (this.conn as any).exec(sql, params, (err: unknown, result: unknown) => {\n if (err) reject(err);\n else resolve(result as unknown[]);\n });\n } else {\n this.conn.exec(sql, (err: unknown, result: unknown) => {\n if (err) reject(err);\n else resolve(result as unknown[]);\n });\n }\n });\n }\n\n private async execBatch(sql: string, paramRows: unknown[][]): Promise<void> {\n if (paramRows.length === 0) return;\n\n if (typeof (this.conn as any).prepare !== \"function\") {\n for (const params of paramRows) {\n await this.exec(sql, params);\n }\n return;\n }\n\n for (let i = 0; i < paramRows.length; i += this.sqlBatchSize) {\n const chunk = paramRows.slice(i, i + this.sqlBatchSize);\n const stmt = await new Promise<any>((resolve, reject) => {\n (this.conn as any).prepare(sql, (err: unknown, prepared: unknown) => {\n if (err) reject(err);\n else resolve(prepared);\n });\n });\n\n try {\n if (typeof stmt.execBatch !== \"function\") {\n for (const params of chunk) {\n await this.exec(sql, params);\n }\n continue;\n }\n\n await new Promise<void>((resolve, reject) => {\n stmt.execBatch(chunk, (err: unknown) => {\n if (err) reject(err);\n else resolve();\n });\n });\n } finally {\n try {\n stmt.drop?.();\n } catch {\n // non-fatal cleanup\n }\n }\n }\n }\n\n private uniqueBy<T>(items: T[], getId: (item: T) => string): T[] {\n const byId = new Map<string, T>();\n\n for (const item of items) {\n byId.set(getId(item), item);\n }\n\n return Array.from(byId.values());\n }\n\n private sanitizeRecord(record: Record<string, unknown> | undefined): Record<string, unknown> {\n const safeRecord: Record<string, unknown> = { ...(record ?? {}) };\n delete (safeRecord as any).kg_nodes;\n delete (safeRecord as any).kg_relations;\n return safeRecord;\n }\n\n private async sparqlExecute(sparql: string, headers = \"\"): Promise<unknown> {\n // Some HANA environments require OUT parameters (e.g. RESPONSE) to be bound.\n // The @sap/hana-client driver provides a proc statement helper which correctly\n // handles OUT params *without* requiring you to pass them in the params array.\n try {\n const streamMod = await import(\"@sap/hana-client/extension/Stream\");\n const Stream = (streamMod as any).default ?? streamMod;\n\n return await new Promise<unknown>((resolve, reject) => {\n Stream.createProcStatement(this.conn as any, \"CALL SPARQL_EXECUTE(?, ?, ?, ?)\", (err: any, stmt: any) => {\n if (err) return reject(err);\n stmt.exec([sparql, headers], (err2: any, scalarParams: any) => {\n // scalarParams contains OUT params by name (e.g. RESPONSE)\n if (err2) return reject(err2);\n resolve(scalarParams);\n });\n });\n });\n } catch {\n // Fallback (may fail on systems requiring bound OUT params)\n return await new Promise<unknown>((resolve, reject) => {\n (this.conn as any).exec(\n \"CALL SPARQL_EXECUTE(?, ?, ?, ?)\",\n // Important: only pass IN params; OUT params are placeholders in SQL.\n [sparql, headers],\n (err: unknown, result: unknown) => {\n if (err) reject(err);\n else resolve(result);\n }\n );\n });\n }\n }\n\n private entityToUri(node: EntityNode): string {\n return `<urn:hkv:${this.graphName}:${node.label}:${encodeURIComponent(node.id)}>`;\n }\n\n private relationToTriples(rel: Relation, subj: EntityNode, obj: EntityNode): string {\n const subjUri = this.entityToUri(subj);\n const objUri = this.entityToUri(obj);\n const predUri = `<urn:hkv:rel:${rel.label}>`;\n return `${subjUri} ${predUri} ${objUri} .`;\n }\n\n private entityToTriples(node: EntityNode): string {\n const uri = this.entityToUri(node);\n const lines: string[] = [];\n lines.push(`${uri} a <urn:hkv:type:${node.label}> .`);\n lines.push(`${uri} <urn:hkv:prop:name> \"${this.escapeLiteral(node.name)}\" .`);\n\n for (const [key, value] of Object.entries(node.properties)) {\n if (value !== undefined && value !== null) {\n const strVal = typeof value === \"string\" ? value : String(value);\n // Skip overly large properties in RDF — they are already stored in the vectors table\n if (strVal.length > 5000) continue;\n const escaped = this.escapeLiteral(strVal);\n lines.push(`${uri} <urn:hkv:prop:${key}> \"${escaped}\" .`);\n }\n }\n\n return lines.join(\"\\n\");\n }\n\n private escapeLiteral(s: string): string {\n return s\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, \"\\\\n\")\n .replace(/\\r/g, \"\\\\r\")\n .replace(/\\t/g, \"\\\\t\");\n }\n\n async upsertNodes(nodes: EntityNode[]): Promise<void> {\n await this.ensureInitialized();\n if (nodes.length === 0) return;\n\n const uniqueNodes = this.uniqueBy(nodes, (node) => node.id);\n\n const triples = uniqueNodes.map((n) => this.entityToTriples(n)).join(\"\\n\");\n const sparql = `INSERT DATA { GRAPH <${this.graphName}> { ${triples} } }`;\n await this.sparqlExecute(sparql);\n\n const rowsWithEmbedding: unknown[][] = [];\n const rowsWithoutEmbedding: unknown[][] = [];\n\n for (const node of uniqueNodes) {\n const safeProps = this.sanitizeRecord(node.properties);\n const row = [node.id, node.label, node.name, JSON.stringify(safeProps)];\n if (node.embedding) {\n rowsWithEmbedding.push([...row, JSON.stringify(node.embedding)]);\n } else {\n rowsWithoutEmbedding.push(row);\n }\n }\n\n await this.execBatch(\n `\n UPSERT ${this.vectorTableName} (id, node_type, label, name, properties)\n VALUES (?, 'entity', ?, ?, ?)\n WITH PRIMARY KEY\n `,\n rowsWithoutEmbedding\n );\n\n await this.execBatch(\n `\n UPSERT ${this.vectorTableName} (id, node_type, label, name, properties, embedding)\n VALUES (?, 'entity', ?, ?, ?, TO_REAL_VECTOR(?))\n WITH PRIMARY KEY\n `,\n rowsWithEmbedding\n );\n }\n\n async upsertRelations(relations: Relation[]): Promise<void> {\n await this.ensureInitialized();\n if (relations.length === 0) return;\n\n const uniqueRelations = this.uniqueBy(\n relations,\n (rel) => rel.id ?? `${rel.sourceId}_${rel.label}_${rel.targetId}`\n );\n\n const nodeIds = new Set<string>();\n for (const rel of uniqueRelations) {\n nodeIds.add(rel.sourceId);\n nodeIds.add(rel.targetId);\n }\n\n const existingNodes = await this.get({ ids: Array.from(nodeIds) });\n const nodeMap = new Map(existingNodes.map((n) => [n.id, n]));\n\n // Write to RDF graph (backward compat — kept for installations that use SPARQL)\n const lines: string[] = [];\n for (const rel of uniqueRelations) {\n const subj = nodeMap.get(rel.sourceId);\n const obj = nodeMap.get(rel.targetId);\n if (subj && obj) {\n lines.push(this.relationToTriples(rel, subj, obj));\n }\n }\n\n if (lines.length > 0) {\n const sparql = `INSERT DATA { GRAPH <${this.graphName}> { ${lines.join(\"\\n\")} } }`;\n await this.sparqlExecute(sparql).catch((err: any) => {\n // SPARQL may fail if KG Engine is not enabled — log but don't block\n console.warn(`[HanaPropertyGraphStore] SPARQL INSERT for relations failed (non-fatal):`, err?.message ?? err);\n });\n }\n\n // Write to _VECTORS table for reliable SQL-based querying via getRelMap()\n const relationRows: unknown[][] = [];\n for (const rel of uniqueRelations) {\n const relId = rel.id ?? `${rel.sourceId}_${rel.label}_${rel.targetId}`;\n const safeProps: Record<string, unknown> = {\n sourceId: rel.sourceId,\n targetId: rel.targetId,\n ...(rel.properties ?? {}),\n };\n delete (safeProps as any).kg_nodes;\n delete (safeProps as any).kg_relations;\n\n relationRows.push([\n relId,\n rel.label,\n `${rel.sourceId} -> ${rel.targetId}`,\n JSON.stringify(safeProps),\n rel.sourceId,\n rel.targetId,\n ]);\n }\n\n await this.execBatch(\n `\n UPSERT ${this.vectorTableName} (id, node_type, label, name, properties, source_id, target_id)\n VALUES (?, 'relation', ?, ?, ?, ?, ?)\n WITH PRIMARY KEY\n `,\n relationRows\n );\n }\n\n async get(opts: { ids: string[] }): Promise<LabelledNode[]> {\n await this.ensureInitialized();\n if (opts.ids.length === 0) return [];\n\n const rows = (await this.exec(\n `SELECT id, label, name, properties FROM ${this.vectorTableName} WHERE id IN (${opts.ids.map(() => \"?\").join(\",\")})`,\n opts.ids\n )) as any[];\n\n return (rows ?? []).map((r) => ({\n id: r.ID ?? r.id,\n label: r.LABEL ?? r.label,\n name: r.NAME ?? r.name,\n properties: JSON.parse(r.PROPERTIES ?? r.properties ?? \"{}\"),\n }));\n }\n\n async getRelMap(opts: {\n nodes: LabelledNode[];\n depth?: number;\n limit?: number;\n ignoreRels?: string[];\n }): Promise<Triplet[]> {\n await this.ensureInitialized();\n if (opts.nodes.length === 0) return [];\n\n const nodeIds = opts.nodes.map((n) => n.id);\n const depth = opts.depth ?? 1;\n const limit = opts.limit ?? 100;\n const ignoreRels = opts.ignoreRels ?? [KG_SOURCE_REL];\n const ignoreRelSet = new Set(ignoreRels);\n\n const triplets: Triplet[] = [];\n const seenKeys = new Set<string>();\n const nodeMap = new Map(opts.nodes.map((n) => [n.id, n]));\n\n // --- SQL path: query _VECTORS WHERE node_type='relation' ---\n const placeholders = nodeIds.map(() => \"?\").join(\",\");\n const sqlRelQuery = `\n SELECT id, label, name, properties, source_id, target_id\n FROM ${this.vectorTableName}\n WHERE node_type = 'relation'\n AND (source_id IN (${placeholders}) OR target_id IN (${placeholders}))\n LIMIT ?\n `;\n const sqlParams = [...nodeIds, ...nodeIds, limit];\n\n try {\n const sqlRows = (await this.exec(sqlRelQuery, sqlParams)) as any[];\n for (const row of sqlRows ?? []) {\n const relLabel = row.LABEL ?? row.label;\n const sourceId = row.SOURCE_ID ?? row.source_id;\n const targetId = row.TARGET_ID ?? row.target_id;\n\n if (ignoreRelSet.has(relLabel)) continue;\n\n const key = `${sourceId}|${relLabel}|${targetId}`;\n if (seenKeys.has(key)) continue;\n seenKeys.add(key);\n\n let subj = nodeMap.get(sourceId);\n let obj = nodeMap.get(targetId);\n\n if (!subj) {\n subj = { id: sourceId, label: \"UNKNOWN\", name: sourceId, properties: {} };\n }\n if (!obj) {\n obj = { id: targetId, label: \"UNKNOWN\", name: targetId, properties: {} };\n }\n\n const rel: Relation = {\n id: row.ID ?? row.id,\n label: relLabel,\n sourceId,\n targetId,\n properties: JSON.parse(row.PROPERTIES ?? row.properties ?? \"{}\"),\n };\n\n triplets.push([subj, rel, obj]);\n }\n } catch (err: any) {\n console.warn(`[HanaPropertyGraphStore] SQL relation query failed (falling back to SPARQL only):`, err?.message ?? err);\n }\n\n // --- SPARQL path: query RDF graph (backward compat) ---\n try {\n const filterClause = nodeIds\n .map((id) => `CONTAINS(STR(?s), \"${encodeURIComponent(id)}\") || CONTAINS(STR(?o), \"${encodeURIComponent(id)}\")`)\n .join(\" || \");\n\n const ignoreFilter = ignoreRels.length > 0\n ? `FILTER(${ignoreRels.map((r) => `!CONTAINS(STR(?p), \"${r}\")`).join(\" && \")})`\n : \"\";\n\n const sparql = `\n SELECT ?s ?p ?o\n FROM <${this.graphName}>\n WHERE {\n ?s ?p ?o .\n FILTER(${filterClause})\n ${ignoreFilter}\n }\n LIMIT ${limit}\n `;\n\n const result = await this.exec(\n `SELECT * FROM SPARQL_TABLE('${sparql.replace(/'/g, \"''\")}')`\n ) as any[];\n\n for (const row of result ?? []) {\n const sUri = row.S ?? row.s;\n const pUri = row.P ?? row.p;\n const oUri = row.O ?? row.o;\n\n const sId = this.extractIdFromUri(sUri);\n const oId = this.extractIdFromUri(oUri);\n const relLabel = this.extractRelLabelFromUri(pUri);\n\n const key = `${sId}|${relLabel}|${oId}`;\n if (seenKeys.has(key)) continue;\n seenKeys.add(key);\n\n let subj = nodeMap.get(sId);\n let obj = nodeMap.get(oId);\n\n if (!subj) {\n subj = { id: sId, label: \"UNKNOWN\", name: sId, properties: {} };\n }\n if (!obj) {\n obj = { id: oId, label: \"UNKNOWN\", name: oId, properties: {} };\n }\n\n const rel: Relation = {\n label: relLabel,\n sourceId: sId,\n targetId: oId,\n properties: {},\n };\n\n triplets.push([subj, rel, obj]);\n }\n } catch (err: any) {\n // SPARQL may fail if KG Engine is not enabled — log but don't block\n console.warn(`[HanaPropertyGraphStore] SPARQL relation query failed (non-fatal):`, err?.message ?? err);\n }\n\n return triplets;\n }\n\n private extractIdFromUri(uri: string): string {\n // URI format: <urn:hkv:graphName:label:id> or just the content without angle brackets\n // The ID is the last segment after the label (second-to-last segment)\n const cleanUri = uri.replace(/^<|>$/g, \"\");\n const parts = cleanUri.split(\":\");\n // ID is the last part, decode it\n if (parts.length >= 2) {\n return decodeURIComponent(parts[parts.length - 1]);\n }\n return uri;\n }\n\n private extractRelLabelFromUri(uri: string): string {\n const match = uri.match(/urn:hkv:rel:(.+)>?$/);\n return match ? match[1].replace(/>$/, \"\") : uri;\n }\n\n async delete(opts: { ids: string[] }): Promise<void> {\n await this.ensureInitialized();\n if (opts.ids.length === 0) return;\n\n for (const id of opts.ids) {\n await this.exec(`DELETE FROM ${this.vectorTableName} WHERE id = ?`, [id]);\n }\n }\n\n async vectorQuery(query: VectorStoreQuery): Promise<[LabelledNode[], number[]]> {\n await this.ensureInitialized();\n\n const sql = `\n SELECT id, label, name, properties,\n COSINE_SIMILARITY(embedding, TO_REAL_VECTOR(?)) AS score\n FROM ${this.vectorTableName}\n WHERE embedding IS NOT NULL\n ORDER BY score DESC\n LIMIT ?\n `;\n\n const rows = (await this.exec(sql, [\n JSON.stringify(query.queryEmbedding),\n query.similarityTopK,\n ])) as any[];\n\n const nodes: LabelledNode[] = [];\n const scores: number[] = [];\n\n for (const row of rows ?? []) {\n nodes.push({\n id: row.ID ?? row.id,\n label: row.LABEL ?? row.label,\n name: row.NAME ?? row.name,\n properties: JSON.parse(row.PROPERTIES ?? row.properties ?? \"{}\"),\n });\n scores.push(row.SCORE ?? row.score ?? 0);\n }\n\n return [nodes, scores];\n }\n\n async upsertDocumentNodes(nodes: DocumentNode[]): Promise<void> {\n await this.ensureInitialized();\n if (nodes.length === 0) return;\n\n const uniqueNodes = this.uniqueBy(nodes, (node) => node.id);\n const docRowsWithEmbedding: unknown[][] = [];\n const docRowsWithoutEmbedding: unknown[][] = [];\n const vectorRowsWithEmbedding: unknown[][] = [];\n const vectorRowsWithoutEmbedding: unknown[][] = [];\n\n for (const node of uniqueNodes) {\n const safeMetadata = this.sanitizeRecord(node.metadata);\n const metadataJson = JSON.stringify(safeMetadata ?? {});\n const docRow = [node.id, node.text, metadataJson, node.hash ?? null];\n const vectorRow = [node.id, node.id, metadataJson];\n\n if (node.embedding) {\n const embeddingJson = JSON.stringify(node.embedding);\n docRowsWithEmbedding.push([...docRow, embeddingJson]);\n vectorRowsWithEmbedding.push([...vectorRow, embeddingJson]);\n } else {\n docRowsWithoutEmbedding.push(docRow);\n vectorRowsWithoutEmbedding.push(vectorRow);\n }\n }\n\n await this.execBatch(\n `\n UPSERT ${this.documentNodesTableName} (id, text, metadata, hash, embedding)\n VALUES (?, ?, ?, ?, TO_REAL_VECTOR(?))\n WITH PRIMARY KEY\n `,\n docRowsWithEmbedding\n );\n\n await this.execBatch(\n `\n UPSERT ${this.documentNodesTableName} (id, text, metadata, hash, embedding)\n VALUES (?, ?, ?, ?, NULL)\n WITH PRIMARY KEY\n `,\n docRowsWithoutEmbedding\n );\n\n await this.execBatch(\n `\n UPSERT ${this.vectorTableName} (id, node_type, label, name, properties, embedding)\n VALUES (?, 'document', 'DOCUMENT', ?, ?, TO_REAL_VECTOR(?))\n WITH PRIMARY KEY\n `,\n vectorRowsWithEmbedding\n );\n\n await this.execBatch(\n `\n UPSERT ${this.vectorTableName} (id, node_type, label, name, properties)\n VALUES (?, 'document', 'DOCUMENT', ?, ?)\n WITH PRIMARY KEY\n `,\n vectorRowsWithoutEmbedding\n );\n }\n\n async getDocumentNodes(ids: string[]): Promise<DocumentNode[]> {\n await this.ensureInitialized();\n if (ids.length === 0) return [];\n\n const rows = (await this.exec(\n `SELECT id, text, metadata, hash FROM ${this.documentNodesTableName} WHERE id IN (${ids.map(() => \"?\").join(\",\")})`,\n ids\n )) as any[];\n\n return (rows ?? []).map((r) => ({\n id: r.ID ?? r.id,\n text: r.TEXT ?? r.text,\n metadata: JSON.parse(r.METADATA ?? r.metadata ?? \"{}\"),\n hash: r.HASH ?? r.hash,\n }));\n }\n}\n","import type { QueryBundle, NodeWithScore } from \"./types\";\nimport type { BasePGRetriever } from \"./retrievers/base\";\n\nexport interface PGRetrieverOptions {\n subRetrievers: BasePGRetriever[];\n showProgress?: boolean;\n}\n\nexport class PGRetriever {\n private readonly subRetrievers: BasePGRetriever[];\n private readonly showProgress: boolean;\n\n constructor(options: PGRetrieverOptions) {\n this.subRetrievers = options.subRetrievers;\n this.showProgress = options.showProgress ?? false;\n }\n\n private deduplicate(nodes: NodeWithScore[]): NodeWithScore[] {\n const seen = new Set<string>();\n const deduped: NodeWithScore[] = [];\n\n for (const node of nodes) {\n if (!seen.has(node.node.text)) {\n deduped.push(node);\n seen.add(node.node.text);\n }\n }\n\n return deduped;\n }\n\n async retrieve(queryBundle: QueryBundle): Promise<NodeWithScore[]> {\n const allResults: NodeWithScore[] = [];\n\n const promises = this.subRetrievers.map((r) => r.retrieve(queryBundle));\n const results = await Promise.all(promises);\n\n for (const result of results) {\n allResults.push(...result);\n }\n\n return this.deduplicate(allResults);\n }\n}\n","import type { PropertyGraphStore } from \"../property-graph-store\";\nimport type { Triplet, NodeWithScore, QueryBundle, LabelledNode } from \"../types\";\nimport { TRIPLET_SOURCE_KEY, tripletToString } from \"../types\";\n\nexport const DEFAULT_PREAMBLE = \"Here are some facts extracted from the provided text:\\n\\n\";\n\nexport interface BasePGRetrieverOptions {\n graphStore: PropertyGraphStore;\n includeText?: boolean;\n includeTextPreamble?: string;\n includeProperties?: boolean;\n}\n\nexport abstract class BasePGRetriever {\n protected readonly graphStore: PropertyGraphStore;\n protected readonly includeText: boolean;\n protected readonly includeTextPreamble: string;\n protected readonly includeProperties: boolean;\n\n constructor(options: BasePGRetrieverOptions) {\n this.graphStore = options.graphStore;\n this.includeText = options.includeText ?? true;\n this.includeTextPreamble = options.includeTextPreamble ?? DEFAULT_PREAMBLE;\n this.includeProperties = options.includeProperties ?? false;\n }\n\n protected getNodesWithScore(triplets: Triplet[], scores?: number[]): NodeWithScore[] {\n const results: NodeWithScore[] = [];\n\n for (let i = 0; i < triplets.length; i++) {\n const triplet = triplets[i];\n const sourceId = triplet[0].properties[TRIPLET_SOURCE_KEY] as string | undefined;\n\n const text = tripletToString(triplet, this.includeProperties);\n\n results.push({\n node: {\n id: `triplet_${i}`,\n text,\n metadata: {},\n refDocId: sourceId,\n },\n score: scores?.[i] ?? 1.0,\n });\n }\n\n return results;\n }\n\n protected async addSourceText(nodes: NodeWithScore[]): Promise<NodeWithScore[]> {\n if (!this.graphStore.getDocumentNodes) {\n return nodes;\n }\n\n const refDocIds = nodes\n .map((n) => n.node.refDocId)\n .filter((id): id is string => id !== undefined);\n\n if (refDocIds.length === 0) return nodes;\n\n const ogNodes = await this.graphStore.getDocumentNodes(refDocIds);\n const nodeMap = new Map(ogNodes.map((n) => [n.id, n]));\n\n const graphNodeMap = new Map<string, string[]>();\n for (const node of nodes) {\n const refDocId = node.node.refDocId ?? \"\";\n if (!graphNodeMap.has(refDocId)) {\n graphNodeMap.set(refDocId, []);\n }\n graphNodeMap.get(refDocId)!.push(node.node.text);\n }\n\n const resultNodes: NodeWithScore[] = [];\n for (const nodeWithScore of nodes) {\n const mappedNode = nodeMap.get(nodeWithScore.node.refDocId ?? \"\");\n\n if (mappedNode) {\n const graphContent = graphNodeMap.get(mappedNode.id) ?? [];\n if (graphContent.length > 0) {\n const graphContentStr = graphContent.join(\"\\n\");\n const newContent = this.includeTextPreamble + graphContentStr + \"\\n\\n\" + mappedNode.text;\n resultNodes.push({\n node: {\n id: mappedNode.id,\n text: newContent,\n metadata: mappedNode.metadata,\n refDocId: nodeWithScore.node.refDocId,\n },\n score: nodeWithScore.score,\n });\n } else {\n resultNodes.push({\n node: {\n id: mappedNode.id,\n text: mappedNode.text,\n metadata: mappedNode.metadata,\n refDocId: nodeWithScore.node.refDocId,\n },\n score: nodeWithScore.score,\n });\n }\n } else {\n resultNodes.push(nodeWithScore);\n }\n }\n\n return resultNodes;\n }\n\n async retrieve(queryBundle: QueryBundle): Promise<NodeWithScore[]> {\n let nodes = await this.retrieveFromGraph(queryBundle);\n if (this.includeText && nodes.length > 0) {\n nodes = await this.addSourceText(nodes);\n }\n return nodes;\n }\n\n abstract retrieveFromGraph(queryBundle: QueryBundle): Promise<NodeWithScore[]>;\n}\n","import type { PropertyGraphStore, VectorStoreQuery } from \"../property-graph-store\";\nimport type { QueryBundle, NodeWithScore, LabelledNode } from \"../types\";\nimport { KG_SOURCE_REL, TRIPLET_SOURCE_KEY, STRUCT_SAME_PAGE, STRUCT_ADJACENT, STRUCT_CONTAINS } from \"../types\";\nimport { BasePGRetriever, type BasePGRetrieverOptions } from \"./base\";\n\nexport interface EmbedModel {\n getTextEmbedding(text: string): Promise<number[]>;\n getTextEmbeddingBatch(texts: string[]): Promise<number[][]>;\n}\n\nexport interface VectorContextRetrieverOptions extends BasePGRetrieverOptions {\n embedModel: EmbedModel;\n similarityTopK?: number;\n pathDepth?: number;\n limit?: number;\n similarityScore?: number;\n /** Enable cross-check boosting: boost scores when vector-matched entities link to graph facts (default: true) */\n crossCheckBoost?: boolean;\n /** Multiplier for cross-check boost (default: 1.25 = 25% boost) */\n crossCheckBoostFactor?: number;\n /** Include structural adjacency relations (ON_SAME_PAGE, ADJACENT_TO) in graph expansion (default: true) */\n includeStructuralEdges?: boolean;\n /** Depth for structural edge traversal (default: 1) */\n structuralDepth?: number;\n}\n\nexport class VectorContextRetriever extends BasePGRetriever {\n private readonly embedModel: EmbedModel;\n private readonly similarityTopK: number;\n private readonly pathDepth: number;\n private readonly limit: number;\n private readonly similarityScore?: number;\n private readonly crossCheckBoost: boolean;\n private readonly crossCheckBoostFactor: number;\n private readonly includeStructuralEdges: boolean;\n private readonly structuralDepth: number;\n\n constructor(options: VectorContextRetrieverOptions) {\n super(options);\n this.embedModel = options.embedModel;\n this.similarityTopK = options.similarityTopK ?? 4;\n this.pathDepth = options.pathDepth ?? 1;\n this.limit = options.limit ?? 30;\n this.similarityScore = options.similarityScore;\n this.crossCheckBoost = options.crossCheckBoost ?? true;\n this.crossCheckBoostFactor = options.crossCheckBoostFactor ?? 1.25;\n this.includeStructuralEdges = options.includeStructuralEdges ?? true;\n this.structuralDepth = options.structuralDepth ?? 1;\n }\n\n async retrieveFromGraph(queryBundle: QueryBundle): Promise<NodeWithScore[]> {\n let embedding = queryBundle.embedding;\n\n if (!embedding) {\n embedding = await this.embedModel.getTextEmbedding(queryBundle.queryStr);\n }\n\n const vectorQuery: VectorStoreQuery = {\n queryEmbedding: embedding,\n similarityTopK: this.similarityTopK,\n };\n\n if (!this.graphStore.supportsVectorQueries || !this.graphStore.vectorQuery) {\n throw new Error(\"Graph store does not support vector queries\");\n }\n\n const [kgNodes, scores] = await this.graphStore.vectorQuery(vectorQuery);\n\n if (kgNodes.length === 0) {\n return [];\n }\n\n // Partition vector results into document nodes and entity/other nodes\n const entityNodes: LabelledNode[] = [];\n const entityScores: number[] = [];\n const documentResults: NodeWithScore[] = [];\n\n for (let i = 0; i < kgNodes.length; i++) {\n const node = kgNodes[i];\n if (node.label === \"DOCUMENT\") {\n // Document nodes are direct results — they don't need graph traversal.\n // Use node.id as refDocId so addSourceText() can fetch full text from _NODES.\n documentResults.push({\n node: {\n id: node.id,\n text: `Document: ${node.name}`,\n metadata: node.properties ?? {},\n refDocId: node.id,\n },\n score: scores[i],\n });\n } else {\n entityNodes.push(node);\n entityScores.push(scores[i]);\n }\n }\n\n // --- Entity flow: graph traversal to find triplets ---\n let tripletResults: NodeWithScore[] = [];\n\n if (entityNodes.length > 0) {\n const kgIds = entityNodes.map((n) => n.id);\n\n // Build provenance set from vector-matched nodes for cross-check boosting\n const provenanceSet = new Set<string>();\n if (this.crossCheckBoost) {\n for (const node of entityNodes) {\n provenanceSet.add(node.id.toLowerCase());\n provenanceSet.add(node.name.toLowerCase());\n\n const props = node.properties ?? {};\n if (props.documentId) {\n provenanceSet.add(String(props.documentId).toLowerCase());\n }\n if (props.sourceChunk) {\n provenanceSet.add(String(props.sourceChunk).toLowerCase());\n }\n }\n }\n\n let triplets = await this.graphStore.getRelMap({\n nodes: entityNodes,\n depth: this.pathDepth,\n limit: this.limit,\n ignoreRels: [KG_SOURCE_REL],\n });\n\n // Structural expansion: traverse ON_SAME_PAGE, ADJACENT_TO edges from source chunks\n if (this.includeStructuralEdges) {\n const chunkIds = new Set<string>();\n for (const node of entityNodes) {\n const sourceId = node.properties?.[TRIPLET_SOURCE_KEY];\n if (sourceId) {\n chunkIds.add(`CHUNK_${String(sourceId).replace(/\\s+/g, \"_\").toUpperCase()}`);\n }\n if (node.label === \"CHUNK\") {\n chunkIds.add(node.id);\n }\n }\n\n if (chunkIds.size > 0) {\n const chunkNodes = await this.graphStore.get({ ids: Array.from(chunkIds) });\n if (chunkNodes.length > 0) {\n const structuralTriplets = await this.graphStore.getRelMap({\n nodes: chunkNodes,\n depth: this.structuralDepth,\n limit: this.limit,\n ignoreRels: [KG_SOURCE_REL],\n });\n\n const structuralRels = [STRUCT_SAME_PAGE, STRUCT_ADJACENT, STRUCT_CONTAINS];\n const structuralOnly = structuralTriplets.filter(([_s, r, _o]) =>\n structuralRels.includes(r.label)\n );\n\n const existingKeys = new Set(triplets.map(([s, r, o]) => `${s.id}|${r.label}|${o.id}`));\n for (const triplet of structuralOnly) {\n const key = `${triplet[0].id}|${triplet[1].label}|${triplet[2].id}`;\n if (!existingKeys.has(key)) {\n triplets.push(triplet);\n existingKeys.add(key);\n }\n }\n }\n }\n }\n\n const newScores: number[] = [];\n for (const triplet of triplets) {\n const idx1 = kgIds.indexOf(triplet[0].id);\n const idx2 = kgIds.indexOf(triplet[2].id);\n const score1 = idx1 >= 0 ? entityScores[idx1] : 0;\n const score2 = idx2 >= 0 ? entityScores[idx2] : 0;\n let baseScore = Math.max(score1, score2);\n\n if (this.crossCheckBoost && baseScore > 0) {\n const shouldBoost = this.checkProvenance(triplet[0], provenanceSet) ||\n this.checkProvenance(triplet[2], provenanceSet);\n if (shouldBoost) {\n baseScore = Math.min(1.0, baseScore * this.crossCheckBoostFactor);\n }\n }\n\n newScores.push(baseScore);\n }\n\n let results = triplets.map((t, i) => ({ triplet: t, score: newScores[i] }));\n\n if (this.similarityScore !== undefined) {\n results = results.filter((r) => r.score >= this.similarityScore!);\n }\n\n results.sort((a, b) => b.score - a.score);\n\n tripletResults = this.getNodesWithScore(\n results.map((r) => r.triplet),\n results.map((r) => r.score)\n );\n }\n\n // --- Merge document results and triplet results, sorted by score ---\n const allResults = [...documentResults, ...tripletResults];\n\n if (this.similarityScore !== undefined) {\n return allResults\n .filter((r) => r.score >= this.similarityScore!)\n .sort((a, b) => b.score - a.score);\n }\n\n return allResults.sort((a, b) => b.score - a.score);\n }\n\n /**\n * Check if a node has provenance linking to the vector-matched nodes.\n * Returns true if the node's properties contain a documentId or sourceChunk\n * that matches something in the provenance set.\n */\n private checkProvenance(node: LabelledNode, provenanceSet: Set<string>): boolean {\n if (provenanceSet.size === 0) return false;\n\n const props = node.properties ?? {};\n \n // Check documentId property\n if (props.documentId && provenanceSet.has(String(props.documentId).toLowerCase())) {\n return true;\n }\n \n // Check sourceChunk property\n if (props.sourceChunk && provenanceSet.has(String(props.sourceChunk).toLowerCase())) {\n return true;\n }\n \n // Check if this node itself is in the provenance set (e.g., a CHUNK node)\n if (provenanceSet.has(node.id.toLowerCase()) || provenanceSet.has(node.name.toLowerCase())) {\n return true;\n }\n\n return false;\n }\n}\n","import type { TransformComponent, TextNode } from \"./types\";\nimport type { EntityNode, Relation } from \"../types\";\nimport { KG_NODES_KEY, KG_RELATIONS_KEY, createEntityNode, createRelation } from \"../types\";\n\nexport class ImplicitPathExtractor implements TransformComponent {\n async transform(nodes: TextNode[], options?: { showProgress?: boolean }): Promise<TextNode[]> {\n const results: TextNode[] = [];\n\n for (const node of nodes) {\n const existingNodes = (node.metadata[KG_NODES_KEY] as EntityNode[]) ?? [];\n const existingRelations = (node.metadata[KG_RELATIONS_KEY] as Relation[]) ?? [];\n\n const safeMetadata: Record<string, unknown> = { ...node.metadata };\n delete safeMetadata[KG_NODES_KEY];\n delete safeMetadata[KG_RELATIONS_KEY];\n\n const chunkNode = createEntityNode({\n label: \"CHUNK\",\n name: node.id,\n properties: {\n text: node.text.slice(0, 500),\n ...safeMetadata,\n },\n });\n\n if (node.metadata.documentId) {\n const docNode = createEntityNode({\n label: \"DOCUMENT\",\n name: String(node.metadata.documentId),\n properties: {},\n });\n\n const rel = createRelation({\n label: \"FROM_DOCUMENT\",\n sourceId: chunkNode.id,\n targetId: docNode.id,\n });\n\n existingNodes.push(docNode);\n existingRelations.push(rel);\n }\n\n existingNodes.push(chunkNode);\n\n results.push({\n ...node,\n metadata: {\n ...node.metadata,\n [KG_NODES_KEY]: existingNodes,\n [KG_RELATIONS_KEY]: existingRelations,\n },\n });\n }\n\n return results;\n }\n}\n","import type { PropertyGraphStore } from \"./property-graph-store\";\nimport type { TransformComponent, TextNode } from \"./extractors/types\";\nimport type { EntityNode, Relation, NodeWithScore, QueryBundle } from \"./types\";\nimport { KG_NODES_KEY, KG_RELATIONS_KEY, TRIPLET_SOURCE_KEY } from \"./types\";\nimport { PGRetriever } from \"./pg-retriever\";\nimport { VectorContextRetriever, type EmbedModel } from \"./retrievers/vector-context\";\nimport { ImplicitPathExtractor } from \"./extractors/implicit\";\nimport { createHash } from \"crypto\";\nimport { get_encoding } from \"tiktoken\";\n\n/** Default token limit with safety buffer for text-embedding-3-small (8 192 token context). */\nconst DEFAULT_MAX_EMBED_TOKENS = 8_000;\n\nexport interface PropertyGraphIndexOptions {\n propertyGraphStore: PropertyGraphStore;\n kgExtractors?: TransformComponent[];\n embedModel?: EmbedModel;\n embedKgNodes?: boolean;\n showProgress?: boolean;\n /** Maximum tokens to send to the embedding model per text.\n * Texts exceeding this limit are truncated using tiktoken (cl100k_base encoding).\n * Default: 8 000 (safe for 8 192-token models like text-embedding-3-small). */\n maxEmbedTokens?: number;\n}\n\nexport class PropertyGraphIndex {\n private readonly propertyGraphStore: PropertyGraphStore;\n private readonly kgExtractors: TransformComponent[];\n private readonly embedModel?: EmbedModel;\n private readonly embedKgNodes: boolean;\n private readonly showProgress: boolean;\n private readonly maxEmbedTokens: number;\n\n constructor(options: PropertyGraphIndexOptions) {\n this.propertyGraphStore = options.propertyGraphStore;\n this.kgExtractors = options.kgExtractors ?? [new ImplicitPathExtractor()];\n this.embedModel = options.embedModel;\n this.embedKgNodes = options.embedKgNodes ?? true;\n this.showProgress = options.showProgress ?? false;\n this.maxEmbedTokens = options.maxEmbedTokens ?? DEFAULT_MAX_EMBED_TOKENS;\n }\n\n get graphStore(): PropertyGraphStore {\n return this.propertyGraphStore;\n }\n\n static fromExisting(options: PropertyGraphIndexOptions): PropertyGraphIndex {\n return new PropertyGraphIndex(options);\n }\n\n private computeHash(text: string): string {\n return createHash(\"md5\").update(text).digest(\"hex\");\n }\n\n private truncateForEmbedding(text: string): string {\n const enc = get_encoding(\"cl100k_base\");\n try {\n const tokens = enc.encode(text);\n if (tokens.length <= this.maxEmbedTokens) return text;\n const truncated = tokens.slice(0, this.maxEmbedTokens);\n return new TextDecoder().decode(enc.decode(truncated));\n } finally {\n enc.free();\n }\n }\n\n async insert(nodes: TextNode[]): Promise<TextNode[]> {\n if (nodes.length === 0) return [];\n\n let processedNodes = nodes;\n for (const extractor of this.kgExtractors) {\n processedNodes = await extractor.transform(processedNodes, {\n showProgress: this.showProgress,\n });\n }\n\n const hasExtractors = this.kgExtractors.length > 0;\n\n const kgNodesToInsert: EntityNode[] = [];\n const kgRelsToInsert: Relation[] = [];\n let newKgNodes: EntityNode[] = [];\n\n if (hasExtractors) {\n for (const node of processedNodes) {\n if (!node.metadata[KG_NODES_KEY] && !node.metadata[KG_RELATIONS_KEY]) {\n throw new Error(`Node ${node.id} has no KG_NODES_KEY or KG_RELATIONS_KEY after extraction`);\n }\n }\n\n for (const node of processedNodes) {\n const kgNodes = (node.metadata[KG_NODES_KEY] as EntityNode[]) ?? [];\n const kgRels = (node.metadata[KG_RELATIONS_KEY] as Relation[]) ?? [];\n\n for (const kgNode of kgNodes) {\n kgNode.properties[TRIPLET_SOURCE_KEY] = node.id;\n }\n for (const kgRel of kgRels) {\n kgRel.properties[TRIPLET_SOURCE_KEY] = node.id;\n }\n\n kgNodesToInsert.push(...kgNodes);\n kgRelsToInsert.push(...kgRels);\n }\n\n const kgNodeIds = [...new Set(kgNodesToInsert.map((n) => n.id))];\n const existingKgNodes = await this.propertyGraphStore.get({ ids: kgNodeIds });\n const existingKgNodeIds = new Set(existingKgNodes.map((n) => n.id));\n newKgNodes = kgNodesToInsert.filter((n) => !existingKgNodeIds.has(n.id));\n }\n\n if (this.propertyGraphStore.getDocumentNodes) {\n const existingDocumentNodes = await this.propertyGraphStore.getDocumentNodes(\n processedNodes.map((n) => n.id)\n );\n const existingHashes = new Set(existingDocumentNodes.map((n) => n.hash));\n processedNodes = processedNodes.filter((n) => {\n const hash = this.computeHash(n.text);\n n.hash = hash;\n return !existingHashes.has(hash);\n });\n }\n\n if (this.embedModel && processedNodes.length > 0) {\n const nodeTexts = processedNodes.map((n) => this.truncateForEmbedding(n.text));\n const embeddings = await this.embedModel.getTextEmbeddingBatch(nodeTexts);\n for (let i = 0; i < processedNodes.length; i++) {\n processedNodes[i].embedding = embeddings[i];\n }\n }\n\n if (this.embedKgNodes && this.embedModel && newKgNodes.length > 0) {\n const kgNodeTexts = newKgNodes.map((n) => `${n.label}: ${n.name}`);\n const kgEmbeddings = await this.embedModel.getTextEmbeddingBatch(kgNodeTexts);\n for (let i = 0; i < newKgNodes.length; i++) {\n newKgNodes[i].embedding = kgEmbeddings[i];\n }\n }\n\n if (this.propertyGraphStore.upsertDocumentNodes && processedNodes.length > 0) {\n await this.propertyGraphStore.upsertDocumentNodes(\n processedNodes.map((n) => ({\n id: n.id,\n text: n.text,\n metadata: n.metadata,\n embedding: n.embedding,\n hash: n.hash,\n }))\n );\n }\n\n if (newKgNodes.length > 0) {\n await this.propertyGraphStore.upsertNodes(newKgNodes);\n }\n\n if (kgRelsToInsert.length > 0) {\n await this.propertyGraphStore.upsertRelations(kgRelsToInsert);\n }\n\n return processedNodes;\n }\n\n async delete(nodeIds: string[]): Promise<void> {\n await this.propertyGraphStore.delete({ ids: nodeIds });\n }\n\n asRetriever(options?: {\n subRetrievers?: Array<{ retrieve(query: QueryBundle): Promise<NodeWithScore[]> }>;\n includeText?: boolean;\n /** Number of top similar nodes to retrieve via vector search (default: 4) */\n similarityTopK?: number;\n /** Graph traversal depth from matched nodes (default: 1) */\n pathDepth?: number;\n /** Maximum number of triplets to return after expansion (default: 30) */\n limit?: number;\n /** Minimum similarity score threshold to include results (default: no threshold) */\n similarityScore?: number;\n /** Enable cross-check boosting: boost scores when vector-matched entities link to graph facts (default: true) */\n crossCheckBoost?: boolean;\n /** Multiplier for cross-check boost (default: 1.25 = 25% boost) */\n crossCheckBoostFactor?: number;\n /** Include structural adjacency relations (ON_SAME_PAGE, ADJACENT_TO) in graph expansion (default: true) */\n includeStructuralEdges?: boolean;\n /** Depth for structural edge traversal (default: 1) */\n structuralDepth?: number;\n }): PGRetriever {\n let subRetrievers = options?.subRetrievers as any[];\n\n if (!subRetrievers && this.embedModel && this.propertyGraphStore.supportsVectorQueries) {\n subRetrievers = [\n new VectorContextRetriever({\n graphStore: this.propertyGraphStore,\n embedModel: this.embedModel,\n includeText: options?.includeText ?? true,\n similarityTopK: options?.similarityTopK,\n pathDepth: options?.pathDepth,\n limit: options?.limit,\n similarityScore: options?.similarityScore,\n crossCheckBoost: options?.crossCheckBoost,\n crossCheckBoostFactor: options?.crossCheckBoostFactor,\n includeStructuralEdges: options?.includeStructuralEdges,\n structuralDepth: options?.structuralDepth,\n }),\n ];\n }\n\n if (!subRetrievers) {\n subRetrievers = [];\n }\n\n return new PGRetriever({ subRetrievers });\n }\n\n async query(queryStr: string, options?: {\n /** Number of top similar nodes to retrieve via vector search (default: 4) */\n similarityTopK?: number;\n /** Graph traversal depth from matched nodes (default: 1) */\n pathDepth?: number;\n /** Maximum number of triplets to return after expansion (default: 30) */\n limit?: number;\n /** Minimum similarity score threshold to include results (default: no threshold) */\n similarityScore?: number;\n /** Enable cross-check boosting: boost scores when vector-matched entities link to graph facts (default: true) */\n crossCheckBoost?: boolean;\n /** Multiplier for cross-check boost (default: 1.25 = 25% boost) */\n crossCheckBoostFactor?: number;\n /** Include structural adjacency relations (ON_SAME_PAGE, ADJACENT_TO) in graph expansion (default: true) */\n includeStructuralEdges?: boolean;\n /** Depth for structural edge traversal (default: 1) */\n structuralDepth?: number;\n }): Promise<NodeWithScore[]> {\n const retriever = this.asRetriever(options);\n return retriever.retrieve({ queryStr });\n }\n}\n","import { z } from \"zod\";\nimport type { TransformComponent, TextNode, SchemaDefinition } from \"./types\";\nimport type { EntityNode, Relation, Triplet } from \"../types\";\nimport { KG_NODES_KEY, KG_RELATIONS_KEY, createEntityNode, createRelation } from \"../types\";\n\n/** Minimal contract for a schema — compatible with Zod 3, Zod 4, or any parse-capable object. */\nexport interface Parseable<T> {\n parse(data: unknown): T;\n}\n\nexport interface LLMClient {\n structuredPredict<T>(schema: Parseable<T>, prompt: string): Promise<T>;\n}\n\nexport interface SchemaLLMPathExtractorOptions {\n llm: LLMClient;\n schema: SchemaDefinition;\n maxTripletsPerChunk?: number;\n strict?: boolean;\n extractPromptTemplate?: string;\n}\n\nconst DEFAULT_EXTRACT_PROMPT = `Given the following text, extract a knowledge graph according to the provided schema.\nExtract up to {maxTriplets} triplets. If fewer are present in the text, return fewer. Do not invent or pad triplets.\n\nSchema:\n- Entity types: {entityTypes}\n- Relation types: {relationTypes}\n- Valid relationships: {validationSchema}\n\nText:\n-------\n{text}\n-------\n\nExtract entities and relationships from the text above.\nReturn your answer as a JSON object with a \"triplets\" array. Each triplet should have:\n- \"subject\": { \"name\": string, \"type\": one of [{entityTypes}] }\n- \"relation\": { \"type\": one of [{relationTypes}] }\n- \"object\": { \"name\": string, \"type\": one of [{entityTypes}] }\n\nExample output format:\n{\n \"triplets\": [\n {\n \"subject\": { \"name\": \"John\", \"type\": \"PERSON\" },\n \"relation\": { \"type\": \"WORKS_AT\" },\n \"object\": { \"name\": \"Acme Corp\", \"type\": \"ORGANIZATION\" }\n }\n ]\n}`;\n\nexport class SchemaLLMPathExtractor implements TransformComponent {\n private readonly llm: LLMClient;\n private readonly schema: SchemaDefinition;\n private readonly maxTripletsPerChunk: number;\n private readonly strict: boolean;\n private readonly extractPromptTemplate: string;\n private readonly tripletSchema: z.ZodType<any>;\n private readonly rawTripletSchema: z.ZodType<any>;\n\n constructor(options: SchemaLLMPathExtractorOptions) {\n this.llm = options.llm;\n this.schema = options.schema;\n this.maxTripletsPerChunk = options.maxTripletsPerChunk ?? 10;\n this.strict = options.strict ?? true;\n this.extractPromptTemplate = options.extractPromptTemplate ?? DEFAULT_EXTRACT_PROMPT;\n\n const entityTypeEnum = z.enum(this.schema.entityTypes as [string, ...string[]]);\n const relationTypeEnum = z.enum(this.schema.relationTypes as [string, ...string[]]);\n\n const entitySchema = z.object({\n name: z.string(),\n type: entityTypeEnum,\n properties: z.record(z.unknown()).optional(),\n });\n\n const relationSchema = z.object({\n type: relationTypeEnum,\n properties: z.record(z.unknown()).optional(),\n });\n\n const tripletSchema = z.object({\n subject: entitySchema,\n relation: relationSchema,\n object: entitySchema,\n });\n\n this.tripletSchema = z.object({\n triplets: z.array(tripletSchema),\n });\n\n this.rawTripletSchema = z.object({\n triplets: z.array(z.any()),\n });\n }\n\n private normalizeType(type: string): string {\n return String(type).toUpperCase().replace(/\\s+/g, \"_\");\n }\n\n private buildPrompt(text: string): string {\n const validationSchemaStr = this.schema.validationSchema\n ?.map(([s, r, o]) => `(${s})-[${r}]->(${o})`)\n .join(\", \") ?? \"Any valid combination\";\n\n return this.extractPromptTemplate\n .replace(\"{maxTriplets}\", String(this.maxTripletsPerChunk))\n .replace(\"{entityTypes}\", this.schema.entityTypes.join(\", \"))\n .replace(\"{relationTypes}\", this.schema.relationTypes.join(\", \"))\n .replace(\"{validationSchema}\", validationSchemaStr)\n .replace(\"{text}\", text);\n }\n\n private isValidTriplet(subjectType: string, relationType: string, objectType: string): boolean {\n if (!this.strict || !this.schema.validationSchema) {\n return true;\n }\n\n const subj = this.normalizeType(subjectType);\n const rel = this.normalizeType(relationType);\n const obj = this.normalizeType(objectType);\n\n return this.schema.validationSchema.some(\n ([s, r, o]) =>\n this.normalizeType(s) === subj &&\n this.normalizeType(r) === rel &&\n this.normalizeType(o) === obj\n );\n }\n\n private pruneInvalidTriplets(\n extracted: { triplets: Array<{ subject: any; relation: any; object: any }> }\n ): Triplet[] {\n const validTriplets: Triplet[] = [];\n\n const allowedEntityTypes = new Set(this.schema.entityTypes.map((t) => this.normalizeType(t)));\n const allowedRelationTypes = new Set(this.schema.relationTypes.map((t) => this.normalizeType(t)));\n\n for (const triplet of extracted.triplets) {\n const subject = (triplet as any)?.subject;\n const relation = (triplet as any)?.relation;\n const object = (triplet as any)?.object;\n\n if (!subject || !relation || !object) continue;\n if (typeof subject.type !== \"string\" || typeof subject.name !== \"string\") continue;\n if (typeof relation.type !== \"string\") continue;\n if (typeof object.type !== \"string\" || typeof object.name !== \"string\") continue;\n\n const subjectType = this.normalizeType(subject.type);\n const relationType = this.normalizeType(relation.type);\n const objectType = this.normalizeType(object.type);\n\n if (this.strict) {\n if (!allowedEntityTypes.has(subjectType)) continue;\n if (!allowedRelationTypes.has(relationType)) continue;\n if (!allowedEntityTypes.has(objectType)) continue;\n }\n\n if (!this.isValidTriplet(subjectType, relationType, objectType)) {\n continue;\n }\n\n const subjectName = String(triplet.subject.name).trim();\n const objectName = String(triplet.object.name).trim();\n\n if (subjectName.toLowerCase() === objectName.toLowerCase()) {\n continue;\n }\n\n const subj = createEntityNode({\n label: subjectType,\n name: subjectName,\n properties: triplet.subject.properties ?? {},\n });\n\n const obj = createEntityNode({\n label: objectType,\n name: objectName,\n properties: triplet.object.properties ?? {},\n });\n\n const rel = createRelation({\n label: relationType,\n sourceId: subj.id,\n targetId: obj.id,\n properties: triplet.relation.properties ?? {},\n });\n\n validTriplets.push([subj, rel, obj]);\n }\n\n return validTriplets;\n }\n\n private async extractFromNode(node: TextNode): Promise<TextNode> {\n const prompt = this.buildPrompt(node.text);\n\n let triplets: Triplet[] = [];\n\n try {\n const result = await this.llm.structuredPredict(this.rawTripletSchema, prompt);\n triplets = this.pruneInvalidTriplets(result);\n\n if (this.maxTripletsPerChunk > 0 && triplets.length > this.maxTripletsPerChunk) {\n triplets = triplets.slice(0, this.maxTripletsPerChunk);\n }\n } catch (err) {\n // Log extraction errors for debugging\n console.warn(`[SchemaLLMPathExtractor] Failed to extract from node ${node.id}:`, err);\n triplets = [];\n }\n\n const existingNodes = (node.metadata[KG_NODES_KEY] as EntityNode[]) ?? [];\n const existingRelations = (node.metadata[KG_RELATIONS_KEY] as Relation[]) ?? [];\n\n // Avoid copying kg_nodes/kg_relations into node properties (can create circular refs)\n const safeMetadata: Record<string, unknown> = { ...node.metadata };\n delete safeMetadata[KG_NODES_KEY];\n delete safeMetadata[KG_RELATIONS_KEY];\n\n for (const [subj, rel, obj] of triplets) {\n subj.properties = { ...subj.properties, ...safeMetadata };\n obj.properties = { ...obj.properties, ...safeMetadata };\n rel.properties = { ...rel.properties, ...safeMetadata };\n\n existingNodes.push(subj);\n existingNodes.push(obj);\n existingRelations.push(rel);\n }\n\n return {\n ...node,\n metadata: {\n ...node.metadata,\n [KG_NODES_KEY]: existingNodes,\n [KG_RELATIONS_KEY]: existingRelations,\n },\n };\n }\n\n async transform(nodes: TextNode[], options?: { showProgress?: boolean }): Promise<TextNode[]> {\n const results: TextNode[] = [];\n\n for (const node of nodes) {\n const processed = await this.extractFromNode(node);\n results.push(processed);\n }\n\n return results;\n }\n}\n","import type { TransformComponent, TextNode } from \"./types\";\nimport type { Relation } from \"../types\";\nimport { KG_RELATIONS_KEY, STRUCT_SAME_PAGE, STRUCT_ADJACENT } from \"../types\";\n\nexport interface AdjacencyLinkerOptions {\n /**\n * Link chunks that share the same pageNumber in metadata.\n * Default: true\n */\n linkSamePage?: boolean;\n\n /**\n * Link chunks that are adjacent by chunkIndex in metadata.\n * Default: true\n */\n linkAdjacent?: boolean;\n\n /**\n * Maximum chunk index distance for ADJACENT_TO links.\n * Default: 1 (immediate neighbors only)\n */\n adjacentDistance?: number;\n\n /**\n * Only create cross-type links (e.g., text↔image), not text↔text.\n * Default: false (link all)\n */\n crossTypeOnly?: boolean;\n}\n\nexport class AdjacencyLinker implements TransformComponent {\n private linkSamePage: boolean;\n private linkAdjacent: boolean;\n private adjacentDistance: number;\n private crossTypeOnly: boolean;\n\n constructor(options: AdjacencyLinkerOptions = {}) {\n this.linkSamePage = options.linkSamePage ?? true;\n this.linkAdjacent = options.linkAdjacent ?? true;\n this.adjacentDistance = options.adjacentDistance ?? 1;\n this.crossTypeOnly = options.crossTypeOnly ?? false;\n }\n\n /**\n * Transform is called AFTER other extractors (SchemaLLMPathExtractor, ImplicitPathExtractor).\n * It adds structural relations between chunks based on metadata.\n */\n async transform(nodes: TextNode[], options?: { showProgress?: boolean }): Promise<TextNode[]> {\n if (nodes.length < 2) return nodes;\n\n // Group nodes by documentId\n const byDocument = new Map<string, TextNode[]>();\n for (const node of nodes) {\n const docId = String(node.metadata.documentId ?? \"unknown\");\n if (!byDocument.has(docId)) byDocument.set(docId, []);\n byDocument.get(docId)!.push(node);\n }\n\n // For each document, create structural links\n // Map from node.id to relations that should be added to that node\n const nodeRelationsMap = new Map<string, Relation[]>();\n\n for (const [docId, docNodes] of byDocument) {\n // Group by page\n const byPage = new Map<number, TextNode[]>();\n for (const node of docNodes) {\n const page = Number(node.metadata.pageNumber ?? 0);\n if (!byPage.has(page)) byPage.set(page, []);\n byPage.get(page)!.push(node);\n }\n\n // Same-page links\n if (this.linkSamePage) {\n for (const [page, pageNodes] of byPage) {\n if (pageNodes.length < 2) continue;\n for (let i = 0; i < pageNodes.length; i++) {\n for (let j = i + 1; j < pageNodes.length; j++) {\n const a = pageNodes[i];\n const b = pageNodes[j];\n if (this.shouldLink(a, b)) {\n this.addRelation(nodeRelationsMap, a.id, b.id, STRUCT_SAME_PAGE, { pageNumber: page, documentId: docId });\n }\n }\n }\n }\n }\n\n // Adjacent chunk links (by chunkIndex)\n if (this.linkAdjacent) {\n // Sort by chunkIndex\n const sorted = [...docNodes].sort((a, b) => {\n const idxA = Number(a.metadata.chunkIndex ?? 0);\n const idxB = Number(b.metadata.chunkIndex ?? 0);\n return idxA - idxB;\n });\n\n for (let i = 0; i < sorted.length; i++) {\n for (let d = 1; d <= this.adjacentDistance && i + d < sorted.length; d++) {\n const a = sorted[i];\n const b = sorted[i + d];\n if (this.shouldLink(a, b)) {\n this.addRelation(nodeRelationsMap, a.id, b.id, STRUCT_ADJACENT, { distance: d, documentId: docId });\n }\n }\n }\n }\n }\n\n // Merge relations into node metadata\n return nodes.map((node) => {\n const existingRelations = (node.metadata[KG_RELATIONS_KEY] as Relation[]) ?? [];\n const newRelations = nodeRelationsMap.get(node.id) ?? [];\n\n return {\n ...node,\n metadata: {\n ...node.metadata,\n [KG_RELATIONS_KEY]: [...existingRelations, ...newRelations],\n },\n };\n });\n }\n\n private shouldLink(a: TextNode, b: TextNode): boolean {\n if (!this.crossTypeOnly) return true;\n const typeA = String(a.metadata.contentType ?? \"text\");\n const typeB = String(b.metadata.contentType ?? \"text\");\n return typeA !== typeB;\n }\n\n private addRelation(\n map: Map<string, Relation[]>,\n sourceNodeId: string,\n targetNodeId: string,\n label: string,\n properties: Record<string, unknown>\n ): void {\n // Create CHUNK entity IDs (matching ImplicitPathExtractor format)\n const sourceId = `CHUNK_${sourceNodeId.replace(/\\s+/g, \"_\").toUpperCase()}`;\n const targetId = `CHUNK_${targetNodeId.replace(/\\s+/g, \"_\").toUpperCase()}`;\n\n const rel: Relation = {\n id: `${sourceId}_${label}_${targetId}`,\n label,\n sourceId,\n targetId,\n properties,\n };\n\n // Add to source node\n if (!map.has(sourceNodeId)) map.set(sourceNodeId, []);\n map.get(sourceNodeId)!.push(rel);\n\n // Bidirectional: also add reverse to target node\n const reverseRel: Relation = {\n id: `${targetId}_${label}_${sourceId}`,\n label,\n sourceId: targetId,\n targetId: sourceId,\n properties,\n };\n\n if (!map.has(targetNodeId)) map.set(targetNodeId, []);\n map.get(targetNodeId)!.push(reverseRel);\n }\n}\n"],"mappings":"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","names":["z","z"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hana-kgvector",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "description": "A TypeScript framework for building hybrid GraphRAG applications using SAP HANA Cloud as the unified backend for knowledge graphs (RDF) and vector embeddings",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -31,13 +31,6 @@
31
31
  "url": "https://github.com/skye0402/hana-kgvector/issues"
32
32
  },
33
33
  "homepage": "https://github.com/skye0402/hana-kgvector#readme",
34
- "scripts": {
35
- "build": "tsup",
36
- "dev": "tsx src/index.ts",
37
- "phase0:litellm": "tsx scripts/validate-litellm.ts",
38
- "phase0:hana": "tsx scripts/validate-hana.ts",
39
- "smoke:pg": "tsx scripts/smoke-property-graph.ts"
40
- },
41
34
  "dependencies": {
42
35
  "openai": "^4.104.0",
43
36
  "tiktoken": "^1.0.22",
@@ -52,5 +45,12 @@
52
45
  "tsup": "^8.5.1",
53
46
  "tsx": "^4.21.0",
54
47
  "typescript": "^5.9.3"
48
+ },
49
+ "scripts": {
50
+ "build": "tsup",
51
+ "dev": "tsx src/index.ts",
52
+ "phase0:litellm": "tsx scripts/validate-litellm.ts",
53
+ "phase0:hana": "tsx scripts/validate-hana.ts",
54
+ "smoke:pg": "tsx scripts/smoke-property-graph.ts"
55
55
  }
56
- }
56
+ }