diff --git a/src/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap b/src/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap index d06f89b05..375db5930 100644 --- a/src/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +++ b/src/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap @@ -2225,21 +2225,38 @@ agent = Agent( ) -# Session and Runner -async def setup_session_and_runner(user_id, session_id): - ensure_credentials_loaded() - session_service = InMemorySessionService() - session = await session_service.create_session( +# Module-level session service and runner (preserves history across invocations) +_session_service = InMemorySessionService() +_runner = None + +def get_or_create_runner(): + global _runner + if _runner is None: + ensure_credentials_loaded() + _runner = Runner( + agent=agent, + app_name=APP_NAME, + session_service=_session_service, + ) + return _runner + + +async def get_or_create_session(user_id, session_id): + session = await _session_service.get_session( app_name=APP_NAME, user_id=user_id, session_id=session_id ) - runner = Runner(agent=agent, app_name=APP_NAME, session_service=session_service) - return session, runner + if session is None: + session = await _session_service.create_session( + app_name=APP_NAME, user_id=user_id, session_id=session_id + ) + return session # Agent Interaction async def call_agent_async(query, user_id, session_id): content = types.Content(role="user", parts=[types.Part(text=query)]) - session, runner = await setup_session_and_runner(user_id, session_id) + runner = get_or_create_runner() + session = await get_or_create_session(user_id, session_id) events = runner.run_async( user_id=user_id, session_id=session.id, new_message=content ) @@ -2524,6 +2541,7 @@ Thumbs.db exports[`Assets Directory Snapshots > Python framework assets > python/python/http/langchain_langgraph/base/main.py should match snapshot 1`] = ` "import os from langchain_core.messages import HumanMessage +from langgraph.checkpoint.memory import InMemorySaver from langgraph.prebuilt import create_react_agent from langchain.tools import tool from bedrock_agentcore.runtime import BedrockAgentCoreApp @@ -2556,6 +2574,9 @@ def add_numbers(a: int, b: int) -> int: # Define a collection of tools used by the model tools = [add_numbers] +# Module-level checkpointer preserves conversation history across invocations +_checkpointer = InMemorySaver() + @app.entrypoint async def invoke(payload, context): @@ -2573,15 +2594,22 @@ async def invoke(payload, context): if mcp_client: mcp_tools = await mcp_client.get_tools() - # Define the agent using create_react_agent - graph = create_react_agent(get_or_create_model(), tools=mcp_tools + tools) + # Define the agent using create_react_agent (checkpointer is shared across invocations) + graph = create_react_agent( + get_or_create_model(), + tools=mcp_tools + tools, + prompt="You are a helpful assistant. Use tools when appropriate.", + checkpointer=_checkpointer, + ) # Process the user prompt prompt = payload.get("prompt", "What can you help me with?") + session_id = getattr(context, "session_id", "default-session") log.info(f"Agent input: {prompt}") - # Run the agent - result = await graph.ainvoke({"messages": [HumanMessage(content=prompt)]}) + # Run the agent (checkpointer auto-loads/saves history per session) + config = {"configurable": {"thread_id": session_id}} + result = await graph.ainvoke({"messages": [HumanMessage(content=prompt)]}, config=config) # Return result output = result["messages"][-1].content @@ -2942,7 +2970,8 @@ Thumbs.db exports[`Assets Directory Snapshots > Python framework assets > python/python/http/openaiagents/base/main.py should match snapshot 1`] = ` "import os -from agents import Agent, Runner, function_tool +from functools import lru_cache +from agents import Agent, Runner, SQLiteSession, function_tool from bedrock_agentcore.runtime import BedrockAgentCoreApp from model.load import load_model {{#if hasGateway}} @@ -2978,28 +3007,35 @@ def add_numbers(a: int, b: int) -> int: return a + b +@lru_cache(maxsize=128) +def get_session(session_id): + return SQLiteSession(session_id) + + # Define the agent execution -async def main(query): +async def main(query, session): ensure_credentials_loaded() try: {{#if hasGateway}} if mcp_servers: agent = Agent( name="{{ name }}", + instructions="You are a helpful assistant. Use tools when appropriate.", model="gpt-4.1", mcp_servers=mcp_servers, tools=[add_numbers] ) - result = await Runner.run(agent, query) + result = await Runner.run(agent, query, session=session) return result else: agent = Agent( name="{{ name }}", + instructions="You are a helpful assistant. Use tools when appropriate.", model="gpt-4.1", mcp_servers=[], tools=[add_numbers] ) - result = await Runner.run(agent, query) + result = await Runner.run(agent, query, session=session) return result {{else}} if mcp_servers: @@ -3007,20 +3043,22 @@ async def main(query): active_servers = [server] agent = Agent( name="{{ name }}", + instructions="You are a helpful assistant. Use tools when appropriate.", model="gpt-4.1", mcp_servers=active_servers, tools=[add_numbers] ) - result = await Runner.run(agent, query) + result = await Runner.run(agent, query, session=session) return result else: agent = Agent( name="{{ name }}", + instructions="You are a helpful assistant. Use tools when appropriate.", model="gpt-4.1", mcp_servers=[], tools=[add_numbers] ) - result = await Runner.run(agent, query) + result = await Runner.run(agent, query, session=session) return result {{/if}} except Exception as e: @@ -3034,9 +3072,11 @@ async def invoke(payload, context): # Process the user prompt prompt = payload.get("prompt", "What can you help me with?") + session_id = getattr(context, "session_id", "default-session") + session = get_session(session_id) - # Run the agent - result = await main(prompt) + # Run the agent (session automatically loads/saves conversation history) + result = await main(prompt, session) # Return result return {"result": result.final_output} @@ -3289,7 +3329,8 @@ Thumbs.db" `; exports[`Assets Directory Snapshots > Python framework assets > python/python/http/strands/base/main.py should match snapshot 1`] = ` -"from strands import Agent, tool +"from functools import lru_cache +from strands import Agent, tool from bedrock_agentcore.runtime import BedrockAgentCoreApp from model.load import load_model {{#if hasGateway}} @@ -3328,37 +3369,26 @@ for mcp_client in mcp_clients: {{#if hasMemory}} -def agent_factory(): - cache = {} - def get_or_create_agent(session_id, user_id): - key = f"{session_id}/{user_id}" - if key not in cache: - # Create an agent for the given session_id and user_id - cache[key] = Agent( - model=load_model(), - session_manager=get_memory_session_manager(session_id, user_id), - system_prompt=""" - You are a helpful assistant. Use tools when appropriate. - """, - tools=tools - ) - return cache[key] - return get_or_create_agent -get_or_create_agent = agent_factory() +@lru_cache(maxsize=128) +def get_or_create_agent(session_id, user_id): + return Agent( + model=load_model(), + session_manager=get_memory_session_manager(session_id, user_id), + system_prompt=""" + You are a helpful assistant. Use tools when appropriate. + """, + tools=tools + ) {{else}} -_agent = None - -def get_or_create_agent(): - global _agent - if _agent is None: - _agent = Agent( - model=load_model(), - system_prompt=""" - You are a helpful assistant. Use tools when appropriate. - """, - tools=tools - ) - return _agent +@lru_cache(maxsize=128) +def get_or_create_agent(session_id): + return Agent( + model=load_model(), + system_prompt=""" + You are a helpful assistant. Use tools when appropriate. + """, + tools=tools + ) {{/if}} @@ -3371,7 +3401,8 @@ async def invoke(payload, context): user_id = getattr(context, 'user_id', 'default-user') agent = get_or_create_agent(session_id, user_id) {{else}} - agent = get_or_create_agent() + session_id = getattr(context, 'session_id', 'default-session') + agent = get_or_create_agent(session_id) {{/if}} # Execute and format response diff --git a/src/assets/python/http/googleadk/base/main.py b/src/assets/python/http/googleadk/base/main.py index 5ce996089..26f4f2997 100644 --- a/src/assets/python/http/googleadk/base/main.py +++ b/src/assets/python/http/googleadk/base/main.py @@ -53,21 +53,38 @@ def ensure_credentials_loaded(): ) -# Session and Runner -async def setup_session_and_runner(user_id, session_id): - ensure_credentials_loaded() - session_service = InMemorySessionService() - session = await session_service.create_session( +# Module-level session service and runner (preserves history across invocations) +_session_service = InMemorySessionService() +_runner = None + +def get_or_create_runner(): + global _runner + if _runner is None: + ensure_credentials_loaded() + _runner = Runner( + agent=agent, + app_name=APP_NAME, + session_service=_session_service, + ) + return _runner + + +async def get_or_create_session(user_id, session_id): + session = await _session_service.get_session( app_name=APP_NAME, user_id=user_id, session_id=session_id ) - runner = Runner(agent=agent, app_name=APP_NAME, session_service=session_service) - return session, runner + if session is None: + session = await _session_service.create_session( + app_name=APP_NAME, user_id=user_id, session_id=session_id + ) + return session # Agent Interaction async def call_agent_async(query, user_id, session_id): content = types.Content(role="user", parts=[types.Part(text=query)]) - session, runner = await setup_session_and_runner(user_id, session_id) + runner = get_or_create_runner() + session = await get_or_create_session(user_id, session_id) events = runner.run_async( user_id=user_id, session_id=session.id, new_message=content ) diff --git a/src/assets/python/http/langchain_langgraph/base/main.py b/src/assets/python/http/langchain_langgraph/base/main.py index 949a652fa..3a55888a7 100644 --- a/src/assets/python/http/langchain_langgraph/base/main.py +++ b/src/assets/python/http/langchain_langgraph/base/main.py @@ -1,5 +1,6 @@ import os from langchain_core.messages import HumanMessage +from langgraph.checkpoint.memory import InMemorySaver from langgraph.prebuilt import create_react_agent from langchain.tools import tool from bedrock_agentcore.runtime import BedrockAgentCoreApp @@ -32,6 +33,9 @@ def add_numbers(a: int, b: int) -> int: # Define a collection of tools used by the model tools = [add_numbers] +# Module-level checkpointer preserves conversation history across invocations +_checkpointer = InMemorySaver() + @app.entrypoint async def invoke(payload, context): @@ -49,15 +53,22 @@ async def invoke(payload, context): if mcp_client: mcp_tools = await mcp_client.get_tools() - # Define the agent using create_react_agent - graph = create_react_agent(get_or_create_model(), tools=mcp_tools + tools) + # Define the agent using create_react_agent (checkpointer is shared across invocations) + graph = create_react_agent( + get_or_create_model(), + tools=mcp_tools + tools, + prompt="You are a helpful assistant. Use tools when appropriate.", + checkpointer=_checkpointer, + ) # Process the user prompt prompt = payload.get("prompt", "What can you help me with?") + session_id = getattr(context, "session_id", "default-session") log.info(f"Agent input: {prompt}") - # Run the agent - result = await graph.ainvoke({"messages": [HumanMessage(content=prompt)]}) + # Run the agent (checkpointer auto-loads/saves history per session) + config = {"configurable": {"thread_id": session_id}} + result = await graph.ainvoke({"messages": [HumanMessage(content=prompt)]}, config=config) # Return result output = result["messages"][-1].content diff --git a/src/assets/python/http/openaiagents/base/main.py b/src/assets/python/http/openaiagents/base/main.py index 57f497554..0e25034d8 100644 --- a/src/assets/python/http/openaiagents/base/main.py +++ b/src/assets/python/http/openaiagents/base/main.py @@ -1,5 +1,6 @@ import os -from agents import Agent, Runner, function_tool +from functools import lru_cache +from agents import Agent, Runner, SQLiteSession, function_tool from bedrock_agentcore.runtime import BedrockAgentCoreApp from model.load import load_model {{#if hasGateway}} @@ -35,28 +36,35 @@ def add_numbers(a: int, b: int) -> int: return a + b +@lru_cache(maxsize=128) +def get_session(session_id): + return SQLiteSession(session_id) + + # Define the agent execution -async def main(query): +async def main(query, session): ensure_credentials_loaded() try: {{#if hasGateway}} if mcp_servers: agent = Agent( name="{{ name }}", + instructions="You are a helpful assistant. Use tools when appropriate.", model="gpt-4.1", mcp_servers=mcp_servers, tools=[add_numbers] ) - result = await Runner.run(agent, query) + result = await Runner.run(agent, query, session=session) return result else: agent = Agent( name="{{ name }}", + instructions="You are a helpful assistant. Use tools when appropriate.", model="gpt-4.1", mcp_servers=[], tools=[add_numbers] ) - result = await Runner.run(agent, query) + result = await Runner.run(agent, query, session=session) return result {{else}} if mcp_servers: @@ -64,20 +72,22 @@ async def main(query): active_servers = [server] agent = Agent( name="{{ name }}", + instructions="You are a helpful assistant. Use tools when appropriate.", model="gpt-4.1", mcp_servers=active_servers, tools=[add_numbers] ) - result = await Runner.run(agent, query) + result = await Runner.run(agent, query, session=session) return result else: agent = Agent( name="{{ name }}", + instructions="You are a helpful assistant. Use tools when appropriate.", model="gpt-4.1", mcp_servers=[], tools=[add_numbers] ) - result = await Runner.run(agent, query) + result = await Runner.run(agent, query, session=session) return result {{/if}} except Exception as e: @@ -91,9 +101,11 @@ async def invoke(payload, context): # Process the user prompt prompt = payload.get("prompt", "What can you help me with?") + session_id = getattr(context, "session_id", "default-session") + session = get_session(session_id) - # Run the agent - result = await main(prompt) + # Run the agent (session automatically loads/saves conversation history) + result = await main(prompt, session) # Return result return {"result": result.final_output} diff --git a/src/assets/python/http/strands/base/main.py b/src/assets/python/http/strands/base/main.py index 215469150..5d26167d1 100644 --- a/src/assets/python/http/strands/base/main.py +++ b/src/assets/python/http/strands/base/main.py @@ -1,3 +1,4 @@ +from functools import lru_cache from strands import Agent, tool from bedrock_agentcore.runtime import BedrockAgentCoreApp from model.load import load_model @@ -37,37 +38,26 @@ def add_numbers(a: int, b: int) -> int: {{#if hasMemory}} -def agent_factory(): - cache = {} - def get_or_create_agent(session_id, user_id): - key = f"{session_id}/{user_id}" - if key not in cache: - # Create an agent for the given session_id and user_id - cache[key] = Agent( - model=load_model(), - session_manager=get_memory_session_manager(session_id, user_id), - system_prompt=""" - You are a helpful assistant. Use tools when appropriate. - """, - tools=tools - ) - return cache[key] - return get_or_create_agent -get_or_create_agent = agent_factory() +@lru_cache(maxsize=128) +def get_or_create_agent(session_id, user_id): + return Agent( + model=load_model(), + session_manager=get_memory_session_manager(session_id, user_id), + system_prompt=""" + You are a helpful assistant. Use tools when appropriate. + """, + tools=tools + ) {{else}} -_agent = None - -def get_or_create_agent(): - global _agent - if _agent is None: - _agent = Agent( - model=load_model(), - system_prompt=""" - You are a helpful assistant. Use tools when appropriate. - """, - tools=tools - ) - return _agent +@lru_cache(maxsize=128) +def get_or_create_agent(session_id): + return Agent( + model=load_model(), + system_prompt=""" + You are a helpful assistant. Use tools when appropriate. + """, + tools=tools + ) {{/if}} @@ -80,7 +70,8 @@ async def invoke(payload, context): user_id = getattr(context, 'user_id', 'default-user') agent = get_or_create_agent(session_id, user_id) {{else}} - agent = get_or_create_agent() + session_id = getattr(context, 'session_id', 'default-session') + agent = get_or_create_agent(session_id) {{/if}} # Execute and format response