Repository navigation
api
Base URL (dev): http://localhost:8000. All responses are JSON. CORS is
restricted to the local dev origin (http://localhost:3000 — configurable via
TOKENPRINT_FRONTEND_PORT) and the deployed frontend origins
(https://sudharsanselvaraj.github.io, https://tokenprint.in, and
subdomains).
Liveness + whether the model is loaded.
{ "status": "ok", "model_loaded": true }{
"model": "Qwen/Qwen2.5-0.5B-Instruct",
"device": "mps",
"num_layers": 24,
"num_heads": 14,
"hidden_size": 896,
"attn_implementation": "eager",
"max_tokens": 40,
"ready": true
}Real architecture metadata + tensor list from the loaded model
(named_parameters() + config). No forward pass.
The GGUF drag-and-drop path (client-side) produces the same shape with
"source": "gguf" and extra metadata (quantization, gguf_version,
file_size, expert_count).
Body: { "sentence": "The cat sat on the mat." } (capped ~40 tokens; returns
400 if longer).
{
"sentence": "The cat sat on the mat.",
"model": "Qwen/Qwen2.5-0.5B-Instruct", "device": "mps",
"num_layers": 24, "num_heads": 14, "hidden_size": 896,
"tokens": [ { "index": 0, "text": "The", "piece": "The", "id": 785, "is_special": false } ],
// real softmax attention, rounded to 3 dp, sub-0.01 zeroed:
"attention": [ /* [layer][head][from][to] */ ],
// PCA projections of real hidden states:
"embeddings_3d": [ [x, y, z] ],
"hidden_states_3d": { "0": [[x,y,z]], "1": [[x,y,z]] /* … */ },
"embedding_norms": [ 0.53 ],
"projection": { "method": "PCA", "note": "3D projection; distances approximate",
"embedding_explained_variance": [0.36, 0.24, 0.18] }
}RAG attribution reduction on top of the existing real attention tensor from
ModelEngine.analyze(). The backend composes:
Context:
<chunk id=...>...</chunk>
...
<query>...</query>
and maps chunk/query text spans to token spans using tokenizer offset mappings.
Body:
{
"query": "What year was the company founded?",
"chunks": [
{ "id": "0", "text": "The company was founded in 2018 in Chennai." },
{ "id": "1", "text": "Its first product launched in 2020." }
],
"reduction_mode": "both",
"ungrounded_threshold": 0.1
}Response additions (on top of /analyze):
{
"query": "What year was the company founded?",
"chunk_spans": { "0": [12, 20], "1": [24, 31] },
"query_span": [36, 45],
"attribution_chunk_ids": ["0", "1"],
"attribution": [ [0.42, 0.08], [0.45, 0.07] ],
"attribution_all_layers_mean": [ [0.42, 0.08], [0.45, 0.07] ],
"attribution_last_layer": [ [0.51, 0.04], [0.48, 0.05] ],
"query_self_attribution": [0.31, 0.29],
"ungrounded": [false, false]
}attribution shape is [query_token][chunk_id]. Values are attention mass from
each query-token row onto each chunk span.
Note Attention mass is a correlate of influence, not a proof of causal influence. TokenPrint reports it as an attribution signal, not a causality claim.
Tip Set
reduction_mode: "both"so the UI can switch between all-layer and last-layer views without recomputing.
A real streamed greedy generation.
Client → server (first message):
{ "prompt": "Name one primary color.", "max_new_tokens": 40, "top_k": 10, "trace": true }Server → client frames:
// 1) meta (once). With trace:true it also carries the op catalog.
{ "type": "meta", "model": "…", "architecture": "qwen2",
"num_layers": 24, "num_layer_stats": 25, "prompt_tokens": ["…"],
"prompt_len": 36, "max_new_tokens": 40, "top_k": 10, "decoding": "greedy",
"uses_kv_cache": true, // the decode loop threads real past_key_values
"op_catalog": [
{ "index": 0, "op_key": "embedding", "label": "Token Embedding", "layer": null,
"param_count": 136134656, "cumulative_params": 136134656,
"in_dim": 896, "out_dim": 151936, "bias_dim": null,
"weight_preview": [[/* up to 8×8 real weights */]] }
// … 219 ops total for this model
] }
// 2) one per generated token
{ "type": "token", "step": 0,
"chosen": { "id": 6893, "text": "Red", "logprob": -0.04 },
"topk": [ { "id": 6893, "text": "Red", "logit": 18.4, "prob": 0.957 } ],
"layer_stats": [ /* 25 mean |activation| values */ ],
"eos": false,
// real KV-cache accounting for this step:
"phase": "prefill", // "prefill" (step 0) | "decode" (later steps)
"n_positions": 39, // tokens actually computed this step (prompt_len, then 1)
"cache_len": 0 } // cached positions reused this step (0, then prompt_len + n)
// 3) done
{ "type": "done", "generated_text": "Red", "total_steps": 2 }Payloads are bounded on purpose: the op catalog is sent once and token frames
reference it by index; topk is 8–12 entries (not the full vocab); weight
previews are ≤ 8×8 slices.
The decode loop runs model(..., use_cache=True) and threads past_key_values
step to step, so the pre-fill vs decode distinction is mechanically real and
reported per frame:
-
step 0 —
prefill: processes the whole prompt at once (n_positions = prompt_len,cache_len = 0), building the cache. -
steps ≥ 1 —
decode: process a single new token (n_positions = 1) and reuse the cached prefix (cache_len = prompt_len + step − 1).
The frontend only shows this distinction when meta.uses_kv_cache is true —
never faked.
Record & replay. When a generation request sets record_trace: true, the full
WebSocket stream is tee'd into an in-memory trace (prompt, model metadata, every
token/layer frame in order, timings). GET /trace returns the last recorded run
as a downloadable .tokenprint.json; POST /trace/replay accepts an uploaded
trace and replays it back to the caller.
The schema is versioned from day one (trace_version: 1, see
backend/app/trace.py) — replay code will outlive the current stream shape, and
parse_trace() rejects an unknown version rather than silently mis-reading it.
A trace is a real forward pass frozen to disk: replaying one is not a simulation, it is the same numbers the model produced, re-emitted with their original pacing. This is what lets the static demo show genuine data with no GPU and no backend.
Stepped inspection. /debug/ops returns the real op catalog (index, label,
op_key, layer, in_dim/out_dim, param_count) so the frontend can set
breakpoints against true op indices. /debug/analyze runs a prompt and returns
the per-layer state needed to inspect a paused position.
Interventions. Accepts a sentence plus the heads and/or layers to zero, installs
forward hooks that null those components' contribution, and re-runs a real
forward pass. The response has the same shape as /analyze, so the frontend can
diff the two logit-lens tables position by position and report exactly how many
predictions changed.
This works on raw architectural components — any head, any block, on whatever model is loaded — with no sparse autoencoder, transcoder, or other pre-trained artifact required. That is the deliberate difference from feature-level intervention tools.
The backend can host a resident .gguf execution engine (see
backend/requirements-gguf.txt). Models are
held in an LRU cache with explicit memory reclamation so repeated loads are
fast without leaking memory.
-
GET /gguf/list— available GGUF files in the watched directory. -
POST /gguf/upload— upload a.gguffile for the engine. -
POST /gguf/open— load (or return from the LRU cache) a resident model. -
POST /gguf/unload— explicitly unload a resident engine and release its memory. Body:{ "path": "/abs/path/to/model.gguf" }. Returns{ "ok": true, "name": ..., "unloaded": true|false }. -
GET /gguf/cache— resident cache statistics, limits, and eviction counters (from the LRU state).
See GGUF format for what the client-side parser reads and the capability boundary between the two paths.
TokenPrint Wiki | Home | Getting Started | User Guide | Concepts | Architecture | API | GitHub
TokenPrint is an interactive 3D visualization platform for exploring transformer architectures, tensors, and real-time LLM inference.
- User Guide
- Architecture Explorer
- Live Inference
- Tensor Inspector
- Camera Controls
- HUD
- Timeline
- Scene Navigation
- Transformer Concepts
- Tokenization
- Embeddings
- Positional Encoding
- RoPE
- Multi-Head Attention
- KV Cache
- LayerNorm
- Feed Forward Network
- Residual Connections
- Logits
- Softmax
- Sampling
- Autoregressive Generation
- Visualization System
- Scene Graph
- Geometry
- Materials
- Animation System
- Color Mapping
- Camera System
- Interaction Model
- Architecture
- Overall Architecture
- Frontend
- Backend
- WebSocket Protocol
- Data Pipeline
- Renderer
- Event System
{ "source": "model", "model": "Qwen/Qwen2.5-0.5B-Instruct", "device": "mps", "metadata": { "architecture": "qwen2", "total_params": 494032768, "num_layers": 24, "hidden_size": 896, "num_heads": 14, "num_kv_heads": 2, "head_dim": 64, "ffn_size": 4864, "vocab_size": 151936, "context_length": 32768, "rope_theta": 1000000.0, "tie_word_embeddings": true, "torch_dtype": "float32" }, "tensor_count": 290, "tensors": [ { "name": "model.embed_tokens.weight", "shape": [151936, 896], "dtype": "float32", "n_params": 136134656 } // … ] }