Spaces:
Running on Zero
Running on Zero
| # Modal model serving | |
| Serverless, OpenAI-compatible endpoints for small models, deployed on | |
| [Modal](https://modal.com). Each provider is an isolated Modal app; all of them | |
| share one battle-tested serving path (vLLM behind an autoscaling web server) so | |
| adding a model is a one-line config change. | |
| ## Layout | |
| ```text | |
| modal/ | |
| catalogue.py SINGLE SOURCE OF TRUTH (stdlib-only): ModelConfig + the | |
| per-provider model lists + PROVIDERS (app names) + URL helpers. | |
| Shared with the engine, which reads it by path. | |
| service.py Reusable serving layer: image + vllm command, register_model() | |
| (provider-agnostic). Imports ModelConfig from catalogue. | |
| registry.py Back-compat re-export of the catalogue's model lists. | |
| app_nvidia.py App "nvidia-llms" β Nemotron 3 Nano 4B + 30B, Cascade 14B Thinking. | |
| app_openbmb.py App "openbmb-llms" β MiniCPM4.1-8B + MiniCPM-o 4.5. | |
| app_google.py App "google-llms" β Gemma 4 12B + 26B. | |
| client.py OpenAI-SDK smoke-test client for any endpoint. | |
| openapi.yaml Checked-in OpenAPI 3.1 spec for the served API surface. | |
| pyproject.toml uv workspace member (deploy/client tooling; non-package). | |
| requirements.txt Deploy/client tooling (vLLM lives in the container image). | |
| docs/ | |
| deploying.md Deploy, configure, auth, GPU sizing, engine integration. | |
| openapi.md API reference: endpoints, auth, examples, client generation. | |
| modal-llms.txt In-repo mirror of Modal's docs index, kept updated. | |
| ``` | |
| Each running endpoint also self-documents at `/docs` (Swagger UI) and | |
| `/openapi.json` (live spec). See [`docs/openapi.md`](docs/openapi.md). | |
| ## Models | |
| | Provider | App | Model | Endpoint name | GPU | | |
| | -------- | -------------- | --------------------------------------- | --------------------- | ------- | | |
| | NVIDIA | `nvidia-llms` | Nemotron-Cascade-14B-Thinking | `nemotron-cascade-14b` | L40S:1 | | |
| | NVIDIA | `nvidia-llms` | NVIDIA-Nemotron-3-Nano-4B-BF16 | `nemotron-3-nano-4b` | L4:1 | | |
| | OpenBMB | `openbmb-llms` | MiniCPM-o-4_5 (omni) | `minicpm-o-4-5` | L40S:1 | | |
| | OpenBMB | `openbmb-llms` | MiniCPM4.1-8B | `minicpm-4-1-8b` | L40S:1 | | |
| | Google | `google-llms` | gemma-4-26B-A4B-it | `gemma-4-26b` | H200:1 | | |
| | Google | `google-llms` | gemma-4-12B | `gemma-4-12b` | L40S:1 | | |
| Every endpoint stays under the hackathon's 32B cap; `nemotron-3-nano-4b` is the | |
| β€4B Tiny Titan tier. | |
| ## Quick start | |
| ```bash | |
| pip install -r modal/requirements.txt | |
| modal token new | |
| modal secret create huggingface-secret HF_TOKEN=hf_xxx # for gated repos | |
| modal deploy modal/app_nvidia.py | |
| python modal/client.py \ | |
| --base-url https://<workspace>--nvidia-llms-nemotron-3-nano-4b.modal.run/v1 \ | |
| --model nvidia/NVIDIA-Nemotron-3-Nano-4B-BF16 \ | |
| --prompt "Hello from the wood." | |
| ``` | |
| See [`docs/deploying.md`](docs/deploying.md) for configuration, auth, GPU | |
| sizing, and how to add models/providers or wire endpoints into the engine. | |
| ## Why this shape | |
| - **Each provider in its own app** β independent deploy, scaling, and blast | |
| radius; one provider's outage or redeploy never touches another. | |
| - **Scalable** β serverless autoscaling, input concurrency, a shared weight | |
| cache (pull once, warm everywhere), and per-model `min_containers` warm pools. | |
| - **One serving path** β Modal's canonical vLLM recipe (an autoscaling | |
| `@app.function` launching `vllm serve` behind a `@modal.web_server`), written | |
| once in `service.py`. No bespoke per-model lifecycle to break (ADR-0034). | |
| - **Fast cold starts on demo day** β the shared `vllm-cache` Volume persists the | |
| torch.compile / CUDA-graph artifacts so only the first container compiles, and | |
| `MODAL_LLM_KEEP_WARM=1` at deploy time pins one warm container per tier model. | |
| See [`docs/deploying.md` β Cold starts](docs/deploying.md#cold-starts). | |
| - **Extensible** β add a model = one `ModelConfig` in `catalogue.py`; add a | |
| provider = one `Provider` entry + one app file. The serving path is written once | |
| in `service.py`, and the engine picks up the new model with no edits (it reads | |
| the same `catalogue.py`). | |
| - **Configurable per task** β GPU, context length, concurrency, tool/reasoning | |
| parsers, and multimodal limits are all data in `catalogue.py`. | |
| - **One source of truth** β `catalogue.py` describes every model once; both the | |
| serving apps and the engine read it, so the served id and endpoint URL never | |
| drift between deploy and call sites (ADR-0019). | |