From 694ce8a329ab060c3a3c37f2295b4ad0179b7773 Mon Sep 17 00:00:00 2001 From: Kunyanli230 <166660863+Kunyanli230@users.noreply.github.com> Date: Wed, 7 Oct 2026 09:38:09 +0000 Subject: [PATCH] Python: preserve custom memory topic slugs on reload --- .../core/agent_framework/_harness/_memory.py | 5 +- .../core/tests/core/test_harness_memory.py | 54 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/python/packages/core/agent_framework/_harness/_memory.py b/python/packages/core/agent_framework/_harness/_memory.py index 66163abed05..40972383eff 100644 --- a/python/packages/core/agent_framework/_harness/_memory.py +++ b/python/packages/core/agent_framework/_harness/_memory.py @@ -823,6 +823,7 @@ def list_topics(self, session: AgentSession, *, source_id: str) -> list[MemoryTo topic_path.read_text(encoding="utf-8"), fallback_topic=topic_path.stem.replace("-", " "), ) + record.slug = topic_path.stem topics.append(record) return sorted(topics, key=lambda record: (record.topic.lower(), record.updated_at)) @@ -831,7 +832,9 @@ def get_topic(self, session: AgentSession, *, source_id: str, topic: str) -> Mem topic_path = self._topic_path(session, source_id=source_id, topic=topic) if not topic_path.exists(): raise FileNotFoundError(f"No memory topic named '{topic}' was found for this owner.") - return MemoryTopicRecord.from_markdown(topic_path.read_text(encoding="utf-8"), fallback_topic=topic) + record = MemoryTopicRecord.from_markdown(topic_path.read_text(encoding="utf-8"), fallback_topic=topic) + record.slug = topic_path.stem + return record def write_topic(self, session: AgentSession, record: MemoryTopicRecord, *, source_id: str) -> None: """Persist one topic memory file.""" diff --git a/python/packages/core/tests/core/test_harness_memory.py b/python/packages/core/tests/core/test_harness_memory.py index d1c7b3dac51..a0fa285b51b 100644 --- a/python/packages/core/tests/core/test_harness_memory.py +++ b/python/packages/core/tests/core/test_harness_memory.py @@ -26,6 +26,7 @@ MemoryStore, MemoryTopicRecord, Message, + SessionContext, ) from .test_filesystem import COLLIDING_IDENTIFIERS @@ -134,6 +135,59 @@ def test_memory_topic_record_round_trips_through_dict_and_markdown() -> None: assert "MemoryTopicRecord(" in repr(record) +@pytest.mark.parametrize("slug", [None, "tools", "Chosen Tools"]) +def test_memory_file_store_preserves_topic_slug(tmp_path: Path, slug: str | None) -> None: + """Reloaded topics keep their filename identity when the heading differs from the slug.""" + session = AgentSession(session_id="session-1") + session.state["owner_id"] = "alice" + store = MemoryFileStore(tmp_path, owner_state_key="owner_id") + record = MemoryTopicRecord( + topic="Preferred Tools", + slug=slug, + summary="Use the preferred tools.", + memories=["Use Python for scripts."], + updated_at="2026-04-21T10:05:00+00:00", + session_ids=[session.session_id], + ) + store.write_topic(session, record, source_id=DEFAULT_MEMORY_SOURCE_ID) + + reopened_store = MemoryFileStore(tmp_path, owner_state_key="owner_id") + loaded = reopened_store.get_topic(session, source_id=DEFAULT_MEMORY_SOURCE_ID, topic=record.slug) + assert loaded == record + assert reopened_store.list_topics(session, source_id=DEFAULT_MEMORY_SOURCE_ID) == [record] + + loaded.summary = "Use the updated tools." + reopened_store.write_topic(session, loaded, source_id=DEFAULT_MEMORY_SOURCE_ID) + assert reopened_store.list_topics(session, source_id=DEFAULT_MEMORY_SOURCE_ID) == [loaded] + assert reopened_store.get_topic(session, source_id=DEFAULT_MEMORY_SOURCE_ID, topic=record.slug) == loaded + + +async def test_memory_context_provider_loads_topic_with_custom_slug(tmp_path: Path) -> None: + """The rebuilt index must point to the existing file so selected memory can be injected.""" + session = AgentSession(session_id="session-1") + session.state["owner_id"] = "alice" + store = MemoryFileStore(tmp_path, owner_state_key="owner_id") + record = MemoryTopicRecord( + topic="Preferred Tools", + slug="tools", + summary="Use the preferred tools.", + memories=["Use Python for scripts."], + updated_at="2026-04-21T10:05:00+00:00", + ) + store.write_topic(session, record, source_id=DEFAULT_MEMORY_SOURCE_ID) + provider = MemoryContextProvider(store=store) + context = SessionContext(session_id=session.session_id, input_messages=[Message("user", ["Which tools?"])]) + + await provider.before_run(agent=None, session=session, context=context, state={}) + + assert "topics/tools.md" in store.get_index_text( + session, source_id=DEFAULT_MEMORY_SOURCE_ID, line_limit=200, line_length=150 + ) + assert any( + "Use Python for scripts." in message.text for message in context.context_messages[DEFAULT_MEMORY_SOURCE_ID] + ) + + async def test_memory_file_store_writes_topics_index_state_and_transcripts(tmp_path) -> None: """The file-backed memory store should manage topics, ``MEMORY.md``, state, and transcript search.""" store = MemoryFileStore(