A local-first autonomous data analysis agent. Ask a real question about your data; it investigates β looking, computing, revising its approach when the data disagrees with it β then verifies the result and explains it, streaming its reasoning as it goes.
Upgrading from w1? See the migration notes β most installs need to change nothing.
Full documentation, including task guides (EDA, model training) and an edge-cases & gotchas reference, lives at wizardw2.vercel.app.
Wizard runs on macOS (Apple Silicon and Intel), Linux (x86-64 and ARM64) and Windows 10/11 (x86-64). Windows on ARM is not supported yet. Nothing below needs administrator rights.
| Platform | Install |
|---|---|
| macOS | brew install Wizard-AIA/wizard/wizard |
| Linux | curl -fsSL https://wizardw2.vercel.app/install.sh | sh |
| Windows (PowerShell) | irm https://wizardw2.vercel.app/install.ps1 | iex |
Then confirm it works and set it up:
wizard --version # wizard CLI v1.0.13, backend API compat v4.0.0
wizard --help
wizard init # choose a provider and models, install what is missing
wizard start # launches Wizard and opens http://localhost:3000Every install method, with options
macOS or Linux, Homebrew (also works on Linux):
brew install Wizard-AIA/wizard/wizardmacOS or Linux, installer script (no Homebrew needed; installs into ~/.wizard):
curl -fsSL https://wizardw2.vercel.app/install.sh | sh
# options: --version 1.0.13 --install-dir DIR --no-modify-path --force --verbose
curl -fsSL https://wizardw2.vercel.app/install.sh | sh -s -- --version 1.0.13 --no-modify-pathWindows, PowerShell 5.1 or 7+ (installs into %LOCALAPPDATA%\Wizard and adds it to your user PATH):
irm https://wizardw2.vercel.app/install.ps1 | iex
# with options:
& ([scriptblock]::Create((irm https://wizardw2.vercel.app/install.ps1))) -Version 1.0.13 -NoModifyPathWindows, Scoop:
scoop install https://github.com/Wizard-AIA/Wizard-w2/releases/latest/download/wizard.jsonAny platform, manual download. Take the archive for your platform from the latest release, check it against SHA256SUMS, extract it, change into the extracted Wizard-v<version>-<platform> directory, and run ./cli/wizard init.
Every option also has an environment variable (WIZARD_VERSION, WIZARD_INSTALL_DIR, WIZARD_NO_MODIFY_PATH), and WIZARD_RELEASE_BASE_URL points the installers at an internal mirror.
Wizard needs Python 3.12+, Node.js 20+, uv and pnpm. Those are minimums: anything newer works, and wizard init uses whatever your machine already has (nvm, fnm, Volta, Homebrew and system installs are all found). It only offers to install what is missing, and then installs the current release. Ollama is needed only if you want local models; cloud-only setups do not need it. Docker is optional.
wizard init walks through the setup on a terminal:
- pick a provider (Ollama, LM Studio, Anthropic, OpenAI, Gemini or a custom gateway) from a list;
- paste an API key if it needs one. Each character shows as
β’, and a receipt (its length and last four characters) proves the paste arrived. The key is never printed, and it is checked with the provider right away; - pick manager and worker models from what that provider actually offers. You are never asked to type a model name or a provider URL;
- everything is a menu: β/β to move, type to jump, Enter to select.
For scripts and CI use wizard init --non-interactive (see wizard init --help); --install-prerequisites lets it install missing tools without asking.
| macOS | Linux | Windows | |
|---|---|---|---|
| Program | ~/.wizard or the Homebrew keg |
~/.wizard |
%LOCALAPPDATA%\Wizard |
| Settings, keys, logs, Python environment | ~/Library/Application Support/Wizard |
~/.config/wizard (or $XDG_CONFIG_HOME) |
%APPDATA%\Wizard |
WIZARD_CONFIG_DIR overrides the settings location. Behind a proxy, the standard HTTPS_PROXY and NO_PROXY variables are honoured by the installers, the updater and model lookups.
| Installed with | Upgrade |
|---|---|
| Homebrew | brew update && brew upgrade wizard, then wizard init (your settings are kept) |
| Installer script or PowerShell | wizard update (wizard update --check only reports) |
| Scoop | scoop update wizard |
wizard update never overwrites files a package manager owns; on a Homebrew or Scoop install it tells you the right command instead.
wizard uninstall # removes the program and its PATH entries; keeps your settings and keys
wizard uninstall --purge # removes everything: program, settings, API keys, logs, Python environmentHomebrew and Scoop installs are removed with brew uninstall wizard / scoop uninstall wizard; wizard uninstall prints the right one. wizard delete removes only your data and leaves the program installed.
Run wizard doctor. It checks the version, platform, where wizard runs from, whether it is on your PATH, the settings directory, Python, Node, uv and pnpm, your configuration, and the running service, and prints a fix for anything that is not a pass (--json for scripts, --network to test connectivity). It exits 3 when something required is missing.
| Symptom | Fix |
|---|---|
wizard: command not found right after installing |
Open a new terminal (the installer edited your shell startup files or user PATH). wizard doctor says exactly what to add. |
checksum mismatch |
The download was corrupted or altered and nothing was installed. Re-run; if it persists, check your proxy. |
another Wizard is already installed |
Two installs shadow each other. Upgrade the existing one, remove it, or pass --force. |
wizard init says Node or pnpm is missing but you have it |
Run wizard doctor: it lists where it looked and why a tool was rejected. |
| Exit codes | 0 ok Β· 1 failure Β· 2 bad usage Β· 3 missing dependency or broken install Β· 4 network |
Integrity. The installers and the updater verify each archive's SHA-256 against the release's SHA256SUMS before unpacking anything. That protects against corrupt or partial downloads; the checksum file comes from the same release, so it is integrity, not a signature.
Building from source is separate from the above; see Contributing & Development at the bottom, or the CLI README.
Wizard runs entirely on your machine. Your data never leaves it, and no API key is required by default β cloud providers are supported too, gated behind an explicit data mode (see below).
You upload a file (or connect a database) and ask a question in plain language. A manager model works out what to do; a worker model writes the Python. The code is statically screened, then executed in its own subprocess under OS-native sandboxing by default (Landlock/seccomp on Linux, sandbox-exec on macOS, a restricted job object on Windows) β or in a Docker container scoped to your session if you opt into that instead.
The important part is what happens next. Rather than following a plan fixed before anything ran, the manager sees the real output and decides what to do next β examine a column, compute something else, consult an attached document, revise the plan outright, or stop and answer. It repeats until it has an answer or runs out of budget. Real analytical questions are not one step; you find out the join key is dirty, or that "active customer" means three different things in three tables, only once you have looked.
Every stage streams to the browser as it happens β the reasoning, each move and what it found, the generated code, the program's stdout, and the answer token by token.
- Local first. Two small Ollama models are enough to be useful. Nothing is sent anywhere.
- You choose the models. Nothing is hardcoded β the app uses whatever your provider actually has, and you can assign a different model, on a different backend, to each role.
- It sizes itself to your hardware. A 1.5B model gets a short leash and deterministic fallbacks; a large model gets a long investigation. Same app, one setting, and
autoworks it out from the model itself. - It runs the code, it doesn't just suggest it. Results come from execution, not from a model claiming an answer.
- It checks its own work. The headline result is recomputed by a different route, and any figure in the answer that appears in no execution output is flagged rather than quietly presented.
- It tells you what it assumed. Dropped nulls, inner joins, top-N cuts β read back out of the code that actually ran, not out of the model's description of it.
- It corrects itself. Failures are fed back with the traceback, and successful repairs are remembered as negative examples for next time.
- Docker is optional, not a fallback. Without it, code runs in a subprocess per session β its own memory ceiling, a per-step timeout, a working Stop button, and variables that persist between steps. Same behaviour, no image to build.
- It fits the machine it is on. Thread count, sandbox memory and the session cap are measured from your CPU and RAM at boot rather than assumed.
- It is honest about degradation. No embedding model? Retrieval falls back to word overlap. Model unreachable? You get a clear message, not a hang. Nothing silently pretends.
After installing, wizard init and wizard start are all you need. Open http://localhost:3000; API docs are at http://localhost:8000/docs.
wizard status # what's running, host sizing, sandbox capability
wizard stop
wizard doctor # if anything looks wrongLocal models need Ollama (or LM Studio); a cloud provider needs only an API key. Docker Desktop is optional, see Docker alternative.
Contributors building from a clone (end users should use the installers above):
git clone https://github.com/Wizard-AIA/Wizard-w2.git && cd Wizard-w2
cd cli && go build -o wizard ./cmd/wizard && cd ..
./cli/wizard init
./cli/wizard startSee cli/README.md for the full subcommand reference. Or do it by hand:
uv pip install --system -r requirements.txt -r requirements-local.txt
cd backend && uvicorn src.api.api:app --port 8000
cd frontend && pnpm install && pnpm devOpen http://localhost:3000. API docs are at http://localhost:8000/docs.
You do not need to install a model first. Go to /models and use Install a model β starter picks are offered per provider, downloads show progress in the page, and nothing sends you to a terminal or the LM Studio window. Ollama models are installed through its own API; LM Studio models through the lms CLI that ships with it.
If you would rather use a terminal, any two models work β a reasoning model and a code model is the useful split, and nothing in the app is tied to a particular pair:
ollama pull qwen3:8b # reasoning
ollama pull qwen2.5-coder:7b # codeOptionally ollama pull embeddinggemma (or nomic-embed-text). Wizard embeds through whichever model server you already run, so this is all that semantic retrieval needs β no extra install, and no GPU libraries. Without one, matching falls back to word overlap.
EXECUTION_BACKEND defaults to host: generated code runs in a subprocess of the backend β a separate process with a memory ceiling, a per-step timeout, an interrupt that works, and a namespace that survives between steps. Docker is opt-in; set EXECUTION_BACKEND=docker to use a container per session instead.
That subprocess is contained by the operating system (HOST_SANDBOX=best-effort): writes are confined to the session workspace, outbound network is denied, and memory and process counts are capped. What your machine can actually enforce differs by platform and is listed on /settings, with a reason for every gap β press Verify there to have it spawn a probe that attempts each forbidden operation rather than take the claim on trust. The static code guard still runs first. Docker remains the strongest option for data or questions you did not write yourself.
Any model you have pulled appears in the picker, and the app picks a sensible one per role on its own β no model name is configured anywhere by default.
A note on model size. The agent decides its own next step each iteration, which is a lot to ask of a very small model. Under 4B parameters it does not ask: it runs a shorter, deterministic loop β write the code, correct it if it fails, answer β with no self-revision and no verification pass. That is three model calls for a question instead of nine, which is the difference between a minute and twenty on a laptop. 7B and up is where the investigation behaviour starts to earn its round-trips. Picking Deep in the composer restores them at any size.
Do not put a reasoning model in the manager role. MODEL_NAME is the model that plans, decides and writes the answer β three to five calls per question. A reasoning model (deepseek-r1, qwq, anything that thinks out loud) spends hundreds to thousands of tokens deliberating before each one, and on a small distill that is minutes per call. Its thinking is stripped correctly and never reaches the answer, but you still pay for it. Use a plain instruct model here β qwen2.5:3b and llama3.2:3b are both good and small. A reasoning model is fine as the WORKER_MODEL_NAME, which is called once per step.
LM Studio works out of the box β no configuration needed if it is on its default port.
- In LM Studio, open Developer and Start Server.
- Turn on Serve on Local Network. LM Studio binds to loopback by default, so the backend container cannot reach it otherwise. (Skip this if you run the backend outside Docker.)
- In the model picker, switch the provider to LM Studio.
The provider is stored per role, so you can leave the reasoning model on Ollama and put only the code model on LM Studio, or vice versa. Models are discovered through LM Studio's native API, which reports quantization, context length and whether a model is currently loaded β an unloaded model is marked, because LM Studio loads it on first use and that can take a while on a laptop.
Any other OpenAI-compatible server (vLLM, llama.cpp, a hosted gateway) works through API_PROVIDER=custom_gateway with GATEWAY_API_URL.
git clone https://github.com/Wizard-AIA/Wizard-w2.git
cd Wizard-w2
docker compose up --build -dOpen http://localhost:3000. API docs are at http://localhost:8000/docs. Already extracted a release zip instead of cloning? The same docker-compose.yml is in it β just run the command from inside the extracted folder.
The sandbox image ships in tiers. standard is the default; pick a smaller one if you are tight on space, and the agent is simply told about a smaller toolkit rather than writing code that then fails to import.
SANDBOX_TIER=core docker compose up --build -d # pandas, numpy, pyarrow, duckdb, matplotlib, openpyxl
SANDBOX_TIER=full docker compose up --build -d # adds survival analysis and geospatialCopy backend/.env.example to backend/.env. Everything has a working default; the values you are most likely to touch:
| Key | Default | Purpose |
|---|---|---|
API_PROVIDER |
ollama |
Default backend: ollama, lmstudio, anthropic, openai, gemini or custom_gateway |
DATA_MODE |
"" (derives) |
local-only, hybrid or cloud-only β what may leave this machine |
MODEL_NAME |
"" |
Pin the reasoning model. Empty = use what the provider has |
WORKER_MODEL_NAME |
"" |
Pin the code model. Empty = use what the provider has |
AGENT_TIER |
auto |
auto, compact, balanced or full β how long an investigation may run |
AGENT_MAX_ITERATIONS |
24 |
Hard ceiling, whatever the tier says |
AGENT_REQUIRE_APPROVAL |
False |
True to approve every plan before it runs |
AGENT_VERIFY |
True |
Recompute the headline result a second way |
AGENT_GROUNDING_CHECK |
True |
Flag figures that appear in no execution output |
CONTEXT_DOCS_ENABLED |
True |
Accept reference documents alongside the data |
OLLAMA_BASE_URL |
http://host.docker.internal:11434 |
Where Ollama lives |
LMSTUDIO_BASE_URL |
http://host.docker.internal:1234 |
Where LM Studio lives (root, not /v1) |
PLOT_FORMAT |
html |
html for interactive Plotly, png for static |
EXECUTION_BACKEND |
host |
host (subprocess, no Docker), docker or inprocess |
SANDBOX_TIER |
standard |
core, standard or full β how much toolkit the image installs |
SYSTEM_PROFILE |
auto |
auto measures the machine; or pin laptop/server/hpc |
HOST_SANDBOX |
best-effort |
off, best-effort or require β OS containment for the host runtime |
HOST_SANDBOX_NETWORK |
deny |
deny blocks outbound traffic from generated code (loopback still allowed) |
SANDBOX_ENABLED |
True |
False disables containers entirely |
SANDBOX_NETWORK_DISABLED |
False |
True is safer, but blocks on-demand package installs |
SANDBOX_DOCKER_RUNTIME |
"" |
Set runsc for gVisor kernel isolation |
CORS_ALLOW_ORIGINS |
http://localhost:3000 |
Comma-separated allowlist |
API_KEY |
"" |
When set, mutating routes require X-API-Key |
EMBEDDING_REMOTE_MODEL |
"" |
Pin the embedding model. Empty = discover one from the provider |
MODEL_MEMORY_FRACTION |
0 |
Share of RAM the models may use. 0 uses 0.60 |
REDIS_URL |
"" |
Empty means in-process cache and queue |
Resource limits β LLM_NUM_THREAD, SANDBOX_MEM_LIMIT, SESSION_MAX_ACTIVE, QUEUE_MAX_WORKERS β are left unset on purpose. They are derived from the machine at boot, and setting one pins it.
The manager and worker alternate several times per question, so what matters is whether both fit in RAM at once. Wizard measures them and decides:
- They fit β both stay resident, so neither is reloaded from disk between steps.
- They do not β each is released after it runs. That costs one reload per step, and avoids two oversized models paging each other to disk, which is one to two orders of magnitude worse and takes the rest of the desktop with it.
Two 7B models want roughly 14 GB; a 16 GB laptop running a browser and a sandbox does not have that. /settings shows the estimate, the budget and which way it went. Using the same model for both roles removes the reload entirely β one resident copy, nothing to evict.
Generated code is untrusted. Three layers apply:
- Static analysis β an AST policy check rejects restricted imports, dynamic execution, interpreter-internals traversal, reflection with computed attribute names, and file access outside the workspace. Malformed code is treated as retryable rather than hostile, so the model gets to fix its own typo.
- OS-level containment β with the default
EXECUTION_BACKEND=host, each session's subprocess is restricted by the operating system: Landlock plus a seccomp filter on Linux, a deny-by-defaultsandbox-execprofile on macOS, a job object and a Low integrity level on Windows. Writes are confined to the session workspace, outbound network is denied (loopback aside), and memory and process counts are capped. What your machine can actually enforce is listed on/settings, with a reason for anything it cannot β outbound network is not enforced on Windows, and it says so. - Process isolation β with
EXECUTION_BACKEND=docker, one container per session withcap_drop=ALL,no-new-privileges, memory and PID limits, and a per-execution timeout; setSANDBOX_DOCKER_RUNTIME=runscfor gVisor. - Scoped filesystem β each session reads and writes only its own workspace directory.
Verify it rather than trust it. /settings has a Verify button β it spawns a probe that tries to write outside the workspace, open an outbound connection and allocate past the ceiling, and reports what stopped each one. GET /api/sandbox/selftest is the same thing from the command line.
Important
The backend mounts the host Docker socket so it can create sandbox containers. That is host-root-equivalent access. Run Wizard on a trusted machine, and set API_KEY and a narrow CORS_ALLOW_ORIGINS before exposing it beyond localhost.
Report vulnerabilities privately β see SECURITY.md.
A question takes many minutes, or never finishes. Four things to check, in order of how much they usually cost:
- Is
MODEL_NAMEa reasoning model? See the note above β this is by far the most common cause.deepseek-r1:1.5bas the manager can spend minutes per call thinking. - Is
LLM_NUM_THREADset inbackend/.env? Delete it. Local inference is memory-bandwidth bound, so more threads than physical cores is contention, not throughput β 8 threads on a 4-core laptop is slower than 4. Unset, it is measured. Same forLLM_NUM_CTX: unset it and the context is sized to the machine, which also stops the provider evicting one model to make room for the other on every step. - Are both models staying loaded?
ollama psduring a run. The manager and worker alternate, so if only one is resident each step is paying a reload from disk. Smaller models, or a smallerLLM_NUM_CTX, fix it. AGENT_TURN_TIMEOUT(default 300s) bounds a turn regardless: on reaching it the agent stops exploring and answers from what it has, and says so. Raise it if you would rather wait.
The backend cannot reach Ollama. If the backend runs outside Docker, leave OLLAMA_BASE_URL unset β the shipped default is rewritten to 127.0.0.1 when the backend is not itself containerised, because host.docker.internal only resolves on machines that have Docker Desktop. Inside compose the file passes the right value itself. On Linux the compose file adds a host-gateway alias; if you still cannot connect, set OLLAMA_BASE_URL=http://172.17.0.1:11434.
Settings shows "Local subprocess" instead of "Docker container". Docker is unreachable, so code is running in a subprocess of the backend. That is a supported mode β bounded, interruptible, and it keeps variables between steps β but it is not isolated from your filesystem. Start Docker Desktop and reload to get a container back.
Settings shows "In-process (no isolation)". Spawning was forbidden. Set EXECUTION_BACKEND=host in backend/.env. Only this mode has no isolation and no persistent namespace.
Retrieval says "Word overlap". No embedding model is installed on your provider. ollama pull embeddinggemma and reload. Nothing breaks without one; matching is just less good at paraphrases.
The model picker is empty. Nothing is installed yet, or the model server is not running. Open /models and use Install a model β there are starter picks per provider, and you do not need a terminal or the LM Studio window. ollama pull qwen3:8b still works if you prefer.
The LM Studio tab is empty but LM Studio is running. Almost always Serve on Local Network being off β with it off LM Studio accepts loopback connections only, and the backend runs in a container. The error under the tab names the exact URL that was tried. Note that LMSTUDIO_BASE_URL wants the root (http://host.docker.internal:1234), not the /v1 endpoint the LM Studio UI displays; a trailing /v1 is stripped for you.
LM Studio answers the first question very slowly. It loads the model on first request. The picker marks models that are not loaded; loading one in LM Studio beforehand avoids the stall.
Analysis keeps failing on the same step. The agent stops after MAX_CORRECTION_RETRIES. The generated code and the traceback are in the "Ran N steps" disclosure β that usually shows a column that does not exist or a type that needs converting first.
A large upload is slow. Files over MAX_INMEMORY_ROWS are sampled for analysis; the full file stays in the workspace and can be read directly in generated code.
Modelling code fails to import xgboost or lightgbm on macOS, host-mode. Both ship compiled libraries that need the OpenMP runtime, which macOS does not include: brew install libomp, then re-run the analysis. This is an xgboost/lightgbm-on-macOS requirement, not a Wizard one β it affects any Python project using them there.
The full developer repository β tests, benchmarks, CI pipelines, and architecture docs β is this same repo: Wizard-AIA/Wizard-w2. See CONTRIBUTING.md for the full workflow and CLAUDE.md for an architecture tour.
graph TD
classDef client fill:#0ea5e9,stroke:#0369a1,stroke-width:2px,color:#fff;
classDef api fill:#10b981,stroke:#047857,stroke-width:2px,color:#fff;
classDef brain fill:#db2777,stroke:#9d174d,stroke-width:2px,color:#fff;
classDef sandbox fill:#f59e0b,stroke:#b45309,stroke-width:2px,color:#000;
classDef store fill:#64748b,stroke:#334155,stroke-width:2px,color:#fff;
UI["Next.js client<br/>(streams every move)"]:::client
WS["FastAPI Β· WS /ws/chat"]:::api
Session["Session<br/>tables Β· documents Β· sandbox"]:::api
Loop["Analysis loop<br/>bounded by the tier budget"]:::api
Manager["Manager model<br/>decide Β· revise Β· answer"]:::brain
Worker["Worker model<br/>Python"]:::brain
Guard["Code guard<br/>AST policy check"]:::sandbox
Box["Per-session container<br/>cap_drop Β· mem/pid limits"]:::sandbox
Trust["Trust layer<br/>verify Β· ground Β· assumptions"]:::store
Store["SQLite<br/>cache Β· trajectories Β· memory"]:::store
Retr["Retriever<br/>columns Β· memory Β· documents"]:::store
UI <-->|typed event frames| WS
WS --> Session --> Loop
Loop <--> Retr <--> Store
Loop -->|"1 what next?"| Manager
Manager -->|"2 inspect / consult"| Retr
Manager -->|"3 write code for this sub-task"| Worker
Worker --> Guard -->|allowed| Box
Box -->|"4 real output"| Loop
Loop -.->|"repeat until answerable"| Manager
Loop -->|5| Trust
Trust -->|"6 synthesise from real output"| Manager
The dotted line is the part that matters. Step 4 feeds back into step 1: the manager sees what the code actually produced and picks the next move from it, so a plan that turns out to be wrong gets rewritten instead of carried out. How many times round that loop is allowed depends on the model β see AGENT_TIER above.
Analysis
- Chooses each next move from real execution output, and revises its plan when the data contradicts it
- Three depths: Auto (it decides), Fast (one pass), Deep (investigate thoroughly)
- Self-corrects on execution failure using the real traceback
- The full analytical stack, not just pandas: duckdb for SQL over dataframes, statsmodels and scipy for inference, scikit-learn/xgboost/lightgbm for modelling, lifelines for survival, networkx for graphs, geopandas for spatial β and the model is told what is actually installed, so a smaller image narrows the toolkit rather than producing code that fails
- Interactive Plotly charts (or static matplotlib, via
PLOT_FORMAT) - Optional plan approval before anything runs, and explicit consent before any web search
Trust
- The headline result is recomputed by a different route, and a disagreement is reported prominently
- Every figure in the answer is traced back to real output; anything that was not computed is flagged
- Silent decisions in the code β dropped nulls, inner joins, top-N cuts, coerced dates β are listed alongside the answer
- Each analysis is written out as a runnable script you can re-run next month against fresh data
Data
- CSV, TSV, Excel, JSON, NDJSON, Parquet and Feather
- Reference documents β data dictionaries, metric definitions, business rules as Markdown, text, PDF or .docx β which the agent consults mid-analysis when a question turns on what a column means
- Large files are sampled for analysis while the full file stays available in the workspace
- Column names are normalised for safe code generation and de-duplicated
- Every loaded table is available to generated code at once as
tables['name'], so cross-table joins need no extra step
Operational
- Per-session isolation: separate dataset, execution namespace, workspace and history
- Two execution backends: a container per session, or a subprocess per session with no Docker at all
- Sizes itself to the host β inference threads from physical cores, runtime memory and the session cap from installed RAM
- Runs without Docker, without an embedding model, and without Redis
- Optional Redis for a shared cache and job state
- Optional
API_KEYfor deployments beyond localhost
Interactive docs: http://localhost:8000/docs.
The session id is returned in the X-Session-Id header and should be sent back on subsequent requests.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/config |
Server capabilities |
POST |
/api/session |
Create a session |
GET |
/api/models |
Models installed on the host |
POST |
/api/models |
Choose models for this session |
POST |
/api/datasets |
Upload a file |
POST |
/api/documents |
Attach a reference document |
DELETE |
/api/documents/{name} |
Remove one |
GET |
/api/data/preview |
Paginated table view |
POST |
/api/chat |
Run a turn, buffered |
WS |
/ws/chat |
Run a turn, streamed |
GET |
/api/workspace/files |
Files this session produced |
GET |
/api/report |
Summary of the session's analyses |
WebSocket frames
Client β server
Server β client
session Β· status Β· step_start Β· step_end Β· reasoning_delta Β· plan_delta Β· content_delta Β· code Β· stdout Β· artifact Β· approval_required Β· warning Β· error Β· final
Reasoning and the final answer arrive as separate delta streams, so the client can render a live "thinking" panel independently of the answer.
uv pip install --system -r requirements.txt # API server
uv pip install --system -r requirements-local.txt # + the analysis toolkit, for running without Docker
cd frontend && pnpm install && cd ..
pytest # 600+ tests; no Docker, model, network or subprocess
ruff check . --fix && ruff format .
cd frontend && pnpm lint && npx tsc --noEmit && pnpm buildTests are organised as unit/, integration/, regression/ and negative/ under backend/tests/. The regression suite pins previously-fixed defects and each test explains what broke β worth reading before changing sessions, the database layer or the code guard.

{ "type": "message", "content": "which day has the highest tips?", "mode": "planning" } { "type": "approval", "approved": true, "tool": "execute_plan", "content": "...", "plan": "..." } { "type": "cancel" } { "type": "ping" }