feat: pass in LETTA_TOOL_ID, LETTA_AGENT_ID, LETTA_PROJECT_ID into tool env (#6439)

This commit is contained in:
Sarah Wooders
2025-11-26 17:26:47 -08:00
committed by Caren Thomas
parent 0a0cf391fc
commit c4d5c380d6
11 changed files with 198 additions and 75 deletions

View File

@@ -56,7 +56,7 @@ class SandboxToolExecutor(ToolExecutor):
# inject some extra env such as PROJECT_ID from agent_state
if agent_state and agent_state.project_id:
fetched_credentials["PROJECT_ID"] = agent_state.project_id
fetched_credentials["PROJECT_ID"] = agent_state.project_id
sandbox_env_vars = {**fetched_credentials, **sandbox_env_vars}
@@ -83,15 +83,16 @@ class SandboxToolExecutor(ToolExecutor):
function_name,
function_args,
actor,
tool_id=tool.id,
agent_id=agent_state.id if agent_state else None,
project_id=agent_state.project_id if agent_state else None,
tool_object=tool,
sandbox_config=sandbox_config,
sandbox_env_vars=sandbox_env_vars,
organization_id=actor.organization_id,
)
# TODO: pass through letta api key
tool_execution_result = await sandbox.run(
agent_id=agent_state.id, agent_state=agent_state_copy, additional_env_vars=sandbox_env_vars
)
tool_execution_result = await sandbox.run(agent_state=agent_state_copy, additional_env_vars=sandbox_env_vars)
except Exception as e:
# Modal execution failed, log and fall back to E2B/LOCAL
logger.warning(f"Modal execution failed for tool {tool.name}: {e}. Falling back to {tool_settings.sandbox_type.value}")
@@ -106,6 +107,9 @@ class SandboxToolExecutor(ToolExecutor):
function_name,
function_args,
actor,
tool_id=tool.id,
agent_id=agent_state.id if agent_state else None,
project_id=agent_state.project_id if agent_state else None,
tool_object=tool,
sandbox_config=sandbox_config,
sandbox_env_vars=sandbox_env_vars,
@@ -115,6 +119,9 @@ class SandboxToolExecutor(ToolExecutor):
function_name,
function_args,
actor,
tool_id=tool.id,
agent_id=agent_state.id if agent_state else None,
project_id=agent_state.project_id if agent_state else None,
tool_object=tool,
sandbox_config=sandbox_config,
sandbox_env_vars=sandbox_env_vars,

View File

@@ -27,6 +27,9 @@ class AsyncToolSandboxBase(ABC):
tool_name: str,
args: JsonDict,
user,
tool_id: str,
agent_id: Optional[str] = None,
project_id: Optional[str] = None,
tool_object: Optional[Tool] = None,
sandbox_config: Optional[SandboxConfig] = None,
sandbox_env_vars: Optional[Dict[str, Any]] = None,
@@ -34,6 +37,9 @@ class AsyncToolSandboxBase(ABC):
self.tool_name = tool_name
self.args = args
self.user = user
self.agent_id = agent_id
self.project_id = project_id
self.tool_id = tool_id
self.tool = tool_object
# Store provided values or create manager to fetch them later
@@ -394,6 +400,13 @@ class AsyncToolSandboxBase(ABC):
if additional_env_vars:
env.update(additional_env_vars)
# Inject agent, project, and tool IDs as environment variables
if self.agent_id:
env["LETTA_AGENT_ID"] = self.agent_id
if self.project_id:
env["LETTA_PROJECT_ID"] = self.project_id
env["LETTA_TOOL_ID"] = self.tool_id
# Filter out None values to prevent subprocess errors
env = {k: v for k, v in env.items() if v is not None}

View File

@@ -31,12 +31,25 @@ class AsyncToolSandboxE2B(AsyncToolSandboxBase):
tool_name: str,
args: JsonDict,
user,
tool_id: str,
agent_id: Optional[str] = None,
project_id: Optional[str] = None,
force_recreate: bool = True,
tool_object: Optional[Tool] = None,
sandbox_config: Optional[SandboxConfig] = None,
sandbox_env_vars: Optional[Dict[str, Any]] = None,
):
super().__init__(tool_name, args, user, tool_object, sandbox_config=sandbox_config, sandbox_env_vars=sandbox_env_vars)
super().__init__(
tool_name,
args,
user,
tool_id=tool_id,
agent_id=agent_id,
project_id=project_id,
tool_object=tool_object,
sandbox_config=sandbox_config,
sandbox_env_vars=sandbox_env_vars,
)
self.force_recreate = force_recreate
@trace_method

View File

@@ -37,12 +37,25 @@ class AsyncToolSandboxLocal(AsyncToolSandboxBase):
tool_name: str,
args: JsonDict,
user,
tool_id: str,
agent_id: Optional[str] = None,
project_id: Optional[str] = None,
force_recreate_venv=False,
tool_object: Optional[Tool] = None,
sandbox_config: Optional[SandboxConfig] = None,
sandbox_env_vars: Optional[Dict[str, Any]] = None,
):
super().__init__(tool_name, args, user, tool_object, sandbox_config=sandbox_config, sandbox_env_vars=sandbox_env_vars)
super().__init__(
tool_name,
args,
user,
tool_id=tool_id,
agent_id=agent_id,
project_id=project_id,
tool_object=tool_object,
sandbox_config=sandbox_config,
sandbox_env_vars=sandbox_env_vars,
)
self.force_recreate_venv = force_recreate_venv
@trace_method

View File

@@ -36,18 +36,29 @@ class AsyncToolSandboxModal(AsyncToolSandboxBase):
tool_name: str,
args: JsonDict,
user,
tool_id: str,
agent_id: Optional[str] = None,
project_id: Optional[str] = None,
force_recreate: bool = True,
tool_object: Optional[Tool] = None,
sandbox_config: Optional[SandboxConfig] = None,
sandbox_env_vars: Optional[Dict[str, Any]] = None,
organization_id: Optional[str] = None,
project_id: str = "default",
):
super().__init__(tool_name, args, user, tool_object, sandbox_config=sandbox_config, sandbox_env_vars=sandbox_env_vars)
super().__init__(
tool_name,
args,
user,
tool_id=tool_id,
agent_id=agent_id,
project_id=project_id,
tool_object=tool_object,
sandbox_config=sandbox_config,
sandbox_env_vars=sandbox_env_vars,
)
self.force_recreate = force_recreate
# Get organization_id from user if not explicitly provided
self.organization_id = organization_id if organization_id is not None else user.organization_id
self.project_id = project_id
# TODO: check to make sure modal app `App(tool.id)` exists
@@ -86,7 +97,6 @@ class AsyncToolSandboxModal(AsyncToolSandboxBase):
@trace_method
async def run(
self,
agent_id: Optional[str] = None,
agent_state: Optional[AgentState] = None,
additional_env_vars: Optional[Dict] = None,
) -> ToolExecutionResult:
@@ -149,7 +159,7 @@ class AsyncToolSandboxModal(AsyncToolSandboxBase):
result = await func.remote.aio(
tool_name=self.tool_name,
agent_state=agent_state_dict,
agent_id=agent_id,
agent_id=self.agent_id,
env_vars=env_vars,
letta_api_key=letta_api_key,
**self.args,

View File

@@ -37,6 +37,9 @@ class AsyncToolSandboxModalV2(AsyncToolSandboxBase):
tool_name: str,
args: JsonDict,
user,
tool_id: str,
agent_id: str | None = None,
project_id: str | None = None,
tool_object: Tool | None = None,
sandbox_config: SandboxConfig | None = None,
sandbox_env_vars: dict[str, Any] | None = None,
@@ -51,6 +54,9 @@ class AsyncToolSandboxModalV2(AsyncToolSandboxBase):
tool_name: Name of the tool to execute
args: Arguments to pass to the tool
user: User/actor for permissions
tool_id: Tool ID for the tool being executed
agent_id: Agent ID (optional)
project_id: Project ID for the tool execution (optional)
tool_object: Tool object (optional)
sandbox_config: Sandbox configuration (optional)
sandbox_env_vars: Environment variables (optional)
@@ -58,7 +64,17 @@ class AsyncToolSandboxModalV2(AsyncToolSandboxBase):
use_locking: Whether to use locking for deployment coordination (default: True)
use_version_tracking: Whether to track and reuse deployments (default: True)
"""
super().__init__(tool_name, args, user, tool_object, sandbox_config=sandbox_config, sandbox_env_vars=sandbox_env_vars)
super().__init__(
tool_name,
args,
user,
tool_id=tool_id,
agent_id=agent_id,
project_id=project_id,
tool_object=tool_object,
sandbox_config=sandbox_config,
sandbox_env_vars=sandbox_env_vars,
)
if not tool_settings.modal_token_id or not tool_settings.modal_token_secret:
raise ValueError("MODAL_TOKEN_ID and MODAL_TOKEN_SECRET must be set.")

View File

@@ -559,12 +559,12 @@ async def test_local_sandbox_default(disable_e2b_api_key, add_integers_tool, tes
# Mock and assert correct pathway was invoked
with patch.object(AsyncToolSandboxLocal, "run") as mock_run:
sandbox = AsyncToolSandboxLocal(add_integers_tool.name, args, user=test_user)
sandbox = AsyncToolSandboxLocal(add_integers_tool.name, args, user=test_user, tool_id=add_integers_tool.id)
await sandbox.run()
mock_run.assert_called_once()
# Run again to get actual response
sandbox = AsyncToolSandboxLocal(add_integers_tool.name, args, user=test_user)
sandbox = AsyncToolSandboxLocal(add_integers_tool.name, args, user=test_user, tool_id=add_integers_tool.id)
result = await sandbox.run()
assert result.func_return == args["x"] + args["y"]
@@ -573,7 +573,7 @@ async def test_local_sandbox_default(disable_e2b_api_key, add_integers_tool, tes
@pytest.mark.local_sandbox
async def test_local_sandbox_stateful_tool(disable_e2b_api_key, clear_core_memory_tool, test_user, agent_state):
args = {}
sandbox = AsyncToolSandboxLocal(clear_core_memory_tool.name, args, user=test_user)
sandbox = AsyncToolSandboxLocal(clear_core_memory_tool.name, args, user=test_user, tool_id=clear_core_memory_tool.id)
result = await sandbox.run(agent_state=agent_state)
assert sandbox.inject_agent_state == True
assert result.agent_state.memory.get_block("human").value == ""
@@ -584,7 +584,7 @@ async def test_local_sandbox_stateful_tool(disable_e2b_api_key, clear_core_memor
@pytest.mark.asyncio
@pytest.mark.local_sandbox
async def test_local_sandbox_with_list_rv(disable_e2b_api_key, list_tool, test_user):
sandbox = AsyncToolSandboxLocal(list_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxLocal(list_tool.name, {}, user=test_user, tool_id=list_tool.id)
result = await sandbox.run()
assert len(result.func_return) == 5
@@ -603,7 +603,7 @@ async def test_local_sandbox_env(disable_e2b_api_key, get_env_tool, test_user):
SandboxEnvironmentVariableCreate(key=key, value=long_random_string), sandbox_config_id=config.id, actor=test_user
)
sandbox = AsyncToolSandboxLocal(get_env_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxLocal(get_env_tool.name, {}, user=test_user, tool_id=get_env_tool.id)
result = await sandbox.run()
assert long_random_string in result.func_return
@@ -625,7 +625,7 @@ async def test_local_sandbox_per_agent_env(disable_e2b_api_key, get_env_tool, ag
correct_val = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(20))
agent_state.secrets = [AgentEnvironmentVariable(key=key, value=correct_val, agent_id=agent_state.id)]
sandbox = AsyncToolSandboxLocal(get_env_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxLocal(get_env_tool.name, {}, user=test_user, tool_id=get_env_tool.id)
result = await sandbox.run(agent_state=agent_state)
assert wrong_val not in result.func_return
assert correct_val in result.func_return
@@ -637,7 +637,7 @@ async def test_local_sandbox_external_codebase_with_venv(
disable_e2b_api_key, custom_test_sandbox_config, external_codebase_tool, test_user
):
args = {"percentage": 10}
sandbox = AsyncToolSandboxLocal(external_codebase_tool.name, args, user=test_user)
sandbox = AsyncToolSandboxLocal(external_codebase_tool.name, args, user=test_user, tool_id=external_codebase_tool.id)
result = await sandbox.run()
assert result.func_return == "Price Adjustments:\nBurger: $8.99 -> $9.89\nFries: $2.99 -> $3.29\nSoda: $1.99 -> $2.19"
assert "Hello World" in result.stdout[0]
@@ -648,7 +648,7 @@ async def test_local_sandbox_external_codebase_with_venv(
async def test_local_sandbox_with_venv_and_warnings_does_not_error(
disable_e2b_api_key, custom_test_sandbox_config, get_warning_tool, test_user
):
sandbox = AsyncToolSandboxLocal(get_warning_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxLocal(get_warning_tool.name, {}, user=test_user, tool_id=get_warning_tool.id)
result = await sandbox.run()
assert result.func_return == "Hello World"
@@ -656,7 +656,7 @@ async def test_local_sandbox_with_venv_and_warnings_does_not_error(
@pytest.mark.asyncio
@pytest.mark.e2b_sandbox
async def test_local_sandbox_with_venv_errors(disable_e2b_api_key, custom_test_sandbox_config, always_err_tool, test_user):
sandbox = AsyncToolSandboxLocal(always_err_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxLocal(always_err_tool.name, {}, user=test_user, tool_id=always_err_tool.id)
result = await sandbox.run()
assert len(result.stdout) != 0
assert "error" in result.stdout[0]
@@ -679,7 +679,7 @@ async def test_local_sandbox_with_venv_pip_installs_basic(disable_e2b_api_key, c
SandboxEnvironmentVariableCreate(key=key, value=long_random_string), sandbox_config_id=config.id, actor=test_user
)
sandbox = AsyncToolSandboxLocal(cowsay_tool.name, {}, user=test_user, force_recreate_venv=True)
sandbox = AsyncToolSandboxLocal(cowsay_tool.name, {}, user=test_user, tool_id=cowsay_tool.id, force_recreate_venv=True)
result = await sandbox.run()
assert long_random_string in result.stdout[0]
@@ -694,7 +694,12 @@ async def test_local_sandbox_with_tool_pip_requirements(disable_e2b_api_key, too
await manager.create_or_update_sandbox_config_async(config_create, test_user)
sandbox = AsyncToolSandboxLocal(
tool_with_pip_requirements.name, {}, user=test_user, tool_object=tool_with_pip_requirements, force_recreate_venv=True
tool_with_pip_requirements.name,
{},
user=test_user,
tool_id=tool_with_pip_requirements.id,
tool_object=tool_with_pip_requirements,
force_recreate_venv=True,
)
result = await sandbox.run()
@@ -718,7 +723,12 @@ async def test_local_sandbox_with_mixed_pip_requirements(disable_e2b_api_key, to
await manager.create_or_update_sandbox_config_async(config_create, test_user)
sandbox = AsyncToolSandboxLocal(
tool_with_pip_requirements.name, {}, user=test_user, tool_object=tool_with_pip_requirements, force_recreate_venv=True
tool_with_pip_requirements.name,
{},
user=test_user,
tool_id=tool_with_pip_requirements.id,
tool_object=tool_with_pip_requirements,
force_recreate_venv=True,
)
result = await sandbox.run()
@@ -741,7 +751,7 @@ async def test_local_sandbox_with_venv_pip_installs_with_update(disable_e2b_api_
SandboxEnvironmentVariableCreate(key=key, value=long_random_string), sandbox_config_id=config.id, actor=test_user
)
sandbox = AsyncToolSandboxLocal(cowsay_tool.name, {}, user=test_user, force_recreate_venv=True)
sandbox = AsyncToolSandboxLocal(cowsay_tool.name, {}, user=test_user, tool_id=cowsay_tool.id, force_recreate_venv=True)
result = await sandbox.run()
assert len(result.stdout) == 0
assert "No module named 'cowsay'" in result.stderr[0]
@@ -751,7 +761,7 @@ async def test_local_sandbox_with_venv_pip_installs_with_update(disable_e2b_api_
)
await manager.create_or_update_sandbox_config_async(config_create, test_user)
sandbox = AsyncToolSandboxLocal(cowsay_tool.name, {}, user=test_user, force_recreate_venv=False)
sandbox = AsyncToolSandboxLocal(cowsay_tool.name, {}, user=test_user, tool_id=cowsay_tool.id, force_recreate_venv=False)
result = await sandbox.run()
assert long_random_string in result.stdout[0]
@@ -766,12 +776,12 @@ async def test_e2b_sandbox_default(check_e2b_key_is_set, add_integers_tool, test
# Mock and assert correct pathway was invoked
with patch.object(AsyncToolSandboxE2B, "run") as mock_run:
sandbox = AsyncToolSandboxE2B(add_integers_tool.name, args, user=test_user)
sandbox = AsyncToolSandboxE2B(add_integers_tool.name, args, user=test_user, tool_id=add_integers_tool.id)
await sandbox.run()
mock_run.assert_called_once()
# Run again to get actual response
sandbox = AsyncToolSandboxE2B(add_integers_tool.name, args, user=test_user)
sandbox = AsyncToolSandboxE2B(add_integers_tool.name, args, user=test_user, tool_id=add_integers_tool.id)
result = await sandbox.run()
assert int(result.func_return) == args["x"] + args["y"]
@@ -791,7 +801,7 @@ async def test_e2b_sandbox_pip_installs(check_e2b_key_is_set, cowsay_tool, test_
actor=test_user,
)
sandbox = AsyncToolSandboxE2B(cowsay_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxE2B(cowsay_tool.name, {}, user=test_user, tool_id=cowsay_tool.id)
result = await sandbox.run()
assert long_random_string in result.stdout[0]
@@ -799,7 +809,7 @@ async def test_e2b_sandbox_pip_installs(check_e2b_key_is_set, cowsay_tool, test_
@pytest.mark.asyncio
@pytest.mark.e2b_sandbox
async def test_e2b_sandbox_stateful_tool(check_e2b_key_is_set, clear_core_memory_tool, test_user, agent_state):
sandbox = AsyncToolSandboxE2B(clear_core_memory_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxE2B(clear_core_memory_tool.name, {}, user=test_user, tool_id=clear_core_memory_tool.id)
result = await sandbox.run(agent_state=agent_state)
assert result.agent_state.memory.get_block("human").value == ""
assert result.agent_state.memory.get_block("persona").value == ""
@@ -813,7 +823,7 @@ async def test_e2b_sandbox_inject_env_var_existing_sandbox(check_e2b_key_is_set,
config_create = SandboxConfigCreate(config=E2BSandboxConfig().model_dump())
config = await manager.create_or_update_sandbox_config_async(config_create, test_user)
sandbox = AsyncToolSandboxE2B(get_env_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxE2B(get_env_tool.name, {}, user=test_user, tool_id=get_env_tool.id)
result = await sandbox.run()
assert result.func_return is None
@@ -825,7 +835,7 @@ async def test_e2b_sandbox_inject_env_var_existing_sandbox(check_e2b_key_is_set,
actor=test_user,
)
sandbox = AsyncToolSandboxE2B(get_env_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxE2B(get_env_tool.name, {}, user=test_user, tool_id=get_env_tool.id)
result = await sandbox.run()
assert long_random_string in result.func_return
@@ -848,7 +858,7 @@ async def test_e2b_sandbox_per_agent_env(check_e2b_key_is_set, get_env_tool, age
agent_state.secrets = [AgentEnvironmentVariable(key=key, value=correct_val, agent_id=agent_state.id)]
sandbox = AsyncToolSandboxE2B(get_env_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxE2B(get_env_tool.name, {}, user=test_user, tool_id=get_env_tool.id)
result = await sandbox.run(agent_state=agent_state)
assert wrong_val not in result.func_return
assert correct_val in result.func_return
@@ -857,7 +867,7 @@ async def test_e2b_sandbox_per_agent_env(check_e2b_key_is_set, get_env_tool, age
@pytest.mark.asyncio
@pytest.mark.e2b_sandbox
async def test_e2b_sandbox_with_list_rv(check_e2b_key_is_set, list_tool, test_user):
sandbox = AsyncToolSandboxE2B(list_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxE2B(list_tool.name, {}, user=test_user, tool_id=list_tool.id)
result = await sandbox.run()
assert len(result.func_return) == 5
@@ -870,7 +880,9 @@ async def test_e2b_sandbox_with_tool_pip_requirements(check_e2b_key_is_set, tool
config_create = SandboxConfigCreate(config=E2BSandboxConfig().model_dump())
await manager.create_or_update_sandbox_config_async(config_create, test_user)
sandbox = AsyncToolSandboxE2B(tool_with_pip_requirements.name, {}, user=test_user, tool_object=tool_with_pip_requirements)
sandbox = AsyncToolSandboxE2B(
tool_with_pip_requirements.name, {}, user=test_user, tool_id=tool_with_pip_requirements.id, tool_object=tool_with_pip_requirements
)
result = await sandbox.run()
# Should succeed since tool pip requirements were installed
@@ -889,7 +901,9 @@ async def test_e2b_sandbox_with_mixed_pip_requirements(check_e2b_key_is_set, too
config_create = SandboxConfigCreate(config=E2BSandboxConfig(pip_requirements=["cowsay"]).model_dump())
await manager.create_or_update_sandbox_config_async(config_create, test_user)
sandbox = AsyncToolSandboxE2B(tool_with_pip_requirements.name, {}, user=test_user, tool_object=tool_with_pip_requirements)
sandbox = AsyncToolSandboxE2B(
tool_with_pip_requirements.name, {}, user=test_user, tool_id=tool_with_pip_requirements.id, tool_object=tool_with_pip_requirements
)
result = await sandbox.run()
# Should succeed since both sandbox and tool pip requirements were installed
@@ -908,7 +922,13 @@ async def test_e2b_sandbox_with_broken_tool_pip_requirements_error_handling(
config_create = SandboxConfigCreate(config=E2BSandboxConfig().model_dump())
await manager.create_or_update_sandbox_config_async(config_create, test_user)
sandbox = AsyncToolSandboxE2B(tool_with_broken_pip_requirements.name, {}, user=test_user, tool_object=tool_with_broken_pip_requirements)
sandbox = AsyncToolSandboxE2B(
tool_with_broken_pip_requirements.name,
{},
user=test_user,
tool_id=tool_with_broken_pip_requirements.id,
tool_object=tool_with_broken_pip_requirements,
)
# Should raise a RuntimeError with informative message
with pytest.raises(RuntimeError) as exc_info:
@@ -935,12 +955,14 @@ async def test_e2b_sandbox_with_broken_tool_pip_requirements_error_handling(
async def test_async_function_detection(add_integers_tool, async_add_integers_tool, test_user):
"""Test that async function detection works correctly"""
# Test sync function detection
sync_sandbox = AsyncToolSandboxE2B(add_integers_tool.name, {}, test_user, tool_object=add_integers_tool)
sync_sandbox = AsyncToolSandboxE2B(add_integers_tool.name, {}, test_user, tool_id=add_integers_tool.id, tool_object=add_integers_tool)
await sync_sandbox._init_async()
assert not sync_sandbox.is_async_function
# Test async function detection
async_sandbox = AsyncToolSandboxE2B(async_add_integers_tool.name, {}, test_user, tool_object=async_add_integers_tool)
async_sandbox = AsyncToolSandboxE2B(
async_add_integers_tool.name, {}, test_user, tool_id=async_add_integers_tool.id, tool_object=async_add_integers_tool
)
await async_sandbox._init_async()
assert async_sandbox.is_async_function
@@ -949,7 +971,7 @@ async def test_async_function_detection(add_integers_tool, async_add_integers_to
async def test_async_template_selection(add_integers_tool, async_add_integers_tool, test_user):
"""Test that correct templates are selected for sync vs async functions"""
# Test sync function uses regular template
sync_sandbox = AsyncToolSandboxE2B(add_integers_tool.name, {}, test_user, tool_object=add_integers_tool)
sync_sandbox = AsyncToolSandboxE2B(add_integers_tool.name, {}, test_user, tool_id=add_integers_tool.id, tool_object=add_integers_tool)
sync_script = await sync_sandbox.generate_execution_script(agent_state=None)
print("=== SYNC SCRIPT ===")
print(sync_script)
@@ -958,7 +980,9 @@ async def test_async_template_selection(add_integers_tool, async_add_integers_to
assert "asyncio.run" not in sync_script
# Test async function uses async template
async_sandbox = AsyncToolSandboxE2B(async_add_integers_tool.name, {}, test_user, tool_object=async_add_integers_tool)
async_sandbox = AsyncToolSandboxE2B(
async_add_integers_tool.name, {}, test_user, tool_id=async_add_integers_tool.id, tool_object=async_add_integers_tool
)
async_script = await async_sandbox.generate_execution_script(agent_state=None)
print("=== ASYNC SCRIPT ===")
print(async_script)
@@ -974,7 +998,7 @@ async def test_local_sandbox_async_function_execution(disable_e2b_api_key, async
"""Test that async functions execute correctly in local sandbox"""
args = {"x": 15, "y": 25}
sandbox = AsyncToolSandboxLocal(async_add_integers_tool.name, args, user=test_user)
sandbox = AsyncToolSandboxLocal(async_add_integers_tool.name, args, user=test_user, tool_id=async_add_integers_tool.id)
result = await sandbox.run()
assert result.func_return == args["x"] + args["y"]
@@ -985,7 +1009,7 @@ async def test_e2b_sandbox_async_function_execution(check_e2b_key_is_set, async_
"""Test that async functions execute correctly in E2B sandbox"""
args = {"x": 20, "y": 30}
sandbox = AsyncToolSandboxE2B(async_add_integers_tool.name, args, user=test_user)
sandbox = AsyncToolSandboxE2B(async_add_integers_tool.name, args, user=test_user, tool_id=async_add_integers_tool.id)
result = await sandbox.run()
assert int(result.func_return) == args["x"] + args["y"]
@@ -996,7 +1020,7 @@ async def test_local_sandbox_async_complex_computation(disable_e2b_api_key, asyn
"""Test complex async computation with multiple awaits in local sandbox"""
args = {"iterations": 2}
sandbox = AsyncToolSandboxLocal(async_complex_tool.name, args, user=test_user)
sandbox = AsyncToolSandboxLocal(async_complex_tool.name, args, user=test_user, tool_id=async_complex_tool.id)
result = await sandbox.run()
assert isinstance(result.func_return, dict)
@@ -1012,7 +1036,7 @@ async def test_e2b_sandbox_async_complex_computation(check_e2b_key_is_set, async
"""Test complex async computation with multiple awaits in E2B sandbox"""
args = {"iterations": 2}
sandbox = AsyncToolSandboxE2B(async_complex_tool.name, args, user=test_user)
sandbox = AsyncToolSandboxE2B(async_complex_tool.name, args, user=test_user, tool_id=async_complex_tool.id)
result = await sandbox.run()
func_return = result.func_return
@@ -1027,7 +1051,7 @@ async def test_e2b_sandbox_async_complex_computation(check_e2b_key_is_set, async
@pytest.mark.local_sandbox
async def test_local_sandbox_async_list_return(disable_e2b_api_key, async_list_tool, test_user):
"""Test async function returning list in local sandbox"""
sandbox = AsyncToolSandboxLocal(async_list_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxLocal(async_list_tool.name, {}, user=test_user, tool_id=async_list_tool.id)
result = await sandbox.run()
assert result.func_return == [1, 2, 3, 4, 5]
@@ -1036,7 +1060,7 @@ async def test_local_sandbox_async_list_return(disable_e2b_api_key, async_list_t
@pytest.mark.e2b_sandbox
async def test_e2b_sandbox_async_list_return(check_e2b_key_is_set, async_list_tool, test_user):
"""Test async function returning list in E2B sandbox"""
sandbox = AsyncToolSandboxE2B(async_list_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxE2B(async_list_tool.name, {}, user=test_user, tool_id=async_list_tool.id)
result = await sandbox.run()
assert result.func_return == [1, 2, 3, 4, 5]
@@ -1059,7 +1083,7 @@ async def test_local_sandbox_async_with_env_vars(disable_e2b_api_key, async_get_
SandboxEnvironmentVariableCreate(key=key, value=test_value), sandbox_config_id=config.id, actor=test_user
)
sandbox = AsyncToolSandboxLocal(async_get_env_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxLocal(async_get_env_tool.name, {}, user=test_user, tool_id=async_get_env_tool.id)
result = await sandbox.run()
assert test_value in result.func_return
@@ -1080,7 +1104,7 @@ async def test_e2b_sandbox_async_with_env_vars(check_e2b_key_is_set, async_get_e
SandboxEnvironmentVariableCreate(key=key, value=test_value), sandbox_config_id=config.id, actor=test_user
)
sandbox = AsyncToolSandboxE2B(async_get_env_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxE2B(async_get_env_tool.name, {}, user=test_user, tool_id=async_get_env_tool.id)
result = await sandbox.run()
assert test_value in result.func_return
@@ -1090,7 +1114,7 @@ async def test_e2b_sandbox_async_with_env_vars(check_e2b_key_is_set, async_get_e
@pytest.mark.local_sandbox
async def test_local_sandbox_async_with_agent_state(disable_e2b_api_key, async_stateful_tool, test_user, agent_state):
"""Test async function with agent state in local sandbox"""
sandbox = AsyncToolSandboxLocal(async_stateful_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxLocal(async_stateful_tool.name, {}, user=test_user, tool_id=async_stateful_tool.id)
result = await sandbox.run(agent_state=agent_state)
assert result.agent_state is not None
@@ -1103,7 +1127,7 @@ async def test_local_sandbox_async_with_agent_state(disable_e2b_api_key, async_s
@pytest.mark.e2b_sandbox
async def test_e2b_sandbox_async_with_agent_state(check_e2b_key_is_set, async_stateful_tool, test_user, agent_state):
"""Test async function with agent state in E2B sandbox"""
sandbox = AsyncToolSandboxE2B(async_stateful_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxE2B(async_stateful_tool.name, {}, user=test_user, tool_id=async_stateful_tool.id)
result = await sandbox.run(agent_state=agent_state)
assert result.agent_state.memory.get_block("human").value == ""
@@ -1115,7 +1139,7 @@ async def test_e2b_sandbox_async_with_agent_state(check_e2b_key_is_set, async_st
@pytest.mark.local_sandbox
async def test_local_sandbox_async_error_handling(disable_e2b_api_key, async_error_tool, test_user):
"""Test async function error handling in local sandbox"""
sandbox = AsyncToolSandboxLocal(async_error_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxLocal(async_error_tool.name, {}, user=test_user, tool_id=async_error_tool.id)
result = await sandbox.run()
# Check that error was captured
@@ -1129,7 +1153,7 @@ async def test_local_sandbox_async_error_handling(disable_e2b_api_key, async_err
@pytest.mark.e2b_sandbox
async def test_e2b_sandbox_async_error_handling(check_e2b_key_is_set, async_error_tool, test_user):
"""Test async function error handling in E2B sandbox"""
sandbox = AsyncToolSandboxE2B(async_error_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxE2B(async_error_tool.name, {}, user=test_user, tool_id=async_error_tool.id)
result = await sandbox.run()
# Check that error was captured
@@ -1157,7 +1181,7 @@ async def test_local_sandbox_async_per_agent_env(disable_e2b_api_key, async_get_
correct_val = "correct_async_local_value"
agent_state.secrets = [AgentEnvironmentVariable(key=key, value=correct_val, agent_id=agent_state.id)]
sandbox = AsyncToolSandboxLocal(async_get_env_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxLocal(async_get_env_tool.name, {}, user=test_user, tool_id=async_get_env_tool.id)
result = await sandbox.run(agent_state=agent_state)
assert wrong_val not in result.func_return
assert correct_val in result.func_return
@@ -1182,7 +1206,7 @@ async def test_e2b_sandbox_async_per_agent_env(check_e2b_key_is_set, async_get_e
agent_state.secrets = [AgentEnvironmentVariable(key=key, value=correct_val, agent_id=agent_state.id)]
sandbox = AsyncToolSandboxE2B(async_get_env_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxE2B(async_get_env_tool.name, {}, user=test_user, tool_id=async_get_env_tool.id)
result = await sandbox.run(agent_state=agent_state)
assert wrong_val not in result.func_return
assert correct_val in result.func_return
@@ -1265,6 +1289,7 @@ async def test_local_sandbox_with_client_injection(disable_e2b_api_key, list_too
tool_name=list_tools_with_client_tool.name,
args={},
user=test_user,
tool_id=list_tools_with_client_tool.id,
tool_object=list_tools_with_client_tool,
sandbox_env_vars=sandbox_env_vars,
)
@@ -1323,6 +1348,7 @@ async def test_e2b_sandbox_with_client_injection(check_e2b_key_is_set, list_tool
tool_name=list_tools_with_client_tool.name,
args={},
user=test_user,
tool_id=list_tools_with_client_tool.id,
tool_object=list_tools_with_client_tool,
sandbox_env_vars=sandbox_env_vars,
)

View File

@@ -485,12 +485,12 @@ async def test_modal_sandbox_default(check_modal_key_is_set, add_integers_tool,
# Mock and assert correct pathway was invoked
with patch.object(AsyncToolSandboxModal, "run") as mock_run:
sandbox = AsyncToolSandboxModal(add_integers_tool.name, args, user=test_user)
sandbox = AsyncToolSandboxModal(add_integers_tool.name, args, user=test_user, tool_id=add_integers_tool.id)
await sandbox.run()
mock_run.assert_called_once()
# Run again to get actual response
sandbox = AsyncToolSandboxModal(add_integers_tool.name, args, user=test_user)
sandbox = AsyncToolSandboxModal(add_integers_tool.name, args, user=test_user, tool_id=add_integers_tool.id)
result = await sandbox.run()
assert int(result.func_return) == args["x"] + args["y"]
@@ -511,7 +511,7 @@ async def test_modal_sandbox_pip_installs(check_modal_key_is_set, cowsay_tool, t
actor=test_user,
)
sandbox = AsyncToolSandboxModal(cowsay_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxModal(cowsay_tool.name, {}, user=test_user, tool_id=cowsay_tool.id)
result = await sandbox.run()
assert long_random_string in result.stdout[0]
@@ -519,7 +519,7 @@ async def test_modal_sandbox_pip_installs(check_modal_key_is_set, cowsay_tool, t
@pytest.mark.asyncio
@pytest.mark.modal_sandbox
async def test_modal_sandbox_stateful_tool(check_modal_key_is_set, clear_core_memory_tool, test_user, agent_state):
sandbox = AsyncToolSandboxModal(clear_core_memory_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxModal(clear_core_memory_tool.name, {}, user=test_user, tool_id=clear_core_memory_tool.id)
result = await sandbox.run(agent_state=agent_state)
assert result.agent_state.memory.get_block("human").value == ""
assert result.agent_state.memory.get_block("persona").value == ""
@@ -533,7 +533,7 @@ async def test_modal_sandbox_inject_env_var_existing_sandbox(check_modal_key_is_
config_create = SandboxConfigCreate(config=ModalSandboxConfig().model_dump())
config = await manager.create_or_update_sandbox_config_async(config_create, test_user)
sandbox = AsyncToolSandboxModal(get_env_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxModal(get_env_tool.name, {}, user=test_user, tool_id=get_env_tool.id)
result = await sandbox.run()
assert result.func_return is None
@@ -545,7 +545,7 @@ async def test_modal_sandbox_inject_env_var_existing_sandbox(check_modal_key_is_
actor=test_user,
)
sandbox = AsyncToolSandboxModal(get_env_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxModal(get_env_tool.name, {}, user=test_user, tool_id=get_env_tool.id)
result = await sandbox.run()
assert long_random_string in result.func_return
@@ -568,7 +568,7 @@ async def test_modal_sandbox_per_agent_env(check_modal_key_is_set, get_env_tool,
agent_state.secrets = [AgentEnvironmentVariable(key=key, value=correct_val, agent_id=agent_state.id)]
sandbox = AsyncToolSandboxModal(get_env_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxModal(get_env_tool.name, {}, user=test_user, tool_id=get_env_tool.id)
result = await sandbox.run(agent_state=agent_state)
assert wrong_val not in result.func_return
assert correct_val in result.func_return
@@ -577,7 +577,7 @@ async def test_modal_sandbox_per_agent_env(check_modal_key_is_set, get_env_tool,
@pytest.mark.asyncio
@pytest.mark.modal_sandbox
async def test_modal_sandbox_with_list_rv(check_modal_key_is_set, list_tool, test_user):
sandbox = AsyncToolSandboxModal(list_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxModal(list_tool.name, {}, user=test_user, tool_id=list_tool.id)
result = await sandbox.run()
assert len(result.func_return) == 5
@@ -590,7 +590,9 @@ async def test_modal_sandbox_with_tool_pip_requirements(check_modal_key_is_set,
config_create = SandboxConfigCreate(config=ModalSandboxConfig().model_dump())
await manager.create_or_update_sandbox_config_async(config_create, test_user)
sandbox = AsyncToolSandboxModal(tool_with_pip_requirements.name, {}, user=test_user, tool_object=tool_with_pip_requirements)
sandbox = AsyncToolSandboxModal(
tool_with_pip_requirements.name, {}, user=test_user, tool_id=tool_with_pip_requirements.id, tool_object=tool_with_pip_requirements
)
result = await sandbox.run()
# Should succeed since tool pip requirements were installed
@@ -611,7 +613,9 @@ async def test_modal_sandbox_with_mixed_pip_requirements(check_modal_key_is_set,
config_create = SandboxConfigCreate(config=ModalSandboxConfig().model_dump())
await manager.create_or_update_sandbox_config_async(config_create, test_user)
sandbox = AsyncToolSandboxModal(tool_with_pip_requirements.name, {}, user=test_user, tool_object=tool_with_pip_requirements)
sandbox = AsyncToolSandboxModal(
tool_with_pip_requirements.name, {}, user=test_user, tool_id=tool_with_pip_requirements.id, tool_object=tool_with_pip_requirements
)
result = await sandbox.run()
# Should succeed since tool pip requirements were installed
@@ -651,12 +655,14 @@ async def test_modal_sandbox_with_broken_tool_pip_requirements_error_handling(ch
async def test_async_function_detection(add_integers_tool, async_add_integers_tool, test_user):
"""Test that async function detection works correctly"""
# Test sync function detection
sync_sandbox = AsyncToolSandboxModal(add_integers_tool.name, {}, test_user, tool_object=add_integers_tool)
sync_sandbox = AsyncToolSandboxModal(add_integers_tool.name, {}, test_user, tool_id=add_integers_tool.id, tool_object=add_integers_tool)
await sync_sandbox._init_async()
assert not sync_sandbox.is_async_function
# Test async function detection
async_sandbox = AsyncToolSandboxModal(async_add_integers_tool.name, {}, test_user, tool_object=async_add_integers_tool)
async_sandbox = AsyncToolSandboxModal(
async_add_integers_tool.name, {}, test_user, tool_id=async_add_integers_tool.id, tool_object=async_add_integers_tool
)
await async_sandbox._init_async()
assert async_sandbox.is_async_function
@@ -667,7 +673,7 @@ async def test_modal_sandbox_async_function_execution(check_modal_key_is_set, as
"""Test that async functions execute correctly in Modal sandbox"""
args = {"x": 20, "y": 30}
sandbox = AsyncToolSandboxModal(async_add_integers_tool.name, args, user=test_user)
sandbox = AsyncToolSandboxModal(async_add_integers_tool.name, args, user=test_user, tool_id=async_add_integers_tool.id)
result = await sandbox.run()
assert int(result.func_return) == args["x"] + args["y"]
@@ -678,7 +684,7 @@ async def test_modal_sandbox_async_complex_computation(check_modal_key_is_set, a
"""Test complex async computation with multiple awaits in Modal sandbox"""
args = {"iterations": 2}
sandbox = AsyncToolSandboxModal(async_complex_tool.name, args, user=test_user)
sandbox = AsyncToolSandboxModal(async_complex_tool.name, args, user=test_user, tool_id=async_complex_tool.id)
result = await sandbox.run()
func_return = result.func_return
@@ -693,7 +699,7 @@ async def test_modal_sandbox_async_complex_computation(check_modal_key_is_set, a
@pytest.mark.modal_sandbox
async def test_modal_sandbox_async_list_return(check_modal_key_is_set, async_list_tool, test_user):
"""Test async function returning list in Modal sandbox"""
sandbox = AsyncToolSandboxModal(async_list_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxModal(async_list_tool.name, {}, user=test_user, tool_id=async_list_tool.id)
result = await sandbox.run()
assert result.func_return == [1, 2, 3, 4, 5]
@@ -713,7 +719,7 @@ async def test_modal_sandbox_async_with_env_vars(check_modal_key_is_set, async_g
SandboxEnvironmentVariableCreate(key=key, value=test_value), sandbox_config_id=config.id, actor=test_user
)
sandbox = AsyncToolSandboxModal(async_get_env_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxModal(async_get_env_tool.name, {}, user=test_user, tool_id=async_get_env_tool.id)
result = await sandbox.run()
assert test_value in result.func_return
@@ -723,7 +729,7 @@ async def test_modal_sandbox_async_with_env_vars(check_modal_key_is_set, async_g
@pytest.mark.modal_sandbox
async def test_modal_sandbox_async_with_agent_state(check_modal_key_is_set, async_stateful_tool, test_user, agent_state):
"""Test async function with agent state in Modal sandbox"""
sandbox = AsyncToolSandboxModal(async_stateful_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxModal(async_stateful_tool.name, {}, user=test_user, tool_id=async_stateful_tool.id)
result = await sandbox.run(agent_state=agent_state)
assert result.agent_state.memory.get_block("human").value == ""
@@ -735,7 +741,7 @@ async def test_modal_sandbox_async_with_agent_state(check_modal_key_is_set, asyn
@pytest.mark.modal_sandbox
async def test_modal_sandbox_async_error_handling(check_modal_key_is_set, async_error_tool, test_user):
"""Test async function error handling in Modal sandbox"""
sandbox = AsyncToolSandboxModal(async_error_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxModal(async_error_tool.name, {}, user=test_user, tool_id=async_error_tool.id)
result = await sandbox.run()
# Check that error was captured
@@ -764,7 +770,7 @@ async def test_modal_sandbox_async_per_agent_env(check_modal_key_is_set, async_g
agent_state.secrets = [AgentEnvironmentVariable(key=key, value=correct_val, agent_id=agent_state.id)]
sandbox = AsyncToolSandboxModal(async_get_env_tool.name, {}, user=test_user)
sandbox = AsyncToolSandboxModal(async_get_env_tool.name, {}, user=test_user, tool_id=async_get_env_tool.id)
result = await sandbox.run(agent_state=agent_state)
assert wrong_val not in result.func_return
assert correct_val in result.func_return

View File

@@ -269,6 +269,7 @@ class TestModalV2BasicExecution:
tool_name="calculate",
args={"operation": "add", "a": 5, "b": 3},
user=test_user,
tool_id=basic_tool.id,
tool_object=basic_tool,
)
@@ -281,6 +282,7 @@ class TestModalV2BasicExecution:
tool_name="calculate",
args={"operation": "divide", "a": 10, "b": 2},
user=test_user,
tool_id=basic_tool.id,
tool_object=basic_tool,
)
@@ -296,6 +298,7 @@ class TestModalV2BasicExecution:
tool_name="calculate",
args={"operation": "divide", "a": 10, "b": 0},
user=test_user,
tool_id=basic_tool.id,
tool_object=basic_tool,
)
@@ -308,6 +311,7 @@ class TestModalV2BasicExecution:
tool_name="calculate",
args={"operation": "unknown", "a": 1, "b": 2},
user=test_user,
tool_id=basic_tool.id,
tool_object=basic_tool,
)
@@ -322,6 +326,7 @@ class TestModalV2BasicExecution:
tool_name="fetch_data",
args={"url": "https://example.com", "delay": 0.01},
user=test_user,
tool_id=async_tool.id,
tool_object=async_tool,
)
@@ -344,6 +349,7 @@ class TestModalV2BasicExecution:
tool_name="calculate",
args={"operation": "add", "a": i, "b": i + 1},
user=test_user,
tool_id=basic_tool.id,
tool_object=basic_tool,
)
for i in range(5)
@@ -594,6 +600,7 @@ class TestModalV2Persistence:
tool_name="calculate",
args={"operation": "add", "a": 1, "b": 1},
user=mock_user,
tool_id=basic_tool.id,
tool_object=basic_tool,
sandbox_config=config1,
)
@@ -602,6 +609,7 @@ class TestModalV2Persistence:
tool_name="calculate",
args={"operation": "add", "a": 2, "b": 2},
user=mock_user,
tool_id=basic_tool.id,
tool_object=basic_tool,
sandbox_config=config2,
)
@@ -696,6 +704,7 @@ def calculate(operation: str, a: float, b: float) -> float:
tool_name="integration_test",
args={"operation": "add", "a": 5, "b": 3},
user=mock_user,
tool_id=tool.id,
tool_object=tool,
sandbox_config=sandbox_config,
)
@@ -791,6 +800,7 @@ class TestModalV2DeploymentStats:
tool_name=tool.name,
args={},
user=test_user,
tool_id=tool.id,
tool_object=tool,
)
await sandbox.run()

View File

@@ -497,7 +497,9 @@ def memory_clear(label: str, agent_id: str, client: "Letta"):
# Simulate tool execution with the reserved keywords
# This would normally happen during agent execution, but we'll test the tool directly
# Create the sandbox for the tool
sandbox = AsyncToolSandboxLocal(tool_name="memory_clear", args={"label": "test_block"}, user=test_user, tool_object=created_tool)
sandbox = AsyncToolSandboxLocal(
tool_name="memory_clear", args={"label": "test_block"}, user=test_user, tool_id=created_tool.id, tool_object=created_tool
)
# Initialize the sandbox to detect reserved keywords
await sandbox._init_async()

View File

@@ -377,6 +377,7 @@ def test_function(x: int, y: int) -> int:
tool_name="test_function",
args={"x": 1, "y": 2},
user=mock_user,
tool_id=mock_tool.id,
tool_object=mock_tool,
sandbox_config=mock_sandbox_config,
)
@@ -396,6 +397,7 @@ def test_function(x: int, y: int) -> int:
tool_name="test_function",
args={"x": 1, "y": 2},
user=mock_user,
tool_id=mock_tool.id,
tool_object=mock_tool,
sandbox_config=mock_sandbox_config,
)
@@ -409,6 +411,7 @@ def test_function(x: int, y: int) -> int:
tool_name="test_function",
args={"x": 1, "y": 2},
user=mock_user,
tool_id=mock_tool.id,
tool_object=mock_tool,
sandbox_config=mock_sandbox_config,
)
@@ -434,6 +437,7 @@ def test_function(x: int, y: int) -> int:
tool_name="test_function",
args={"x": 1, "y": 2},
user=mock_user,
tool_id=mock_tool.id,
tool_object=mock_tool,
)
@@ -510,6 +514,7 @@ def test_function(x: int, y: int) -> int:
tool_name="test_function",
args={"x": 1, "y": 2},
user=mock_user,
tool_id=mock_tool.id,
tool_object=mock_tool,
sandbox_config=mock_sandbox_config,
)
@@ -543,6 +548,7 @@ def test_function(x: int, y: int) -> int:
tool_name="sync_func",
args={},
user=mock_user,
tool_id=sync_tool.id,
tool_object=sync_tool,
)
@@ -560,6 +566,7 @@ def test_function(x: int, y: int) -> int:
tool_name="async_func",
args={},
user=mock_user,
tool_id=async_tool.id,
tool_object=async_tool,
)