Text Generation
Transformers
Safetensors
MLX
code
llama
fill-in-the-middle
multi-token-prediction
speculative-decoding
apple-silicon
text-generation-inference
Instructions to use philipjohnbasile/wisp-coder-110m with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use philipjohnbasile/wisp-coder-110m with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="philipjohnbasile/wisp-coder-110m")# Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("philipjohnbasile/wisp-coder-110m") model = AutoModelForCausalLM.from_pretrained("philipjohnbasile/wisp-coder-110m", device_map="auto") - MLX
How to use philipjohnbasile/wisp-coder-110m with MLX:
# Make sure mlx-lm is installed # pip install --upgrade mlx-lm # if on a CUDA device, also pip install mlx[cuda] # Generate text with mlx-lm from mlx_lm import load, generate model, tokenizer = load("philipjohnbasile/wisp-coder-110m") prompt = "Once upon a time in" text = generate(model, tokenizer, prompt=prompt, verbose=True) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- vLLM
How to use philipjohnbasile/wisp-coder-110m with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "philipjohnbasile/wisp-coder-110m" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "philipjohnbasile/wisp-coder-110m", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/philipjohnbasile/wisp-coder-110m
- SGLang
How to use philipjohnbasile/wisp-coder-110m with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "philipjohnbasile/wisp-coder-110m" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "philipjohnbasile/wisp-coder-110m", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "philipjohnbasile/wisp-coder-110m" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "philipjohnbasile/wisp-coder-110m", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - MLX LM
How to use philipjohnbasile/wisp-coder-110m with MLX LM:
Generate or start a chat session
# Install MLX LM uv tool install mlx-lm # Generate some text mlx_lm.generate --model "philipjohnbasile/wisp-coder-110m" --prompt "Once upon a time"
- Docker Model Runner
How to use philipjohnbasile/wisp-coder-110m with Docker Model Runner:
docker model run hf.co/philipjohnbasile/wisp-coder-110m
- Atomic Chat
| """ | |
| Generate from a trained checkpoint, and measure MTP draft acceptance. | |
| Two modes: | |
| --mode ar plain autoregressive decoding with a KV cache. This is your | |
| throughput baseline. | |
| --mode mtp self-speculative decoding: the MTP module drafts K tokens, the | |
| trunk verifies them in one batched forward, and acceptance uses | |
| the Leviathan-Chen probability ratio with residual correction | |
| (p - q)+ on rejection. Reported per-depth acceptance is the number | |
| that tells you whether the heads actually trained. | |
| The mtp path reuses a successful verification forward as the next cycle's target | |
| state. After a rejection it recomputes the corrected full prefix rather than | |
| rolling back a KV cache. That makes it a correctness and rollout-acceptance | |
| reference, not the final speed path. Speed is MTPLX's job once the model is | |
| exported. | |
| Usage: | |
| python sample.py --ckpt out/run1/ckpt_latest --mode mtp --depth 3 \\ | |
| --prompt "def quicksort(arr):" | |
| """ | |
| import argparse | |
| import json | |
| import os | |
| import time | |
| import mlx.core as mx | |
| import numpy as np | |
| from mlx.utils import tree_map, tree_unflatten | |
| from tokenizers import Tokenizer | |
| from model import Wisp, ModelArgs, causal_mask | |
| from scripts.rollout_metrics import normalized_entropy, token_ids_sha256 | |
| DTYPES = {"float32": mx.float32, "bfloat16": mx.bfloat16, "float16": mx.float16} | |
| def load_model(ckpt_dir: str, compute_dtype): | |
| with open(os.path.join(ckpt_dir, "meta.json")) as f: | |
| meta = json.load(f) | |
| args = ModelArgs.from_dict(meta["model_args"]) | |
| model = Wisp(args) | |
| master = tree_unflatten(list(mx.load(os.path.join(ckpt_dir, "master.safetensors")).items())) | |
| model.update(tree_map( | |
| lambda a: a.astype(compute_dtype) if isinstance(a, mx.array) else a, master | |
| )) | |
| model.eval() | |
| mx.eval(model.parameters()) | |
| return model, args, meta | |
| def to_probs(logits_row: np.ndarray, temperature: float, top_k: int, top_p: float) -> np.ndarray: | |
| """Turn one row of logits into the exact distribution we will sample from.""" | |
| x = logits_row.astype(np.float64) | |
| if temperature <= 0: | |
| probs = np.zeros_like(x) | |
| probs[int(np.argmax(x))] = 1.0 | |
| return probs | |
| x = x / temperature | |
| if top_k and top_k < x.shape[0]: | |
| cutoff = np.partition(x, -top_k)[-top_k] | |
| x = np.where(x < cutoff, -np.inf, x) | |
| x = x - np.max(x) | |
| probs = np.exp(x) | |
| probs /= probs.sum() | |
| if top_p and top_p < 1.0: | |
| order = np.argsort(-probs) | |
| cumulative = np.cumsum(probs[order]) | |
| # Conventional nucleus sampling keeps the smallest set whose mass is at | |
| # least top_p, which means the token that *crosses* the threshold is | |
| # included. `cumulative <= top_p` drops it, so the retained mass is below | |
| # top_p and "top_p=0.95" silently means something tighter than 0.95. | |
| # Acceptance stays internally exact either way, but the knob has to mean | |
| # what everyone else means by it. | |
| crossing = int(np.searchsorted(cumulative, top_p)) | |
| keep = np.zeros_like(cumulative, dtype=bool) | |
| keep[:min(crossing + 1, keep.size)] = True | |
| mask = np.zeros_like(probs, dtype=bool) | |
| mask[order[keep]] = True | |
| probs = np.where(mask, probs, 0.0) | |
| probs /= probs.sum() | |
| return probs | |
| def sample_from(probs: np.ndarray, rng: np.random.Generator) -> int: | |
| return int(rng.choice(probs.shape[0], p=probs)) | |
| def generate_ar(model, args, prompt_ids, max_tokens, temperature, top_k, top_p, rng): | |
| tokens = list(prompt_ids) | |
| caches = None | |
| fed = mx.array([tokens], dtype=mx.int32) | |
| dtype = model.norm.weight.dtype | |
| start = time.time() | |
| generated = 0 | |
| for _ in range(max_tokens): | |
| length = fed.shape[1] | |
| mask = causal_mask(length, dtype) if length > 1 else None | |
| if caches is not None and length == 1: | |
| mask = None | |
| logits, _, caches = model(fed, mask, caches) | |
| mx.eval(logits) | |
| probs = to_probs(np.array(logits[0, -1].astype(mx.float32), copy=False), temperature, top_k, top_p) | |
| nxt = sample_from(probs, rng) | |
| tokens.append(nxt) | |
| generated += 1 | |
| fed = mx.array([[nxt]], dtype=mx.int32) | |
| elapsed = time.time() - start | |
| return tokens, generated / max(elapsed, 1e-9) | |
| def generate_mtp(model, args, prompt_ids, max_tokens, depth, temperature, top_k, top_p, | |
| draft_temperature, rng, policy="fixed", | |
| entropy_threshold=0.5, capture_trace=False): | |
| """ | |
| Self-speculative decoding with either a fixed draft depth or an adaptive one. | |
| The adaptive policy exists because draft depth is a bet, not a constant. Every | |
| drafted token costs a recursion of the MTP module and a column of the verify | |
| forward, and a draft sampled from a flat distribution is very likely to be | |
| rejected, which throws away that cost and everything drafted behind it. When | |
| the drafter is confident, deeper drafting is nearly free yield. When it is not, | |
| the marginal draft is negative expected value. | |
| So the policy drafts one token at a time and keeps going only while the | |
| drafter's normalized full-vocabulary entropy stays at or below | |
| `entropy_threshold`, up to `depth`. The first draft is always issued. The | |
| signal needs no extra model call because its logits are already available. | |
| A successful verification already contains both the hidden state and target | |
| logits needed to start the next cycle. Those are reused when every draft is | |
| accepted. A rejection replaces one draft with a residual-correction token, so | |
| hidden states from that position onward are invalid; the next cycle recomputes | |
| the corrected full prefix. The target-forward count is therefore one initial | |
| prefix pass, one pass per verification, and one recovery pass after each | |
| rejection that is followed by more generation. | |
| This still has no rollback-capable KV cache, so wall-clock throughput is a | |
| reference rather than the deployment result. The reported | |
| `mean_accepted_per_verification` and rollout acceptance by depth are real | |
| sequential histories, unlike the teacher-forced E1 diagnostic. | |
| Set `capture_trace=True` to include a strict cycle-level account of the | |
| generation in `stats["generation_trace"]`. Trace hashes bind each state to | |
| the exact token IDs consumed by the corresponding model forward. | |
| """ | |
| tokens = list(prompt_ids) | |
| prompt_token_count = len(tokens) | |
| dtype = model.norm.weight.dtype | |
| accepted_at_depth = np.zeros(depth, dtype=np.int64) | |
| trials_at_depth = np.zeros(depth, dtype=np.int64) | |
| drafts_issued = 0 | |
| draft_recursions = 0 | |
| corrections = 0 | |
| cycles = 0 | |
| prefix_forwards = 0 | |
| recovery_forwards = 0 | |
| verification_forwards = 0 | |
| fully_accepted_verifications = 0 | |
| draft_entropies = [[] for _ in range(depth)] | |
| prefix_hidden = None | |
| next_target_logits = None | |
| generation_cycles = [] if capture_trace else None | |
| start = time.time() | |
| produced = 0 | |
| while produced < max_tokens: | |
| cycle_index = cycles | |
| output_start = produced | |
| prefix_token_count = len(tokens) | |
| state_source = ( | |
| "full_recompute" | |
| if prefix_hidden is None | |
| else "verification_reuse" | |
| ) | |
| cycles += 1 | |
| if prefix_hidden is None: | |
| seq = mx.array([tokens], dtype=mx.int32) | |
| mask = causal_mask(seq.shape[1], dtype) | |
| prefix_hidden, _ = model.trunk(seq, mask) | |
| logits = model.head(prefix_hidden[:, -1:, :]) | |
| mx.eval(prefix_hidden, logits) | |
| next_target_logits = np.array( | |
| logits[0, -1].astype(mx.float32), copy=False | |
| ) | |
| prefix_forwards += 1 | |
| if cycles > 1: | |
| recovery_forwards += 1 | |
| hidden = prefix_hidden | |
| # Bonus token: comes straight from the target, always valid. | |
| p0 = to_probs( | |
| next_target_logits, temperature, top_k, top_p | |
| ) | |
| bonus = sample_from(p0, rng) | |
| tokens.append(bonus) | |
| produced += 1 | |
| emitted_token_ids = [bonus] | |
| if produced >= max_tokens: | |
| if capture_trace: | |
| generation_cycles.append({ | |
| "cycle_index": cycle_index, | |
| "output_start": output_start, | |
| "prefix_token_count": prefix_token_count, | |
| "prefix_sha256": token_ids_sha256( | |
| int(token) for token in tokens[:-1] | |
| ), | |
| "state_source": state_source, | |
| "bonus_token": bonus, | |
| "draft_attempts": [], | |
| "verification": None, | |
| "emitted_token_ids": emitted_token_ids, | |
| "next_state": "terminal", | |
| }) | |
| break | |
| # Draft `depth` tokens by recursing the MTP module over the whole prefix. | |
| # | |
| # The module contains a transformer block, and during training that block | |
| # attends across the full window under a causal mask. Handing it a single | |
| # position at inference would leave its attention with nothing but itself | |
| # to look at, which is a different function from the one that was trained | |
| # and shows up as depressed acceptance rather than as an error. Recomputing | |
| # over the prefix is the same tradeoff this mode already makes for the | |
| # trunk: correctness reference first, speed is MTPLX's job. | |
| drafts, draft_probs = [], [] | |
| draft_attempts = [] | |
| prefix_len = hidden.shape[1] | |
| mtp_mask = causal_mask(prefix_len, dtype) | |
| cur = hidden | |
| # Position i consumes the embedding of token i+k at depth k, mirroring | |
| # model.loss. At entry that window is token indices 1 through prefix_len. | |
| window = tokens[1:prefix_len + 1] | |
| for _ in range(depth): | |
| emb = model.tok_emb(mx.array([window], dtype=mx.int32)) | |
| cur, _ = model.mtp(cur, emb, mtp_mask) | |
| dl = model.head(cur[:, -1:, :]) | |
| mx.eval(dl) | |
| draft_recursions += 1 | |
| draft_logits = np.array( | |
| dl[0, -1].astype(mx.float32), copy=False | |
| ) | |
| policy_probs = to_probs(draft_logits, 1.0, 0, 1.0) | |
| entropy = normalized_entropy(policy_probs) | |
| draft_index = len(drafts) | |
| draft_entropies[draft_index].append(entropy) | |
| if ( | |
| policy == "adaptive" | |
| and draft_index > 0 | |
| and entropy > entropy_threshold | |
| ): | |
| if capture_trace: | |
| draft_attempts.append({ | |
| "depth": draft_index + 1, | |
| "normalized_entropy": float(entropy), | |
| "issued": False, | |
| "draft_token": None, | |
| }) | |
| break | |
| q = to_probs( | |
| draft_logits, | |
| draft_temperature, | |
| top_k, | |
| top_p, | |
| ) | |
| tok = sample_from(q, rng) | |
| drafts.append(tok) | |
| draft_probs.append(q) | |
| if capture_trace: | |
| draft_attempts.append({ | |
| "depth": draft_index + 1, | |
| "normalized_entropy": float(entropy), | |
| "issued": True, | |
| "draft_token": tok, | |
| }) | |
| window = window[1:] + [tok] | |
| # Verify all drafts in one batched trunk forward. | |
| candidate = tokens + drafts | |
| seq = mx.array([candidate], dtype=mx.int32) | |
| mask = causal_mask(seq.shape[1], dtype) | |
| vh, _ = model.trunk(seq, mask) | |
| base = len(tokens) - 1 # position whose logits predict drafts[0] | |
| # The final row predicts the next cycle's bonus when every draft is | |
| # accepted, so compute it in the same projection. | |
| vlogits = model.head(vh[:, base:base + len(drafts) + 1, :]) | |
| mx.eval(vh, vlogits) | |
| vlogits_np = np.array(vlogits[0].astype(mx.float32), copy=False) | |
| verification_forwards += 1 | |
| drafts_issued += len(drafts) | |
| accepted_this_cycle = 0 | |
| rejection_depth = None | |
| outcomes = [] | |
| for j in range(len(drafts)): | |
| trials_at_depth[j] += 1 | |
| p = to_probs(vlogits_np[j], temperature, top_k, top_p) | |
| q = draft_probs[j] | |
| tok = drafts[j] | |
| ratio = 1.0 if q[tok] <= 0 else min(1.0, p[tok] / q[tok]) | |
| if rng.random() < ratio: | |
| accepted_at_depth[j] += 1 | |
| accepted_this_cycle += 1 | |
| tokens.append(tok) | |
| produced += 1 | |
| emitted_token = tok | |
| accepted = True | |
| if produced >= max_tokens: | |
| if capture_trace: | |
| emitted_token_ids.append(emitted_token) | |
| outcomes.append({ | |
| "depth": j + 1, | |
| "draft_token": tok, | |
| "target_argmax_token": int( | |
| np.argmax(vlogits_np[j]) | |
| ), | |
| "target_row_index": base + j, | |
| "accepted": accepted, | |
| "emitted_token": emitted_token, | |
| }) | |
| break | |
| else: | |
| residual = np.maximum(p - q, 0.0) | |
| total = residual.sum() | |
| residual = p if total <= 0 else residual / total | |
| emitted_token = sample_from(residual, rng) | |
| tokens.append(emitted_token) | |
| corrections += 1 | |
| produced += 1 | |
| accepted = False | |
| rejection_depth = j + 1 | |
| if capture_trace: | |
| emitted_token_ids.append(emitted_token) | |
| outcomes.append({ | |
| "depth": j + 1, | |
| "draft_token": tok, | |
| "target_argmax_token": int(np.argmax(vlogits_np[j])), | |
| "target_row_index": base + j, | |
| "accepted": accepted, | |
| "emitted_token": emitted_token, | |
| }) | |
| if not accepted: | |
| break | |
| fully_accepted = accepted_this_cycle == len(drafts) | |
| if fully_accepted: | |
| fully_accepted_verifications += 1 | |
| if produced < max_tokens: | |
| prefix_hidden = vh | |
| next_target_logits = vlogits_np[-1] | |
| elif produced < max_tokens: | |
| # The correction token was not part of `vh`; recompute its prefix at | |
| # the start of the next cycle. | |
| prefix_hidden = None | |
| next_target_logits = None | |
| if capture_trace: | |
| generation_cycles.append({ | |
| "cycle_index": cycle_index, | |
| "output_start": output_start, | |
| "prefix_token_count": prefix_token_count, | |
| "prefix_sha256": token_ids_sha256( | |
| int(token) | |
| for token in candidate[:prefix_token_count] | |
| ), | |
| "state_source": state_source, | |
| "bonus_token": bonus, | |
| "draft_attempts": draft_attempts, | |
| "verification": { | |
| "candidate_sha256": token_ids_sha256( | |
| int(token) for token in candidate | |
| ), | |
| "base_row_index": base, | |
| "projected_row_count": len(drafts) + 1, | |
| "outcomes": outcomes, | |
| "rejection_depth": rejection_depth, | |
| "fully_accepted": fully_accepted, | |
| "unused_drafts": len(drafts) - len(outcomes), | |
| }, | |
| "emitted_token_ids": emitted_token_ids, | |
| "next_state": ( | |
| "terminal" | |
| if produced >= max_tokens | |
| else ( | |
| "verification_reuse" | |
| if fully_accepted | |
| else "full_recompute" | |
| ) | |
| ), | |
| }) | |
| elapsed = time.time() - start | |
| rollout_acceptance = [ | |
| round(float(a) / float(t), 4) if t else None | |
| for a, t in zip(accepted_at_depth, trials_at_depth) | |
| ] | |
| target_forwards = prefix_forwards + verification_forwards | |
| accepted_drafts = int(accepted_at_depth.sum()) | |
| stats = { | |
| "policy": policy, | |
| "entropy_threshold": ( | |
| entropy_threshold if policy == "adaptive" else None | |
| ), | |
| "cycles": cycles, | |
| "tokens": produced, | |
| "corrections": corrections, | |
| "drafts_issued": int(drafts_issued), | |
| "draft_recursions": int(draft_recursions), | |
| "elapsed_seconds": elapsed, | |
| "tok_per_sec": produced / max(elapsed, 1e-9), | |
| "acceptance_per_depth": rollout_acceptance, | |
| "rollout_acceptance_per_depth": rollout_acceptance, | |
| "rollout_accepted_per_depth": [ | |
| int(value) for value in accepted_at_depth | |
| ], | |
| "rollout_trials_per_depth": [ | |
| int(value) for value in trials_at_depth | |
| ], | |
| "prefix_forwards": prefix_forwards, | |
| "recovery_forwards": recovery_forwards, | |
| "verification_forwards": verification_forwards, | |
| "target_forwards": target_forwards, | |
| "fully_accepted_verifications": fully_accepted_verifications, | |
| "mean_accepted_per_verification": round( | |
| accepted_drafts / max(verification_forwards, 1), 3 | |
| ), | |
| "mean_output_tokens_per_target_forward": round( | |
| produced / max(target_forwards, 1), 3 | |
| ), | |
| "mean_accepted_per_cycle": round( | |
| accepted_drafts / max(cycles, 1), 3 | |
| ), | |
| "mean_tokens_per_cycle": round(produced / max(cycles, 1), 3), | |
| "mean_drafts_per_cycle": round(drafts_issued / max(cycles, 1), 3), | |
| "mean_draft_recursions_per_cycle": round( | |
| draft_recursions / max(cycles, 1), 3 | |
| ), | |
| "draft_yield": round( | |
| accepted_drafts / max(drafts_issued, 1), 4 | |
| ), | |
| "mean_normalized_entropy_per_depth": [ | |
| ( | |
| round(float(np.mean(values)), 6) | |
| if values else None | |
| ) | |
| for values in draft_entropies | |
| ], | |
| } | |
| if capture_trace: | |
| stats["generation_trace"] = { | |
| "schema_version": 1, | |
| "prompt_token_count": prompt_token_count, | |
| "max_tokens": max_tokens, | |
| "cycles": generation_cycles, | |
| } | |
| return tokens, stats | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--ckpt", required=True) | |
| ap.add_argument("--tokenizer", default="tokenizer/code32k.json") | |
| ap.add_argument("--prompt", default="def quicksort(arr):") | |
| ap.add_argument("--mode", choices=["ar", "mtp"], default="ar") | |
| ap.add_argument("--max-tokens", type=int, default=128) | |
| ap.add_argument("--depth", type=int, default=2, | |
| help="fixed draft depth, or the cap under --policy adaptive") | |
| ap.add_argument("--policy", choices=["fixed", "adaptive"], default="fixed", | |
| help="adaptive stops drafting when normalized entropy is high") | |
| ap.add_argument( | |
| "--entropy-threshold", | |
| type=float, | |
| default=0.5, | |
| help="adaptive policy: draft deeper while normalized H(q) stays below this", | |
| ) | |
| ap.add_argument("--temperature", type=float, default=0.6) | |
| ap.add_argument("--draft-temperature", type=float, default=0.7) | |
| ap.add_argument("--top-k", type=int, default=20) | |
| ap.add_argument("--top-p", type=float, default=0.95) | |
| ap.add_argument("--dtype", default="bfloat16") | |
| ap.add_argument("--seed", type=int, default=0) | |
| cli = ap.parse_args() | |
| if cli.depth < 1: | |
| raise ValueError("--depth must be positive") | |
| if not 0.0 <= cli.entropy_threshold <= 1.0: | |
| raise ValueError("--entropy-threshold must be between 0 and 1") | |
| rng = np.random.default_rng(cli.seed) | |
| compute_dtype = DTYPES[cli.dtype] | |
| model, args, meta = load_model(cli.ckpt, compute_dtype) | |
| tok = Tokenizer.from_file(cli.tokenizer) | |
| prompt_ids = tok.encode(cli.prompt).ids | |
| print(f"checkpoint step {meta['step']}, {args.n_layers}L/{args.dim}d, " | |
| f"mtp_layers={args.mtp_layers}") | |
| if cli.mode == "ar": | |
| tokens, tps = generate_ar( | |
| model, args, prompt_ids, cli.max_tokens, | |
| cli.temperature, cli.top_k, cli.top_p, rng, | |
| ) | |
| print(f"\n{tok.decode(tokens)}\n") | |
| print(json.dumps({"mode": "ar", "tok_per_sec": round(tps, 2)})) | |
| else: | |
| tokens, stats = generate_mtp( | |
| model, args, prompt_ids, cli.max_tokens, cli.depth, | |
| cli.temperature, cli.top_k, cli.top_p, cli.draft_temperature, rng, | |
| policy=cli.policy, entropy_threshold=cli.entropy_threshold, | |
| ) | |
| print(f"\n{tok.decode(tokens)}\n") | |
| print(json.dumps({"mode": "mtp", "depth": cli.depth, **stats})) | |
| if __name__ == "__main__": | |
| main() | |