mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-09-21 02:16:27 +00:00
fix(memory): preserve reusable scope configs (#7068)
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
This commit is contained in:
@@ -26,6 +26,7 @@ def _ensure_memory_kind(value: Any) -> Any:
|
||||
Pass-through for non-dict values (instances, ``bool``, ``None``).
|
||||
"""
|
||||
if isinstance(value, dict) and "memory_kind" not in value:
|
||||
value = dict(value)
|
||||
if "scopes" in value:
|
||||
value["memory_kind"] = "slice"
|
||||
elif "root_path" in value:
|
||||
@@ -55,6 +56,7 @@ class MemoryScope(BaseModel):
|
||||
return data
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError(f"Expected dict or MemoryScope, got {type(data).__name__}")
|
||||
data = dict(data)
|
||||
memory = data.pop("memory", None)
|
||||
instance: MemoryScope = handler(data)
|
||||
if memory is not None:
|
||||
@@ -245,6 +247,7 @@ class MemorySlice(BaseModel):
|
||||
return data
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError(f"Expected dict or MemorySlice, got {type(data).__name__}")
|
||||
data = dict(data)
|
||||
memory = data.pop("memory", None)
|
||||
data["scopes"] = [s.rstrip("/") or "/" for s in data.get("scopes", [])]
|
||||
instance: MemorySlice = handler(data)
|
||||
|
||||
@@ -240,6 +240,50 @@ def test_memory_scope_slice(tmp_path: Path, mock_embedder: MagicMock) -> None:
|
||||
assert "/a" in sl.scopes and "/b" in sl.scopes
|
||||
|
||||
|
||||
def test_memory_scope_config_can_be_reused() -> None:
|
||||
"""Constructing a scope must not remove the memory from caller-owned config."""
|
||||
from crewai.memory.memory_scope import MemoryScope
|
||||
|
||||
memory = MagicMock()
|
||||
config = {"memory": memory, "root_path": "/agent/1"}
|
||||
|
||||
first = MemoryScope.model_validate(config)
|
||||
second = MemoryScope.model_validate(config)
|
||||
|
||||
assert config == {"memory": memory, "root_path": "/agent/1"}
|
||||
assert first._require_memory() is memory
|
||||
assert second._require_memory() is memory
|
||||
|
||||
|
||||
def test_memory_slice_config_can_be_reused_without_normalizing_it_in_place() -> None:
|
||||
"""Constructing a slice must preserve caller-owned dependencies and paths."""
|
||||
from crewai.memory.memory_scope import MemorySlice
|
||||
|
||||
memory = MagicMock()
|
||||
config = {"memory": memory, "scopes": ["/team/", "/"]}
|
||||
|
||||
first = MemorySlice.model_validate(config)
|
||||
second = MemorySlice.model_validate(config)
|
||||
|
||||
assert config == {"memory": memory, "scopes": ["/team/", "/"]}
|
||||
assert first.scopes == ["/team", "/"]
|
||||
assert second.scopes == ["/team", "/"]
|
||||
assert first._require_memory() is memory
|
||||
assert second._require_memory() is memory
|
||||
|
||||
|
||||
def test_memory_kind_inference_preserves_input() -> None:
|
||||
"""Inferring a legacy config's discriminator must not mutate that config."""
|
||||
from crewai.memory.memory_scope import _ensure_memory_kind
|
||||
|
||||
config = {"root_path": "/agent/1"}
|
||||
|
||||
normalized = _ensure_memory_kind(config)
|
||||
|
||||
assert config == {"root_path": "/agent/1"}
|
||||
assert normalized == {"root_path": "/agent/1", "memory_kind": "scope"}
|
||||
|
||||
|
||||
def test_memory_list_scopes_info_tree(tmp_path: Path, mock_embedder: MagicMock) -> None:
|
||||
from crewai.memory.unified_memory import Memory
|
||||
|
||||
|
||||
Reference in New Issue
Block a user