-
Notifications
You must be signed in to change notification settings - Fork 80
feat(memory): update session manager for Strands integration #170
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
base: main
Are you sure you want to change the base?
Conversation
| class ShortTermRetrievalConfig(BaseModel): | ||
| """Configuration for Short term memory retrieval operations""" | ||
|
|
||
| branch_filter: Optional[bool] = True |
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.
Is there a plan to have branching on LTM? We can refactor later but then we should consider lifting the branch_filter into a base class
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.
yes there is.
| default_branch: Optional[BranchConfig] = Field( | ||
| default_factory=lambda: BranchConfig(name="main", root_event_id="") | ||
| ) | ||
| short_term_retrieval_config: Optional[ShortTermRetrievalConfig] = ( |
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 this needs it's own variable? Can we allow for this to be set to the retrieval_config? Or do we anticipate users passing in both STM and LTM config for the same session?
| if self.config.metadata: | ||
| metadata.update(self.config.metadata) | ||
|
|
||
| if branch_name == "main": |
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.
since this is a special case let's make it a constant at the top
| created_event = self.memory_session_manager.create_event(**event_params) | ||
| else: | ||
| created_event = self.memory_session_manager.fork_conversation( | ||
| actor_id=self.config.actor_id, |
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.
is it necessary to pass in all these self parameters? This method signature is very bulky. Let's use the self data from inside that fork_conversation method
| def create_session(self, session: Session, **kwargs: Any) -> Session: | ||
| """Create a new session.""" | ||
| self._validate_session_id(session.session_id) | ||
| return session | ||
|
|
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.
where in agentcore is the session data stored?
| if payload and "blob" in payload[0]: | ||
| blob_data = json.loads(payload[0]["blob"]) | ||
| # Extract SessionAgent from blob. | ||
| session_data = blob_data.get("session_state") or blob_data.get( |
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.
what's with this or here? are we storing it either as a session_state or a session object?
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.
will remove session
| if role in self.config.message_types: | ||
| content = message.get("content", [{}])[0].get("text", "") | ||
| mapped_role = role_map.get(role, MessageRole.ASSISTANT) | ||
| if role == "user": |
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 can use the mapped_role here
| registry (HookRegistry): The hook registry to register callbacks with. | ||
| **kwargs: Additional keyword arguments. | ||
| """ | ||
| RepositorySessionManager.register_hooks(self, registry, **kwargs) |
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 to be clear, this line removes all the hooks that are expected as part of the session management approach in strands
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.
yes, I think it should override those. We don't need the parent hooks alongside these for Session Management.
| messages[-1]["content"][0][ | ||
| "text" | ||
| ] = f"<previous_context>\n{context_text}\n</previous_context>\n\n{original_text}\n" | ||
| event.agent.messages[-1]["content"][0]["text"] = context_text |
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.
what's the intent behind this why we are modifying the message directly? This will not get saved to LTM as the message added hook will not be triggered. Is this the expected behavior?
| except Exception as e: | ||
| logger.error("Failed to list messages from AgentCore Memory: %s", 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.
I'm starting to see this error come up each time I initialize an agent now. Here is my code:
import uuid
import boto3
from datetime import date, datetime
from strands import Agent
from bedrock_agentcore.memory import MemoryClient
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig, RetrievalConfig
from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager
MEM_ID = 'BasicTestMemory20251103223555-Mowu0s9pwM'
ACTOR_ID = "actor_id_test_%s" % datetime.now().strftime("%Y%m%d%H%M%S")
SESSION_ID = "testing_session_id_%s" % datetime.now().strftime("%Y%m%d%H%M%S")
config = AgentCoreMemoryConfig(
memory_id=MEM_ID,
session_id=SESSION_ID,
actor_id=ACTOR_ID
)
stm_session_manager = AgentCoreMemorySessionManager(config, region_name='us-east-1')
stm_agent_1 = Agent(session_manager=stm_session_manager)then
stm_agent_1("hello")
I see Failed to retrieve customer context: 'NoneType' object has no attribute 'items' printed out.
And then if I now initialize another agent with the same session manager I see:
stm_agent_2 = Agent(session_manager=stm_session_manager)
Failed to list messages from AgentCore Memory: Expecting value: line 1 column 1 (char 0)
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.
yes fix added to check empty events
| short_term_retrieval_config: Optional[ShortTermRetrievalConfig] = ( | ||
| ShortTermRetrievalConfig() | ||
| ) | ||
| message_types: Optional[List[str]] = Field(default=["user", "assistant"]) |
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 to emphasize its agentcore message_types ("user", "assistant", "toolResult", "other"). *check if this filter issues. add to default "other", "tool_use".
| logger.error("Failed to retrieve customer context: %s", e) | ||
|
|
||
| @override | ||
| def register_hooks(self, registry: HookRegistry, **kwargs) -> 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.
This PR got rid of session because
- if you want to support multiple agents, agentcore memory can only handle one session per agent.
| boto3_session=boto_session, | ||
| boto_client_config=boto_client_config, | ||
| ) | ||
| self.agent_id = 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.
THIS PR has no idea how to use agent_id. Research what agent_id used for strands.
| self._latest_agent_message[agent.agent_id] = session_message | ||
| # Load previous messages | ||
| prev_messages = self.list_messages( | ||
| self.config.session_id, agent.agent_id, limit=10 |
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.
check if this breaks the current behavior, where we only store 10.
| logger.info("Retrieved %s customer context items", len(all_context)) | ||
| messages[-1]["content"][0][ | ||
| "text" | ||
| ] = f"<previous_context>\n{context_text}\n</previous_context>\n\n{original_text}\n" |
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.
This should still show the issue where the model will focus the user stuff.
| """Configuration for Short term memory retrieval operations""" | ||
|
|
||
| branch_filter: Optional[bool] = True | ||
| metadata: Optional[Dict[str, StringValue]] = 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.
This is how you would filter by agent_id. Use to get a agent's memory
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.
this is currently supportly supported.
This should be named additional_metadata_fields.
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.
Customers don't really need metadata filtering just keep agent_id.
| ) | ||
| message_types: Optional[List[str]] = Field(default=["user", "assistant"]) | ||
| metadata: Optional[Dict[str, StringValue]] = ( | ||
| None # Currently only supports agent_id. Will be extended further. |
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.
the comment is lie. it allow customer's metada.
Issue #, if available: #149
Description of changes: Update Agentcore SessionManager for strands to use branching and metadata features of 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.