mdb-engine 0.7.2__py3-none-any.whl → 0.7.3__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,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.2" # Memory service update functionality
86
- # - ADDED: Memory service update() method for in-place memory updates
87
- # - ADDED: Support for updating memory content and metadata while preserving IDs
88
- # - ADDED: Automatic embedding recomputation via Mem0's update method
89
- # - ADDED: Comprehensive unit tests for memory update functionality (17 tests)
90
- # - ENHANCED: Memory service now uses Mem0's native update method exclusively
91
- # - REMOVED: Direct MongoDB update fallback (simplified implementation)
92
- )
84
+ __version__ = "0.7.3"
93
85
 
94
86
  __all__ = [
95
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.2", 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.
@@ -268,10 +268,13 @@ class Mem0MemoryService:
268
268
  if isinstance(messages, str):
269
269
  messages = [{"role": "user", "content": messages}]
270
270
 
271
- # Merge metadata
272
271
  final_metadata = dict(metadata) if metadata else {}
273
272
 
274
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
+
275
278
  if bucket_id:
276
279
  final_metadata["bucket_id"] = bucket_id
277
280
  final_metadata["context_id"] = bucket_id # Backwards compatibility
@@ -279,7 +282,6 @@ class Mem0MemoryService:
279
282
  if bucket_type:
280
283
  final_metadata["bucket_type"] = bucket_type
281
284
 
282
- # Store raw_content in metadata if provided (metadata convenience)
283
285
  if raw_content:
284
286
  final_metadata["raw_content"] = raw_content
285
287
 
@@ -502,12 +504,12 @@ class Mem0MemoryService:
502
504
  memory_id: The ID of the memory to update (required)
503
505
  user_id: The user ID who owns the memory (for scoping and security)
504
506
  memory: New memory content as a string (optional)
505
- data: Alternative parameter name for memory content (backward compatibility).
507
+ data: Alternative parameter name for memory content.
506
508
  Can be a string or dict with 'memory'/'text'/'content' key.
507
509
  messages: Alternative way to provide content as messages (optional).
508
510
  Can be a string or list of dicts with 'content' key.
509
- metadata: Metadata updates to merge with existing metadata (optional).
510
- Merged, not replaced - existing keys are preserved unless overridden.
511
+ metadata: Metadata updates (optional, but NOT SUPPORTED by Mem0 update API).
512
+ This parameter is accepted for API consistency but will be ignored.
511
513
  **kwargs: Additional arguments passed to Mem0 operations
512
514
 
513
515
  Returns:
@@ -535,16 +537,14 @@ class Mem0MemoryService:
535
537
  )
536
538
  ```
537
539
  """
538
- # Input validation
539
540
  if not memory_id or not isinstance(memory_id, str) or not memory_id.strip():
540
541
  raise ValueError("memory_id is required and must be a non-empty string")
541
542
 
542
543
  try:
543
- # Normalize data parameter (backward compatibility)
544
+ # Normalize data parameter (alternative to memory parameter)
544
545
  normalized_memory = self._normalize_content_input(memory, data, messages)
545
546
  normalized_metadata = self._normalize_metadata_input(metadata, data)
546
547
 
547
- # Verify memory exists before attempting update
548
548
  existing_memory = self.get(memory_id=memory_id, user_id=user_id, **kwargs)
549
549
  if not existing_memory:
550
550
  logger.warning(
@@ -675,46 +675,21 @@ class Mem0MemoryService:
675
675
  """
676
676
  Update memory using Mem0's built-in update method.
677
677
 
678
- This is the primary update path. Mem0's update method handles:
679
- - In-place updates preserving memory ID and created_at timestamp
680
- - Automatic embedding recomputation when content changes
681
- - Metadata merging
682
- - User scoping for security
678
+ Note: Mem0's update() only supports content updates (text parameter).
679
+ Metadata updates are not supported by the Mem0 API.
683
680
 
684
681
  Args:
685
682
  memory_id: Memory ID to update
686
- user_id: User ID for scoping
683
+ user_id: User ID for scoping (not used in update call)
687
684
  memory: New memory content (normalized)
688
- metadata: Metadata to merge (normalized)
685
+ metadata: Metadata to merge (ignored - not supported by Mem0 update API)
689
686
  **kwargs: Additional arguments passed to Mem0
690
687
 
691
688
  Returns:
692
689
  Updated memory dict or None if not found
693
-
694
- Raises:
695
- Various exceptions from Mem0 if update fails
696
690
  """
697
- # Build update parameters matching Mem0's API
698
- # Mem0's update method signature:
699
- # update(memory_id, text=None, metadata=None, user_id=None, **kwargs)
700
- update_kwargs: dict[str, Any] = {"memory_id": memory_id}
701
-
702
- # Add user_id for scoping (Mem0 supports this)
703
- if user_id:
704
- update_kwargs["user_id"] = str(user_id)
705
-
706
- # Add text/content if provided
707
- # Mem0 uses "text" parameter for content
708
- if memory:
709
- update_kwargs["text"] = memory
710
-
711
- # Add metadata if provided
712
- # Mem0 merges metadata automatically
713
- if metadata is not None:
714
- update_kwargs["metadata"] = metadata
715
-
716
- # Pass through any additional kwargs
717
- update_kwargs.update(kwargs)
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"}
718
693
 
719
694
  logger.debug(
720
695
  f"Calling mem0.update() for memory_id={memory_id}",
@@ -726,24 +701,29 @@ class Mem0MemoryService:
726
701
  },
727
702
  )
728
703
 
729
- # Call Mem0's update method directly
730
- # This handles all the complexity: embedding recomputation, ID preservation, etc.
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
+
731
715
  result = self.memory.update(**update_kwargs)
732
716
 
733
- # Normalize result format
734
- # Mem0 may return dict, list, or other formats
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
735
721
  if isinstance(result, dict):
736
722
  return result
737
723
  elif isinstance(result, list) and len(result) > 0:
738
- # If list, return first item
739
724
  return result[0] if isinstance(result[0], dict) else None
740
725
  else:
741
- # If result is None or unexpected format, return None to trigger fallback
742
- logger.debug(
743
- f"Mem0 update returned unexpected format: {type(result)}",
744
- extra={"memory_id": memory_id},
745
- )
746
- return None
726
+ return self.get(memory_id=memory_id)
747
727
 
748
728
  def _normalize_result(self, result: Any) -> list[dict[str, Any]]:
749
729
  """Normalize Mem0's return type (dict vs list)."""
@@ -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.2
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.
@@ -1,5 +1,5 @@
1
1
  mdb_engine/README.md,sha256=T3EFGcPopY9LslYW3lxgG3hohWkAOmBNbYG0FDMUJiY,3502
2
- mdb_engine/__init__.py,sha256=12PLLmXDANoV30dDxw5To9taKAZnIMolRoV1Vwdjkwk,3618
2
+ mdb_engine/__init__.py,sha256=AwWt-MyzGNDl_2YEkTO0-x15qnUw3tF-oiOJJAYGkIs,3097
3
3
  mdb_engine/config.py,sha256=DTAyxfKB8ogyI0v5QR9Y-SJOgXQr_eDBCKxNBSqEyLc,7269
4
4
  mdb_engine/constants.py,sha256=eaotvW57TVOg7rRbLziGrVNoP7adgw_G9iVByHezc_A,7837
5
5
  mdb_engine/dependencies.py,sha256=MJuYQhZ9ZGzXlip1ha5zba9Rvn04HDPWahJFJH81Q2s,14107
@@ -35,7 +35,7 @@ mdb_engine/auth/utils.py,sha256=YkexCo0xV37mpOJUI32cntRHVOUUS7r19TIMPWHcgpA,2734
35
35
  mdb_engine/auth/websocket_sessions.py,sha256=7eFNagY2K3Rp1x7d_cO5JcpT-DrYkc__cmVhl6pAC2M,15081
36
36
  mdb_engine/auth/websocket_tickets.py,sha256=VoIArcnQBtYqXRMs-5m7NSvCJB1dEeHrLl7j7yG-H-A,9887
37
37
  mdb_engine/cli/__init__.py,sha256=PANRi4THmL34d1mawlqxIrnuItXMdqoMTq5Z1zHd7rM,301
38
- mdb_engine/cli/main.py,sha256=cp-fxcijZN4o4taBTP2sR57Y1A-ZId0Qga7AhgrU48o,811
38
+ mdb_engine/cli/main.py,sha256=qDVVUJOXajdZmbId9AIGBS380U7OlhA8ILrAIfURbSE,811
39
39
  mdb_engine/cli/utils.py,sha256=bNRGJgdzxUjXAOVe1aoxWJ5M_IqtAE-eW4pfAkwiDDM,2760
40
40
  mdb_engine/cli/commands/__init__.py,sha256=ZSzMhKdV9ILD5EbOSxDV9nURHo1e4bQ0c8AWpqsTEqM,115
41
41
  mdb_engine/cli/commands/generate.py,sha256=VEcn7qNQkIPQlLEK3oXUBgYMwD-G0FyomXQcWTtKsbs,17304
@@ -76,7 +76,7 @@ mdb_engine/indexes/helpers.py,sha256=tJHqDm18jKLrW-P2ofmnUnaf4_-V5xDLjqP_WU8W9MM
76
76
  mdb_engine/indexes/manager.py,sha256=ekrYBfKD-GOBpZMjXIZWSQ7UPLgrD5BINYzVteWkSI8,32508
77
77
  mdb_engine/memory/README.md,sha256=Xa7pYut4zgZyRcb2TALcucMVAHCAu9uNGBKSwE7stY8,14764
78
78
  mdb_engine/memory/__init__.py,sha256=e4kAYgxd_-WAH8GovTwjEBO9hvASu_kXEupMgksAL-U,1008
79
- mdb_engine/memory/service.py,sha256=QWMNZfMaS3-SPZZswnO410C4-3ibAOUw0LH93WjZLsM,29550
79
+ mdb_engine/memory/service.py,sha256=huWIjHAsV-g_Cs9djRkd8v5ZgnqiXqtNEbNjAgMfzs4,28846
80
80
  mdb_engine/observability/README.md,sha256=CMgQaC1H8ESmCitfbhJifz6-XoXH_FPNE4MvuZ-oFas,13085
81
81
  mdb_engine/observability/__init__.py,sha256=jjLsrW6Gy2ayrbfLrgHsDB61NxWWkYLHwv0q-N3fxjA,1213
82
82
  mdb_engine/observability/health.py,sha256=ORjxF_rHYA9vCoxUqel5p0n9g3PLmHsHQn68Q402b6g,9212
@@ -88,12 +88,12 @@ mdb_engine/repositories/mongo.py,sha256=Wg32_6v0KHAHumhz5z8QkoqJRWAMJFA7Y2lYIJ7L
88
88
  mdb_engine/repositories/unit_of_work.py,sha256=XvmwGOspEDj4hsfOULPsQKjB1QZqh83TJo6vGV4tiqU,5118
89
89
  mdb_engine/routing/README.md,sha256=zk-Wux-QpuWWhprfVeiUREv8PEDJZNgh2QblXIa4v4M,14201
90
90
  mdb_engine/routing/__init__.py,sha256=reupjHi_RTc2ZBA4AH5XzobAmqy4EQIsfSUcTkFknUM,2438
91
- mdb_engine/routing/websockets.py,sha256=jgdLsrSOKyBiY4yzE7XYXV1MOChLw2CIWpT6RDL23hk,51649
91
+ mdb_engine/routing/websockets.py,sha256=w0m1XMpXKPpPrEBcZNIj8B8PwWeTNrseUZE1P8cipdM,53441
92
92
  mdb_engine/utils/__init__.py,sha256=lDxQSGqkV4fVw5TWIk6FA6_eey_ZnEtMY0fir3cpAe8,236
93
93
  mdb_engine/utils/mongo.py,sha256=Oqtv4tQdpiiZzrilGLEYQPo8Vmh8WsTQypxQs8Of53s,3369
94
- mdb_engine-0.7.2.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
95
- mdb_engine-0.7.2.dist-info/METADATA,sha256=NkjWW4qrAnJs_T4NxvCvQvZkln4vRYSRa101sBbsXY4,19695
96
- mdb_engine-0.7.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
97
- mdb_engine-0.7.2.dist-info/entry_points.txt,sha256=INCbYdFbBzJalwPwxliEzLmPfR57IvQ7RAXG_pn8cL8,48
98
- mdb_engine-0.7.2.dist-info/top_level.txt,sha256=PH0UEBwTtgkm2vWvC9He_EOMn7hVn_Wg_Jyc0SmeO8k,11
99
- mdb_engine-0.7.2.dist-info/RECORD,,
94
+ mdb_engine-0.7.3.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
95
+ mdb_engine-0.7.3.dist-info/METADATA,sha256=3XBbRELougI8lJsaJ1A02SuhIpsv2ojQspD2XidEXbo,18843
96
+ mdb_engine-0.7.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
97
+ mdb_engine-0.7.3.dist-info/entry_points.txt,sha256=INCbYdFbBzJalwPwxliEzLmPfR57IvQ7RAXG_pn8cL8,48
98
+ mdb_engine-0.7.3.dist-info/top_level.txt,sha256=PH0UEBwTtgkm2vWvC9He_EOMn7hVn_Wg_Jyc0SmeO8k,11
99
+ mdb_engine-0.7.3.dist-info/RECORD,,