diff --git a/tests/unit/vertexai/genai/replays/test_create_agent_engine_memory.py b/tests/unit/vertexai/genai/replays/test_create_agent_engine_memory.py index c6fa20a732..ad6177f7cb 100644 --- a/tests/unit/vertexai/genai/replays/test_create_agent_engine_memory.py +++ b/tests/unit/vertexai/genai/replays/test_create_agent_engine_memory.py @@ -25,11 +25,28 @@ def test_create_memory_with_ttl(client): assert isinstance(agent_engine, types.AgentEngine) assert isinstance(agent_engine.api_resource, types.ReasoningEngine) + metadata = { + "my_string_key": types.MemoryMetadataValue( + string_value="my_string_value" + ), + "my_double_key": types.MemoryMetadataValue(double_value=123.456), + "my_boolean_key": types.MemoryMetadataValue(bool_value=True), + "my_timestamp_key": types.MemoryMetadataValue( + timestamp_value=datetime.datetime( + 2027, 1, 1, 12, 30, 00, tzinfo=datetime.timezone.utc + ) + ), + } + operation = client.agent_engines.memories.create( name=agent_engine.api_resource.name, fact="memory_fact", scope={"user_id": "123"}, - config=types.AgentEngineMemoryConfig(display_name="my_memory_fact", ttl="120s"), + config=types.AgentEngineMemoryConfig( + display_name="my_memory_fact", + ttl="120s", + metadata=metadata, + ), ) assert isinstance(operation, types.AgentEngineMemoryOperation) assert operation.response.fact == "memory_fact" @@ -42,6 +59,7 @@ def test_create_memory_with_ttl(client): <= operation.response.expire_time <= operation.response.create_time + datetime.timedelta(seconds=120.5) ) + assert operation.response.metadata == metadata # Clean up resources. client.agent_engines.delete(name=agent_engine.api_resource.name, force=True) @@ -51,7 +69,7 @@ def test_create_memory_with_expire_time(client): assert isinstance(agent_engine, types.AgentEngine) assert isinstance(agent_engine.api_resource, types.ReasoningEngine) expire_time = datetime.datetime( - 2026, 1, 1, 12, 30, 00, tzinfo=datetime.timezone.utc + 2027, 1, 1, 12, 30, 00, tzinfo=datetime.timezone.utc ) operation = client.agent_engines.memories.create( diff --git a/tests/unit/vertexai/genai/replays/test_generate_agent_engine_memories.py b/tests/unit/vertexai/genai/replays/test_generate_agent_engine_memories.py index dbd6cc855e..cd62e3d17a 100644 --- a/tests/unit/vertexai/genai/replays/test_generate_agent_engine_memories.py +++ b/tests/unit/vertexai/genai/replays/test_generate_agent_engine_memories.py @@ -14,6 +14,7 @@ # # pylint: disable=protected-access,bad-continuation,missing-function-docstring +import datetime import pytest @@ -145,6 +146,138 @@ def test_generate_memories_direct_memories_source(client): client.agent_engines.delete(name=agent_engine.api_resource.name, force=True) +def test_generate_memories_with_metadata(client): + agent_engine = client.agent_engines.create() + metadata = { + "my_string_key": types.MemoryMetadataValue( + string_value="my_string_value" + ), + "my_double_key": types.MemoryMetadataValue(double_value=123.456), + "my_boolean_key": types.MemoryMetadataValue(bool_value=True), + "my_timestamp_key": types.MemoryMetadataValue( + timestamp_value=datetime.datetime( + 2027, 1, 1, 12, 30, 00, tzinfo=datetime.timezone.utc + ) + ), + } + # Reuse the same content and scope for all generation requests to ensure + # that the same memory is updated. + direct_memories_source = types.GenerateMemoriesRequestDirectMemoriesSource( + direct_memories=[ + types.GenerateMemoriesRequestDirectMemoriesSourceDirectMemory( + fact="I am a software engineer." + ), + ] + ) + scope = {"user_id": "test-user-id"} + + operation = client.agent_engines.memories.generate( + name=agent_engine.api_resource.name, + scope=scope, + direct_memories_source=direct_memories_source, + config=types.GenerateAgentEngineMemoriesConfig( + metadata=metadata + ), + ) + assert len(operation.response.generated_memories) >= 1 + memory = client.agent_engines.memories.get( + name=operation.response.generated_memories[0].memory.name + ) + assert memory.metadata == metadata + + # Overwrite the metadata. + overwrite_metadata = { + "my_string_key": types.MemoryMetadataValue(string_value="new_value"), + } + operation = client.agent_engines.memories.generate( + name=agent_engine.api_resource.name, + scope=scope, + direct_memories_source=direct_memories_source, + config=types.GenerateAgentEngineMemoriesConfig( + metadata=overwrite_metadata, + metadata_merge_strategy=types.MemoryMetadataMergeStrategy.OVERWRITE, + ), + ) + assert len(operation.response.generated_memories) >= 1 + assert ( + operation.response.generated_memories[0].action + == types.GenerateMemoriesResponseGeneratedMemoryAction.UPDATED + ) + memory = client.agent_engines.memories.get( + name=operation.response.generated_memories[0].memory.name + ) + assert memory.metadata == overwrite_metadata + + # Merge the metadata. + new_metadata = { + "my_double_key": types.MemoryMetadataValue(double_value=123.456), + } + operation = client.agent_engines.memories.generate( + name=agent_engine.api_resource.name, + scope=scope, + direct_memories_source=direct_memories_source, + config=types.GenerateAgentEngineMemoriesConfig( + metadata=new_metadata, + metadata_merge_strategy=types.MemoryMetadataMergeStrategy.MERGE, + ), + ) + assert len(operation.response.generated_memories) >= 1 + assert ( + operation.response.generated_memories[0].action + == types.GenerateMemoriesResponseGeneratedMemoryAction.UPDATED + ) + memory = client.agent_engines.memories.get( + name=operation.response.generated_memories[0].memory.name + ) + assert memory.metadata == {**overwrite_metadata, **new_metadata} + + # Restrict consolidation based on metadata values. For the first request, + # there's no existing memories that match the metadata, so a new memory is + # created. + restricted_metadata = { + "my_string_key": types.MemoryMetadataValue(string_value="new_value2"), + } + operation = client.agent_engines.memories.generate( + name=agent_engine.api_resource.name, + scope=scope, + direct_memories_source=direct_memories_source, + config=types.GenerateAgentEngineMemoriesConfig( + metadata=restricted_metadata, + metadata_merge_strategy="REQUIRE_EXACT_MATCH", + ), + ) + assert len(operation.response.generated_memories) == 1 + # Metadata doesn't match existing memory, so a new memory is created. + assert ( + operation.response.generated_memories[0].action + == types.GenerateMemoriesResponseGeneratedMemoryAction.CREATED + ) + memory = client.agent_engines.memories.get( + name=operation.response.generated_memories[0].memory.name + ) + assert memory.metadata == restricted_metadata + + # Send a second request where the metadata matches only one of the existing + # memories. + operation = client.agent_engines.memories.generate( + name=agent_engine.api_resource.name, + scope=scope, + direct_memories_source=direct_memories_source, + config=types.GenerateAgentEngineMemoriesConfig( + metadata=restricted_metadata, + metadata_merge_strategy="REQUIRE_EXACT_MATCH", + ), + ) + assert len(operation.response.generated_memories) == 1 + assert ( + operation.response.generated_memories[0].action + == types.GenerateMemoriesResponseGeneratedMemoryAction.UPDATED + ) + assert operation.response.generated_memories[0].memory.name == memory.name + + client.agent_engines.delete(name=agent_engine.api_resource.name, force=True) + + pytestmark = pytest_helper.setup( file=__file__, globals_for_file=globals(), diff --git a/tests/unit/vertexai/genai/replays/test_retrieve_agent_engine_memories.py b/tests/unit/vertexai/genai/replays/test_retrieve_agent_engine_memories.py index 15cb7caca8..1a9c1d6045 100644 --- a/tests/unit/vertexai/genai/replays/test_retrieve_agent_engine_memories.py +++ b/tests/unit/vertexai/genai/replays/test_retrieve_agent_engine_memories.py @@ -14,6 +14,7 @@ # # pylint: disable=protected-access,bad-continuation,missing-function-docstring +import datetime import pytest @@ -115,6 +116,57 @@ def test_retrieve_memories_with_simple_retrieval_params(client): agent_engine.delete(force=True) +def test_retrieve_memories_with_metadata(client): + agent_engine = client.agent_engines.create() + metadata = { + "my_string_key": types.MemoryMetadataValue( + string_value="my_string_value" + ), + "my_double_key": types.MemoryMetadataValue(double_value=123.456), + "my_boolean_key": types.MemoryMetadataValue(bool_value=True), + "my_timestamp_key": types.MemoryMetadataValue( + timestamp_value=datetime.datetime( + 2027, 1, 1, 12, 30, 00, tzinfo=datetime.timezone.utc + ) + ), + } + scope = {"user_id": "123"} + client.agent_engines.memories.create( + name=agent_engine.api_resource.name, + fact="memory_fact_1", + scope=scope, + ) + operation = client.agent_engines.memories.create( + name=agent_engine.api_resource.name, + fact="memory_fact_2", + scope=scope, + config={"metadata": metadata}, + ) + memory_name2 = operation.response.name + + results = client.agent_engines.memories.retrieve( + name=agent_engine.api_resource.name, + scope=scope, + config={ + "filter_groups": [ + { + "filters": [ + { + "key": "my_string_key", + "value": {"string_value": "my_string_value"} + } + ] + } + ], + }, + ) + assert len(results) == 1 + assert results[0].memory.name == memory_name2 + + # Clean up resources. + agent_engine.delete(force=True) + + pytestmark = pytest_helper.setup( file=__file__, globals_for_file=globals(), diff --git a/vertexai/_genai/memories.py b/vertexai/_genai/memories.py index c710e7a68e..9f4f1dcde2 100644 --- a/vertexai/_genai/memories.py +++ b/vertexai/_genai/memories.py @@ -77,6 +77,9 @@ def _AgentEngineMemoryConfig_to_vertex( parent_object, ["topics"], [item for item in getv(from_object, ["topics"])] ) + if getv(from_object, ["metadata"]) is not None: + setv(parent_object, ["metadata"], getv(from_object, ["metadata"])) + return to_object @@ -153,6 +156,16 @@ def _GenerateAgentEngineMemoriesConfig_to_vertex( getv(from_object, ["disable_memory_revisions"]), ) + if getv(from_object, ["metadata"]) is not None: + setv(parent_object, ["metadata"], getv(from_object, ["metadata"])) + + if getv(from_object, ["metadata_merge_strategy"]) is not None: + setv( + parent_object, + ["metadataMergeStrategy"], + getv(from_object, ["metadata_merge_strategy"]), + ) + return to_object @@ -316,6 +329,13 @@ def _RetrieveAgentEngineMemoriesConfig_to_vertex( if getv(from_object, ["filter"]) is not None: setv(parent_object, ["filter"], getv(from_object, ["filter"])) + if getv(from_object, ["filter_groups"]) is not None: + setv( + parent_object, + ["filterGroups"], + [item for item in getv(from_object, ["filter_groups"])], + ) + return to_object @@ -413,6 +433,9 @@ def _UpdateAgentEngineMemoryConfig_to_vertex( parent_object, ["topics"], [item for item in getv(from_object, ["topics"])] ) + if getv(from_object, ["metadata"]) is not None: + setv(parent_object, ["metadata"], getv(from_object, ["metadata"])) + if getv(from_object, ["update_mask"]) is not None: setv( parent_object, ["_query", "updateMask"], getv(from_object, ["update_mask"]) diff --git a/vertexai/_genai/types/__init__.py b/vertexai/_genai/types/__init__.py index 939f04301a..904c390edf 100644 --- a/vertexai/_genai/types/__init__.py +++ b/vertexai/_genai/types/__init__.py @@ -578,7 +578,17 @@ from .common import MemoryBankCustomizationConfigMemoryTopicManagedMemoryTopicOrDict from .common import MemoryBankCustomizationConfigMemoryTopicOrDict from .common import MemoryBankCustomizationConfigOrDict +from .common import MemoryConjunctionFilter +from .common import MemoryConjunctionFilterDict +from .common import MemoryConjunctionFilterOrDict from .common import MemoryDict +from .common import MemoryFilter +from .common import MemoryFilterDict +from .common import MemoryFilterOrDict +from .common import MemoryMetadataMergeStrategy +from .common import MemoryMetadataValue +from .common import MemoryMetadataValueDict +from .common import MemoryMetadataValueOrDict from .common import MemoryOrDict from .common import MemoryRevision from .common import MemoryRevisionDict @@ -613,6 +623,7 @@ from .common import ObservabilityEvalCase from .common import ObservabilityEvalCaseDict from .common import ObservabilityEvalCaseOrDict +from .common import Operator from .common import OptimizeConfig from .common import OptimizeConfigDict from .common import OptimizeConfigOrDict @@ -1523,6 +1534,15 @@ "RetrieveMemoriesRequestSimpleRetrievalParams", "RetrieveMemoriesRequestSimpleRetrievalParamsDict", "RetrieveMemoriesRequestSimpleRetrievalParamsOrDict", + "MemoryMetadataValue", + "MemoryMetadataValueDict", + "MemoryMetadataValueOrDict", + "MemoryFilter", + "MemoryFilterDict", + "MemoryFilterOrDict", + "MemoryConjunctionFilter", + "MemoryConjunctionFilterDict", + "MemoryConjunctionFilterOrDict", "RetrieveAgentEngineMemoriesConfig", "RetrieveAgentEngineMemoriesConfigDict", "RetrieveAgentEngineMemoriesConfigOrDict", @@ -1909,6 +1929,7 @@ "IdentityType", "AgentServerMode", "ManagedTopicEnum", + "Operator", "Language", "MachineConfig", "State", @@ -1917,6 +1938,7 @@ "RubricContentType", "EvaluationRunState", "OptimizeTarget", + "MemoryMetadataMergeStrategy", "GenerateMemoriesResponseGeneratedMemoryAction", "PromptOptimizerMethod", "PromptData", diff --git a/vertexai/_genai/types/common.py b/vertexai/_genai/types/common.py index 7c48303729..9511fbf7d1 100644 --- a/vertexai/_genai/types/common.py +++ b/vertexai/_genai/types/common.py @@ -243,6 +243,19 @@ class ManagedTopicEnum(_common.CaseInSensitiveEnum): """Information that the user explicitly requested to remember or forget.""" +class Operator(_common.CaseInSensitiveEnum): + """Operator to apply to the filter. If not set, then EQUAL will be used.""" + + OPERATOR_UNSPECIFIED = "OPERATOR_UNSPECIFIED" + """Unspecified operator. Defaults to EQUAL.""" + EQUAL = "EQUAL" + """Equal to.""" + GREATER_THAN = "GREATER_THAN" + """Greater than.""" + LESS_THAN = "LESS_THAN" + """Less than.""" + + class Language(_common.CaseInSensitiveEnum): """The coding language supported in this environment.""" @@ -345,6 +358,19 @@ class OptimizeTarget(_common.CaseInSensitiveEnum): """The prompt optimizer based on user provided examples with target responses.""" +class MemoryMetadataMergeStrategy(_common.CaseInSensitiveEnum): + """The strategy to use when applying metadata to existing memories during consolidation.""" + + METADATA_MERGE_STRATEGY_UNSPECIFIED = "METADATA_MERGE_STRATEGY_UNSPECIFIED" + """The metadata merge strategy is unspecified.""" + OVERWRITE = "OVERWRITE" + """Replace the metadata of the updated memories with the new metadata.""" + MERGE = "MERGE" + """Append new metadata to the existing metadata. If there are duplicate keys, the existing values will be overwritten.""" + REQUIRE_EXACT_MATCH = "REQUIRE_EXACT_MATCH" + """Restrict consolidation to memories that have exactly the same metadata as the request. If a memory doesn't have the same metadata, it is not eligible for consolidation.""" + + class GenerateMemoriesResponseGeneratedMemoryAction(_common.CaseInSensitiveEnum): """The action to take.""" @@ -6622,6 +6648,10 @@ class AgentEngineMemoryConfig(_common.BaseModel): topics: Optional[list[MemoryTopicId]] = Field( default=None, description="""Optional. The topics of the memory.""" ) + metadata: Optional[dict[str, "MemoryMetadataValue"]] = Field( + default=None, + description="""Optional. User-provided metadata for the Memory. This information was provided when creating, updating, or generating the Memory. It was not generated by Memory Bank.""", + ) class AgentEngineMemoryConfigDict(TypedDict, total=False): @@ -6659,6 +6689,9 @@ class AgentEngineMemoryConfigDict(TypedDict, total=False): topics: Optional[list[MemoryTopicIdDict]] """Optional. The topics of the memory.""" + metadata: Optional[dict[str, "MemoryMetadataValueDict"]] + """Optional. User-provided metadata for the Memory. This information was provided when creating, updating, or generating the Memory. It was not generated by Memory Bank.""" + AgentEngineMemoryConfigOrDict = Union[ AgentEngineMemoryConfig, AgentEngineMemoryConfigDict @@ -6769,6 +6802,10 @@ class Memory(_common.BaseModel): topics: Optional[list[MemoryTopicId]] = Field( default=None, description="""Optional. The Topics of the Memory.""" ) + metadata: Optional[dict[str, "MemoryMetadataValue"]] = Field( + default=None, + description="""Optional. User-provided metadata for the Memory. This information was provided when creating, updating, or generating the Memory. It was not generated by Memory Bank.""", + ) class MemoryDict(TypedDict, total=False): @@ -6813,6 +6850,9 @@ class MemoryDict(TypedDict, total=False): topics: Optional[list[MemoryTopicIdDict]] """Optional. The Topics of the Memory.""" + metadata: Optional[dict[str, "MemoryMetadataValueDict"]] + """Optional. User-provided metadata for the Memory. This information was provided when creating, updating, or generating the Memory. It was not generated by Memory Bank.""" + MemoryOrDict = Union[Memory, MemoryDict] @@ -7124,6 +7164,14 @@ class GenerateAgentEngineMemoriesConfig(_common.BaseModel): default=None, description="""Optional. Input only. If true, no revisions will be created for this request.""", ) + metadata: Optional[dict[str, "MemoryMetadataValue"]] = Field( + default=None, + description="""Optional. User-provided metadata for the generated memories. This is not generated by Memory Bank.""", + ) + metadata_merge_strategy: Optional[MemoryMetadataMergeStrategy] = Field( + default=None, + description="""Optional. The strategy to use when applying metadata to existing memories.""", + ) class GenerateAgentEngineMemoriesConfigDict(TypedDict, total=False): @@ -7155,6 +7203,12 @@ class GenerateAgentEngineMemoriesConfigDict(TypedDict, total=False): disable_memory_revisions: Optional[bool] """Optional. Input only. If true, no revisions will be created for this request.""" + metadata: Optional[dict[str, "MemoryMetadataValueDict"]] + """Optional. User-provided metadata for the generated memories. This is not generated by Memory Bank.""" + + metadata_merge_strategy: Optional[MemoryMetadataMergeStrategy] + """Optional. The strategy to use when applying metadata to existing memories.""" + GenerateAgentEngineMemoriesConfigOrDict = Union[ GenerateAgentEngineMemoriesConfig, GenerateAgentEngineMemoriesConfigDict @@ -7612,6 +7666,95 @@ class RetrieveMemoriesRequestSimpleRetrievalParamsDict(TypedDict, total=False): ] +class MemoryMetadataValue(_common.BaseModel): + """Memory metadata.""" + + timestamp_value: Optional[datetime.datetime] = Field( + default=None, + description="""Timestamp value. When filtering on timestamp values, only the seconds field will be compared.""", + ) + double_value: Optional[float] = Field(default=None, description="""Double value.""") + bool_value: Optional[bool] = Field(default=None, description="""Boolean value.""") + string_value: Optional[str] = Field(default=None, description="""String value.""") + + +class MemoryMetadataValueDict(TypedDict, total=False): + """Memory metadata.""" + + timestamp_value: Optional[datetime.datetime] + """Timestamp value. When filtering on timestamp values, only the seconds field will be compared.""" + + double_value: Optional[float] + """Double value.""" + + bool_value: Optional[bool] + """Boolean value.""" + + string_value: Optional[str] + """String value.""" + + +MemoryMetadataValueOrDict = Union[MemoryMetadataValue, MemoryMetadataValueDict] + + +class MemoryFilter(_common.BaseModel): + """Filter to apply when retrieving memories.""" + + op: Optional[Operator] = Field( + default=None, + description="""Operator to apply to the filter. If not set, then EQUAL will be used.""", + ) + negate: Optional[bool] = Field( + default=None, description="""If true, the filter will be negated.""" + ) + key: Optional[str] = Field( + default=None, + description="""Key of the filter. For example, "author" would apply to `metadata` entries with the key "author".""", + ) + value: Optional[MemoryMetadataValue] = Field( + default=None, description="""Value to compare to.""" + ) + + +class MemoryFilterDict(TypedDict, total=False): + """Filter to apply when retrieving memories.""" + + op: Optional[Operator] + """Operator to apply to the filter. If not set, then EQUAL will be used.""" + + negate: Optional[bool] + """If true, the filter will be negated.""" + + key: Optional[str] + """Key of the filter. For example, "author" would apply to `metadata` entries with the key "author".""" + + value: Optional[MemoryMetadataValueDict] + """Value to compare to.""" + + +MemoryFilterOrDict = Union[MemoryFilter, MemoryFilterDict] + + +class MemoryConjunctionFilter(_common.BaseModel): + """The conjunction filter for memories.""" + + filters: Optional[list[MemoryFilter]] = Field( + default=None, description="""Filters that will combined using AND logic.""" + ) + + +class MemoryConjunctionFilterDict(TypedDict, total=False): + """The conjunction filter for memories.""" + + filters: Optional[list[MemoryFilterDict]] + """Filters that will combined using AND logic.""" + + +MemoryConjunctionFilterOrDict = Union[ + MemoryConjunctionFilter, MemoryConjunctionFilterDict +] + + class RetrieveAgentEngineMemoriesConfig(_common.BaseModel): """Config for retrieving memories.""" @@ -7629,6 +7772,23 @@ class RetrieveAgentEngineMemoriesConfig(_common.BaseModel): * `update_time` """, ) + filter_groups: Optional[list[MemoryConjunctionFilter]] = Field( + default=None, + description="""Metadata filters that will be applied to the retrieved memories' + `metadata` using OR logic. Filters are defined using disjunctive normal + form (OR of ANDs). + + For example: + `filter_groups: [{filters: [{key: "author", value: {string_value: "agent + `123"}, op: EQUAL}]}, {filters: [{key: "label", value: {string_value: + "travel"}, op: EQUAL}, {key: "author", value: {string_value: "agent 321"}, + op: EQUAL}]}]` + + would be equivalent to the logical expression: + `(metadata.author = "agent 123" OR (metadata.label = "travel" AND + metadata.author = "agent 321"))`. + """, + ) class RetrieveAgentEngineMemoriesConfigDict(TypedDict, total=False): @@ -7647,6 +7807,22 @@ class RetrieveAgentEngineMemoriesConfigDict(TypedDict, total=False): * `update_time` """ + filter_groups: Optional[list[MemoryConjunctionFilterDict]] + """Metadata filters that will be applied to the retrieved memories' + `metadata` using OR logic. Filters are defined using disjunctive normal + form (OR of ANDs). + + For example: + `filter_groups: [{filters: [{key: "author", value: {string_value: "agent + `123"}, op: EQUAL}]}, {filters: [{key: "label", value: {string_value: + "travel"}, op: EQUAL}, {key: "author", value: {string_value: "agent 321"}, + op: EQUAL}]}]` + + would be equivalent to the logical expression: + `(metadata.author = "agent 123" OR (metadata.label = "travel" AND + metadata.author = "agent 321"))`. + """ + RetrieveAgentEngineMemoriesConfigOrDict = Union[ RetrieveAgentEngineMemoriesConfig, RetrieveAgentEngineMemoriesConfigDict @@ -7913,6 +8089,10 @@ class UpdateAgentEngineMemoryConfig(_common.BaseModel): topics: Optional[list[MemoryTopicId]] = Field( default=None, description="""Optional. The topics of the memory.""" ) + metadata: Optional[dict[str, MemoryMetadataValue]] = Field( + default=None, + description="""Optional. User-provided metadata for the Memory. This information was provided when creating, updating, or generating the Memory. It was not generated by Memory Bank.""", + ) update_mask: Optional[str] = Field( default=None, description="""The update mask to apply. For the `FieldMask` definition, see @@ -7955,6 +8135,9 @@ class UpdateAgentEngineMemoryConfigDict(TypedDict, total=False): topics: Optional[list[MemoryTopicIdDict]] """Optional. The topics of the memory.""" + metadata: Optional[dict[str, MemoryMetadataValueDict]] + """Optional. User-provided metadata for the Memory. This information was provided when creating, updating, or generating the Memory. It was not generated by Memory Bank.""" + update_mask: Optional[str] """The update mask to apply. For the `FieldMask` definition, see https://protobuf.dev/reference/protobuf/google.protobuf/#field-mask."""