Skip to content

docs(examples): update remaining filter prefix demo usage - #1347

Closed
haosenwang1018 wants to merge 7 commits into
MemMachine:mainfrom
haosenwang1018:docs/demo-filter-prefix-tail
Closed

haosenwang1018 wants to merge 7 commits into
MemMachine:mainfrom
haosenwang1018:docs/demo-filter-prefix-tail

Conversation

@haosenwang1018

Copy link
Copy Markdown
Contributor

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.py from 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

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (does not change functionality, e.g., code style improvements, linting)
  • Documentation update
  • Project Maintenance (updates to build scripts, CI, etc., that do not affect the main project)
  • Security (improves security without changing functionality)

How Has This Been Tested?

  • Unit Test
  • Integration Test
  • End-to-end Test
  • Test Script (please provide)
  • Manual verification (list step-by-step instructions)

Manual verification:

  1. Verified the programming search example now uses filter_dict={"metadata.category": "programming"}
  2. Verified the conference search example now uses filter_dict={"metadata.category": "conference"}
  3. Verified the old unqualified category examples were removed

Checklist

  • I have signed the commit(s) within this pull request
  • My code follows the style guidelines of this project (See STYLE_GUIDE.md)
  • I have performed a self-review of my own code
  • I have commented my code
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added unit tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules
  • I have checked my code and corrected any misspellings

Maintainer Checklist

  • Confirmed all checks passed
  • Contributor has signed the commit(s)
  • Reviewed the code
  • Run, Tested, and Verified the change(s) work as expected

@sscargal

Copy link
Copy Markdown
Contributor

@haosenwang1018, please sign your commits and resolve the failing tests. Thanks.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 filter string parameter to the Python client’s Memory.search() and Memory.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 SearchResult shape).

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.

Comment on lines +39 to +50
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()

Copilot AI Apr 17, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +135 to +136
for idx, item in enumerate(results.episodic_memory or []):
print(f"[{idx+1}] {item.content} (Role: {item.producer_role})")

Copilot AI Apr 17, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines 459 to 467
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,

Copilot AI Apr 17, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines 361 to 372
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,

Copilot AI Apr 17, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Comment on lines 455 to 464
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

Copilot AI Apr 17, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread packages/client/README.md
Comment on lines +109 to 114
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}")

Copilot AI Apr 17, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
@sscargal sscargal added this to the v0.3.9 milestone May 13, 2026
malatewang added a commit that referenced this pull request May 13, 2026
…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]>
@sscargal

Copy link
Copy Markdown
Contributor

Closed by #1403

@sscargal sscargal closed this May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants