{
  "schema_version": 2,
  "id": "develop/ai/context-engine/agent-memory/summary-views",
  "title": "Summarize long-term memories with Summary Views",
  "url": "https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/summary-views/",
  "summary": "Create owned summaries of long-term memories, refresh them, and read text or structured results.",
  "tags": [
    "docs",
    "develop",
    "ai"
  ],
  "last_updated": "2026-11-03T10:02:56-08:00",
  "page_type": "content",
  "content_hash": "eb566626d17a4e4170bc81ee4d2203b5851e22410097d4d089effb699aa60f6f",
  "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\nA Summary View turns selected long-term memories into a reusable text or structured summary. For example, a travel assistant can maintain a compact profile instead of adding every matching memory to each model request.\n\nA **definition** specifies the sources, filters, grouping, output, and refresh policy. A **partition** is one materialized result for an exact combination of grouping values. An ungrouped definition has one result. Results are derived data; they do not enter long-term-memory search or replace the source records.\n\nSummary Views differ from [session summarization](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/sessions#configure-automatic-summarization), which compacts conversation events. The supported source is `LONG_TERM`. Document-managed MemoryETL records are excluded from Summary View sources."
    },
    {
      "id": "prepare-the-service-and-identity",
      "title": "Prepare the service and identity",
      "role": "content",
      "text": "Summary Views are available in Redis Cloud and enabled in self-managed deployments. For self-managed model and capacity settings, see [Configure Summary Views](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/configuration#enable-summary-views).\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\nThe server assigns the definition's immutable `ownerId` from the caller's authenticated user ID. A create request cannot supply an owner field.\n\nIn on-prem deployments with ownership enforcement enabled, creating a view requires an authenticated user ID, including when an agent key is used. Use [delegated user identity](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/authentication#delegate-a-user-with-an-agent-key) and send the user token on every Summary View request: pass `http_headers={\"X-Iris-User-Token\": user_token}` to each Python call, or add `-H \"X-Iris-User-Token: $USER_TOKEN\"` to each cURL request. The verified user becomes the owner, so filter sources by that user's ID.\n\nIn on-prem deployments with `ownership.enabled: false`, agent keys without a user can create, refresh, and read views of either visibility. `ownerId` is empty when the caller has no authenticated user ID."
    },
    {
      "id": "choose-private-or-shared-visibility",
      "title": "Choose private or shared visibility",
      "role": "content",
      "text": "In on-prem deployments with ownership enforcement enabled:\n\n| Visibility | Who can read | Sources |\n|:-----------|:-------------|:--------|\n| `PRIVATE` (default) | The definition owner. | The owner's personal and store-authorized shared memories, narrowed by the definition's filters. |\n| `SHARED` | Callers with store read access. The owner retains lifecycle control. | Shared memories only. Personal records cannot be published through a shared view. |\n\nShared does not mean a particular team. Enforce team membership in your backend or use separate stores and grants.\n\nIn on-prem deployments with enforcement off, both visibilities use store-level access without ownership checks. `PRIVATE` does not restrict readers, and `SHARED` views are not restricted to shared memories. Source selection for either visibility does not check record ownership or namespace scope, including personal namespace filters. `PRIVATE` does not replace backend authorization. A view records its source-access policy at creation. Changing on-prem enforcement requires replacing existing views and configuring server and worker alike."
    },
    {
      "id": "create-refresh-and-read-a-view",
      "title": "Create, refresh, and read a view",
      "role": "content",
      "text": "These examples create one private profile for the quickstart user, request a full refresh, and poll its result. First create ordinary long-term memories for that user. For more ways to write them, see [Create a custom memory directly](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/long-term-memory#create-a-custom-memory-directly):\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\nCreate the view and save its ID for later requests:\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\nRequest a full refresh. Creation and refresh are asynchronous; their responses do not contain generated output:\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\nPoll the result until it is ready. A successful refresh with no matching sources returns `EMPTY`:\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\nText output uses `value.text`; structured output uses `value.data`. Python output configuration uses `format_` and `schema_`, serialized as `format` and `schema`. Do not assume every result has a `value`."
    },
    {
      "id": "handle-asynchronous-result-states",
      "title": "Handle asynchronous result states",
      "role": "content",
      "text": "| Status | Application behavior |\n|:-------|:---------------------|\n| `COLD` | No result is stored for this group. It may not have been generated yet, or it may have been evicted. |\n| `PENDING` | Work is queued. Previous output may be withheld because its sources changed or became inaccessible. |\n| `READY` | A value is available. Check `stale` and coverage fields. |\n| `EMPTY` | No sources match. There is no generated value. |\n| `REFRESHING` | New work is running. A last good value can remain. |\n| `FAILED` | Inspect `errorCode`. A last good value may remain, or `value` may be absent. |\n\n`stale` is independent of status. It compares the result with the current definition and source changes. Decide whether to use a last good stale value, wait, or fall back to direct recall. Do not turn an absent value into an empty summary.\n\nResult reads validate contributing source records. Deleted, changed, or newly inaccessible sources can cause old output to be withheld and repair to be requested. Repair is asynchronous; an old value need not remain available."
    },
    {
      "id": "filter-and-group-sources",
      "title": "Filter and group sources",
      "role": "content",
      "text": "Use `filter` for owners, sessions, stable namespace IDs, topics, memory types, creation times, and typed attributes. `filter.op` defaults to `ALL`; `ANY` combines optional filters with OR. Exact partition scope and time-window restrictions apply separately, as do ownership checks in on-prem deployments. Legacy `namespace` labels are unsupported for Summary Views; use `namespaceId`.\n\n`groupBy` accepts up to four distinct fields: `ownerId`, `sessionId`, `namespaceId`, and `memoryType`. A result read or `target: PARTITION` refresh must supply exactly those fields. Missing or extra scope fields are invalid. `groupBy: []` creates a singleton; omit all scope fields when reading it.\n\nFor `groupBy: [\"ownerId\", \"namespaceId\"]`, use this partition refresh body:\n\n[code example]\n\nSend the same scope as query parameters on result reads. `target: FULL` reconciles current and previous partitions.\n\n`evictionPolicy.maxPartitions` bounds grouped results within the deployment limit. Grouped views use the operator's partition limit when the policy is omitted; singleton views omit it. Matching source mutations make a scope recent; reads do not. Evicted scopes can return `COLD`. `timeWindowSeconds` selects sources by creation time in a rolling window; it does not change source retention."
    },
    {
      "id": "choose-structured-output-and-continuous-refresh",
      "title": "Choose structured output and continuous refresh",
      "role": "content",
      "text": "For structured output, replace `output` with:\n\n[code example]\n\nThe schema accepts 1–50 fields of type `STRING`, `INTEGER`, `FLOAT`, `BOOLEAN`, `STRING_LIST`, `FLOAT_LIST`, or `OBJECT`. Generated fields are optional. Arbitrary JSON Schema, required-field declarations, and nested object schemas are unsupported. Text output must omit `schema`; structured output requires it. Invalid generated data is reported as `OUTPUT_VALIDATION_FAILED`.\n\nFor continuous refresh, use:\n\n[code example]\n\nThe reconcile interval is required and must fit deployment limits. Debounce coalesces mutations; reconciliation also handles changes such as a moving time window. Manual mode omits both interval fields. Both modes run asynchronously within queue, source, generation, and concurrency limits."
    },
    {
      "id": "check-coverage-and-manage-definitions",
      "title": "Check coverage and manage definitions",
      "role": "content",
      "text": "Inspect `sourceMemoryCount` and `summarizedMemoryCount`. Selection can omit whole records (`sourcesOmitted`) or clip included content (`sourceContentTruncated`). `truncated` combines both. Check `truncated` even when every record was included: some text may have been cut. Concurrent writes can change the source set while counts are measured.\n\nRequest `includeSourceMemoryIds=true` for the selected source IDs. These identify contributing records, not every match. `computedAt`, `sourceUpdatedAt`, and `definitionVersion` help assess freshness.\n\n| Action | Python method | REST |\n|:-------|:--------------|:-----|\n| List definitions | `memory.client.list_summary_views` | `GET /summary-views` |\n| Read a definition | `memory.client.get_summary_view` | `GET /summary-views/{viewId}` |\n| Update a definition | `memory.client.update_summary_view` | `PATCH /summary-views/{viewId}` |\n| Delete a definition | `memory.client.delete_summary_view` | `DELETE /summary-views/{viewId}` |\n\nREST paths are relative to `/iris/data/v1/agent-memory/stores/{storeId}`. Lists return `items` and optional `nextCursor`; pass it as `cursor` with the same query. Definition states (`ACTIVE`, `DISABLED`, `DELETING`) are separate from result status. Each Python method also has an `_async` variant.\n\nThis example updates the description, which does not trigger generation, and lists the store's definitions:\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| Change | Effect |\n|:-------|:-------|\n| Name or description | Does not trigger generation. |\n| Filter, output, or refresh policy | Increments `definitionVersion`. |\n| `groupBy` | Increments `definitionVersion` and replaces the partition layout. |\n| Owner, visibility, or source | Cannot be changed. Create a new definition. |\n| `state: DISABLED` | Disables the view. Re-enabling requests refresh work. |\n\n`clearFilter: true` and `clearTimeWindow: true` remove those settings; do not also supply replacements.\n\nDeletion returns `202` while cleanup runs. Repeated deletion is idempotent; the definition becomes `DELETING` before schedules and results are removed. Source memories remain.\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\nFor quota failures, inspect the API error and deployment limits. For generation or source-access failures, keep the view ID and address the reported error instead of creating duplicates. [Ingestion webhooks](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/ingestion-webhooks) can reject generated content before publication."
    }
  ],
  "examples": [
    {
      "id": "create-refresh-and-read-a-view-ex0",
      "language": "python",
      "code": "memory.remember(\"The user is vegetarian and prefers spicy food.\", owner=OWNER_ID)\nmemory.remember(\"The user wants to visit Tokyo and Kyoto next month.\", owner=OWNER_ID)",
      "section_id": "create-refresh-and-read-a-view"
    },
    {
      "id": "create-refresh-and-read-a-view-ex1",
      "language": "bash",
      "code": "jq -n --arg owner \"$OWNER_ID\" --arg key \"$(uuidgen)\" '{\n  memories: [\n    {memoryId: \"example-diet\", ownerId: $owner, text: \"The user is vegetarian and prefers spicy food.\"},\n    {memoryId: \"example-trip\", ownerId: $owner, text: \"The user wants to visit Tokyo and Kyoto next month.\"}\n  ],\n  idempotencyKey: $key\n}' > memories.json\ncurl --fail-with-body --silent --show-error \\\n  -X POST \"$MEMORY_API/memories\" \\\n  -H \"Authorization: Bearer $IRIS_AGENT_MEMORY_API_KEY\" \\\n  -H 'Content-Type: application/json' --data-binary @memories.json | jq .",
      "section_id": "create-refresh-and-read-a-view"
    },
    {
      "id": "create-refresh-and-read-a-view-ex2",
      "language": "python",
      "code": "view = memory.client.create_summary_view(\n    name=\"example-travel-profile\",\n    source=models.SummaryViewSource.LONG_TERM,\n    visibility=models.SummaryViewVisibility.PRIVATE,\n    group_by=[],\n    filter_={\"owner_id\": {\"eq\": OWNER_ID}},\n    output={\n        \"format_\": \"TEXT\",\n        \"instructions\": \"Summarize the travel preferences in these memories.\",\n    },\n    refresh_policy={\"mode\": \"MANUAL\"},\n)\nview_id = view.view_id\nprint(\"View ID:\", view_id, \"owner:\", repr(view.owner_id))",
      "section_id": "create-refresh-and-read-a-view"
    },
    {
      "id": "create-refresh-and-read-a-view-ex3",
      "language": "bash",
      "code": "jq -n --arg owner \"$OWNER_ID\" '{\n  name: \"example-travel-profile\",\n  source: \"LONG_TERM\",\n  visibility: \"PRIVATE\",\n  groupBy: [],\n  filter: {ownerId: {eq: $owner}},\n  output: {format: \"TEXT\", instructions: \"Summarize the travel preferences in these memories.\"},\n  refreshPolicy: {mode: \"MANUAL\"}\n}' > view.json\nVIEW_ID=$(curl --fail-with-body --silent --show-error \\\n  -X POST \"$MEMORY_API/summary-views\" \\\n  -H \"Authorization: Bearer $IRIS_AGENT_MEMORY_API_KEY\" \\\n  -H 'Content-Type: application/json' --data-binary @view.json | jq -er .viewId)\necho \"View ID: $VIEW_ID\"",
      "section_id": "create-refresh-and-read-a-view"
    },
    {
      "id": "create-refresh-and-read-a-view-ex4",
      "language": "python",
      "code": "accepted = memory.client.refresh_summary_view(\n    view_id=view_id, target=models.SummaryViewRefreshTarget.FULL,\n)\nprint(\"Refresh accepted:\", accepted.accepted)",
      "section_id": "create-refresh-and-read-a-view"
    },
    {
      "id": "create-refresh-and-read-a-view-ex5",
      "language": "bash",
      "code": "curl --fail-with-body --silent --show-error \\\n  -X POST \"$MEMORY_API/summary-views/$VIEW_ID/refresh\" \\\n  -H \"Authorization: Bearer $IRIS_AGENT_MEMORY_API_KEY\" \\\n  -H 'Content-Type: application/json' --data '{\"target\": \"FULL\"}' | jq .",
      "section_id": "create-refresh-and-read-a-view"
    },
    {
      "id": "create-refresh-and-read-a-view-ex6",
      "language": "python",
      "code": "Status = models.SummaryViewResultStatus\nfor _ in range(60):  # generation runs in the background\n    result = memory.client.get_summary_view_result(\n        view_id=view_id, include_source_memory_ids=True,\n    )\n    if result.status == Status.FAILED:\n        raise RuntimeError(f\"Refresh failed: {result.error_code}\")\n    if result.status == Status.EMPTY:\n        print(\"No matching source memories.\")\n        break\n    if result.status == Status.READY and not result.stale and result.value is not None:\n        print(result.value.text)\n        print(f\"{result.summarized_memory_count} of {result.source_memory_count} memories,\",\n              \"truncated:\", result.truncated)\n        break\n    time.sleep(5)\nelse:\n    raise TimeoutError(f\"Continue polling view {view_id}\")",
      "section_id": "create-refresh-and-read-a-view"
    },
    {
      "id": "create-refresh-and-read-a-view-ex7",
      "language": "bash",
      "code": "for _ in $(seq 60); do  # generation runs in the background\n  RESULT=$(curl --fail-with-body --silent --show-error --get \\\n    \"$MEMORY_API/summary-views/$VIEW_ID/result\" \\\n    -H \"Authorization: Bearer $IRIS_AGENT_MEMORY_API_KEY\" \\\n    --data-urlencode 'includeSourceMemoryIds=true')\n  case \"$(jq -r .status <<< \"$RESULT\")\" in\n    FAILED) jq . <<< \"$RESULT\"; exit 1 ;;\n    EMPTY) echo \"No matching source memories.\"; break ;;\n    READY) if jq -e '.stale == false and .value != null' <<< \"$RESULT\" > /dev/null; then\n        jq '{text: .value.text, sourceMemoryCount, summarizedMemoryCount, truncated}' <<< \"$RESULT\"\n        break\n      fi ;;\n  esac\n  sleep 5\ndone",
      "section_id": "create-refresh-and-read-a-view"
    },
    {
      "id": "filter-and-group-sources-ex0",
      "language": "json",
      "code": "{\n  \"target\": \"PARTITION\",\n  \"scope\": {\"ownerId\":\"<user-id>\",\"namespaceId\":\"<namespace-id>\"}\n}",
      "section_id": "filter-and-group-sources"
    },
    {
      "id": "choose-structured-output-and-continuous-refresh-ex0",
      "language": "json",
      "code": "{\n  \"format\": \"STRUCTURED\",\n  \"instructions\": \"Summarize only preferences supported by the selected memories.\",\n  \"schema\": {\n    \"fields\": [\n      {\"name\":\"cities\",\"type\":\"STRING_LIST\",\"description\":\"Cities the user wants to visit\"},\n      {\"name\":\"vegetarian\",\"type\":\"BOOLEAN\",\"description\":\"Whether the user has stated a vegetarian diet\"}\n    ]\n  }\n}",
      "section_id": "choose-structured-output-and-continuous-refresh"
    },
    {
      "id": "choose-structured-output-and-continuous-refresh-ex1",
      "language": "json",
      "code": "{\"mode\":\"CONTINUOUS\",\"debounceSeconds\":5,\"reconcileIntervalSeconds\":60}",
      "section_id": "choose-structured-output-and-continuous-refresh"
    },
    {
      "id": "check-coverage-and-manage-definitions-ex0",
      "language": "python",
      "code": "updated = memory.client.update_summary_view(\n    view_id=view_id, description=\"Travel preferences for trip planning.\",\n)\nprint(updated.name, \"version\", updated.definition_version)\nfor item in memory.client.list_summary_views(limit=20).items:\n    print(item.view_id, item.name, item.state)",
      "section_id": "check-coverage-and-manage-definitions"
    },
    {
      "id": "check-coverage-and-manage-definitions-ex1",
      "language": "bash",
      "code": "curl --fail-with-body --silent --show-error \\\n  -X PATCH \"$MEMORY_API/summary-views/$VIEW_ID\" \\\n  -H \"Authorization: Bearer $IRIS_AGENT_MEMORY_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"description\": \"Travel preferences for trip planning.\"}' | jq '{name, definitionVersion}'\ncurl --fail-with-body --silent --show-error --get \\\n  \"$MEMORY_API/summary-views\" \\\n  -H \"Authorization: Bearer $IRIS_AGENT_MEMORY_API_KEY\" \\\n  --data-urlencode 'limit=20' | jq '.items[] | {viewId, name, state}'",
      "section_id": "check-coverage-and-manage-definitions"
    },
    {
      "id": "check-coverage-and-manage-definitions-ex2",
      "language": "python",
      "code": "memory.client.delete_summary_view(view_id=view_id)",
      "section_id": "check-coverage-and-manage-definitions"
    },
    {
      "id": "check-coverage-and-manage-definitions-ex3",
      "language": "bash",
      "code": "curl --fail-with-body --silent --show-error \\\n  -X DELETE \"$MEMORY_API/summary-views/$VIEW_ID\" \\\n  -H \"Authorization: Bearer $IRIS_AGENT_MEMORY_API_KEY\" | jq .",
      "section_id": "check-coverage-and-manage-definitions"
    }
  ]
}
