Compare commits

..
Author SHA1 Message Date
Mgeeeek 8e00815589 chore: drop test scaffold and CI workflow to keep PR minimal 2026-05-13 21:46:56 +05:30
Mgeeeek e2fd8dc7a8 chore: drop internal planning docs from public branch 2026-05-13 21:34:05 +05:30
Mgeeeek d19cde4a15 ci(server): add pytest workflow on PRs touching server/ 2026-05-13 21:11:20 +05:30
Mgeeeek 565a76960b docs(rest-api): document admin-only routes and clarify per-user-key scope 2026-05-13 21:10:23 +05:30
Mgeeeek d987243584 test(server): regression coverage for routes untouched by authz fix 2026-05-13 21:09:13 +05:30
Mgeeeek 4ca67801c0 feat(server): admin-gate unfiltered GET /memories branch 2026-05-13 21:06:53 +05:30
Mgeeeek 739402946c feat(server): admin-gate GET /requests 2026-05-13 21:03:38 +05:30
Mgeeeek 240168aefe feat(server): admin-gate GET /entities and DELETE /entities/{type}/{id} 2026-05-13 21:02:45 +05:30
Mgeeeek 07b9ad519d feat(server): admin-gate POST /reset 2026-05-13 21:02:02 +05:30
Mgeeeek 6ba334ef94 feat(server): admin-gate GET/POST /configure 2026-05-13 21:01:30 +05:30
Mgeeeek 8bd7b913c6 feat(server): add _ensure_admin helper and require_admin FastAPI dep 2026-05-13 20:52:41 +05:30
Mgeeeek 367512b525 test(server): document why StaticPool is required in test_engine fixture 2026-05-13 20:51:03 +05:30
Mgeeeek 79e04c56f6 test(server): add pytest scaffold with SQLite + mocked memory backend
Lays the groundwork for Tasks 2-10 of the REST API authorization fix plan.
Adds an in-memory SQLite test engine, a TestClient fixture that
short-circuits Memory.from_config() (pgvector is unavailable in tests),
and reusable fixtures for admin/member users, JWTs, the legacy
ADMIN_API_KEY env, and AUTH_DISABLED mode.
2026-05-13 20:47:17 +05:30
Mgeeeek 7b109d90de docs: add implementation plan for REST API authorization fix
Eleven-task TDD plan tracking the spec at
docs/superpowers/specs/2026-05-13-rest-api-authz-fix-design.md
(commit 8f285c75). Adds _ensure_admin helper, six admin-gated routes,
inside-route guard on GET /memories, server/ pytest scaffold (SQLite +
MagicMock), server-ci.yml workflow, and surgical docs edits. Each task
follows red/green/commit TDD; every gate covers the four supported
deployment modes (admin JWT, admin per-user key, ADMIN_API_KEY env,
AUTH_DISABLED).
2026-05-13 20:47:17 +05:30
Mgeeeek f687951b1a docs: add design spec for REST API authorization fix
Adds the design document for the B+ scope fix in response to the
2026-05-01 security report on per-user API key authorization. Spec
covers the require_admin helper, six admin-gated routes, the
inside-route guard on GET /memories, surgical docs edits, and a
minimal test scaffold for server/. owner_id work and multi-user invite
flow are explicitly deferred to a tracked follow-up issue.
2026-05-13 20:47:17 +05:30
Agam PandeyandGitHub 70bc9e51d5 docs(readme): update LongMemEval benchmark to 94.8 and add Temporal Reasoning (#5131) 2026-05-13 14:31:15 +05:30
Agam PandeyandGitHub 0107fd53b8 feat: add temporal reasoning cookbook and docs (#5061) 2026-05-13 01:59:38 +05:30
Mragank ShekharandGitHub 54a03cc721 chore(plugin): bump mem0 plugin to v0.1.2 (#5094) 2026-05-09 20:56:34 +05:30
19 changed files with 261 additions and 35 deletions
+3 -2
View File
@@ -47,7 +47,7 @@
| Benchmark | Old | New | Tokens | Latency p50 |
| --- | --- | --- | --- | --- |
| **LoCoMo** | 71.4 | **91.6** | 7.0K | 0.88s |
| **LongMemEval** | 67.8 | **93.4** | 6.8K | 1.09s |
| **LongMemEval** | 67.8 | **94.8** | 6.8K | 1.09s |
| **BEAM (1M)** | — | **64.1** | 6.7K | 1.00s |
| **BEAM (10M)** | — | **48.6** | 6.9K | 1.05s |
@@ -58,12 +58,13 @@ All benchmarks run on the same production-representative model stack. Single-pas
- **Agent-generated facts are first-class** -- when an agent confirms an action, that information is now stored with equal weight.
- **Entity linking** -- entities are extracted, embedded, and linked across memories for retrieval boosting.
- **Multi-signal retrieval** -- semantic, BM25 keyword, and entity matching scored in parallel and fused.
- **Temporal Reasoning** -- time-aware retrieval that ranks the right dated instance for queries about current state, past events, and upcoming plans.
See the [migration guide](https://docs.mem0.ai/migration/oss-v2-to-v3) for upgrade instructions. The [evaluation framework](https://github.com/mem0ai/memory-benchmarks) is open-sourced so anyone can reproduce the numbers.
## Research Highlights
- **91.6 on LoCoMo** -- +20 points over the previous algorithm
- **93.4 on LongMemEval** -- +26 points, with +53.6 on assistant memory recall
- **94.8 on LongMemEval** -- +27 points, with +53.6 on assistant memory recall
- **64.1 on BEAM (1M)** -- production-scale memory evaluation at 1M tokens
- [Read the full paper](https://mem0.ai/research)
+2
View File
@@ -5,3 +5,5 @@ openapi: get /v1/event/{event_id}/
---
Retrieve details about a specific event by passing its `event_id`. This endpoint is particularly helpful for tracking the status, payload, and completion details of asynchronous memory operations.
For `POST /v3/memories/add/`, the event confirms that the write pipeline completed. Temporal reasoning enrichment runs asynchronously by default, so the event may be `SUCCEEDED` slightly before temporal ranking signals are available to subsequent `search` calls.
@@ -83,4 +83,3 @@ The request is queued for background processing. The response contains an `event
<Info>
Poll the event status via `GET /v1/event/{event_id}/`. Status will be `SUCCEEDED` or `FAILED` once processing completes.
</Info>
@@ -64,4 +64,3 @@ memories = client.get_all(
<Info>
The response is a paginated envelope with `count`, `next`, `previous`, and `results`. Use `page` and `page_size` query params to step through results.
</Info>
@@ -49,6 +49,7 @@ related_memories = client.search(
{
"id": "ea925981-272f-40dd-b576-be64e4871429",
"memory": "Likes to play cricket and plays cricket on weekends.",
"user_id": "alice",
"metadata": {
"category": "hobbies"
},
+16 -1
View File
@@ -4,6 +4,21 @@ description: "Major product launches, headline features, and milestones for Mem0
mode: "wide"
---
<Update label="2026-05-13" description="Temporal Reasoning for Mem0 Platform v3">
**Temporal Reasoning — Time-Aware Retrieval for Platform v3**
Mem0 Platform v3 can now interpret time-aware memories and queries so assistants retrieve the right information for questions about the past, upcoming plans, and current state.
- **Time-aware search intent** — Queries like `last week`, `upcoming`, `right now`, and `as of March 2025` return contextually appropriate results automatically
- **Enabled by default** — No per-request toggle required for v3 writes or searches
- **Anchored relative queries** — `reference_date` anchors relative search phrases for tests, backfills, and reproducible demos
- **Normal response shape** — Temporal reasoning affects ranking while preserving existing client response patterns
See [Temporal Reasoning](/platform/features/temporal-reasoning) for usage details.
</Update>
<Update label="2026-05-08" description="Memory Decay">
**Memory Decay — Recently-Used Memories Surface Higher, Automatically**
@@ -112,4 +127,4 @@ Major expansion of the provider ecosystem:
First skill launch — a dedicated Mem0 skill providing platform API reference, quickstart patterns, and integration examples directly inside agent sessions. Available on [skills.sh](https://skills.sh) for any compatible AI coding agent.
</Update>
</Update>
+11 -1
View File
@@ -4,6 +4,17 @@ description: "Release notes for the Mem0 hosted platform — backend, dashboard,
mode: "wide"
---
<Update label="2026-05-13" description="">
**New Features:**
- **Memory:** Added Temporal Reasoning for Platform v3 to improve ranking for time-aware queries such as `last week`, `upcoming`, `right now`, and `as of ...`
- **Search:** Added `reference_date` support to anchor relative temporal queries for tests, backfills, and reproducible demos
**Improvements:**
- **API:** Temporal reasoning preserves the normal client response shape for search and get-all results
</Update>
<Update label="2026-05-04" description="">
**New Features:**
@@ -301,4 +312,3 @@ mode: "wide"
- **Core:** Fixed unicode error in user_id, agent_id, run_id and app_id
</Update>
+1 -1
View File
@@ -55,7 +55,7 @@ mode: "wide"
**Breaking Changes:**
- **`add()` returns ADD-only events** — No more `"UPDATE"` or `"DELETE"` events. Memories accumulate; nothing is overwritten ([#4805](https://github.com/mem0ai/mem0/pull/4805))
- **`search()` default `threshold` is now `0.1`** — Pass `threshold=0.0` for previous behavior ([#4805](https://github.com/mem0ai/mem0/pull/4805))
- **`search()` `score` is now a combined multi-signal score** — The top-level `score` fuses semantic similarity, BM25 keyword match, and entity boost into one value. Absolute numbers shift versus the old raw cosine score; retune any hard thresholds against representative queries. Per-signal scores are not exposed on the response ([#4805](https://github.com/mem0ai/mem0/pull/4805), [#4836](https://github.com/mem0ai/mem0/pull/4836))
- **`search()` `score` is now a combined multi-signal score** — The top-level `score` fuses semantic similarity, BM25 keyword match, entity signals, and temporal boosts into one value. Absolute numbers shift versus the old raw cosine score; retune any hard thresholds against representative queries ([#4805](https://github.com/mem0ai/mem0/pull/4805), [#4836](https://github.com/mem0ai/mem0/pull/4836))
- **`search()` default `rerank` is now `False`** — Pass `rerank=True` for previous behavior ([#4805](https://github.com/mem0ai/mem0/pull/4805))
- **`top_k` default changed 100 → 20** in `Memory.get_all()` and `Memory.search()` (sync + async). Pass `top_k=100` explicitly to restore the old behavior ([#4843](https://github.com/mem0ai/mem0/pull/4843))
- **Entity ID validation:** `user_id` / `agent_id` / `run_id` are trimmed; empty-string and whitespace-only values now raise `ValueError` ([#4843](https://github.com/mem0ai/mem0/pull/4843))
@@ -156,6 +156,10 @@ const memories = memory.search("food preferences", {
Expect an array of memory documents. Platform responses include vectors, metadata, and timestamps; OSS returns your stored schema.
</Info>
<Note>
On Mem0 Platform v3, time-aware queries use Temporal Reasoning internally while preserving the normal search response shape. See <Link href="/platform/features/temporal-reasoning">Temporal Reasoning</Link>.
</Note>
## Filter patterns
Filters help narrow down search results. Common use cases:
@@ -251,4 +255,4 @@ For the full list of filter logic, comparison operators, and optional search par
icon="rocket"
href="/cookbooks/operations/support-inbox"
/>
</CardGroup>
</CardGroup>
+3 -2
View File
@@ -72,7 +72,8 @@
"platform/features/entity-scoped-memory",
"platform/features/async-client",
"platform/features/multimodal-support",
"platform/features/custom-categories"
"platform/features/custom-categories",
"platform/features/temporal-reasoning"
]
},
{
@@ -1144,4 +1145,4 @@
"destination": "/introduction"
}
]
}
}
+1
View File
@@ -185,6 +185,7 @@ If the user is on a pre-current major (Python < 2, TS < 3, or Platform `output_f
### Features - Advanced Retrieval
- [Advanced Retrieval](https://docs.mem0.ai/platform/features/advanced-retrieval) [Platform]: Use when the user needs keyword search, reranking, or hybrid retrieval.
- [Criteria-Based Retrieval](https://docs.mem0.ai/platform/features/criteria-retrieval) [Platform]: Use when targeting memories by custom criteria, not just semantic similarity.
- [Temporal Reasoning](https://docs.mem0.ai/platform/features/temporal-reasoning) [Platform]: Use when time-aware searches like last week, upcoming, or right now need better result ordering.
- [Contextual Add](https://docs.mem0.ai/platform/features/contextual-add) [Platform]: Use when `add()` should consider the surrounding conversation, not just the latest turn.
- [Custom Instructions](https://docs.mem0.ai/platform/features/custom-instructions) [Platform]: Use when tailoring what Mem0 extracts and stores on Platform.
- [Memory Decay](https://docs.mem0.ai/platform/features/memory-decay) [Platform]: Use when search results should boost recently-reinforced memories and dampen stale ones — opt-in per project, search-time only, never filters candidates out.
+3 -2
View File
@@ -42,7 +42,7 @@ Previously, when an agent said something like "I've booked your flight for March
### Retrieval is hybrid now
Search now uses hybrid retrieval, which improves ranking quality — especially for queries involving exact keywords, proper nouns, or entities that appear across multiple memories. The response shape is unchanged:
Search now uses hybrid retrieval, which improves ranking quality — especially for queries involving exact keywords, proper nouns, entities that appear across multiple memories, and time-aware queries (via Temporal Reasoning). The response shape is unchanged:
```json
{
@@ -58,7 +58,7 @@ Search now uses hybrid retrieval, which improves ranking quality — especially
}
```
The top-level `score` remains a `[0, 1]` value. Relative ranking between results stays comparable to v2, but absolute numbers shift since the scoring method changed — retune any hard thresholds in your app against representative queries.
The top-level `score` remains a `[0, 1]` value. Relative ranking between results stays comparable to v2, but absolute numbers shift since the scoring method changed — retune any hard thresholds in your app against representative queries. Temporal signals are applied internally during ranking and are not returned as extra client-facing fields.
## API Changes
@@ -289,6 +289,7 @@ If your application previously read graph relations from the API response (`rela
- **V1 and V2 endpoints continue to work.** There is no requirement to migrate to V3 endpoints immediately.
- **Existing memories are preserved.** The new algorithm does not modify or re-process previously stored memories.
- **Search response shape is unchanged.** The top-level `score` and `results[]` array are the same; existing code that reads `score` continues to work. What changed is the scoring method behind the number (multi-signal fusion instead of pure cosine), so the absolute values shift even when ranking stays comparable.
- **Search remains backward-compatible at the top level.** Existing code that reads `results[]` and `score` continues to work. Temporal signals are applied internally during retrieval and do not change the client response shape.
- **List response shape changed.** `get_all` now returns a paginated envelope (`{count, next, previous, results}`) instead of a bare `{results: [...]}`. Update code that reads `response["results"]` to continue working, or switch to the client SDKs which handle both shapes.
## Performance Improvements
+9 -9
View File
@@ -131,7 +131,7 @@ Auth is on by default. Protected endpoints require either a JWT (from the dashbo
| Mode | How to send it | When to use it |
|---|---|---|
| Bearer JWT | `Authorization: Bearer <access_token>` | Dashboard sessions; tokens come from `POST /auth/login` and refresh via `POST /auth/refresh` |
| Per-user API key | `X-API-Key: m0sk_...` | Programmatic access scoped to a single dashboard user |
| Per-user API key | `X-API-Key: m0sk_...` | Programmatic access tied to the single admin user today. Per-user scoping arrives with the upcoming multi-user invite flow |
| Legacy `ADMIN_API_KEY` | `X-API-Key: <env value>` | Back-compat for deployments that set the `ADMIN_API_KEY` env var |
| `AUTH_DISABLED=true` | — | Local development only; bypasses auth entirely |
@@ -182,7 +182,7 @@ curl -X POST http://localhost:8888/memories \
}'
```
Per-user keys inherit the creating user's scope. List or revoke them via `GET /api-keys` and `DELETE /api-keys/{id}`.
List or revoke per-user keys via `GET /api-keys` and `DELETE /api-keys/{id}`. The dashboard user issued the key remains its owner; admin-only routes such as `/configure`, `/reset`, `/entities`, and `/requests` require the issuing user to have the admin role.
### Legacy `ADMIN_API_KEY`
@@ -246,8 +246,8 @@ The OSS REST server exposes the following endpoints. None use the `/v1/` prefix.
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/configure` | Set memory configuration. Rejects unbundled providers with a 400 |
| `GET` | `/configure` | Get the current memory configuration |
| `POST` | `/configure` | **Admin only.** Set memory configuration. Rejects unbundled providers with a 400 |
| `GET` | `/configure` | **Admin only.** Get the current memory configuration |
| `GET` | `/configure/providers` | List the LLM and embedder providers bundled in the container |
| `POST` | `/memories` | Create memories |
| `GET` | `/memories` | Get all memories (filter by `user_id`, `agent_id`, or `run_id`) |
@@ -257,14 +257,14 @@ The OSS REST server exposes the following endpoints. None use the `/v1/` prefix.
| `DELETE` | `/memories` | Delete all memories for an identifier |
| `GET` | `/memories/{memory_id}/history` | Get memory history |
| `POST` | `/search` | Search memories |
| `POST` | `/reset` | Reset all memories |
| `POST` | `/reset` | **Admin only.** Reset all memories |
### Authentication
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/auth/setup-status` | Returns `{needsSetup: bool}`. Open, no auth required |
| `POST` | `/auth/register` | Register the first admin. Registration closes after the first admin is created; additional accounts are provisioned by the existing admin. |
| `POST` | `/auth/register` | Register the first admin. Registration closes after the first admin is created; additional accounts will be provisioned by the existing admin (multi-user invite flow coming soon). |
| `POST` | `/auth/login` | Exchange email and password for access and refresh JWTs |
| `POST` | `/auth/refresh` | Exchange a refresh token for a new access token |
| `GET` | `/auth/me` | Get the current authenticated user (JWT required) |
@@ -285,14 +285,14 @@ All `/api-keys` endpoints require a JWT.
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/requests?limit=N` | Recent API call log (JWT or admin key) |
| `GET` | `/requests?limit=N` | **Admin only.** Recent API call log |
### Entities
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/entities` | Distinct `user_id` / `agent_id` / `run_id` values with memory counts |
| `DELETE` | `/entities/{entity_type}/{entity_id}` | Cascade-delete all memories for an entity; `entity_type` is `user`, `agent`, or `run` |
| `GET` | `/entities` | **Admin only.** Distinct `user_id` / `agent_id` / `run_id` values with memory counts |
| `DELETE` | `/entities/{entity_type}/{entity_id}` | **Admin only.** Cascade-delete all memories for an entity; `entity_type` is `user`, `agent`, or `run` |
The `/auth/*`, `/api-keys`, `/requests`, and `/entities` routes are new to the self-hosted server and primarily back the dashboard, but you can call them directly from your own tooling.
+17 -2
View File
@@ -419,7 +419,7 @@
},
"results": {
"type": "array",
"description": "Array of results produced by the event."
"description": "Array of results produced by the event. For add events, this confirms the write completed; temporal reasoning enrichment runs asynchronously by default."
},
"created_at": {
"type": "string",
@@ -2071,7 +2071,7 @@
"memories"
],
"summary": "Search memories (V3)",
"description": "Relevance-ranked search across stored memories. V3 uses hybrid retrieval — the returned `score` is a combined `[0, 1]` value; per-signal component scores are not exposed on the response. Entity IDs **must** be passed inside the `filters` object — top-level `user_id` / `agent_id` / `run_id` are rejected with 400. At least one entity ID is required.",
"description": "Relevance-ranked search across stored memories. V3 uses hybrid retrieval and can also apply temporal reasoning for time-aware queries. Entity IDs **must** be passed inside the `filters` object — top-level `user_id` / `agent_id` / `run_id` are rejected with 400. At least one entity ID is required.",
"operationId": "memories_search_v3",
"requestBody": {
"required": true,
@@ -2112,6 +2112,21 @@
"type": "boolean",
"default": false,
"description": "Apply the managed reranker for better ordering (adds latency)."
},
"reference_date": {
"oneOf": [
{
"type": "integer"
},
{
"type": "number"
},
{
"type": "string"
}
],
"nullable": true,
"description": "Optional query anchor time for relative temporal interpretation. Accepts Unix epoch, YYYY-MM-DD, or ISO datetime."
}
}
},
@@ -0,0 +1,145 @@
---
title: Temporal Reasoning
description: "Time-aware memory retrieval for Mem0 Platform v3 so queries like 'last week', 'upcoming', and 'right now' return the right memories."
icon: "clock"
badge: "v3"
---
Some memories matter because of **when** they happened, not just because they sound similar. Temporal Reasoning lets Mem0 Platform v3 understand time-aware queries and return the most contextually appropriate results.
<Info>
**Use Temporal Reasoning when…**
- Users ask questions like "what happened last week?" or "what do I have coming up?"
- Your app stores both past events and future plans for the same person
- You want time-aware retrieval without building your own date-parsing layer
</Info>
<Warning>
Temporal Reasoning is a **Mem0 Platform v3** feature. It is not available on OSS memory stores or older Platform endpoints.
</Warning>
## Configure access
Confirm your `MEM0_API_KEY` is set and that you are using the v3 Platform client:
```python
from mem0 import MemoryClient
client = MemoryClient(api_key="your-api-key")
```
## How it works
When a memory describes an event, a future plan, or an ongoing state, Temporal Reasoning recognizes the time context so the right results surface at search time.
A query like `what did I do last week?` should return a completed past event — not an upcoming appointment and not a stable fact that hasn't changed. Temporal Reasoning handles that distinction automatically.
### Memory types Temporal Reasoning handles
| Type | What it represents | Example |
| --- | --- | --- |
| Dated occurrence | Something that happened at a known time | "I finished the Q1 review on March 10, 2025." |
| Future plan | A future commitment or scheduled item | "I have a dentist appointment on March 18, 2025." |
| Ongoing state | A fact that remains true over time | "I am the product lead at Acme Corp." |
| Relationship | A durable connection between people or entities | "Priya manages Jordan." |
| Preference | A stable preference or habit | "I prefer morning meetings." |
Results come back in the normal search response shape — Temporal Reasoning affects ranking, not the response format.
## Configure it
Temporal Reasoning is enabled by default for all v3 searches and writes. There is no per-request toggle.
Two parameters give you precise control when you need it:
- `timestamp` on `add()` — anchors an imported memory to the time it actually happened, rather than the time it was added to Mem0
- `reference_date` on `search()` — resolves relative phrases like `last week` against a fixed point in time
<CodeGroup>
```python Python
from datetime import datetime, timezone
from mem0 import MemoryClient
client = MemoryClient(api_key="your-api-key")
# Import a historical memory anchored to when it happened
client.add(
[{"role": "user", "content": "I finished the Q1 review on March 10, 2025."}],
user_id="jordan",
timestamp=int(datetime(2025, 3, 10, tzinfo=timezone.utc).timestamp()),
)
# Search with a relative query anchored to a known date
results = client.search(
"what did I do last week?",
filters={"user_id": "jordan"},
reference_date="2025-03-21T00:00:00Z",
)
```
```javascript JavaScript
import { MemoryClient } from "mem0ai";
const client = new MemoryClient({ apiKey: "your-api-key" });
// Import a historical memory anchored to when it happened
await client.add(
[{ role: "user", content: "I finished the Q1 review on March 10, 2025." }],
{
userId: "jordan",
timestamp: Math.floor(new Date("2025-03-10T00:00:00Z").getTime() / 1000),
}
);
// Search with a relative query anchored to a known date
const results = await client.search("what did I do last week?", {
filters: { user_id: "jordan" },
referenceDate: "2025-03-21T00:00:00Z",
});
```
</CodeGroup>
<Tip>
`reference_date` is especially useful in automated tests and demos because it makes relative phrases like `last week` resolve consistently every time.
</Tip>
## Supported query patterns
<AccordionGroup>
<Accordion title="Historical questions">
Examples: `last week`, `last month`, `in March 2025`, `on 2025-03-10`
</Accordion>
<Accordion title="Upcoming questions">
Examples: `upcoming`, `next week`, `tomorrow`, `what do I have coming up?`
</Accordion>
<Accordion title="Current-state questions">
Examples: `right now`, `currently`, `where do I work now?`
</Accordion>
<Accordion title="As-of questions">
Examples: `as of March 2025`, `where was I living as of 2024?`
</Accordion>
<Accordion title="Duration questions">
Examples: `how long have I lived here?`, `since when have I worked there?`
</Accordion>
</AccordionGroup>
## Verify the feature is working
- Run a temporal search with a time-aware query (e.g., "what did I do last week?") and confirm the memory that fits the time window ranks first.
- Use `reference_date` in test queries so relative phrases resolve consistently across runs.
- For backfilled data, pass `timestamp` on `add()` to confirm the memory reflects the right point in time.
## Best practices
- Use explicit dates in source conversations when events or plans matter temporally.
- Pass `timestamp` during historical imports so the ingestion time does not become the only time anchor.
- Scope searches with `filters` so time-aware ranking operates inside the right user boundary.
- Use `reference_date` in automated tests and reproducible demos.
<CardGroup cols={1}>
<Card title="Memory Timestamps" icon="calendar" href="/platform/features/timestamp">
Anchor imported memories to when they actually happened.
</Card>
</CardGroup>
<Snippet file="get-help.mdx" />
+24
View File
@@ -184,3 +184,27 @@ async def require_auth(
return default_user
raise HTTPException(status_code=401, detail="Authentication required.")
return user
def _ensure_admin(request: Request, user: User | None) -> None:
"""Single source of truth for admin gating.
Allows: legacy ADMIN_API_KEY env (X-API-Key header), AUTH_DISABLED=true,
or a registered User with role == 'admin'. Raises 403 otherwise.
Pure function — reusable from FastAPI deps and inline route guards.
"""
auth_type = getattr(request.state, "auth_type", "none")
if auth_type in {"admin_api_key", "disabled"}:
return
if user is not None and user.role == "admin":
return
raise HTTPException(status_code=403, detail="Admin role required.")
async def require_admin(
request: Request,
user: User | None = Depends(verify_auth),
) -> None:
"""FastAPI dependency. Admin-only gate; returns nothing."""
_ensure_admin(request, user)
+14 -6
View File
@@ -13,7 +13,7 @@ from slowapi import _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded
from sqlalchemy import func, select
from auth import ADMIN_API_KEY, AUTH_DISABLED, JWT_SECRET, verify_auth
from auth import ADMIN_API_KEY, AUTH_DISABLED, JWT_SECRET, _ensure_admin, require_admin, verify_auth
from errors import (
UpstreamError,
install_request_id_logging,
@@ -301,7 +301,7 @@ async def log_requests(request: Request, call_next):
@app.get("/configure", summary="Get current Mem0 configuration")
def get_config(_auth=Depends(verify_auth)):
def get_config(_admin=Depends(require_admin)):
return _redact_config(get_current_config())
@@ -311,7 +311,7 @@ def list_bundled_providers(_auth=Depends(verify_auth)):
@app.post("/configure", summary="Configure Mem0")
def set_config(config: Dict[str, Any], _auth=Depends(verify_auth)):
def set_config(config: Dict[str, Any], _admin=Depends(require_admin)):
"""Set memory configuration."""
_validate_bundled_providers(config)
update_config(config)
@@ -386,19 +386,27 @@ def _list_all_memories(limit: int = ALL_MEMORIES_LIMIT) -> Dict[str, Any]:
@app.get("/memories", summary="Get memories")
def get_all_memories(
request: Request,
user_id: Optional[str] = None,
run_id: Optional[str] = None,
agent_id: Optional[str] = None,
_auth=Depends(verify_auth),
user: User | None = Depends(verify_auth),
):
"""Retrieve stored memories. Lists all memories when no identifier is provided."""
"""Retrieve stored memories. Lists all memories when no identifier is provided.
Note: the unfiltered listing branch is admin-only — it would otherwise leak
every payload in the vector store across tenants once multi-user lands.
Filtered queries remain available to any authenticated caller."""
try:
if not any([user_id, run_id, agent_id]):
_ensure_admin(request, user)
return _list_all_memories()
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
}
return get_memory_instance().get_all(filters=filters)
except HTTPException:
raise
except Exception:
raise upstream_error()
@@ -473,7 +481,7 @@ def delete_all_memories(
@app.post("/reset", summary="Reset all memories")
def reset_memory(_auth=Depends(verify_auth)):
def reset_memory(_admin=Depends(require_admin)):
"""Completely reset stored memories."""
try:
get_memory_instance().reset()
+3 -3
View File
@@ -5,7 +5,7 @@ from typing import Any, Literal, Optional
from fastapi import APIRouter, Depends
from pydantic import BaseModel
from auth import verify_auth
from auth import require_admin
from errors import upstream_error
from schemas import MessageResponse
from server_state import get_memory_instance
@@ -42,7 +42,7 @@ def _parse_timestamp(value: Any) -> Optional[datetime]:
@router.get("", response_model=list[Entity])
def list_entities(_auth=Depends(verify_auth)):
def list_entities(_admin=Depends(require_admin)):
buckets: dict[tuple[EntityType, str], dict[str, Any]] = defaultdict(
lambda: {"total_memories": 0, "created_at": None, "updated_at": None}
)
@@ -69,7 +69,7 @@ def list_entities(_auth=Depends(verify_auth)):
@router.delete("/{entity_type}/{entity_id}", response_model=MessageResponse)
def delete_entity(entity_type: EntityType, entity_id: str, _auth=Depends(verify_auth)):
def delete_entity(entity_type: EntityType, entity_id: str, _admin=Depends(require_admin)):
try:
get_memory_instance().delete_all(**{TYPE_TO_FIELD[entity_type]: entity_id})
except Exception:
+3 -3
View File
@@ -6,9 +6,9 @@ from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.orm import Session
from auth import require_auth
from auth import require_admin
from db import get_db
from models import RequestLog, User
from models import RequestLog
router = APIRouter(prefix="/requests", tags=["requests"])
@@ -30,7 +30,7 @@ API_KEY_AUTH_TYPES = ("api_key", "admin_api_key")
@router.get("", response_model=list[RequestLogItem])
def list_requests(
user: User = Depends(require_auth),
_admin=Depends(require_admin),
db: Session = Depends(get_db),
limit: int = Query(default=50, ge=1, le=200),
):