@computesdk/modal
v1.8.42
Published
Modal provider for ComputeSDK - serverless Python execution with GPU support and zero cold starts
Maintainers
Readme
@computesdk/modal
Modal provider for ComputeSDK — execute code in serverless Modal sandboxes with optional GPU support.
Installation
npm install @computesdk/modalThis package uses the official Modal JS SDK (v0.7).
Setup
- Get your Modal token from modal.com
- Set the credentials:
export MODAL_TOKEN_ID=your_token_id
export MODAL_TOKEN_SECRET=your_token_secretQuick Start
import { modal } from '@computesdk/modal';
const compute = modal({
tokenId: process.env.MODAL_TOKEN_ID,
tokenSecret: process.env.MODAL_TOKEN_SECRET,
});
const sandbox = await compute.sandbox.create();
const result = await sandbox.runCommand(`python - <<'PY'
import sys
print(f"Python {sys.version}")
PY`);
console.log(result.stdout);
await sandbox.destroy();Configuration
Environment Variables
export MODAL_TOKEN_ID=your_token_id
export MODAL_TOKEN_SECRET=your_token_secretConfiguration Options
interface ModalConfig {
/** Modal token ID - falls back to MODAL_TOKEN_ID env var */
tokenId?: string;
/** Modal token secret - falls back to MODAL_TOKEN_SECRET env var */
tokenSecret?: string;
/** Execution timeout in milliseconds */
timeout?: number;
/** Modal environment name (e.g. 'main', 'sandbox') */
environment?: string;
/** Ports to expose (unencrypted tunnels by default) */
ports?: number[];
/** Modal App name (default: 'computesdk-modal') */
appName?: string;
}Exposing Ports
Ports declared on the provider are exposed as public Modal tunnels and reachable via getUrl():
const compute = modal({
tokenId: process.env.MODAL_TOKEN_ID,
tokenSecret: process.env.MODAL_TOKEN_SECRET,
ports: [3000, 8080],
});
const sandbox = await compute.sandbox.create();
await sandbox.runCommand(`node - <<'JS'
const http = require('http');
http.createServer((req, res) => {
res.end('Hello from Modal sandbox\\n');
}).listen(3000);
JS`, { background: true });
const url = await sandbox.getUrl({ port: 3000 });
console.log(`Server reachable at: ${url}`);Note: Tunnels are unencrypted by default for maximum compatibility.
Features
- ✅ Command Execution — shell commands via
Sandbox.exec() - ✅ Filesystem Operations — read, write, mkdir, ls, rm
- ✅ GPU Support — Modal's native GPU access for ML workloads
- ✅ Serverless Scaling — scale to thousands of concurrent sandboxes
- ✅ Snapshots — save and restore sandbox state
- ❌ Interactive Terminals — not exposed by the provider
API Reference
Command Execution
// Run Python code via heredoc
const result = await sandbox.runCommand(`python - <<'PY'
import torch
print(f"CUDA available: {torch.cuda.is_available()}")
PY`);
// Run shell commands directly
await sandbox.runCommand('pip install numpy');
await sandbox.runCommand('ls -la');
// Background process
await sandbox.runCommand('python server.py', { background: true });
// With env vars and cwd
await sandbox.runCommand('python script.py', {
cwd: '/app',
env: { DEBUG: 'true' },
});Filesystem Operations
await sandbox.filesystem.writeFile('/app/script.py', 'print("hello")');
const content = await sandbox.filesystem.readFile('/app/script.py');
await sandbox.filesystem.mkdir('/app/data');
const entries = await sandbox.filesystem.readdir('/app');
const exists = await sandbox.filesystem.exists('/app/script.py');
await sandbox.filesystem.remove('/app/script.py');Sandbox Management
// Get sandbox info
const info = await sandbox.getInfo();
console.log(info.id, info.provider, info.status);
// Reconnect to existing sandbox
const existing = await compute.sandbox.getById('sandbox-id');
// Destroy sandbox
await sandbox.destroy();Error Handling
import { modal } from '@computesdk/modal';
try {
const compute = modal({
tokenId: process.env.MODAL_TOKEN_ID,
tokenSecret: process.env.MODAL_TOKEN_SECRET,
});
const sandbox = await compute.sandbox.create();
const result = await sandbox.runCommand('python -c "import nonexistent"');
if (result.exitCode !== 0) {
console.error('Command failed:', result.stderr);
}
} catch (error) {
if (error.message.includes('Missing Modal')) {
console.error('Set MODAL_TOKEN_ID and MODAL_TOKEN_SECRET');
} else if (error.message.includes('authentication')) {
console.error('Check your Modal credentials');
}
}Examples
GPU-Accelerated Inference
import { modal } from '@computesdk/modal';
const compute = modal({
tokenId: process.env.MODAL_TOKEN_ID,
tokenSecret: process.env.MODAL_TOKEN_SECRET,
});
const sandbox = await compute.sandbox.create();
const result = await sandbox.runCommand(`python - <<'PY'
import torch
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"Using device: {device}")
x = torch.randn(1000, 1000, device=device)
y = torch.matmul(x, x.T)
print(f"Result shape: {y.shape}")
PY`);
console.log(result.stdout);
await sandbox.destroy();Parallel Task Processing
import { modal } from '@computesdk/modal';
const compute = modal({
tokenId: process.env.MODAL_TOKEN_ID,
tokenSecret: process.env.MODAL_TOKEN_SECRET,
});
const tasks = ['task1.json', 'task2.json', 'task3.json'];
const results = await Promise.all(tasks.map(async (taskFile) => {
const sandbox = await compute.sandbox.create();
try {
return await sandbox.runCommand(`python -c "import json; print(json.dumps({'task': '${taskFile}'}))"`);
} finally {
await sandbox.destroy();
}
}));
console.log(results.map(r => r.stdout));Best Practices
- Resource Management — destroy sandboxes when done; Modal scales but you still pay per use
- Error Handling — check
exitCodefor command failures, catch for SDK/auth errors - GPU Workloads — leverage Modal's GPU offering for ML workloads
- Long Tasks — set generous
timeoutfor training or large-file workflows
Limitations
- Network Access — subject to Modal's network policies
- Billing — pay-per-use Modal pricing applies
- Tunnel Encryption — exposed ports use unencrypted tunnels by default
Support
License
MIT
