docs(examples): sync python demo summary#1348
Conversation
|
@haosenwang1018 Thanks for this PR. Please sign your commits, which is a requirement for merging. See section 3.5 of the CONTRIBUTING guide. Thanks. |
There was a problem hiding this comment.
Pull request overview
Updates the Python client, LangGraph integration, and documentation/examples to align with the current v2 API filtering semantics and typed response models.
Changes:
- Add optional raw
filterstring support to Python clientMemory.search()andMemory.list()and pass it through LangGraph tools. - Update docs/examples to describe typed
SearchResultusage and metadata-prefixed filter keys. - Update/extend client tests for raw filter string handling and metadata-prefixed filter dict keys.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/client/src/memmachine_client/memory.py | Adds raw filter string support and combines it with built-in + filter_dict filters. |
| packages/client/src/memmachine_client/langgraph.py | Adds episode_type passthrough/normalization and raw filter passthrough for searches. |
| packages/client/client_tests/test_memory.py | Adds tests for raw filter string behavior; updates some filter-string expectations. |
| packages/client/client_tests/test_langgraph.py | Updates tests for new episode_type param and raw filter passthrough. |
| packages/client/client_tests/test_integration_complete.py | Updates integration test filter dict to use metadata. prefix. |
| packages/client/README.md | Updates Python usage snippets to typed results and metadata-prefixed filters. |
| examples/memmachine_client_demo.py | Updates demo printing and filter dict examples to typed results + metadata-prefixed filters. |
| examples/README.md | Updates examples index description for the Python demo. |
| docs/api_reference/python/client.mdx | Updates docs snippet to typed accessors for search results. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| *, | ||
| filter: str | None = None, | ||
| set_metadata: dict[str, JsonValue] | None = None, | ||
| agent_mode: bool = False, |
There was a problem hiding this comment.
The PR description/title indicates this is a docs-only sync for the examples index, but this change introduces new public client API surface (filter raw string parameter). Please either update the PR description to reflect the behavioral/API changes, or split the docs-only update from the client API change to keep review/rollout risk clear.
| episodic_memories = results.episodic_memory or [] | ||
| if not episodic_memories: | ||
| print(" No memories found.") | ||
| return | ||
|
|
||
| episodic_memories = results["episodic_memory"] | ||
| if episodic_memories and len(episodic_memories) > 0: | ||
| print(f" Found {len(episodic_memories[0])} relevant memories:") | ||
| for i, memory in enumerate(episodic_memories[0][:3], 1): # Show top 3 | ||
| print(f" Time: {memory['timestamp']}") | ||
| print(f" {i}. {memory['content']}") | ||
| if memory.get("user_metadata"): | ||
| print(f" Metadata: {memory['user_metadata']}") | ||
| print() | ||
| print(f" Found {len(episodic_memories)} relevant memories:") | ||
| for i, memory in enumerate(episodic_memories[:3], 1): # Show top 3 | ||
| print(f" Time: {memory.timestamp}") | ||
| print(f" {i}. {memory.content}") | ||
| if memory.user_metadata: | ||
| print(f" Metadata: {memory.user_metadata}") |
There was a problem hiding this comment.
memory.search() returns a SearchResult whose episodic/semantic payloads live under results.content (per memmachine_common.api.spec.SearchResult). Accessing results.episodic_memory and treating it as a flat list will raise AttributeError, and EpisodeResponse fields are created_at/metadata (not timestamp/user_metadata). Update this helper to pull episodes from results.content.episodic_memory.{short_term_memory,long_term_memory}.episodes and print the correct fields.
| print(f"Episodic memory: {results.episodic_memory or []}") | ||
| print(f"Semantic memory: {results.semantic_memory or []}") | ||
|
|
||
| # Search with filters | ||
| work_results = memory.search("Tell me about work", filter_dict={"category": "work"}) | ||
| work_results = memory.search("Tell me about work", filter_dict={"metadata.category": "work"}) | ||
| print(f"Work results: {work_results}") |
There was a problem hiding this comment.
This README example uses results.episodic_memory / results.semantic_memory, but the Python client returns a SearchResult with these fields under results.content (SearchResultContent.episodic_memory / .semantic_memory). As written, the snippet will raise AttributeError. Update the example to access results.content and, for episodic, iterate the episode lists inside the short/long-term containers.
| print("Results found:") | ||
| for idx, item in enumerate(results.get('episodic_memory', [])): | ||
| print(f"[{idx+1}] {item['content']} (Role: {item['producer_role']})") | ||
| for idx, item in enumerate(results.episodic_memory or []): |
There was a problem hiding this comment.
This snippet iterates results.episodic_memory, but SearchResult models store results under results.content.episodic_memory (and episodic results are nested under short/long-term memory, not a flat list). As written it will fail at runtime. Adjust the docs example to extract the episode list(s) from results.content.episodic_memory before iterating.
| for idx, item in enumerate(results.episodic_memory or []): | |
| episodes = [] | |
| episodic_memory = results.content.episodic_memory if results.content else None | |
| if episodic_memory: | |
| episodes.extend(episodic_memory.short_term or []) | |
| episodes.extend(episodic_memory.long_term or []) | |
| for idx, item in enumerate(episodes): |
| assert "category='work'" in json_data["filter"] | ||
| assert call_args[1]["timeout"] == 15 |
There was a problem hiding this comment.
This assertion expects an unprefixed category='work' filter to be sent. Given the server-side filter normalization only treats user metadata as user metadata when prefixed with metadata. or m., an unprefixed category field is likely treated as an unknown system field (and ignored / rejected depending on server version). Consider updating this backward-compat positional-args test to use metadata.category (and update the asserted substring) so it reflects the current filter semantics.
| def add_memory_tool( | ||
| content: str, | ||
| user_id: str | None = None, | ||
| metadata: dict[str, Any] | None = None, | ||
| episode_type: EpisodeType | str | None = None, | ||
| ) -> dict[str, Any]: |
There was a problem hiding this comment.
create_add_memory_tool's return type annotation still describes a 3-argument callable, but add_memory_tool now accepts a 4th episode_type parameter. Please update the Callable[[...]] type signature (and any related docs) to match the actual tool function signature so type checkers/users don't get incorrect guidance.
| def list( | ||
| self, | ||
| memory_type: MemoryType = MemoryType.Episodic, | ||
| page_size: int = 100, | ||
| page_num: int = 0, | ||
| filter_dict: dict[str, str] | None = None, | ||
| filter: str | None = None, | ||
| set_metadata: dict[str, JsonValue] | None = None, | ||
| timeout: int | None = None, | ||
| ) -> ListResult: |
There was a problem hiding this comment.
list() adds a new positional parameter (filter) between filter_dict and set_metadata, which can silently break any callers passing set_metadata/timeout positionally (their dict/int would be interpreted as the raw filter string). To preserve backward compatibility, make filter keyword-only (e.g., move it after a * or place it after timeout as a kw-only arg) or append it at the end without changing existing positional ordering.
|
Hi @haosenwang1018! For this PR (and all your other PRs) to merge, we need you to sign your commits. Also, please review the feedback CoPilot has generated and respond accordingly. Conflicts are especially important to respond to. Kind Regards, |
Add three small features to the Python client and its LangGraph wrapper so callers can pass structured filter expressions and either-enum-or-string episode types directly. 1. `Memory.search(filter=...)` and `Memory.list(filter=...)` Accept an optional raw filter string alongside `filter_dict`. When both are provided, the two are combined with `AND`. The raw filter is passed through to the v2 `SearchMemoriesSpec.filter` / `ListMemoriesSpec.filter` fields unchanged. 2. `MemMachineTools.search_memory(filter=...)` Pipes the same raw filter through the LangGraph search-memory tool. 3. `MemMachineTools.add_memory(episode_type=...)` Accept either an `EpisodeType` enum or its string value (e.g. `"message"`), normalizing strings via `EpisodeType(...)` before delegating to `Memory.add`. The factory tool's return-type annotation was widened to match. The `filter` parameter shadows the Python builtin, which is the same trade-off `memmachine_common.api.SearchMemoriesSpec` already made for its `filter:` field — keeping the parameter name aligned with the API field. `# noqa: A002` is applied at the three call sites with a comment pointing at the API spec. This commit consolidates the substantive work from haosenwang1018's 9-commit stack (MemMachine#1341 → MemMachine#1349) into a single rebased+linted commit against current `main`. The original stack's prefix-style doc and test changes have been omitted because they have already landed on `main` via MemMachine#1352 and MemMachine#1311. The original commits authored by haosenwang1018: - 921b55f feat(client): support raw filter strings - e0849bb feat(langgraph): support raw filter strings - 53b489e fix(langgraph): normalize episode type strings Closes MemMachine#1341, MemMachine#1342, MemMachine#1343, MemMachine#1344, MemMachine#1345, MemMachine#1346, MemMachine#1347, MemMachine#1348, MemMachine#1349 Co-authored-by: Steve Scargall <steve.scargall@gmail.com> Signed-off-by: Steve Scargall <37674041+sscargal@users.noreply.github.com>
…ion (#1403) * feat(client+langgraph): raw filter strings and EpisodeType normalization Add three small features to the Python client and its LangGraph wrapper so callers can pass structured filter expressions and either-enum-or-string episode types directly. 1. `Memory.search(filter=...)` and `Memory.list(filter=...)` Accept an optional raw filter string alongside `filter_dict`. When both are provided, the two are combined with `AND`. The raw filter is passed through to the v2 `SearchMemoriesSpec.filter` / `ListMemoriesSpec.filter` fields unchanged. 2. `MemMachineTools.search_memory(filter=...)` Pipes the same raw filter through the LangGraph search-memory tool. 3. `MemMachineTools.add_memory(episode_type=...)` Accept either an `EpisodeType` enum or its string value (e.g. `"message"`), normalizing strings via `EpisodeType(...)` before delegating to `Memory.add`. The factory tool's return-type annotation was widened to match. The `filter` parameter shadows the Python builtin, which is the same trade-off `memmachine_common.api.SearchMemoriesSpec` already made for its `filter:` field — keeping the parameter name aligned with the API field. `# noqa: A002` is applied at the three call sites with a comment pointing at the API spec. This commit consolidates the substantive work from haosenwang1018's 9-commit stack (#1341 → #1349) into a single rebased+linted commit against current `main`. The original stack's prefix-style doc and test changes have been omitted because they have already landed on `main` via #1352 and #1311. The original commits authored by haosenwang1018: - 921b55f feat(client): support raw filter strings - e0849bb feat(langgraph): support raw filter strings - 53b489e fix(langgraph): normalize episode type strings Closes #1341, #1342, #1343, #1344, #1345, #1346, #1347, #1348, #1349 Co-authored-by: Steve Scargall <steve.scargall@gmail.com> Signed-off-by: Steve Scargall <37674041+sscargal@users.noreply.github.com> * docs(langgraph): document filter and episode type support --------- Signed-off-by: Steve Scargall <37674041+sscargal@users.noreply.github.com> Co-authored-by: haosenwang1018 <haosenwang1018@users.noreply.github.com> Co-authored-by: Shu Wang <33640803+malatewang@users.noreply.github.com>
|
Closed by #1403 |
Purpose of the change
Keep the examples index page aligned with the current Python client demo behavior.
Description
Follow-up to #973 / #1305 / #1307
This updates
examples/README.mdso the summary formemmachine_client_demo.pyreflects the current example semantics:SearchResultobjectsThis keeps the top-level examples index consistent with the demo file that was updated in recent follow-up PRs.
Type of change
How Has This Been Tested?
Manual verification:
SearchResultusage for the Python client demoexamples/README.mdChecklist
Maintainer Checklist