mdb-engine 0.7.1__tar.gz → 0.7.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mdb_engine-0.7.1/mdb_engine.egg-info → mdb_engine-0.7.3}/PKG-INFO +1 -15
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/README.md +0 -14
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/__init__.py +1 -9
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/main.py +1 -1
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/memory/README.md +34 -3
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/memory/service.py +266 -2
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/routing/websockets.py +45 -6
- {mdb_engine-0.7.1 → mdb_engine-0.7.3/mdb_engine.egg-info}/PKG-INFO +1 -15
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/pyproject.toml +1 -1
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/setup.py +1 -1
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/LICENSE +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/MANIFEST.in +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/README.md +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/ARCHITECTURE.md +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/README.md +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/__init__.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/audit.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/base.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/casbin_factory.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/casbin_models.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/config_defaults.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/config_helpers.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/cookie_utils.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/csrf.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/decorators.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/dependencies.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/helpers.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/integration.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/jwt.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/middleware.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/oso_factory.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/provider.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/rate_limiter.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/restrictions.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/session_manager.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/shared_middleware.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/shared_users.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/token_lifecycle.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/token_store.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/users.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/utils.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/websocket_sessions.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/websocket_tickets.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/__init__.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/commands/__init__.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/commands/generate.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/commands/migrate.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/commands/show.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/commands/validate.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/utils.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/config.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/constants.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/README.md +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/__init__.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/app_registration.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/app_secrets.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/connection.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/encryption.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/engine.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/index_management.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/manifest.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/ray_integration.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/seeding.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/service_initialization.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/types.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/database/README.md +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/database/__init__.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/database/abstraction.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/database/connection.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/database/query_validator.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/database/resource_limiter.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/database/scoped_wrapper.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/dependencies.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/di/__init__.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/di/container.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/di/providers.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/di/scopes.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/embeddings/README.md +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/embeddings/__init__.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/embeddings/dependencies.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/embeddings/service.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/exceptions.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/indexes/README.md +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/indexes/__init__.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/indexes/helpers.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/indexes/manager.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/memory/__init__.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/observability/README.md +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/observability/__init__.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/observability/health.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/observability/logging.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/observability/metrics.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/repositories/__init__.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/repositories/base.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/repositories/mongo.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/repositories/unit_of_work.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/routing/README.md +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/routing/__init__.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/utils/__init__.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/utils/mongo.py +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine.egg-info/SOURCES.txt +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine.egg-info/dependency_links.txt +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine.egg-info/entry_points.txt +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine.egg-info/requires.txt +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine.egg-info/top_level.txt +0 -0
- {mdb_engine-0.7.1 → mdb_engine-0.7.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mdb-engine
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
4
4
|
Summary: MongoDB Engine
|
|
5
5
|
Home-page: https://github.com/ranfysvalle02/mdb-engine
|
|
6
6
|
Author: Fabian Valle
|
|
@@ -72,20 +72,6 @@ Dynamic: requires-python
|
|
|
72
72
|
[](https://www.python.org/downloads/)
|
|
73
73
|
[](https://opensource.org/licenses/AGPL-3.0)
|
|
74
74
|
|
|
75
|
-
## 🎉 What's New in v0.7.0
|
|
76
|
-
|
|
77
|
-
**FastAPI Native WebSocket Support**: MDB-Engine now uses FastAPI's `APIRouter` approach for WebSocket registration in both single-app and multi-app modes. This provides:
|
|
78
|
-
|
|
79
|
-
- ✅ **Full FastAPI Feature Support**: Dependency injection, OpenAPI documentation, request/response models
|
|
80
|
-
- ✅ **Consistency**: Same registration pattern across single-app and multi-app modes
|
|
81
|
-
- ✅ **Best Practices**: Follows FastAPI's recommended WebSocket registration patterns
|
|
82
|
-
- ✅ **Better Maintainability**: Uses FastAPI abstractions instead of low-level Starlette APIs
|
|
83
|
-
- ✅ **Route Priority**: WebSocket routes registered before mounted apps ensure proper routing
|
|
84
|
-
|
|
85
|
-
**Value**: This change ensures WebSocket endpoints benefit from all FastAPI features, making your code more maintainable and consistent with FastAPI best practices.
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
75
|
## 🎯 manifest.json: The Key to Everything
|
|
90
76
|
|
|
91
77
|
**`manifest.json` is the foundation of your application.** It's a single configuration file that defines your app's identity, data structure, authentication, indexes, and services. Everything flows from this file.
|
|
@@ -6,20 +6,6 @@
|
|
|
6
6
|
[](https://www.python.org/downloads/)
|
|
7
7
|
[](https://opensource.org/licenses/AGPL-3.0)
|
|
8
8
|
|
|
9
|
-
## 🎉 What's New in v0.7.0
|
|
10
|
-
|
|
11
|
-
**FastAPI Native WebSocket Support**: MDB-Engine now uses FastAPI's `APIRouter` approach for WebSocket registration in both single-app and multi-app modes. This provides:
|
|
12
|
-
|
|
13
|
-
- ✅ **Full FastAPI Feature Support**: Dependency injection, OpenAPI documentation, request/response models
|
|
14
|
-
- ✅ **Consistency**: Same registration pattern across single-app and multi-app modes
|
|
15
|
-
- ✅ **Best Practices**: Follows FastAPI's recommended WebSocket registration patterns
|
|
16
|
-
- ✅ **Better Maintainability**: Uses FastAPI abstractions instead of low-level Starlette APIs
|
|
17
|
-
- ✅ **Route Priority**: WebSocket routes registered before mounted apps ensure proper routing
|
|
18
|
-
|
|
19
|
-
**Value**: This change ensures WebSocket endpoints benefit from all FastAPI features, making your code more maintainable and consistent with FastAPI best practices.
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
9
|
## 🎯 manifest.json: The Key to Everything
|
|
24
10
|
|
|
25
11
|
**`manifest.json` is the foundation of your application.** It's a single configuration file that defines your app's identity, data structure, authentication, indexes, and services. Everything flows from this file.
|
|
@@ -81,15 +81,7 @@ from .repositories import Entity, MongoRepository, Repository, UnitOfWork
|
|
|
81
81
|
# Utilities
|
|
82
82
|
from .utils import clean_mongo_doc, clean_mongo_docs
|
|
83
83
|
|
|
84
|
-
__version__ =
|
|
85
|
-
"0.7.1" # Memory service initialization fix for multi-app setups
|
|
86
|
-
# - FIXED: Memory service initialization in create_multi_app context
|
|
87
|
-
# - FIXED: get_memory_service() now returns service when memory_config.enabled: true
|
|
88
|
-
# - ENHANCED: Explicit memory service initialization in create_multi_app lifespan
|
|
89
|
-
# - ENHANCED: Better error handling and logging for memory service initialization
|
|
90
|
-
# - ADDED: Comprehensive integration tests for memory service in multi-app context
|
|
91
|
-
# - ADDED: Unit tests for memory service initialization edge cases
|
|
92
|
-
)
|
|
84
|
+
__version__ = "0.7.3"
|
|
93
85
|
|
|
94
86
|
__all__ = [
|
|
95
87
|
# Core Engine
|
|
@@ -181,18 +181,49 @@ memories = await memory_service.get_all(
|
|
|
181
181
|
|
|
182
182
|
### Update Memory
|
|
183
183
|
|
|
184
|
-
Update existing memories:
|
|
184
|
+
Update existing memories in-place while preserving the original memory ID and creation timestamp:
|
|
185
185
|
|
|
186
186
|
```python
|
|
187
|
-
# Update memory
|
|
188
|
-
updated =
|
|
187
|
+
# Update memory content and metadata
|
|
188
|
+
updated = memory_service.update(
|
|
189
|
+
memory_id="memory_123",
|
|
190
|
+
user_id="user123",
|
|
191
|
+
memory="Updated memory content",
|
|
192
|
+
metadata={"updated": True, "category": "technical"}
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Update using messages format
|
|
196
|
+
updated = memory_service.update(
|
|
189
197
|
memory_id="memory_123",
|
|
190
198
|
user_id="user123",
|
|
191
199
|
messages=[{"role": "user", "content": "Updated content"}],
|
|
192
200
|
metadata={"updated": True}
|
|
193
201
|
)
|
|
202
|
+
|
|
203
|
+
# Update only metadata (content unchanged)
|
|
204
|
+
updated = memory_service.update(
|
|
205
|
+
memory_id="memory_123",
|
|
206
|
+
user_id="user123",
|
|
207
|
+
metadata={"category": "updated"}
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Backward compatibility: using 'data' parameter
|
|
211
|
+
updated = memory_service.update(
|
|
212
|
+
memory_id="memory_123",
|
|
213
|
+
user_id="user123",
|
|
214
|
+
data="Updated content",
|
|
215
|
+
metadata={"updated": True}
|
|
216
|
+
)
|
|
194
217
|
```
|
|
195
218
|
|
|
219
|
+
**Key Features:**
|
|
220
|
+
- **Preserves Memory ID**: The original memory ID is maintained
|
|
221
|
+
- **Preserves Creation Timestamp**: `created_at` is not modified
|
|
222
|
+
- **Updates Timestamp**: `updated_at` is automatically set to current time
|
|
223
|
+
- **Recomputes Embeddings**: If content changes, the embedding vector is automatically recomputed
|
|
224
|
+
- **Metadata Merging**: New metadata is merged with existing metadata (doesn't replace)
|
|
225
|
+
- **Partial Updates**: Can update content only, metadata only, or both
|
|
226
|
+
|
|
196
227
|
### Delete Memory
|
|
197
228
|
|
|
198
229
|
Remove memories:
|
|
@@ -44,6 +44,20 @@ class Mem0MemoryServiceError(Exception):
|
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
class Mem0MemoryService:
|
|
47
|
+
"""
|
|
48
|
+
Production-ready Mem0 Memory Service with MongoDB integration.
|
|
49
|
+
|
|
50
|
+
Features:
|
|
51
|
+
- In-place memory updates preserving IDs and timestamps
|
|
52
|
+
- Automatic embedding recomputation on content changes
|
|
53
|
+
- Knowledge graph support (if enabled in Mem0 config)
|
|
54
|
+
- Comprehensive error handling and logging
|
|
55
|
+
- Backward compatibility with existing code
|
|
56
|
+
|
|
57
|
+
All operations go through Mem0's API to ensure proper state management,
|
|
58
|
+
graph updates, and relationship handling.
|
|
59
|
+
"""
|
|
60
|
+
|
|
47
61
|
def __init__(
|
|
48
62
|
self,
|
|
49
63
|
mongo_uri: str,
|
|
@@ -54,6 +68,9 @@ class Mem0MemoryService:
|
|
|
54
68
|
if not _check_mem0_available():
|
|
55
69
|
raise Mem0MemoryServiceError("Mem0 not installed. pip install mem0ai")
|
|
56
70
|
|
|
71
|
+
if not mongo_uri or not db_name or not app_slug:
|
|
72
|
+
raise Mem0MemoryServiceError("mongo_uri, db_name, and app_slug are required parameters")
|
|
73
|
+
|
|
57
74
|
self.mongo_uri = mongo_uri
|
|
58
75
|
self.db_name = db_name
|
|
59
76
|
self.app_slug = app_slug
|
|
@@ -251,10 +268,13 @@ class Mem0MemoryService:
|
|
|
251
268
|
if isinstance(messages, str):
|
|
252
269
|
messages = [{"role": "user", "content": messages}]
|
|
253
270
|
|
|
254
|
-
# Merge metadata
|
|
255
271
|
final_metadata = dict(metadata) if metadata else {}
|
|
256
272
|
|
|
257
273
|
# CRITICAL: Database indexing relies on these fields being in metadata
|
|
274
|
+
# Include user_id in metadata ONLY if provided (supports non-SSO use cases)
|
|
275
|
+
if user_id:
|
|
276
|
+
final_metadata["user_id"] = str(user_id)
|
|
277
|
+
|
|
258
278
|
if bucket_id:
|
|
259
279
|
final_metadata["bucket_id"] = bucket_id
|
|
260
280
|
final_metadata["context_id"] = bucket_id # Backwards compatibility
|
|
@@ -262,7 +282,6 @@ class Mem0MemoryService:
|
|
|
262
282
|
if bucket_type:
|
|
263
283
|
final_metadata["bucket_type"] = bucket_type
|
|
264
284
|
|
|
265
|
-
# Store raw_content in metadata if provided (metadata convenience)
|
|
266
285
|
if raw_content:
|
|
267
286
|
final_metadata["raw_content"] = raw_content
|
|
268
287
|
|
|
@@ -461,6 +480,251 @@ class Mem0MemoryService:
|
|
|
461
480
|
):
|
|
462
481
|
return False
|
|
463
482
|
|
|
483
|
+
def update(
|
|
484
|
+
self,
|
|
485
|
+
memory_id: str,
|
|
486
|
+
user_id: str | None = None,
|
|
487
|
+
memory: str | None = None,
|
|
488
|
+
data: str | dict[str, Any] | None = None,
|
|
489
|
+
messages: str | list[dict[str, str]] | None = None,
|
|
490
|
+
metadata: dict[str, Any] | None = None,
|
|
491
|
+
**kwargs,
|
|
492
|
+
) -> dict[str, Any] | None:
|
|
493
|
+
"""
|
|
494
|
+
Update an existing memory in-place with production-grade error handling.
|
|
495
|
+
|
|
496
|
+
Updates the memory content and/or metadata while preserving:
|
|
497
|
+
- Original memory ID (never changes)
|
|
498
|
+
- Creation timestamp (created_at) - preserved
|
|
499
|
+
- Other existing fields - preserved unless explicitly updated
|
|
500
|
+
|
|
501
|
+
If content is updated, the embedding vector is automatically recomputed.
|
|
502
|
+
|
|
503
|
+
Args:
|
|
504
|
+
memory_id: The ID of the memory to update (required)
|
|
505
|
+
user_id: The user ID who owns the memory (for scoping and security)
|
|
506
|
+
memory: New memory content as a string (optional)
|
|
507
|
+
data: Alternative parameter name for memory content.
|
|
508
|
+
Can be a string or dict with 'memory'/'text'/'content' key.
|
|
509
|
+
messages: Alternative way to provide content as messages (optional).
|
|
510
|
+
Can be a string or list of dicts with 'content' key.
|
|
511
|
+
metadata: Metadata updates (optional, but NOT SUPPORTED by Mem0 update API).
|
|
512
|
+
This parameter is accepted for API consistency but will be ignored.
|
|
513
|
+
**kwargs: Additional arguments passed to Mem0 operations
|
|
514
|
+
|
|
515
|
+
Returns:
|
|
516
|
+
Updated memory object with same ID, or None if memory not found
|
|
517
|
+
|
|
518
|
+
Raises:
|
|
519
|
+
Mem0MemoryServiceError: If update operation fails
|
|
520
|
+
ValueError: If memory_id is invalid or empty
|
|
521
|
+
|
|
522
|
+
Example:
|
|
523
|
+
```python
|
|
524
|
+
# Update content and metadata
|
|
525
|
+
updated = memory_service.update(
|
|
526
|
+
memory_id="04f78986-dfad-46fe-8381-034bbee9a2fc",
|
|
527
|
+
user_id="user123",
|
|
528
|
+
memory="I love Python programming",
|
|
529
|
+
metadata={"category": "technical", "updated": True}
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
# Update only metadata (content unchanged)
|
|
533
|
+
updated = memory_service.update(
|
|
534
|
+
memory_id="04f78986-dfad-46fe-8381-034bbee9a2fc",
|
|
535
|
+
user_id="user123",
|
|
536
|
+
metadata={"category": "updated"}
|
|
537
|
+
)
|
|
538
|
+
```
|
|
539
|
+
"""
|
|
540
|
+
if not memory_id or not isinstance(memory_id, str) or not memory_id.strip():
|
|
541
|
+
raise ValueError("memory_id is required and must be a non-empty string")
|
|
542
|
+
|
|
543
|
+
try:
|
|
544
|
+
# Normalize data parameter (alternative to memory parameter)
|
|
545
|
+
normalized_memory = self._normalize_content_input(memory, data, messages)
|
|
546
|
+
normalized_metadata = self._normalize_metadata_input(metadata, data)
|
|
547
|
+
|
|
548
|
+
existing_memory = self.get(memory_id=memory_id, user_id=user_id, **kwargs)
|
|
549
|
+
if not existing_memory:
|
|
550
|
+
logger.warning(
|
|
551
|
+
f"Memory {memory_id} not found for update",
|
|
552
|
+
extra={"memory_id": memory_id, "user_id": user_id},
|
|
553
|
+
)
|
|
554
|
+
return None
|
|
555
|
+
|
|
556
|
+
# Use Mem0's built-in update method
|
|
557
|
+
# Mem0's Memory class update method handles:
|
|
558
|
+
# - In-place updates preserving memory ID
|
|
559
|
+
# - Automatic embedding recomputation
|
|
560
|
+
# - Metadata merging
|
|
561
|
+
# - User scoping
|
|
562
|
+
# - Knowledge graph updates (if enabled)
|
|
563
|
+
# - Relationship management
|
|
564
|
+
if not hasattr(self.memory, "update") or not callable(self.memory.update):
|
|
565
|
+
raise Mem0MemoryServiceError(
|
|
566
|
+
"Mem0 update method not available. "
|
|
567
|
+
"Please ensure you're using a compatible version of mem0ai "
|
|
568
|
+
"that supports updates. Install with: pip install --upgrade mem0ai"
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
result = self._update_via_mem0(
|
|
572
|
+
memory_id=memory_id,
|
|
573
|
+
user_id=user_id,
|
|
574
|
+
memory=normalized_memory,
|
|
575
|
+
metadata=normalized_metadata,
|
|
576
|
+
**kwargs,
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
if result is None:
|
|
580
|
+
logger.warning(
|
|
581
|
+
f"Mem0 update returned None for memory {memory_id}",
|
|
582
|
+
extra={"memory_id": memory_id, "user_id": user_id},
|
|
583
|
+
)
|
|
584
|
+
return None
|
|
585
|
+
|
|
586
|
+
logger.info(
|
|
587
|
+
f"Successfully updated memory {memory_id} using Mem0 update method",
|
|
588
|
+
extra={
|
|
589
|
+
"memory_id": memory_id,
|
|
590
|
+
"content_updated": bool(normalized_memory),
|
|
591
|
+
"metadata_updated": bool(normalized_metadata),
|
|
592
|
+
},
|
|
593
|
+
)
|
|
594
|
+
return result
|
|
595
|
+
|
|
596
|
+
except ValueError:
|
|
597
|
+
# Re-raise validation errors as-is
|
|
598
|
+
raise
|
|
599
|
+
except (AttributeError, TypeError, ValueError, KeyError) as e:
|
|
600
|
+
logger.exception(
|
|
601
|
+
f"Error updating memory {memory_id}",
|
|
602
|
+
extra={"memory_id": memory_id, "user_id": user_id},
|
|
603
|
+
)
|
|
604
|
+
raise Mem0MemoryServiceError(f"Update failed: {e}") from e
|
|
605
|
+
|
|
606
|
+
def _normalize_content_input(
|
|
607
|
+
self,
|
|
608
|
+
memory: str | None,
|
|
609
|
+
data: str | dict[str, Any] | None,
|
|
610
|
+
messages: str | list[dict[str, str]] | None,
|
|
611
|
+
) -> str | None:
|
|
612
|
+
"""
|
|
613
|
+
Normalize content input from various parameter formats.
|
|
614
|
+
|
|
615
|
+
Priority: memory > data > messages
|
|
616
|
+
"""
|
|
617
|
+
# Already have memory content
|
|
618
|
+
if memory:
|
|
619
|
+
if not isinstance(memory, str):
|
|
620
|
+
raise TypeError("memory parameter must be a string")
|
|
621
|
+
return memory.strip() if memory.strip() else None
|
|
622
|
+
|
|
623
|
+
# Check data parameter
|
|
624
|
+
if data:
|
|
625
|
+
if isinstance(data, str):
|
|
626
|
+
return data.strip() if data.strip() else None
|
|
627
|
+
elif isinstance(data, dict):
|
|
628
|
+
content = data.get("memory") or data.get("text") or data.get("content")
|
|
629
|
+
if content and isinstance(content, str):
|
|
630
|
+
return content.strip() if content.strip() else None
|
|
631
|
+
|
|
632
|
+
# Check messages parameter
|
|
633
|
+
if messages:
|
|
634
|
+
if isinstance(messages, str):
|
|
635
|
+
return messages.strip() if messages.strip() else None
|
|
636
|
+
elif isinstance(messages, list):
|
|
637
|
+
content_parts = []
|
|
638
|
+
for msg in messages:
|
|
639
|
+
if isinstance(msg, dict) and "content" in msg:
|
|
640
|
+
content = msg["content"]
|
|
641
|
+
if isinstance(content, str) and content.strip():
|
|
642
|
+
content_parts.append(content.strip())
|
|
643
|
+
if content_parts:
|
|
644
|
+
return " ".join(content_parts)
|
|
645
|
+
|
|
646
|
+
return None
|
|
647
|
+
|
|
648
|
+
def _normalize_metadata_input(
|
|
649
|
+
self, metadata: dict[str, Any] | None, data: dict[str, Any] | None
|
|
650
|
+
) -> dict[str, Any] | None:
|
|
651
|
+
"""Normalize metadata input, extracting from data dict if needed."""
|
|
652
|
+
if metadata is not None and not isinstance(metadata, dict):
|
|
653
|
+
raise TypeError("metadata must be a dict or None")
|
|
654
|
+
|
|
655
|
+
# If metadata provided directly, use it
|
|
656
|
+
if metadata is not None:
|
|
657
|
+
return metadata
|
|
658
|
+
|
|
659
|
+
# Check if metadata is in data dict
|
|
660
|
+
if isinstance(data, dict) and "metadata" in data:
|
|
661
|
+
data_metadata = data.get("metadata")
|
|
662
|
+
if isinstance(data_metadata, dict):
|
|
663
|
+
return data_metadata
|
|
664
|
+
|
|
665
|
+
return None
|
|
666
|
+
|
|
667
|
+
def _update_via_mem0(
|
|
668
|
+
self,
|
|
669
|
+
memory_id: str,
|
|
670
|
+
user_id: str | None,
|
|
671
|
+
memory: str | None,
|
|
672
|
+
metadata: dict[str, Any] | None,
|
|
673
|
+
**kwargs,
|
|
674
|
+
) -> dict[str, Any] | None:
|
|
675
|
+
"""
|
|
676
|
+
Update memory using Mem0's built-in update method.
|
|
677
|
+
|
|
678
|
+
Note: Mem0's update() only supports content updates (text parameter).
|
|
679
|
+
Metadata updates are not supported by the Mem0 API.
|
|
680
|
+
|
|
681
|
+
Args:
|
|
682
|
+
memory_id: Memory ID to update
|
|
683
|
+
user_id: User ID for scoping (not used in update call)
|
|
684
|
+
memory: New memory content (normalized)
|
|
685
|
+
metadata: Metadata to merge (ignored - not supported by Mem0 update API)
|
|
686
|
+
**kwargs: Additional arguments passed to Mem0
|
|
687
|
+
|
|
688
|
+
Returns:
|
|
689
|
+
Updated memory dict or None if not found
|
|
690
|
+
"""
|
|
691
|
+
# Filter out user_id from kwargs to prevent passing it as a direct parameter
|
|
692
|
+
filtered_kwargs = {k: v for k, v in kwargs.items() if k != "user_id"}
|
|
693
|
+
|
|
694
|
+
logger.debug(
|
|
695
|
+
f"Calling mem0.update() for memory_id={memory_id}",
|
|
696
|
+
extra={
|
|
697
|
+
"memory_id": memory_id,
|
|
698
|
+
"has_content": bool(memory),
|
|
699
|
+
"has_metadata": bool(metadata),
|
|
700
|
+
"user_id": user_id,
|
|
701
|
+
},
|
|
702
|
+
)
|
|
703
|
+
|
|
704
|
+
if metadata:
|
|
705
|
+
logger.warning(
|
|
706
|
+
f"Metadata update requested for memory {memory_id} but Mem0 update() "
|
|
707
|
+
f"does not support metadata parameter. Metadata will not be updated."
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
update_kwargs = {"memory_id": memory_id}
|
|
711
|
+
if memory:
|
|
712
|
+
update_kwargs["data"] = memory
|
|
713
|
+
update_kwargs.update(filtered_kwargs)
|
|
714
|
+
|
|
715
|
+
result = self.memory.update(**update_kwargs)
|
|
716
|
+
|
|
717
|
+
# Note: Mem0's update() doesn't support metadata parameter
|
|
718
|
+
# Metadata updates would require direct MongoDB access, which we avoid
|
|
719
|
+
|
|
720
|
+
# Normalize result to dict
|
|
721
|
+
if isinstance(result, dict):
|
|
722
|
+
return result
|
|
723
|
+
elif isinstance(result, list) and len(result) > 0:
|
|
724
|
+
return result[0] if isinstance(result[0], dict) else None
|
|
725
|
+
else:
|
|
726
|
+
return self.get(memory_id=memory_id)
|
|
727
|
+
|
|
464
728
|
def _normalize_result(self, result: Any) -> list[dict[str, Any]]:
|
|
465
729
|
"""Normalize Mem0's return type (dict vs list)."""
|
|
466
730
|
if result is None:
|
|
@@ -472,19 +472,58 @@ async def _validate_websocket_origin_in_handler(websocket: Any, app_slug: str) -
|
|
|
472
472
|
|
|
473
473
|
# Normalize origin for comparison
|
|
474
474
|
def normalize_origin(orig: str) -> str:
|
|
475
|
-
"""
|
|
475
|
+
"""
|
|
476
|
+
Normalize origin handling localhost variants and protocol differences.
|
|
477
|
+
|
|
478
|
+
Handles:
|
|
479
|
+
- Protocol conversion: ws/wss -> http/https (browsers send http/https)
|
|
480
|
+
- Localhost normalization: 127.0.0.1, 0.0.0.0, ::1 -> localhost
|
|
481
|
+
- Docker IP normalization: container IPs -> localhost (in development)
|
|
482
|
+
- Port normalization: removes :80 and :443
|
|
483
|
+
"""
|
|
476
484
|
if not orig:
|
|
477
485
|
return orig
|
|
486
|
+
import os
|
|
478
487
|
import re
|
|
479
488
|
|
|
480
|
-
normalized =
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
489
|
+
normalized = orig.lower()
|
|
490
|
+
|
|
491
|
+
# Normalize protocol: convert ws/wss to http/https for comparison
|
|
492
|
+
# WebSocket origins come as http/https from browsers, but configs may use ws/wss
|
|
493
|
+
normalized = re.sub(r"^ws://", "http://", normalized)
|
|
494
|
+
normalized = re.sub(r"^wss://", "https://", normalized)
|
|
495
|
+
|
|
496
|
+
# Check if we're in development/Docker environment
|
|
497
|
+
is_dev = (
|
|
498
|
+
os.getenv("ENVIRONMENT", "").lower() in ["development", "dev"]
|
|
499
|
+
or os.getenv("G_NOME_ENV", "").lower() in ["development", "dev"]
|
|
500
|
+
or os.path.exists("/.dockerenv") # Docker container indicator
|
|
485
501
|
)
|
|
502
|
+
|
|
503
|
+
# Normalize localhost variants
|
|
504
|
+
# In development/Docker, also normalize common Docker IP ranges to localhost
|
|
505
|
+
if is_dev:
|
|
506
|
+
# Match localhost, 127.0.0.1, 0.0.0.0, ::1, and Docker container IPs
|
|
507
|
+
# Docker typically uses 172.17.0.0/16 or 172.20.0.0/16
|
|
508
|
+
normalized = re.sub(
|
|
509
|
+
r"://(0\.0\.0\.0|127\.0\.0\.1|localhost|::1|172\.(17|20)\.\d+\.\d+)",
|
|
510
|
+
"://localhost",
|
|
511
|
+
normalized,
|
|
512
|
+
flags=re.IGNORECASE,
|
|
513
|
+
)
|
|
514
|
+
else:
|
|
515
|
+
# Production: only normalize standard localhost variants
|
|
516
|
+
normalized = re.sub(
|
|
517
|
+
r"://(0\.0\.0\.0|127\.0\.0\.1|localhost|::1)",
|
|
518
|
+
"://localhost",
|
|
519
|
+
normalized,
|
|
520
|
+
flags=re.IGNORECASE,
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
# Remove default ports
|
|
486
524
|
normalized = re.sub(r":80$", "", normalized)
|
|
487
525
|
normalized = re.sub(r":443$", "", normalized)
|
|
526
|
+
|
|
488
527
|
return normalized.rstrip("/")
|
|
489
528
|
|
|
490
529
|
normalized_origin = normalize_origin(origin)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mdb-engine
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
4
4
|
Summary: MongoDB Engine
|
|
5
5
|
Home-page: https://github.com/ranfysvalle02/mdb-engine
|
|
6
6
|
Author: Fabian Valle
|
|
@@ -72,20 +72,6 @@ Dynamic: requires-python
|
|
|
72
72
|
[](https://www.python.org/downloads/)
|
|
73
73
|
[](https://opensource.org/licenses/AGPL-3.0)
|
|
74
74
|
|
|
75
|
-
## 🎉 What's New in v0.7.0
|
|
76
|
-
|
|
77
|
-
**FastAPI Native WebSocket Support**: MDB-Engine now uses FastAPI's `APIRouter` approach for WebSocket registration in both single-app and multi-app modes. This provides:
|
|
78
|
-
|
|
79
|
-
- ✅ **Full FastAPI Feature Support**: Dependency injection, OpenAPI documentation, request/response models
|
|
80
|
-
- ✅ **Consistency**: Same registration pattern across single-app and multi-app modes
|
|
81
|
-
- ✅ **Best Practices**: Follows FastAPI's recommended WebSocket registration patterns
|
|
82
|
-
- ✅ **Better Maintainability**: Uses FastAPI abstractions instead of low-level Starlette APIs
|
|
83
|
-
- ✅ **Route Priority**: WebSocket routes registered before mounted apps ensure proper routing
|
|
84
|
-
|
|
85
|
-
**Value**: This change ensures WebSocket endpoints benefit from all FastAPI features, making your code more maintainable and consistent with FastAPI best practices.
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
75
|
## 🎯 manifest.json: The Key to Everything
|
|
90
76
|
|
|
91
77
|
**`manifest.json` is the foundation of your application.** It's a single configuration file that defines your app's identity, data structure, authentication, indexes, and services. Everything flows from this file.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|