Artifacts and Workloads
Content-addressed weights, serving configuration, workload vs deployment distinction.
Wingsmith separates what you’re serving (the weights) from how you’re serving it (the configuration). An artifact is immutable model weights. A workload is a specific way to serve those weights: the engine, parallelism, context length, cache settings, and device shape, declared once and placed across clusters.
Artifacts
An artifact is content-addressed model weights with one or more storage locations. Artifacts are immutable: you register them once, and their identity is their content.
Each artifact has:
digest: Content hash (prefixhf:for HuggingFace sources, computed from the manifest)name: Display nameslug: Lowercase model identifier, derived from the source (e.g.,moonshotai/kimi-k3)locations[]: Where the weights live (e.g.,hf://moonshotai/Kimi-K3,s3://bucket/path)format: Weight format (safetensors,pytorch,gguf)size_bytes: Total size of weight filesparameter_count: Number of parametersverified: Whether the digest is a hash (true) or a name (false)
Content addressing
The digest is computed from the HuggingFace manifest, not by downloading the weights. For each weight file, the Hub publishes an LFS sha256 in its file tree. Wingsmith hashes the sorted path:oid lines of those weight files, producing a digest that identifies the weights without transferring a terabyte.
This means:
- Registering an artifact costs one API call, not hours of download.
- A README edit, tokenizer change, or generation config tweak doesn’t mint a new artifact. The weights are the same.
- Two workloads referencing the same digest serve the same weights, even if the repository has moved.
If a weight file has no LFS oid (it’s not content-addressed at the source), the artifact is verified: false and its digest is a name, not a hash.
Sources and locations
An artifact’s locations array holds where to fetch the weights. Common forms:
hf://org/model(HuggingFace Hub, optionally with@revision)s3://bucket/pathgs://bucket/pathaz://container/path
Multiple locations are supported for redundancy or regional caching. The operator tries them in order.
When you create an artifact with POST /v1/artifacts, you provide a source (e.g., hf://moonshotai/Kimi-K3), and the plane resolves the manifest, computes the digest, and stores the artifact. The slug is derived from the source automatically: moonshotai/kimi-k3 for that example.
Adapter artifacts
Artifacts can be LoRA adapters. An adapter artifact references a base artifact and holds the delta weights. Workloads can attach adapters, and the operator loads them alongside the base model.
Adapters are first-class artifacts with their own digests and locations. They’re not a special case in the serving path.
Workloads
A workload is what you asked to run, globally. It’s the umbrella object that says “serve these weights this way, with this scale, in these regions.” A workload has many deployments (one per cluster it runs on), but a workload itself is plane-only and never syncs to a cluster.
Each workload carries:
artifact: Which weights to serve (by artifact ID)serving: A block describing how to serve themkind:service(only value accepted today;taskandgangare reserved)priority_class: Named priority for schedulingscale: Total min/max/desired replica counts across all clustersproject: Which project owns this workload
The serving block
The serving block is the reproducible way to serve an artifact. It includes:
engine:vllm,sglang, ortrt-llmengine_runtime_version: Version of the engineengine_runtime_image: Container image for workersfrontend_image: Container image for the Dynamo frontendquantization:awq,gptq,fp8, etc.dtype: Data type (auto,float16,bfloat16)parallelism:{tensor_parallel, pipeline_parallel, expert_parallel}context_length: Maximum sequence lengthmax_batch_tokens: Batch size in tokenskv_cache_dtype: Data type for KV cachekv_cache_fraction: Fraction of VRAM for KV cacheprefix_cache_enabled: Whether to use prefix cachingrouter: Dynamo frontend routing mode and configresources: Device shape and memory requirementengine_args: Additional flags passed to the engine
This block describes the engine process that runs. Two workloads declaring the same block run the same process and have the same serving_digest.
Context length
context_length lives on the workload, not on the artifact, because it determines the device shape. A 70B model at 32k context fits on four H100s; at 128k it needs eight. Putting context on a catalog entry forces one way of serving everything. Wingsmith lets you declare multiple workloads over one artifact, each with a different context length and device count.
Serving digest
The serving_digest is a hash of the serving block fields that reach the engine, prefixed svg_. It’s derived on write and read-only. Two workloads with the same digest run identical processes.
The digest includes artifact, engine, engine_runtime_version, quantization, dtype, parallelism, context_length, and cache settings. It excludes labels, served_model_name, and imported_args, because those don’t change what runs.
Kind: service, task, gang
Every workload has a kind. Today only service is accepted:
service: Long-running inference server. Scales by replica count. Has an endpoint.task: Reserved. Would run to completion (batch inference).gang: Reserved. Would require co-scheduled ranks (multi-node training).
The operator only renders service workloads. POST /v1/workloads with kind: task or kind: gang is refused because no renderer exists for them yet.
Workload vs Deployment
A workload is global. A deployment is one workload on one cluster.
| Workload | Deployment | |
|---|---|---|
| Scope | Plane-wide | One cluster |
| Created by | You (API call) | Placement (when it expands a workload) |
| Carries | Total scale, artifact, serving config, constraints | This cluster’s share of scale, admission phase, authority |
| Synced to cluster | No | Yes |
The canonical example: “run gpt-20b in asia and in europe” is one workload, two deployments.
Placement expands a workload into deployments by picking clusters and dividing the total replica count. The workload says desired: 6; placement might give iad-1 four replicas and fra-1 two.
Each deployment has its own admission phase, so a workload can be running in one cluster and queued on quota in another. A workload’s overall phase is derived from its deployments.
Scale
Scale is declared as a total and read as a share:
- Workload carries
min,max,desired: totals across all clusters. - Deployment carries
min_replicas,max_replicas,desired_replicas: this cluster’s share.
Placement divides the total. Nothing else sums, scales, or re-derives. The default fill strategy is pack (fill one cluster before opening another), not spread. Spreading is opt-in via strategy: spread.
Scaling a workload is an operation, not a field write, because it’s long-running and can fail. POST /v1/operations with type: workload.scale and {"desired": n}. A scale sits pending if quota blocks it.
Weight cache vs KV cache
Don’t confuse the two:
Weight cache: Model files on disk, shared across replicas. Declared as a warm-set per cluster; residency observed as CacheEntry.
KV cache / prefix cache: Attention state, per replica. Configured in serving.kv_cache_dtype, kv_cache_fraction, prefix_cache_enabled. This is runtime behavior, not a residency question.
The warm-set is a declaration next to demand. KV config is a field in the serving block.