diff --git a/datamind/capabilities/memory/service.py b/datamind/capabilities/memory/service.py index 882b858..c79b45b 100644 --- a/datamind/capabilities/memory/service.py +++ b/datamind/capabilities/memory/service.py @@ -92,12 +92,20 @@ async def recall( session_id: str | None = None, top_k: int = 8, kinds: Sequence[str] | None = None, + scope_filter: Sequence[str] | None = None, include_archived: bool = False, ) -> list[dict[str, Any]]: + effective_profile = profile or self._default_profile + effective_session = session_id + if scope_filter: + if 'profile' not in scope_filter: + effective_profile = None + if 'session' not in scope_filter: + effective_session = None hits = await self.long_term.recall( query, - profile=profile or self._default_profile, - session_id=session_id, + profile=effective_profile, + session_id=effective_session, top_k=top_k, kinds=kinds, include_archived=include_archived, diff --git a/datamind/capabilities/memory/tools.py b/datamind/capabilities/memory/tools.py index a328fa3..dc5eb50 100644 --- a/datamind/capabilities/memory/tools.py +++ b/datamind/capabilities/memory/tools.py @@ -82,6 +82,7 @@ async def _recall( session_id=eff_session, top_k=top_k, kinds=kinds, + scope_filter=scope_filter, ) return {"query": query, "count": len(hits), "results": hits} diff --git a/datamind/tests/test_memory.py b/datamind/tests/test_memory.py index 9b9c452..1f47a56 100644 --- a/datamind/tests/test_memory.py +++ b/datamind/tests/test_memory.py @@ -8,6 +8,7 @@ from datamind.capabilities.memory import ( MemoryService, ShortTermMemory, + build_memory_tools, ) from datamind.capabilities.memory.providers.sqlite_store import SQLiteMemoryStore from datamind.core.protocols import MemoryStore @@ -276,6 +277,35 @@ async def test_service_combines_short_and_long(tmp_path): assert await svc.forget(rid) +@pytest.mark.asyncio +async def test_memory_recall_scope_filter_excludes_unselected_scopes(tmp_path): + '''A global-only recall must not reintroduce the default profile.''' + lt = SQLiteMemoryStore(db_path=str(tmp_path / 'm.db'), embedding=_FakeEmbed()) + svc = MemoryService( + short_term=ShortTermMemory(max_turns=5), + long_term=lt, + default_profile='acme', + ) + tools = {tool.name: tool.handler for tool in build_memory_tools(svc)} + + await tools['memory_save']('global fact', scope='global') + await tools['memory_save']('profile fact', scope='profile', profile='acme') + await tools['memory_save']( + 'session fact', scope='session', session_id='chat-1' + ) + + result = await tools['memory_recall']( + 'fact', + profile='acme', + session_id='chat-1', + scope_filter=['global'], + ) + + assert result['count'] == 1 + assert result['results'][0]['scope'] == 'global' + assert result['results'][0]['content'] == 'global fact' + + @pytest.mark.asyncio async def test_service_default_profile_used_when_omitted(tmp_path): """save without explicit profile should land under default_profile."""