Instructions to use Serveurperso/small-test with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use Serveurperso/small-test with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf Serveurperso/small-test:F16 # Run inference directly in the terminal: llama cli -hf Serveurperso/small-test:F16
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf Serveurperso/small-test:F16 # Run inference directly in the terminal: llama cli -hf Serveurperso/small-test:F16
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf Serveurperso/small-test:F16 # Run inference directly in the terminal: ./llama-cli -hf Serveurperso/small-test:F16
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf Serveurperso/small-test:F16 # Run inference directly in the terminal: ./build/bin/llama-cli -hf Serveurperso/small-test:F16
Use Docker
docker model run hf.co/Serveurperso/small-test:F16
- LM Studio
- Jan
- vLLM
How to use Serveurperso/small-test with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Serveurperso/small-test" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Serveurperso/small-test", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/Serveurperso/small-test:F16
- Ollama
How to use Serveurperso/small-test with Ollama:
ollama run hf.co/Serveurperso/small-test:F16
- Unsloth Desktop
- Pi
How to use Serveurperso/small-test with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf Serveurperso/small-test:F16
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "Serveurperso/small-test:F16" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Docker Model Runner
How to use Serveurperso/small-test with Docker Model Runner:
docker model run hf.co/Serveurperso/small-test:F16
- Lemonade
How to use Serveurperso/small-test with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull Serveurperso/small-test:F16
Run and chat with the model
lemonade run user.small-test-F16
List all available models
lemonade list
- Hermes Agent
How to use Serveurperso/small-test with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf Serveurperso/small-test:F16
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default Serveurperso/small-test:F16
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use Serveurperso/small-test with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf Serveurperso/small-test:F16
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "Serveurperso/small-test:F16" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
small-test: a 95M multimodal fixture for the llama.cpp server CI
Idea and first version: Xuan-Son Nguyen (ngxson). Recovery training, data pipeline, evaluation and this release: Pascal (Serveurperso). Built together, one machine each, over a week of evenings.
One model that exercises every path of llama-server in one go: chat, tool calling in the Qwen3.5 XML format, OCR through mmproj, and speculative decoding with its own MTP head as the draft model. 82M text parameters, 12.6M MTP head, 58M vision tower. 190 MB in f16, 102 MB in Q8_0. It is deterministic in greedy mode: the 19 server tests below pass identically run after run.
This is a test fixture, not an assistant. It is a pruned Qwen3.5-0.8B with 6 of 31 layers, trained to be predictable on the shapes the llama.cpp CI asks for. It will happily tell you that C++ is simple and easy to use.
Files
| file | what |
|---|---|
small-test-f16.gguf |
text model + MTP head, f16, 190 MB, the reference for CI |
small-test-mmproj-f16.gguf |
vision projector for --mmproj, 120 MB |
small-test-q8_0.gguf |
Q8_0, 102 MB, passes everything except the strict MTP draft equality test |
hf/ |
HF weights, config, tokenizer and the reasoning free chat template |
src/ |
the whole pipeline, from pruning to the server test file |
Use in llama.cpp
llama-server -hf Serveurperso/small-test --mmproj-url https://huggingface.co/Serveurperso/small-test/resolve/main/small-test-mmproj-f16.gguf --jinja --spec-type draft-mtp
In tools/server/tests the model is ServerPreset.small_test() and unit/test_small_test.py runs 19 tests against it: required and auto tool calls, tool results, no tool call cases, OCR on a rendered image, and MTP draft equivalence with the target. About 12 seconds on CPU.
Architecture
Qwen3.5-0.8B pruned to layers [0, 11, 14, 19, 22, 23] of 31, chosen by leave one out loss on a text subset, alternating linear (Gated DeltaNet) and full attention. FFN cut to 1024. Vocabulary pruned from 150k to 24935 tokens by byte level BPE frequency with merge closure (min_count 600). The MTP head is trained jointly at weight 0.3 and used as the draft model (--spec-type draft-mtp). The vision tower keeps 6 blocks and is trained jointly on synthetic OCR images. Nothing is randomly initialized: every surviving weight comes from the parent, and the loss starts at ln(vocab) = 10.1 because the pruned chain has to be reconnected.
Two constraints found the hard way: llama.cpp enables thinking by default on Qwen3.5 templates, so the template ships with all reasoning handling removed and tokenizer_config.json carries no chat_template key (gguf-py prefers it over the jinja file). And mtmd letterboxes images with black padding where the HF processor stretches them, so OCR training uses the llama.cpp preprocessing for 75 percent of samples.
Training
One script (train.py), one cosine schedule of 17000 steps at batch 32 x 2048, warmup 200, Muon at 2e-3 on the 2D hidden matrices and AdamW at 1e-3 on embeddings and norms, weight decay 0.01, fp32 master weights with bf16 autocast. Steps 0 to 6000 are text only, steps 6000 to 17000 add synthetic OCR images on 35 percent of packed documents. Fused Liger cross entropy on assistant spans only, documents packed whole and never truncated. 1.1B tokens, 2 hours on one RTX PRO 6000 at 150k tokens per second in the text phase.
Data mix in tokens: general chat from smol-smoltalk 38 percent, single turn tool calling from apigen 7 percent, Hermes function calling 3 percent capped at a few epochs, nvidia/Nemotron-Agentic-v1 tool calling trajectories under 2048 tokens with reasoning stripped 27 percent, a procedural generator of multi step tool trajectories 23 percent (synth_tools.py), and 1.5 percent of documents shaped like the CI scenarios themselves (ci_shapes.py, built from the test definitions).
The generator produces random tool schemas with distractor examples in the descriptions, chains of 2 to 7 steps where every argument is copied either from the user prompt or from a named field of an earlier tool result, parallel calls, threshold gates, conditional last steps that are skipped half the time, follow up turns, vague prompts with a single tool, a code execution tool with real Python, weather lookups with the location copied verbatim, questions about the tools answered in plain text, and random system prompts. Entity and tool names never overlap the CI tests.
What we measured and what failed
The starting point scored 4/10 on scripts/server-test-function-call.py. Its training data had no document with two or more tool calling turns at all: the Hermes "multi turn" set was multi turn in chat only, and a 7000 character filter had dropped the long ones. The 644 surviving documents were repeated 39 times. That was the whole problem, not model capacity: a 15 minute warm restart with the first procedural trajectories already went to 6/10 and copied unseen domain names correctly.
Things that did not work, all measured on the same script with five fixed seeds and on the 19 server tests:
- Logit distillation from Qwen3.5-2B and from the 0.8B parent. Lower cross entropy on every held out set, and an agent that loops: after every tool result the student calls the same tool again and never answers. Measured on held out Nemotron trajectories, the parent puts
<tool_call>as argmax after a tool result 87 percent of the time where the gold answer is a call 46 percent of the time, and it puts 100 percent of its mass on<think>at the start of every assistant turn. Masking the think tokens fixed the empty answers, not the looping. Distillation improves the language model and degrades the agent at this size. Dropped. - A longer joint phase (17k instead of 11k steps). OCR improved, chat improved, tool calling collapsed to 4/10 and the MTP draft stopped matching the target. Text and images compete for 82M parameters.
- 8 layers instead of 6. Best language and OCR losses of the series, no gain on tool calling, which is decided by the data mix, not by capacity.
- Heavier shares of the hardest pattern (a gate on two quotes followed by heterogeneous tools). 4 percent of the mix passed the failing scenario in one run and broke single turn behavior everywhere else; 1 percent did neither.
- Anything evaluated with sampling. The script does not set a temperature; a 95M model sampled at 0.8 will drop a character of a 6 token domain name from time to time. A CI fixture has to be evaluated greedy, and the server tests are.
What worked: the procedural generator, Nemotron for real trajectories, system prompts in the synthetic data (the server tests always send one), harder OCR strings, whole document packing, one clean schedule from the pruned weights instead of warm restarts, one variable per run, and finally training on the CI shapes themselves. A fixture tests the server, not the model's intelligence: being predictable on what the CI asks is the point. The rule for maintainers is simple: add the shape of a new test to ci_shapes.py and rerun run.sh.
Results
| checkpoint | script greedy | script, 5 seeds | server tests |
|---|---|---|---|
| initial version | 4/10 | 2 to 5 | not runnable |
| clean SFT, generator v1 | 8/10 | 7.2 | 10/19 |
| + system prompts, code, weather, OCR hardened | 8/10 | 7.6 | 18/19 |
| + CI shapes (this release) | 10/10 | 10, 10, 10, 10, 10 | 19/19 |
Held out cross entropy on assistant tokens for this release: smoltalk 1.44, apigen 0.06, hermes multi turn 0.80, Nemotron 0.80, OCR 1.48. The general losses are a little worse than the best general checkpoint (smoltalk 1.37, hermes 0.61): the price of reliability on the CI shapes.
Reproduce
pip install --index-url https://download.pytorch.org/whl/cu128 --extra-index-url https://pypi.org/simple -r src/requirements.txt
export LLAMA_CPP=/path/to/llama.cpp
python3 dl.py # datasets
python3 prune_model.py --layers 0,11,14,19,22,23 --min-count 600 # -> ./pruned
./run.sh sft # render, synth, tokenize, text phase, joint phase
./ci.sh ckpt/sft-joint sft # GGUF + function call script, greedy and seeds
Limitations
Chat is a toy, world knowledge is close to zero, OCR reads words it knows and guesses the rest, and copies of long rare strings can fail under sampling. Use greedy decoding. This model exists so that llama.cpp can test tool calling, vision and speculative decoding on every pull request with a 190 MB download.
- Downloads last month
- 305
8-bit
16-bit