Skip to content

Conversation

@aneeshsabarad
Copy link
Contributor

  • 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

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.

name: str,
description: Optional[str] = None,
namespaces: Optional[List[str]] = None,
reflection_namespaces: Optional[List[str]] = None,

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?

Copy link
Contributor Author

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

Copy link
Contributor Author

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,

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

except Exception as e:
assert "Unexpected error" in str(e)


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

Copy link
Contributor

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(
Copy link
Contributor

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.

Copy link
Contributor Author

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

Copy link
Contributor

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.

Comment on lines 1553 to 1554
"appendToPrompt": reflection_config["prompt"],
"modelId": reflection_config["modelId"],
Copy link
Contributor

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}")

Copy link
Contributor Author

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}")

Copy link
Contributor Author

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 {}
                            ),
                        },
                    }
                },
            }
        }

except Exception as e:
assert "Unexpected error" in str(e)


Copy link
Contributor

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):
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. 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?

  2. 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
Copy link
Contributor

@jariy17 jariy17 left a 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] = {
Copy link
Contributor

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"):
Copy link
Contributor

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

Copy link
Contributor Author

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

@jariy17 jariy17 merged commit 0df9757 into main Jan 12, 2026
18 of 19 checks passed
@jariy17 jariy17 deleted the episodic_memory_strategy branch January 12, 2026 21:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants