strands-code-agent 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. strands_code_agent-0.1.0/.github/workflows/publish.yml +19 -0
  2. strands_code_agent-0.1.0/.gitignore +26 -0
  3. strands_code_agent-0.1.0/CODE_OF_CONDUCT.md +4 -0
  4. strands_code_agent-0.1.0/CONTRIBUTING.md +59 -0
  5. strands_code_agent-0.1.0/LICENSE +17 -0
  6. strands_code_agent-0.1.0/PKG-INFO +163 -0
  7. strands_code_agent-0.1.0/README.md +137 -0
  8. strands_code_agent-0.1.0/pyproject.toml +36 -0
  9. strands_code_agent-0.1.0/strands_code_agent/__init__.py +4 -0
  10. strands_code_agent-0.1.0/strands_code_agent/code_agent.py +142 -0
  11. strands_code_agent-0.1.0/strands_code_agent/document_code.py +63 -0
  12. strands_code_agent-0.1.0/strands_code_agent/imports.py +36 -0
  13. strands_code_agent-0.1.0/strands_code_agent/python_environments/__init__.py +0 -0
  14. strands_code_agent-0.1.0/strands_code_agent/python_environments/base.py +44 -0
  15. strands_code_agent-0.1.0/strands_code_agent/python_environments/local_exec.py +28 -0
  16. strands_code_agent-0.1.0/strands_code_agent/python_environments/local_sandboxed.py +63 -0
  17. strands_code_agent-0.1.0/strands_code_agent/toolkits.py +42 -0
  18. strands_code_agent-0.1.0/strands_code_agent/utils.py +7 -0
  19. strands_code_agent-0.1.0/tests/__init__.py +1 -0
  20. strands_code_agent-0.1.0/tests/test_code_agent.py +277 -0
  21. strands_code_agent-0.1.0/tests/test_document_code.py +148 -0
  22. strands_code_agent-0.1.0/tests/test_exec_python_interpreter.py +130 -0
  23. strands_code_agent-0.1.0/tests/test_readme_examples.py +128 -0
  24. strands_code_agent-0.1.0/tests/test_sandboxed_python_interpreter.py +244 -0
  25. strands_code_agent-0.1.0/tests/test_toolkits.py +150 -0
  26. strands_code_agent-0.1.0/tests/test_utils.py +41 -0
@@ -0,0 +1,19 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ id-token: write
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.11"
17
+ - run: pip install build
18
+ - run: python -m build
19
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,26 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ *.egg
9
+
10
+ # Virtual environments
11
+ .venv/
12
+ venv/
13
+
14
+ # IDE
15
+ .idea/
16
+ .vscode/
17
+ *.swp
18
+
19
+ # OS
20
+ .DS_Store
21
+ Thumbs.db
22
+
23
+ # Testing
24
+ .pytest_cache/
25
+ .coverage
26
+ htmlcov/
@@ -0,0 +1,4 @@
1
+ ## Code of Conduct
2
+ This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3
+ For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4
+ opensource-codeofconduct@amazon.com with any additional questions or comments.
@@ -0,0 +1,59 @@
1
+ # Contributing Guidelines
2
+
3
+ Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4
+ documentation, we greatly value feedback and contributions from our community.
5
+
6
+ Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7
+ information to effectively respond to your bug report or contribution.
8
+
9
+
10
+ ## Reporting Bugs/Feature Requests
11
+
12
+ We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13
+
14
+ When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already
15
+ reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16
+
17
+ * A reproducible test case or series of steps
18
+ * The version of our code being used
19
+ * Any modifications you've made relevant to the bug
20
+ * Anything unusual about your environment or deployment
21
+
22
+
23
+ ## Contributing via Pull Requests
24
+ Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25
+
26
+ 1. You are working against the latest source on the *main* branch.
27
+ 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28
+ 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29
+
30
+ To send us a pull request, please:
31
+
32
+ 1. Fork the repository.
33
+ 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34
+ 3. Ensure local tests pass.
35
+ 4. Commit to your fork using clear commit messages.
36
+ 5. Send us a pull request, answering any default questions in the pull request interface.
37
+ 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38
+
39
+ GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40
+ [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41
+
42
+
43
+ ## Finding contributions to work on
44
+ Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start.
45
+
46
+
47
+ ## Code of Conduct
48
+ This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49
+ For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50
+ opensource-codeofconduct@amazon.com with any additional questions or comments.
51
+
52
+
53
+ ## Security issue notifications
54
+ If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55
+
56
+
57
+ ## Licensing
58
+
59
+ See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
@@ -0,0 +1,17 @@
1
+ MIT No Attribution
2
+
3
+ Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
13
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
15
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
+
@@ -0,0 +1,163 @@
1
+ Metadata-Version: 2.4
2
+ Name: strands-code-agent
3
+ Version: 0.1.0
4
+ Summary: A coding agent built on Strands Agents SDK that uses code generation as the primary action interface
5
+ Project-URL: Homepage, https://github.com/aws-samples/sample-strands-code-agent
6
+ Project-URL: Repository, https://github.com/aws-samples/sample-strands-code-agent
7
+ Project-URL: Issues, https://github.com/aws-samples/sample-strands-code-agent/issues
8
+ Author: Emilio Monti
9
+ License-Expression: MIT-0
10
+ License-File: LICENSE
11
+ Keywords: agents,code-generation,llm,repl,strands
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: jinja2>=3.0
23
+ Requires-Dist: smolagents>=1.0.0
24
+ Requires-Dist: strands-agents>=0.1.0
25
+ Description-Content-Type: text/markdown
26
+
27
+ # strands-code-agent
28
+
29
+ A coding agent built on [Strands Agents SDK](https://github.com/strands-agents/sdk-python) that replaces the tool-calling paradigm with code generation as the agent's primary action interface. Rather than invoking structured tools by name and passing results through the conversation context, the agent writes Python code in a persistent REPL where domain capabilities (database queries, APIs, etc.) are exposed as importable library functions. This keeps intermediate data as native Python objects in memory and lets the agent compose multi-step logic in a single code block instead of orchestrating sequential tool calls. In empirical evaluations on the Data Agent Benchmark, this code-generation paradigm achieves higher accuracy (+7%) while consuming 78% fewer input tokens, completing tasks 56% faster, and requiring 35% fewer reasoning cycles compared to an equivalent tool-calling agent. The library makes it easy to configure the Python environment with the libraries and domain-specific code your agent needs.
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ pip install strands-code-agent
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ ```python
40
+ from strands_code_agent import CodeAgent
41
+
42
+ agent = CodeAgent(system_prompt="You are a helpful data analyst.")
43
+
44
+ response = agent("What is 2 ** 10?")
45
+ ```
46
+
47
+ The agent receives a `python_repl` tool automatically and solves tasks by writing and executing Python code.
48
+
49
+ ## CodeAgent
50
+
51
+ `CodeAgent` extends the Strands `Agent` with a built-in Python REPL and automatic system-prompt enrichment.
52
+
53
+ | Parameter | Type | Description |
54
+ |---|---|---|
55
+ | `system_prompt` | `str \| None` | Base system prompt, extended with coding instructions. |
56
+ | `tools` | `list \| None` | Additional tools alongside the built-in Python REPL. |
57
+ | `toolkits` | `list[Toolkit] \| None` | Toolkits that configure the REPL environment (see below). |
58
+ | `tmp_dir` | `bool` | If `True` (default), creates a temp directory and documents its path in the prompt. |
59
+ | `python_interpreter_class` | `type[PythonInterpreter]` | The interpreter backend. Defaults to `SandboxedPythonInterpreter` (import restrictions via allowlist). Use `ExecPythonInterpreter` for lightweight unrestricted `exec()`-based execution. |
60
+ | `**kwargs` | | Forwarded to the Strands `Agent` base class (e.g. `model`, `callback_handler`). |
61
+
62
+ ## Toolkit
63
+
64
+ A `Toolkit` bundles everything the REPL needs for a specific domain. Each field influences the `CodeAgent` in a specific way:
65
+
66
+ | Parameter | Type | Effect on `PythonInterpreter` | Effect on System Prompt |
67
+ |---|---|---|---|
68
+ | `libraries` | `list[str] \| None` | Added to `authorized_imports` — the REPL will only allow imports from this allowlist. | — |
69
+ | `initialization_code` | `str \| None` | Prepended to `state_initialization` — runs before every Agent snippet. | Documented so the agent knows which symbols are pre-loaded. |
70
+ | `usage_instructions` | `str \| None` | — | Appended as-is, giving the agent guidance on how to use the libraries. |
71
+ | `domain_specific_code` | `list \| None` | Auto-imported in `state_initialization` (modules added to `authorized_imports`). | Full signature + docstring of each symbol is documented so the agent can use them. |
72
+
73
+ ### Example
74
+
75
+ ```python
76
+ from strands_code_agent.toolkits import Toolkit
77
+
78
+ VISUALIZATION_TOOLKIT = Toolkit(
79
+ # 1. libraries → PythonInterpreter.authorized_imports
80
+ # Allows the REPL to import these modules.
81
+ # Use "module.*" to allow a module and all its submodules.
82
+ libraries=["matplotlib.*", "seaborn.*"],
83
+
84
+ # 2. initialization_code → PythonInterpreter.state_initialization + System Prompt
85
+ # Runs before user code; also shown in the prompt so the agent
86
+ # knows plt and sns are already available.
87
+ initialization_code="""
88
+ import matplotlib
89
+ matplotlib.use('Agg') # Use non-interactive backend
90
+ import matplotlib.pyplot as plt
91
+ import seaborn as sns
92
+ """,
93
+
94
+ # 3. usage_instructions → System Prompt only
95
+ # Tells the agent how to behave with these libraries.
96
+ usage_instructions="Do not try to show any matplotlib image: the python_repl tool executes the code in a sub-process without a GUI.",
97
+ )
98
+ ```
99
+
100
+ ### Built-in Toolkits
101
+
102
+ The library ships with ready-to-use toolkits:
103
+
104
+ ```python
105
+ from strands_code_agent.toolkits import (
106
+ VISUALIZATION_TOOLKIT, # matplotlib + seaborn (non-interactive backend)
107
+ DATA_ANALYSIS_TOOLKIT, # numpy + pandas + scipy + datetime
108
+ )
109
+ ```
110
+
111
+ ### Domain-Specific Code
112
+
113
+ Pass your own functions or classes via `domain_specific_code`. The `CodeAgent` will:
114
+
115
+ 1. **Auto-import** them in `PythonInterpreter.state_initialization` (their modules are added to `authorized_imports`).
116
+ 2. **Document** each symbol's full signature and docstring in the **System Prompt**, so the agent knows how to call them.
117
+
118
+ ```python
119
+ from strands_code_agent import CodeAgent, Toolkit
120
+
121
+ def calculate_roi(investment: float, returns: float) -> float:
122
+ """Calculate return on investment as a percentage."""
123
+ return (returns - investment) / investment * 100
124
+
125
+
126
+ agent = CodeAgent(
127
+ system_prompt="You are a finance assistant.",
128
+ toolkits=[
129
+ Toolkit(domain_specific_code=[calculate_roi])
130
+ ],
131
+ )
132
+
133
+ response = agent("What is the ROI if I invest 1000 and get back 1250?")
134
+ ```
135
+
136
+ ### Combining Toolkits
137
+
138
+ ```python
139
+ from strands_code_agent import CodeAgent
140
+ from strands_code_agent.toolkits import DATA_ANALYSIS_TOOLKIT, VISUALIZATION_TOOLKIT
141
+
142
+ agent = CodeAgent(
143
+ system_prompt="You are a data analyst.",
144
+ toolkits=[DATA_ANALYSIS_TOOLKIT, VISUALIZATION_TOOLKIT],
145
+ )
146
+ ```
147
+
148
+ ## Running Tests
149
+
150
+ The test suite uses [pytest](https://docs.pytest.org/). Install it and run from the project root:
151
+
152
+ ```bash
153
+ pip install pytest
154
+ python -m pytest tests/ -v
155
+ ```
156
+
157
+ ## Security
158
+
159
+ See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.
160
+
161
+ ## License
162
+
163
+ This library is licensed under the MIT-0 License. See the LICENSE file.
@@ -0,0 +1,137 @@
1
+ # strands-code-agent
2
+
3
+ A coding agent built on [Strands Agents SDK](https://github.com/strands-agents/sdk-python) that replaces the tool-calling paradigm with code generation as the agent's primary action interface. Rather than invoking structured tools by name and passing results through the conversation context, the agent writes Python code in a persistent REPL where domain capabilities (database queries, APIs, etc.) are exposed as importable library functions. This keeps intermediate data as native Python objects in memory and lets the agent compose multi-step logic in a single code block instead of orchestrating sequential tool calls. In empirical evaluations on the Data Agent Benchmark, this code-generation paradigm achieves higher accuracy (+7%) while consuming 78% fewer input tokens, completing tasks 56% faster, and requiring 35% fewer reasoning cycles compared to an equivalent tool-calling agent. The library makes it easy to configure the Python environment with the libraries and domain-specific code your agent needs.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install strands-code-agent
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from strands_code_agent import CodeAgent
15
+
16
+ agent = CodeAgent(system_prompt="You are a helpful data analyst.")
17
+
18
+ response = agent("What is 2 ** 10?")
19
+ ```
20
+
21
+ The agent receives a `python_repl` tool automatically and solves tasks by writing and executing Python code.
22
+
23
+ ## CodeAgent
24
+
25
+ `CodeAgent` extends the Strands `Agent` with a built-in Python REPL and automatic system-prompt enrichment.
26
+
27
+ | Parameter | Type | Description |
28
+ |---|---|---|
29
+ | `system_prompt` | `str \| None` | Base system prompt, extended with coding instructions. |
30
+ | `tools` | `list \| None` | Additional tools alongside the built-in Python REPL. |
31
+ | `toolkits` | `list[Toolkit] \| None` | Toolkits that configure the REPL environment (see below). |
32
+ | `tmp_dir` | `bool` | If `True` (default), creates a temp directory and documents its path in the prompt. |
33
+ | `python_interpreter_class` | `type[PythonInterpreter]` | The interpreter backend. Defaults to `SandboxedPythonInterpreter` (import restrictions via allowlist). Use `ExecPythonInterpreter` for lightweight unrestricted `exec()`-based execution. |
34
+ | `**kwargs` | | Forwarded to the Strands `Agent` base class (e.g. `model`, `callback_handler`). |
35
+
36
+ ## Toolkit
37
+
38
+ A `Toolkit` bundles everything the REPL needs for a specific domain. Each field influences the `CodeAgent` in a specific way:
39
+
40
+ | Parameter | Type | Effect on `PythonInterpreter` | Effect on System Prompt |
41
+ |---|---|---|---|
42
+ | `libraries` | `list[str] \| None` | Added to `authorized_imports` — the REPL will only allow imports from this allowlist. | — |
43
+ | `initialization_code` | `str \| None` | Prepended to `state_initialization` — runs before every Agent snippet. | Documented so the agent knows which symbols are pre-loaded. |
44
+ | `usage_instructions` | `str \| None` | — | Appended as-is, giving the agent guidance on how to use the libraries. |
45
+ | `domain_specific_code` | `list \| None` | Auto-imported in `state_initialization` (modules added to `authorized_imports`). | Full signature + docstring of each symbol is documented so the agent can use them. |
46
+
47
+ ### Example
48
+
49
+ ```python
50
+ from strands_code_agent.toolkits import Toolkit
51
+
52
+ VISUALIZATION_TOOLKIT = Toolkit(
53
+ # 1. libraries → PythonInterpreter.authorized_imports
54
+ # Allows the REPL to import these modules.
55
+ # Use "module.*" to allow a module and all its submodules.
56
+ libraries=["matplotlib.*", "seaborn.*"],
57
+
58
+ # 2. initialization_code → PythonInterpreter.state_initialization + System Prompt
59
+ # Runs before user code; also shown in the prompt so the agent
60
+ # knows plt and sns are already available.
61
+ initialization_code="""
62
+ import matplotlib
63
+ matplotlib.use('Agg') # Use non-interactive backend
64
+ import matplotlib.pyplot as plt
65
+ import seaborn as sns
66
+ """,
67
+
68
+ # 3. usage_instructions → System Prompt only
69
+ # Tells the agent how to behave with these libraries.
70
+ usage_instructions="Do not try to show any matplotlib image: the python_repl tool executes the code in a sub-process without a GUI.",
71
+ )
72
+ ```
73
+
74
+ ### Built-in Toolkits
75
+
76
+ The library ships with ready-to-use toolkits:
77
+
78
+ ```python
79
+ from strands_code_agent.toolkits import (
80
+ VISUALIZATION_TOOLKIT, # matplotlib + seaborn (non-interactive backend)
81
+ DATA_ANALYSIS_TOOLKIT, # numpy + pandas + scipy + datetime
82
+ )
83
+ ```
84
+
85
+ ### Domain-Specific Code
86
+
87
+ Pass your own functions or classes via `domain_specific_code`. The `CodeAgent` will:
88
+
89
+ 1. **Auto-import** them in `PythonInterpreter.state_initialization` (their modules are added to `authorized_imports`).
90
+ 2. **Document** each symbol's full signature and docstring in the **System Prompt**, so the agent knows how to call them.
91
+
92
+ ```python
93
+ from strands_code_agent import CodeAgent, Toolkit
94
+
95
+ def calculate_roi(investment: float, returns: float) -> float:
96
+ """Calculate return on investment as a percentage."""
97
+ return (returns - investment) / investment * 100
98
+
99
+
100
+ agent = CodeAgent(
101
+ system_prompt="You are a finance assistant.",
102
+ toolkits=[
103
+ Toolkit(domain_specific_code=[calculate_roi])
104
+ ],
105
+ )
106
+
107
+ response = agent("What is the ROI if I invest 1000 and get back 1250?")
108
+ ```
109
+
110
+ ### Combining Toolkits
111
+
112
+ ```python
113
+ from strands_code_agent import CodeAgent
114
+ from strands_code_agent.toolkits import DATA_ANALYSIS_TOOLKIT, VISUALIZATION_TOOLKIT
115
+
116
+ agent = CodeAgent(
117
+ system_prompt="You are a data analyst.",
118
+ toolkits=[DATA_ANALYSIS_TOOLKIT, VISUALIZATION_TOOLKIT],
119
+ )
120
+ ```
121
+
122
+ ## Running Tests
123
+
124
+ The test suite uses [pytest](https://docs.pytest.org/). Install it and run from the project root:
125
+
126
+ ```bash
127
+ pip install pytest
128
+ python -m pytest tests/ -v
129
+ ```
130
+
131
+ ## Security
132
+
133
+ See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.
134
+
135
+ ## License
136
+
137
+ This library is licensed under the MIT-0 License. See the LICENSE file.
@@ -0,0 +1,36 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "strands-code-agent"
7
+ version = "0.1.0"
8
+ description = "A coding agent built on Strands Agents SDK that uses code generation as the primary action interface"
9
+ readme = "README.md"
10
+ license = "MIT-0"
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ { name = "Emilio Monti" },
14
+ ]
15
+ keywords = ["agents", "code-generation", "llm", "strands", "repl"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
26
+ ]
27
+ dependencies = [
28
+ "strands-agents>=0.1.0",
29
+ "smolagents>=1.0.0",
30
+ "jinja2>=3.0",
31
+ ]
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/aws-samples/sample-strands-code-agent"
35
+ Repository = "https://github.com/aws-samples/sample-strands-code-agent"
36
+ Issues = "https://github.com/aws-samples/sample-strands-code-agent/issues"
@@ -0,0 +1,4 @@
1
+ from strands_code_agent.code_agent import CodeAgent
2
+ from strands_code_agent.toolkits import Toolkit
3
+
4
+ __all__ = ["CodeAgent", "Toolkit"]
@@ -0,0 +1,142 @@
1
+ import tempfile
2
+
3
+ from strands import Agent
4
+ from jinja2 import Template
5
+
6
+ from strands_code_agent.document_code import get_documentation
7
+ from strands_code_agent.python_environments.local_sandboxed import SandboxedPythonInterpreter
8
+ from strands_code_agent.imports import get_import_string, extract_imports
9
+
10
+
11
+ CODE_AGENT_INSTRUCTIONS = """
12
+ You are a code agent. You solve tasks by writing and executing Python code using the python_repl tool.
13
+
14
+ The Python interpreter state resets completely with each new user message, but it persists across multiple tool invocations within a single response.
15
+ You can perform multi-step computation within a single turn, but do not assume that results from a previous turn are still in memory.
16
+ """
17
+
18
+ CODE_PREAMBLE_TEMPLATE = Template("""
19
+ The python environment of the python_repl tool is initialised with the following code (you do not need to rewrite this code):
20
+ ```python
21
+ {{CODE_PREAMBLE}}
22
+ ```
23
+ """)
24
+
25
+ TEMP_DIR_TEMPLATE = Template("""
26
+ If you need to generate a file use this temporary directory: {{TEMP_DIR}}
27
+ The user has no access to this temporary directory.
28
+ """)
29
+
30
+ DOMAIN_SPECIFIC_DOC_TEMPLATE = Template("""
31
+ You can use the following Domain Specific Code:
32
+ {{SYMBOLS_DOCUMENTATION}}
33
+ """)
34
+
35
+
36
+ class CodeAgent(Agent):
37
+ """A coding agent that extends Strands Agent with a sandboxed Python REPL and domain-specific symbol documentation.
38
+
39
+ CodeAgent wraps a :class:`PythonInterpreter` as a built-in ``python_repl`` tool and
40
+ assembles a system prompt from the provided toolkits. Each :class:`Toolkit` can contribute:
41
+
42
+ - **libraries** – module names authorized for import in the sandboxed interpreter.
43
+ - **initialization_code** – Python code executed at interpreter startup (e.g. imports, config).
44
+ Any modules imported in this code are automatically authorized.
45
+ - **usage_instructions** – free-text guidance appended to the system prompt.
46
+ - **domain_specific_code** – callable symbols whose source and docstrings are documented in
47
+ the system prompt and made available in the interpreter.
48
+
49
+ The interpreter state persists across tool invocations within a single agent turn but
50
+ resets completely between user messages.
51
+
52
+ Args:
53
+ system_prompt: Optional base system prompt prepended before the coding instructions.
54
+
55
+ tools: Additional tools to include alongside the built-in Python REPL.
56
+
57
+ toolkits: :class:`Toolkit` instances that supply libraries, initialization code,
58
+ usage instructions, and domain-specific symbols to the REPL environment.
59
+
60
+ tmp_dir: If ``True`` (default), creates a temporary directory under ``/tmp`` and
61
+ documents its path in the system prompt so the agent can write files there.
62
+
63
+ timeout_seconds: Maximum execution time in seconds for each ``python_repl``
64
+ invocation. Defaults to ``60``.
65
+
66
+ python_interpreter_class: The :class:`PythonInterpreter` subclass to use for
67
+ code execution. Defaults to :class:`ExecPythonInterpreter` (lightweight,
68
+ unrestricted ``exec()``-based). Use :class:`SandboxedPythonInterpreter`
69
+ for import restrictions and sandboxed execution.
70
+
71
+ **kwargs: Additional arguments forwarded to the Strands :class:`Agent` base class
72
+ (e.g. ``model``, ``callback_handler``).
73
+ """
74
+ def __init__(self,
75
+ system_prompt:str|None=None,
76
+ tools:list|None=None,
77
+ toolkits:list|None=None,
78
+ tmp_dir=True,
79
+ timeout_seconds=60,
80
+ python_interpreter_class=SandboxedPythonInterpreter,
81
+ **kwargs):
82
+ authorized_imports = set()
83
+ initialization_code = []
84
+ usage_instructions = []
85
+ domain_specific_code = []
86
+ if toolkits is not None:
87
+ for toolkit in toolkits:
88
+ if toolkit.libraries is not None:
89
+ authorized_imports.update(toolkit.libraries)
90
+ if toolkit.initialization_code is not None:
91
+ initialization_code.append(toolkit.initialization_code.strip())
92
+ if toolkit.usage_instructions is not None:
93
+ usage_instructions.append(toolkit.usage_instructions.strip())
94
+ if toolkit.domain_specific_code is not None:
95
+ domain_specific_code.extend(toolkit.domain_specific_code)
96
+
97
+ additional_functions = {}
98
+ domain_specific_doc = ""
99
+ if domain_specific_code:
100
+ authorized_imports.update(sym.__module__ for sym in domain_specific_code if sym.__module__ != "__main__")
101
+ initialization_code.append(get_import_string(domain_specific_code))
102
+ sym_doc = "\n".join([get_documentation(sym) for sym in domain_specific_code])
103
+ domain_specific_doc = DOMAIN_SPECIFIC_DOC_TEMPLATE.render(SYMBOLS_DOCUMENTATION=sym_doc)
104
+ additional_functions = {sym.__qualname__.split(".")[0]: sym for sym in domain_specific_code}
105
+
106
+ code_preamble = "\n".join(initialization_code)
107
+ # Auto-authorize any modules imported in initialization code so users
108
+ # don't have to duplicate them in both `libraries` and `initialization_code`.
109
+ authorized_imports.update(extract_imports(code_preamble))
110
+ code_preamble_doc = CODE_PREAMBLE_TEMPLATE.render(CODE_PREAMBLE=code_preamble) if code_preamble else ""
111
+
112
+ tmp_dir_doc = ""
113
+ if tmp_dir:
114
+ self.tmp_dir = tempfile.mkdtemp(dir='/tmp')
115
+ tmp_dir_doc = TEMP_DIR_TEMPLATE.render(TEMP_DIR=self.tmp_dir)
116
+
117
+ system_prompt = '\n'.join([
118
+ system_prompt if system_prompt is not None else "",
119
+ CODE_AGENT_INSTRUCTIONS,
120
+ code_preamble_doc,
121
+ "\n".join(usage_instructions),
122
+ tmp_dir_doc,
123
+ domain_specific_doc
124
+ ])
125
+
126
+ self.python_repl = python_interpreter_class(
127
+ code_preamble,
128
+ authorized_imports=authorized_imports,
129
+ additional_functions=additional_functions,
130
+ timeout_seconds=timeout_seconds,
131
+ )
132
+ python_repl_tool = self.python_repl.get_tool()
133
+ if tools is not None:
134
+ tools.append(python_repl_tool)
135
+ else:
136
+ tools = [python_repl_tool]
137
+
138
+ kwargs.update({
139
+ "system_prompt": system_prompt,
140
+ "tools": tools
141
+ })
142
+ super().__init__(**kwargs)
@@ -0,0 +1,63 @@
1
+ import inspect
2
+ from typing import Callable, Type, Union
3
+
4
+
5
+ def format_function(func, indent: str = "") -> str:
6
+ try:
7
+ sig = inspect.signature(func)
8
+ result = f"{indent}def {func.__name__}{sig}:\n"
9
+ except (ValueError, TypeError):
10
+ result = f"{indent}def {func.__name__}(...):\n"
11
+
12
+ doc = inspect.getdoc(func)
13
+ if doc:
14
+ doc_lines = doc.split('\n')
15
+ result += f'{indent} """\n'
16
+ for line in doc_lines:
17
+ result += f"{indent} {line}\n"
18
+ result += f'{indent} """\n'
19
+
20
+ result += f"{indent} ...\n"
21
+ return result
22
+
23
+
24
+ def get_documentation(obj: Union[Callable, Type]) -> str:
25
+ """
26
+ Extract documentation from a Python function or class.
27
+ Returns formatted text suitable for a coding agent.
28
+ """
29
+ if inspect.isclass(obj):
30
+ # Class header with constructor signature
31
+ try:
32
+ sig = inspect.signature(obj)
33
+ output = f"class {obj.__name__}{sig}:\n"
34
+ except (ValueError, TypeError):
35
+ output = f"class {obj.__name__}:\n"
36
+
37
+ # Class docstring
38
+ class_doc = inspect.getdoc(obj)
39
+ if class_doc:
40
+ output += ' """\n'
41
+ for line in class_doc.split('\n'):
42
+ output += f" {line}\n"
43
+ output += ' """\n\n'
44
+
45
+ # Methods (public + key dunder methods)
46
+ important_dunders = {
47
+ '__init__', '__call__', '__enter__', '__exit__',
48
+ '__iter__', '__next__', '__getitem__', '__setitem__',
49
+ '__len__', '__contains__', '__repr__', '__str__'
50
+ }
51
+
52
+ for name, method in inspect.getmembers(obj, predicate=inspect.isfunction):
53
+ if name.startswith('_') and name not in important_dunders:
54
+ continue
55
+ output += format_function(method, indent=" ") + "\n"
56
+
57
+ return output.rstrip() + "\n"
58
+
59
+ elif callable(obj):
60
+ return format_function(obj)
61
+
62
+ else:
63
+ raise TypeError(f"Expected a function or class, got {type(obj)}")