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
| # DATA: corpus provenance, filtering, and the safety review | |
| What goes into Wisp, where it came from, what was removed, and what was checked. | |
| This is the record the model card is built from, so it is written to be read by | |
| someone deciding whether to trust the weights. | |
| ## Sources | |
| | Source | Subset | Configured weight | Realized train tokens | Source terms | | |
| |---|---|---:|---:|---| | |
| | `bigcode/starcoderdata` | python | 0.24 | 1,200,566,506 | card label `other`; original repository terms apply | | |
| | `bigcode/starcoderdata` | javascript | 0.13 | 650,722,190 | as above | | |
| | `bigcode/starcoderdata` | typescript | 0.12 | 600,701,168 | as above | | |
| | `bigcode/starcoderdata` | go | 0.09 | 451,081,764 | as above | | |
| | `bigcode/starcoderdata` | rust | 0.09 | 451,511,705 | as above | | |
| | `bigcode/starcoderdata` | java | 0.08 | 400,525,337 | as above | | |
| | `bigcode/starcoderdata` | c | 0.05 | 250,363,883 | as above | | |
| | `bigcode/starcoderdata` | shell | 0.03 | 150,445,965 | as above | | |
| | `bigcode/starcoderdata` | sql | 0.02 | 100,765,073 | as above | | |
| | `bigcode/starcoderdata` | markdown | 0.07 | 351,026,251 | as above | | |
| | `HuggingFaceFW/fineweb-edu` | sample-10BT | 0.08 | 400,290,897 | ODC-By 1.0 and Common Crawl terms | | |
| The final build log survived and is now preserved byte-for-byte as | |
| `evidence/run1_corpus_build.log`. Its mtime matches the final shard index, and | |
| the source totals sum exactly to the index's 5,008,000,739 train tokens. The | |
| realized train split is 92.006972 percent StarCoderData, including 84.997663 | |
| percent implementation-language code and 7.009309 percent Markdown, plus | |
| 7.993028 percent FineWeb-Edu. The web share exists to keep natural-language | |
| comments, docstrings, and identifiers from drifting, and was cut from 15 percent | |
| to 8 to buy back budget for code when the niche was fixed as FIM-first. | |
| This recovered log closes the aggregate train-token-count gap. It does not | |
| recover accepted row identities, rejection counts, source metadata, per-source | |
| validation overshoot, or row-level licensing obligations. | |
| `starcoderdata` is gated. Access is granted per account by accepting the terms on | |
| the dataset page. Gated access is not a permissive-license attestation. Its card | |
| declares `license: other`; the original repository licenses and attribution | |
| clauses remain applicable, together with the dataset's update and removal terms. | |
| FineWeb-Edu is distributed under ODC-By 1.0 and remains subject to Common Crawl | |
| terms. | |
| All ten code subsets above were verified to stream after access. Run 1 retained | |
| tokenized text only. It did not preserve StarCoderData row IDs, repository names | |
| or paths, FineWeb IDs or URLs, a per-row license mapping, or an attribution index. | |
| The resulting shards cannot support a local per-file licensing or attribution | |
| audit. Apache 2.0 applies to the Wisp artifact; it does not override source-data | |
| terms or the licenses of code the model may generate. | |
| ## The malware scan finding, resolved | |
| Hugging Face flagged four files in `bigcode/starcoderdata` as unsafe. They were | |
| checked individually rather than taken on the file name, because three of the four | |
| names are ambiguous across subsets. | |
| | Flagged file | Subset it is actually in | Used by Wisp | | |
| |---|---|---| | |
| | `train-00008-of-00055.parquet` | `git-commits-cleaned` | no | | |
| | `train-00019-of-00055.parquet` | `git-commits-cleaned` | no | | |
| | `train-00027-of-00055.parquet` | `git-commits-cleaned` | no | | |
| | `train-00043-of-00059.parquet` | `github-issues-filtered-structured` | no | | |
| **None of the four are in a subset this project reads.** The fourth needed | |
| resolving: a file of that exact name exists in both | |
| `github-issues-filtered-structured` and in `python`, which Wisp does use. Querying | |
| the per-file scan status settles it. The `github-issues` copy is `unsafe`; the | |
| `python` copy is `queued`, which means unscanned, not flagged. | |
| Worth stating plainly, since "queued" is most of the repository: the majority of | |
| files carry no scan verdict either way. Four are affirmatively flagged, none of | |
| them ours, and the rest are largely unscanned rather than certified clean. | |
| ### Why this is a low risk here regardless | |
| - **Parquet has no execution vector on read.** It is columnar data parsed by | |
| pyarrow. This is not the pickle situation, where deserialising *is* execution. | |
| - **A flag on a GitHub-derived corpus is expected.** Commit messages and issue | |
| threads contain malware source, exploit proofs of concept, and EICAR test | |
| strings, because those things live on GitHub. The scanner is matching content, | |
| and it is right to. | |
| - **Nothing in this pipeline executes corpus content.** The path is stream, then | |
| tokenize, then write uint16 shards. Where enabled, `ast.parse` and `json.loads` | |
| parse without evaluating. | |
| - **The real exposure is to the model, not the machine.** Training on malware | |
| source teaches malware patterns. That argues for excluding such data on model | |
| safety grounds, and it is excluded here, since the affected subsets are not in | |
| the mixture. | |
| ## The quality gate | |
| Run 1 applied the structural checks below to every source. Verifier-first was | |
| borrowed from `agent-toolkit`'s `verify/verifiers.py` in stance rather than in | |
| code: executing scraped code to decide whether to train on it would be the | |
| security incident. | |
| Rejected: | |
| - Longest line over 1000 characters. Minified bundles, generated files, data on | |
| one line. | |
| - Mean line length over 120 characters. Past anything hand written. | |
| - Alphanumeric-plus-whitespace fraction under 0.55 in the first 20KB. Base64, | |
| hex dumps, embedded binary assets. | |
| - Anything under 64 characters. | |
| An earlier draft also claimed that run 1 rejected Python that failed `ast.parse` | |
| and JSON that failed `json.loads`. That was wrong. The Hub loader requested the | |
| column `path`, while StarCoderData exposes `max_stars_repo_path`. Its rows | |
| therefore reached the structural checks without a file extension, and neither | |
| extension parser activated during the run 1 Hub build. | |
| The 92.7 percent retention figure came from a local forced-Python pilot, not from | |
| the final streamed corpus. It remains evidence that the structural gate is not | |
| vacuous, but it is not a final-corpus retention measurement. | |
| After run 1 launched, the loader was repaired to map the pinned StarCoderData | |
| schema, fail closed if its path field disappears, and remove the leading | |
| `<reponame>` metadata line only for syntax parsing. The original text remains the | |
| payload. Synthetic mutation checks prove invalid Python is rejected. This repair | |
| applies to future source streaming only; it does not change the active process or | |
| existing shards. E2 must not use the repaired stream to select a different | |
| document population. Its no-FIM data is instead produced by the registered | |
| deterministic normalization of run 1's token stream. | |
| The rationale is capacity, not tidiness. At 108M parameters, tokens spent | |
| modelling a webpack bundle are tokens not spent on code. | |
| ## The tokenizer, benchmarked rather than assumed | |
| 32,768 byte-level BPE, digits split individually, whitespace runs allowed to | |
| merge, trained on 400,000 documents drawn round-robin across the eleven | |
| configured source entries. The tokenizer sampler did not apply the configured | |
| token weights. Each language entry therefore contributed about one eleventh of | |
| the document sample, while Python contributed 24 percent of the later shard | |
| budget. This is a reproducibility limitation and a future tokenizer experiment, | |
| not a reason to alter the tokenizer under an active model. | |
| Measured on 366KB of fresh starcoderdata across Python, JavaScript, Go, and Rust, | |
| none of it in the tokenizer's training sample: | |
| | tokenizer | vocab | chars/token | | |
| |---|---|---| | |
| | wisp code32k | 32,768 | **3.52** | | |
| | bigcode/starcoder2-15b | 49,152 | 3.54 | | |
| | Qwen/Qwen2.5-Coder | 151,643 | 4.06 | | |
| Compression matters because it sets how much code 5B tokens actually buys. A | |
| tokenizer 15 percent worse means 15 percent less code for the same compute. | |
| The 32K tokenizer therefore matches StarCoder2's compression while carrying a | |
| third less vocabulary. Qwen's 15.6 percent advantage is not practical at this | |
| scale: a 151,643-token embedding at d_model 768 alone costs 116M parameters, | |
| larger than this entire model. | |
| That comparison did not answer the more relevant challenge, 16K versus 32K. | |
| `benchmark_tokenizer_vocab.py` now does. Greedy BPE learns merges sequentially, | |
| so truncating the frozen tokenizer at the 16K merge boundary gives the exact | |
| same-training-sample 16K merge prefix. Both tokenizers were measured on all 343 | |
| documents in the clean, source-weighted publication holdout: | |
| | vocab | tokens | chars/token | tied embedding | model if only vocab changes | training head MACs/token | | |
| |---|---:|---:|---:|---:|---:| | |
| | 16,384 | 1,117,129 | 3.2123 | 12.58M | 95.57M | 37.75M | | |
| | 32,768 | 1,069,055 | 3.3568 | 25.17M | 108.16M | 75.50M | | |
| The 16K tokenizer needs 4.50 percent more tokens for the same text, or sees 4.30 | |
| percent fewer characters at a fixed token budget. In return it saves 12,582,912 | |
| parameters, 11.63 percent of the whole 32K model, and halves the width of each of | |
| the three output projections evaluated per training position. | |
| So 32K was not forced, and the old wording claiming that was wrong. Run 1 remains | |
| 32K because its tokenizer, corpus, and active weights are already frozen. The | |
| evidence makes 16K a serious future-successor candidate, but compression and | |
| projection arithmetic do not establish end-to-end speed or model quality. Those | |
| require a post-run Metal throughput arm and a matched training-quality arm. | |
| Registered run 2 remains a 32K no-FIM ablation. | |
| `config/tokenizer_vocab_receipt.json` records the exact hashes and measurements. | |
| ## Fill in the middle, and what the window geometry does to it | |
| The configured chunk-level rate is not the rate a sampled training window sees, | |
| and the gap is large enough to matter. | |
| After tokenization, each source document is split into chunks of at most 1024 | |
| tokens. FIM is selected independently for each chunk at prepare time. Training | |
| then samples random 2051-token windows out of the concatenated stream. A window | |
| can see a complete frame, a fragment such as a suffix with no prefix sentinel, | |
| or no frame at all. Measured while comparing the discarded whole-document build | |
| with the final chunked build, sampling 4,000 windows per shard: | |
| | build | complete frame | partial | none | | |
| |---|---|---|---| | |
| | whole-document FIM | 43.2% / 62.1% / 69.9% | 9 to 19% | 20 to 38% | | |
| | 1024-token chunks | **90.1% / 89.8% / 91.2%** | 5 to 7% | 3 to 4% | | |
| The headline capability was being diluted by roughly a fifth before the fix. | |
| **The arithmetic, and its side effect.** With chunks of 1024 and windows of 2051, | |
| a window spans about two chunks, and a complete frame appears if *either* chunk was | |
| transformed. At `fim_rate` 0.7 that is `1 - 0.3^2 = 91%`, which matches the | |
| measurement. The same arithmetic says pure left-to-right windows fall to about | |
| 9%, well under the 30% that would follow from treating the configured value as a | |
| window-level rate. | |
| That is a real change to the mixture and it was not the goal, it is a consequence. | |
| Kept deliberately, for two reasons. Every token is still predicted | |
| autoregressively, so next-token capability is not lost; what shrinks is exposure to | |
| the *unframed* format, not to left-to-right prediction itself. And the deployment | |
| target is a cursor inside an existing file, which is the framed case. | |
| If a future run wants the window-level rate to match a 70 percent target, | |
| solve `1 - (1 - r)^2 = 0.7` for `r`, giving `fim_rate` about 0.45. The research | |
| design is insulated from this either way: the primary endpoint compares FIM against | |
| FIM-with-shuffled-suffix, both of which are framed, so format exposure cancels. | |
| The FIM over L2R secondary number is the one this affects. | |
| ## Fill in the middle | |
| Applied at prepare time, not train time, independently to 70 percent of tokenized | |
| chunks of at most 1024 tokens, split evenly between PSM and SPM orderings. It was | |
| not applied once per source document. The rate was raised from 0.5 when the niche | |
| was fixed, since cursor completion is the deployment target. `fim_rate` now lives | |
| in the config and the CLI flag overrides it. | |
| ## Validation split in the final corpus | |
| The first corpus build filled one shared validation counter from Python and was | |
| discarded. The final corpus was rebuilt after `prepare_data.py` changed to | |
| allocate validation tokens per source in the same proportions as training. | |
| Run 1 therefore uses a source-stratified validation shard with 20,006,112 | |
| tokens. The discarded build is not used by the trainer. | |
| ## Scheduled tokens are not unique corpus coverage | |
| Run 1 draws random 2051-token windows with replacement. Its 19,073 steps, | |
| 8-example micro batch, and 16-way accumulation schedule 2,441,344 windows and | |
| 4,999,872,512 prediction positions. The registered step-300 sampler reset | |
| repeats the first 38,400 window coordinates, leaving 2,402,944 distinct | |
| scheduled draws at most. | |
| Under the sampler's uniform-start distribution, the interval-coverage | |
| approximation `1 - exp(-4,921,229,312 / 5,008,000,739)` is 62.57 percent. | |
| Roughly 37.43 percent of corpus positions are therefore expected not to appear | |
| in a prediction window, while other positions repeat. This is expected | |
| with-replacement sampling, not shard corruption. A future run should compare a | |
| checkpointed shuffled block order without replacement so nearly the full corpus | |
| is exposed before repetition. E2 keeps run 1's sampler semantics for a matched | |
| comparison. | |
| ## Corpus-build attestation | |
| Run 1's existing index did not include shard hashes, so a full read-only | |
| attestation now binds all 52 files, 10,056,013,702 bytes, the source index, the | |
| tokenizer, and the normalization program in | |
| `config/run1_shard_integrity_receipt.json`. The scan found 27 detectable FIM | |
| frames split by literal EOS tokens inside their payloads. It reassembles those | |
| visible groups and restores 33 internal EOS tokens. The resulting normalized | |
| totals are 4,992,043,184 train tokens and 19,949,502 validation tokens. | |
| This is deterministic token-stream normalization, not proof of exact original | |
| examples. An EOS inside unframed content, an EOS after the final FIM marker, and | |
| raw content that already has the visible grammar of a valid FIM frame are | |
| observationally indistinguishable from preprocessing structure after | |
| tokenization. The receipt records `exact_original_units_proven: false`, the | |
| visible grouping manifest, and domain-separated source, recovered, and | |
| destination hashes. | |
| The run 2 builder never reopens a Hub source. It requires the attested run 1 | |
| files, a missing or empty real output directory, and the exact registered | |
| derivation strategy. It rejects symbolic links, unsafe or duplicate paths, | |
| mutation during reads, malformed FIM grammar, cross-shard recovery, residual FIM | |
| sentinels, and any count or hash mismatch. It writes one destination shard and | |
| one fixed-width unit-boundary sidecar per source shard. Only after independently | |
| rereading every output does it atomically publish a schema-3 index with | |
| per-shard hashes, sidecar hashes, and split-level normalization evidence. | |
| Both normalized splits fall just below the old requested 5B and 20M corpus | |
| floors because three generated sentinels are removed from every visible FIM | |
| frame. The fixed run 2 schedule still samples 4,999,872,512 token positions. | |
| This changes neither registered E2 endpoint nor the model geometry, but it | |
| supersedes the earlier claim that exact raw examples could be recovered. | |
| This was added before any run2 corpus existed. The amendment is recorded in | |
| `config/eval_format_ablation_receipt.json`; it changes build provenance and | |
| failure behavior, not either registered endpoint. Run1's checkpoint did not | |
| record source-file hashes or the sampler state. Its step-300 process recovery | |
| reset the legacy shard sampler and therefore repeats 78,643,200 scheduled token | |
| positions, 1.57 percent of the training budget. That observed reset is now | |
| registered in both E2 arms as `sampler_reset_steps: [300]`. New checkpoints | |
| store the exact sampler RNG state, and legacy checkpoints reconstruct the | |
| registered position. This matches reset timing, not raw examples across | |
| different arm shards. The model and optimizer are unchanged, but exact run1 | |
| source-state equivalence cannot be proven. | |
| Run 1 also did not pin Hub revisions at build time or save an ordered raw-source | |
| manifest. Revisions captured from the local cache afterward are useful | |
| repository-level evidence, but they cannot reconstruct the accepted rows, | |
| repository paths, rejection counts, validation overshoot by source, or row-level | |
| obligations. Aggregate realized train-token totals survive in the preserved | |
| final build log. | |
| `config/training_data_receipt.json` binds these limitations, the audited source | |
| cards and schemas, the prospective parser repair, and the deterministic | |
| normalization limitation. | |
| ## Held out data | |
| The publication evaluation corpus is not the old local-file fallback. It is built | |
| from `config/eval_repos.json`, which pins 29 repository-language strata at exact | |
| commits. Repositories are non-forks with allowed licenses, GitHub creation dates | |
| after 2024-01-01, and no reachable root commit before that cutoff. The selected | |
| files carry source URLs, exact content hashes, and first-history dates. | |
| The source-weighted build produced 350 candidates across all ten training | |
| languages. `check_holdout_overlap.py` then scanned up to 32 exact 64-token anchors | |
| per document against all 5,008,000,739 final training tokens. Seven documents had | |
| at least one exact hit and were excluded wholesale, leaving 343 clean documents. | |
| The registered 200-example evaluation draw covers all ten languages and 24 | |
| repositories, with every shuffled suffix matched to the same language and a | |
| different repository. | |
| `config/eval_holdout_receipt.json` records the candidate and clean SHA-256 values, | |
| tokenizer and training-index hashes, scan parameters, language counts, and all | |
| seven exclusions. A clean anchor scan is not proof that no shorter fragment | |
| overlaps training; it is an auditable conservative check at the recorded | |
| 64-token granularity. | |