diff --git a/docs/cookbooks/essentials/building-ai-companion.mdx b/docs/cookbooks/essentials/building-ai-companion.mdx index bcaaada17..95e6df5c9 100644 --- a/docs/cookbooks/essentials/building-ai-companion.mdx +++ b/docs/cookbooks/essentials/building-ai-companion.mdx @@ -144,9 +144,14 @@ Retrieve just constraints for workout planning: ```python constraints = mem0_client.search( - "injury concerns", - user_id="max", - filters={"categories": {"in": ["constraints"]}} + query="injury concerns", + filters={ + "AND": [ + {"user_id": "max"}, + {"categories": {"in": ["constraints"]}} + ] + }, + threshold=0.0 # optional: widen recall for short phrases ) print([m["memory"] for m in constraints["results"]]) # Output: ["Max's right knee flares up on downhills"] diff --git a/docs/cookbooks/essentials/entity-partitioning-playbook.mdx b/docs/cookbooks/essentials/entity-partitioning-playbook.mdx index 6511d95a2..9295bbf8c 100644 --- a/docs/cookbooks/essentials/entity-partitioning-playbook.mdx +++ b/docs/cookbooks/essentials/entity-partitioning-playbook.mdx @@ -63,8 +63,10 @@ print(agent_memories) **Output:** ``` +# User scope returns user's memory {'results': [{'memory': 'avoids shellfish and prefers boutique hotels', ...}]} -{'results': [{'memory': 'avoids shellfish and prefers boutique hotels', ...}]} +# Agent scope returns agent's own memory +{'results': [{'memory': 'Cam prefers boutique hotels and avoids shellfish', ...}]} ``` @@ -137,7 +139,9 @@ Nora white-labels her travel service for a sports brand. Use `app_id` to keep en ```python enterprise_filters = { "AND": [ - {"app_id": "sports_brand_portal"}, + {"app_id": "sports_brand_portal"} + ], + "OR": [ {"user_id": "*"}, {"agent_id": "*"} ] @@ -176,7 +180,10 @@ client.delete_all(app_id="sports_brand_portal") ```python # Nightly audits - check all data for an app def audit_app(app_id: str): - filters = {"AND": [{"app_id": app_id}, {"user_id": "*"}, {"agent_id": "*"}]} + filters = { + "AND": [{"app_id": app_id}], + "OR": [{"user_id": "*"}, {"agent_id": "*"}] + } return client.get_all(filters=filters, page=1, page_size=50) # Session cleanup - delete temporary conversations diff --git a/docs/platform/features/entity-scoped-memory.mdx b/docs/platform/features/entity-scoped-memory.mdx index b7f09bfcb..d325623d2 100644 --- a/docs/platform/features/entity-scoped-memory.mdx +++ b/docs/platform/features/entity-scoped-memory.mdx @@ -73,6 +73,10 @@ client.add( The response will include one or more memory IDs. Check the dashboard → Memories to confirm the entry appears under the correct user, agent, app, and run. + +Platform writes that include both `user_id` and `agent_id` (or other combinations) are persisted as separate records per entity so we can enforce privacy boundaries. Each record carries exactly one primary entity, which is why `{"AND": [{"user_id": ...}, {"agent_id": ...}]}` never returns results. Plan searches per entity scope or combine scopes with `OR`. + + The HTTP equivalent uses `POST /v1/memories/` with the same identifiers in the JSON body. See the Add Memories API reference for REST details. ## See it in action @@ -133,7 +137,9 @@ Want to experiment with AND/OR logic, nested operators, or wildcards? The `, `>=`, `<`, `<=`, `=`, `!=` | `{"created_at": {"gte": "2024-01-01"}}` | -| `updated_at` | `>`, `>=`, `<`, `<=`, `=`, `!=` | `{"updated_at": {"lt": "2024-12-31"}}` | -| `timestamp` | `>`, `>=`, `<`, `<=`, `=`, `!=` | `{"timestamp": {"gt": "2024-01-01"}}` | +| `created_at` | `gt`, `gte`, `lt`, `lte`, `eq`, `ne` | `{"created_at": {"gte": "2024-01-01"}}` | +| `updated_at` | `gt`, `gte`, `lt`, `lte`, `eq`, `ne` | `{"updated_at": {"lt": "2024-12-31"}}` | +| `timestamp` | `gt`, `gte`, `lt`, `lte`, `eq`, `ne` | `{"timestamp": {"gt": "2024-01-01"}}` | ### Content fields | Field | Operators | Example | |-------|-----------|---------| -| `categories` | `=`, `!=`, `in`, `contains` | `{"categories": {"in": ["finance"]}}` | -| `metadata` | `=`, `!=` | `{"metadata": {"key": "value"}}` | +| `categories` | `eq`, `ne`, `in`, `contains` | `{"categories": {"in": ["finance"]}}` | +| `metadata` | `eq`, `ne`, `contains` | `{"metadata": {"key": "value"}}` | | `keywords` | `contains`, `icontains` | `{"keywords": {"icontains": "invoice"}}` | ### Special fields @@ -66,6 +66,10 @@ Filters use a nested JSON structure with logical operators at the root: The `*` wildcard matches any non-null value. Records with null values for that field are excluded. + +Use operator keywords exactly as shown (`eq`, `ne`, `gte`, etc.). SQL-style symbols such as `>=` or `!=` are rejected by the Platform API. + + ## Common filter patterns Use these ready-made filters to target typical retrieval scenarios without rebuilding logic from scratch. @@ -101,6 +105,20 @@ Use these ready-made filters to target typical retrieval scenarios without rebui + +Metadata filters only support bare values/`eq`, `contains`, and `ne`. Operators such as `in`, `gt`, or `lt` trigger a `FilterValidationError`. For multi-value checks, wrap multiple equality clauses in `OR`. + + +```python +# Multi-value metadata workaround +filters = { + "OR": [ + {"metadata": {"type": "semantic"}}, + {"metadata": {"type": "episodic"}} + ] +} +``` + ### Content search Find memories containing specific text, categories, or metadata values. @@ -240,13 +258,13 @@ Combine various filters for complex queries across different dimensions. ``` - + ```python - # Require every entity field to be non-null + # Require user_id plus non-null run/app IDs + # (Memories are stored separately per entity, so scope one dimension at a time.) filters = { "AND": [ - {"user_id": "*"}, - {"agent_id": "*"}, + {"user_id": "user_123"}, {"run_id": "*"}, {"app_id": "*"} ] @@ -275,23 +293,20 @@ Level up foundational patterns with compound filters that coordinate entity scop ``` - + ```python - # From specific agent, all users + # Query agent scope on its own filters = { "AND": [ - {"agent_id": "finance_bot"}, - {"user_id": "*"} + {"agent_id": "finance_bot"} ] } - # Any entity populated + # Or broaden within that scope using wildcards filters = { - "OR": [ - {"user_id": "*"}, - {"agent_id": "*"}, - {"run_id": "*"}, - {"app_id": "*"} + "AND": [ + {"agent_id": "finance_bot"}, + {"run_id": "*"} ] } ``` @@ -327,7 +342,7 @@ Use `"*"` to match any non-null value for a field. -When you specify `user_id` only, other entity fields default to null. Use wildcards to include them. +Memories are stored per-entity (user, agent, app, run). Combining `user_id` **and** `agent_id` in the same `AND` clause returns no results because no record contains both values at once. Query one entity scope at a time or use `OR` logic for parallel lookups. ## Troubleshooting @@ -336,9 +351,9 @@ When you specify `user_id` only, other entity fields default to null. Use wildca **Problem**: Filtered by `user_id` but don't see agent memories. - **Solution**: Add a wildcard for `agent_id` so non-null entries return: + **Solution**: User and agent memories are stored as separate records. Use OR to query both scopes: ```python - {"AND": [{"user_id": "user_123"}, {"agent_id": "*"}]} + {"OR": [{"user_id": "user_123"}, {"agent_id": "agent_name"}]} ``` @@ -400,7 +415,7 @@ When you specify `user_id` only, other entity fields default to null. Use wildca Use `keywords` with `contains` (case-sensitive) or `icontains` (case-insensitive). - + ```python { "AND": [ @@ -414,3 +429,9 @@ When you specify `user_id` only, other entity fields default to null. Use wildca ``` + +## Known limitations + +- Entity filters operate on a single scope per record. Use separate queries or `OR` logic to compare users vs agents. +- Metadata supports only bare/`eq`, `contains`, and `ne` comparisons. +- Wildcards (`"*"` ) match only records where the field is already non-null.