Sandbox de código Python para IA

Stateful Python code-interpreter sandboxes for AI agents

O Runbox dá a um agente de IA um interpretador Python isolado e com estado: o usuário anexa uma planilha, o modelo sobe o arquivo, roda pandas/matplotlib em turnos (as variáveis persistem entre execuções) e recebe gráficos e arquivos de volta como URLs públicas — nunca base64 no contexto do modelo.

Runbox gives an AI agent an isolated, stateful Python interpreter: upload the user's spreadsheet, run pandas/matplotlib across turns (variables persist between runs), and get charts/files back as public URLs — never base64 in the model context.

Base: https://runbox.api.insyde.one — autenticada / authenticated.  ·  Playground  ·  llms.txt (cole numa IA / paste into an AI)

1. Autenticação / Authentication

Token por tenant no formato tenant:hash, via header (ou ?key=):

Per-tenant token in tenant:hash form, via header (or ?key=):

Authorization: Bearer cnn:55abc47c…
Isolamento absoluto: um token só enxerga as sessões do próprio tenant — qualquer outra coisa responde 404 uniforme (a existência nunca vaza).
Absolute isolation: a token only sees its own tenant's sessions — anything else is a uniform 404 (existence never leaks).

2. O fluxo do agente / The agent flow

# 1. uma sessão por conversa (estado persiste entre turnos)
POST /sessions                          → { "sessionId": "mdl_sb-…", "expiresAt": "…" }

# 2. anexos do usuário entram no filesystem do sandbox
POST /sessions/{id}/files               → { "path": "/work/data.csv" }

# 3. cada turno roda código; df definido no turno 1 existe no turno 2
POST /sessions/{id}/run                 → stdout, result, error, displays, artifacts

# 4. fim da conversa (opcional — a sessão expira sozinha por idle)
DELETE /sessions/{id}

3. Criar sessão / Create a session

POST /sessions
{ "conversationId": "conv-123", "idleTtlSec": 120, "maxLifetimeSec": 3600 }
Campo / FieldDescrição / Description
idleTtlSecSessão morre após esse tempo sem runs — janela deslizante, cada run renova (60–300, padrão 120). / Sliding idle window, each run renews it.
maxLifetimeSecTeto absoluto (300–7200, padrão 3600). / Hard ceiling.
conversationIdMetadado livre para mapear sessão ↔ conversa. / Free metadata to map session ↔ conversation.

409 se o tenant exceder a quota de sessões simultâneas (padrão 5). / if the tenant exceeds the concurrent-session quota (default 5).

4. Subir arquivos / Upload files

# multipart
curl -X POST .../sessions/{id}/files -H "Authorization: Bearer …" -F "file=@dados.csv"

# ou por URL (o servidor baixa)
{ "url": "https://exemplo.com/dados.xlsx", "name": "dados.xlsx" }

Resposta { "path": "/work/dados.csv" } — use exatamente esse path no código. Máx 25MB. / Use this exact path in code. Max 25MB.

5. Executar código / Run code

POST /sessions/{id}/run
{ "code": "import pandas as pd\ndf = pd.read_csv('/work/dados.csv')\ndf.describe()",
  "timeoutMs": 30000 }

Resposta com semântica Jupyter / Response with Jupyter semantics:

{
  "ok": true,
  "stdout": "…",                  // print(), cap de 64KB (flag truncated)
  "result": "42",                 // repr da ÚLTIMA EXPRESSÃO (ou null)
  "error": null,                  // ou {ename, evalue, traceback[]} estruturado
  "displays": [                   // representações ricas (DataFrame → tabela HTML)
    { "mimeType": "text/html", "data": "<table>…" }
  ],
  "artifacts": [                  // capturados AUTOMATICAMENTE neste run
    { "name": "figure-1-1.png", "mimeType": "image/png",
      "url": "https://runbox.api.insyde.one/a/…", "sizeBytes": 9981 }
  ],
  "durationMs": 776
}
Captura automática: toda figura matplotlib aberta e todo arquivo escrito em outputs/ vira artifact com URL — o modelo não chama savefig(). As URLs são públicas e não-adivinháveis: passe direto pro chat renderizar. Válidas por 7 dias (ou até o DELETE da sessão).
Auto-capture: every open matplotlib figure and every file written to outputs/ becomes an artifact URL — the model never calls savefig(). URLs are public and unguessable: hand them straight to the chat UI. Artifact URLs stay valid for 7 days (or until the session is DELETEd).
Erros são corrigíveis: exceção retorna HTTP 200 com ok:false e traceback limpo em error — o modelo conserta o código e roda de novo na mesma sessão (o estado sobrevive ao erro).
Errors are fixable: exceptions return HTTP 200 with ok:false and a clean traceback in error — the model fixes the code and reruns in the same session (state survives the error).

6. Ambiente do sandbox / Sandbox environment

Pacotes pré-instalados (nome pip → nome de import quando diferem): / Preinstalled packages (pip name → import name where they differ):

Categoria / CategoryPacotes / Packages
Análise de dados / Data analysispandas, numpy, scipy, statsmodels
Gráficos / Plottingmatplotlib, seaborn, squarify (treemaps)
Estilo D3 / Vegaaltair (gramática Vega-Lite) + vl-convert (render offline em SVG/PNG, sem navegador)
Mapas / Geogeopandas, shapely, pyproj, pyogrio (ler shp/geojson/gpkg), mapclassify (faixas de coroplético)
Parlamentoparliamentarch (hemiciclo em SVG), cairosvg (rasterizar SVG→PNG)
Planilhas / Spreadsheetsopenpyxl (ler/escrever xlsx), xlsxwriter (escrever xlsx), pyarrow (parquet/feather)
PDFpdfplumber (extrair texto/tabelas), pypdf (dividir/juntar/metadados), reportlab (gerar PDF diagramado)
Documentos / Documentspython-docxdocx, PillowPIL
Parsingbeautifulsoup4bs4, lxml, unidecode (tirar acento), tabulate (tabela em texto)
Texto & localecharset-normalizercharset_normalizer (detecção de encoding), babel (formatação de número/data/moeda)
HTTPrequests (inutilizável — rede bloqueada / unusable — network blocked)
Gráficos já no padrão insyde.one. O matplotlib vem pré-estilizado com a marca (navy #0f0051, verde #00ff80, azul #3cc5f5, fonte DM Sans, decimais com vírgula pt-BR). É o default — só plote; não chame plt.style.use(...) nem sns.set_theme(). / Charts are pre-styled with the insyde.one brand — it's the matplotlib default, just plot.
Vetor & estilo D3. Para broadcast, salve SVG vetorial e editável: fig.savefig("outputs/grafico.svg") (vira artifact com URL). Para o visual declarativo estilo D3, use Altair — renderiza offline via vl-convert (sem navegador); aplique o tema da marca com uma linha: import insyde_theme e depois chart.save("outputs/c.svg"). / Save vector SVG for broadcast; use Altair (offline via vl-convert) for the D3-style look.
Mapas offline. Malhas de estados e municípios (IBGE) e do mundo (países e continentes) já estão na imagem em /opt/runbox/data/geo/ — leia com geopandas.read_parquet (EPSG:4326): br_estados.parquet, br_municipios.parquet, mundo_paises.parquet, mundo_continentes.parquet. Faça o join dos seus dados na coluna-chave e gdf.plot(column=…, scheme="quantiles", legend=True) para um coroplético.

7. Galeria de exemplos / Example gallery

Tudo abaixo saiu do sandbox sem estilização manual — o padrão insyde.one é o default do matplotlib, e as malhas vêm da imagem. / Everything below came straight from the sandbox with no manual styling — the insyde.one look is the matplotlib default and the boundaries ship in the image.

Gráfico de linha multi-série
Linha multi-série — paleta navy/verde/azul, ticks pt-BR. / Multi-series line.
Barras agrupadas
Barras agrupadas (2 séries). / Grouped bars.
Treemap
squarify treemap.
Coroplético de municípios de SP
645 municípios de SP (IBGE), coroplético quantílico. / SP municipalities choropleth.
Coroplético mundial por país
Mundo por país (Natural Earth). / World choropleth by country.

O coroplético de SP, ponta a ponta: / The SP choropleth, end to end:

import geopandas as gpd
g = gpd.read_parquet("/opt/runbox/data/geo/br_municipios.parquet")
sp = g[g.abbrev_state == "SP"].copy()
sp["idh"] = meus_dados            # join your column here
ax = sp.plot(column="idh", scheme="quantiles", k=5, legend=True,
             edgecolor="white", linewidth=0.15)
ax.set_axis_off()                  # figura capturada automaticamente / auto-captured

8. Altair / Vega-Lite (estilo D3) & hemiciclo

Para o visual declarativo e polido do tipo D3, o sandbox traz Altair (gramática Vega-Lite). Ele renderiza offline via vl-convert (motor em Rust, sem navegador) — então chart.save("outputs/x.png") ou .svg funciona normalmente. Aplique o tema da marca com uma linha: import insyde_theme. / Altair (Vega-Lite) gives the polished, D3-style declarative look, rendered offline via vl-convert. One line for the brand theme: import insyde_theme.

Datasets da galeria oficial. A galeria do Altair usa from altair.datasets import data (Altair 6 — antes era vega_datasets, e os nomes de coluna passaram de under_score para com espaço). Mas altair.datasets baixa os dados da internet — e a sandbox é sem rede, então não funciona aqui. Use o seu próprio DataFrame (o caso real — o usuário sobe a planilha), ou o pacote vega_datasets que traz os clássicos localmente (atenção: colunas com under_score). / The official gallery's altair.datasets fetches over the network and won't work offline — bring your own DataFrame, or use the bundled vega_datasets (underscore column names).
Altair barras
mark_bar() com tema da marca.
Altair linhas
mark_line(point=True) multi-série.
Altair dispersão
mark_circle() por categoria.
Altair heatmap
mark_rect() heatmap (escala viridis).

Um gráfico Altair, ponta a ponta (tema da marca + render offline): / An Altair chart, end to end:

import insyde_theme, altair as alt, pandas as pd
df = pd.read_csv("/work/vendas.csv")         # seu DataFrame / your own data
chart = (alt.Chart(df, title="Receita por praça")
         .mark_bar()
         .encode(x="praca:N", y=alt.Y("valor:Q", title="R$ mi"))
         .properties(width=360, height=240))
chart.save("outputs/receita.png", scale_factor=2)  # ou .svg (vetor) — vira artifact

Hemiciclo / Parliament

Hemiciclo parlamentar
Hemiciclo de 513 cadeiras — matplotlib puro, cores por partido.

Duas vias: parliamentarch (gera o SVG das cadeiras → rasterize com cairosvg.svg2png), ou ~25 linhas de matplotlib (arcos concêntricos de pontos — já sai no padrão da marca, em PNG). / Either parliamentarch (seat SVG + cairosvg) or a small matplotlib version.

9. Demais endpoints / Other endpoints

Rota / RouteDescrição / Description
GET /sessions/{id}Status: {alive, createdAt, expiresAt, conversationId}
GET /sessions/{id}/filesLista arquivos do sandbox (?path= opcional) / list sandbox files
GET /sessions/{id}/files/{path}Baixa um arquivo / download a file
DELETE /sessions/{id}Mata o sandbox e apaga seus artifacts / kill + purge artifacts
GET /a/{…}/{…}/{nome}Público — download de artifact (URL não-adivinhável, cache imutável via CDN)
GET /healthSem auth / no auth

10. Códigos de erro / Error codes

HTTPSignificado / MeaningO que fazer / What to do
401Token ausente/inválido / missing or bad tokenConfira o tenant:hash
404Sessão inexistente, expirada ou de outro tenant / unknown, expired or cross-tenantCrie nova sessão e re-suba os arquivos / create a new session, re-upload files
409Quota de sessões / session quotaReuse ou DELETE uma sessão viva
413Arquivo > 25MB / file too largeReduza o arquivo
422Entrada inválida / bad inputcode obrigatório; só language=python
502/504Falha do provider / run perdidoTente de novo; persistindo, nova sessão / retry; then new session

11. Virando tool de IA / Wiring it as an AI tool

Três funções bastam: runbox_create_session, runbox_upload_url e runbox_run — cada uma é um fetch com o Bearer token. Os schemas completos (OpenAI tools + Anthropic input_schema), prontos para copiar, estão no runbox-llms.txt — feito para colar inteiro no contexto de outra IA.

Three functions are enough — each is one fetch with the Bearer token. The full copy-paste schemas (OpenAI tools + Anthropic input_schema) live in runbox-llms.txt, written to be pasted whole into another AI's context.

// executor mínimo / minimal executor
const BASE = 'https://runbox.api.insyde.one';
const H = { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' };

async function runboxRun(sessionId, code) {
  const r = await fetch(`${BASE}/sessions/${sessionId}/run`,
    { method: 'POST', headers: H, body: JSON.stringify({ code }) });
  return r.json();  // devolva stdout/result/error ao modelo; URLs vão pro chat
}
Boas práticas: uma sessão por conversa · runs pequenos e incrementais (estado persiste) · em 404, recrie a sessão e re-suba os anexos · nunca traga bytes de artifact para o prompt — só as URLs.
Best practices: one session per conversation · small incremental runs · on 404 recreate and re-upload · never pull artifact bytes into the prompt.

Playground · llms.txt · Back to Insyde APIs