Feat: add temporal reasoning cookbook and docs
This commit is contained in:
@@ -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. If your deployment uses asynchronous temporal enrichment, the event may be `SUCCEEDED` slightly before temporal search fields become visible in subsequent `search` responses.
|
||||
|
||||
@@ -49,7 +49,12 @@ Provide conversation messages for Mem0 to extract memories from. At least one en
|
||||
| `run_id` | string | No* | Associates the memory with a run. |
|
||||
| `app_id` | string | No* | Associates the memory with an app. |
|
||||
| `metadata` | object | Optional | Custom key/value metadata (e.g., `{"topic": "preferences"}`). |
|
||||
| `timestamp` | integer | Optional | Unix timestamp used to anchor imported or historical memories. |
|
||||
| `observation_date` | string | Optional | Original conversation date (`YYYY-MM-DD`) for temporal grounding. |
|
||||
| `observation_datetime` | string | Optional | Original conversation datetime for more precise temporal grounding. |
|
||||
| `timezone` | string | Optional | IANA timezone used when resolving relative dates. |
|
||||
| `infer` | boolean (default `true`) | Optional | Set to `false` to skip inference and store the provided text as-is. |
|
||||
| `temporal_reasoning` | boolean | Optional | Overrides the deployment default for temporal enrichment on this write. |
|
||||
|
||||
> \* At least one entity ID (`user_id`, `agent_id`, `app_id`, or `run_id`) is required.
|
||||
|
||||
@@ -84,3 +89,6 @@ The request is queued for background processing. The response contains an `event
|
||||
Poll the event status via `GET /v1/event/{event_id}/`. Status will be `SUCCEEDED` or `FAILED` once processing completes.
|
||||
</Info>
|
||||
|
||||
<Note>
|
||||
Some deployments run temporal enrichment in a follow-up async step. In that mode, the add event can complete successfully before temporal search fields are fully available on later searches.
|
||||
</Note>
|
||||
|
||||
@@ -19,6 +19,31 @@ The `filters` object supports complex logical operations (AND, OR, NOT) and comp
|
||||
|
||||
Pass `page` and `page_size` as query parameters to paginate through results.
|
||||
|
||||
## Temporal fields on `get_all`
|
||||
|
||||
`get_all` returns the **stored** memory record. For temporally enriched memories, that can include top-level fields such as:
|
||||
|
||||
- `event_date`
|
||||
- `event_start`
|
||||
- `event_end`
|
||||
- `time_precision`
|
||||
- `timezone`
|
||||
- `polarity`
|
||||
- `temporal_relation`
|
||||
- `state_key`
|
||||
- `plan_status`
|
||||
|
||||
These are stored memory attributes, not search-time interpretation fields.
|
||||
|
||||
`get_all` does **not** add search-only fields such as:
|
||||
|
||||
- `effective_plan_status`
|
||||
- `temporal_surface`
|
||||
- `reference_date_used`
|
||||
- `score_breakdown`
|
||||
|
||||
Those fields belong to `search`, where Mem0 interprets a query in context.
|
||||
|
||||
<CodeGroup>
|
||||
```python Code
|
||||
memories = client.get_all(
|
||||
@@ -46,14 +71,38 @@ memories = client.get_all(
|
||||
{
|
||||
"id": "f4cbdb08-7062-4f3e-8eb2-9f5c80dfe64c",
|
||||
"memory": "Alex is planning a trip to San Francisco from July 1st to July 10th",
|
||||
"user_id": "alex",
|
||||
"metadata": {},
|
||||
"categories": [],
|
||||
"created_at": "2024-07-01T12:00:00Z",
|
||||
"updated_at": "2024-07-01T12:00:00Z"
|
||||
"updated_at": "2024-07-01T12:00:00Z",
|
||||
"event_date": "2024-07-01",
|
||||
"event_start": "2024-07-01",
|
||||
"event_end": "2024-07-10",
|
||||
"time_precision": "interval",
|
||||
"timezone": "UTC",
|
||||
"polarity": "positive",
|
||||
"temporal_relation": "during",
|
||||
"state_key": null,
|
||||
"plan_status": "pending"
|
||||
},
|
||||
{
|
||||
"id": "a2b8c3d4-5e6f-7g8h-9i0j-1k2l3m4n5o6p",
|
||||
"memory": "Alex prefers vegetarian restaurants",
|
||||
"user_id": "alex",
|
||||
"metadata": {},
|
||||
"categories": [],
|
||||
"created_at": "2024-07-05T15:30:00Z",
|
||||
"updated_at": "2024-07-05T15:30:00Z"
|
||||
"updated_at": "2024-07-05T15:30:00Z",
|
||||
"event_date": null,
|
||||
"event_start": null,
|
||||
"event_end": null,
|
||||
"time_precision": null,
|
||||
"timezone": null,
|
||||
"polarity": null,
|
||||
"temporal_relation": null,
|
||||
"state_key": null,
|
||||
"plan_status": null
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -65,3 +114,6 @@ memories = client.get_all(
|
||||
The response is a paginated envelope with `count`, `next`, `previous`, and `results`. Use `page` and `page_size` query params to step through results.
|
||||
</Info>
|
||||
|
||||
<Note>
|
||||
`get_all` is best for exports, audits, and raw record inspection. Use <a href="/api-reference/memory/search-memories">Search Memories</a> when you want time-aware ranking or query interpretation.
|
||||
</Note>
|
||||
|
||||
@@ -18,6 +18,21 @@ The `filters` object supports complex logical operations (AND, OR, NOT) and comp
|
||||
- `icontains`: Case-insensitive containment check
|
||||
- `*`: Wildcard character that matches everything
|
||||
|
||||
## Temporal fields in V3 responses
|
||||
|
||||
When Temporal Reasoning is enabled for a query, search results can include additional **top-level** fields such as:
|
||||
|
||||
- `memory_type`
|
||||
- `event_date`
|
||||
- `event_start`
|
||||
- `event_end`
|
||||
- `plan_status`
|
||||
- `effective_plan_status`
|
||||
- `time_precision`
|
||||
- `temporal_surface`
|
||||
- `reference_date_used`
|
||||
- `score_breakdown.temporal_boost`
|
||||
|
||||
### Search parameter defaults
|
||||
|
||||
| Parameter | V1/V2 | V3 |
|
||||
@@ -49,10 +64,17 @@ 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"
|
||||
},
|
||||
"score": 0.82,
|
||||
"score_breakdown": {
|
||||
"semantic": 0.79,
|
||||
"bm25": 0.0,
|
||||
"entity": 0.0,
|
||||
"temporal_boost": 0.0
|
||||
},
|
||||
"created_at": "2024-07-26T10:29:36.630547-07:00",
|
||||
"updated_at": null,
|
||||
"categories": ["hobbies"]
|
||||
@@ -62,6 +84,54 @@ related_memories = client.search(
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Temporal example
|
||||
|
||||
<CodeGroup>
|
||||
```python Platform API Example
|
||||
results = client.search(
|
||||
query="what did I do last week?",
|
||||
filters={"user_id": "alice"},
|
||||
reference_date="2025-03-21T00:00:00Z",
|
||||
)
|
||||
```
|
||||
|
||||
```json Output
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"id": "9b59b0fd-0b31-4a0b-a3ad-7f26bb4f1f8d",
|
||||
"memory": "Finished the Q1 product review on March 10, 2025.",
|
||||
"user_id": "alice",
|
||||
"score": 0.91,
|
||||
"score_breakdown": {
|
||||
"semantic": 0.84,
|
||||
"bm25": 0.0,
|
||||
"entity": 0.0,
|
||||
"temporal_boost": 0.07
|
||||
},
|
||||
"memory_type": "event",
|
||||
"event_date": "2025-03-10",
|
||||
"event_start": "2025-03-10",
|
||||
"event_end": "2025-03-10",
|
||||
"plan_status": null,
|
||||
"effective_plan_status": null,
|
||||
"time_precision": "day",
|
||||
"temporal_surface": "Mar 10, 2025",
|
||||
"reference_date_used": "2025-03-21",
|
||||
"metadata": {},
|
||||
"categories": [],
|
||||
"created_at": "2025-03-10T00:00:00Z",
|
||||
"updated_at": "2025-03-10T00:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<Note>
|
||||
Temporal fields are most useful on `search`. The paginated `get_all` endpoint remains a broader retrieval surface and does not mirror the full time-aware search result shape.
|
||||
</Note>
|
||||
|
||||
<CodeGroup>
|
||||
```python Wildcard Example
|
||||
# Using wildcard to match all run_ids for a specific user
|
||||
|
||||
@@ -41,7 +41,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 compatible temporal boosts into one value. Absolute numbers shift versus the old raw cosine score; retune any hard thresholds against representative queries. Platform V3 responses may also include `score_breakdown` and temporal result fields for compatible queries, so treat those as optional enrichments rather than required keys ([#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))
|
||||
|
||||
@@ -0,0 +1,390 @@
|
||||
---
|
||||
title: Time-Aware Retrieval
|
||||
description: Use Mem0 Platform v3 Temporal Reasoning to make one memory store answer past, upcoming, and current questions correctly.
|
||||
---
|
||||
|
||||
An assistant often stores all of these in the same user profile:
|
||||
|
||||
- "I finished the Q1 product review on March 10, 2025."
|
||||
- "I have a dentist appointment on April 18, 2025 at 2 PM."
|
||||
- "I am the product lead at Acme Corp."
|
||||
|
||||
Those memories are all relevant to the same person, but they should not rank the same way for every question. A query like `what did I do last week?` should favor the finished review. A query like `what do I have coming up?` should favor the appointment. A query like `where do I work right now?` should favor the ongoing role.
|
||||
|
||||
This cookbook shows how to get that behavior with Mem0 Platform v3 using the normal `add()` and `search()` flow.
|
||||
|
||||
<Info icon="clock">
|
||||
**Time to complete:** ~15 minutes · **Languages:** Python, JavaScript
|
||||
</Info>
|
||||
|
||||
## Setup
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(api_key="your-api-key")
|
||||
USER_ID = "jordan"
|
||||
REFERENCE_DATE = "2025-03-21T00:00:00Z"
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
import MemoryClient from "mem0ai";
|
||||
|
||||
const client = new MemoryClient({ apiKey: process.env.MEM0_API_KEY });
|
||||
const USER_ID = "jordan";
|
||||
const REFERENCE_DATE = "2025-03-21T00:00:00Z";
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<Note>
|
||||
These examples target **Mem0 Platform v3**. Writes are asynchronous, so allow a short delay before validating search behavior.
|
||||
</Note>
|
||||
|
||||
## Seed Memories
|
||||
|
||||
Start with one past event, one future plan, and one ongoing state:
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
client.add(
|
||||
[{"role": "user", "content": "I finished the Q1 product review on March 10, 2025."}],
|
||||
user_id=USER_ID,
|
||||
)
|
||||
|
||||
client.add(
|
||||
[{"role": "user", "content": "I have a dentist appointment on April 18, 2025 at 2 PM."}],
|
||||
user_id=USER_ID,
|
||||
)
|
||||
|
||||
client.add(
|
||||
[{"role": "user", "content": "I am the product lead at Acme Corp."}],
|
||||
user_id=USER_ID,
|
||||
)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
await client.add(
|
||||
[{ role: "user", content: "I finished the Q1 product review on March 10, 2025." }],
|
||||
{ userId: USER_ID }
|
||||
);
|
||||
|
||||
await client.add(
|
||||
[{ role: "user", content: "I have a dentist appointment on April 18, 2025 at 2 PM." }],
|
||||
{ userId: USER_ID }
|
||||
);
|
||||
|
||||
await client.add(
|
||||
[{ role: "user", content: "I am the product lead at Acme Corp." }],
|
||||
{ userId: USER_ID }
|
||||
);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Compare Retrieval Modes
|
||||
|
||||
The easiest way to understand Temporal Reasoning is to compare the same query in two modes:
|
||||
|
||||
- **Standard retrieval** with `temporal_reasoning=False`
|
||||
- **Temporal retrieval** with the default time-aware mode enabled
|
||||
|
||||
### Mode 1: Standard Retrieval
|
||||
|
||||
This is useful for broad semantic lookups, but it does not try to interpret `last week` against a timeline.
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
results = client.search(
|
||||
"what did I do last week?",
|
||||
filters={"user_id": USER_ID},
|
||||
reference_date=REFERENCE_DATE,
|
||||
temporal_reasoning=False,
|
||||
)
|
||||
|
||||
for item in results["results"]:
|
||||
print(item["memory"])
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
const results = await client.search("what did I do last week?", {
|
||||
filters: { user_id: USER_ID },
|
||||
referenceDate: REFERENCE_DATE,
|
||||
temporalReasoning: false,
|
||||
});
|
||||
|
||||
results.results.forEach((item) => console.log(item.memory));
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
**Illustrative output:**
|
||||
```text
|
||||
I finished the Q1 product review on March 10, 2025.
|
||||
I have a dentist appointment on April 18, 2025 at 2 PM.
|
||||
I am the product lead at Acme Corp.
|
||||
```
|
||||
|
||||
That result set is semantically reasonable, but it is not yet behaving like a time-aware assistant.
|
||||
|
||||
### Mode 2: Temporal Retrieval
|
||||
|
||||
Now run the same query with temporal mode enabled. This is the default, so you can omit the flag.
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
results = client.search(
|
||||
"what did I do last week?",
|
||||
filters={"user_id": USER_ID},
|
||||
reference_date=REFERENCE_DATE,
|
||||
)
|
||||
|
||||
for item in results["results"]:
|
||||
print(
|
||||
item["memory"],
|
||||
item.get("memory_type"),
|
||||
item.get("event_date"),
|
||||
item.get("temporal_surface"),
|
||||
)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
const results = await client.search("what did I do last week?", {
|
||||
filters: { user_id: USER_ID },
|
||||
referenceDate: REFERENCE_DATE,
|
||||
});
|
||||
|
||||
results.results.forEach((item) => {
|
||||
console.log(item.memory, item.memoryType, item.eventDate, item.temporalSurface);
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
**Illustrative output:**
|
||||
```text
|
||||
I finished the Q1 product review on March 10, 2025. event 2025-03-10 Mar 10, 2025
|
||||
```
|
||||
|
||||
The impact is not just better ranking. Mem0 is now using the query’s time intent plus the stored temporal fields to prefer the memory that fits `last week`.
|
||||
|
||||
<Info icon="check">
|
||||
Expected behavior: with temporal mode on, the March 10 event should lead for `last week`, while the April 18 appointment and the ongoing job title should not dominate the answer.
|
||||
</Info>
|
||||
|
||||
## See the Three Query Shapes
|
||||
|
||||
The same memory store can now support three different retrieval patterns.
|
||||
|
||||
### 1. Past-oriented retrieval
|
||||
|
||||
Use this for questions about completed activity.
|
||||
|
||||
```python
|
||||
client.search(
|
||||
"what did I do last week?",
|
||||
filters={"user_id": USER_ID},
|
||||
reference_date=REFERENCE_DATE,
|
||||
)
|
||||
```
|
||||
|
||||
Expected outcome: the completed review ranks above future plans and ongoing profile facts.
|
||||
|
||||
### 2. Upcoming retrieval
|
||||
|
||||
Use this for scheduled commitments and future-facing assistant flows.
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
upcoming = client.search(
|
||||
"what do I have coming up?",
|
||||
filters={"user_id": USER_ID},
|
||||
reference_date=REFERENCE_DATE,
|
||||
)
|
||||
|
||||
for item in upcoming["results"]:
|
||||
print(item["memory"], item.get("memory_type"), item.get("effective_plan_status"))
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
const upcoming = await client.search("what do I have coming up?", {
|
||||
filters: { user_id: USER_ID },
|
||||
referenceDate: REFERENCE_DATE,
|
||||
});
|
||||
|
||||
upcoming.results.forEach((item) => {
|
||||
console.log(item.memory, item.memoryType, item.effectivePlanStatus);
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
**Illustrative output:**
|
||||
```text
|
||||
I have a dentist appointment on April 18, 2025 at 2 PM. plan upcoming
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Upcoming queries work best when the source memory includes a real date or date range. Vague text like "we should schedule something soon" is less reliable for future-oriented retrieval.
|
||||
</Warning>
|
||||
|
||||
### 3. Current-state retrieval
|
||||
|
||||
Use this for durable facts that are still true now.
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
current = client.search(
|
||||
"where do I work right now?",
|
||||
filters={"user_id": USER_ID},
|
||||
reference_date=REFERENCE_DATE,
|
||||
)
|
||||
|
||||
for item in current["results"]:
|
||||
print(item["memory"], item.get("memory_type"), item.get("temporal_surface"))
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
const current = await client.search("where do I work right now?", {
|
||||
filters: { user_id: USER_ID },
|
||||
referenceDate: REFERENCE_DATE,
|
||||
});
|
||||
|
||||
current.results.forEach((item) => {
|
||||
console.log(item.memory, item.memoryType, item.temporalSurface);
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
**Illustrative output:**
|
||||
```text
|
||||
I am the product lead at Acme Corp. state ongoing
|
||||
```
|
||||
|
||||
## Use the Right Controls
|
||||
|
||||
There are two public controls that matter most in production:
|
||||
|
||||
- `reference_date` anchors relative phrases like `last week`, `tomorrow`, and `right now`
|
||||
- `temporal_reasoning` lets you switch between standard retrieval and temporal retrieval for a specific request
|
||||
|
||||
### Deterministic tests and demos
|
||||
|
||||
Use `reference_date` whenever the wording is relative.
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
client.search(
|
||||
"what did I do last week?",
|
||||
filters={"user_id": USER_ID},
|
||||
reference_date="2025-03-21T00:00:00Z",
|
||||
)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
await client.search("what did I do last week?", {
|
||||
filters: { user_id: USER_ID },
|
||||
referenceDate: "2025-03-21T00:00:00Z",
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Standard retrieval for timeless queries
|
||||
|
||||
Turn temporal mode off when a question is not time-sensitive.
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
client.search(
|
||||
"what is my job title?",
|
||||
filters={"user_id": USER_ID},
|
||||
temporal_reasoning=False,
|
||||
)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
await client.search("what is my job title?", {
|
||||
filters: { user_id: USER_ID },
|
||||
temporalReasoning: false,
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Historical imports
|
||||
|
||||
If you are backfilling old data, send the original event time instead of relying only on ingestion time.
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
from datetime import datetime, timezone
|
||||
|
||||
client.add(
|
||||
[{"role": "user", "content": "I attended the partner summit on November 15, 2024."}],
|
||||
user_id=USER_ID,
|
||||
timestamp=int(datetime(2024, 11, 15, tzinfo=timezone.utc).timestamp()),
|
||||
)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
await client.add(
|
||||
[{ role: "user", content: "I attended the partner summit on November 15, 2024." }],
|
||||
{
|
||||
userId: USER_ID,
|
||||
timestamp: Math.floor(new Date("2024-11-15T00:00:00Z").getTime() / 1000),
|
||||
}
|
||||
);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Combine with normal Platform filters
|
||||
|
||||
Temporal retrieval still respects the normal entity filters.
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
client.search(
|
||||
"what did we discuss last week?",
|
||||
filters={
|
||||
"AND": [
|
||||
{"user_id": USER_ID},
|
||||
{"agent_id": "task-planner"},
|
||||
]
|
||||
},
|
||||
reference_date=REFERENCE_DATE,
|
||||
)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
await client.search("what did we discuss last week?", {
|
||||
filters: {
|
||||
AND: [
|
||||
{ user_id: USER_ID },
|
||||
{ agent_id: "task-planner" },
|
||||
],
|
||||
},
|
||||
referenceDate: REFERENCE_DATE,
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## What You Built
|
||||
|
||||
- Past questions can favor completed events instead of just semantically related memories.
|
||||
- Upcoming questions can surface scheduled plans instead of historical notes.
|
||||
- Current-state questions can keep stable facts available without letting them take over every search.
|
||||
- You can switch between standard retrieval and temporal retrieval per request with `temporal_reasoning`.
|
||||
|
||||
## Next Steps
|
||||
|
||||
If your application depends on relative time language, keep `reference_date` in your tests and evals, and pass `timestamp` when you backfill older conversations. That keeps time-aware retrieval predictable without adding a separate timeline system to your stack.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Temporal Reasoning Feature Guide"
|
||||
description="See the supported query patterns, public response fields, and configuration knobs."
|
||||
icon="clock"
|
||||
href="/platform/features/temporal-reasoning"
|
||||
/>
|
||||
<Card
|
||||
title="Control Memory Ingestion"
|
||||
description="Pair time-aware retrieval with stricter write controls and cleaner stored facts."
|
||||
icon="filter"
|
||||
href="/cookbooks/essentials/controlling-memory-ingestion"
|
||||
/>
|
||||
</CardGroup>
|
||||
@@ -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 can also return top-level temporal fields such as `memory_type`, `event_date`, `event_start`, `event_end`, `effective_plan_status`, `temporal_surface`, and `score_breakdown.temporal_boost`. See <Link href="/platform/features/temporal-reasoning">Temporal Reasoning</Link>.
|
||||
</Note>
|
||||
|
||||
## Filter patterns
|
||||
|
||||
Filters help narrow down search results. Common use cases:
|
||||
@@ -211,7 +215,7 @@ client.search("preferences", filters={
|
||||
- **Use natural language**: Mem0 understands intent, so describe what you're looking for naturally
|
||||
- **Scope with user ID**: Always provide `user_id` to scope search to relevant memories
|
||||
- **Platform API**: Use `filters={"user_id": "alice"}`
|
||||
- **OSS**: Use `user_id="alice"` as parameter
|
||||
- **OSS**: Use `filters={"user_id": "alice"}`
|
||||
- **Combine filters**: Use AND/OR logic to create precise queries (Platform)
|
||||
- **Consider wildcard filters**: Use wildcard filters (e.g., `run_id: "*"`) for broader matches
|
||||
- **Tune parameters**: Adjust `top_k` for result count, `threshold` for relevance cutoff
|
||||
@@ -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>
|
||||
|
||||
+4
-2
@@ -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"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -329,6 +330,7 @@
|
||||
"cookbooks/essentials/building-ai-companion",
|
||||
"cookbooks/essentials/entity-partitioning-playbook",
|
||||
"cookbooks/essentials/controlling-memory-ingestion",
|
||||
"cookbooks/essentials/temporal-memory-assistant",
|
||||
"cookbooks/essentials/tagging-and-organizing-memories",
|
||||
"cookbooks/essentials/exporting-memories"
|
||||
]
|
||||
@@ -1143,4 +1145,4 @@
|
||||
"destination": "/introduction"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -38,8 +38,8 @@ client.add(
|
||||
)
|
||||
|
||||
# Read
|
||||
client.search("What does Alice like to do?", user_id="alice")
|
||||
client.get_all(user_id="alice")
|
||||
client.search("What does Alice like to do?", filters={"user_id": "alice"})
|
||||
client.get_all(filters={"user_id": "alice"}, page=1, page_size=50)
|
||||
client.get(memory_id="<id>")
|
||||
|
||||
# Update
|
||||
@@ -68,8 +68,8 @@ await client.add(
|
||||
);
|
||||
|
||||
// Read
|
||||
await client.search("What does Alice like to do?", { user_id: "alice" });
|
||||
await client.getAll({ user_id: "alice" });
|
||||
await client.search("What does Alice like to do?", { filters: { user_id: "alice" } });
|
||||
await client.getAll({ filters: { user_id: "alice" }, page: 1, pageSize: 50 });
|
||||
await client.get("<memory_id>");
|
||||
|
||||
// Update
|
||||
|
||||
@@ -72,13 +72,13 @@ client = MemoryClient(api_key="m0-...")
|
||||
```
|
||||
|
||||
<Info icon="check">
|
||||
Run `client.get_all(filters={"user_id": "test_connection"})` to verify your API key works. It should return an empty list or valid results.
|
||||
Run `client.get_all(filters={"user_id": "test_connection"}, page=1, page_size=1)` to verify your API key works. A healthy response is a paginated envelope like `{"count": 0, "next": null, "previous": null, "results": []}` or the same shape with results.
|
||||
</Info>
|
||||
|
||||
### 3. Update Retrieval Calls (Critical)
|
||||
|
||||
<Warning>
|
||||
**Critical Change**: Platform uses v2 endpoints that require filtering parameters to be nested inside a `filters` dictionary.
|
||||
**Critical Change**: Platform uses V3 endpoints and requires entity filters to be nested inside a `filters` dictionary.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
@@ -95,6 +95,10 @@ client = MemoryClient(api_key="m0-...")
|
||||
|
||||
Note: `add()` and `delete()` methods remain unchanged. The `update()` method is not available in Platform - use delete + add pattern instead.
|
||||
|
||||
<Note>
|
||||
Platform search stays compatible with the normal `results[]` shape, but V3 may include additional top-level fields such as `score_breakdown`, `memory_type`, `event_date`, and `effective_plan_status` when the platform has structured context for the result.
|
||||
</Note>
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Search Memories">
|
||||
<CodeGroup>
|
||||
@@ -132,11 +136,11 @@ Note: `add()` and `delete()` methods remain unchanged. The `update()` method is
|
||||
```
|
||||
|
||||
```python Platform (New)
|
||||
# Get all memories for a user
|
||||
memories = client.get_all(filters={"user_id": "alex"}, top_k=10)
|
||||
# Get the first page of memories for a user
|
||||
page = client.get_all(filters={"user_id": "alex"}, page=1, page_size=10)
|
||||
|
||||
# Get memories with pagination
|
||||
memories = client.get_all(filters={"user_id": "alex"}, top_k=5, offset=10)
|
||||
# Get the next page
|
||||
next_page = client.get_all(filters={"user_id": "alex"}, page=2, page_size=10)
|
||||
```
|
||||
</CodeGroup>
|
||||
</Accordion>
|
||||
|
||||
@@ -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. Existing code that reads `results[]` and the top-level `score` continues to work, but V3 can return additional top-level fields when the platform has more structured context available:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -51,14 +51,20 @@ Search now uses hybrid retrieval, which improves ranking quality — especially
|
||||
"id": "mem-uuid",
|
||||
"memory": "User moved to San Francisco in January 2026",
|
||||
"score": 0.82,
|
||||
"score_breakdown": {
|
||||
"temporal_boost": 0.18
|
||||
},
|
||||
"metadata": {},
|
||||
"categories": ["location"]
|
||||
"categories": ["location"],
|
||||
"memory_type": "state",
|
||||
"event_date": "2026-01-15",
|
||||
"temporal_surface": "January 2026"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
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. Search responses may include fields such as `score_breakdown`, `memory_type`, `event_date`, `effective_plan_status`, `temporal_surface`, and `reference_date_used` when relevant.
|
||||
|
||||
## API Changes
|
||||
|
||||
@@ -164,8 +170,15 @@ Poll status via `GET /v1/event/{event_id}/` — status will be `SUCCEEDED` or `F
|
||||
"id": "mem-uuid",
|
||||
"memory": "User moved to San Francisco from New York in January 2026",
|
||||
"score": 0.82,
|
||||
"score_breakdown": {
|
||||
"temporal_boost": 0.18
|
||||
},
|
||||
"metadata": {},
|
||||
"categories": ["location"],
|
||||
"memory_type": "state",
|
||||
"event_date": "2026-01-15",
|
||||
"temporal_surface": "January 2026",
|
||||
"reference_date_used": "2026-01-20",
|
||||
"created_at": "2026-01-15T10:30:00Z",
|
||||
"updated_at": "2026-01-15T10:30:00Z"
|
||||
}
|
||||
@@ -186,6 +199,12 @@ Poll status via `GET /v1/event/{event_id}/` — status will be `SUCCEEDED` or `F
|
||||
"memory": "...",
|
||||
"metadata": {},
|
||||
"categories": [],
|
||||
"event_date": "2026-01-15",
|
||||
"event_start": "2026-01-15",
|
||||
"event_end": null,
|
||||
"time_precision": "day",
|
||||
"timezone": "UTC",
|
||||
"plan_status": null,
|
||||
"created_at": "2026-01-15T10:30:00Z",
|
||||
"updated_at": "2026-01-15T10:30:00Z"
|
||||
}
|
||||
@@ -288,7 +307,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, but V3 can now include extra top-level result fields such as `score_breakdown`, `memory_type`, and temporal fields when available.
|
||||
- **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
|
||||
|
||||
+180
-31
@@ -417,10 +417,10 @@
|
||||
"nullable": true,
|
||||
"description": "Additional metadata associated with the event."
|
||||
},
|
||||
"results": {
|
||||
"type": "array",
|
||||
"description": "Array of results produced by the event."
|
||||
},
|
||||
"results": {
|
||||
"type": "array",
|
||||
"description": "Array of results produced by the event. For add events, this confirms the write completed; temporal enrichment may continue asynchronously on some deployments."
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
@@ -1593,7 +1593,7 @@
|
||||
},
|
||||
{
|
||||
"lang": "JavaScript",
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst query = \"Your search query here\";\n\nclient.search(query, { user_id: \"<user_id>\" })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
"source": "// To use the JavaScript SDK, install the package:\n// npm i mem0ai\n\nimport MemoryClient from 'mem0ai';\nconst client = new MemoryClient({ apiKey: \"your-api-key\" });\n\nconst query = \"Your search query here\";\n\nclient.search(query, { filters: { user_id: \"<user_id>\" } })\n .then(result => console.log(result))\n .catch(error => console.error(error));"
|
||||
},
|
||||
{
|
||||
"lang": "cURL",
|
||||
@@ -1828,17 +1828,30 @@
|
||||
"type": "string",
|
||||
"description": "The extracted memory fact."
|
||||
},
|
||||
"score": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"description": "Combined multi-signal relevance score in [0, 1] (search responses only)."
|
||||
"user_id": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "External user identifier scoping this memory, if any."
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"description": "User-supplied metadata attached to the memory."
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "External agent identifier scoping this memory, if any."
|
||||
},
|
||||
"app_id": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "External app identifier scoping this memory, if any."
|
||||
},
|
||||
"run_id": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "External run identifier scoping this memory, if any."
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"description": "User-supplied metadata attached to the memory."
|
||||
},
|
||||
"categories": {
|
||||
"type": "array",
|
||||
@@ -1853,8 +1866,56 @@
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
},
|
||||
},
|
||||
"plan_status": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Stored plan status if this memory is a plan."
|
||||
},
|
||||
"event_date": {
|
||||
"type": "string",
|
||||
"format": "date",
|
||||
"nullable": true,
|
||||
"description": "Primary event date when available."
|
||||
},
|
||||
"event_start": {
|
||||
"type": "string",
|
||||
"format": "date",
|
||||
"nullable": true,
|
||||
"description": "Start date for an event or interval when available."
|
||||
},
|
||||
"event_end": {
|
||||
"type": "string",
|
||||
"format": "date",
|
||||
"nullable": true,
|
||||
"description": "End date for an event or interval when available."
|
||||
},
|
||||
"time_precision": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Temporal precision for the resolved date information."
|
||||
},
|
||||
"timezone": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Stored timezone associated with the temporal record, when available."
|
||||
},
|
||||
"polarity": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Stored temporal polarity, when available."
|
||||
},
|
||||
"temporal_relation": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Stored temporal relation, when available."
|
||||
},
|
||||
"state_key": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Stored state key for state-like memories, when available."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"memory",
|
||||
@@ -1875,18 +1936,28 @@
|
||||
"next": "https://api.mem0.ai/v3/memories/?page=2&page_size=100",
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": "mem-uuid",
|
||||
"memory": "User moved to San Francisco from New York in January 2026",
|
||||
"metadata": {},
|
||||
"categories": [
|
||||
"location"
|
||||
],
|
||||
"created_at": "2026-01-15T10:30:00Z",
|
||||
"updated_at": "2026-01-15T10:30:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"id": "mem-uuid",
|
||||
"memory": "User moved to San Francisco from New York in January 2026",
|
||||
"user_id": "alice",
|
||||
"metadata": {},
|
||||
"categories": [
|
||||
"location"
|
||||
],
|
||||
"created_at": "2026-01-15T10:30:00Z",
|
||||
"updated_at": "2026-01-15T10:30:00Z",
|
||||
"event_date": "2026-01-15",
|
||||
"event_start": "2026-01-15",
|
||||
"event_end": null,
|
||||
"time_precision": "day",
|
||||
"timezone": "UTC",
|
||||
"polarity": "positive",
|
||||
"temporal_relation": "on",
|
||||
"state_key": null,
|
||||
"plan_status": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1968,6 +2039,10 @@
|
||||
"type": "string",
|
||||
"description": "Scope memories to this agent."
|
||||
},
|
||||
"app_id": {
|
||||
"type": "string",
|
||||
"description": "Scope memories to this app."
|
||||
},
|
||||
"run_id": {
|
||||
"type": "string",
|
||||
"description": "Scope memories to this session / run."
|
||||
@@ -1981,10 +2056,48 @@
|
||||
"type": "string",
|
||||
"description": "Project-level instructions that guide extraction for this call."
|
||||
},
|
||||
"observation_date": {
|
||||
"type": "string",
|
||||
"format": "date",
|
||||
"nullable": true,
|
||||
"description": "Original conversation date used for temporal grounding."
|
||||
},
|
||||
"observation_datetime": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"nullable": true,
|
||||
"description": "Original conversation datetime used for temporal grounding."
|
||||
},
|
||||
"timezone": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "IANA timezone used to resolve observation dates and relative dates."
|
||||
},
|
||||
"multilingual": {
|
||||
"type": "boolean",
|
||||
"nullable": true,
|
||||
"description": "Extract memories in the same language as the input."
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"description": "Unix epoch timestamp for when the conversation happened."
|
||||
},
|
||||
"expiration_date": {
|
||||
"type": "string",
|
||||
"format": "date",
|
||||
"nullable": true,
|
||||
"description": "Optional expiration date for stored memories."
|
||||
},
|
||||
"infer": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "When `false`, stores each message verbatim without running the extraction LLM."
|
||||
},
|
||||
"temporal_reasoning": {
|
||||
"type": "boolean",
|
||||
"nullable": true,
|
||||
"description": "Enable V3 temporal reasoning write-path metadata. Defaults to the deployment setting."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2071,7 +2184,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 +2225,26 @@
|
||||
"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."
|
||||
},
|
||||
"temporal_reasoning": {
|
||||
"type": "boolean",
|
||||
"nullable": true,
|
||||
"description": "Enable V3 temporal intent and temporal scoring. Defaults to the deployment setting."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2191,7 +2324,23 @@
|
||||
{
|
||||
"id": "mem-uuid",
|
||||
"memory": "User moved to San Francisco from New York in January 2026",
|
||||
"user_id": "alice",
|
||||
"score": 0.82,
|
||||
"score_breakdown": {
|
||||
"semantic": 0.78,
|
||||
"bm25": 0.0,
|
||||
"entity": 0.0,
|
||||
"temporal_boost": 0.04
|
||||
},
|
||||
"memory_type": "event",
|
||||
"event_date": "2026-01-15",
|
||||
"event_start": "2026-01-15",
|
||||
"event_end": "2026-01-15",
|
||||
"plan_status": null,
|
||||
"effective_plan_status": null,
|
||||
"time_precision": "day",
|
||||
"temporal_surface": "Jan 15, 2026",
|
||||
"reference_date_used": "2026-01-20",
|
||||
"metadata": {},
|
||||
"categories": [
|
||||
"location"
|
||||
@@ -6241,4 +6390,4 @@
|
||||
}
|
||||
},
|
||||
"x-original-swagger-version": "2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ await client.search("What is Alice's favorite sport?", { filters: { userId: "ali
|
||||
|
||||
### Get All
|
||||
|
||||
Retrieve all memories for a user asynchronously.
|
||||
Retrieve a paginated list of memories for a user asynchronously.
|
||||
|
||||
<Callout type="warning" title="Filters Required">
|
||||
`get_all()` now requires filters to be specified.
|
||||
@@ -82,15 +82,27 @@ Retrieve all memories for a user asynchronously.
|
||||
<CodeGroup>
|
||||
|
||||
```python Python
|
||||
await client.get_all(filters={"AND": [{"user_id": "alice"}]})
|
||||
page = await client.get_all(
|
||||
filters={"AND": [{"user_id": "alice"}]},
|
||||
page=1,
|
||||
page_size=50,
|
||||
)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
await client.getAll({ filters: {"AND": [{"user_id": "alice"}]} });
|
||||
const page = await client.getAll({
|
||||
filters: { "AND": [{ user_id: "alice" }] },
|
||||
page: 1,
|
||||
pageSize: 50,
|
||||
});
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<Note>
|
||||
The async client returns the same paginated envelope as the sync client: `{"count", "next", "previous", "results"}`.
|
||||
</Note>
|
||||
|
||||
### Delete
|
||||
|
||||
Delete a specific memory asynchronously.
|
||||
|
||||
@@ -23,11 +23,27 @@ client.add(messages, user_id="alice", infer=False)
|
||||
```
|
||||
|
||||
```markdown Output
|
||||
[]
|
||||
{
|
||||
"message": "Memories stored successfully",
|
||||
"status": "SUCCEEDED",
|
||||
"event_id": "evt_123",
|
||||
"results": [
|
||||
{
|
||||
"id": "19d6d7aa-2454-4e58-96fc-e74d9e9f8dd1",
|
||||
"memory": "Alice loves playing badminton",
|
||||
"event": "ADD"
|
||||
},
|
||||
{
|
||||
"id": "8557f05d-7b3c-47e5-b409-9886f9e314fc",
|
||||
"memory": "Alice mostly cooks at home because of her gym plan",
|
||||
"event": "ADD"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
You can see that the output of the add call is an empty list.
|
||||
In V3, `infer=False` stores the supplied memory text directly and returns a successful event envelope with the created memories in `results`.
|
||||
|
||||
<Note>Only messages with the role "user" will be used for storage. Messages with roles such as "assistant" or "system" will be ignored during the storage process.</Note>
|
||||
|
||||
@@ -79,6 +95,9 @@ client.get_all(filters={"AND": [{"user_id": "alice"}]})
|
||||
|
||||
```json Output
|
||||
{
|
||||
"count": 2,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": "19d6d7aa-2454-4e58-96fc-e74d9e9f8dd1",
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
---
|
||||
title: Temporal Reasoning
|
||||
description: "Use time-aware memory retrieval in Mem0 Platform v3 so searches like 'last week', 'upcoming', and 'right now' return the right memories."
|
||||
icon: "clock"
|
||||
badge: "v3"
|
||||
---
|
||||
|
||||
Some memories should matter because of **when** they happened, not just because they sound similar. Temporal Reasoning helps Mem0 Platform v3 classify time-aware memories on write and interpret time-aware queries on search.
|
||||
|
||||
<Info>
|
||||
**You'll use this 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 does not apply to OSS memory stores or older Platform endpoints.
|
||||
</Warning>
|
||||
|
||||
## Configure access
|
||||
|
||||
Confirm your `MEM0_API_KEY` is configured and that you are using the v3 Platform client:
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(api_key="your-api-key")
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
Temporal Reasoning has two public-facing parts:
|
||||
|
||||
- **Write-time enrichment**: when Mem0 stores a memory, it can attach temporal structure such as memory type and date bounds when that information is present in the text.
|
||||
- **Search-time interpretation**: when a query includes time language like `last week`, `upcoming`, or `right now`, Mem0 uses the stored temporal fields to return more appropriate results.
|
||||
|
||||
### Memory types
|
||||
|
||||
Mem0 normalizes temporal memories into a small set of developer-facing types:
|
||||
|
||||
| Type | What it represents | Example |
|
||||
| --- | --- | --- |
|
||||
| `event` | A dated occurrence | "I finished the Q1 review on March 10, 2025." |
|
||||
| `plan` | A future commitment or scheduled item | "I have a dentist appointment on March 18, 2025." |
|
||||
| `state` | An ongoing fact that can remain 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." |
|
||||
|
||||
On search responses, temporal fields are returned as top-level result attributes when Temporal Reasoning is active. The most useful ones are:
|
||||
|
||||
- `memory_type`
|
||||
- `event_date`
|
||||
- `event_start`
|
||||
- `event_end`
|
||||
- `plan_status`
|
||||
- `effective_plan_status`
|
||||
- `time_precision`
|
||||
- `temporal_surface`
|
||||
- `reference_date_used`
|
||||
- `score_breakdown.temporal_boost`
|
||||
|
||||
## Configure it
|
||||
|
||||
### Default behavior
|
||||
|
||||
Temporal Reasoning is enabled by default for supported v3 searches and writes.
|
||||
|
||||
### Per-request override
|
||||
|
||||
Use `temporal_reasoning=False` when you want a specific call to behave like a normal semantic add or search.
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
client.add(
|
||||
[{"role": "user", "content": "Preferred language is English."}],
|
||||
user_id="jordan",
|
||||
temporal_reasoning=False,
|
||||
)
|
||||
|
||||
results = client.search(
|
||||
"preferred language",
|
||||
filters={"user_id": "jordan"},
|
||||
temporal_reasoning=False,
|
||||
)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
await client.add(
|
||||
[{ role: "user", content: "Preferred language is English." }],
|
||||
{ userId: "jordan", temporalReasoning: false }
|
||||
);
|
||||
|
||||
const results = await client.search("preferred language", {
|
||||
filters: { user_id: "jordan" },
|
||||
temporalReasoning: false,
|
||||
});
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
curl -X POST "https://api.mem0.ai/v3/memories/search/" \
|
||||
-H "Authorization: Token your-api-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "preferred language",
|
||||
"filters": { "user_id": "jordan" },
|
||||
"temporal_reasoning": false
|
||||
}'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Anchor imports and tests
|
||||
|
||||
Two fields matter when you want predictable temporal behavior:
|
||||
|
||||
- `timestamp` on `add()` anchors imported memories to the time they actually happened
|
||||
- `reference_date` on `search()` anchors relative queries like `last week` to a known point in time
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
from datetime import datetime, timezone
|
||||
|
||||
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()),
|
||||
)
|
||||
|
||||
results = client.search(
|
||||
"what did I do last week?",
|
||||
filters={"user_id": "jordan"},
|
||||
reference_date="2025-03-21T00:00:00Z",
|
||||
)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
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),
|
||||
}
|
||||
);
|
||||
|
||||
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 for tests, backfills, and cookbook examples because it makes relative phrases reproducible.
|
||||
</Tip>
|
||||
|
||||
## See it in action
|
||||
|
||||
### Add a few dated memories
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
client.add(
|
||||
[{"role": "user", "content": "I finished the Q1 product review on March 10, 2025."}],
|
||||
user_id="jordan",
|
||||
)
|
||||
|
||||
client.add(
|
||||
[{"role": "user", "content": "I have a dentist appointment on April 18, 2025 at 2 PM."}],
|
||||
user_id="jordan",
|
||||
)
|
||||
|
||||
client.add(
|
||||
[{"role": "user", "content": "I am the product lead at Acme Corp."}],
|
||||
user_id="jordan",
|
||||
)
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
await client.add(
|
||||
[{ role: "user", content: "I finished the Q1 product review on March 10, 2025." }],
|
||||
{ userId: "jordan" }
|
||||
);
|
||||
|
||||
await client.add(
|
||||
[{ role: "user", content: "I have a dentist appointment on April 18, 2025 at 2 PM." }],
|
||||
{ userId: "jordan" }
|
||||
);
|
||||
|
||||
await client.add(
|
||||
[{ role: "user", content: "I am the product lead at Acme Corp." }],
|
||||
{ userId: "jordan" }
|
||||
);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<Note>
|
||||
Platform writes are asynchronous. For quick validation, wait briefly before searching or verify the completed event in the dashboard or CLI.
|
||||
</Note>
|
||||
|
||||
### Search with a temporal query
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
results = client.search(
|
||||
"what did I do last week?",
|
||||
filters={"user_id": "jordan"},
|
||||
reference_date="2025-03-21T00:00:00Z",
|
||||
)
|
||||
|
||||
for item in results["results"]:
|
||||
print(item["memory"], item.get("memory_type"), item.get("event_date"))
|
||||
```
|
||||
|
||||
```javascript JavaScript
|
||||
const results = await client.search("what did I do last week?", {
|
||||
filters: { user_id: "jordan" },
|
||||
referenceDate: "2025-03-21T00:00:00Z",
|
||||
});
|
||||
|
||||
results.results.forEach((item) => {
|
||||
console.log(item.memory, item.memoryType, item.eventDate);
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<Info icon="check">
|
||||
Expected behavior: the March 10 review appears as a past `event`, the April 18 appointment does not lead this result set, and the ongoing role at Acme does not crowd out the dated answer.
|
||||
</Info>
|
||||
|
||||
## 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 and confirm results include top-level fields like `memory_type`, `event_date`, or `effective_plan_status`.
|
||||
- Repeat the same query with `temporal_reasoning=False` and confirm ranking changes for time-sensitive prompts.
|
||||
- Use `reference_date` in test queries so relative phrases resolve consistently.
|
||||
- For backfilled data, confirm imported memories use the expected chronology after you set `timestamp`.
|
||||
|
||||
## Best practices
|
||||
|
||||
- Use explicit dates in source conversations when plans or events matter.
|
||||
- Pass `timestamp` during historical imports so ingestion time does not become the only time anchor.
|
||||
- Keep search scoped with `filters` so time-aware ranking runs inside the right entity boundary.
|
||||
- Use `reference_date` in automated tests and reproducible demos.
|
||||
- Disable Temporal Reasoning only for clearly timeless lookups such as static profile fields.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Memory Timestamps" icon="calendar" href="/platform/features/timestamp">
|
||||
Anchor imported memories to when they actually happened.
|
||||
</Card>
|
||||
<Card title="Answer Time-Aware Questions" icon="book-open" href="/cookbooks/essentials/temporal-memory-assistant">
|
||||
Follow the cookbook for an end-to-end temporal retrieval workflow.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Snippet file="get-help.mdx" />
|
||||
Reference in New Issue
Block a user