MUST be used when adding Pyodide or Python tool support to a Flows app. Do NOT manually configure usePyodideRuntime or wire pythonRuntime into useAtlasChat — this skill handles pyodide installation, hook setup, loading UI, and chat hook wiring. Prerequisite: integrate-atlas-chat (vendored src/atlas-agent + atlas chat wiring). Triggers: Pyodide, Python tools, pythonRuntime, usePyodideRuntime, runPythonCode, Python execution, client-side Python.
---
name: setup-python-tools
description: "MUST be used when adding Pyodide or Python tool support to a Flows app. Do NOT manually configure usePyodideRuntime or wire pythonRuntime into useAtlasChat — this skill handles pyodide installation, hook setup, loading UI, and chat hook wiring. Prerequisite: integrate-atlas-chat (vendored src/atlas-agent + atlas chat wiring). Triggers: Pyodide, Python tools, pythonRuntime, usePyodideRuntime, runPythonCode, Python execution, client-side Python."
allowed-tools: Read, Glob, Grep, Edit, Write, Bash
metadata:
argument-hint: "[tool-names or agent-external-id]"
---
# Set Up Python Tool Execution
Add client-side Python tool execution via Pyodide to this Flows app.
Target: **$ARGUMENTS**
## Prerequisite
**`integrate-atlas-chat`** must already be complete: the app should have vendored atlas-agent code under `src/atlas-agent/` (including `react.ts` for `useAtlasChat`) and the peer dependencies from that skill (`@sinclair/typebox`, `ajv`, `ajv-formats`). Copy the Python-related modules from the **`integrate-atlas-chat`** skill `code/` directory into `src/atlas-agent/` when adding Pyodide (`python.ts`, `pyodide.ts`, `pyodide-react.ts`, `pyodide-runtime.ts` — see **`integrate-atlas-chat`** Step 5).
## Background
Atlas agents can have Python tools defined in their CDF config (`type: "runPythonCode"`).
When the agent calls one, it arrives as a `toolConfirmation` (auto-allowed) followed by a
`clientTool` action. The library fetches the tool's Python code from the agent config
automatically and executes it via the provided `pythonRuntime`.
You only need to:
1. Set up `usePyodideRuntime` to get a runtime instance
2. Pass `pythonRuntime` to `useAtlasChat`
No `PythonToolConfig` entries — the library reads the code from the agent's CDF config.
The flow is:
1. `usePyodideRuntime` loads Pyodide (~30MB, cached after first load), installs packages,
and injects Cognite SDK credentials into the Python environment
2. When the agent calls a Python tool, the library fetches its code from the agent's CDF
config (cached per session), wraps it, executes it in Pyodide, and returns the result
---
## Step 1 — Understand the app
Read these files before touching anything:
- `package.json` — detect package manager and existing deps
- The component that calls `useAtlasChat` — understand current tools/config
---
## Step 2 — Install Pyodide
Install **exactly** `pyodide@0.29.3` using the app's package manager.
This version must match the CDN artifacts loaded at runtime — installing a different version will cause errors.
- pnpm → `pnpm add pyodide@0.29.3`
- npm → `npm install pyodide@0.29.3`
- yarn → `yarn add pyodide@0.29.3`
> **Note**: After **`integrate-atlas-chat`**, `@sinclair/typebox`, `ajv`, and `ajv-formats` should
> already be installed. If anything is missing, install the versions listed in that skill's **Dependencies** table.
---
## Step 3 — Set up usePyodideRuntime
In the component that calls `useAtlasChat`, add the Pyodide runtime hook:
```tsx
import { loadPyodide } from "pyodide";
import { usePyodideRuntime } from "./atlas-agent/pyodide-react";
import { useAtlasChat } from "./atlas-agent/react";
function MyChat() {
const { sdk, isLoading } = useDune();
// Initialize Python runtime (loads Pyodide, installs packages, sets up Cognite SDK)
const {
runtime: pythonRuntime,
loading: pythonLoading,
progress: pythonProgress,
error: pythonError,
isReady: pythonReady,
} = usePyodideRuntime({
loadPyodide,
client: isLoading ? null : sdk,
requirements: ["pandas", "numpy"], // optional — additional packages
});
// ... useAtlasChat below
}
```
### Hook API reference
| Return field | Type | Description |
|---|---|---|
| `runtime` | `PythonRuntime \| undefined` | The initialized runtime, or undefined if not ready |
| `loading` | `boolean` | True while Pyodide is loading / initializing |
| `error` | `string \| null` | Error message if initialization failed |
| `progress` | `{ stage: string; percent: number }` | Current init progress for UI display |
| `isReady` | `boolean` | Convenience: `!loading && !error && runtime !== undefined` |
### Loading state UI
Place the loading indicator **above the chat input**, not in the message list.
Keep it compact — a pill/badge showing stage text and percent. Show an error badge separately.
First load is ~30-60s (downloads ~30MB); subsequent loads are <2s from browser cache.
```tsx
{/* Loading — shown above the input while Pyodide initializes */}
{pythonLoading && (
<div className="flex items-center gap-2 rounded-lg border bg-muted/50 px-3 py-2 text-sm text-muted-foreground">
{/* Optional: <IconBrandPython /> from @tabler/icons-react */}
<span>{pythonProgress.stage || "Initializing Python..."}</span>
{pythonProgress.percent > 0 && pythonProgress.percent < 100 && (
<span className="text-xs opacity-70">({pythonProgress.percent}%)</span>
)}
</div>
)}
{/* Error — shown if init fails (after loading finishes) */}
{pythonError && !pythonLoading && (
<div className="flex items-center gap-2 rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive">
<span>Python runtime failed to load</span>
</div>
)}
```
---
## Step 4 — Wire into useAtlasChat
Pass the runtime to `useAtlasChat`. That's all — no tool configs needed:
```tsx
const { messages, send, isStreaming, progress, error, reset, abort } = useAtlasChat({
client: isLoading ? null : sdk,
agentExternalId: "my-agent",
tools: [renderTimeSeries], // regular client tools (declared to agent), if any
pythonRuntime, // from usePyodideRuntime — enables Python tool execution
});
```
**Note**: Python tools are NOT declared to the agent via `tools`. The agent already knows
about them from its CDF config. The library fetches the code automatically when needed.
---
## Step 5 — Disable input while Python loads
The user shouldn't send messages before the runtime is ready. Disable the **entire input area**
(not just the send button) so the state is unambiguous:
```tsx
<ChatInput
onSend={handleSend}
disabled={isStreaming || pythonLoading}
// ...
/>
```
If you have a home page with suggestion chips, disable those too:
```tsx
<ChatHomePage
onSuggestionClick={handleSuggestionClick}
disabled={pythonLoading}
/>
```
---
## Done
The app can now execute Python tools client-side via Pyodide. When the agent calls a Python
tool, the library automatically fetches its code from the agent config, runs it in the
browser, and returns the result to the agent.
Creator's repository · cognitedata/builder-skills