Build

Developer workspace

Python notebooks at /notebooks that execute on sandboxed server kernels — real CPython with pip and the agentic frameworks pre-installed. Start from read-only framework samples, or write your own and call models and knowledge bases through your account.

Framework samples

The workspace ships with four read-only sample notebooks under Learn by example:

  • LangChain fundamentals — models, prompt templates, output parsers, LCEL chains, tools/agents, memory, and RAG.
  • LangGraph fundamentals — state, nodes, edges, conditional routing, cycles, human-in-the-loop, and multi-agent supervisors.
  • LlamaIndex fundamentals — documents, nodes, indexes, retrievers, and query engines.
  • Mix of all — the agentic stack — using your knowledge base, building tools, composing skills, applying guardrails, reaching MCP servers, and wiring a small multi-agent RAG service.
Every cell is real framework code — actual langchain, langgraph and llama_index imports executing on a container kernel. Model calls go through agentswarms.chat_model(), a genuine LangChain BaseChatModel that routes via the platform, so your IAM model rules, budgets and Traces apply and no provider key ever exists inside the sandbox. All four samples are executed end-to-end in CI-style verification before release.

Read-only & fork

Samples can be run cell-by-cell but not edited. Click Fork to my notebooks to copy a sample into an editable notebook owned by your account — then change anything and it autosaves. Your own notebooks live under My notebooks and are private to you.

How cells run

  • Each code cell is a real editor (CodeMirror) — run it with the play button or Shift+Enter. stdout, the last expression's value, errors, and run duration appear under the cell.
  • Cells execute on a sandboxed container kernel — non-root, read-only root filesystem, capabilities dropped, network egress restricted to an allowlist. The kernel starts on your first run and is reaped when idle.
  • All cells share one interpreter, Jupyter-style: variables defined in one cell are visible in the next, and top-level await is supported.
  • LangChain, LangGraph, LlamaIndex, pandas and numpy are pre-installed; anything else is a !pip install away (writes land in a per-session writable layer).

The agentswarms helper

An agentswarms module is injected into every notebook. It routes calls through your account — your provider keys, your IAM model rules, your budgets, logged in Traces:

  • reply = await agentswarms.chat("…", provider="openrouter", model="openai/gpt-4o-mini") — a chat completion through any OpenAI-compatible provider you've connected under Integrations (openrouter, openai, gemini, groq, grok, qwen, ollama, vllm, nvidia).
  • hits = await agentswarms.kb_search("refund policy", top_k=5) — hybrid (vector + keyword) retrieval over your Knowledge Base. Access is enforced by the same rules as everywhere else: you only search KBs you own, were granted, or that are public samples.
  • kbs = await agentswarms.list_knowledge_bases() — the knowledge bases your account can read.
  • agentswarms.format_context(hits) — turn retrieval results into a numbered context block for a prompt.
  • llm = agentswarms.chat_model(model="openai/gpt-4o-mini") — a real LangChain BaseChatModel. Use it anywhere a LangChain model is accepted: chain = prompt | llm | StrOutputParser(), LangGraph nodes, and so on. It supports tool-calling via llm.bind_tools([...]), so LangGraph's create_react_agent and ToolNode work too.
  • agentswarms.llama_llm(...) and agentswarms.kb_retriever(top_k=4) — a LlamaIndex LLM and a retriever over your Knowledge Base (managed hybrid search, no embedding model to configure).
Each session gets its own container: non-root, read-only root filesystem, all Linux capabilities dropped, no-new-privileges, CPU/memory/PID limits, and outbound network restricted to an operator-managed allowlist. Kernels are ephemeral — files written during a session are discarded on teardown; notebooks themselves live in the database. Operators enable and tune all of this under Admin → Developer runtime.

Where the workspace fits

The Developer workspace is the free-form counterpart to the rest of the platform: sketch a pattern here in Python with real model calls and retrieval, then rebuild it as a durable, deployable system on the Swarm Canvas and in Agent Builder.

There is one runtime, and it is off by default

Notebook cells execute on real container kernels — full CPython, working pip install, and the actual agentic frameworks. There is no in-browser fallback: until an administrator enables the server runtime, opening a notebook shows a "runtime required" panel with the command to run rather than a half-working editor.

bash
docker compose --profile notebooks up -d --build

Then turn it on under Admin → Developer runtime. Enabling it mints the signing secret automatically.

Installing libraries

The kernel image ships build-essential and sets PIP_USER=1 against a writable per-session ~/.local, so packages with C extensions compile and install fine:

python
!pip install polars duckdb scikit-learn

Installs only reach hosts on the egress allow-list

Kernels have no direct internet — their only route out is a default-deny proxy. PyPI is always permitted, so ordinary installs work. Anything that fetches from somewhere else — pip install git+https://github.com/…, a model download from Hugging Face, a private index — fails until an administrator adds that host under Admin → Developer runtime → Egress allow-list. Saving there now writes the list and restarts the proxy, and the UI tells you if either step didn't happen.
Env varValuesPurpose
NOTEBOOK_RUNTIME_ENABLEDon / offMaster switch
NOTEBOOK_RUNTIME_BACKENDdocker | k8s | e2bWhere kernels are launched
NOTEBOOK_RUNTIME_IMAGEimage refKernel image
NOTEBOOK_GATEWAY_URLurlWebsocket gateway the browser connects to
NOTEBOOK_RUNTIME_SECRETsecretSession-token signing key; generated if omitted
NOTEBOOK_CRON_TOKENtokenPresented by the external cron that reaps idle sessions

The docker backend is single-host

It launches kernel containers on the machine the app runs on. To spread kernels across nodes, use the k8s backend.

Kernel network isolation

Kernels sit on an internal network with no direct internet access. Their only route out is a default-deny egress proxy with an allow-list, so a notebook cannot exfiltrate data to an arbitrary host — and a pip install only works for hosts you permit.

Calling your deployed agents and swarms

The helper also reaches the things you have already built, so a notebook can orchestrate them rather than re-implement them:

CallReturns
await agentswarms.chat(prompt, model=…)A raw model completion
await agentswarms.kb_search(query)Knowledge base hits
await agentswarms.list_knowledge_bases()Collections you can read
await agentswarms.run_swarm(swarm_id, input)A saved swarm's final output
await agentswarms.list_agents()Your agents and swarms, with ids
await agentswarms.run_agent(agent_id, prompt)Not available — returns 501. See below.
python
import agentswarms

catalog = await agentswarms.list_agents()
swarm_id = catalog["swarms"][0]["id"]

result = await agentswarms.run_swarm(swarm_id, "Summarise ticket 48213")
print(result)

Why it works this way

run_swarm is not chat with extra steps. It runs what you built exactly as configured, so a notebook gets the same behaviour as the canvas instead of a Python re-implementation that drifts from it. Provider keys never enter the sandbox — the call is brokered by the platform and stays governed by IAM model rules, budgets and traces.

run_agent returns 501, and always has

The kernel authenticates with a runtime session token; the chat route accepts only a user JWT or the internal-run secret, so this call has never succeeded from a notebook. It is listed here because the function exists in the helper and you will find it — not because it works.

To run one saved agent from a notebook, wrap it in a single-node swarm. Importing an agent onto a swarm node copies its prompt, model, tools and knowledge base onto that node, so the run is faithful to the agent. The one difference worth knowing: the copy is a snapshot, so later edits to the agent do not reach a swarm built from it — re-import the node to pick them up.

Approval nodes are rejected, not awaited

A notebook cell is unattended, so a swarm that reaches a human approval gate fails fast rather than hanging until the run timeout.

Note

Sample notebooks ship read-only. Fork one to get an editable copy — that way the originals stay a working reference no matter what you do to your copy.

Publishing a notebook as an API

A notebook can be called by your own systems. Open it and click Publish to mint an API key; anything that can send an HTTP request can then run it.

  • Key name — how you will recognise it later. Mint one per caller so you can revoke a single integration without breaking the others.
  • Entrypoint function — the function called with the request body, defaults to entrypoint. Leave it empty to run the notebook top to bottom and return the last expression instead.

Define the entrypoint in any cell. It receives the JSON you post under inputs and its return value becomes the response:

python
async def entrypoint(inputs):
    date = inputs.get("date")
    hits = await agentswarms.kb_search(f"incidents on {date}", top_k=5)
    summary = await agentswarms.chat(
        f"Summarise these incidents:\n{agentswarms.format_context(hits)}"
    )
    return {"date": date, "summary": summary}
bash
curl -X POST https://your-instance/api/notebook/run \
  -H "Authorization: Bearer nbk_…" \
  -H "Content-Type: application/json" \
  -d '{"inputs": {"date": "2026-07-28"}}'
FieldTypeMeaning
inputsobjectPassed to the entrypoint. Optional — omit for none.
asyncbooleantrue returns 202 with a runId immediately instead of waiting

A synchronous call waits up to 110 seconds. If the run is still going it returns 202 with a runId rather than holding the connection open — poll it the same way an async run is polled:

bash
curl -X POST https://your-instance/api/notebook/run/status \
  -H "Authorization: Bearer nbk_…" \
  -H "Content-Type: application/json" \
  -d '{"runId": "…"}'

Status is queued, running, succeeded (with result) or error (with error).

Why it works this way

The endpoint runs the notebook on the same governed batch kernel a manual run uses — the same sandbox, egress allow-list, IAM model rules, budgets and traces. Publishing changes who can trigger a notebook, not what it is allowed to do.

The key is shown once

Keys are stored as a SHA-256 hash, so the plaintext cannot be recovered — copy it when the dialog shows it. Lost one? Mint a replacement and revoke the old key. Revoking keeps the row (and its last-used timestamp and run count) as an audit trail rather than deleting it, and every run is attributed to the key that started it.

Version control (Git)

Click Version in a notebook to commit it to your GitHub or GitLab repository. It is written as a plain Python file in the widely used percent format — the same # %% cell markers Jupytext, VS Code and PyCharm understand:

python
# %% [markdown]
# ## Load yesterday's incidents

# %%
hits = await agentswarms.kb_search("incidents", top_k=5)
  • Commit writes the notebook plus a small .json manifest recording how it is published — the entrypoint and the key prefixes, never a key itself.
  • History lists every commit with a link to it, and the header says Uncommitted changes or Up to date so you can tell at a glance whether the repo matches what is running.
  • Restore reads the file back at that commit and replaces the notebook's cells. Anything not committed is lost, so it asks first.

Why it works this way

A JSON blob of cells is technically versionable and practically useless — a one-word edit rewrites the whole line and no reviewer can read the diff. Committing real Python means a pull request on a published notebook looks like a pull request on any other code, which is the only reason to put it in git in the first place.

Note

The repository is the same per-user connection BI uses for dashboards and semantic models — connect it from either place. Published notebooks are also included in the bulk Export now sync there. The token is stored encrypted and never read back.