Wingsmith
Concepts

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 (prefix hf: for HuggingFace sources, computed from the manifest)
  • name: Display name
  • slug: 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 files
  • parameter_count: Number of parameters
  • verified: 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/path
  • gs://bucket/path
  • az://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 them
  • kind: service (only value accepted today; task and gang are reserved)
  • priority_class: Named priority for scheduling
  • scale: Total min/max/desired replica counts across all clusters
  • project: Which project owns this workload

The serving block

The serving block is the reproducible way to serve an artifact. It includes:

  • engine: vllm, sglang, or trt-llm
  • engine_runtime_version: Version of the engine
  • engine_runtime_image: Container image for workers
  • frontend_image: Container image for the Dynamo frontend
  • quantization: awq, gptq, fp8, etc.
  • dtype: Data type (auto, float16, bfloat16)
  • parallelism: {tensor_parallel, pipeline_parallel, expert_parallel}
  • context_length: Maximum sequence length
  • max_batch_tokens: Batch size in tokens
  • kv_cache_dtype: Data type for KV cache
  • kv_cache_fraction: Fraction of VRAM for KV cache
  • prefix_cache_enabled: Whether to use prefix caching
  • router: Dynamo frontend routing mode and config
  • resources: Device shape and memory requirement
  • engine_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.

WorkloadDeployment
ScopePlane-wideOne cluster
Created byYou (API call)Placement (when it expands a workload)
CarriesTotal scale, artifact, serving config, constraintsThis cluster’s share of scale, admission phase, authority
Synced to clusterNoYes

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.