Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6684b96f7 | ||
|
|
1fa0f0a157 | ||
|
|
2754f45387 | ||
|
|
ecd4d91046 | ||
|
|
a5a247b161 | ||
|
|
d47cb8d284 | ||
|
|
fa15db089d | ||
|
|
d35065c887 |
@@ -56,6 +56,8 @@ jobs:
|
||||
run: sudo apt-get update && sudo apt-get install -y libgeos-dev
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install --upgrade pip wheel setuptools
|
||||
pip install --only-binary=shapely shapely
|
||||
make install_all
|
||||
pip install -e ".[test]"
|
||||
pip install pinecone pinecone-text
|
||||
|
||||
@@ -13,7 +13,7 @@ install:
|
||||
install_all:
|
||||
pip install ruff==0.6.9 groq together boto3 litellm ollama chromadb weaviate weaviate-client sentence_transformers vertexai \
|
||||
google-generativeai elasticsearch opensearch-py vecs "pinecone<7.0.0" pinecone-text faiss-cpu langchain-community \
|
||||
upstash-vector azure-search-documents langchain-memgraph langchain-neo4j rank-bm25
|
||||
upstash-vector azure-search-documents langchain-memgraph langchain-neo4j rank-bm25 pymochow
|
||||
|
||||
# Format code with ruff
|
||||
format:
|
||||
|
||||
+224
-244
@@ -8,6 +8,127 @@ mode: "wide"
|
||||
<Tabs>
|
||||
<Tab title="Python">
|
||||
|
||||
<Update label="2025-06-19" description="v0.1.109">
|
||||
|
||||
**New Features:**
|
||||
- **AgentOps:** Added AgentOps integration
|
||||
- **LM Studio:** Added response_format parameter for LM Studio configuration
|
||||
- **Examples:** Added Memory agent powered by voice (Cartesia + Agno)
|
||||
|
||||
**Improvements:**
|
||||
- **AI SDK:** Added output_format parameter
|
||||
- **Client:** Enhanced update method to support metadata
|
||||
- **Google:** Added Google Genai library support
|
||||
|
||||
**Bug Fixes:**
|
||||
- **Build:** Fixed Build CI failure
|
||||
- **Pinecone:** Fixed pinecone for async memory
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-06-14" description="v0.1.108">
|
||||
|
||||
**New Features:**
|
||||
- **MongoDB:** Added MongoDB Vector Store support
|
||||
- **Client:** Added client support for summary functionality
|
||||
|
||||
**Improvements:**
|
||||
- **Pinecone:** Fixed pinecone version issues
|
||||
- **OpenSearch:** Added logger support
|
||||
- **Testing:** Added python version test environments
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-06-11" description="v0.1.107">
|
||||
|
||||
**Improvements:**
|
||||
- **Documentation:**
|
||||
- Updated Livekit documentation migration
|
||||
- Updated OpenMemory hosted version documentation
|
||||
- **Core:** Updated categorization flow
|
||||
- **Storage:** Fixed migration issues
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-06-09" description="v0.1.106">
|
||||
|
||||
**New Features:**
|
||||
- **Cloudflare:** Added Cloudflare vector store support
|
||||
- **Search:** Added threshold parameter to search functionality
|
||||
- **API:** Added wildcard character support for v2 Memory APIs
|
||||
|
||||
**Improvements:**
|
||||
- **Documentation:** Updated README docs for OpenMemory environment setup
|
||||
- **Core:** Added support for unique user IDs
|
||||
|
||||
**Bug Fixes:**
|
||||
- **Core:** Fixed error handling exceptions
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-06-03" description="v0.1.104">
|
||||
|
||||
**Bug Fixes:**
|
||||
- **Vector Stores:** Fixed GET_ALL functionality for FAISS and OpenSearch
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-06-02" description="v0.1.103">
|
||||
|
||||
**New Features:**
|
||||
- **LLM:** Added support for OpenAI compatible LLM providers with baseUrl configuration
|
||||
|
||||
**Improvements:**
|
||||
- **Documentation:**
|
||||
- Fixed broken links
|
||||
- Improved Graph Memory features documentation clarity
|
||||
- Updated enable_graph documentation
|
||||
- **TypeScript SDK:** Updated Google SDK peer dependency version
|
||||
- **Client:** Added async mode parameter
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-26" description="v0.1.102">
|
||||
|
||||
**New Features:**
|
||||
- **Examples:** Added Neo4j example
|
||||
- **AI SDK:** Added Google provider support
|
||||
- **OpenMemory:** Added LLM and Embedding Providers support
|
||||
|
||||
**Improvements:**
|
||||
- **Documentation:**
|
||||
- Updated memory export documentation
|
||||
- Enhanced role-based memory attribution rules documentation
|
||||
- Updated API reference and messages documentation
|
||||
- Added Mastra and Raycast documentation
|
||||
- Added NOT filter documentation for Search and GetAll V2
|
||||
- Announced Claude 4 support
|
||||
- **Core:**
|
||||
- Removed support for passing string as input in client.add()
|
||||
- Added support for sarvam-m model
|
||||
- **TypeScript SDK:** Fixed types from message interface
|
||||
|
||||
**Bug Fixes:**
|
||||
- **Memory:** Prevented saving prompt artifacts as memory when no new facts are present
|
||||
- **OpenMemory:** Fixed typos in MCP tool description
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-15" description="v0.1.101">
|
||||
|
||||
**New Features:**
|
||||
- **Neo4j:** Added base label configuration support
|
||||
|
||||
**Improvements:**
|
||||
- **Documentation:**
|
||||
- Updated Healthcare example index
|
||||
- Enhanced collaborative task agent documentation clarity
|
||||
- Added criteria-based filtering documentation
|
||||
- **OpenMemory:** Added cURL command for easy installation
|
||||
- **Build:** Migrated to Hatch build system
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-10" description="v0.1.100">
|
||||
|
||||
**New Features:**
|
||||
@@ -288,6 +409,11 @@ mode: "wide"
|
||||
|
||||
<Tab title="TypeScript">
|
||||
|
||||
<Update label="2025-06-20" description="v2.1.32">
|
||||
**Improvement :**
|
||||
- **Client:** Made `api_version` V2 as default.
|
||||
</Update>
|
||||
|
||||
<Update label="2025-06-17" description="v2.1.31">
|
||||
**Improvement :**
|
||||
- **Client:** Added param `filter_memories`.
|
||||
@@ -442,6 +568,104 @@ mode: "wide"
|
||||
|
||||
<Tab title="Platform">
|
||||
|
||||
<Update label="2025-06-19" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Rate Limiting:** Implemented comprehensive rate limiting system
|
||||
|
||||
**Improvements:**
|
||||
- **Performance:** Added performance indexes for memory stats query
|
||||
|
||||
**Bug Fixes:**
|
||||
- **Search:** Fixed search events not respecting top-k parameter
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-06-18" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Memory Management:** Implemented OpenAI Batch API for Memory Cleaning with fallback
|
||||
- **Playground:** Added Claude 4 support on Playground
|
||||
|
||||
**Improvements:**
|
||||
- **Memory:** Added ability to update memory metadata
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-06-17" description="">
|
||||
|
||||
**New Features:**
|
||||
- **UI:** New Memories Page UI design
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-06-16" description="">
|
||||
|
||||
**Improvements:**
|
||||
- **Infrastructure:** Migrated to Application Load Balancer (ALB)
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-06-13" description="">
|
||||
|
||||
**Improvements:**
|
||||
- **Memory Management:** Enhanced Memory Management with Cosine Similarity Fallback
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-06-11" description="">
|
||||
|
||||
**New Features:**
|
||||
- **OMM:** Added OMM Script and UI functionality
|
||||
|
||||
**Improvements:**
|
||||
- **API:** Added filters validation to semantic_search_v2 endpoint
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-06-09" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Intercom:** Set Intercom events for ADD and SEARCH operations
|
||||
- **OpenMemory:** Added Posthog integration and feedback functionality
|
||||
- **MCP:** New JavaScript MCP Server with feedback support
|
||||
|
||||
**Improvements:**
|
||||
- **Structured Data:** Enhanced structured data handling in memory management
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-06-06" description="">
|
||||
|
||||
**New Features:**
|
||||
- **OAuth:** Added Mem0 OAuth integration
|
||||
- **OMM:** Added OMM-Mem0 sync for deleted memories
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-06-05" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Filters:** Implemented Wildcard Filters and refactored filter logic in V2 Views
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-06-02" description="">
|
||||
|
||||
**New Features:**
|
||||
- **OpenMemory Cloud:** Added OpenMemory Cloud support
|
||||
- **Structured Data:** Added 'structured_attributes' field to Memory model
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-30" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Projects:** Added version and enable_graph to project views
|
||||
- **OpenMemory:** Added Postgres support for OpenMemory
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-19" description="">
|
||||
|
||||
**Bug Fixes:**
|
||||
@@ -449,250 +673,6 @@ mode: "wide"
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-17" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Graph:** Added Neo4J Graph Migration
|
||||
- **API:** Added API to set custom instructions
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-16" description="">
|
||||
|
||||
**New Features:**
|
||||
- **API:** Added Org-wide API Limit and Usage
|
||||
|
||||
**Improvements:**
|
||||
- **Database:** Added migration for "is_deleted" column
|
||||
- **Graph:** Improved graph queries
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-15" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Lambda:** Added actions to lambda
|
||||
- **Core:** Added background runs support
|
||||
- **Models:** Added o4-mini for pro users
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-10" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Integrations:** Added Intercom Events integration
|
||||
- **Billing:** Added prefilled email for payments
|
||||
- **Organizations:** Added Pro organization marking
|
||||
|
||||
**Improvements:**
|
||||
- **UI:** Fixed loading jitter for organization selection
|
||||
- **Infrastructure:** Improved production scaling
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-09" description="">
|
||||
|
||||
**Improvements:**
|
||||
- **Memory:** Fixed filters in Memory Page
|
||||
- **Deployment:** Added custom categories for on-premise
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-08" description="">
|
||||
|
||||
**Improvements:**
|
||||
- **Backend:** Updated Django settings for metrics
|
||||
- **Memory:** Added retries to memory filtering
|
||||
- **Search:** Added scoring mechanism
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-07" description="">
|
||||
|
||||
**Improvements:**
|
||||
- **Deployment:** Updated deployment scripts
|
||||
- **Testing:** Added code coverage tracking
|
||||
- **Memory:** Added background cron job for memory quality
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-06" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Models:** Added support for 4.1-mini model
|
||||
|
||||
**Improvements:**
|
||||
- **Infrastructure:** Increased instance count
|
||||
- **API:** Added V2 for Manage Entities
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-05-04" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Testing:** Added code coverage tracking
|
||||
- **AI:** Added Keywords AI integration
|
||||
|
||||
**Improvements:**
|
||||
- **UI:** Updated UI with tabs
|
||||
- **Database:** Added migrations for custom instructions
|
||||
- **Search:** Added criteria filtering
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-26" description="">
|
||||
|
||||
**Improvements:**
|
||||
- **Performance:** Parallelized embedding calls
|
||||
- **Monitoring:** Added timing for LLM calls
|
||||
- **Search:** Added category checking in Search V2
|
||||
- **Bug Fixes:** Fixed issues with ADD filters
|
||||
- **Graph:** Implemented new graph updates
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-25" description="">
|
||||
|
||||
**Improvements:**
|
||||
- **Memory:** Fixed memory export functionality
|
||||
- **Analytics:** Added logging for project
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-24" description="">
|
||||
|
||||
**Improvements:**
|
||||
- **Output:** Added memory_type display for ADD output
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-23" description="">
|
||||
|
||||
**New Features:**
|
||||
- **UI:** Added new Pricing Component
|
||||
- **Memory:** Implemented Long/Short term memory categorization
|
||||
- **Output:** Modified serializer to hide memory_type
|
||||
|
||||
**Documentation:**
|
||||
- Updated README for deployment
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-22" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Memory:** Added timestamp to ADD call
|
||||
|
||||
**Bug Fixes:**
|
||||
- Fixed issues with coreV2
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-21" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Memory:** Implemented backdating with migrations and backfilling script
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-17" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Billing:** Integrated Stripe Billing Dashboard
|
||||
- **Admin:** Added webhook creation functionality
|
||||
|
||||
**Bug Fixes:**
|
||||
- Fixed Users Page issues
|
||||
- Fixed Custom Categories
|
||||
- Fixed Table components
|
||||
- Updated Stripe configuration
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-16" description="">
|
||||
|
||||
**Improvements:**
|
||||
- **Performance:** Made Admin panel and Memory Page faster
|
||||
- **Security:** Implemented active session cancellation
|
||||
- **Analytics:** Added Stripe customer ID capture
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-12" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Memory Management:**
|
||||
- Added ability to delete memories from Project level with filters
|
||||
- Added delete memories capability on Memories Page
|
||||
- **Memory Visualization:** Released V1 Graph Memory Visualization
|
||||
- **Graph Playground:** Enabled for @mem0.ai users
|
||||
- **Notifications:** Added email alerts to organization owners when new members join
|
||||
- **Memory Export:** Added date support for filtering memory exports
|
||||
|
||||
**Improvements:**
|
||||
- **Performance:**
|
||||
- Optimized graph for better performance
|
||||
- Optimized database calls in ADD method
|
||||
- **Analytics:** Added flagging of paid users in Posthog
|
||||
- **CI/CD:** Improved CI pipeline and fixed lint issues
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-10" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Notifications:** Implemented email notifications for organization owners when new members join
|
||||
|
||||
**Improvements:**
|
||||
- **CI/CD:** Fixed Dockerfile for CI tests
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-09" description="">
|
||||
|
||||
**Improvements:**
|
||||
- **Integrations:** Updated chat model for Together Qwen
|
||||
- **Platform:** Removed older platforms
|
||||
- **Bug Fixes:** Fixed FILTER_MAPPING
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-03" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Memory:** Added implicit memory capabilities
|
||||
- **API:** Improved implicit lambda and get_all v2 functionality
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-04-02" description="">
|
||||
|
||||
**New Features:**
|
||||
- **Integrations:** Added Clay integration
|
||||
|
||||
**Improvements:**
|
||||
- **Integrations:** Removed deepseek coder from Together
|
||||
- **API:** Added custom instructions for add v2
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-03-31" description="">
|
||||
|
||||
**Security:**
|
||||
- **Validation:** Added key validation in messages
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2025-03-28" description="">
|
||||
- **Updated Playground Prompt**
|
||||
- **Send Email on User Addition to Org/Proj**
|
||||
- **Fix Search Entity**
|
||||
</Update>
|
||||
|
||||
<Update label="2025-03-19" description="">
|
||||
- **General Stability & Performance Improvements**
|
||||
</Update>
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="Vercel AI SDK">
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Baidu VectorDB (Mochow)
|
||||
---
|
||||
|
||||
[Baidu VectorDB](https://cloud.baidu.com/doc/VDB/index.html) is an enterprise-level distributed vector database service developed by Baidu Intelligent Cloud. It is powered by Baidu's proprietary "Mochow" vector database kernel, providing high performance, availability, and security for vector search.
|
||||
|
||||
### Usage
|
||||
|
||||
```python
|
||||
import os
|
||||
from mem0 import Memory
|
||||
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "baidu",
|
||||
"config": {
|
||||
"endpoint": "http://your-mochow-endpoint:8287",
|
||||
"account": "root",
|
||||
"api_key": "your-api-key",
|
||||
"database_name": "mem0",
|
||||
"table_name": "mem0_table",
|
||||
"embedding_model_dims": 1536,
|
||||
"metric_type": "COSINE"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m = Memory.from_config(config)
|
||||
messages = [
|
||||
{"role": "user", "content": "I'm planning to watch a movie tonight. Any recommendations?"},
|
||||
{"role": "assistant", "content": "How about a thriller movie? 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."}
|
||||
]
|
||||
m.add(messages, user_id="alice", metadata={"category": "movies"})
|
||||
```
|
||||
|
||||
### Config
|
||||
|
||||
Here are the available parameters for the `mochow` config:
|
||||
|
||||
| Parameter | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `endpoint` | Endpoint URL for your Baidu VectorDB instance | Required |
|
||||
| `account` | Baidu VectorDB account name | `root` |
|
||||
| `api_key` | API key for accessing Baidu VectorDB | Required |
|
||||
| `database_name` | Name of the database | `mem0` |
|
||||
| `table_name` | Name of the table | `mem0_table` |
|
||||
| `embedding_model_dims` | Dimensions of the embedding model | `1536` |
|
||||
| `metric_type` | Distance metric for similarity search | `L2` |
|
||||
|
||||
### Distance Metrics
|
||||
|
||||
The following distance metrics are supported:
|
||||
|
||||
- `L2`: Euclidean distance (default)
|
||||
- `IP`: Inner product
|
||||
- `COSINE`: Cosine similarity
|
||||
|
||||
### Index Configuration
|
||||
|
||||
The vector index is automatically configured with the following HNSW parameters:
|
||||
|
||||
- `m`: 16 (number of connections per element)
|
||||
- `efconstruction`: 200 (size of the dynamic candidate list)
|
||||
- `auto_build`: true (automatically build index)
|
||||
- `auto_build_index_policy`: Incremental build with 10000 rows increment
|
||||
+2
-1
@@ -146,7 +146,8 @@
|
||||
"components/vectordbs/dbs/vertex_ai",
|
||||
"components/vectordbs/dbs/weaviate",
|
||||
"components/vectordbs/dbs/faiss",
|
||||
"components/vectordbs/dbs/langchain"
|
||||
"components/vectordbs/dbs/langchain",
|
||||
"components/vectordbs/dbs/baidu"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
+6
-6
@@ -1769,27 +1769,27 @@
|
||||
"x-code-samples": [
|
||||
{
|
||||
"lang": "Python",
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\n# Update a memory\nmemory_id = \"<memory_id>\"\nmessage = \"Your updated memory message here\"\nclient.update(memory_id, message)"
|
||||
"source": "# To use the Python SDK, install the package:\n# pip install mem0ai\n\nfrom mem0 import MemoryClient\nclient = MemoryClient(api_key=\"your_api_key\", org_id=\"your_org_id\", project_id=\"your_project_id\")\n\n# Update a memory\nmemory_id = \"<memory_id>\"\nclient.update(\n memory_id=memory_id,\n text=\"Your updated memory message here\",\n metadata={\"category\": \"example\"}\n)"
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\n// Update a specific memory\nconst memory_id=<memory_id>\nconst message=\"Your updated memory message here\"\nclient.update(memory_id, message)\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\n// Update a specific memory\nconst memory_id = \"<memory_id>\";\nclient.update(memory_id, { \n text: \"Your updated memory message here\",\n metadata: { category: \"example\" }\n})\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
"source": "curl --request PUT \\\n --url https://api.mem0.ai/v1/memories/{memory_id}/ \\\n --header 'Authorization: Token <api-key>' \\\n --header 'Content-Type: application/json' \\\n --data '{\"text\": \"Your updated memory text here\"}'"
|
||||
"source": "curl --request PUT \\\n --url https://api.mem0.ai/v1/memories/{memory_id}/ \\\n --header 'Authorization: Token <api-key>' \\\n --header 'Content-Type: application/json' \\\n --data '{\"text\": \"Your updated memory text here\", \"metadata\": {\"category\": \"example\"}}'"
|
||||
},
|
||||
{
|
||||
"lang": "Go",
|
||||
"source": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\n\turl := \"https://api.mem0.ai/v1/memories/{memory_id}/\"\n\n\tpayload := strings.NewReader(`{\n\t\"text\": \"Your updated memory text here\"\n}`)\n\n\treq, _ := http.NewRequest(\"PUT\", url, payload)\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(res)\n\tfmt.Println(string(body))\n\n}"
|
||||
"source": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"net/http\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\n\turl := \"https://api.mem0.ai/v1/memories/{memory_id}/\"\n\n\tpayload := strings.NewReader(`{\n\t\"text\": \"Your updated memory text here\",\n\t\"metadata\": {\n\t\t\"category\": \"example\"\n\t}\n}`)\n\n\treq, _ := http.NewRequest(\"PUT\", url, payload)\n\n\treq.Header.Add(\"Authorization\", \"Token <api-key>\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tres, _ := http.DefaultClient.Do(req)\n\n\tdefer res.Body.Close()\n\tbody, _ := ioutil.ReadAll(res.Body)\n\n\tfmt.Println(res)\n\tfmt.Println(string(body))\n\n}"
|
||||
},
|
||||
{
|
||||
"lang": "PHP",
|
||||
"source": "<?php\n\n$curl = curl_init();\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/v1/memories/{memory_id}/\",\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"PUT\",\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\",\n \"Content-Type: application/json\"\n ],\n CURLOPT_POSTFIELDS => json_encode({\n \"text\": \"Your updated memory text here\"\n })\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
"source": "<?php\n\n$curl = curl_init();\n\ncurl_setopt_array($curl, [\n CURLOPT_URL => \"https://api.mem0.ai/v1/memories/{memory_id}/\",\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_ENCODING => \"\",\n CURLOPT_MAXREDIRS => 10,\n CURLOPT_TIMEOUT => 30,\n CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,\n CURLOPT_CUSTOMREQUEST => \"PUT\",\n CURLOPT_HTTPHEADER => [\n \"Authorization: Token <api-key>\",\n \"Content-Type: application/json\"\n ],\n CURLOPT_POSTFIELDS => json_encode([\n \"text\" => \"Your updated memory text here\",\n \"metadata\" => [\"category\" => \"example\"]\n ])\n]);\n\n$response = curl_exec($curl);\n$err = curl_error($curl);\n\ncurl_close($curl);\n\nif ($err) {\n echo \"cURL Error #:\" . $err;\n} else {\n echo $response;\n}"
|
||||
},
|
||||
{
|
||||
"lang": "Java",
|
||||
"source": "HttpResponse<String> response = Unirest.put(\"https://api.mem0.ai/v1/memories/{memory_id}/\")\n .header(\"Authorization\", \"Token <api-key>\")\n .header(\"Content-Type\", \"application/json\")\n .body({\"text\": \"Your updated memory text here\"})\n .asString();"
|
||||
"source": "HttpResponse<String> response = Unirest.put(\"https://api.mem0.ai/v1/memories/{memory_id}/\")\n .header(\"Authorization\", \"Token <api-key>\")\n .header(\"Content-Type\", \"application/json\")\n .body(\"{\\\"text\\\": \\\"Your updated memory text here\\\", \\\"metadata\\\": {\\\"category\\\": \\\"example\\\"}}\")\n .asString();"
|
||||
}
|
||||
],
|
||||
"x-codegen-request-body-name": "data"
|
||||
|
||||
+14
-2
@@ -333,11 +333,23 @@ const memory = new Memory();
|
||||
<CodeGroup>
|
||||
```python Code
|
||||
# For a user
|
||||
result = m.add("I like to drink coffee in the morning and go for a walk.", user_id="alice", metadata={"category": "preferences"})
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I like to drink coffee in the morning and go for a walk"
|
||||
}
|
||||
]
|
||||
result = m.add(messages, user_id="alice", metadata={"category": "preferences"})
|
||||
```
|
||||
|
||||
```typescript TypeScript
|
||||
const result = memory.add("I like to drink coffee in the morning and go for a walk.", { userId: "alice", metadata: { category: "preferences" } });
|
||||
const messages = [
|
||||
{
|
||||
role: "user",
|
||||
content: "I like to drink coffee in the morning and go for a walk"
|
||||
}
|
||||
];
|
||||
const result = memory.add(messages, { userId: "alice", metadata: { category: "preferences" } });
|
||||
```
|
||||
|
||||
```json Output
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mem0ai",
|
||||
"version": "2.1.31",
|
||||
"version": "2.1.32",
|
||||
"description": "The Memory Layer For Your AI Apps",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
|
||||
@@ -229,7 +229,7 @@ export default class MemoryClient {
|
||||
}
|
||||
|
||||
if (options.api_version) {
|
||||
options.version = options.api_version.toString();
|
||||
options.version = options.api_version.toString() || "v2";
|
||||
}
|
||||
|
||||
const payload = this._preparePayload(messages, options);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import type { TelemetryClient, TelemetryOptions } from "./telemetry.types";
|
||||
|
||||
let version = "2.1.26";
|
||||
let version = "2.1.32";
|
||||
|
||||
// Safely check for process.env in different environments
|
||||
let MEM0_TELEMETRY = true;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
from enum import Enum
|
||||
from typing import Any, Dict
|
||||
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
|
||||
|
||||
class BaiduDBConfig(BaseModel):
|
||||
endpoint: str = Field("http://localhost:8287", description="Endpoint URL for Baidu VectorDB")
|
||||
account: str = Field("root", description="Account for Baidu VectorDB")
|
||||
api_key: str = Field(None, description="API Key for Baidu VectorDB")
|
||||
database_name: str = Field("mem0", description="Name of the database")
|
||||
table_name: str = Field("mem0", description="Name of the table")
|
||||
embedding_model_dims: int = Field(1536, description="Dimensions of the embedding model")
|
||||
metric_type: str = Field("L2", description="Metric type for similarity search")
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def validate_extra_fields(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
||||
allowed_fields = set(cls.model_fields.keys())
|
||||
input_fields = set(values.keys())
|
||||
extra_fields = input_fields - allowed_fields
|
||||
if extra_fields:
|
||||
raise ValueError(
|
||||
f"Extra fields not allowed: {', '.join(extra_fields)}. Please input only the following fields: {', '.join(allowed_fields)}"
|
||||
)
|
||||
return values
|
||||
|
||||
model_config = {
|
||||
"arbitrary_types_allowed": True,
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
import logging
|
||||
import time
|
||||
from typing import Dict, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from mem0.vector_stores.base import VectorStoreBase
|
||||
|
||||
try:
|
||||
import pymochow
|
||||
from pymochow.configuration import Configuration
|
||||
from pymochow.auth.bce_credentials import BceCredentials
|
||||
from pymochow.model.enum import FieldType, MetricType, IndexType, TableState, ServerErrCode
|
||||
from pymochow.model.schema import Field, Schema, VectorIndex, FilteringIndex, HNSWParams, AutoBuildRowCountIncrement
|
||||
from pymochow.model.table import Partition, Row, VectorSearchConfig, VectorTopkSearchRequest, FloatVector
|
||||
from pymochow.exception import ServerError
|
||||
except ImportError:
|
||||
raise ImportError("The 'pymochow' library is required. Please install it using 'pip install pymochow'.")
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OutputData(BaseModel):
|
||||
id: Optional[str] # memory id
|
||||
score: Optional[float] # distance
|
||||
payload: Optional[Dict] # metadata
|
||||
|
||||
|
||||
class BaiduDB(VectorStoreBase):
|
||||
def __init__(
|
||||
self,
|
||||
endpoint: str,
|
||||
account: str,
|
||||
api_key: str,
|
||||
database_name: str,
|
||||
table_name: str,
|
||||
embedding_model_dims: int,
|
||||
metric_type: MetricType,
|
||||
) -> None:
|
||||
"""Initialize the BaiduDB database.
|
||||
|
||||
Args:
|
||||
endpoint (str): Endpoint URL for Baidu VectorDB.
|
||||
account (str): Account for Baidu VectorDB.
|
||||
api_key (str): API Key for Baidu VectorDB.
|
||||
database_name (str): Name of the database.
|
||||
table_name (str): Name of the table.
|
||||
embedding_model_dims (int): Dimensions of the embedding model.
|
||||
metric_type (MetricType): Metric type for similarity search.
|
||||
"""
|
||||
self.endpoint = endpoint
|
||||
self.account = account
|
||||
self.api_key = api_key
|
||||
self.database_name = database_name
|
||||
self.table_name = table_name
|
||||
self.embedding_model_dims = embedding_model_dims
|
||||
self.metric_type = metric_type
|
||||
|
||||
# Initialize Mochow client
|
||||
config = Configuration(credentials=BceCredentials(account, api_key), endpoint=endpoint)
|
||||
self.client = pymochow.MochowClient(config)
|
||||
|
||||
# Ensure database and table exist
|
||||
self._create_database_if_not_exists()
|
||||
self.create_col(
|
||||
name=self.table_name,
|
||||
vector_size=self.embedding_model_dims,
|
||||
distance=self.metric_type,
|
||||
)
|
||||
|
||||
def _create_database_if_not_exists(self):
|
||||
"""Create database if it doesn't exist."""
|
||||
try:
|
||||
# Check if database exists
|
||||
databases = self.client.list_databases()
|
||||
db_exists = any(db.database_name == self.database_name for db in databases)
|
||||
if not db_exists:
|
||||
self._database = self.client.create_database(self.database_name)
|
||||
logger.info(f"Created database: {self.database_name}")
|
||||
else:
|
||||
self._database = self.client.database(self.database_name)
|
||||
logger.info(f"Database {self.database_name} already exists")
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating database: {e}")
|
||||
raise
|
||||
|
||||
def create_col(self, name, vector_size, distance):
|
||||
"""Create a new table.
|
||||
|
||||
Args:
|
||||
name (str): Name of the table to create.
|
||||
vector_size (int): Dimension of the vector.
|
||||
distance (str): Metric type for similarity search.
|
||||
"""
|
||||
# Check if table already exists
|
||||
try:
|
||||
tables = self._database.list_table()
|
||||
table_exists = any(table.table_name == name for table in tables)
|
||||
if table_exists:
|
||||
logger.info(f"Table {name} already exists. Skipping creation.")
|
||||
self._table = self._database.describe_table(name)
|
||||
return
|
||||
|
||||
# Convert distance string to MetricType enum
|
||||
metric_type = None
|
||||
for k, v in MetricType.__members__.items():
|
||||
if k == distance:
|
||||
metric_type = v
|
||||
if metric_type is None:
|
||||
raise ValueError(f"Unsupported metric_type: {distance}")
|
||||
|
||||
# Define table schema
|
||||
fields = [
|
||||
Field(
|
||||
"id", FieldType.STRING, primary_key=True, partition_key=True, auto_increment=False, not_null=True
|
||||
),
|
||||
Field("vector", FieldType.FLOAT_VECTOR, dimension=vector_size),
|
||||
Field("metadata", FieldType.JSON),
|
||||
]
|
||||
|
||||
# Create vector index
|
||||
indexes = [
|
||||
VectorIndex(
|
||||
index_name="vector_idx",
|
||||
index_type=IndexType.HNSW,
|
||||
field="vector",
|
||||
metric_type=metric_type,
|
||||
params=HNSWParams(m=16, efconstruction=200),
|
||||
auto_build=True,
|
||||
auto_build_index_policy=AutoBuildRowCountIncrement(row_count_increment=10000),
|
||||
),
|
||||
FilteringIndex(index_name="metadata_filtering_idx", fields=["metadata"]),
|
||||
]
|
||||
|
||||
schema = Schema(fields=fields, indexes=indexes)
|
||||
|
||||
# Create table
|
||||
self._table = self._database.create_table(
|
||||
table_name=name, replication=3, partition=Partition(partition_num=1), schema=schema
|
||||
)
|
||||
logger.info(f"Created table: {name}")
|
||||
|
||||
# Wait for table to be ready
|
||||
while True:
|
||||
time.sleep(2)
|
||||
table = self._database.describe_table(name)
|
||||
if table.state == TableState.NORMAL:
|
||||
logger.info(f"Table {name} is ready.")
|
||||
break
|
||||
logger.info(f"Waiting for table {name} to be ready, current state: {table.state}")
|
||||
self._table = table
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating table: {e}")
|
||||
raise
|
||||
|
||||
def insert(self, vectors, payloads=None, ids=None):
|
||||
"""Insert vectors into the table.
|
||||
|
||||
Args:
|
||||
vectors (List[List[float]]): List of vectors to insert.
|
||||
payloads (List[Dict], optional): List of payloads corresponding to vectors.
|
||||
ids (List[str], optional): List of IDs corresponding to vectors.
|
||||
"""
|
||||
# Prepare data for insertion
|
||||
for idx, vector, metadata in zip(ids, vectors, payloads):
|
||||
row = Row(id=idx, vector=vector, metadata=metadata)
|
||||
self._table.upsert(rows=[row])
|
||||
|
||||
def search(self, query: str, vectors: list, limit: int = 5, filters: dict = None) -> list:
|
||||
"""
|
||||
Search for similar vectors.
|
||||
|
||||
Args:
|
||||
query (str): Query string.
|
||||
vectors (List[float]): Query vector.
|
||||
limit (int, optional): Number of results to return. Defaults to 5.
|
||||
filters (Dict, optional): Filters to apply to the search. Defaults to None.
|
||||
|
||||
Returns:
|
||||
list: Search results.
|
||||
"""
|
||||
# Add filters if provided
|
||||
search_filter = None
|
||||
if filters:
|
||||
search_filter = self._create_filter(filters)
|
||||
|
||||
# Create AnnSearch for vector search
|
||||
request = VectorTopkSearchRequest(
|
||||
vector_field="vector",
|
||||
vector=FloatVector(vectors),
|
||||
limit=limit,
|
||||
filter=search_filter,
|
||||
config=VectorSearchConfig(ef=200),
|
||||
)
|
||||
|
||||
# Perform search
|
||||
projections = ["id", "metadata"]
|
||||
res = self._table.vector_search(request=request, projections=projections)
|
||||
|
||||
# Parse results
|
||||
output = []
|
||||
for row in res.rows:
|
||||
row_data = row.get("row", {})
|
||||
output_data = OutputData(
|
||||
id=row_data.get("id"), score=row.get("score", 0.0), payload=row_data.get("metadata", {})
|
||||
)
|
||||
output.append(output_data)
|
||||
|
||||
return output
|
||||
|
||||
def delete(self, vector_id):
|
||||
"""
|
||||
Delete a vector by ID.
|
||||
|
||||
Args:
|
||||
vector_id (str): ID of the vector to delete.
|
||||
"""
|
||||
self._table.delete(primary_key={"id": vector_id})
|
||||
|
||||
def update(self, vector_id=None, vector=None, payload=None):
|
||||
"""
|
||||
Update a vector and its payload.
|
||||
|
||||
Args:
|
||||
vector_id (str): ID of the vector to update.
|
||||
vector (List[float], optional): Updated vector.
|
||||
payload (Dict, optional): Updated payload.
|
||||
"""
|
||||
row = Row(id=vector_id, vector=vector, metadata=payload)
|
||||
self._table.upsert(rows=[row])
|
||||
|
||||
def get(self, vector_id):
|
||||
"""
|
||||
Retrieve a vector by ID.
|
||||
|
||||
Args:
|
||||
vector_id (str): ID of the vector to retrieve.
|
||||
|
||||
Returns:
|
||||
OutputData: Retrieved vector.
|
||||
"""
|
||||
projections = ["id", "metadata"]
|
||||
result = self._table.query(primary_key={"id": vector_id}, projections=projections)
|
||||
row = result.row
|
||||
return OutputData(id=row.get("id"), score=None, payload=row.get("metadata", {}))
|
||||
|
||||
def list_cols(self):
|
||||
"""
|
||||
List all tables (collections).
|
||||
|
||||
Returns:
|
||||
List[str]: List of table names.
|
||||
"""
|
||||
tables = self._database.list_table()
|
||||
return [table.table_name for table in tables]
|
||||
|
||||
def delete_col(self):
|
||||
"""Delete the table."""
|
||||
try:
|
||||
tables = self._database.list_table()
|
||||
|
||||
# skip drop table if table not exists
|
||||
table_exists = any(table.table_name == self.table_name for table in tables)
|
||||
if not table_exists:
|
||||
logger.info(f"Table {self.table_name} does not exist, skipping deletion")
|
||||
return
|
||||
|
||||
# Delete the table
|
||||
self._database.drop_table(self.table_name)
|
||||
logger.info(f"Initiated deletion of table {self.table_name}")
|
||||
|
||||
# Wait for table to be completely deleted
|
||||
while True:
|
||||
time.sleep(2)
|
||||
try:
|
||||
self._database.describe_table(self.table_name)
|
||||
logger.info(f"Waiting for table {self.table_name} to be deleted...")
|
||||
except ServerError as e:
|
||||
if e.code == ServerErrCode.TABLE_NOT_EXIST:
|
||||
logger.info(f"Table {self.table_name} has been completely deleted")
|
||||
break
|
||||
logger.error(f"Error checking table status: {e}")
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting table: {e}")
|
||||
raise
|
||||
|
||||
def col_info(self):
|
||||
"""
|
||||
Get information about the table.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: Table information.
|
||||
"""
|
||||
return self._table.stats()
|
||||
|
||||
def list(self, filters: dict = None, limit: int = 100) -> list:
|
||||
"""
|
||||
List all vectors in the table.
|
||||
|
||||
Args:
|
||||
filters (Dict, optional): Filters to apply to the list.
|
||||
limit (int, optional): Number of vectors to return. Defaults to 100.
|
||||
|
||||
Returns:
|
||||
List[OutputData]: List of vectors.
|
||||
"""
|
||||
projections = ["id", "metadata"]
|
||||
list_filter = self._create_filter(filters) if filters else None
|
||||
result = self._table.select(filter=list_filter, projections=projections, limit=limit)
|
||||
|
||||
memories = []
|
||||
for row in result.rows:
|
||||
obj = OutputData(id=row.get("id"), score=None, payload=row.get("metadata", {}))
|
||||
memories.append(obj)
|
||||
|
||||
return [memories]
|
||||
|
||||
def reset(self):
|
||||
"""Reset the table by deleting and recreating it."""
|
||||
logger.warning(f"Resetting table {self.table_name}...")
|
||||
try:
|
||||
self.delete_col()
|
||||
self.create_col(
|
||||
name=self.table_name,
|
||||
vector_size=self.embedding_model_dims,
|
||||
distance=self.metric_type,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Error resetting table: {e}")
|
||||
raise
|
||||
|
||||
def _create_filter(self, filters: dict) -> str:
|
||||
"""
|
||||
Create filter expression for queries.
|
||||
|
||||
Args:
|
||||
filters (dict): Filter conditions.
|
||||
|
||||
Returns:
|
||||
str: Filter expression.
|
||||
"""
|
||||
conditions = []
|
||||
for key, value in filters.items():
|
||||
if isinstance(value, str):
|
||||
conditions.append(f'metadata["{key}"] = "{value}"')
|
||||
else:
|
||||
conditions.append(f'metadata["{key}"] = {value}')
|
||||
return " AND ".join(conditions)
|
||||
@@ -17,6 +17,7 @@ class VectorStoreConfig(BaseModel):
|
||||
"pinecone": "PineconeConfig",
|
||||
"mongodb": "MongoDBConfig",
|
||||
"milvus": "MilvusDBConfig",
|
||||
"baidu": "BaiduDBConfig",
|
||||
"upstash_vector": "UpstashVectorConfig",
|
||||
"azure_ai_search": "AzureAISearchConfig",
|
||||
"redis": "RedisDBConfig",
|
||||
|
||||
@@ -83,8 +83,8 @@ class OpenSearchDB(VectorStoreBase):
|
||||
},
|
||||
}
|
||||
|
||||
logger.warning(f"Creating index {name}, it might take 1-2 minutes...")
|
||||
if not self.client.indices.exists(index=name):
|
||||
logger.warning(f"Creating index {name}, it might take 1-2 minutes...")
|
||||
self.client.indices.create(index=name, body=index_settings)
|
||||
|
||||
# Wait for index to be ready
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "mem0ai"
|
||||
version = "0.1.109"
|
||||
version = "0.1.110"
|
||||
description = "Long-term memory for AI Agents"
|
||||
authors = [
|
||||
{ name = "Mem0", email = "founders@mem0.ai" }
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
from unittest.mock import Mock, patch, PropertyMock
|
||||
|
||||
import pytest
|
||||
|
||||
from mem0.vector_stores.baidu import BaiduDB, OutputData
|
||||
from pymochow.model.enum import MetricType, TableState, ServerErrCode
|
||||
from pymochow.model.schema import Field, Schema, VectorIndex, FilteringIndex, HNSWParams, AutoBuildRowCountIncrement
|
||||
from pymochow.model.table import Partition, Row, VectorSearchConfig, VectorTopkSearchRequest, FloatVector, Table
|
||||
from pymochow.exception import ServerError
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_mochow_client():
|
||||
with patch("pymochow.MochowClient") as mock_client:
|
||||
yield mock_client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_configuration():
|
||||
with patch("pymochow.configuration.Configuration") as mock_config:
|
||||
yield mock_config
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_bce_credentials():
|
||||
with patch("pymochow.auth.bce_credentials.BceCredentials") as mock_creds:
|
||||
yield mock_creds
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_table():
|
||||
mock_table = Mock(spec=Table)
|
||||
# 设置 Table 类的属性
|
||||
type(mock_table).database_name = PropertyMock(return_value="test_db")
|
||||
type(mock_table).table_name = PropertyMock(return_value="test_table")
|
||||
type(mock_table).schema = PropertyMock(return_value=Mock())
|
||||
type(mock_table).replication = PropertyMock(return_value=1)
|
||||
type(mock_table).partition = PropertyMock(return_value=Mock())
|
||||
type(mock_table).enable_dynamic_field = PropertyMock(return_value=False)
|
||||
type(mock_table).description = PropertyMock(return_value="")
|
||||
type(mock_table).create_time = PropertyMock(return_value="")
|
||||
type(mock_table).state = PropertyMock(return_value=TableState.NORMAL)
|
||||
type(mock_table).aliases = PropertyMock(return_value=[])
|
||||
return mock_table
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mochow_instance(mock_mochow_client, mock_configuration, mock_bce_credentials, mock_table):
|
||||
mock_database = Mock()
|
||||
mock_client_instance = Mock()
|
||||
|
||||
# Mock the client creation
|
||||
mock_mochow_client.return_value = mock_client_instance
|
||||
|
||||
# Mock database operations
|
||||
mock_client_instance.list_databases.return_value = []
|
||||
mock_client_instance.create_database.return_value = mock_database
|
||||
mock_client_instance.database.return_value = mock_database
|
||||
|
||||
# Mock table operations
|
||||
mock_database.list_table.return_value = []
|
||||
mock_database.create_table.return_value = mock_table
|
||||
mock_database.describe_table.return_value = Mock(state=TableState.NORMAL)
|
||||
mock_database.table.return_value = mock_table
|
||||
|
||||
return BaiduDB(
|
||||
endpoint="http://localhost:8287",
|
||||
account="test_account",
|
||||
api_key="test_api_key",
|
||||
database_name="test_db",
|
||||
table_name="test_table",
|
||||
embedding_model_dims=128,
|
||||
metric_type="COSINE",
|
||||
)
|
||||
|
||||
|
||||
def test_insert(mochow_instance, mock_mochow_client):
|
||||
vectors = [[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]]
|
||||
payloads = [{"name": "vector1"}, {"name": "vector2"}]
|
||||
ids = ["id1", "id2"]
|
||||
|
||||
mochow_instance.insert(vectors=vectors, payloads=payloads, ids=ids)
|
||||
|
||||
# Verify table.upsert was called with correct data
|
||||
assert mochow_instance._table.upsert.call_count == 2
|
||||
calls = mochow_instance._table.upsert.call_args_list
|
||||
|
||||
# Check first call
|
||||
first_row = calls[0][1]["rows"][0]
|
||||
assert first_row._data["id"] == "id1"
|
||||
assert first_row._data["vector"] == [0.1, 0.2, 0.3]
|
||||
assert first_row._data["metadata"] == {"name": "vector1"}
|
||||
|
||||
# Check second call
|
||||
second_row = calls[1][1]["rows"][0]
|
||||
assert second_row._data["id"] == "id2"
|
||||
assert second_row._data["vector"] == [0.4, 0.5, 0.6]
|
||||
assert second_row._data["metadata"] == {"name": "vector2"}
|
||||
|
||||
|
||||
def test_search(mochow_instance, mock_mochow_client):
|
||||
# Mock search results
|
||||
mock_search_results = Mock()
|
||||
mock_search_results.rows = [
|
||||
{"row": {"id": "id1", "metadata": {"name": "vector1"}}, "score": 0.1},
|
||||
{"row": {"id": "id2", "metadata": {"name": "vector2"}}, "score": 0.2},
|
||||
]
|
||||
mochow_instance._table.vector_search.return_value = mock_search_results
|
||||
|
||||
vectors = [0.1, 0.2, 0.3]
|
||||
results = mochow_instance.search(query="test", vectors=vectors, limit=2)
|
||||
|
||||
# Verify search was called with correct parameters
|
||||
mochow_instance._table.vector_search.assert_called_once()
|
||||
call_args = mochow_instance._table.vector_search.call_args
|
||||
request = call_args[0][0] if call_args[0] else call_args[1]["request"]
|
||||
|
||||
assert isinstance(request, VectorTopkSearchRequest)
|
||||
assert request._vector_field == "vector"
|
||||
assert isinstance(request._vector, FloatVector)
|
||||
assert request._vector._floats == vectors
|
||||
assert request._limit == 2
|
||||
assert isinstance(request._config, VectorSearchConfig)
|
||||
assert request._config._ef == 200
|
||||
|
||||
# Verify results
|
||||
assert len(results) == 2
|
||||
assert results[0].id == "id1"
|
||||
assert results[0].score == 0.1
|
||||
assert results[0].payload == {"name": "vector1"}
|
||||
assert results[1].id == "id2"
|
||||
assert results[1].score == 0.2
|
||||
assert results[1].payload == {"name": "vector2"}
|
||||
|
||||
|
||||
def test_search_with_filters(mochow_instance, mock_mochow_client):
|
||||
mochow_instance._table.vector_search.return_value = Mock(rows=[])
|
||||
|
||||
vectors = [0.1, 0.2, 0.3]
|
||||
filters = {"user_id": "user123", "agent_id": "agent456"}
|
||||
|
||||
mochow_instance.search(query="test", vectors=vectors, limit=2, filters=filters)
|
||||
|
||||
# Verify search was called with filter
|
||||
call_args = mochow_instance._table.vector_search.call_args
|
||||
request = call_args[0][0] if call_args[0] else call_args[1]["request"]
|
||||
|
||||
assert request._filter == 'metadata["user_id"] = "user123" AND metadata["agent_id"] = "agent456"'
|
||||
|
||||
|
||||
def test_delete(mochow_instance, mock_mochow_client):
|
||||
vector_id = "id1"
|
||||
mochow_instance.delete(vector_id=vector_id)
|
||||
|
||||
mochow_instance._table.delete.assert_called_once_with(primary_key={"id": vector_id})
|
||||
|
||||
|
||||
def test_update(mochow_instance, mock_mochow_client):
|
||||
vector_id = "id1"
|
||||
new_vector = [0.7, 0.8, 0.9]
|
||||
new_payload = {"name": "updated_vector"}
|
||||
|
||||
mochow_instance.update(vector_id=vector_id, vector=new_vector, payload=new_payload)
|
||||
|
||||
mochow_instance._table.upsert.assert_called_once()
|
||||
call_args = mochow_instance._table.upsert.call_args
|
||||
row = call_args[0][0] if call_args[0] else call_args[1]["rows"][0]
|
||||
|
||||
assert row._data["id"] == vector_id
|
||||
assert row._data["vector"] == new_vector
|
||||
assert row._data["metadata"] == new_payload
|
||||
|
||||
|
||||
def test_get(mochow_instance, mock_mochow_client):
|
||||
# Mock query result
|
||||
mock_result = Mock()
|
||||
mock_result.row = {"id": "id1", "metadata": {"name": "vector1"}}
|
||||
mochow_instance._table.query.return_value = mock_result
|
||||
|
||||
result = mochow_instance.get(vector_id="id1")
|
||||
|
||||
mochow_instance._table.query.assert_called_once_with(primary_key={"id": "id1"}, projections=["id", "metadata"])
|
||||
|
||||
assert result.id == "id1"
|
||||
assert result.score is None
|
||||
assert result.payload == {"name": "vector1"}
|
||||
|
||||
|
||||
def test_list(mochow_instance, mock_mochow_client):
|
||||
# Mock select result
|
||||
mock_result = Mock()
|
||||
mock_result.rows = [{"id": "id1", "metadata": {"name": "vector1"}}, {"id": "id2", "metadata": {"name": "vector2"}}]
|
||||
mochow_instance._table.select.return_value = mock_result
|
||||
|
||||
results = mochow_instance.list(limit=2)
|
||||
|
||||
mochow_instance._table.select.assert_called_once_with(filter=None, projections=["id", "metadata"], limit=2)
|
||||
|
||||
assert len(results[0]) == 2
|
||||
assert results[0][0].id == "id1"
|
||||
assert results[0][1].id == "id2"
|
||||
|
||||
|
||||
def test_list_cols(mochow_instance, mock_mochow_client):
|
||||
# Mock table list
|
||||
mock_tables = [
|
||||
Mock(spec=Table, database_name="test_db", table_name="table1"),
|
||||
Mock(spec=Table, database_name="test_db", table_name="table2"),
|
||||
]
|
||||
mochow_instance._database.list_table.return_value = mock_tables
|
||||
|
||||
result = mochow_instance.list_cols()
|
||||
|
||||
assert result == ["table1", "table2"]
|
||||
|
||||
|
||||
def test_delete_col_not_exists(mochow_instance, mock_mochow_client):
|
||||
# 使用正确的 ServerErrCode 枚举值
|
||||
mochow_instance._database.drop_table.side_effect = ServerError(
|
||||
"Table not exists", code=ServerErrCode.TABLE_NOT_EXIST
|
||||
)
|
||||
|
||||
# Should not raise exception
|
||||
mochow_instance.delete_col()
|
||||
|
||||
|
||||
def test_col_info(mochow_instance, mock_mochow_client):
|
||||
mock_table_info = {"table_name": "test_table", "fields": []}
|
||||
mochow_instance._table.stats.return_value = mock_table_info
|
||||
|
||||
result = mochow_instance.col_info()
|
||||
|
||||
assert result == mock_table_info
|
||||
Reference in New Issue
Block a user