mdb-engine 0.7.3__py3-none-any.whl → 0.7.5__py3-none-any.whl

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/__init__.py CHANGED
@@ -81,7 +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__ = "0.7.3"
84
+ __version__ = "0.7.5"
85
85
 
86
86
  __all__ = [
87
87
  # Core Engine
mdb_engine/cli/main.py CHANGED
@@ -15,7 +15,7 @@ from .commands.validate import validate
15
15
 
16
16
 
17
17
  @click.group()
18
- @click.version_option(version="0.7.3", prog_name="mdb")
18
+ @click.version_option(version="0.7.5", prog_name="mdb")
19
19
  def cli() -> None:
20
20
  """
21
21
  MDB_ENGINE CLI - Manifest management tool.
mdb_engine/core/engine.py CHANGED
@@ -910,13 +910,14 @@ class MongoDBEngine:
910
910
 
911
911
  def get_memory_service(self, slug: str) -> Any | None:
912
912
  """
913
- Get Mem0 memory service for an app.
913
+ Get memory service for an app (returns BaseMemoryService instance).
914
914
 
915
915
  Args:
916
916
  slug: App slug
917
917
 
918
918
  Returns:
919
- Mem0MemoryService instance if memory is enabled for this app, None otherwise
919
+ BaseMemoryService instance (currently Mem0MemoryService) if memory is enabled
920
+ for this app, None otherwise
920
921
 
921
922
  Example:
922
923
  ```python
@@ -63,10 +63,10 @@ class ServiceInitializer:
63
63
  self, slug: str, memory_config: dict[str, Any] | None
64
64
  ) -> None:
65
65
  """
66
- Initialize Mem0 memory service for an app.
66
+ Initialize memory service for an app (defaults to Mem0 implementation).
67
67
 
68
68
  Memory support is OPTIONAL - only processes if dependencies are available.
69
- mem0 handles embeddings and LLM via environment variables (.env).
69
+ Currently uses Mem0.ai which handles embeddings and LLM via environment variables (.env).
70
70
 
71
71
  Args:
72
72
  slug: App slug
@@ -356,13 +356,14 @@ class ServiceInitializer:
356
356
 
357
357
  def get_memory_service(self, slug: str) -> Any | None:
358
358
  """
359
- Get Mem0 memory service for an app.
359
+ Get memory service for an app (returns BaseMemoryService instance).
360
360
 
361
361
  Args:
362
362
  slug: App slug
363
363
 
364
364
  Returns:
365
- Mem0MemoryService instance if memory is enabled for this app, None otherwise
365
+ BaseMemoryService instance (currently Mem0MemoryService) if memory is enabled
366
+ for this app, None otherwise
366
367
  """
367
368
  try:
368
369
  service = self._memory_services.get(slug)
mdb_engine/core/types.py CHANGED
@@ -11,9 +11,9 @@ This module is part of MDB_ENGINE - MongoDB Engine.
11
11
  from typing import TYPE_CHECKING, Any, Literal, TypedDict
12
12
 
13
13
  if TYPE_CHECKING:
14
- from ..memory import Mem0MemoryService
14
+ from ..memory import BaseMemoryService
15
15
  else:
16
- Mem0MemoryService = Any
16
+ BaseMemoryService = Any
17
17
 
18
18
 
19
19
  # ============================================================================
@@ -33,7 +33,7 @@ if TYPE_CHECKING:
33
33
  from .core.engine import MongoDBEngine
34
34
  from .database.scoped_wrapper import ScopedMongoWrapper
35
35
  from .embeddings.service import EmbeddingService
36
- from .memory.service import Mem0MemoryService
36
+ from .memory import BaseMemoryService
37
37
 
38
38
  logger = logging.getLogger(__name__)
39
39
 
@@ -121,8 +121,8 @@ async def get_embedding_service(request: Request) -> "EmbeddingService":
121
121
  raise HTTPException(503, f"Failed to initialize embedding service: {e}") from e
122
122
 
123
123
 
124
- async def get_memory_service(request: Request) -> Optional["Mem0MemoryService"]:
125
- """Get the Mem0 memory service if configured."""
124
+ async def get_memory_service(request: Request) -> Optional["BaseMemoryService"]:
125
+ """Get the memory service if configured (defaults to Mem0 implementation)."""
126
126
  engine = getattr(request.app.state, "engine", None)
127
127
  slug = getattr(request.app.state, "app_slug", None)
128
128
  if not engine or not slug:
@@ -2,9 +2,45 @@
2
2
 
3
3
  Mem0.ai integration for intelligent memory management in MDB_ENGINE applications. Provides semantic memory storage, retrieval, and inference capabilities with MongoDB integration.
4
4
 
5
+ ## 🎉 What's New
6
+
7
+ ### Extensible Architecture (Latest)
8
+
9
+ **Base Class Pattern for Future Extensibility!**
10
+
11
+ The memory service now uses an abstract base class pattern, enabling future memory provider implementations while maintaining backward compatibility:
12
+
13
+ - **🏗️ BaseMemoryService**: Abstract base class defining the memory service interface
14
+ - **🔌 Provider Extensibility**: Easy to add new memory providers (LangChain, custom implementations, etc.)
15
+ - **✅ Backward Compatible**: All existing code continues to work without changes
16
+ - **📝 Type Safety**: Better IDE support and type checking with abstract base class
17
+ - **🎯 Consistent API**: All memory providers implement the same interface
18
+
19
+ ### v0.7.5 Enhancements
20
+
21
+ **Memory Injection and Enhanced Delete Capabilities!**
22
+
23
+ - **💉 Inject Method**: New `inject()` method for manual memory insertion without LLM inference - perfect for adding facts, preferences, or structured data directly
24
+ - **🗑️ Enhanced Delete**: Comprehensive delete functionality with improved documentation and user experience
25
+ - **🧠 Memory Explorer UI**: Added memory explorer interface in chit_chat example with inject and delete buttons
26
+
27
+ ### v0.7.4 Enhancements
28
+
29
+ **Enhanced Mem0 Integration - Production Ready!**
30
+
31
+ - **🔧 Hybrid Update Pattern**: Content updates via Mem0 (triggers re-embedding), metadata updates via direct MongoDB (full control, no API limitations)
32
+ - **📊 Direct MongoDB Access**: Reliable data retrieval directly from MongoDB, bypassing Mem0 API inconsistencies
33
+ - **🏷️ Full Metadata Support**: Update any metadata field without restrictions - not limited by Mem0's API
34
+ - **✅ Correct Mem0 Structure**: Properly handles Mem0's MongoDB structure (`_id` as document ID, `payload` for memory data)
35
+ - **🛡️ Robust Error Handling**: Specific exception handling with proper KeyboardInterrupt/SystemExit propagation
36
+ - **🔍 Reliable Returns**: Always returns normalized documents fetched directly from MongoDB (guaranteed structure)
37
+
38
+ > 📖 **Want to understand why we use manual MongoDB access?** See the [Mem0 Implementation Guide](../../docs/guides/MEM0_IMPLEMENTATION.md) for detailed explanations of our architectural decisions, Mem0's MongoDB structure, and things to watch out for.
39
+
5
40
  ## Features
6
41
 
7
- - **Mem0 Integration**: Wrapper around Mem0.ai for intelligent memory management
42
+ - **Extensible Architecture**: Base class pattern allows for multiple memory provider implementations
43
+ - **Mem0 Integration**: Default implementation using Mem0.ai for intelligent memory management
8
44
  - **MongoDB Storage**: Built-in MongoDB vector store integration
9
45
  - **Auto-Detection**: Automatically detects OpenAI or Azure OpenAI from environment variables
10
46
  - **Semantic Search**: Vector-based semantic memory search
@@ -67,7 +103,7 @@ Enable memory service in your `manifest.json`:
67
103
  ### Basic Usage
68
104
 
69
105
  ```python
70
- from mdb_engine.memory import Mem0MemoryService
106
+ from mdb_engine.memory import BaseMemoryService # Base class for type hints
71
107
  from mdb_engine.core import MongoDBEngine
72
108
 
73
109
  # Initialize engine
@@ -75,7 +111,8 @@ engine = MongoDBEngine(mongo_uri="...", db_name="...")
75
111
  await engine.initialize()
76
112
 
77
113
  # Get memory service (automatically configured from manifest)
78
- memory_service = engine.get_memory_service("my_app")
114
+ # Returns BaseMemoryService instance (currently Mem0MemoryService)
115
+ memory_service: BaseMemoryService = engine.get_memory_service("my_app")
79
116
 
80
117
  # Add memory
81
118
  memory = await memory_service.add(
@@ -140,6 +177,45 @@ memories = await memory_service.add_all(
140
177
  )
141
178
  ```
142
179
 
180
+ ### Inject Memory (Manual Insertion)
181
+
182
+ Manually inject memories without LLM inference. This is useful when you want to directly add facts, preferences, or structured data without going through the inference pipeline:
183
+
184
+ ```python
185
+ # Inject memory as a string
186
+ memory = await memory_service.inject(
187
+ memory="The user prefers dark mode interfaces",
188
+ user_id="user123",
189
+ metadata={"source": "manual", "category": "preference"}
190
+ )
191
+
192
+ # Inject memory as a dict
193
+ memory = await memory_service.inject(
194
+ memory={"memory": "Project deadline is next Friday", "category": "work"},
195
+ user_id="user123",
196
+ metadata={"source": "manual", "type": "deadline"}
197
+ )
198
+
199
+ # Returns: {"id": "...", "memory": "...", "metadata": {...}, ...}
200
+ ```
201
+
202
+ **Key Differences from `add()`:**
203
+ - **`inject()`**: Direct insertion without LLM inference (faster, no API costs)
204
+ - **`add()`**: Uses LLM inference to extract facts from messages (slower, costs API calls)
205
+ - **`inject()`**: Returns a single memory dict
206
+ - **`add()`**: Returns a list of extracted memories
207
+
208
+ **When to use `inject()`:**
209
+ - Adding known facts or preferences directly
210
+ - Importing structured data
211
+ - When you want to avoid LLM inference costs
212
+ - When you have pre-formatted memory content
213
+
214
+ **When to use `add()`:**
215
+ - Extracting memories from conversations
216
+ - When you want the LLM to identify key facts
217
+ - Processing unstructured text into memories
218
+
143
219
  ### Search Memories
144
220
 
145
221
  Semantic search across stored memories:
@@ -163,14 +239,17 @@ results = await memory_service.search(
163
239
 
164
240
  ### Get Memories
165
241
 
166
- Retrieve memories for a user:
242
+ Retrieve memories for a user. The service automatically normalizes Mem0's MongoDB structure (`_id`, `payload`) into a consistent API format:
167
243
 
168
244
  ```python
169
245
  # Get all memories
170
246
  all_memories = await memory_service.get_all(user_id="user123")
247
+ # Returns normalized format: [{"id": "...", "memory": "...", "metadata": {...}, ...}]
171
248
 
172
249
  # Get specific memory
173
250
  memory = await memory_service.get(memory_id="memory_123", user_id="user123")
251
+ # Returns normalized format: {"id": "...", "memory": "...", "metadata": {...}, ...}
252
+ # Note: memory_id can be either Mem0's _id or the normalized id field
174
253
 
175
254
  # Get memories with filters
176
255
  memories = await memory_service.get_all(
@@ -179,9 +258,16 @@ memories = await memory_service.get_all(
179
258
  )
180
259
  ```
181
260
 
261
+ **Note**: The service handles Mem0's internal MongoDB structure (`_id` as document ID, `payload` containing memory data) automatically. All methods return normalized documents with consistent `id`, `memory`, `text`, and `metadata` fields.
262
+
182
263
  ### Update Memory
183
264
 
184
- Update existing memories in-place while preserving the original memory ID and creation timestamp:
265
+ Update existing memories using a **hybrid approach** that combines Mem0's embedding capabilities with direct MongoDB control:
266
+
267
+ **Architecture:**
268
+ - **Content Updates**: Routed via Mem0 (triggers automatic re-embedding)
269
+ - **Metadata Updates**: Routed via direct PyMongo (full control, no API limitations)
270
+ - **Return Value**: Always fetched from MongoDB (guaranteed correct structure)
185
271
 
186
272
  ```python
187
273
  # Update memory content and metadata
@@ -200,14 +286,21 @@ updated = memory_service.update(
200
286
  metadata={"updated": True}
201
287
  )
202
288
 
203
- # Update only metadata (content unchanged)
289
+ # Update only metadata (content unchanged) - FULLY SUPPORTED
290
+ updated = memory_service.update(
291
+ memory_id="memory_123",
292
+ user_id="user123",
293
+ metadata={"category": "updated", "priority": "high"}
294
+ )
295
+
296
+ # Update only content (no metadata changes)
204
297
  updated = memory_service.update(
205
298
  memory_id="memory_123",
206
299
  user_id="user123",
207
- metadata={"category": "updated"}
300
+ memory="Updated content only"
208
301
  )
209
302
 
210
- # Backward compatibility: using 'data' parameter
303
+ # Using 'data' parameter
211
304
  updated = memory_service.update(
212
305
  memory_id="memory_123",
213
306
  user_id="user123",
@@ -217,25 +310,63 @@ updated = memory_service.update(
217
310
  ```
218
311
 
219
312
  **Key Features:**
313
+ - **Hybrid Architecture**: Mem0 handles embeddings, MongoDB handles data persistence
314
+ - **Full Metadata Support**: Update any metadata field (not limited by Mem0 API)
220
315
  - **Preserves Memory ID**: The original memory ID is maintained
221
316
  - **Preserves Creation Timestamp**: `created_at` is not modified
222
317
  - **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)
318
+ - **Recomputes Embeddings**: If content changes, the embedding vector is automatically recomputed via Mem0
319
+ - **Reliable Returns**: Always returns the actual document from MongoDB (not Mem0's response format)
225
320
  - **Partial Updates**: Can update content only, metadata only, or both
321
+ - **Security**: Validates user_id ownership before allowing updates
322
+ - **Mem0 Structure Aware**: Correctly handles Mem0's MongoDB structure (`_id` as document ID, `payload` for memory data)
323
+ - **Direct MongoDB Access**: Uses PyMongo for reliable data operations, ensuring consistency
324
+ - **Normalized Responses**: All methods return consistent document structure regardless of Mem0's internal format
226
325
 
227
326
  ### Delete Memory
228
327
 
229
- Remove memories:
328
+ Remove memories from storage. Both methods return `True` on success, `False` on failure:
230
329
 
231
330
  ```python
232
- # Delete single memory
233
- await memory_service.delete(memory_id="memory_123", user_id="user123")
234
-
235
- # Delete all memories for user
236
- await memory_service.delete_all(user_id="user123")
331
+ # Delete single memory by ID
332
+ success = await memory_service.delete(memory_id="memory_123", user_id="user123")
333
+ if success:
334
+ print("Memory deleted successfully")
335
+ else:
336
+ print("Failed to delete memory (may not exist)")
337
+
338
+ # Delete all memories for a user (use with caution!)
339
+ success = await memory_service.delete_all(user_id="user123")
340
+ if success:
341
+ print("All memories deleted successfully")
342
+
343
+ # Delete with error handling
344
+ try:
345
+ success = await memory_service.delete(memory_id="memory_123", user_id="user123")
346
+ if not success:
347
+ logger.warning(f"Memory {memory_id} not found or already deleted")
348
+ except Mem0MemoryServiceError as e:
349
+ logger.error(f"Error deleting memory: {e}")
237
350
  ```
238
351
 
352
+ **Key Features:**
353
+ - **User Scoping**: Both methods respect `user_id` for security
354
+ - **Safe Deletion**: Returns `False` if memory doesn't exist (doesn't raise exception)
355
+ - **Bulk Deletion**: `delete_all()` removes all memories for a user
356
+ - **Idempotent**: Safe to call multiple times (returns `False` if already deleted)
357
+
358
+ **When to use `delete()`:**
359
+ - Removing a specific memory by ID
360
+ - Cleaning up outdated or incorrect memories
361
+ - User-initiated memory removal
362
+
363
+ **When to use `delete_all()`:**
364
+ - Resetting user memory (e.g., account deletion)
365
+ - Clearing test data
366
+ - Bulk cleanup operations
367
+
368
+ **Note**: Deletion is permanent and cannot be undone. Consider implementing soft deletes if you need to recover deleted memories.
369
+
239
370
  ### Bucket Organization
240
371
 
241
372
  Organize memories into buckets for better management:
@@ -336,31 +467,100 @@ for memory in insights:
336
467
  print(f"Insights: {memory.get('insights')}")
337
468
  ```
338
469
 
470
+ ## Architecture
471
+
472
+ ### Base Class Pattern
473
+
474
+ The memory service uses an abstract base class pattern for extensibility:
475
+
476
+ ```python
477
+ from mdb_engine.memory import BaseMemoryService, MemoryServiceError
478
+
479
+ # BaseMemoryService defines the interface
480
+ # Mem0MemoryService implements it (default provider)
481
+ # Future providers can inherit from BaseMemoryService
482
+ ```
483
+
484
+ **Benefits:**
485
+ - **Type Safety**: Use `BaseMemoryService` for type hints
486
+ - **Extensibility**: Easy to add new providers (LangChain, custom, etc.)
487
+ - **Consistency**: All providers implement the same interface
488
+ - **Backward Compatible**: Existing code works without changes
489
+
490
+ ### Creating Custom Memory Providers
491
+
492
+ To create a custom memory provider, inherit from `BaseMemoryService`:
493
+
494
+ ```python
495
+ from mdb_engine.memory import BaseMemoryService, MemoryServiceError
496
+
497
+ class CustomMemoryService(BaseMemoryService):
498
+ """Custom memory service implementation."""
499
+
500
+ def __init__(self, mongo_uri: str, db_name: str, app_slug: str, config: dict | None = None):
501
+ # Initialize your custom implementation
502
+ pass
503
+
504
+ def add(self, messages, user_id=None, metadata=None, **kwargs):
505
+ # Implement add method
506
+ pass
507
+
508
+ # Implement all other abstract methods...
509
+ def get_all(self, user_id=None, limit=100, filters=None, **kwargs):
510
+ pass
511
+
512
+ def search(self, query, user_id=None, limit=5, filters=None, **kwargs):
513
+ pass
514
+
515
+ def get(self, memory_id, user_id=None, **kwargs):
516
+ pass
517
+
518
+ def delete(self, memory_id, user_id=None, **kwargs):
519
+ pass
520
+
521
+ def delete_all(self, user_id=None, **kwargs):
522
+ pass
523
+
524
+ def update(self, memory_id, user_id=None, memory=None, metadata=None, **kwargs):
525
+ pass
526
+ ```
527
+
339
528
  ## API Reference
340
529
 
530
+ ### BaseMemoryService
531
+
532
+ Abstract base class for all memory service implementations. Defines the standard interface.
533
+
341
534
  ### Mem0MemoryService
342
535
 
536
+ Default implementation using Mem0.ai. Inherits from `BaseMemoryService`.
537
+
343
538
  #### Initialization
344
539
 
345
540
  ```python
346
541
  Mem0MemoryService(
347
542
  mongo_uri: str,
348
543
  db_name: str,
349
- collection_name: str = "memories",
350
- app_slug: str = None,
351
- embedding_model: str = "text-embedding-3-small",
352
- embedding_dimensions: int = None,
353
- chat_model: str = "gpt-4",
354
- temperature: float = 0.7,
355
- infer: bool = True,
356
- enable_graph: bool = False,
357
- config: dict = None
544
+ app_slug: str,
545
+ config: dict = None # Optional configuration
546
+ )
547
+
548
+ # Or use the factory function
549
+ from mdb_engine.memory import get_memory_service
550
+
551
+ memory_service = get_memory_service(
552
+ mongo_uri="...",
553
+ db_name="...",
554
+ app_slug="...",
555
+ config={...},
556
+ provider="mem0" # Default, future providers can be specified here
358
557
  )
359
558
  ```
360
559
 
361
560
  #### Methods
362
561
 
363
562
  - `add(messages, user_id, metadata=None, bucket_id=None, bucket_type=None, store_raw_content=False, raw_content=None)` - Add single memory with optional bucket and raw content storage
563
+ - `inject(memory, user_id, metadata=None)` - Manually inject a memory without LLM inference
364
564
  - `add_with_raw_content(messages, raw_content, user_id, bucket_id=None, bucket_type=None)` - Store both extracted facts and raw content
365
565
  - `get_buckets(user_id, bucket_type=None, limit=None)` - Get all buckets for a user
366
566
  - `get_bucket_memories(bucket_id, user_id, include_raw_content=False, limit=None)` - Get all memories in a bucket
@@ -513,15 +713,19 @@ knowledge = await memory_service.search(
513
713
  ## Error Handling
514
714
 
515
715
  ```python
516
- from mdb_engine.memory import Mem0MemoryServiceError
716
+ from mdb_engine.memory import MemoryServiceError, Mem0MemoryServiceError
517
717
 
518
718
  try:
519
719
  memory = await memory_service.add(
520
720
  messages=[{"role": "user", "content": "Test"}],
521
721
  user_id="user123"
522
722
  )
523
- except Mem0MemoryServiceError as e:
723
+ except MemoryServiceError as e:
724
+ # Base exception for all memory service errors
524
725
  print(f"Memory service error: {e}")
726
+ except Mem0MemoryServiceError as e:
727
+ # Specific exception for Mem0 implementation
728
+ print(f"Mem0 memory service error: {e}")
525
729
  except (ValueError, TypeError, ConnectionError) as e:
526
730
  print(f"Configuration or connection error: {e}")
527
731
  ```
@@ -14,16 +14,25 @@ Key Features:
14
14
  - **Optional LLM Inference**: Can leverage LLM service for automatic memory
15
15
  extraction (set infer: false to disable)
16
16
  - **Graph Support**: Optional knowledge graph construction for entity relationships
17
+ - **Extensible Architecture**: Base class allows for future memory provider implementations
17
18
 
18
19
  Dependencies:
19
20
  pip install mem0ai
20
21
  """
21
22
 
23
+ # Import base classes
24
+ from .base import BaseMemoryService, MemoryServiceError
25
+
22
26
  # Import service components (mem0 import is lazy within service.py)
23
27
  from .service import Mem0MemoryService, Mem0MemoryServiceError, get_memory_service
24
28
 
25
29
  __all__ = [
30
+ # Base classes (for extensibility)
31
+ "BaseMemoryService",
32
+ "MemoryServiceError",
33
+ # Mem0 implementation (default)
26
34
  "Mem0MemoryService",
27
35
  "Mem0MemoryServiceError",
36
+ # Factory function
28
37
  "get_memory_service",
29
38
  ]