From e35b15ca547c95fcea4af8795fca6ce53a377add Mon Sep 17 00:00:00 2001 From: Andrew Brookins Date: Mon, 14 Sep 2026 09:00:37 -0700 Subject: [PATCH 1/3] Show namespace placement and structured memory use --- .../ai/context-engine/agent-memory/_index.md | 6 +- .../api-reference/openapi-agent-memory.json | 1876 ++++++++++++++++- .../agent-memory/developer-guide.md | 92 +- .../agent-memory/python-sdk-quickstart.md | 99 +- .../agent-memory/rest-api-quickstart.md | 87 +- .../agent-memory/typescript-sdk-quickstart.md | 100 +- .../agent-memory/self-managed/api-examples.md | 28 +- 7 files changed, 2146 insertions(+), 142 deletions(-) diff --git a/content/develop/ai/context-engine/agent-memory/_index.md b/content/develop/ai/context-engine/agent-memory/_index.md index 153a0d221d..50f21de1d2 100644 --- a/content/develop/ai/context-engine/agent-memory/_index.md +++ b/content/develop/ai/context-engine/agent-memory/_index.md @@ -91,7 +91,7 @@ Agent: Good to know! I'll help you find some vegetarian-friendly restaurants in

Custom memory types

-

Stores domain-specific information in a custom trip_preference type with fields such as destination, travel_period, and dietary_requirement.

+

Stores domain-specific information in a custom trip_preference type with fields such as destinations, travel_period, and dietary_requirements.

@@ -114,7 +114,9 @@ Get started with Redis Agent Memory on Redis Cloud or join the private preview f ### Choose a quickstart -After your Redis Agent Memory service is ready, choose a client. Each quickstart follows the same travel planning scenario through session memory, automatic extraction, summarization, custom memory types, and sensitive-data exclusions. +After your Redis Agent Memory service is ready, choose a client. Each quickstart follows the same travel planning scenario through namespaces, session memory, automatic extraction, summarization, custom memory types, and sensitive-data exclusions. + +Use the developer guide to [organize memories with namespaces]({{< relref "/develop/ai/context-engine/agent-memory/developer-guide#organize-memories-with-namespaces" >}}) and [use custom memory types]({{< relref "/develop/ai/context-engine/agent-memory/developer-guide#define-custom-memory-types" >}}).
diff --git a/content/develop/ai/context-engine/agent-memory/api-reference/openapi-agent-memory.json b/content/develop/ai/context-engine/agent-memory/api-reference/openapi-agent-memory.json index bbab8e30d1..f14395130b 100644 --- a/content/develop/ai/context-engine/agent-memory/api-reference/openapi-agent-memory.json +++ b/content/develop/ai/context-engine/agent-memory/api-reference/openapi-agent-memory.json @@ -21,10 +21,7 @@ } } } - }, - "tags": [ - "Service Health" - ] + } } }, "/v1/stores/{storeId}/health": { @@ -117,6 +114,16 @@ } } }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, "424": { "description": "FailedDependencyError 424 response", "content": { @@ -147,10 +154,7 @@ } } } - }, - "tags": [ - "Store Health" - ] + } } }, "/v1/stores/{storeId}/long-term-memory": { @@ -253,6 +257,16 @@ } } }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, "424": { "description": "FailedDependencyError 424 response", "content": { @@ -283,10 +297,7 @@ } } } - }, - "tags": [ - "Long-Term Memory" - ] + } }, "post": { "description": "Creates long-term memories in bulk.", @@ -296,6 +307,18 @@ "application/json": { "schema": { "$ref": "#/components/schemas/BulkCreateLongTermMemoriesRequestContent" + }, + "example": { + "memories": [ + { + "id": "mem-1", + "text": "The user prefers dark mode", + "ownerId": "user-1", + "namespaceRef": { + "namespaceId": "ns-product" + } + } + ] } } }, @@ -387,6 +410,177 @@ } } }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, + "424": { + "description": "MemoryFailedDependencyError 424 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemoryFailedDependencyErrorResponseContent" + } + } + } + }, + "429": { + "description": "TooManyRequestsError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TooManyRequestsErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + } + } + }, + "/v1/stores/{storeId}/long-term-memory/move": { + "post": { + "description": "Moves long-term memories to one existing namespace resource. Returns 409 when the destination namespace is archived.", + "operationId": "MoveLongTermMemories", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MoveLongTermMemoriesRequestContent" + }, + "example": { + "memoryIds": [ + "mem-1" + ], + "namespaceRef": { + "namespaceId": "ns-product" + } + } + } + }, + "required": true + }, + "parameters": [ + { + "name": "storeId", + "in": "path", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated store IDs are typically 32-character UUIDs without dashes.", + "schema": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated store IDs are typically 32-character UUIDs without dashes." + }, + "required": true + } + ], + "responses": { + "200": { + "description": "MoveLongTermMemories 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MoveLongTermMemoriesResponseContent" + } + } + } + }, + "400": { + "description": "BadRequestError 400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequestErrorResponseContent" + } + } + } + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "404": { + "description": "NotFoundError 404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "408": { + "description": "TimeoutError 408 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TimeoutErrorResponseContent" + } + } + } + }, + "409": { + "description": "ConflictError 409 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConflictErrorResponseContent" + } + } + } + }, + "413": { + "description": "PayloadTooLargeError 413 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PayloadTooLargeErrorResponseContent" + } + } + } + }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, "424": { "description": "FailedDependencyError 424 response", "content": { @@ -417,10 +611,7 @@ } } } - }, - "tags": [ - "Long-Term Memory" - ] + } } }, "/v1/stores/{storeId}/long-term-memory/search": { @@ -432,6 +623,14 @@ "application/json": { "schema": { "$ref": "#/components/schemas/SearchLongTermMemoryRequestContent" + }, + "example": { + "text": "preferred theme", + "filter": { + "namespaceRef": { + "eq": "ns-product" + } + } } } } @@ -522,12 +721,22 @@ } } }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, "424": { - "description": "FailedDependencyError 424 response", + "description": "MemoryFailedDependencyError 424 response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FailedDependencyErrorResponseContent" + "$ref": "#/components/schemas/MemoryFailedDependencyErrorResponseContent" } } } @@ -552,10 +761,7 @@ } } } - }, - "tags": [ - "Long-Term Memory" - ] + } } }, "/v1/stores/{storeId}/long-term-memory/{memoryId}": { @@ -661,6 +867,16 @@ } } }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, "424": { "description": "FailedDependencyError 424 response", "content": { @@ -691,10 +907,7 @@ } } } - }, - "tags": [ - "Long-Term Memory" - ] + } }, "patch": { "description": "Partially updates a long-term memory by its ID.", @@ -807,12 +1020,22 @@ } } }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, "424": { - "description": "FailedDependencyError 424 response", + "description": "MemoryFailedDependencyError 424 response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FailedDependencyErrorResponseContent" + "$ref": "#/components/schemas/MemoryFailedDependencyErrorResponseContent" } } } @@ -837,10 +1060,7 @@ } } } - }, - "tags": [ - "Long-Term Memory" - ] + } } }, "/v1/stores/{storeId}/long-term-memory/{memoryId}/fields": { @@ -939,12 +1159,22 @@ } } }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, "424": { - "description": "FailedDependencyError 424 response", + "description": "MemoryFailedDependencyError 424 response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FailedDependencyErrorResponseContent" + "$ref": "#/components/schemas/MemoryFailedDependencyErrorResponseContent" } } } @@ -969,76 +1199,870 @@ } } } - }, - "tags": [ - "Long-Term Memory" - ] + } } }, - "/v1/stores/{storeId}/session-memory": { + "/v1/stores/{storeId}/namespaces": { "get": { - "description": "Returns a paginated list of session IDs for a store.", - "operationId": "ListSessions", + "description": "Lists namespace roots or direct children.", + "operationId": "ListNamespaces", "parameters": [ { "name": "storeId", "in": "path", - "description": "The store instance ID.", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated store IDs are typically 32-character UUIDs without dashes.", "schema": { "type": "string", "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "The store instance ID." + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated store IDs are typically 32-character UUIDs without dashes." }, "required": true }, { - "name": "limit", + "name": "parentId", "in": "query", - "description": "Maximum number of sessions to return. Defaults to 100. Allowed range: 1-1000.", + "description": "List direct children of this namespace. Omit to list roots.", "schema": { - "type": "integer", - "default": 100, - "maximum": 1000, - "minimum": 1, - "description": "Maximum number of sessions to return. Defaults to 100. Allowed range: 1-1000.", - "format": "int32" + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "List direct children of this namespace. Omit to list roots." } }, { - "name": "pageToken", + "name": "scope", "in": "query", - "description": "Opaque token from a previous response for the next page.", + "description": "Restrict root results to one scope. Cannot be combined with parentId.", "schema": { - "type": "string", - "description": "Opaque token from a previous response for the next page." + "$ref": "#/components/schemas/NamespaceScope" } }, { - "name": "filterOwnerId", + "name": "ownerId", "in": "query", - "description": "Filter sessions by owner. Matching is case-sensitive. Mutually exclusive with includeAll.", + "description": "Restrict personal root results to an owner. Cannot be combined with parentId; ignored for shared roots.", "schema": { "type": "string", "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "Filter sessions by owner. Matching is case-sensitive. Mutually exclusive with includeAll." + "description": "Restrict personal root results to an owner. Cannot be combined with parentId; ignored for shared roots." } }, { - "name": "includeAll", + "name": "limit", "in": "query", - "description": "Set to true to list all sessions. Required when no filter is given; mutually exclusive with filters.", + "description": "Maximum number of namespaces to return. Defaults to 100. Allowed range: 1-1000.", "schema": { - "type": "boolean", - "description": "Set to true to list all sessions. Required when no filter is given; mutually exclusive with filters." + "type": "integer", + "default": 100, + "maximum": 1000, + "minimum": 1, + "description": "Maximum number of namespaces to return. Defaults to 100. Allowed range: 1-1000.", + "format": "int32" } - } - ], - "responses": { - "200": { + }, + { + "name": "pageToken", + "in": "query", + "description": "Opaque token from a previous response for the next page.", + "schema": { + "type": "string", + "description": "Opaque token from a previous response for the next page." + } + } + ], + "responses": { + "200": { + "description": "ListNamespaces 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListNamespacesResponseContent" + } + } + } + }, + "400": { + "description": "BadRequestError 400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequestErrorResponseContent" + } + } + } + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "404": { + "description": "NotFoundError 404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "408": { + "description": "TimeoutError 408 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TimeoutErrorResponseContent" + } + } + } + }, + "413": { + "description": "PayloadTooLargeError 413 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PayloadTooLargeErrorResponseContent" + } + } + } + }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, + "424": { + "description": "FailedDependencyError 424 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FailedDependencyErrorResponseContent" + } + } + } + }, + "429": { + "description": "TooManyRequestsError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TooManyRequestsErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + } + }, + "post": { + "description": "Creates a namespace resource as a root, direct child, or explicit hierarchy path. Returns 409 when an active or archived namespace already occupies the requested location.", + "operationId": "CreateNamespace", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateNamespaceRequestContent" + }, + "example": { + "name": "product", + "scope": "SHARED" + } + } + } + }, + "parameters": [ + { + "name": "storeId", + "in": "path", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated store IDs are typically 32-character UUIDs without dashes.", + "schema": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated store IDs are typically 32-character UUIDs without dashes." + }, + "required": true + } + ], + "responses": { + "201": { + "description": "CreateNamespace 201 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateNamespaceResponseContent" + } + } + } + }, + "400": { + "description": "BadRequestError 400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequestErrorResponseContent" + } + } + } + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "404": { + "description": "NotFoundError 404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "408": { + "description": "TimeoutError 408 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TimeoutErrorResponseContent" + } + } + } + }, + "409": { + "description": "ConflictError 409 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConflictErrorResponseContent" + } + } + } + }, + "413": { + "description": "PayloadTooLargeError 413 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PayloadTooLargeErrorResponseContent" + } + } + } + }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, + "424": { + "description": "FailedDependencyError 424 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FailedDependencyErrorResponseContent" + } + } + } + }, + "429": { + "description": "TooManyRequestsError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TooManyRequestsErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + } + } + }, + "/v1/stores/{storeId}/namespaces/{namespaceId}": { + "delete": { + "description": "Deletes an empty leaf namespace. Returns 409 when the namespace has children or memory placements, or when it changed concurrently.", + "operationId": "DeleteNamespace", + "parameters": [ + { + "name": "storeId", + "in": "path", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated store IDs are typically 32-character UUIDs without dashes.", + "schema": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated store IDs are typically 32-character UUIDs without dashes." + }, + "required": true + }, + { + "name": "namespaceId", + "in": "path", + "description": "Server-generated namespace identifier.", + "schema": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Server-generated namespace identifier." + }, + "required": true + } + ], + "responses": { + "204": { + "description": "DeleteNamespace 204 response" + }, + "400": { + "description": "BadRequestError 400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequestErrorResponseContent" + } + } + } + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "404": { + "description": "NotFoundError 404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "408": { + "description": "TimeoutError 408 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TimeoutErrorResponseContent" + } + } + } + }, + "409": { + "description": "ConflictError 409 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConflictErrorResponseContent" + } + } + } + }, + "413": { + "description": "PayloadTooLargeError 413 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PayloadTooLargeErrorResponseContent" + } + } + } + }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, + "424": { + "description": "FailedDependencyError 424 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FailedDependencyErrorResponseContent" + } + } + } + }, + "429": { + "description": "TooManyRequestsError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TooManyRequestsErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + } + }, + "get": { + "description": "Gets one namespace by its canonical ID.", + "operationId": "GetNamespace", + "parameters": [ + { + "name": "storeId", + "in": "path", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated store IDs are typically 32-character UUIDs without dashes.", + "schema": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated store IDs are typically 32-character UUIDs without dashes." + }, + "required": true + }, + { + "name": "namespaceId", + "in": "path", + "description": "Server-generated namespace identifier.", + "schema": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Server-generated namespace identifier." + }, + "required": true + } + ], + "responses": { + "200": { + "description": "GetNamespace 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetNamespaceResponseContent" + } + } + } + }, + "400": { + "description": "BadRequestError 400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequestErrorResponseContent" + } + } + } + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "404": { + "description": "NotFoundError 404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "408": { + "description": "TimeoutError 408 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TimeoutErrorResponseContent" + } + } + } + }, + "413": { + "description": "PayloadTooLargeError 413 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PayloadTooLargeErrorResponseContent" + } + } + } + }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, + "424": { + "description": "FailedDependencyError 424 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FailedDependencyErrorResponseContent" + } + } + } + }, + "429": { + "description": "TooManyRequestsError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TooManyRequestsErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + } + }, + "patch": { + "description": "Renames or archives a namespace. Returns 409 when the namespace changed concurrently, an active or archived sibling already uses the requested name, or the namespace is archived.", + "operationId": "UpdateNamespace", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateNamespaceRequestContent" + } + } + } + }, + "parameters": [ + { + "name": "storeId", + "in": "path", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated store IDs are typically 32-character UUIDs without dashes.", + "schema": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Store identifier (1-64 chars, alphanumeric and dashes). Generated store IDs are typically 32-character UUIDs without dashes." + }, + "required": true + }, + { + "name": "namespaceId", + "in": "path", + "description": "Server-generated namespace identifier.", + "schema": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Server-generated namespace identifier." + }, + "required": true + } + ], + "responses": { + "200": { + "description": "UpdateNamespace 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateNamespaceResponseContent" + } + } + } + }, + "400": { + "description": "BadRequestError 400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequestErrorResponseContent" + } + } + } + }, + "401": { + "description": "AuthenticationError 401 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationErrorResponseContent" + } + } + } + }, + "403": { + "description": "ForbiddenError 403 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenErrorResponseContent" + } + } + } + }, + "404": { + "description": "NotFoundError 404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundErrorResponseContent" + } + } + } + }, + "408": { + "description": "TimeoutError 408 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TimeoutErrorResponseContent" + } + } + } + }, + "409": { + "description": "ConflictError 409 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConflictErrorResponseContent" + } + } + } + }, + "413": { + "description": "PayloadTooLargeError 413 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PayloadTooLargeErrorResponseContent" + } + } + } + }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, + "424": { + "description": "FailedDependencyError 424 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FailedDependencyErrorResponseContent" + } + } + } + }, + "429": { + "description": "TooManyRequestsError 429 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TooManyRequestsErrorResponseContent" + } + } + } + }, + "500": { + "description": "UnexpectedError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnexpectedErrorResponseContent" + } + } + } + } + } + } + }, + "/v1/stores/{storeId}/session-memory": { + "get": { + "description": "Returns a paginated list of session IDs for a store.", + "operationId": "ListSessions", + "parameters": [ + { + "name": "storeId", + "in": "path", + "description": "The store instance ID.", + "schema": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "The store instance ID." + }, + "required": true + }, + { + "name": "limit", + "in": "query", + "description": "Maximum number of sessions to return. Defaults to 100. Allowed range: 1-1000.", + "schema": { + "type": "integer", + "default": 100, + "maximum": 1000, + "minimum": 1, + "description": "Maximum number of sessions to return. Defaults to 100. Allowed range: 1-1000.", + "format": "int32" + } + }, + { + "name": "pageToken", + "in": "query", + "description": "Opaque token from a previous response for the next page.", + "schema": { + "type": "string", + "description": "Opaque token from a previous response for the next page." + } + }, + { + "name": "filterOwnerId", + "in": "query", + "description": "Filter sessions by owner. Matching is case-sensitive. Mutually exclusive with includeAll.", + "schema": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Filter sessions by owner. Matching is case-sensitive. Mutually exclusive with includeAll." + } + }, + { + "name": "includeAll", + "in": "query", + "description": "Set to true to list all sessions. Required when no filter is given; mutually exclusive with filters.", + "schema": { + "type": "boolean", + "description": "Set to true to list all sessions. Required when no filter is given; mutually exclusive with filters." + } + } + ], + "responses": { + "200": { "description": "ListSessions 200 response", "content": { "application/json": { @@ -1108,6 +2132,16 @@ } } }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, "424": { "description": "FailedDependencyError 424 response", "content": { @@ -1138,10 +2172,7 @@ } } } - }, - "tags": [ - "Session Memory" - ] + } } }, "/v1/stores/{storeId}/session-memory/events": { @@ -1153,6 +2184,20 @@ "application/json": { "schema": { "$ref": "#/components/schemas/AddSessionEventRequestContent" + }, + "example": { + "sessionId": "session-user42-20240315", + "actorId": "user-42", + "role": "USER", + "createdAt": "2024-03-15T10:00:00Z", + "content": [ + { + "text": "What were the action items from last week's meeting?" + } + ], + "namespaceRef": { + "namespaceId": "ns-product" + } } } }, @@ -1244,6 +2289,16 @@ } } }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, "424": { "description": "FailedDependencyError 424 response", "content": { @@ -1274,10 +2329,7 @@ } } } - }, - "tags": [ - "Session Memory" - ] + } } }, "/v1/stores/{storeId}/session-memory/{sessionId}": { @@ -1376,6 +2428,16 @@ } } }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, "424": { "description": "FailedDependencyError 424 response", "content": { @@ -1406,10 +2468,7 @@ } } } - }, - "tags": [ - "Session Memory" - ] + } }, "get": { "description": "Returns the session memory for a session.", @@ -1439,7 +2498,8 @@ "pattern": "^[a-zA-Z0-9-]+$", "description": "The session ID." }, - "required": true + "required": true, + "example": "session-user42-20240315" }, { "name": "includeSummarisedEvents", @@ -1522,6 +2582,16 @@ } } }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, "424": { "description": "FailedDependencyError 424 response", "content": { @@ -1552,10 +2622,7 @@ } } } - }, - "tags": [ - "Session Memory" - ] + } } }, "/v1/stores/{storeId}/session-memory/{sessionId}/events/{eventId}": { @@ -1667,6 +2734,16 @@ } } }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, "424": { "description": "FailedDependencyError 424 response", "content": { @@ -1697,10 +2774,7 @@ } } } - }, - "tags": [ - "Session Memory" - ] + } }, "get": { "description": "Returns a single event from a session by event ID.", @@ -1817,6 +2891,16 @@ } } }, + "423": { + "description": "ResourceSuspendedError 423 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceSuspendedErrorResponseContent" + } + } + } + }, "424": { "description": "FailedDependencyError 424 response", "content": { @@ -1847,10 +2931,7 @@ } } } - }, - "tags": [ - "Session Memory" - ] + } } } }, @@ -1864,36 +2945,52 @@ "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "Optional session ID. If omitted, the server generates a unique ID." + "description": "Optional session ID. If omitted, the server generates a unique ID.", + "example": "session-user42-20240315" }, "actorId": { "type": "string", "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "Unique actor identifier (1-255 chars). Can represent a user, agent, or any participant." + "description": "Unique actor identifier (1-255 chars). Can represent a user, agent, or any participant.", + "example": "user-42" }, "namespace": { "type": "string", "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "Optional namespace for the session, used as the namespace of every long-term memory extracted from it.\n\nLike `ownerId`, this is a session-level value taken from the first event: the value sent with the first event of a session is stored on the session, and values sent with later events are ignored. Omit it to leave extracted memories without a namespace." + "description": "Optional deprecated namespace label for the session. This label does not create or resolve a namespace resource. Do not supply it with namespaceRef.\n\nLike `ownerId`, this is a session-level value taken from the first event: the value sent with the first event of a session is stored on the session, and values sent with later events are ignored. Omit it to leave extracted memories without a namespace.", + "deprecated": true + }, + "namespaceRef": { + "$ref": "#/components/schemas/NamespaceRefInput", + "example": { + "namespaceId": "ns-product" + } }, "role": { - "$ref": "#/components/schemas/MessageRole" + "$ref": "#/components/schemas/MessageRole", + "example": "USER" }, "content": { "type": "array", "items": { "$ref": "#/components/schemas/Content" }, - "description": "The message content as an array of typed content parts." + "description": "The message content as an array of typed content parts.", + "example": [ + { + "text": "What were the action items from last week's meeting?" + } + ] }, "createdAt": { "type": "string", "description": "Client-supplied timestamp for when the event happened (UTC).", - "format": "date-time" + "format": "date-time", + "example": "2024-03-15T10:00:00Z" }, "metadata": { "description": "Optional metadata as any valid JSON value." @@ -1904,7 +3001,21 @@ "content", "createdAt", "role" - ] + ], + "example": { + "sessionId": "session-user42-20240315", + "actorId": "user-42", + "role": "USER", + "createdAt": "2024-03-15T10:00:00Z", + "content": [ + { + "text": "What were the action items from last week's meeting?" + } + ], + "namespaceRef": { + "namespaceId": "ns-product" + } + } }, "AddSessionEventResponseContent": { "type": "object", @@ -2191,6 +3302,45 @@ "id" ] }, + "ConflictErrorResponseContent": { + "type": "object", + "description": "The request conflicts with the current state of the resource.", + "properties": { + "title": { + "type": "string", + "description": "A short, human-readable summary of the problem\n type. It SHOULD NOT change from occurrence to occurrence of the\n problem, except for purposes of localization (e.g., using\n proactive content negotiation; see [RFC7231], Section 3.4)." + }, + "status": { + "type": "integer", + "default": 409, + "description": "The HTTP status code ([RFC7231], Section 6) generated by the origin server for this occurrence of the problem.", + "format": "int32" + }, + "detail": { + "type": "string", + "description": "A human-readable explanation specific to this occurrence of the problem." + }, + "instance": { + "type": "string", + "description": "A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced." + }, + "type": { + "$ref": "#/components/schemas/ConflictErrorType" + } + }, + "required": [ + "status", + "title", + "type" + ] + }, + "ConflictErrorType": { + "type": "string", + "description": "Problem type URI for conflict errors.", + "enum": [ + "/errors/conflict" + ] + }, "Content": { "description": "A single content item in a message. Exactly one member is set.", "oneOf": [ @@ -2200,7 +3350,8 @@ "properties": { "text": { "type": "string", - "description": "Text content." + "description": "Text content.", + "example": "What were the action items from last week's meeting?" } }, "required": [ @@ -2218,13 +3369,15 @@ "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "Client-provided memory record ID for idempotent creation." + "description": "Client-provided memory record ID for idempotent creation.", + "example": "mem-1" }, "text": { "type": "string", "maxLength": 50000, "minLength": 1, - "description": "The memory content (1-50000 chars)." + "description": "The memory content (1-50000 chars).", + "example": "The user prefers dark mode" }, "memoryType": { "type": "string", @@ -2245,14 +3398,22 @@ "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "Owner ID associated with this memory (1-64 chars, alphanumeric and dashes)." + "description": "Owner ID associated with this memory (1-64 chars, alphanumeric and dashes).", + "example": "user-1" }, "namespace": { "type": "string", "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "Optional namespace for logical grouping (1-64 chars, alphanumeric and dashes). Omit to create the record without a namespace. Records created here are explicit writes, so this value is taken as given; extraction instead inherits the source session's namespace." + "description": "Optional deprecated namespace label. It does not create or resolve a namespace resource. Omit it to create the record without a namespace, and do not supply it with namespaceRef.", + "deprecated": true + }, + "namespaceRef": { + "$ref": "#/components/schemas/NamespaceRefInput", + "example": { + "namespaceId": "ns-product" + } }, "topics": { "type": "array", @@ -2273,6 +3434,66 @@ "id", "ownerId", "text" + ], + "example": { + "id": "mem-1", + "text": "The user prefers dark mode", + "ownerId": "user-1", + "namespaceRef": { + "namespaceId": "ns-product" + } + } + }, + "CreateNamespaceRequestContent": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9_-]+$", + "description": "One namespace segment. Use with scope for a root or parentId for a child.", + "example": "product" + }, + "path": { + "type": "string", + "maxLength": 1024, + "minLength": 1, + "description": "A complete hierarchy path to create. Missing segments are created as needed. Mutually exclusive with name and parentId. The path does not encode scope." + }, + "scope": { + "$ref": "#/components/schemas/NamespaceScope", + "example": "SHARED" + }, + "parentId": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Parent namespace for a direct child." + }, + "ownerId": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Owner for a personal root or path. Required for personal scope; omit for shared roots and children." + } + }, + "example": { + "name": "product", + "scope": "SHARED" + } + }, + "CreateNamespaceResponseContent": { + "type": "object", + "properties": { + "namespace": { + "$ref": "#/components/schemas/Namespace" + } + }, + "required": [ + "namespace" ] }, "CreatedAtFilter": { @@ -2457,7 +3678,11 @@ "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "Namespace for logical grouping. Absent when the record has none: either it was written directly without one, or it was extracted from a session that has no namespace." + "description": "Deprecated namespace label. Absent when the record uses namespaceRef or has no namespace.", + "deprecated": true + }, + "namespaceRef": { + "$ref": "#/components/schemas/NamespaceRef" }, "topics": { "type": "array", @@ -2491,6 +3716,17 @@ "updatedAt" ] }, + "GetNamespaceResponseContent": { + "type": "object", + "properties": { + "namespace": { + "$ref": "#/components/schemas/Namespace" + } + }, + "required": [ + "namespace" + ] + }, "GetSessionEventResponseContent": { "type": "object", "description": "Response containing a single requested session event.", @@ -2526,7 +3762,11 @@ "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "The namespace of the session, set from the namespace of the first event. Absent when the first event did not supply one; long-term memories extracted from this session then carry no namespace." + "description": "Deprecated namespace label for the session, set from the first event. Absent when the session uses namespaceRef or has no namespace.", + "deprecated": true + }, + "namespaceRef": { + "$ref": "#/components/schemas/NamespaceRef" }, "events": { "type": "array", @@ -2564,6 +3804,24 @@ "healthy" ] }, + "ListNamespacesResponseContent": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Namespace" + } + }, + "nextPageToken": { + "type": "string", + "description": "Opaque token for fetching the next page. Omitted when no more results." + } + }, + "required": [ + "items" + ] + }, "ListSessionsResponseContent": { "type": "object", "description": "Paginated list of session IDs for a store.", @@ -2607,6 +3865,16 @@ "namespace": { "$ref": "#/components/schemas/NamespaceFilter" }, + "namespaceRef": { + "$ref": "#/components/schemas/NamespaceRefFilter", + "example": { + "eq": "ns-product" + } + }, + "unnamespaced": { + "type": "boolean", + "description": "When true, matches only records that have neither a deprecated namespace label nor a namespace resource placement. This scope is always combined with every other filter condition. Do not supply namespace or namespaceRef with this field." + }, "topics": { "$ref": "#/components/schemas/TopicsFilter" }, @@ -2621,6 +3889,48 @@ } } }, + "MemoryFailedDependencyErrorResponseContent": { + "type": "object", + "description": "A resource or model-provider dependency required by Agent Memory could not complete the request.", + "properties": { + "title": { + "type": "string", + "description": "A short, human-readable summary of the problem\n type. It SHOULD NOT change from occurrence to occurrence of the\n problem, except for purposes of localization (e.g., using\n proactive content negotiation; see [RFC7231], Section 3.4)." + }, + "status": { + "type": "integer", + "default": 424, + "description": "The HTTP status code ([RFC7231], Section 6) generated by the origin server for this occurrence of the problem.", + "format": "int32" + }, + "detail": { + "type": "string", + "description": "A human-readable explanation specific to this occurrence of the problem." + }, + "instance": { + "type": "string", + "description": "A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced." + }, + "type": { + "$ref": "#/components/schemas/MemoryFailedDependencyErrorType" + } + }, + "required": [ + "status", + "title", + "type" + ] + }, + "MemoryFailedDependencyErrorType": { + "type": "string", + "description": "Problem type URI for Agent Memory failed-dependency errors.", + "enum": [ + "/errors/resource-unavailable", + "/errors/database-out-of-memory", + "/errors/invalid-model-credentials", + "/errors/model-quota-exceeded" + ] + }, "MemoryRecord": { "type": "object", "description": "A long-term memory record.", @@ -2662,7 +3972,11 @@ "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "Namespace for logical grouping. Absent when the record has none: either it was written directly without one, or it was extracted from a session that has no namespace." + "description": "Deprecated namespace label. Absent when the record uses namespaceRef or has no namespace.", + "deprecated": true + }, + "namespaceRef": { + "$ref": "#/components/schemas/NamespaceRef" }, "topics": { "type": "array", @@ -2733,23 +4047,155 @@ "SYSTEM" ] }, + "MoveLongTermMemoriesRequestContent": { + "type": "object", + "properties": { + "memoryIds": { + "type": "array", + "items": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Memory record identifier (1-64 chars, alphanumeric and dashes). Client-provided for idempotent long-term memory creation." + }, + "maxItems": 100, + "minItems": 1 + }, + "namespaceRef": { + "$ref": "#/components/schemas/NamespaceRefInput" + } + }, + "required": [ + "memoryIds", + "namespaceRef" + ], + "example": { + "memoryIds": [ + "mem-1" + ], + "namespaceRef": { + "namespaceId": "ns-product" + } + } + }, + "MoveLongTermMemoriesResponseContent": { + "type": "object", + "properties": { + "moved": { + "type": "array", + "items": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Memory record identifier (1-64 chars, alphanumeric and dashes). Client-provided for idempotent long-term memory creation." + } + }, + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BulkOperationError" + } + } + }, + "required": [ + "moved" + ] + }, + "Namespace": { + "type": "object", + "description": "A namespace hierarchy record.", + "properties": { + "namespaceId": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Server-generated namespace identifier." + }, + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9_-]+$", + "description": "One case-sensitive namespace path segment." + }, + "parentId": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Server-generated namespace identifier." + }, + "rootId": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Server-generated namespace identifier." + }, + "ownerId": { + "type": "string", + "minLength": 1, + "description": "An opaque application-user ID selected by the platform for a namespace root." + }, + "scope": { + "$ref": "#/components/schemas/NamespaceScope" + }, + "state": { + "$ref": "#/components/schemas/NamespaceState" + }, + "revision": { + "type": "integer", + "format": "int64" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "path": { + "type": "string", + "maxLength": 1024, + "minLength": 1, + "description": "Current case-sensitive path, derived from the live hierarchy." + } + }, + "required": [ + "createdAt", + "name", + "namespaceId", + "path", + "revision", + "rootId", + "scope", + "state", + "updatedAt" + ] + }, "NamespaceFilter": { "type": "object", - "description": "Filter by namespace. Matching is case-insensitive.\n\nNamespace is optional, so records without one need care. The positive operators (`eq`, `in`, `all`) require the field to be present and therefore never match a record that has no namespace — including memories extracted from a session that has none. `ne` is a plain negation and *does* match those records, so use `eq` or `in` when you need strict namespace isolation rather than `ne` against the namespaces you want to exclude.\n\nOmit this filter to search across every namespace, and use it to keep recall inside one project, workspace, team, environment, or tenant.", + "description": "Filter by namespace. Matching is case-insensitive.\n\nNamespace is optional, so records without one need care. The positive operators (`eq`, `in`, `all`) require the field to be present and therefore never match a record that has no namespace \u2014 including memories extracted from a session that has none. `ne` is a plain negation and *does* match those records, so use `eq` or `in` when you need strict namespace isolation rather than `ne` against the namespaces you want to exclude.\n\nOmit this filter to search across every namespace, and use it to keep recall inside one project, workspace, team, environment, or tenant.", "properties": { "eq": { "type": "string", "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "Equals the given value." + "description": "Equals the given value.", + "deprecated": true }, "ne": { "type": "string", "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "Not equal to the given value." + "description": "Not equal to the given value.", + "deprecated": true }, "in": { "type": "array", @@ -2758,7 +4204,8 @@ "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "Namespace for logical grouping (1-64 chars, alphanumeric and dashes).\n\nThis is the shared namespace format for both session memory and long-term memory: sessions carry it as session-level state, and long-term memory records carry it per record. The same constraints apply everywhere it is used.\n\nNamespace is always optional; a record without one is valid. Applications use it to isolate memories by project, workspace, team, environment, or tenant. See `NamespaceFilter` for how records without a namespace behave under search.\n\nA namespace reaches a record one of two ways. Records written directly carry whatever namespace the caller sets. Records produced by extraction inherit the namespace of the session they were extracted from, which is fixed when that session is created; a session with no namespace yields records with no namespace. Existing records are never backfilled when a namespace is introduced later." + "description": "Deprecated flat namespace label for logical grouping. The value is 1-64 alphanumeric characters or dashes and has no hierarchy or scope semantics. Matching through `filter.namespace` is case-insensitive.\n\nThe field remains optional for backward compatibility. New integrations should use `namespaceRef` to reference an existing namespace resource.\n\nRecords produced by extraction inherit the namespace label stored on the session when the session was created.\n\nThis shape is deprecated: Use namespaceRef to reference a namespace resource.", + "deprecated": true }, "description": "Matches any of the given values." }, @@ -2769,12 +4216,112 @@ "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "Namespace for logical grouping (1-64 chars, alphanumeric and dashes).\n\nThis is the shared namespace format for both session memory and long-term memory: sessions carry it as session-level state, and long-term memory records carry it per record. The same constraints apply everywhere it is used.\n\nNamespace is always optional; a record without one is valid. Applications use it to isolate memories by project, workspace, team, environment, or tenant. See `NamespaceFilter` for how records without a namespace behave under search.\n\nA namespace reaches a record one of two ways. Records written directly carry whatever namespace the caller sets. Records produced by extraction inherit the namespace of the session they were extracted from, which is fixed when that session is created; a session with no namespace yields records with no namespace. Existing records are never backfilled when a namespace is introduced later." + "description": "Deprecated flat namespace label for logical grouping. The value is 1-64 alphanumeric characters or dashes and has no hierarchy or scope semantics. Matching through `filter.namespace` is case-insensitive.\n\nThe field remains optional for backward compatibility. New integrations should use `namespaceRef` to reference an existing namespace resource.\n\nRecords produced by extraction inherit the namespace label stored on the session when the session was created.\n\nThis shape is deprecated: Use namespaceRef to reference a namespace resource.", + "deprecated": true }, "description": "Matches all of the given values." } } }, + "NamespaceRef": { + "type": "object", + "description": "Reference to a namespace resource with its stable identifier and current display data.", + "properties": { + "namespaceId": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Stable namespace resource identifier." + }, + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9_-]+$", + "description": "Current namespace name." + }, + "path": { + "type": "string", + "maxLength": 1024, + "minLength": 1, + "description": "Current namespace path." + } + }, + "required": [ + "name", + "namespaceId", + "path" + ] + }, + "NamespaceRefFilter": { + "type": "object", + "description": "Filter by stable namespace resource identifier. Supports exact or any-of matching and matches only resource-backed records, never deprecated namespace labels.", + "properties": { + "eq": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Equals the given value.", + "example": "ns-product" + }, + "in": { + "type": "array", + "items": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Server-generated namespace identifier." + }, + "description": "Matches any of the given values." + } + } + }, + "NamespaceRefInput": { + "type": "object", + "description": "Reference to an existing namespace resource for a write or namespace-scoped operation.", + "properties": { + "namespaceId": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9-]+$", + "description": "Stable namespace resource identifier.", + "example": "ns-product" + } + }, + "required": [ + "namespaceId" + ], + "example": { + "namespaceId": "ns-product" + } + }, + "NamespaceScope": { + "type": "string", + "description": "The root scope of a namespace hierarchy.", + "enum": [ + "PERSONAL", + "SHARED" + ] + }, + "NamespaceState": { + "type": "string", + "description": "Whether a namespace accepts new children and placements.", + "enum": [ + "ACTIVE", + "ARCHIVED" + ] + }, + "NamespaceUpdateState": { + "type": "string", + "description": "The archive-only state transition accepted by UpdateNamespace.", + "enum": [ + "ARCHIVED" + ] + }, "NotFoundErrorResponseContent": { "type": "object", "description": "The requested resource does not exist.", @@ -2895,13 +4442,53 @@ "/errors/payload-too-large" ] }, + "ResourceSuspendedErrorResponseContent": { + "type": "object", + "description": "The requested resource exists but is suspended by an administrator.", + "properties": { + "title": { + "type": "string", + "description": "A short, human-readable summary of the problem\n type. It SHOULD NOT change from occurrence to occurrence of the\n problem, except for purposes of localization (e.g., using\n proactive content negotiation; see [RFC7231], Section 3.4)." + }, + "status": { + "type": "integer", + "default": 423, + "description": "The HTTP status code ([RFC7231], Section 6) generated by the origin server for this occurrence of the problem.", + "format": "int32" + }, + "detail": { + "type": "string", + "description": "A human-readable explanation specific to this occurrence of the problem." + }, + "instance": { + "type": "string", + "description": "A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced." + }, + "type": { + "$ref": "#/components/schemas/ResourceSuspendedErrorType" + } + }, + "required": [ + "status", + "title", + "type" + ] + }, + "ResourceSuspendedErrorType": { + "type": "string", + "description": "Problem type URI for suspended resources.", + "enum": [ + "/errors/resource-suspended" + ] + }, "SearchLongTermMemoryRequestContent": { "type": "object", "description": "Semantic search request for long-term memory records.", "properties": { "text": { "type": "string", - "description": "Text to use for semantic search." + "description": "Text to use for semantic search.", + "example": "preferred theme" }, "similarityThreshold": { "type": "number", @@ -2928,6 +4515,14 @@ "type": "string", "description": "Opaque token from a previous response for the next page." } + }, + "example": { + "text": "preferred theme", + "filter": { + "namespaceRef": { + "eq": "ns-product" + } + } } }, "SearchLongTermMemoryResponseContent": { @@ -3299,7 +4894,11 @@ "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "The caller's namespace. The update is rejected with 409 unless the record's stored namespace matches exactly.\n\nOmit it to assert that the record has no namespace — an omitted value matches only records stored without one, it does not mean \"any namespace\". The check is applied atomically with the update, so a record cannot be re-filed into another namespace between the check and the write." + "description": "The caller's current deprecated namespace label. The update is rejected with 409 unless the record still stores that label. Do not supply it with namespaceRef.\n\nOmit both namespace and namespaceRef to assert that the record has no namespace placement. Omission does not mean \"any namespace\".", + "deprecated": true + }, + "namespaceRef": { + "$ref": "#/components/schemas/NamespaceRefInput" }, "text": { "type": "string", @@ -3345,7 +4944,11 @@ }, "namespace": { "type": "string", - "description": "Updated namespace. When provided, must be 1-64 chars alphanumeric and dashes; omit the field or send an empty string to clear.\n\nThis is the only way to change the namespace of an existing record, including one produced by extraction: a record inherits its session's namespace at creation and is not revised afterwards, so use this to re-file a record or to add a namespace to one created before the session had any." + "description": "Updated deprecated namespace label. When provided, it must be 1-64 alphanumeric characters or dashes. Send an empty string to clear it.\nDo not supply it with namespaceRef.\n\nThis shape is deprecated: Use namespaceRef to reference a namespace resource.", + "deprecated": true + }, + "namespaceRef": { + "$ref": "#/components/schemas/NamespaceRefInput" }, "ownerId": { "type": "string", @@ -3398,7 +5001,11 @@ "maxLength": 64, "minLength": 1, "pattern": "^[a-zA-Z0-9-]+$", - "description": "Namespace for logical grouping. Absent when the record has none: either it was written directly without one, or it was extracted from a session that has no namespace." + "description": "Deprecated namespace label. Absent when the record uses namespaceRef or has no namespace.", + "deprecated": true + }, + "namespaceRef": { + "$ref": "#/components/schemas/NamespaceRef" }, "topics": { "type": "array", @@ -3431,7 +5038,44 @@ "text", "updatedAt" ] + }, + "UpdateNamespaceRequestContent": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 64, + "minLength": 1, + "pattern": "^[a-zA-Z0-9_-]+$", + "description": "One case-sensitive namespace path segment." + }, + "state": { + "$ref": "#/components/schemas/NamespaceUpdateState" + } + } + }, + "UpdateNamespaceResponseContent": { + "type": "object", + "properties": { + "namespace": { + "$ref": "#/components/schemas/Namespace" + } + }, + "required": [ + "namespace" + ] } + }, + "securitySchemes": { + "apiKey": { + "type": "http", + "scheme": "bearer" + } + } + }, + "security": [ + { + "apiKey": [] } - } + ] } diff --git a/content/develop/ai/context-engine/agent-memory/developer-guide.md b/content/develop/ai/context-engine/agent-memory/developer-guide.md index f0520ec30d..023c6ba9f1 100644 --- a/content/develop/ai/context-engine/agent-memory/developer-guide.md +++ b/content/develop/ai/context-engine/agent-memory/developer-guide.md @@ -49,10 +49,76 @@ Follow the [Redis Cloud setup guide](/content/operate/iris/agent-memory/create-s | `sessionId` | Identifies a conversation or interaction session. | | `actorId` | Identifies the actor that produced a session event. | | `ownerId` | Identifies the user or entity associated with a long-term memory. | +| `namespaceId` | Identifies a namespace resource used to group sessions and long-term memories. | | Memory ID | Uniquely identifies a long-term memory within the store. | An application can use the same user identifier for `actorId` and `ownerId`, but the fields describe different relationships. +## Organize memories with namespaces + +Use a namespace to group memories for a project, team, or user. For example, a travel agent can keep a user's trip memories in a personal `travel` namespace. A namespace controls where you place and search for memories; a custom memory type describes the information in each record. + +Create the namespace before writing to it. The response contains a server-generated `namespaceId`. Save this ID and use it in `namespaceRef` on session events and long-term memory writes. Names and paths can change when you rename a namespace; its ID stays the same. + +### Create a namespace hierarchy + +Choose a root scope when you create a namespace: + +| Scope | Creation fields | Example use | +|:------|:----------------|:------------| +| `PERSONAL` | `name`, `scope`, and `ownerId` | A user's travel plans. | +| `SHARED` | `name` and `scope`, without `ownerId` | Information used across a team. | + +Create a child with `name` and `parentId`. You can also create a hierarchy with `path`, such as `travel/japan`, and the root scope. Supply `ownerId` for a personal path. Missing path segments are created as needed. Do not combine `path` with `name` or `parentId`. + +Names and paths are case-sensitive. The path does not encode scope. Use the returned ID to distinguish namespaces that have the same path under different owners or scopes. + +Follow the [REST quickstart]({{< relref "/develop/ai/context-engine/agent-memory/rest-api-quickstart#create-a-namespace" >}}), [Python quickstart]({{< relref "/develop/ai/context-engine/agent-memory/python-sdk-quickstart#create-a-namespace" >}}), or [TypeScript quickstart]({{< relref "/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart#create-a-namespace" >}}) to create a personal namespace and use its ID throughout a conversation. + +### Place and retrieve memories + +Pass `namespaceRef: {"namespaceId": ""}` when you start a session. Memories extracted from the session use that namespace. Use a new session ID when following the quickstarts if you already ran them without a namespace. + +For direct long-term memory creation, set `namespaceRef` on each record. To move existing records, use `MoveLongTermMemories` with their IDs and the destination `namespaceRef`. Check both `moved` and `errors` in the response before treating a batch as complete. + +Search with `filter.namespaceRef.eq` for one namespace or `filter.namespaceRef.in` for several IDs. These filters match exact namespace IDs; they do not expand a parent into its descendants. List the children and include their IDs when you need to search several levels of a hierarchy. Keep the `ownerId` filter when recalling one user's memories. Namespace scope and search filters do not replace your application's access checks. + +### Manage namespaces + +| Operation | Behavior | +|:----------|:---------| +| List | List roots, or pass `parentId` to list direct children. Follow `nextPageToken` for additional pages. | +| Get | Retrieve the current name, path, scope, and state by ID. | +| Rename | Update `name`; continue using the same ID for placement and retrieval. | +| Archive | Set `state` to `ARCHIVED` to stop new children and placements. The update API does not offer an unarchive transition. | +| Delete | Delete an empty leaf namespace. A namespace with children or memory placements returns `409 Conflict`. | + +Creating a namespace at an occupied location returns `409 Conflict`, including when the existing namespace is archived. Save and reuse the returned ID instead of creating the same namespace on each agent turn. For request fields and error responses, see the [namespace API reference]({{< relref "/develop/ai/context-engine/agent-memory/api-reference" >}}#operation/CreateNamespace). + +### Migrate legacy namespace labels + +The `namespace` string is deprecated. It is a label, and does not create or resolve a namespace resource. Creating a resource with the same name does not move records that carry the old label. A `namespaceRef` search matches only records placed in a namespace resource. + +1. Create the destination namespace and save its ID. +1. Find the records to move with the legacy `filter.namespace` and the appropriate owner filter. Follow search pagination and review the IDs before moving them. +1. Move the selected records with `MoveLongTermMemories`. +1. Check `moved` and any per-record `errors`, then search with `filter.namespaceRef` to verify the new placement. +1. Update application writes and searches to use `namespaceRef`. Start new sessions with the namespace reference for future extraction. + +Using the connection variables from the [REST quickstart]({{< relref "/develop/ai/context-engine/agent-memory/rest-api-quickstart#save-the-connection-values" >}}), move a selected record as follows. Set `NAMESPACE_ID` to the destination ID and replace `` with a reviewed record ID: + +```sh +curl --fail-with-body --silent --show-error \ + --request POST \ + --header "Authorization: Bearer $API_KEY" \ + --header 'Content-Type: application/json' \ + --data "$(jq -n --arg ns "$NAMESPACE_ID" --arg id '' \ + '{memoryIds: [$id], namespaceRef: {namespaceId: $ns}}')" \ + "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/long-term-memory/move" | jq +``` + +Do not send `namespace` and `namespaceRef` together. Moving long-term records does not reconfigure the session that produced them. + ## Work with session memory Use a stable `sessionId` to store a conversation as an ordered sequence of events. Add an event for each user, assistant, or system message that your application needs to retain. @@ -102,7 +168,8 @@ Long-term memory stores information that remains useful beyond one conversation. | `memoryType` | Built-in or custom memory type. | | `sessionId` | Session associated with the memory. | | `ownerId` | User or entity associated with the memory. | -| `namespace` | Logical grouping for the memory. | +| `namespaceRef` | Namespace ID and its current name and path. | +| `attributes` | Custom memory fields and their values. | | `topics` | Topic tags used to categorize the memory. | | `createdAt` | Time the memory was created. | | `updatedAt` | Time the memory was last updated. | @@ -161,12 +228,33 @@ For example, a travel application could define a `trip_preference` type with the | Field | Type | Captures | |:------|:-----|:---------| -| `destination` | `str` | City or country the user plans to visit. | +| `destinations` | `list[str]` | Cities or countries the user plans to visit. | | `travel_period` | `str` | Dates or period of the trip. | | `dietary_requirements` | `list[str]` | Dietary requirements that affect recommendations. | +| `food_preferences` | `list[str]` | Cuisines, flavors, or dining preferences. | When 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. +Search with `memoryType`, `ownerId`, and `namespaceRef` filters together to retrieve this user's trip preferences from the intended namespace. Read the structured fields from each result's `attributes` object. For example, this excerpt shows fields your application can use to constrain restaurant recommendations: + +```json +{ + "memoryType": "trip_preference", + "attributes": { + "destinations": ["Tokyo", "Kyoto"], + "travel_period": "next month", + "dietary_requirements": ["vegetarian"], + "food_preferences": ["spicy food"] + } +} +``` + +This is an illustrative excerpt, not a guaranteed extraction result. Check for missing fields before using them. Extraction runs asynchronously: an empty `items` array can mean that processing is not finished or no matching information was extracted. Retry after a short wait, then check the type's enabled setting, extraction prompt, and search filters if results remain empty. Do not interpret an empty result as the absence of a dietary requirement. + +You can also create custom memories directly. Register the type on the service first, then send `id`, `text`, `ownerId`, `memoryType`, `attributes`, and optionally `namespaceRef` through `BulkCreateLongTermMemories`. Attribute names and JSON values must match the registered fields. Use this path for structured data from an application form or an import. It does not run the extraction prompt or sensitive-data exclusions. + +The quickstarts show both extraction and direct creation with the same `trip_preference` schema. Custom types are configured for the service; placing a record in a namespace does not define a new type. + See [custom memory types](/content/operate/iris/agent-memory/create-service.md#custom-memory-types) for configuration requirements and limits. Follow any of the client quickstarts to create and search long-term memory. For complete schemas, see the [long-term-memory API reference](/content/develop/ai/context-engine/agent-memory/api-reference.md#tag/long-term-memory). diff --git a/content/develop/ai/context-engine/agent-memory/python-sdk-quickstart.md b/content/develop/ai/context-engine/agent-memory/python-sdk-quickstart.md index f98b5534ee..f44d3126f1 100644 --- a/content/develop/ai/context-engine/agent-memory/python-sdk-quickstart.md +++ b/content/develop/ai/context-engine/agent-memory/python-sdk-quickstart.md @@ -39,8 +39,12 @@ Keep the API key out of source control, application logs, and other unsecured lo ## Install the SDK +Create and activate a virtual environment with [uv](https://docs.astral.sh/uv/): + ```sh -python -m pip install redis-agent-memory +uv venv +source .venv/bin/activate +uv pip install "redis-agent-memory==0.4.0" ``` ## Create the client and check the service health @@ -87,13 +91,35 @@ python quickstart.py A healthy response confirms that the client can reach Redis Agent Memory and authenticate with the API key. The first store request validates the Store ID. +## Create a namespace + +Add this code after the health check, inside the `with` block. It creates a personal namespace for the user's travel memories when `NAMESPACE_ID` is not set: + +```python + namespace_id = os.environ.get("NAMESPACE_ID") + if not namespace_id: + created_namespace = agent_memory.create_namespace(request={ + "name": "travel", + "scope": models.NamespaceScope.PERSONAL, + "owner_id": USER_ID, + }) + namespace_id = created_namespace.namespace.namespace_id + print(f'export NAMESPACE_ID="{namespace_id}"') + namespace_ref = {"namespace_id": namespace_id} +``` + +Run the file once, then run the printed `export` command in your shell before running the file again. Later runs reuse that ID. Creating the same namespace again returns `409 Conflict`. If you lose the ID, use `list_namespaces` with `scope="PERSONAL"` and `owner_id=USER_ID` to find it. + +Use a fresh `SESSION_ID` if you already ran this quickstart without a namespace. The session events will reference this namespace, and memories extracted from the session will use it. + ## 1. Build conversation context with session memory -Session memory stores a conversation as an ordered sequence of events. Add the following code after the health check, inside the `with` block: +Session memory stores a conversation as an ordered sequence of events. Add the following code after the namespace setup, inside the `with` block: ```python event = agent_memory.add_session_event( session_id=SESSION_ID, + namespace_ref=namespace_ref, actor_id=USER_ID, role=models.MessageRole.USER, content=[models.Text( @@ -117,7 +143,7 @@ Run the file again. The session response contains the stored message, its role, > [!NOTE] > **What to expect:** The `events` array contains the travel message. Redis Agent Memory adds an `eventId` and `systemTimestamp`, showing that the application can recover the complete event later using only the session ID. -After the event is stored, comment out the call to `add_session_event` before subsequent runs to avoid adding the same message again. +After the event is stored, comment out its creation and the `show("Created event", event)` line before subsequent runs. Keep the namespace setup and session retrieval. This avoids adding the same message again. ## 2. Recall automatically extracted information @@ -130,6 +156,7 @@ Wait at least one minute, then add this search after the session retrieval: request={ "text": "What dietary requirements and food preferences does the user have?", "filter_": { + "namespace_ref": {"eq": namespace_id}, "owner_id": { "eq": USER_ID, } @@ -172,6 +199,7 @@ Add this code after the first session event to continue the conversation past th for role, text in turns: agent_memory.add_session_event( session_id=SESSION_ID, + namespace_ref=namespace_ref, actor_id=USER_ID if role == models.MessageRole.USER else "travel-agent", role=role, content=[models.Text(text=text)], @@ -210,6 +238,7 @@ Search for the structured memory: request={ "text": "What are the requirements for the user's trip?", "filter_": { + "namespace_ref": {"eq": namespace_id}, "owner_id": {"eq": USER_ID}, "memory_type": {"eq": "trip_preference"}, }, @@ -219,10 +248,62 @@ Search for the structured memory: show("Trip preference memories", custom_results) ``` -The result uses `trip_preference` as its `memoryType` and contains travel information extracted from the conversation. The exact text and returned fields depend on the conversation, extraction model, and client. +The `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: + +```json +{ + "memoryType": "trip_preference", + "attributes": { + "destinations": ["Tokyo", "Kyoto"], + "travel_period": "next month", + "dietary_requirements": ["vegetarian"], + "food_preferences": ["spicy food"] + } +} +``` + +The 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. -> [!NOTE] -> **What to expect:** A result with `memoryType` set to `trip_preference` that combines the destinations, travel period, and dietary preferences. This shows that the custom type processed the same conversation independently from the built-in memory types. +Add this code after the custom-memory search to collect dietary requirements for the next agent turn: + +```python + dietary_requirements = set() + if not custom_results.items: + print("No matching trip preferences yet. Retry after a short wait.") + for memory in custom_results.items: + attributes = memory.attributes or {} + requirements = attributes.get("dietary_requirements", []) + if isinstance(requirements, list): + dietary_requirements.update( + value for value in requirements if isinstance(value, str) + ) + print("Restaurant requirements:", sorted(dietary_requirements)) +``` + +If results remain empty, check that the type is enabled and that the owner and namespace IDs match the stored records. An empty result does not mean the user has no dietary requirements. + +### Create a custom memory directly + +If your application already has structured trip data, write it directly using the registered `trip_preference` type. This optional example represents data from a form and uses the same namespace: + +```python + direct_result = agent_memory.bulk_create_long_term_memories(memories=[{ + "id": "trip-form-1", + "text": "The user plans to visit Tokyo and Kyoto next month and requires vegetarian food.", + "owner_id": USER_ID, + "memory_type": "trip_preference", + "namespace_ref": namespace_ref, + "attributes": { + "destinations": ["Tokyo", "Kyoto"], + "travel_period": "next month", + "dietary_requirements": ["vegetarian"], + "food_preferences": ["spicy food"], + }, + }]) + show("Directly created trip preference", direct_result) +``` + +Inspect the bulk response 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. Run this write once; the memory ID identifies the record within the store. See [custom memory types](/content/operate/iris/agent-memory/create-service.md#custom-memory-types) for configuration requirements and limits. @@ -233,6 +314,7 @@ The semantic exclusion prompt tells Redis Agent Memory which information should ```python sensitive_event = agent_memory.add_session_event( session_id=SESSION_ID, + namespace_ref=namespace_ref, actor_id=USER_ID, role=models.MessageRole.USER, content=[models.Text( @@ -246,13 +328,14 @@ The semantic exclusion prompt tells Redis Agent Memory which information should show("Event with excluded information", sensitive_event) ``` -Run the code once, then comment out the call to `add_session_event`. Wait at least one minute and search for the safe hotel information: +Run the code once, then comment out the event creation and its `show` line. Wait at least one minute and search for the safe hotel information: ```python exclusion_results = agent_memory.search_long_term_memory( request={ "text": "Where is the user staying in Tokyo?", "filter_": { + "namespace_ref": {"eq": namespace_id}, "owner_id": {"eq": USER_ID}, }, "limit": 5, @@ -273,6 +356,8 @@ See [sensitive-data exclusions](/content/operate/iris/agent-memory/create-servic ## Next steps +* Learn how to [organize memories with namespaces]({{< relref "/develop/ai/context-engine/agent-memory/developer-guide#organize-memories-with-namespaces" >}}). + * Review the [Python SDK package and reference](https://pypi.org/project/redis-agent-memory/). * Try the [TypeScript SDK quickstart](/content/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart.md) or [REST API quickstart](/content/develop/ai/context-engine/agent-memory/rest-api-quickstart.md). * Learn when to [create long term memories directly](/content/develop/ai/context-engine/agent-memory/developer-guide.md#create-long-term-memories). diff --git a/content/develop/ai/context-engine/agent-memory/rest-api-quickstart.md b/content/develop/ai/context-engine/agent-memory/rest-api-quickstart.md index 9a5d00feab..f347b8c9a0 100644 --- a/content/develop/ai/context-engine/agent-memory/rest-api-quickstart.md +++ b/content/develop/ai/context-engine/agent-memory/rest-api-quickstart.md @@ -54,6 +54,26 @@ curl --fail-with-body --silent --show-error \ "$AGENT_MEMORY_URL/health" | jq ``` +## Create a namespace + +Create a personal namespace for the user's travel memories. Run this request once and keep the returned ID in your shell: + +```sh +NAMESPACE_ID=$(curl --fail-with-body --silent --show-error \ + --request POST \ + --header "Authorization: Bearer $API_KEY" \ + --header 'Content-Type: application/json' \ + --data "$(jq -n --arg owner "$OWNER_ID" \ + '{name: "travel", scope: "PERSONAL", ownerId: $owner}')" \ + "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/namespaces" | jq -er '.namespace.namespaceId') +export NAMESPACE_ID +printf '%s\n' "$NAMESPACE_ID" +``` + +Continue only after the request succeeds and prints a namespace ID. Save that ID for later runs. Creating the same namespace again returns `409 Conflict`; use the existing namespace ID instead. If you lose the ID, list personal roots with `GET /v1/stores/{storeId}/namespaces?scope=PERSONAL&ownerId=`. + +Use a fresh `SESSION_ID` if you already ran this quickstart without a namespace. The session events will reference this namespace, and memories extracted from the session will use it. + ## 1. Build conversation context with session memory Session memory stores a conversation as an ordered sequence of events. Add a user message that contains details the travel agent will need later: @@ -69,6 +89,7 @@ curl --fail-with-body --silent --show-error \ "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/session-memory/events" < [!NOTE] -> **What to expect:** A result with `memoryType` set to `trip_preference` that combines the destinations, travel period, and dietary preferences. This shows that the custom type processed the same conversation independently from the built-in memory types. +```json +{ + "memoryType": "trip_preference", + "attributes": { + "destinations": ["Tokyo", "Kyoto"], + "travel_period": "next month", + "dietary_requirements": ["vegetarian"], + "food_preferences": ["spicy food"] + } +} +``` + +The 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. + +To inspect the structured fields, run the search with this `jq` filter in place of the final `jq` command: + +```sh +jq 'if (.items | length) == 0 then + {status: "No matching trip preferences yet. Retry after a short wait."} + else + .items[] | {id, attributes} + end' +``` + +If results remain empty, check that the type is enabled and that the owner and namespace IDs match the stored records. An empty result does not mean the user has no dietary requirements. + +### Create a custom memory directly + +If your application already has structured trip data, write it directly using the registered `trip_preference` type. This optional example represents data from a form and uses the same namespace: + +```sh +curl --fail-with-body --silent --show-error \ + --request POST \ + --header "Authorization: Bearer $API_KEY" \ + --header 'Content-Type: application/json' \ + --data @- \ + "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/long-term-memory" <}}). + * Follow the [Python SDK quickstart](/content/develop/ai/context-engine/agent-memory/python-sdk-quickstart.md) or [TypeScript SDK quickstart](/content/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart.md). * Learn when to [create long term memories directly](/content/develop/ai/context-engine/agent-memory/developer-guide.md#create-long-term-memories). * Use the [Redis Agent Memory API reference](/content/develop/ai/context-engine/agent-memory/api-reference.md) for endpoint and schema details. diff --git a/content/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart.md b/content/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart.md index e4e513a8d1..1fa5988959 100644 --- a/content/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart.md +++ b/content/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart.md @@ -92,7 +92,7 @@ Keep the API key out of source control, application logs, and other unsecured lo mkdir agent-memory-quickstart cd agent-memory-quickstart npm init -y -npm install @redis-iris/agent-memory +npm install @redis-iris/agent-memory@0.3.0 npm install --save-dev tsx ``` @@ -139,13 +139,36 @@ npx tsx quickstart.ts A healthy response confirms that the client can reach Redis Agent Memory and authenticate with the API key. The first store request validates the Store ID. +## Create a namespace + +Add this code after the health check, inside `run`. It creates a personal namespace for the user's travel memories when `NAMESPACE_ID` is not set: + +```typescript + let namespaceId = process.env.NAMESPACE_ID; + if (!namespaceId) { + const createdNamespace = await agentMemory.createNamespace({ + name: "travel", + scope: "PERSONAL", + ownerId: userId, + }); + namespaceId = createdNamespace.namespace.namespaceId; + console.log(`export NAMESPACE_ID="${namespaceId}"`); + } + const namespaceRef = { namespaceId }; +``` + +Run the file once, then run the printed `export` command in your shell before running the file again. Later runs reuse that ID. Creating the same namespace again returns `409 Conflict`. If you lose the ID, use `listNamespaces` with `scope: "PERSONAL"` and `ownerId: userId` to find it. + +Use a fresh `sessionId` if you already ran this quickstart without a namespace. The session events will reference this namespace, and memories extracted from the session will use it. + ## 1. Build conversation context with session memory -Session memory stores a conversation as an ordered sequence of events. Add the following code after the health check, inside `run`: +Session memory stores a conversation as an ordered sequence of events. Add the following code after the namespace setup, inside `run`: ```typescript const event = await agentMemory.addSessionEvent({ sessionId, + namespaceRef, actorId: userId, role: "USER", content: [{ @@ -166,7 +189,7 @@ Run the file again. The session response contains the stored message, its role, > [!NOTE] > **What to expect:** The `events` array contains the travel message. Redis Agent Memory adds an `eventId` and `systemTimestamp`, showing that the application can recover the complete event later using only the session ID. -After the event is stored, comment out the call to `addSessionEvent` before subsequent runs to avoid adding the same message again. +After the event is stored, comment out its creation and the two lines that print it before subsequent runs. Keep the namespace setup and session retrieval. This avoids adding the same message again. ## 2. Recall automatically extracted information @@ -178,6 +201,7 @@ Wait at least one minute, then add this search after the session retrieval: const results = await agentMemory.searchLongTermMemory({ text: "What dietary requirements and food preferences does the user have?", filter: { + namespaceRef: { eq: namespaceId }, ownerId: { eq: userId, }, @@ -218,6 +242,7 @@ Add this code after the first session event to continue the conversation past th for (const turn of turns) { await agentMemory.addSessionEvent({ sessionId, + namespaceRef, actorId: turn.actorId, role: turn.role, content: [{ text: turn.text }], @@ -255,6 +280,7 @@ Search for the structured memory: const customResults = await agentMemory.searchLongTermMemory({ text: "What are the requirements for the user's trip?", filter: { + namespaceRef: { eq: namespaceId }, ownerId: { eq: userId }, memoryType: { eq: "trip_preference" }, }, @@ -264,10 +290,66 @@ Search for the structured memory: console.dir(customResults, { depth: null }); ``` -The result uses `trip_preference` as its `memoryType` and contains travel information extracted from the conversation. The exact text and returned fields depend on the conversation, extraction model, and client. +The `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: -> [!NOTE] -> **What to expect:** A result with `memoryType` set to `trip_preference` that combines the destinations, travel period, and dietary preferences. This shows that the custom type processed the same conversation independently from the built-in memory types. +```json +{ + "memoryType": "trip_preference", + "attributes": { + "destinations": ["Tokyo", "Kyoto"], + "travel_period": "next month", + "dietary_requirements": ["vegetarian"], + "food_preferences": ["spicy food"] + } +} +``` + +The 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. + +Add this code after the custom-memory search to collect dietary requirements for the next agent turn: + +```typescript + const dietaryRequirements = new Set(); + if (customResults.items.length === 0) { + console.log("No matching trip preferences yet. Retry after a short wait."); + } + for (const memory of customResults.items) { + const requirements = memory.attributes?.dietary_requirements; + if (Array.isArray(requirements)) { + for (const value of requirements) { + if (typeof value === "string") dietaryRequirements.add(value); + } + } + } + console.log("Restaurant requirements:", [...dietaryRequirements].sort()); +``` + +If results remain empty, check that the type is enabled and that the owner and namespace IDs match the stored records. An empty result does not mean the user has no dietary requirements. + +### Create a custom memory directly + +If your application already has structured trip data, write it directly using the registered `trip_preference` type. This optional example represents data from a form and uses the same namespace: + +```typescript + const directResult = await agentMemory.bulkCreateLongTermMemories({ + memories: [{ + id: "trip-form-1", + text: "The user plans to visit Tokyo and Kyoto next month and requires vegetarian food.", + ownerId: userId, + memoryType: "trip_preference", + namespaceRef, + attributes: { + destinations: ["Tokyo", "Kyoto"], + travel_period: "next month", + dietary_requirements: ["vegetarian"], + food_preferences: ["spicy food"], + }, + }], + }); + console.dir(directResult, { depth: null }); +``` + +Inspect the bulk response 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. Run this write once; the memory ID identifies the record within the store. See [custom memory types](/content/operate/iris/agent-memory/create-service.md#custom-memory-types) for configuration requirements and limits. @@ -278,6 +360,7 @@ The semantic exclusion prompt tells Redis Agent Memory which information should ```typescript const sensitiveEvent = await agentMemory.addSessionEvent({ sessionId, + namespaceRef, actorId: userId, role: "USER", content: [{ @@ -289,12 +372,13 @@ The semantic exclusion prompt tells Redis Agent Memory which information should console.dir(sensitiveEvent, { depth: null }); ``` -Run the code once, then comment out the call to `addSessionEvent`. Wait at least one minute and search for the safe hotel information: +Run the code once, then comment out the event creation and the two lines that print it. Wait at least one minute and search for the safe hotel information: ```typescript const exclusionResults = await agentMemory.searchLongTermMemory({ text: "Where is the user staying in Tokyo?", filter: { + namespaceRef: { eq: namespaceId }, ownerId: { eq: userId }, }, limit: 5, @@ -315,6 +399,8 @@ See [sensitive-data exclusions](/content/operate/iris/agent-memory/create-servic ## Next steps +* Learn how to [organize memories with namespaces]({{< relref "/develop/ai/context-engine/agent-memory/developer-guide#organize-memories-with-namespaces" >}}). + * Review the [TypeScript SDK package and reference](https://www.npmjs.com/package/@redis-iris/agent-memory). * Try the [Python SDK quickstart](/content/develop/ai/context-engine/agent-memory/python-sdk-quickstart.md) or [REST API quickstart](/content/develop/ai/context-engine/agent-memory/rest-api-quickstart.md). * Learn when to [create long term memories directly](/content/develop/ai/context-engine/agent-memory/developer-guide.md#create-long-term-memories). diff --git a/content/operate/iris/agent-memory/self-managed/api-examples.md b/content/operate/iris/agent-memory/self-managed/api-examples.md index 6bca9153f7..f7ebef0a06 100644 --- a/content/operate/iris/agent-memory/self-managed/api-examples.md +++ b/content/operate/iris/agent-memory/self-managed/api-examples.md @@ -246,37 +246,55 @@ curl -sS "$DP_URL/v1/stores/$STORE_ID/session-memory?includeAll=true" \ ### Create long-term memories directly +Create a personal namespace for the user's preferences and save its ID. Run this once, then reuse the ID for later writes: + +```bash +NAMESPACE_ID=$(curl --fail-with-body -sS -X POST "$DP_URL/v1/stores/$STORE_ID/namespaces" \ + -H "Authorization: Bearer $RAM_AGENT_KEY" \ + -H "Content-Type: application/json" \ + -d '{"name":"preferences","scope":"PERSONAL","ownerId":"user-001"}' \ + | jq -er '.namespace.namespaceId') +export NAMESPACE_ID +``` + +After the request succeeds, place the memory in that namespace: + ```bash curl -sS -X POST "$DP_URL/v1/stores/$STORE_ID/long-term-memory" \ -H "Authorization: Bearer $RAM_AGENT_KEY" \ -H "Content-Type: application/json" \ - -d '{ + -d "$(jq -n --arg ns "$NAMESPACE_ID" '{ "memories": [ { "id": "pref-email-updates", "text": "User prefers email updates.", "memoryType": "semantic", "ownerId": "user-001", - "namespace": "preferences", + "namespaceRef": {"namespaceId": $ns}, "topics": ["communications"] } ] - }' + }')" ``` +See [organize memories with namespaces]({{< relref "/develop/ai/context-engine/agent-memory/developer-guide#organize-memories-with-namespaces" >}}) for hierarchy, search, and migration guidance. + ### Search long-term memory +Filter by the namespace ID to recall preferences from that namespace: + ```bash curl -sS -X POST "$DP_URL/v1/stores/$STORE_ID/long-term-memory/search" \ -H "Authorization: Bearer $RAM_AGENT_KEY" \ -H "Content-Type: application/json" \ - -d '{ + -d "$(jq -n --arg ns "$NAMESPACE_ID" '{ "text": "How should we contact this user?", "filter": { + "namespaceRef": {"eq": $ns}, "ownerId": { "eq": "user-001" } }, "limit": 5 - }' + }')" ``` From 770f120b72ad55dfd0047677406b45f7d563f461 Mon Sep 17 00:00:00 2001 From: Andrew Brookins Date: Mon, 14 Sep 2026 17:14:16 -0700 Subject: [PATCH 2/3] Make Agent Memory docs easier to learn and apply --- content/develop/ai/context-engine/_index.md | 2 +- .../ai/context-engine/agent-memory/_index.md | 63 +-- .../context-engine/agent-memory/concepts.md | 38 ++ .../agent-memory/developer-guide.md | 260 ++--------- .../agent-memory/long-term-memory.md | 436 ++++++++++++++++++ .../context-engine/agent-memory/namespaces.md | 234 ++++++++++ .../agent-memory/python-sdk-quickstart.md | 363 --------------- .../context-engine/agent-memory/quickstart.md | 372 +++++++++++++++ .../agent-memory/rest-api-quickstart.md | 336 -------------- .../context-engine/agent-memory/sessions.md | 170 +++++++ .../agent-memory/typescript-sdk-quickstart.md | 406 ---------------- content/embeds/rc-agent-memory-get-started.md | 2 +- ...-agent-memory-quickstart-create-service.md | 33 +- .../iris/agent-memory/create-service.md | 4 +- .../agent-memory/self-managed/api-examples.md | 2 +- .../operate/iris/agent-memory/view-service.md | 2 +- 16 files changed, 1310 insertions(+), 1413 deletions(-) create mode 100644 content/develop/ai/context-engine/agent-memory/concepts.md create mode 100644 content/develop/ai/context-engine/agent-memory/long-term-memory.md create mode 100644 content/develop/ai/context-engine/agent-memory/namespaces.md delete mode 100644 content/develop/ai/context-engine/agent-memory/python-sdk-quickstart.md create mode 100644 content/develop/ai/context-engine/agent-memory/quickstart.md delete mode 100644 content/develop/ai/context-engine/agent-memory/rest-api-quickstart.md create mode 100644 content/develop/ai/context-engine/agent-memory/sessions.md delete mode 100644 content/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart.md diff --git a/content/develop/ai/context-engine/_index.md b/content/develop/ai/context-engine/_index.md index 0c25a643d9..a2fec44eeb 100644 --- a/content/develop/ai/context-engine/_index.md +++ b/content/develop/ai/context-engine/_index.md @@ -77,7 +77,7 @@ POST /v1/caches/{cacheId}/entries } ``` -See [LangCache API examples](/content/develop/ai/context-engine/langcache/api-examples.md) and the [Agent Memory REST API quickstart](/content/develop/ai/context-engine/agent-memory/rest-api-quickstart.md) for more. +See [LangCache API examples](/content/develop/ai/context-engine/langcache/api-examples.md) and the [Agent Memory quickstart]({{< relref "/develop/ai/context-engine/agent-memory/quickstart" >}}) for more. Redis Iris context engine includes four services: diff --git a/content/develop/ai/context-engine/agent-memory/_index.md b/content/develop/ai/context-engine/agent-memory/_index.md index 50f21de1d2..86061424c6 100644 --- a/content/develop/ai/context-engine/agent-memory/_index.md +++ b/content/develop/ai/context-engine/agent-memory/_index.md @@ -23,11 +23,7 @@ When enabled, automatic summarization compacts session memory by summarizing old Access Redis Agent Memory through the Python and TypeScript SDKs or its REST API. It works with any agent framework or LLM provider. -
- {{< image-card image="images/python-sdk-mark.svg" alt="Python SDK mark" title="Python SDK quickstart — Explore Redis Agent Memory with Python" url="/develop/ai/context-engine/agent-memory/python-sdk-quickstart" >}} - {{< image-card image="images/typescript-sdk-mark.svg" alt="TypeScript SDK mark" title="TypeScript SDK quickstart — Explore Redis Agent Memory with TypeScript" url="/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart" >}} - {{< image-card image="images/rest-api-mark.svg" alt="REST API mark" title="REST API quickstart — Explore Redis Agent Memory with curl" url="/develop/ai/context-engine/agent-memory/rest-api-quickstart" >}} -
+Follow the [quickstart]({{< relref "/develop/ai/context-engine/agent-memory/quickstart" >}}) with Python, TypeScript, or curl. ## Why use Redis Agent Memory? @@ -35,35 +31,13 @@ Access Redis Agent Memory through the Python and TypeScript SDKs or its REST API * **Automatic session summarization:** Automatically summarize older conversation events while retaining recent messages in full. * **Automatic long-term memory:** Automatically extract durable information from session events in the background. You can also create long-term memories directly from external data. * **Sensitive-data exclusions:** Guide automatic extraction away from information that should not be kept in long-term memory. -* **Relevant retrieval:** Search long-term memory using semantic, keyword, or hybrid search. +* **Relevant retrieval:** Search long-term memory semantically and narrow results with filters. * **Multi-session recall:** Retrieve relevant memories across conversations and filter results by owner, session, namespace, topic, or memory type. * **Custom memory types:** Define memory types for your business domain, with structured fields and instructions that control what Redis Agent Memory extracts. ## Two-tier memory model -Redis Agent Memory provides two memory tiers: - -
-
-

Session memory

-

Stores the ordered events and metadata for a conversation.

-
    -
  • Configurable retention: Set a TTL to control how long session events are retained.
  • -
  • Automatic summarization: Condense older events after a configured threshold while retaining recent events in full, reducing the conversation history sent to the model's context window.
  • -
-
-
-

Long-term memory

-

Stores durable information that can be retrieved across sessions using semantic, keyword, or hybrid search.

-
    -
  • Automatic extraction: Process session events asynchronously and store important information with vector embeddings and metadata.
  • -
  • Custom memory types: Define domain-specific memories with structured fields and extraction instructions.
  • -
  • Sensitive-data exclusions: Guide automatic extraction away from specified sensitive information.
  • -
  • Direct memory creation: Create memories through the API or import knowledge from external sources.
  • -
  • Configurable retention: Set a separate TTL for long-term memories.
  • -
-
-
+Session memory keeps the current conversation available to the agent. Long-term memory preserves useful information across conversations. See [Concepts]({{< relref "/develop/ai/context-engine/agent-memory/concepts" >}}) for an overview of both and the optional namespaces that organize them. ### Example: Travel planning agent @@ -112,28 +86,17 @@ Get started with Redis Agent Memory on Redis Cloud or join the private preview f
-### Choose a quickstart +### Follow the quickstart -After your Redis Agent Memory service is ready, choose a client. Each quickstart follows the same travel planning scenario through namespaces, session memory, automatic extraction, summarization, custom memory types, and sensitive-data exclusions. +After your service is ready, follow the [quickstart]({{< relref "/develop/ai/context-engine/agent-memory/quickstart" >}}). Choose Python, TypeScript, or curl to explore the same travel planning scenario. -Use the developer guide to [organize memories with namespaces]({{< relref "/develop/ai/context-engine/agent-memory/developer-guide#organize-memories-with-namespaces" >}}) and [use custom memory types]({{< relref "/develop/ai/context-engine/agent-memory/developer-guide#define-custom-memory-types" >}}). +## Explore the documentation -
-
-

Python SDK

-

Explore the Redis Agent Memory workflow with the Python SDK.

-

}}">Open the Python quickstart

-
-
-

TypeScript SDK

-

Explore the Redis Agent Memory workflow with the TypeScript SDK.

-

}}">Open the TypeScript quickstart

-
-
-

REST API

-

Explore the Redis Agent Memory workflow with curl.

-

}}">Open the REST API quickstart

-
-
+* [Concepts]({{< relref "/develop/ai/context-engine/agent-memory/concepts" >}}): Understand session memory, long-term memory, and namespaces. +* [Quickstart]({{< relref "/develop/ai/context-engine/agent-memory/quickstart" >}}): Store a conversation and recall an extracted memory. +* [Developer guide]({{< relref "/develop/ai/context-engine/agent-memory/developer-guide" >}}): Put the pieces together in an application. +* [Sessions]({{< relref "/develop/ai/context-engine/agent-memory/sessions" >}}): Store conversation events and configure retention and summarization. +* [Long-term memory]({{< relref "/develop/ai/context-engine/agent-memory/long-term-memory" >}}): Create and retrieve memories across conversations, including custom memory types. +* [Namespaces]({{< relref "/develop/ai/context-engine/agent-memory/namespaces" >}}): Organize memories and manage namespace hierarchies. -For shared integration concepts, identifiers, and authentication, see the [Redis Agent Memory developer guide](/content/develop/ai/context-engine/agent-memory/developer-guide.md). +* [API reference]({{< relref "/develop/ai/context-engine/agent-memory/api-reference" >}}): Look up request and response schemas. diff --git a/content/develop/ai/context-engine/agent-memory/concepts.md b/content/develop/ai/context-engine/agent-memory/concepts.md new file mode 100644 index 0000000000..6dcb51a40e --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/concepts.md @@ -0,0 +1,38 @@ +--- +alwaysopen: false +categories: +- docs +- develop +- ai +description: Understand session memory, long-term memory, and namespaces in Redis Agent Memory. +hideListLinks: true +linktitle: Concepts +title: Concepts +weight: 4 +--- + +Redis Agent Memory keeps conversation context in session memory and information for future conversations in long-term memory. Optional namespaces organize both. + +## Session memory + +Session memory is the ordered history of a conversation. It includes messages and their roles, timestamps, and metadata. Use it to give an agent the context it needs to continue an interaction, such as the cities a user has discussed with a travel agent. + +Sessions have configurable retention. Automatic summarization condenses older messages while keeping recent messages in full, so a long conversation takes less space in the model's context window. + +For event fields, retrieval, retention, and summarization settings, see [Sessions]({{< relref "/develop/ai/context-engine/agent-memory/sessions" >}}). + +## Long-term memory + +Long-term memory holds information that remains useful across conversations, such as a user's dietary requirements. It lets an agent recall relevant information in a later session, including after the original session expires. Long-term memory has its own retention settings. + +Redis Agent Memory can extract memories from session events in the background. Applications can also create memories directly. Custom memory types add structured fields for information specific to your application. + +For creation and retrieval details, see [Long-term memory]({{< relref "/develop/ai/context-engine/agent-memory/long-term-memory" >}}). For schemas and extraction instructions, see [Custom memory types]({{< relref "/develop/ai/context-engine/agent-memory/long-term-memory#custom-memory-types" >}}). + +## Namespaces + +Namespaces are optional groups for session memory and long-term memory. Use them to organize information by user, project, or team. A travel application could group a user's conversations and extracted memories in a personal travel namespace. + +Namespaces can form a hierarchy, with personal or shared scope. Each namespace has a stable ID, so applications can keep referring to it when its name or path changes. + +For creation, memory placement, search, and management, see [Namespaces]({{< relref "/develop/ai/context-engine/agent-memory/namespaces" >}}). diff --git a/content/develop/ai/context-engine/agent-memory/developer-guide.md b/content/develop/ai/context-engine/agent-memory/developer-guide.md index 023c6ba9f1..a3d42e4b61 100644 --- a/content/develop/ai/context-engine/agent-memory/developer-guide.md +++ b/content/develop/ai/context-engine/agent-memory/developer-guide.md @@ -8,262 +8,82 @@ description: Connect an application to Redis Agent Memory and work with session hideListLinks: true linktitle: Developer guide title: Redis Agent Memory developer guide -weight: 5 +weight: 6 --- -Use the Python SDK, TypeScript SDK, or REST API to add session events, retrieve conversation context, create long-term memories, and search for relevant information. +Use session memory and long-term memory in your agent's request cycle. Complete the [quickstart]({{< relref "/develop/ai/context-engine/agent-memory/quickstart" >}}) first to verify the connection and make your first requests. For the memory model, see [Concepts]({{< relref "/develop/ai/context-engine/agent-memory/concepts" >}}). -## Integration workflow +## Configure the application -1. Connect to a Redis Agent Memory service with its endpoint, Store ID, and API key. -1. Add conversation events to session memory. -1. Retrieve session memory before an agent turn to reconstruct the conversation context. -1. Search long-term memory for information relevant to the current interaction. +Keep the service endpoint, Store ID, and application programming interface (API) key in application configuration. Load the key from a secret store or environment variable. The quickstart shows client initialization for Python, TypeScript, and curl. -Redis Agent Memory can automatically summarize older session events and extract long-term memories in the background. Applications can also create long-term memories directly. +Choose identifiers your application can reuse: -## Choose a client +| Identifier | Application choice | +|:-----------|:-------------------| +| `sessionId` | Use one ID for a conversation. Reuse it on later turns and use a new ID for a new conversation. | +| `actorId` | Identify who produced each event, such as the user or assistant. | +| `ownerId` | Use a stable user or entity ID when creating and searching long-term memories. | -| Client | Use it when | Package and quickstart | -|:-------|:------------|:-----------------------| -| Python SDK | Your application or agent uses Python. | Install [`redis-agent-memory`](https://pypi.org/project/redis-agent-memory/) and follow the [Python SDK quickstart](/content/develop/ai/context-engine/agent-memory/python-sdk-quickstart.md). | -| TypeScript SDK | Your application or agent uses JavaScript or TypeScript. | Install [`@redis-iris/agent-memory`](https://www.npmjs.com/package/@redis-iris/agent-memory) and follow the [TypeScript SDK quickstart](/content/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart.md). | -| REST API | You need language-independent HTTP access or don't want an SDK dependency. | No package required. Follow the [REST API quickstart](/content/develop/ai/context-engine/agent-memory/rest-api-quickstart.md). | +Resolve the user and their allowed sessions in your application before making memory requests. Build recall filters from that identity. Search filters do not replace application access checks. -## Connect to a Redis Agent Memory service +## Build context for each agent turn -Every client requires: +For example, a user returns to a travel agent and asks for restaurants in Kyoto. The application needs the current conversation and any relevant preferences from earlier conversations. -* The Redis Agent Memory endpoint. -* The Store ID. -* A Redis Agent Memory API key. +1. Retrieve session memory with the conversation's `sessionId`. +1. Search long-term memory using the user's request and an `ownerId` filter. +1. Build the model context from the session summary, recent events, relevant search results, and the current user message. +1. Call the model to produce the assistant's response. +1. Store the new user and assistant messages as session events, with their roles and actor IDs. -The Python and TypeScript SDKs accept the endpoint, Store ID, and API key when you create the client. When you use the REST API, send the API key as a bearer token and include the Store ID in request paths. +Include the current user message once. If your application stores it before retrieval, avoid adding a second copy to the model context. Store the assistant message after it has been generated. -Follow the [Redis Cloud setup guide](/content/operate/iris/agent-memory/create-service.md) if you don't have a service. After you create one, copy its endpoint and Store ID from the **Configuration** tab and save the API key securely. +Select relevant memories and recent conversation history that fit your model's context window. Use the session summary when automatic summarization is enabled. See [Sessions]({{< relref "/develop/ai/context-engine/agent-memory/sessions" >}}) for event fields, retention, and summarization. -## Identify users and conversations +## Handle long-term recall -| Identifier | Purpose | -|:-----------|:--------| -| `sessionId` | Identifies a conversation or interaction session. | -| `actorId` | Identifies the actor that produced a session event. | -| `ownerId` | Identifies the user or entity associated with a long-term memory. | -| `namespaceId` | Identifies a namespace resource used to group sessions and long-term memories. | -| Memory ID | Uniquely identifies a long-term memory within the store. | +Automatic extraction runs in the background. A successful event write does not mean that a long-term memory is ready to search. Use the current message and session history to answer the current turn; allow later turns to recall extracted information. -An application can use the same user identifier for `actorId` and `ownerId`, but the fields describe different relationships. +An empty search result means that no memories matched the request. It can also occur while extraction is pending. Continue with the available conversation context. If expected memories remain absent, check the extraction settings and search filters. -## Organize memories with namespaces - -Use a namespace to group memories for a project, team, or user. For example, a travel agent can keep a user's trip memories in a personal `travel` namespace. A namespace controls where you place and search for memories; a custom memory type describes the information in each record. - -Create the namespace before writing to it. The response contains a server-generated `namespaceId`. Save this ID and use it in `namespaceRef` on session events and long-term memory writes. Names and paths can change when you rename a namespace; its ID stays the same. - -### Create a namespace hierarchy - -Choose a root scope when you create a namespace: - -| Scope | Creation fields | Example use | -|:------|:----------------|:------------| -| `PERSONAL` | `name`, `scope`, and `ownerId` | A user's travel plans. | -| `SHARED` | `name` and `scope`, without `ownerId` | Information used across a team. | - -Create a child with `name` and `parentId`. You can also create a hierarchy with `path`, such as `travel/japan`, and the root scope. Supply `ownerId` for a personal path. Missing path segments are created as needed. Do not combine `path` with `name` or `parentId`. - -Names and paths are case-sensitive. The path does not encode scope. Use the returned ID to distinguish namespaces that have the same path under different owners or scopes. - -Follow the [REST quickstart]({{< relref "/develop/ai/context-engine/agent-memory/rest-api-quickstart#create-a-namespace" >}}), [Python quickstart]({{< relref "/develop/ai/context-engine/agent-memory/python-sdk-quickstart#create-a-namespace" >}}), or [TypeScript quickstart]({{< relref "/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart#create-a-namespace" >}}) to create a personal namespace and use its ID throughout a conversation. - -### Place and retrieve memories - -Pass `namespaceRef: {"namespaceId": ""}` when you start a session. Memories extracted from the session use that namespace. Use a new session ID when following the quickstarts if you already ran them without a namespace. - -For direct long-term memory creation, set `namespaceRef` on each record. To move existing records, use `MoveLongTermMemories` with their IDs and the destination `namespaceRef`. Check both `moved` and `errors` in the response before treating a batch as complete. - -Search with `filter.namespaceRef.eq` for one namespace or `filter.namespaceRef.in` for several IDs. These filters match exact namespace IDs; they do not expand a parent into its descendants. List the children and include their IDs when you need to search several levels of a hierarchy. Keep the `ownerId` filter when recalling one user's memories. Namespace scope and search filters do not replace your application's access checks. - -### Manage namespaces - -| Operation | Behavior | -|:----------|:---------| -| List | List roots, or pass `parentId` to list direct children. Follow `nextPageToken` for additional pages. | -| Get | Retrieve the current name, path, scope, and state by ID. | -| Rename | Update `name`; continue using the same ID for placement and retrieval. | -| Archive | Set `state` to `ARCHIVED` to stop new children and placements. The update API does not offer an unarchive transition. | -| Delete | Delete an empty leaf namespace. A namespace with children or memory placements returns `409 Conflict`. | - -Creating a namespace at an occupied location returns `409 Conflict`, including when the existing namespace is archived. Save and reuse the returned ID instead of creating the same namespace on each agent turn. For request fields and error responses, see the [namespace API reference]({{< relref "/develop/ai/context-engine/agent-memory/api-reference" >}}#operation/CreateNamespace). - -### Migrate legacy namespace labels - -The `namespace` string is deprecated. It is a label, and does not create or resolve a namespace resource. Creating a resource with the same name does not move records that carry the old label. A `namespaceRef` search matches only records placed in a namespace resource. - -1. Create the destination namespace and save its ID. -1. Find the records to move with the legacy `filter.namespace` and the appropriate owner filter. Follow search pagination and review the IDs before moving them. -1. Move the selected records with `MoveLongTermMemories`. -1. Check `moved` and any per-record `errors`, then search with `filter.namespaceRef` to verify the new placement. -1. Update application writes and searches to use `namespaceRef`. Start new sessions with the namespace reference for future extraction. - -Using the connection variables from the [REST quickstart]({{< relref "/develop/ai/context-engine/agent-memory/rest-api-quickstart#save-the-connection-values" >}}), move a selected record as follows. Set `NAMESPACE_ID` to the destination ID and replace `` with a reviewed record ID: - -```sh -curl --fail-with-body --silent --show-error \ - --request POST \ - --header "Authorization: Bearer $API_KEY" \ - --header 'Content-Type: application/json' \ - --data "$(jq -n --arg ns "$NAMESPACE_ID" --arg id '' \ - '{memoryIds: [$id], namespaceRef: {namespaceId: $ns}}')" \ - "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/long-term-memory/move" | jq -``` - -Do not send `namespace` and `namespaceRef` together. Moving long-term records does not reconfigure the session that produced them. - -## Work with session memory - -Use a stable `sessionId` to store a conversation as an ordered sequence of events. Add an event for each user, assistant, or system message that your application needs to retain. - -Each stored event can include: - -| Field | Purpose | -|:------|:--------| -| `eventId` | Server-generated identifier for the event. | -| `sessionId` | Session that contains the event. | -| `actorId` | User, agent, or other actor that produced the event. | -| `role` | Role of the message in the conversation. | -| `content` | Message content, including its text. | -| `createdAt` | Time the event occurred in the application. | -| `systemTimestamp` | Time Redis Agent Memory stored the event. | -| `metadata` | Optional application-specific information associated with the event. | - -Before an agent turn, retrieve the session by `sessionId` and provide the relevant events to the agent. This lets the application reconstruct the conversation without maintaining a separate conversation store. - -### Session retention +Treat a failed request separately from an empty result. Decide whether your application can answer with reduced context or needs to ask the user to retry. Check the API response before retrying: correct authentication and request errors first. Before retrying a write after a timeout, check whether it succeeded to avoid duplicate events or records. -The session-memory TTL controls how long sessions remain available. Configure it according to the retention requirements of your application. When a session expires, its events are no longer available through session-memory retrieval. - -See [memory configuration](/content/operate/iris/agent-memory/create-service.md#memory-configuration) to configure the session-memory TTL in Redis Cloud. - -### Automatic session summarization - -Automatic summarization limits the amount of conversation history that must be added to a model's context window. Configure: - -* **Summarize after:** The number of messages a session can contain before older messages are summarized. -* **Keep most recent:** The number of recent messages that remain available in full. - -For example, with **Summarize after** set to 20 and **Keep most recent** set to 10, Redis Agent Memory summarizes the older 10 messages when the session reaches 20 messages and retains the 10 most recent messages in full. +### Create long-term memories -See [automatic summarization](/content/operate/iris/agent-memory/create-service.md#automatic-summarization) to enable summarization and configure both thresholds in Redis Cloud. +Create memories directly when your application already has information to store, such as a preference submitted through a form. Set the owner and a stable record ID, and check the bulk response for per-record errors. Direct creation does not apply automatic extraction or sensitive-data exclusions. -Follow any of the client quickstarts to add and retrieve a session event. For complete schemas, see the [session-memory API reference](/content/develop/ai/context-engine/agent-memory/api-reference.md#tag/session-memory). +See [Long-term memory]({{< relref "/develop/ai/context-engine/agent-memory/long-term-memory" >}}) for creation methods, search fields, retention, and extraction controls. -## Work with long-term memory +## Define custom memory types -Long-term memory stores information that remains useful beyond one conversation. A long-term memory record includes content and fields that let applications classify, scope, and retrieve it: +If your application needs structured values, define a custom memory type. For example, `trip_preference` can capture `destinations`, `travel_period`, `dietary_requirements`, and `food_preferences`. -| Field | Purpose | -|:------|:--------| -| `id` | Unique identifier for the memory. | -| `text` | Memory content used for retrieval. | -| `memoryType` | Built-in or custom memory type. | -| `sessionId` | Session associated with the memory. | -| `ownerId` | User or entity associated with the memory. | -| `namespaceRef` | Namespace ID and its current name and path. | -| `attributes` | Custom memory fields and their values. | -| `topics` | Topic tags used to categorize the memory. | -| `createdAt` | Time the memory was created. | -| `updatedAt` | Time the memory was last updated. | +Enable the type and supply extraction instructions. Search with the `memoryType` and `ownerId` filters, then read each result's `attributes`. Check for missing fields before using them to choose restaurants. -### Create long-term memories +See [Custom memory types]({{< relref "/develop/ai/context-engine/agent-memory/long-term-memory#custom-memory-types" >}}) to define fields, handle extraction results, and create records directly. -Redis Agent Memory provides two creation paths: +## Organize memories with namespaces -* **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. -* **Direct creation:** Your application creates one or more long-term memories through the API or an SDK. Use direct creation when importing existing information or when your application determines exactly what to store. +Namespaces are optional. Add them when your application needs to group conversations and memories by user, project, or team. -Configure the long-term-memory TTL separately from the session-memory TTL. +For example, create a personal `travel` namespace and save its `namespaceId`. Use that ID in `namespaceRef` when starting a session or creating long-term memories. You will also use this ID to search for memories in that namespace. Names and paths can change when you rename a namespace, but its ID stays the same. -See [memory configuration](/content/operate/iris/agent-memory/create-service.md#memory-configuration) to configure the extraction cadence and long-term-memory TTL in Redis Cloud. +See [Namespaces]({{< relref "/develop/ai/context-engine/agent-memory/namespaces" >}}) for hierarchy, placement, search filters, and management. -### Exclude sensitive data from automatic extraction +## Exclude sensitive data from automatic extraction -Sensitive-data exclusions keep specified information out of long-term memory during automatic extraction. A store's exclusions policy combines three mechanisms that you enable independently: +Sensitive-data exclusions keep specified information out of long-term memory during automatic extraction. Enable the controls your store needs: -* **Built-in detectors:** Validated patterns for common identifiers, such as payment card numbers, email addresses, phone numbers, IP addresses, and US Social Security numbers. Select the ones a store should apply. +* **Built-in detectors:** Validated patterns for common identifiers, such as payment card numbers, email addresses, phone numbers, Internet Protocol (IP) addresses, and US Social Security numbers. Select the ones a store should apply. * **Custom detectors:** Regular expressions you write yourself, for identifiers specific to your domain such as tenant IDs or internal reference numbers. * **Semantic exclusions:** A plain-language exclusion prompt describing what must not be kept, such as passwords, access tokens, recovery codes, payment card information, or booking confirmation codes. Use it for concepts a pattern cannot express. -Every detector chooses what happens to a memory it matches: redact the matched text and keep the rest, or drop the memory. When a memory matches several detectors that choose different actions, the memory is dropped. +For each detector, choose an action: redact the matched text and keep the rest, or drop the memory. When a memory matches several detectors that choose different actions, the memory is dropped. Exclusions apply to automatic extraction from session events. They do not apply when an application creates long-term memories directly, and session memory itself is never altered. > [!WARNING] > Detector matches are deterministic, but semantic exclusions are advisory and do not guarantee exclusion. Sensitive session content still reaches the extraction model provider. Use appropriate controls before sending sensitive information to Redis Agent Memory or the model provider. -See [sensitive-data exclusions](/content/operate/iris/agent-memory/create-service.md#sensitive-data-exclusions) to configure the feature in Redis Cloud. - -### Search long-term memory - -Search long-term memory using semantic, keyword, or hybrid retrieval. Scope results with filters for owners, sessions, namespaces, topics, and memory types. - -Use `ownerId` to restrict recall to the relevant user or entity. Add narrower filters when the application needs memories from a particular session, namespace, topic, or memory type. - -For request fields, filter operators, and response schemas, see [`SearchLongTermMemory`](/content/develop/ai/context-engine/agent-memory/api-reference.md#tag/long-term-memory/operation/SearchLongTermMemory). - -### Define custom memory types - -Custom memory types capture structured information specific to your business domain. Define them in the Redis Agent Memory service configuration with: - -| Setting | Purpose | -|:--------|:--------| -| **Name** | Unique name used as the memory's `memoryType`. | -| **Description** | Description of the information represented by the type. | -| **Extraction prompt** | Instructions that tell Redis Agent Memory when and how to extract the custom memory from session events. | -| **Enabled** | Controls whether Redis Agent Memory extracts new memories for the type. | -| **Custom fields** | Structured fields added to memories of this type. | - -Custom 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 listed above. - -For example, a travel application could define a `trip_preference` type with these fields: - -| Field | Type | Captures | -|:------|:-----|:---------| -| `destinations` | `list[str]` | Cities or countries the user plans to visit. | -| `travel_period` | `str` | Dates or period of the trip. | -| `dietary_requirements` | `list[str]` | Dietary requirements that affect recommendations. | -| `food_preferences` | `list[str]` | Cuisines, flavors, or dining preferences. | - -When 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. - -Search with `memoryType`, `ownerId`, and `namespaceRef` filters together to retrieve this user's trip preferences from the intended namespace. Read the structured fields from each result's `attributes` object. For example, this excerpt shows fields your application can use to constrain restaurant recommendations: - -```json -{ - "memoryType": "trip_preference", - "attributes": { - "destinations": ["Tokyo", "Kyoto"], - "travel_period": "next month", - "dietary_requirements": ["vegetarian"], - "food_preferences": ["spicy food"] - } -} -``` - -This is an illustrative excerpt, not a guaranteed extraction result. Check for missing fields before using them. Extraction runs asynchronously: an empty `items` array can mean that processing is not finished or no matching information was extracted. Retry after a short wait, then check the type's enabled setting, extraction prompt, and search filters if results remain empty. Do not interpret an empty result as the absence of a dietary requirement. - -You can also create custom memories directly. Register the type on the service first, then send `id`, `text`, `ownerId`, `memoryType`, `attributes`, and optionally `namespaceRef` through `BulkCreateLongTermMemories`. Attribute names and JSON values must match the registered fields. Use this path for structured data from an application form or an import. It does not run the extraction prompt or sensitive-data exclusions. - -The quickstarts show both extraction and direct creation with the same `trip_preference` schema. Custom types are configured for the service; placing a record in a namespace does not define a new type. - -See [custom memory types](/content/operate/iris/agent-memory/create-service.md#custom-memory-types) for configuration requirements and limits. - -Follow any of the client quickstarts to create and search long-term memory. For complete schemas, see the [long-term-memory API reference](/content/develop/ai/context-engine/agent-memory/api-reference.md#tag/long-term-memory). - -## References - -* [Python SDK quickstart](/content/develop/ai/context-engine/agent-memory/python-sdk-quickstart.md) -* [TypeScript SDK quickstart](/content/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart.md) -* [Python SDK reference](https://pypi.org/project/redis-agent-memory/) -* [TypeScript SDK reference](https://www.npmjs.com/package/@redis-iris/agent-memory) -* [REST API quickstart](/content/develop/ai/context-engine/agent-memory/rest-api-quickstart.md) -* [Redis Agent Memory API reference](/content/develop/ai/context-engine/agent-memory/api-reference.md) +See [sensitive-data exclusions]({{< relref "/operate/iris/agent-memory/create-service#sensitive-data-exclusions" >}}) to configure the feature in Redis Cloud. diff --git a/content/develop/ai/context-engine/agent-memory/long-term-memory.md b/content/develop/ai/context-engine/agent-memory/long-term-memory.md new file mode 100644 index 0000000000..7789c9557b --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/long-term-memory.md @@ -0,0 +1,436 @@ +--- +alwaysopen: false +categories: +- docs +- develop +- ai +description: Create and search memories that remain useful across conversations. +hideListLinks: true +linktitle: Long-term memory +title: Long-term memory +weight: 11 +aliases: +- /develop/ai/context-engine/agent-memory/custom-memory-types/ +--- + +Long-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. + +## Memory fields + +A long-term memory record includes: + +| Field | Purpose | +|:------|:--------| +| `id` | Unique identifier for the memory. | +| `text` | Memory content used for retrieval. | +| `memoryType` | Built-in or custom memory type. | +| `sessionId` | Session associated with the memory. | +| `ownerId` | User or entity associated with the memory. | +| `namespaceRef` | Optional namespace ID and its current name and path. | +| `attributes` | Custom memory fields and their values. | +| `topics` | Topic tags used to categorize the memory. | +| `createdAt` | Time the memory was created. | +| `updatedAt` | Time the memory was last updated. | + +## Create long-term memories + +Redis Agent Memory provides two ways to create memories: + +* **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. +* **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. + +Configure the long-term-memory time to live (TTL) separately from the session-memory TTL. + +See [memory configuration]({{< relref "/operate/iris/agent-memory/create-service#memory-configuration" >}}) to configure the extraction cadence and long-term-memory TTL in Redis Cloud. + +## Search long-term memory + +Search long-term memory semantically and narrow results with filters. Scope results with filters for owners, sessions, namespaces, topics, and memory types. + +Use `ownerId` to restrict recall to the relevant user or entity. Add narrower filters when the application needs memories from a particular session, namespace, topic, or memory type. + +For request fields, filter operators, and response schemas, see [`SearchLongTermMemory`]({{< relref "/develop/ai/context-engine/agent-memory/api-reference" >}}#operation/SearchLongTermMemory). + +For domain-specific fields and extraction instructions, see [Custom memory types]({{< relref "/develop/ai/context-engine/agent-memory/long-term-memory#custom-memory-types" >}}). To group memories in a hierarchy, see [Namespaces]({{< relref "/develop/ai/context-engine/agent-memory/namespaces" >}}). + +For controls that apply during automatic extraction, see [Exclude sensitive data from automatic extraction]({{< relref "/develop/ai/context-engine/agent-memory/developer-guide#exclude-sensitive-data-from-automatic-extraction" >}}). + +## Custom memory types + +Custom memory types capture structured information specific to your business domain. + +### Define a memory type + +Configure the type on your Redis Agent Memory service with: + +| Setting | Purpose | +|:--------|:--------| +| **Name** | Unique name used as the memory's `memoryType`. | +| **Description** | Description of the information represented by the type. | +| **Extraction prompt** | Instructions that tell Redis Agent Memory when and how to extract the custom memory from session events. | +| **Enabled** | Controls whether Redis Agent Memory extracts new memories for the type. | +| **Custom fields** | Structured fields added to memories of this type. | + +Custom 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]({{< relref "/develop/ai/context-engine/agent-memory/long-term-memory#memory-fields" >}}). + +For example, a travel application can define a `trip_preference` type with these fields: + +| Field | Type | Captures | +|:------|:-----|:---------| +| `destinations` | `list[str]` | Cities or countries the user plans to visit. | +| `travel_period` | `str` | Dates or period of the trip. | +| `dietary_requirements` | `list[str]` | Dietary requirements that affect recommendations. | +| `food_preferences` | `list[str]` | Cuisines, flavors, or dining preferences. | + +When 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. + +### Extract custom memories + +Complete the [quickstart]({{< relref "/develop/ai/context-engine/agent-memory/quickstart" >}}) first. Reuse its client, connection values, and user and session identifiers. Add Python snippets inside the `with` block and TypeScript snippets inside `run`. Run curl commands in the same shell. Run each write once; comment out completed writes and their output statements before rerunning an SDK file. + +Custom memory types let an application extract structured information for its business domain. Configure and enable the `trip_preference` type with the fields in [Define a memory type]({{< relref "/develop/ai/context-engine/agent-memory/long-term-memory#define-a-memory-type" >}}). Use this extraction prompt: + +```text +Extract trip requirements only when the user states a destination or travel plan. Preserve explicit dietary requirements and food preferences. +``` + +After enabling the type, add the travel message from the quickstart to a new session. Wait at least one extraction interval before searching. + +Search for the structured memory: + +{{< multitabs id="custom-memory-search" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +```python + custom_results = agent_memory.search_long_term_memory( + request={ + "text": "What are the requirements for the user's trip?", + "filter_": { + "owner_id": {"eq": USER_ID}, + "memory_type": {"eq": "trip_preference"}, + }, + "limit": 5, + }, + ) + show("Trip preference memories", custom_results) +``` + +-tab-sep- + +```typescript + const customResults = await agentMemory.searchLongTermMemory({ + text: "What are the requirements for the user's trip?", + filter: { + ownerId: { eq: userId }, + memoryType: { eq: "trip_preference" }, + }, + limit: 5, + }); + console.log("Trip preference memories:"); + console.dir(customResults, { depth: null }); +``` + +-tab-sep- + +```sh +curl --fail-with-body --silent --show-error \ + --request POST \ + --header "Authorization: Bearer $API_KEY" \ + --header 'Content-Type: application/json' \ + --data @- \ + "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/long-term-memory/search" <}} + +The `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: + +```json +{ + "memoryType": "trip_preference", + "attributes": { + "destinations": ["Tokyo", "Kyoto"], + "travel_period": "next month", + "dietary_requirements": ["vegetarian"], + "food_preferences": ["spicy food"] + } +} +``` + +The 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. + +Inspect the structured fields. The SDK examples collect dietary requirements for the next agent turn: + +{{< multitabs id="custom-memory-fields" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +```python + dietary_requirements = set() + if not custom_results.items: + print("No matching trip preferences yet. Retry after a short wait.") + for memory in custom_results.items: + attributes = memory.attributes or {} + requirements = attributes.get("dietary_requirements", []) + if isinstance(requirements, list): + dietary_requirements.update( + value for value in requirements if isinstance(value, str) + ) + print("Restaurant requirements:", sorted(dietary_requirements)) +``` + +-tab-sep- + +```typescript + const dietaryRequirements = new Set(); + if (customResults.items.length === 0) { + console.log("No matching trip preferences yet. Retry after a short wait."); + } + for (const memory of customResults.items) { + const requirements = memory.attributes?.dietary_requirements; + if (Array.isArray(requirements)) { + for (const value of requirements) { + if (typeof value === "string") dietaryRequirements.add(value); + } + } + } + console.log("Restaurant requirements:", [...dietaryRequirements].sort()); +``` + +-tab-sep- + +Run the search with this filter in place of the final `jq` command: + +```sh +jq 'if (.items | length) == 0 then + {status: "No matching trip preferences yet. Retry after a short wait."} + else + .items[] | {id, attributes} + end' +``` + +{{< /multitabs >}} + +If 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. + + +### Create a custom memory directly + +If your application already has structured trip data, write it directly using the registered `trip_preference` type. This optional example represents data from a form and uses the same user identifier: + +{{< multitabs id="direct-memory" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +```python + direct_result = agent_memory.bulk_create_long_term_memories(memories=[{ + "id": "trip-form-1", + "text": "The user plans to visit Tokyo and Kyoto next month and requires vegetarian food.", + "owner_id": USER_ID, + "memory_type": "trip_preference", + "attributes": { + "destinations": ["Tokyo", "Kyoto"], + "travel_period": "next month", + "dietary_requirements": ["vegetarian"], + "food_preferences": ["spicy food"], + }, + }]) + show("Directly created trip preference", direct_result) +``` + +-tab-sep- + +```typescript + const directResult = await agentMemory.bulkCreateLongTermMemories({ + memories: [{ + id: "trip-form-1", + text: "The user plans to visit Tokyo and Kyoto next month and requires vegetarian food.", + ownerId: userId, + memoryType: "trip_preference", + attributes: { + destinations: ["Tokyo", "Kyoto"], + travel_period: "next month", + dietary_requirements: ["vegetarian"], + food_preferences: ["spicy food"], + }, + }], + }); + console.dir(directResult, { depth: null }); +``` + +-tab-sep- + +```sh +curl --fail-with-body --silent --show-error \ + --request POST \ + --header "Authorization: Bearer $API_KEY" \ + --header 'Content-Type: application/json' \ + --data @- \ + "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/long-term-memory" <}} + +Inspect the bulk response 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. Run this write once; the memory ID identifies the record within the store. + +See [custom memory types]({{< relref "/operate/iris/agent-memory/create-service#custom-memory-types" >}}) for configuration requirements and limits. + +## Test semantic exclusions + +Complete the [quickstart]({{< relref "/develop/ai/context-engine/agent-memory/quickstart" >}}) first. Reuse its client, connection values, and user and session identifiers. Add Python snippets inside the `with` block and TypeScript snippets inside `run`. Run curl commands in the same shell. Run each write once; comment out completed writes and their output statements before rerunning an SDK file. + +In the service configuration, enable **Semantic exclusions** and set this prompt: + +```text +Do not keep passwords, access tokens, recovery codes, payment card information, or booking confirmation codes in long-term memory. +``` + +The 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: + +{{< multitabs id="exclusion-event" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +```python + sensitive_event = agent_memory.add_session_event( + session_id=SESSION_ID, + actor_id=USER_ID, + role=models.MessageRole.USER, + content=[models.Text( + text=( + "I booked Hotel Sakura in Tokyo. For this example, " + "the fictional booking confirmation code is DEMO-7QX9." + ), + )], + created_at=datetime.now(timezone.utc), + ) + show("Event with excluded information", sensitive_event) +``` + +-tab-sep- + +```typescript + const sensitiveEvent = await agentMemory.addSessionEvent({ + sessionId, + actorId: userId, + role: "USER", + content: [{ + text: "I booked Hotel Sakura in Tokyo. For this example, the fictional booking confirmation code is DEMO-7QX9.", + }], + createdAt: new Date(), + }); + console.log("Event with excluded information:"); + console.dir(sensitiveEvent, { depth: null }); +``` + +-tab-sep- + +```sh +export EVENT_CREATED_AT="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" + +curl --fail-with-body --silent --show-error \ + --request POST \ + --header "Authorization: Bearer $API_KEY" \ + --header 'Content-Type: application/json' \ + --data @- \ + "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/session-memory/events" <}} + +Run the event write once. In the SDK files, comment out event creation and its output statements before subsequent runs. Wait at least one minute and search for the safe hotel information: + +{{< multitabs id="exclusion-search" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +```python + exclusion_results = agent_memory.search_long_term_memory( + request={ + "text": "Where is the user staying in Tokyo?", + "filter_": { + "owner_id": {"eq": USER_ID}, + }, + "limit": 5, + }, + ) + show("Memories after semantic exclusion", exclusion_results) +``` + +-tab-sep- + +```typescript + const exclusionResults = await agentMemory.searchLongTermMemory({ + text: "Where is the user staying in Tokyo?", + filter: { + ownerId: { eq: userId }, + }, + limit: 5, + }); + console.log("Memories after semantic exclusion:"); + console.dir(exclusionResults, { depth: null }); +``` + +-tab-sep- + +```sh +curl --fail-with-body --silent --show-error \ + --request POST \ + --header "Authorization: Bearer $API_KEY" \ + --header 'Content-Type: application/json' \ + --data @- \ + "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/long-term-memory/search" <}} + +Inspect the returned memories. They can retain the hotel name, but should not contain `DEMO-7QX9` because the exclusion prompt covers booking confirmation codes. + +> [!NOTE] +> **What to expect:** A memory similar to `User booked Hotel Sakura in Tokyo` without the fictional confirmation code. If the code appears, refine the exclusion prompt and test again. Exclusions remain advisory. + +> [!WARNING] +> 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. + +See [sensitive-data exclusions]({{< relref "/operate/iris/agent-memory/create-service#sensitive-data-exclusions" >}}) for configuration details. diff --git a/content/develop/ai/context-engine/agent-memory/namespaces.md b/content/develop/ai/context-engine/agent-memory/namespaces.md new file mode 100644 index 0000000000..de6a314f9c --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/namespaces.md @@ -0,0 +1,234 @@ +--- +alwaysopen: false +categories: +- docs +- develop +- ai +description: Group session memory and long-term memory with optional namespaces. +hideListLinks: true +linktitle: Namespaces +title: Namespaces +weight: 13 +--- + +Namespaces are optional. Use them to group memories for a project, team, or user. For example, a travel agent can keep a user's trip memories in a personal `travel` namespace. + +Create the namespace before writing to it. The response contains a server-generated `namespaceId`. Save this ID and use it in `namespaceRef` on session events and long-term memory writes. You will also use this ID to search for memories in that namespace. Names and paths can change when you rename a namespace, but its ID stays the same. + +## Create a namespace hierarchy + +Choose a root scope when you create a namespace: + +| Scope | Creation fields | Example use | +|:------|:----------------|:------------| +| `PERSONAL` | `name`, `scope`, and `ownerId` | A user's travel plans. | +| `SHARED` | `name` and `scope`, without `ownerId` | Information used across a team. | + +Create a child with `name` and `parentId`. You can also create a hierarchy with `path`, such as `travel/japan`, and the root scope. Supply `ownerId` for a personal path. Missing path segments are created as needed. Do not combine `path` with `name` or `parentId`. + +Names and paths are case-sensitive. The path does not encode scope. Use the returned ID to distinguish namespaces that have the same path under different owners or scopes. + +For runnable examples, see [Create a personal namespace]({{< relref "/develop/ai/context-engine/agent-memory/namespaces#create-a-personal-namespace" >}}) or [Create a shared namespace]({{< relref "/develop/ai/context-engine/agent-memory/namespaces#create-a-shared-namespace" >}}). + +## Place and retrieve memories + +Pass `namespaceRef: {"namespaceId": ""}` when you start a session. Memories extracted from the session use that namespace. Use a new session ID when adding a namespace to the quickstart example. + +For direct long-term memory creation, set `namespaceRef` on each record. To move existing records, use `MoveLongTermMemories` with their IDs and the destination `namespaceRef`. Check both `moved` and `errors` in the response before treating a batch as complete. + +Search with `filter.namespaceRef.eq` for one namespace or `filter.namespaceRef.in` for several IDs. These filters match exact namespace IDs; they do not expand a parent into its descendants. List the children and include their IDs when you need to search several levels of a hierarchy. Keep the `ownerId` filter when recalling one user's memories. Namespace scope and search filters do not replace your application's access checks. + +## Manage namespaces + +| Operation | Behavior | +|:----------|:---------| +| List | List roots, or pass `parentId` to list direct children. Follow `nextPageToken` for additional pages. | +| Get | Retrieve the current name, path, scope, and state by ID. | +| Rename | Update `name`; continue using the same ID for placement and retrieval. | +| Archive | Set `state` to `ARCHIVED` to stop new children and placements. The update application programming interface (API) does not offer an unarchive transition. | +| Delete | Delete an empty leaf namespace. A namespace with children or memory placements returns `409 Conflict`. | + +Creating a namespace at an occupied location returns `409 Conflict`, including when the existing namespace is archived. Save and reuse the returned ID instead of creating the same namespace on each agent turn. For request fields and error responses, see the [namespace API reference]({{< relref "/develop/ai/context-engine/agent-memory/api-reference" >}}#operation/CreateNamespace). + +## Create a personal namespace + +Complete the [quickstart]({{< relref "/develop/ai/context-engine/agent-memory/quickstart" >}}) first. Reuse its client, connection values, and user and session identifiers. Add Python snippets inside the `with` block and TypeScript snippets inside `run`. Run curl commands in the same shell. Run each write once; comment out completed writes and their output statements before rerunning an SDK file. + +Namespaces are optional. This example uses a personal namespace to group the trip memories. + +{{< multitabs id="namespace" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +Add this code inside the `with` block. It creates a personal namespace for the user's travel memories when `NAMESPACE_ID` is not set: + +```python + namespace_id = os.environ.get("NAMESPACE_ID") + if not namespace_id: + created_namespace = agent_memory.create_namespace(request={ + "name": "travel", + "scope": models.NamespaceScope.PERSONAL, + "owner_id": USER_ID, + }) + namespace_id = created_namespace.namespace.namespace_id + print(f'export NAMESPACE_ID="{namespace_id}"') + namespace_ref = {"namespace_id": namespace_id} +``` + +Run the file once, then run the printed `export` command in your shell before running the file again. Later runs reuse that ID. Creating the same namespace again returns `409 Conflict`. If you lose the ID, use `list_namespaces` with `scope="PERSONAL"` and `owner_id=USER_ID` to find it. + +Use a fresh `SESSION_ID` if you already ran this quickstart without a namespace. For new session events, add `namespace_ref=namespace_ref` in Python, `namespaceRef` in TypeScript, or `"namespaceRef": {"namespaceId": "$NAMESPACE_ID"}` in curl. Extracted memories use the session namespace. + +-tab-sep- + +Add this code inside `run`. It creates a personal namespace for the user's travel memories when `NAMESPACE_ID` is not set: + +```typescript + let namespaceId = process.env.NAMESPACE_ID; + if (!namespaceId) { + const createdNamespace = await agentMemory.createNamespace({ + name: "travel", + scope: "PERSONAL", + ownerId: userId, + }); + namespaceId = createdNamespace.namespace.namespaceId; + console.log(`export NAMESPACE_ID="${namespaceId}"`); + } + const namespaceRef = { namespaceId }; +``` + +Run the file once, then run the printed `export` command in your shell before running the file again. Later runs reuse that ID. Creating the same namespace again returns `409 Conflict`. If you lose the ID, use `listNamespaces` with `scope: "PERSONAL"` and `ownerId: userId` to find it. + +Use a fresh `sessionId` if you already ran this quickstart without a namespace. For new session events, add `namespace_ref=namespace_ref` in Python, `namespaceRef` in TypeScript, or `"namespaceRef": {"namespaceId": "$NAMESPACE_ID"}` in curl. Extracted memories use the session namespace. + +-tab-sep- + +Create a personal namespace for the user's travel memories. Run this request once and keep the returned ID in your shell: + +```sh +NAMESPACE_ID=$(curl --fail-with-body --silent --show-error \ + --request POST \ + --header "Authorization: Bearer $API_KEY" \ + --header 'Content-Type: application/json' \ + --data "$(jq -n --arg owner "$OWNER_ID" \ + '{name: "travel", scope: "PERSONAL", ownerId: $owner}')" \ + "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/namespaces" | jq -er '.namespace.namespaceId') +export NAMESPACE_ID +printf '%s\n' "$NAMESPACE_ID" +``` + +Continue only after the request succeeds and prints a namespace ID. Save that ID for later runs. Creating the same namespace again returns `409 Conflict`; use the existing namespace ID instead. If you lose the ID, list personal roots with `GET /v1/stores/{storeId}/namespaces?scope=PERSONAL&ownerId=`. + +Use a fresh `SESSION_ID` if you already ran this quickstart without a namespace. For new session events, add `namespace_ref=namespace_ref` in Python, `namespaceRef` in TypeScript, or `"namespaceRef": {"namespaceId": "$NAMESPACE_ID"}` in curl. Extracted memories use the session namespace. + +{{< /multitabs >}} + +## Create a shared namespace + +A shared namespace groups memories that an application uses across users or agents. For example, a travel-planning team can use a shared `team-travel` namespace for information that everyone on the team needs. The namespace has `SHARED` scope and no namespace owner. Individual long-term memories still have their own `ownerId` values. + +Applications use the same namespace ID when starting sessions or creating long-term memories for the team. Each conversation keeps its own session ID. Memories extracted from those sessions use the shared namespace, so a later search can recall information from several conversations and owners. + +Your application decides who can contribute to and retrieve the team's memories. Check the user's access before writing or searching. `SHARED` scope describes how memories are grouped; it does not grant users permission to access them. + +Use the client and connection values from the [quickstart]({{< relref "/develop/ai/context-engine/agent-memory/quickstart" >}}). Create the namespace with `scope` set to `SHARED` and omit `ownerId`: + +{{< multitabs id="shared-namespace" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +Add this code inside the `with` block: + +```python + shared_namespace_id = os.environ.get("SHARED_NAMESPACE_ID") + if not shared_namespace_id: + shared_namespace = agent_memory.create_namespace(request={ + "name": "team-travel", + "scope": models.NamespaceScope.SHARED, + }) + shared_namespace_id = shared_namespace.namespace.namespace_id + print(f'export SHARED_NAMESPACE_ID="{shared_namespace_id}"') + shared_namespace_ref = {"namespace_id": shared_namespace_id} +``` + +Run the file once, then run the printed `export` command in your shell. Later runs reuse the namespace ID. + +-tab-sep- + +Add this code inside `run`: + +```typescript + let sharedNamespaceId = process.env.SHARED_NAMESPACE_ID; + if (!sharedNamespaceId) { + const sharedNamespace = await agentMemory.createNamespace({ + name: "team-travel", + scope: "SHARED", + }); + sharedNamespaceId = sharedNamespace.namespace.namespaceId; + console.log(`export SHARED_NAMESPACE_ID="${sharedNamespaceId}"`); + } + const sharedNamespaceRef = { namespaceId: sharedNamespaceId }; +``` + +Run the file once, then run the printed `export` command in your shell. Later runs reuse the namespace ID. + +-tab-sep- + +Run this request once and save the returned ID: + +```sh +SHARED_NAMESPACE_ID=$(curl --fail-with-body --silent --show-error \ + --request POST \ + --header "Authorization: Bearer $API_KEY" \ + --header 'Content-Type: application/json' \ + --data '{"name":"team-travel","scope":"SHARED"}' \ + "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/namespaces" | jq -er '.namespace.namespaceId') +export SHARED_NAMESPACE_ID +printf '%s\n' "$SHARED_NAMESPACE_ID" +``` + +Continue after the request succeeds and prints a namespace ID. Reuse that ID for later requests. + +{{< /multitabs >}} + +Creating the same root again returns `409 Conflict`. If you lose the ID, list root namespaces with `scope=SHARED` and find `team-travel`. + +To add a conversation to this namespace, use a new session ID and set its first event's namespace reference to the shared ID. Use `namespace_ref=shared_namespace_ref` in Python, `namespaceRef: sharedNamespaceRef` in TypeScript, or `"namespaceRef": {"namespaceId": "$SHARED_NAMESPACE_ID"}` in curl. For direct long-term memory creation, set the reference on each record. Creating the namespace does not move existing memories into it. + +### Recall shared memories + +After writing memories to the shared namespace, search with its namespace ID. The examples omit the owner filter so results can include memories associated with different owners in that namespace. Add an `ownerId` filter when you need only one owner's memories. An empty namespace returns no memories. + +{{< multitabs id="shared-memory-search" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +```python + shared_results = agent_memory.search_long_term_memory(request={ + "text": "What travel requirements has the team discussed?", + "filter_": {"namespace_ref": {"eq": shared_namespace_id}}, + "limit": 5, + }) + show("Shared travel memories", shared_results) +``` + +-tab-sep- + +```typescript + const sharedResults = await agentMemory.searchLongTermMemory({ + text: "What travel requirements has the team discussed?", + filter: { namespaceRef: { eq: sharedNamespaceId } }, + limit: 5, + }); + console.dir(sharedResults, { depth: null }); +``` + +-tab-sep- + +```sh +curl --fail-with-body --silent --show-error \ + --request POST \ + --header "Authorization: Bearer $API_KEY" \ + --header 'Content-Type: application/json' \ + --data "$(jq -n --arg ns "$SHARED_NAMESPACE_ID" \ + '{text: "What travel requirements has the team discussed?", filter: {namespaceRef: {eq: $ns}}, limit: 5}')" \ + "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/long-term-memory/search" | jq +``` + +{{< /multitabs >}} + +The namespace filter selects that exact namespace. To search its children too, list them and include their IDs with `filter.namespaceRef.in`. diff --git a/content/develop/ai/context-engine/agent-memory/python-sdk-quickstart.md b/content/develop/ai/context-engine/agent-memory/python-sdk-quickstart.md deleted file mode 100644 index f44d3126f1..0000000000 --- a/content/develop/ai/context-engine/agent-memory/python-sdk-quickstart.md +++ /dev/null @@ -1,363 +0,0 @@ ---- -alwaysopen: false -categories: -- docs -- develop -- ai -description: Explore session memory, automatic extraction, summarization, custom memory types, and sensitive-data exclusions with the Redis Agent Memory Python SDK. -hideListLinks: true -linktitle: Python SDK quickstart -title: Redis Agent Memory Python SDK quickstart -weight: 6 ---- - -Use this quickstart to follow a travel planning conversation through Redis Agent Memory. You will retrieve the conversation from session memory, recall information extracted in the background, inspect an automatically generated session summary, extract structured travel information, and guide extraction away from sensitive data. - -## Before you begin - -To complete this quickstart, you need: - -{{< embed-md "rc-agent-memory-quickstart-prerequisites.md" >}} - -You also need Python 3.10 or later. - -## Create a Redis Agent Memory service - -{{< embed-md "rc-agent-memory-quickstart-create-service.md" >}} - -## Save the connection values - -1. Open the Redis Agent Memory service in the Redis Cloud console. -1. On the **Configuration** tab, copy the **Endpoint** and **Store ID**. -1. Export the API key in your shell: - - ```sh - export API_KEY='' - ``` - -Keep the API key out of source control, application logs, and other unsecured locations. - -## Install the SDK - -Create and activate a virtual environment with [uv](https://docs.astral.sh/uv/): - -```sh -uv venv -source .venv/bin/activate -uv pip install "redis-agent-memory==0.4.0" -``` - -## Create the client and check the service health - -Create `quickstart.py` with the following code. Replace `` and `` with the values from Redis Cloud. The endpoint must include `https://`. - -```python -import os -from datetime import datetime, timezone - -from redis_agent_memory import AgentMemory, models - - -ENDPOINT = "" -STORE_ID = "" -SESSION_ID = "travel-planning-session" -USER_ID = "quickstart-user" - - -def show(label, response): - print(f"{label}:") - print(response.model_dump_json(by_alias=True, indent=2)) - - -def main(): - with AgentMemory( - ENDPOINT, - store_id=STORE_ID, - api_key=os.environ["API_KEY"], - ) as agent_memory: - health = agent_memory.health() - show("Service health", health) - - -if __name__ == "__main__": - main() -``` - -Run the file: - -```sh -python quickstart.py -``` - -A healthy response confirms that the client can reach Redis Agent Memory and authenticate with the API key. The first store request validates the Store ID. - -## Create a namespace - -Add this code after the health check, inside the `with` block. It creates a personal namespace for the user's travel memories when `NAMESPACE_ID` is not set: - -```python - namespace_id = os.environ.get("NAMESPACE_ID") - if not namespace_id: - created_namespace = agent_memory.create_namespace(request={ - "name": "travel", - "scope": models.NamespaceScope.PERSONAL, - "owner_id": USER_ID, - }) - namespace_id = created_namespace.namespace.namespace_id - print(f'export NAMESPACE_ID="{namespace_id}"') - namespace_ref = {"namespace_id": namespace_id} -``` - -Run the file once, then run the printed `export` command in your shell before running the file again. Later runs reuse that ID. Creating the same namespace again returns `409 Conflict`. If you lose the ID, use `list_namespaces` with `scope="PERSONAL"` and `owner_id=USER_ID` to find it. - -Use a fresh `SESSION_ID` if you already ran this quickstart without a namespace. The session events will reference this namespace, and memories extracted from the session will use it. - -## 1. Build conversation context with session memory - -Session memory stores a conversation as an ordered sequence of events. Add the following code after the namespace setup, inside the `with` block: - -```python - event = agent_memory.add_session_event( - session_id=SESSION_ID, - namespace_ref=namespace_ref, - actor_id=USER_ID, - role=models.MessageRole.USER, - content=[models.Text( - text=( - "I am visiting Tokyo and Kyoto next month. " - "I am vegetarian and prefer spicy food." - ), - )], - created_at=datetime.now(timezone.utc), - ) - show("Created event", event) - - session = agent_memory.get_session_memory( - session_id=SESSION_ID, - ) - show("Session memory", session) -``` - -Run the file again. The session response contains the stored message, its role, actor, and timestamps. An application can retrieve this session before the next agent turn and add the events to the model's context. - -> [!NOTE] -> **What to expect:** The `events` array contains the travel message. Redis Agent Memory adds an `eventId` and `systemTimestamp`, showing that the application can recover the complete event later using only the session ID. - -After the event is stored, comment out its creation and the `show("Created event", event)` line before subsequent runs. Keep the namespace setup and session retrieval. This avoids adding the same message again. - -## 2. Recall automatically extracted information - -Redis Agent Memory processes session events in the background and creates long term memories for information that may be useful in later conversations. You configured the extraction cadence to one minute when you created the service. You do not need to call a memory creation method. - -Wait at least one minute, then add this search after the session retrieval: - -```python - results = agent_memory.search_long_term_memory( - request={ - "text": "What dietary requirements and food preferences does the user have?", - "filter_": { - "namespace_ref": {"eq": namespace_id}, - "owner_id": { - "eq": USER_ID, - } - }, - "limit": 5, - }, - ) - show("Automatically extracted memories", results) -``` - -Run the file. The `items` array should contain memories derived from the conversation, such as the vegetarian requirement or preference for spicy food. Extraction is asynchronous, so run the search again if the array is empty. - -> [!NOTE] -> **What to expect:** Results similar to `User is a vegetarian` and `User prefers spicy food`. Your application did not create these memories directly. Redis Agent Memory derived them from the session event. The exact text and memory types can vary. - -The extracted memory remains searchable after the session expires, subject to the long term memory TTL. You can change the extraction cadence and both TTLs in the [Redis Agent Memory service configuration](/content/operate/iris/agent-memory/create-service.md#memory-configuration). - -The Python SDK uses snake case for method arguments and request fields. Serialized API requests and responses use camel case. - -## 3. Keep long conversations concise with automatic summarization - -Automatic summarization condenses older events and retains the most recent events in full. The retrieved session then contains a `summary` object and the recent `events` array, so the application can provide useful history without filling the model's context window with every original message. - -You enabled automatic summarization when you created the service. When the session reaches six events, Redis Agent Memory summarizes the older events and retains the two most recent events in full. - -### Add conversation turns - -Add this code after the first session event to continue the conversation past the configured threshold: - -```python - turns = [ - (models.MessageRole.ASSISTANT, "What dates are you traveling?"), - (models.MessageRole.USER, "I arrive on October 10 and leave on October 18."), - (models.MessageRole.ASSISTANT, "Would you like formal or casual restaurants?"), - (models.MessageRole.USER, "Mostly casual places near public transit."), - (models.MessageRole.ASSISTANT, "Do you have a preferred budget?"), - (models.MessageRole.USER, "About 40 euros per person."), - ] - - for role, text in turns: - agent_memory.add_session_event( - session_id=SESSION_ID, - namespace_ref=namespace_ref, - actor_id=USER_ID if role == models.MessageRole.USER else "travel-agent", - role=role, - content=[models.Text(text=text)], - created_at=datetime.now(timezone.utc), - ) -``` - -Run the code once, then comment out the loop to avoid adding the same turns again. Summarization runs in the background. - -### Retrieve the summarized session - -After a short wait, retrieve the session again: - -```python - compacted_session = agent_memory.get_session_memory( - session_id=SESSION_ID, - ) - show("Compacted session memory", compacted_session) -``` - -Run the retrieval again after a short wait if `summary` is not present. Compare `summary.text` with the recent events. The summary should preserve earlier trip decisions while recent turns remain available in full. - -> [!NOTE] -> **What to expect:** A `summary` object that preserves details such as Tokyo, Kyoto, the travel dates, and food preferences. `summarizedUpToEventId` identifies the last event covered by the summary, while `events` contains the newer turns that remain in full. The exact summary text can vary. - -See [automatic summarization configuration](/content/operate/iris/agent-memory/create-service.md#automatic-summarization) for details. - -## 4. Extract business specific data with a custom memory type - -Built in memories preserve generally useful information. Custom memory types let an application extract structured information for its business domain. You configured `trip_preference` when you created the service, so it processed the same travel planning event independently. - -Search for the structured memory: - -```python - custom_results = agent_memory.search_long_term_memory( - request={ - "text": "What are the requirements for the user's trip?", - "filter_": { - "namespace_ref": {"eq": namespace_id}, - "owner_id": {"eq": USER_ID}, - "memory_type": {"eq": "trip_preference"}, - }, - "limit": 5, - }, - ) - show("Trip preference memories", custom_results) -``` - -The `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: - -```json -{ - "memoryType": "trip_preference", - "attributes": { - "destinations": ["Tokyo", "Kyoto"], - "travel_period": "next month", - "dietary_requirements": ["vegetarian"], - "food_preferences": ["spicy food"] - } -} -``` - -The 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. - -Add this code after the custom-memory search to collect dietary requirements for the next agent turn: - -```python - dietary_requirements = set() - if not custom_results.items: - print("No matching trip preferences yet. Retry after a short wait.") - for memory in custom_results.items: - attributes = memory.attributes or {} - requirements = attributes.get("dietary_requirements", []) - if isinstance(requirements, list): - dietary_requirements.update( - value for value in requirements if isinstance(value, str) - ) - print("Restaurant requirements:", sorted(dietary_requirements)) -``` - -If results remain empty, check that the type is enabled and that the owner and namespace IDs match the stored records. An empty result does not mean the user has no dietary requirements. - -### Create a custom memory directly - -If your application already has structured trip data, write it directly using the registered `trip_preference` type. This optional example represents data from a form and uses the same namespace: - -```python - direct_result = agent_memory.bulk_create_long_term_memories(memories=[{ - "id": "trip-form-1", - "text": "The user plans to visit Tokyo and Kyoto next month and requires vegetarian food.", - "owner_id": USER_ID, - "memory_type": "trip_preference", - "namespace_ref": namespace_ref, - "attributes": { - "destinations": ["Tokyo", "Kyoto"], - "travel_period": "next month", - "dietary_requirements": ["vegetarian"], - "food_preferences": ["spicy food"], - }, - }]) - show("Directly created trip preference", direct_result) -``` - -Inspect the bulk response 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. Run this write once; the memory ID identifies the record within the store. - -See [custom memory types](/content/operate/iris/agent-memory/create-service.md#custom-memory-types) for configuration requirements and limits. - -## 5. Guide extraction away from sensitive data - -The 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: - -```python - sensitive_event = agent_memory.add_session_event( - session_id=SESSION_ID, - namespace_ref=namespace_ref, - actor_id=USER_ID, - role=models.MessageRole.USER, - content=[models.Text( - text=( - "I booked Hotel Sakura in Tokyo. For this example, " - "the fictional booking confirmation code is DEMO-7QX9." - ), - )], - created_at=datetime.now(timezone.utc), - ) - show("Event with excluded information", sensitive_event) -``` - -Run the code once, then comment out the event creation and its `show` line. Wait at least one minute and search for the safe hotel information: - -```python - exclusion_results = agent_memory.search_long_term_memory( - request={ - "text": "Where is the user staying in Tokyo?", - "filter_": { - "namespace_ref": {"eq": namespace_id}, - "owner_id": {"eq": USER_ID}, - }, - "limit": 5, - }, - ) - show("Memories after semantic exclusion", exclusion_results) -``` - -Inspect the returned memories. They can retain the hotel name, but should not contain `DEMO-7QX9` because the exclusion prompt covers booking confirmation codes. - -> [!NOTE] -> **What to expect:** A memory similar to `User booked Hotel Sakura in Tokyo` without the fictional confirmation code. If the code appears, refine the exclusion prompt and test again. Exclusions remain advisory. - -> [!WARNING] -> 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. - -See [sensitive-data exclusions](/content/operate/iris/agent-memory/create-service.md#sensitive-data-exclusions) for configuration details. - -## Next steps - -* Learn how to [organize memories with namespaces]({{< relref "/develop/ai/context-engine/agent-memory/developer-guide#organize-memories-with-namespaces" >}}). - -* Review the [Python SDK package and reference](https://pypi.org/project/redis-agent-memory/). -* Try the [TypeScript SDK quickstart](/content/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart.md) or [REST API quickstart](/content/develop/ai/context-engine/agent-memory/rest-api-quickstart.md). -* Learn when to [create long term memories directly](/content/develop/ai/context-engine/agent-memory/developer-guide.md#create-long-term-memories). diff --git a/content/develop/ai/context-engine/agent-memory/quickstart.md b/content/develop/ai/context-engine/agent-memory/quickstart.md new file mode 100644 index 0000000000..f10d8ba278 --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/quickstart.md @@ -0,0 +1,372 @@ +--- +alwaysopen: false +categories: +- docs +- develop +- ai +description: Store and retrieve a conversation, then recall long-term memory with Python, TypeScript, or curl. +hideListLinks: true +linktitle: Quickstart +title: Redis Agent Memory quickstart +weight: 5 +aliases: +- /develop/ai/context-engine/agent-memory/python-sdk-quickstart/ +- /develop/ai/context-engine/agent-memory/typescript-sdk-quickstart/ +- /develop/ai/context-engine/agent-memory/rest-api-quickstart/ +- /develop/ai/context-engine/agent-memory/api-examples/ +- /operate/rc/context-engine/agent-memory/use-agent-memory/ +--- + +Store a travel-planning message, retrieve the conversation, and recall a preference extracted into long-term memory. Choose Python, TypeScript, or curl for each example. + +## Before you begin + +To complete this quickstart, you need: + +{{< embed-md "rc-agent-memory-quickstart-prerequisites.md" >}} + +Choose Python, TypeScript, or curl for the examples and use it throughout this page. + +{{< multitabs id="prerequisites" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +You also need Python 3.10 or later. + +-tab-sep- + +You also need Node.js and npm. + +-tab-sep- + +You also need a shell with `curl` and `jq` installed. + +{{< /multitabs >}} + +## Create a Redis Agent Memory service + +{{< embed-md "rc-agent-memory-quickstart-create-service.md" >}} + +## Save the connection values + +1. Open the Redis Agent Memory service in the Redis Cloud console. +1. On the **Configuration** tab, copy the **Endpoint** and **Store ID**. +1. Export the values for your chosen client in your shell: + +{{< multitabs id="connection-values" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +```sh +export API_KEY='' +``` + +-tab-sep- + +```sh +export API_KEY='' +``` + +-tab-sep- + +```sh +export AGENT_MEMORY_URL='' +export STORE_ID='' +export API_KEY='' +export SESSION_ID='travel-planning-session' +export OWNER_ID='quickstart-user' +``` + +{{< /multitabs >}} + +The endpoint must include `https://`. Keep the API key out of source control, application logs, and other unsecured locations. + +## Install the SDK + +{{< multitabs id="installation" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +Create and activate a virtual environment with [uv](https://docs.astral.sh/uv/): + +```sh +uv venv +source .venv/bin/activate +uv pip install "redis-agent-memory==0.4.0" +``` + +-tab-sep- + +```sh +mkdir agent-memory-quickstart +cd agent-memory-quickstart +npm init -y +npm install @redis-iris/agent-memory@0.3.0 +npm install --save-dev tsx +``` + +-tab-sep- + +Use `curl` and `jq` directly. No SDK installation is required. + +{{< /multitabs >}} + + + +## Create the client and check the service health + +{{< multitabs id="health" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +Create `quickstart.py` with the following code. Replace `` and `` with the values from Redis Cloud. The endpoint must include `https://`. + +```python +import os +from datetime import datetime, timezone + +from redis_agent_memory import AgentMemory, models + + +ENDPOINT = "" +STORE_ID = "" +SESSION_ID = "travel-planning-session" +USER_ID = "quickstart-user" + + +def show(label, response): + print(f"{label}:") + print(response.model_dump_json(by_alias=True, indent=2)) + + +def main(): + with AgentMemory( + ENDPOINT, + store_id=STORE_ID, + api_key=os.environ["API_KEY"], + ) as agent_memory: + health = agent_memory.health() + show("Service health", health) + + +if __name__ == "__main__": + main() +``` + +Run the file: + +```sh +python quickstart.py +``` + +-tab-sep- + +Create `quickstart.ts` with the following code. Replace `` and `` with the values from Redis Cloud. The endpoint must include `https://`. + +```typescript +import { AgentMemory } from "@redis-iris/agent-memory"; + +const apiKey = process.env.API_KEY; +if (!apiKey) { + throw new Error("Set the API_KEY environment variable."); +} + +const serverURL = ""; +const storeId = ""; +const sessionId = "travel-planning-session"; +const userId = "quickstart-user"; + +const agentMemory = new AgentMemory({ + serverURL, + storeId, + apiKey, +}); + +async function run() { + const health = await agentMemory.health(); + console.log("Service health:"); + console.dir(health, { depth: null }); +} + +run().catch((error) => { + console.error(error); + process.exitCode = 1; +}); +``` + +Run the file: + +```sh +npx tsx quickstart.ts +``` + +-tab-sep- + +Verify that the service is available: + +```sh +curl --fail-with-body --silent --show-error \ + --header "Authorization: Bearer $API_KEY" \ + "$AGENT_MEMORY_URL/health" | jq +``` + +{{< /multitabs >}} + +A healthy response confirms that the client can reach Redis Agent Memory and authenticate with the API key. The first store request validates the Store ID. + +For Python, add subsequent snippets inside the `with` block in `main`. For TypeScript, add them inside `run`. After each step, run `python quickstart.py` or `npx tsx quickstart.ts`. With curl, run each command in the same shell. Run writes once; comment out completed writes and their output statements before rerunning an SDK file. + +## 1. Build conversation context with session memory + +Session memory stores a conversation as an ordered sequence of events. Add a user message after the health check, then retrieve the session: + +{{< multitabs id="session-events" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +```python + event = agent_memory.add_session_event( + session_id=SESSION_ID, + actor_id=USER_ID, + role=models.MessageRole.USER, + content=[models.Text( + text=( + "I am visiting Tokyo and Kyoto next month. " + "I am vegetarian and prefer spicy food." + ), + )], + created_at=datetime.now(timezone.utc), + ) + show("Created event", event) + + session = agent_memory.get_session_memory( + session_id=SESSION_ID, + ) + show("Session memory", session) +``` + +-tab-sep- + +```typescript + const event = await agentMemory.addSessionEvent({ + sessionId, + actorId: userId, + role: "USER", + content: [{ + text: "I am visiting Tokyo and Kyoto next month. I am vegetarian and prefer spicy food.", + }], + createdAt: new Date(), + }); + console.log("Created event:"); + console.dir(event, { depth: null }); + + const session = await agentMemory.getSessionMemory(sessionId); + console.log("Session memory:"); + console.dir(session, { depth: null }); +``` + +-tab-sep- + +```sh +export EVENT_CREATED_AT="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" + +curl --fail-with-body --silent --show-error \ + --request POST \ + --header "Authorization: Bearer $API_KEY" \ + --header 'Content-Type: application/json' \ + --data @- \ + "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/session-memory/events" <}} + +Run the example. The session response contains the stored message, its role, actor, and timestamps. An application can retrieve this session before the next agent turn and add the events to the model's context. + +> [!NOTE] +> **What to expect:** The `events` array contains the travel message. Redis Agent Memory adds an `eventId` and `systemTimestamp`, showing that the application can recover the complete event later using only the session ID. + +Run the event write once. In the SDK files, comment out event creation and its output statements before subsequent runs. Keep the session retrieval. + + +## 2. Recall automatically extracted information + +Redis Agent Memory processes session events in the background and creates long-term memories for information that may be useful in later conversations. You configured the extraction cadence to one minute when you created the service. + +Wait at least one minute, then add this search after the session retrieval: + +{{< multitabs id="memory-search" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +```python + results = agent_memory.search_long_term_memory( + request={ + "text": "What dietary requirements and food preferences does the user have?", + "filter_": { + "owner_id": { + "eq": USER_ID, + } + }, + "limit": 5, + }, + ) + show("Automatically extracted memories", results) +``` + +-tab-sep- + +```typescript + const results = await agentMemory.searchLongTermMemory({ + text: "What dietary requirements and food preferences does the user have?", + filter: { + ownerId: { + eq: userId, + }, + }, + limit: 5, + }); + console.log("Automatically extracted memories:"); + console.dir(results, { depth: null }); +``` + +-tab-sep- + +```sh +curl --fail-with-body --silent --show-error \ + --request POST \ + --header "Authorization: Bearer $API_KEY" \ + --header 'Content-Type: application/json' \ + --data @- \ + "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/long-term-memory/search" <}} + +Run the search. The `items` array should contain memories derived from the conversation, such as the vegetarian requirement or preference for spicy food. Extraction is asynchronous, so run the search again if the array is empty. + +> [!NOTE] +> **What to expect:** Results similar to `User is a vegetarian` and `User prefers spicy food`. Your application did not create these memories directly. Redis Agent Memory derived them from the session event. The exact text and memory types can vary. + +The extracted memory remains searchable after the session expires, subject to the long-term memory time to live (TTL). You can change the extraction cadence and both TTLs in the [Redis Agent Memory service configuration]({{< relref "/operate/iris/agent-memory/create-service#memory-configuration" >}}). + +## Next steps + +Follow the [developer guide]({{< relref "/develop/ai/context-engine/agent-memory/developer-guide" >}}) to use memory in an agent’s request cycle. For specific features, see [Sessions]({{< relref "/develop/ai/context-engine/agent-memory/sessions" >}}), [Custom memory types]({{< relref "/develop/ai/context-engine/agent-memory/long-term-memory#custom-memory-types" >}}), and [Namespaces]({{< relref "/develop/ai/context-engine/agent-memory/namespaces" >}}). diff --git a/content/develop/ai/context-engine/agent-memory/rest-api-quickstart.md b/content/develop/ai/context-engine/agent-memory/rest-api-quickstart.md deleted file mode 100644 index f347b8c9a0..0000000000 --- a/content/develop/ai/context-engine/agent-memory/rest-api-quickstart.md +++ /dev/null @@ -1,336 +0,0 @@ ---- -alwaysopen: false -categories: -- docs -- develop -- ai -description: Explore session memory, automatic extraction, summarization, custom memory types, and sensitive-data exclusions with the Redis Agent Memory REST API. -hideListLinks: true -linktitle: REST quickstart -title: Redis Agent Memory REST API quickstart -weight: 8 -aliases: -- /operate/rc/context-engine/agent-memory/use-agent-memory/ -- /develop/ai/context-engine/agent-memory/api-examples/ ---- - -Use this quickstart to follow a travel planning conversation through Redis Agent Memory. You will retrieve the conversation from session memory, recall information extracted in the background, inspect an automatically generated session summary, extract structured travel information, and guide extraction away from sensitive data. - -## Before you begin - -To complete this quickstart, you need: - -* A Redis Cloud account that can create Redis Agent Memory services. -* An eligible Redis Cloud database, or permission to create one. -* A shell with `curl` and `jq` installed. - -## Create a Redis Agent Memory service - -{{< embed-md "rc-agent-memory-quickstart-create-service.md" >}} - -## Save the connection values - -1. Open the Redis Agent Memory service in the Redis Cloud console. -1. On the **Configuration** tab, copy the **Endpoint** and **Store ID**. -1. Export the values in your shell: - - ```sh - export AGENT_MEMORY_URL='' - export STORE_ID='' - export API_KEY='' - export SESSION_ID='travel-planning-session' - export OWNER_ID='quickstart-user' - ``` - -`AGENT_MEMORY_URL` must include `https://`. Keep the API key out of source control, application logs, and other unsecured locations. - -## Check the service health - -Verify that the service is available: - -```sh -curl --fail-with-body --silent --show-error \ - --header "Authorization: Bearer $API_KEY" \ - "$AGENT_MEMORY_URL/health" | jq -``` - -## Create a namespace - -Create a personal namespace for the user's travel memories. Run this request once and keep the returned ID in your shell: - -```sh -NAMESPACE_ID=$(curl --fail-with-body --silent --show-error \ - --request POST \ - --header "Authorization: Bearer $API_KEY" \ - --header 'Content-Type: application/json' \ - --data "$(jq -n --arg owner "$OWNER_ID" \ - '{name: "travel", scope: "PERSONAL", ownerId: $owner}')" \ - "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/namespaces" | jq -er '.namespace.namespaceId') -export NAMESPACE_ID -printf '%s\n' "$NAMESPACE_ID" -``` - -Continue only after the request succeeds and prints a namespace ID. Save that ID for later runs. Creating the same namespace again returns `409 Conflict`; use the existing namespace ID instead. If you lose the ID, list personal roots with `GET /v1/stores/{storeId}/namespaces?scope=PERSONAL&ownerId=`. - -Use a fresh `SESSION_ID` if you already ran this quickstart without a namespace. The session events will reference this namespace, and memories extracted from the session will use it. - -## 1. Build conversation context with session memory - -Session memory stores a conversation as an ordered sequence of events. Add a user message that contains details the travel agent will need later: - -```sh -export EVENT_CREATED_AT="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" - -curl --fail-with-body --silent --show-error \ - --request POST \ - --header "Authorization: Bearer $API_KEY" \ - --header 'Content-Type: application/json' \ - --data @- \ - "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/session-memory/events" < [!NOTE] -> **What to expect:** The `events` array contains the travel message. Redis Agent Memory adds an `eventId` and `systemTimestamp`, showing that the application can recover the complete event later using only the session ID. - -## 2. Recall automatically extracted information - -Redis Agent Memory processes session events in the background and creates long term memories for information that may be useful in later conversations. You configured the extraction cadence to one minute when you created the service. You do not need to submit a separate memory creation request. - -Wait at least one minute, then search for the user's dietary requirements: - -```sh -curl --fail-with-body --silent --show-error \ - --request POST \ - --header "Authorization: Bearer $API_KEY" \ - --header 'Content-Type: application/json' \ - --data @- \ - "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/long-term-memory/search" < [!NOTE] -> **What to expect:** Results similar to `User is a vegetarian` and `User prefers spicy food`. Your application did not create these memories directly. Redis Agent Memory derived them from the session event. The exact text and memory types can vary. - -The extracted memory remains searchable after the session expires, subject to the long term memory TTL. You can change the extraction cadence and both TTLs in the [Redis Agent Memory service configuration](/content/operate/iris/agent-memory/create-service.md#memory-configuration). - -## 3. Keep long conversations concise with automatic summarization - -Automatic summarization condenses older events and retains the most recent events in full. The retrieved session then contains a `summary` object and the recent `events` array, so the application can provide useful history without filling the model's context window with every original message. - -You enabled automatic summarization when you created the service. When the session reaches six events, Redis Agent Memory summarizes the older events and retains the two most recent events in full. - -### Add conversation turns - -Add enough user and assistant events to reach the configured threshold. Use the request from the first step and change `role`, `actorId`, `content`, and `createdAt` for each event. The Python and TypeScript quickstarts use six additional turns about travel dates, restaurant style, and budget. - -Summarization runs in the background after the session reaches the threshold. - -### Retrieve the summarized session - -After a short wait, retrieve the session again: - -```sh -curl --fail-with-body --silent --show-error \ - --header "Authorization: Bearer $API_KEY" \ - "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/session-memory/$SESSION_ID" | jq -``` - -Repeat the retrieval after a short wait if `summary` is not present. Compare `summary.text` with the recent events. The summary should preserve earlier decisions about the trip while recent turns remain available in full. - -> [!NOTE] -> **What to expect:** A `summary` object that preserves details such as Tokyo, Kyoto, the travel dates, and food preferences. `summarizedUpToEventId` identifies the last event covered by the summary, while `events` contains the newer turns that remain in full. The exact summary text can vary. - -See [automatic summarization configuration](/content/operate/iris/agent-memory/create-service.md#automatic-summarization) for details. - -## 4. Extract business specific data with a custom memory type - -Built in memories preserve generally useful information. Custom memory types let an application extract structured information for its business domain. You configured `trip_preference` when you created the service, so it processed the same travel planning event independently. - -Search for the structured memory: - -```sh -curl --fail-with-body --silent --show-error \ - --request POST \ - --header "Authorization: Bearer $API_KEY" \ - --header 'Content-Type: application/json' \ - --data @- \ - "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/long-term-memory/search" < [!NOTE] -> **What to expect:** A memory similar to `User booked Hotel Sakura in Tokyo` without the fictional confirmation code. If the code appears, refine the exclusion prompt and test again. Exclusions remain advisory. - -> [!WARNING] -> 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. - -See [sensitive-data exclusions](/content/operate/iris/agent-memory/create-service.md#sensitive-data-exclusions) for configuration details. - -## Next steps - -* Learn how to [organize memories with namespaces]({{< relref "/develop/ai/context-engine/agent-memory/developer-guide#organize-memories-with-namespaces" >}}). - -* Follow the [Python SDK quickstart](/content/develop/ai/context-engine/agent-memory/python-sdk-quickstart.md) or [TypeScript SDK quickstart](/content/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart.md). -* Learn when to [create long term memories directly](/content/develop/ai/context-engine/agent-memory/developer-guide.md#create-long-term-memories). -* Use the [Redis Agent Memory API reference](/content/develop/ai/context-engine/agent-memory/api-reference.md) for endpoint and schema details. diff --git a/content/develop/ai/context-engine/agent-memory/sessions.md b/content/develop/ai/context-engine/agent-memory/sessions.md new file mode 100644 index 0000000000..ccb2221038 --- /dev/null +++ b/content/develop/ai/context-engine/agent-memory/sessions.md @@ -0,0 +1,170 @@ +--- +alwaysopen: false +categories: +- docs +- develop +- ai +description: Add and retrieve conversation events, configure retention, and summarize older messages. +hideListLinks: true +linktitle: Sessions +title: Sessions +weight: 10 +--- + +Use a stable `sessionId` to store a conversation as an ordered sequence of events. Add an event for each user, assistant, or system message that your application needs to retain. + +## Add session events + +Each stored event can include: + +| Field | Purpose | +|:------|:--------| +| `eventId` | Server-generated identifier for the event. | +| `sessionId` | Session that contains the event. | +| `actorId` | User, agent, or other actor that produced the event. | +| `role` | Role of the message in the conversation. | +| `content` | Message content, including its text. | +| `createdAt` | Time the event occurred in the application. | +| `systemTimestamp` | Time Redis Agent Memory stored the event. | +| `metadata` | Optional application-specific information associated with the event. | + +## Retrieve conversation context + +Before an agent turn, retrieve the session by `sessionId` and provide the relevant events to the agent. Use these events as conversation context for the model. + +## Configure session retention + +The session-memory time to live (TTL) controls how long sessions remain available. Configure it according to the retention requirements of your application. When a session expires, its events are no longer available through session-memory retrieval. + +See [memory configuration]({{< relref "/operate/iris/agent-memory/create-service#memory-configuration" >}}) to configure the session-memory TTL in Redis Cloud. + +## Configure automatic summarization + +Automatic summarization limits the amount of conversation history that must be added to a model's context window. Configure: + +* **Summarize after:** The number of messages a session can contain before older messages are summarized. +* **Keep most recent:** The number of recent messages that remain available in full. + +For example, with **Summarize after** set to 20 and **Keep most recent** set to 10, Redis Agent Memory summarizes the older 10 messages when the session reaches 20 messages and retains the 10 most recent messages in full. + +See [automatic summarization]({{< relref "/operate/iris/agent-memory/create-service#automatic-summarization" >}}) to enable summarization and configure both thresholds in Redis Cloud. + +For an example that combines session events with long-term recall, follow the [developer guide]({{< relref "/develop/ai/context-engine/agent-memory/developer-guide" >}}). For complete schemas, see the [session-memory application programming interface (API) reference]({{< relref "/develop/ai/context-engine/agent-memory/api-reference" >}}#operation/GetSessionMemory). + +To group a session and its extracted memories, use an optional [namespace]({{< relref "/develop/ai/context-engine/agent-memory/namespaces#place-and-retrieve-memories" >}}). + +## Try automatic summarization + +Complete the [quickstart]({{< relref "/develop/ai/context-engine/agent-memory/quickstart" >}}) first. Reuse its client, connection values, and user and session identifiers. Add Python snippets inside the `with` block and TypeScript snippets inside `run`. Run curl commands in the same shell. Run each write once; comment out completed writes and their output statements before rerunning an SDK file. + +Automatic summarization condenses older events and retains the most recent events in full. The retrieved session then contains a `summary` object and the recent `events` array, so the application can provide useful history without filling the model's context window with every original message. + +In the service configuration, enable automatic summarization. Set **Summarize after** to `6` and **Keep most recent** to `2` for this example. When the session reaches six events, Redis Agent Memory summarizes the older events and retains the two most recent events in full. + + +### Add conversation turns + +Add these events to the session from the quickstart to continue the conversation past the configured threshold: + +{{< multitabs id="conversation-turns" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +```python + turns = [ + (models.MessageRole.ASSISTANT, "What dates are you traveling?"), + (models.MessageRole.USER, "I arrive on October 10 and leave on October 18."), + (models.MessageRole.ASSISTANT, "Would you like formal or casual restaurants?"), + (models.MessageRole.USER, "Mostly casual places near public transit."), + (models.MessageRole.ASSISTANT, "Do you have a preferred budget?"), + (models.MessageRole.USER, "About 40 euros per person."), + ] + + for role, text in turns: + agent_memory.add_session_event( + session_id=SESSION_ID, + actor_id=USER_ID if role == models.MessageRole.USER else "travel-agent", + role=role, + content=[models.Text(text=text)], + created_at=datetime.now(timezone.utc), + ) +``` + +-tab-sep- + +```typescript + const turns = [ + { role: "ASSISTANT", actorId: "travel-agent", text: "What dates are you traveling?" }, + { role: "USER", actorId: userId, text: "I arrive on October 10 and leave on October 18." }, + { role: "ASSISTANT", actorId: "travel-agent", text: "Would you like formal or casual restaurants?" }, + { role: "USER", actorId: userId, text: "Mostly casual places near public transit." }, + { role: "ASSISTANT", actorId: "travel-agent", text: "Do you have a preferred budget?" }, + { role: "USER", actorId: userId, text: "About 40 euros per person." }, + ] as const; + + for (const turn of turns) { + await agentMemory.addSessionEvent({ + sessionId, + actorId: turn.actorId, + role: turn.role, + content: [{ text: turn.text }], + createdAt: new Date(), + }); + } +``` + +-tab-sep- + +Add enough user and assistant events to reach the configured threshold. Use the session-event request from the quickstart and change `role`, `actorId`, `content`, and `createdAt` for each event. Add these six turns once, using `travel-agent` as the assistant actor and `$OWNER_ID` as the user actor: + +| Role | Content | +|:---|:---| +| `ASSISTANT` | What dates are you traveling? | +| `USER` | I arrive on October 10 and leave on October 18. | +| `ASSISTANT` | Would you like formal or casual restaurants? | +| `USER` | Mostly casual places near public transit. | +| `ASSISTANT` | Do you have a preferred budget? | +| `USER` | About 40 euros per person. | + +Summarization runs in the background after the session reaches the threshold. + +{{< /multitabs >}} + +Run these writes once. In the SDK files, comment out the loop before subsequent runs. Summarization runs in the background. + + +### Retrieve the summarized session + +After a short wait, retrieve the session again: + +{{< multitabs id="session-summary" tab1="Python" tab2="TypeScript" tab3="curl" >}} + +```python + compacted_session = agent_memory.get_session_memory( + session_id=SESSION_ID, + ) + show("Compacted session memory", compacted_session) +``` + +-tab-sep- + +```typescript + const compactedSession = await agentMemory.getSessionMemory(sessionId); + console.log("Compacted session memory:"); + console.dir(compactedSession, { depth: null }); +``` + +-tab-sep- + +```sh +curl --fail-with-body --silent --show-error \ + --header "Authorization: Bearer $API_KEY" \ + "$AGENT_MEMORY_URL/v1/stores/$STORE_ID/session-memory/$SESSION_ID" | jq +``` + +{{< /multitabs >}} + +Run the retrieval again after a short wait if `summary` is not present. Compare `summary.text` with the recent events. The summary should preserve earlier trip decisions while recent turns remain available in full. + +> [!NOTE] +> **What to expect:** A `summary` object that preserves details such as Tokyo, Kyoto, the travel dates, and food preferences. `summarizedUpToEventId` identifies the last event covered by the summary, while `events` contains the newer turns that remain in full. The exact summary text can vary. + +See [automatic summarization configuration]({{< relref "/operate/iris/agent-memory/create-service#automatic-summarization" >}}) for details. diff --git a/content/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart.md b/content/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart.md deleted file mode 100644 index 1fa5988959..0000000000 --- a/content/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart.md +++ /dev/null @@ -1,406 +0,0 @@ ---- -alwaysopen: false -categories: -- docs -- develop -- ai -description: Explore session memory, automatic extraction, summarization, custom memory types, and sensitive-data exclusions with the Redis Agent Memory TypeScript SDK. -hideListLinks: true -linktitle: TypeScript SDK quickstart -title: Redis Agent Memory TypeScript SDK quickstart -weight: 7 ---- - -Use this quickstart to follow a travel planning conversation through Redis Agent Memory. You will retrieve the conversation from session memory, recall information extracted in the background, inspect an automatically generated session summary, extract structured travel information, and guide extraction away from sensitive data. - -## Before you begin - -To complete this quickstart, you need: - -{{< embed-md "rc-agent-memory-quickstart-prerequisites.md" >}} - -You also need Node.js and npm. - -## Create a Redis Agent Memory service - -1. Sign in to the [Redis Cloud console](https://cloud.redis.io/). -1. Select **Agent Memory** from the navigation menu. -1. If Redis Cloud displays the public preview terms, review and accept them. -1. Select **Create custom service**. -1. Enter a service name, select an eligible database, and select its `default` user. -1. Under **Memory configuration**, enter these values: - - | Setting | Value | What it controls | - |:--------|:------|:-----------------| - | **Short-term TTL** | `1` day | How long session memory is retained. | - | **Long-term TTL** | `365` days | How long long-term memories are retained. | - | **Extraction cadence** | `1` minute | How often session events are processed for extraction. One minute is for this quickstart; use a longer production interval unless you need rapid extraction. | - | **Automatic summarization** | Enabled | Whether older session events are condensed into a summary. | - | **Summarize after (messages)** | `6` | The event count that triggers summarization. Six is for this quickstart; use a higher production threshold. | - | **Keep most recent (messages)** | `2` | How many recent events remain in full. Two is for this quickstart; retain more in production when recent turns are needed. | - -1. Under **Memory types & extraction**, select **Add type** and configure this custom memory type: - - | Setting | Value | What it controls | - |:--------|:------|:-----------------| - | **Name** | `trip_preference` | The identifier stored in `memoryType` and used in search filters. | - | **Description** | `Structured requirements for a planned trip` | The purpose of the custom memory type. | - | **Extraction prompt** | `Extract trip requirements only when the user states a destination or travel plan. Preserve explicit dietary requirements and food preferences.` | When to create the memory and which information to capture. | - | **Enabled** | Enabled | Whether new memories of this type are extracted. | - -1. Add these custom fields: - - | Field | Type | Description | - |:------|:-----|:------------| - | `destinations` | `list[str]` | Cities or countries the user plans to visit. | - | `travel_period` | `str` | When the user plans to travel. | - | `dietary_requirements` | `list[str]` | Dietary requirements that affect recommendations. | - | `food_preferences` | `list[str]` | Cuisines, flavors, or dining preferences stated by the user. | - -1. Under **Sensitive-data exclusions**, enable **Semantic exclusions** and enter this exclusion prompt: - - ```text - Do not keep passwords, access tokens, recovery codes, payment card information, or booking confirmation codes in long-term memory. - ``` - -1. Select **Create**. -1. Copy the Redis Agent Memory API key and store it securely. - -> [!WARNING] -> Redis Cloud displays the Redis Agent Memory API key only once. If you lose it, [generate a new API key](/content/operate/iris/agent-memory/view-service.md#replace-service-api-key). - -These settings keep the background stages short enough to observe during the quickstart. For screenshots and configuration details, see [create a Redis Agent Memory service](/content/operate/iris/agent-memory/create-service.md). - -> [!WARNING] -> Sensitive-data exclusions guide the extraction model but do not guarantee exclusion. Sensitive session content still reaches the model provider. Exclusions do not apply when an application creates long-term memories directly. - -## Save the connection values - -1. Open the Redis Agent Memory service in the Redis Cloud console. -1. On the **Configuration** tab, copy the **Endpoint** and **Store ID**. -1. Export the API key in your shell: - - ```sh - export API_KEY='' - ``` - -Keep the API key out of source control, application logs, and other unsecured locations. - -## Install the SDK - -```sh -mkdir agent-memory-quickstart -cd agent-memory-quickstart -npm init -y -npm install @redis-iris/agent-memory@0.3.0 -npm install --save-dev tsx -``` - -## Create the client and check the service health - -Create `quickstart.ts` with the following code. Replace `` and `` with the values from Redis Cloud. The endpoint must include `https://`. - -```typescript -import { AgentMemory } from "@redis-iris/agent-memory"; - -const apiKey = process.env.API_KEY; -if (!apiKey) { - throw new Error("Set the API_KEY environment variable."); -} - -const serverURL = ""; -const storeId = ""; -const sessionId = "travel-planning-session"; -const userId = "quickstart-user"; - -const agentMemory = new AgentMemory({ - serverURL, - storeId, - apiKey, -}); - -async function run() { - const health = await agentMemory.health(); - console.log("Service health:"); - console.dir(health, { depth: null }); -} - -run().catch((error) => { - console.error(error); - process.exitCode = 1; -}); -``` - -Run the file: - -```sh -npx tsx quickstart.ts -``` - -A healthy response confirms that the client can reach Redis Agent Memory and authenticate with the API key. The first store request validates the Store ID. - -## Create a namespace - -Add this code after the health check, inside `run`. It creates a personal namespace for the user's travel memories when `NAMESPACE_ID` is not set: - -```typescript - let namespaceId = process.env.NAMESPACE_ID; - if (!namespaceId) { - const createdNamespace = await agentMemory.createNamespace({ - name: "travel", - scope: "PERSONAL", - ownerId: userId, - }); - namespaceId = createdNamespace.namespace.namespaceId; - console.log(`export NAMESPACE_ID="${namespaceId}"`); - } - const namespaceRef = { namespaceId }; -``` - -Run the file once, then run the printed `export` command in your shell before running the file again. Later runs reuse that ID. Creating the same namespace again returns `409 Conflict`. If you lose the ID, use `listNamespaces` with `scope: "PERSONAL"` and `ownerId: userId` to find it. - -Use a fresh `sessionId` if you already ran this quickstart without a namespace. The session events will reference this namespace, and memories extracted from the session will use it. - -## 1. Build conversation context with session memory - -Session memory stores a conversation as an ordered sequence of events. Add the following code after the namespace setup, inside `run`: - -```typescript - const event = await agentMemory.addSessionEvent({ - sessionId, - namespaceRef, - actorId: userId, - role: "USER", - content: [{ - text: "I am visiting Tokyo and Kyoto next month. I am vegetarian and prefer spicy food.", - }], - createdAt: new Date(), - }); - console.log("Created event:"); - console.dir(event, { depth: null }); - - const session = await agentMemory.getSessionMemory(sessionId); - console.log("Session memory:"); - console.dir(session, { depth: null }); -``` - -Run the file again. The session response contains the stored message, its role, actor, and timestamps. An application can retrieve this session before the next agent turn and add the events to the model's context. - -> [!NOTE] -> **What to expect:** The `events` array contains the travel message. Redis Agent Memory adds an `eventId` and `systemTimestamp`, showing that the application can recover the complete event later using only the session ID. - -After the event is stored, comment out its creation and the two lines that print it before subsequent runs. Keep the namespace setup and session retrieval. This avoids adding the same message again. - -## 2. Recall automatically extracted information - -Redis Agent Memory processes session events in the background and creates long term memories for information that may be useful in later conversations. You configured the extraction cadence to one minute when you created the service. You do not need to call a memory creation method. - -Wait at least one minute, then add this search after the session retrieval: - -```typescript - const results = await agentMemory.searchLongTermMemory({ - text: "What dietary requirements and food preferences does the user have?", - filter: { - namespaceRef: { eq: namespaceId }, - ownerId: { - eq: userId, - }, - }, - limit: 5, - }); - console.log("Automatically extracted memories:"); - console.dir(results, { depth: null }); -``` - -Run the file. The `items` array should contain memories derived from the conversation, such as the vegetarian requirement or preference for spicy food. Extraction is asynchronous, so run the search again if the array is empty. - -> [!NOTE] -> **What to expect:** Results similar to `User is a vegetarian` and `User prefers spicy food`. Your application did not create these memories directly. Redis Agent Memory derived them from the session event. The exact text and memory types can vary. - -The extracted memory remains searchable after the session expires, subject to the long term memory TTL. You can change the extraction cadence and both TTLs in the [Redis Agent Memory service configuration](/content/operate/iris/agent-memory/create-service.md#memory-configuration). - -## 3. Keep long conversations concise with automatic summarization - -Automatic summarization condenses older events and retains the most recent events in full. The retrieved session then contains a `summary` object and the recent `events` array, so the application can provide useful history without filling the model's context window with every original message. - -You enabled automatic summarization when you created the service. When the session reaches six events, Redis Agent Memory summarizes the older events and retains the two most recent events in full. - -### Add conversation turns - -Add this code after the first session event to continue the conversation past the configured threshold: - -```typescript - const turns = [ - { role: "ASSISTANT", actorId: "travel-agent", text: "What dates are you traveling?" }, - { role: "USER", actorId: userId, text: "I arrive on October 10 and leave on October 18." }, - { role: "ASSISTANT", actorId: "travel-agent", text: "Would you like formal or casual restaurants?" }, - { role: "USER", actorId: userId, text: "Mostly casual places near public transit." }, - { role: "ASSISTANT", actorId: "travel-agent", text: "Do you have a preferred budget?" }, - { role: "USER", actorId: userId, text: "About 40 euros per person." }, - ] as const; - - for (const turn of turns) { - await agentMemory.addSessionEvent({ - sessionId, - namespaceRef, - actorId: turn.actorId, - role: turn.role, - content: [{ text: turn.text }], - createdAt: new Date(), - }); - } -``` - -Run the code once, then comment out the loop to avoid adding the same turns again. Summarization runs in the background. - -### Retrieve the summarized session - -After a short wait, retrieve the session again: - -```typescript - const compactedSession = await agentMemory.getSessionMemory(sessionId); - console.log("Compacted session memory:"); - console.dir(compactedSession, { depth: null }); -``` - -Run the retrieval again after a short wait if `summary` is not present. Compare `summary.text` with the recent events. The summary should preserve earlier trip decisions while recent turns remain available in full. - -> [!NOTE] -> **What to expect:** A `summary` object that preserves details such as Tokyo, Kyoto, the travel dates, and food preferences. `summarizedUpToEventId` identifies the last event covered by the summary, while `events` contains the newer turns that remain in full. The exact summary text can vary. - -See [automatic summarization configuration](/content/operate/iris/agent-memory/create-service.md#automatic-summarization) for details. - -## 4. Extract business specific data with a custom memory type - -Built in memories preserve generally useful information. Custom memory types let an application extract structured information for its business domain. You configured `trip_preference` when you created the service, so it processed the same travel planning event independently. - -Search for the structured memory: - -```typescript - const customResults = await agentMemory.searchLongTermMemory({ - text: "What are the requirements for the user's trip?", - filter: { - namespaceRef: { eq: namespaceId }, - ownerId: { eq: userId }, - memoryType: { eq: "trip_preference" }, - }, - limit: 5, - }); - console.log("Trip preference memories:"); - console.dir(customResults, { depth: null }); -``` - -The `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: - -```json -{ - "memoryType": "trip_preference", - "attributes": { - "destinations": ["Tokyo", "Kyoto"], - "travel_period": "next month", - "dietary_requirements": ["vegetarian"], - "food_preferences": ["spicy food"] - } -} -``` - -The 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. - -Add this code after the custom-memory search to collect dietary requirements for the next agent turn: - -```typescript - const dietaryRequirements = new Set(); - if (customResults.items.length === 0) { - console.log("No matching trip preferences yet. Retry after a short wait."); - } - for (const memory of customResults.items) { - const requirements = memory.attributes?.dietary_requirements; - if (Array.isArray(requirements)) { - for (const value of requirements) { - if (typeof value === "string") dietaryRequirements.add(value); - } - } - } - console.log("Restaurant requirements:", [...dietaryRequirements].sort()); -``` - -If results remain empty, check that the type is enabled and that the owner and namespace IDs match the stored records. An empty result does not mean the user has no dietary requirements. - -### Create a custom memory directly - -If your application already has structured trip data, write it directly using the registered `trip_preference` type. This optional example represents data from a form and uses the same namespace: - -```typescript - const directResult = await agentMemory.bulkCreateLongTermMemories({ - memories: [{ - id: "trip-form-1", - text: "The user plans to visit Tokyo and Kyoto next month and requires vegetarian food.", - ownerId: userId, - memoryType: "trip_preference", - namespaceRef, - attributes: { - destinations: ["Tokyo", "Kyoto"], - travel_period: "next month", - dietary_requirements: ["vegetarian"], - food_preferences: ["spicy food"], - }, - }], - }); - console.dir(directResult, { depth: null }); -``` - -Inspect the bulk response 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. Run this write once; the memory ID identifies the record within the store. - -See [custom memory types](/content/operate/iris/agent-memory/create-service.md#custom-memory-types) for configuration requirements and limits. - -## 5. Guide extraction away from sensitive data - -The 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: - -```typescript - const sensitiveEvent = await agentMemory.addSessionEvent({ - sessionId, - namespaceRef, - actorId: userId, - role: "USER", - content: [{ - text: "I booked Hotel Sakura in Tokyo. For this example, the fictional booking confirmation code is DEMO-7QX9.", - }], - createdAt: new Date(), - }); - console.log("Event with excluded information:"); - console.dir(sensitiveEvent, { depth: null }); -``` - -Run the code once, then comment out the event creation and the two lines that print it. Wait at least one minute and search for the safe hotel information: - -```typescript - const exclusionResults = await agentMemory.searchLongTermMemory({ - text: "Where is the user staying in Tokyo?", - filter: { - namespaceRef: { eq: namespaceId }, - ownerId: { eq: userId }, - }, - limit: 5, - }); - console.log("Memories after semantic exclusion:"); - console.dir(exclusionResults, { depth: null }); -``` - -Inspect the returned memories. They can retain the hotel name, but should not contain `DEMO-7QX9` because the exclusion prompt covers booking confirmation codes. - -> [!NOTE] -> **What to expect:** A memory similar to `User booked Hotel Sakura in Tokyo` without the fictional confirmation code. If the code appears, refine the exclusion prompt and test again. Exclusions remain advisory. - -> [!WARNING] -> 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. - -See [sensitive-data exclusions](/content/operate/iris/agent-memory/create-service.md#sensitive-data-exclusions) for configuration details. - -## Next steps - -* Learn how to [organize memories with namespaces]({{< relref "/develop/ai/context-engine/agent-memory/developer-guide#organize-memories-with-namespaces" >}}). - -* Review the [TypeScript SDK package and reference](https://www.npmjs.com/package/@redis-iris/agent-memory). -* Try the [Python SDK quickstart](/content/develop/ai/context-engine/agent-memory/python-sdk-quickstart.md) or [REST API quickstart](/content/develop/ai/context-engine/agent-memory/rest-api-quickstart.md). -* Learn when to [create long term memories directly](/content/develop/ai/context-engine/agent-memory/developer-guide.md#create-long-term-memories). diff --git a/content/embeds/rc-agent-memory-get-started.md b/content/embeds/rc-agent-memory-get-started.md index a91027b30a..d21b18c4fa 100644 --- a/content/embeds/rc-agent-memory-get-started.md +++ b/content/embeds/rc-agent-memory-get-started.md @@ -1,3 +1,3 @@ -Follow the [Redis Cloud setup guide]({{< relref "/operate/iris/agent-memory/create-service" >}}) to create a Redis Agent Memory service. Then use the [REST API quickstart]({{< relref "/develop/ai/context-engine/agent-memory/rest-api-quickstart" >}}) to make your first session-memory and long-term-memory requests. +Follow the [Redis Cloud setup guide]({{< relref "/operate/iris/agent-memory/create-service" >}}) to create a Redis Agent Memory service. Then use the [quickstart]({{< relref "/develop/ai/context-engine/agent-memory/quickstart" >}}) to make your first session-memory and long-term-memory requests. After you set up Redis Agent Memory, you can [view and manage your service]({{< relref "/operate/iris/agent-memory/view-service" >}}). diff --git a/content/embeds/rc-agent-memory-quickstart-create-service.md b/content/embeds/rc-agent-memory-quickstart-create-service.md index c813e11be9..c9ab065876 100644 --- a/content/embeds/rc-agent-memory-quickstart-create-service.md +++ b/content/embeds/rc-agent-memory-quickstart-create-service.md @@ -10,33 +10,6 @@ | **Short-term TTL** | `1` day | How long session memory is retained. | | **Long-term TTL** | `365` days | How long long-term memories are retained. | | **Extraction cadence** | `1` minute | How often session events are processed for extraction. One minute is for this quickstart; use a longer production interval unless you need rapid extraction. | - | **Automatic summarization** | Enabled | Whether older session events are condensed into a summary. | - | **Summarize after (messages)** | `6` | The event count that triggers summarization. Six is for this quickstart; use a higher production threshold. | - | **Keep most recent (messages)** | `2` | How many recent events remain in full. Two is for this quickstart; retain more in production when recent turns are needed. | - -1. Under **Memory types & extraction**, select **Add type** and configure this custom memory type: - - | Setting | Value | What it controls | - |:--------|:------|:-----------------| - | **Name** | `trip_preference` | The identifier stored in `memoryType` and used in search filters. | - | **Description** | `Structured requirements for a planned trip` | The purpose of the custom memory type. | - | **Extraction prompt** | `Extract trip requirements only when the user states a destination or travel plan. Preserve explicit dietary requirements and food preferences.` | When to create the memory and which information to capture. | - | **Enabled** | Enabled | Whether new memories of this type are extracted. | - -1. Add these custom fields: - - | Field | Type | Description | - |:------|:-----|:------------| - | `destinations` | `list[str]` | Cities or countries the user plans to visit. | - | `travel_period` | `str` | When the user plans to travel. | - | `dietary_requirements` | `list[str]` | Dietary requirements that affect recommendations. | - | `food_preferences` | `list[str]` | Cuisines, flavors, or dining preferences stated by the user. | - -1. Under **Sensitive-data exclusions**, enable **Semantic exclusions** and enter this exclusion prompt: - - ```text - Do not keep passwords, access tokens, recovery codes, payment card information, or booking confirmation codes in long-term memory. - ``` 1. Select **Create**. 1. Copy the Redis Agent Memory API key and store it securely. @@ -45,8 +18,4 @@ Redis Cloud displays the Redis Agent Memory API key only once. If you lose it, [generate a new API key]({{< relref "/operate/iris/agent-memory/view-service#replace-service-api-key" >}}). {{< /warning >}} -These settings keep the background stages short enough to observe during the quickstart. For screenshots and configuration details, see [create a Redis Agent Memory service]({{< relref "/operate/iris/agent-memory/create-service" >}}). - -{{< warning >}} -Sensitive-data exclusions guide the extraction model but do not guarantee exclusion. Sensitive session content still reaches the model provider. Exclusions do not apply when an application creates long-term memories directly. -{{< /warning >}} +The one-minute extraction interval makes the result easier to observe in this example. For screenshots and configuration details, see [create a Redis Agent Memory service]({{< relref "/operate/iris/agent-memory/create-service" >}}). diff --git a/content/operate/iris/agent-memory/create-service.md b/content/operate/iris/agent-memory/create-service.md index f16b2c6d00..25a5b1c357 100644 --- a/content/operate/iris/agent-memory/create-service.md +++ b/content/operate/iris/agent-memory/create-service.md @@ -53,7 +53,7 @@ This is the only time the value of the user key is available. Save it to a secur If you lose the service key value, you will need to [generate a new service key]({{< relref "/operate/iris/agent-memory/view-service#replace-service-api-key" >}}) to be able to use the Agent Memory API. {{}} - After Redis Cloud creates your service, [continue with the REST API quickstart]({{< relref "/develop/ai/context-engine/agent-memory/rest-api-quickstart" >}}). + After Redis Cloud creates your service, [continue with the quickstart]({{< relref "/develop/ai/context-engine/agent-memory/quickstart" >}}). - If you want to customize your Redis Agent Memory service, select **Create custom**. @@ -248,6 +248,6 @@ If an error occurs, verify that your database is active. For help, [contact supp ## Next steps -After Redis Cloud creates your service, [continue with the REST API quickstart]({{< relref "/develop/ai/context-engine/agent-memory/rest-api-quickstart" >}}). +After Redis Cloud creates your service, [continue with the quickstart]({{< relref "/develop/ai/context-engine/agent-memory/quickstart" >}}). You can also [view and edit the service]({{< relref "/operate/iris/agent-memory/view-service" >}}). diff --git a/content/operate/iris/agent-memory/self-managed/api-examples.md b/content/operate/iris/agent-memory/self-managed/api-examples.md index f7ebef0a06..0220a8426b 100644 --- a/content/operate/iris/agent-memory/self-managed/api-examples.md +++ b/content/operate/iris/agent-memory/self-managed/api-examples.md @@ -277,7 +277,7 @@ curl -sS -X POST "$DP_URL/v1/stores/$STORE_ID/long-term-memory" \ }')" ``` -See [organize memories with namespaces]({{< relref "/develop/ai/context-engine/agent-memory/developer-guide#organize-memories-with-namespaces" >}}) for hierarchy, search, and migration guidance. +See [organize memories with namespaces]({{< relref "/develop/ai/context-engine/agent-memory/namespaces" >}}) for hierarchy, search, and management guidance. ### Search long-term memory diff --git a/content/operate/iris/agent-memory/view-service.md b/content/operate/iris/agent-memory/view-service.md index 0f82fae1e4..2a3b02c8a2 100644 --- a/content/operate/iris/agent-memory/view-service.md +++ b/content/operate/iris/agent-memory/view-service.md @@ -51,7 +51,7 @@ The **General settings** section provides the connection details and general set Select the **Copy** button next to the Store ID and API Base URL to copy them to the clipboard. -Follow the [Redis Agent Memory REST API quickstart]({{< relref "/develop/ai/context-engine/agent-memory/rest-api-quickstart" >}}) to use the connection information and API key. +Follow the [Redis Agent Memory quickstart]({{< relref "/develop/ai/context-engine/agent-memory/quickstart" >}}) to use the connection information and API key. ### Memory configuration From 7287b0969732c5fbc944018f703cba7cc84594bc Mon Sep 17 00:00:00 2001 From: Andrew Brookins Date: Tue, 15 Sep 2026 10:43:24 -0700 Subject: [PATCH 3/3] Make Agent Memory guides easier to find --- content/develop/ai/context-engine/agent-memory/_index.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/content/develop/ai/context-engine/agent-memory/_index.md b/content/develop/ai/context-engine/agent-memory/_index.md index 86061424c6..b4ba08e208 100644 --- a/content/develop/ai/context-engine/agent-memory/_index.md +++ b/content/develop/ai/context-engine/agent-memory/_index.md @@ -23,7 +23,14 @@ When enabled, automatic summarization compacts session memory by summarizing old Access Redis Agent Memory through the Python and TypeScript SDKs or its REST API. It works with any agent framework or LLM provider. -Follow the [quickstart]({{< relref "/develop/ai/context-engine/agent-memory/quickstart" >}}) with Python, TypeScript, or curl. +
+ {{< image-card image="images/ai-brain.svg" alt="Concepts icon" title="Concepts — Understand the memory model" url="/develop/ai/context-engine/agent-memory/concepts" >}} + {{< image-card image="images/ai-cube.svg" alt="Quickstart icon" title="Quickstart — Make your first requests" url="/develop/ai/context-engine/agent-memory/quickstart" >}} + {{< image-card image="images/ai-lib.svg" alt="Developer guide icon" title="Developer guide — Add memory to your application" url="/develop/ai/context-engine/agent-memory/developer-guide" >}} + {{< image-card image="images/ai-LLM-memory.svg" alt="Sessions icon" title="Sessions — Store conversation events" url="/develop/ai/context-engine/agent-memory/sessions" >}} + {{< image-card image="images/ai-brain-2.svg" alt="Long-term memory icon" title="Long-term memory — Recall information across conversations" url="/develop/ai/context-engine/agent-memory/long-term-memory" >}} + {{< image-card image="images/ai-semantic-routing.svg" alt="Namespaces icon" title="Namespaces — Organize your memories" url="/develop/ai/context-engine/agent-memory/namespaces" >}} +
## Why use Redis Agent Memory?