Skip to content

Commit 2bc0624

Browse files
sundargthbSundar Raghavan
andauthored
feat: Add automatic session management to Code Interpreter tool (#284)
* feat: Add automatic session management to Code Interpreter tool - Make session_name parameter optional in all action models - Implement auto-session mode with _ensure_session() method - Enable automatic session creation when sessions don't exist - Update tool description to prioritize common use cases - Improve developer experience by reducing manual session management * fix: docstring * fix: address PE feedback. Improved Isolation Multi-Case Support. Removed large tool description formatting changes from this PR * fix: address comments on docstrings, comments * fix: updated import uuid * fix: integration test issues --------- Co-authored-by: Sundar Raghavan <[email protected]>
1 parent 8e663df commit 2bc0624

File tree

8 files changed

+547
-235
lines changed

8 files changed

+547
-235
lines changed

src/strands_tools/code_interpreter/__init__.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,4 @@
3131
from .agent_core_code_interpreter import AgentCoreCodeInterpreter
3232
from .code_interpreter import CodeInterpreter
3333

34-
__all__ = [
35-
# Base classes
36-
"CodeInterpreter",
37-
# Platform implementations
38-
"AgentCoreCodeInterpreter",
39-
]
34+
__all__ = ["CodeInterpreter", "AgentCoreCodeInterpreter"]

src/strands_tools/code_interpreter/agent_core_code_interpreter.py

Lines changed: 125 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import uuid
23
from dataclasses import dataclass
34
from typing import Any, Dict, List, Optional
45

@@ -42,88 +43,59 @@ class SessionInfo:
4243

4344

4445
class AgentCoreCodeInterpreter(CodeInterpreter):
45-
"""
46-
Bedrock AgentCore implementation of the CodeInterpreter.
47-
48-
This class provides a code interpreter interface using AWS Bedrock AgentCore services.
49-
It supports executing Python, JavaScript, and TypeScript code in isolated sandbox
50-
environments with custom code interpreter identifiers.
51-
52-
The class maintains session state and provides methods for code execution, file
53-
operations, and session management. It supports both default AWS code interpreter
54-
environments and custom environments specified by identifier.
55-
56-
Examples:
57-
Basic usage with default identifier:
58-
59-
>>> interpreter = AgentCoreCodeInterpreter(region="us-west-2")
60-
>>> # Uses default identifier: "aws.codeinterpreter.v1"
61-
62-
Using a custom code interpreter identifier:
63-
64-
>>> custom_id = "my-custom-interpreter-abc123"
65-
>>> interpreter = AgentCoreCodeInterpreter(
66-
... region="us-west-2",
67-
... identifier=custom_id
68-
... )
69-
70-
Environment-specific usage:
71-
72-
>>> # For testing environments
73-
>>> test_interpreter = AgentCoreCodeInterpreter(
74-
... region="us-east-1",
75-
... identifier="test-interpreter-xyz789"
76-
... )
77-
78-
>>> # For production environments
79-
>>> prod_interpreter = AgentCoreCodeInterpreter(
80-
... region="us-west-2",
81-
... identifier="prod-interpreter-def456"
82-
... )
83-
84-
Attributes:
85-
region (str): The AWS region where the code interpreter service is hosted.
86-
identifier (str): The code interpreter identifier being used for sessions.
87-
"""
88-
89-
def __init__(self, region: Optional[str] = None, identifier: Optional[str] = None) -> None:
46+
def __init__(
47+
self,
48+
region: Optional[str] = None,
49+
identifier: Optional[str] = None,
50+
session_name: Optional[str] = None,
51+
auto_create: bool = True,
52+
) -> None:
9053
"""
9154
Initialize the Bedrock AgentCore code interpreter.
9255
9356
Args:
94-
region (Optional[str]): AWS region for the sandbox service. If not provided,
95-
the region will be resolved from AWS configuration (environment variables,
96-
AWS config files, or instance metadata). Defaults to None.
97-
identifier (Optional[str]): Custom code interpreter identifier to use
98-
for code execution sessions. This allows you to specify custom code
99-
interpreter environments instead of the default AWS-provided one.
100-
101-
Valid formats include:
102-
- Default identifier: "aws.codeinterpreter.v1" (used when None)
103-
- Custom identifier: "my-custom-interpreter-abc123"
104-
- Environment-specific: "test-interpreter-xyz789"
105-
106-
Note: Use the code interpreter ID, not the full ARN. The AWS service
107-
expects the identifier portion only (e.g., "my-interpreter-123" rather
108-
than "arn:aws:bedrock-agentcore:region:account:code-interpreter-custom/my-interpreter-123").
109-
110-
If not provided, defaults to "aws.codeinterpreter.v1" for backward
111-
compatibility. Defaults to None.
112-
113-
Note:
114-
This constructor maintains full backward compatibility. Existing code
115-
that doesn't specify the identifier parameter will continue to work
116-
unchanged with the default AWS code interpreter environment.
117-
118-
Raises:
119-
Exception: If there are issues with AWS region resolution or client
120-
initialization during session creation.
57+
region: AWS region for the sandbox service.
58+
identifier: Custom code interpreter identifier.
59+
session_name: Session name or strategy:
60+
- None (default): Generate random session ID per instance
61+
- "runtime": Use AgentCore runtime session_id (set at invocation time)
62+
- Specific string: Use this exact session name
63+
auto_create: Automatically create sessions if they don't exist.
64+
- True (default): Create sessions on-demand
65+
- False: Fail if session doesn't exist (strict mode)
66+
67+
Examples:
68+
# Case 1: Random session per instance (default)
69+
interpreter = AgentCoreCodeInterpreter()
70+
71+
# Case 2: Bind to runtime session (recommended for production)
72+
session_id = getattr(context, 'session_id', None)
73+
interpreter = AgentCoreCodeInterpreter(session_name=session_id)
74+
75+
# Case 3: Named session with auto-create
76+
interpreter = AgentCoreCodeInterpreter(session_name="my-analysis")
77+
78+
# Case 4: Strict mode - must pre-initialize
79+
interpreter = AgentCoreCodeInterpreter(
80+
session_name="must-exist",
81+
auto_create=False
82+
)
12183
"""
12284
super().__init__()
12385
self.region = resolve_region(region)
12486
self.identifier = identifier or "aws.codeinterpreter.v1"
87+
self.auto_create = auto_create
88+
89+
# Generate session name strategy
90+
if session_name is None:
91+
self.default_session = f"session-{uuid.uuid4().hex[:12]}"
92+
else:
93+
self.default_session = session_name
94+
12595
self._sessions: Dict[str, SessionInfo] = {}
12696

97+
logger.info(f"Initialized CodeInterpreter with session='{self.default_session}', " f"auto_create={auto_create}")
98+
12799
def start_platform(self) -> None:
128100
"""Initialize the Bedrock AgentCoreplatform connection."""
129101
pass
@@ -240,84 +212,127 @@ def list_local_sessions(self) -> Dict[str, Any]:
240212
"content": [{"json": {"sessions": sessions_info, "totalSessions": len(sessions_info)}}],
241213
}
242214

215+
def _ensure_session(self, session_name: Optional[str]) -> tuple[str, Optional[Dict[str, Any]]]:
216+
"""
217+
Ensure a session exists based on configuration.
218+
219+
Behavior matrix:
220+
| session_name | auto_create | Behavior |
221+
|--------------|-------------|----------|
222+
| None | True | Use default_session, create if needed |
223+
| None | False | Use default_session, error if missing |
224+
| "my-session" | True | Use "my-session", create if needed |
225+
| "my-session" | False | Use "my-session", error if missing |
226+
227+
Args:
228+
session_name: Explicit session name from action, or None to use default
229+
230+
Returns:
231+
Tuple of (session_name, error_dict or None)
232+
"""
233+
# Determine which session to use
234+
target_session = session_name if session_name else self.default_session
235+
236+
# Session already exists - use it
237+
if target_session in self._sessions:
238+
logger.debug(f"Using existing session: {target_session}")
239+
return target_session, None
240+
241+
# Session doesn't exist - check auto_create
242+
if self.auto_create:
243+
# Auto-create the session
244+
logger.info(f"Auto-creating session: {target_session}")
245+
init_action = InitSessionAction(
246+
type="initSession", session_name=target_session, description="Auto-initialized session"
247+
)
248+
result = self.init_session(init_action)
249+
250+
if result.get("status") != "success":
251+
return target_session, result
252+
253+
logger.info(f"Successfully auto-created session: {target_session}")
254+
return target_session, None
255+
256+
# auto_create=False and session doesn't exist - raise exception
257+
logger.debug(f"Session '{target_session}' not found (auto_create disabled)")
258+
raise ValueError(f"Session '{target_session}' not found. Create it first using initSession.")
259+
243260
def execute_code(self, action: ExecuteCodeAction) -> Dict[str, Any]:
244-
"""Execute code in a Bedrock AgentCoresession."""
245-
if action.session_name not in self._sessions:
246-
return {"status": "error", "content": [{"text": f"Session '{action.session_name}' not found"}]}
261+
"""Execute code in a Bedrock AgentCore session with automatic session management."""
262+
session_name, error = self._ensure_session(action.session_name)
263+
if error:
264+
return error
247265

248-
logger.debug(f"Executing {action.language} code in session '{action.session_name}'")
266+
logger.debug(f"Executing {action.language} code in session '{session_name}'")
249267

250-
# Use the invoke method with proper parameters as shown in the example
251268
params = {"code": action.code, "language": action.language.value, "clearContext": action.clear_context}
252-
response = self._sessions[action.session_name].client.invoke("executeCode", params)
269+
response = self._sessions[session_name].client.invoke("executeCode", params)
253270

254271
return self._create_tool_result(response)
255272

256273
def execute_command(self, action: ExecuteCommandAction) -> Dict[str, Any]:
257-
"""Execute a command in a Bedrock AgentCoresession."""
258-
if action.session_name not in self._sessions:
259-
return {"status": "error", "content": [{"text": f"Session '{action.session_name}' not found"}]}
274+
"""Execute a command in a Bedrock AgentCore session with automatic session management."""
275+
session_name, error = self._ensure_session(action.session_name)
276+
if error:
277+
return error
260278

261-
logger.debug(f"Executing command in session '{action.session_name}': {action.command}")
279+
logger.debug(f"Executing command in session '{session_name}': {action.command}")
262280

263-
# Use the invoke method with proper parameters as shown in the example
264281
params = {"command": action.command}
265-
response = self._sessions[action.session_name].client.invoke("executeCommand", params)
282+
response = self._sessions[session_name].client.invoke("executeCommand", params)
266283

267284
return self._create_tool_result(response)
268285

269286
def read_files(self, action: ReadFilesAction) -> Dict[str, Any]:
270-
"""Read files from a Bedrock AgentCoresession."""
271-
if action.session_name not in self._sessions:
272-
return {"status": "error", "content": [{"text": f"Session '{action.session_name}' not found"}]}
287+
"""Read files from a Bedrock AgentCore session with automatic session management."""
288+
session_name, error = self._ensure_session(action.session_name)
289+
if error:
290+
return error
273291

274-
logger.debug(f"Reading files from session '{action.session_name}': {action.paths}")
292+
logger.debug(f"Reading files from session '{session_name}': {action.paths}")
275293

276-
# Use the invoke method with proper parameters as shown in the example
277294
params = {"paths": action.paths}
278-
response = self._sessions[action.session_name].client.invoke("readFiles", params)
295+
response = self._sessions[session_name].client.invoke("readFiles", params)
279296

280297
return self._create_tool_result(response)
281298

282299
def list_files(self, action: ListFilesAction) -> Dict[str, Any]:
283-
"""List files in a Bedrock AgentCoresession directory."""
284-
if action.session_name not in self._sessions:
285-
return {"status": "error", "content": [{"text": f"Session '{action.session_name}' not found"}]}
300+
"""List files in a Bedrock AgentCore session directory with automatic session management."""
301+
session_name, error = self._ensure_session(action.session_name)
302+
if error:
303+
return error
286304

287-
logger.debug(f"Listing files in session '{action.session_name}' at path: {action.path}")
305+
logger.debug(f"Listing files in session '{session_name}' at path: {action.path}")
288306

289-
# Use the invoke method with proper parameters as shown in the example
290307
params = {"path": action.path}
291-
response = self._sessions[action.session_name].client.invoke("listFiles", params)
308+
response = self._sessions[session_name].client.invoke("listFiles", params)
292309

293310
return self._create_tool_result(response)
294311

295312
def remove_files(self, action: RemoveFilesAction) -> Dict[str, Any]:
296-
"""Remove files from a Bedrock AgentCoresession."""
297-
if action.session_name not in self._sessions:
298-
return {"status": "error", "content": [{"text": f"Session '{action.session_name}' not found"}]}
313+
"""Remove files from a Bedrock AgentCore session with automatic session management."""
314+
session_name, error = self._ensure_session(action.session_name)
315+
if error:
316+
return error
299317

300-
logger.debug(f"Removing files from session '{action.session_name}': {action.paths}")
318+
logger.debug(f"Removing files from session '{session_name}': {action.paths}")
301319

302-
# Use the invoke method with proper parameters as shown in the example
303320
params = {"paths": action.paths}
304-
response = self._sessions[action.session_name].client.invoke("removeFiles", params)
321+
response = self._sessions[session_name].client.invoke("removeFiles", params)
305322

306323
return self._create_tool_result(response)
307324

308325
def write_files(self, action: WriteFilesAction) -> Dict[str, Any]:
309-
"""Write files to a Bedrock AgentCoresession."""
310-
if action.session_name not in self._sessions:
311-
return {"status": "error", "content": [{"text": f"Session '{action.session_name}' not found"}]}
326+
"""Write files to a Bedrock AgentCore session with automatic session management."""
327+
session_name, error = self._ensure_session(action.session_name)
328+
if error:
329+
return error
312330

313-
logger.debug(f"Writing {len(action.content)} files to session '{action.session_name}'")
331+
logger.debug(f"Writing {len(action.content)} files to session '{session_name}'")
314332

315-
# Convert FileContent objects to dictionaries for the API
316333
content_dicts = [{"path": fc.path, "text": fc.text} for fc in action.content]
317-
318-
# Use the invoke method with proper parameters as shown in the example
319334
params = {"content": content_dicts}
320-
response = self._sessions[action.session_name].client.invoke("writeFiles", params)
335+
response = self._sessions[session_name].client.invoke("writeFiles", params)
321336

322337
return self._create_tool_result(response)
323338

0 commit comments

Comments
 (0)