Skip to content

feat(search): add an optional source_file filter to mempalace_search (#1815) - #1817

Merged
igorls merged 3 commits into
MemPalace:developfrom
mvalentsev:fix/1815-source-file-filter
Jun 22, 2026
Merged

feat(search): add an optional source_file filter to mempalace_search (#1815)#1817
igorls merged 3 commits into
MemPalace:developfrom
mvalentsev:fix/1815-source-file-filter

Conversation

@mvalentsev

Copy link
Copy Markdown
Contributor

Fixes #1815

What does this PR do?

Adds an optional source_file filter to the mempalace_search MCP tool, next
to the existing wing/room filters, so a query can be scoped to a single
source document.

  • build_where_filter(wing, room, source_file) now builds 0, 1, or 2+ clauses
    (ChromaDB needs $and only for 2+ clauses; a single clause is returned bare).
  • The filter is threaded through every path mempalace_search can take, so it
    never silently no-ops when search degrades: the main vector drawer+closet
    query, the index-mismatch fallback (_query_drawers_with_filter_fallback),
    the vector-disabled BM25/SQLite path (_bm25_only_via_sqlite, in both the SQL
    filter and the Python post-filter), and the lexical union candidate path.
  • Matching is on the exact, full stored value (no glob, no basename matching).
    Mined drawers store the full path while results display only the basename, so
    each result now also carries source_path (the full stored value); a caller
    reads source_path from a result and passes it back as the source_file
    filter.
  • The mempalace_search schema gains a source_file property (required:
    tools/call rejects arguments not declared in the schema). The response
    filters block echoes the applied source_file.
  • A source_file is a path, not a name, so it skips sanitize_name (which
    rejects / and ..) and uses a dedicated check: it is matched verbatim as a
    metadata / parameterized-SQL value and never touches the filesystem, so there
    is no traversal risk, but null bytes, lone surrogates, and overlong values
    are still rejected for parity with sanitize_name.

Out of scope (no source_file parameter, so nothing to bypass): the CLI
search command and the layers.* retrieval stack.

How to test

uv run pytest tests/test_searcher.py tests/test_mcp_server.py \
  tests/test_hybrid_search.py tests/test_hybrid_candidate_union.py -q

End to end through the MCP tool:

  1. Add two drawers with different source_file values (for example /a/x.md
    and /a/y.md).
  2. Call mempalace_search with source_file="/a/x.md": only that source comes
    back, filters.source_file echoes it, and each hit's source_path is the
    full path.
  3. The same query with no source_file returns both sources.

Checklist

  • Tests pass (python -m pytest tests/ -v)
  • No hardcoded paths
  • Linter passes (ruff check .)

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces a new source_file filter to the semantic search functionality, allowing users to scope search results to a specific source file. The changes include adding input validation for the new filter, updating the ChromaDB and SQLite BM25 search backends to apply the filter, exposing the full source_path in results, and adding comprehensive tests. The feedback highlights a potential AttributeError in the validation function _sanitize_optional_source_file if a non-string value is passed, suggesting an explicit type check to handle this gracefully.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread mempalace/mcp_server.py Outdated
@mvalentsev

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces an optional source_file filter to the semantic search functionality (mempalace_search tool and search_memories API). It adds input validation for the source_file parameter (allowing path characters but guarding against null bytes, lone surrogates, and excessive length), updates the ChromaDB and SQLite BM25 query builders to support filtering by the full stored path, exposes the full source_path alongside the basename source_file in search results, and includes comprehensive unit and integration tests. There are no review comments, so I have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@mvalentsev
mvalentsev marked this pull request as ready for review June 17, 2026 21:45
mvalentsev and others added 3 commits June 21, 2026 18:20
…emPalace#1815)

Expose source_file alongside wing/room on mempalace_search. build_where_filter
generalizes to 0/1/2+ clauses and the filter threads through the main vector
path, the index-mismatch fallback, the vector-disabled BM25/SQLite path, and
the union lexical path so it never silently no-ops. Matching is on the exact
full stored value; results now expose source_path (the full path) for round
tripping, since the displayed source_file is a basename. The MCP schema gains
the source_file property and a path-tolerant sanitizer rejects null bytes,
lone surrogates, and overlong values.

Fixes MemPalace#1815

Co-Authored-By: rendigua2025-gif <253093224+rendigua2025-gif@users.noreply.github.com>
…#1815)

A JSON number or boolean passed for source_file is not coerced by the
string schema type, so it reached _sanitize_optional_source_file and
raised AttributeError from .strip() rather than a clean validation error.
Add an isinstance guard that raises ValueError, which tool_search returns
as a structured error. Regression test added.
@mvalentsev
mvalentsev force-pushed the fix/1815-source-file-filter branch from 005c401 to c203aac Compare June 21, 2026 13:29
@igorls
igorls merged commit 7392ab8 into MemPalace:develop Jun 22, 2026
8 checks passed
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.

Expose source_file filtering in mempalace_search

2 participants