diff --git a/python/docs/src/user-guide/core-user-guide/components/tools.ipynb b/python/docs/src/user-guide/core-user-guide/components/tools.ipynb index 2599f4cebeae..78fc3ae40a40 100644 --- a/python/docs/src/user-guide/core-user-guide/components/tools.ipynb +++ b/python/docs/src/user-guide/core-user-guide/components/tools.ipynb @@ -22,7 +22,7 @@ "source": [ "## Built-in Tools\n", "\n", - "One of the built-in tools is the {py:class}`~autogen_ext.tools.code_execution.PythonCodeExecutionTool`,\n", + "One of the built-in tools is the {py:class}`~autogen_ext.tools.code_execution.CodeExecutionTool`,\n", "which allows agents to execute Python code snippets.\n", "\n", "Here is how you create the tool and use it." @@ -45,17 +45,17 @@ "source": [ "from autogen_core import CancellationToken\n", "from autogen_ext.code_executors.docker import DockerCommandLineCodeExecutor\n", - "from autogen_ext.tools.code_execution import PythonCodeExecutionTool\n", + "from autogen_ext.tools.code_execution import CodeExecutionTool\n", "\n", "# Create the tool.\n", "code_executor = DockerCommandLineCodeExecutor()\n", "await code_executor.start()\n", - "code_execution_tool = PythonCodeExecutionTool(code_executor)\n", + "code_execution_tool = CodeExecutionTool(code_executor)\n", "cancellation_token = CancellationToken()\n", "\n", "# Use the tool directly without an agent.\n", "code = \"print('Hello, world!')\"\n", - "result = await code_execution_tool.run_json({\"code\": code}, cancellation_token)\n", + "result = await code_execution_tool.run_json({\"language\":\"python\", \"code\": code}, cancellation_token)\n", "print(code_execution_tool.return_value_as_string(result))" ] }, @@ -66,7 +66,7 @@ "The {py:class}`~autogen_ext.code_executors.docker.DockerCommandLineCodeExecutor`\n", "class is a built-in code executor that runs Python code snippets in a subprocess\n", "in the command line environment of a docker container.\n", - "The {py:class}`~autogen_ext.tools.code_execution.PythonCodeExecutionTool` class wraps the code executor\n", + "The {py:class}`~autogen_ext.tools.code_execution.CodeExecutionTool` class wraps the code executor\n", "and provides a simple interface to execute Python code snippets.\n", "\n", "Examples of other built-in tools\n", diff --git a/python/docs/src/user-guide/core-user-guide/cookbook/tool-use-with-intervention.ipynb b/python/docs/src/user-guide/core-user-guide/cookbook/tool-use-with-intervention.ipynb index cd7a79f9aaba..b1c5c908602d 100644 --- a/python/docs/src/user-guide/core-user-guide/cookbook/tool-use-with-intervention.ipynb +++ b/python/docs/src/user-guide/core-user-guide/cookbook/tool-use-with-intervention.ipynb @@ -40,7 +40,7 @@ "from autogen_core.tools import ToolSchema\n", "from autogen_ext.code_executors.docker import DockerCommandLineCodeExecutor\n", "from autogen_ext.models.openai import OpenAIChatCompletionClient\n", - "from autogen_ext.tools.code_execution import PythonCodeExecutionTool" + "from autogen_ext.tools.code_execution import CodeExecutionTool" ] }, { @@ -169,7 +169,7 @@ "First, we create a Docker-based command-line code executor\n", "using {py:class}`~autogen_ext.code_executors.docker.DockerCommandLineCodeExecutor`,\n", "and then use it to instantiate a built-in Python code execution tool\n", - "{py:class}`~autogen_core.tools.PythonCodeExecutionTool`\n", + "{py:class}`~autogen_core.tools.CodeExecutionTool`\n", "that runs code in a Docker container." ] }, @@ -182,8 +182,8 @@ "# Create the docker executor for the Python code execution tool.\n", "docker_executor = DockerCommandLineCodeExecutor()\n", "\n", - "# Create the Python code execution tool.\n", - "python_tool = PythonCodeExecutionTool(executor=docker_executor)" + "# Create the code execution tool.\n", + "execution_tool = CodeExecutionTool(executor=docker_executor)" ] }, { @@ -216,7 +216,7 @@ " \"tool_executor_agent\",\n", " lambda: ToolAgent(\n", " description=\"Tool Executor Agent\",\n", - " tools=[python_tool],\n", + " tools=[execution_tool],\n", " ),\n", ")\n", "model_client = OpenAIChatCompletionClient(model=\"gpt-4o-mini\")\n", @@ -227,7 +227,7 @@ " description=\"Tool Use Agent\",\n", " system_messages=[SystemMessage(content=\"You are a helpful AI Assistant. Use your tools to solve problems.\")],\n", " model_client=model_client,\n", - " tool_schema=[python_tool.schema],\n", + " tool_schema=[execution_tool.schema],\n", " tool_agent_type=tool_agent_type,\n", " ),\n", ")" diff --git a/python/packages/autogen-ext/src/autogen_ext/agents/openai/_openai_agent.py b/python/packages/autogen-ext/src/autogen_ext/agents/openai/_openai_agent.py index db9360d4160c..9862267425c1 100644 --- a/python/packages/autogen-ext/src/autogen_ext/agents/openai/_openai_agent.py +++ b/python/packages/autogen-ext/src/autogen_ext/agents/openai/_openai_agent.py @@ -536,7 +536,7 @@ def _add_builtin_tool(self, tool_name: str) -> None: f"Tool 'local_shell' is only supported with model 'codex-mini-latest', " f"but current model is '{self._model}'. " f"This tool is available exclusively through the Responses API and has severe limitations. " - f"Consider using autogen_ext.tools.code_execution.PythonCodeExecutionTool with " + f"Consider using autogen_ext.tools.code_execution.CodeExecutionTool with " f"autogen_ext.code_executors.local.LocalCommandLineCodeExecutor for shell execution instead." ) self._tools.append({"type": "local_shell"}) @@ -580,7 +580,7 @@ def _add_configured_tool(self, tool_config: BuiltinToolConfig) -> None: f"Tool 'local_shell' is only supported with model 'codex-mini-latest', " f"but current model is '{self._model}'. " f"This tool is available exclusively through the Responses API and has severe limitations. " - f"Consider using autogen_ext.tools.code_execution.PythonCodeExecutionTool with " + f"Consider using autogen_ext.tools.code_execution.CodeExecutionTool with " f"autogen_ext.code_executors.local.LocalCommandLineCodeExecutor for shell execution instead." ) tool_def = {"type": "local_shell"} diff --git a/python/packages/autogen-ext/src/autogen_ext/code_executors/docker_jupyter/_docker_jupyter.py b/python/packages/autogen-ext/src/autogen_ext/code_executors/docker_jupyter/_docker_jupyter.py index a8f370d159ee..f9c15387b930 100644 --- a/python/packages/autogen-ext/src/autogen_ext/code_executors/docker_jupyter/_docker_jupyter.py +++ b/python/packages/autogen-ext/src/autogen_ext/code_executors/docker_jupyter/_docker_jupyter.py @@ -97,7 +97,7 @@ async def main() -> None: asyncio.run(main()) - Example of using it with :class:`~autogen_ext.tools.code_execution.PythonCodeExecutionTool`: + Example of using it with :class:`~autogen_ext.tools.code_execution.CodeExecutionTool`: .. code-block:: python @@ -105,13 +105,13 @@ async def main() -> None: from autogen_agentchat.agents import AssistantAgent from autogen_ext.code_executors.docker_jupyter import DockerJupyterCodeExecutor, DockerJupyterServer from autogen_ext.models.openai import OpenAIChatCompletionClient - from autogen_ext.tools.code_execution import PythonCodeExecutionTool + from autogen_ext.tools.code_execution import CodeExecutionTool async def main() -> None: async with DockerJupyterServer() as jupyter_server: async with DockerJupyterCodeExecutor(jupyter_server=jupyter_server) as executor: - tool = PythonCodeExecutionTool(executor) + tool = CodeExecutionTool(executor) model_client = OpenAIChatCompletionClient(model="gpt-4o") agent = AssistantAgent("assistant", model_client=model_client, tools=[tool]) result = await agent.run(task="What is the 10th Fibonacci number? Use Python to calculate it.") diff --git a/python/packages/autogen-ext/src/autogen_ext/code_executors/jupyter/_jupyter_code_executor.py b/python/packages/autogen-ext/src/autogen_ext/code_executors/jupyter/_jupyter_code_executor.py index 2476b5a3349f..700f0fc35203 100644 --- a/python/packages/autogen-ext/src/autogen_ext/code_executors/jupyter/_jupyter_code_executor.py +++ b/python/packages/autogen-ext/src/autogen_ext/code_executors/jupyter/_jupyter_code_executor.py @@ -72,7 +72,7 @@ async def main() -> None: asyncio.run(main()) - Example of using it with :class:`~autogen_ext.tools.code_execution.PythonCodeExecutionTool`: + Example of using it with :class:`~autogen_ext.tools.code_execution.CodeExecutionTool`: .. code-block:: python @@ -80,12 +80,12 @@ async def main() -> None: from autogen_agentchat.agents import AssistantAgent from autogen_ext.code_executors.jupyter import JupyterCodeExecutor from autogen_ext.models.openai import OpenAIChatCompletionClient - from autogen_ext.tools.code_execution import PythonCodeExecutionTool + from autogen_ext.tools.code_execution import CodeExecutionTool async def main() -> None: async with JupyterCodeExecutor() as executor: - tool = PythonCodeExecutionTool(executor) + tool = CodeExecutionTool(executor) model_client = OpenAIChatCompletionClient(model="gpt-4o") agent = AssistantAgent("assistant", model_client=model_client, tools=[tool]) result = await agent.run(task="What is the 10th Fibonacci number? Use Python to calculate it.") diff --git a/python/packages/autogen-ext/src/autogen_ext/tools/code_execution/__init__.py b/python/packages/autogen-ext/src/autogen_ext/tools/code_execution/__init__.py index f58ea00df66e..23aa11147a4e 100644 --- a/python/packages/autogen-ext/src/autogen_ext/tools/code_execution/__init__.py +++ b/python/packages/autogen-ext/src/autogen_ext/tools/code_execution/__init__.py @@ -1,3 +1,3 @@ -from ._code_execution import CodeExecutionInput, CodeExecutionResult, PythonCodeExecutionTool +from ._code_execution import CodeExecutionInput, CodeExecutionResult, CodeExecutionTool -__all__ = ["CodeExecutionInput", "CodeExecutionResult", "PythonCodeExecutionTool"] +__all__ = ["CodeExecutionInput", "CodeExecutionResult", "CodeExecutionTool"] diff --git a/python/packages/autogen-ext/src/autogen_ext/tools/code_execution/_code_execution.py b/python/packages/autogen-ext/src/autogen_ext/tools/code_execution/_code_execution.py index fd6d51a7783b..019690f2bbf3 100644 --- a/python/packages/autogen-ext/src/autogen_ext/tools/code_execution/_code_execution.py +++ b/python/packages/autogen-ext/src/autogen_ext/tools/code_execution/_code_execution.py @@ -6,7 +6,8 @@ class CodeExecutionInput(BaseModel): - code: str = Field(description="The contents of the Python code block that should be executed") + language: str = Field(description="The programming language used by the code block") + code: str = Field(description="The contents of the code block that should be executed") class CodeExecutionResult(BaseModel): @@ -18,17 +19,15 @@ def ser_model(self) -> str: return self.output -class PythonCodeExecutionToolConfig(BaseModel): - """Configuration for PythonCodeExecutionTool""" +class CodeExecutionToolConfig(BaseModel): + """Configuration for CodeExecutionTool""" executor: ComponentModel - description: str = "Execute Python code blocks." + description: str = "Execute code blocks." -class PythonCodeExecutionTool( - BaseTool[CodeExecutionInput, CodeExecutionResult], Component[PythonCodeExecutionToolConfig] -): - """A tool that executes Python code in a code executor and returns output. +class CodeExecutionTool(BaseTool[CodeExecutionInput, CodeExecutionResult], Component[CodeExecutionToolConfig]): + """A tool that executes code in a code executor and returns output. Example executors: @@ -49,11 +48,11 @@ class PythonCodeExecutionTool( from autogen_agentchat.ui import Console from autogen_ext.models.openai import OpenAIChatCompletionClient from autogen_ext.code_executors.local import LocalCommandLineCodeExecutor - from autogen_ext.tools.code_execution import PythonCodeExecutionTool + from autogen_ext.tools.code_execution import CodeExecutionTool async def main() -> None: - tool = PythonCodeExecutionTool(LocalCommandLineCodeExecutor(work_dir="coding")) + tool = CodeExecutionTool(LocalCommandLineCodeExecutor(work_dir="coding")) agent = AssistantAgent( "assistant", OpenAIChatCompletionClient(model="gpt-4o"), tools=[tool], reflect_on_tool_use=True ) @@ -71,26 +70,26 @@ async def main() -> None: executor (CodeExecutor): The code executor that will be used to execute the code blocks. """ - component_config_schema = PythonCodeExecutionToolConfig - component_provider_override = "autogen_ext.tools.code_execution.PythonCodeExecutionTool" + component_config_schema = CodeExecutionToolConfig + component_provider_override = "autogen_ext.tools.code_execution.CodeExecutionTool" def __init__(self, executor: CodeExecutor): - super().__init__(CodeExecutionInput, CodeExecutionResult, "CodeExecutor", "Execute Python code blocks.") + super().__init__(CodeExecutionInput, CodeExecutionResult, "CodeExecutor", "Execute code blocks.") self._executor = executor async def run(self, args: CodeExecutionInput, cancellation_token: CancellationToken) -> CodeExecutionResult: - code_blocks = [CodeBlock(code=args.code, language="python")] + code_blocks = [CodeBlock(code=args.code, language=args.language)] result = await self._executor.execute_code_blocks( code_blocks=code_blocks, cancellation_token=cancellation_token ) return CodeExecutionResult(success=result.exit_code == 0, output=result.output) - def _to_config(self) -> PythonCodeExecutionToolConfig: + def _to_config(self) -> CodeExecutionToolConfig: """Convert current instance to config object""" - return PythonCodeExecutionToolConfig(executor=self._executor.dump_component()) + return CodeExecutionToolConfig(executor=self._executor.dump_component()) @classmethod - def _from_config(cls, config: PythonCodeExecutionToolConfig) -> Self: + def _from_config(cls, config: CodeExecutionToolConfig) -> Self: """Create instance from config object""" executor = CodeExecutor.load_component(config.executor) return cls(executor=executor) diff --git a/python/packages/autogen-ext/tests/tools/test_python_code_executor_tool.py b/python/packages/autogen-ext/tests/tools/test_code_executor_tool.py similarity index 50% rename from python/packages/autogen-ext/tests/tools/test_python_code_executor_tool.py rename to python/packages/autogen-ext/tests/tools/test_code_executor_tool.py index 8ecb1700ef30..b9733c692e50 100644 --- a/python/packages/autogen-ext/tests/tools/test_python_code_executor_tool.py +++ b/python/packages/autogen-ext/tests/tools/test_code_executor_tool.py @@ -4,22 +4,24 @@ import pytest from autogen_core import CancellationToken from autogen_ext.code_executors.local import LocalCommandLineCodeExecutor -from autogen_ext.tools.code_execution import CodeExecutionInput, PythonCodeExecutionTool +from autogen_ext.tools.code_execution import CodeExecutionInput, CodeExecutionTool @pytest.mark.asyncio -async def test_python_code_execution_tool(caplog: pytest.LogCaptureFixture) -> None: - """Test basic functionality of PythonCodeExecutionTool.""" +async def test_code_execution_tool(caplog: pytest.LogCaptureFixture) -> None: + """Test basic functionality of CodeExecutionTool.""" # Create a temporary directory for the executor with tempfile.TemporaryDirectory() as temp_dir: # Initialize the executor and tool executor = LocalCommandLineCodeExecutor(work_dir=temp_dir) - tool = PythonCodeExecutionTool(executor=executor) + tool = CodeExecutionTool(executor=executor) with caplog.at_level(logging.INFO): # Test simple code execution code = "print('hello world!')" - result = await tool.run_json(args={"code": code}, cancellation_token=CancellationToken()) + result = await tool.run_json( + args={"code": code, "language": "python"}, cancellation_token=CancellationToken() + ) # Check log output assert "hello world!" in caplog.text @@ -30,7 +32,9 @@ async def test_python_code_execution_tool(caplog: pytest.LogCaptureFixture) -> N # Test code with computation code = """a = 100 + 200 \nprint(f'Result: {a}') """ - result = await tool.run(args=CodeExecutionInput(code=code), cancellation_token=CancellationToken()) + result = await tool.run( + args=CodeExecutionInput(language="python", code=code), cancellation_token=CancellationToken() + ) # Verify computation result assert result.success is True @@ -38,26 +42,45 @@ async def test_python_code_execution_tool(caplog: pytest.LogCaptureFixture) -> N # Test error handling code = "print(undefined_variable)" - result = await tool.run(args=CodeExecutionInput(code=code), cancellation_token=CancellationToken()) + result = await tool.run( + args=CodeExecutionInput(language="python", code=code), cancellation_token=CancellationToken() + ) # Verify error handling assert result.success is False assert "NameError" in result.output + # Test shell command execution + code = "echo 'hello world!'" + result = await tool.run( + args=CodeExecutionInput(language="sh", code=code), cancellation_token=CancellationToken() + ) -def test_python_code_execution_tool_serialization() -> None: - """Test serialization and deserialization of PythonCodeExecutionTool.""" + assert result.success is True + assert "hello world!" in result.output + + code = "fake_command" + result = await tool.run( + args=CodeExecutionInput(language="sh", code=code), cancellation_token=CancellationToken() + ) + + assert result.success is False + assert "fake_command: not found" in result.output + + +def test_code_execution_tool_serialization() -> None: + """Test serialization and deserialization of CodeExecutionTool.""" with tempfile.TemporaryDirectory() as temp_dir: # Create original tool executor = LocalCommandLineCodeExecutor(work_dir=temp_dir) - original_tool = PythonCodeExecutionTool(executor=executor) + original_tool = CodeExecutionTool(executor=executor) # Serialize config = original_tool.dump_component() assert config.config.get("executor") is not None # Deserialize - loaded_tool = PythonCodeExecutionTool.load_component(config) + loaded_tool = CodeExecutionTool.load_component(config) # Verify the loaded tool has the same configuration - assert isinstance(loaded_tool, PythonCodeExecutionTool) + assert isinstance(loaded_tool, CodeExecutionTool)