Repository navigation
[bug] POST /api/assistants/search ignores the metadata filter #7634
Description
Problem summary
POST /api/assistants/search accepts a metadata filter and silently ignores it.
Affected area(s)
Backend API (gateway / endpoints / SSE)
What happened?
AssistantSearchRequest declares metadata: dict[str, Any] | None, and the LangGraph SDK documents that field as an exact-match filter for each key/value pair. The handler filters only on graph_id and name, so {"metadata": {"created_by": "user"}} returns every registered assistant, including ones whose metadata does not match — and a filter that matches nothing still returns everything.
Expected behavior
Only assistants whose metadata matches every requested key/value pair are returned (an empty list when nothing matches). At minimum, a value the endpoint does not implement should be rejected rather than silently ignored.
Steps to reproduce
Add this to backend/tests/test_assistants_compat.py, reusing its _make_app style:
def _assistant_with_meta(name: str, created_by: str) -> assistants_compat.AssistantResponse:
return assistants_compat.AssistantResponse(
assistant_id=name,
graph_id="lead_agent",
name=name,
metadata={"created_by": created_by},
)
def test_search_filters_by_metadata(monkeypatch: pytest.MonkeyPatch) -> None:
app = FastAPI()
app.include_router(assistants_compat.router)
monkeypatch.setattr(
assistants_compat,
"_list_assistants",
lambda: [
_assistant_with_meta("lead_agent", "system"),
_assistant_with_meta("researcher", "user"),
_assistant_with_meta("writer", "user"),
],
)
with TestClient(app) as client:
response = client.post(
"/api/assistants/search", json={"metadata": {"created_by": "user"}}
)
assert response.status_code == 200
assert [a["assistant_id"] for a in response.json()] == ["researcher", "writer"]Relevant logs
$ pytest -q backend/tests/test_assistants_compat.py -k metadata
status: 200 ids: ['lead_agent', 'researcher', 'writer'] # asked metadata={"created_by": "user"}
status: 200 ids: ['lead_agent', 'researcher', 'writer'] # asked metadata={"created_by": "nobody"}
2 failed
How are you running DeerFlow?
Other (in-process FastAPI TestClient against the router, same setup as the repo's contract tests — not the full make dev stack)
Operating system
Windows
Platform details
Windows 11, Git Bash; Python 3.12
Notes
Searched existing issues for "assistants" / "metadata" first; nothing matches (#1644 is about assistant_id → agent_name mapping).
Activity
Jun516 commentedon Oct 28, 2026
I can take this as a narrowly scoped compatibility bug fix. The
AssistantSearchRequest.metadatafield is accepted butsearch_assistants()currently applies onlygraph_idandname, so the filter is ignored.Proposed fix: apply an exact key/value match for every requested metadata entry before offset/limit slicing; an absent key must not match
null, and an empty metadata object should preserve unfiltered behavior. Keep existing graph/name filtering and the response shape unchanged. Add parameterized FastAPITestClientregressions covering matching/nonmatching values, absent keys, multiple/nested keys,null, and pagination after filtering.I reproduced this using the two current upstream source/test files in an isolated local FastAPI test harness: 6 of the new cases failed before the change, and 16 total focused cases passed with the proposed fix (Python 3.13; no live services). This is not a full checkout or full backend-suite pass; Git clone is blocked by DNS in this environment. I'll keep the PR Draft with the AI assistance disclosure and human-review checkbox unchecked until the required review/validation conditions are met. I checked recent open/closed PR search for this exact endpoint and did not find a competing fix.
BlueX888 commentedon Oct 28, 2026
Thanks @Jun516 — I independently ran the reproduction from this report against #7635's head (
f33ce09) in a fresh worktree, and it now behaves as described here:{"metadata": {"created_by": "user"}}→["researcher", "writer"]{"metadata": {"created_by": "nobody"}}→[]The "an absent key must not match
null" rule you proposed matches what I saw, and it is stricter than themetadata.get(key) == valueshape I had sketched locally, so I'm not carrying a competing patch. Nothing else needed from my side.