kirograph 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +4 -3
  2. package/dist/bin/commands/data.js +52 -0
  3. package/dist/bin/commands/data.js.map +2 -2
  4. package/dist/bin/commands/docs.js +2 -1
  5. package/dist/bin/commands/docs.js.map +2 -2
  6. package/dist/bin/commands/gain.js +21 -1
  7. package/dist/bin/commands/gain.js.map +2 -2
  8. package/dist/bin/commands/status.js +23 -1
  9. package/dist/bin/commands/status.js.map +2 -2
  10. package/dist/bin/installer/config-prompt.js +30 -1
  11. package/dist/bin/installer/config-prompt.js.map +2 -2
  12. package/dist/bin/installer/index.js +137 -0
  13. package/dist/bin/installer/index.js.map +2 -2
  14. package/dist/bin/kirograph.js +1 -1
  15. package/dist/config.js +19 -2
  16. package/dist/config.js.map +2 -2
  17. package/dist/core/pipeline.js +3 -1
  18. package/dist/core/pipeline.js.map +2 -2
  19. package/dist/data/indexer.js +4 -4
  20. package/dist/data/indexer.js.map +2 -2
  21. package/dist/data/linker.js +9 -5
  22. package/dist/data/linker.js.map +2 -2
  23. package/dist/data/linker.test.js +60 -0
  24. package/dist/data/linker.test.js.map +7 -0
  25. package/dist/data/parsers/__fixtures__/pdf-inspector-stub.js +41 -0
  26. package/dist/data/parsers/__fixtures__/pdf-inspector-stub.js.map +7 -0
  27. package/dist/data/parsers/index.js +3 -1
  28. package/dist/data/parsers/index.js.map +2 -2
  29. package/dist/data/parsers/pdf.js +95 -0
  30. package/dist/data/parsers/pdf.js.map +7 -0
  31. package/dist/data/parsers/pdf.test.js +125 -0
  32. package/dist/data/parsers/pdf.test.js.map +7 -0
  33. package/dist/data/queries.js +39 -2
  34. package/dist/data/queries.js.map +2 -2
  35. package/dist/data/types.js.map +1 -1
  36. package/dist/db/data-schema.sql +2 -1
  37. package/dist/db/database.js +7 -0
  38. package/dist/db/database.js.map +2 -2
  39. package/dist/docs/indexer.js +2 -1
  40. package/dist/docs/indexer.js.map +2 -2
  41. package/dist/docs/queries.js +2 -1
  42. package/dist/docs/queries.js.map +2 -2
  43. package/dist/docs/vectors.js +46 -5
  44. package/dist/docs/vectors.js.map +2 -2
  45. package/dist/extraction/extractor.js +26 -3
  46. package/dist/extraction/extractor.js.map +2 -2
  47. package/dist/extraction/grammars.js +1 -0
  48. package/dist/extraction/grammars.js.map +2 -2
  49. package/dist/extraction/languages.js +5 -1
  50. package/dist/extraction/languages.js.map +2 -2
  51. package/dist/index.js +1 -0
  52. package/dist/index.js.map +2 -2
  53. package/dist/mcp/tools.js +1 -1
  54. package/dist/mcp/tools.js.map +2 -2
  55. package/dist/memory/index.js +2 -1
  56. package/dist/memory/index.js.map +2 -2
  57. package/dist/memory/vectors.js +52 -8
  58. package/dist/memory/vectors.js.map +3 -3
  59. package/dist/patterns/library/code-eval-elixir.yaml +11 -0
  60. package/dist/patterns/library/command-injection-c.yaml +11 -0
  61. package/dist/patterns/library/command-injection-go.yaml +21 -3
  62. package/dist/patterns/library/command-injection-kotlin.yaml +10 -0
  63. package/dist/patterns/library/command-injection-php.yaml +11 -0
  64. package/dist/patterns/library/command-injection-ruby.yaml +11 -0
  65. package/dist/patterns/library/command-injection-rust.yaml +11 -0
  66. package/dist/patterns/library/dangerous-eval-bash.yaml +11 -0
  67. package/dist/patterns/library/dangerous-eval-php.yaml +11 -0
  68. package/dist/patterns/library/dangerous-eval-ruby.yaml +11 -0
  69. package/dist/patterns/library/dynamic-load-lua.yaml +10 -0
  70. package/dist/patterns/library/os-cmd-elixir.yaml +16 -0
  71. package/dist/patterns/library/os-execute-lua.yaml +10 -0
  72. package/dist/patterns/library/path-traversal-go.yaml +4 -4
  73. package/dist/patterns/library/process-launch-swift.yaml +11 -0
  74. package/dist/patterns/library/process-run-dart.yaml +15 -0
  75. package/dist/patterns/library/source-injection-bash.yaml +11 -0
  76. package/dist/patterns/library/sql-injection-dart.yaml +9 -0
  77. package/dist/patterns/library/sql-injection-kotlin.yaml +11 -0
  78. package/dist/patterns/library/sql-injection-php.yaml +9 -0
  79. package/dist/patterns/library/sql-injection-swift.yaml +11 -0
  80. package/dist/patterns/library/subshell-injection-ruby.yaml +8 -0
  81. package/dist/patterns/library/unsafe-block-rust.yaml +8 -0
  82. package/dist/patterns/library/unsafe-string-c.yaml +11 -0
  83. package/dist/patterns/runner.js +45 -27
  84. package/dist/patterns/runner.js.map +2 -2
  85. package/dist/security/integrator.js +8 -8
  86. package/dist/security/integrator.js.map +2 -2
  87. package/dist/types.js.map +2 -2
  88. package/dist/vectors/index.js +66 -3
  89. package/dist/vectors/index.js.map +2 -2
  90. package/dist/vectors/turboquant-index.js +261 -0
  91. package/dist/vectors/turboquant-index.js.map +7 -0
  92. package/dist/vectors/turbovec-index.js +189 -0
  93. package/dist/vectors/turbovec-index.js.map +7 -0
  94. package/package.json +26 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/vectors/index.ts"],
4
- "sourcesContent": ["/**\n * KiroGraph Vector Manager\n *\n * Semantic search via configurable local embedding models using @huggingface/transformers v3.\n * Mirrors CodeGraph src/vectors/ (embedder.ts + manager.ts) adapted for KiroGraph:\n * - Cache dir: ~/.kirograph/models/ (not ~/.codegraph/models/)\n * - Embeddings stored in the `vectors` SQLite table\n * - Cosine similarity search done in-process by default (no extra deps)\n * - ANN search via sqlite-vec opt-in: set config.useVecIndex = true\n * (requires `npm install better-sqlite3 sqlite-vec`)\n * - Disabled by default; opt-in via config.enableEmbeddings = true\n */\n\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { homedir } from 'os';\nimport { logDebug, logWarn, logError } from '../errors';\nimport type { KiroGraphConfig } from '../config';\nimport type { Node } from '../types';\nimport type { GraphDatabase } from '../db/database';\nimport { VecIndex } from './vec-index';\nimport { OramaIndex } from './orama-index';\nimport { PGliteIndex } from './pglite-index';\nimport { LanceDBIndex } from './lancedb-index';\nimport { QdrantIndex } from './qdrant-index';\nimport { TypesenseIndex } from './typesense-index';\n\n// \u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst DEFAULT_MODEL = 'nomic-ai/nomic-embed-text-v1.5';\nconst DEFAULT_EMBEDDING_DIM = 768;\nconst GLOBAL_MODELS_DIR = path.join(homedir(), '.kirograph', 'models');\nconst BATCH_SIZE = 32;\nconst MIN_BATCH_SIZE = 4;\nconst MAX_TOKEN_CHARS = 2000;\n\n/** Node kinds worth embedding \u2014 high information density */\nconst EMBEDDABLE_KINDS = new Set<Node['kind']>([\n 'function', 'method', 'class', 'interface', 'type_alias', 'component', 'module',\n]);\n\n// \u2500\u2500 Embedder \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype Pipeline = any;\nlet transformers: typeof import('@huggingface/transformers') | null = null;\n\nasync function getTransformers() {\n if (!transformers) {\n transformers = await import('@huggingface/transformers');\n }\n return transformers;\n}\n\n/**\n * Build a searchable text representation of a node.\n * Mirrors CodeGraph TextEmbedder.createNodeText().\n * Truncated to MAX_TOKEN_CHARS to stay within the model's memory-safe token limit.\n * Priority: kind + name > qualified name > file path > signature > docstring.\n * This ensures the most important search signal is always preserved.\n */\nfunction nodeToText(node: Node): string {\n const parts = [`${node.kind}: ${node.name}`];\n if (node.qualifiedName && node.qualifiedName !== node.name) {\n parts.push(`path: ${node.qualifiedName}`);\n }\n parts.push(`file: ${node.filePath}`);\n if (node.signature) parts.push(`signature: ${node.signature}`);\n if (node.docstring) parts.push(`documentation: ${node.docstring}`);\n const text = parts.join('\\n');\n if (text.length > MAX_TOKEN_CHARS) {\n logDebug(`nodeToText: truncated ${node.id} from ${text.length} to ${MAX_TOKEN_CHARS} chars`);\n return text.slice(0, MAX_TOKEN_CHARS);\n }\n return text;\n}\n\n/** Cosine similarity between two equal-length Float32Arrays. */\nfunction cosine(a: Float32Array, b: Float32Array): number {\n let dot = 0, na = 0, nb = 0;\n for (let i = 0; i < a.length; i++) {\n dot += a[i]! * b[i]!;\n na += a[i]! * a[i]!;\n nb += b[i]! * b[i]!;\n }\n const denom = Math.sqrt(na) * Math.sqrt(nb);\n return denom === 0 ? 0 : dot / denom;\n}\n\n/** Extract a flat Float32Array from the transformer pipeline output. */\nfunction toFloat32Array(data: unknown): Float32Array {\n if (data instanceof Float32Array) return data;\n if (Array.isArray(data)) return new Float32Array(data);\n if (data && typeof data === 'object' && 'length' in data) {\n return Float32Array.from(Array.from(data as ArrayLike<number>));\n }\n throw new Error('Unsupported embedding data format');\n}\n\n// \u2500\u2500 VectorManager \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class VectorManager {\n private pipeline: Pipeline | null = null;\n private _initialized = false;\n private vecIndex: VecIndex | null = null;\n private oramaIndex: OramaIndex | null = null;\n private pgliteIndex: PGliteIndex | null = null;\n private lancedbIndex: LanceDBIndex | null = null;\n private qdrantIndex: QdrantIndex | null = null;\n private typesenseIndex: TypesenseIndex | null = null;\n private _engineFallback: string | null = null;\n\n constructor(\n private readonly db: GraphDatabase,\n private readonly config: KiroGraphConfig,\n private readonly projectRoot?: string,\n ) {}\n\n isInitialized(): boolean {\n return this.config.enableEmbeddings === true && this._initialized;\n }\n\n /** Non-null when the configured engine failed to load and cosine is being used instead. */\n getEngineFallback(): string | null {\n return this._engineFallback;\n }\n\n /**\n * Load the embedding model. No-op when embeddings are disabled.\n * Fails silently so callers can continue without semantic search.\n * When config.useVecIndex is true, also initializes the sqlite-vec ANN index.\n */\n async initialize(onProgress?: (file: string, loaded: number, total: number, done: boolean) => void): Promise<void> {\n if (!this.config.enableEmbeddings) {\n logDebug('VectorManager: embeddings disabled');\n return;\n }\n if (this._initialized) return;\n\n const rawModel = this.config.embeddingModel || DEFAULT_MODEL;\n // Guard against an invalid model ID (e.g. 'y' typed by mistake in the installer)\n const modelId = rawModel.includes('/') ? rawModel : DEFAULT_MODEL;\n if (rawModel !== modelId) {\n logWarn(`VectorManager: invalid embeddingModel \"${rawModel}\", using default instead`, { default: DEFAULT_MODEL });\n }\n const cacheDir = GLOBAL_MODELS_DIR;\n\n try {\n const { pipeline, env } = await getTransformers();\n env.cacheDir = cacheDir;\n\n if (!fs.existsSync(cacheDir)) {\n fs.mkdirSync(cacheDir, { recursive: true });\n }\n\n // Skip remote check if model already cached (HuggingFace uses '--' as path separator)\n // HuggingFace transformers v3 stores models as <org>/<model>/ (v2 used <org>--<model>)\n const cached = fs.existsSync(path.join(cacheDir, modelId));\n if (cached) env.allowRemoteModels = false;\n\n let lastFile = '';\n this.pipeline = await pipeline('feature-extraction', modelId, {\n dtype: 'auto',\n ...(!cached && onProgress ? {\n progress_callback: (p: { status: string; file?: string; loaded?: number; total?: number }) => {\n if (p.status === 'progress' && p.file) {\n lastFile = p.file;\n onProgress(p.file, p.loaded ?? 0, p.total ?? 0, false);\n } else if (p.status === 'done' && lastFile) {\n onProgress(lastFile, 1, 1, true);\n lastFile = '';\n }\n },\n } : {}),\n });\n\n // Validate embeddingDim against the model's actual output shape.\n // Run a single test embedding and compare dims[1] to config.embeddingDim.\n // If they differ, warn and update the runtime dim so downstream engines\n // are initialised with the correct size (config.json is not rewritten here).\n try {\n const testOut = await this.pipeline('test', { pooling: 'mean', normalize: true });\n const actualDim: number = testOut.dims?.[1] ?? (testOut.data?.length as number);\n const configuredDim = this.config.embeddingDim ?? DEFAULT_EMBEDDING_DIM;\n if (actualDim && actualDim !== configuredDim) {\n logWarn(\n `VectorManager: embeddingDim mismatch \u2014 config says ${configuredDim} but model outputs ${actualDim}. Using actual dim.`,\n { model: modelId, configuredDim, actualDim },\n );\n // Patch the runtime config so all engine constructors below use the correct dim\n (this.config as unknown as Record<string, unknown>).embeddingDim = actualDim;\n }\n } catch { /* test embedding failed \u2014 proceed with configured dim */ }\n\n this._initialized = true;\n logDebug('VectorManager: model loaded', { modelId, embeddingDim: this.config.embeddingDim ?? DEFAULT_EMBEDDING_DIM });\n } catch (err) {\n logError('VectorManager: failed to load embedding model', {\n model: modelId,\n error: String(err),\n });\n this._initialized = false;\n return;\n }\n\n // Initialize the selected search engine\n const engine = this.config.semanticEngine ?? (this.config.useVecIndex ? 'sqlite-vec' : 'cosine');\n\n if (this.projectRoot && engine !== 'cosine') {\n const kirographDir = path.join(this.projectRoot, '.kirograph');\n\n const embeddingDim = this.config.embeddingDim ?? DEFAULT_EMBEDDING_DIM;\n\n if (engine === 'sqlite-vec') {\n this.vecIndex = new VecIndex(kirographDir, embeddingDim);\n await this.vecIndex.initialize();\n if (this.vecIndex.isAvailable()) {\n logDebug('VectorManager: sqlite-vec ANN index ready');\n } else {\n this._engineFallback = 'sqlite-vec unavailable \u2014 run: npm install better-sqlite3 sqlite-vec';\n logDebug('VectorManager: sqlite-vec unavailable, falling back to in-process cosine');\n }\n } else if (engine === 'orama') {\n this.oramaIndex = new OramaIndex(kirographDir, embeddingDim);\n await this.oramaIndex.initialize();\n if (this.oramaIndex.isAvailable()) {\n logDebug('VectorManager: Orama hybrid index ready');\n } else {\n this._engineFallback = 'orama unavailable \u2014 run: npm install @orama/orama @orama/plugin-data-persistence';\n logDebug('VectorManager: Orama unavailable, falling back to in-process cosine');\n }\n } else if (engine === 'pglite') {\n this.pgliteIndex = new PGliteIndex(kirographDir, embeddingDim);\n await this.pgliteIndex.initialize();\n if (this.pgliteIndex.isAvailable()) {\n logDebug('VectorManager: PGlite+pgvector hybrid index ready');\n } else {\n this._engineFallback = 'pglite unavailable \u2014 run: npm install @electric-sql/pglite';\n logDebug('VectorManager: PGlite unavailable, falling back to in-process cosine');\n }\n } else if (engine === 'lancedb') {\n this.lancedbIndex = new LanceDBIndex(kirographDir, embeddingDim);\n await this.lancedbIndex.initialize();\n if (this.lancedbIndex.isAvailable()) {\n logDebug('VectorManager: LanceDB ANN index ready');\n } else {\n this._engineFallback = 'lancedb unavailable \u2014 run: npm install @lancedb/lancedb';\n logDebug('VectorManager: LanceDB unavailable, falling back to in-process cosine');\n }\n } else if (engine === 'qdrant') {\n this.qdrantIndex = new QdrantIndex(kirographDir, embeddingDim);\n await this.qdrantIndex.initialize();\n if (this.qdrantIndex.isAvailable()) {\n logDebug('VectorManager: Qdrant ANN index ready');\n } else {\n this._engineFallback = 'qdrant unavailable \u2014 run: npm install qdrant-local';\n logDebug('VectorManager: Qdrant unavailable, falling back to in-process cosine');\n }\n } else if (engine === 'typesense') {\n this.typesenseIndex = new TypesenseIndex(kirographDir, embeddingDim);\n await this.typesenseIndex.initialize();\n if (this.typesenseIndex.isAvailable()) {\n logDebug('VectorManager: Typesense ANN index ready');\n } else {\n const reason = this.typesenseIndex.getFailReason();\n this._engineFallback = reason\n ? `typesense initialization failed: ${reason}`\n : 'typesense unavailable \u2014 run: npm install typesense';\n logDebug('VectorManager: Typesense unavailable, falling back to in-process cosine');\n }\n }\n }\n }\n\n /**\n * Embed a single node and persist to the vectors table.\n * Skips silently when disabled; logs on failure without throwing.\n */\n async embedNode(node: Node): Promise<void> {\n if (!this.config.enableEmbeddings) return;\n if (!this._initialized || !this.pipeline) {\n logError('Embedding model unavailable', { model: this.config.embeddingModel });\n return;\n }\n if (!EMBEDDABLE_KINDS.has(node.kind)) return;\n\n try {\n const text = `search_document: ${nodeToText(node)}`;\n const output = await this.pipeline(text, { pooling: 'mean', normalize: true });\n const embedding = toFloat32Array(output.data);\n // non-cosine engines are sole stores of record when active \u2014 skip the SQLite vectors table\n if (!this.vecIndex?.isAvailable() && !this.oramaIndex?.isAvailable() && !this.pgliteIndex?.isAvailable() && !this.lancedbIndex?.isAvailable() && !this.qdrantIndex?.isAvailable() && !this.typesenseIndex?.isAvailable()) {\n this.db.storeEmbedding(node.id, embedding, this.config.embeddingModel || DEFAULT_MODEL);\n }\n this.vecIndex?.upsert(node.id, embedding);\n if (this.oramaIndex?.isAvailable()) await this.oramaIndex.upsert(node, embedding);\n if (this.pgliteIndex?.isAvailable()) await this.pgliteIndex.upsert(node, embedding);\n if (this.lancedbIndex?.isAvailable()) await this.lancedbIndex.upsert(node, embedding);\n if (this.qdrantIndex?.isAvailable()) await this.qdrantIndex.upsert(node, embedding);\n if (this.typesenseIndex?.isAvailable()) await this.typesenseIndex.upsert(node, embedding);\n } catch (err) {\n logWarn('Failed to embed node', { nodeId: node.id, error: String(err) });\n }\n }\n\n /** Number of entries currently in the active ANN/hybrid index (0 when not in use). */\n async vecIndexCount(): Promise<number> {\n if (this.vecIndex?.isAvailable()) return this.vecIndex.count();\n if (this.oramaIndex?.isAvailable()) return this.oramaIndex.count();\n if (this.pgliteIndex?.isAvailable()) return this.pgliteIndex.count();\n if (this.lancedbIndex?.isAvailable()) return this.lancedbIndex.count();\n if (this.qdrantIndex?.isAvailable()) return this.qdrantIndex.count();\n if (this.typesenseIndex?.isAvailable()) return this.typesenseIndex.count();\n return 0;\n }\n\n /**\n * Remove embeddings for the given node IDs from the active index.\n * For cosine the `vectors` SQLite table is cleaned up automatically via FK cascade\n * when nodes are deleted. For sqlite-vec, orama, and pglite the engine itself is\n * the sole store of record, so we delete from it explicitly here.\n */\n async deleteEmbeddings(nodeIds: string[]): Promise<void> {\n for (const id of nodeIds) {\n this.vecIndex?.delete(id);\n if (this.oramaIndex?.isAvailable()) await this.oramaIndex.delete(id);\n if (this.pgliteIndex?.isAvailable()) await this.pgliteIndex.delete(id);\n if (this.lancedbIndex?.isAvailable()) await this.lancedbIndex.delete(id);\n if (this.qdrantIndex?.isAvailable()) await this.qdrantIndex.delete(id);\n if (this.typesenseIndex?.isAvailable()) await this.typesenseIndex.delete(id);\n }\n }\n\n /**\n * Embed all eligible nodes in the database that don't yet have embeddings.\n * Streams nodes in pages to avoid loading the entire node set into memory \u2014\n * critical for large codebases (100K+ symbols) where a single getAllNodes()\n * call can exhaust the Node.js heap or the WASM linear memory.\n *\n * Uses dynamic batch sizing: starts at BATCH_SIZE, halves on OOM errors,\n * and recovers back to full size after successful batches.\n *\n * Emits a pre-flight warning via onProgress when the embeddable node count\n * exceeds LARGE_CODEBASE_THRESHOLD so the CLI can surface it to the user.\n */\n async embedAll(onProgress?: (current: number, total: number) => void): Promise<number> {\n if (!this.isInitialized() || !this.pipeline) return 0;\n\n const modelId = this.config.embeddingModel || DEFAULT_MODEL;\n const EMBEDDABLE_KINDS_ARRAY = [...EMBEDDABLE_KINDS] as string[];\n\n // Pre-flight: count embeddable nodes without loading them\n const totalEmbeddable = this.db.countEmbeddableNodes(EMBEDDABLE_KINDS_ARRAY);\n const LARGE_CODEBASE_THRESHOLD = 100_000;\n if (totalEmbeddable > LARGE_CODEBASE_THRESHOLD) {\n // Signal the large-codebase warning via a special progress event.\n // current=-1 is the sentinel; the CLI renderer checks for it.\n onProgress?.(-1, totalEmbeddable);\n }\n\n // Collect already-embedded IDs (needed to skip nodes already in the index)\n const existingIds = new Set(\n this.typesenseIndex?.isAvailable()\n ? await this.typesenseIndex.getEmbeddedNodeIds()\n : this.qdrantIndex?.isAvailable()\n ? await this.qdrantIndex.getEmbeddedNodeIds()\n : this.lancedbIndex?.isAvailable()\n ? await this.lancedbIndex.getEmbeddedNodeIds()\n : this.pgliteIndex?.isAvailable()\n ? await this.pgliteIndex.getEmbeddedNodeIds()\n : this.oramaIndex?.isAvailable()\n ? await this.oramaIndex.getEmbeddedNodeIds()\n : this.vecIndex?.isAvailable()\n ? this.vecIndex.getEmbeddedNodeIds()\n : this.db.getEmbeddedNodeIds()\n );\n\n // Stream nodes in pages \u2014 never hold more than PAGE_SIZE nodes in memory at once\n const PAGE_SIZE = 2000;\n let pageOffset = 0;\n let processed = 0;\n let currentBatchSize = BATCH_SIZE;\n let consecutiveSuccesses = 0;\n\n // We need to know the total pending count for progress reporting.\n // Approximate: totalEmbeddable - existingIds.size (may be slightly off if\n // existingIds contains IDs for non-embeddable kinds, but close enough).\n const totalPending = Math.max(0, totalEmbeddable - existingIds.size);\n if (totalPending === 0) {\n if (this.oramaIndex?.isAvailable()) await this.oramaIndex.save();\n return 0;\n }\n\n logDebug(`VectorManager: embedding ~${totalPending} nodes (${existingIds.size} already embedded, streaming in pages of ${PAGE_SIZE})`);\n\n // Buffer of pending nodes from the current page\n let pageBuffer: Node[] = [];\n let pageBufferOffset = 0; // index within pageBuffer\n let pageExhausted = false;\n\n const fetchNextPage = () => {\n const page = this.db.getEmbeddableNodesPaged(EMBEDDABLE_KINDS_ARRAY, PAGE_SIZE, pageOffset);\n pageOffset += page.length;\n pageBuffer = page.filter(n => !existingIds.has(n.id));\n pageBufferOffset = 0;\n if (page.length < PAGE_SIZE) pageExhausted = true;\n };\n\n fetchNextPage();\n\n while (pageBuffer.length > 0 || (!pageExhausted && pageBufferOffset >= pageBuffer.length)) {\n // Refill page buffer when exhausted\n if (pageBufferOffset >= pageBuffer.length) {\n if (pageExhausted) break;\n fetchNextPage();\n if (pageBuffer.length === 0) break;\n }\n\n // Slice a batch from the current page buffer\n const batch = pageBuffer.slice(pageBufferOffset, pageBufferOffset + currentBatchSize);\n if (batch.length === 0) break;\n\n const texts = batch.map(n => `search_document: ${nodeToText(n)}`);\n\n try {\n const outputs = await this.pipeline(texts, { pooling: 'mean', normalize: true });\n const dims: number[] = outputs.dims;\n const dim = dims[1] ?? (this.config.embeddingDim ?? DEFAULT_EMBEDDING_DIM);\n const flat = toFloat32Array(outputs.data);\n\n const tsNodes: Node[] = [];\n const tsEmbeddings: Float32Array[] = [];\n const qdNodes: Node[] = [];\n const qdEmbeddings: Float32Array[] = [];\n\n for (let j = 0; j < batch.length; j++) {\n const node = batch[j]!;\n const embedding = flat.slice(j * dim, (j + 1) * dim);\n if (!this.vecIndex?.isAvailable() && !this.oramaIndex?.isAvailable() && !this.pgliteIndex?.isAvailable() && !this.lancedbIndex?.isAvailable() && !this.qdrantIndex?.isAvailable() && !this.typesenseIndex?.isAvailable()) {\n this.db.storeEmbedding(node.id, embedding, modelId);\n }\n this.vecIndex?.upsert(node.id, embedding);\n if (this.oramaIndex?.isAvailable()) await this.oramaIndex.upsert(node, embedding);\n if (this.pgliteIndex?.isAvailable()) await this.pgliteIndex.upsert(node, embedding);\n if (this.lancedbIndex?.isAvailable()) await this.lancedbIndex.upsert(node, embedding);\n if (this.qdrantIndex?.isAvailable()) { qdNodes.push(node); qdEmbeddings.push(embedding); }\n if (this.typesenseIndex?.isAvailable()) { tsNodes.push(node); tsEmbeddings.push(embedding); }\n }\n\n if (qdNodes.length > 0) await this.qdrantIndex!.bulkUpsert(qdNodes, qdEmbeddings);\n if (tsNodes.length > 0) await this.typesenseIndex!.bulkUpsert(tsNodes, tsEmbeddings);\n\n pageBufferOffset += batch.length;\n processed += batch.length;\n consecutiveSuccesses++;\n\n if (currentBatchSize < BATCH_SIZE && consecutiveSuccesses >= 5) {\n currentBatchSize = Math.min(currentBatchSize * 2, BATCH_SIZE);\n logDebug(`VectorManager: batch size recovered to ${currentBatchSize}`);\n consecutiveSuccesses = 0;\n }\n } catch (err) {\n const errMsg = String(err);\n const isOOM = errMsg.includes('bad allocation') || errMsg.includes('OOM') || errMsg.includes('out of memory');\n\n if (isOOM && currentBatchSize > MIN_BATCH_SIZE) {\n currentBatchSize = Math.max(Math.floor(currentBatchSize / 2), MIN_BATCH_SIZE);\n consecutiveSuccesses = 0;\n logWarn(`VectorManager: OOM at batch offset ${pageBufferOffset} \u2014 reducing batch size to ${currentBatchSize} and retrying`, {\n batchSize: currentBatchSize,\n nodeIds: batch.slice(0, 3).map(n => n.id),\n });\n // Don't advance pageBufferOffset \u2014 retry the same batch\n } else if (isOOM && currentBatchSize <= MIN_BATCH_SIZE) {\n logError(`VectorManager: OOM even at minimum batch size ${MIN_BATCH_SIZE} \u2014 skipping ${batch.length} nodes`, {\n batchStart: pageBufferOffset,\n skippedNodes: batch.map(n => `${n.kind}:${n.name} (${n.filePath})`).slice(0, 5),\n });\n pageBufferOffset += batch.length;\n processed += batch.length;\n consecutiveSuccesses = 0;\n } else {\n logWarn(`VectorManager: batch embedding failed at offset ${pageBufferOffset}`, {\n batchSize: currentBatchSize,\n error: errMsg,\n });\n pageBufferOffset += batch.length;\n processed += batch.length;\n consecutiveSuccesses = 0;\n }\n }\n\n onProgress?.(processed, totalPending);\n }\n\n if (this.oramaIndex?.isAvailable()) await this.oramaIndex.save();\n\n logDebug(`VectorManager: embedding complete \u2014 ${processed} processed, final batch size: ${currentBatchSize}`);\n\n return processed;\n }\n\n /**\n * Semantic search: embed the query and return top-N nodes by similarity.\n *\n * When sqlite-vec is available (useVecIndex: true), delegates ANN search to\n * VecIndex (fast, sub-linear). Otherwise falls back to in-process cosine\n * similarity over all stored embeddings (linear scan, no extra deps).\n *\n * Returns empty array when not initialized.\n */\n async search(query: string, topN = 10): Promise<Node[]> {\n if (!this.isInitialized() || !this.pipeline) return [];\n\n try {\n const text = `search_query: ${query}`;\n const output = await this.pipeline(text, { pooling: 'mean', normalize: true });\n const queryVec = toFloat32Array(output.data);\n\n let nodeIds: string[];\n\n if (this.pgliteIndex?.isAvailable()) {\n // Hybrid search via PGlite+pgvector \u2014 exact vector + full-text in one SQL query\n nodeIds = await this.pgliteIndex.search(query, queryVec, topN);\n } else if (this.oramaIndex?.isAvailable()) {\n // Hybrid search via Orama \u2014 full-text + vector combined\n nodeIds = await this.oramaIndex.search(query, queryVec, topN);\n } else if (this.lancedbIndex?.isAvailable()) {\n // ANN search via LanceDB \u2014 columnar Lance format, cosine metric\n nodeIds = await this.lancedbIndex.search(queryVec, topN);\n } else if (this.qdrantIndex?.isAvailable()) {\n // ANN search via Qdrant \u2014 HNSW index, cosine metric\n nodeIds = await this.qdrantIndex.search(queryVec, topN);\n } else if (this.typesenseIndex?.isAvailable()) {\n // ANN search via Typesense \u2014 HNSW index, cosine metric\n nodeIds = await this.typesenseIndex.search(queryVec, topN);\n } else if (this.vecIndex?.isAvailable()) {\n // ANN search via sqlite-vec \u2014 fast, sub-linear\n nodeIds = this.vecIndex.search(queryVec, topN);\n } else {\n // In-process cosine similarity \u2014 linear scan over all stored embeddings\n const allEmbeddings = this.db.getAllEmbeddings();\n nodeIds = allEmbeddings\n .map(({ nodeId, embedding }) => ({ nodeId, score: cosine(queryVec, embedding) }))\n .filter(r => r.score >= 0.3)\n .sort((a, b) => b.score - a.score)\n .slice(0, topN)\n .map(r => r.nodeId);\n }\n\n const results: Node[] = [];\n for (const nodeId of nodeIds) {\n const node = this.db.getNode(nodeId);\n if (node) results.push(node);\n }\n return results;\n } catch (err) {\n logWarn('VectorManager: search failed', { error: String(err) });\n return [];\n }\n }\n\n /** Release engine resources (e.g. kill Qdrant child process). */\n close(): void {\n this.qdrantIndex?.close();\n this.typesenseIndex?.close();\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * KiroGraph Vector Manager\n *\n * Semantic search via configurable local embedding models using @huggingface/transformers v3.\n * Mirrors CodeGraph src/vectors/ (embedder.ts + manager.ts) adapted for KiroGraph:\n * - Cache dir: ~/.kirograph/models/ (not ~/.codegraph/models/)\n * - Embeddings stored in the `vectors` SQLite table\n * - Cosine similarity search done in-process by default (no extra deps)\n * - ANN search via sqlite-vec opt-in: set config.useVecIndex = true\n * (requires `npm install better-sqlite3 sqlite-vec`)\n * - Disabled by default; opt-in via config.enableEmbeddings = true\n */\n\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { homedir } from 'os';\nimport { logDebug, logWarn, logError } from '../errors';\nimport type { KiroGraphConfig } from '../config';\nimport type { Node } from '../types';\nimport type { GraphDatabase } from '../db/database';\nimport { VecIndex } from './vec-index';\nimport { OramaIndex } from './orama-index';\nimport { PGliteIndex } from './pglite-index';\nimport { LanceDBIndex } from './lancedb-index';\nimport { QdrantIndex } from './qdrant-index';\nimport { TypesenseIndex } from './typesense-index';\nimport { TurboQuantIndex, writeTurboQuantStats } from './turboquant-index';\nimport { TurboVecIndex } from './turbovec-index';\n\n// \u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst DEFAULT_MODEL = 'nomic-ai/nomic-embed-text-v1.5';\nconst DEFAULT_EMBEDDING_DIM = 768;\nconst GLOBAL_MODELS_DIR = path.join(homedir(), '.kirograph', 'models');\nconst BATCH_SIZE = 32;\nconst MIN_BATCH_SIZE = 4;\nconst MAX_TOKEN_CHARS = 2000;\n\n/** Node kinds worth embedding \u2014 high information density */\nconst EMBEDDABLE_KINDS = new Set<Node['kind']>([\n 'function', 'method', 'class', 'interface', 'type_alias', 'component', 'module',\n]);\n\n// \u2500\u2500 Embedder \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype Pipeline = any;\nlet transformers: typeof import('@huggingface/transformers') | null = null;\n\nasync function getTransformers() {\n if (!transformers) {\n transformers = await import('@huggingface/transformers');\n }\n return transformers;\n}\n\n/**\n * Build a searchable text representation of a node.\n * Mirrors CodeGraph TextEmbedder.createNodeText().\n * Truncated to MAX_TOKEN_CHARS to stay within the model's memory-safe token limit.\n * Priority: kind + name > qualified name > file path > signature > docstring.\n * This ensures the most important search signal is always preserved.\n */\nfunction nodeToText(node: Node): string {\n const parts = [`${node.kind}: ${node.name}`];\n if (node.qualifiedName && node.qualifiedName !== node.name) {\n parts.push(`path: ${node.qualifiedName}`);\n }\n parts.push(`file: ${node.filePath}`);\n if (node.signature) parts.push(`signature: ${node.signature}`);\n if (node.docstring) parts.push(`documentation: ${node.docstring}`);\n const text = parts.join('\\n');\n if (text.length > MAX_TOKEN_CHARS) {\n logDebug(`nodeToText: truncated ${node.id} from ${text.length} to ${MAX_TOKEN_CHARS} chars`);\n return text.slice(0, MAX_TOKEN_CHARS);\n }\n return text;\n}\n\n/** Cosine similarity between two equal-length Float32Arrays. */\nfunction cosine(a: Float32Array, b: Float32Array): number {\n let dot = 0, na = 0, nb = 0;\n for (let i = 0; i < a.length; i++) {\n dot += a[i]! * b[i]!;\n na += a[i]! * a[i]!;\n nb += b[i]! * b[i]!;\n }\n const denom = Math.sqrt(na) * Math.sqrt(nb);\n return denom === 0 ? 0 : dot / denom;\n}\n\n/** Extract a flat Float32Array from the transformer pipeline output. */\nfunction toFloat32Array(data: unknown): Float32Array {\n if (data instanceof Float32Array) return data;\n if (Array.isArray(data)) return new Float32Array(data);\n if (data && typeof data === 'object' && 'length' in data) {\n return Float32Array.from(Array.from(data as ArrayLike<number>));\n }\n throw new Error('Unsupported embedding data format');\n}\n\n// \u2500\u2500 VectorManager \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class VectorManager {\n private pipeline: Pipeline | null = null;\n private _initialized = false;\n private vecIndex: VecIndex | null = null;\n private oramaIndex: OramaIndex | null = null;\n private pgliteIndex: PGliteIndex | null = null;\n private lancedbIndex: LanceDBIndex | null = null;\n private qdrantIndex: QdrantIndex | null = null;\n private typesenseIndex: TypesenseIndex | null = null;\n private turboquantIndex: TurboQuantIndex | null = null;\n private turbovecIndex: TurboVecIndex | null = null;\n private _engineFallback: string | null = null;\n\n constructor(\n private readonly db: GraphDatabase,\n private readonly config: KiroGraphConfig,\n private readonly projectRoot?: string,\n ) {}\n\n isInitialized(): boolean {\n return this.config.enableEmbeddings === true && this._initialized;\n }\n\n /** Non-null when the configured engine failed to load and cosine is being used instead. */\n getEngineFallback(): string | null {\n return this._engineFallback;\n }\n\n /**\n * Load the embedding model. No-op when embeddings are disabled.\n * Fails silently so callers can continue without semantic search.\n * When config.useVecIndex is true, also initializes the sqlite-vec ANN index.\n */\n async initialize(onProgress?: (file: string, loaded: number, total: number, done: boolean) => void): Promise<void> {\n if (!this.config.enableEmbeddings) {\n logDebug('VectorManager: embeddings disabled');\n return;\n }\n if (this._initialized) return;\n\n const rawModel = this.config.embeddingModel || DEFAULT_MODEL;\n // Guard against an invalid model ID (e.g. 'y' typed by mistake in the installer)\n const modelId = rawModel.includes('/') ? rawModel : DEFAULT_MODEL;\n if (rawModel !== modelId) {\n logWarn(`VectorManager: invalid embeddingModel \"${rawModel}\", using default instead`, { default: DEFAULT_MODEL });\n }\n const cacheDir = GLOBAL_MODELS_DIR;\n\n try {\n const { pipeline, env } = await getTransformers();\n env.cacheDir = cacheDir;\n\n if (!fs.existsSync(cacheDir)) {\n fs.mkdirSync(cacheDir, { recursive: true });\n }\n\n // Skip remote check if model already cached (HuggingFace uses '--' as path separator)\n // HuggingFace transformers v3 stores models as <org>/<model>/ (v2 used <org>--<model>)\n const cached = fs.existsSync(path.join(cacheDir, modelId));\n if (cached) env.allowRemoteModels = false;\n\n let lastFile = '';\n this.pipeline = await pipeline('feature-extraction', modelId, {\n dtype: 'auto',\n ...(!cached && onProgress ? {\n progress_callback: (p: { status: string; file?: string; loaded?: number; total?: number }) => {\n if (p.status === 'progress' && p.file) {\n lastFile = p.file;\n onProgress(p.file, p.loaded ?? 0, p.total ?? 0, false);\n } else if (p.status === 'done' && lastFile) {\n onProgress(lastFile, 1, 1, true);\n lastFile = '';\n }\n },\n } : {}),\n });\n\n // Validate embeddingDim against the model's actual output shape.\n // Run a single test embedding and compare dims[1] to config.embeddingDim.\n // If they differ, warn and update the runtime dim so downstream engines\n // are initialised with the correct size (config.json is not rewritten here).\n try {\n const testOut = await this.pipeline('test', { pooling: 'mean', normalize: true });\n const actualDim: number = testOut.dims?.[1] ?? (testOut.data?.length as number);\n const configuredDim = this.config.embeddingDim ?? DEFAULT_EMBEDDING_DIM;\n if (actualDim && actualDim !== configuredDim) {\n logWarn(\n `VectorManager: embeddingDim mismatch \u2014 config says ${configuredDim} but model outputs ${actualDim}. Using actual dim.`,\n { model: modelId, configuredDim, actualDim },\n );\n // Patch the runtime config so all engine constructors below use the correct dim\n (this.config as unknown as Record<string, unknown>).embeddingDim = actualDim;\n }\n } catch { /* test embedding failed \u2014 proceed with configured dim */ }\n\n this._initialized = true;\n logDebug('VectorManager: model loaded', { modelId, embeddingDim: this.config.embeddingDim ?? DEFAULT_EMBEDDING_DIM });\n } catch (err) {\n logError('VectorManager: failed to load embedding model', {\n model: modelId,\n error: String(err),\n });\n this._initialized = false;\n return;\n }\n\n // Initialize the selected search engine\n const engine = this.config.semanticEngine ?? (this.config.useVecIndex ? 'sqlite-vec' : 'cosine');\n\n if (this.projectRoot && engine !== 'cosine') {\n const kirographDir = path.join(this.projectRoot, '.kirograph');\n\n const embeddingDim = this.config.embeddingDim ?? DEFAULT_EMBEDDING_DIM;\n\n if (engine === 'sqlite-vec') {\n this.vecIndex = new VecIndex(kirographDir, embeddingDim);\n await this.vecIndex.initialize();\n if (this.vecIndex.isAvailable()) {\n logDebug('VectorManager: sqlite-vec ANN index ready');\n } else {\n this._engineFallback = 'sqlite-vec unavailable \u2014 run: npm install better-sqlite3 sqlite-vec';\n logDebug('VectorManager: sqlite-vec unavailable, falling back to in-process cosine');\n }\n } else if (engine === 'orama') {\n this.oramaIndex = new OramaIndex(kirographDir, embeddingDim);\n await this.oramaIndex.initialize();\n if (this.oramaIndex.isAvailable()) {\n logDebug('VectorManager: Orama hybrid index ready');\n } else {\n this._engineFallback = 'orama unavailable \u2014 run: npm install @orama/orama @orama/plugin-data-persistence';\n logDebug('VectorManager: Orama unavailable, falling back to in-process cosine');\n }\n } else if (engine === 'pglite') {\n this.pgliteIndex = new PGliteIndex(kirographDir, embeddingDim);\n await this.pgliteIndex.initialize();\n if (this.pgliteIndex.isAvailable()) {\n logDebug('VectorManager: PGlite+pgvector hybrid index ready');\n } else {\n this._engineFallback = 'pglite unavailable \u2014 run: npm install @electric-sql/pglite';\n logDebug('VectorManager: PGlite unavailable, falling back to in-process cosine');\n }\n } else if (engine === 'lancedb') {\n this.lancedbIndex = new LanceDBIndex(kirographDir, embeddingDim);\n await this.lancedbIndex.initialize();\n if (this.lancedbIndex.isAvailable()) {\n logDebug('VectorManager: LanceDB ANN index ready');\n } else {\n this._engineFallback = 'lancedb unavailable \u2014 run: npm install @lancedb/lancedb';\n logDebug('VectorManager: LanceDB unavailable, falling back to in-process cosine');\n }\n } else if (engine === 'qdrant') {\n this.qdrantIndex = new QdrantIndex(kirographDir, embeddingDim);\n await this.qdrantIndex.initialize();\n if (this.qdrantIndex.isAvailable()) {\n logDebug('VectorManager: Qdrant ANN index ready');\n } else {\n this._engineFallback = 'qdrant unavailable \u2014 run: npm install qdrant-local';\n logDebug('VectorManager: Qdrant unavailable, falling back to in-process cosine');\n }\n } else if (engine === 'typesense') {\n this.typesenseIndex = new TypesenseIndex(kirographDir, embeddingDim);\n await this.typesenseIndex.initialize();\n if (this.typesenseIndex.isAvailable()) {\n logDebug('VectorManager: Typesense ANN index ready');\n } else {\n const reason = this.typesenseIndex.getFailReason();\n this._engineFallback = reason\n ? `typesense initialization failed: ${reason}`\n : 'typesense unavailable \u2014 run: npm install typesense';\n logDebug('VectorManager: Typesense unavailable, falling back to in-process cosine');\n }\n } else if (engine === 'turboquant') {\n const bits = this.config.turboquantBits ?? 3;\n this.turboquantIndex = new TurboQuantIndex(kirographDir, 'turboquant.bin', embeddingDim, bits);\n await this.turboquantIndex.initialize();\n if (this.turboquantIndex.isAvailable()) {\n logDebug('VectorManager: TurboQuant ANN index ready', { bits });\n } else {\n this._engineFallback = 'turboquant unavailable \u2014 run: npm install turboquant-js';\n logDebug('VectorManager: TurboQuant unavailable, falling back to in-process cosine');\n }\n } else if (engine === 'turbovec') {\n const bits = this.config.turbovecBits ?? 4;\n this.turbovecIndex = new TurboVecIndex(kirographDir, 'turbovec.tvim', embeddingDim, bits);\n await this.turbovecIndex.initialize();\n if (this.turbovecIndex.isAvailable()) {\n logDebug('VectorManager: TurboVec ANN index ready', { bits });\n } else {\n this._engineFallback = 'turbovec unavailable \u2014 build it: cd native/turbovec-node && npm install && npm run build';\n logDebug('VectorManager: TurboVec unavailable, falling back to in-process cosine');\n }\n }\n }\n }\n\n /**\n * Embed a single node and persist to the vectors table.\n * Skips silently when disabled; logs on failure without throwing.\n */\n async embedNode(node: Node): Promise<void> {\n if (!this.config.enableEmbeddings) return;\n if (!this._initialized || !this.pipeline) {\n logError('Embedding model unavailable', { model: this.config.embeddingModel });\n return;\n }\n if (!EMBEDDABLE_KINDS.has(node.kind)) return;\n\n try {\n const text = `search_document: ${nodeToText(node)}`;\n const output = await this.pipeline(text, { pooling: 'mean', normalize: true });\n const embedding = toFloat32Array(output.data);\n // non-cosine engines are sole stores of record when active \u2014 skip the SQLite vectors table\n if (!this.vecIndex?.isAvailable() && !this.oramaIndex?.isAvailable() && !this.pgliteIndex?.isAvailable() && !this.lancedbIndex?.isAvailable() && !this.qdrantIndex?.isAvailable() && !this.typesenseIndex?.isAvailable() && !this.turboquantIndex?.isAvailable() && !this.turbovecIndex?.isAvailable()) {\n this.db.storeEmbedding(node.id, embedding, this.config.embeddingModel || DEFAULT_MODEL);\n }\n this.vecIndex?.upsert(node.id, embedding);\n if (this.oramaIndex?.isAvailable()) await this.oramaIndex.upsert(node, embedding);\n if (this.pgliteIndex?.isAvailable()) await this.pgliteIndex.upsert(node, embedding);\n if (this.lancedbIndex?.isAvailable()) await this.lancedbIndex.upsert(node, embedding);\n if (this.qdrantIndex?.isAvailable()) await this.qdrantIndex.upsert(node, embedding);\n if (this.typesenseIndex?.isAvailable()) await this.typesenseIndex.upsert(node, embedding);\n this.turboquantIndex?.upsert(node.id, embedding);\n this.turbovecIndex?.upsert(node.id, embedding);\n } catch (err) {\n logWarn('Failed to embed node', { nodeId: node.id, error: String(err) });\n }\n }\n\n /** Number of entries currently in the active ANN/hybrid index (0 when not in use). */\n async vecIndexCount(): Promise<number> {\n if (this.turbovecIndex?.isAvailable()) return this.turbovecIndex.count();\n if (this.turboquantIndex?.isAvailable()) return this.turboquantIndex.count();\n if (this.vecIndex?.isAvailable()) return this.vecIndex.count();\n if (this.oramaIndex?.isAvailable()) return this.oramaIndex.count();\n if (this.pgliteIndex?.isAvailable()) return this.pgliteIndex.count();\n if (this.lancedbIndex?.isAvailable()) return this.lancedbIndex.count();\n if (this.qdrantIndex?.isAvailable()) return this.qdrantIndex.count();\n if (this.typesenseIndex?.isAvailable()) return this.typesenseIndex.count();\n return 0;\n }\n\n /**\n * Remove embeddings for the given node IDs from the active index.\n * For cosine the `vectors` SQLite table is cleaned up automatically via FK cascade\n * when nodes are deleted. For sqlite-vec, orama, and pglite the engine itself is\n * the sole store of record, so we delete from it explicitly here.\n */\n async deleteEmbeddings(nodeIds: string[]): Promise<void> {\n for (const id of nodeIds) {\n this.vecIndex?.delete(id);\n if (this.oramaIndex?.isAvailable()) await this.oramaIndex.delete(id);\n if (this.pgliteIndex?.isAvailable()) await this.pgliteIndex.delete(id);\n if (this.lancedbIndex?.isAvailable()) await this.lancedbIndex.delete(id);\n if (this.qdrantIndex?.isAvailable()) await this.qdrantIndex.delete(id);\n if (this.typesenseIndex?.isAvailable()) await this.typesenseIndex.delete(id);\n this.turboquantIndex?.delete(id);\n this.turbovecIndex?.delete(id);\n }\n }\n\n /**\n * Embed all eligible nodes in the database that don't yet have embeddings.\n * Streams nodes in pages to avoid loading the entire node set into memory \u2014\n * critical for large codebases (100K+ symbols) where a single getAllNodes()\n * call can exhaust the Node.js heap or the WASM linear memory.\n *\n * Uses dynamic batch sizing: starts at BATCH_SIZE, halves on OOM errors,\n * and recovers back to full size after successful batches.\n *\n * Emits a pre-flight warning via onProgress when the embeddable node count\n * exceeds LARGE_CODEBASE_THRESHOLD so the CLI can surface it to the user.\n */\n async embedAll(onProgress?: (current: number, total: number) => void): Promise<number> {\n if (!this.isInitialized() || !this.pipeline) return 0;\n\n const modelId = this.config.embeddingModel || DEFAULT_MODEL;\n const EMBEDDABLE_KINDS_ARRAY = [...EMBEDDABLE_KINDS] as string[];\n\n // Pre-flight: count embeddable nodes without loading them\n const totalEmbeddable = this.db.countEmbeddableNodes(EMBEDDABLE_KINDS_ARRAY);\n const LARGE_CODEBASE_THRESHOLD = 100_000;\n if (totalEmbeddable > LARGE_CODEBASE_THRESHOLD) {\n // Signal the large-codebase warning via a special progress event.\n // current=-1 is the sentinel; the CLI renderer checks for it.\n onProgress?.(-1, totalEmbeddable);\n }\n\n // Collect already-embedded IDs (needed to skip nodes already in the index)\n const existingIds = new Set(\n this.typesenseIndex?.isAvailable()\n ? await this.typesenseIndex.getEmbeddedNodeIds()\n : this.qdrantIndex?.isAvailable()\n ? await this.qdrantIndex.getEmbeddedNodeIds()\n : this.lancedbIndex?.isAvailable()\n ? await this.lancedbIndex.getEmbeddedNodeIds()\n : this.pgliteIndex?.isAvailable()\n ? await this.pgliteIndex.getEmbeddedNodeIds()\n : this.oramaIndex?.isAvailable()\n ? await this.oramaIndex.getEmbeddedNodeIds()\n : this.turbovecIndex?.isAvailable()\n ? this.turbovecIndex.getEmbeddedIds()\n : this.turboquantIndex?.isAvailable()\n ? this.turboquantIndex.getEmbeddedIds()\n : this.vecIndex?.isAvailable()\n ? this.vecIndex.getEmbeddedNodeIds()\n : this.db.getEmbeddedNodeIds()\n );\n\n // Stream nodes in pages \u2014 never hold more than PAGE_SIZE nodes in memory at once\n const PAGE_SIZE = 2000;\n let pageOffset = 0;\n let processed = 0;\n let currentBatchSize = BATCH_SIZE;\n let consecutiveSuccesses = 0;\n\n // We need to know the total pending count for progress reporting.\n // Approximate: totalEmbeddable - existingIds.size (may be slightly off if\n // existingIds contains IDs for non-embeddable kinds, but close enough).\n const totalPending = Math.max(0, totalEmbeddable - existingIds.size);\n if (totalPending === 0) {\n if (this.oramaIndex?.isAvailable()) await this.oramaIndex.save();\n return 0;\n }\n\n logDebug(`VectorManager: embedding ~${totalPending} nodes (${existingIds.size} already embedded, streaming in pages of ${PAGE_SIZE})`);\n\n // Buffer of pending nodes from the current page\n let pageBuffer: Node[] = [];\n let pageBufferOffset = 0; // index within pageBuffer\n let pageExhausted = false;\n\n const fetchNextPage = () => {\n const page = this.db.getEmbeddableNodesPaged(EMBEDDABLE_KINDS_ARRAY, PAGE_SIZE, pageOffset);\n pageOffset += page.length;\n pageBuffer = page.filter(n => !existingIds.has(n.id));\n pageBufferOffset = 0;\n if (page.length < PAGE_SIZE) pageExhausted = true;\n };\n\n fetchNextPage();\n\n while (pageBuffer.length > 0 || (!pageExhausted && pageBufferOffset >= pageBuffer.length)) {\n // Refill page buffer when exhausted\n if (pageBufferOffset >= pageBuffer.length) {\n if (pageExhausted) break;\n fetchNextPage();\n if (pageBuffer.length === 0) break;\n }\n\n // Slice a batch from the current page buffer\n const batch = pageBuffer.slice(pageBufferOffset, pageBufferOffset + currentBatchSize);\n if (batch.length === 0) break;\n\n const texts = batch.map(n => `search_document: ${nodeToText(n)}`);\n\n try {\n const outputs = await this.pipeline(texts, { pooling: 'mean', normalize: true });\n const dims: number[] = outputs.dims;\n const dim = dims[1] ?? (this.config.embeddingDim ?? DEFAULT_EMBEDDING_DIM);\n const flat = toFloat32Array(outputs.data);\n\n const tsNodes: Node[] = [];\n const tsEmbeddings: Float32Array[] = [];\n const qdNodes: Node[] = [];\n const qdEmbeddings: Float32Array[] = [];\n\n for (let j = 0; j < batch.length; j++) {\n const node = batch[j]!;\n const embedding = flat.slice(j * dim, (j + 1) * dim);\n if (!this.vecIndex?.isAvailable() && !this.oramaIndex?.isAvailable() && !this.pgliteIndex?.isAvailable() && !this.lancedbIndex?.isAvailable() && !this.qdrantIndex?.isAvailable() && !this.typesenseIndex?.isAvailable() && !this.turboquantIndex?.isAvailable() && !this.turbovecIndex?.isAvailable()) {\n this.db.storeEmbedding(node.id, embedding, modelId);\n }\n this.vecIndex?.upsert(node.id, embedding);\n if (this.oramaIndex?.isAvailable()) await this.oramaIndex.upsert(node, embedding);\n if (this.pgliteIndex?.isAvailable()) await this.pgliteIndex.upsert(node, embedding);\n if (this.lancedbIndex?.isAvailable()) await this.lancedbIndex.upsert(node, embedding);\n if (this.qdrantIndex?.isAvailable()) { qdNodes.push(node); qdEmbeddings.push(embedding); }\n if (this.typesenseIndex?.isAvailable()) { tsNodes.push(node); tsEmbeddings.push(embedding); }\n this.turboquantIndex?.upsert(node.id, embedding);\n this.turbovecIndex?.upsert(node.id, embedding);\n }\n\n if (qdNodes.length > 0) await this.qdrantIndex!.bulkUpsert(qdNodes, qdEmbeddings);\n if (tsNodes.length > 0) await this.typesenseIndex!.bulkUpsert(tsNodes, tsEmbeddings);\n\n pageBufferOffset += batch.length;\n processed += batch.length;\n consecutiveSuccesses++;\n\n if (currentBatchSize < BATCH_SIZE && consecutiveSuccesses >= 5) {\n currentBatchSize = Math.min(currentBatchSize * 2, BATCH_SIZE);\n logDebug(`VectorManager: batch size recovered to ${currentBatchSize}`);\n consecutiveSuccesses = 0;\n }\n } catch (err) {\n const errMsg = String(err);\n const isOOM = errMsg.includes('bad allocation') || errMsg.includes('OOM') || errMsg.includes('out of memory');\n\n if (isOOM && currentBatchSize > MIN_BATCH_SIZE) {\n currentBatchSize = Math.max(Math.floor(currentBatchSize / 2), MIN_BATCH_SIZE);\n consecutiveSuccesses = 0;\n logWarn(`VectorManager: OOM at batch offset ${pageBufferOffset} \u2014 reducing batch size to ${currentBatchSize} and retrying`, {\n batchSize: currentBatchSize,\n nodeIds: batch.slice(0, 3).map(n => n.id),\n });\n // Don't advance pageBufferOffset \u2014 retry the same batch\n } else if (isOOM && currentBatchSize <= MIN_BATCH_SIZE) {\n logError(`VectorManager: OOM even at minimum batch size ${MIN_BATCH_SIZE} \u2014 skipping ${batch.length} nodes`, {\n batchStart: pageBufferOffset,\n skippedNodes: batch.map(n => `${n.kind}:${n.name} (${n.filePath})`).slice(0, 5),\n });\n pageBufferOffset += batch.length;\n processed += batch.length;\n consecutiveSuccesses = 0;\n } else {\n logWarn(`VectorManager: batch embedding failed at offset ${pageBufferOffset}`, {\n batchSize: currentBatchSize,\n error: errMsg,\n });\n pageBufferOffset += batch.length;\n processed += batch.length;\n consecutiveSuccesses = 0;\n }\n }\n\n onProgress?.(processed, totalPending);\n }\n\n if (this.oramaIndex?.isAvailable()) await this.oramaIndex.save();\n\n if (this.turboquantIndex?.isAvailable()) {\n await this.turboquantIndex.save();\n if (this.projectRoot) {\n const kirographDir = path.join(this.projectRoot, '.kirograph');\n const stats = this.turboquantIndex.memoryStats();\n const count = this.turboquantIndex.count();\n const dim = this.config.embeddingDim ?? DEFAULT_EMBEDDING_DIM;\n const rawBytes = count * dim * 4;\n writeTurboQuantStats(kirographDir, {\n count,\n dim,\n bits: this.config.turboquantBits ?? 3,\n compressionRatio: stats.compressionRatio,\n actualBytes: stats.actualBytes,\n rawBytes,\n savedBytes: rawBytes - stats.actualBytes,\n });\n }\n }\n\n if (this.turbovecIndex?.isAvailable()) {\n await this.turbovecIndex.save();\n logDebug('VectorManager: TurboVec index saved', {\n count: this.turbovecIndex.count(),\n bits: this.config.turbovecBits ?? 4,\n });\n }\n\n logDebug(`VectorManager: embedding complete \u2014 ${processed} processed, final batch size: ${currentBatchSize}`);\n\n return processed;\n }\n\n /**\n * Semantic search: embed the query and return top-N nodes by similarity.\n *\n * When sqlite-vec is available (useVecIndex: true), delegates ANN search to\n * VecIndex (fast, sub-linear). Otherwise falls back to in-process cosine\n * similarity over all stored embeddings (linear scan, no extra deps).\n *\n * Returns empty array when not initialized.\n */\n async search(query: string, topN = 10): Promise<Node[]> {\n if (!this.isInitialized() || !this.pipeline) return [];\n\n try {\n const text = `search_query: ${query}`;\n const output = await this.pipeline(text, { pooling: 'mean', normalize: true });\n const queryVec = toFloat32Array(output.data);\n\n let nodeIds: string[];\n\n if (this.pgliteIndex?.isAvailable()) {\n // Hybrid search via PGlite+pgvector \u2014 exact vector + full-text in one SQL query\n nodeIds = await this.pgliteIndex.search(query, queryVec, topN);\n } else if (this.oramaIndex?.isAvailable()) {\n // Hybrid search via Orama \u2014 full-text + vector combined\n nodeIds = await this.oramaIndex.search(query, queryVec, topN);\n } else if (this.lancedbIndex?.isAvailable()) {\n // ANN search via LanceDB \u2014 columnar Lance format, cosine metric\n nodeIds = await this.lancedbIndex.search(queryVec, topN);\n } else if (this.qdrantIndex?.isAvailable()) {\n // ANN search via Qdrant \u2014 HNSW index, cosine metric\n nodeIds = await this.qdrantIndex.search(queryVec, topN);\n } else if (this.typesenseIndex?.isAvailable()) {\n // ANN search via Typesense \u2014 HNSW index, cosine metric\n nodeIds = await this.typesenseIndex.search(queryVec, topN);\n } else if (this.turbovecIndex?.isAvailable()) {\n // ANN search via TurboVec \u2014 Rust/SIMD NEON+AVX-512 quantized index\n nodeIds = this.turbovecIndex.search(queryVec, topN);\n } else if (this.turboquantIndex?.isAvailable()) {\n // ANN search via TurboQuant \u2014 compressed in-memory index, zero native deps\n nodeIds = this.turboquantIndex.search(queryVec, topN);\n } else if (this.vecIndex?.isAvailable()) {\n // ANN search via sqlite-vec \u2014 fast, sub-linear\n nodeIds = this.vecIndex.search(queryVec, topN);\n } else {\n // In-process cosine similarity \u2014 linear scan over all stored embeddings\n const allEmbeddings = this.db.getAllEmbeddings();\n nodeIds = allEmbeddings\n .map(({ nodeId, embedding }) => ({ nodeId, score: cosine(queryVec, embedding) }))\n .filter(r => r.score >= 0.3)\n .sort((a, b) => b.score - a.score)\n .slice(0, topN)\n .map(r => r.nodeId);\n }\n\n const results: Node[] = [];\n for (const nodeId of nodeIds) {\n const node = this.db.getNode(nodeId);\n if (node) results.push(node);\n }\n return results;\n } catch (err) {\n logWarn('VectorManager: search failed', { error: String(err) });\n return [];\n }\n }\n\n /** Release engine resources (e.g. kill Qdrant child process). */\n close(): void {\n this.qdrantIndex?.close();\n this.typesenseIndex?.close();\n this.turbovecIndex?.close();\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,261 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var turboquant_index_exports = {};
30
+ __export(turboquant_index_exports, {
31
+ TurboQuantIndex: () => TurboQuantIndex,
32
+ readTurboQuantStats: () => readTurboQuantStats,
33
+ writeTurboQuantStats: () => writeTurboQuantStats
34
+ });
35
+ module.exports = __toCommonJS(turboquant_index_exports);
36
+ var path = __toESM(require("path"));
37
+ var fs = __toESM(require("fs"));
38
+ var import_errors = require("../errors");
39
+ const STATS_FILE = "turboquant-stats.json";
40
+ function readTurboQuantStats(kirographDir) {
41
+ try {
42
+ const p = path.join(kirographDir, STATS_FILE);
43
+ if (!fs.existsSync(p)) return null;
44
+ return JSON.parse(fs.readFileSync(p, "utf8"));
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+ function writeTurboQuantStats(kirographDir, patch) {
50
+ const p = path.join(kirographDir, STATS_FILE);
51
+ const tmp = p + ".tmp";
52
+ try {
53
+ const existing = readTurboQuantStats(kirographDir) ?? {
54
+ count: 0,
55
+ dim: 768,
56
+ bits: 3,
57
+ compressionRatio: 1,
58
+ actualBytes: 0,
59
+ rawBytes: 0,
60
+ savedBytes: 0,
61
+ memEnabled: false,
62
+ memCount: 0,
63
+ memActualBytes: 0,
64
+ memRawBytes: 0,
65
+ docsEnabled: false,
66
+ docsCount: 0,
67
+ docsActualBytes: 0,
68
+ docsRawBytes: 0,
69
+ updatedAt: 0
70
+ };
71
+ const updated = { ...existing, ...patch, updatedAt: Math.floor(Date.now() / 1e3) };
72
+ fs.writeFileSync(tmp, JSON.stringify(updated, null, 2));
73
+ fs.renameSync(tmp, p);
74
+ } catch (err) {
75
+ (0, import_errors.logWarn)("TurboQuantIndex: failed to write stats file", { error: String(err) });
76
+ try {
77
+ fs.unlinkSync(tmp);
78
+ } catch {
79
+ }
80
+ }
81
+ }
82
+ class TurboQuantIndex {
83
+ constructor(kirographDir, binName, dim, bits = 3) {
84
+ this.kirographDir = kirographDir;
85
+ this.binName = binName;
86
+ this.dim = dim;
87
+ this.bits = bits;
88
+ this.index = null;
89
+ this._available = false;
90
+ }
91
+ isAvailable() {
92
+ return this._available;
93
+ }
94
+ /**
95
+ * Load turboquant-js and restore index from disk if the .bin file exists.
96
+ * Silent no-op when the optional dependency is missing.
97
+ */
98
+ async initialize() {
99
+ if (this._available) return;
100
+ let TQModule;
101
+ try {
102
+ TQModule = require("turboquant-js");
103
+ } catch {
104
+ (0, import_errors.logDebug)("TurboQuantIndex: turboquant-js not installed \u2014 turboquant engine unavailable");
105
+ return;
106
+ }
107
+ const VectorIndex = TQModule?.VectorIndex ?? TQModule?.default?.VectorIndex ?? (typeof TQModule === "function" ? TQModule : null);
108
+ if (!VectorIndex || typeof VectorIndex !== "function") {
109
+ (0, import_errors.logWarn)("TurboQuantIndex: unrecognised turboquant-js module shape \u2014 turboquant engine unavailable");
110
+ return;
111
+ }
112
+ const binPath = path.join(this.kirographDir, this.binName);
113
+ try {
114
+ if (fs.existsSync(binPath) && typeof VectorIndex.fromBuffer === "function") {
115
+ const nodeBuf = fs.readFileSync(binPath);
116
+ const ab = nodeBuf.buffer.slice(nodeBuf.byteOffset, nodeBuf.byteOffset + nodeBuf.byteLength);
117
+ this.index = VectorIndex.fromBuffer(ab, { dimension: this.dim, bits: this.bits });
118
+ (0, import_errors.logDebug)("TurboQuantIndex: restored from disk", { binPath });
119
+ } else {
120
+ this.index = new VectorIndex({ dimension: this.dim, bits: this.bits });
121
+ (0, import_errors.logDebug)("TurboQuantIndex: new index", { dim: this.dim, bits: this.bits });
122
+ }
123
+ this._available = true;
124
+ } catch (err) {
125
+ (0, import_errors.logError)("TurboQuantIndex: initialization failed", { error: String(err) });
126
+ this._available = false;
127
+ this.index = null;
128
+ }
129
+ }
130
+ /** Add or update an embedding (turboquant-js compresses at index time). */
131
+ upsert(id, vec) {
132
+ if (!this._available || !this.index) return;
133
+ try {
134
+ this.index.add(id, vec);
135
+ } catch (err) {
136
+ (0, import_errors.logWarn)("TurboQuantIndex: upsert failed", { id, error: String(err) });
137
+ }
138
+ }
139
+ /** Remove an entry from the index (permanent via turboquant-js remove()). */
140
+ delete(id) {
141
+ if (!this._available || !this.index) return;
142
+ try {
143
+ this.index.remove(id);
144
+ } catch (err) {
145
+ (0, import_errors.logWarn)("TurboQuantIndex: delete failed", { id, error: String(err) });
146
+ }
147
+ }
148
+ /** ANN search: returns IDs sorted by descending similarity. */
149
+ search(queryVec, topN = 10) {
150
+ return this.searchWithScores(queryVec, topN).map((r) => r.id);
151
+ }
152
+ /**
153
+ * ANN search: returns [{id, score}] sorted by descending similarity.
154
+ * Used by MemoryVectorManager and DocsVectorManager to preserve scores for
155
+ * hybrid merge.
156
+ */
157
+ searchWithScores(queryVec, topN = 10) {
158
+ if (!this._available || !this.index) return [];
159
+ try {
160
+ const raw = this.index.search(queryVec, topN);
161
+ return this._normaliseSearchResults(raw).slice(0, topN);
162
+ } catch (err) {
163
+ (0, import_errors.logWarn)("TurboQuantIndex: search failed", { error: String(err) });
164
+ return [];
165
+ }
166
+ }
167
+ /**
168
+ * Persist the compressed index to disk atomically.
169
+ * Called once at the end of `kirograph index` — not on every upsert.
170
+ */
171
+ async save() {
172
+ if (!this._available || !this.index) return;
173
+ if (typeof this.index.toBuffer !== "function") {
174
+ (0, import_errors.logDebug)("TurboQuantIndex: toBuffer() not available \u2014 index not persisted");
175
+ return;
176
+ }
177
+ const binPath = path.join(this.kirographDir, this.binName);
178
+ const tmp = binPath + ".tmp";
179
+ try {
180
+ const raw = this.index.toBuffer();
181
+ const buf = raw instanceof ArrayBuffer ? Buffer.from(raw) : raw;
182
+ fs.writeFileSync(tmp, buf);
183
+ fs.renameSync(tmp, binPath);
184
+ (0, import_errors.logDebug)("TurboQuantIndex: saved", { binPath, bytes: buf.length });
185
+ } catch (err) {
186
+ (0, import_errors.logWarn)("TurboQuantIndex: save failed", { error: String(err) });
187
+ try {
188
+ fs.unlinkSync(tmp);
189
+ } catch {
190
+ }
191
+ }
192
+ }
193
+ /**
194
+ * Memory usage stats from the compressed index.
195
+ * Falls back to a formula-based estimate when memoryUsage is unavailable.
196
+ */
197
+ memoryStats() {
198
+ if (this._available && this.index?.memoryUsage) {
199
+ return this.index.memoryUsage;
200
+ }
201
+ const n = this.count();
202
+ const rawBytes = n * this.dim * 4;
203
+ const actualBytes = Math.ceil(n * this.dim * this.bits / 8);
204
+ return {
205
+ totalBits: actualBytes * 8,
206
+ bitsPerVector: this.bits * this.dim,
207
+ compressionRatio: actualBytes > 0 ? rawBytes / actualBytes : 1,
208
+ actualBytes
209
+ };
210
+ }
211
+ count() {
212
+ if (!this._available || !this.index) return 0;
213
+ try {
214
+ if (this.index.idToIndex instanceof Map) return this.index.idToIndex.size;
215
+ if (typeof this.index.size === "number") return this.index.size;
216
+ } catch {
217
+ }
218
+ return 0;
219
+ }
220
+ /** All IDs currently in the index. */
221
+ getEmbeddedIds() {
222
+ if (!this._available || !this.index) return [];
223
+ try {
224
+ if (this.index.idToIndex instanceof Map) {
225
+ return [...this.index.idToIndex.keys()];
226
+ }
227
+ } catch {
228
+ }
229
+ return [];
230
+ }
231
+ close() {
232
+ this.index = null;
233
+ this._available = false;
234
+ }
235
+ // ── Private ────────────────────────────────────────────────────────────────
236
+ _normaliseSearchResults(raw) {
237
+ if (!Array.isArray(raw)) return [];
238
+ return raw.map((item, rank) => {
239
+ if (item && typeof item === "object" && !Array.isArray(item)) {
240
+ const obj = item;
241
+ const id = obj["id"] ?? obj["key"] ?? "";
242
+ const score = obj["score"] ?? obj["similarity"] ?? obj["distance"] ?? 1 - rank / raw.length;
243
+ return { id, score: typeof score === "number" ? score : 1 - rank / raw.length };
244
+ }
245
+ if (Array.isArray(item) && item.length >= 2) {
246
+ return { id: String(item[0]), score: typeof item[1] === "number" ? item[1] : 1 - rank / raw.length };
247
+ }
248
+ if (typeof item === "string") {
249
+ return { id: item, score: 1 - rank / raw.length };
250
+ }
251
+ return null;
252
+ }).filter((r) => r !== null && r.id.length > 0);
253
+ }
254
+ }
255
+ // Annotate the CommonJS export names for ESM import in node:
256
+ 0 && (module.exports = {
257
+ TurboQuantIndex,
258
+ readTurboQuantStats,
259
+ writeTurboQuantStats
260
+ });
261
+ //# sourceMappingURL=turboquant-index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/vectors/turboquant-index.ts"],
4
+ "sourcesContent": ["/**\n * KiroGraph TurboQuant ANN Index\n *\n * Wraps turboquant-js (Google TurboQuant algorithm) as a drop-in ANN index.\n * Compresses embeddings at index time via Walsh-Hadamard rotation + Lloyd-Max\n * scalar quantization \u2014 zero native dependencies.\n *\n * Optional dep: npm install turboquant-js\n *\n * Algorithm: https://research.google/blog/turboquant-redefining-ai-efficiency-with-extreme-compression/\n * Implementation: https://github.com/danilodevhub/turboquant-js (by Danilo Dev)\n *\n * Falls back silently to cosine if turboquant-js is not installed.\n *\n * Compression at 3 bits (default):\n * 768-dim Float32 (3,072 bytes) \u2192 ~120 bytes (\u224825\u00D7 smaller)\n * 10K vectors: ~30 MB raw \u2192 ~1.2 MB compressed\n */\n\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { logDebug, logWarn, logError } from '../errors';\n\n// \u2500\u2500 TurboQuantStats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface TurboQuantStats {\n count: number;\n dim: number;\n bits: number;\n compressionRatio: number;\n actualBytes: number;\n rawBytes: number;\n savedBytes: number;\n memEnabled: boolean;\n memCount: number;\n memActualBytes: number;\n memRawBytes: number;\n docsEnabled: boolean;\n docsCount: number;\n docsActualBytes: number;\n docsRawBytes: number;\n updatedAt: number;\n}\n\nconst STATS_FILE = 'turboquant-stats.json';\n\nexport function readTurboQuantStats(kirographDir: string): TurboQuantStats | null {\n try {\n const p = path.join(kirographDir, STATS_FILE);\n if (!fs.existsSync(p)) return null;\n return JSON.parse(fs.readFileSync(p, 'utf8')) as TurboQuantStats;\n } catch {\n return null;\n }\n}\n\nexport function writeTurboQuantStats(kirographDir: string, patch: Partial<TurboQuantStats>): void {\n const p = path.join(kirographDir, STATS_FILE);\n const tmp = p + '.tmp';\n try {\n const existing = readTurboQuantStats(kirographDir) ?? {\n count: 0, dim: 768, bits: 3,\n compressionRatio: 1, actualBytes: 0, rawBytes: 0, savedBytes: 0,\n memEnabled: false, memCount: 0, memActualBytes: 0, memRawBytes: 0,\n docsEnabled: false, docsCount: 0, docsActualBytes: 0, docsRawBytes: 0,\n updatedAt: 0,\n };\n const updated: TurboQuantStats = { ...existing, ...patch, updatedAt: Math.floor(Date.now() / 1000) };\n fs.writeFileSync(tmp, JSON.stringify(updated, null, 2));\n fs.renameSync(tmp, p);\n } catch (err) {\n logWarn('TurboQuantIndex: failed to write stats file', { error: String(err) });\n try { fs.unlinkSync(tmp); } catch { /* ignore */ }\n }\n}\n\n// \u2500\u2500 TurboQuantIndex \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class TurboQuantIndex {\n private index: any = null;\n private _available = false;\n\n constructor(\n private readonly kirographDir: string,\n private readonly binName: string, // 'turboquant.bin' | 'turboquant-mem.bin' | 'turboquant-doc.bin'\n private readonly dim: number,\n private readonly bits: number = 3,\n ) {}\n\n isAvailable(): boolean {\n return this._available;\n }\n\n /**\n * Load turboquant-js and restore index from disk if the .bin file exists.\n * Silent no-op when the optional dependency is missing.\n */\n async initialize(): Promise<void> {\n if (this._available) return;\n\n let TQModule: any;\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n TQModule = require('turboquant-js');\n } catch {\n logDebug('TurboQuantIndex: turboquant-js not installed \u2014 turboquant engine unavailable');\n return;\n }\n\n // Handle various export shapes (default export, named export, CJS wrapper)\n const VectorIndex =\n TQModule?.VectorIndex ??\n TQModule?.default?.VectorIndex ??\n (typeof TQModule === 'function' ? TQModule : null);\n\n if (!VectorIndex || typeof VectorIndex !== 'function') {\n logWarn('TurboQuantIndex: unrecognised turboquant-js module shape \u2014 turboquant engine unavailable');\n return;\n }\n\n const binPath = path.join(this.kirographDir, this.binName);\n\n try {\n if (fs.existsSync(binPath) && typeof VectorIndex.fromBuffer === 'function') {\n const nodeBuf = fs.readFileSync(binPath);\n // fromBuffer() requires a pure ArrayBuffer, not a Node Buffer/Uint8Array\n const ab = nodeBuf.buffer.slice(nodeBuf.byteOffset, nodeBuf.byteOffset + nodeBuf.byteLength);\n this.index = VectorIndex.fromBuffer(ab, { dimension: this.dim, bits: this.bits });\n logDebug('TurboQuantIndex: restored from disk', { binPath });\n } else {\n this.index = new VectorIndex({ dimension: this.dim, bits: this.bits });\n logDebug('TurboQuantIndex: new index', { dim: this.dim, bits: this.bits });\n }\n this._available = true;\n } catch (err) {\n logError('TurboQuantIndex: initialization failed', { error: String(err) });\n this._available = false;\n this.index = null;\n }\n }\n\n /** Add or update an embedding (turboquant-js compresses at index time). */\n upsert(id: string, vec: Float32Array): void {\n if (!this._available || !this.index) return;\n try {\n this.index.add(id, vec);\n } catch (err) {\n logWarn('TurboQuantIndex: upsert failed', { id, error: String(err) });\n }\n }\n\n /** Remove an entry from the index (permanent via turboquant-js remove()). */\n delete(id: string): void {\n if (!this._available || !this.index) return;\n try {\n this.index.remove(id);\n } catch (err) {\n logWarn('TurboQuantIndex: delete failed', { id, error: String(err) });\n }\n }\n\n /** ANN search: returns IDs sorted by descending similarity. */\n search(queryVec: Float32Array, topN = 10): string[] {\n return this.searchWithScores(queryVec, topN).map(r => r.id);\n }\n\n /**\n * ANN search: returns [{id, score}] sorted by descending similarity.\n * Used by MemoryVectorManager and DocsVectorManager to preserve scores for\n * hybrid merge.\n */\n searchWithScores(queryVec: Float32Array, topN = 10): Array<{ id: string; score: number }> {\n if (!this._available || !this.index) return [];\n try {\n const raw: unknown = this.index.search(queryVec, topN);\n return this._normaliseSearchResults(raw).slice(0, topN);\n } catch (err) {\n logWarn('TurboQuantIndex: search failed', { error: String(err) });\n return [];\n }\n }\n\n /**\n * Persist the compressed index to disk atomically.\n * Called once at the end of `kirograph index` \u2014 not on every upsert.\n */\n async save(): Promise<void> {\n if (!this._available || !this.index) return;\n if (typeof this.index.toBuffer !== 'function') {\n logDebug('TurboQuantIndex: toBuffer() not available \u2014 index not persisted');\n return;\n }\n\n const binPath = path.join(this.kirographDir, this.binName);\n const tmp = binPath + '.tmp';\n try {\n const raw = this.index.toBuffer();\n // toBuffer() may return ArrayBuffer \u2014 Node fs requires Buffer/TypedArray\n const buf = raw instanceof ArrayBuffer ? Buffer.from(raw) : (raw as Buffer);\n fs.writeFileSync(tmp, buf);\n fs.renameSync(tmp, binPath);\n logDebug('TurboQuantIndex: saved', { binPath, bytes: buf.length });\n } catch (err) {\n logWarn('TurboQuantIndex: save failed', { error: String(err) });\n try { fs.unlinkSync(tmp); } catch { /* ignore */ }\n }\n }\n\n /**\n * Memory usage stats from the compressed index.\n * Falls back to a formula-based estimate when memoryUsage is unavailable.\n */\n memoryStats(): { totalBits: number; bitsPerVector: number; compressionRatio: number; actualBytes: number } {\n if (this._available && this.index?.memoryUsage) {\n return this.index.memoryUsage as { totalBits: number; bitsPerVector: number; compressionRatio: number; actualBytes: number };\n }\n const n = this.count();\n const rawBytes = n * this.dim * 4;\n const actualBytes = Math.ceil(n * this.dim * this.bits / 8);\n return {\n totalBits: actualBytes * 8,\n bitsPerVector: this.bits * this.dim,\n compressionRatio: actualBytes > 0 ? rawBytes / actualBytes : 1,\n actualBytes,\n };\n }\n\n count(): number {\n if (!this._available || !this.index) return 0;\n try {\n // idToIndex is an internal Map \u2014 reliable count that survives remove()\n if (this.index.idToIndex instanceof Map) return this.index.idToIndex.size;\n if (typeof this.index.size === 'number') return this.index.size;\n } catch { /* ignore */ }\n return 0;\n }\n\n /** All IDs currently in the index. */\n getEmbeddedIds(): string[] {\n if (!this._available || !this.index) return [];\n try {\n if (this.index.idToIndex instanceof Map) {\n return [...this.index.idToIndex.keys()];\n }\n } catch { /* ignore */ }\n return [];\n }\n\n close(): void {\n this.index = null;\n this._available = false;\n }\n\n // \u2500\u2500 Private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private _normaliseSearchResults(raw: unknown): Array<{ id: string; score: number }> {\n if (!Array.isArray(raw)) return [];\n\n return raw\n .map((item: unknown, rank: number) => {\n if (item && typeof item === 'object' && !Array.isArray(item)) {\n const obj = item as Record<string, unknown>;\n const id = (obj['id'] ?? obj['key'] ?? '') as string;\n const score = (obj['score'] ?? obj['similarity'] ?? obj['distance'] ?? (1 - rank / raw.length)) as number;\n return { id, score: typeof score === 'number' ? score : 1 - rank / raw.length };\n }\n if (Array.isArray(item) && item.length >= 2) {\n return { id: String(item[0]), score: typeof item[1] === 'number' ? item[1] : 1 - rank / raw.length };\n }\n if (typeof item === 'string') {\n return { id: item, score: 1 - rank / raw.length };\n }\n return null;\n })\n .filter((r): r is { id: string; score: number } => r !== null && r.id.length > 0);\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }