fix(server): fetch filtered dashboard memories beyond default page (#5753)
This commit is contained in:
@@ -27,6 +27,8 @@ import { useApiQuery } from "@/hooks/use-api-query";
|
|||||||
import { Memory } from "@/types/api";
|
import { Memory } from "@/types/api";
|
||||||
|
|
||||||
const PAGE_SIZE = 20;
|
const PAGE_SIZE = 20;
|
||||||
|
// Keep in sync with ALL_MEMORIES_LIMIT in server/main.py.
|
||||||
|
const MEMORY_FETCH_LIMIT = 1000;
|
||||||
|
|
||||||
export default function MemoriesPage() {
|
export default function MemoriesPage() {
|
||||||
const [userId, setUserId] = useState("");
|
const [userId, setUserId] = useState("");
|
||||||
@@ -41,7 +43,9 @@ export default function MemoriesPage() {
|
|||||||
refetch,
|
refetch,
|
||||||
} = useApiQuery<Memory[]>(
|
} = useApiQuery<Memory[]>(
|
||||||
async () => {
|
async () => {
|
||||||
const params = userId.trim() ? { user_id: userId.trim() } : undefined;
|
const params = userId.trim()
|
||||||
|
? { user_id: userId.trim(), top_k: MEMORY_FETCH_LIMIT }
|
||||||
|
: { top_k: MEMORY_FETCH_LIMIT };
|
||||||
const res = await api.get(MEMORY_ENDPOINTS.BASE, { params });
|
const res = await api.get(MEMORY_ENDPOINTS.BASE, { params });
|
||||||
const raw = res.data?.results ?? res.data ?? [];
|
const raw = res.data?.results ?? res.data ?? [];
|
||||||
return Array.isArray(raw) ? raw : [];
|
return Array.isArray(raw) ? raw : [];
|
||||||
@@ -96,7 +100,7 @@ export default function MemoriesPage() {
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h1 className="text-xl font-semibold font-fustat">Memories</h1>
|
<h1 className="text-xl font-semibold font-fustat">Memories</h1>
|
||||||
|
|
||||||
{memories.length >= 1000 && (
|
{memories.length >= MEMORY_FETCH_LIMIT && (
|
||||||
<UpgradeBanner
|
<UpgradeBanner
|
||||||
id="memories-1k"
|
id="memories-1k"
|
||||||
message="1,000+ memories stored. Categories can help organize them."
|
message="1,000+ memories stored. Categories can help organize them."
|
||||||
|
|||||||
+7
-3
@@ -16,7 +16,7 @@ from errors import (
|
|||||||
upstream_error,
|
upstream_error,
|
||||||
upstream_error_handler,
|
upstream_error_handler,
|
||||||
)
|
)
|
||||||
from fastapi import Depends, FastAPI, HTTPException, Request
|
from fastapi import Depends, FastAPI, HTTPException, Query, Request
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.responses import JSONResponse, RedirectResponse
|
from fastapi.responses import JSONResponse, RedirectResponse
|
||||||
from mem0.exceptions import ValidationError as Mem0ValidationError
|
from mem0.exceptions import ValidationError as Mem0ValidationError
|
||||||
@@ -409,6 +409,7 @@ def get_all_memories(
|
|||||||
user_id: Optional[str] = None,
|
user_id: Optional[str] = None,
|
||||||
run_id: Optional[str] = None,
|
run_id: Optional[str] = None,
|
||||||
agent_id: Optional[str] = None,
|
agent_id: Optional[str] = None,
|
||||||
|
top_k: Optional[int] = Query(None, ge=0, le=ALL_MEMORIES_LIMIT),
|
||||||
_auth=Depends(verify_auth),
|
_auth=Depends(verify_auth),
|
||||||
):
|
):
|
||||||
"""Retrieve stored memories. Lists all memories when no identifier is provided (admin only)."""
|
"""Retrieve stored memories. Lists all memories when no identifier is provided (admin only)."""
|
||||||
@@ -417,11 +418,14 @@ def get_all_memories(
|
|||||||
auth_type = getattr(request.state, "auth_type", "none")
|
auth_type = getattr(request.state, "auth_type", "none")
|
||||||
if _auth is not None and _auth.role != "admin" and auth_type not in {"admin_api_key", "disabled"}:
|
if _auth is not None and _auth.role != "admin" and auth_type not in {"admin_api_key", "disabled"}:
|
||||||
raise HTTPException(status_code=403, detail="Admin role required to list all memories.")
|
raise HTTPException(status_code=403, detail="Admin role required to list all memories.")
|
||||||
return _list_all_memories()
|
return _list_all_memories(limit=top_k if top_k is not None else ALL_MEMORIES_LIMIT)
|
||||||
filters = {
|
filters = {
|
||||||
k: v for k, v in {"user_id": user_id, "run_id": run_id, "agent_id": agent_id}.items() if v is not None
|
k: v for k, v in {"user_id": user_id, "run_id": run_id, "agent_id": agent_id}.items() if v is not None
|
||||||
}
|
}
|
||||||
return get_memory_instance().get_all(filters=filters)
|
params = {"filters": filters}
|
||||||
|
if top_k is not None:
|
||||||
|
params["top_k"] = top_k
|
||||||
|
return get_memory_instance().get_all(**params)
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@@ -618,6 +618,31 @@ class TestGetMemories:
|
|||||||
# 3. Verify the core logic: the param was mapped to the filters dict!
|
# 3. Verify the core logic: the param was mapped to the filters dict!
|
||||||
_, kwargs = mock_memory.get_all.call_args
|
_, kwargs = mock_memory.get_all.call_args
|
||||||
assert kwargs["filters"] == {"user_id": "test_routing_user"}
|
assert kwargs["filters"] == {"user_id": "test_routing_user"}
|
||||||
|
assert "top_k" not in kwargs
|
||||||
|
|
||||||
|
def test_get_memories_entity_filters_forward_top_k(self, client, mock_memory):
|
||||||
|
response = client.get("/memories?user_id=test_routing_user&top_k=1000")
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
_, kwargs = mock_memory.get_all.call_args
|
||||||
|
assert kwargs["filters"] == {"user_id": "test_routing_user"}
|
||||||
|
assert kwargs["top_k"] == 1000
|
||||||
|
|
||||||
|
def test_get_memories_admin_top_k_zero_not_defaulted(self, client, mock_memory):
|
||||||
|
mock_memory.vector_store.list.return_value = []
|
||||||
|
|
||||||
|
response = client.get("/memories?top_k=0")
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
_, kwargs = mock_memory.vector_store.list.call_args
|
||||||
|
assert kwargs["top_k"] == 0
|
||||||
|
|
||||||
|
def test_get_memories_rejects_top_k_above_limit(self, client, mock_memory):
|
||||||
|
response = client.get("/memories?user_id=test_routing_user&top_k=1001")
|
||||||
|
|
||||||
|
assert response.status_code == 422
|
||||||
|
mock_memory.get_all.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user