Skip to content

winml build

Run the entire winml-cli pipeline (export → optimize → quantize → compile) in one command.

When to use this

Use winml build when you want to go from a Hugging Face model ID (or an existing .onnx file) to a deployment-ready artifact in a single invocation, without manually chaining winml export, winml optimize, winml quantize, and winml compile. A build config file — generated by winml config — controls every stage of the pipeline.

Synopsis

$ winml build [options]

Flags

Flag Short Type Default Description
--config -c path None WinMLBuildConfig JSON file, generated by winml config. If omitted, config is auto-generated from -m.
--model -m string None Hugging Face model ID or path to an existing .onnx file.
--export-type choice generic Output selector: generic builds the stock single/composite ONNX model; optimized builds the family's registered runtime-optimized recipe (today the onnxruntime-genai NPU bundle) for the resolved --ep/--device. optimized fails fast if the family has no recipe or the resolved target is not one the recipe supports.
--output-dir -o path None Directory for all build artifacts. Mutually exclusive with --use-cache.
--use-cache/--no-use-cache flag false Store artifacts in the winml-cli global cache (~/.cache/winml/). Mutually exclusive with --output-dir.
--rebuild/--no-rebuild flag false Overwrite existing artifacts and re-run the full pipeline.
--quant/--no-quant flag true Run the quantization stage (use --no-quant to skip), overriding the config.
--no-compile / --compile flag None Override compilation. --compile forces enable (config must have a compile section). --no-compile forces skip. Default: inherit from config.
--optimize/--no-optimize flag true Run the optimization stage (use --no-optimize to skip).
--ep string None Target execution provider for the analyzer (e.g., qnn). Falls back to the compile config EP if not set.
--device -d string auto Target device for the analyzer (e.g., npu, gpu). Default: auto (auto-detect).
--precision -p string auto Target precision (e.g., fp16, int8, w8a16). With -c, applied only when --device or --precision is passed.
--analyze/--no-analyze flag true Run the analyzer loop during build (use --no-analyze to skip).
--max-optim-iterations integer None Maximum autoconf re-optimization rounds (3 enforced internally when not set). --no-analyze implicitly sets this to 0.
--shape-config path None JSON shape overrides used while auto-generating a Hugging Face export config, for example {"height": 480, "width": 480}. Only valid when --config is omitted.
--input-specs path None JSON input tensor specs to merge into the Hugging Face export config. Symbolic string dimensions infer dynamic axes.
--export-config path None JSON ONNX export config overrides to merge into the Hugging Face export config.
--dynamic-axes path None JSON dynamic axes mapping for Hugging Face ONNX export, for example {"input_ids": {"0": "batch", "1": "sequence"}}.
--trust-remote-code/--no-trust-remote-code flag false Allow executing custom code from model repositories. Use only with trusted sources.
--allow-unsupported-nodes/--no-allow-unsupported-nodes flag false Allow unsupported nodes to remain in the graph instead of failing the build.
--help -h flag Show this message and exit.

How it works

winml build reads a WinMLBuildConfig JSON file (from winml config) that encodes device, precision, export, quantization, and compilation settings. When -m is a Hugging Face model ID, the full pipeline runs: export → optimize → quantize → compile. When -m points to an existing .onnx file, the export stage is skipped and the pipeline starts at optimization. After compilation, an optional analyzer loop (--max-optim-iterations) re-evaluates graph quality and applies further passes; --no-analyze disables it for a deterministic single-pass build. Individual stages can be suppressed with --no-quant, --no-compile, and --no-optimize without touching the config file.

Reproducible CI/CD builds

The config file is a portable, self-contained pipeline specification. Check it into source control and invoke winml build -c config.json in CI to produce identical artifacts without manual flag management. Set "auto": false in the config to disable the autoconf discovery loop for fully deterministic output.

Genai bundles for decoder LLMs (NPU + QNN)

For a registered decoder-LLM family (currently Qwen3), --export-type optimized switches winml build from the stock per-model ONNX output to a complete onnxruntime-genai bundle directory:

File Role Precision
ctx.onnx Transformer prefill graph w8a16 (default)
iter.onnx Transformer decode graph w8a16 (default)
embeddings.onnx Token embedding table (CPU) fp32
lm_head.onnx Vocab projection (CPU) w4a32
genai_config.json + tokenizer onnxruntime-genai runtime metadata
# One command: HF decoder LLM → full onnxruntime-genai bundle
winml build -m Qwen/Qwen3-0.6B -o out/qwen3-bundle --export-type optimized

--export-type optimized resolves --ep/--device the same way a generic build does — an explicit value is honored, otherwise the host is probed — and then builds the recipe for that resolved target. On an NPU host the command above needs no flags; on a host without the accelerator the resolved target has no recipe and the build fails fast (naming the ep/device) instead of silently producing a generic model. Pin --ep qnn --device npu to build the bundle anywhere (e.g. CI), since an explicit target skips host detection. An unregistered family, a pre-exported .onnx input, or module mode also fail fast. --output-dir is required (the bundle is a directory) and --use-cache is not supported. Use --precision to override the transformer precision; the CPU companions always use their bundle-standard precisions.

Backward-compatible shortcut

When --export-type is omitted, a registered family with an explicit --ep qnn on an NPU target still routes to its optimized bundle. The NPU target may be explicit (--device npu) or resolved from auto — whether --device auto is typed or --device is left off entirely:

winml build -m Qwen/Qwen3-0.6B -o out/qwen3-bundle --device npu --ep qnn
# or let device auto-detection pick the NPU (--device may be omitted):
winml build -m Qwen/Qwen3-0.6B -o out/qwen3-bundle --ep qnn

--export-type generic always forces the stock composite build, even for a registered family on the NPU.

See Qwen3 — Genai Bundle for the full walkthrough.

Examples

# Full pipeline: HF model → export → optimize → quantize → compile
winml build -c config.json -m microsoft/resnet-50 -o output/
winml build
  Config:     config.json
  Model:      microsoft/resnet-50
  Output:     output/

  export       done  (28.3s)
  optimize     done  (4.1s)
  quantize     done  (6.8s)
  compile      done  (14.2s)

  Build complete in 53.4s
  Final artifact: output/resnet50_ctx.onnx
# Start from a pre-exported ONNX file (skips export stage)
winml build -c config.json -m resnet50.onnx -o output/
# Export and optimize only — skip quantization and compilation for quick testing
winml build -c config.json -m bert-base-uncased -o output/ \
  --no-quant --no-compile
# Force a clean rebuild, overwriting any cached artifacts
winml build -c config.json -m facebook/convnext-tiny-224 -o output/ --rebuild
# Use the global cache and cap optimizer iterations for faster turnaround
winml build -c config.json -m microsoft/resnet-50 \
  --use-cache --max-optim-iterations 1
# Build with a dynamic batch axis during Hugging Face export
winml build -m microsoft/resnet-50 -o output/ \
  --dynamic-axes dynamic_axes.json

Common pitfalls

  • Either --output-dir or --use-cache is required; they are mutually exclusive. Omitting both raises an error immediately.
  • --use-cache is not supported in module mode. When the config is a JSON array (module mode), only --output-dir is accepted.
  • The config file must come from winml config. The schema is strict; unknown keys are rejected.
  • Export-shape flags only affect Hugging Face export. They are rejected for pre-exported ONNX inputs because the export step has already happened.
  • Existing artifacts are reused by default. Pass --rebuild to force a fresh run after changing the config.
  • Genai bundles require --output-dir, not --use-cache. An optimized decoder-LLM bundle (--export-type optimized, or the --device npu --ep qnn shortcut) writes a directory of components and rejects --use-cache.

See also