{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "39c2dcc0",
   "metadata": {},
   "source": [
    "# Executor: LocalCodeExecutor Basic Examples\n",
    "\n",
    "This notebook shows how to execute Python and shell snippets in **isolated, cached virtual environments**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4ff4b2b",
   "metadata": {},
   "source": [
    "## Install Required Libraries\n",
    "Before starting, ensure the required libraries are installed:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5b41a66a",
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install dapr-agents"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a9c01be3",
   "metadata": {},
   "source": [
    "## Setup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "508fd446",
   "metadata": {},
   "outputs": [],
   "source": [
    "import logging\n",
    "\n",
    "from dapr_agents.executors.local import LocalCodeExecutor\n",
    "from dapr_agents.types.executor import CodeSnippet, ExecutionRequest\n",
    "from rich.console import Console\n",
    "from rich.ansi import AnsiDecoder\n",
    "import shutil"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "27594072",
   "metadata": {},
   "outputs": [],
   "source": [
    "logging.basicConfig(level=logging.INFO)\n",
    "\n",
    "executor = LocalCodeExecutor()\n",
    "console = Console()\n",
    "decoder = AnsiDecoder()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d663475",
   "metadata": {},
   "source": [
    "## Running a basic Python Code Snippet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "ba45ddc8",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:dapr_agents.executors.local:Sandbox backend enabled: seatbelt\n",
      "INFO:dapr_agents.executors.local:Created a new virtual environment\n",
      "INFO:dapr_agents.executors.local:Installing print, rich\n",
      "INFO:dapr_agents.executors.local:Snippet 1 finished in 2.442s\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #008000; text-decoration-color: #008000; font-weight: bold\">Hello executor!</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1;32mHello executor!\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "code = \"\"\"\n",
    "from rich import print\n",
    "print(\"[bold green]Hello executor![/bold green]\")\n",
    "\"\"\"\n",
    "\n",
    "request = ExecutionRequest(snippets=[\n",
    "    CodeSnippet(language='python', code=code, timeout=10)\n",
    "])\n",
    "\n",
    "results = await executor.execute(request)\n",
    "results[0]  # raw result\n",
    "\n",
    "# pretty‑print with Rich\n",
    "console.print(*decoder.decode(results[0].output))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d28c7531",
   "metadata": {},
   "source": [
    "## Run a Shell Snipper"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "4ea89b85",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:dapr_agents.executors.local:Sandbox backend enabled: seatbelt\n",
      "INFO:dapr_agents.executors.local:Snippet 1 finished in 0.019s\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[ExecutionResult(status='success', output='4\\n', exit_code=0)]"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "shell_request = ExecutionRequest(snippets=[\n",
    "    CodeSnippet(language='sh', code='echo $((2+2))', timeout=5)\n",
    "])\n",
    "\n",
    "await executor.execute(shell_request)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "da281b6e",
   "metadata": {},
   "source": [
    "## Reuse the cached virtual environment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "3e9e7e9b",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:dapr_agents.executors.local:Sandbox backend enabled: seatbelt\n",
      "INFO:dapr_agents.executors.local:Reusing cached virtual environment.\n",
      "INFO:dapr_agents.executors.local:Installing print, rich\n",
      "INFO:dapr_agents.executors.local:Snippet 1 finished in 0.297s\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[ExecutionResult(status='success', output='\\x1b[1;32mHello executor!\\x1b[0m\\n', exit_code=0)]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Re‑running the same Python request will reuse the cached venv, so it is faster\n",
    "await executor.execute(request)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "14dc3e4c",
   "metadata": {},
   "source": [
    "## Inject Helper Functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "82f9a168",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:dapr_agents.executors.local:Sandbox backend enabled: seatbelt\n",
      "INFO:dapr_agents.executors.local:Created a new virtual environment\n",
      "INFO:dapr_agents.executors.local:Snippet 1 finished in 1.408s\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[ExecutionResult(status='success', output='42\\n', exit_code=0)]"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def fancy_sum(a: int, b: int) -> int:\n",
    "    return a + b\n",
    "\n",
    "executor.user_functions.append(fancy_sum)\n",
    "\n",
    "helper_request = ExecutionRequest(snippets=[\n",
    "    CodeSnippet(language='python', code='print(fancy_sum(40, 2))', timeout=5)\n",
    "])\n",
    "\n",
    "await executor.execute(helper_request)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25f9718c",
   "metadata": {},
   "source": [
    "## Clean Up"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "b09059f1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cache directory removed ✅\n"
     ]
    }
   ],
   "source": [
    "shutil.rmtree(executor.cache_dir, ignore_errors=True)\n",
    "print(\"Cache directory removed ✅\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c93cdef",
   "metadata": {},
   "source": [
    "## Package-manager detection & automatic bootstrap"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "8691f3e3",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dapr_agents.executors.utils import package_manager as pm\n",
    "import pathlib, tempfile"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9e08d81",
   "metadata": {},
   "source": [
    "### Create a throw-away project"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "4c7dd9c3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tmp project: /var/folders/9z/8xhqw8x1611fcbhzl339yrs40000gn/T/tmpmssk0m2b\n"
     ]
    }
   ],
   "source": [
    "tmp_proj = pathlib.Path(tempfile.mkdtemp())\n",
    "(tmp_proj / \"requirements.txt\").write_text(\"rich==13.7.0\\n\")\n",
    "print(\"tmp project:\", tmp_proj)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03558a95",
   "metadata": {},
   "source": [
    "### Show what the helper detects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "3b5acbfb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "detect_package_managers -> [<PackageManagerType.PIP: 'pip'>]\n",
      "get_install_command    -> pip install -r requirements.txt\n"
     ]
    }
   ],
   "source": [
    "print(\"detect_package_managers ->\",\n",
    "      [m.name for m in pm.detect_package_managers(tmp_proj)])\n",
    "print(\"get_install_command    ->\",\n",
    "      pm.get_install_command(tmp_proj))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42f1ae7c",
   "metadata": {},
   "source": [
    "### Point the executor at that directory"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "81e53cf4",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from contextlib import contextmanager, ExitStack\n",
    "\n",
    "@contextmanager\n",
    "def chdir(path):\n",
    "    \"\"\"\n",
    "    Temporarily change the process CWD to *path*.\n",
    "\n",
    "    Works on every CPython ≥ 3.6 (and PyPy) and restores the old directory\n",
    "    even if an exception is raised inside the block.\n",
    "    \"\"\"\n",
    "    old_cwd = os.getcwd()\n",
    "    os.chdir(path)\n",
    "    try:\n",
    "        yield\n",
    "    finally:\n",
    "        os.chdir(old_cwd)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "fb2f5052",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:dapr_agents.executors.local:bootstrapping python project with 'pip install -r requirements.txt'\n",
      "INFO:dapr_agents.executors.local:Sandbox backend enabled: seatbelt\n",
      "INFO:dapr_agents.executors.local:Created a new virtual environment\n",
      "INFO:dapr_agents.executors.local:Snippet 1 finished in 1.433s\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">bootstrap OK\n",
       "\n",
       "</pre>\n"
      ],
      "text/plain": [
       "bootstrap OK\n",
       "\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "with ExitStack() as stack:\n",
    "    # keep a directory handle open (optional but handy if you’ll delete tmp_proj later)\n",
    "    stack.enter_context(os.scandir(tmp_proj))\n",
    "\n",
    "    # <-- our portable replacement for contextlib.chdir()\n",
    "    stack.enter_context(chdir(tmp_proj))\n",
    "\n",
    "    # run a trivial snippet; executor will bootstrap because it now “sees”\n",
    "    # requirements.txt in the current working directory\n",
    "    out = await executor.execute(\n",
    "        ExecutionRequest(snippets=[\n",
    "            CodeSnippet(language=\"python\", code=\"print('bootstrap OK')\", timeout=5)\n",
    "        ])\n",
    "    )\n",
    "    console.print(out[0].output)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "45de2386",
   "metadata": {},
   "source": [
    "### Clean Up the throw-away project "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "0c7aa010",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cache directory removed ✅\n",
      "temporary project removed ✅\n"
     ]
    }
   ],
   "source": [
    "shutil.rmtree(executor.cache_dir, ignore_errors=True)\n",
    "print(\"Cache directory removed ✅\")\n",
    "shutil.rmtree(tmp_proj, ignore_errors=True)\n",
    "print(\"temporary project removed ✅\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36ea4010",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.13.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
