From e72ae96ad4a569b2f0e7f7a01f3a6bf802e1ad78 Mon Sep 17 00:00:00 2001 From: Bartok Date: Wed, 8 Jul 2026 08:55:06 -0600 Subject: [PATCH] feat(ts-sdk): add Milvus vector store provider (closes #5774) (#5889) Co-authored-by: kartik-mem0 --- docs/components/vectordbs/dbs/milvus.mdx | 50 +- docs/components/vectordbs/overview.mdx | 2 +- docs/open-source/configuration.mdx | 2 +- mem0-ts/package.json | 6 +- mem0-ts/pnpm-lock.yaml | 724 +++++++++++++++++++- mem0-ts/src/oss/src/index.ts | 1 + mem0-ts/src/oss/src/tests/milvus.test.ts | 517 ++++++++++++++ mem0-ts/src/oss/src/utils/factory.ts | 3 + mem0-ts/src/oss/src/vector_stores/milvus.ts | 510 ++++++++++++++ mem0-ts/tsup.config.ts | 1 + 10 files changed, 1803 insertions(+), 13 deletions(-) create mode 100644 mem0-ts/src/oss/src/tests/milvus.test.ts create mode 100644 mem0-ts/src/oss/src/vector_stores/milvus.ts diff --git a/docs/components/vectordbs/dbs/milvus.mdx b/docs/components/vectordbs/dbs/milvus.mdx index 796095f65..cb5f55fa2 100644 --- a/docs/components/vectordbs/dbs/milvus.mdx +++ b/docs/components/vectordbs/dbs/milvus.mdx @@ -6,7 +6,14 @@ description: "Use Milvus as an open-source vector database in Mem0, scalable fro ### Usage -```python +The TypeScript SDK loads the Milvus client lazily. Install it alongside `mem0ai` when you use this provider: + +```bash +npm install @zilliz/milvus2-sdk-node +``` + + +```python Python import os from mem0 import Memory @@ -33,10 +40,39 @@ messages = [ m.add(messages, user_id="alice", metadata={"category": "movies"}) ``` +```typescript TypeScript +import { Memory } from 'mem0ai/oss'; + +const config = { + vectorStore: { + provider: 'milvus', + config: { + collectionName: 'test', + embeddingModelDims: 1536, + url: 'http://localhost:19530', + token: '8e4b8ca8cf2c67', + dbName: 'my_database', + }, + }, +}; + +const memory = new Memory(config); +const messages = [ + { role: "user", content: "I'm planning to watch a movie tonight. Any recommendations?" }, + { role: "assistant", content: "How about thriller movies? They can be quite engaging." }, + { role: "user", content: "I'm not a big fan of thriller movies but I love sci-fi movies." }, + { role: "assistant", content: "Got it! I'll avoid thriller recommendations and suggest sci-fi movies in the future." }, +]; +await memory.add(messages, { userId: "alice", metadata: { category: "movies" } }); +``` + + ### Config Here are the parameters available for configuring Milvus: + + | Parameter | Description | Default Value | | --- | --- | --- | | `url` | Full URL/Uri for Milvus/Zilliz server | `http://localhost:19530` | @@ -45,3 +81,15 @@ Here are the parameters available for configuring Milvus: | `embedding_model_dims` | Dimensions of the embedding model | `1536` | | `metric_type` | Metric type for similarity search | `L2` | | `db_name` | Name of the database | `""` | + + +| Parameter | Description | Default Value | +| --- | --- | --- | +| `url` | Full URL/Uri for Milvus/Zilliz server | `http://localhost:19530` | +| `token` | Token for Zilliz Cloud (optional for a local setup) | `undefined` | +| `collectionName` | The name of the collection | `mem0` | +| `embeddingModelDims` | Dimensions of the embedding model | `1536` | +| `metricType` | Metric type for similarity search (`L2`, `IP`, `COSINE`, `HAMMING`, `JACCARD`) | `L2` | +| `dbName` | Name of the database | `undefined` | + + diff --git a/docs/components/vectordbs/overview.mdx b/docs/components/vectordbs/overview.mdx index cc00af3ef..b71568453 100644 --- a/docs/components/vectordbs/overview.mdx +++ b/docs/components/vectordbs/overview.mdx @@ -10,7 +10,7 @@ Mem0 includes built-in support for various popular databases. Memory can utilize See the list of supported vector databases below. - The following vector databases are supported in the Python implementation. The TypeScript implementation currently supports Qdrant, Redis, PGVector, Supabase, LangChain, Azure AI Search, Vectorize, Amazon S3 Vectors, and an in-memory store. + The following vector databases are supported in the Python implementation. The TypeScript implementation currently supports Qdrant, Redis, PGVector, Supabase, LangChain, Azure AI Search, Vectorize, Amazon S3 Vectors, Milvus, and an in-memory store. diff --git a/docs/open-source/configuration.mdx b/docs/open-source/configuration.mdx index acffe1f9a..38bd074b8 100644 --- a/docs/open-source/configuration.mdx +++ b/docs/open-source/configuration.mdx @@ -121,7 +121,7 @@ Change the `provider` string to switch backends. The most common options: | --- | --- | --- | | LLM | `openai`, `anthropic`, `gemini`, `groq`, `ollama`, `aws_bedrock`, `azure_openai`, `litellm` | `openai`, `anthropic`, `gemini`, `groq`, `ollama`, `azure_openai`, `mistral`, `deepseek` | | Embedder | `openai`, `gemini`, `azure_openai`, `ollama`, `huggingface`, `vertexai`, `aws_bedrock` | `openai`, `gemini`, `azure_openai`, `ollama` | -| Vector store | `qdrant`, `pgvector`, `chroma`, `pinecone`, `redis`, `weaviate`, `milvus`, `elasticsearch` | `memory`, `qdrant`, `pgvector`, `redis`, `supabase`, `azure-ai-search`, `vectorize` | +| Vector store | `qdrant`, `pgvector`, `chroma`, `pinecone`, `redis`, `weaviate`, `milvus`, `elasticsearch` | `memory`, `qdrant`, `pgvector`, `redis`, `supabase`, `azure-ai-search`, `vectorize`, `milvus` | See the full catalog in Components. diff --git a/mem0-ts/package.json b/mem0-ts/package.json index 461bb7f26..0362feea8 100644 --- a/mem0-ts/package.json +++ b/mem0-ts/package.json @@ -138,11 +138,15 @@ "iovalkey": "^0.3.3", "compromise": "^14.0.0", "natural": "^8.0.1", - "mysql2": "^3.0.0" + "mysql2": "^3.0.0", + "@zilliz/milvus2-sdk-node": "^2.4.0 || ^3.0.0" }, "peerDependenciesMeta": { "mysql2": { "optional": true + }, + "@zilliz/milvus2-sdk-node": { + "optional": true } }, "engines": { diff --git a/mem0-ts/pnpm-lock.yaml b/mem0-ts/pnpm-lock.yaml index 8df3854b1..f8d41837d 100644 --- a/mem0-ts/pnpm-lock.yaml +++ b/mem0-ts/pnpm-lock.yaml @@ -57,7 +57,7 @@ importers: version: 1.52.0 '@langchain/core': specifier: ^1.1.47 - version: 1.1.48(@opentelemetry/api@1.9.1)(openai@4.104.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0) + version: 1.1.48(@opentelemetry/api@1.9.1)(openai@4.104.0(ws@5.2.5)(zod@3.25.76))(ws@5.2.5) '@mistralai/mistralai': specifier: ^1.5.2 version: 1.15.1 @@ -85,6 +85,9 @@ importers: '@upstash/vector': specifier: ^1.2.3 version: 1.2.3 + '@zilliz/milvus2-sdk-node': + specifier: ^2.4.0 || ^3.0.0 + version: 3.0.3 axios: specifier: ^1.16.0 version: 1.17.0 @@ -123,7 +126,7 @@ importers: version: 0.5.18 openai: specifier: ^4.93.0 - version: 4.104.0(ws@8.21.0)(zod@3.25.76) + version: 4.104.0(ws@5.2.5)(zod@3.25.76) pg: specifier: 8.11.3 version: 8.11.3 @@ -223,6 +226,14 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + '@aws-sdk/checksums@3.1000.13': + resolution: {integrity: sha512-USI1N1d+NbCcfjeiYLKIl94PSYykl5IIjj4X3WqdL33Ln5qktA6sjXYct0o/gG2Cte7cuzbXKkDBkzUA4VPHrg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-s3@3.1080.0': + resolution: {integrity: sha512-erKuxbwhYLKs0ZviBeTDvXxC0E4AtugHXLbt5kFk8u9/rQMglh/QJNK5f9KuLjlMrbFSJgkBmjNSY5O03YoK4A==} + engines: {node: '>=20.0.0'} + '@aws-sdk/client-s3vectors@3.967.0': resolution: {integrity: sha512-DDmq8Jm/4IwliVEYVUbTub0RhcYJXVexRklhsIOdIWM42zf4ZMWPeeNvHJBSI+DMNbpqRN57TLCfH5tgiPdyVQ==} engines: {node: '>=18.0.0'} @@ -235,38 +246,74 @@ packages: resolution: {integrity: sha512-sJmuP7GrVmlbO6DpXkuf9Mbn6jGNNvy6PLawvaxVF150c8bpNk3w39rerRls6q1dot1dBFV2D29hBXMY1agNMg==} engines: {node: '>=18.0.0'} + '@aws-sdk/core@3.974.28': + resolution: {integrity: sha512-4/1DtLwgLqzIg2uFzkFaFjMQHhhhwHIZN4PfziIVqXYX7koO78omuchQlLHyzQBw80l255dtmTA/J4W5yhb7zw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-env@3.967.0': resolution: {integrity: sha512-+XWw0+f/txeMbEVRtTFZhgSw1ymH1ffaVKkdMBSnw48rfSohJElKmitCqdihagRTZpzh7m8qI6tIQ5t3OUqugw==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-env@3.972.54': + resolution: {integrity: sha512-F4WQCG8GULIt+XrMHsqUM9dZc0eTwZM3HUWByOjIKOwBqJSzUxX8CFAtbgMvWfCua52FS1oi5FXarH3+khFq8A==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-http@3.967.0': resolution: {integrity: sha512-0/GIAEv5pY5htg6IBMuYccBgzz3oS2DqHjHi396ziTrwlhbrCNX96AbNhQhzAx3LBZUk13sPfeapjyQ7G57Ekg==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-http@3.972.56': + resolution: {integrity: sha512-PiwHgEK2srdfi/lFveyQ+3w/qikyh6MKvuZAdlMC+dwQi4K5iVBr32oh7cugnYw+jA8iGtnQMhCGvLm/mqplmw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-ini@3.967.0': resolution: {integrity: sha512-U8dMpaM6Qf6+2Qvp1uG6OcWv1RlrZW7tQkpmzEVWH8HZTGrVHIXXju64NMtIOr7yOnNwd0CKcytuD1QG+phCwQ==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-ini@3.972.61': + resolution: {integrity: sha512-9CZWMjhBlgfKlqJk40R7kvMOLEIoOr3HJ1J/ELjAH9H5LzR4bCxLujPVM/1PKAEjzSZKZCxWOalm7JUGZbhQqw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-login@3.967.0': resolution: {integrity: sha512-kbvZsZL6CBlfnb71zuJdJmBUFZN5utNrcziZr/DZ2olEOkA9vlmizE8i9BUIbmS7ptjgvRnmcY1A966yfhiblw==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-login@3.972.60': + resolution: {integrity: sha512-Ak6OOrCbXvACyxLFIP1mcS+JTLS9ZpW1ZqyBtqu6axvdpsbG1gVNhUlAfQq8TK/gar/h2w35LrzlQU0PcUzJpw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-node@3.967.0': resolution: {integrity: sha512-WuNbHs9rfKKSVok4+OBrZf0AHfzDgFYYMxN2G/q6ZfUmY4QmiPyxV5HkNFh1rqDxS9VV6kAZPo0EBmry10idSg==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-node@3.972.63': + resolution: {integrity: sha512-YmgWtTPZDStyT74ApSHpApD3r7W9znsc+WEZjW0vceom+NAxRx9/F3TyukOKix8kJkPaa49aIREQJdpGeLDiEw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-process@3.967.0': resolution: {integrity: sha512-sNCY5JDV0whsfsZ6c2+6eUwH33H7UhKbqvCPbEYlIIa8wkGjCtCyFI3zZIJHVcMKJJ3117vSUFHEkNA7g+8rtw==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-process@3.972.54': + resolution: {integrity: sha512-UNmUjtTnp3wH3YTZcctN7yK17S2AdaJpiNIdIf0l70hHOdGuDfdMxk62P5rt64GwGm6qkFOYG0js/cU6cdZDdg==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-sso@3.967.0': resolution: {integrity: sha512-0K6kITKNytFjk1UYabYUsTThgU6TQkyW6Wmt8S5zd1A/up7NSQGpp58Rpg9GIf4amQDQwb+p9FGG7emmV8FEeA==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-sso@3.972.60': + resolution: {integrity: sha512-zH8SvJkTRw1Kb7GARjGPjzkQPfL3jDi/OxK32I5SQq7bgwgFHJZaoDEwkEvFlfNfpByM6n4Rs20Y1mLyOdb6ag==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-web-identity@3.967.0': resolution: {integrity: sha512-Vkr7S2ec7q/v8i/MzkHcBEdqqfWz3lyb8FDjb+NjslEwdxC3f6XwADRZzWwV1pChfx6SbsvJXKfkcF/pKAelhA==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-web-identity@3.972.60': + resolution: {integrity: sha512-q2rJSQ/AMjemUS18OtQqczqSo2R6VjOCLmLVmLVcdrP5/AKoj632lGCrMUWQ6EgnaLMEHbq0UO4mWh/u6gjPhA==} + engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-host-header@3.965.0': resolution: {integrity: sha512-SfpSYqoPOAmdb3DBsnNsZ0vix+1VAtkUkzXM79JL3R5IfacpyKE2zytOgVAQx/FjhhlpSTwuXd+LRhUEVb3MaA==} engines: {node: '>=18.0.0'} @@ -279,6 +326,10 @@ packages: resolution: {integrity: sha512-6dvD+18Ni14KCRu+tfEoNxq1sIGVp9tvoZDZ7aMvpnA7mDXuRLrOjRQ/TAZqXwr9ENKVGyxcPl0cRK8jk1YWjA==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-sdk-s3@3.972.59': + resolution: {integrity: sha512-KCf/UjbnzV3QIXR1C2MeE9eRUG8EUXUf0V4y65hf2bKWQXzol5f3I8wOhE6OBdEYohn8zHfnC/cyy5+s7/HwLw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-user-agent@3.967.0': resolution: {integrity: sha512-2qzJzZj5u+cZiG7kz3XJPaTH4ssUY/aet1kwJsUTFKrWeHUf7mZZkDFfkXP5cOffgiOyR5ZkrmJoLKAde9hshg==} engines: {node: '>=18.0.0'} @@ -287,10 +338,22 @@ packages: resolution: {integrity: sha512-PYa7V8w0gaNux6Sz/Z7zrHmPloEE+EKpRxQIOG/D0askTr5Yd4oO2KGgcInf65uHK3f0Z9U4CTUGHZvQvABypA==} engines: {node: '>=18.0.0'} + '@aws-sdk/nested-clients@3.997.28': + resolution: {integrity: sha512-1oG/mM3jmE2M3kad2zWJS6IKIY8hjRV4l5kAgg+xTQdLMTtehhcSucL/y4WqQpcHmQwi6+gRK8E46GYl1NBW9Q==} + engines: {node: '>=20.0.0'} + '@aws-sdk/region-config-resolver@3.965.0': resolution: {integrity: sha512-RoMhu9ly2B0coxn8ctXosPP2WmDD0MkQlZGLjoYHQUOCBmty5qmCxOqBmBDa6wbWbB8xKtMQ/4VXloQOgzjHXg==} engines: {node: '>=18.0.0'} + '@aws-sdk/signature-v4-multi-region@3.996.38': + resolution: {integrity: sha512-C379Sk+MiFZCfWZphKlMyLHKxV22OjoGM5KJjj5IJNJcOCWL4IGIpnEGzv1FQiRwhYXfq55SJMfxlqPE08JJ9g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1080.0': + resolution: {integrity: sha512-8PufAQvncWXvdZUvODbuyXa8l3aszefEzwSMBUcgheNbZOmJMcNn388Ebt/piVrUHyxKN1MlxnR2OonzTyZaGw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/token-providers@3.967.0': resolution: {integrity: sha512-Qnd/nJ0CgeUa7zQczgmdQm0vYUF7pD1G0C+dR1T7huHQHRIsgCWIsCV9wNKzOFluqtcr6YAeuTwvY0+l8XWxnA==} engines: {node: '>=18.0.0'} @@ -303,6 +366,10 @@ packages: resolution: {integrity: sha512-pEHZqRkAlHfnfAU9tK+WpKv/gBNjGJrHMgA3A0iYRGyswBS2t0pfez+lWlwktb3Bqa0ovh7w/QJTFwp3fDxLNg==} engines: {node: '>=20.0.0'} + '@aws-sdk/types@3.973.15': + resolution: {integrity: sha512-IULn8uBV/SMtmOIANsm4WHXIOtVPBWfOWs3WGL0j/sI+KhaYehvOw0ET+9urnn8MBpiijuU/0JOpuwKOE451PQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/util-endpoints@3.965.0': resolution: {integrity: sha512-WqSCB0XIsGUwZWvrYkuoofi2vzoVHqyeJ2kN+WyoOsxPLTiQSBIoqm/01R/qJvoxwK/gOOF7su9i84Vw2NQQpQ==} engines: {node: '>=18.0.0'} @@ -327,10 +394,18 @@ packages: resolution: {integrity: sha512-Tcod25/BTupraQwtb+Q+GX8bmEZfxIFjjJ/AvkhUZsZlkPeVluzq1uu3Oeqf145DCdMjzLIN6vab5MrykbDP+g==} engines: {node: '>=18.0.0'} + '@aws-sdk/xml-builder@3.972.33': + resolution: {integrity: sha512-ezbwz9WpuLctm6o7P2t2naDhVVPI5jFGrVefVybhcKGjU57VIyT46pQVO0RI2RYkUdhdj2Z9uSIlAzGZE9NW9A==} + 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'} + '@aws/lambda-invoke-store@0.3.0': + resolution: {integrity: sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==} + engines: {node: '>=18.0.0'} + '@azure/abort-controller@2.1.2': resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==} engines: {node: '>=18.0.0'} @@ -561,10 +636,17 @@ packages: '@cloudflare/workers-types@4.20260611.1': resolution: {integrity: sha512-DLiz8Ol1OIWLigJ+dGvuQ5Nm66D/CHNPasl8YnPiz6fGo10ggYSIVuEDMlFk6oho+piAHstNmZMl088w8xqW6g==} + '@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==} + '@elastic/elasticsearch@9.4.2': resolution: {integrity: sha512-H9myMlLUeotkZhZ4ppinoMGDFxmW3lY8/s+4TIk1vFHyCvWU1Ej4T7azX5buCzemyFApgN0ywnEuvOtpel2VZg==} engines: {node: '>=20'} @@ -920,6 +1002,9 @@ packages: resolution: {integrity: sha512-icc5xCzndZfhuJMy5oqk5AvloWquR7jtae74qzpkKkhGp8BivK+oCcEXgGnjCdTfp8hA44l+w8gE8yYJbocJJw==} engines: {node: '>=14'} + '@petamoriken/float16@3.9.3': + resolution: {integrity: sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g==} + '@pinecone-database/pinecone@8.0.0': resolution: {integrity: sha512-dItFqLdis2Pd5lC67aKn8HhvXajzlOz4+0RyK1CRcZMdSwG8YxUCBtH4yXPC8/6uLxfr2dvMWnRFuKgtPwwajQ==} engines: {node: '>=20.0.0'} @@ -1158,6 +1243,10 @@ packages: cpu: [x64] os: [win32] + '@shanghaikid/parquetjs@1.8.8': + resolution: {integrity: sha512-uaYXx4yP4cipVU8EZR4OZohiklsazyAcIXtfNEEYBcsMlnz5w4gEvv9vw+cP3yZBv7d1+OQYKfCfKI7oK0iUZQ==} + engines: {node: '>=18.18.2'} + '@sinclair/typebox@0.27.10': resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} @@ -1175,14 +1264,26 @@ packages: resolution: {integrity: sha512-mLUktFAn+Pa2agl1J7VgtYNFWCX8/b4GMJSK1hCu4YCvtBfM6F8Os3EP4ry+DFFlXOf3wyvlgXhuUdFoy52D3g==} engines: {node: '>=18.0.0'} + '@smithy/core@3.29.1': + resolution: {integrity: sha512-qoiY4nrk5OCu1+eIR1VB8l5DmON/oKiqrd5zZFAhXJXjJlLWQusKEW/SkBDAtGDcPaz86m9kfcE1lngU0GlM6A==} + engines: {node: '>=18.0.0'} + '@smithy/credential-provider-imds@4.4.2': resolution: {integrity: sha512-18UMDMyrAbDcpmL1gLUA7ww0fRTcdCrSjSJOi2Sbld+tVjwD/pW+OAwjlScFLR7vvBnhZrIPQ7kVuTf1mnJLug==} engines: {node: '>=18.0.0'} + '@smithy/credential-provider-imds@4.4.6': + resolution: {integrity: sha512-B2WQ/PV/H6Jeg3lrIq6bKUfa6Hy01mtK7CGs6lhjzHA6k4aagldH6T6eEjnzKl4HI0cJnAsxfJ19pgb5PV+CVQ==} + engines: {node: '>=18.0.0'} + '@smithy/fetch-http-handler@5.5.2': resolution: {integrity: sha512-Ei/UK/QMhq0rKaMqGPlOAkE2yS9DZeYmZdk1RAKc3vp3zxgleZHZyBLlZv8yLsxljX4svCRuMTD6u3LLIcU4Bg==} engines: {node: '>=18.0.0'} + '@smithy/fetch-http-handler@5.6.3': + resolution: {integrity: sha512-CwCc/7SMTj45y97MUnDTbTaxvtAsiNNRm81z3abROIuMbMsC2Iy5EKfkkVdsKrz8WExQAAMx1EJapq+9j4fFTQ==} + engines: {node: '>=18.0.0'} + '@smithy/hash-node@4.4.2': resolution: {integrity: sha512-M/8PUgwzekn0GcMBjcrTXV4PGFIe1AKuMuHjkHNBfofCPQ9+9Nr35lCvRfQK9BwYHQM+LKiKj8GY0pXSwYm6qQ==} engines: {node: '>=18.0.0'} @@ -1223,6 +1324,10 @@ packages: resolution: {integrity: sha512-wfl1uwrAqMH9/pi4kqBo5LBcFwrJLxuDLqL7p7qNcJIFcyZDUc6pzhYk4CYv+DP7fIUpQCZumwNnkhPKS52osQ==} engines: {node: '>=18.0.0'} + '@smithy/node-http-handler@4.9.3': + resolution: {integrity: sha512-qZTa4gQFUo8RM02rk6q5UVTDLNrQ1oS20LsepBzqq1QBVc/EHJ03OOUADcqMZiXHArW+Y7+OGY0BpdTwZRq/Yg==} + engines: {node: '>=18.0.0'} + '@smithy/property-provider@4.4.2': resolution: {integrity: sha512-V74/UQ+fPPIqZHzeXIUd0H+k/MF+WpeO3I+5wrMCMgsllLt6uqSNbWnLhJs6pMeXZGu6ry+g1bpIdcHNeL+Oww==} engines: {node: '>=18.0.0'} @@ -1239,6 +1344,10 @@ packages: resolution: {integrity: sha512-7xHpmPY4rt0IOmeAA8EfjgEH8isT+587TCdy9H6a7d4OMi5CQ0oEHhWllunvPu4j4Cq0vTFwdxXN/kABWPjdyA==} engines: {node: '>=18.0.0'} + '@smithy/signature-v4@5.6.2': + resolution: {integrity: sha512-QgHflghMoPxCJ9axiCVh8KZfbC9fuP6vkXXyK//E3cq7nLaSSyyLj0GAoqVWezYeDQmXIZhmlRvLE16jsqDK6g==} + engines: {node: '>=18.0.0'} + '@smithy/smithy-client@4.14.2': resolution: {integrity: sha512-H3fp0F/IhG8a6qnoWHeGTH1qDPRR9VVZjcxM9NMXPB6nqzaqcoYIX8D4FFI1Ap2siGr+yvYgKqHC76+afBgF1A==} engines: {node: '>=18.0.0'} @@ -1247,6 +1356,10 @@ packages: resolution: {integrity: sha512-Z5TAOxygoFvybJV3igo5SloFflSokHx2hu1eFA+DxDTcn+FtKxUSui+rbTRG1pAafMA888Z3MVvCWUuvCrTXjg==} engines: {node: '>=18.0.0'} + '@smithy/types@4.15.1': + resolution: {integrity: sha512-x3L0XSACF6UYzKpa9biqiRMgvH5+wnFFew9Tm/grFYqgaupPwx/+ojDPpPJM8dZON3S9tjz5U+PQYsCBd1Mw5Q==} + engines: {node: '>=18.0.0'} + '@smithy/url-parser@4.4.2': resolution: {integrity: sha512-vx7FUYJMRfQqdqPRLYwpisOuYU6e1PaabcPUTEc7xG6X0VEr4B6PnjZloHz67qva0pQ9/eNs3xPxvQ6w5gRGAw==} engines: {node: '>=18.0.0'} @@ -1299,6 +1412,9 @@ packages: resolution: {integrity: sha512-N5CpfaL+/LPQU9PFdOT55ayUo5T0QypG4Almzd1/efJvoDypuT1shkgJk1+hhg/02scYluW6Q2JGnSHIPwCEGQ==} engines: {node: '>=18.0.0'} + '@so-ric/colorspace@1.1.6': + resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -1380,24 +1496,39 @@ packages: '@types/node-fetch@2.6.13': resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} + '@types/node-int64@0.4.32': + resolution: {integrity: sha512-xf/JsSlnXQ+mzvc0IpXemcrO4BrCfpgNpMco+GLcXkFk01k/gW9lGJu+Vof0ZSvHK6DsHJDPSbjFPs36QkWXqw==} + '@types/node@18.19.130': resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} '@types/node@22.19.21': resolution: {integrity: sha512-VMeFBSCKQKmm2swI2kW51SFusDqekC6q9trBCvJ/JliDchFSuoYYKN7yVNjPthP1HKZcx3U1gI/wTcEBjEFKTA==} + '@types/node@25.9.4': + resolution: {integrity: sha512-dszCsrKb5U7ZsVZBWiHFklTloVl0mSEnWH/iZXfZUlI4rzCUnsvGmgqfuVRHL54ugE7/wRuxEIXRa2iMZ+BG6g==} + '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} '@types/pg@8.11.0': resolution: {integrity: sha512-sDAlRiBNthGjNFfvt0k6mtotoVYVQ63pA8R4EMWka7crawSR60waVYR0HAgmPRs/e2YaeJTD/43OoZ3PFw80pw==} + '@types/q@1.5.8': + resolution: {integrity: sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==} + '@types/retry@0.12.0': resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + '@types/thrift@0.10.17': + resolution: {integrity: sha512-bDX6d5a5ZDWC81tgDv224n/3PKNYfIQJTPHzlbk4vBWJrYXF6Tg1ncaVmP/c3JbGN2AK9p7zmHorJC2D6oejGQ==} + + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/uuid@9.0.8': resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} @@ -1420,6 +1551,17 @@ packages: '@upstash/vector@1.2.3': resolution: {integrity: sha512-yXsWKeuHNYyH72BcSZd3bV5ZD5MybAoTvKxkMaeV2UzuGfNzbHBVh5eO+ysTWTFAf8I9XcOueF4tZfAGjCa4Iw==} + '@xterm/xterm@5.5.0': + resolution: {integrity: sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==} + + '@zenfs/core@2.5.7': + resolution: {integrity: sha512-g3C86mvi91Q2JuzGOwjG83tqEvZ0sl94wtw6AdUvEuRYocovFU3BwGrMobhg3ecvMQlVbqMJHaoA+jZgJ4duig==} + engines: {node: '>= 18'} + hasBin: true + + '@zilliz/milvus2-sdk-node@3.0.3': + resolution: {integrity: sha512-7rC+MDmzfctc9wscIfqxkzTJCxWafSdkyFDESMIzEX1G2ndImRQzHfkrzjcPFqPBVgQHQScGnGa3W7edqcThjA==} + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -1499,6 +1641,12 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + async-limiter@1.0.1: + resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -1593,6 +1741,13 @@ packages: 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} @@ -1605,6 +1760,10 @@ packages: bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + bson@6.10.4: + resolution: {integrity: sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==} + engines: {node: '>=16.20.1'} + bson@7.2.0: resolution: {integrity: sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ==} engines: {node: '>=20.19.0'} @@ -1622,6 +1781,9 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} @@ -1753,9 +1915,25 @@ 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.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'} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -1800,6 +1978,9 @@ packages: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} + dayjs@1.11.21: + resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -1917,6 +2098,9 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} @@ -1966,6 +2150,9 @@ packages: eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + 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'} @@ -2021,6 +2208,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} @@ -2043,6 +2233,9 @@ packages: resolution: {integrity: sha512-5z2nZxrnKxk+jLq5TyD0xbPXI2I18FF+knIZVG55e0CXWgXF/F4SpCBsiW7JTBPwghqXsC66T2yctnVT/sMO0g==} hasBin: true + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + follow-redirects@1.16.0: resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} engines: {node: '>=4.0'} @@ -2276,6 +2469,9 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + int53@1.0.0: + resolution: {integrity: sha512-u8BMiMa05OPBgd32CKTead0CVTsFVgwFk23nNXo1teKPF6Sxcu0lXxEzP//zTcaKzXbGgPDXGmj/woyv+I4C5w==} + iovalkey@0.3.3: resolution: {integrity: sha512-4rTJX6Q5wTYEvxboXi8DsEiUo+OvqJGtLYOSGm37KpdRXsG5XJjbVtYKGJpPSWP+QT7rWscA4vsrdmzbEbenpw==} engines: {node: '>=18.12.0'} @@ -2341,6 +2537,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: '>=8.20.1' + istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -2552,6 +2753,9 @@ packages: resolution: {integrity: sha512-kpSuLD3/7RenBnjnJdOHXCKC8dTd1JzeOiJhN0necWWci6cC+qX+VuwPnMVgb+a4+KNJSfgqahpnfWaeDXCimw==} engines: {node: '>=18.0.0'} + kerium@1.3.9: + resolution: {integrity: sha512-uoiTcutvUOjOpck8mmUUq7EsuNPhdfFoYMVehLmJGfdLmWC3nABU8KB63MQM0ydkAM4q+2zZmRIKovbx2LF8iA==} + kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -2559,6 +2763,9 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + langsmith@0.6.3: resolution: {integrity: sha512-pXrQ4/4myQvjFFOAUmt5pWRrLEZR20gzIJD7MNdUH+5/S5nLI4ZRBo/SYKC6coaYj9pYTfQdBIzcs+3kfJ5uDA==} peerDependencies: @@ -2635,6 +2842,10 @@ packages: lodash.once@4.1.1: resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + logform@2.7.0: + resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} + engines: {node: '>= 12.0.0'} + long@5.2.5: resolution: {integrity: sha512-e0r9YBBgNCq1D1o5Dp8FMH0N5hsFtXDBiVa0qoJPHpakvZkmDKPRoGffZJII/XsHvj9An9blm+cRJ01yQqU+Dw==} @@ -2647,6 +2858,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} + lru.min@1.1.4: resolution: {integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==} engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} @@ -2675,6 +2890,9 @@ packages: md5@2.3.0: resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} + memium@0.4.5: + resolution: {integrity: sha512-mnmHWWlcyKnkKSSU0qL1Jsg+CsoikVZ2xsOatAN89R+j4V3m3WlQr0nUoqcjKDyUVF5ZGdNinRiZGVd+7A+TJQ==} + memjs@1.3.2: resolution: {integrity: sha512-qUEg2g8vxPe+zPn09KidjIStHPtoBO8Cttm8bgJFWWabbsjQ9Av9Ky+6UcvKx6ue0LLb/LEhtcyQpRyKfzeXcg==} engines: {node: '>=0.10.0'} @@ -2880,6 +3098,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'} @@ -3123,6 +3344,10 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + 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'} @@ -3156,6 +3381,14 @@ packages: pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + 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) + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -3174,6 +3407,10 @@ packages: 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'} @@ -3317,6 +3554,9 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + snappyjs@0.7.0: + resolution: {integrity: sha512-u5iEEXkMe2EInQio6Wv9LWHOQYRDbD2O9hzS27GpT/lwfIQhTCnHCTqedqHIHe9ZcvQo+9au6vngQayipz1NYw==} + source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} @@ -3354,6 +3594,9 @@ packages: resolution: {integrity: sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw==} engines: {bun: '>=1.0.0', deno: '>=2.0.0', node: '>=12.0.0'} + 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'} @@ -3463,6 +3706,9 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} + 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'} @@ -3470,6 +3716,10 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + thrift@0.23.0: + resolution: {integrity: sha512-j7F1ls8JogClU88Ta/pwD/OzYuiFeD6Z5GoWw7ip+jcDhcNYFKgfXYEsyLXYpiNfJO94fbLnITGxyfZ19YzA6Q==} + engines: {node: '>= 10.18.0'} + tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} @@ -3499,6 +3749,10 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -3613,6 +3867,9 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + undici@7.28.0: resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} engines: {node: '>=20.18.1'} @@ -3626,6 +3883,11 @@ 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@11.1.1: resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} hasBin: true @@ -3644,6 +3906,9 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + varint@6.0.0: + resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} + walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} @@ -3677,6 +3942,14 @@ packages: engines: {node: '>= 8'} hasBin: true + 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'} + wordnet-db@3.1.14: resolution: {integrity: sha512-zVyFsvE+mq9MCmwXUWHIcpfbrHHClZWZiVOzKSxNJruIcFn2RbY55zkhiAMMxM8zCVSmtNiViq8FsAZSFpMYag==} engines: {node: '>=0.6.0'} @@ -3699,6 +3972,17 @@ packages: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + 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.21.0: resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} engines: {node: '>=10.0.0'} @@ -3723,6 +4007,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'} @@ -3822,6 +4109,28 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 + '@aws-sdk/checksums@3.1000.13': + dependencies: + '@aws-sdk/core': 3.974.28 + '@aws-sdk/types': 3.973.15 + '@smithy/core': 3.29.1 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + + '@aws-sdk/client-s3@3.1080.0': + dependencies: + '@aws-sdk/checksums': 3.1000.13 + '@aws-sdk/core': 3.974.28 + '@aws-sdk/credential-provider-node': 3.972.63 + '@aws-sdk/middleware-sdk-s3': 3.972.59 + '@aws-sdk/signature-v4-multi-region': 3.996.38 + '@aws-sdk/types': 3.973.15 + '@smithy/core': 3.29.1 + '@smithy/fetch-http-handler': 5.6.3 + '@smithy/node-http-handler': 4.9.3 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@aws-sdk/client-s3vectors@3.967.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -3925,6 +4234,17 @@ snapshots: '@smithy/util-utf8': 4.4.2 tslib: 2.8.1 + '@aws-sdk/core@3.974.28': + dependencies: + '@aws-sdk/types': 3.973.15 + '@aws-sdk/xml-builder': 3.972.33 + '@aws/lambda-invoke-store': 0.3.0 + '@smithy/core': 3.29.1 + '@smithy/signature-v4': 5.6.2 + '@smithy/types': 4.15.1 + bowser: 2.14.1 + tslib: 2.8.1 + '@aws-sdk/credential-provider-env@3.967.0': dependencies: '@aws-sdk/core': 3.967.0 @@ -3933,6 +4253,14 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 + '@aws-sdk/credential-provider-env@3.972.54': + dependencies: + '@aws-sdk/core': 3.974.28 + '@aws-sdk/types': 3.973.15 + '@smithy/core': 3.29.1 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@aws-sdk/credential-provider-http@3.967.0': dependencies: '@aws-sdk/core': 3.967.0 @@ -3946,6 +4274,16 @@ snapshots: '@smithy/util-stream': 4.7.2 tslib: 2.8.1 + '@aws-sdk/credential-provider-http@3.972.56': + dependencies: + '@aws-sdk/core': 3.974.28 + '@aws-sdk/types': 3.973.15 + '@smithy/core': 3.29.1 + '@smithy/fetch-http-handler': 5.6.3 + '@smithy/node-http-handler': 4.9.3 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@aws-sdk/credential-provider-ini@3.967.0': dependencies: '@aws-sdk/core': 3.967.0 @@ -3965,6 +4303,22 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-ini@3.972.61': + dependencies: + '@aws-sdk/core': 3.974.28 + '@aws-sdk/credential-provider-env': 3.972.54 + '@aws-sdk/credential-provider-http': 3.972.56 + '@aws-sdk/credential-provider-login': 3.972.60 + '@aws-sdk/credential-provider-process': 3.972.54 + '@aws-sdk/credential-provider-sso': 3.972.60 + '@aws-sdk/credential-provider-web-identity': 3.972.60 + '@aws-sdk/nested-clients': 3.997.28 + '@aws-sdk/types': 3.973.15 + '@smithy/core': 3.29.1 + '@smithy/credential-provider-imds': 4.4.6 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@aws-sdk/credential-provider-login@3.967.0': dependencies: '@aws-sdk/core': 3.967.0 @@ -3978,6 +4332,15 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-login@3.972.60': + dependencies: + '@aws-sdk/core': 3.974.28 + '@aws-sdk/nested-clients': 3.997.28 + '@aws-sdk/types': 3.973.15 + '@smithy/core': 3.29.1 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@aws-sdk/credential-provider-node@3.967.0': dependencies: '@aws-sdk/credential-provider-env': 3.967.0 @@ -3995,6 +4358,20 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-node@3.972.63': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.54 + '@aws-sdk/credential-provider-http': 3.972.56 + '@aws-sdk/credential-provider-ini': 3.972.61 + '@aws-sdk/credential-provider-process': 3.972.54 + '@aws-sdk/credential-provider-sso': 3.972.60 + '@aws-sdk/credential-provider-web-identity': 3.972.60 + '@aws-sdk/types': 3.973.15 + '@smithy/core': 3.29.1 + '@smithy/credential-provider-imds': 4.4.6 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@aws-sdk/credential-provider-process@3.967.0': dependencies: '@aws-sdk/core': 3.967.0 @@ -4004,6 +4381,14 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 + '@aws-sdk/credential-provider-process@3.972.54': + dependencies: + '@aws-sdk/core': 3.974.28 + '@aws-sdk/types': 3.973.15 + '@smithy/core': 3.29.1 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@aws-sdk/credential-provider-sso@3.967.0': dependencies: '@aws-sdk/client-sso': 3.967.0 @@ -4017,6 +4402,16 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-sso@3.972.60': + dependencies: + '@aws-sdk/core': 3.974.28 + '@aws-sdk/nested-clients': 3.997.28 + '@aws-sdk/token-providers': 3.1080.0 + '@aws-sdk/types': 3.973.15 + '@smithy/core': 3.29.1 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@aws-sdk/credential-provider-web-identity@3.967.0': dependencies: '@aws-sdk/core': 3.967.0 @@ -4029,6 +4424,15 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-web-identity@3.972.60': + dependencies: + '@aws-sdk/core': 3.974.28 + '@aws-sdk/nested-clients': 3.997.28 + '@aws-sdk/types': 3.973.15 + '@smithy/core': 3.29.1 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@aws-sdk/middleware-host-header@3.965.0': dependencies: '@aws-sdk/types': 3.965.0 @@ -4050,6 +4454,15 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 + '@aws-sdk/middleware-sdk-s3@3.972.59': + dependencies: + '@aws-sdk/core': 3.974.28 + '@aws-sdk/signature-v4-multi-region': 3.996.38 + '@aws-sdk/types': 3.973.15 + '@smithy/core': 3.29.1 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@aws-sdk/middleware-user-agent@3.967.0': dependencies: '@aws-sdk/core': 3.967.0 @@ -4103,6 +4516,17 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/nested-clients@3.997.28': + dependencies: + '@aws-sdk/core': 3.974.28 + '@aws-sdk/signature-v4-multi-region': 3.996.38 + '@aws-sdk/types': 3.973.15 + '@smithy/core': 3.29.1 + '@smithy/fetch-http-handler': 5.6.3 + '@smithy/node-http-handler': 4.9.3 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@aws-sdk/region-config-resolver@3.965.0': dependencies: '@aws-sdk/types': 3.965.0 @@ -4111,6 +4535,22 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 + '@aws-sdk/signature-v4-multi-region@3.996.38': + dependencies: + '@aws-sdk/types': 3.973.15 + '@smithy/signature-v4': 5.6.2 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1080.0': + dependencies: + '@aws-sdk/core': 3.974.28 + '@aws-sdk/nested-clients': 3.997.28 + '@aws-sdk/types': 3.973.15 + '@smithy/core': 3.29.1 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@aws-sdk/token-providers@3.967.0': dependencies: '@aws-sdk/core': 3.967.0 @@ -4133,6 +4573,11 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 + '@aws-sdk/types@3.973.15': + dependencies: + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@aws-sdk/util-endpoints@3.965.0': dependencies: '@aws-sdk/types': 3.965.0 @@ -4166,8 +4611,15 @@ snapshots: fast-xml-parser: 5.9.3 tslib: 2.8.1 + '@aws-sdk/xml-builder@3.972.33': + dependencies: + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@aws/lambda-invoke-store@0.2.4': {} + '@aws/lambda-invoke-store@0.3.0': {} + '@azure/abort-controller@2.1.2': dependencies: tslib: 2.8.1 @@ -4468,10 +4920,18 @@ snapshots: '@cloudflare/workers-types@4.20260611.1': {} + '@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 + '@elastic/elasticsearch@9.4.2': dependencies: '@elastic/transport': 9.3.7 @@ -4828,12 +5288,12 @@ snapshots: '@js-sdsl/ordered-map@4.4.2': {} - '@langchain/core@1.1.48(@opentelemetry/api@1.9.1)(openai@4.104.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0)': + '@langchain/core@1.1.48(@opentelemetry/api@1.9.1)(openai@4.104.0(ws@5.2.5)(zod@3.25.76))(ws@5.2.5)': dependencies: '@cfworker/json-schema': 4.1.1 '@standard-schema/spec': 1.1.0 js-tiktoken: 1.0.21 - langsmith: 0.6.3(@opentelemetry/api@1.9.1)(openai@4.104.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0) + langsmith: 0.6.3(@opentelemetry/api@1.9.1)(openai@4.104.0(ws@5.2.5)(zod@3.25.76))(ws@5.2.5) mustache: 4.2.0 p-queue: 6.6.2 zod: 3.25.76 @@ -4891,6 +5351,8 @@ snapshots: '@opentelemetry/semantic-conventions@1.42.0': {} + '@petamoriken/float16@3.9.3': {} + '@pinecone-database/pinecone@8.0.0': {} '@pkgjs/parseargs@0.11.0': @@ -5049,6 +5511,25 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.61.1': optional: true + '@shanghaikid/parquetjs@1.8.8': + dependencies: + '@aws-sdk/client-s3': 3.1080.0 + '@types/node-int64': 0.4.32 + '@types/thrift': 0.10.17 + '@zenfs/core': 2.5.7 + 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 + '@sinclair/typebox@0.27.10': {} '@sinonjs/commons@3.0.1': @@ -5070,18 +5551,35 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 + '@smithy/core@3.29.1': + dependencies: + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@smithy/credential-provider-imds@4.4.2': dependencies: '@smithy/core': 3.26.0 '@smithy/types': 4.15.0 tslib: 2.8.1 + '@smithy/credential-provider-imds@4.4.6': + dependencies: + '@smithy/core': 3.29.1 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@smithy/fetch-http-handler@5.5.2': dependencies: '@smithy/core': 3.26.0 '@smithy/types': 4.15.0 tslib: 2.8.1 + '@smithy/fetch-http-handler@5.6.3': + dependencies: + '@smithy/core': 3.29.1 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@smithy/hash-node@4.4.2': dependencies: '@smithy/core': 3.26.0 @@ -5132,6 +5630,12 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 + '@smithy/node-http-handler@4.9.3': + dependencies: + '@smithy/core': 3.29.1 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@smithy/property-provider@4.4.2': dependencies: '@smithy/core': 3.26.0 @@ -5153,6 +5657,12 @@ snapshots: '@smithy/types': 4.15.0 tslib: 2.8.1 + '@smithy/signature-v4@5.6.2': + dependencies: + '@smithy/core': 3.29.1 + '@smithy/types': 4.15.1 + tslib: 2.8.1 + '@smithy/smithy-client@4.14.2': dependencies: '@smithy/core': 3.26.0 @@ -5163,6 +5673,10 @@ snapshots: dependencies: tslib: 2.8.1 + '@smithy/types@4.15.1': + dependencies: + tslib: 2.8.1 + '@smithy/url-parser@4.4.2': dependencies: '@smithy/core': 3.26.0 @@ -5228,6 +5742,11 @@ snapshots: '@smithy/core': 3.26.0 tslib: 2.8.1 + '@so-ric/colorspace@1.1.6': + dependencies: + color: 5.0.3 + text-hex: 1.0.0 + '@standard-schema/spec@1.1.0': {} '@supabase/auth-js@2.108.1': @@ -5326,6 +5845,10 @@ snapshots: '@types/node': 22.19.21 form-data: 4.0.6 + '@types/node-int64@0.4.32': + dependencies: + '@types/node': 22.19.21 + '@types/node@18.19.130': dependencies: undici-types: 5.26.5 @@ -5334,6 +5857,10 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/node@25.9.4': + dependencies: + undici-types: 7.24.6 + '@types/normalize-package-data@2.4.4': {} '@types/pg@8.11.0': @@ -5342,10 +5869,20 @@ snapshots: pg-protocol: 1.14.0 pg-types: 4.1.0 + '@types/q@1.5.8': {} + '@types/retry@0.12.0': {} '@types/stack-utils@2.0.3': {} + '@types/thrift@0.10.17': + dependencies: + '@types/node': 22.19.21 + '@types/node-int64': 0.4.32 + '@types/q': 1.5.8 + + '@types/triple-beam@1.3.5': {} + '@types/uuid@9.0.8': {} '@types/webidl-conversions@7.0.3': {} @@ -5370,6 +5907,35 @@ snapshots: '@upstash/vector@1.2.3': {} + '@xterm/xterm@5.5.0': + optional: true + + '@zenfs/core@2.5.7': + dependencies: + '@types/node': 25.9.4 + 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@3.0.3': + dependencies: + '@grpc/grpc-js': 1.14.4 + '@grpc/proto-loader': 0.8.1 + '@opentelemetry/api': 1.9.1 + '@petamoriken/float16': 3.9.3 + '@shanghaikid/parquetjs': 1.8.8 + dayjs: 1.11.21 + generic-pool: 3.9.0 + lru-cache: 9.1.2 + protobufjs: 7.6.3 + winston: 3.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -5431,6 +5997,10 @@ snapshots: argparse@2.0.1: {} + async-limiter@1.0.1: {} + + async@3.2.6: {} + asynckit@0.4.0: {} aws-ssl-profiles@1.1.2: {} @@ -5552,6 +6122,10 @@ snapshots: 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.35 @@ -5568,6 +6142,8 @@ snapshots: dependencies: node-int64: 0.4.0 + bson@6.10.4: {} + bson@7.2.0: {} buffer-equal-constant-time@1.0.1: {} @@ -5581,6 +6157,11 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + bundle-name@4.1.0: dependencies: run-applescript: 7.1.0 @@ -5702,8 +6283,23 @@ snapshots: dependencies: color-name: 1.1.4 + color-convert@3.1.3: + dependencies: + color-name: 2.1.0 + 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 + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -5751,6 +6347,8 @@ snapshots: data-uri-to-buffer@4.0.1: {} + dayjs@1.11.21: {} + debug@4.4.3(supports-color@5.5.0): dependencies: ms: 2.1.3 @@ -5840,6 +6438,8 @@ snapshots: emoji-regex@9.2.2: {} + enabled@2.0.0: {} + end-of-stream@1.4.5: dependencies: once: 1.4.0 @@ -5904,6 +6504,8 @@ snapshots: eventemitter3@4.0.7: {} + eventemitter3@5.0.4: {} + events@3.3.0: {} execa@5.1.1: @@ -5976,6 +6578,8 @@ snapshots: optionalDependencies: picomatch: 4.0.4 + fecha@4.2.3: {} + fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 @@ -6005,6 +6609,8 @@ snapshots: kolorist: 1.8.0 read-pkg: 8.1.0 + fn.name@1.1.0: {} + follow-redirects@1.16.0: {} foreground-child@3.3.1: @@ -6284,6 +6890,8 @@ snapshots: ini@1.3.8: {} + int53@1.0.0: {} + iovalkey@0.3.3: dependencies: '@iovalkey/commands': 0.1.0 @@ -6340,6 +6948,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-instrument@5.2.1: @@ -6750,17 +7362,23 @@ snapshots: kareem@3.3.0: {} + kerium@1.3.9: + dependencies: + utilium: 3.4.0 + kleur@3.0.3: {} kolorist@1.8.0: {} - langsmith@0.6.3(@opentelemetry/api@1.9.1)(openai@4.104.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0): + kuler@2.0.0: {} + + langsmith@0.6.3(@opentelemetry/api@1.9.1)(openai@4.104.0(ws@5.2.5)(zod@3.25.76))(ws@5.2.5): dependencies: p-queue: 6.6.2 optionalDependencies: '@opentelemetry/api': 1.9.1 - openai: 4.104.0(ws@8.21.0)(zod@3.25.76) - ws: 8.21.0 + openai: 4.104.0(ws@5.2.5)(zod@3.25.76) + ws: 5.2.5 leven@3.1.0: {} @@ -6798,6 +7416,15 @@ snapshots: lodash.once@4.1.1: {} + 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.2.5: {} long@5.3.2: {} @@ -6808,6 +7435,8 @@ snapshots: dependencies: yallist: 3.1.1 + lru-cache@9.1.2: {} + lru.min@1.1.4: {} magic-string@0.30.21: @@ -6836,6 +7465,11 @@ snapshots: crypt: 0.0.2 is-buffer: 1.1.6 + memium@0.4.5: + dependencies: + kerium: 1.3.9 + utilium: 3.4.0 + memjs@1.3.2: {} memory-pager@1.5.0: {} @@ -7041,6 +7675,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 @@ -7060,7 +7698,7 @@ snapshots: is-inside-container: 1.0.0 wsl-utils: 0.1.0 - openai@4.104.0(ws@8.21.0)(zod@3.25.76): + openai@4.104.0(ws@5.2.5)(zod@3.25.76): dependencies: '@types/node': 18.19.130 '@types/node-fetch': 2.6.13 @@ -7070,7 +7708,7 @@ snapshots: formdata-node: 4.4.1 node-fetch: 2.7.0 optionalDependencies: - ws: 8.21.0 + ws: 5.2.5 zod: 3.25.76 transitivePeerDependencies: - encoding @@ -7273,6 +7911,8 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 + process@0.11.10: {} + progress@2.0.3: {} prompts@2.4.2: @@ -7312,6 +7952,8 @@ snapshots: pure-rand@6.1.0: {} + q@1.5.1: {} + queue-microtask@1.2.3: {} rc@1.2.8: @@ -7336,6 +7978,14 @@ snapshots: 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 @@ -7494,6 +8144,8 @@ snapshots: slash@3.0.0: {} + snappyjs@0.7.0: {} + source-map-support@0.5.13: dependencies: buffer-from: 1.1.2 @@ -7527,6 +8179,8 @@ snapshots: sql-escaper@1.3.3: {} + stack-trace@0.0.10: {} + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 @@ -7650,6 +8304,8 @@ snapshots: glob: 7.2.3 minimatch: 3.1.5 + text-hex@1.0.0: {} + thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -7658,6 +8314,18 @@ snapshots: dependencies: any-promise: 1.3.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 + tinyexec@0.3.2: {} tinyglobby@0.2.17: @@ -7681,6 +8349,8 @@ snapshots: tree-kill@1.2.2: {} + triple-beam@1.4.1: {} + ts-interface-checker@0.1.13: {} ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.7))(esbuild@0.28.1)(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)))(typescript@5.5.4): @@ -7780,6 +8450,8 @@ snapshots: undici-types@6.21.0: {} + undici-types@7.24.6: {} + undici@7.28.0: {} update-browserslist-db@1.2.3(browserslist@4.28.2): @@ -7790,6 +8462,12 @@ snapshots: util-deprecate@1.0.2: {} + utilium@3.4.0: + dependencies: + eventemitter3: 5.0.4 + optionalDependencies: + '@xterm/xterm': 5.5.0 + uuid@11.1.1: {} uuid@13.0.2: {} @@ -7807,6 +8485,8 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 + varint@6.0.0: {} + walker@1.0.8: dependencies: makeerror: 1.0.12 @@ -7835,6 +8515,26 @@ snapshots: dependencies: isexe: 2.0.0 + 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 + wordnet-db@3.1.14: {} wordwrap@1.0.0: {} @@ -7858,6 +8558,10 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 + ws@5.2.5: + dependencies: + async-limiter: 1.0.1 + ws@8.21.0: {} wsl-utils@0.1.0: @@ -7868,6 +8572,8 @@ snapshots: xtend@4.0.2: {} + xxhash-wasm@1.1.0: {} + y18n@5.0.8: {} yallist@3.1.1: {} diff --git a/mem0-ts/src/oss/src/index.ts b/mem0-ts/src/oss/src/index.ts index 44cb5fd5d..031af0fbf 100644 --- a/mem0-ts/src/oss/src/index.ts +++ b/mem0-ts/src/oss/src/index.ts @@ -41,6 +41,7 @@ export * from "./vector_stores/s3_vectors"; export * from "./vector_stores/vertex_ai_vector_search"; export * from "./vector_stores/pinecone"; export * from "./vector_stores/turbopuffer"; +export * from "./vector_stores/milvus"; export * from "./vector_stores/mongodb"; export * from "./vector_stores/opensearch"; export * from "./utils/factory"; diff --git a/mem0-ts/src/oss/src/tests/milvus.test.ts b/mem0-ts/src/oss/src/tests/milvus.test.ts new file mode 100644 index 000000000..9f1a92625 --- /dev/null +++ b/mem0-ts/src/oss/src/tests/milvus.test.ts @@ -0,0 +1,517 @@ +import { Milvus } from "../vector_stores/milvus"; + +/** + * In-memory fake of the subset of the `@zilliz/milvus2-sdk-node` MilvusClient + * API that the Milvus vector store uses. Lets us exercise the provider's + * request shaping and response parsing without a live Milvus server or the SDK. + */ +class FakeMilvusClient { + public calls: { method: string; args: any }[] = []; + private collections = new Set(); + // collection -> id -> row + private store: Record> = {}; + // collection -> declared vector dimension (recorded from createCollection) + private dims: Record = {}; + // collection -> declared field names (surfaced via describeCollection) + private fieldNames: Record = {}; + + // Allow tests to script search responses. + public searchResponse: any = { results: [] }; + + constructor(opts?: { existing?: string[]; bm25?: string[] }) { + // Legacy dense-only collections: no text/sparse fields. + for (const c of opts?.existing || []) { + this.collections.add(c); + this.store[c] = {}; + this.fieldNames[c] = ["id", "vectors", "metadata"]; + } + // Pre-existing collections that already carry the BM25 schema. + for (const c of opts?.bm25 || []) { + this.collections.add(c); + this.store[c] = {}; + this.fieldNames[c] = ["id", "vectors", "metadata", "text", "sparse"]; + } + } + + async hasCollection({ collection_name }: any) { + this.calls.push({ method: "hasCollection", args: { collection_name } }); + return { value: this.collections.has(collection_name) }; + } + + async createCollection(args: any) { + this.calls.push({ method: "createCollection", args }); + // Mirror Milvus's real server constraint: a FloatVector field's dim must be + // in [2, 32768]. Vector fields are the ones carrying a numeric `dim`. + for (const f of args.fields || []) { + if (typeof f.dim === "number" && (f.dim < 2 || f.dim > 32768)) { + throw new Error( + `invalid dimension: ${f.dim}. should be in range 2 ~ 32768`, + ); + } + } + const vectorField = (args.fields || []).find( + (f: any) => typeof f.dim === "number", + ); + if (vectorField) this.dims[args.collection_name] = vectorField.dim; + this.fieldNames[args.collection_name] = (args.fields || []).map( + (f: any) => f.name, + ); + this.collections.add(args.collection_name); + this.store[args.collection_name] = this.store[args.collection_name] || {}; + } + + async describeCollection({ collection_name }: any) { + this.calls.push({ + method: "describeCollection", + args: { collection_name }, + }); + const fields = (this.fieldNames[collection_name] || []).map((name) => ({ + name, + })); + return { schema: { fields } }; + } + + // Reject rows whose vector length disagrees with the collection's declared + // dim, exactly as the real server would. + private checkDims(collection: string, data: any[]) { + const dim = this.dims[collection]; + if (dim == null) return; // pre-seeded collection: dim not tracked + for (const row of data || []) { + if (Array.isArray(row.vectors) && row.vectors.length !== dim) { + throw new Error( + `vector dimension mismatch: expected ${dim}, got ${row.vectors.length}`, + ); + } + } + } + + async loadCollection(args: any) { + this.calls.push({ method: "loadCollection", args }); + } + + async dropCollection(args: any) { + this.calls.push({ method: "dropCollection", args }); + this.collections.delete(args.collection_name); + delete this.store[args.collection_name]; + } + + async insert(args: any) { + this.calls.push({ method: "insert", args }); + this.checkDims(args.collection_name, args.data); + const col = (this.store[args.collection_name] = + this.store[args.collection_name] || {}); + for (const row of args.data) col[String(row.id)] = row; + } + + async upsert(args: any) { + this.calls.push({ method: "upsert", args }); + this.checkDims(args.collection_name, args.data); + const col = (this.store[args.collection_name] = + this.store[args.collection_name] || {}); + for (const row of args.data) col[String(row.id)] = row; + } + + async delete(args: any) { + this.calls.push({ method: "delete", args }); + const col = this.store[args.collection_name] || {}; + for (const id of args.ids) delete col[String(id)]; + } + + async get(args: any) { + this.calls.push({ method: "get", args }); + const col = this.store[args.collection_name] || {}; + const data = args.ids.map((id: string) => col[String(id)]).filter(Boolean); + return { data }; + } + + async query(args: any) { + this.calls.push({ method: "query", args }); + const col = this.store[args.collection_name] || {}; + return { data: Object.values(col).slice(0, args.limit ?? 100) }; + } + + async search(args: any) { + this.calls.push({ method: "search", args }); + return this.searchResponse; + } +} + +// Suppress the constructor's fire-and-forget initialize() console noise and the +// legacy-collection BM25 warning. +beforeAll(() => { + jest.spyOn(console, "error").mockImplementation(() => {}); + jest.spyOn(console, "warn").mockImplementation(() => {}); +}); +afterAll(() => { + (console.error as jest.Mock).mockRestore?.(); + (console.warn as jest.Mock).mockRestore?.(); +}); + +describe("Milvus vector store (TS OSS SDK)", () => { + function makeStore(client: FakeMilvusClient, overrides: any = {}) { + return new Milvus({ + client, + collectionName: "mem0", + embeddingModelDims: 3, + ...overrides, + }); + } + + it("creates the collection on initialize when it does not exist", async () => { + const client = new FakeMilvusClient(); + const store = makeStore(client); + await store.initialize(); + + const created = client.calls.find((c) => c.method === "createCollection"); + expect(created).toBeDefined(); + expect(created!.args.collection_name).toBe("mem0"); + const vectorField = created!.args.fields.find( + (f: any) => f.name === "vectors", + ); + expect(vectorField.dim).toBe(3); + // AUTOINDEX dense index with the default metric (L2, matching the Python provider). + expect(created!.args.index_params[0].metric_type).toBe("L2"); + }); + + it("does not recreate an existing collection", async () => { + const client = new FakeMilvusClient({ existing: ["mem0"] }); + const store = makeStore(client); + await store.initialize(); + expect(client.calls.some((c) => c.method === "createCollection")).toBe( + false, + ); + }); + + it("inserts records mapping payloads into the metadata field", async () => { + const client = new FakeMilvusClient({ existing: ["mem0"] }); + const store = makeStore(client); + await store.initialize(); + + await store.insert( + [ + [0.1, 0.2, 0.3], + [0.4, 0.5, 0.6], + ], + ["a", "b"], + [ + { data: "first", user_id: "u1" }, + { data: "second", user_id: "u1" }, + ], + ); + + const insertCall = client.calls.find((c) => c.method === "insert")!; + expect(insertCall.args.data).toHaveLength(2); + expect(insertCall.args.data[0]).toEqual({ + id: "a", + vectors: [0.1, 0.2, 0.3], + metadata: { data: "first", user_id: "u1" }, + }); + }); + + it("round-trips a stored record through get()", async () => { + const client = new FakeMilvusClient({ existing: ["mem0"] }); + const store = makeStore(client); + await store.initialize(); + await store.insert([[1, 0, 0]], ["x"], [{ data: "hello" }]); + + const got = await store.get("x"); + expect(got).not.toBeNull(); + expect(got!.id).toBe("x"); + expect(got!.payload).toEqual({ data: "hello" }); + + const missing = await store.get("nope"); + expect(missing).toBeNull(); + }); + + it("updates a record in place via upsert", async () => { + const client = new FakeMilvusClient({ existing: ["mem0"] }); + const store = makeStore(client); + await store.initialize(); + await store.insert([[1, 0, 0]], ["a"], [{ data: "old" }]); + + await store.update("a", [0, 1, 0], { data: "new" }); + + const upsertCall = client.calls.find((c) => c.method === "upsert")!; + expect(upsertCall.args.data[0]).toEqual({ + id: "a", + vectors: [0, 1, 0], + metadata: { data: "new" }, + }); + const got = await store.get("a"); + expect(got!.payload).toEqual({ data: "new" }); + }); + + it("builds an AND-combined equality filter expression for search", async () => { + const client = new FakeMilvusClient({ existing: ["mem0"] }); + client.searchResponse = { + results: [{ id: "a", score: 0.9, metadata: { data: "first" } }], + }; + // Pin a non-L2 metric so the raw score passes through unnormalised. + const store = makeStore(client, { metricType: "COSINE" }); + await store.initialize(); + + const res = await store.search([0.1, 0.2, 0.3], 5, { + user_id: "u1", + agent_id: 7 as any, + }); + + const searchCall = client.calls.find((c) => c.method === "search")!; + expect(searchCall.args.filter).toBe( + '(metadata["user_id"] == "u1") and (metadata["agent_id"] == 7)', + ); + expect(searchCall.args.limit).toBe(5); + expect(res[0]).toEqual({ + id: "a", + payload: { data: "first" }, + score: 0.9, + }); + }); + + it("normalises L2 distances into a 0..1 similarity score", async () => { + const client = new FakeMilvusClient({ existing: ["mem0"] }); + client.searchResponse = { + results: [{ id: "a", score: 3.0, metadata: {} }], + }; + const store = makeStore(client, { metricType: "L2" }); + await store.initialize(); + + const res = await store.search([0, 0, 1], 1); + // 1 / (1 + 3) = 0.25 + expect(res[0].score).toBeCloseTo(0.25, 6); + }); + + it("escapes embedded quotes in string filter values", async () => { + const client = new FakeMilvusClient({ existing: ["mem0"] }); + const store = makeStore(client); + await store.initialize(); + await store.list({ data: 'a"b' }); + const queryCall = client.calls.filter((c) => c.method === "query").pop()!; + expect(queryCall.args.filter).toBe('(metadata["data"] == "a\\"b")'); + }); + + it("escapes backslashes in string filter values", async () => { + const client = new FakeMilvusClient({ existing: ["mem0"] }); + const store = makeStore(client); + await store.initialize(); + // Value is a\b (one backslash); it must be doubled so the expression + // stays well-formed and the backslash can't escape the closing quote. + await store.list({ data: "a\\b" }); + const queryCall = client.calls.filter((c) => c.method === "query").pop()!; + expect(queryCall.args.filter).toBe('(metadata["data"] == "a\\\\b")'); + }); + + it("rejects filter keys that are not safe identifiers", async () => { + const client = new FakeMilvusClient({ existing: ["mem0"] }); + const store = makeStore(client); + await store.initialize(); + await expect( + store.list({ 'x"] or true or ["y': "z" } as any), + ).rejects.toThrow(/Invalid filter key/); + }); + + it("lists records and returns the count", async () => { + const client = new FakeMilvusClient({ existing: ["mem0"] }); + const store = makeStore(client); + await store.initialize(); + await store.insert( + [ + [1, 0, 0], + [0, 1, 0], + ], + ["a", "b"], + [{ data: "x" }, { data: "y" }], + ); + + const [results, count] = await store.list(undefined, 100); + expect(count).toBe(2); + expect(results.map((r) => r.id).sort()).toEqual(["a", "b"]); + }); + + it("deletes a record by id", async () => { + const client = new FakeMilvusClient({ existing: ["mem0"] }); + const store = makeStore(client); + await store.initialize(); + await store.insert([[1, 0, 0]], ["a"], [{ data: "x" }]); + await store.delete("a"); + const got = await store.get("a"); + expect(got).toBeNull(); + }); + + it("generates and persists a user id, then reads it back", async () => { + const client = new FakeMilvusClient({ existing: ["mem0"] }); + const store = makeStore(client); + await store.initialize(); + + const created = await store.getUserId(); + expect(typeof created).toBe("string"); + expect(created.length).toBeGreaterThan(0); + + const readBack = await store.getUserId(); + expect(readBack).toBe(created); + }); + + it("setUserId overwrites in place instead of appending rows", async () => { + const client = new FakeMilvusClient(); + const store = makeStore(client); + await store.initialize(); + + await store.setUserId("user-1"); + await store.setUserId("user-2"); + + // A fresh insert per call would leave two rows; the reuse-id upsert keeps one. + const all = await client.query({ + collection_name: "memory_migrations", + limit: 100, + }); + expect(all.data).toHaveLength(1); + expect(all.data[0].user_id).toBe("user-2"); + expect(await store.getUserId()).toBe("user-2"); + }); + + it("creates the telemetry collection with a Milvus-valid vector dim (>= 2)", async () => { + // Regression: the helper collection previously used dim 1, which a real + // Milvus server rejects (valid range is 2~32768), silently breaking + // getUserId/setUserId. The fake now enforces the same bound, so a dim < 2 + // would throw here instead of passing as it did against the old mock. + const client = new FakeMilvusClient({ existing: ["mem0"] }); + const store = makeStore(client); + await store.initialize(); + await store.getUserId(); + + const migCreate = client.calls.find( + (c) => + c.method === "createCollection" && + c.args.collection_name === "memory_migrations", + )!; + expect(migCreate).toBeDefined(); + const vectorField = migCreate.args.fields.find( + (f: any) => f.name === "vectors", + ); + expect(vectorField.dim).toBeGreaterThanOrEqual(2); + }); + + it("keywordSearch returns null on a collection without the BM25 schema", async () => { + const client = new FakeMilvusClient({ existing: ["mem0"] }); + const store = makeStore(client); + await store.initialize(); + expect(await store.keywordSearch("hello")).toBeNull(); + }); + + it("creates a BM25 hybrid schema on a fresh collection", async () => { + const client = new FakeMilvusClient(); + const store = makeStore(client); + await store.initialize(); + + const created = client.calls.find((c) => c.method === "createCollection")!; + const fieldNames = created.args.fields.map((f: any) => f.name); + expect(fieldNames).toEqual( + expect.arrayContaining(["id", "vectors", "metadata", "text", "sparse"]), + ); + // The text field must have the analyzer enabled for BM25 tokenization. + const textField = created.args.fields.find((f: any) => f.name === "text"); + expect(textField.enable_analyzer).toBe(true); + // BM25 function maps text -> sparse. + expect(created.args.functions[0].input_field_names).toEqual(["text"]); + expect(created.args.functions[0].output_field_names).toEqual(["sparse"]); + // Sparse BM25 index sits alongside the dense vector index. + const sparseIdx = created.args.index_params.find( + (i: any) => i.field_name === "sparse", + ); + expect(sparseIdx.index_type).toBe("SPARSE_INVERTED_INDEX"); + expect(sparseIdx.metric_type).toBe("BM25"); + }); + + it("populates the BM25 text field from payload on a fresh collection", async () => { + const client = new FakeMilvusClient(); + const store = makeStore(client); + await store.initialize(); + + // Prefers the lemmatized text when present. + await store.insert( + [[0.1, 0.2, 0.3]], + ["a"], + [{ data: "hello world", text_lemmatized: "hello world lemma" }], + ); + // Falls back to raw data when there is no lemmatized text. + await store.insert([[0.4, 0.5, 0.6]], ["b"], [{ data: "just data" }]); + + const insertCalls = client.calls.filter((c) => c.method === "insert"); + expect(insertCalls[0].args.data[0].text).toBe("hello world lemma"); + expect(insertCalls[1].args.data[0].text).toBe("just data"); + }); + + it("writes the BM25 text field on update for a BM25 collection", async () => { + const client = new FakeMilvusClient(); + const store = makeStore(client); + await store.initialize(); + await store.insert([[1, 0, 0]], ["a"], [{ data: "old" }]); + + await store.update("a", [0, 1, 0], { data: "new" }); + + const upsertCall = client.calls.filter((c) => c.method === "upsert").pop()!; + expect(upsertCall.args.data[0].text).toBe("new"); + expect(upsertCall.args.data[0].metadata).toEqual({ data: "new" }); + }); + + it("names the dense anns_field when searching a BM25 collection", async () => { + const client = new FakeMilvusClient(); + client.searchResponse = { + results: [{ id: "a", score: 0.5, metadata: {} }], + }; + const store = makeStore(client, { metricType: "COSINE" }); + await store.initialize(); + + await store.search([0.1, 0.2, 0.3], 3); + const searchCall = client.calls.find((c) => c.method === "search")!; + expect(searchCall.args.anns_field).toBe("vectors"); + }); + + it("omits anns_field when searching a legacy dense-only collection", async () => { + const client = new FakeMilvusClient({ existing: ["mem0"] }); + client.searchResponse = { results: [] }; + const store = makeStore(client, { metricType: "COSINE" }); + await store.initialize(); + + await store.search([0.1, 0.2, 0.3], 3); + const searchCall = client.calls.find((c) => c.method === "search")!; + expect(searchCall.args.anns_field).toBeUndefined(); + }); + + it("runs a BM25 keyword search over the sparse field and parses hits", async () => { + const client = new FakeMilvusClient(); + client.searchResponse = { + results: [{ id: "a", score: 4.2, metadata: { data: "kw hit" } }], + }; + // COSINE so the BM25 score passes through unnormalised for a clean assert. + const store = makeStore(client, { metricType: "COSINE" }); + await store.initialize(); + + const res = await store.keywordSearch("hello", 7, { user_id: "u1" }); + + const searchCall = client.calls.filter((c) => c.method === "search").pop()!; + expect(searchCall.args.data).toEqual(["hello"]); // raw text, not a vector + expect(searchCall.args.anns_field).toBe("sparse"); + expect(searchCall.args.limit).toBe(7); + expect(searchCall.args.filter).toBe('(metadata["user_id"] == "u1")'); + expect(res).not.toBeNull(); + expect(res![0]).toEqual({ + id: "a", + payload: { data: "kw hit" }, + score: 4.2, + }); + }); + + it("detects the BM25 schema on a pre-existing collection via describeCollection", async () => { + const client = new FakeMilvusClient({ bm25: ["mem0"] }); + client.searchResponse = { results: [] }; + const store = makeStore(client, { metricType: "COSINE" }); + await store.initialize(); + + // Detected as BM25: keywordSearch runs (returns []) instead of null, and + // insert writes the text field. + expect(await store.keywordSearch("q")).not.toBeNull(); + await store.insert([[1, 2, 3]], ["a"], [{ data: "d" }]); + const insertCall = client.calls.find((c) => c.method === "insert")!; + expect(insertCall.args.data[0].text).toBe("d"); + }); +}); diff --git a/mem0-ts/src/oss/src/utils/factory.ts b/mem0-ts/src/oss/src/utils/factory.ts index fa604bf76..1b02978d4 100644 --- a/mem0-ts/src/oss/src/utils/factory.ts +++ b/mem0-ts/src/oss/src/utils/factory.ts @@ -56,6 +56,7 @@ import { CassandraDB } from "../vector_stores/cassandra"; import { PineconeDB } from "../vector_stores/pinecone"; import { S3Vectors } from "../vector_stores/s3_vectors"; import { TurbopufferDB } from "../vector_stores/turbopuffer"; +import { Milvus } from "../vector_stores/milvus"; import { MongoDB } from "../vector_stores/mongodb"; export class EmbedderFactory { @@ -172,6 +173,8 @@ export class VectorStoreFactory { return new S3Vectors(config as any); case "turbopuffer": return new TurbopufferDB(config as any); + case "milvus": + return new Milvus(config as any); case "mongodb": return new MongoDB(config as any); default: diff --git a/mem0-ts/src/oss/src/vector_stores/milvus.ts b/mem0-ts/src/oss/src/vector_stores/milvus.ts new file mode 100644 index 000000000..9d63b0023 --- /dev/null +++ b/mem0-ts/src/oss/src/vector_stores/milvus.ts @@ -0,0 +1,510 @@ +import { VectorStore } from "./base"; +import { SearchFilters, VectorStoreConfig, VectorStoreResult } from "../types"; + +/** + * Supported Milvus metric types. Mirrors the Python provider + * (`mem0/configs/vector_stores/milvus.py`). + */ +export type MilvusMetricType = "L2" | "IP" | "COSINE" | "HAMMING" | "JACCARD"; + +export interface MilvusConfig extends VectorStoreConfig { + /** + * Full URL/address for the Milvus or Zilliz server. + * Defaults to `http://localhost:19530`. + */ + url?: string; + /** Token / API key for Zilliz Cloud. Optional for a local setup. */ + token?: string; + /** Name of the database. Optional (Milvus default database when empty). */ + dbName?: string; + /** Collection name. Defaults to `mem0`. */ + collectionName?: string; + /** Embedding dimensionality. Defaults to 1536 (OpenAI). */ + embeddingModelDims?: number; + dimension?: number; + /** Similarity metric. Defaults to `L2` (matches the Python provider). */ + metricType?: MilvusMetricType; + /** + * Pre-constructed `MilvusClient` instance. When provided, `url`/`token`/`dbName` + * are ignored. Primarily useful for dependency injection in tests. + */ + client?: any; +} + +/** + * Milvus vector store provider for the TypeScript OSS SDK. + * + * Mirrors the Python provider in `mem0/vector_stores/milvus.py`: dense-vector + * CRUD (insert / search / get / update / delete / list + user-id helpers) plus + * BM25 hybrid keyword search. New collections are created with a `text` + + * `sparse` field pair and a BM25 function so `keywordSearch` can run full-text + * search; collections created before BM25 support keep working with it disabled. + * + * The `@zilliz/milvus2-sdk-node` dependency is lazily required so the package + * remains optional. Importing this module never forces the SDK to be installed + * until a Milvus store is actually constructed. + */ +export class Milvus implements VectorStore { + private client: any; + private readonly collectionName: string; + private readonly dimension: number; + private readonly metricType: MilvusMetricType; + private _initPromise?: Promise; + // Lazily-resolved SDK enums (set during client construction). + private DataType: any; + private FunctionType: any; + // Whether this collection has the `text` + `sparse` fields for BM25 hybrid + // search. Collections created before BM25 support lack them, so writing a + // top-level `text` field or passing a sparse anns_field would be rejected. + private hasBm25Schema = false; + + constructor(config: MilvusConfig) { + this.collectionName = config.collectionName || "mem0"; + this.dimension = config.embeddingModelDims || config.dimension || 1536; + this.metricType = config.metricType || "L2"; + + if (config.client) { + this.client = config.client; + // Best-effort SDK enum resolution for an injected client (undefined when + // the SDK isn't installed, e.g. unit tests that pass a fake client). + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const sdk = require("@zilliz/milvus2-sdk-node"); + this.DataType = sdk.DataType; + this.FunctionType = sdk.FunctionType; + } catch (_) { + this.DataType = undefined; + this.FunctionType = undefined; + } + } else { + let MilvusClient: any; + let DataType: any; + let FunctionType: any; + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const sdk = require("@zilliz/milvus2-sdk-node"); + MilvusClient = sdk.MilvusClient; + DataType = sdk.DataType; + FunctionType = sdk.FunctionType; + } catch (_) { + throw new Error( + "The '@zilliz/milvus2-sdk-node' package is required to use the Milvus vector store. " + + "Install it with: npm install @zilliz/milvus2-sdk-node", + ); + } + this.DataType = DataType; + this.FunctionType = FunctionType; + this.client = new MilvusClient({ + address: config.url || "http://localhost:19530", + token: config.token, + database: config.dbName || undefined, + }); + } + + this.initialize().catch((err) => + console.error("Error initializing Milvus:", err), + ); + } + + async initialize(): Promise { + if (!this._initPromise) { + this._initPromise = this.createCol(this.collectionName, this.dimension); + } + return this._initPromise; + } + + /** + * Create the collection if it does not already exist, with an AUTOINDEX + * dense-vector index plus a BM25 `text` -> `sparse` full-text index. Idempotent + * (mirrors the Python `create_col`). When the collection already exists, detect + * whether the BM25 `text`/`sparse` fields are present so keyword search + * degrades gracefully on collections created before BM25 support. + */ + private async createCol( + collectionName: string, + vectorSize: number, + ): Promise { + const has = await this.client.hasCollection({ + collection_name: collectionName, + }); + // milvus2-sdk-node returns { value: boolean } for hasCollection. + const exists = typeof has === "object" && has !== null ? has.value : has; + if (exists) { + // A pre-existing collection may predate BM25 support. Inspect its schema + // so insert/search/keywordSearch know whether the text + sparse fields + // exist instead of assuming and getting rejected by the server. + const desc = await this.client.describeCollection({ + collection_name: collectionName, + }); + const names = new Set( + (desc?.schema?.fields || []).map((f: any) => f.name), + ); + this.hasBm25Schema = names.has("text") && names.has("sparse"); + if (!this.hasBm25Schema) { + console.warn( + `Milvus collection '${collectionName}' predates BM25 hybrid search ` + + "(no 'text'/'sparse' fields). Keyword scoring is disabled for it; " + + "semantic search still works. Use a fresh collection to enable it.", + ); + } + return; + } + + const DataType = this.DataType || {}; + const fields = [ + { + name: "id", + data_type: DataType.VarChar, + is_primary_key: true, + max_length: 512, + }, + { + name: "vectors", + data_type: DataType.FloatVector, + dim: vectorSize, + }, + { + name: "metadata", + data_type: DataType.JSON, + }, + // Analyzer-enabled text field that feeds the BM25 function below. + { + name: "text", + data_type: DataType.VarChar, + max_length: 65535, + enable_analyzer: true, + }, + // Sparse vectors are generated automatically by the BM25 function. + { + name: "sparse", + data_type: DataType.SparseFloatVector, + }, + ]; + + await this.client.createCollection({ + collection_name: collectionName, + fields, + enable_dynamic_field: true, + // BM25 turns the `text` field into `sparse` vectors for full-text search. + functions: [ + { + name: "bm25", + type: this.FunctionType?.BM25, + input_field_names: ["text"], + output_field_names: ["sparse"], + params: {}, + }, + ], + index_params: [ + { + field_name: "vectors", + index_type: "AUTOINDEX", + metric_type: this.metricType, + index_name: "vector_index", + }, + { + field_name: "sparse", + index_type: "SPARSE_INVERTED_INDEX", + metric_type: "BM25", + index_name: "sparse_index", + }, + ], + }); + + await this.client.loadCollection({ collection_name: collectionName }); + this.hasBm25Schema = true; + } + + /** + * Filter keys are interpolated straight into the expression, so restrict them + * to safe identifiers (same rule as the Python provider) to block injection. + */ + private static readonly SAFE_FILTER_KEY = /^[a-zA-Z_][a-zA-Z0-9_]*$/; + + /** + * Build a Milvus boolean filter expression from a flat filters object. + * Mirrors the Python `_create_filter` (equality only, AND-combined): validate + * each key, escape string values (backslash first, then double-quote), and + * reject value types Milvus can't compare against a scalar field. + */ + private createFilter(filters?: SearchFilters): string | undefined { + if (!filters || Object.keys(filters).length === 0) return undefined; + const operands: string[] = []; + for (const [key, value] of Object.entries(filters)) { + if (value === undefined || value === null) continue; + if (!Milvus.SAFE_FILTER_KEY.test(key)) { + throw new Error(`Invalid filter key: ${JSON.stringify(key)}`); + } + if (typeof value === "string") { + // Escape backslashes before quotes so a value can't break out of the + // string literal (order matters, exactly as in the Python provider). + const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); + operands.push(`(metadata["${key}"] == "${escaped}")`); + } else if (typeof value === "number" || typeof value === "boolean") { + operands.push(`(metadata["${key}"] == ${value})`); + } else { + throw new Error( + `Filter value for ${JSON.stringify(key)} must be a string, number, or boolean, got ${typeof value}`, + ); + } + } + return operands.length > 0 ? operands.join(" and ") : undefined; + } + + /** + * Text fed to the BM25 sparse index for a payload. Prefers the lemmatized + * text, falls back to the raw memory `data`, and truncates to the VarChar + * limit (mirrors the Python provider). + */ + private bm25Text(payload?: Record): string { + if (!payload) return ""; + const raw = payload.text_lemmatized || payload.data || ""; + return String(raw).slice(0, 65535); + } + + async insert( + vectors: number[][], + ids: string[], + payloads: Record[], + ): Promise { + const data = vectors.map((vector, idx) => { + const metadata = payloads[idx] || {}; + const row: Record = { + id: ids[idx], + vectors: vector, + metadata, + }; + // Only write `text` when the collection has the BM25 schema; legacy + // collections reject an unknown top-level field. + if (this.hasBm25Schema) row.text = this.bm25Text(metadata); + return row; + }); + await this.client.insert({ + collection_name: this.collectionName, + data, + }); + } + + /** + * Map raw Milvus search hits to VectorStoreResult. For the L2 metric, + * distances are unbounded and smaller-is-better, so normalise them to a 0..1 + * similarity; every other metric passes the raw score through. Shared by + * search and keywordSearch so both match the Python provider's `_parse_output` + * (which likewise normalises by metric type regardless of dense vs BM25 score). + */ + private parseHits(hits: any[]): VectorStoreResult[] { + return hits.map((hit: any) => { + const rawDistance = hit.score ?? hit.distance; + let score = rawDistance; + if (rawDistance != null && this.metricType === "L2") { + score = 1.0 / (1.0 + rawDistance); + } + return { + id: String(hit.id), + payload: hit.metadata || {}, + score, + } as VectorStoreResult; + }); + } + + async search( + query: number[], + topK: number = 5, + filters?: SearchFilters, + ): Promise { + const filter = this.createFilter(filters); + const req: Record = { + collection_name: this.collectionName, + data: [query], + limit: topK, + filter, + output_fields: ["*"], + }; + // A BM25 collection has both a dense `vectors` and a sparse `sparse` field, + // so anns_field is ambiguous unless we name the dense one explicitly. + if (this.hasBm25Schema) req.anns_field = "vectors"; + const res = await this.client.search(req); + return this.parseHits(res?.results || []); + } + + /** + * BM25 full-text keyword search over the sparse field. Milvus tokenizes the + * raw query string via the collection's BM25 function. Returns null when the + * collection has no BM25 schema so callers fall back to dense search only + * (mirrors the Python provider). + */ + async keywordSearch( + query: string, + topK: number = 5, + filters?: SearchFilters, + ): Promise { + if (!this.hasBm25Schema) return null; + try { + const filter = this.createFilter(filters); + const res = await this.client.search({ + collection_name: this.collectionName, + data: [query], + anns_field: "sparse", + limit: topK, + filter, + output_fields: ["*"], + }); + return this.parseHits(res?.results || []); + } catch (_) { + // Keyword search is best-effort; degrade to null rather than failing the + // whole retrieval path. + return null; + } + } + + async get(vectorId: string): Promise { + const res = await this.client.get({ + collection_name: this.collectionName, + ids: [vectorId], + output_fields: ["id", "metadata"], + }); + const rows = res?.data || []; + if (!rows.length) return null; + return { + id: String(rows[0].id), + payload: rows[0].metadata || {}, + }; + } + + async update( + vectorId: string, + vector: number[], + payload: Record, + ): Promise { + const row: Record = { + id: vectorId, + vectors: vector, + metadata: payload, + }; + if (this.hasBm25Schema) row.text = this.bm25Text(payload); + await this.client.upsert({ + collection_name: this.collectionName, + data: [row], + }); + } + + async delete(vectorId: string): Promise { + await this.client.delete({ + collection_name: this.collectionName, + ids: [vectorId], + }); + } + + async deleteCol(): Promise { + await this.client.dropCollection({ + collection_name: this.collectionName, + }); + } + + async list( + filters?: SearchFilters, + topK: number = 100, + ): Promise<[VectorStoreResult[], number]> { + const filter = this.createFilter(filters); + const res = await this.client.query({ + collection_name: this.collectionName, + filter: filter ?? "", + limit: topK, + output_fields: ["id", "metadata"], + }); + const rows = res?.data || []; + const results: VectorStoreResult[] = rows.map((row: any) => ({ + id: String(row.id), + payload: row.metadata || {}, + })); + return [results, results.length]; + } + + private generateUUID(): string { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + const v = c === "x" ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); + } + + private async ensureMigrationsCol(): Promise { + const name = "memory_migrations"; + const has = await this.client.hasCollection({ collection_name: name }); + const exists = typeof has === "object" && has !== null ? has.value : has; + if (exists) return; + + const DataType = this.DataType || {}; + await this.client.createCollection({ + collection_name: name, + fields: [ + { + name: "id", + data_type: DataType.VarChar, + is_primary_key: true, + max_length: 512, + }, + // Milvus rejects a FloatVector with dim < 2, so use the minimum (2). This + // helper collection is never vector-searched; the vector is a fixed + // placeholder that exists only to satisfy the required-vector-field schema. + { name: "vectors", data_type: DataType.FloatVector, dim: 2 }, + { name: "user_id", data_type: DataType.VarChar, max_length: 512 }, + ], + index_params: [ + { + field_name: "vectors", + index_type: "AUTOINDEX", + // Fixed metric: this helper collection is never vector-searched, and a + // zero vector has no direction, so COSINE would be degenerate here. + metric_type: "L2", + index_name: "vector_index", + }, + ], + }); + await this.client.loadCollection({ collection_name: name }); + } + + async getUserId(): Promise { + await this.ensureMigrationsCol(); + const res = await this.client.query({ + collection_name: "memory_migrations", + filter: "", + limit: 1, + output_fields: ["*"], + }); + const rows = res?.data || []; + if (rows.length > 0 && rows[0].user_id) { + return String(rows[0].user_id); + } + const randomUserId = + Math.random().toString(36).substring(2, 15) + + Math.random().toString(36).substring(2, 15); + await this.client.insert({ + collection_name: "memory_migrations", + data: [ + { id: this.generateUUID(), vectors: [0, 0], user_id: randomUserId }, + ], + }); + return randomUserId; + } + + async setUserId(userId: string): Promise { + await this.ensureMigrationsCol(); + // Keep a single row: reuse the existing row's id when present so the upsert + // overwrites it in place instead of appending a new row on every call + // (mirrors the qdrant provider's single-row telemetry id). + const existing = await this.client.query({ + collection_name: "memory_migrations", + filter: "", + limit: 1, + output_fields: ["id"], + }); + const rows = existing?.data || []; + const id = + rows.length > 0 && rows[0].id ? String(rows[0].id) : this.generateUUID(); + await this.client.upsert({ + collection_name: "memory_migrations", + data: [{ id, vectors: [0, 0], user_id: userId }], + }); + } +} diff --git a/mem0-ts/tsup.config.ts b/mem0-ts/tsup.config.ts index 2109c740c..a7198c2fa 100644 --- a/mem0-ts/tsup.config.ts +++ b/mem0-ts/tsup.config.ts @@ -21,6 +21,7 @@ const external = [ "@mistralai/mistralai", "@supabase/supabase-js", "@upstash/vector", + "@zilliz/milvus2-sdk-node", "@azure/search-documents", "@azure/identity", "cloudflare",