--- sidebar_position: 3 title: "Python Code Execution" --- # 🐍 Python Code Execution ## Overview Open WebUI provides two ways to execute Python code: 1. **Manual Code Execution**: Run Python code blocks generated by LLMs using a "Run" button in the browser (uses Pyodide/WebAssembly). 2. **Code Interpreter**: An AI capability that allows models to automatically write and execute Python code as part of their response (uses Pyodide or Jupyter). Both methods support visual outputs like matplotlib charts that can be displayed inline in your chat. When using the Pyodide engine, a **persistent virtual filesystem** at `/mnt/uploads/` is available — files survive across code executions and page reloads, and files attached to messages are automatically placed there for your code to access. ## Code Interpreter Capability The Code Interpreter is a model capability that enables LLMs to write and execute Python code autonomously during a conversation. When enabled, models can: - Perform calculations and data analysis - Generate visualizations (charts, graphs, plots) - Process data dynamically - Execute multi-step computational tasks ### Enabling Code Interpreter **Per-Model Setup (Admin):** 1. Go to **Admin Panel → Models** 2. Select the model you want to configure 3. Under **Capabilities**, enable **Code Interpreter** 4. Save changes **Global Configuration (Admin Panel):** These settings can be configured at **Admin Panel → Settings → Code Execution**: - Enable/disable code interpreter - Select engine: **Pyodide** (recommended) or **Jupyter (Legacy)** - Configure Jupyter connection settings - Set blocked modules **Global Configuration (Environment Variables):** | Variable | Default | Description | |----------|---------|-------------| | `ENABLE_CODE_INTERPRETER` | `true` | Enable/disable code interpreter globally | | `CODE_INTERPRETER_ENGINE` | `pyodide` | Engine to use: `pyodide` (browser, recommended) or `jupyter` (server, legacy) | | `CODE_INTERPRETER_PROMPT_TEMPLATE` | (built-in) | Custom prompt template for code interpreter | | `CODE_INTERPRETER_BLACKLISTED_MODULES` | `""` | Comma-separated list of blocked Python modules | For Jupyter configuration, see the [Jupyter Notebook Integration](/tutorials/integrations/dev-tools/jupyter) tutorial. :::note Filesystem Prompt Injection When the Pyodide engine is selected, Open WebUI automatically appends a filesystem-awareness prompt to the code interpreter instructions. This tells the model about `/mnt/uploads/` and how to discover user-uploaded files. When using Jupyter, this filesystem prompt is not appended (since Jupyter has its own filesystem). You do not need to include filesystem instructions in your custom `CODE_INTERPRETER_PROMPT_TEMPLATE` — they are added automatically. ::: ### Native Function Calling (Native Mode) When using **Native function calling mode** with a capable model (e.g., GPT-5, Claude 4.5, MiniMax M2.5), the code interpreter is available as a builtin tool called `execute_code`. This provides a more integrated experience: - **No XML tags required**: The model calls `execute_code(code)` directly - **Same image handling**: Base64 image URLs in output are replaced with file URLs; model embeds via markdown **Requirements:** 1. `ENABLE_CODE_INTERPRETER` must be enabled globally 2. Model must have `code_interpreter` capability enabled 3. Model must use **Native** function calling mode (set in model's advanced params) For more details on builtin tools and native mode, see the [Tool Development Guide](/features/extensibility/plugin/tools#built-in-system-tools-nativeagentic-mode). ## Displaying Images Inline (matplotlib, etc.) When using matplotlib or other visualization libraries, images can be displayed directly in the chat. For this to work correctly, the code must output the image as a **base64 data URL**. ### Recommended Pattern for matplotlib ```python import matplotlib.pyplot as plt import io import base64 # Create your chart plt.figure(figsize=(10, 6)) plt.bar(['A', 'B', 'C'], [4, 7, 5]) plt.title('Sample Chart') # Output as base64 data URL (triggers automatic upload) buf = io.BytesIO() plt.savefig(buf, format='png', dpi=150, bbox_inches='tight') buf.seek(0) img_base64 = base64.b64encode(buf.read()).decode('utf-8') print(f"data:image/png;base64,{img_base64}") plt.close() ``` ### How Image Display Works 1. The code executes and prints `data:image/png;base64,...` to stdout 2. Open WebUI's middleware detects the base64 image data in the output 3. The image is automatically uploaded and stored as a file 4. The base64 string is **replaced with a file URL** (e.g., `/api/v1/files/{id}/content`) 5. The model sees this URL in the code output and can reference it in its response 6. The image renders inline in the chat :::info Understanding the Flow The model's code should print the base64 data URL. Open WebUI intercepts this and converts it to a permanent file URL. The model should then use this **resulting URL** in markdown like `![Chart](/api/v1/files/abc123/content)` — it should NOT paste the raw base64 string into its response text. If you see raw base64 text appearing in chat responses, the model is incorrectly echoing the base64 instead of using the converted URL from the code output. ::: ### Example Prompt > Create a bar chart showing quarterly sales: Q1: 150, Q2: 230, Q3: 180, Q4: 310. **Expected model behavior:** 1. Model writes Python code using the base64 pattern above 2. Code executes and outputs `data:image/png;base64,...` 3. Open WebUI converts this to a file URL in the output (e.g., `![Output Image](/api/v1/files/abc123/content)`) 4. Model references this URL in its response to display the chart ### Common Issues | Issue | Cause | Solution | |-------|-------|----------| | Raw base64 text appears in chat | Model output the base64 in its response text | Instruct model to only print base64 in code, not repeat it | | Image doesn't display | Code used `plt.show()` without base64 output | Use the base64 pattern above instead | | "Analyzing..." spinner stuck | Code execution timeout or error | Check backend logs for errors | ## Manual Code Execution (Pyodide) Open WebUI includes a browser-based Python environment using [Pyodide](https://pyodide.org/) (WebAssembly). This allows running Python scripts directly in your browser with no server-side setup. The Pyodide worker is **persistent** — it is created once and reused across code executions. This means variables, imported modules, and files written to the virtual filesystem are retained between executions within the same session. ### Running Code Manually 1. Ask an LLM to write Python code 2. A **Run** button appears in the code block 3. Click to execute the code using Pyodide 4. Output appears below the code block ### Supported Libraries Pyodide includes the following packages, which are auto-detected from import statements and loaded on demand: | Package | Use case | |---------|----------| | micropip | Package installer (internal use) | | requests | HTTP requests | | beautifulsoup4 | HTML/XML parsing | | numpy | Numerical computing | | pandas | Data analysis and manipulation | | matplotlib | Chart and plot generation | | seaborn | Statistical data visualization | | scikit-learn | Machine learning | | scipy | Scientific computing | | regex | Advanced regular expressions | | sympy | Symbolic mathematics | | tiktoken | Token counting for LLMs | | pytz | Timezone handling | The Python standard library is also fully available (json, csv, math, datetime, os, io, etc.). :::warning No runtime installation The AI **cannot install additional libraries** beyond the list above. Any code that imports an unsupported package will fail with an import error. Packages that require C extensions, system calls, or native binaries (e.g., torch, tensorflow, opencv, psycopg2) are **not available** and cannot be made available in Pyodide. Pyodide is best suited for **basic file analysis, simple calculations, text processing, and chart generation**. For full Python package access, use **[Open Terminal](/features/chat-conversations/chat-features/code-execution#open-terminal)** instead. ::: ## Persistent File System When using the Pyodide engine, a persistent virtual filesystem is mounted at `/mnt/uploads/`. This filesystem is backed by the browser's IndexedDB via [IDBFS](https://emscripten.org/docs/api_reference/Filesystem-API.html#filesystem-api-idbfs) and provides: - **Cross-execution persistence** — files written by one code execution are accessible in subsequent executions. - **Cross-reload persistence** — files survive page reloads (stored in IndexedDB). - **Automatic upload mounting** — files attached to messages are fetched from the server and placed in `/mnt/uploads/` before code execution, so the model can read them directly. - **File browser panel** — when Code Interpreter is enabled, a file browser appears in the chat controls sidebar. You can browse, preview, upload, download, and delete files — no terminal needed. ### Working with Files in Code ```python import os # List uploaded files print(os.listdir('/mnt/uploads')) # Read a user-uploaded CSV import pandas as pd df = pd.read_csv('/mnt/uploads/data.csv') print(df.head()) # Write output to the persistent filesystem (downloadable via file browser) df.to_csv('/mnt/uploads/result.csv', index=False) print('Saved result.csv to /mnt/uploads/') ``` :::tip The file browser panel lets you download any file the model creates. Ask the model to save its output to `/mnt/uploads/` and it will appear in the file browser for download. ::: :::note Jupyter Engine The persistent filesystem prompt and `/mnt/uploads/` integration are **Pyodide-only**. When using the Jupyter engine, files are managed through Jupyter's own filesystem. The file browser panel is not available for Jupyter. ::: ## Example: Creating a Chart **Prompt:** > "Create a bar chart with matplotlib showing: Acuity 4.1, Signify 7.2, Hubbell 5.6, Legrand 8.9. Output the chart as a base64 data URL so it displays inline." **Expected Code Output:** ```python import matplotlib.pyplot as plt import io import base64 companies = ['Acuity', 'Signify', 'Hubbell', 'Legrand'] values = [4.1, 7.2, 5.6, 8.9] plt.figure(figsize=(10, 6)) bars = plt.bar(companies, values, color=['#3498db', '#2ecc71', '#e74c3c', '#9b59b6']) for bar, value in zip(bars, values): plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.1, str(value), ha='center', va='bottom', fontsize=12) plt.title('Company Values', fontsize=16, fontweight='bold') plt.xlabel('Company', fontsize=12) plt.ylabel('Value', fontsize=12) plt.tight_layout() buf = io.BytesIO() plt.savefig(buf, format='png', dpi=150, bbox_inches='tight') buf.seek(0) print(f"data:image/png;base64,{base64.b64encode(buf.read()).decode()}") plt.close() ``` The image will be automatically uploaded and displayed inline in your chat. ## Browser Compatibility ### Microsoft Edge: Pyodide Crashes If Pyodide-based code execution causes Microsoft Edge to crash with a `STATUS_ACCESS_VIOLATION` error, this is caused by Edge's enhanced security mode. **Symptom:** The browser tab or entire browser crashes when attempting to run Python code, with no useful error message. **Cause:** Edge's "Enhance your security on the web" setting (found at `edge://settings/privacy/security`) enables stricter security mitigations that are incompatible with WebAssembly-based runtimes like Pyodide. **Solutions:** 1. **Disable enhanced security in Edge:** - Go to `edge://settings/privacy/security` - Turn off **"Enhance your security on the web"** 2. **Use a different browser:** - Chrome and Firefox do not have this issue 3. **Use Jupyter backend:** - Switch `CODE_INTERPRETER_ENGINE` to `jupyter` to avoid browser-based execution entirely :::note This is a known compatibility issue between Edge's enhanced security mode and WebAssembly. The same crash occurs on the official [Pyodide console](https://pyodide.org/en/stable/console.html) when this setting is enabled. ::: ## Tips for Better Results - **Mention the environment**: Tell the LLM it's running in a "Pyodide environment" or "code interpreter" for better code generation - **Be explicit about output**: Ask for "base64 data URL output" for images - **Use print statements**: Results must be printed to appear in the output - **Check library support**: Verify the libraries you need are available in Pyodide ## Further Reading - [Pyodide Documentation](https://pyodide.org/en/stable/) - [Jupyter Notebook Integration](/tutorials/integrations/dev-tools/jupyter) - [Environment Configuration](/reference/env-configuration) (search for `CODE_INTERPRETER`)