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.
Files changed (106) hide show
  1. {mdb_engine-0.7.1/mdb_engine.egg-info → mdb_engine-0.7.3}/PKG-INFO +1 -15
  2. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/README.md +0 -14
  3. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/__init__.py +1 -9
  4. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/main.py +1 -1
  5. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/memory/README.md +34 -3
  6. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/memory/service.py +266 -2
  7. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/routing/websockets.py +45 -6
  8. {mdb_engine-0.7.1 → mdb_engine-0.7.3/mdb_engine.egg-info}/PKG-INFO +1 -15
  9. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/pyproject.toml +1 -1
  10. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/setup.py +1 -1
  11. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/LICENSE +0 -0
  12. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/MANIFEST.in +0 -0
  13. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/README.md +0 -0
  14. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/ARCHITECTURE.md +0 -0
  15. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/README.md +0 -0
  16. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/__init__.py +0 -0
  17. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/audit.py +0 -0
  18. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/base.py +0 -0
  19. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/casbin_factory.py +0 -0
  20. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/casbin_models.py +0 -0
  21. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/config_defaults.py +0 -0
  22. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/config_helpers.py +0 -0
  23. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/cookie_utils.py +0 -0
  24. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/csrf.py +0 -0
  25. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/decorators.py +0 -0
  26. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/dependencies.py +0 -0
  27. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/helpers.py +0 -0
  28. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/integration.py +0 -0
  29. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/jwt.py +0 -0
  30. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/middleware.py +0 -0
  31. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/oso_factory.py +0 -0
  32. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/provider.py +0 -0
  33. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/rate_limiter.py +0 -0
  34. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/restrictions.py +0 -0
  35. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/session_manager.py +0 -0
  36. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/shared_middleware.py +0 -0
  37. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/shared_users.py +0 -0
  38. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/token_lifecycle.py +0 -0
  39. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/token_store.py +0 -0
  40. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/users.py +0 -0
  41. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/utils.py +0 -0
  42. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/websocket_sessions.py +0 -0
  43. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/auth/websocket_tickets.py +0 -0
  44. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/__init__.py +0 -0
  45. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/commands/__init__.py +0 -0
  46. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/commands/generate.py +0 -0
  47. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/commands/migrate.py +0 -0
  48. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/commands/show.py +0 -0
  49. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/commands/validate.py +0 -0
  50. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/cli/utils.py +0 -0
  51. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/config.py +0 -0
  52. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/constants.py +0 -0
  53. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/README.md +0 -0
  54. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/__init__.py +0 -0
  55. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/app_registration.py +0 -0
  56. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/app_secrets.py +0 -0
  57. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/connection.py +0 -0
  58. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/encryption.py +0 -0
  59. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/engine.py +0 -0
  60. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/index_management.py +0 -0
  61. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/manifest.py +0 -0
  62. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/ray_integration.py +0 -0
  63. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/seeding.py +0 -0
  64. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/service_initialization.py +0 -0
  65. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/core/types.py +0 -0
  66. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/database/README.md +0 -0
  67. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/database/__init__.py +0 -0
  68. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/database/abstraction.py +0 -0
  69. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/database/connection.py +0 -0
  70. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/database/query_validator.py +0 -0
  71. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/database/resource_limiter.py +0 -0
  72. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/database/scoped_wrapper.py +0 -0
  73. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/dependencies.py +0 -0
  74. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/di/__init__.py +0 -0
  75. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/di/container.py +0 -0
  76. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/di/providers.py +0 -0
  77. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/di/scopes.py +0 -0
  78. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/embeddings/README.md +0 -0
  79. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/embeddings/__init__.py +0 -0
  80. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/embeddings/dependencies.py +0 -0
  81. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/embeddings/service.py +0 -0
  82. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/exceptions.py +0 -0
  83. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/indexes/README.md +0 -0
  84. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/indexes/__init__.py +0 -0
  85. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/indexes/helpers.py +0 -0
  86. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/indexes/manager.py +0 -0
  87. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/memory/__init__.py +0 -0
  88. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/observability/README.md +0 -0
  89. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/observability/__init__.py +0 -0
  90. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/observability/health.py +0 -0
  91. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/observability/logging.py +0 -0
  92. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/observability/metrics.py +0 -0
  93. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/repositories/__init__.py +0 -0
  94. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/repositories/base.py +0 -0
  95. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/repositories/mongo.py +0 -0
  96. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/repositories/unit_of_work.py +0 -0
  97. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/routing/README.md +0 -0
  98. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/routing/__init__.py +0 -0
  99. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/utils/__init__.py +0 -0
  100. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine/utils/mongo.py +0 -0
  101. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine.egg-info/SOURCES.txt +0 -0
  102. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine.egg-info/dependency_links.txt +0 -0
  103. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine.egg-info/entry_points.txt +0 -0
  104. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine.egg-info/requires.txt +0 -0
  105. {mdb_engine-0.7.1 → mdb_engine-0.7.3}/mdb_engine.egg-info/top_level.txt +0 -0
  106. {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.1
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
  [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
73
73
  [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](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
  [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
7
7
  [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](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
@@ -15,7 +15,7 @@ from .commands.validate import validate
15
15
 
16
16
 
17
17
  @click.group()
18
- @click.version_option(version="0.7.1", prog_name="mdb")
18
+ @click.version_option(version="0.7.3", prog_name="mdb")
19
19
  def cli() -> None:
20
20
  """
21
21
  MDB_ENGINE CLI - Manifest management tool.
@@ -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 = await memory_service.update(
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
- """Normalize origin handling localhost variants."""
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 = re.sub(
481
- r"://(0\.0\.0\.0|127\.0\.0\.1|localhost|::1)",
482
- "://localhost",
483
- orig.lower(),
484
- flags=re.IGNORECASE,
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.1
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
  [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
73
73
  [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](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.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mdb-engine"
7
- version = "0.7.1"
7
+ version = "0.7.3"
8
8
  description = "MongoDB Engine"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -14,7 +14,7 @@ if readme_file.exists():
14
14
 
15
15
  setup(
16
16
  name="mdb-engine",
17
- version="0.7.1",
17
+ version="0.7.3",
18
18
  description="MongoDB Engine",
19
19
  long_description=long_description,
20
20
  long_description_content_type="text/markdown",
File without changes
File without changes
File without changes