@workglow/knowledge-base 0.0.117 → 0.0.119

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.
@@ -8,7 +8,7 @@
8
8
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { ChunkRecord } from \"../chunk/ChunkSchema\";\nimport type { DocumentMetadata, DocumentNode } from \"./DocumentSchema\";\n\n/**\n * Document represents a hierarchical document with chunks\n *\n * Key features:\n * - Single source-of-truth tree structure (root node)\n * - Single set of chunks\n * - Separate persistence for document structure vs vectors\n */\nexport class Document {\n public doc_id: string | undefined;\n public readonly metadata: DocumentMetadata;\n public readonly root: DocumentNode;\n private chunks: ChunkRecord[];\n\n constructor(\n root: DocumentNode,\n metadata: DocumentMetadata,\n chunks: ChunkRecord[] = [],\n doc_id?: string\n ) {\n this.doc_id = doc_id;\n this.root = root;\n this.metadata = metadata;\n this.chunks = chunks || [];\n }\n\n /**\n * Set chunks for the document\n */\n setChunks(chunks: ChunkRecord[]): void {\n this.chunks = chunks;\n }\n\n /**\n * Get all chunks\n */\n getChunks(): ChunkRecord[] {\n return this.chunks;\n }\n\n /**\n * Set the document ID\n */\n setDocId(doc_id: string): void {\n this.doc_id = doc_id;\n }\n\n /**\n * Find chunks by nodeId\n */\n findChunksByNodeId(nodeId: string): ChunkRecord[] {\n return this.chunks.filter((chunk) => chunk.nodePath.includes(nodeId));\n }\n\n /**\n * Serialize to JSON\n */\n toJSON(): {\n metadata: DocumentMetadata;\n root: DocumentNode;\n chunks: ChunkRecord[];\n } {\n return {\n metadata: this.metadata,\n root: this.root,\n chunks: this.chunks,\n };\n }\n\n /**\n * Deserialize from JSON\n */\n static fromJSON(json: string, doc_id?: string): Document {\n const obj = JSON.parse(json);\n return new Document(obj.root, obj.metadata, obj.chunks, doc_id);\n }\n}\n",
9
9
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { HybridSearchOptions, VectorSearchOptions } from \"@workglow/storage\";\nimport type { TypedArray } from \"@workglow/util\";\nimport type { ChunkRecord } from \"../chunk/ChunkSchema\";\nimport type {\n ChunkSearchResult,\n ChunkVectorEntity,\n ChunkVectorStorage,\n InsertChunkVectorEntity,\n} from \"../chunk/ChunkVectorStorageSchema\";\nimport { Document } from \"../document/Document\";\nimport type { DocumentNode } from \"../document/DocumentSchema\";\nimport type {\n DocumentStorageEntity,\n DocumentTabularStorage,\n InsertDocumentStorageEntity,\n} from \"../document/DocumentStorageSchema\";\n\n/**\n * Unified KnowledgeBase that owns both document and vector storage,\n * providing lifecycle management and cascading deletes.\n */\nexport class KnowledgeBase {\n readonly name: string;\n readonly title: string;\n readonly description: string;\n private tabularStorage: DocumentTabularStorage;\n private chunkStorage: ChunkVectorStorage;\n\n constructor(\n name: string,\n documentStorage: DocumentTabularStorage,\n chunkStorage: ChunkVectorStorage,\n title?: string,\n description?: string\n ) {\n this.name = name;\n this.title = title ?? name;\n this.description = description ?? \"\";\n this.tabularStorage = documentStorage;\n this.chunkStorage = chunkStorage;\n }\n\n // ===========================================================================\n // Document CRUD\n // ===========================================================================\n\n /**\n * Upsert a document.\n * @returns The document with the generated doc_id if it was auto-generated\n */\n async upsertDocument(document: Document): Promise<Document> {\n const serialized = JSON.stringify(document.toJSON());\n\n const insertEntity: InsertDocumentStorageEntity = {\n doc_id: document.doc_id,\n data: serialized,\n };\n const entity = await this.tabularStorage.put(insertEntity);\n\n if (document.doc_id !== entity.doc_id) {\n document.setDocId(entity.doc_id);\n }\n return document;\n }\n\n /**\n * Get a document by ID\n */\n async getDocument(doc_id: string): Promise<Document | undefined> {\n const entity = await this.tabularStorage.get({ doc_id });\n if (!entity) {\n return undefined;\n }\n return Document.fromJSON(entity.data, entity.doc_id);\n }\n\n /**\n * Delete a document and all its chunks (cascading delete).\n */\n async deleteDocument(doc_id: string): Promise<void> {\n await this.deleteChunksForDocument(doc_id);\n await this.tabularStorage.delete({ doc_id });\n }\n\n /**\n * List all document IDs\n */\n async listDocuments(): Promise<string[]> {\n const entities = await this.tabularStorage.getAll();\n if (!entities) {\n return [];\n }\n return entities.map((e: DocumentStorageEntity) => e.doc_id);\n }\n\n // ===========================================================================\n // Tree traversal\n // ===========================================================================\n\n /**\n * Get a specific node by ID from a document\n */\n async getNode(doc_id: string, nodeId: string): Promise<DocumentNode | undefined> {\n const doc = await this.getDocument(doc_id);\n if (!doc) {\n return undefined;\n }\n\n const traverse = (node: DocumentNode): DocumentNode | undefined => {\n if (node.nodeId === nodeId) {\n return node;\n }\n if (\"children\" in node && Array.isArray(node.children)) {\n for (const child of node.children) {\n const found = traverse(child);\n if (found) return found;\n }\n }\n return undefined;\n };\n\n return traverse(doc.root);\n }\n\n /**\n * Get ancestors of a node (from root to target node)\n */\n async getAncestors(doc_id: string, nodeId: string): Promise<DocumentNode[]> {\n const doc = await this.getDocument(doc_id);\n if (!doc) {\n return [];\n }\n\n const path: string[] = [];\n const findPath = (node: DocumentNode): boolean => {\n path.push(node.nodeId);\n if (node.nodeId === nodeId) {\n return true;\n }\n if (\"children\" in node && Array.isArray(node.children)) {\n for (const child of node.children) {\n if (findPath(child)) {\n return true;\n }\n }\n }\n path.pop();\n return false;\n };\n\n if (!findPath(doc.root)) {\n return [];\n }\n\n const ancestors: DocumentNode[] = [];\n let currentNode: DocumentNode = doc.root;\n ancestors.push(currentNode);\n\n for (let i = 1; i < path.length; i++) {\n const targetId = path[i];\n if (\"children\" in currentNode && Array.isArray(currentNode.children)) {\n const found = currentNode.children.find((child: DocumentNode) => child.nodeId === targetId);\n if (found) {\n currentNode = found;\n ancestors.push(currentNode);\n } else {\n break;\n }\n } else {\n break;\n }\n }\n\n return ancestors;\n }\n\n // ===========================================================================\n // Chunk CRUD\n // ===========================================================================\n\n /**\n * Upsert a single chunk vector entity\n */\n async upsertChunk(chunk: InsertChunkVectorEntity): Promise<ChunkVectorEntity> {\n if (chunk.vector.length !== this.getVectorDimensions()) {\n throw new Error(\n `Vector dimension mismatch: expected ${this.getVectorDimensions()}, got ${chunk.vector.length}.`\n );\n }\n return this.chunkStorage.put(chunk);\n }\n\n /**\n * Upsert multiple chunk vector entities\n */\n async upsertChunksBulk(chunks: InsertChunkVectorEntity[]): Promise<ChunkVectorEntity[]> {\n const expected = this.getVectorDimensions();\n for (const chunk of chunks) {\n if (chunk.vector.length !== expected) {\n throw new Error(\n `Vector dimension mismatch: expected ${expected}, got ${chunk.vector.length}.`\n );\n }\n }\n return this.chunkStorage.putBulk(chunks);\n }\n\n /**\n * Delete all chunks for a specific document\n */\n async deleteChunksForDocument(doc_id: string): Promise<void> {\n await this.chunkStorage.deleteSearch({ doc_id });\n }\n\n /**\n * Get all chunks for a specific document\n */\n async getChunksForDocument(doc_id: string): Promise<ChunkVectorEntity[]> {\n const results = await this.chunkStorage.query({ doc_id });\n return (results ?? []) as ChunkVectorEntity[];\n }\n\n // ===========================================================================\n // Search\n // ===========================================================================\n\n /**\n * Search for similar chunks using vector similarity\n */\n async similaritySearch(\n query: TypedArray,\n options?: VectorSearchOptions<ChunkRecord>\n ): Promise<ChunkSearchResult[]> {\n return this.chunkStorage.similaritySearch(query, options);\n }\n\n /**\n * Hybrid search combining vector similarity and full-text search\n */\n async hybridSearch(\n query: TypedArray,\n options: HybridSearchOptions<ChunkRecord>\n ): Promise<ChunkSearchResult[]> {\n if (typeof this.chunkStorage.hybridSearch !== \"function\") {\n throw new Error(\n \"Hybrid search is not supported by the configured chunk storage backend. \" +\n \"Please use a vector storage implementation that provides `hybridSearch`.\"\n );\n }\n return this.chunkStorage.hybridSearch(query, options);\n }\n\n // ===========================================================================\n // Lifecycle\n // ===========================================================================\n\n /**\n * Prepare a document for re-indexing: deletes all chunks but keeps the document.\n * @returns The document if found, undefined otherwise\n */\n async prepareReindex(doc_id: string): Promise<Document | undefined> {\n const doc = await this.getDocument(doc_id);\n if (!doc) {\n return undefined;\n }\n await this.deleteChunksForDocument(doc_id);\n return doc;\n }\n\n /**\n * Setup the underlying databases\n */\n async setupDatabase(): Promise<void> {\n await this.tabularStorage.setupDatabase();\n await this.chunkStorage.setupDatabase();\n }\n\n /**\n * Destroy storage instances\n */\n destroy(): void {\n this.tabularStorage.destroy();\n this.chunkStorage.destroy();\n }\n\n // ===========================================================================\n // Accessors\n // ===========================================================================\n\n /**\n * Get a chunk by ID\n */\n async getChunk(chunk_id: string): Promise<ChunkVectorEntity | undefined> {\n return this.chunkStorage.get({ chunk_id });\n }\n\n /**\n * Store a single chunk (alias for upsertChunk)\n */\n async put(chunk: InsertChunkVectorEntity): Promise<ChunkVectorEntity> {\n return this.chunkStorage.put(chunk);\n }\n\n /**\n * Store multiple chunks (alias for upsertChunksBulk)\n */\n async putBulk(chunks: InsertChunkVectorEntity[]): Promise<ChunkVectorEntity[]> {\n return this.chunkStorage.putBulk(chunks);\n }\n\n /**\n * Get all chunks\n */\n async getAllChunks(): Promise<ChunkVectorEntity[] | undefined> {\n return this.chunkStorage.getAll() as Promise<ChunkVectorEntity[] | undefined>;\n }\n\n /**\n * Get chunk count\n */\n async chunkCount(): Promise<number> {\n return this.chunkStorage.size();\n }\n\n /**\n * Clear all chunks\n */\n async clearChunks(): Promise<void> {\n return this.chunkStorage.deleteAll();\n }\n\n /**\n * Get vector dimensions\n */\n getVectorDimensions(): number {\n return this.chunkStorage.getVectorDimensions();\n }\n\n // ===========================================================================\n // Document chunk helpers\n // ===========================================================================\n\n /**\n * Get chunks from the document JSON (not from vector storage)\n */\n async getDocumentChunks(doc_id: string): Promise<ChunkRecord[]> {\n const doc = await this.getDocument(doc_id);\n if (!doc) {\n return [];\n }\n return doc.getChunks();\n }\n\n /**\n * Find chunks in document JSON that contain a specific nodeId in their path\n */\n async findChunksByNodeId(doc_id: string, nodeId: string): Promise<ChunkRecord[]> {\n const doc = await this.getDocument(doc_id);\n if (!doc) {\n return [];\n }\n return doc.findChunksByNodeId(nodeId);\n }\n}\n",
10
10
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { DataPortSchemaObject, FromSchema } from \"@workglow/util\";\n\n/**\n * Schema for persisting KnowledgeBase metadata to tabular storage.\n */\nexport const KnowledgeBaseRecordSchema = {\n type: \"object\",\n properties: {\n kb_id: { type: \"string\" },\n title: { type: \"string\" },\n description: { type: \"string\" },\n vector_dimensions: { type: \"integer\" },\n document_table: { type: \"string\" },\n chunk_table: { type: \"string\" },\n created_at: { type: \"string\" },\n updated_at: { type: \"string\" },\n },\n required: [\n \"kb_id\",\n \"title\",\n \"description\",\n \"vector_dimensions\",\n \"document_table\",\n \"chunk_table\",\n \"created_at\",\n \"updated_at\",\n ],\n additionalProperties: false,\n} as const satisfies DataPortSchemaObject;\n\nexport type KnowledgeBaseRecord = FromSchema<typeof KnowledgeBaseRecordSchema>;\nexport const KnowledgeBasePrimaryKeyNames = [\"kb_id\"] as const;\n\n/**\n * Generates SQL-safe table names for a knowledge base's document and chunk storage.\n */\nexport function knowledgeBaseTableNames(kbId: string): {\n readonly documentTable: string;\n readonly chunkTable: string;\n} {\n const safe = kbId.replace(/[^a-zA-Z0-9_]/g, \"_\");\n return {\n documentTable: `kb_docs_${safe}`,\n chunkTable: `kb_chunks_${safe}`,\n };\n}\n",
11
- "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { type BaseTabularStorage } from \"@workglow/storage\";\nimport { EventEmitter, type EventParameters } from \"@workglow/util\";\n\nimport {\n KnowledgeBasePrimaryKeyNames,\n type KnowledgeBaseRecord,\n KnowledgeBaseRecordSchema,\n} from \"./KnowledgeBaseSchema\";\n\n/**\n * Events that can be emitted by the KnowledgeBaseRepository\n */\n\nexport type KnowledgeBaseEventListeners = {\n knowledge_base_added: (record: KnowledgeBaseRecord) => void;\n knowledge_base_removed: (record: KnowledgeBaseRecord) => void;\n knowledge_base_updated: (record: KnowledgeBaseRecord) => void;\n};\n\nexport type KnowledgeBaseEvents = keyof KnowledgeBaseEventListeners;\n\nexport type KnowledgeBaseEventListener<Event extends KnowledgeBaseEvents> =\n KnowledgeBaseEventListeners[Event];\n\nexport type KnowledgeBaseEventParameters<Event extends KnowledgeBaseEvents> = EventParameters<\n KnowledgeBaseEventListeners,\n Event\n>;\n\n/**\n * Repository for persisting KnowledgeBase metadata to tabular storage.\n * Follows the same pattern as ModelRepository.\n */\nexport class KnowledgeBaseRepository {\n /**\n * Storage for KnowledgeBase records\n */\n protected readonly storage: BaseTabularStorage<\n typeof KnowledgeBaseRecordSchema,\n typeof KnowledgeBasePrimaryKeyNames\n >;\n\n constructor(\n storage: BaseTabularStorage<\n typeof KnowledgeBaseRecordSchema,\n typeof KnowledgeBasePrimaryKeyNames\n >\n ) {\n this.storage = storage;\n }\n\n /** Event emitter for repository events */\n protected events = new EventEmitter<KnowledgeBaseEventListeners>();\n\n /**\n * Sets up the database for the repository.\n * Must be called before using any other methods.\n */\n async setupDatabase(): Promise<void> {\n await this.storage.setupDatabase?.();\n }\n\n /**\n * Registers an event listener for the specified event\n */\n on<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.on(name, fn);\n }\n\n /**\n * Removes an event listener for the specified event\n */\n off<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.off(name, fn);\n }\n\n /**\n * Adds an event listener that will only be called once\n */\n once<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.once(name, fn);\n }\n\n /**\n * Returns when the event was emitted (promise form of once)\n */\n waitOn<Event extends KnowledgeBaseEvents>(name: Event) {\n return this.events.waitOn(name);\n }\n\n /**\n * Adds a new knowledge base record to the repository\n */\n async addKnowledgeBase(record: KnowledgeBaseRecord): Promise<KnowledgeBaseRecord> {\n await this.storage.put(record);\n this.events.emit(\"knowledge_base_added\", record);\n return record;\n }\n\n /**\n * Removes a knowledge base record from the repository\n */\n async removeKnowledgeBase(kb_id: string): Promise<void> {\n const record = await this.storage.get({ kb_id });\n if (!record) {\n throw new Error(`KnowledgeBase with id \"${kb_id}\" not found`);\n }\n await this.storage.delete({ kb_id });\n this.events.emit(\"knowledge_base_removed\", record);\n }\n\n /**\n * Retrieves a knowledge base record by ID\n */\n async getKnowledgeBase(kb_id: string): Promise<KnowledgeBaseRecord | undefined> {\n if (typeof kb_id !== \"string\") return undefined;\n const record = await this.storage.get({ kb_id });\n return record ?? undefined;\n }\n\n /**\n * Enumerates all knowledge base records\n */\n async enumerateAll(): Promise<KnowledgeBaseRecord[]> {\n const records = await this.storage.getAll();\n if (!records || records.length === 0) return [];\n return records;\n }\n\n /**\n * Gets the total number of knowledge base records\n */\n async size(): Promise<number> {\n return await this.storage.size();\n }\n}\n",
11
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { type ITabularStorage } from \"@workglow/storage\";\nimport { EventEmitter, type EventParameters } from \"@workglow/util\";\n\nimport {\n KnowledgeBasePrimaryKeyNames,\n type KnowledgeBaseRecord,\n KnowledgeBaseRecordSchema,\n} from \"./KnowledgeBaseSchema\";\n\n/**\n * Events that can be emitted by the KnowledgeBaseRepository\n */\n\nexport type KnowledgeBaseEventListeners = {\n knowledge_base_added: (record: KnowledgeBaseRecord) => void;\n knowledge_base_removed: (record: KnowledgeBaseRecord) => void;\n knowledge_base_updated: (record: KnowledgeBaseRecord) => void;\n};\n\nexport type KnowledgeBaseEvents = keyof KnowledgeBaseEventListeners;\n\nexport type KnowledgeBaseEventListener<Event extends KnowledgeBaseEvents> =\n KnowledgeBaseEventListeners[Event];\n\nexport type KnowledgeBaseEventParameters<Event extends KnowledgeBaseEvents> = EventParameters<\n KnowledgeBaseEventListeners,\n Event\n>;\n\n/**\n * Repository for persisting KnowledgeBase metadata to tabular storage.\n * Follows the same pattern as ModelRepository.\n */\nexport class KnowledgeBaseRepository {\n /**\n * Storage for KnowledgeBase records\n */\n protected readonly storage: ITabularStorage<\n typeof KnowledgeBaseRecordSchema,\n typeof KnowledgeBasePrimaryKeyNames\n >;\n\n constructor(\n storage: ITabularStorage<\n typeof KnowledgeBaseRecordSchema,\n typeof KnowledgeBasePrimaryKeyNames\n >\n ) {\n this.storage = storage;\n }\n\n /** Event emitter for repository events */\n protected events = new EventEmitter<KnowledgeBaseEventListeners>();\n\n /**\n * Sets up the database for the repository.\n * Must be called before using any other methods.\n */\n async setupDatabase(): Promise<void> {\n await this.storage.setupDatabase?.();\n }\n\n /**\n * Registers an event listener for the specified event\n */\n on<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.on(name, fn);\n }\n\n /**\n * Removes an event listener for the specified event\n */\n off<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.off(name, fn);\n }\n\n /**\n * Adds an event listener that will only be called once\n */\n once<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.once(name, fn);\n }\n\n /**\n * Returns when the event was emitted (promise form of once)\n */\n waitOn<Event extends KnowledgeBaseEvents>(name: Event) {\n return this.events.waitOn(name);\n }\n\n /**\n * Adds a new knowledge base record to the repository\n */\n async addKnowledgeBase(record: KnowledgeBaseRecord): Promise<KnowledgeBaseRecord> {\n await this.storage.put(record);\n this.events.emit(\"knowledge_base_added\", record);\n return record;\n }\n\n /**\n * Removes a knowledge base record from the repository\n */\n async removeKnowledgeBase(kb_id: string): Promise<void> {\n const record = await this.storage.get({ kb_id });\n if (!record) {\n throw new Error(`KnowledgeBase with id \"${kb_id}\" not found`);\n }\n await this.storage.delete({ kb_id });\n this.events.emit(\"knowledge_base_removed\", record);\n }\n\n /**\n * Retrieves a knowledge base record by ID\n */\n async getKnowledgeBase(kb_id: string): Promise<KnowledgeBaseRecord | undefined> {\n if (typeof kb_id !== \"string\") return undefined;\n const record = await this.storage.get({ kb_id });\n return record ?? undefined;\n }\n\n /**\n * Enumerates all knowledge base records\n */\n async enumerateAll(): Promise<KnowledgeBaseRecord[]> {\n const records = await this.storage.getAll();\n if (!records || records.length === 0) return [];\n return records;\n }\n\n /**\n * Gets the total number of knowledge base records\n */\n async size(): Promise<number> {\n return await this.storage.size();\n }\n}\n",
12
12
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { InMemoryTabularStorage } from \"@workglow/storage\";\nimport { KnowledgeBaseRepository } from \"./KnowledgeBaseRepository\";\nimport { KnowledgeBasePrimaryKeyNames, KnowledgeBaseRecordSchema } from \"./KnowledgeBaseSchema\";\n\n/**\n * In-memory implementation of a knowledge base repository.\n */\nexport class InMemoryKnowledgeBaseRepository extends KnowledgeBaseRepository {\n constructor() {\n super(new InMemoryTabularStorage(KnowledgeBaseRecordSchema, KnowledgeBasePrimaryKeyNames));\n }\n}\n",
13
13
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {\n createServiceToken,\n globalServiceRegistry,\n registerInputResolver,\n ServiceRegistry,\n} from \"@workglow/util\";\nimport { InMemoryKnowledgeBaseRepository } from \"./InMemoryKnowledgeBaseRepository\";\nimport type { KnowledgeBase } from \"./KnowledgeBase\";\nimport { KnowledgeBaseRepository } from \"./KnowledgeBaseRepository\";\nimport { knowledgeBaseTableNames, type KnowledgeBaseRecord } from \"./KnowledgeBaseSchema\";\n\n/**\n * Service token for the knowledge base registry\n * Maps knowledge base IDs to KnowledgeBase instances\n */\nexport const KNOWLEDGE_BASES =\n createServiceToken<Map<string, KnowledgeBase>>(\"knowledge-base.registry\");\n\n/**\n * Service token for the knowledge base repository\n */\nexport const KNOWLEDGE_BASE_REPOSITORY = createServiceToken<KnowledgeBaseRepository>(\n \"knowledge-base.repository\"\n);\n\n// Register default factory for live KB map if not already registered\nif (!globalServiceRegistry.has(KNOWLEDGE_BASES)) {\n globalServiceRegistry.register(\n KNOWLEDGE_BASES,\n (): Map<string, KnowledgeBase> => new Map(),\n true\n );\n}\n\n// Register default factory for KB repository if not already registered\nif (!globalServiceRegistry.has(KNOWLEDGE_BASE_REPOSITORY)) {\n globalServiceRegistry.register(\n KNOWLEDGE_BASE_REPOSITORY,\n (): KnowledgeBaseRepository => new InMemoryKnowledgeBaseRepository(),\n true\n );\n}\n\n/**\n * Gets the global knowledge base registry\n */\nexport function getGlobalKnowledgeBases(): Map<string, KnowledgeBase> {\n return globalServiceRegistry.get(KNOWLEDGE_BASES);\n}\n\n/**\n * Gets the global knowledge base repository instance\n */\nexport function getGlobalKnowledgeBaseRepository(): KnowledgeBaseRepository {\n return globalServiceRegistry.get(KNOWLEDGE_BASE_REPOSITORY);\n}\n\n/**\n * Sets the global knowledge base repository instance\n */\nexport function setGlobalKnowledgeBaseRepository(repository: KnowledgeBaseRepository): void {\n globalServiceRegistry.registerInstance(KNOWLEDGE_BASE_REPOSITORY, repository);\n}\n\n/**\n * Registers a knowledge base globally by ID.\n * Adds to both the live Map and the persistent repository.\n */\nexport async function registerKnowledgeBase(id: string, kb: KnowledgeBase): Promise<void> {\n const kbs = getGlobalKnowledgeBases();\n kbs.set(id, kb);\n\n const now = new Date().toISOString();\n const tableNames = knowledgeBaseTableNames(id);\n const record: KnowledgeBaseRecord = {\n kb_id: id,\n title: kb.title,\n description: kb.description,\n vector_dimensions: kb.getVectorDimensions(),\n document_table: tableNames.documentTable,\n chunk_table: tableNames.chunkTable,\n created_at: now,\n updated_at: now,\n };\n const repo = getGlobalKnowledgeBaseRepository();\n await repo.addKnowledgeBase(record);\n}\n\n/**\n * Gets a knowledge base by ID from the global registry\n */\nexport function getKnowledgeBase(id: string): KnowledgeBase | undefined {\n return getGlobalKnowledgeBases().get(id);\n}\n\n/**\n * Resolves a knowledge base ID from the registry.\n * Used by the input resolver system.\n */\nasync function resolveKnowledgeBaseFromRegistry(\n id: string,\n format: string,\n registry: ServiceRegistry\n): Promise<KnowledgeBase> {\n const kbs = registry.has(KNOWLEDGE_BASES)\n ? registry.get<Map<string, KnowledgeBase>>(KNOWLEDGE_BASES)\n : getGlobalKnowledgeBases();\n\n const kb = kbs.get(id);\n if (!kb) {\n throw new Error(`Knowledge base \"${id}\" not found in registry`);\n }\n return kb;\n}\n\n// Register the resolver for format: \"knowledge-base\"\nregisterInputResolver(\"knowledge-base\", resolveKnowledgeBaseFromRegistry);\n",
14
14
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { InMemoryTabularStorage, InMemoryVectorStorage } from \"@workglow/storage\";\nimport type { TypedArray } from \"@workglow/util\";\nimport { ChunkVectorPrimaryKey, ChunkVectorStorageSchema } from \"../chunk/ChunkVectorStorageSchema\";\nimport type { ChunkVectorStorage } from \"../chunk/ChunkVectorStorageSchema\";\nimport { DocumentStorageKey, DocumentStorageSchema } from \"../document/DocumentStorageSchema\";\nimport type { DocumentTabularStorage } from \"../document/DocumentStorageSchema\";\nimport { KnowledgeBase } from \"./KnowledgeBase\";\nimport { registerKnowledgeBase } from \"./KnowledgeBaseRegistry\";\n\nexport interface CreateKnowledgeBaseOptions {\n readonly name: string;\n readonly vectorDimensions: number;\n readonly vectorType?: { new (array: number[]): TypedArray };\n readonly register?: boolean;\n readonly title?: string;\n readonly description?: string;\n}\n\n/**\n * Factory function to create a KnowledgeBase with minimal configuration.\n *\n * @example\n * ```typescript\n * const kb = await createKnowledgeBase({\n * name: \"my-kb\",\n * vectorDimensions: 1024,\n * });\n * ```\n */\nexport async function createKnowledgeBase(\n options: CreateKnowledgeBaseOptions\n): Promise<KnowledgeBase> {\n const {\n name,\n vectorDimensions,\n vectorType = Float32Array,\n register: shouldRegister = true,\n title,\n description,\n } = options;\n\n const tabularStorage = new InMemoryTabularStorage(DocumentStorageSchema, DocumentStorageKey);\n await tabularStorage.setupDatabase();\n\n const vectorStorage = new InMemoryVectorStorage(\n ChunkVectorStorageSchema,\n ChunkVectorPrimaryKey,\n [],\n vectorDimensions,\n vectorType\n );\n await vectorStorage.setupDatabase();\n\n const kb = new KnowledgeBase(\n name,\n tabularStorage as unknown as DocumentTabularStorage,\n vectorStorage as unknown as ChunkVectorStorage,\n title,\n description\n );\n\n if (shouldRegister) {\n await registerKnowledgeBase(name, kb);\n }\n\n return kb;\n}\n",
package/dist/bun.js.map CHANGED
@@ -8,7 +8,7 @@
8
8
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { ChunkRecord } from \"../chunk/ChunkSchema\";\nimport type { DocumentMetadata, DocumentNode } from \"./DocumentSchema\";\n\n/**\n * Document represents a hierarchical document with chunks\n *\n * Key features:\n * - Single source-of-truth tree structure (root node)\n * - Single set of chunks\n * - Separate persistence for document structure vs vectors\n */\nexport class Document {\n public doc_id: string | undefined;\n public readonly metadata: DocumentMetadata;\n public readonly root: DocumentNode;\n private chunks: ChunkRecord[];\n\n constructor(\n root: DocumentNode,\n metadata: DocumentMetadata,\n chunks: ChunkRecord[] = [],\n doc_id?: string\n ) {\n this.doc_id = doc_id;\n this.root = root;\n this.metadata = metadata;\n this.chunks = chunks || [];\n }\n\n /**\n * Set chunks for the document\n */\n setChunks(chunks: ChunkRecord[]): void {\n this.chunks = chunks;\n }\n\n /**\n * Get all chunks\n */\n getChunks(): ChunkRecord[] {\n return this.chunks;\n }\n\n /**\n * Set the document ID\n */\n setDocId(doc_id: string): void {\n this.doc_id = doc_id;\n }\n\n /**\n * Find chunks by nodeId\n */\n findChunksByNodeId(nodeId: string): ChunkRecord[] {\n return this.chunks.filter((chunk) => chunk.nodePath.includes(nodeId));\n }\n\n /**\n * Serialize to JSON\n */\n toJSON(): {\n metadata: DocumentMetadata;\n root: DocumentNode;\n chunks: ChunkRecord[];\n } {\n return {\n metadata: this.metadata,\n root: this.root,\n chunks: this.chunks,\n };\n }\n\n /**\n * Deserialize from JSON\n */\n static fromJSON(json: string, doc_id?: string): Document {\n const obj = JSON.parse(json);\n return new Document(obj.root, obj.metadata, obj.chunks, doc_id);\n }\n}\n",
9
9
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { HybridSearchOptions, VectorSearchOptions } from \"@workglow/storage\";\nimport type { TypedArray } from \"@workglow/util\";\nimport type { ChunkRecord } from \"../chunk/ChunkSchema\";\nimport type {\n ChunkSearchResult,\n ChunkVectorEntity,\n ChunkVectorStorage,\n InsertChunkVectorEntity,\n} from \"../chunk/ChunkVectorStorageSchema\";\nimport { Document } from \"../document/Document\";\nimport type { DocumentNode } from \"../document/DocumentSchema\";\nimport type {\n DocumentStorageEntity,\n DocumentTabularStorage,\n InsertDocumentStorageEntity,\n} from \"../document/DocumentStorageSchema\";\n\n/**\n * Unified KnowledgeBase that owns both document and vector storage,\n * providing lifecycle management and cascading deletes.\n */\nexport class KnowledgeBase {\n readonly name: string;\n readonly title: string;\n readonly description: string;\n private tabularStorage: DocumentTabularStorage;\n private chunkStorage: ChunkVectorStorage;\n\n constructor(\n name: string,\n documentStorage: DocumentTabularStorage,\n chunkStorage: ChunkVectorStorage,\n title?: string,\n description?: string\n ) {\n this.name = name;\n this.title = title ?? name;\n this.description = description ?? \"\";\n this.tabularStorage = documentStorage;\n this.chunkStorage = chunkStorage;\n }\n\n // ===========================================================================\n // Document CRUD\n // ===========================================================================\n\n /**\n * Upsert a document.\n * @returns The document with the generated doc_id if it was auto-generated\n */\n async upsertDocument(document: Document): Promise<Document> {\n const serialized = JSON.stringify(document.toJSON());\n\n const insertEntity: InsertDocumentStorageEntity = {\n doc_id: document.doc_id,\n data: serialized,\n };\n const entity = await this.tabularStorage.put(insertEntity);\n\n if (document.doc_id !== entity.doc_id) {\n document.setDocId(entity.doc_id);\n }\n return document;\n }\n\n /**\n * Get a document by ID\n */\n async getDocument(doc_id: string): Promise<Document | undefined> {\n const entity = await this.tabularStorage.get({ doc_id });\n if (!entity) {\n return undefined;\n }\n return Document.fromJSON(entity.data, entity.doc_id);\n }\n\n /**\n * Delete a document and all its chunks (cascading delete).\n */\n async deleteDocument(doc_id: string): Promise<void> {\n await this.deleteChunksForDocument(doc_id);\n await this.tabularStorage.delete({ doc_id });\n }\n\n /**\n * List all document IDs\n */\n async listDocuments(): Promise<string[]> {\n const entities = await this.tabularStorage.getAll();\n if (!entities) {\n return [];\n }\n return entities.map((e: DocumentStorageEntity) => e.doc_id);\n }\n\n // ===========================================================================\n // Tree traversal\n // ===========================================================================\n\n /**\n * Get a specific node by ID from a document\n */\n async getNode(doc_id: string, nodeId: string): Promise<DocumentNode | undefined> {\n const doc = await this.getDocument(doc_id);\n if (!doc) {\n return undefined;\n }\n\n const traverse = (node: DocumentNode): DocumentNode | undefined => {\n if (node.nodeId === nodeId) {\n return node;\n }\n if (\"children\" in node && Array.isArray(node.children)) {\n for (const child of node.children) {\n const found = traverse(child);\n if (found) return found;\n }\n }\n return undefined;\n };\n\n return traverse(doc.root);\n }\n\n /**\n * Get ancestors of a node (from root to target node)\n */\n async getAncestors(doc_id: string, nodeId: string): Promise<DocumentNode[]> {\n const doc = await this.getDocument(doc_id);\n if (!doc) {\n return [];\n }\n\n const path: string[] = [];\n const findPath = (node: DocumentNode): boolean => {\n path.push(node.nodeId);\n if (node.nodeId === nodeId) {\n return true;\n }\n if (\"children\" in node && Array.isArray(node.children)) {\n for (const child of node.children) {\n if (findPath(child)) {\n return true;\n }\n }\n }\n path.pop();\n return false;\n };\n\n if (!findPath(doc.root)) {\n return [];\n }\n\n const ancestors: DocumentNode[] = [];\n let currentNode: DocumentNode = doc.root;\n ancestors.push(currentNode);\n\n for (let i = 1; i < path.length; i++) {\n const targetId = path[i];\n if (\"children\" in currentNode && Array.isArray(currentNode.children)) {\n const found = currentNode.children.find((child: DocumentNode) => child.nodeId === targetId);\n if (found) {\n currentNode = found;\n ancestors.push(currentNode);\n } else {\n break;\n }\n } else {\n break;\n }\n }\n\n return ancestors;\n }\n\n // ===========================================================================\n // Chunk CRUD\n // ===========================================================================\n\n /**\n * Upsert a single chunk vector entity\n */\n async upsertChunk(chunk: InsertChunkVectorEntity): Promise<ChunkVectorEntity> {\n if (chunk.vector.length !== this.getVectorDimensions()) {\n throw new Error(\n `Vector dimension mismatch: expected ${this.getVectorDimensions()}, got ${chunk.vector.length}.`\n );\n }\n return this.chunkStorage.put(chunk);\n }\n\n /**\n * Upsert multiple chunk vector entities\n */\n async upsertChunksBulk(chunks: InsertChunkVectorEntity[]): Promise<ChunkVectorEntity[]> {\n const expected = this.getVectorDimensions();\n for (const chunk of chunks) {\n if (chunk.vector.length !== expected) {\n throw new Error(\n `Vector dimension mismatch: expected ${expected}, got ${chunk.vector.length}.`\n );\n }\n }\n return this.chunkStorage.putBulk(chunks);\n }\n\n /**\n * Delete all chunks for a specific document\n */\n async deleteChunksForDocument(doc_id: string): Promise<void> {\n await this.chunkStorage.deleteSearch({ doc_id });\n }\n\n /**\n * Get all chunks for a specific document\n */\n async getChunksForDocument(doc_id: string): Promise<ChunkVectorEntity[]> {\n const results = await this.chunkStorage.query({ doc_id });\n return (results ?? []) as ChunkVectorEntity[];\n }\n\n // ===========================================================================\n // Search\n // ===========================================================================\n\n /**\n * Search for similar chunks using vector similarity\n */\n async similaritySearch(\n query: TypedArray,\n options?: VectorSearchOptions<ChunkRecord>\n ): Promise<ChunkSearchResult[]> {\n return this.chunkStorage.similaritySearch(query, options);\n }\n\n /**\n * Hybrid search combining vector similarity and full-text search\n */\n async hybridSearch(\n query: TypedArray,\n options: HybridSearchOptions<ChunkRecord>\n ): Promise<ChunkSearchResult[]> {\n if (typeof this.chunkStorage.hybridSearch !== \"function\") {\n throw new Error(\n \"Hybrid search is not supported by the configured chunk storage backend. \" +\n \"Please use a vector storage implementation that provides `hybridSearch`.\"\n );\n }\n return this.chunkStorage.hybridSearch(query, options);\n }\n\n // ===========================================================================\n // Lifecycle\n // ===========================================================================\n\n /**\n * Prepare a document for re-indexing: deletes all chunks but keeps the document.\n * @returns The document if found, undefined otherwise\n */\n async prepareReindex(doc_id: string): Promise<Document | undefined> {\n const doc = await this.getDocument(doc_id);\n if (!doc) {\n return undefined;\n }\n await this.deleteChunksForDocument(doc_id);\n return doc;\n }\n\n /**\n * Setup the underlying databases\n */\n async setupDatabase(): Promise<void> {\n await this.tabularStorage.setupDatabase();\n await this.chunkStorage.setupDatabase();\n }\n\n /**\n * Destroy storage instances\n */\n destroy(): void {\n this.tabularStorage.destroy();\n this.chunkStorage.destroy();\n }\n\n // ===========================================================================\n // Accessors\n // ===========================================================================\n\n /**\n * Get a chunk by ID\n */\n async getChunk(chunk_id: string): Promise<ChunkVectorEntity | undefined> {\n return this.chunkStorage.get({ chunk_id });\n }\n\n /**\n * Store a single chunk (alias for upsertChunk)\n */\n async put(chunk: InsertChunkVectorEntity): Promise<ChunkVectorEntity> {\n return this.chunkStorage.put(chunk);\n }\n\n /**\n * Store multiple chunks (alias for upsertChunksBulk)\n */\n async putBulk(chunks: InsertChunkVectorEntity[]): Promise<ChunkVectorEntity[]> {\n return this.chunkStorage.putBulk(chunks);\n }\n\n /**\n * Get all chunks\n */\n async getAllChunks(): Promise<ChunkVectorEntity[] | undefined> {\n return this.chunkStorage.getAll() as Promise<ChunkVectorEntity[] | undefined>;\n }\n\n /**\n * Get chunk count\n */\n async chunkCount(): Promise<number> {\n return this.chunkStorage.size();\n }\n\n /**\n * Clear all chunks\n */\n async clearChunks(): Promise<void> {\n return this.chunkStorage.deleteAll();\n }\n\n /**\n * Get vector dimensions\n */\n getVectorDimensions(): number {\n return this.chunkStorage.getVectorDimensions();\n }\n\n // ===========================================================================\n // Document chunk helpers\n // ===========================================================================\n\n /**\n * Get chunks from the document JSON (not from vector storage)\n */\n async getDocumentChunks(doc_id: string): Promise<ChunkRecord[]> {\n const doc = await this.getDocument(doc_id);\n if (!doc) {\n return [];\n }\n return doc.getChunks();\n }\n\n /**\n * Find chunks in document JSON that contain a specific nodeId in their path\n */\n async findChunksByNodeId(doc_id: string, nodeId: string): Promise<ChunkRecord[]> {\n const doc = await this.getDocument(doc_id);\n if (!doc) {\n return [];\n }\n return doc.findChunksByNodeId(nodeId);\n }\n}\n",
10
10
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { DataPortSchemaObject, FromSchema } from \"@workglow/util\";\n\n/**\n * Schema for persisting KnowledgeBase metadata to tabular storage.\n */\nexport const KnowledgeBaseRecordSchema = {\n type: \"object\",\n properties: {\n kb_id: { type: \"string\" },\n title: { type: \"string\" },\n description: { type: \"string\" },\n vector_dimensions: { type: \"integer\" },\n document_table: { type: \"string\" },\n chunk_table: { type: \"string\" },\n created_at: { type: \"string\" },\n updated_at: { type: \"string\" },\n },\n required: [\n \"kb_id\",\n \"title\",\n \"description\",\n \"vector_dimensions\",\n \"document_table\",\n \"chunk_table\",\n \"created_at\",\n \"updated_at\",\n ],\n additionalProperties: false,\n} as const satisfies DataPortSchemaObject;\n\nexport type KnowledgeBaseRecord = FromSchema<typeof KnowledgeBaseRecordSchema>;\nexport const KnowledgeBasePrimaryKeyNames = [\"kb_id\"] as const;\n\n/**\n * Generates SQL-safe table names for a knowledge base's document and chunk storage.\n */\nexport function knowledgeBaseTableNames(kbId: string): {\n readonly documentTable: string;\n readonly chunkTable: string;\n} {\n const safe = kbId.replace(/[^a-zA-Z0-9_]/g, \"_\");\n return {\n documentTable: `kb_docs_${safe}`,\n chunkTable: `kb_chunks_${safe}`,\n };\n}\n",
11
- "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { type BaseTabularStorage } from \"@workglow/storage\";\nimport { EventEmitter, type EventParameters } from \"@workglow/util\";\n\nimport {\n KnowledgeBasePrimaryKeyNames,\n type KnowledgeBaseRecord,\n KnowledgeBaseRecordSchema,\n} from \"./KnowledgeBaseSchema\";\n\n/**\n * Events that can be emitted by the KnowledgeBaseRepository\n */\n\nexport type KnowledgeBaseEventListeners = {\n knowledge_base_added: (record: KnowledgeBaseRecord) => void;\n knowledge_base_removed: (record: KnowledgeBaseRecord) => void;\n knowledge_base_updated: (record: KnowledgeBaseRecord) => void;\n};\n\nexport type KnowledgeBaseEvents = keyof KnowledgeBaseEventListeners;\n\nexport type KnowledgeBaseEventListener<Event extends KnowledgeBaseEvents> =\n KnowledgeBaseEventListeners[Event];\n\nexport type KnowledgeBaseEventParameters<Event extends KnowledgeBaseEvents> = EventParameters<\n KnowledgeBaseEventListeners,\n Event\n>;\n\n/**\n * Repository for persisting KnowledgeBase metadata to tabular storage.\n * Follows the same pattern as ModelRepository.\n */\nexport class KnowledgeBaseRepository {\n /**\n * Storage for KnowledgeBase records\n */\n protected readonly storage: BaseTabularStorage<\n typeof KnowledgeBaseRecordSchema,\n typeof KnowledgeBasePrimaryKeyNames\n >;\n\n constructor(\n storage: BaseTabularStorage<\n typeof KnowledgeBaseRecordSchema,\n typeof KnowledgeBasePrimaryKeyNames\n >\n ) {\n this.storage = storage;\n }\n\n /** Event emitter for repository events */\n protected events = new EventEmitter<KnowledgeBaseEventListeners>();\n\n /**\n * Sets up the database for the repository.\n * Must be called before using any other methods.\n */\n async setupDatabase(): Promise<void> {\n await this.storage.setupDatabase?.();\n }\n\n /**\n * Registers an event listener for the specified event\n */\n on<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.on(name, fn);\n }\n\n /**\n * Removes an event listener for the specified event\n */\n off<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.off(name, fn);\n }\n\n /**\n * Adds an event listener that will only be called once\n */\n once<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.once(name, fn);\n }\n\n /**\n * Returns when the event was emitted (promise form of once)\n */\n waitOn<Event extends KnowledgeBaseEvents>(name: Event) {\n return this.events.waitOn(name);\n }\n\n /**\n * Adds a new knowledge base record to the repository\n */\n async addKnowledgeBase(record: KnowledgeBaseRecord): Promise<KnowledgeBaseRecord> {\n await this.storage.put(record);\n this.events.emit(\"knowledge_base_added\", record);\n return record;\n }\n\n /**\n * Removes a knowledge base record from the repository\n */\n async removeKnowledgeBase(kb_id: string): Promise<void> {\n const record = await this.storage.get({ kb_id });\n if (!record) {\n throw new Error(`KnowledgeBase with id \"${kb_id}\" not found`);\n }\n await this.storage.delete({ kb_id });\n this.events.emit(\"knowledge_base_removed\", record);\n }\n\n /**\n * Retrieves a knowledge base record by ID\n */\n async getKnowledgeBase(kb_id: string): Promise<KnowledgeBaseRecord | undefined> {\n if (typeof kb_id !== \"string\") return undefined;\n const record = await this.storage.get({ kb_id });\n return record ?? undefined;\n }\n\n /**\n * Enumerates all knowledge base records\n */\n async enumerateAll(): Promise<KnowledgeBaseRecord[]> {\n const records = await this.storage.getAll();\n if (!records || records.length === 0) return [];\n return records;\n }\n\n /**\n * Gets the total number of knowledge base records\n */\n async size(): Promise<number> {\n return await this.storage.size();\n }\n}\n",
11
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { type ITabularStorage } from \"@workglow/storage\";\nimport { EventEmitter, type EventParameters } from \"@workglow/util\";\n\nimport {\n KnowledgeBasePrimaryKeyNames,\n type KnowledgeBaseRecord,\n KnowledgeBaseRecordSchema,\n} from \"./KnowledgeBaseSchema\";\n\n/**\n * Events that can be emitted by the KnowledgeBaseRepository\n */\n\nexport type KnowledgeBaseEventListeners = {\n knowledge_base_added: (record: KnowledgeBaseRecord) => void;\n knowledge_base_removed: (record: KnowledgeBaseRecord) => void;\n knowledge_base_updated: (record: KnowledgeBaseRecord) => void;\n};\n\nexport type KnowledgeBaseEvents = keyof KnowledgeBaseEventListeners;\n\nexport type KnowledgeBaseEventListener<Event extends KnowledgeBaseEvents> =\n KnowledgeBaseEventListeners[Event];\n\nexport type KnowledgeBaseEventParameters<Event extends KnowledgeBaseEvents> = EventParameters<\n KnowledgeBaseEventListeners,\n Event\n>;\n\n/**\n * Repository for persisting KnowledgeBase metadata to tabular storage.\n * Follows the same pattern as ModelRepository.\n */\nexport class KnowledgeBaseRepository {\n /**\n * Storage for KnowledgeBase records\n */\n protected readonly storage: ITabularStorage<\n typeof KnowledgeBaseRecordSchema,\n typeof KnowledgeBasePrimaryKeyNames\n >;\n\n constructor(\n storage: ITabularStorage<\n typeof KnowledgeBaseRecordSchema,\n typeof KnowledgeBasePrimaryKeyNames\n >\n ) {\n this.storage = storage;\n }\n\n /** Event emitter for repository events */\n protected events = new EventEmitter<KnowledgeBaseEventListeners>();\n\n /**\n * Sets up the database for the repository.\n * Must be called before using any other methods.\n */\n async setupDatabase(): Promise<void> {\n await this.storage.setupDatabase?.();\n }\n\n /**\n * Registers an event listener for the specified event\n */\n on<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.on(name, fn);\n }\n\n /**\n * Removes an event listener for the specified event\n */\n off<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.off(name, fn);\n }\n\n /**\n * Adds an event listener that will only be called once\n */\n once<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.once(name, fn);\n }\n\n /**\n * Returns when the event was emitted (promise form of once)\n */\n waitOn<Event extends KnowledgeBaseEvents>(name: Event) {\n return this.events.waitOn(name);\n }\n\n /**\n * Adds a new knowledge base record to the repository\n */\n async addKnowledgeBase(record: KnowledgeBaseRecord): Promise<KnowledgeBaseRecord> {\n await this.storage.put(record);\n this.events.emit(\"knowledge_base_added\", record);\n return record;\n }\n\n /**\n * Removes a knowledge base record from the repository\n */\n async removeKnowledgeBase(kb_id: string): Promise<void> {\n const record = await this.storage.get({ kb_id });\n if (!record) {\n throw new Error(`KnowledgeBase with id \"${kb_id}\" not found`);\n }\n await this.storage.delete({ kb_id });\n this.events.emit(\"knowledge_base_removed\", record);\n }\n\n /**\n * Retrieves a knowledge base record by ID\n */\n async getKnowledgeBase(kb_id: string): Promise<KnowledgeBaseRecord | undefined> {\n if (typeof kb_id !== \"string\") return undefined;\n const record = await this.storage.get({ kb_id });\n return record ?? undefined;\n }\n\n /**\n * Enumerates all knowledge base records\n */\n async enumerateAll(): Promise<KnowledgeBaseRecord[]> {\n const records = await this.storage.getAll();\n if (!records || records.length === 0) return [];\n return records;\n }\n\n /**\n * Gets the total number of knowledge base records\n */\n async size(): Promise<number> {\n return await this.storage.size();\n }\n}\n",
12
12
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { InMemoryTabularStorage } from \"@workglow/storage\";\nimport { KnowledgeBaseRepository } from \"./KnowledgeBaseRepository\";\nimport { KnowledgeBasePrimaryKeyNames, KnowledgeBaseRecordSchema } from \"./KnowledgeBaseSchema\";\n\n/**\n * In-memory implementation of a knowledge base repository.\n */\nexport class InMemoryKnowledgeBaseRepository extends KnowledgeBaseRepository {\n constructor() {\n super(new InMemoryTabularStorage(KnowledgeBaseRecordSchema, KnowledgeBasePrimaryKeyNames));\n }\n}\n",
13
13
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {\n createServiceToken,\n globalServiceRegistry,\n registerInputResolver,\n ServiceRegistry,\n} from \"@workglow/util\";\nimport { InMemoryKnowledgeBaseRepository } from \"./InMemoryKnowledgeBaseRepository\";\nimport type { KnowledgeBase } from \"./KnowledgeBase\";\nimport { KnowledgeBaseRepository } from \"./KnowledgeBaseRepository\";\nimport { knowledgeBaseTableNames, type KnowledgeBaseRecord } from \"./KnowledgeBaseSchema\";\n\n/**\n * Service token for the knowledge base registry\n * Maps knowledge base IDs to KnowledgeBase instances\n */\nexport const KNOWLEDGE_BASES =\n createServiceToken<Map<string, KnowledgeBase>>(\"knowledge-base.registry\");\n\n/**\n * Service token for the knowledge base repository\n */\nexport const KNOWLEDGE_BASE_REPOSITORY = createServiceToken<KnowledgeBaseRepository>(\n \"knowledge-base.repository\"\n);\n\n// Register default factory for live KB map if not already registered\nif (!globalServiceRegistry.has(KNOWLEDGE_BASES)) {\n globalServiceRegistry.register(\n KNOWLEDGE_BASES,\n (): Map<string, KnowledgeBase> => new Map(),\n true\n );\n}\n\n// Register default factory for KB repository if not already registered\nif (!globalServiceRegistry.has(KNOWLEDGE_BASE_REPOSITORY)) {\n globalServiceRegistry.register(\n KNOWLEDGE_BASE_REPOSITORY,\n (): KnowledgeBaseRepository => new InMemoryKnowledgeBaseRepository(),\n true\n );\n}\n\n/**\n * Gets the global knowledge base registry\n */\nexport function getGlobalKnowledgeBases(): Map<string, KnowledgeBase> {\n return globalServiceRegistry.get(KNOWLEDGE_BASES);\n}\n\n/**\n * Gets the global knowledge base repository instance\n */\nexport function getGlobalKnowledgeBaseRepository(): KnowledgeBaseRepository {\n return globalServiceRegistry.get(KNOWLEDGE_BASE_REPOSITORY);\n}\n\n/**\n * Sets the global knowledge base repository instance\n */\nexport function setGlobalKnowledgeBaseRepository(repository: KnowledgeBaseRepository): void {\n globalServiceRegistry.registerInstance(KNOWLEDGE_BASE_REPOSITORY, repository);\n}\n\n/**\n * Registers a knowledge base globally by ID.\n * Adds to both the live Map and the persistent repository.\n */\nexport async function registerKnowledgeBase(id: string, kb: KnowledgeBase): Promise<void> {\n const kbs = getGlobalKnowledgeBases();\n kbs.set(id, kb);\n\n const now = new Date().toISOString();\n const tableNames = knowledgeBaseTableNames(id);\n const record: KnowledgeBaseRecord = {\n kb_id: id,\n title: kb.title,\n description: kb.description,\n vector_dimensions: kb.getVectorDimensions(),\n document_table: tableNames.documentTable,\n chunk_table: tableNames.chunkTable,\n created_at: now,\n updated_at: now,\n };\n const repo = getGlobalKnowledgeBaseRepository();\n await repo.addKnowledgeBase(record);\n}\n\n/**\n * Gets a knowledge base by ID from the global registry\n */\nexport function getKnowledgeBase(id: string): KnowledgeBase | undefined {\n return getGlobalKnowledgeBases().get(id);\n}\n\n/**\n * Resolves a knowledge base ID from the registry.\n * Used by the input resolver system.\n */\nasync function resolveKnowledgeBaseFromRegistry(\n id: string,\n format: string,\n registry: ServiceRegistry\n): Promise<KnowledgeBase> {\n const kbs = registry.has(KNOWLEDGE_BASES)\n ? registry.get<Map<string, KnowledgeBase>>(KNOWLEDGE_BASES)\n : getGlobalKnowledgeBases();\n\n const kb = kbs.get(id);\n if (!kb) {\n throw new Error(`Knowledge base \"${id}\" not found in registry`);\n }\n return kb;\n}\n\n// Register the resolver for format: \"knowledge-base\"\nregisterInputResolver(\"knowledge-base\", resolveKnowledgeBaseFromRegistry);\n",
14
14
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { InMemoryTabularStorage, InMemoryVectorStorage } from \"@workglow/storage\";\nimport type { TypedArray } from \"@workglow/util\";\nimport { ChunkVectorPrimaryKey, ChunkVectorStorageSchema } from \"../chunk/ChunkVectorStorageSchema\";\nimport type { ChunkVectorStorage } from \"../chunk/ChunkVectorStorageSchema\";\nimport { DocumentStorageKey, DocumentStorageSchema } from \"../document/DocumentStorageSchema\";\nimport type { DocumentTabularStorage } from \"../document/DocumentStorageSchema\";\nimport { KnowledgeBase } from \"./KnowledgeBase\";\nimport { registerKnowledgeBase } from \"./KnowledgeBaseRegistry\";\n\nexport interface CreateKnowledgeBaseOptions {\n readonly name: string;\n readonly vectorDimensions: number;\n readonly vectorType?: { new (array: number[]): TypedArray };\n readonly register?: boolean;\n readonly title?: string;\n readonly description?: string;\n}\n\n/**\n * Factory function to create a KnowledgeBase with minimal configuration.\n *\n * @example\n * ```typescript\n * const kb = await createKnowledgeBase({\n * name: \"my-kb\",\n * vectorDimensions: 1024,\n * });\n * ```\n */\nexport async function createKnowledgeBase(\n options: CreateKnowledgeBaseOptions\n): Promise<KnowledgeBase> {\n const {\n name,\n vectorDimensions,\n vectorType = Float32Array,\n register: shouldRegister = true,\n title,\n description,\n } = options;\n\n const tabularStorage = new InMemoryTabularStorage(DocumentStorageSchema, DocumentStorageKey);\n await tabularStorage.setupDatabase();\n\n const vectorStorage = new InMemoryVectorStorage(\n ChunkVectorStorageSchema,\n ChunkVectorPrimaryKey,\n [],\n vectorDimensions,\n vectorType\n );\n await vectorStorage.setupDatabase();\n\n const kb = new KnowledgeBase(\n name,\n tabularStorage as unknown as DocumentTabularStorage,\n vectorStorage as unknown as ChunkVectorStorage,\n title,\n description\n );\n\n if (shouldRegister) {\n await registerKnowledgeBase(name, kb);\n }\n\n return kb;\n}\n",
@@ -3,7 +3,7 @@
3
3
  * Copyright 2025 Steven Roussey <sroussey@gmail.com>
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- import { type BaseTabularStorage } from "@workglow/storage";
6
+ import { type ITabularStorage } from "@workglow/storage";
7
7
  import { EventEmitter, type EventParameters } from "@workglow/util";
8
8
  import { KnowledgeBasePrimaryKeyNames, type KnowledgeBaseRecord, KnowledgeBaseRecordSchema } from "./KnowledgeBaseSchema";
9
9
  /**
@@ -25,8 +25,8 @@ export declare class KnowledgeBaseRepository {
25
25
  /**
26
26
  * Storage for KnowledgeBase records
27
27
  */
28
- protected readonly storage: BaseTabularStorage<typeof KnowledgeBaseRecordSchema, typeof KnowledgeBasePrimaryKeyNames>;
29
- constructor(storage: BaseTabularStorage<typeof KnowledgeBaseRecordSchema, typeof KnowledgeBasePrimaryKeyNames>);
28
+ protected readonly storage: ITabularStorage<typeof KnowledgeBaseRecordSchema, typeof KnowledgeBasePrimaryKeyNames>;
29
+ constructor(storage: ITabularStorage<typeof KnowledgeBaseRecordSchema, typeof KnowledgeBasePrimaryKeyNames>);
30
30
  /** Event emitter for repository events */
31
31
  protected events: EventEmitter<KnowledgeBaseEventListeners>;
32
32
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"KnowledgeBaseRepository.d.ts","sourceRoot":"","sources":["../../src/knowledge-base/KnowledgeBaseRepository.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEpE,OAAO,EACL,4BAA4B,EAC5B,KAAK,mBAAmB,EACxB,yBAAyB,EAC1B,MAAM,uBAAuB,CAAC;AAE/B;;GAEG;AAEH,MAAM,MAAM,2BAA2B,GAAG;IACxC,oBAAoB,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC5D,sBAAsB,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC9D,sBAAsB,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC/D,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,MAAM,2BAA2B,CAAC;AAEpE,MAAM,MAAM,0BAA0B,CAAC,KAAK,SAAS,mBAAmB,IACtE,2BAA2B,CAAC,KAAK,CAAC,CAAC;AAErC,MAAM,MAAM,4BAA4B,CAAC,KAAK,SAAS,mBAAmB,IAAI,eAAe,CAC3F,2BAA2B,EAC3B,KAAK,CACN,CAAC;AAEF;;;GAGG;AACH,qBAAa,uBAAuB;IAClC;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAC5C,OAAO,yBAAyB,EAChC,OAAO,4BAA4B,CACpC,CAAC;gBAGA,OAAO,EAAE,kBAAkB,CACzB,OAAO,yBAAyB,EAChC,OAAO,4BAA4B,CACpC;IAKH,0CAA0C;IAC1C,SAAS,CAAC,MAAM,4CAAmD;IAEnE;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAIpC;;OAEG;IACH,EAAE,CAAC,KAAK,SAAS,mBAAmB,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,0BAA0B,CAAC,KAAK,CAAC;IAIxF;;OAEG;IACH,GAAG,CAAC,KAAK,SAAS,mBAAmB,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,0BAA0B,CAAC,KAAK,CAAC;IAIzF;;OAEG;IACH,IAAI,CAAC,KAAK,SAAS,mBAAmB,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,0BAA0B,CAAC,KAAK,CAAC;IAI1F;;OAEG;IACH,MAAM,CAAC,KAAK,SAAS,mBAAmB,EAAE,IAAI,EAAE,KAAK;IAIrD;;OAEG;IACG,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAMjF;;OAEG;IACG,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASvD;;OAEG;IACG,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAM/E;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAMpD;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAG9B"}
1
+ {"version":3,"file":"KnowledgeBaseRepository.d.ts","sourceRoot":"","sources":["../../src/knowledge-base/KnowledgeBaseRepository.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEpE,OAAO,EACL,4BAA4B,EAC5B,KAAK,mBAAmB,EACxB,yBAAyB,EAC1B,MAAM,uBAAuB,CAAC;AAE/B;;GAEG;AAEH,MAAM,MAAM,2BAA2B,GAAG;IACxC,oBAAoB,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC5D,sBAAsB,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC9D,sBAAsB,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC/D,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,MAAM,2BAA2B,CAAC;AAEpE,MAAM,MAAM,0BAA0B,CAAC,KAAK,SAAS,mBAAmB,IACtE,2BAA2B,CAAC,KAAK,CAAC,CAAC;AAErC,MAAM,MAAM,4BAA4B,CAAC,KAAK,SAAS,mBAAmB,IAAI,eAAe,CAC3F,2BAA2B,EAC3B,KAAK,CACN,CAAC;AAEF;;;GAGG;AACH,qBAAa,uBAAuB;IAClC;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,eAAe,CACzC,OAAO,yBAAyB,EAChC,OAAO,4BAA4B,CACpC,CAAC;gBAGA,OAAO,EAAE,eAAe,CACtB,OAAO,yBAAyB,EAChC,OAAO,4BAA4B,CACpC;IAKH,0CAA0C;IAC1C,SAAS,CAAC,MAAM,4CAAmD;IAEnE;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAIpC;;OAEG;IACH,EAAE,CAAC,KAAK,SAAS,mBAAmB,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,0BAA0B,CAAC,KAAK,CAAC;IAIxF;;OAEG;IACH,GAAG,CAAC,KAAK,SAAS,mBAAmB,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,0BAA0B,CAAC,KAAK,CAAC;IAIzF;;OAEG;IACH,IAAI,CAAC,KAAK,SAAS,mBAAmB,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,0BAA0B,CAAC,KAAK,CAAC;IAI1F;;OAEG;IACH,MAAM,CAAC,KAAK,SAAS,mBAAmB,EAAE,IAAI,EAAE,KAAK;IAIrD;;OAEG;IACG,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAMjF;;OAEG;IACG,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASvD;;OAEG;IACG,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAM/E;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAMpD;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAG9B"}
package/dist/node.js.map CHANGED
@@ -8,7 +8,7 @@
8
8
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { ChunkRecord } from \"../chunk/ChunkSchema\";\nimport type { DocumentMetadata, DocumentNode } from \"./DocumentSchema\";\n\n/**\n * Document represents a hierarchical document with chunks\n *\n * Key features:\n * - Single source-of-truth tree structure (root node)\n * - Single set of chunks\n * - Separate persistence for document structure vs vectors\n */\nexport class Document {\n public doc_id: string | undefined;\n public readonly metadata: DocumentMetadata;\n public readonly root: DocumentNode;\n private chunks: ChunkRecord[];\n\n constructor(\n root: DocumentNode,\n metadata: DocumentMetadata,\n chunks: ChunkRecord[] = [],\n doc_id?: string\n ) {\n this.doc_id = doc_id;\n this.root = root;\n this.metadata = metadata;\n this.chunks = chunks || [];\n }\n\n /**\n * Set chunks for the document\n */\n setChunks(chunks: ChunkRecord[]): void {\n this.chunks = chunks;\n }\n\n /**\n * Get all chunks\n */\n getChunks(): ChunkRecord[] {\n return this.chunks;\n }\n\n /**\n * Set the document ID\n */\n setDocId(doc_id: string): void {\n this.doc_id = doc_id;\n }\n\n /**\n * Find chunks by nodeId\n */\n findChunksByNodeId(nodeId: string): ChunkRecord[] {\n return this.chunks.filter((chunk) => chunk.nodePath.includes(nodeId));\n }\n\n /**\n * Serialize to JSON\n */\n toJSON(): {\n metadata: DocumentMetadata;\n root: DocumentNode;\n chunks: ChunkRecord[];\n } {\n return {\n metadata: this.metadata,\n root: this.root,\n chunks: this.chunks,\n };\n }\n\n /**\n * Deserialize from JSON\n */\n static fromJSON(json: string, doc_id?: string): Document {\n const obj = JSON.parse(json);\n return new Document(obj.root, obj.metadata, obj.chunks, doc_id);\n }\n}\n",
9
9
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { HybridSearchOptions, VectorSearchOptions } from \"@workglow/storage\";\nimport type { TypedArray } from \"@workglow/util\";\nimport type { ChunkRecord } from \"../chunk/ChunkSchema\";\nimport type {\n ChunkSearchResult,\n ChunkVectorEntity,\n ChunkVectorStorage,\n InsertChunkVectorEntity,\n} from \"../chunk/ChunkVectorStorageSchema\";\nimport { Document } from \"../document/Document\";\nimport type { DocumentNode } from \"../document/DocumentSchema\";\nimport type {\n DocumentStorageEntity,\n DocumentTabularStorage,\n InsertDocumentStorageEntity,\n} from \"../document/DocumentStorageSchema\";\n\n/**\n * Unified KnowledgeBase that owns both document and vector storage,\n * providing lifecycle management and cascading deletes.\n */\nexport class KnowledgeBase {\n readonly name: string;\n readonly title: string;\n readonly description: string;\n private tabularStorage: DocumentTabularStorage;\n private chunkStorage: ChunkVectorStorage;\n\n constructor(\n name: string,\n documentStorage: DocumentTabularStorage,\n chunkStorage: ChunkVectorStorage,\n title?: string,\n description?: string\n ) {\n this.name = name;\n this.title = title ?? name;\n this.description = description ?? \"\";\n this.tabularStorage = documentStorage;\n this.chunkStorage = chunkStorage;\n }\n\n // ===========================================================================\n // Document CRUD\n // ===========================================================================\n\n /**\n * Upsert a document.\n * @returns The document with the generated doc_id if it was auto-generated\n */\n async upsertDocument(document: Document): Promise<Document> {\n const serialized = JSON.stringify(document.toJSON());\n\n const insertEntity: InsertDocumentStorageEntity = {\n doc_id: document.doc_id,\n data: serialized,\n };\n const entity = await this.tabularStorage.put(insertEntity);\n\n if (document.doc_id !== entity.doc_id) {\n document.setDocId(entity.doc_id);\n }\n return document;\n }\n\n /**\n * Get a document by ID\n */\n async getDocument(doc_id: string): Promise<Document | undefined> {\n const entity = await this.tabularStorage.get({ doc_id });\n if (!entity) {\n return undefined;\n }\n return Document.fromJSON(entity.data, entity.doc_id);\n }\n\n /**\n * Delete a document and all its chunks (cascading delete).\n */\n async deleteDocument(doc_id: string): Promise<void> {\n await this.deleteChunksForDocument(doc_id);\n await this.tabularStorage.delete({ doc_id });\n }\n\n /**\n * List all document IDs\n */\n async listDocuments(): Promise<string[]> {\n const entities = await this.tabularStorage.getAll();\n if (!entities) {\n return [];\n }\n return entities.map((e: DocumentStorageEntity) => e.doc_id);\n }\n\n // ===========================================================================\n // Tree traversal\n // ===========================================================================\n\n /**\n * Get a specific node by ID from a document\n */\n async getNode(doc_id: string, nodeId: string): Promise<DocumentNode | undefined> {\n const doc = await this.getDocument(doc_id);\n if (!doc) {\n return undefined;\n }\n\n const traverse = (node: DocumentNode): DocumentNode | undefined => {\n if (node.nodeId === nodeId) {\n return node;\n }\n if (\"children\" in node && Array.isArray(node.children)) {\n for (const child of node.children) {\n const found = traverse(child);\n if (found) return found;\n }\n }\n return undefined;\n };\n\n return traverse(doc.root);\n }\n\n /**\n * Get ancestors of a node (from root to target node)\n */\n async getAncestors(doc_id: string, nodeId: string): Promise<DocumentNode[]> {\n const doc = await this.getDocument(doc_id);\n if (!doc) {\n return [];\n }\n\n const path: string[] = [];\n const findPath = (node: DocumentNode): boolean => {\n path.push(node.nodeId);\n if (node.nodeId === nodeId) {\n return true;\n }\n if (\"children\" in node && Array.isArray(node.children)) {\n for (const child of node.children) {\n if (findPath(child)) {\n return true;\n }\n }\n }\n path.pop();\n return false;\n };\n\n if (!findPath(doc.root)) {\n return [];\n }\n\n const ancestors: DocumentNode[] = [];\n let currentNode: DocumentNode = doc.root;\n ancestors.push(currentNode);\n\n for (let i = 1; i < path.length; i++) {\n const targetId = path[i];\n if (\"children\" in currentNode && Array.isArray(currentNode.children)) {\n const found = currentNode.children.find((child: DocumentNode) => child.nodeId === targetId);\n if (found) {\n currentNode = found;\n ancestors.push(currentNode);\n } else {\n break;\n }\n } else {\n break;\n }\n }\n\n return ancestors;\n }\n\n // ===========================================================================\n // Chunk CRUD\n // ===========================================================================\n\n /**\n * Upsert a single chunk vector entity\n */\n async upsertChunk(chunk: InsertChunkVectorEntity): Promise<ChunkVectorEntity> {\n if (chunk.vector.length !== this.getVectorDimensions()) {\n throw new Error(\n `Vector dimension mismatch: expected ${this.getVectorDimensions()}, got ${chunk.vector.length}.`\n );\n }\n return this.chunkStorage.put(chunk);\n }\n\n /**\n * Upsert multiple chunk vector entities\n */\n async upsertChunksBulk(chunks: InsertChunkVectorEntity[]): Promise<ChunkVectorEntity[]> {\n const expected = this.getVectorDimensions();\n for (const chunk of chunks) {\n if (chunk.vector.length !== expected) {\n throw new Error(\n `Vector dimension mismatch: expected ${expected}, got ${chunk.vector.length}.`\n );\n }\n }\n return this.chunkStorage.putBulk(chunks);\n }\n\n /**\n * Delete all chunks for a specific document\n */\n async deleteChunksForDocument(doc_id: string): Promise<void> {\n await this.chunkStorage.deleteSearch({ doc_id });\n }\n\n /**\n * Get all chunks for a specific document\n */\n async getChunksForDocument(doc_id: string): Promise<ChunkVectorEntity[]> {\n const results = await this.chunkStorage.query({ doc_id });\n return (results ?? []) as ChunkVectorEntity[];\n }\n\n // ===========================================================================\n // Search\n // ===========================================================================\n\n /**\n * Search for similar chunks using vector similarity\n */\n async similaritySearch(\n query: TypedArray,\n options?: VectorSearchOptions<ChunkRecord>\n ): Promise<ChunkSearchResult[]> {\n return this.chunkStorage.similaritySearch(query, options);\n }\n\n /**\n * Hybrid search combining vector similarity and full-text search\n */\n async hybridSearch(\n query: TypedArray,\n options: HybridSearchOptions<ChunkRecord>\n ): Promise<ChunkSearchResult[]> {\n if (typeof this.chunkStorage.hybridSearch !== \"function\") {\n throw new Error(\n \"Hybrid search is not supported by the configured chunk storage backend. \" +\n \"Please use a vector storage implementation that provides `hybridSearch`.\"\n );\n }\n return this.chunkStorage.hybridSearch(query, options);\n }\n\n // ===========================================================================\n // Lifecycle\n // ===========================================================================\n\n /**\n * Prepare a document for re-indexing: deletes all chunks but keeps the document.\n * @returns The document if found, undefined otherwise\n */\n async prepareReindex(doc_id: string): Promise<Document | undefined> {\n const doc = await this.getDocument(doc_id);\n if (!doc) {\n return undefined;\n }\n await this.deleteChunksForDocument(doc_id);\n return doc;\n }\n\n /**\n * Setup the underlying databases\n */\n async setupDatabase(): Promise<void> {\n await this.tabularStorage.setupDatabase();\n await this.chunkStorage.setupDatabase();\n }\n\n /**\n * Destroy storage instances\n */\n destroy(): void {\n this.tabularStorage.destroy();\n this.chunkStorage.destroy();\n }\n\n // ===========================================================================\n // Accessors\n // ===========================================================================\n\n /**\n * Get a chunk by ID\n */\n async getChunk(chunk_id: string): Promise<ChunkVectorEntity | undefined> {\n return this.chunkStorage.get({ chunk_id });\n }\n\n /**\n * Store a single chunk (alias for upsertChunk)\n */\n async put(chunk: InsertChunkVectorEntity): Promise<ChunkVectorEntity> {\n return this.chunkStorage.put(chunk);\n }\n\n /**\n * Store multiple chunks (alias for upsertChunksBulk)\n */\n async putBulk(chunks: InsertChunkVectorEntity[]): Promise<ChunkVectorEntity[]> {\n return this.chunkStorage.putBulk(chunks);\n }\n\n /**\n * Get all chunks\n */\n async getAllChunks(): Promise<ChunkVectorEntity[] | undefined> {\n return this.chunkStorage.getAll() as Promise<ChunkVectorEntity[] | undefined>;\n }\n\n /**\n * Get chunk count\n */\n async chunkCount(): Promise<number> {\n return this.chunkStorage.size();\n }\n\n /**\n * Clear all chunks\n */\n async clearChunks(): Promise<void> {\n return this.chunkStorage.deleteAll();\n }\n\n /**\n * Get vector dimensions\n */\n getVectorDimensions(): number {\n return this.chunkStorage.getVectorDimensions();\n }\n\n // ===========================================================================\n // Document chunk helpers\n // ===========================================================================\n\n /**\n * Get chunks from the document JSON (not from vector storage)\n */\n async getDocumentChunks(doc_id: string): Promise<ChunkRecord[]> {\n const doc = await this.getDocument(doc_id);\n if (!doc) {\n return [];\n }\n return doc.getChunks();\n }\n\n /**\n * Find chunks in document JSON that contain a specific nodeId in their path\n */\n async findChunksByNodeId(doc_id: string, nodeId: string): Promise<ChunkRecord[]> {\n const doc = await this.getDocument(doc_id);\n if (!doc) {\n return [];\n }\n return doc.findChunksByNodeId(nodeId);\n }\n}\n",
10
10
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { DataPortSchemaObject, FromSchema } from \"@workglow/util\";\n\n/**\n * Schema for persisting KnowledgeBase metadata to tabular storage.\n */\nexport const KnowledgeBaseRecordSchema = {\n type: \"object\",\n properties: {\n kb_id: { type: \"string\" },\n title: { type: \"string\" },\n description: { type: \"string\" },\n vector_dimensions: { type: \"integer\" },\n document_table: { type: \"string\" },\n chunk_table: { type: \"string\" },\n created_at: { type: \"string\" },\n updated_at: { type: \"string\" },\n },\n required: [\n \"kb_id\",\n \"title\",\n \"description\",\n \"vector_dimensions\",\n \"document_table\",\n \"chunk_table\",\n \"created_at\",\n \"updated_at\",\n ],\n additionalProperties: false,\n} as const satisfies DataPortSchemaObject;\n\nexport type KnowledgeBaseRecord = FromSchema<typeof KnowledgeBaseRecordSchema>;\nexport const KnowledgeBasePrimaryKeyNames = [\"kb_id\"] as const;\n\n/**\n * Generates SQL-safe table names for a knowledge base's document and chunk storage.\n */\nexport function knowledgeBaseTableNames(kbId: string): {\n readonly documentTable: string;\n readonly chunkTable: string;\n} {\n const safe = kbId.replace(/[^a-zA-Z0-9_]/g, \"_\");\n return {\n documentTable: `kb_docs_${safe}`,\n chunkTable: `kb_chunks_${safe}`,\n };\n}\n",
11
- "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { type BaseTabularStorage } from \"@workglow/storage\";\nimport { EventEmitter, type EventParameters } from \"@workglow/util\";\n\nimport {\n KnowledgeBasePrimaryKeyNames,\n type KnowledgeBaseRecord,\n KnowledgeBaseRecordSchema,\n} from \"./KnowledgeBaseSchema\";\n\n/**\n * Events that can be emitted by the KnowledgeBaseRepository\n */\n\nexport type KnowledgeBaseEventListeners = {\n knowledge_base_added: (record: KnowledgeBaseRecord) => void;\n knowledge_base_removed: (record: KnowledgeBaseRecord) => void;\n knowledge_base_updated: (record: KnowledgeBaseRecord) => void;\n};\n\nexport type KnowledgeBaseEvents = keyof KnowledgeBaseEventListeners;\n\nexport type KnowledgeBaseEventListener<Event extends KnowledgeBaseEvents> =\n KnowledgeBaseEventListeners[Event];\n\nexport type KnowledgeBaseEventParameters<Event extends KnowledgeBaseEvents> = EventParameters<\n KnowledgeBaseEventListeners,\n Event\n>;\n\n/**\n * Repository for persisting KnowledgeBase metadata to tabular storage.\n * Follows the same pattern as ModelRepository.\n */\nexport class KnowledgeBaseRepository {\n /**\n * Storage for KnowledgeBase records\n */\n protected readonly storage: BaseTabularStorage<\n typeof KnowledgeBaseRecordSchema,\n typeof KnowledgeBasePrimaryKeyNames\n >;\n\n constructor(\n storage: BaseTabularStorage<\n typeof KnowledgeBaseRecordSchema,\n typeof KnowledgeBasePrimaryKeyNames\n >\n ) {\n this.storage = storage;\n }\n\n /** Event emitter for repository events */\n protected events = new EventEmitter<KnowledgeBaseEventListeners>();\n\n /**\n * Sets up the database for the repository.\n * Must be called before using any other methods.\n */\n async setupDatabase(): Promise<void> {\n await this.storage.setupDatabase?.();\n }\n\n /**\n * Registers an event listener for the specified event\n */\n on<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.on(name, fn);\n }\n\n /**\n * Removes an event listener for the specified event\n */\n off<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.off(name, fn);\n }\n\n /**\n * Adds an event listener that will only be called once\n */\n once<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.once(name, fn);\n }\n\n /**\n * Returns when the event was emitted (promise form of once)\n */\n waitOn<Event extends KnowledgeBaseEvents>(name: Event) {\n return this.events.waitOn(name);\n }\n\n /**\n * Adds a new knowledge base record to the repository\n */\n async addKnowledgeBase(record: KnowledgeBaseRecord): Promise<KnowledgeBaseRecord> {\n await this.storage.put(record);\n this.events.emit(\"knowledge_base_added\", record);\n return record;\n }\n\n /**\n * Removes a knowledge base record from the repository\n */\n async removeKnowledgeBase(kb_id: string): Promise<void> {\n const record = await this.storage.get({ kb_id });\n if (!record) {\n throw new Error(`KnowledgeBase with id \"${kb_id}\" not found`);\n }\n await this.storage.delete({ kb_id });\n this.events.emit(\"knowledge_base_removed\", record);\n }\n\n /**\n * Retrieves a knowledge base record by ID\n */\n async getKnowledgeBase(kb_id: string): Promise<KnowledgeBaseRecord | undefined> {\n if (typeof kb_id !== \"string\") return undefined;\n const record = await this.storage.get({ kb_id });\n return record ?? undefined;\n }\n\n /**\n * Enumerates all knowledge base records\n */\n async enumerateAll(): Promise<KnowledgeBaseRecord[]> {\n const records = await this.storage.getAll();\n if (!records || records.length === 0) return [];\n return records;\n }\n\n /**\n * Gets the total number of knowledge base records\n */\n async size(): Promise<number> {\n return await this.storage.size();\n }\n}\n",
11
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { type ITabularStorage } from \"@workglow/storage\";\nimport { EventEmitter, type EventParameters } from \"@workglow/util\";\n\nimport {\n KnowledgeBasePrimaryKeyNames,\n type KnowledgeBaseRecord,\n KnowledgeBaseRecordSchema,\n} from \"./KnowledgeBaseSchema\";\n\n/**\n * Events that can be emitted by the KnowledgeBaseRepository\n */\n\nexport type KnowledgeBaseEventListeners = {\n knowledge_base_added: (record: KnowledgeBaseRecord) => void;\n knowledge_base_removed: (record: KnowledgeBaseRecord) => void;\n knowledge_base_updated: (record: KnowledgeBaseRecord) => void;\n};\n\nexport type KnowledgeBaseEvents = keyof KnowledgeBaseEventListeners;\n\nexport type KnowledgeBaseEventListener<Event extends KnowledgeBaseEvents> =\n KnowledgeBaseEventListeners[Event];\n\nexport type KnowledgeBaseEventParameters<Event extends KnowledgeBaseEvents> = EventParameters<\n KnowledgeBaseEventListeners,\n Event\n>;\n\n/**\n * Repository for persisting KnowledgeBase metadata to tabular storage.\n * Follows the same pattern as ModelRepository.\n */\nexport class KnowledgeBaseRepository {\n /**\n * Storage for KnowledgeBase records\n */\n protected readonly storage: ITabularStorage<\n typeof KnowledgeBaseRecordSchema,\n typeof KnowledgeBasePrimaryKeyNames\n >;\n\n constructor(\n storage: ITabularStorage<\n typeof KnowledgeBaseRecordSchema,\n typeof KnowledgeBasePrimaryKeyNames\n >\n ) {\n this.storage = storage;\n }\n\n /** Event emitter for repository events */\n protected events = new EventEmitter<KnowledgeBaseEventListeners>();\n\n /**\n * Sets up the database for the repository.\n * Must be called before using any other methods.\n */\n async setupDatabase(): Promise<void> {\n await this.storage.setupDatabase?.();\n }\n\n /**\n * Registers an event listener for the specified event\n */\n on<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.on(name, fn);\n }\n\n /**\n * Removes an event listener for the specified event\n */\n off<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.off(name, fn);\n }\n\n /**\n * Adds an event listener that will only be called once\n */\n once<Event extends KnowledgeBaseEvents>(name: Event, fn: KnowledgeBaseEventListener<Event>) {\n this.events.once(name, fn);\n }\n\n /**\n * Returns when the event was emitted (promise form of once)\n */\n waitOn<Event extends KnowledgeBaseEvents>(name: Event) {\n return this.events.waitOn(name);\n }\n\n /**\n * Adds a new knowledge base record to the repository\n */\n async addKnowledgeBase(record: KnowledgeBaseRecord): Promise<KnowledgeBaseRecord> {\n await this.storage.put(record);\n this.events.emit(\"knowledge_base_added\", record);\n return record;\n }\n\n /**\n * Removes a knowledge base record from the repository\n */\n async removeKnowledgeBase(kb_id: string): Promise<void> {\n const record = await this.storage.get({ kb_id });\n if (!record) {\n throw new Error(`KnowledgeBase with id \"${kb_id}\" not found`);\n }\n await this.storage.delete({ kb_id });\n this.events.emit(\"knowledge_base_removed\", record);\n }\n\n /**\n * Retrieves a knowledge base record by ID\n */\n async getKnowledgeBase(kb_id: string): Promise<KnowledgeBaseRecord | undefined> {\n if (typeof kb_id !== \"string\") return undefined;\n const record = await this.storage.get({ kb_id });\n return record ?? undefined;\n }\n\n /**\n * Enumerates all knowledge base records\n */\n async enumerateAll(): Promise<KnowledgeBaseRecord[]> {\n const records = await this.storage.getAll();\n if (!records || records.length === 0) return [];\n return records;\n }\n\n /**\n * Gets the total number of knowledge base records\n */\n async size(): Promise<number> {\n return await this.storage.size();\n }\n}\n",
12
12
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { InMemoryTabularStorage } from \"@workglow/storage\";\nimport { KnowledgeBaseRepository } from \"./KnowledgeBaseRepository\";\nimport { KnowledgeBasePrimaryKeyNames, KnowledgeBaseRecordSchema } from \"./KnowledgeBaseSchema\";\n\n/**\n * In-memory implementation of a knowledge base repository.\n */\nexport class InMemoryKnowledgeBaseRepository extends KnowledgeBaseRepository {\n constructor() {\n super(new InMemoryTabularStorage(KnowledgeBaseRecordSchema, KnowledgeBasePrimaryKeyNames));\n }\n}\n",
13
13
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {\n createServiceToken,\n globalServiceRegistry,\n registerInputResolver,\n ServiceRegistry,\n} from \"@workglow/util\";\nimport { InMemoryKnowledgeBaseRepository } from \"./InMemoryKnowledgeBaseRepository\";\nimport type { KnowledgeBase } from \"./KnowledgeBase\";\nimport { KnowledgeBaseRepository } from \"./KnowledgeBaseRepository\";\nimport { knowledgeBaseTableNames, type KnowledgeBaseRecord } from \"./KnowledgeBaseSchema\";\n\n/**\n * Service token for the knowledge base registry\n * Maps knowledge base IDs to KnowledgeBase instances\n */\nexport const KNOWLEDGE_BASES =\n createServiceToken<Map<string, KnowledgeBase>>(\"knowledge-base.registry\");\n\n/**\n * Service token for the knowledge base repository\n */\nexport const KNOWLEDGE_BASE_REPOSITORY = createServiceToken<KnowledgeBaseRepository>(\n \"knowledge-base.repository\"\n);\n\n// Register default factory for live KB map if not already registered\nif (!globalServiceRegistry.has(KNOWLEDGE_BASES)) {\n globalServiceRegistry.register(\n KNOWLEDGE_BASES,\n (): Map<string, KnowledgeBase> => new Map(),\n true\n );\n}\n\n// Register default factory for KB repository if not already registered\nif (!globalServiceRegistry.has(KNOWLEDGE_BASE_REPOSITORY)) {\n globalServiceRegistry.register(\n KNOWLEDGE_BASE_REPOSITORY,\n (): KnowledgeBaseRepository => new InMemoryKnowledgeBaseRepository(),\n true\n );\n}\n\n/**\n * Gets the global knowledge base registry\n */\nexport function getGlobalKnowledgeBases(): Map<string, KnowledgeBase> {\n return globalServiceRegistry.get(KNOWLEDGE_BASES);\n}\n\n/**\n * Gets the global knowledge base repository instance\n */\nexport function getGlobalKnowledgeBaseRepository(): KnowledgeBaseRepository {\n return globalServiceRegistry.get(KNOWLEDGE_BASE_REPOSITORY);\n}\n\n/**\n * Sets the global knowledge base repository instance\n */\nexport function setGlobalKnowledgeBaseRepository(repository: KnowledgeBaseRepository): void {\n globalServiceRegistry.registerInstance(KNOWLEDGE_BASE_REPOSITORY, repository);\n}\n\n/**\n * Registers a knowledge base globally by ID.\n * Adds to both the live Map and the persistent repository.\n */\nexport async function registerKnowledgeBase(id: string, kb: KnowledgeBase): Promise<void> {\n const kbs = getGlobalKnowledgeBases();\n kbs.set(id, kb);\n\n const now = new Date().toISOString();\n const tableNames = knowledgeBaseTableNames(id);\n const record: KnowledgeBaseRecord = {\n kb_id: id,\n title: kb.title,\n description: kb.description,\n vector_dimensions: kb.getVectorDimensions(),\n document_table: tableNames.documentTable,\n chunk_table: tableNames.chunkTable,\n created_at: now,\n updated_at: now,\n };\n const repo = getGlobalKnowledgeBaseRepository();\n await repo.addKnowledgeBase(record);\n}\n\n/**\n * Gets a knowledge base by ID from the global registry\n */\nexport function getKnowledgeBase(id: string): KnowledgeBase | undefined {\n return getGlobalKnowledgeBases().get(id);\n}\n\n/**\n * Resolves a knowledge base ID from the registry.\n * Used by the input resolver system.\n */\nasync function resolveKnowledgeBaseFromRegistry(\n id: string,\n format: string,\n registry: ServiceRegistry\n): Promise<KnowledgeBase> {\n const kbs = registry.has(KNOWLEDGE_BASES)\n ? registry.get<Map<string, KnowledgeBase>>(KNOWLEDGE_BASES)\n : getGlobalKnowledgeBases();\n\n const kb = kbs.get(id);\n if (!kb) {\n throw new Error(`Knowledge base \"${id}\" not found in registry`);\n }\n return kb;\n}\n\n// Register the resolver for format: \"knowledge-base\"\nregisterInputResolver(\"knowledge-base\", resolveKnowledgeBaseFromRegistry);\n",
14
14
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { InMemoryTabularStorage, InMemoryVectorStorage } from \"@workglow/storage\";\nimport type { TypedArray } from \"@workglow/util\";\nimport { ChunkVectorPrimaryKey, ChunkVectorStorageSchema } from \"../chunk/ChunkVectorStorageSchema\";\nimport type { ChunkVectorStorage } from \"../chunk/ChunkVectorStorageSchema\";\nimport { DocumentStorageKey, DocumentStorageSchema } from \"../document/DocumentStorageSchema\";\nimport type { DocumentTabularStorage } from \"../document/DocumentStorageSchema\";\nimport { KnowledgeBase } from \"./KnowledgeBase\";\nimport { registerKnowledgeBase } from \"./KnowledgeBaseRegistry\";\n\nexport interface CreateKnowledgeBaseOptions {\n readonly name: string;\n readonly vectorDimensions: number;\n readonly vectorType?: { new (array: number[]): TypedArray };\n readonly register?: boolean;\n readonly title?: string;\n readonly description?: string;\n}\n\n/**\n * Factory function to create a KnowledgeBase with minimal configuration.\n *\n * @example\n * ```typescript\n * const kb = await createKnowledgeBase({\n * name: \"my-kb\",\n * vectorDimensions: 1024,\n * });\n * ```\n */\nexport async function createKnowledgeBase(\n options: CreateKnowledgeBaseOptions\n): Promise<KnowledgeBase> {\n const {\n name,\n vectorDimensions,\n vectorType = Float32Array,\n register: shouldRegister = true,\n title,\n description,\n } = options;\n\n const tabularStorage = new InMemoryTabularStorage(DocumentStorageSchema, DocumentStorageKey);\n await tabularStorage.setupDatabase();\n\n const vectorStorage = new InMemoryVectorStorage(\n ChunkVectorStorageSchema,\n ChunkVectorPrimaryKey,\n [],\n vectorDimensions,\n vectorType\n );\n await vectorStorage.setupDatabase();\n\n const kb = new KnowledgeBase(\n name,\n tabularStorage as unknown as DocumentTabularStorage,\n vectorStorage as unknown as ChunkVectorStorage,\n title,\n description\n );\n\n if (shouldRegister) {\n await registerKnowledgeBase(name, kb);\n }\n\n return kb;\n}\n",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@workglow/knowledge-base",
3
3
  "type": "module",
4
- "version": "0.0.117",
4
+ "version": "0.0.119",
5
5
  "description": "Dataset package for Workglow.",
6
6
  "scripts": {
7
7
  "watch": "concurrently -c 'auto' 'bun:watch-*'",
@@ -21,9 +21,9 @@
21
21
  "test": "bun test"
22
22
  },
23
23
  "peerDependencies": {
24
- "@workglow/storage": "0.0.117",
25
- "@workglow/util": "0.0.117",
26
- "@workglow/sqlite": "0.0.117"
24
+ "@workglow/storage": "0.0.119",
25
+ "@workglow/util": "0.0.119",
26
+ "@workglow/sqlite": "0.0.119"
27
27
  },
28
28
  "peerDependenciesMeta": {
29
29
  "@workglow/storage": {
@@ -37,9 +37,9 @@
37
37
  }
38
38
  },
39
39
  "devDependencies": {
40
- "@workglow/storage": "0.0.117",
41
- "@workglow/util": "0.0.117",
42
- "@workglow/sqlite": "0.0.117"
40
+ "@workglow/storage": "0.0.119",
41
+ "@workglow/util": "0.0.119",
42
+ "@workglow/sqlite": "0.0.119"
43
43
  },
44
44
  "exports": {
45
45
  ".": {