-
Notifications
You must be signed in to change notification settings - Fork 80
feat(memory): add episodic memory strategy support #208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| name: str, | ||
| description: Optional[str] = None, | ||
| namespaces: Optional[List[str]] = None, | ||
| reflection_namespaces: Optional[List[str]] = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be optional if we require a reflection namespace?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should leave it to match the same behavior as what is defined in the cli and model, as validation of this will come from the service side, not the client side
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discussed offline and it is changed now
| name: str, | ||
| description: Optional[str] = None, | ||
| namespaces: Optional[List[str]] = None, | ||
| reflection_namespaces: Optional[List[str]] = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment as above. This should be required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
344b39d to
5528322
Compare
| except Exception as e: | ||
| assert "Unexpected error" in str(e) | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: We should also add negative test cases to check for errors being thrown
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would like negative tests too
| self.add_user_preference_strategy(memory_id, name, description, namespaces) | ||
| return self._wait_for_memory_active(memory_id, max_wait, poll_interval) | ||
|
|
||
| def add_episodic_strategy( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: The parameter order differs from other strategy methods like add_user_preference_strategy(memory_id, name, description, namespaces). Having
reflection_namespaces (required) between name and description breaks the pattern. The only impact is that the inconsistent API may confuse users familiar with other strategy methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, decided to move reflection_namespaces before description since the required parameters should all come before the optional ones ideally
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me, I also thought the same but then noticed that odd difference.
| "appendToPrompt": reflection_config["prompt"], | ||
| "modelId": reflection_config["modelId"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Should we consider adding input validation on reflection_config["prompt"] and reflection_config["modelId"]? I think this may raise a KeyError with an unclear error message if they are missing. We could instead raise a ValueError like:
if key not in reflection_config:
raise ValueError(f"reflection_config missing required key: {key}")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this should be there. Prompt and modelId are required for all extraction, consolidation, and reflection config also so we can add a validation to check all of them like:
for config, config_name in [
(extraction_config, "extraction_config"),
(consolidation_config, "consolidation_config"),
(reflection_config, "reflection_config"),
]:
for key in ("prompt", "modelId"):
if key not in config:
raise ValueError(f"{config_name} missing required key: {key}")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also will move reflection to be in the strategy block to read cleaner like:
strategy = {
StrategyType.CUSTOM.value: {
"name": name,
"configuration": {
"episodicOverride": {
"extraction": {
"appendToPrompt": extraction_config["prompt"],
"modelId": extraction_config["modelId"],
},
"consolidation": {
"appendToPrompt": consolidation_config["prompt"],
"modelId": consolidation_config["modelId"],
},
"reflection": {
"appendToPrompt": reflection_config["prompt"],
"modelId": reflection_config["modelId"],
**(
{"namespaces": reflection_config["namespaces"]}
if "namespaces" in reflection_config
else {}
),
},
}
},
}
}
5528322 to
4ab866c
Compare
| except Exception as e: | ||
| assert "Unexpected error" in str(e) | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would like negative tests too
| wrapped_config["extraction"] = { | ||
| "customExtractionConfiguration": {CUSTOM_EXTRACTION_WRAPPER_KEYS[override_enum]: extraction} | ||
| } | ||
| elif strategy_type == "CUSTOM" and override_type and any(key in extraction for key in custom_override_keys): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a little messy here to understand. What does this part of code achieve?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
None of the other memory strategies have negative tests, so I just followed the same structure for the strategy related tests in this one. Should we still add negative tests just for episodic?
-
Refactoring this to be simpler, the wrapping was off before itself but it should be fine now. Tested it to match the behavior of the bedrock-agentcore-control cli.
- Add EPISODIC to StrategyType, MemoryStrategyTypeEnum, OverrideType enums - Add EPISODIC to DEFAULT_NAMESPACES - Add episodic wrapper keys for extraction, consolidation, reflection - Add add_episodic_strategy() and add_episodic_strategy_and_wait() - Add add_custom_episodic_strategy() and add_custom_episodic_strategy_and_wait() - Update _wrap_configuration() to handle EPISODIC_OVERRIDE - Add unit tests for all new episodic functionality
4ab866c to
8bc4b00
Compare
jariy17
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just need to address non-nit comments.
| @@ -86,12 +90,18 @@ class MessageRole(Enum): | |||
| CUSTOM_EXTRACTION_WRAPPER_KEYS: Dict[OverrideType, str] = { | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: OVERRIDE_EXTRACTION_WRAPPERS_KEYS
| wrapped_config["extraction"] = extraction | ||
| elif any(key in extraction for key in builtin_config_keys): | ||
| strategy_type_enum = MemoryStrategyTypeEnum(strategy_type) | ||
| if strategy_type in ("SEMANTIC", "USER_PREFERENCE"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does episodic strategy has a special wrapped_config dictionary like SEMANTIC and USER_PREFERENCES
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ran local integration tests to verify the behavior on update memory operations match what is expected from the service, and the wrapped values are accepted
Issue #, if available:
Description of changes:
Enable support for using Episodic Strategies with AgentCore Memory
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.