HTTP API¶
neurondump web serves a JSON API under /api. Every call needs the session token printed at
startup, in the x-neurondump-token header, or as ?token= for links and Server-Sent Events
(which cannot set headers). Without it: 401 {"error": "missing or invalid session token"}.
T=<token>; H="x-neurondump-token: $T"; U=http://127.0.0.1:7878/api
M=17d2889ccc34 # model id of llama3.2:1b (ids come from /api/models)
curl -s -H "$H" $U/info
Models are referred to by id, never by path. Errors are {"error": "…"} with 400 (bad request),
404 (unknown model / node / job), 409 (job not finished) or 500.
Server and models¶
| Method | Path | Returns |
|---|---|---|
| GET | /api/info |
{"version","schema_version","output_dir","trace_architectures":[…],"job_kinds":[…]} |
| GET | /api/models |
{"models":[{id,name,source,architecture,params,quantization,size,…}],"warnings":[]} |
| POST | /api/models/rescan |
{"models": 9} |
| GET | /api/models/{id} |
overview: model, file, architecture, tokenizer, Ollama layers, projector, warnings |
| GET | /api/models/{id}/metadata?full= |
metadata keys (full=true: arrays not summarized) |
| GET | /api/models/{id}/vocab?q=®ex=&class=&script=&lang=&offset=&limit= |
{"summary", "page": {"total","tokens":[…]}} |
| GET | /api/models/{id}/vocab/texts |
{"texts":["!", "\"", …], "classes":["special",…]}: every token's decoded text by id |
| GET | /api/models/{id}/tensors?layer=&role=&q=&sort=&offset=&limit= |
{"total","tensors":[…]} |
| GET | /api/models/{id}/layers |
per-layer kinds |
| GET | /api/models/{id}/template |
template decision trees |
| GET | /api/models/{id}/tensors/{name}/stats |
statistics of one tensor |
| GET | /api/search?q=&model=&limit= |
models, plus tokens / tensors / metadata of model |
| GET | /api/compare?models=a,b |
comparison report |
| GET | /api/export/{id}?format=json\|csv\|ndjson&table= |
download |
Graph¶
| Method | Path | |
|---|---|---|
| GET | /api/models/{id}/graph?level=&group_by=&expand= |
the graph view: {"nodes":[…],"edges":[…],"level","group_by","expand"}. Layouts are computed by the browser. |
| GET | /api/models/{id}/nodes/{node} |
{"node","ancestors","children","incoming","outgoing"} |
| POST | /api/models/{id}/impact |
static impact of a cut |
| POST | /api/models/{id}/plan-from-ablation |
a prune plan from a cut or a sweep job |
curl -s -H "$H" "$U/models/$M/graph?level=layer&expand=layer.7,layer.7.attn"
curl -s -H "$H" -X POST -H "content-type: application/json" -d '{"cut":["layer.7"],"force":false}' $U/models/$M/impact
# {"verdict":"risky","params_removed":60817408,"new_size":1256447808,"llama_cpp_loads":true,…}
Token inspector¶
POST /api/models/{id}/tokenize¶
Static (no inference, any model).
curl -s -H "$H" -X POST -H "content-type: application/json" \
-d '{"text":"fn main() {","chat":true,"system":null,"neighbors":5}' $U/models/$M/tokenize
{
"schema_version": 1, "model": "llama3.2:1b", "tokenizer": "gpt2",
"input": "fn main() {",
"chat": { "source": "ollama", "system": null, "text": "<|start_header_id|>system<|end_header_id|>…", "prompt_span": [96, 107], "template": "…" },
"full_text": "<|start_header_id|>system<|end_header_id|>…",
"tokens": [
{ "index": 0, "id": 128000, "raw": "<|begin_of_text|>", "text": "<|begin_of_text|>", "class": "special", "token_type": "control",
"special": true, "added": true, "embedding_norm": 0.43, "output_norm": 0.43, "in_output": true, "banned": false, "neighbors": [] },
{ "index": 12, "id": 8998, "raw": "fn", "text": "fn", "class": "code", "special": false, "added": false, "span": [96, 98],
"embedding_norm": 0.51, "in_output": true, "neighbors": [ { "id": 5079, "text": "func", "cosine": 0.41 } ] }
],
"tied_embeddings": true, "has_output_head": true, "embedding_tensor": "token_embd.weight", "warnings": [], "elapsed_ms": 812
}
(Values are illustrative.) chat wraps the text in the model's chat template (Ollama's, else a
built-in one for llama3 / chatml / gemma / phi3 families); added marks tokens that come from the
template or the tokenizer (BOS); banned reflects ban edits on the stack.
GET /api/models/{id}/embedding-map¶
The vocabulary projected to 3D with PCA. Binary application/octet-stream: little-endian float32,
4 values per token (x, y, z, class), token id = index. Headers: x-neurondump-count,
x-neurondump-stride: 4, x-neurondump-explained (variance share of the 3 axes). The first call
computes it (seconds); later calls come from the cache.
curl -s -D - -o map.bin -H "$H" $U/models/$M/embedding-map | grep -i x-neurondump
curl -s -H "$H" "$U/models/$M/embedding-map?format=json"
# {"count":128256,"dim":2048,"sample":16384,"tensor":"token_embd.weight","explained":[…],"stride":4,"classes":["special",…]}
Edits¶
All edit endpoints return the edit view:
{
"model": "llama3.2:1b", "digest": "…",
"edits": [ { "id": 1, "enabled": true, "label": "zero layer.7", "created": 1792000000, "op": "zero", "target": "layer.7" } ],
"can_undo": true, "can_redo": false, "active": 1,
"compiled": { "intervention": { "skip_layers": [7], … }, "sampling": null, "system": null, "template": null, "touched": ["layer.7"], "notes": [], "active": 1 },
"error": null,
"snapshots": ["formal-steer"]
}
| Method | Path | Body | |
|---|---|---|---|
| GET | /api/models/{id}/edits |
the stack | |
| POST | /api/models/{id}/edits |
an op, e.g. {"op":"scale","target":"layer.7.attn.head.3","factor":0} (optional label) |
push an edit |
| POST | /api/models/{id}/edits |
{"schema_version":1,"edits":[…]} (an edits.json) |
replace the stack (import; undoable). The view gets warnings when the file was made for another model |
| DELETE | /api/models/{id}/edits |
restore all (back to the original model; undoable) | |
| DELETE | /api/models/{id}/edits/{n} |
remove edit n |
|
| POST | /api/models/{id}/edits/{n}/toggle |
switch edit n on / off |
|
| POST | /api/models/{id}/edits/undo |
undo ("changed": false when there is nothing to undo) |
|
| POST | /api/models/{id}/edits/redo |
redo | |
| POST | /api/models/{id}/edits/restore |
restore all | |
| GET | /api/models/{id}/edits/export |
download <model>.edits.json |
|
| GET | /api/models/{id}/snapshots |
{"snapshots":[…]} |
|
| POST | /api/models/{id}/snapshots |
{"name":"formal","action":"save"} or {"name":"formal","action":"load"} |
save / load a named snapshot |
| POST | /api/models/{id}/bake |
{"out":"llama-edited.gguf"} |
starts a bake job → 202 {"id":"bake-…"}; written to <output_dir>/bake/ |
Ops and their fields are listed in Interventions. A push that does not compile (unknown node, token out of range, a bad template) returns 400 and leaves the stack as it was.
curl -s -H "$H" -X POST -H "content-type: application/json" -d '{"op":"zero","target":"layer.7"}' $U/models/$M/edits
curl -s -H "$H" -X POST $U/models/$M/edits/undo
curl -s -H "$H" -X POST -H "content-type: application/json" \
-d '{"op":"steer","layer":6,"a":"Dear Sir or Madam, I am writing to formally request","b":"hey dude, just wanna ask","strength":4}' $U/models/$M/edits
curl -s -H "$H" -X POST $U/models/$M/edits/1/toggle
curl -s -H "$H" -X DELETE $U/models/$M/edits
Jobs¶
| Method | Path | |
|---|---|---|
| POST | /api/jobs |
{"kind","model":"<id>","params":{…}} → 202 {"id"} |
| GET | /api/jobs |
every job |
| GET | /api/jobs/{id} |
one job: status, progress, step, error, cached |
| DELETE | /api/jobs/{id} |
cancel → {"cancelling": true} |
| GET | /api/jobs/{id}/events |
Server-Sent Events: progress …, then done |
| GET | /api/jobs/{id}/result |
the result JSON (409 while running) |
Kinds: inspect, stats, analyze, slim, trace, ablate, sweep, journey, generate,
compare, bake. The API never accepts server file paths: corpus files are stripped (send texts),
slim / bake outputs must be plain file names.
journey, generate and compare use the model's current edit stack unless params.edits is
given (a list of edits; [] = none); journey and generate also take "baseline": true to ignore
the edits. The edits are part of the cache key.
journey¶
{ "kind": "journey", "model": "<id>", "params": { "prompt": "fn main() {", "chat": false, "token": -1, "top_k": 5 } }
token is the position (negative counts from the end); append adds token ids after the prompt
(the timeline uses it for generated tokens). Result:
{
"tokens": [ { "id": 128000, "text": "<|begin_of_text|>", "special": true }, … ],
"position": 4, "next": null,
"prediction": [ { "id": 198, "text": "\n", "prob": 0.61 }, … ],
"first_top1": 12, "first_top5": 9,
"layers": [ { "index": 0, "residual_norm": 7.4, "lens": […], "target_prob": 0.0, "target_rank": 5012,
"attention": [[…per key…] …per head…], "attended": [[0, 0.71], …],
"ffn_buckets": [["layer.0.ffn.n.512-575", 0.21], …], "top_neurons": [[1031, 2.9], …], "skipped": false } ],
"decision": { "chosen": {…}, "runner_up": {…}, "logit_diff": 1.9, "attributed": 1.8, "embedding": 0.02,
"layers": [ { "layer": 0, "attn": 0.01, "ffn": -0.03 }, … ],
"top_heads": [ { "layer": 13, "head": 7, "value": 0.42 }, … ],
"top_neurons": [ { "layer": 15, "neuron": 4012, "activation": 3.1, "value": 0.31 }, … ] },
"highlight": ["layer.13.attn.head.7", "layer.15", "layer.15.ffn", …],
"edited": false, "warnings": [], "elapsed_ms": 4200
}
generate¶
{ "kind": "generate", "model": "<id>", "params": { "prompt": "The name of the star that Earth orbits is", "steps": 12, "explain": false,
"temperature": 0.8, "sample_top_k": 40, "top_p": 0.9, "seed": 1 } }
Result: steps: [{index, position, token:{id,text,prob}, entropy, alternatives:[…], forced, decision?}],
generated, generated_ids, sampling, tokens_per_s. explain: true adds a decision breakdown to
every step.
compare¶
{ "kind": "compare", "model": "<id>", "params": { "prompt": "The name of the star that Earth orbits is", "tokens": 12, "corpus": ["Paris is the capital of France."] } }
Result: {"edits":[…], "compiled":{…}, "simulation": SimReport}. Each simulation.prompts[] entry has
baseline / ablated (= edited) sides with top_k and generated, kl, top1_changed, lens
(logit lens per layer), edited_prompt (when a prompt edit changed the text) and
decision_baseline / decision_edited; simulation.corpus has the perplexities and mean KL.
bake¶
{"out": "name.gguf"} (plus optional edits). Result: output, modelfile, original_size,
new_size, applied, skipped (with reasons), tensors_changed, requantized, notes, validation.
Older kinds¶
ablate: {"cut":["layer.7"],"prompt":"…","corpus":[…],"tokens":12,"force":false,"static":false};
sweep: {"level":"layer"|"head","layer":7,"corpus":[…]}; trace: {"prompt"|"corpus","top_k","max_tokens"};
analyze: {"corpus_text","compute_stats","skip_layer_similarity"}; slim: {"plan","only","out"};
stats, inspect.
Assets¶
/ serves the UI; /assets/{path} the embedded files with their MIME types: .js as
text/javascript, .css, .html, and .wasm as application/wasm (needed by
WebAssembly.instantiateStreaming for wamr3d).