{
  "schema_version": 2,
  "id": "develop/ai/context-engine/agent-memory/long-term-memory",
  "title": "Long-term memory",
  "url": "https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/long-term-memory/",
  "summary": "Create and search memories that remain useful across conversations.",
  "aliases": [
    "/develop/ai/context-engine/agent-memory/custom-memory-types/"
  ],
  "tags": [
    "docs",
    "develop",
    "ai"
  ],
  "last_updated": "2026-11-03T10:02:56-08:00",
  "page_type": "content",
  "content_hash": "1c3db69e77569469f05c050130419c770963b34a1c7c7ec709ebb6de50c1b4d1",
  "sections": [
    {
      "id": "code-examples",
      "title": "Code examples",
      "role": "example",
      "text": "These examples connect to Iris services using SDKs or HTTP requests with curl. Follow one language throughout each walkthrough.\n\n\nLong-term memory stores information that remains useful beyond one conversation. Use automatic extraction or direct creation to store memories, then search for information relevant to an agent turn."
    },
    {
      "id": "create-long-term-memories",
      "title": "Create long-term memories",
      "role": "content",
      "text": "Redis Agent Memory provides two ways to create memories:\n\n* **Automatic extraction:** Redis Agent Memory processes session events asynchronously and creates durable memories from relevant information. Configure the extraction cadence to control how often session events are processed.\n* **Direct creation:** Your application creates one or more long-term memories through the application programming interface (API) or a software development kit (SDK). Use direct creation when importing existing information or when your application determines exactly what to store.\n\nConfigure the long-term-memory time to live (TTL) separately from the session-memory TTL.\n\nSee [memory configuration](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#memory-configuration) to configure the extraction cadence and long-term-memory TTL in Redis Cloud.\n\nThese examples continue from the [quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/quickstart). Load the same `.env` connection values and reuse its user ID. Expand an example to copy a complete program that connects for you.\n\nEach run repeats every write in the program. Session-event writes and memory creates with a new idempotency key add new records, so run cleanup or change the identifiers before rerunning.\n\n\nThis example creates three memories for the quickstart user. Direct creation does not wait for background extraction, so the memories are searchable immediately:\n\n**Available in:** Python (Iris), cURL (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n\n\nIn Python, `remember` derives a stable memory ID from the owner and text; the cURL example sets each `memoryId` explicitly. Either way, running the example again does not duplicate the memories. A `memoryId` that already exists is rejected for that record and reported in the response's `errors` array, and the stored memory is left unchanged. To change a memory, see [Update a memory](#update-a-memory).\n\nReuse an `idempotencyKey` with the same request to retry a bulk create safely. Redis Agent Memory replays the saved result during the retry window, which lasts at most 24 hours and no longer than the store's memory TTL. Reusing a key with a different request returns `409 Conflict`."
    },
    {
      "id": "memory-fields",
      "title": "Memory fields",
      "role": "content",
      "text": "When you create a memory directly, you supply `memoryId`, `text`, and `ownerId`. The Python SDK's `remember` method derives `memoryId` when you omit it. The server sets `createdAt`, `updatedAt`, and `revision`. During automatic extraction, the server creates the memory ID and content and copies the session owner into `ownerId`. Use that same stable user ID when querying personal memories; see [Authorize every memory request](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/developer-guide#authorize-every-memory-request).\n\nA long-term memory record includes:\n\n| Field | Purpose |\n|:------|:--------|\n| `memoryId` | Unique identifier for the memory. |\n| `text` | Memory content used for retrieval. |\n| `memoryType` | Built-in or custom memory type. Defaults to `semantic`. |\n| `sessionId` | Session associated with the memory. |\n| `ownerId` | User or entity associated with the memory. |\n| `namespaceId` | Optional stable namespace ID. |\n| `attributes` | Custom memory fields and their values. |\n| `topics` | Topic tags used to categorize the memory. |\n| `revision` | Opaque token that changes on every write. Use it for [conditional updates](#update-a-memory). |\n| `documentManaged` | `true` when [MemoryETL](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/memory-etl) created the memory. |\n| `createdAt` | Time the memory was created. |\n| `updatedAt` | Time the memory was last updated. |"
    },
    {
      "id": "search-long-term-memory",
      "title": "Search long-term memory",
      "role": "content",
      "text": "Search by meaning and use `ownerId` to restrict recall to the relevant user or entity. Add filters for a session, namespace, topic, or memory type as needed.\n\nFor request fields, filter operators, and response schemas, see [`SearchLongTermMemory`](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference#operation/SearchLongTermMemory).\n\nFor domain-specific fields and extraction instructions, see [Custom memory types](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/long-term-memory#custom-memory-types). To group memories in a hierarchy, see [Namespaces](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/namespaces).\n\nFor controls that apply during automatic extraction, see [Exclude sensitive data from automatic extraction](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/developer-guide#exclude-sensitive-data-from-automatic-extraction)."
    },
    {
      "id": "search-by-meaning",
      "title": "Search by meaning",
      "role": "content",
      "text": "This example finds memories about the user's food preferences. `recall` restricts the search to the owner's memories:\n\n**Available in:** Python (Iris), cURL (Iris), CLI (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n##### CLI (Iris)\n\n[code example]"
    },
    {
      "id": "filter-and-sort-without-semantic-search",
      "title": "Filter and sort without semantic search",
      "role": "content",
      "text": "Omit `query` to retrieve records by filters without a semantic query. For example, retrieve the user's most recently updated memories:\n\n**Available in:** Python (Iris), cURL (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n\n\n`sortBy` accepts `createdAt` or `updatedAt` only when `query` is absent. `sortOrder` accepts `ASC` or `DESC`, defaults to descending, and requires `sortBy`. Semantic searches remain ranked by similarity and cannot use these sort fields. `limit` accepts 1–100 and defaults to 50. Pass `nextCursor` as `cursor` with the same query to continue.\n\nFilter conditions use `filter.op: \"ALL\"` (AND) by default; `\"ANY\"` uses OR. To restrict a query to a user, keep the owner condition mandatory. Do not use `\"ANY\"` with separate owner and namespace conditions when both must match. These retrieval filters do not replace application access checks.\n\nSet `filter.unnamespaced: true` to match records with neither the older `namespace` string label nor a namespace resource. It cannot be combined with `namespaceId`, and remains mandatory even with `op: \"ANY\"`.\n\n[MemoryETL](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/memory-etl) outputs are included in search by default. Set top-level `excludeDocumentManaged: true` to omit them. Returned `documentManaged` records are protected from generic writes: memory creation, `PUT`, and `PATCH` requests cannot change their content. The one exception is a `PATCH` that changes only `namespaceId`, which moves the record and keeps its TTL. Use the document-ingestion workflow to manage their content.\n\nFor custom fields, use `filter.attributes`, keyed by the field name, with the operators described in [`AttributeFilterClause`](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference#operation/SearchLongTermMemory). Combine it with `memoryType` to select the registered custom type.\n\nFor example, this search request combines the owner, type, and dietary requirement with `op: \"ALL\"`. A `list` clause requires every listed value to be present:\n\n[code example]\n\nUse exactly one typed clause per attribute: `string`, `number`, `boolean`, or `list`, matching the registered field type."
    },
    {
      "id": "update-a-memory",
      "title": "Update a memory",
      "role": "content",
      "text": "Redis Agent Memory provides two ways to change a memory you manage:\n\n* **Partial update:** [`PATCH /memories/{memoryId}`](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference#operation/UpdateLongTermMemory) changes only the fields you send: `text`, `memoryType`, `topics`, `namespaceId`, `ownerId`, or `sessionId`. It does not change `attributes`. An update that changes only `namespaceId` keeps the memory's current expiration; any other update restarts the long-term-memory TTL.\n* **Replace or create:** [`PUT /memories/{memoryId}`](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference#operation/UpsertLongTermMemory) creates the memory if it does not exist, or replaces it completely. It requires `text` and `ownerId`. Omitted optional fields, including `attributes`, are cleared, and `memoryType` defaults to `semantic`. Replacement keeps `createdAt` and restarts the TTL. The response's `created` field reports whether the memory was new.\n\nTo change a custom memory's `attributes`, replace the record with `PUT`.\n\nThe example program first creates a memory that records a seat preference. Read the memory before you change it. The HTTP response's `revision` identifies the version you read:\n\n**Available in:** Python (Iris), cURL (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n\n\nChange the text and topics with a partial update:\n\n**Available in:** Python (Iris), cURL (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n\n\nReplace the whole memory with `PUT`:\n\n**Available in:** cURL (Iris)\n\n##### cURL (Iris)\n\n[code example]"
    },
    {
      "id": "prevent-lost-updates",
      "title": "Prevent lost updates",
      "role": "content",
      "text": "Send `expectedRevision` with the `revision` you read to apply a `PATCH` or `PUT` only if nobody changed the memory since. A stale revision returns `412 Precondition Failed` with the type `/errors/revision-conflict`. Read the memory again, reapply your change, and retry. Omit `expectedRevision` for an unconditional write. Search results from older records can omit `revision`; read the memory by ID before a conditional update.\n\nThis request reuses the revision read before the partial update, so it fails:\n\n**Available in:** cURL (Iris)\n\n##### cURL (Iris)\n\n[code example]\n\n\n\n`PATCH` and `PUT` also accept an `idempotencyKey`. Reuse the key with the same request to retry safely; Redis Agent Memory replays the saved result during the retry window, which lasts at most 24 hours and no longer than the store's memory TTL. Reusing a key with a different request returns `409 Conflict`.\n\nThe Python SDK does not yet expose `revision`, `expectedRevision`, update idempotency keys, or the `PUT` operation. Use the HTTP API for replacement and conditional writes."
    },
    {
      "id": "delete-memories",
      "title": "Delete memories",
      "role": "content",
      "text": "Delete memories by ID. This example deletes the memories created in [Create long-term memories](#create-long-term-memories):\n\n**Available in:** Python (Iris), cURL (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n\n\nTo delete every session and memory that a user owns, see [Clean up](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/quickstart#clean-up) in the quickstart."
    },
    {
      "id": "custom-memory-types",
      "title": "Custom memory types",
      "role": "content",
      "text": "Custom memory types capture structured information specific to your business domain."
    },
    {
      "id": "define-a-memory-type",
      "title": "Define a memory type",
      "role": "content",
      "text": "Configure the type on your Redis Agent Memory service with:\n\n| Setting | Purpose |\n|:--------|:--------|\n| **Name** | Unique name used as the memory's `memoryType`. |\n| **Description** | Description of the information represented by the type. |\n| **Extraction prompt** | Instructions that tell Redis Agent Memory when and how to extract the custom memory from session events. |\n| **Enabled** | Controls whether Redis Agent Memory extracts new memories for the type. |\n| **Custom fields** | Structured fields added to memories of this type. |\n\nCustom fields support `str`, `int`, `float`, `bool`, `list[str]`, `list[float]`, and `object`. Each field has a name and description that explain what it captures. Every custom memory also includes the built-in [long-term memory fields](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/long-term-memory#memory-fields).\n\nFor example, a travel application can define a `trip_preference` type with these fields:\n\n| Field | Type | Captures |\n|:------|:-----|:---------|\n| `destinations` | `list[str]` | Cities or countries the user plans to visit. |\n| `travel_period` | `str` | Dates or period of the trip. |\n| `dietary_requirements` | `list[str]` | Dietary requirements that affect recommendations. |\n| `food_preferences` | `list[str]` | Cuisines, flavors, or dining preferences. |\n\nWhen this type is enabled, Redis Agent Memory can extract a structured `trip_preference` memory from relevant session events. Each enabled custom type processes session events independently."
    },
    {
      "id": "extract-custom-memories",
      "title": "Extract custom memories",
      "role": "content",
      "text": "These examples continue from the [quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/quickstart). Load the same `.env` connection values and reuse its user ID. Expand an example to copy a complete program that connects for you.\n\nEach run repeats every write in the program. Session-event writes and memory creates with a new idempotency key add new records, so run cleanup or change the identifiers before rerunning.\n\n\nIn the Redis Cloud console, open your service’s **Configuration** tab, select **Edit**, and under **Memory types & extraction** select **Add type**. Configure and enable `trip_preference` with the fields in [Define a memory type](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/long-term-memory#define-a-memory-type). Use this extraction prompt:\n\n[code example]\n\nThe examples in this section require the registered type. Until it exists, requests that create a `trip_preference` memory return `400 Bad Request` with the message `memory type \"trip_preference\" is not registered on this store`.\n\nAfter enabling the type, add a travel message to a new session:\n\n**Available in:** Python (Iris), cURL (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n\n\nSearch for the structured memory. The examples search every 10 seconds until extraction creates one:\n\n**Available in:** Python (Iris), cURL (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n\n\nThe `items` array contains records with `memoryType` set to `trip_preference`. Custom fields are inside each record's `attributes` object. For example, a result can include this excerpt:\n\n[code example]\n\nThe values depend on the conversation and extraction model. An application can use `dietary_requirements` to constrain restaurant recommendations, while `food_preferences` helps rank suitable choices. Check that each field is present and has the expected type before using it.\n\nInspect the structured fields. The examples collect dietary requirements for the next agent turn:\n\n**Available in:** Python (Iris), cURL (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n\n\nIf results remain empty, check that the type is enabled and that the owner ID matches the stored records. An empty result does not mean the user has no dietary requirements."
    },
    {
      "id": "create-a-custom-memory-directly",
      "title": "Create a custom memory directly",
      "role": "content",
      "text": "If your application already has structured trip data, write it directly using the registered `trip_preference` type. This example represents data from a form and uses the same user identifier:\n\n**Available in:** Python (Iris), cURL (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n\n\nInspect the response's `errors` array for per-record errors. Run the custom-memory search again to retrieve the record. Direct creation does not wait for background extraction and does not apply the extraction prompt or sensitive-data exclusions. Creating a record whose `memoryId` already exists fails for that record and leaves the stored memory unchanged; use [`PUT`](#update-a-memory) to replace it. Access checks and protection for document-managed records still apply.\n\nSee [custom memory types](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#custom-memory-types) for configuration requirements and limits."
    },
    {
      "id": "test-semantic-exclusions",
      "title": "Test semantic exclusions",
      "role": "content",
      "text": "These examples continue from the [quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/quickstart). Load the same `.env` connection values and reuse its user ID. Expand an example to copy a complete program that connects for you.\n\nEach run repeats every write in the program. Session-event writes and memory creates with a new idempotency key add new records, so run cleanup or change the identifiers before rerunning.\n\n\nIn the Redis Cloud console, open your service’s **Configuration** tab and select **Edit**. Under **Sensitive-data exclusions**, enable **Semantic exclusions** and set this prompt:\n\n[code example]\n\nThe semantic exclusion prompt tells Redis Agent Memory which information should not be kept in long-term memory. Add an event containing a fictional booking code and information that is safe to retain:\n\n**Available in:** Python (Iris), cURL (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n\n\nSearch for the safe hotel information. The examples search every 10 seconds until a memory about the hotel appears:\n\n**Available in:** Python (Iris), cURL (Iris)\n\n##### Python (Iris)\n\n[code example]\n\n##### cURL (Iris)\n\n[code example]\n\n\n\n> [!NOTE]\n> **What to expect:** A memory similar to `User booked Hotel Sakura in Tokyo` without the fictional confirmation code `DEMO-7QX9`. If the code appears, refine the exclusion prompt and test again.\n\n> [!WARNING]\n> Semantic exclusions are advisory and do not guarantee that sensitive information is excluded. Session content still reaches the extraction model provider. Do not use real sensitive data in this exercise. Exclusions do not apply to directly created long-term memories.\n\nSee [sensitive-data exclusions](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#sensitive-data-exclusions) for configuration details."
    }
  ],
  "examples": [
    {
      "id": "create-long-term-memories-ex0",
      "language": "python",
      "code": "seeded = [\n    memory.remember(\"The user is vegetarian.\", owner=OWNER_ID, topics=[\"food\"]),\n    memory.remember(\"The user prefers spicy food.\", owner=OWNER_ID, topics=[\"food\"]),\n    memory.remember(\"The user prefers a window seat.\", owner=OWNER_ID, topics=[\"travel\"]),\n]\nfor created in seeded:\n    print(\"Stored\", created.id, created.text)",
      "section_id": "create-long-term-memories"
    },
    {
      "id": "create-long-term-memories-ex1",
      "language": "bash",
      "code": "jq -n --arg owner \"$OWNER_ID\" --arg key \"$(uuidgen)\" '{\n  idempotencyKey: $key,\n  memories: [\n    {memoryId: \"example-diet\", text: \"The user is vegetarian.\", ownerId: $owner, topics: [\"food\"]},\n    {memoryId: \"example-cuisine\", text: \"The user prefers spicy food.\", ownerId: $owner, topics: [\"food\"]},\n    {memoryId: \"example-seat\", text: \"The user prefers a window seat.\", ownerId: $owner, topics: [\"travel\"]}\n  ]\n}' > memories.json\ncurl --fail-with-body --silent --show-error \\\n  -X POST \"$MEMORY_API/memories\" \\\n  -H \"$AUTH\" -H 'Content-Type: application/json' --data-binary @memories.json | jq .",
      "section_id": "create-long-term-memories"
    },
    {
      "id": "search-by-meaning-ex0",
      "language": "python",
      "code": "known = memory.recall(\n    \"What dietary requirements and food preferences does the user have?\",\n    owner=OWNER_ID,\n    limit=5,\n)\nprint(known.as_prompt())",
      "section_id": "search-by-meaning"
    },
    {
      "id": "search-by-meaning-ex1",
      "language": "bash",
      "code": "jq -n --arg owner \"$OWNER_ID\" '{\n  query: \"What dietary requirements and food preferences does the user have?\",\n  filter: {ownerId: {eq: $owner}},\n  limit: 5\n}' > search.json\ncurl --fail-with-body --silent --show-error \\\n  -X POST \"$MEMORY_API/memories/search\" \\\n  -H \"$AUTH\" -H 'Content-Type: application/json' --data-binary @search.json \\\n  | jq '.items[] | {memoryId, text, memoryType}'",
      "section_id": "search-by-meaning"
    },
    {
      "id": "search-by-meaning-ex2",
      "language": "bash",
      "code": "set -euo pipefail\n\niris agent-memory memories search \\\n  \"What dietary requirements and food preferences does the user have?\" \\\n  --owner quickstart-user --limit 5",
      "section_id": "search-by-meaning"
    },
    {
      "id": "filter-and-sort-without-semantic-search-ex0",
      "language": "python",
      "code": "recent = memory.client.search_long_term_memory(request={\n    \"filter_\": {\"owner_id\": {\"eq\": OWNER_ID}},\n    \"sort_by\": \"updatedAt\",\n    \"sort_order\": \"DESC\",\n    \"limit\": 10,\n})\nfor record in recent.items:\n    print(record.updated_at, record.memory_id, record.text)",
      "section_id": "filter-and-sort-without-semantic-search"
    },
    {
      "id": "filter-and-sort-without-semantic-search-ex1",
      "language": "bash",
      "code": "jq -n --arg owner \"$OWNER_ID\" '{\n  filter: {ownerId: {eq: $owner}},\n  sortBy: \"updatedAt\",\n  sortOrder: \"DESC\",\n  limit: 10\n}' > recent.json\ncurl --fail-with-body --silent --show-error \\\n  -X POST \"$MEMORY_API/memories/search\" \\\n  -H \"$AUTH\" -H 'Content-Type: application/json' --data-binary @recent.json \\\n  | jq '.items[] | {updatedAt, memoryId, text}'",
      "section_id": "filter-and-sort-without-semantic-search"
    },
    {
      "id": "filter-and-sort-without-semantic-search-ex2",
      "language": "json",
      "code": "{\n  \"filter\": {\n    \"op\": \"ALL\",\n    \"ownerId\": {\"eq\": \"<user-id>\"},\n    \"memoryType\": {\"eq\": \"trip_preference\"},\n    \"attributes\": {\"dietary_requirements\": {\"list\": [\"vegetarian\"]}}\n  },\n  \"limit\": 10\n}",
      "section_id": "filter-and-sort-without-semantic-search"
    },
    {
      "id": "update-a-memory-ex0",
      "language": "python",
      "code": "current = memory.client.get_long_term_memory(memory_id=MEMORY_ID)\nprint(current.text, current.topics, current.updated_at)",
      "section_id": "update-a-memory"
    },
    {
      "id": "update-a-memory-ex1",
      "language": "bash",
      "code": "curl --fail-with-body --silent --show-error \\\n  \"$MEMORY_API/memories/$MEMORY_ID\" -H \"$AUTH\" > current.json\njq '{text, topics, revision}' current.json\nREVISION=$(jq -r .revision current.json)",
      "section_id": "update-a-memory"
    },
    {
      "id": "update-a-memory-ex2",
      "language": "python",
      "code": "updated = memory.client.update_long_term_memory(\n    memory_id=MEMORY_ID,\n    text=\"The user prefers an aisle seat on long flights.\",\n    topics=[\"travel\", \"flights\"],\n)\nprint(updated.text, updated.topics, updated.updated_at)",
      "section_id": "update-a-memory"
    },
    {
      "id": "update-a-memory-ex3",
      "language": "bash",
      "code": "jq -n --arg revision \"$REVISION\" --arg key \"$(uuidgen)\" '{\n  text: \"The user prefers an aisle seat on long flights.\",\n  topics: [\"travel\", \"flights\"],\n  expectedRevision: $revision,\n  idempotencyKey: $key\n}' > patch.json\ncurl --fail-with-body --silent --show-error \\\n  -X PATCH \"$MEMORY_API/memories/$MEMORY_ID\" \\\n  -H \"$AUTH\" -H 'Content-Type: application/json' --data-binary @patch.json > updated.json\njq '{text, topics, revision}' updated.json",
      "section_id": "update-a-memory"
    },
    {
      "id": "update-a-memory-ex4",
      "language": "bash",
      "code": "jq -n --arg owner \"$OWNER_ID\" --arg revision \"$(jq -r .revision updated.json)\" --arg key \"$(uuidgen)\" '{\n  text: \"The user prefers an aisle seat and travels with a carry-on bag only.\",\n  ownerId: $owner,\n  topics: [\"travel\", \"flights\"],\n  expectedRevision: $revision,\n  idempotencyKey: $key\n}' > replace.json\ncurl --fail-with-body --silent --show-error \\\n  -X PUT \"$MEMORY_API/memories/$MEMORY_ID\" \\\n  -H \"$AUTH\" -H 'Content-Type: application/json' --data-binary @replace.json | jq .",
      "section_id": "update-a-memory"
    },
    {
      "id": "prevent-lost-updates-ex0",
      "language": "bash",
      "code": "jq -n --arg owner \"$OWNER_ID\" --arg revision \"$REVISION\" '{\n  text: \"The user prefers a window seat.\",\n  ownerId: $owner,\n  expectedRevision: $revision\n}' > stale.json\nSTATUS=$(curl --silent --show-error -o conflict.json -w '%{http_code}' \\\n  -X PUT \"$MEMORY_API/memories/$MEMORY_ID\" \\\n  -H \"$AUTH\" -H 'Content-Type: application/json' --data-binary @stale.json)\necho \"HTTP $STATUS\"\njq '{status, type, detail}' conflict.json",
      "section_id": "prevent-lost-updates"
    },
    {
      "id": "delete-memories-ex0",
      "language": "python",
      "code": "memory.delete(created.id for created in seeded)\nprint(\"Deleted\", len(seeded), \"memories\")",
      "section_id": "delete-memories"
    },
    {
      "id": "delete-memories-ex1",
      "language": "bash",
      "code": "curl --fail-with-body --silent --show-error \\\n  -X POST \"$MEMORY_API/memories/bulk-delete\" \\\n  -H \"$AUTH\" -H 'Content-Type: application/json' \\\n  --data '{\"memoryIds\": [\"example-diet\", \"example-cuisine\", \"example-seat\"]}' | jq .",
      "section_id": "delete-memories"
    },
    {
      "id": "extract-custom-memories-ex0",
      "language": "text",
      "code": "Extract trip requirements only when the user states a destination or travel plan. Preserve explicit dietary requirements and food preferences.",
      "section_id": "extract-custom-memories"
    },
    {
      "id": "extract-custom-memories-ex1",
      "language": "python",
      "code": "conversation = memory.conversation(owner=OWNER_ID, session_id=\"example-trip-preference-session\")\nsaved = conversation.user(\"I am visiting Tokyo and Kyoto next month. I am vegetarian.\")\nprint(\"Saved event\", saved.event_id)",
      "section_id": "extract-custom-memories"
    },
    {
      "id": "extract-custom-memories-ex2",
      "language": "bash",
      "code": "jq -n --arg owner \"$OWNER_ID\" --arg now \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" --arg key \"$(uuidgen)\" '{\n  actorId: $owner,\n  role: \"USER\",\n  content: [{text: \"I am visiting Tokyo and Kyoto next month. I am vegetarian.\"}],\n  createdAt: $now,\n  idempotencyKey: $key\n}' > event.json\ncurl --fail-with-body --silent --show-error \\\n  -X POST \"$MEMORY_API/sessions/example-trip-preference-session/events\" \\\n  -H \"$AUTH\" -H 'Content-Type: application/json' --data-binary @event.json | jq .",
      "section_id": "extract-custom-memories"
    },
    {
      "id": "extract-custom-memories-ex3",
      "language": "python",
      "code": "search = {\n    \"query\": \"What are the requirements for the user's trip?\",\n    \"filter_\": {\n        \"owner_id\": {\"eq\": OWNER_ID},\n        \"memory_type\": {\"eq\": \"trip_preference\"},\n    },\n    \"limit\": 5,\n}\nfor _ in range(30):  # extraction runs in the background\n    custom_results = memory.client.search_long_term_memory(request=search)\n    if custom_results.items:\n        break\n    time.sleep(10)\nfor record in custom_results.items:\n    print(record.memory_id, record.memory_type, record.attributes)",
      "section_id": "extract-custom-memories"
    },
    {
      "id": "extract-custom-memories-ex4",
      "language": "bash",
      "code": "jq -n --arg owner \"$OWNER_ID\" --arg query \"What are the requirements for the user's trip?\" '{\n  query: $query,\n  filter: {ownerId: {eq: $owner}, memoryType: {eq: \"trip_preference\"}},\n  limit: 5\n}' > search.json\nfor _ in $(seq 30); do  # extraction runs in the background\n  RESULTS=$(curl --fail-with-body --silent --show-error \\\n    -X POST \"$MEMORY_API/memories/search\" \\\n    -H \"$AUTH\" -H 'Content-Type: application/json' --data-binary @search.json)\n  [ \"$(jq '.items | length' <<< \"$RESULTS\")\" -gt 0 ] && break\n  sleep 10\ndone\njq '.items[] | {memoryId, memoryType, attributes}' <<< \"$RESULTS\"",
      "section_id": "extract-custom-memories"
    },
    {
      "id": "extract-custom-memories-ex5",
      "language": "json",
      "code": "{\n  \"memoryType\": \"trip_preference\",\n  \"attributes\": {\n    \"destinations\": [\"Tokyo\", \"Kyoto\"],\n    \"travel_period\": \"next month\",\n    \"dietary_requirements\": [\"vegetarian\"],\n    \"food_preferences\": [\"spicy food\"]\n  }\n}",
      "section_id": "extract-custom-memories"
    },
    {
      "id": "extract-custom-memories-ex6",
      "language": "python",
      "code": "dietary_requirements = set()\nif not custom_results.items:\n    print(\"No matching trip preferences yet. Retry after a short wait.\")\nfor record in custom_results.items:\n    attributes = record.attributes or {}\n    requirements = attributes.get(\"dietary_requirements\", [])\n    if isinstance(requirements, list):\n        dietary_requirements.update(\n            value for value in requirements if isinstance(value, str)\n        )\nprint(\"Restaurant requirements:\", sorted(dietary_requirements))",
      "section_id": "extract-custom-memories"
    },
    {
      "id": "extract-custom-memories-ex7",
      "language": "bash",
      "code": "jq -r '[.items[].attributes.dietary_requirements // empty | .[] | strings] | unique\n  | if length == 0 then \"No matching trip preferences yet. Retry after a short wait.\"\n    else \"Restaurant requirements: \\(join(\", \"))\" end' <<< \"$RESULTS\"",
      "section_id": "extract-custom-memories"
    },
    {
      "id": "create-a-custom-memory-directly-ex0",
      "language": "python",
      "code": "direct_result = memory.client.bulk_create_long_term_memories(memories=[{\n    \"memory_id\": \"example-trip-form-1\",\n    \"text\": \"The user plans to visit Tokyo and Kyoto next month and requires vegetarian food.\",\n    \"owner_id\": OWNER_ID,\n    \"memory_type\": \"trip_preference\",\n    \"attributes\": {\n        \"destinations\": [\"Tokyo\", \"Kyoto\"],\n        \"travel_period\": \"next month\",\n        \"dietary_requirements\": [\"vegetarian\"],\n        \"food_preferences\": [\"spicy food\"],\n    },\n}])\nprint(\"Created:\", direct_result.created)\nfor error in direct_result.errors or []:\n    print(\"Not created:\", error)",
      "section_id": "create-a-custom-memory-directly"
    },
    {
      "id": "create-a-custom-memory-directly-ex1",
      "language": "bash",
      "code": "jq -n --arg owner \"$OWNER_ID\" --arg key \"$(uuidgen)\" '{\n  idempotencyKey: $key,\n  memories: [{\n    memoryId: \"example-trip-form-1\",\n    text: \"The user plans to visit Tokyo and Kyoto next month and requires vegetarian food.\",\n    ownerId: $owner,\n    memoryType: \"trip_preference\",\n    attributes: {\n      destinations: [\"Tokyo\", \"Kyoto\"],\n      travel_period: \"next month\",\n      dietary_requirements: [\"vegetarian\"],\n      food_preferences: [\"spicy food\"]\n    }\n  }]\n}' > direct.json\ncurl --fail-with-body --silent --show-error \\\n  -X POST \"$MEMORY_API/memories\" \\\n  -H \"$AUTH\" -H 'Content-Type: application/json' --data-binary @direct.json | jq .",
      "section_id": "create-a-custom-memory-directly"
    },
    {
      "id": "test-semantic-exclusions-ex0",
      "language": "text",
      "code": "Do not keep passwords, access tokens, recovery codes, payment card information, or booking confirmation codes in long-term memory.",
      "section_id": "test-semantic-exclusions"
    },
    {
      "id": "test-semantic-exclusions-ex1",
      "language": "python",
      "code": "conversation = memory.conversation(owner=OWNER_ID, session_id=SESSION_ID)\nsaved = conversation.user(\n    \"I booked Hotel Sakura in Tokyo. For this example, \"\n    \"the fictional booking confirmation code is DEMO-7QX9.\"\n)\nprint(\"Saved event\", saved.event_id)",
      "section_id": "test-semantic-exclusions"
    },
    {
      "id": "test-semantic-exclusions-ex2",
      "language": "bash",
      "code": "jq -n --arg owner \"$OWNER_ID\" --arg now \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" --arg key \"$(uuidgen)\" '{\n  actorId: $owner,\n  role: \"USER\",\n  content: [{text: \"I booked Hotel Sakura in Tokyo. For this example, the fictional booking confirmation code is DEMO-7QX9.\"}],\n  createdAt: $now,\n  idempotencyKey: $key\n}' > event.json\ncurl --fail-with-body --silent --show-error \\\n  -X POST \"$MEMORY_API/sessions/$SESSION_ID/events\" \\\n  -H \"$AUTH\" -H 'Content-Type: application/json' --data-binary @event.json | jq .",
      "section_id": "test-semantic-exclusions"
    },
    {
      "id": "test-semantic-exclusions-ex3",
      "language": "python",
      "code": "question = \"Where is the user staying in Tokyo?\"\nfor _ in range(30):  # extraction runs in the background\n    known = memory.recall(question, owner=OWNER_ID)\n    if any(\"Sakura\" in found.text for found in known):\n        break\n    time.sleep(10)\nprint(known.as_prompt())",
      "section_id": "test-semantic-exclusions"
    },
    {
      "id": "test-semantic-exclusions-ex4",
      "language": "bash",
      "code": "jq -n --arg owner \"$OWNER_ID\" '{\n  query: \"Where is the user staying in Tokyo?\",\n  filter: {ownerId: {eq: $owner}},\n  limit: 5\n}' > search.json\nfor _ in $(seq 30); do  # extraction runs in the background\n  RESULTS=$(curl --fail-with-body --silent --show-error \\\n    -X POST \"$MEMORY_API/memories/search\" \\\n    -H \"$AUTH\" -H 'Content-Type: application/json' --data-binary @search.json)\n  jq -e 'any(.items[]; .text | contains(\"Sakura\"))' <<< \"$RESULTS\" > /dev/null && break\n  sleep 10\ndone\njq '.items[] | {memoryId, text}' <<< \"$RESULTS\"",
      "section_id": "test-semantic-exclusions"
    }
  ]
}
