Build & serve your SLM
Serve your SLM
Serve via Ollama, vLLM, or llama.cpp
- Fine-tuning with LoRA and QLoRA (browse)
- vLLM (browse)
- Quantization for inference (browse)
- Ollama (browse)
- Hugging Face (browse)
- Serving and streaming (browse)
- Fine-tune vs prompt vs RAG: a decision framework (example)
- Open-source stack for an AI feature: vLLM, Ollama, and graph orchestrators (example)
Learning objectives
- Serve via Ollama, vLLM, or llama.cpp
- Expose an OpenAI-compatible endpoint
- Hit it from the same client code as Talk to models
Owning weights includes owning a serve path
A LoRA adapter in a folder is not a product. Serving turns base + adapter into an HTTP API your applications call — ideally OpenAI-compatible so code from the chat APIs lesson swaps endpoints with one config change.
Options for this course:
| Stack | Best for | OpenAI compat |
|---|---|---|
| Ollama | Laptop dev, quick Modelfile | /v1/chat/completions |
| vLLM | Throughput, multi-user | Native OpenAI route |
| llama.cpp server | CPU/Apple Silicon edge | OpenAI-style API |
| TGI | HF ecosystem production | Supported |
Pick one matching your hardware; document tradeoffs in README.
Callout — same client, different base_url:
OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")— verify with one script from m4.
Ollama path (developer default)
Create Modelfile:
FROM llama3.2:3b
ADAPTER ./m6/adapters/ticket_classifier_v1
SYSTEM Classify support tickets into billing, shipping, account, bug, other. Reply label only.ollama create ticket-classifier -f Modelfile
ollama serve
curl http://localhost:11434/v1/chat/completions -d '{...}'Convert HF adapter to GGUF if stack requires — follow tool docs for your base.
vLLM path (GPU throughput)
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.2-3B-Instruct \
--enable-lora --lora-modules ticket=m6/adapters/ticket_classifier_v1Hit http://localhost:8000/v1/chat/completions with model: ticket.
Tune --max-model-len to match training context.
Integration with existing prompt pack client
Refactor client from lesson 4.1:
def get_client():
if os.getenv("USE_LOCAL_SLM"):
return OpenAI(base_url=os.environ["SLM_BASE_URL"], api_key="local")
return OpenAI()Run same cases.jsonl through local SLM; compare outputs to teacher in eval harness.
Operational basics
Health: GET /health or lightweight completion on startup probe.
Concurrency: Ollama serializes some paths; vLLM batches — load-test if multiple users.
Model load time: Cold start seconds to minutes — note in OPS.md.
Versioning: Tag served artifact ticket_classifier_v1 aligned with adapter folder and report card.
Rollback: Keep previous Modelfile / LoRA module registered as ticket_classifier_v0.
Security on localhost
Even local servers bind 0.0.0.0 in some configs — firewall or bind 127.0.0.1 only. No auth on default Ollama; do not expose raw to internet.
Logging
Log request id, model version, latency, token counts. Redact user content if logs leave machine.
Callout — template at serve time: Ensure server applies chat template identical to training — some servers need explicit
--chat-templateflags.
Docker packaging for serve
Wrap Ollama or vLLM in Docker for reproducible demos:
# conceptual — pin image versions
FROM ollama/ollama:latest
COPY m6/adapters/ticket_classifier_v1 /adapters/
COPY m6/serve/Modelfile /root/ModelfileDocument GPU passthrough (--gpus all) vs CPU-only fallback. Course reviewers on Apple Silicon may use Ollama native without Docker — note both paths in SERVE.md.
Healthcheck in compose:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
interval: 10sLoad testing basics
Even a simple loop helps:
for i in $(seq 1 50); do curl -s -o /dev/null -w "%{time_total}\n" ... & done; waitRecord p50/p95 latency at concurrency 1 vs 4. Ollama may queue; vLLM batches — choose stack matching expected concurrency.
Adapter hot-swap
Run multiple LoRA adapters on one base in vLLM:
--lora-modules ticket=m6/adapters/ticket_v1,summarize=m6/adapters/summarize_v1Client passes model name per request — useful when portfolio grows multiple narrow tasks without loading full bases repeatedly.
Document memory footprint: one base + two adapters vs two merged models — merged simplifies ops, adapters simplify experimentation.
Troubleshooting serve mismatches
When local outputs diverge from training notebook:
| Symptom | Check |
|---|---|
| Gibberish labels | Chat template mismatch |
All other |
System prompt not loaded in Modelfile |
| Slow first request | Cold load; warm with dummy completion |
| 404 model | Ollama model name typo vs client model= field |
Keep a smoke_expected.jsonl with 3 inputs and gold labels — run after every serve config change.
Environment variables reference
Document in SERVE.md:
| Variable | Example | Purpose |
|---|---|---|
SLM_BASE_URL |
http://localhost:11434/v1 |
OpenAI client override |
USE_LOCAL_SLM |
1 |
Toggle in client factory |
SLM_MODEL |
ticket-classifier |
Modelfile name |
Consistent env naming lets m4 scripts run unchanged against local or cloud endpoints.
llama.cpp and Apple Silicon
On Mac, many students serve GGUF exports:
./llama-server -m ticket-classifier-Q4_K_M.gguf --port 8080Convert HF adapter merge to GGUF via llama.cpp conversion scripts or Ollama import — document exact command chain in SERVE.md. Metal acceleration improves token/sec; still log p95 for batch=1 interactive calls.
CPU-only Linux fallback uses same GGUF with -ngl 0 — slower but valuable when no GPU available.
Streaming responses
OpenAI-compatible servers support stream: true — wire streaming in smoke client optional stretch. Streaming improves perceived latency for chat UIs; classification tasks often skip streaming for simpler parsing and easier golden-test assertions.
Engineering problem (staff framing)
Serving is the product: batching, KV-cache, OpenAI-compatible API, health checks.
Diagram — Serve path
flowchart LR
Client --> Gateway --> Engine[vLLM/TGI/llama.cpp]
Engine --> GPU
Gateway --> Met[Metrics]
Precise definitions & mental model
Continuous batching, p50/p95 TTFT/TPOT, autoscaling.
Tradeoffs — when to use what
Throughput vs latency SLOs; quant quality.
Failure modes (interview + on-call)
OOM at context max; no queue limits; noisy neighbor GPUs.
Production & OSS practices
Load tests; circuit breakers; model warm pools.
Micro-project: Local OpenAI-compatible API
In m6/serve/:
- Serve your adapter via Ollama, vLLM, or llama.cpp with OpenAI-compatible route.
smoke.py— reuse m4 client patterns against local endpoint for 3 cases.SERVE.md— start commands, ports, hardware, cold start notes.- Optional: Dockerfile wrapping serve stack for reproducibility.
Checklist
- OpenAI client works with only base_url change
- Model version documented
- Smoke script passes
- Serve docs include stop, start, and rollback
ShipAI delivery model is: