Skip to content

Commit 6605c41

Browse files
dyikeyuanfeng.dev
andauthored
feat(tools): support shell execution in run_code (#709)
Co-authored-by: yuanfeng.dev <yuanfeng.dev@bytedance.com>
1 parent 1a80aef commit 6605c41

3 files changed

Lines changed: 136 additions & 12 deletions

File tree

tests/tools/builtin_tools/test_agentkit.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414

1515
import importlib.util
16+
import json
1617
import os
1718
import sys
1819
import types
@@ -160,5 +161,47 @@ def test_resolve_raises_when_all_tool_ids_missing(self):
160161
self.agentkit_module.resolve_agentkit_tool_id("AGENTKIT_TOOL_ID_SCRIPT")
161162

162163

164+
class TestInvokeAgentkitExecBash(unittest.TestCase):
165+
@classmethod
166+
def setUpClass(cls):
167+
cls.agentkit_module = _load_agentkit_module()
168+
169+
def test_builds_exec_bash_invoke_tool_request(self):
170+
with patch.object(
171+
self.agentkit_module,
172+
"ve_request",
173+
return_value={"Result": {"Result": "shell output"}},
174+
) as ve_request:
175+
result = self.agentkit_module.invoke_agentkit_exec_bash(
176+
tool_id="shell-tool",
177+
tool_user_session_id="kk",
178+
command="echo hello",
179+
exec_dir="/tmp",
180+
env={"DEMO_ENV": "from-invoke-tool"},
181+
timeout=30,
182+
hard_timeout=60,
183+
max_output_length=30000,
184+
ttl=1800,
185+
)
186+
187+
self.assertEqual(result, {"Result": {"Result": "shell output"}})
188+
request_body = ve_request.call_args.kwargs["request_body"]
189+
self.assertEqual(request_body["ToolId"], "shell-tool")
190+
self.assertEqual(request_body["OperationType"], "ExecBash")
191+
self.assertEqual(request_body["UserSessionId"], "kk")
192+
self.assertEqual(request_body["Ttl"], 1800)
193+
self.assertEqual(
194+
json.loads(request_body["OperationPayload"]),
195+
{
196+
"command": "echo hello",
197+
"exec_dir": "/tmp",
198+
"env": {"DEMO_ENV": "from-invoke-tool"},
199+
"timeout": 30,
200+
"hard_timeout": 60,
201+
"max_output_length": 30000,
202+
},
203+
)
204+
205+
163206
if __name__ == "__main__":
164207
unittest.main()

veadk/tools/builtin_tools/_agentkit.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,56 @@ def invoke_agentkit_run_code(
153153
header=header,
154154
scheme=scheme,
155155
)
156+
157+
158+
def invoke_agentkit_exec_bash(
159+
*,
160+
tool_id: str,
161+
tool_user_session_id: str,
162+
command: str,
163+
exec_dir: Optional[str] = None,
164+
env: Optional[dict[str, str]] = None,
165+
timeout: int = 30,
166+
hard_timeout: Optional[int] = None,
167+
max_output_length: Optional[int] = None,
168+
tool_state: Optional[dict[str, Any]] = None,
169+
ttl: Optional[int] = None,
170+
) -> dict[str, Any]:
171+
"""Invoke AgentKit's Bash execution operation through InvokeTool."""
172+
service, region, host, scheme = get_agentkit_endpoint_config()
173+
ak, sk, header = get_agentkit_credentials(tool_state)
174+
175+
operation_payload: dict[str, Any] = {
176+
"command": command,
177+
"timeout": timeout,
178+
}
179+
if exec_dir is not None:
180+
operation_payload["exec_dir"] = exec_dir
181+
if env is not None:
182+
operation_payload["env"] = env
183+
if hard_timeout is not None:
184+
operation_payload["hard_timeout"] = hard_timeout
185+
if max_output_length is not None:
186+
operation_payload["max_output_length"] = max_output_length
187+
188+
request_body: dict[str, Any] = {
189+
"ToolId": tool_id,
190+
"OperationType": "ExecBash",
191+
"UserSessionId": tool_user_session_id,
192+
"OperationPayload": json.dumps(operation_payload),
193+
}
194+
if ttl is not None:
195+
request_body["Ttl"] = ttl
196+
197+
return ve_request(
198+
request_body=request_body,
199+
action="InvokeTool",
200+
ak=ak,
201+
sk=sk,
202+
service=service,
203+
version="2025-10-30",
204+
region=region,
205+
host=host,
206+
header=header,
207+
scheme=scheme,
208+
)

veadk/tools/builtin_tools/run_code.py

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from veadk.tools.builtin_tools._agentkit import (
2020
get_agentkit_endpoint_config,
21+
invoke_agentkit_exec_bash,
2122
invoke_agentkit_run_code,
2223
resolve_agentkit_tool_id,
2324
)
@@ -27,15 +28,26 @@
2728

2829

2930
def run_code(
30-
code: str, language: str, tool_context: ToolContext, timeout: int = 30
31+
code: str,
32+
language: str,
33+
tool_context: ToolContext,
34+
timeout: int = 30,
35+
exec_dir: str = "/tmp",
36+
env: dict[str, str] | None = None,
37+
hard_timeout: int = 300,
38+
max_output_length: int = 30000,
3139
) -> str:
3240
"""Run code in a code sandbox and return the output.
3341
For C++ code, don't execute it directly, compile and execute via Python; write sources and object files to /tmp.
3442
3543
Args:
3644
code (str): The code to run.
37-
language (str): The programming language of the code. Language must be one of the supported languages: python3.
45+
language (str): The execution language. Use ``python3`` for code or ``bash`` for shell scripts.
3846
timeout (int, optional): The timeout in seconds for the code execution. Defaults to 30.
47+
exec_dir (str, optional): Working directory for Bash execution. Defaults to ``/tmp``.
48+
env (dict[str, str], optional): Environment variables for Bash execution.
49+
hard_timeout (int, optional): Hard timeout for Bash execution. Defaults to 300 seconds.
50+
max_output_length (int, optional): Maximum Bash output length. Defaults to 30000.
3951
4052
Returns:
4153
str: The output of the code execution.
@@ -55,19 +67,35 @@ def run_code(
5567
f"Running code in language: {language}, session_id={session_id}, code={code}, tool_id={tool_id}, host={host}, service={service}, region={region}, timeout={timeout}"
5668
)
5769

58-
res = invoke_agentkit_run_code(
59-
tool_id=tool_id,
60-
tool_user_session_id=tool_user_session_id,
61-
code=code,
62-
timeout=timeout,
63-
kernel_name=language,
64-
tool_state=tool_context.state if tool_context else None,
65-
ttl=int(os.getenv("AGENTKIT_TOOL_TTL", "1800")),
66-
)
70+
tool_state = tool_context.state if tool_context else None
71+
ttl = int(os.getenv("AGENTKIT_TOOL_TTL", "1800"))
72+
if language.lower() in {"bash", "shell"}:
73+
res = invoke_agentkit_exec_bash(
74+
tool_id=tool_id,
75+
tool_user_session_id=tool_user_session_id,
76+
command=code,
77+
exec_dir=exec_dir,
78+
env=env,
79+
timeout=timeout,
80+
hard_timeout=hard_timeout,
81+
max_output_length=max_output_length,
82+
tool_state=tool_state,
83+
ttl=ttl,
84+
)
85+
else:
86+
res = invoke_agentkit_run_code(
87+
tool_id=tool_id,
88+
tool_user_session_id=tool_user_session_id,
89+
code=code,
90+
timeout=timeout,
91+
kernel_name=language,
92+
tool_state=tool_state,
93+
ttl=ttl,
94+
)
6795
logger.debug(f"Invoke run code response: {res}")
6896

6997
try:
7098
return res["Result"]["Result"]
71-
except KeyError as e:
99+
except (KeyError, TypeError) as e:
72100
logger.error(f"Error occurred while running code: {e}, response is {res}")
73101
return res

0 commit comments

Comments
 (0)