Serve a model
Enumerate the model catalog and deploy a servable model from it with urun serve — no app.py to write
urun serve deploys a model from the model catalog without you having to write a custom App. It resolves a catalog entry, creates a templated App pinned to that entry, and deploys it through the normal urun deploy pipeline.
Enumerate the catalog
urun serve catalogPrints the resolvable matrix — every (model, variant, GPU) placement the catalog knows about, with its engine, weights size, and latency targets:
MODEL VARIANT GPU ENGINE TARGETS (ttft/tps)
qwen-coder fp8 l4:1 vllm 600ms / 120
glm-5.2 UD-IQ2_M l4:1 llamacpp 60000ms / 3
diffusiongemma-26b Q4_K_M l4:1 llamacpp-diffusion 4000ms / 40
qwen-coder-480b fp8 b200:8 vllm 350ms / 450Targets are goals, not benchmarks
The TARGETS column shows the placement's design goals (ttft_ms / output_tps), not measured throughput. The GGUF tiered-offload rows carry deliberately conservative targets — they trade speed for fitting enormous models on small GPUs.
Serve a model
urun serve <id>[:<variant>] [--gpu SPEC]urun serve qwen-coder # default variant, first placement
urun serve qwen-coder:fp8 # pin the variant
urun serve qwen-coder:fp8 --gpu l4:1 # pin variant + GPU placement
urun serve glm-5.2:UD-IQ2_M --gpu l4:1 # GGUF tiered offload on a single L4What happens:
resolve catalog entry → map entry → serve config → create templated App
→ urun deploy- Resolve —
<id>with no variant resolves to the model's default variant; with no--gputhe variant's first placement is used. The resolved row carries everything needed to run the model: engine configuration, weights size, latency targets. - Create — a templated serve App is created, pinned to that exact entry. You don't author or maintain it.
- Deploy — the templated App ships through the standard deploy pipeline.
Flags
| Flag | Default | Effect |
|---|---|---|
--gpu SPEC | the variant's first placement | Select a specific GPU placement (e.g. l4:1, h100:1, b200:8) |
--name NAME | derived from <id>:<variant> | Override the deployed App name |
--no-wait | off | Finalize the deployment but don't poll for readiness |
After it's up
What you get is a live LLM session — the same session primitive every uRun App gets. The client attaches once, the model stays warm across turns, and token deltas stream back the moment they are generated. The published client path is @urun-sh/core: session.requestStream(...) streams a reply token-by-token, and session.complete(...) resolves with the full body when you don't need deltas. Payloads use the industry-standard Chat Completions / Responses shape (OpenAI-compatible), so existing tooling drops in unchanged.
import { App } from '@urun-sh/core'
const session = App('urun-serve', { baseUrl, orgId, jwt }).serve()
// Streaming — one string per token delta, ends on the terminal response
for await (const delta of session.requestStream({
model: 'qwen-coder',
messages: [{ role: 'user', content: 'explain MoE routing' }],
stream: true,
})) {
process.stdout.write(delta)
}
// Convenience — resolves with the full `chat.completion` body when you don't need deltas
const res = await session.complete({
model: 'qwen-coder',
messages: [{ role: 'user', content: 'write quicksort in python' }],
})The client and the Python serving runtime share one contract, riding the session's own doc + stream primitives: the shared registry on doc key llm, plus a dedicated consumer-addressed response stream per call (llm-resp:<id>) carrying token deltas and a terminal body.
Next
urun.model/urun.serve(SDK) — the Python equivalent- Manage the deployed App — status, scale, retire