{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Command Line Code Executors\n", "\n", "Command line code execution is the simplest form of code execution.\n", "Generally speaking, it will save each code block to a file and then execute that file.\n", "This means that each code block is executed in a new process. There are two forms of this executor:\n", "\n", "- Docker ({py:class}`~autogen_ext.code_executors.docker.DockerCommandLineCodeExecutor`) - this is where all commands are executed in a Docker container\n", "- Local ({py:class}`~autogen_ext.code_executors.local.LocalCommandLineCodeExecutor`) - this is where all commands are executed on the host machine\n", "\n", "## Docker\n", "\n", "```{note}\n", "To use {py:class}`~autogen_ext.code_executors.docker.DockerCommandLineCodeExecutor`, ensure the `autogen-ext[docker]` package is installed. For more details, see the [Packages Documentation](https://microsoft.github.io/autogen/dev/packages/index.html).\n", "\n", "```\n", "\n", "The {py:class}`~autogen_ext.code_executors.docker.DockerCommandLineCodeExecutor` will create a Docker container and run all commands within that container. \n", "The default image that is used is `python:3-slim`, this can be customized by passing the `image` parameter to the constructor. \n", "If the image is not found locally then the class will try to pull it. \n", "Therefore, having built the image locally is enough. The only thing required for this image to be compatible with the executor is to have `sh` and `python` installed. \n", "Therefore, creating a custom image is a simple and effective way to ensure required system dependencies are available.\n", "\n", "You can use the executor as a context manager to ensure the container is cleaned up after use. \n", "Otherwise, the `atexit` module will be used to stop the container when the program exits.\n", "\n", "### Inspecting the container\n", "\n", "If you wish to keep the container around after AutoGen is finished using it for whatever reason (e.g. to inspect the container), \n", "then you can set the `auto_remove` parameter to `False` when creating the executor. \n", "`stop_container` can also be set to `False` to prevent the container from being stopped at the end of the execution.\n", "\n", "### Example" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CommandLineCodeResult(exit_code=0, output='Hello, World!\\n', code_file='coding/tmp_code_07da107bb575cc4e02b0e1d6d99cc204.python')\n" ] } ], "source": [ "from pathlib import Path\n", "\n", "from autogen_core import CancellationToken\n", "from autogen_core.code_executor import CodeBlock\n", "from autogen_ext.code_executors.docker import DockerCommandLineCodeExecutor\n", "\n", "work_dir = Path(\"coding\")\n", "work_dir.mkdir(exist_ok=True)\n", "\n", "async with DockerCommandLineCodeExecutor(work_dir=work_dir) as executor: # type: ignore\n", " print(\n", " await executor.execute_code_blocks(\n", " code_blocks=[\n", " CodeBlock(language=\"python\", code=\"print('Hello, World!')\"),\n", " ],\n", " cancellation_token=CancellationToken(),\n", " )\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Combining an Application in Docker with a Docker based executor\n", "\n", "It is desirable to bundle your application into a Docker image. But then, how do you allow your containerised application to execute code in a different container?\n", "\n", "The recommended approach to this is called \"Docker out of Docker\", where the Docker socket is mounted to the main AutoGen container, so that it can spawn and control \"sibling\" containers on the host. This is better than what is called \"Docker in Docker\", where the main container runs a Docker daemon and spawns containers within itself. You can read more about this [here](https://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/).\n", "\n", "To do this you would need to mount the Docker socket into the container running your application. This can be done by adding the following to the `docker run` command:\n", "\n", "```bash\n", "-v /var/run/docker.sock:/var/run/docker.sock\n", "```\n", "\n", "This will allow your application's container to spawn and control sibling containers on the host.\n", "\n", "If you need to bind a working directory to the application's container but the directory belongs to your host machine, \n", "use the `bind_dir` parameter. This will allow the application's container to bind the *host* directory to the new spawned containers and allow it to access the files within the said directory. If the `bind_dir` is not specified, it will fallback to `work_dir`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Local\n", "\n", "```{attention}\n", "The local version will run code on your local system. Use it with caution.\n", "```\n", "\n", "To execute code on the host machine, as in the machine running your application, {py:class}`~autogen_ext.code_executors.local.LocalCommandLineCodeExecutor` can be used.\n", "\n", "### Example" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CommandLineCodeResult(exit_code=0, output='Hello, World!\\n', code_file='/home/ekzhu/agnext/python/packages/autogen-core/docs/src/guides/coding/tmp_code_07da107bb575cc4e02b0e1d6d99cc204.py')\n" ] } ], "source": [ "from pathlib import Path\n", "\n", "from autogen_core import CancellationToken\n", "from autogen_core.code_executor import CodeBlock\n", "from autogen_ext.code_executors.local import LocalCommandLineCodeExecutor\n", "\n", "work_dir = Path(\"coding\")\n", "work_dir.mkdir(exist_ok=True)\n", "\n", "local_executor = LocalCommandLineCodeExecutor(work_dir=work_dir)\n", "print(\n", " await local_executor.execute_code_blocks(\n", " code_blocks=[\n", " CodeBlock(language=\"python\", code=\"print('Hello, World!')\"),\n", " ],\n", " cancellation_token=CancellationToken(),\n", " )\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Local within a Virtual Environment\n", "\n", "If you want the code to run within a virtual environment created as part of the application’s setup, you can specify a directory for the newly created environment and pass its context to {py:class}`~autogen_ext.code_executors.local.LocalCommandLineCodeExecutor`. This setup allows the executor to use the specified virtual environment consistently throughout the application's lifetime, ensuring isolated dependencies and a controlled runtime environment." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "CommandLineCodeResult(exit_code=0, output='', code_file='/Users/gziz/Dev/autogen/python/packages/autogen-core/docs/src/user-guide/core-user-guide/framework/coding/tmp_code_d2a7db48799db3cc785156a11a38822a45c19f3956f02ec69b92e4169ecbf2ca.bash')" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import venv\n", "from pathlib import Path\n", "\n", "from autogen_core import CancellationToken\n", "from autogen_core.code_executor import CodeBlock\n", "from autogen_ext.code_executors.local import LocalCommandLineCodeExecutor\n", "\n", "work_dir = Path(\"coding\")\n", "work_dir.mkdir(exist_ok=True)\n", "\n", "venv_dir = work_dir / \".venv\"\n", "venv_builder = venv.EnvBuilder(with_pip=True)\n", "venv_builder.create(venv_dir)\n", "venv_context = venv_builder.ensure_directories(venv_dir)\n", "\n", "local_executor = LocalCommandLineCodeExecutor(work_dir=work_dir, virtual_env_context=venv_context)\n", "await local_executor.execute_code_blocks(\n", " code_blocks=[\n", " CodeBlock(language=\"bash\", code=\"pip install matplotlib\"),\n", " ],\n", " cancellation_token=CancellationToken(),\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As we can see, the code has executed successfully, and the installation has been isolated to the newly created virtual environment, without affecting our global environment." ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.5" } }, "nbformat": 4, "nbformat_minor": 2 }