From b06cb0037e89890f33c7bf2acb8ded3ae7c23b28 Mon Sep 17 00:00:00 2001 From: Indra Zulfi Date: Tue, 16 Jun 2026 14:57:48 +0700 Subject: [PATCH 1/3] feat: add Pinecone and Milvus vector store adapters Add @anvia/pinecone and @anvia/milvus packages following the existing adapter pattern (Chroma, PgVector, Qdrant). Both implement the VectorSearchIndex interface from @anvia/core with full filter translation, multi-embedding support, and asTool() integration. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- packages/vector-stores/milvus/package.json | 43 + packages/vector-stores/milvus/src/index.ts | 292 +++++ packages/vector-stores/milvus/tsconfig.json | 7 + packages/vector-stores/pinecone/package.json | 43 + packages/vector-stores/pinecone/src/index.ts | 248 +++++ packages/vector-stores/pinecone/tsconfig.json | 7 + pnpm-lock.yaml | 996 +++++++++++++++++- 7 files changed, 1633 insertions(+), 3 deletions(-) create mode 100644 packages/vector-stores/milvus/package.json create mode 100644 packages/vector-stores/milvus/src/index.ts create mode 100644 packages/vector-stores/milvus/tsconfig.json create mode 100644 packages/vector-stores/pinecone/package.json create mode 100644 packages/vector-stores/pinecone/src/index.ts create mode 100644 packages/vector-stores/pinecone/tsconfig.json diff --git a/packages/vector-stores/milvus/package.json b/packages/vector-stores/milvus/package.json new file mode 100644 index 00000000..95204214 --- /dev/null +++ b/packages/vector-stores/milvus/package.json @@ -0,0 +1,43 @@ +{ + "name": "@anvia/milvus", + "version": "0.2.5", + "description": "Milvus vector store adapter for Anvia.", + "author": "anvia", + "maintainer": "Indra Zulfi", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/anvia-hq/anvia", + "directory": "packages/vector-stores/milvus" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "scripts": { + "build": "tsup src/index.ts --format esm --dts --sourcemap --clean", + "test": "vitest run", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@anvia/core": "workspace:*", + "@zilliz/milvus2-sdk-node": "^2.5.0" + }, + "devDependencies": { + "@types/node": "^24.9.1", + "tsup": "^8.5.0", + "typescript": "^5.9.3", + "vitest": "^4.0.8" + } +} diff --git a/packages/vector-stores/milvus/src/index.ts b/packages/vector-stores/milvus/src/index.ts new file mode 100644 index 00000000..e2e4e31a --- /dev/null +++ b/packages/vector-stores/milvus/src/index.ts @@ -0,0 +1,292 @@ +import { createHash } from "node:crypto"; +import { + type EmbeddedDocument, + type EmbeddingModel, + embedText, + type VectorMetadata, +} from "@anvia/core/embeddings"; +import type { Tool } from "@anvia/core/tool"; +import { + createVectorSearchTool, + type VectorFilter, + type VectorSearchIndex, + type VectorSearchRequest, + type VectorSearchResult, + type VectorSearchToolOptions, +} from "@anvia/core/vector-store"; + +const documentIdFieldName = "__anvia_document_id"; +const documentFieldName = "__anvia_document"; +const reservedFieldPrefix = "__anvia_"; + +export type MilvusMetric = "COSINE" | "L2" | "IP"; + +type MilvusClientLike = { + hasCollection(options: { collection_name: string }): Promise<{ value: boolean }>; + createCollection(options: Record): Promise; + createIndex(options: Record): Promise; + loadCollection(options: { collection_name: string }): Promise; + insert(options: Record): Promise; + search(options: Record): Promise; +}; + +export type MilvusVectorStoreConnectOptions = { + client?: MilvusClientLike | undefined; + collectionName: string; + vectorSize: number; + createIfMissing?: boolean | undefined; + metric?: MilvusMetric | undefined; +}; + +export class MilvusVectorStore { + private constructor( + private readonly client: MilvusClientLike, + private readonly collectionName: string, + ) {} + + static async connect( + options: MilvusVectorStoreConnectOptions, + ): Promise> { + const client = options.client ?? (await defaultMilvusClient()); + + if (options.createIfMissing !== false) { + await ensureCollection( + client, + options.collectionName, + options.vectorSize, + options.metric ?? "COSINE", + ); + } + + await client.loadCollection({ collection_name: options.collectionName }); + return new MilvusVectorStore(client, options.collectionName); + } + + async upsertDocuments(documents: Array>): Promise { + const rows = documents.flatMap((document) => milvusRows(document)); + await this.client.insert({ + collection_name: this.collectionName, + data: rows, + }); + } + + index(model: EmbeddingModel): MilvusVectorIndex { + return new MilvusVectorIndex(model, this.client, this.collectionName); + } +} + +export class MilvusVectorIndex + implements VectorSearchIndex +{ + constructor( + private readonly model: EmbeddingModel, + private readonly client: MilvusClientLike, + private readonly collectionName: string, + ) {} + + async search(request: VectorSearchRequest): Promise>> { + const queryEmbedding = await embedText(this.model, request.query); + const filterExpr = filterToMilvusExpr(request.filter); + const response = await this.client.search({ + collection_name: this.collectionName, + vector: [queryEmbedding.vector], + limit: request.topK, + ...(filterExpr !== undefined ? { filter: filterExpr } : {}), + output_fields: [documentIdFieldName, documentFieldName, "*"], + }); + return parseQueryResults(response, request.threshold); + } + + async searchIds(request: VectorSearchRequest): Promise> { + return (await this.search(request)).map(({ score, id }) => ({ score, id })); + } + + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { + return createVectorSearchTool(this, options); + } +} + +export function filterToMilvusExpr(filter: VectorFilter | undefined): string | undefined { + if (filter === undefined) { + return undefined; + } + + switch (filter.type) { + case "eq": { + const val = milvusLiteral(filter.value); + return `${filter.key} == ${val}`; + } + case "gt": { + const val = milvusLiteral(filter.value); + return `${filter.key} > ${val}`; + } + case "lt": { + const val = milvusLiteral(filter.value); + return `${filter.key} < ${val}`; + } + case "and": { + const parts = filter.filters + .map(filterToMilvusExpr) + .filter((part): part is string => part !== undefined); + return parts.length > 0 ? parts.map((p) => `(${p})`).join(" && ") : undefined; + } + case "or": { + const parts = filter.filters + .map(filterToMilvusExpr) + .filter((part): part is string => part !== undefined); + return parts.length > 0 ? parts.map((p) => `(${p})`).join(" || ") : undefined; + } + } +} + +function milvusLiteral(value: string | number | boolean | null): string { + if (value === null) { + return "null"; + } + if (typeof value === "string") { + return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`; + } + return String(value); +} + +async function defaultMilvusClient(): Promise { + const { MilvusClient } = await import("@zilliz/milvus2-sdk-node"); + return new MilvusClient({ address: "localhost:19530" }) as unknown as MilvusClientLike; +} + +async function ensureCollection( + client: MilvusClientLike, + collectionName: string, + vectorSize: number, + metric: MilvusMetric, +): Promise { + const { value: exists } = await client.hasCollection({ collection_name: collectionName }); + if (exists) { + return; + } + + await client.createCollection({ + collection_name: collectionName, + fields: [ + { name: "id", data_type: "VarChar", max_length: 64, is_primary_key: true }, + { name: documentIdFieldName, data_type: "VarChar", max_length: 4096 }, + { name: documentFieldName, data_type: "VarChar", max_length: 65535 }, + { name: "vector", data_type: "FloatVector", dim: vectorSize }, + ], + metric_type: metric, + }); + + await client.createIndex({ + collection_name: collectionName, + field_name: "vector", + index_type: "HNSW", + metric_type: metric, + params: { M: 16, efConstruction: 256 }, + }); +} + +function milvusRows( + document: EmbeddedDocument, +): Array> { + if (document.embeddings.length === 0) { + throw new Error(`Document ${document.id} has no embeddings`); + } + + assertNoReservedFields(document.metadata); + + return document.embeddings.map((embedding, index) => { + const logicalId = + document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; + return { + id: pointId(logicalId), + [documentIdFieldName]: document.id, + [documentFieldName]: serializeDocument(document.document), + vector: embedding.vector, + ...(document.metadata ?? {}), + }; + }); +} + +function assertNoReservedFields(metadata: VectorMetadata | undefined): void { + for (const key of Object.keys(metadata ?? {})) { + if (key.startsWith(reservedFieldPrefix)) { + throw new Error(`Metadata key ${key} is reserved for Anvia Milvus fields`); + } + } +} + +function pointId(id: string): string { + return createHash("sha256").update(id).digest("hex").slice(0, 32); +} + +function serializeDocument(document: unknown): string { + return typeof document === "string" ? document : JSON.stringify(document); +} + +function parseQueryResults( + response: unknown, + threshold: number | undefined, +): Array> { + const raw = response as { + results?: Array< + Array<{ + id: string; + score?: number; + [documentIdFieldName]?: string; + [documentFieldName]?: string; + [key: string]: unknown; + }> + >; + }; + const matches = raw.results?.[0] ?? []; + + const byId = new Map>(); + + for (const match of matches) { + const score = match.score ?? 0; + if (threshold !== undefined && score < threshold) { + continue; + } + + const id = String(match[documentIdFieldName] ?? match.id); + const result = { + id, + score, + document: parseDocument(match[documentFieldName]), + ...metadataFromRow(match), + } as VectorSearchResult; + + const current = byId.get(id); + if (current === undefined || result.score > current.score) { + byId.set(id, result); + } + } + + return [...byId.values()]; +} + +function parseDocument(document: unknown): T { + if (document === null || document === undefined) { + return "" as T; + } + if (typeof document !== "string") { + return document as T; + } + try { + return JSON.parse(document) as T; + } catch { + return document as T; + } +} + +function metadataFromRow( + row: Record, +): { metadata?: Metadata | undefined } { + const skipKeys = new Set(["id", "score", documentIdFieldName, documentFieldName, "vector"]); + const metadata = Object.fromEntries( + Object.entries(row).filter( + ([key]) => !skipKeys.has(key) && !key.startsWith(reservedFieldPrefix), + ), + ) as Metadata; + return Object.keys(metadata).length === 0 ? {} : { metadata }; +} diff --git a/packages/vector-stores/milvus/tsconfig.json b/packages/vector-stores/milvus/tsconfig.json new file mode 100644 index 00000000..9fd54c2d --- /dev/null +++ b/packages/vector-stores/milvus/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "test", "vitest.config.ts"] +} diff --git a/packages/vector-stores/pinecone/package.json b/packages/vector-stores/pinecone/package.json new file mode 100644 index 00000000..3a7f9e2d --- /dev/null +++ b/packages/vector-stores/pinecone/package.json @@ -0,0 +1,43 @@ +{ + "name": "@anvia/pinecone", + "version": "0.2.5", + "description": "Pinecone vector store adapter for Anvia.", + "author": "anvia", + "maintainer": "Indra Zulfi", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/anvia-hq/anvia", + "directory": "packages/vector-stores/pinecone" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "scripts": { + "build": "tsup src/index.ts --format esm --dts --sourcemap --clean", + "test": "vitest run", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@anvia/core": "workspace:*", + "@pinecone-database/pinecone": "^5.1.0" + }, + "devDependencies": { + "@types/node": "^24.9.1", + "tsup": "^8.5.0", + "typescript": "^5.9.3", + "vitest": "^4.0.8" + } +} diff --git a/packages/vector-stores/pinecone/src/index.ts b/packages/vector-stores/pinecone/src/index.ts new file mode 100644 index 00000000..b7145b6f --- /dev/null +++ b/packages/vector-stores/pinecone/src/index.ts @@ -0,0 +1,248 @@ +import { createHash } from "node:crypto"; +import { + type EmbeddedDocument, + type EmbeddingModel, + embedText, + type VectorMetadata, +} from "@anvia/core/embeddings"; +import type { Tool } from "@anvia/core/tool"; +import { + createVectorSearchTool, + type VectorFilter, + type VectorSearchIndex, + type VectorSearchRequest, + type VectorSearchResult, + type VectorSearchToolOptions, +} from "@anvia/core/vector-store"; + +const documentIdMetadataKey = "__anvia_document_id"; +const documentMetadataKey = "__anvia_document"; +const reservedMetadataPrefix = "__anvia_"; + +export type PineconeMetric = "cosine" | "euclidean" | "dotproduct"; + +type PineconeClientLike = { + listIndexes(): Promise; + createIndex(options: Record): Promise; + index(indexName: string): PineconeIndexLike; +}; + +type PineconeIndexLike = { + namespace(namespace: string): PineconeNamespaceLike; +}; + +type PineconeNamespaceLike = { + upsert(vectors: Array>): Promise; + query(options: Record): Promise; +}; + +export type PineconeVectorStoreConnectOptions = { + client?: PineconeClientLike | undefined; + indexName: string; + namespace?: string | undefined; + createIfMissing?: boolean | undefined; + metric?: PineconeMetric | undefined; +}; + +export class PineconeVectorStore { + private constructor(private readonly namespace: PineconeNamespaceLike) {} + + static async connect( + options: PineconeVectorStoreConnectOptions, + ): Promise> { + const client = options.client ?? (await defaultPineconeClient()); + + if (options.createIfMissing !== false) { + await ensureIndex(client, options.indexName, options.metric ?? "cosine"); + } + + const namespace = client.index(options.indexName).namespace(options.namespace ?? ""); + return new PineconeVectorStore(namespace); + } + + async upsertDocuments(documents: Array>): Promise { + const vectors = documents.flatMap((document) => pineconeVectors(document)); + await this.namespace.upsert(vectors); + } + + index(model: EmbeddingModel): PineconeVectorIndex { + return new PineconeVectorIndex(model, this.namespace); + } +} + +export class PineconeVectorIndex + implements VectorSearchIndex +{ + constructor( + private readonly model: EmbeddingModel, + private readonly namespace: PineconeNamespaceLike, + ) {} + + async search(request: VectorSearchRequest): Promise>> { + const queryEmbedding = await embedText(this.model, request.query); + const response = await this.namespace.query({ + vector: queryEmbedding.vector, + topK: request.topK, + filter: filterToPineconeFilter(request.filter), + includeMetadata: true, + }); + return parseQueryResults(response, request.threshold); + } + + async searchIds(request: VectorSearchRequest): Promise> { + return (await this.search(request)).map(({ score, id }) => ({ score, id })); + } + + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { + return createVectorSearchTool(this, options); + } +} + +export function filterToPineconeFilter(filter: VectorFilter | undefined): unknown { + if (filter === undefined) { + return undefined; + } + + switch (filter.type) { + case "eq": + return { [filter.key]: { $eq: filter.value } }; + case "gt": + return { [filter.key]: { $gt: filter.value } }; + case "lt": + return { [filter.key]: { $lt: filter.value } }; + case "and": + return { $and: filter.filters.map(filterToPineconeFilter) }; + case "or": + return { $or: filter.filters.map(filterToPineconeFilter) }; + } +} + +async function defaultPineconeClient(): Promise { + const { Pinecone } = await import("@pinecone-database/pinecone"); + return new Pinecone() as unknown as PineconeClientLike; +} + +async function ensureIndex( + client: PineconeClientLike, + indexName: string, + metric: PineconeMetric, +): Promise { + try { + await client.listIndexes(); + } catch { + return; + } + + try { + await client.createIndex({ + name: indexName, + dimension: undefined, + metric, + spec: { serverless: { cloud: "aws", region: "us-east-1" } }, + waitUntilReady: true, + }); + } catch { + // Index already exists + } +} + +function pineconeVectors( + document: EmbeddedDocument, +): Array> { + if (document.embeddings.length === 0) { + throw new Error(`Document ${document.id} has no embeddings`); + } + + assertNoReservedMetadata(document.metadata); + + return document.embeddings.map((embedding, index) => { + const logicalId = + document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; + return { + id: pointId(logicalId), + values: embedding.vector, + metadata: { + [documentIdMetadataKey]: document.id, + [documentMetadataKey]: serializeDocument(document.document), + ...(document.metadata ?? {}), + }, + }; + }); +} + +function assertNoReservedMetadata(metadata: VectorMetadata | undefined): void { + for (const key of Object.keys(metadata ?? {})) { + if (key.startsWith(reservedMetadataPrefix)) { + throw new Error(`Metadata key ${key} is reserved for Anvia Pinecone payloads`); + } + } +} + +function pointId(id: string): string { + return createHash("sha256").update(id).digest("hex").slice(0, 32); +} + +function serializeDocument(document: unknown): string { + return typeof document === "string" ? document : JSON.stringify(document); +} + +function parseQueryResults( + response: unknown, + threshold: number | undefined, +): Array> { + const raw = response as { + matches?: Array<{ + id: string; + score?: number; + metadata?: Record | null; + }>; + }; + const matches = raw.matches ?? []; + + const byId = new Map>(); + + for (const match of matches) { + const score = match.score ?? 0; + if (threshold !== undefined && score < threshold) { + continue; + } + + const id = String(match.metadata?.[documentIdMetadataKey] ?? match.id); + const result = { + id, + score, + document: parseDocument(match.metadata?.[documentMetadataKey]), + ...metadataFromPayload(match.metadata), + } as VectorSearchResult; + + const current = byId.get(id); + if (current === undefined || result.score > current.score) { + byId.set(id, result); + } + } + + return [...byId.values()]; +} + +function parseDocument(document: unknown): T { + if (document === null || document === undefined) { + return "" as T; + } + if (typeof document !== "string") { + return document as T; + } + try { + return JSON.parse(document) as T; + } catch { + return document as T; + } +} + +function metadataFromPayload( + payload: Record | null | undefined, +): { metadata?: Metadata | undefined } { + const metadata = Object.fromEntries( + Object.entries(payload ?? {}).filter(([key]) => !key.startsWith(reservedMetadataPrefix)), + ) as Metadata; + return Object.keys(metadata).length === 0 ? {} : { metadata }; +} diff --git a/packages/vector-stores/pinecone/tsconfig.json b/packages/vector-stores/pinecone/tsconfig.json new file mode 100644 index 00000000..9fd54c2d --- /dev/null +++ b/packages/vector-stores/pinecone/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "test", "vitest.config.ts"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c6a55523..1f8df2d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,7 +34,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.8 - version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) apps/docs: dependencies: @@ -497,7 +497,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.8 - version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) packages/server: devDependencies: @@ -658,6 +658,28 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) + packages/vector-stores/milvus: + dependencies: + '@anvia/core': + specifier: workspace:* + version: link:../../core + '@zilliz/milvus2-sdk-node': + specifier: ^2.5.0 + version: 2.6.17 + devDependencies: + '@types/node': + specifier: ^24.9.1 + version: 24.12.2 + tsup: + specifier: ^8.5.0 + version: 8.5.1(jiti@2.6.1)(postcss@8.5.12)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.0.8 + version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) + packages/vector-stores/pgvector: dependencies: '@anvia/core': @@ -686,6 +708,28 @@ importers: specifier: ^4.0.8 version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) + packages/vector-stores/pinecone: + dependencies: + '@anvia/core': + specifier: workspace:* + version: link:../../core + '@pinecone-database/pinecone': + specifier: ^5.1.0 + version: 5.1.2 + devDependencies: + '@types/node': + specifier: ^24.9.1 + version: 24.12.2 + tsup: + specifier: ^8.5.0 + version: 8.5.1(jiti@2.6.1)(postcss@8.5.12)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.0.8 + version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.9.0)) + packages/vector-stores/qdrant: dependencies: '@anvia/core': @@ -748,6 +792,109 @@ packages: resolution: {integrity: sha512-IQD9wkVReKAhsEAbDjh/0KrBGTEXelqZLpOBRDaIRvlzZ9sjmUP+gKbpvzyJnei2JHQiE8JAgj7YcNloINbGBw==} engines: {node: '>= 10'} + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/crc32c@5.2.0': + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + + '@aws-crypto/sha1-browser@5.2.0': + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/checksums@3.1000.6': + resolution: {integrity: sha512-RMCrCteiUwYTEv2G9zfP/BEuKHv57665vVieJyp9cf8VgilWxP/KrWVtMdfdDlIH8nFhvu3rIMc29z3ebGEZ1w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-s3@3.1069.0': + resolution: {integrity: sha512-zkwCW4D88foQ0YHyjVyFhDOIWG+IWiTGXYg8+kUgH6M19son3OsZLvioZGyZdNtlRgLNYfQGBjfArSJOYYt2QQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.974.21': + resolution: {integrity: sha512-P5JAHvn4dTi96UsAGS67LVOqqpUNNRhnfFXqzCYtdBIGZtqBue4CXvRr9YenOO7PALj/Pn8uuyw53FBCiCYw8w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.47': + resolution: {integrity: sha512-3YoPwJczcc+MtX2xxXaYaOOWO6xKUJr1ZIIDIFuninr51BYONVVcF/CP8K2xfVRC/PztJjqKWxNGFH7BWQAw1Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.49': + resolution: {integrity: sha512-2UtGUPy+x3lqyceHrtC1uEuVxBZbDalPF6KAFqBwYgm4edWdBrZKNnCqzDs7KynWUvEC6mrR+ojRk+ZgQz9C2w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.972.54': + resolution: {integrity: sha512-Hx4gO4YRjFwitf3MVl3cDwYe1aryJthC4txVl9b+JAURovA50M2ywf9r8j1E/Q6SCTPT4qQpjOAbKYIC9CG+Vw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.53': + resolution: {integrity: sha512-+71sluhkgPqdhbbD3UDwUpj24GCkng9HQx6z7qoBFb8dwkF4ktpOcVKDeHpgg8PvBgLYwAnUYLTEGRC/PniCiQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.56': + resolution: {integrity: sha512-iI+4o0dvQQ4NHel4FMDiFy5q2gaU/ryLK3niOsoPccAt9WLFRkV4XTYPWRr9XvmBUqEzXG73S4p/8gm0Lu/W3A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.47': + resolution: {integrity: sha512-tAizPm9IFo/PHn06c+LQJlzfY2AGOlyF0CUljFejrU6LcZBjnk8pmbZK3/xoIDdnIzjEdbClfvY3mXfr818ZEg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.972.53': + resolution: {integrity: sha512-pUXE3fu4tfEDV8BksIgf4dXvuIH10FhwHMl/wu8rBD5T1sMpryQWFVitH3kdPS90wlgrGYJQ/meQTSPacyZfeg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.53': + resolution: {integrity: sha512-JmMGlhVvSj8uSG9CpeDkJAXT35H89tc6v84iMgEIE75q4yp1MKVVKvopv6Gg28HJIR7hMNkojRF8H2m5W44wyg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.974.31': + resolution: {integrity: sha512-Yzj6NRYVZdBaCp7o1BwHGyeDBfixdeToLIAMprshIITEdl9wKVSiidVOfeaiH8FyeC1hBmBfDZFvs/aH1Y3xpw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.972.52': + resolution: {integrity: sha512-rerjP08onRqkBh0AcCqip6GkKvESapmLoTgi1xysZ4C6a1xMrIMtTBcEbUb6EY71oeajnigeUD4KwZjtIO+aWQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/nested-clients@3.997.21': + resolution: {integrity: sha512-eC7Vl7Qom/BGhZjG9GEqPwdQ/fk45hg1t5LP4EUxG5d1fdshLbaxCiwh/tszUzDX/4mW40mu2QsbeJJRPBbqUw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.996.35': + resolution: {integrity: sha512-6L/VWs+Wch2stHemCGTmUNqKLMzURxQDK5boNG3Jn3kAOp71meDUuS5sbObpEvFxHDq0uWeSLFDNSYsjNt+Dlg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1069.0': + resolution: {integrity: sha512-ks4X+kngC3PA5howV7Qu1TgG4bfC4jPykKdvw3nmBSXR9yZxRJouBholFSNQ5kY3L+Fgwyw+LCjzQmNi+KR91g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.973.13': + resolution: {integrity: sha512-pEHZqRkAlHfnfAU9tK+WpKv/gBNjGJrHMgA3A0iYRGyswBS2t0pfez+lWlwktb3Bqa0ovh7w/QJTFwp3fDxLNg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-locate-window@3.965.8': + resolution: {integrity: sha512-uUbMs1cBZPafD0ohUj6EwNf0fPZ534NvBxHox4hjX+0Rxq5paSYUem7+hi833pYrzrcnBATKIYpR02MDXT5M9g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/xml-builder@3.972.30': + resolution: {integrity: sha512-StElZPEoBquWwNqw1AcfpzEyZqJvFxouG+mpDNYlcH6ZOrqd2CuIryv+8LV8gNHZUOyKyJF3Dq9vxaXEmDR9TQ==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.4': + resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} + engines: {node: '>=18.0.0'} + '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -1092,10 +1239,17 @@ packages: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} + '@dabh/diagnostics@2.0.8': + resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==} + '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} @@ -1967,6 +2121,9 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 + '@nodable/entities@2.2.0': + resolution: {integrity: sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -2330,6 +2487,9 @@ packages: '@oxc-project/types@0.127.0': resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} + '@petamoriken/float16@3.9.3': + resolution: {integrity: sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g==} + '@phosphor-icons/react@2.1.10': resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==} engines: {node: '>=10'} @@ -2337,6 +2497,10 @@ packages: react: '>= 16.8' react-dom: '>= 16.8' + '@pinecone-database/pinecone@5.1.2': + resolution: {integrity: sha512-z7737KUA1hXwd508q1+o4bnRxj0NpMmzA2beyaFm7Y+EC2TYLT5ABuYsn/qhiwiEsYp8v1qS596eBhhvgNagig==} + engines: {node: '>=18.0.0'} + '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} @@ -3322,6 +3486,10 @@ packages: cpu: [x64] os: [win32] + '@shanghaikid/parquetjs@1.8.8': + resolution: {integrity: sha512-uaYXx4yP4cipVU8EZR4OZohiklsazyAcIXtfNEEYBcsMlnz5w4gEvv9vw+cP3yZBv7d1+OQYKfCfKI7oK0iUZQ==} + engines: {node: '>=18.18.2'} + '@shikijs/core@4.0.2': resolution: {integrity: sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==} engines: {node: '>=20'} @@ -3361,6 +3529,45 @@ packages: resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} engines: {node: '>=18'} + '@smithy/core@3.25.0': + resolution: {integrity: sha512-TTD6el7tvKyafkXBf7XO3jLOE+qVxOTrLjp/fEGiV3BMfUHK/LfdYlQO9YgZvzxC7kqA3H/IhJXNqQgnbgjb7A==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.4.0': + resolution: {integrity: sha512-pPQmNdEvMJttv9z2kdYxoui83p/nr32zjMf0aMfmzmGmFEgKXUfy0vXiNg0fx4R5XLQzmJBLM9Wg0guEq2/q8A==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.5.0': + resolution: {integrity: sha512-OG8kBYAgX7lf32+xLzgirvuLffn1KNoszaSiButt45i2cRa5irk8LQXLYQ5Smij1SBTN4KMNcBsRwRrLPfIGyA==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/node-http-handler@4.8.0': + resolution: {integrity: sha512-Mq7TNt/VhlEWiYRLQGpzUWeUxh899UGpjKh7Ru0WVIDIjnE+cTRAn0NYlFQ6bWfsQnKnpCbWJj86HzmcG0qEdg==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.5.0': + resolution: {integrity: sha512-vW6UdK7e7gV2wU/tXRsPq4pMQMusb8VymdVOyIFNA1FtyRmEClRFkYDtYI8UcO/HM0wK3qqjvvQs3HOlbgMbdg==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.15.0': + resolution: {integrity: sha512-Z5TAOxygoFvybJV3igo5SloFflSokHx2hu1eFA+DxDTcn+FtKxUSui+rbTRG1pAafMA888Z3MVvCWUuvCrTXjg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@so-ric/colorspace@1.1.6': + resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==} + '@speed-highlight/core@1.2.15': resolution: {integrity: sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==} @@ -3764,15 +3971,24 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/node-int64@0.4.32': + resolution: {integrity: sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==} + '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} '@types/node@24.12.2': resolution: {integrity: sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==} + '@types/node@25.9.3': + resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==} + '@types/pg@8.20.0': resolution: {integrity: sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==} + '@types/q@1.5.8': + resolution: {integrity: sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -3784,6 +4000,12 @@ packages: '@types/retry@0.12.0': resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + '@types/thrift@0.10.17': + resolution: {integrity: sha512-bDX6d5a5ZDWC81tgDv224n/3PKNYfIQJTPHzlbk4vBWJrYXF6Tg1ncaVmP/c3JbGN2AK9p7zmHorJC2D6oejGQ==} + + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -3801,6 +4023,7 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + deprecated: Potential CWE-502 - Update to 1.3.1 or higher '@upsetjs/venn.js@2.0.0': resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} @@ -3856,6 +4079,9 @@ packages: '@vitest/utils@4.1.5': resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==} + '@xterm/xterm@5.5.0': + resolution: {integrity: sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==} + '@xyflow/react@12.11.0': resolution: {integrity: sha512-na4IO33FSs2OS72hASgZDmTYwFAkef7Z74uBUVrong3ARmQQHfnRUVaCFn1kTt5LbS6pK03TbYjCPGLjLFfziA==} peerDependencies: @@ -3872,6 +4098,18 @@ packages: '@xyflow/system@0.0.77': resolution: {integrity: sha512-qCDCMCQAAgUu8yHnhloHG9F5mwPX5E+Wl8McpYIOPSSXfzFJJoZcwOcsDiAjitVKIg2de1WmJbCHfpcvxprsgg==} + '@zenfs/core@2.5.6': + resolution: {integrity: sha512-1BLPS6fsBN2keGDErbz548Cu6fT0xbVQlrVYNK5OAr9aviR2mC/F/4wxdb+dQZ4P4pVxxHI2DVouyniZ8yHxTw==} + engines: {node: '>= 18'} + hasBin: true + + '@zilliz/milvus2-sdk-node@2.6.17': + resolution: {integrity: sha512-Z+fCxhuHLV4kogU1T5SS9hDJqLH3KBEceU5snqDV/WDl6uxgNomqZY9md8TTRkHBISqNzDawEaZsi4MF6Y0oWA==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -3960,6 +4198,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + anynum@1.0.0: + resolution: {integrity: sha512-xjR9/zBVnUOP6ztMIIgShjsxui80nQUQH+5xJnvrYLs+90bF25/KJqaAi8mk+B4RDtX1Nspi6fmp4YTEts8SfA==} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -3988,6 +4229,12 @@ packages: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true + async-limiter@1.0.1: + resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + atomic-sleep@1.0.0: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} @@ -4035,18 +4282,35 @@ packages: resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + brotli-wasm@3.0.1: + resolution: {integrity: sha512-U3K72/JAi3jITpdhZBqzSUq+DUY697tLxOuFXB+FpAE/Ug+5C3VZrv4uA674EUZHxNAuQ9wETXNqQkxZD6oL4A==} + engines: {node: '>=v18.0.0'} + + browser-or-node@1.3.0: + resolution: {integrity: sha512-0F2z/VSnLbmEeBcUrSuDH5l0HxTXdQQzLjkmBR4cYfvg1zJrKSlmIZFqyFR8oX0NrwPhy3c3HQ6i3OxMbew4Tg==} + browserslist@4.28.2: resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + bson@6.10.4: + resolution: {integrity: sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==} + engines: {node: '>=16.20.1'} + buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + bundle-require@5.1.0: resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -4237,12 +4501,28 @@ packages: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} + color-convert@3.1.3: + resolution: {integrity: sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==} + engines: {node: '>=14.6'} + color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-name@2.1.0: + resolution: {integrity: sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==} + engines: {node: '>=12.20'} + + color-string@2.1.4: + resolution: {integrity: sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==} + engines: {node: '>=18'} + + color@5.0.3: + resolution: {integrity: sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==} + engines: {node: '>=18'} + comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -4588,6 +4868,9 @@ packages: emojilib@2.4.0: resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -4720,6 +5003,17 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + eventsource-parser@3.0.8: resolution: {integrity: sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==} engines: {node: '>=18.0.0'} @@ -4764,6 +5058,13 @@ packages: fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fast-xml-builder@1.2.0: + resolution: {integrity: sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==} + + fast-xml-parser@5.7.3: + resolution: {integrity: sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==} + hasBin: true + fastembed@2.1.0: resolution: {integrity: sha512-oQkpcRHBppJ3+a3w9dU0uytSY0N1cnEa/iVMc8AXEd+tvT529GekOEFhNviJy89R3lvQXF6cdIMTXHj1Gi00xQ==} @@ -4779,6 +5080,9 @@ packages: picomatch: optional: true + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + fetch-blob@3.2.0: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} @@ -4801,6 +5105,9 @@ packages: flatbuffers@25.9.23: resolution: {integrity: sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==} + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + formdata-polyfill@4.0.10: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} @@ -4962,6 +5269,10 @@ packages: resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==} engines: {node: '>=18'} + generic-pool@3.9.0: + resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} + engines: {node: '>= 4'} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -5137,6 +5448,9 @@ packages: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -5177,6 +5491,9 @@ packages: inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + int53@1.0.0: + resolution: {integrity: sha512-u8BMiMa05OPBgd32CKTead0CVTsFVgwFk23nNXo1teKPF6Sxcu0lXxEzP//zTcaKzXbGgPDXGmj/woyv+I4C5w==} + internmap@1.0.1: resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} @@ -5240,6 +5557,10 @@ packages: is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + is-subdir@1.2.0: resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} engines: {node: '>=4'} @@ -5255,6 +5576,11 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isomorphic-ws@4.0.1: + resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} + peerDependencies: + ws: '*' + istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -5331,6 +5657,9 @@ packages: resolution: {integrity: sha512-WHy4Coo+bGZyH7NwJKHkS04YFsFcarWbAEOAC3EMndzdN6VSZqklLLIgfxzyaW9jDoeGYJX9SWbJPKpecox0Uw==} hasBin: true + kerium@1.3.9: + resolution: {integrity: sha512-uoiTcutvUOjOpck8mmUUq7EsuNPhdfFoYMVehLmJGfdLmWC3nABU8KB63MQM0ydkAM4q+2zZmRIKovbx2LF8iA==} + khroma@2.1.0: resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} @@ -5338,6 +5667,9 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + layout-base@1.0.2: resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} @@ -5442,6 +5774,10 @@ packages: lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + logform@2.7.0: + resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} + engines: {node: '>= 12.0.0'} + long@5.3.2: resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} @@ -5451,6 +5787,10 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@9.1.2: + resolution: {integrity: sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==} + engines: {node: 14 || >=16.14} + lucide-react@1.14.0: resolution: {integrity: sha512-+1mdWcfSJVUsaTIjN9zoezmUhfXo5l0vP7ekBMPo3jcS/aIkxHnXqAPsByszMZx/Y8oQBRJxJx5xg+RH3urzxA==} peerDependencies: @@ -5569,6 +5909,9 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} + memium@0.4.5: + resolution: {integrity: sha512-mnmHWWlcyKnkKSSU0qL1Jsg+CsoikVZ2xsOatAN89R+j4V3m3WlQr0nUoqcjKDyUVF5ZGdNinRiZGVd+7A+TJQ==} + merge-descriptors@2.0.0: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} @@ -5795,6 +6138,9 @@ packages: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + node-releases@2.0.38: resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} @@ -5831,6 +6177,9 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -5941,6 +6290,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-expression-matcher@1.5.0: + resolution: {integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==} + engines: {node: '>=14.0.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -6107,6 +6460,10 @@ packages: process-warning@5.0.0: resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -6130,6 +6487,14 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} + q@1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + deprecated: |- + You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. + + (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) + qs@6.15.1: resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} engines: {node: '>=0.6'} @@ -6227,6 +6592,14 @@ packages: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -6467,6 +6840,9 @@ packages: resolution: {integrity: sha512-SO/3iYL5S3W57LLEniscOGPZgOqZUPCx6d3dB+52B80yJ0XstzsC/eV8gnA4tM3MHDrKz+OCFSLNjswdSC+/bA==} engines: {node: '>=22'} + snappyjs@0.7.0: + resolution: {integrity: sha512-u5iEEXkMe2EInQio6Wv9LWHOQYRDbD2O9hzS27GpT/lwfIQhTCnHCTqedqHIHe9ZcvQo+9au6vngQayipz1NYw==} + sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} @@ -6504,6 +6880,9 @@ packages: engines: {node: '>=20.16.0'} hasBin: true + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -6529,6 +6908,9 @@ packages: resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} engines: {node: '>=20'} + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} @@ -6544,6 +6926,9 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strnum@2.4.0: + resolution: {integrity: sha512-sHrVyWWdq28RbhjuJdZsA1SnGRJV6NiXbk6AXBxDOsgAcA+lmpUZCYjOdLBxkXMwis6RRe7dlZt4VlIWFVzkmg==} + style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} @@ -6608,6 +6993,9 @@ packages: resolution: {integrity: sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ==} engines: {node: '>=18'} + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -6619,6 +7007,10 @@ packages: resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==} engines: {node: '>=20'} + thrift@0.23.0: + resolution: {integrity: sha512-j7F1ls8JogClU88Ta/pwD/OzYuiFeD6Z5GoWw7ip+jcDhcNYFKgfXYEsyLXYpiNfJO94fbLnITGxyfZ19YzA6Q==} + engines: {node: '>= 10.18.0'} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -6656,6 +7048,10 @@ packages: trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} @@ -6719,6 +7115,9 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + undici@6.25.0: resolution: {integrity: sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==} engines: {node: '>=18.17'} @@ -6808,10 +7207,22 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + utilium@3.4.0: + resolution: {integrity: sha512-z4PavqKX0P4XB9SKtXRWWBCDyoDxV6vmTEH4WDBN1/lrYf6MDVk+gntCY6YbryOKjBHGIsYHOS6BTzHf5cF9vQ==} + engines: {node: '>=22.0.0'} + hasBin: true + + uuid@13.0.2: + resolution: {integrity: sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==} + hasBin: true + uuid@14.0.0: resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} hasBin: true + varint@6.0.0: + resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -6954,6 +7365,14 @@ packages: resolution: {integrity: sha512-U89AsyEeAsyoF0zVJBkG9zBgekjgjK7yk9sje3F4IQpXBJ10TF6ByLlIfjMhcmHMJgHZI4KHt4rdNfktzxIAMA==} engines: {node: '>=20'} + winston-transport@4.9.0: + resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==} + engines: {node: '>= 12.0.0'} + + winston@3.19.0: + resolution: {integrity: sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==} + engines: {node: '>= 12.0.0'} + workerd@1.20260430.1: resolution: {integrity: sha512-KEgIWyiw3Jmn+DCd/L3ePo5fmiiYb/UcwKvDWPf/nLLOiwShDFzDSsegU5NY/JcwgvO/QsLHVi2FYrbkcXNY5Q==} engines: {node: '>=16'} @@ -6980,6 +7399,17 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@5.2.5: + resolution: {integrity: sha512-G0gACQIjFmv7NqpaOAXEpe9nEtRYD6ZCy2Ip/B4EzR08qEwnf/wYJoQd3cjosPdnJsHkeh0j2tzOaIBZIOC1yA==} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -7016,6 +7446,10 @@ packages: utf-8-validate: optional: true + xml-naming@0.1.0: + resolution: {integrity: sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==} + engines: {node: '>=16.0.0'} + xmlbuilder2@4.0.3: resolution: {integrity: sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA==} engines: {node: '>=20.0'} @@ -7024,6 +7458,9 @@ packages: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} + xxhash-wasm@1.1.0: + resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -7139,6 +7576,236 @@ snapshots: '@anush008/tokenizers-linux-x64-gnu': 0.0.0 '@anush008/tokenizers-win32-x64-msvc': 0.0.0 + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.13 + tslib: 2.8.1 + + '@aws-crypto/crc32c@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.13 + tslib: 2.8.1 + + '@aws-crypto/sha1-browser@5.2.0': + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.13 + '@aws-sdk/util-locate-window': 3.965.8 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.13 + '@aws-sdk/util-locate-window': 3.965.8 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.13 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.973.13 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/checksums@3.1000.6': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.974.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/client-s3@3.1069.0': + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.974.21 + '@aws-sdk/credential-provider-node': 3.972.56 + '@aws-sdk/middleware-flexible-checksums': 3.974.31 + '@aws-sdk/middleware-sdk-s3': 3.972.52 + '@aws-sdk/signature-v4-multi-region': 3.996.35 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/fetch-http-handler': 5.5.0 + '@smithy/node-http-handler': 4.8.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/core@3.974.21': + dependencies: + '@aws-sdk/types': 3.973.13 + '@aws-sdk/xml-builder': 3.972.30 + '@aws/lambda-invoke-store': 0.2.4 + '@smithy/core': 3.25.0 + '@smithy/signature-v4': 5.5.0 + '@smithy/types': 4.15.0 + bowser: 2.14.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.47': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.49': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/fetch-http-handler': 5.5.0 + '@smithy/node-http-handler': 4.8.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.972.54': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/credential-provider-env': 3.972.47 + '@aws-sdk/credential-provider-http': 3.972.49 + '@aws-sdk/credential-provider-login': 3.972.53 + '@aws-sdk/credential-provider-process': 3.972.47 + '@aws-sdk/credential-provider-sso': 3.972.53 + '@aws-sdk/credential-provider-web-identity': 3.972.53 + '@aws-sdk/nested-clients': 3.997.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/credential-provider-imds': 4.4.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-login@3.972.53': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/nested-clients': 3.997.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-node@3.972.56': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.47 + '@aws-sdk/credential-provider-http': 3.972.49 + '@aws-sdk/credential-provider-ini': 3.972.54 + '@aws-sdk/credential-provider-process': 3.972.47 + '@aws-sdk/credential-provider-sso': 3.972.53 + '@aws-sdk/credential-provider-web-identity': 3.972.53 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/credential-provider-imds': 4.4.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-process@3.972.47': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.972.53': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/nested-clients': 3.997.21 + '@aws-sdk/token-providers': 3.1069.0 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-web-identity@3.972.53': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/nested-clients': 3.997.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-flexible-checksums@3.974.31': + dependencies: + '@aws-sdk/checksums': 3.1000.6 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.972.52': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/signature-v4-multi-region': 3.996.35 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.997.21': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.974.21 + '@aws-sdk/signature-v4-multi-region': 3.996.35 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/fetch-http-handler': 5.5.0 + '@smithy/node-http-handler': 4.8.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.996.35': + dependencies: + '@aws-sdk/types': 3.973.13 + '@smithy/signature-v4': 5.5.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1069.0': + dependencies: + '@aws-sdk/core': 3.974.21 + '@aws-sdk/nested-clients': 3.997.21 + '@aws-sdk/types': 3.973.13 + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/types@3.973.13': + dependencies: + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.8': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.30': + dependencies: + '@smithy/types': 4.15.0 + fast-xml-parser: 5.7.3 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.2.4': {} + '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -7532,10 +8199,18 @@ snapshots: '@colors/colors@1.5.0': optional: true + '@colors/colors@1.6.0': {} + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 + '@dabh/diagnostics@2.0.8': + dependencies: + '@so-ric/colorspace': 1.1.6 + enabled: 2.0.0 + kuler: 2.0.0 + '@emnapi/core@1.10.0': dependencies: '@emnapi/wasi-threads': 1.2.1 @@ -7836,7 +8511,7 @@ snapshots: dependencies: lodash.camelcase: 4.3.0 long: 5.3.2 - protobufjs: 7.5.6 + protobufjs: 7.6.1 yargs: 17.7.2 '@hono/node-server@1.19.14(hono@4.12.23)': @@ -8161,6 +8836,8 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true + '@nodable/entities@2.2.0': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -8666,11 +9343,15 @@ snapshots: '@oxc-project/types@0.127.0': {} + '@petamoriken/float16@3.9.3': {} + '@phosphor-icons/react@2.1.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: react: 19.2.7 react-dom: 19.2.7(react@19.2.7) + '@pinecone-database/pinecone@5.1.2': {} + '@pinojs/redact@0.4.0': {} '@poppinss/colors@4.1.6': @@ -9500,6 +10181,25 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.60.2': optional: true + '@shanghaikid/parquetjs@1.8.8': + dependencies: + '@aws-sdk/client-s3': 3.1069.0 + '@types/node-int64': 0.4.32 + '@types/thrift': 0.10.17 + '@zenfs/core': 2.5.6 + brotli-wasm: 3.0.1 + bson: 6.10.4 + int53: 1.0.0 + long: 5.3.2 + node-int64: 0.4.0 + snappyjs: 0.7.0 + thrift: 0.23.0 + varint: 6.0.0 + xxhash-wasm: 1.1.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@shikijs/core@4.0.2': dependencies: '@shikijs/primitive': 4.0.2 @@ -9544,6 +10244,59 @@ snapshots: '@sindresorhus/is@7.2.0': {} + '@smithy/core@3.25.0': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.4.0': + dependencies: + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.5.0': + dependencies: + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/node-http-handler@4.8.0': + dependencies: + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@smithy/signature-v4@5.5.0': + dependencies: + '@smithy/core': 3.25.0 + '@smithy/types': 4.15.0 + tslib: 2.8.1 + + '@smithy/types@4.15.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@so-ric/colorspace@1.1.6': + dependencies: + color: 5.0.3 + text-hex: 1.0.0 + '@speed-highlight/core@1.2.15': {} '@stablelib/base64@1.0.1': {} @@ -10012,18 +10765,28 @@ snapshots: '@types/ms@2.1.0': {} + '@types/node-int64@0.4.32': + dependencies: + '@types/node': 24.12.2 + '@types/node@12.20.55': {} '@types/node@24.12.2': dependencies: undici-types: 7.16.0 + '@types/node@25.9.3': + dependencies: + undici-types: 7.24.6 + '@types/pg@8.20.0': dependencies: '@types/node': 24.12.2 pg-protocol: 1.13.0 pg-types: 2.2.0 + '@types/q@1.5.8': {} + '@types/react-dom@19.2.3(@types/react@19.2.14)': dependencies: '@types/react': 19.2.14 @@ -10034,6 +10797,14 @@ snapshots: '@types/retry@0.12.0': {} + '@types/thrift@0.10.17': + dependencies: + '@types/node': 24.12.2 + '@types/node-int64': 0.4.32 + '@types/q': 1.5.8 + + '@types/triple-beam@1.3.5': {} + '@types/trusted-types@2.0.7': optional: true @@ -10122,6 +10893,9 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 + '@xterm/xterm@5.5.0': + optional: true + '@xyflow/react@12.11.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@xyflow/system': 0.0.77 @@ -10147,6 +10921,36 @@ snapshots: d3-selection: 3.0.0 d3-zoom: 3.0.0 + '@zenfs/core@2.5.6': + dependencies: + '@types/node': 25.9.3 + buffer: 6.0.3 + eventemitter3: 5.0.4 + kerium: 1.3.9 + memium: 0.4.5 + readable-stream: 4.7.0 + utilium: 3.4.0 + + '@zilliz/milvus2-sdk-node@2.6.17': + dependencies: + '@grpc/grpc-js': 1.14.3 + '@grpc/proto-loader': 0.8.0 + '@opentelemetry/api': 1.9.1 + '@petamoriken/float16': 3.9.3 + '@shanghaikid/parquetjs': 1.8.8 + dayjs: 1.11.20 + generic-pool: 3.9.0 + lru-cache: 9.1.2 + protobufjs: 7.6.1 + winston: 3.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -10212,6 +11016,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.2 + anynum@1.0.0: {} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -10238,6 +11044,10 @@ snapshots: astring@1.9.0: {} + async-limiter@1.0.1: {} + + async@3.2.6: {} + atomic-sleep@1.0.0: {} auto-bind@5.0.1: {} @@ -10285,10 +11095,16 @@ snapshots: boolean@3.2.0: {} + bowser@2.14.1: {} + braces@3.0.3: dependencies: fill-range: 7.1.1 + brotli-wasm@3.0.1: {} + + browser-or-node@1.3.0: {} + browserslist@4.28.2: dependencies: baseline-browser-mapping: 2.10.24 @@ -10297,8 +11113,15 @@ snapshots: node-releases: 2.0.38 update-browserslist-db: 1.2.3(browserslist@4.28.2) + bson@6.10.4: {} + buffer-equal-constant-time@1.0.1: {} + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + bundle-require@5.1.0(esbuild@0.27.7): dependencies: esbuild: 0.27.7 @@ -10493,10 +11316,25 @@ snapshots: dependencies: color-name: 1.1.4 + color-convert@3.1.3: + dependencies: + color-name: 2.1.0 + color-name@1.1.3: {} color-name@1.1.4: {} + color-name@2.1.0: {} + + color-string@2.1.4: + dependencies: + color-name: 2.1.0 + + color@5.0.3: + dependencies: + color-convert: 3.1.3 + color-string: 2.1.4 + comma-separated-tokens@2.0.3: {} commander@4.1.1: {} @@ -10838,6 +11676,8 @@ snapshots: emojilib@2.4.0: {} + enabled@2.0.0: {} + encodeurl@2.0.0: {} encoding-sniffer@0.2.1: @@ -11033,6 +11873,12 @@ snapshots: etag@1.8.1: {} + event-target-shim@5.0.1: {} + + eventemitter3@5.0.4: {} + + events@3.3.0: {} + eventsource-parser@3.0.8: {} eventsource@3.0.7: @@ -11099,6 +11945,18 @@ snapshots: fast-uri@3.1.0: {} + fast-xml-builder@1.2.0: + dependencies: + path-expression-matcher: 1.5.0 + xml-naming: 0.1.0 + + fast-xml-parser@5.7.3: + dependencies: + '@nodable/entities': 2.2.0 + fast-xml-builder: 1.2.0 + path-expression-matcher: 1.5.0 + strnum: 2.4.0 + fastembed@2.1.0: dependencies: '@anush008/tokenizers': 0.0.0 @@ -11115,6 +11973,8 @@ snapshots: optionalDependencies: picomatch: 4.0.4 + fecha@4.2.3: {} + fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 @@ -11148,6 +12008,8 @@ snapshots: flatbuffers@25.9.23: {} + fn.name@1.1.0: {} + formdata-polyfill@4.0.10: dependencies: fetch-blob: 3.2.0 @@ -11299,6 +12161,8 @@ snapshots: transitivePeerDependencies: - supports-color + generic-pool@3.9.0: {} + gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} @@ -11564,6 +12428,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + ieee754@1.2.1: {} + ignore@5.3.2: {} import-in-the-middle@3.0.1: @@ -11623,6 +12489,8 @@ snapshots: inline-style-parser@0.2.7: {} + int53@1.0.0: {} + internmap@1.0.1: {} internmap@2.0.3: {} @@ -11666,6 +12534,8 @@ snapshots: is-promise@4.0.0: {} + is-stream@2.0.1: {} + is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 @@ -11676,6 +12546,10 @@ snapshots: isexe@2.0.0: {} + isomorphic-ws@4.0.1(ws@5.2.5): + dependencies: + ws: 5.2.5 + istanbul-lib-coverage@3.2.2: {} istanbul-lib-report@3.0.1: @@ -11746,10 +12620,16 @@ snapshots: dependencies: commander: 8.3.0 + kerium@1.3.9: + dependencies: + utilium: 3.4.0 + khroma@2.1.0: {} kleur@4.1.5: {} + kuler@2.0.0: {} + layout-base@1.0.2: {} layout-base@2.0.1: {} @@ -11819,6 +12699,15 @@ snapshots: lodash.startcase@4.4.0: {} + logform@2.7.0: + dependencies: + '@colors/colors': 1.6.0 + '@types/triple-beam': 1.3.5 + fecha: 4.2.3 + ms: 2.1.3 + safe-stable-stringify: 2.5.0 + triple-beam: 1.4.1 + long@5.3.2: {} longest-streak@3.1.0: {} @@ -11827,6 +12716,8 @@ snapshots: dependencies: yallist: 3.1.1 + lru-cache@9.1.2: {} + lucide-react@1.14.0(react@19.2.5): dependencies: react: 19.2.5 @@ -12051,6 +12942,11 @@ snapshots: media-typer@1.1.0: {} + memium@0.4.5: + dependencies: + kerium: 1.3.9 + utilium: 3.4.0 + merge-descriptors@2.0.0: {} merge2@1.4.1: {} @@ -12444,6 +13340,8 @@ snapshots: fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 + node-int64@0.4.0: {} + node-releases@2.0.38: {} normalize-path@3.0.0: {} @@ -12470,6 +13368,10 @@ snapshots: dependencies: wrappy: 1.0.2 + one-time@1.0.0: + dependencies: + fn.name: 1.1.0 + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -12582,6 +13484,8 @@ snapshots: path-exists@4.0.0: {} + path-expression-matcher@1.5.0: {} + path-key@3.1.1: {} path-to-regexp@6.3.0: {} @@ -12724,6 +13628,8 @@ snapshots: process-warning@5.0.0: {} + process@0.11.10: {} + progress@2.0.3: {} property-information@7.1.0: {} @@ -12778,6 +13684,8 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 + q@1.5.1: {} + qs@6.15.1: dependencies: side-channel: 1.1.0 @@ -12904,6 +13812,20 @@ snapshots: pify: 4.0.1 strip-bom: 3.0.0 + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + readdirp@3.6.0: dependencies: picomatch: 2.3.2 @@ -13283,6 +14205,8 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 + snappyjs@0.7.0: {} + sonic-boom@4.2.1: dependencies: atomic-sleep: 1.0.0 @@ -13308,6 +14232,8 @@ snapshots: srvx@0.11.16: {} + stack-trace@0.0.10: {} + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 @@ -13334,6 +14260,10 @@ snapshots: get-east-asian-width: 1.5.0 strip-ansi: 7.2.0 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 @@ -13349,6 +14279,10 @@ snapshots: strip-bom@3.0.0: {} + strnum@2.4.0: + dependencies: + anynum: 1.0.0 + style-to-js@1.1.21: dependencies: style-to-object: 1.0.14 @@ -13415,6 +14349,8 @@ snapshots: terminal-size@4.0.1: {} + text-hex@1.0.0: {} + thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -13427,6 +14363,18 @@ snapshots: dependencies: real-require: 1.0.0 + thrift@0.23.0: + dependencies: + browser-or-node: 1.3.0 + isomorphic-ws: 4.0.1(ws@5.2.5) + node-int64: 0.4.0 + q: 1.5.1 + uuid: 13.0.2 + ws: 5.2.5 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -13455,6 +14403,8 @@ snapshots: trim-lines@3.0.1: {} + triple-beam@1.4.1: {} + trough@2.2.0: {} ts-algebra@2.0.0: {} @@ -13518,6 +14468,8 @@ snapshots: undici-types@7.16.0: {} + undici-types@7.24.6: {} + undici@6.25.0: {} undici@7.24.8: {} @@ -13628,8 +14580,18 @@ snapshots: util-deprecate@1.0.2: {} + utilium@3.4.0: + dependencies: + eventemitter3: 5.0.4 + optionalDependencies: + '@xterm/xterm': 5.5.0 + + uuid@13.0.2: {} + uuid@14.0.0: {} + varint@6.0.0: {} + vary@1.1.2: {} vfile-location@5.0.3: @@ -13768,6 +14730,26 @@ snapshots: dependencies: string-width: 8.2.1 + winston-transport@4.9.0: + dependencies: + logform: 2.7.0 + readable-stream: 3.6.2 + triple-beam: 1.4.1 + + winston@3.19.0: + dependencies: + '@colors/colors': 1.6.0 + '@dabh/diagnostics': 2.0.8 + async: 3.2.6 + is-stream: 2.0.1 + logform: 2.7.0 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.5.0 + stack-trace: 0.0.10 + triple-beam: 1.4.1 + winston-transport: 4.9.0 + workerd@1.20260430.1: optionalDependencies: '@cloudflare/workerd-darwin-64': 1.20260430.1 @@ -13806,12 +14788,18 @@ snapshots: wrappy@1.0.2: {} + ws@5.2.5: + dependencies: + async-limiter: 1.0.1 + ws@8.18.0: {} ws@8.20.0: {} ws@8.21.0: {} + xml-naming@0.1.0: {} + xmlbuilder2@4.0.3: dependencies: '@oozcitak/dom': 2.0.2 @@ -13821,6 +14809,8 @@ snapshots: xtend@4.0.2: {} + xxhash-wasm@1.1.0: {} + y18n@5.0.8: {} yallist@3.1.1: {} From 41fdea380cbad6bfd0bac815a0d95de72c2c7ea2 Mon Sep 17 00:00:00 2001 From: Indra Zulfi Date: Tue, 16 Jun 2026 15:36:43 +0700 Subject: [PATCH 2/3] refactor: split vector-stores into flat file modules Split each monolithic src/index.ts (220-364 lines) into focused modules: - types.ts (client interfaces, config types) - store.ts (*VectorStore class) - search-index.ts (*VectorIndex class) - filters.ts (VectorFilter translation) - helpers.ts (serialization, parsing, ID generation) - index.ts (barrel re-exports) Applied to all 5 packages: chroma, qdrant, pgvector, pinecone, milvus. Public API surface unchanged -- all exports preserved via barrel. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- packages/vector-stores/chroma/src/filters.ts | 20 + packages/vector-stores/chroma/src/helpers.ts | 103 +++++ packages/vector-stores/chroma/src/index.ts | 228 +---------- .../vector-stores/chroma/src/search-index.ts | 40 ++ packages/vector-stores/chroma/src/store.ts | 43 ++ packages/vector-stores/chroma/src/types.ts | 18 + packages/vector-stores/milvus/src/filters.ts | 44 +++ packages/vector-stores/milvus/src/helpers.ts | 152 +++++++ packages/vector-stores/milvus/src/index.ts | 300 +------------- .../vector-stores/milvus/src/search-index.ts | 43 ++ packages/vector-stores/milvus/src/store.ts | 41 ++ packages/vector-stores/milvus/src/types.ts | 22 ++ .../vector-stores/pgvector/src/filters.ts | 79 ++++ .../vector-stores/pgvector/src/helpers.ts | 145 +++++++ packages/vector-stores/pgvector/src/index.ts | 373 +----------------- .../pgvector/src/search-index.ts | 63 +++ packages/vector-stores/pgvector/src/store.ts | 76 ++++ packages/vector-stores/pgvector/src/types.ts | 21 + .../vector-stores/pinecone/src/filters.ts | 20 + .../vector-stores/pinecone/src/helpers.ts | 140 +++++++ packages/vector-stores/pinecone/src/index.ts | 258 +----------- .../pinecone/src/search-index.ts | 40 ++ packages/vector-stores/pinecone/src/store.ts | 30 ++ packages/vector-stores/pinecone/src/types.ts | 28 ++ packages/vector-stores/qdrant/src/filters.ts | 20 + packages/vector-stores/qdrant/src/helpers.ts | 154 ++++++++ packages/vector-stores/qdrant/src/index.ts | 276 +------------ .../vector-stores/qdrant/src/search-index.ts | 41 ++ packages/vector-stores/qdrant/src/store.ts | 42 ++ packages/vector-stores/qdrant/src/types.ts | 20 + 30 files changed, 1488 insertions(+), 1392 deletions(-) create mode 100644 packages/vector-stores/chroma/src/filters.ts create mode 100644 packages/vector-stores/chroma/src/helpers.ts create mode 100644 packages/vector-stores/chroma/src/search-index.ts create mode 100644 packages/vector-stores/chroma/src/store.ts create mode 100644 packages/vector-stores/chroma/src/types.ts create mode 100644 packages/vector-stores/milvus/src/filters.ts create mode 100644 packages/vector-stores/milvus/src/helpers.ts create mode 100644 packages/vector-stores/milvus/src/search-index.ts create mode 100644 packages/vector-stores/milvus/src/store.ts create mode 100644 packages/vector-stores/milvus/src/types.ts create mode 100644 packages/vector-stores/pgvector/src/filters.ts create mode 100644 packages/vector-stores/pgvector/src/helpers.ts create mode 100644 packages/vector-stores/pgvector/src/search-index.ts create mode 100644 packages/vector-stores/pgvector/src/store.ts create mode 100644 packages/vector-stores/pgvector/src/types.ts create mode 100644 packages/vector-stores/pinecone/src/filters.ts create mode 100644 packages/vector-stores/pinecone/src/helpers.ts create mode 100644 packages/vector-stores/pinecone/src/search-index.ts create mode 100644 packages/vector-stores/pinecone/src/store.ts create mode 100644 packages/vector-stores/pinecone/src/types.ts create mode 100644 packages/vector-stores/qdrant/src/filters.ts create mode 100644 packages/vector-stores/qdrant/src/helpers.ts create mode 100644 packages/vector-stores/qdrant/src/search-index.ts create mode 100644 packages/vector-stores/qdrant/src/store.ts create mode 100644 packages/vector-stores/qdrant/src/types.ts diff --git a/packages/vector-stores/chroma/src/filters.ts b/packages/vector-stores/chroma/src/filters.ts new file mode 100644 index 00000000..94b6c059 --- /dev/null +++ b/packages/vector-stores/chroma/src/filters.ts @@ -0,0 +1,20 @@ +import type { VectorFilter } from "@anvia/core/vector-store"; + +export function filterToChromaWhere(filter: VectorFilter | undefined): unknown { + if (filter === undefined) { + return undefined; + } + + switch (filter.type) { + case "eq": + return { [filter.key]: { $eq: filter.value } }; + case "gt": + return { [filter.key]: { $gt: filter.value } }; + case "lt": + return { [filter.key]: { $lt: filter.value } }; + case "and": + return { $and: filter.filters.map(filterToChromaWhere) }; + case "or": + return { $or: filter.filters.map(filterToChromaWhere) }; + } +} diff --git a/packages/vector-stores/chroma/src/helpers.ts b/packages/vector-stores/chroma/src/helpers.ts new file mode 100644 index 00000000..9b77c727 --- /dev/null +++ b/packages/vector-stores/chroma/src/helpers.ts @@ -0,0 +1,103 @@ +import type { EmbeddedDocument, VectorMetadata } from "@anvia/core/embeddings"; +import type { VectorSearchResult } from "@anvia/core/vector-store"; +import type { ChromaClientLike, ChromaCollectionLike } from "./types.js"; + +export function serializeDocument(document: unknown): string { + return typeof document === "string" ? document : JSON.stringify(document); +} + +export function parseDocument(document: string | null | undefined): T { + if (document === null || document === undefined) { + return "" as T; + } + try { + return JSON.parse(document) as T; + } catch { + return document as T; + } +} + +export function chromaRecords( + document: EmbeddedDocument, +): Array<{ + id: string; + document: string; + metadata: VectorMetadata | undefined; + embedding: number[]; +}> { + if (document.embeddings.length === 0) { + throw new Error(`Document ${document.id} has no embeddings`); + } + + return document.embeddings.map((embedding, index) => ({ + id: document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`, + document: serializeDocument(document.document), + metadata: document.metadata, + embedding: embedding.vector, + })); +} + +export function logicalDocumentId(id: string): string { + return id.replace(/#embedding:\d+$/, ""); +} + +export function distanceToCosineScore(distance: number): number { + return 1 - distance; +} + +export function parseQueryResults( + response: unknown, + threshold: number | undefined, +): Array> { + const raw = response as { + ids?: string[][]; + documents?: Array>; + metadatas?: Array>; + distances?: number[][]; + }; + const ids = raw.ids?.[0] ?? []; + const documents = raw.documents?.[0] ?? []; + const metadatas = raw.metadatas?.[0] ?? []; + const distances = raw.distances?.[0] ?? []; + + const results = ids.flatMap((id, index) => { + const score = distanceToCosineScore(distances[index] ?? 0); + if (threshold !== undefined && score < threshold) { + return []; + } + return [ + { + id: logicalDocumentId(id), + score, + document: parseDocument(documents[index]), + ...(metadatas[index] === null || metadatas[index] === undefined + ? {} + : { metadata: metadatas[index] }), + } as VectorSearchResult, + ]; + }); + + const byId = new Map>(); + for (const result of results) { + if (!byId.has(result.id)) { + byId.set(result.id, result); + } + } + return [...byId.values()]; +} + +export async function defaultChromaClient(): Promise { + const chroma = await import("chromadb"); + return new chroma.ChromaClient() as ChromaClientLike; +} + +export async function getOrCreateCollection( + client: ChromaClientLike, + options: Record, +): Promise { + try { + return await client.getCollection(options); + } catch { + return await client.createCollection(options); + } +} diff --git a/packages/vector-stores/chroma/src/index.ts b/packages/vector-stores/chroma/src/index.ts index fe809e34..753a956d 100644 --- a/packages/vector-stores/chroma/src/index.ts +++ b/packages/vector-stores/chroma/src/index.ts @@ -1,220 +1,8 @@ -import { - type EmbeddedDocument, - type EmbeddingModel, - embedText, - type VectorMetadata, -} from "@anvia/core/embeddings"; -import type { Tool } from "@anvia/core/tool"; -import { - createVectorSearchTool, - type VectorFilter, - type VectorSearchIndex, - type VectorSearchRequest, - type VectorSearchResult, - type VectorSearchToolOptions, -} from "@anvia/core/vector-store"; - -type ChromaClientLike = { - getCollection(options: Record): Promise; - createCollection(options: Record): Promise; - getOrCreateCollection?(options: Record): Promise; -}; - -type ChromaCollectionLike = { - upsert(options: Record): Promise; - query(options: Record): Promise; -}; - -export type ChromaVectorStoreConnectOptions = { - client?: ChromaClientLike | undefined; - collectionName: string; - createIfMissing?: boolean | undefined; - metadata?: Record | undefined; - configuration?: Record | undefined; -}; - -export class ChromaVectorStore { - private constructor(private readonly collection: ChromaCollectionLike) {} - - static async connect( - options: ChromaVectorStoreConnectOptions, - ): Promise> { - const client = options.client ?? (await defaultChromaClient()); - const collectionOptions = { - name: options.collectionName, - metadata: options.metadata ?? { "hnsw:space": "cosine" }, - configuration: options.configuration, - embeddingFunction: null, - }; - const collection = - options.createIfMissing === false - ? await client.getCollection(collectionOptions) - : client.getOrCreateCollection !== undefined - ? await client.getOrCreateCollection(collectionOptions) - : await getOrCreateCollection(client, collectionOptions); - return new ChromaVectorStore(collection); - } - - async upsertDocuments(documents: Array>): Promise { - const records = documents.flatMap((document) => chromaRecords(document)); - await this.collection.upsert({ - ids: records.map((record) => record.id), - documents: records.map((record) => record.document), - embeddings: records.map((record) => record.embedding), - ...(records.some((record) => record.metadata !== undefined) - ? { metadatas: records.map((record) => record.metadata ?? null) } - : {}), - }); - } - - index(model: EmbeddingModel): ChromaVectorIndex { - return new ChromaVectorIndex(model, this.collection); - } -} - -export class ChromaVectorIndex - implements VectorSearchIndex -{ - constructor( - private readonly model: EmbeddingModel, - private readonly collection: ChromaCollectionLike, - ) {} - - async search(request: VectorSearchRequest): Promise>> { - const queryEmbedding = await embedText(this.model, request.query); - const response = await this.collection.query({ - queryEmbeddings: [queryEmbedding.vector], - nResults: request.topK, - where: filterToChromaWhere(request.filter), - include: ["documents", "metadatas", "distances"], - }); - return parseQueryResults(response, request.threshold); - } - - async searchIds(request: VectorSearchRequest): Promise> { - return (await this.search(request)).map(({ score, id }) => ({ score, id })); - } - - asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { - return createVectorSearchTool(this, options); - } -} - -export function filterToChromaWhere(filter: VectorFilter | undefined): unknown { - if (filter === undefined) { - return undefined; - } - - switch (filter.type) { - case "eq": - return { [filter.key]: { $eq: filter.value } }; - case "gt": - return { [filter.key]: { $gt: filter.value } }; - case "lt": - return { [filter.key]: { $lt: filter.value } }; - case "and": - return { $and: filter.filters.map(filterToChromaWhere) }; - case "or": - return { $or: filter.filters.map(filterToChromaWhere) }; - } -} - -async function defaultChromaClient(): Promise { - const chroma = await import("chromadb"); - return new chroma.ChromaClient() as ChromaClientLike; -} - -async function getOrCreateCollection( - client: ChromaClientLike, - options: Record, -): Promise { - try { - return await client.getCollection(options); - } catch { - return await client.createCollection(options); - } -} - -function serializeDocument(document: unknown): string { - return typeof document === "string" ? document : JSON.stringify(document); -} - -function parseQueryResults( - response: unknown, - threshold: number | undefined, -): Array> { - const raw = response as { - ids?: string[][]; - documents?: Array>; - metadatas?: Array>; - distances?: number[][]; - }; - const ids = raw.ids?.[0] ?? []; - const documents = raw.documents?.[0] ?? []; - const metadatas = raw.metadatas?.[0] ?? []; - const distances = raw.distances?.[0] ?? []; - - const results = ids.flatMap((id, index) => { - const score = distanceToCosineScore(distances[index] ?? 0); - if (threshold !== undefined && score < threshold) { - return []; - } - return [ - { - id: logicalDocumentId(id), - score, - document: parseDocument(documents[index]), - ...(metadatas[index] === null || metadatas[index] === undefined - ? {} - : { metadata: metadatas[index] }), - } as VectorSearchResult, - ]; - }); - - const byId = new Map>(); - for (const result of results) { - if (!byId.has(result.id)) { - byId.set(result.id, result); - } - } - return [...byId.values()]; -} - -function distanceToCosineScore(distance: number): number { - return 1 - distance; -} - -function parseDocument(document: string | null | undefined): T { - if (document === null || document === undefined) { - return "" as T; - } - try { - return JSON.parse(document) as T; - } catch { - return document as T; - } -} - -function chromaRecords( - document: EmbeddedDocument, -): Array<{ - id: string; - document: string; - metadata: VectorMetadata | undefined; - embedding: number[]; -}> { - if (document.embeddings.length === 0) { - throw new Error(`Document ${document.id} has no embeddings`); - } - - return document.embeddings.map((embedding, index) => ({ - id: document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`, - document: serializeDocument(document.document), - metadata: document.metadata, - embedding: embedding.vector, - })); -} - -function logicalDocumentId(id: string): string { - return id.replace(/#embedding:\d+$/, ""); -} +export { filterToChromaWhere } from "./filters.js"; +export { ChromaVectorIndex } from "./search-index.js"; +export { ChromaVectorStore } from "./store.js"; +export type { + ChromaClientLike, + ChromaCollectionLike, + ChromaVectorStoreConnectOptions, +} from "./types.js"; diff --git a/packages/vector-stores/chroma/src/search-index.ts b/packages/vector-stores/chroma/src/search-index.ts new file mode 100644 index 00000000..bc7f72f4 --- /dev/null +++ b/packages/vector-stores/chroma/src/search-index.ts @@ -0,0 +1,40 @@ +import { type EmbeddingModel, embedText, type VectorMetadata } from "@anvia/core/embeddings"; +import type { Tool } from "@anvia/core/tool"; +import { + createVectorSearchTool, + type VectorSearchIndex, + type VectorSearchRequest, + type VectorSearchResult, + type VectorSearchToolOptions, +} from "@anvia/core/vector-store"; +import { filterToChromaWhere } from "./filters.js"; +import { parseQueryResults } from "./helpers.js"; +import type { ChromaCollectionLike } from "./types.js"; + +export class ChromaVectorIndex + implements VectorSearchIndex +{ + constructor( + private readonly model: EmbeddingModel, + private readonly collection: ChromaCollectionLike, + ) {} + + async search(request: VectorSearchRequest): Promise>> { + const queryEmbedding = await embedText(this.model, request.query); + const response = await this.collection.query({ + queryEmbeddings: [queryEmbedding.vector], + nResults: request.topK, + where: filterToChromaWhere(request.filter), + include: ["documents", "metadatas", "distances"], + }); + return parseQueryResults(response, request.threshold); + } + + async searchIds(request: VectorSearchRequest): Promise> { + return (await this.search(request)).map(({ score, id }) => ({ score, id })); + } + + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { + return createVectorSearchTool(this, options); + } +} diff --git a/packages/vector-stores/chroma/src/store.ts b/packages/vector-stores/chroma/src/store.ts new file mode 100644 index 00000000..5dc7be5d --- /dev/null +++ b/packages/vector-stores/chroma/src/store.ts @@ -0,0 +1,43 @@ +import type { EmbeddedDocument, EmbeddingModel, VectorMetadata } from "@anvia/core/embeddings"; +import { chromaRecords, defaultChromaClient, getOrCreateCollection } from "./helpers.js"; +import { ChromaVectorIndex } from "./search-index.js"; +import type { ChromaCollectionLike, ChromaVectorStoreConnectOptions } from "./types.js"; + +export class ChromaVectorStore { + private constructor(private readonly collection: ChromaCollectionLike) {} + + static async connect( + options: ChromaVectorStoreConnectOptions, + ): Promise> { + const client = options.client ?? (await defaultChromaClient()); + const collectionOptions = { + name: options.collectionName, + metadata: options.metadata ?? { "hnsw:space": "cosine" }, + configuration: options.configuration, + embeddingFunction: null, + }; + const collection = + options.createIfMissing === false + ? await client.getCollection(collectionOptions) + : client.getOrCreateCollection !== undefined + ? await client.getOrCreateCollection(collectionOptions) + : await getOrCreateCollection(client, collectionOptions); + return new ChromaVectorStore(collection); + } + + async upsertDocuments(documents: Array>): Promise { + const records = documents.flatMap((document) => chromaRecords(document)); + await this.collection.upsert({ + ids: records.map((record) => record.id), + documents: records.map((record) => record.document), + embeddings: records.map((record) => record.embedding), + ...(records.some((record) => record.metadata !== undefined) + ? { metadatas: records.map((record) => record.metadata ?? null) } + : {}), + }); + } + + index(model: EmbeddingModel): ChromaVectorIndex { + return new ChromaVectorIndex(model, this.collection); + } +} diff --git a/packages/vector-stores/chroma/src/types.ts b/packages/vector-stores/chroma/src/types.ts new file mode 100644 index 00000000..8a72b79f --- /dev/null +++ b/packages/vector-stores/chroma/src/types.ts @@ -0,0 +1,18 @@ +export type ChromaClientLike = { + getCollection(options: Record): Promise; + createCollection(options: Record): Promise; + getOrCreateCollection?(options: Record): Promise; +}; + +export type ChromaCollectionLike = { + upsert(options: Record): Promise; + query(options: Record): Promise; +}; + +export type ChromaVectorStoreConnectOptions = { + client?: ChromaClientLike | undefined; + collectionName: string; + createIfMissing?: boolean | undefined; + metadata?: Record | undefined; + configuration?: Record | undefined; +}; diff --git a/packages/vector-stores/milvus/src/filters.ts b/packages/vector-stores/milvus/src/filters.ts new file mode 100644 index 00000000..8f7b80d7 --- /dev/null +++ b/packages/vector-stores/milvus/src/filters.ts @@ -0,0 +1,44 @@ +import type { VectorFilter } from "@anvia/core/vector-store"; + +export function filterToMilvusExpr(filter: VectorFilter | undefined): string | undefined { + if (filter === undefined) { + return undefined; + } + + switch (filter.type) { + case "eq": { + const val = milvusLiteral(filter.value); + return `${filter.key} == ${val}`; + } + case "gt": { + const val = milvusLiteral(filter.value); + return `${filter.key} > ${val}`; + } + case "lt": { + const val = milvusLiteral(filter.value); + return `${filter.key} < ${val}`; + } + case "and": { + const parts = filter.filters + .map(filterToMilvusExpr) + .filter((part): part is string => part !== undefined); + return parts.length > 0 ? parts.map((p) => `(${p})`).join(" && ") : undefined; + } + case "or": { + const parts = filter.filters + .map(filterToMilvusExpr) + .filter((part): part is string => part !== undefined); + return parts.length > 0 ? parts.map((p) => `(${p})`).join(" || ") : undefined; + } + } +} + +function milvusLiteral(value: string | number | boolean | null): string { + if (value === null) { + return "null"; + } + if (typeof value === "string") { + return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`; + } + return String(value); +} diff --git a/packages/vector-stores/milvus/src/helpers.ts b/packages/vector-stores/milvus/src/helpers.ts new file mode 100644 index 00000000..7e00516f --- /dev/null +++ b/packages/vector-stores/milvus/src/helpers.ts @@ -0,0 +1,152 @@ +import { createHash } from "node:crypto"; +import type { EmbeddedDocument, VectorMetadata } from "@anvia/core/embeddings"; +import type { VectorSearchResult } from "@anvia/core/vector-store"; +import { + documentFieldName, + documentIdFieldName, + type MilvusClientLike, + type MilvusMetric, + reservedFieldPrefix, +} from "./types.js"; + +export function pointId(id: string): string { + return createHash("sha256").update(id).digest("hex").slice(0, 32); +} + +export function serializeDocument(document: unknown): string { + return typeof document === "string" ? document : JSON.stringify(document); +} + +export function parseDocument(document: unknown): T { + if (document === null || document === undefined) { + return "" as T; + } + if (typeof document !== "string") { + return document as T; + } + try { + return JSON.parse(document) as T; + } catch { + return document as T; + } +} + +export function assertNoReservedFields(metadata: VectorMetadata | undefined): void { + for (const key of Object.keys(metadata ?? {})) { + if (key.startsWith(reservedFieldPrefix)) { + throw new Error(`Metadata key ${key} is reserved for Anvia Milvus fields`); + } + } +} + +export function milvusRows( + document: EmbeddedDocument, +): Array> { + if (document.embeddings.length === 0) { + throw new Error(`Document ${document.id} has no embeddings`); + } + + assertNoReservedFields(document.metadata); + + return document.embeddings.map((embedding, index) => { + const logicalId = + document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; + return { + id: pointId(logicalId), + [documentIdFieldName]: document.id, + [documentFieldName]: serializeDocument(document.document), + vector: embedding.vector, + ...(document.metadata ?? {}), + }; + }); +} + +export function parseQueryResults( + response: unknown, + threshold: number | undefined, +): Array> { + const raw = response as { + results?: Array< + Array<{ + id: string; + score?: number; + [documentIdFieldName]?: string; + [documentFieldName]?: string; + [key: string]: unknown; + }> + >; + }; + const matches = raw.results?.[0] ?? []; + + const byId = new Map>(); + + for (const match of matches) { + const score = match.score ?? 0; + if (threshold !== undefined && score < threshold) { + continue; + } + + const id = String(match[documentIdFieldName] ?? match.id); + const result = { + id, + score, + document: parseDocument(match[documentFieldName]), + ...metadataFromRow(match), + } as VectorSearchResult; + + const current = byId.get(id); + if (current === undefined || result.score > current.score) { + byId.set(id, result); + } + } + + return [...byId.values()]; +} + +export async function defaultMilvusClient(): Promise { + const { MilvusClient } = await import("@zilliz/milvus2-sdk-node"); + return new MilvusClient({ address: "localhost:19530" }) as unknown as MilvusClientLike; +} + +export async function ensureCollection( + client: MilvusClientLike, + collectionName: string, + vectorSize: number, + metric: MilvusMetric, +): Promise { + const { value: exists } = await client.hasCollection({ collection_name: collectionName }); + if (exists) { + return; + } + + await client.createCollection({ + collection_name: collectionName, + fields: [ + { name: "id", data_type: "VarChar", max_length: 64, is_primary_key: true }, + { name: documentIdFieldName, data_type: "VarChar", max_length: 4096 }, + { name: documentFieldName, data_type: "VarChar", max_length: 65535 }, + { name: "vector", data_type: "FloatVector", dim: vectorSize }, + ], + metric_type: metric, + }); + + await client.createIndex({ + collection_name: collectionName, + field_name: "vector", + index_type: "HNSW", + metric_type: metric, + params: { M: 16, efConstruction: 256 }, + }); +} + +function metadataFromRow( + row: Record, +): { metadata?: Metadata | undefined } { + const skipKeys = new Set(["id", "score", documentIdFieldName, documentFieldName, "vector"]); + const metadata = Object.fromEntries( + Object.entries(row).filter( + ([key]) => !skipKeys.has(key) && !key.startsWith(reservedFieldPrefix), + ), + ) as Metadata; + return Object.keys(metadata).length === 0 ? {} : { metadata }; +} diff --git a/packages/vector-stores/milvus/src/index.ts b/packages/vector-stores/milvus/src/index.ts index e2e4e31a..1b1b531f 100644 --- a/packages/vector-stores/milvus/src/index.ts +++ b/packages/vector-stores/milvus/src/index.ts @@ -1,292 +1,8 @@ -import { createHash } from "node:crypto"; -import { - type EmbeddedDocument, - type EmbeddingModel, - embedText, - type VectorMetadata, -} from "@anvia/core/embeddings"; -import type { Tool } from "@anvia/core/tool"; -import { - createVectorSearchTool, - type VectorFilter, - type VectorSearchIndex, - type VectorSearchRequest, - type VectorSearchResult, - type VectorSearchToolOptions, -} from "@anvia/core/vector-store"; - -const documentIdFieldName = "__anvia_document_id"; -const documentFieldName = "__anvia_document"; -const reservedFieldPrefix = "__anvia_"; - -export type MilvusMetric = "COSINE" | "L2" | "IP"; - -type MilvusClientLike = { - hasCollection(options: { collection_name: string }): Promise<{ value: boolean }>; - createCollection(options: Record): Promise; - createIndex(options: Record): Promise; - loadCollection(options: { collection_name: string }): Promise; - insert(options: Record): Promise; - search(options: Record): Promise; -}; - -export type MilvusVectorStoreConnectOptions = { - client?: MilvusClientLike | undefined; - collectionName: string; - vectorSize: number; - createIfMissing?: boolean | undefined; - metric?: MilvusMetric | undefined; -}; - -export class MilvusVectorStore { - private constructor( - private readonly client: MilvusClientLike, - private readonly collectionName: string, - ) {} - - static async connect( - options: MilvusVectorStoreConnectOptions, - ): Promise> { - const client = options.client ?? (await defaultMilvusClient()); - - if (options.createIfMissing !== false) { - await ensureCollection( - client, - options.collectionName, - options.vectorSize, - options.metric ?? "COSINE", - ); - } - - await client.loadCollection({ collection_name: options.collectionName }); - return new MilvusVectorStore(client, options.collectionName); - } - - async upsertDocuments(documents: Array>): Promise { - const rows = documents.flatMap((document) => milvusRows(document)); - await this.client.insert({ - collection_name: this.collectionName, - data: rows, - }); - } - - index(model: EmbeddingModel): MilvusVectorIndex { - return new MilvusVectorIndex(model, this.client, this.collectionName); - } -} - -export class MilvusVectorIndex - implements VectorSearchIndex -{ - constructor( - private readonly model: EmbeddingModel, - private readonly client: MilvusClientLike, - private readonly collectionName: string, - ) {} - - async search(request: VectorSearchRequest): Promise>> { - const queryEmbedding = await embedText(this.model, request.query); - const filterExpr = filterToMilvusExpr(request.filter); - const response = await this.client.search({ - collection_name: this.collectionName, - vector: [queryEmbedding.vector], - limit: request.topK, - ...(filterExpr !== undefined ? { filter: filterExpr } : {}), - output_fields: [documentIdFieldName, documentFieldName, "*"], - }); - return parseQueryResults(response, request.threshold); - } - - async searchIds(request: VectorSearchRequest): Promise> { - return (await this.search(request)).map(({ score, id }) => ({ score, id })); - } - - asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { - return createVectorSearchTool(this, options); - } -} - -export function filterToMilvusExpr(filter: VectorFilter | undefined): string | undefined { - if (filter === undefined) { - return undefined; - } - - switch (filter.type) { - case "eq": { - const val = milvusLiteral(filter.value); - return `${filter.key} == ${val}`; - } - case "gt": { - const val = milvusLiteral(filter.value); - return `${filter.key} > ${val}`; - } - case "lt": { - const val = milvusLiteral(filter.value); - return `${filter.key} < ${val}`; - } - case "and": { - const parts = filter.filters - .map(filterToMilvusExpr) - .filter((part): part is string => part !== undefined); - return parts.length > 0 ? parts.map((p) => `(${p})`).join(" && ") : undefined; - } - case "or": { - const parts = filter.filters - .map(filterToMilvusExpr) - .filter((part): part is string => part !== undefined); - return parts.length > 0 ? parts.map((p) => `(${p})`).join(" || ") : undefined; - } - } -} - -function milvusLiteral(value: string | number | boolean | null): string { - if (value === null) { - return "null"; - } - if (typeof value === "string") { - return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`; - } - return String(value); -} - -async function defaultMilvusClient(): Promise { - const { MilvusClient } = await import("@zilliz/milvus2-sdk-node"); - return new MilvusClient({ address: "localhost:19530" }) as unknown as MilvusClientLike; -} - -async function ensureCollection( - client: MilvusClientLike, - collectionName: string, - vectorSize: number, - metric: MilvusMetric, -): Promise { - const { value: exists } = await client.hasCollection({ collection_name: collectionName }); - if (exists) { - return; - } - - await client.createCollection({ - collection_name: collectionName, - fields: [ - { name: "id", data_type: "VarChar", max_length: 64, is_primary_key: true }, - { name: documentIdFieldName, data_type: "VarChar", max_length: 4096 }, - { name: documentFieldName, data_type: "VarChar", max_length: 65535 }, - { name: "vector", data_type: "FloatVector", dim: vectorSize }, - ], - metric_type: metric, - }); - - await client.createIndex({ - collection_name: collectionName, - field_name: "vector", - index_type: "HNSW", - metric_type: metric, - params: { M: 16, efConstruction: 256 }, - }); -} - -function milvusRows( - document: EmbeddedDocument, -): Array> { - if (document.embeddings.length === 0) { - throw new Error(`Document ${document.id} has no embeddings`); - } - - assertNoReservedFields(document.metadata); - - return document.embeddings.map((embedding, index) => { - const logicalId = - document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; - return { - id: pointId(logicalId), - [documentIdFieldName]: document.id, - [documentFieldName]: serializeDocument(document.document), - vector: embedding.vector, - ...(document.metadata ?? {}), - }; - }); -} - -function assertNoReservedFields(metadata: VectorMetadata | undefined): void { - for (const key of Object.keys(metadata ?? {})) { - if (key.startsWith(reservedFieldPrefix)) { - throw new Error(`Metadata key ${key} is reserved for Anvia Milvus fields`); - } - } -} - -function pointId(id: string): string { - return createHash("sha256").update(id).digest("hex").slice(0, 32); -} - -function serializeDocument(document: unknown): string { - return typeof document === "string" ? document : JSON.stringify(document); -} - -function parseQueryResults( - response: unknown, - threshold: number | undefined, -): Array> { - const raw = response as { - results?: Array< - Array<{ - id: string; - score?: number; - [documentIdFieldName]?: string; - [documentFieldName]?: string; - [key: string]: unknown; - }> - >; - }; - const matches = raw.results?.[0] ?? []; - - const byId = new Map>(); - - for (const match of matches) { - const score = match.score ?? 0; - if (threshold !== undefined && score < threshold) { - continue; - } - - const id = String(match[documentIdFieldName] ?? match.id); - const result = { - id, - score, - document: parseDocument(match[documentFieldName]), - ...metadataFromRow(match), - } as VectorSearchResult; - - const current = byId.get(id); - if (current === undefined || result.score > current.score) { - byId.set(id, result); - } - } - - return [...byId.values()]; -} - -function parseDocument(document: unknown): T { - if (document === null || document === undefined) { - return "" as T; - } - if (typeof document !== "string") { - return document as T; - } - try { - return JSON.parse(document) as T; - } catch { - return document as T; - } -} - -function metadataFromRow( - row: Record, -): { metadata?: Metadata | undefined } { - const skipKeys = new Set(["id", "score", documentIdFieldName, documentFieldName, "vector"]); - const metadata = Object.fromEntries( - Object.entries(row).filter( - ([key]) => !skipKeys.has(key) && !key.startsWith(reservedFieldPrefix), - ), - ) as Metadata; - return Object.keys(metadata).length === 0 ? {} : { metadata }; -} +export { filterToMilvusExpr } from "./filters.js"; +export { MilvusVectorIndex } from "./search-index.js"; +export { MilvusVectorStore } from "./store.js"; +export type { + MilvusClientLike, + MilvusMetric, + MilvusVectorStoreConnectOptions, +} from "./types.js"; diff --git a/packages/vector-stores/milvus/src/search-index.ts b/packages/vector-stores/milvus/src/search-index.ts new file mode 100644 index 00000000..c75182bd --- /dev/null +++ b/packages/vector-stores/milvus/src/search-index.ts @@ -0,0 +1,43 @@ +import { type EmbeddingModel, embedText, type VectorMetadata } from "@anvia/core/embeddings"; +import type { Tool } from "@anvia/core/tool"; +import { + createVectorSearchTool, + type VectorSearchIndex, + type VectorSearchRequest, + type VectorSearchResult, + type VectorSearchToolOptions, +} from "@anvia/core/vector-store"; +import { filterToMilvusExpr } from "./filters.js"; +import { parseQueryResults } from "./helpers.js"; +import { documentFieldName, documentIdFieldName, type MilvusClientLike } from "./types.js"; + +export class MilvusVectorIndex + implements VectorSearchIndex +{ + constructor( + private readonly model: EmbeddingModel, + private readonly client: MilvusClientLike, + private readonly collectionName: string, + ) {} + + async search(request: VectorSearchRequest): Promise>> { + const queryEmbedding = await embedText(this.model, request.query); + const filterExpr = filterToMilvusExpr(request.filter); + const response = await this.client.search({ + collection_name: this.collectionName, + vector: [queryEmbedding.vector], + limit: request.topK, + ...(filterExpr !== undefined ? { filter: filterExpr } : {}), + output_fields: [documentIdFieldName, documentFieldName, "*"], + }); + return parseQueryResults(response, request.threshold); + } + + async searchIds(request: VectorSearchRequest): Promise> { + return (await this.search(request)).map(({ score, id }) => ({ score, id })); + } + + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { + return createVectorSearchTool(this, options); + } +} diff --git a/packages/vector-stores/milvus/src/store.ts b/packages/vector-stores/milvus/src/store.ts new file mode 100644 index 00000000..c5f6b32e --- /dev/null +++ b/packages/vector-stores/milvus/src/store.ts @@ -0,0 +1,41 @@ +import type { EmbeddedDocument, EmbeddingModel, VectorMetadata } from "@anvia/core/embeddings"; +import { defaultMilvusClient, ensureCollection, milvusRows } from "./helpers.js"; +import { MilvusVectorIndex } from "./search-index.js"; +import type { MilvusClientLike, MilvusVectorStoreConnectOptions } from "./types.js"; + +export class MilvusVectorStore { + private constructor( + private readonly client: MilvusClientLike, + private readonly collectionName: string, + ) {} + + static async connect( + options: MilvusVectorStoreConnectOptions, + ): Promise> { + const client = options.client ?? (await defaultMilvusClient()); + + if (options.createIfMissing !== false) { + await ensureCollection( + client, + options.collectionName, + options.vectorSize, + options.metric ?? "COSINE", + ); + } + + await client.loadCollection({ collection_name: options.collectionName }); + return new MilvusVectorStore(client, options.collectionName); + } + + async upsertDocuments(documents: Array>): Promise { + const rows = documents.flatMap((document) => milvusRows(document)); + await this.client.insert({ + collection_name: this.collectionName, + data: rows, + }); + } + + index(model: EmbeddingModel): MilvusVectorIndex { + return new MilvusVectorIndex(model, this.client, this.collectionName); + } +} diff --git a/packages/vector-stores/milvus/src/types.ts b/packages/vector-stores/milvus/src/types.ts new file mode 100644 index 00000000..dfc925ca --- /dev/null +++ b/packages/vector-stores/milvus/src/types.ts @@ -0,0 +1,22 @@ +export const documentIdFieldName = "__anvia_document_id"; +export const documentFieldName = "__anvia_document"; +export const reservedFieldPrefix = "__anvia_"; + +export type MilvusMetric = "COSINE" | "L2" | "IP"; + +export type MilvusClientLike = { + hasCollection(options: { collection_name: string }): Promise<{ value: boolean }>; + createCollection(options: Record): Promise; + createIndex(options: Record): Promise; + loadCollection(options: { collection_name: string }): Promise; + insert(options: Record): Promise; + search(options: Record): Promise; +}; + +export type MilvusVectorStoreConnectOptions = { + client?: MilvusClientLike | undefined; + collectionName: string; + vectorSize: number; + createIfMissing?: boolean | undefined; + metric?: MilvusMetric | undefined; +}; diff --git a/packages/vector-stores/pgvector/src/filters.ts b/packages/vector-stores/pgvector/src/filters.ts new file mode 100644 index 00000000..d162eb34 --- /dev/null +++ b/packages/vector-stores/pgvector/src/filters.ts @@ -0,0 +1,79 @@ +import type { VectorMetadataValue } from "@anvia/core/embeddings"; +import type { VectorFilter } from "@anvia/core/vector-store"; +import type { PgVectorWhere } from "./types.js"; + +export function filterToPgVectorWhere( + filter: VectorFilter | undefined, + startIndex = 1, +): PgVectorWhere | undefined { + if (filter === undefined) { + return undefined; + } + const state = { nextIndex: startIndex }; + const sql = buildFilterSql(filter, state); + return { sql, values: stateValues(filter) }; +} + +function buildFilterSql(filter: VectorFilter, state: { nextIndex: number }): string { + switch (filter.type) { + case "eq": { + const keyIndex = state.nextIndex++; + const valueIndex = state.nextIndex++; + return `(metadata ->> $${keyIndex}) = $${valueIndex}`; + } + case "gt": { + assertNumericFilterValue(filter.value, filter.type); + const keyIndex = state.nextIndex++; + const valueIndex = state.nextIndex++; + return `(metadata ->> $${keyIndex})::numeric > $${valueIndex}`; + } + case "lt": { + assertNumericFilterValue(filter.value, filter.type); + const keyIndex = state.nextIndex++; + const valueIndex = state.nextIndex++; + return `(metadata ->> $${keyIndex})::numeric < $${valueIndex}`; + } + case "and": + return `(${buildFilterSql(filter.filters[0], state)} AND ${buildFilterSql( + filter.filters[1], + state, + )})`; + case "or": + return `(${buildFilterSql(filter.filters[0], state)} OR ${buildFilterSql( + filter.filters[1], + state, + )})`; + default: { + const _exhaustive: never = filter; + throw new Error(`Unknown filter type: ${(_exhaustive as VectorFilter).type}`); + } + } +} + +function stateValues(filter: VectorFilter): unknown[] { + switch (filter.type) { + case "eq": + return [filter.key, serializeMetadataValue(filter.value)]; + case "gt": + case "lt": + assertNumericFilterValue(filter.value, filter.type); + return [filter.key, filter.value]; + case "and": + case "or": + return [...stateValues(filter.filters[0]), ...stateValues(filter.filters[1])]; + default: { + const _exhaustive: never = filter; + throw new Error(`Unknown filter type: ${(_exhaustive as VectorFilter).type}`); + } + } +} + +function serializeMetadataValue(value: VectorMetadataValue): string | null { + return value === null ? null : String(value); +} + +function assertNumericFilterValue(value: VectorMetadataValue, operator: "gt" | "lt"): void { + if (typeof value !== "number") { + throw new Error(`PgVector ${operator} filters require numeric metadata values`); + } +} diff --git a/packages/vector-stores/pgvector/src/helpers.ts b/packages/vector-stores/pgvector/src/helpers.ts new file mode 100644 index 00000000..dc0abfd9 --- /dev/null +++ b/packages/vector-stores/pgvector/src/helpers.ts @@ -0,0 +1,145 @@ +import { createHash } from "node:crypto"; +import type { EmbeddedDocument, VectorMetadata } from "@anvia/core/embeddings"; +import type { VectorSearchResult } from "@anvia/core/vector-store"; +import type { PgClientLike, PgVectorDistance } from "./types.js"; +import { reservedMetadataPrefix } from "./types.js"; + +export function pointId(id: string): string { + const hex = createHash("sha256").update(id).digest("hex").slice(0, 32); + return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice( + 16, + 20, + )}-${hex.slice(20)}`; +} + +export function pgVectorRows( + document: EmbeddedDocument, +): Array<{ + id: string; + documentId: string; + document: T; + metadata: Metadata | undefined; + embedding: number[]; +}> { + if (document.embeddings.length === 0) { + throw new Error(`Document ${document.id} has no embeddings`); + } + assertNoReservedMetadata(document.metadata); + + return document.embeddings.map((embedding, index) => { + const logicalId = + document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; + return { + id: pointId(logicalId), + documentId: document.id, + document: document.document, + metadata: document.metadata, + embedding: embedding.vector, + }; + }); +} + +export function assertNoReservedMetadata(metadata: VectorMetadata | undefined): void { + for (const key of Object.keys(metadata ?? {})) { + if (key.startsWith(reservedMetadataPrefix)) { + throw new Error(`Metadata key ${key} is reserved for Anvia pgvector metadata`); + } + } +} + +export function distanceOperator(distance: PgVectorDistance): "<=>" | "<->" | "<#>" { + switch (distance) { + case "cosine": + return "<=>"; + case "l2": + return "<->"; + case "innerProduct": + return "<#>"; + } +} + +export function scoreFromDistance(distance: number, strategy: PgVectorDistance): number { + return strategy === "cosine" ? 1 - distance : -distance; +} + +export function parseSearchRows( + rows: Array<{ + id: string; + document_id: string; + document: unknown; + metadata: Metadata | null; + distance: number | string; + }>, + threshold: number | undefined, + distanceStrategy: PgVectorDistance, +): Array> { + const byId = new Map>(); + + for (const row of rows) { + const score = scoreFromDistance(Number(row.distance), distanceStrategy); + if (threshold !== undefined && score < threshold) { + continue; + } + const result = { + id: row.document_id, + score, + document: row.document as T, + ...(row.metadata === null ? {} : { metadata: row.metadata }), + } as VectorSearchResult; + const current = byId.get(result.id); + if (current === undefined || result.score > current.score) { + byId.set(result.id, result); + } + } + + return [...byId.values()]; +} + +export function normalizedTopK(topK: number): number { + return Math.max(0, Math.trunc(topK)); +} + +export function quoteQualifiedIdentifier(identifier: string): string { + const parts = identifier.split("."); + if (parts.length === 0 || parts.some((part) => part.length === 0)) { + throw new Error(`Invalid Postgres identifier: ${identifier}`); + } + return parts.map(quoteIdentifier).join("."); +} + +function quoteIdentifier(identifier: string): string { + if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier)) { + throw new Error(`Invalid Postgres identifier: ${identifier}`); + } + return `"${identifier.replaceAll('"', '""')}"`; +} + +export async function validateTable( + client: PgClientLike, + tableName: string, + vectorSize: number, +): Promise { + const result = await client.query( + `SELECT a.atttypmod AS vector_size +FROM pg_attribute a +WHERE a.attrelid = $1::regclass + AND a.attname = 'embedding' + AND NOT a.attisdropped`, + [tableName], + ); + const rawSize = result.rows[0]?.vector_size; + if (rawSize === undefined) { + throw new Error(`PgVector table ${tableName} is missing an embedding vector column`); + } + const actualSize = Number(rawSize); + if (actualSize !== vectorSize) { + throw new Error( + `PgVector table ${tableName} has vector size ${actualSize}; expected ${vectorSize}`, + ); + } +} + +export async function defaultPgClient(connectionString: string | undefined): Promise { + const pg = await import("pg"); + return new pg.Pool(connectionString === undefined ? {} : { connectionString }) as PgClientLike; +} diff --git a/packages/vector-stores/pgvector/src/index.ts b/packages/vector-stores/pgvector/src/index.ts index 5f46eb05..4a6afc88 100644 --- a/packages/vector-stores/pgvector/src/index.ts +++ b/packages/vector-stores/pgvector/src/index.ts @@ -1,364 +1,9 @@ -import { createHash } from "node:crypto"; -import { - type EmbeddedDocument, - type EmbeddingModel, - embedText, - type VectorMetadata, - type VectorMetadataValue, -} from "@anvia/core/embeddings"; -import type { Tool } from "@anvia/core/tool"; -import { - createVectorSearchTool, - type VectorFilter, - type VectorSearchIndex, - type VectorSearchRequest, - type VectorSearchResult, - type VectorSearchToolOptions, -} from "@anvia/core/vector-store"; -import pgvector from "pgvector"; - -const reservedMetadataPrefix = "__anvia_"; - -export type PgVectorDistance = "cosine" | "l2" | "innerProduct"; - -export type PgVectorWhere = { - sql: string; - values: unknown[]; -}; - -type PgClientLike = { - query(text: string, values?: readonly unknown[]): Promise<{ rows: Record[] }>; -}; - -export type PgVectorStoreConnectOptions = { - client?: PgClientLike | undefined; - connectionString?: string | undefined; - tableName: string; - vectorSize: number; - createIfMissing?: boolean | undefined; - distance?: PgVectorDistance | undefined; -}; - -export class PgVectorStore { - private constructor( - private readonly client: PgClientLike, - private readonly tableName: string, - private readonly distance: PgVectorDistance, - ) {} - - static async connect( - options: PgVectorStoreConnectOptions, - ): Promise> { - const client = options.client ?? (await defaultPgClient(options.connectionString)); - const tableName = quoteQualifiedIdentifier(options.tableName); - const distance = options.distance ?? "cosine"; - - if (options.createIfMissing !== false) { - await client.query("CREATE EXTENSION IF NOT EXISTS vector"); - await client.query(`CREATE TABLE IF NOT EXISTS ${tableName} ( - id text PRIMARY KEY, - document_id text NOT NULL, - document jsonb NOT NULL, - metadata jsonb, - embedding vector(${options.vectorSize}) NOT NULL -)`); - } - - await validateTable(client, tableName, options.vectorSize); - return new PgVectorStore(client, tableName, distance); - } - - async upsertDocuments(documents: Array>): Promise { - const rows = documents.flatMap((document) => pgVectorRows(document)); - if (rows.length === 0) { - return; - } - - const values = rows.flatMap((row) => [ - row.id, - row.documentId, - JSON.stringify(row.document), - row.metadata === undefined ? null : JSON.stringify(row.metadata), - pgvector.toSql(row.embedding), - ]); - const placeholders = rows.map((_, index) => { - const offset = index * 5; - return `($${offset + 1}, $${offset + 2}, $${offset + 3}::jsonb, $${offset + 4}::jsonb, $${ - offset + 5 - }::vector)`; - }); - - await this.client.query( - `INSERT INTO ${this.tableName} (id, document_id, document, metadata, embedding) -VALUES ${placeholders.join(", ")} -ON CONFLICT (id) DO UPDATE SET - document_id = EXCLUDED.document_id, - document = EXCLUDED.document, - metadata = EXCLUDED.metadata, - embedding = EXCLUDED.embedding`, - values, - ); - } - - index(model: EmbeddingModel): PgVectorIndex { - return new PgVectorIndex(model, this.client, this.tableName, this.distance); - } -} - -export class PgVectorIndex - implements VectorSearchIndex -{ - constructor( - private readonly model: EmbeddingModel, - private readonly client: PgClientLike, - private readonly tableName: string, - private readonly distance: PgVectorDistance, - ) {} - - async search(request: VectorSearchRequest): Promise>> { - const queryEmbedding = await embedText(this.model, request.query); - const operator = distanceOperator(this.distance); - const where = filterToPgVectorWhere(request.filter, 2); - const limitParameter = 2 + (where?.values.length ?? 0); - const response = await this.client.query( - `SELECT id, document_id, document, metadata, embedding ${operator} $1::vector AS distance -FROM ${this.tableName} -${where === undefined ? "" : `WHERE ${where.sql}`} -ORDER BY embedding ${operator} $1::vector -LIMIT $${limitParameter}`, - [ - pgvector.toSql(queryEmbedding.vector), - ...(where?.values ?? []), - normalizedTopK(request.topK), - ], - ); - - return parseSearchRows( - response.rows as Array<{ - id: string; - document_id: string; - document: unknown; - metadata: Metadata | null; - distance: number | string; - }>, - request.threshold, - this.distance, - ); - } - - async searchIds(request: VectorSearchRequest): Promise> { - return (await this.search(request)).map(({ score, id }) => ({ score, id })); - } - - asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { - return createVectorSearchTool(this, options); - } -} - -export function filterToPgVectorWhere( - filter: VectorFilter | undefined, - startIndex = 1, -): PgVectorWhere | undefined { - if (filter === undefined) { - return undefined; - } - const state = { nextIndex: startIndex }; - const sql = buildFilterSql(filter, state); - return { sql, values: stateValues(filter) }; -} - -async function defaultPgClient(connectionString: string | undefined): Promise { - const pg = await import("pg"); - return new pg.Pool(connectionString === undefined ? {} : { connectionString }) as PgClientLike; -} - -async function validateTable( - client: PgClientLike, - tableName: string, - vectorSize: number, -): Promise { - const result = await client.query( - `SELECT a.atttypmod AS vector_size -FROM pg_attribute a -WHERE a.attrelid = $1::regclass - AND a.attname = 'embedding' - AND NOT a.attisdropped`, - [tableName], - ); - const rawSize = result.rows[0]?.vector_size; - if (rawSize === undefined) { - throw new Error(`PgVector table ${tableName} is missing an embedding vector column`); - } - const actualSize = Number(rawSize); - if (actualSize !== vectorSize) { - throw new Error( - `PgVector table ${tableName} has vector size ${actualSize}; expected ${vectorSize}`, - ); - } -} - -function distanceOperator(distance: PgVectorDistance): "<=>" | "<->" | "<#>" { - switch (distance) { - case "cosine": - return "<=>"; - case "l2": - return "<->"; - case "innerProduct": - return "<#>"; - } -} - -function scoreFromDistance(distance: number, strategy: PgVectorDistance): number { - return strategy === "cosine" ? 1 - distance : -distance; -} - -function parseSearchRows( - rows: Array<{ - id: string; - document_id: string; - document: unknown; - metadata: Metadata | null; - distance: number | string; - }>, - threshold: number | undefined, - distanceStrategy: PgVectorDistance, -): Array> { - const byId = new Map>(); - - for (const row of rows) { - const score = scoreFromDistance(Number(row.distance), distanceStrategy); - if (threshold !== undefined && score < threshold) { - continue; - } - const result = { - id: row.document_id, - score, - document: row.document as T, - ...(row.metadata === null ? {} : { metadata: row.metadata }), - } as VectorSearchResult; - const current = byId.get(result.id); - if (current === undefined || result.score > current.score) { - byId.set(result.id, result); - } - } - - return [...byId.values()]; -} - -function pgVectorRows( - document: EmbeddedDocument, -): Array<{ - id: string; - documentId: string; - document: T; - metadata: Metadata | undefined; - embedding: number[]; -}> { - if (document.embeddings.length === 0) { - throw new Error(`Document ${document.id} has no embeddings`); - } - assertNoReservedMetadata(document.metadata); - - return document.embeddings.map((embedding, index) => { - const logicalId = - document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; - return { - id: pointId(logicalId), - documentId: document.id, - document: document.document, - metadata: document.metadata, - embedding: embedding.vector, - }; - }); -} - -function assertNoReservedMetadata(metadata: VectorMetadata | undefined): void { - for (const key of Object.keys(metadata ?? {})) { - if (key.startsWith(reservedMetadataPrefix)) { - throw new Error(`Metadata key ${key} is reserved for Anvia pgvector metadata`); - } - } -} - -function pointId(id: string): string { - const hex = createHash("sha256").update(id).digest("hex").slice(0, 32); - return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice( - 16, - 20, - )}-${hex.slice(20)}`; -} - -function quoteQualifiedIdentifier(identifier: string): string { - const parts = identifier.split("."); - if (parts.length === 0 || parts.some((part) => part.length === 0)) { - throw new Error(`Invalid Postgres identifier: ${identifier}`); - } - return parts.map(quoteIdentifier).join("."); -} - -function quoteIdentifier(identifier: string): string { - if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier)) { - throw new Error(`Invalid Postgres identifier: ${identifier}`); - } - return `"${identifier.replaceAll('"', '""')}"`; -} - -function normalizedTopK(topK: number): number { - return Math.max(0, Math.trunc(topK)); -} - -function buildFilterSql(filter: VectorFilter, state: { nextIndex: number }): string { - switch (filter.type) { - case "eq": { - const keyIndex = state.nextIndex++; - const valueIndex = state.nextIndex++; - return `(metadata ->> $${keyIndex}) = $${valueIndex}`; - } - case "gt": { - assertNumericFilterValue(filter.value, filter.type); - const keyIndex = state.nextIndex++; - const valueIndex = state.nextIndex++; - return `(metadata ->> $${keyIndex})::numeric > $${valueIndex}`; - } - case "lt": { - assertNumericFilterValue(filter.value, filter.type); - const keyIndex = state.nextIndex++; - const valueIndex = state.nextIndex++; - return `(metadata ->> $${keyIndex})::numeric < $${valueIndex}`; - } - case "and": - return `(${buildFilterSql(filter.filters[0], state)} AND ${buildFilterSql( - filter.filters[1], - state, - )})`; - case "or": - return `(${buildFilterSql(filter.filters[0], state)} OR ${buildFilterSql( - filter.filters[1], - state, - )})`; - } -} - -function stateValues(filter: VectorFilter): unknown[] { - switch (filter.type) { - case "eq": - return [filter.key, serializeMetadataValue(filter.value)]; - case "gt": - case "lt": - assertNumericFilterValue(filter.value, filter.type); - return [filter.key, filter.value]; - case "and": - case "or": - return [...stateValues(filter.filters[0]), ...stateValues(filter.filters[1])]; - } -} - -function serializeMetadataValue(value: VectorMetadataValue): string | null { - return value === null ? null : String(value); -} - -function assertNumericFilterValue(value: VectorMetadataValue, operator: "gt" | "lt"): void { - if (typeof value !== "number") { - throw new Error(`PgVector ${operator} filters require numeric metadata values`); - } -} +export { filterToPgVectorWhere } from "./filters.js"; +export { PgVectorIndex } from "./search-index.js"; +export { PgVectorStore } from "./store.js"; +export type { + PgClientLike, + PgVectorDistance, + PgVectorStoreConnectOptions, + PgVectorWhere, +} from "./types.js"; diff --git a/packages/vector-stores/pgvector/src/search-index.ts b/packages/vector-stores/pgvector/src/search-index.ts new file mode 100644 index 00000000..5d82a809 --- /dev/null +++ b/packages/vector-stores/pgvector/src/search-index.ts @@ -0,0 +1,63 @@ +import { type EmbeddingModel, embedText, type VectorMetadata } from "@anvia/core/embeddings"; +import type { Tool } from "@anvia/core/tool"; +import { + createVectorSearchTool, + type VectorSearchIndex, + type VectorSearchRequest, + type VectorSearchResult, + type VectorSearchToolOptions, +} from "@anvia/core/vector-store"; +import pgvector from "pgvector"; +import { filterToPgVectorWhere } from "./filters.js"; +import { distanceOperator, normalizedTopK, parseSearchRows } from "./helpers.js"; +import type { PgClientLike, PgVectorDistance } from "./types.js"; + +export class PgVectorIndex + implements VectorSearchIndex +{ + constructor( + private readonly model: EmbeddingModel, + private readonly client: PgClientLike, + private readonly tableName: string, + private readonly distance: PgVectorDistance, + ) {} + + async search(request: VectorSearchRequest): Promise>> { + const queryEmbedding = await embedText(this.model, request.query); + const operator = distanceOperator(this.distance); + const where = filterToPgVectorWhere(request.filter, 2); + const limitParameter = 2 + (where?.values.length ?? 0); + const response = await this.client.query( + `SELECT id, document_id, document, metadata, embedding ${operator} $1::vector AS distance +FROM ${this.tableName} +${where === undefined ? "" : `WHERE ${where.sql}`} +ORDER BY embedding ${operator} $1::vector +LIMIT $${limitParameter}`, + [ + pgvector.toSql(queryEmbedding.vector), + ...(where?.values ?? []), + normalizedTopK(request.topK), + ], + ); + + return parseSearchRows( + response.rows as Array<{ + id: string; + document_id: string; + document: unknown; + metadata: Metadata | null; + distance: number | string; + }>, + request.threshold, + this.distance, + ); + } + + async searchIds(request: VectorSearchRequest): Promise> { + return (await this.search(request)).map(({ score, id }) => ({ score, id })); + } + + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { + return createVectorSearchTool(this, options); + } +} diff --git a/packages/vector-stores/pgvector/src/store.ts b/packages/vector-stores/pgvector/src/store.ts new file mode 100644 index 00000000..fc9fd40e --- /dev/null +++ b/packages/vector-stores/pgvector/src/store.ts @@ -0,0 +1,76 @@ +import type { EmbeddedDocument, EmbeddingModel, VectorMetadata } from "@anvia/core/embeddings"; +import pgvector from "pgvector"; +import { + defaultPgClient, + pgVectorRows, + quoteQualifiedIdentifier, + validateTable, +} from "./helpers.js"; +import { PgVectorIndex } from "./search-index.js"; +import type { PgClientLike, PgVectorDistance, PgVectorStoreConnectOptions } from "./types.js"; + +export class PgVectorStore { + private constructor( + private readonly client: PgClientLike, + private readonly tableName: string, + private readonly distance: PgVectorDistance, + ) {} + + static async connect( + options: PgVectorStoreConnectOptions, + ): Promise> { + const client = options.client ?? (await defaultPgClient(options.connectionString)); + const tableName = quoteQualifiedIdentifier(options.tableName); + const distance = options.distance ?? "cosine"; + + if (options.createIfMissing !== false) { + await client.query("CREATE EXTENSION IF NOT EXISTS vector"); + await client.query(`CREATE TABLE IF NOT EXISTS ${tableName} ( + id text PRIMARY KEY, + document_id text NOT NULL, + document jsonb NOT NULL, + metadata jsonb, + embedding vector(${options.vectorSize}) NOT NULL +)`); + } + + await validateTable(client, tableName, options.vectorSize); + return new PgVectorStore(client, tableName, distance); + } + + async upsertDocuments(documents: Array>): Promise { + const rows = documents.flatMap((document) => pgVectorRows(document)); + if (rows.length === 0) { + return; + } + + const values = rows.flatMap((row) => [ + row.id, + row.documentId, + JSON.stringify(row.document), + row.metadata === undefined ? null : JSON.stringify(row.metadata), + pgvector.toSql(row.embedding), + ]); + const placeholders = rows.map((_, index) => { + const offset = index * 5; + return `($${offset + 1}, $${offset + 2}, $${offset + 3}::jsonb, $${offset + 4}::jsonb, $${ + offset + 5 + }::vector)`; + }); + + await this.client.query( + `INSERT INTO ${this.tableName} (id, document_id, document, metadata, embedding) +VALUES ${placeholders.join(", ")} +ON CONFLICT (id) DO UPDATE SET + document_id = EXCLUDED.document_id, + document = EXCLUDED.document, + metadata = EXCLUDED.metadata, + embedding = EXCLUDED.embedding`, + values, + ); + } + + index(model: EmbeddingModel): PgVectorIndex { + return new PgVectorIndex(model, this.client, this.tableName, this.distance); + } +} diff --git a/packages/vector-stores/pgvector/src/types.ts b/packages/vector-stores/pgvector/src/types.ts new file mode 100644 index 00000000..8389220d --- /dev/null +++ b/packages/vector-stores/pgvector/src/types.ts @@ -0,0 +1,21 @@ +export const reservedMetadataPrefix = "__anvia_"; + +export type PgVectorDistance = "cosine" | "l2" | "innerProduct"; + +export type PgVectorWhere = { + sql: string; + values: unknown[]; +}; + +export type PgClientLike = { + query(text: string, values?: readonly unknown[]): Promise<{ rows: Record[] }>; +}; + +export type PgVectorStoreConnectOptions = { + client?: PgClientLike | undefined; + connectionString?: string | undefined; + tableName: string; + vectorSize: number; + createIfMissing?: boolean | undefined; + distance?: PgVectorDistance | undefined; +}; diff --git a/packages/vector-stores/pinecone/src/filters.ts b/packages/vector-stores/pinecone/src/filters.ts new file mode 100644 index 00000000..fc226755 --- /dev/null +++ b/packages/vector-stores/pinecone/src/filters.ts @@ -0,0 +1,20 @@ +import type { VectorFilter } from "@anvia/core/vector-store"; + +export function filterToPineconeFilter(filter: VectorFilter | undefined): unknown { + if (filter === undefined) { + return undefined; + } + + switch (filter.type) { + case "eq": + return { [filter.key]: { $eq: filter.value } }; + case "gt": + return { [filter.key]: { $gt: filter.value } }; + case "lt": + return { [filter.key]: { $lt: filter.value } }; + case "and": + return { $and: filter.filters.map(filterToPineconeFilter) }; + case "or": + return { $or: filter.filters.map(filterToPineconeFilter) }; + } +} diff --git a/packages/vector-stores/pinecone/src/helpers.ts b/packages/vector-stores/pinecone/src/helpers.ts new file mode 100644 index 00000000..8f8187d0 --- /dev/null +++ b/packages/vector-stores/pinecone/src/helpers.ts @@ -0,0 +1,140 @@ +import { createHash } from "node:crypto"; +import type { EmbeddedDocument, VectorMetadata } from "@anvia/core/embeddings"; +import type { VectorSearchResult } from "@anvia/core/vector-store"; +import { + documentIdMetadataKey, + documentMetadataKey, + type PineconeClientLike, + type PineconeMetric, + reservedMetadataPrefix, +} from "./types.js"; + +export function pointId(id: string): string { + return createHash("sha256").update(id).digest("hex").slice(0, 32); +} + +export function serializeDocument(document: unknown): string { + return typeof document === "string" ? document : JSON.stringify(document); +} + +export function parseDocument(document: unknown): T { + if (document === null || document === undefined) { + return "" as T; + } + if (typeof document !== "string") { + return document as T; + } + try { + return JSON.parse(document) as T; + } catch { + return document as T; + } +} + +export function assertNoReservedMetadata(metadata: VectorMetadata | undefined): void { + for (const key of Object.keys(metadata ?? {})) { + if (key.startsWith(reservedMetadataPrefix)) { + throw new Error(`Metadata key ${key} is reserved for Anvia Pinecone payloads`); + } + } +} + +export function pineconeVectors( + document: EmbeddedDocument, +): Array> { + if (document.embeddings.length === 0) { + throw new Error(`Document ${document.id} has no embeddings`); + } + + assertNoReservedMetadata(document.metadata); + + return document.embeddings.map((embedding, index) => { + const logicalId = + document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; + return { + id: pointId(logicalId), + values: embedding.vector, + metadata: { + [documentIdMetadataKey]: document.id, + [documentMetadataKey]: serializeDocument(document.document), + ...(document.metadata ?? {}), + }, + }; + }); +} + +export function parseQueryResults( + response: unknown, + threshold: number | undefined, +): Array> { + const raw = response as { + matches?: Array<{ + id: string; + score?: number; + metadata?: Record | null; + }>; + }; + const matches = raw.matches ?? []; + + const byId = new Map>(); + + for (const match of matches) { + const score = match.score ?? 0; + if (threshold !== undefined && score < threshold) { + continue; + } + + const id = String(match.metadata?.[documentIdMetadataKey] ?? match.id); + const result = { + id, + score, + document: parseDocument(match.metadata?.[documentMetadataKey]), + ...metadataFromPayload(match.metadata), + } as VectorSearchResult; + + const current = byId.get(id); + if (current === undefined || result.score > current.score) { + byId.set(id, result); + } + } + + return [...byId.values()]; +} + +export async function defaultPineconeClient(): Promise { + const { Pinecone } = await import("@pinecone-database/pinecone"); + return new Pinecone() as unknown as PineconeClientLike; +} + +export async function ensureIndex( + client: PineconeClientLike, + indexName: string, + metric: PineconeMetric, +): Promise { + try { + await client.listIndexes(); + } catch { + return; + } + + try { + await client.createIndex({ + name: indexName, + dimension: undefined, + metric, + spec: { serverless: { cloud: "aws", region: "us-east-1" } }, + waitUntilReady: true, + }); + } catch { + // Index already exists + } +} + +function metadataFromPayload( + payload: Record | null | undefined, +): { metadata?: Metadata | undefined } { + const metadata = Object.fromEntries( + Object.entries(payload ?? {}).filter(([key]) => !key.startsWith(reservedMetadataPrefix)), + ) as Metadata; + return Object.keys(metadata).length === 0 ? {} : { metadata }; +} diff --git a/packages/vector-stores/pinecone/src/index.ts b/packages/vector-stores/pinecone/src/index.ts index b7145b6f..e50bf67b 100644 --- a/packages/vector-stores/pinecone/src/index.ts +++ b/packages/vector-stores/pinecone/src/index.ts @@ -1,248 +1,10 @@ -import { createHash } from "node:crypto"; -import { - type EmbeddedDocument, - type EmbeddingModel, - embedText, - type VectorMetadata, -} from "@anvia/core/embeddings"; -import type { Tool } from "@anvia/core/tool"; -import { - createVectorSearchTool, - type VectorFilter, - type VectorSearchIndex, - type VectorSearchRequest, - type VectorSearchResult, - type VectorSearchToolOptions, -} from "@anvia/core/vector-store"; - -const documentIdMetadataKey = "__anvia_document_id"; -const documentMetadataKey = "__anvia_document"; -const reservedMetadataPrefix = "__anvia_"; - -export type PineconeMetric = "cosine" | "euclidean" | "dotproduct"; - -type PineconeClientLike = { - listIndexes(): Promise; - createIndex(options: Record): Promise; - index(indexName: string): PineconeIndexLike; -}; - -type PineconeIndexLike = { - namespace(namespace: string): PineconeNamespaceLike; -}; - -type PineconeNamespaceLike = { - upsert(vectors: Array>): Promise; - query(options: Record): Promise; -}; - -export type PineconeVectorStoreConnectOptions = { - client?: PineconeClientLike | undefined; - indexName: string; - namespace?: string | undefined; - createIfMissing?: boolean | undefined; - metric?: PineconeMetric | undefined; -}; - -export class PineconeVectorStore { - private constructor(private readonly namespace: PineconeNamespaceLike) {} - - static async connect( - options: PineconeVectorStoreConnectOptions, - ): Promise> { - const client = options.client ?? (await defaultPineconeClient()); - - if (options.createIfMissing !== false) { - await ensureIndex(client, options.indexName, options.metric ?? "cosine"); - } - - const namespace = client.index(options.indexName).namespace(options.namespace ?? ""); - return new PineconeVectorStore(namespace); - } - - async upsertDocuments(documents: Array>): Promise { - const vectors = documents.flatMap((document) => pineconeVectors(document)); - await this.namespace.upsert(vectors); - } - - index(model: EmbeddingModel): PineconeVectorIndex { - return new PineconeVectorIndex(model, this.namespace); - } -} - -export class PineconeVectorIndex - implements VectorSearchIndex -{ - constructor( - private readonly model: EmbeddingModel, - private readonly namespace: PineconeNamespaceLike, - ) {} - - async search(request: VectorSearchRequest): Promise>> { - const queryEmbedding = await embedText(this.model, request.query); - const response = await this.namespace.query({ - vector: queryEmbedding.vector, - topK: request.topK, - filter: filterToPineconeFilter(request.filter), - includeMetadata: true, - }); - return parseQueryResults(response, request.threshold); - } - - async searchIds(request: VectorSearchRequest): Promise> { - return (await this.search(request)).map(({ score, id }) => ({ score, id })); - } - - asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { - return createVectorSearchTool(this, options); - } -} - -export function filterToPineconeFilter(filter: VectorFilter | undefined): unknown { - if (filter === undefined) { - return undefined; - } - - switch (filter.type) { - case "eq": - return { [filter.key]: { $eq: filter.value } }; - case "gt": - return { [filter.key]: { $gt: filter.value } }; - case "lt": - return { [filter.key]: { $lt: filter.value } }; - case "and": - return { $and: filter.filters.map(filterToPineconeFilter) }; - case "or": - return { $or: filter.filters.map(filterToPineconeFilter) }; - } -} - -async function defaultPineconeClient(): Promise { - const { Pinecone } = await import("@pinecone-database/pinecone"); - return new Pinecone() as unknown as PineconeClientLike; -} - -async function ensureIndex( - client: PineconeClientLike, - indexName: string, - metric: PineconeMetric, -): Promise { - try { - await client.listIndexes(); - } catch { - return; - } - - try { - await client.createIndex({ - name: indexName, - dimension: undefined, - metric, - spec: { serverless: { cloud: "aws", region: "us-east-1" } }, - waitUntilReady: true, - }); - } catch { - // Index already exists - } -} - -function pineconeVectors( - document: EmbeddedDocument, -): Array> { - if (document.embeddings.length === 0) { - throw new Error(`Document ${document.id} has no embeddings`); - } - - assertNoReservedMetadata(document.metadata); - - return document.embeddings.map((embedding, index) => { - const logicalId = - document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; - return { - id: pointId(logicalId), - values: embedding.vector, - metadata: { - [documentIdMetadataKey]: document.id, - [documentMetadataKey]: serializeDocument(document.document), - ...(document.metadata ?? {}), - }, - }; - }); -} - -function assertNoReservedMetadata(metadata: VectorMetadata | undefined): void { - for (const key of Object.keys(metadata ?? {})) { - if (key.startsWith(reservedMetadataPrefix)) { - throw new Error(`Metadata key ${key} is reserved for Anvia Pinecone payloads`); - } - } -} - -function pointId(id: string): string { - return createHash("sha256").update(id).digest("hex").slice(0, 32); -} - -function serializeDocument(document: unknown): string { - return typeof document === "string" ? document : JSON.stringify(document); -} - -function parseQueryResults( - response: unknown, - threshold: number | undefined, -): Array> { - const raw = response as { - matches?: Array<{ - id: string; - score?: number; - metadata?: Record | null; - }>; - }; - const matches = raw.matches ?? []; - - const byId = new Map>(); - - for (const match of matches) { - const score = match.score ?? 0; - if (threshold !== undefined && score < threshold) { - continue; - } - - const id = String(match.metadata?.[documentIdMetadataKey] ?? match.id); - const result = { - id, - score, - document: parseDocument(match.metadata?.[documentMetadataKey]), - ...metadataFromPayload(match.metadata), - } as VectorSearchResult; - - const current = byId.get(id); - if (current === undefined || result.score > current.score) { - byId.set(id, result); - } - } - - return [...byId.values()]; -} - -function parseDocument(document: unknown): T { - if (document === null || document === undefined) { - return "" as T; - } - if (typeof document !== "string") { - return document as T; - } - try { - return JSON.parse(document) as T; - } catch { - return document as T; - } -} - -function metadataFromPayload( - payload: Record | null | undefined, -): { metadata?: Metadata | undefined } { - const metadata = Object.fromEntries( - Object.entries(payload ?? {}).filter(([key]) => !key.startsWith(reservedMetadataPrefix)), - ) as Metadata; - return Object.keys(metadata).length === 0 ? {} : { metadata }; -} +export { filterToPineconeFilter } from "./filters.js"; +export { PineconeVectorIndex } from "./search-index.js"; +export { PineconeVectorStore } from "./store.js"; +export type { + PineconeClientLike, + PineconeIndexLike, + PineconeMetric, + PineconeNamespaceLike, + PineconeVectorStoreConnectOptions, +} from "./types.js"; diff --git a/packages/vector-stores/pinecone/src/search-index.ts b/packages/vector-stores/pinecone/src/search-index.ts new file mode 100644 index 00000000..3c234471 --- /dev/null +++ b/packages/vector-stores/pinecone/src/search-index.ts @@ -0,0 +1,40 @@ +import { type EmbeddingModel, embedText, type VectorMetadata } from "@anvia/core/embeddings"; +import type { Tool } from "@anvia/core/tool"; +import { + createVectorSearchTool, + type VectorSearchIndex, + type VectorSearchRequest, + type VectorSearchResult, + type VectorSearchToolOptions, +} from "@anvia/core/vector-store"; +import { filterToPineconeFilter } from "./filters.js"; +import { parseQueryResults } from "./helpers.js"; +import type { PineconeNamespaceLike } from "./types.js"; + +export class PineconeVectorIndex + implements VectorSearchIndex +{ + constructor( + private readonly model: EmbeddingModel, + private readonly namespace: PineconeNamespaceLike, + ) {} + + async search(request: VectorSearchRequest): Promise>> { + const queryEmbedding = await embedText(this.model, request.query); + const response = await this.namespace.query({ + vector: queryEmbedding.vector, + topK: request.topK, + filter: filterToPineconeFilter(request.filter), + includeMetadata: true, + }); + return parseQueryResults(response, request.threshold); + } + + async searchIds(request: VectorSearchRequest): Promise> { + return (await this.search(request)).map(({ score, id }) => ({ score, id })); + } + + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { + return createVectorSearchTool(this, options); + } +} diff --git a/packages/vector-stores/pinecone/src/store.ts b/packages/vector-stores/pinecone/src/store.ts new file mode 100644 index 00000000..96917a89 --- /dev/null +++ b/packages/vector-stores/pinecone/src/store.ts @@ -0,0 +1,30 @@ +import type { EmbeddedDocument, EmbeddingModel, VectorMetadata } from "@anvia/core/embeddings"; +import { defaultPineconeClient, ensureIndex, pineconeVectors } from "./helpers.js"; +import { PineconeVectorIndex } from "./search-index.js"; +import type { PineconeNamespaceLike, PineconeVectorStoreConnectOptions } from "./types.js"; + +export class PineconeVectorStore { + private constructor(private readonly namespace: PineconeNamespaceLike) {} + + static async connect( + options: PineconeVectorStoreConnectOptions, + ): Promise> { + const client = options.client ?? (await defaultPineconeClient()); + + if (options.createIfMissing !== false) { + await ensureIndex(client, options.indexName, options.metric ?? "cosine"); + } + + const namespace = client.index(options.indexName).namespace(options.namespace ?? ""); + return new PineconeVectorStore(namespace); + } + + async upsertDocuments(documents: Array>): Promise { + const vectors = documents.flatMap((document) => pineconeVectors(document)); + await this.namespace.upsert(vectors); + } + + index(model: EmbeddingModel): PineconeVectorIndex { + return new PineconeVectorIndex(model, this.namespace); + } +} diff --git a/packages/vector-stores/pinecone/src/types.ts b/packages/vector-stores/pinecone/src/types.ts new file mode 100644 index 00000000..1f6400da --- /dev/null +++ b/packages/vector-stores/pinecone/src/types.ts @@ -0,0 +1,28 @@ +export const documentIdMetadataKey = "__anvia_document_id"; +export const documentMetadataKey = "__anvia_document"; +export const reservedMetadataPrefix = "__anvia_"; + +export type PineconeMetric = "cosine" | "euclidean" | "dotproduct"; + +export type PineconeClientLike = { + listIndexes(): Promise; + createIndex(options: Record): Promise; + index(indexName: string): PineconeIndexLike; +}; + +export type PineconeIndexLike = { + namespace(namespace: string): PineconeNamespaceLike; +}; + +export type PineconeNamespaceLike = { + upsert(vectors: Array>): Promise; + query(options: Record): Promise; +}; + +export type PineconeVectorStoreConnectOptions = { + client?: PineconeClientLike | undefined; + indexName: string; + namespace?: string | undefined; + createIfMissing?: boolean | undefined; + metric?: PineconeMetric | undefined; +}; diff --git a/packages/vector-stores/qdrant/src/filters.ts b/packages/vector-stores/qdrant/src/filters.ts new file mode 100644 index 00000000..883755c4 --- /dev/null +++ b/packages/vector-stores/qdrant/src/filters.ts @@ -0,0 +1,20 @@ +import type { VectorFilter } from "@anvia/core/vector-store"; + +export function filterToQdrantFilter(filter: VectorFilter | undefined): unknown { + if (filter === undefined) { + return undefined; + } + + switch (filter.type) { + case "eq": + return { must: [{ key: filter.key, match: { value: filter.value } }] }; + case "gt": + return { must: [{ key: filter.key, range: { gt: filter.value } }] }; + case "lt": + return { must: [{ key: filter.key, range: { lt: filter.value } }] }; + case "and": + return { must: filter.filters.map(filterToQdrantFilter) }; + case "or": + return { should: filter.filters.map(filterToQdrantFilter) }; + } +} diff --git a/packages/vector-stores/qdrant/src/helpers.ts b/packages/vector-stores/qdrant/src/helpers.ts new file mode 100644 index 00000000..c28ac3b9 --- /dev/null +++ b/packages/vector-stores/qdrant/src/helpers.ts @@ -0,0 +1,154 @@ +import { createHash } from "node:crypto"; +import type { EmbeddedDocument, VectorMetadata } from "@anvia/core/embeddings"; +import type { VectorSearchResult } from "@anvia/core/vector-store"; +import { + documentIdPayloadKey, + documentPayloadKey, + type QdrantClientLike, + reservedPayloadPrefix, +} from "./types.js"; + +export function assertNoReservedMetadata(metadata: VectorMetadata | undefined): void { + for (const key of Object.keys(metadata ?? {})) { + if (key.startsWith(reservedPayloadPrefix)) { + throw new Error(`Metadata key ${key} is reserved for Anvia Qdrant payloads`); + } + } +} + +export function pointId(id: string): string { + const hex = createHash("sha256").update(id).digest("hex").slice(0, 32); + return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice( + 16, + 20, + )}-${hex.slice(20)}`; +} + +export function serializeDocument(document: unknown): string { + return typeof document === "string" ? document : JSON.stringify(document); +} + +export function parseDocument(document: unknown): T { + if (document === null || document === undefined) { + return "" as T; + } + if (typeof document !== "string") { + return document as T; + } + try { + return JSON.parse(document) as T; + } catch { + return document as T; + } +} + +export function qdrantPoints( + document: EmbeddedDocument, +): Array<{ + id: string; + vector: number[]; + payload: Record; +}> { + if (document.embeddings.length === 0) { + throw new Error(`Document ${document.id} has no embeddings`); + } + assertNoReservedMetadata(document.metadata); + + return document.embeddings.map((embedding, index) => { + const logicalId = + document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; + return { + id: pointId(logicalId), + vector: embedding.vector, + payload: { + [documentIdPayloadKey]: document.id, + [documentPayloadKey]: serializeDocument(document.document), + ...(document.metadata ?? {}), + }, + }; + }); +} + +export function parseQueryResults( + response: unknown, + threshold: number | undefined, +): Array> { + const points = rawPoints(response); + const byId = new Map>(); + + for (const point of points) { + if (threshold !== undefined && point.score < threshold) { + continue; + } + + const id = String(point.payload?.[documentIdPayloadKey] ?? point.id); + const result = { + id, + score: point.score, + document: parseDocument(point.payload?.[documentPayloadKey]), + ...metadataFromPayload(point.payload), + } as VectorSearchResult; + const current = byId.get(id); + if (current === undefined || result.score > current.score) { + byId.set(id, result); + } + } + + return [...byId.values()]; +} + +export async function defaultQdrantClient(): Promise { + const qdrant = await import("@qdrant/js-client-rest"); + return new qdrant.QdrantClient({}) as QdrantClientLike; +} + +function rawPoints(response: unknown): Array<{ + id: string | number; + score: number; + payload?: Record | null; +}> { + const raw = response as { + points?: Array<{ + id: string | number; + score?: number; + payload?: Record | null; + }>; + result?: + | { + points?: Array<{ + id: string | number; + score?: number; + payload?: Record | null; + }>; + } + | Array<{ id: string | number; score?: number; payload?: Record | null }>; + }; + const responseArray = Array.isArray(response) + ? (response as Array<{ + id: string | number; + score?: number; + payload?: Record | null; + }>) + : undefined; + const points = + responseArray ?? + (Array.isArray(raw.result) + ? raw.result + : Array.isArray(raw.result?.points) + ? raw.result.points + : raw.points); + return (points ?? []).map((point) => ({ + id: point.id, + score: point.score ?? 0, + ...(point.payload === undefined ? {} : { payload: point.payload }), + })); +} + +function metadataFromPayload( + payload: Record | null | undefined, +): { metadata?: Metadata | undefined } { + const metadata = Object.fromEntries( + Object.entries(payload ?? {}).filter(([key]) => !key.startsWith(reservedPayloadPrefix)), + ) as Metadata; + return Object.keys(metadata).length === 0 ? {} : { metadata }; +} diff --git a/packages/vector-stores/qdrant/src/index.ts b/packages/vector-stores/qdrant/src/index.ts index e8a16b35..002af359 100644 --- a/packages/vector-stores/qdrant/src/index.ts +++ b/packages/vector-stores/qdrant/src/index.ts @@ -1,268 +1,8 @@ -import { createHash } from "node:crypto"; -import { - type EmbeddedDocument, - type EmbeddingModel, - embedText, - type VectorMetadata, -} from "@anvia/core/embeddings"; -import type { Tool } from "@anvia/core/tool"; -import { - createVectorSearchTool, - type VectorFilter, - type VectorSearchIndex, - type VectorSearchRequest, - type VectorSearchResult, - type VectorSearchToolOptions, -} from "@anvia/core/vector-store"; - -const documentIdPayloadKey = "__anvia_document_id"; -const documentPayloadKey = "__anvia_document"; -const reservedPayloadPrefix = "__anvia_"; - -export type QdrantDistance = "Cosine" | "Dot" | "Euclid"; - -type QdrantClientLike = { - getCollection(collectionName: string): Promise; - createCollection(collectionName: string, options: Record): Promise; - upsert(collectionName: string, options: Record): Promise; - search(collectionName: string, options: Record): Promise; -}; - -export type QdrantVectorStoreConnectOptions = { - client?: QdrantClientLike | undefined; - collectionName: string; - vectorSize: number; - createIfMissing?: boolean | undefined; - distance?: QdrantDistance | undefined; -}; - -export class QdrantVectorStore { - private constructor( - private readonly client: QdrantClientLike, - private readonly collectionName: string, - ) {} - - static async connect( - options: QdrantVectorStoreConnectOptions, - ): Promise> { - const client = options.client ?? (await defaultQdrantClient()); - if (options.createIfMissing === false) { - await client.getCollection(options.collectionName); - return new QdrantVectorStore(client, options.collectionName); - } - - try { - await client.getCollection(options.collectionName); - } catch { - await client.createCollection(options.collectionName, { - vectors: { - size: options.vectorSize, - distance: options.distance ?? "Cosine", - }, - }); - } - return new QdrantVectorStore(client, options.collectionName); - } - - async upsertDocuments(documents: Array>): Promise { - const points = documents.flatMap((document) => qdrantPoints(document)); - await this.client.upsert(this.collectionName, { points }); - } - - index(model: EmbeddingModel): QdrantVectorIndex { - return new QdrantVectorIndex(model, this.client, this.collectionName); - } -} - -export class QdrantVectorIndex - implements VectorSearchIndex -{ - constructor( - private readonly model: EmbeddingModel, - private readonly client: QdrantClientLike, - private readonly collectionName: string, - ) {} - - async search(request: VectorSearchRequest): Promise>> { - const queryEmbedding = await embedText(this.model, request.query); - const response = await this.client.search(this.collectionName, { - vector: queryEmbedding.vector, - limit: request.topK, - filter: filterToQdrantFilter(request.filter), - with_payload: true, - }); - return parseQueryResults(response, request.threshold); - } - - async searchIds(request: VectorSearchRequest): Promise> { - return (await this.search(request)).map(({ score, id }) => ({ score, id })); - } - - asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { - return createVectorSearchTool(this, options); - } -} - -export function filterToQdrantFilter(filter: VectorFilter | undefined): unknown { - if (filter === undefined) { - return undefined; - } - - switch (filter.type) { - case "eq": - return { must: [{ key: filter.key, match: { value: filter.value } }] }; - case "gt": - return { must: [{ key: filter.key, range: { gt: filter.value } }] }; - case "lt": - return { must: [{ key: filter.key, range: { lt: filter.value } }] }; - case "and": - return { must: filter.filters.map(filterToQdrantFilter) }; - case "or": - return { should: filter.filters.map(filterToQdrantFilter) }; - } -} - -async function defaultQdrantClient(): Promise { - const qdrant = await import("@qdrant/js-client-rest"); - return new qdrant.QdrantClient({}) as QdrantClientLike; -} - -function qdrantPoints( - document: EmbeddedDocument, -): Array<{ - id: string; - vector: number[]; - payload: Record; -}> { - if (document.embeddings.length === 0) { - throw new Error(`Document ${document.id} has no embeddings`); - } - assertNoReservedMetadata(document.metadata); - - return document.embeddings.map((embedding, index) => { - const logicalId = - document.embeddings.length === 1 ? document.id : `${document.id}#embedding:${index}`; - return { - id: pointId(logicalId), - vector: embedding.vector, - payload: { - [documentIdPayloadKey]: document.id, - [documentPayloadKey]: serializeDocument(document.document), - ...(document.metadata ?? {}), - }, - }; - }); -} - -function assertNoReservedMetadata(metadata: VectorMetadata | undefined): void { - for (const key of Object.keys(metadata ?? {})) { - if (key.startsWith(reservedPayloadPrefix)) { - throw new Error(`Metadata key ${key} is reserved for Anvia Qdrant payloads`); - } - } -} - -function pointId(id: string): string { - const hex = createHash("sha256").update(id).digest("hex").slice(0, 32); - return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice( - 16, - 20, - )}-${hex.slice(20)}`; -} - -function serializeDocument(document: unknown): string { - return typeof document === "string" ? document : JSON.stringify(document); -} - -function parseQueryResults( - response: unknown, - threshold: number | undefined, -): Array> { - const points = rawPoints(response); - const byId = new Map>(); - - for (const point of points) { - if (threshold !== undefined && point.score < threshold) { - continue; - } - - const id = String(point.payload?.[documentIdPayloadKey] ?? point.id); - const result = { - id, - score: point.score, - document: parseDocument(point.payload?.[documentPayloadKey]), - ...metadataFromPayload(point.payload), - } as VectorSearchResult; - const current = byId.get(id); - if (current === undefined || result.score > current.score) { - byId.set(id, result); - } - } - - return [...byId.values()]; -} - -function rawPoints(response: unknown): Array<{ - id: string | number; - score: number; - payload?: Record | null; -}> { - const raw = response as { - points?: Array<{ - id: string | number; - score?: number; - payload?: Record | null; - }>; - result?: - | { - points?: Array<{ - id: string | number; - score?: number; - payload?: Record | null; - }>; - } - | Array<{ id: string | number; score?: number; payload?: Record | null }>; - }; - const responseArray = Array.isArray(response) - ? (response as Array<{ - id: string | number; - score?: number; - payload?: Record | null; - }>) - : undefined; - const points = - responseArray ?? - (Array.isArray(raw.result) - ? raw.result - : Array.isArray(raw.result?.points) - ? raw.result.points - : raw.points); - return (points ?? []).map((point) => ({ - id: point.id, - score: point.score ?? 0, - ...(point.payload === undefined ? {} : { payload: point.payload }), - })); -} - -function parseDocument(document: unknown): T { - if (document === null || document === undefined) { - return "" as T; - } - if (typeof document !== "string") { - return document as T; - } - try { - return JSON.parse(document) as T; - } catch { - return document as T; - } -} - -function metadataFromPayload( - payload: Record | null | undefined, -): { metadata?: Metadata | undefined } { - const metadata = Object.fromEntries( - Object.entries(payload ?? {}).filter(([key]) => !key.startsWith(reservedPayloadPrefix)), - ) as Metadata; - return Object.keys(metadata).length === 0 ? {} : { metadata }; -} +export { filterToQdrantFilter } from "./filters.js"; +export { QdrantVectorIndex } from "./search-index.js"; +export { QdrantVectorStore } from "./store.js"; +export type { + QdrantClientLike, + QdrantDistance, + QdrantVectorStoreConnectOptions, +} from "./types.js"; diff --git a/packages/vector-stores/qdrant/src/search-index.ts b/packages/vector-stores/qdrant/src/search-index.ts new file mode 100644 index 00000000..e9bf95ba --- /dev/null +++ b/packages/vector-stores/qdrant/src/search-index.ts @@ -0,0 +1,41 @@ +import { type EmbeddingModel, embedText, type VectorMetadata } from "@anvia/core/embeddings"; +import type { Tool } from "@anvia/core/tool"; +import { + createVectorSearchTool, + type VectorSearchIndex, + type VectorSearchRequest, + type VectorSearchResult, + type VectorSearchToolOptions, +} from "@anvia/core/vector-store"; +import { filterToQdrantFilter } from "./filters.js"; +import { parseQueryResults } from "./helpers.js"; +import type { QdrantClientLike } from "./types.js"; + +export class QdrantVectorIndex + implements VectorSearchIndex +{ + constructor( + private readonly model: EmbeddingModel, + private readonly client: QdrantClientLike, + private readonly collectionName: string, + ) {} + + async search(request: VectorSearchRequest): Promise>> { + const queryEmbedding = await embedText(this.model, request.query); + const response = await this.client.search(this.collectionName, { + vector: queryEmbedding.vector, + limit: request.topK, + filter: filterToQdrantFilter(request.filter), + with_payload: true, + }); + return parseQueryResults(response, request.threshold); + } + + async searchIds(request: VectorSearchRequest): Promise> { + return (await this.search(request)).map(({ score, id }) => ({ score, id })); + } + + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown> { + return createVectorSearchTool(this, options); + } +} diff --git a/packages/vector-stores/qdrant/src/store.ts b/packages/vector-stores/qdrant/src/store.ts new file mode 100644 index 00000000..5f25785f --- /dev/null +++ b/packages/vector-stores/qdrant/src/store.ts @@ -0,0 +1,42 @@ +import type { EmbeddedDocument, EmbeddingModel, VectorMetadata } from "@anvia/core/embeddings"; +import { defaultQdrantClient, qdrantPoints } from "./helpers.js"; +import { QdrantVectorIndex } from "./search-index.js"; +import type { QdrantClientLike, QdrantVectorStoreConnectOptions } from "./types.js"; + +export class QdrantVectorStore { + private constructor( + private readonly client: QdrantClientLike, + private readonly collectionName: string, + ) {} + + static async connect( + options: QdrantVectorStoreConnectOptions, + ): Promise> { + const client = options.client ?? (await defaultQdrantClient()); + if (options.createIfMissing === false) { + await client.getCollection(options.collectionName); + return new QdrantVectorStore(client, options.collectionName); + } + + try { + await client.getCollection(options.collectionName); + } catch { + await client.createCollection(options.collectionName, { + vectors: { + size: options.vectorSize, + distance: options.distance ?? "Cosine", + }, + }); + } + return new QdrantVectorStore(client, options.collectionName); + } + + async upsertDocuments(documents: Array>): Promise { + const points = documents.flatMap((document) => qdrantPoints(document)); + await this.client.upsert(this.collectionName, { points }); + } + + index(model: EmbeddingModel): QdrantVectorIndex { + return new QdrantVectorIndex(model, this.client, this.collectionName); + } +} diff --git a/packages/vector-stores/qdrant/src/types.ts b/packages/vector-stores/qdrant/src/types.ts new file mode 100644 index 00000000..c1aa3ed3 --- /dev/null +++ b/packages/vector-stores/qdrant/src/types.ts @@ -0,0 +1,20 @@ +export const documentIdPayloadKey = "__anvia_document_id"; +export const documentPayloadKey = "__anvia_document"; +export const reservedPayloadPrefix = "__anvia_"; + +export type QdrantDistance = "Cosine" | "Dot" | "Euclid"; + +export type QdrantClientLike = { + getCollection(collectionName: string): Promise; + createCollection(collectionName: string, options: Record): Promise; + upsert(collectionName: string, options: Record): Promise; + search(collectionName: string, options: Record): Promise; +}; + +export type QdrantVectorStoreConnectOptions = { + client?: QdrantClientLike | undefined; + collectionName: string; + vectorSize: number; + createIfMissing?: boolean | undefined; + distance?: QdrantDistance | undefined; +}; From ea68748176d0d5a2574bac32ab02194259d0b9bb Mon Sep 17 00:00:00 2001 From: Indra Zulfi Date: Tue, 16 Jun 2026 15:48:32 +0700 Subject: [PATCH 3/3] docs: add Pinecone and Milvus reference docs and changeset Add reference documentation for @anvia/pinecone and @anvia/milvus, register them in the reference coverage check, and create a minor changeset for both new packages. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- .changeset/add-pinecone-milvus.md | 6 ++ apps/docs/content/docs/changelog/chroma.mdx | 7 ++ apps/docs/content/docs/changelog/core.mdx | 6 ++ .../docs/content/docs/changelog/fastembed.mdx | 7 ++ apps/docs/content/docs/changelog/index.mdx | 26 ++--- apps/docs/content/docs/changelog/logger.mdx | 7 ++ apps/docs/content/docs/changelog/meta.json | 2 + apps/docs/content/docs/changelog/milvus.mdx | 17 +++ apps/docs/content/docs/changelog/mistral.mdx | 7 ++ apps/docs/content/docs/changelog/openai.mdx | 7 ++ apps/docs/content/docs/changelog/otel.mdx | 7 ++ apps/docs/content/docs/changelog/pgvector.mdx | 7 ++ apps/docs/content/docs/changelog/pinecone.mdx | 17 +++ apps/docs/content/docs/changelog/qdrant.mdx | 7 ++ apps/docs/content/docs/changelog/sandbox.mdx | 7 ++ apps/docs/content/docs/changelog/studio.mdx | 7 ++ .../content/docs/changelog/transformers.mdx | 7 ++ .../docs/reference/integrations/chroma.mdx | 11 ++ .../docs/reference/integrations/milvus.mdx | 81 ++++++++++++++ .../docs/reference/integrations/pinecone.mdx | 102 ++++++++++++++++++ .../docs/scripts/check-reference-coverage.mjs | 2 + apps/docs/src/routeTree.gen.ts | 9 -- 22 files changed, 335 insertions(+), 21 deletions(-) create mode 100644 .changeset/add-pinecone-milvus.md create mode 100644 apps/docs/content/docs/changelog/milvus.mdx create mode 100644 apps/docs/content/docs/changelog/pinecone.mdx create mode 100644 apps/docs/content/docs/reference/integrations/milvus.mdx create mode 100644 apps/docs/content/docs/reference/integrations/pinecone.mdx diff --git a/.changeset/add-pinecone-milvus.md b/.changeset/add-pinecone-milvus.md new file mode 100644 index 00000000..4e3bb24b --- /dev/null +++ b/.changeset/add-pinecone-milvus.md @@ -0,0 +1,6 @@ +--- +"@anvia/pinecone": minor +"@anvia/milvus": minor +--- + +Add Pinecone and Milvus vector store adapters following the existing pattern (Chroma, PgVector, Qdrant). Both implement the `VectorSearchIndex` interface with full filter translation, multi-embedding support, and `asTool()` integration. diff --git a/apps/docs/content/docs/changelog/chroma.mdx b/apps/docs/content/docs/changelog/chroma.mdx index 300b9ff9..bafa18c0 100644 --- a/apps/docs/content/docs/changelog/chroma.mdx +++ b/apps/docs/content/docs/changelog/chroma.mdx @@ -9,6 +9,13 @@ ChromaDB vector store adapter for Anvia. Source: [`packages/vector-stores/chroma/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/chroma/CHANGELOG.md) +## 0.2.5 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.4 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/core.mdx b/apps/docs/content/docs/changelog/core.mdx index 921b4826..332edfca 100644 --- a/apps/docs/content/docs/changelog/core.mdx +++ b/apps/docs/content/docs/changelog/core.mdx @@ -9,6 +9,12 @@ Core runtime primitives for context-aware Anvia agents. Source: [`packages/core/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/core/CHANGELOG.md) +## 0.6.1 + +### Patch Changes + +- 2d039f6: Add ergonomic tool result message helpers and export `ToolContent` from the root entrypoint. + ## 0.6.0 ### Minor Changes diff --git a/apps/docs/content/docs/changelog/fastembed.mdx b/apps/docs/content/docs/changelog/fastembed.mdx index 856a1159..2f52c02c 100644 --- a/apps/docs/content/docs/changelog/fastembed.mdx +++ b/apps/docs/content/docs/changelog/fastembed.mdx @@ -9,6 +9,13 @@ FastEmbed embedding model adapter for Anvia. Source: [`packages/embeddings/fastembed/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/embeddings/fastembed/CHANGELOG.md) +## 0.2.5 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.4 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/index.mdx b/apps/docs/content/docs/changelog/index.mdx index d7e09ba1..870ac269 100644 --- a/apps/docs/content/docs/changelog/index.mdx +++ b/apps/docs/content/docs/changelog/index.mdx @@ -13,7 +13,7 @@ Developers should keep writing release notes with `pnpm changeset`. The docs pag | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/core` | `0.6.0` | [View changelog](/docs/changelog/core) | +| `@anvia/core` | `0.6.1` | [View changelog](/docs/changelog/core) | ## Providers @@ -21,43 +21,45 @@ Developers should keep writing release notes with `pnpm changeset`. The docs pag | --- | --- | --- | | `@anvia/anthropic` | `0.3.5` | [View changelog](/docs/changelog/anthropic) | | `@anvia/gemini` | `0.2.4` | [View changelog](/docs/changelog/gemini) | -| `@anvia/mistral` | `0.2.5` | [View changelog](/docs/changelog/mistral) | -| `@anvia/openai` | `0.3.6` | [View changelog](/docs/changelog/openai) | +| `@anvia/mistral` | `0.2.6` | [View changelog](/docs/changelog/mistral) | +| `@anvia/openai` | `0.3.7` | [View changelog](/docs/changelog/openai) | ## Embeddings | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/fastembed` | `0.2.4` | [View changelog](/docs/changelog/fastembed) | -| `@anvia/transformers` | `0.2.4` | [View changelog](/docs/changelog/transformers) | +| `@anvia/fastembed` | `0.2.5` | [View changelog](/docs/changelog/fastembed) | +| `@anvia/transformers` | `0.2.5` | [View changelog](/docs/changelog/transformers) | ## Vector Stores | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/chroma` | `0.2.4` | [View changelog](/docs/changelog/chroma) | -| `@anvia/pgvector` | `0.2.4` | [View changelog](/docs/changelog/pgvector) | -| `@anvia/qdrant` | `0.2.4` | [View changelog](/docs/changelog/qdrant) | +| `@anvia/chroma` | `0.2.5` | [View changelog](/docs/changelog/chroma) | +| `@anvia/milvus` | `0.2.5` | [View changelog](/docs/changelog/milvus) | +| `@anvia/pgvector` | `0.2.5` | [View changelog](/docs/changelog/pgvector) | +| `@anvia/pinecone` | `0.2.5` | [View changelog](/docs/changelog/pinecone) | +| `@anvia/qdrant` | `0.2.5` | [View changelog](/docs/changelog/qdrant) | ## Logger | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/logger` | `0.3.5` | [View changelog](/docs/changelog/logger) | +| `@anvia/logger` | `0.3.6` | [View changelog](/docs/changelog/logger) | ## Observability | Package | Current version | Release notes | | --- | --- | --- | | `@anvia/langfuse` | `0.2.3` | [View changelog](/docs/changelog/langfuse) | -| `@anvia/otel` | `0.2.4` | [View changelog](/docs/changelog/otel) | +| `@anvia/otel` | `0.2.5` | [View changelog](/docs/changelog/otel) | ## Tools | Package | Current version | Release notes | | --- | --- | --- | -| `@anvia/sandbox` | `0.2.1` | [View changelog](/docs/changelog/sandbox) | -| `@anvia/studio` | `0.5.10` | [View changelog](/docs/changelog/studio) | +| `@anvia/sandbox` | `0.2.2` | [View changelog](/docs/changelog/sandbox) | +| `@anvia/studio` | `0.5.11` | [View changelog](/docs/changelog/studio) | ## React diff --git a/apps/docs/content/docs/changelog/logger.mdx b/apps/docs/content/docs/changelog/logger.mdx index 09b6c155..025773e4 100644 --- a/apps/docs/content/docs/changelog/logger.mdx +++ b/apps/docs/content/docs/changelog/logger.mdx @@ -9,6 +9,13 @@ Structured logger adapters for Anvia. Source: [`packages/logger/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/logger/CHANGELOG.md) +## 0.3.6 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.3.5 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/meta.json b/apps/docs/content/docs/changelog/meta.json index 7cc6ecba..0edc4162 100644 --- a/apps/docs/content/docs/changelog/meta.json +++ b/apps/docs/content/docs/changelog/meta.json @@ -19,7 +19,9 @@ "transformers", "---Vector Stores---", "chroma", + "milvus", "pgvector", + "pinecone", "qdrant", "---Logger---", "logger", diff --git a/apps/docs/content/docs/changelog/milvus.mdx b/apps/docs/content/docs/changelog/milvus.mdx new file mode 100644 index 00000000..48c82f41 --- /dev/null +++ b/apps/docs/content/docs/changelog/milvus.mdx @@ -0,0 +1,17 @@ +--- +title: "@anvia/milvus" +description: "Release notes for @anvia/milvus." +--- + +# `@anvia/milvus` + +Milvus vector store adapter for Anvia. + +Source: [`packages/vector-stores/milvus/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/milvus/CHANGELOG.md) + +## No release notes yet + +`@anvia/milvus` is public, but `packages/vector-stores/milvus/CHANGELOG.md` does not exist yet. + +Release notes will appear here after the next Changesets version bump creates the package changelog. + diff --git a/apps/docs/content/docs/changelog/mistral.mdx b/apps/docs/content/docs/changelog/mistral.mdx index ea7a34a7..b78015ff 100644 --- a/apps/docs/content/docs/changelog/mistral.mdx +++ b/apps/docs/content/docs/changelog/mistral.mdx @@ -9,6 +9,13 @@ Mistral provider adapter for Anvia. Source: [`packages/providers/mistral/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/providers/mistral/CHANGELOG.md) +## 0.2.6 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.5 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/openai.mdx b/apps/docs/content/docs/changelog/openai.mdx index 2b3e719a..22e2c0fc 100644 --- a/apps/docs/content/docs/changelog/openai.mdx +++ b/apps/docs/content/docs/changelog/openai.mdx @@ -9,6 +9,13 @@ OpenAI provider adapter for Anvia. Source: [`packages/providers/openai/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/providers/openai/CHANGELOG.md) +## 0.3.7 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.3.6 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/otel.mdx b/apps/docs/content/docs/changelog/otel.mdx index 90b9f41c..b96975b9 100644 --- a/apps/docs/content/docs/changelog/otel.mdx +++ b/apps/docs/content/docs/changelog/otel.mdx @@ -9,6 +9,13 @@ OpenTelemetry tracing adapter for Anvia. Source: [`packages/observability/otel/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/observability/otel/CHANGELOG.md) +## 0.2.5 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.4 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/pgvector.mdx b/apps/docs/content/docs/changelog/pgvector.mdx index e0aa61a8..06f2ba6f 100644 --- a/apps/docs/content/docs/changelog/pgvector.mdx +++ b/apps/docs/content/docs/changelog/pgvector.mdx @@ -9,6 +9,13 @@ Postgres pgvector store adapter for Anvia. Source: [`packages/vector-stores/pgvector/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/pgvector/CHANGELOG.md) +## 0.2.5 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.4 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/pinecone.mdx b/apps/docs/content/docs/changelog/pinecone.mdx new file mode 100644 index 00000000..80d5f1c5 --- /dev/null +++ b/apps/docs/content/docs/changelog/pinecone.mdx @@ -0,0 +1,17 @@ +--- +title: "@anvia/pinecone" +description: "Release notes for @anvia/pinecone." +--- + +# `@anvia/pinecone` + +Pinecone vector store adapter for Anvia. + +Source: [`packages/vector-stores/pinecone/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/pinecone/CHANGELOG.md) + +## No release notes yet + +`@anvia/pinecone` is public, but `packages/vector-stores/pinecone/CHANGELOG.md` does not exist yet. + +Release notes will appear here after the next Changesets version bump creates the package changelog. + diff --git a/apps/docs/content/docs/changelog/qdrant.mdx b/apps/docs/content/docs/changelog/qdrant.mdx index 298df780..8cc189dd 100644 --- a/apps/docs/content/docs/changelog/qdrant.mdx +++ b/apps/docs/content/docs/changelog/qdrant.mdx @@ -9,6 +9,13 @@ Qdrant vector store adapter for Anvia. Source: [`packages/vector-stores/qdrant/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/vector-stores/qdrant/CHANGELOG.md) +## 0.2.5 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.4 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/sandbox.mdx b/apps/docs/content/docs/changelog/sandbox.mdx index 090604c1..a39dbd51 100644 --- a/apps/docs/content/docs/changelog/sandbox.mdx +++ b/apps/docs/content/docs/changelog/sandbox.mdx @@ -9,6 +9,13 @@ Sandboxed workspace tools for Anvia agents. Source: [`packages/tools/sandbox/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/tools/sandbox/CHANGELOG.md) +## 0.2.2 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.1 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/studio.mdx b/apps/docs/content/docs/changelog/studio.mdx index 355f89d5..029b79ee 100644 --- a/apps/docs/content/docs/changelog/studio.mdx +++ b/apps/docs/content/docs/changelog/studio.mdx @@ -9,6 +9,13 @@ Studio UI and HTTP runtime for Anvia agents. Source: [`packages/tools/studio/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/tools/studio/CHANGELOG.md) +## 0.5.11 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.5.10 ### Patch Changes diff --git a/apps/docs/content/docs/changelog/transformers.mdx b/apps/docs/content/docs/changelog/transformers.mdx index 253f1161..d0579ab2 100644 --- a/apps/docs/content/docs/changelog/transformers.mdx +++ b/apps/docs/content/docs/changelog/transformers.mdx @@ -9,6 +9,13 @@ Transformers.js embedding model adapter for Anvia. Source: [`packages/embeddings/transformers/CHANGELOG.md`](https://github.com/anvia-hq/anvia/blob/main/packages/embeddings/transformers/CHANGELOG.md) +## 0.2.5 + +### Patch Changes + +- Updated dependencies [2d039f6] + - @anvia/core@0.6.1 + ## 0.2.4 ### Patch Changes diff --git a/apps/docs/content/docs/reference/integrations/chroma.mdx b/apps/docs/content/docs/reference/integrations/chroma.mdx index 9d015f71..310cf69e 100644 --- a/apps/docs/content/docs/reference/integrations/chroma.mdx +++ b/apps/docs/content/docs/reference/integrations/chroma.mdx @@ -71,3 +71,14 @@ Purpose: convert Anvia vector filters to Chroma `where` filters. Return behavior: returns `undefined` when no filter is supplied. Notable errors: none directly. + +## ChromaCollectionLike + +```ts +type ChromaCollectionLike = { + upsert(options: Record): Promise; + query(options: Record): Promise; +}; +``` + +Purpose: duck-typed interface for a Chroma collection. diff --git a/apps/docs/content/docs/reference/integrations/milvus.mdx b/apps/docs/content/docs/reference/integrations/milvus.mdx new file mode 100644 index 00000000..ebba33da --- /dev/null +++ b/apps/docs/content/docs/reference/integrations/milvus.mdx @@ -0,0 +1,81 @@ +--- +title: Milvus +description: Public exports from @anvia/milvus. +--- + +Import from `@anvia/milvus`. + +## MilvusVectorStoreConnectOptions + +```ts +type MilvusVectorStoreConnectOptions = { + client?: MilvusClientLike; + collectionName: string; + vectorSize: number; + createIfMissing?: boolean; + metric?: "COSINE" | "L2" | "IP"; +}; +``` + +Purpose: connection options for a Milvus collection. + +Return behavior: consumed by `MilvusVectorStore.connect(...)`. + +Notable errors: collection lookup fails when `createIfMissing` is `false` and the collection does not exist. + +Design note: `connect(...)` optionally creates a collection with HNSW index, then loads it into memory. This keeps constructors synchronous and side-effect free while ensuring the collection exists before ingestion or search. + +## MilvusVectorStore + +```ts +class MilvusVectorStore { + static connect( + options: MilvusVectorStoreConnectOptions, + ): Promise>; + upsertDocuments(documents: Array>): Promise; + index(model: EmbeddingModel): MilvusVectorIndex; +} +``` + +Purpose: Milvus-backed document storage. + +Return behavior: `connect(...)` resolves a store; `index(...)` binds it to an embedding model. + +Notable errors: connection and insert calls reject on Milvus errors; `upsertDocuments(...)` throws when a document has no embeddings. + +## MilvusVectorIndex + +```ts +class MilvusVectorIndex + implements VectorSearchIndex { + search(request: VectorSearchRequest): Promise>>; + searchIds(request: VectorSearchRequest): Promise>; + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown>; +} +``` + +Purpose: query-time Milvus search adapter. + +Return behavior: embeds the query, searches Milvus with boolean expression filters, deduplicates multi-embedding document IDs, and returns normalized results. + +Notable errors: embedding or Milvus search failures reject. + +## filterToMilvusExpr + +```ts +function filterToMilvusExpr(filter: VectorFilter | undefined): string | undefined; +``` + +Purpose: convert Anvia vector filters to Milvus boolean expression strings. + +Return behavior: returns `undefined` when no filter is supplied. + +Notable errors: none directly. + +## MilvusMetric + +```ts +type MilvusMetric = "COSINE" | "L2" | "IP"; +``` + +Purpose: distance metric for a Milvus collection. diff --git a/apps/docs/content/docs/reference/integrations/pinecone.mdx b/apps/docs/content/docs/reference/integrations/pinecone.mdx new file mode 100644 index 00000000..ba9a0e26 --- /dev/null +++ b/apps/docs/content/docs/reference/integrations/pinecone.mdx @@ -0,0 +1,102 @@ +--- +title: Pinecone +description: Public exports from @anvia/pinecone. +--- + +Import from `@anvia/pinecone`. + +## PineconeVectorStoreConnectOptions + +```ts +type PineconeVectorStoreConnectOptions = { + client?: PineconeClientLike; + indexName: string; + namespace?: string; + createIfMissing?: boolean; + metric?: "cosine" | "euclidean" | "dotproduct"; +}; +``` + +Purpose: connection options for a Pinecone index. + +Return behavior: consumed by `PineconeVectorStore.connect(...)`. + +Notable errors: index lookup fails when `createIfMissing` is `false` and the index does not exist. + +Design note: `connect(...)` optionally creates a serverless index, then resolves a namespace handle. This keeps constructors synchronous and side-effect free while ensuring the index exists before ingestion or search. + +## PineconeVectorStore + +```ts +class PineconeVectorStore { + static connect( + options: PineconeVectorStoreConnectOptions, + ): Promise>; + upsertDocuments(documents: Array>): Promise; + index(model: EmbeddingModel): PineconeVectorIndex; +} +``` + +Purpose: Pinecone-backed document storage. + +Return behavior: `connect(...)` resolves a store; `index(...)` binds it to an embedding model. + +Notable errors: connection and upsert calls reject on Pinecone errors; `upsertDocuments(...)` throws when a document has no embeddings. + +## PineconeVectorIndex + +```ts +class PineconeVectorIndex + implements VectorSearchIndex { + search(request: VectorSearchRequest): Promise>>; + searchIds(request: VectorSearchRequest): Promise>; + asTool(options: VectorSearchToolOptions): Tool<{ query: string; topK?: number }, unknown>; +} +``` + +Purpose: query-time Pinecone search adapter. + +Return behavior: embeds the query, queries Pinecone with metadata, deduplicates multi-embedding document IDs, and returns normalized results. + +Notable errors: embedding or Pinecone query failures reject. + +## filterToPineconeFilter + +```ts +function filterToPineconeFilter(filter: VectorFilter | undefined): unknown; +``` + +Purpose: convert Anvia vector filters to Pinecone filter objects. + +Return behavior: returns `undefined` when no filter is supplied. + +Notable errors: none directly. + +## PineconeMetric + +```ts +type PineconeMetric = "cosine" | "euclidean" | "dotproduct"; +``` + +Purpose: distance metric for a Pinecone index. + +## PineconeIndexLike + +```ts +type PineconeIndexLike = { + namespace(namespace: string): PineconeNamespaceLike; +}; +``` + +Purpose: duck-typed interface for a Pinecone index handle. + +## PineconeNamespaceLike + +```ts +type PineconeNamespaceLike = { + upsert(vectors: Array>): Promise; + query(options: Record): Promise; +}; +``` + +Purpose: duck-typed interface for a Pinecone namespace. diff --git a/apps/docs/scripts/check-reference-coverage.mjs b/apps/docs/scripts/check-reference-coverage.mjs index df63c457..b9ea08da 100644 --- a/apps/docs/scripts/check-reference-coverage.mjs +++ b/apps/docs/scripts/check-reference-coverage.mjs @@ -20,6 +20,8 @@ const packageDocs = new Map([ ["@anvia/chroma", "apps/docs/content/docs/reference/integrations/chroma.mdx"], ["@anvia/pgvector", "apps/docs/content/docs/reference/integrations/pgvector.mdx"], ["@anvia/qdrant", "apps/docs/content/docs/reference/integrations/qdrant.mdx"], + ["@anvia/pinecone", "apps/docs/content/docs/reference/integrations/pinecone.mdx"], + ["@anvia/milvus", "apps/docs/content/docs/reference/integrations/milvus.mdx"], ["@anvia/logger", "apps/docs/content/docs/reference/integrations/logger.mdx"], ["@anvia/langfuse", "apps/docs/content/docs/reference/integrations/langfuse.mdx"], ["@anvia/otel", "apps/docs/content/docs/reference/integrations/otel.mdx"], diff --git a/apps/docs/src/routeTree.gen.ts b/apps/docs/src/routeTree.gen.ts index 9080bbc1..26a371fa 100644 --- a/apps/docs/src/routeTree.gen.ts +++ b/apps/docs/src/routeTree.gen.ts @@ -208,12 +208,3 @@ const rootRouteChildren: RootRouteChildren = { export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() - -import type { getRouter } from './router.tsx' -import type { createStart } from '@tanstack/react-start' -declare module '@tanstack/react-start' { - interface Register { - ssr: true - router: Awaited> - } -}