Guidance for working in this repository.
It is open source. Keep it that way: no personal Modal workspace names, account IDs, hostnames, IP addresses, real names, emails, or private/employer data in code, comments, docs, or test fixtures. App names, volume names, and secret names are generic on purpose so anyone can deploy on their own account. If you port code from elsewhere, scrub it.
gemma4/, qwen/, and pipeline/ are separate uv projects. uv, modal, and
pytest all run per-project — there is no tooling at the repo root. cd into
the project before running anything.
| Project | Entry point |
|---|---|
gemma4/ |
SGLang serving for the Gemma 4 family, solo + concurrent |
qwen/ |
SGLang serving for Qwen3.6-27B and 35B-A3B, solo + concurrent |
pipeline/ |
vLLM serve → eval → corpus → SFT, over the Chinook SQL agent |
Each serving project is built from the same parts. Learn it once.
_common/model_registry.py—ModelSpec(HF repo, pinned revision, GPU class, native context, MTP drafter) keyed by short name. The single source of truth.serve.pycallsget("<short>")._common/sglang_common.py—make_sglang_image()(pinnedlmsysorg/sglangbase) andbuild_serve_cmd()(the model- and shape-agnostic argv builder forsglang.launch_server). MTP speculative profiles live here too._common/health.py—/healthpolling and the memory-snapshot helpers (release_/resume_memory_occupation,send_warmup_request).pipeline/_common/addsvllm_common.py, thegemma4_parser.pyclient-side tool-call parser, andeval_scoring.py.
To tune a deployment, edit only the constants block at the top of its
serve.py. build_serve_cmd() is deliberately model-agnostic; keep it that
way.
-
Image build order is fixed. Base SGLang image →
add_local_file(..., copy=True)for each baked chat template →add_local_python_source("_common")as the last step. Modal forbids further build steps after a non-copyadd_local_*, so_commongoes last and the helpers never calladd_local_*themselves. -
Ingress is Modal's public web endpoint. There is no Tailscale. The server binds
0.0.0.0:8000.@modal.web_server(port=8000)publishes the public*.modal.runURL and is the innermost decorator. No auth is baked in — seedocs/securing-endpoints.md. The health/warmup helpers reach the server over the loopback; that's the only place127.0.0.1should appear.
- Single-GPU deployments use
@app.clswith@modal.enter(snap=True/False)andenable_memory_snapshot=True+experimental_options={"enable_gpu_snapshot": True}to skip CUDA-graph capture and warmup on cold starts after the first. - Multi-GPU deployments (the 26B-A4B at TP=2) use a plain
@app.function— snapshots are incompatible with multi-GPU. @modal.concurrent(max_inputs=, target_inputs=)sets the SGLang scheduler ceiling. MatchCUDA_GRAPH_BStomax_running_requests.
- Add a
ModelSpecto the project's_common/model_registry.py. - Copy the nearest sibling's
serve.py(dense → dense, MoE → MoE) and changeSPEC = get("<short>")plus the constants block. No code below the constants should need to change. - Pick the chat template. For Gemma, the 31B/26B/12B upstream templates are byte-identical, so the custom fork applies to all three; E2B/E4B use their own upstream. If you change a template, re-run the conformance suite.
- The Triton attention backend is mandatory across the family (fixed
head_dim=256, 512-wide global head). FlashInfer/trtllm reject it. The wrong backend produces garbled output, not an error — the boot health check on the first real request is what catches a misconfigure. - FP8 KV cache (
fp8_e5m2) is the default for the dense models at long context. - The 12B has no published MTP drafter, so it serves without speculative decoding
(
draft=None,MTP_OFF).
# Chat-template conformance (no GPU, renders Jinja locally):
cd gemma4 && uv run --group dev pytest tests/ -v # 20 tests
cd qwen && uv run --group dev pytest tests/ -v # 39 tests
# Pipeline eval rubric unit tests:
cd pipeline && uv run --group dev pytest eval/test_eval_scoring.py -vgemma4/README.md,qwen/README.md,pipeline/README.md— per-project guides.gemma4/chat_templates/TESTING.md— how the template fork is tested.docs/securing-endpoints.md— auth options for the public endpoint.docs/deploy-byo-cloud.md— running off Modal.