If you’re building AI agents that generate Python code, you’ve probably hit this problem: where does the code actually run?
The usual options:
- E2B — works great, but costs money per execution
- Modal — same deal, server-side, pay per use
- Roll your own — spin up containers, manage infrastructure, cry
What if your agent could just run Python in the browser? No server. No bills. No infra.
That’s why I built sandpy.
What is sandpy?
A browser-native Python sandbox built on Pyodide (Python compiled to WebAssembly). But unlike raw Pyodide, sandpy is designed specifically for AI agent integration:
- Timeouts — prevent infinite loops from crashing the tab
- Streaming output — see results as they print
- Snapshots — s…
If you’re building AI agents that generate Python code, you’ve probably hit this problem: where does the code actually run?
The usual options:
- E2B — works great, but costs money per execution
- Modal — same deal, server-side, pay per use
- Roll your own — spin up containers, manage infrastructure, cry
What if your agent could just run Python in the browser? No server. No bills. No infra.
That’s why I built sandpy.
What is sandpy?
A browser-native Python sandbox built on Pyodide (Python compiled to WebAssembly). But unlike raw Pyodide, sandpy is designed specifically for AI agent integration:
- Timeouts — prevent infinite loops from crashing the tab
- Streaming output — see results as they print
- Snapshots — save and restore Python state across sessions
- Matplotlib capture — auto-capture plots as base64 images
- Vision hook — let GPT-4V describe generated visualizations
- Framework integrations — LangChain and Vercel AI SDK ready
Quick Start
npm install sandpy
import { Sandpy } from 'sandpy'
const sandbox = await Sandpy.create()
const result = await sandbox.run('print(2 + 2)')
console.log(result.stdout) // "4"
That’s it. Python running in the browser.
Using with LangChain
import { createLangChainTool } from 'sandpy'
import { DynamicTool } from 'langchain/tools'
const pythonTool = new DynamicTool(createLangChainTool({
timeout: 30000
}))
// Add to your agent
const agent = new Agent({
tools: [pythonTool]
})
Now your agent can execute Python. If it writes bad code, it times out instead of hanging forever.
Using with Vercel AI SDK
import { createVercelAITool } from 'sandpy'
import { generateText } from 'ai'
const pythonTool = createVercelAITool({ timeout: 30000 })
const result = await generateText({
model: yourModel,
tools: { python: pythonTool },
prompt: 'Calculate the first 20 fibonacci numbers'
})
The Vision Hook (Agents That Can "See")
Here’s where it gets interesting. When your agent generates a matplotlib plot, sandpy captures it as a base64 image. You can then pass that to a vision model:
const result = await sandbox.run(plotCode, {
describeArtifact: async (artifact) => {
const response = await openai.chat.completions.create({
model: 'gpt-4-vision-preview',
messages: [{
role: 'user',
content: [
{ type: 'text', text: 'Describe this chart.' },
{
type: 'image_url',
image_url: {
url: `data:${artifact.type};base64,${artifact.content}`
}
}
]
}]
})
return response.choices[0].message.content
}
})
Now artifact.alt contains something like: "A line chart showing exponential growth from 0 to 100."
Your agent can see its own output and self-correct if the visualization is wrong.
Handling Data with Pandas
const sandbox = await Sandpy.create({ preload: ['pandas'] })
await sandbox.writeFile('/sandbox/data.csv', csvContent)
const result = await sandbox.run(`
import pandas as pd
df = pd.read_csv('/sandbox/data.csv')
print(df.describe())
`)
Files in /sandbox/ persist across page reloads using OPFS (with IndexedDB fallback).
Session Persistence with Snapshots
Users building up state over multiple interactions? Save and restore it:
// User builds state
await sandbox.run('x = 42')
await sandbox.run('data = [1, 2, 3]')
// Save
const snapshot = await sandbox.snapshot()
localStorage.setItem('session', JSON.stringify(snapshot))
// Later (even after refresh)
const saved = JSON.parse(localStorage.getItem('session'))
await sandbox.restore(saved)
await sandbox.run('print(x)') // "42"
Why Browser-Native Matters
| | Server Sandbox | Browser Sandbox | | | ––––––– | ————— | | Cost | ~$0.10/session | Free | | Latency | Network round-trip | Instant | | Privacy | Code goes to their server | Stays in browser | | Offline | No | Yes | | Scale | Pay per use | Unlimited |
For AI agents in consumer apps, browser-native is the right default.
Try It
- Demo: https://sandpy.vercel.app
- GitHub: https://github.com/Raynan00/sandpy
- npm:
npm install sandpy
If you’re building AI agents that need code execution, give it a try. Happy to answer questions in the comments.