diff --git a/docs/cookbooks/essentials/building-ai-companion.mdx b/docs/cookbooks/essentials/building-ai-companion.mdx index 681ad03a5..c4ec094d4 100644 --- a/docs/cookbooks/essentials/building-ai-companion.mdx +++ b/docs/cookbooks/essentials/building-ai-companion.mdx @@ -588,9 +588,9 @@ mem0_client.project.update( Exclude: greetings, filler, casual chat """, custom_categories=[ - {"name": "goals", "description": "Training targets"}, - {"name": "constraints", "description": "Injuries and limitations"}, - {"name": "preferences", "description": "Training style"} + {"goals": "Training targets"}, + {"constraints": "Injuries and limitations"}, + {"preferences": "Training style"} ] ) ``` diff --git a/docs/cookbooks/essentials/tagging-and-organizing-memories.mdx b/docs/cookbooks/essentials/tagging-and-organizing-memories.mdx index c0fbf4502..b22f95f70 100644 --- a/docs/cookbooks/essentials/tagging-and-organizing-memories.mdx +++ b/docs/cookbooks/essentials/tagging-and-organizing-memories.mdx @@ -19,7 +19,7 @@ client = MemoryClient(api_key="your-api-key") ``` -Define custom categories at the **project level** with `client.project.update()` before adding memories. Categories apply to all future memories: Mem0 auto-assigns them based on content semantics. +Define custom categories at the **project level** with `client.project.update()` before adding memories. Categories apply to all future memories: Mem0 auto-assigns them based on content semantics. You can also pass `custom_categories` on a single `client.add()` call to override the project list for just those memories. See [Custom Categories](/platform/features/custom-categories). --- @@ -96,6 +96,10 @@ Start with 3-5 clear categories that match how your team thinks. Too many catego These categories are now available project-wide. Every memory can be tagged with one or more categories. + +Need a different vocabulary for one tenant or one kind of conversation? Pass `custom_categories=[...]` to `client.add()`. That list replaces the project list for the memories created by that call, and it does not change the project configuration. + + --- ## Tagging Memories diff --git a/docs/migration/oss-to-platform.mdx b/docs/migration/oss-to-platform.mdx index c478701e2..e201c89a7 100644 --- a/docs/migration/oss-to-platform.mdx +++ b/docs/migration/oss-to-platform.mdx @@ -339,21 +339,22 @@ The Platform introduces powerful capabilities not available in OSS: ```python # Set custom categories for your project - client.projects.update_categories( - project_id="proj_123", - categories=[ - "Customer Preferences", - "Product Feedback", - "Support Issues", - "Feature Requests" + client.project.update( + custom_categories=[ + {"customer_preferences": "Likes, dislikes, and product preferences"}, + {"product_feedback": "Feature requests and complaints about the product"}, + {"support_issues": "Problems reported and how they were resolved"} ] ) - # Memories will use these categories + # Mem0 assigns these categories automatically as memories come in + client.add("User wants dark mode in dashboard", user_id="alex") + + # Or pass a different catalog for a single call client.add( "User wants dark mode in dashboard", user_id="alex", - categories=["Customer Preferences"] + custom_categories=[{"ui_requests": "Requests about interface and appearance"}] ) ``` diff --git a/docs/openapi.json b/docs/openapi.json index 03ac222c7..09344356e 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -1992,6 +1992,17 @@ "type": "string", "description": "Project-level instructions that guide extraction for this call." }, + "custom_categories": { + "type": "array", + "description": "Category catalog for this call. Replaces the project-level list rather than merging with it. Omit to fall back to the project list, then the default catalog.", + "items": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Maps a category name to the description the classifier matches against." + } + }, "infer": { "type": "boolean", "default": true, diff --git a/docs/platform/features/custom-categories.mdx b/docs/platform/features/custom-categories.mdx index d9f0ae22a..ce699177a 100644 --- a/docs/platform/features/custom-categories.mdx +++ b/docs/platform/features/custom-categories.mdx @@ -1,6 +1,6 @@ --- title: Custom Categories -description: "Replace default memory tags with custom category labels that match your product terminology at the project level." +description: "Replace default memory tags with custom category labels that match your product terminology, set once per project or per individual add call." --- # Custom Categories @@ -14,9 +14,7 @@ Mem0 automatically tags every memory, but the default labels (travel, sports, mu - You’re moving from the open-source version and want the same labels here. - - Per-request overrides (`custom_categories=...` on `client.add`) are not supported on the managed API yet. Set categories at the project level, then ingest memories as usual. - +You can set the list once for the whole project, or pass a different list on an individual `add` call. ## Configure access @@ -27,7 +25,20 @@ Mem0 automatically tags every memory, but the default labels (travel, sports, mu - **Default list**: Each project starts with 15 broad categories like `travel`, `sports`, and `music`. - **Project override**: When you call `project.update(custom_categories=[...])`, that list replaces the defaults for future memories. -- **Automatic tags**: As new memories come in, Mem0 picks the closest matches from your list and saves them in the `categories` field. +- **Per-call override**: When you pass `custom_categories=[...]` to `client.add(...)`, that list is used for the memories extracted from that call. +- **Automatic tags**: As new memories come in, Mem0 picks the closest matches from the active list and saves them in the `categories` field. + +### Which list wins + +Mem0 resolves the category catalog for each `add` call in this order, and stops at the first one it finds: + +1. `custom_categories` passed on the `add` call +2. `custom_categories` set on the project +3. The built-in default catalog + +A per-call list **fully replaces** the project list for that call. The two are not merged, so a memory added with a per-call list can only be tagged with categories from that list. + +Categories are applied at ingestion time. Changing the project list, or passing a new per-call list, does not re-tag memories that already exist. Default catalog: `personal_details`, `family`, `professional_details`, `sports`, `travel`, `food`, `music`, `health`, `technology`, `hobbies`, `fashion`, `entertainment`, `milestones`, `user_preferences`, `misc`. @@ -84,6 +95,82 @@ print(categories) ``` +`get` echoes back the shape you set. `update` also accepts a plain list of names, such as `["billing", "support"]`, in which case `get` returns that same list of names. Descriptions are optional here, and the classifier uses them to disambiguate when it has them. + + + `add` is stricter than `update`. Every entry in a per-call `custom_categories` list must be an object mapping a name to a description. Passing bare names to `add` fails with `400 Expected a dictionary of items but got type "str"`. + + +### 3. Override categories on a single add call + +Pass `custom_categories` directly to `add` when one call needs a different catalog than the project default. The memories created by that call are tagged from the list you pass, and the project list is left untouched. + + +```python Python +health_messages = [ + {"role": "user", "content": "My doctor bumped my metformin to 1000mg and I see her again on the 14th."}, + {"role": "assistant", "content": "Noted the new dosage and the follow-up appointment."}, +] + +health_categories = [ + {"symptoms": "Reported physical or mental symptoms"}, + {"medications": "Prescriptions, dosages, and adherence"}, + {"appointments": "Scheduled visits and follow-ups"}, +] + +client.add( + health_messages, + user_id="alice", + custom_categories=health_categories, +) +``` + +```javascript JavaScript +const healthMessages = [ + { role: "user", content: "My doctor bumped my metformin to 1000mg and I see her again on the 14th." }, + { role: "assistant", content: "Noted the new dosage and the follow-up appointment." }, +]; + +const healthCategories = [ + { symptoms: "Reported physical or mental symptoms" }, + { medications: "Prescriptions, dosages, and adherence" }, + { appointments: "Scheduled visits and follow-ups" }, +]; + +await client.add(healthMessages, { + userId: "alice", + customCategories: healthCategories, +}); +``` + +```text Resulting categories +["medications", "appointments"] +``` + + +The memory is tagged from `health_categories` alone. The project catalog is not consulted for this call, and it is not modified. + +#### Per-user categories inside one project + +The main reason to reach for a per-call list is to give different users, tenants, or entities their own vocabulary without splitting them across projects. Keep one project, and pass the list that fits the entity you are writing for. + + +```python Python +patient_categories = [ + {"symptoms": "Reported physical or mental symptoms"}, + {"medications": "Prescriptions, dosages, and adherence"}, +] + +clinician_categories = [ + {"caseload": "Patients under this clinician's care"}, + {"availability": "Shift patterns and on-call windows"}, +] + +client.add("My metformin is now 1000mg.", user_id="alice", custom_categories=patient_categories) +client.add("I'm on call Tuesdays and Thursdays.", user_id="dr-reyes", custom_categories=clinician_categories) +``` + + ## See it in action ### Add a memory (uses the project catalog automatically) @@ -104,46 +191,57 @@ client.add(messages, user_id="alice") ### Retrieve memories and inspect categories +`get_all` returns a paginated object. The memories are under `results`, and each one carries its own `categories` list. + ```python Code -memories = client.get_all(filters={"user_id": "alice"}) +response = client.get_all(filters={"user_id": "alice"}) + +for memory in response["results"]: + print(memory["memory"], memory["categories"]) ``` -```json Output -["lifestyle_management_concerns", "seeking_structure"] +```text Output +User introduced herself as Alice and expressed a desire for help organizing her daily schedule. ['lifestyle_management_concerns', 'seeking_structure', 'personal_information'] +User feels overwhelmed trying to balance work responsibilities, regular exercise, and a social life, indicating difficulty managing time across these areas. ['lifestyle_management_concerns'] +User's goals include becoming more productive at work, maintaining a consistent workout routine, and preserving enough energy for friends and hobbies. ['lifestyle_management_concerns', 'seeking_structure'] ``` +Extraction is model driven, so the exact wording and the number of memories vary between runs. The categories are drawn from the active list. + **Sample memory payload** ```json { - "id": "33d2***", - "memory": "Trying to balance work and workouts", + "id": "638008c4-***", + "memory": "User is seeking to balance work responsibilities with regular workout sessions and requests a personalized schedule to manage both.", "user_id": "alice", "metadata": null, - "categories": ["wellness"], // ← matches the custom category we set - "created_at": "2025-11-01T02:13:32.828364-07:00", - "updated_at": "2025-11-01T02:13:32.830896-07:00", + "categories": ["lifestyle_management_concerns", "seeking_structure"], + "created_at": "2026-07-10T06:13:12-07:00", + "updated_at": "2026-07-10T06:13:20-07:00", "expiration_date": null, "structured_attributes": { - "day": 1, - "hour": 9, - "year": 2025, - "month": 11, + "year": 2026, + "month": 7, + "day": 10, + "hour": 13, "minute": 13, - "quarter": 4, - "is_weekend": true, - "day_of_week": "saturday", - "day_of_year": 305, - "week_of_year": 44 + "day_of_week": "friday", + "week_of_year": 28, + "day_of_year": 191, + "quarter": 3, + "is_weekend": false } } ``` +Categorization runs asynchronously, a moment after the memory itself is written. A memory fetched immediately after `add` may not show up in `get_all` yet, or can come back with `categories: null` and pick up its tags a moment later. Poll until `categories` is populated rather than reading once. + - Need ad-hoc labels for a single call? Store them in `metadata` until per-request overrides become available. + Need ad-hoc labels for a single call? Pass `custom_categories` on that `add` call. Use `metadata` instead when the label is a fixed value you already know, rather than something the classifier should infer. ## Default categories (fallback) @@ -208,11 +306,13 @@ client.project.get(["custom_categories"]) ```json Output { - "custom_categories": None + "custom_categories": null } ``` +A project that has never set a list returns `null`. One you have reset with `project.update(custom_categories=[])` returns `[]`. Both mean the default catalog is active. + ## Verify the feature is working - `client.project.get(["custom_categories"])` returns the category list you set. @@ -223,7 +323,8 @@ client.project.get(["custom_categories"]) - Keep category descriptions concise but specific; the classifier uses them to disambiguate. - Review memories with empty `categories` to see where you might extend or rename your list. -- Stick with project-level overrides until per-request support is released; mixing approaches causes confusion. +- Set the catalog your app uses most often at the project level, and reserve per-call lists for the calls that genuinely need a different vocabulary. +- If a per-call list should also keep the project categories, include them in the list you pass. Passing a list replaces, it does not extend. diff --git a/integrations/mem0-plugin/skills/mem0/references/features.md b/integrations/mem0-plugin/skills/mem0/references/features.md index 66875ee75..1328f1fc7 100644 --- a/integrations/mem0-plugin/skills/mem0/references/features.md +++ b/integrations/mem0-plugin/skills/mem0/references/features.md @@ -102,9 +102,29 @@ await client.updateProject({ customCategories: newCategories }); categories = client.project.get(fields=["custom_categories"]) ``` -### Key Constraint +**Override categories for a single add call:** +```python +client.add(messages, user_id="alice", custom_categories=per_call_categories) +``` -Per-request overrides (`custom_categories=...` on `client.add`) are **not supported** on the managed API. Only project-level configuration works. Workaround: store ad-hoc labels in `metadata` field. +```javascript +await client.add(messages, { userId: "alice", customCategories: perCallCategories }); +``` + +### Resolution Order + +1. `custom_categories` passed on the `add` call +2. `custom_categories` set on the project +3. Built-in default catalog + +### Key Constraints + +- A per-call list **fully replaces** the project list for that call. The lists are not merged. +- Categories are applied at ingestion time. Changing the list later does not re-tag existing memories. + +### Main Use Case + +Per-call lists give different users or entities their own vocabulary inside a single project, without splitting them across projects. --- diff --git a/skills/mem0/references/features.md b/skills/mem0/references/features.md index b5d5e73ea..90e3308c9 100644 --- a/skills/mem0/references/features.md +++ b/skills/mem0/references/features.md @@ -102,9 +102,29 @@ await client.updateProject({ customCategories: newCategories }); categories = client.project.get(fields=["custom_categories"]) ``` -### Key Constraint +**Override categories for a single add call:** +```python +client.add(messages, user_id="alice", custom_categories=per_call_categories) +``` -Per-request overrides (`custom_categories=...` on `client.add`) are **not supported** on the managed API. Only project-level configuration works. Workaround: store ad-hoc labels in `metadata` field. +```javascript +await client.add(messages, { userId: "alice", customCategories: perCallCategories }); +``` + +### Resolution Order + +1. `custom_categories` passed on the `add` call +2. `custom_categories` set on the project +3. Built-in default catalog + +### Key Constraints + +- A per-call list **fully replaces** the project list for that call. The lists are not merged. +- Categories are applied at ingestion time. Changing the list later does not re-tag existing memories. + +### Main Use Case + +Per-call lists give different users or entities their own vocabulary inside a single project, without splitting them across projects. ---