Repository navigation
docs(examples): update remaining filter prefix demo usage - #1347
haosenwang1018 wants to merge 7 commits into
Conversation
|
@haosenwang1018, please sign your commits and resolve the failing tests. Thanks. |
There was a problem hiding this comment.
Pull request overview
This PR aims to finish updating client-facing examples/docs to use the strict, prefixed metadata filter keys required by the backend filter validator, and also introduces support for passing a raw filter string through the Python client API.
Changes:
- Add an optional raw
filterstring parameter to the Python client’sMemory.search()andMemory.list()and plumb it through LangGraph tool helpers. - Update tests and integration docs/examples to use
metadata.-prefixed metadata filter keys. - Update README/docs/demo snippets to use attribute access patterns (but some updated snippets currently don’t match the actual
SearchResultshape).
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/client/src/memmachine_client/memory.py | Adds filter param and AND-combines dict-based and explicit filters (also introduces a positional-arg compatibility concern in list()). |
| packages/client/src/memmachine_client/langgraph.py | Adds episode_type passthrough/normalization and a raw filter passthrough to search_memory. |
| packages/client/client_tests/test_memory.py | Adds coverage for raw filter string combination; updates some dict-to-filter-string expectations to prefixed keys (but still contains some unprefixed filter_dict usage in the edited hunk). |
| packages/client/client_tests/test_langgraph.py | Updates mocks/expectations for new filter passthrough and episode_type normalization. |
| packages/client/client_tests/test_integration_complete.py | Updates integration test example to use metadata. prefix in filter_dict. |
| packages/client/README.md | Updates examples to new result access + prefixed filter keys (but result access is currently incorrect for SearchResult). |
| examples/memmachine_client_demo.py | Updates demo filters to prefixed keys and refactors result printing (but currently assumes an incorrect search result structure). |
| docs/api_reference/python/client.mdx | Updates snippet to attribute access (but currently assumes incorrect top-level results.episodic_memory). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 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}") | ||
| print() |
There was a problem hiding this comment.
print_memory_results() assumes results.episodic_memory is a flat list of episode objects with .timestamp and .user_metadata. In the current client, memory.search() returns a SearchResult with episodes under results.content.episodic_memory.short_term_memory.episodes and .long_term_memory.episodes, and episode timestamps/metadata are created_at / metadata. This demo will break at runtime unless it is updated to match the actual response model.
| for idx, item in enumerate(results.episodic_memory or []): | ||
| print(f"[{idx+1}] {item.content} (Role: {item.producer_role})") |
There was a problem hiding this comment.
This docs snippet iterates results.episodic_memory, but memory.search() returns a SearchResult with episodic hits under results.content.episodic_memory (and then short_term_memory.episodes / long_term_memory.episodes). Update the example to traverse results.content and extract the episode list before iterating; otherwise the docs example will not run.
| 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, |
There was a problem hiding this comment.
Memory.list() adds a new filter parameter before existing set_metadata and timeout without making it keyword-only. This changes the positional-argument order and is a breaking change for any callers that previously passed set_metadata / timeout positionally. Consider making filter (and likely set_metadata/timeout) keyword-only (e.g., introducing *) to preserve backward compatibility.
| def search( | ||
| self, | ||
| query: str, | ||
| limit: int | None = None, | ||
| expand_context: int = 0, | ||
| score_threshold: float | None = None, | ||
| filter_dict: dict[str, str] | None = None, | ||
| timeout: int | None = None, | ||
| *, | ||
| filter: str | None = None, | ||
| set_metadata: dict[str, JsonValue] | None = None, | ||
| agent_mode: bool = False, |
There was a problem hiding this comment.
The PR title/description indicate this is a docs/examples-only update for filter prefixing, but this diff also introduces new public API surface (filter parameter in Memory.search()/list()) and new LangGraph tool inputs (episode_type). If these changes are intentional, the PR description/title should be updated to reflect the API changes (or the API changes split into a separate PR).
| memory.search("query", 20, 1, 0.4, {"category": "work"}, 15) | ||
|
|
||
| call_args = mock_client.request.call_args | ||
| json_data = call_args[1]["json"] | ||
| assert json_data["top_k"] == 20 | ||
| assert json_data["expand_context"] == 1 | ||
| assert json_data["score_threshold"] == 0.4 | ||
| assert json_data["agent_mode"] is False | ||
| assert "category='work'" in json_data["filter"] | ||
| assert call_args[1]["timeout"] == 15 |
There was a problem hiding this comment.
These unit tests still build filter_dict with unprefixed user-metadata keys (e.g., {"category": "work"}) and assert the outgoing filter string contains category='work'. With strict filter validation, user metadata fields must be prefixed (e.g., metadata.category / m.category), so these tests are now validating an invalid request shape. Update the test inputs/assertions to use the prefixed form while keeping the positional-args coverage.
| 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.
The README example now accesses results.episodic_memory / results.semantic_memory, but Memory.search() returns memmachine_common.api.spec.SearchResult where episodic/semantic data live under results.content.*. As written, this snippet will raise an AttributeError. Update the example to read from results.content (and note that episodic results are nested under short_term_memory/long_term_memory episodes).
…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 <[email protected]> Signed-off-by: Steve Scargall <[email protected]> * docs(langgraph): document filter and episode type support --------- Signed-off-by: Steve Scargall <[email protected]> Co-authored-by: haosenwang1018 <[email protected]> Co-authored-by: Shu Wang <[email protected]>
|
Closed by #1403 |
Purpose of the change
Finish cleaning up the remaining outdated filter examples in the main Python client demo.
Description
Follow-up to #1305 and #1307
This updates the remaining filtered search examples in
examples/memmachine_client_demo.pyfrom bare metadata keys to the current prefixed form required by strict filter validation:{"category": "programming"}→{"metadata.category": "programming"}{"category": "conference"}→{"metadata.category": "conference"}This keeps the official demo aligned with the current Python client and backend filter semantics.
Type of change
How Has This Been Tested?
Manual verification:
filter_dict={"metadata.category": "programming"}filter_dict={"metadata.category": "conference"}categoryexamples were removedChecklist
Maintainer Checklist