strands-code-agent 0.1.0__tar.gz → 0.2.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.
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/PKG-INFO +11 -3
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/README.md +9 -2
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/pyproject.toml +2 -1
- strands_code_agent-0.2.0/strands_code_agent/callback_handler.py +80 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/strands_code_agent/code_agent.py +8 -3
- strands_code_agent-0.2.0/strands_code_agent/utils.py +30 -0
- strands_code_agent-0.1.0/strands_code_agent/utils.py +0 -7
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/.github/workflows/publish.yml +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/.gitignore +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/CODE_OF_CONDUCT.md +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/CONTRIBUTING.md +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/LICENSE +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/strands_code_agent/__init__.py +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/strands_code_agent/document_code.py +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/strands_code_agent/imports.py +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/strands_code_agent/python_environments/__init__.py +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/strands_code_agent/python_environments/base.py +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/strands_code_agent/python_environments/local_exec.py +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/strands_code_agent/python_environments/local_sandboxed.py +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/strands_code_agent/toolkits.py +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/tests/__init__.py +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/tests/test_code_agent.py +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/tests/test_document_code.py +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/tests/test_exec_python_interpreter.py +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/tests/test_readme_examples.py +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/tests/test_sandboxed_python_interpreter.py +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/tests/test_toolkits.py +0 -0
- {strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: strands-code-agent
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A coding agent built on Strands Agents SDK that uses code generation as the primary action interface
|
|
5
5
|
Project-URL: Homepage, https://github.com/aws-samples/sample-strands-code-agent
|
|
6
6
|
Project-URL: Repository, https://github.com/aws-samples/sample-strands-code-agent
|
|
@@ -20,13 +20,14 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
20
20
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
21
|
Requires-Python: >=3.10
|
|
22
22
|
Requires-Dist: jinja2>=3.0
|
|
23
|
+
Requires-Dist: rich>=13.0
|
|
23
24
|
Requires-Dist: smolagents>=1.0.0
|
|
24
25
|
Requires-Dist: strands-agents>=0.1.0
|
|
25
26
|
Description-Content-Type: text/markdown
|
|
26
27
|
|
|
27
28
|
# strands-code-agent
|
|
28
29
|
|
|
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
|
+
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, 67% fewer output 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
|
|
|
31
32
|
## Installation
|
|
32
33
|
|
|
@@ -39,7 +40,7 @@ pip install strands-code-agent
|
|
|
39
40
|
```python
|
|
40
41
|
from strands_code_agent import CodeAgent
|
|
41
42
|
|
|
42
|
-
agent = CodeAgent(
|
|
43
|
+
agent = CodeAgent()
|
|
43
44
|
|
|
44
45
|
response = agent("What is 2 ** 10?")
|
|
45
46
|
```
|
|
@@ -161,3 +162,10 @@ See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more inform
|
|
|
161
162
|
## License
|
|
162
163
|
|
|
163
164
|
This library is licensed under the MIT-0 License. See the LICENSE file.
|
|
165
|
+
|
|
166
|
+
## Example Agents
|
|
167
|
+
|
|
168
|
+
Example agents built with this library:
|
|
169
|
+
|
|
170
|
+
- [Data Analyst Agent](https://github.com/aws-solutions-library-samples/guidance-for-agentic-data-analyst-using-amazon-bedrock-agentcore-on-aws/blob/main/agent/aws_data_analyst/data_analyst_agent.py#L92)
|
|
171
|
+
- [Geospatial Agent](https://github.com/aws-samples/sample-geospatial-code-agent/blob/main/agent/geospatial_agent/agent.py#L93)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# strands-code-agent
|
|
2
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.
|
|
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, 67% fewer output 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
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@ pip install strands-code-agent
|
|
|
13
13
|
```python
|
|
14
14
|
from strands_code_agent import CodeAgent
|
|
15
15
|
|
|
16
|
-
agent = CodeAgent(
|
|
16
|
+
agent = CodeAgent()
|
|
17
17
|
|
|
18
18
|
response = agent("What is 2 ** 10?")
|
|
19
19
|
```
|
|
@@ -135,3 +135,10 @@ See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more inform
|
|
|
135
135
|
## License
|
|
136
136
|
|
|
137
137
|
This library is licensed under the MIT-0 License. See the LICENSE file.
|
|
138
|
+
|
|
139
|
+
## Example Agents
|
|
140
|
+
|
|
141
|
+
Example agents built with this library:
|
|
142
|
+
|
|
143
|
+
- [Data Analyst Agent](https://github.com/aws-solutions-library-samples/guidance-for-agentic-data-analyst-using-amazon-bedrock-agentcore-on-aws/blob/main/agent/aws_data_analyst/data_analyst_agent.py#L92)
|
|
144
|
+
- [Geospatial Agent](https://github.com/aws-samples/sample-geospatial-code-agent/blob/main/agent/geospatial_agent/agent.py#L93)
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "strands-code-agent"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "A coding agent built on Strands Agents SDK that uses code generation as the primary action interface"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT-0"
|
|
@@ -28,6 +28,7 @@ dependencies = [
|
|
|
28
28
|
"strands-agents>=0.1.0",
|
|
29
29
|
"smolagents>=1.0.0",
|
|
30
30
|
"jinja2>=3.0",
|
|
31
|
+
"rich>=13.0",
|
|
31
32
|
]
|
|
32
33
|
|
|
33
34
|
[project.urls]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
import ast
|
|
3
|
+
|
|
4
|
+
from rich.syntax import Syntax
|
|
5
|
+
from rich.json import JSON
|
|
6
|
+
from rich.markdown import Markdown
|
|
7
|
+
from rich.pretty import Pretty
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def format_message(text):
|
|
12
|
+
# Python data-structure
|
|
13
|
+
try:
|
|
14
|
+
return Pretty(ast.literal_eval(text))
|
|
15
|
+
except (ValueError, SyntaxError):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
# JSON?
|
|
19
|
+
try:
|
|
20
|
+
return JSON(text, indent=4)
|
|
21
|
+
except ValueError:
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
# Markdown? (rough heuristic)
|
|
25
|
+
import re
|
|
26
|
+
if re.search(r"(^#{1,6} |\*\*|__|\[.+\]\(.+\)|```)", text, re.MULTILINE):
|
|
27
|
+
return Markdown(text)
|
|
28
|
+
|
|
29
|
+
return text
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CodeAgentCallbackHandler:
|
|
33
|
+
def __init__(self, code_tools=None, output_prefix="STDOUT:", format_text=True, **kwargs) -> None:
|
|
34
|
+
self.console = Console()
|
|
35
|
+
if code_tools is None:
|
|
36
|
+
code_tools = {
|
|
37
|
+
'python_repl': 'python'
|
|
38
|
+
}
|
|
39
|
+
self.code_tools = code_tools
|
|
40
|
+
self.output_prefix = output_prefix
|
|
41
|
+
if format_text:
|
|
42
|
+
self.format_text = format_message
|
|
43
|
+
else:
|
|
44
|
+
self.format_text = lambda x: x
|
|
45
|
+
|
|
46
|
+
def __call__(self, **kwargs: Any) -> None:
|
|
47
|
+
if 'message' not in kwargs:
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
message = kwargs['message']
|
|
51
|
+
role = message['role']
|
|
52
|
+
for content_item in message['content']:
|
|
53
|
+
if 'text' in content_item:
|
|
54
|
+
self.console.print(f"\n[{role.title()}]", end=" ")
|
|
55
|
+
self.console.print(self.format_text(content_item['text'].strip()), end="\n\n")
|
|
56
|
+
|
|
57
|
+
if 'toolUse' in content_item:
|
|
58
|
+
tool_use = content_item['toolUse']
|
|
59
|
+
name = tool_use['name']
|
|
60
|
+
self.console.print(f"\n[Tool] {name}")
|
|
61
|
+
if name in self.code_tools:
|
|
62
|
+
language = self.code_tools[name]
|
|
63
|
+
syntax = Syntax(tool_use['input']['code'], language)
|
|
64
|
+
self.console.print(syntax)
|
|
65
|
+
else:
|
|
66
|
+
for var, value in tool_use['input'].items():
|
|
67
|
+
self.console.print(f"\t- {var}: {value}")
|
|
68
|
+
|
|
69
|
+
if 'toolResult' in content_item:
|
|
70
|
+
tool_result = content_item['toolResult']
|
|
71
|
+
self.console.print(f"\n[Tool Result] Status: {tool_result['status']}")
|
|
72
|
+
for tool_item in tool_result['content']:
|
|
73
|
+
if 'text' in tool_item:
|
|
74
|
+
text = tool_item['text'].strip()
|
|
75
|
+
if text.startswith(self.output_prefix):
|
|
76
|
+
body = text.removeprefix(self.output_prefix).strip()
|
|
77
|
+
self.console.print(self.output_prefix)
|
|
78
|
+
self.console.print(self.format_text(body))
|
|
79
|
+
else:
|
|
80
|
+
self.console.print(text)
|
|
@@ -6,6 +6,7 @@ from jinja2 import Template
|
|
|
6
6
|
from strands_code_agent.document_code import get_documentation
|
|
7
7
|
from strands_code_agent.python_environments.local_sandboxed import SandboxedPythonInterpreter
|
|
8
8
|
from strands_code_agent.imports import get_import_string, extract_imports
|
|
9
|
+
from strands_code_agent.callback_handler import CodeAgentCallbackHandler
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
CODE_AGENT_INSTRUCTIONS = """
|
|
@@ -32,6 +33,8 @@ You can use the following Domain Specific Code:
|
|
|
32
33
|
{{SYMBOLS_DOCUMENTATION}}
|
|
33
34
|
""")
|
|
34
35
|
|
|
36
|
+
DEFAULT_CODE_AGENT_CALLBACK_HANDLER = CodeAgentCallbackHandler()
|
|
37
|
+
|
|
35
38
|
|
|
36
39
|
class CodeAgent(Agent):
|
|
37
40
|
"""A coding agent that extends Strands Agent with a sandboxed Python REPL and domain-specific symbol documentation.
|
|
@@ -76,8 +79,9 @@ class CodeAgent(Agent):
|
|
|
76
79
|
tools:list|None=None,
|
|
77
80
|
toolkits:list|None=None,
|
|
78
81
|
tmp_dir=True,
|
|
79
|
-
timeout_seconds=
|
|
82
|
+
timeout_seconds=180,
|
|
80
83
|
python_interpreter_class=SandboxedPythonInterpreter,
|
|
84
|
+
callback_handler=DEFAULT_CODE_AGENT_CALLBACK_HANDLER,
|
|
81
85
|
**kwargs):
|
|
82
86
|
authorized_imports = set()
|
|
83
87
|
initialization_code = []
|
|
@@ -134,9 +138,10 @@ class CodeAgent(Agent):
|
|
|
134
138
|
tools.append(python_repl_tool)
|
|
135
139
|
else:
|
|
136
140
|
tools = [python_repl_tool]
|
|
137
|
-
|
|
141
|
+
|
|
138
142
|
kwargs.update({
|
|
139
143
|
"system_prompt": system_prompt,
|
|
140
|
-
"tools": tools
|
|
144
|
+
"tools": tools,
|
|
145
|
+
"callback_handler": callback_handler
|
|
141
146
|
})
|
|
142
147
|
super().__init__(**kwargs)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def image_to_base64(image_path):
|
|
5
|
+
"""Read an image file and return its contents as a base64-encoded string."""
|
|
6
|
+
with open(image_path, 'rb') as f:
|
|
7
|
+
return base64.b64encode(f.read()).decode('utf-8')
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_response_metrics(
|
|
11
|
+
response,
|
|
12
|
+
price_1M_input_tokens=None,
|
|
13
|
+
price_1M_output_tokens=None):
|
|
14
|
+
"""
|
|
15
|
+
For the latest pricing see: https://aws.amazon.com/bedrock/pricing
|
|
16
|
+
"""
|
|
17
|
+
summary = response.metrics.get_summary()
|
|
18
|
+
inputTokens = summary['accumulated_usage']['inputTokens']
|
|
19
|
+
outputTokens = summary['accumulated_usage']['outputTokens']
|
|
20
|
+
metrics = {
|
|
21
|
+
'total_cycles': summary['total_cycles'],
|
|
22
|
+
'total_duration': summary['total_duration'],
|
|
23
|
+
'input_tokens': inputTokens,
|
|
24
|
+
'output_tokens': outputTokens,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if price_1M_input_tokens and price_1M_output_tokens:
|
|
28
|
+
metrics['cost'] = (inputTokens * price_1M_input_tokens / 1_000_000) + (outputTokens * price_1M_output_tokens / 1_000_000)
|
|
29
|
+
|
|
30
|
+
return metrics
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/strands_code_agent/python_environments/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{strands_code_agent-0.1.0 → strands_code_agent-0.2.0}/tests/test_sandboxed_python_interpreter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|