Skip to content

perf: run block-wise int4/int8 QMoE CPU experts on the MLAS QNBit GEMM kernels - #32644

Merged
Tianlei Wu (tianleiwu) merged 11 commits into
microsoft:mainfrom
ykhrustalev:ykhrustalev/qmoe-cpu-qnbit-gemm
Sep 17, 2026
Merged

Tianlei Wu (tianleiwu) merged 11 commits into
microsoft:mainfrom
ykhrustalev:ykhrustalev/qmoe-cpu-qnbit-gemm

Conversation

@ykhrustalev

@ykhrustalev Yuri Khrustalev (ykhrustalev) commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Description

Problem
On CPU, QMoE runs block-wise 4-bit and 8-bit experts slowly. Before every matrix multiply it converts the expert weights back to fp32, and it gives each expert its own thread. During decode only top_k experts run, so most threads sit idle. On an 8B-A1B model that means about 3 tokens/s. The existing MLAS Q4 fast path doesn't solve this: it only runs on AVX-512, is off by default, and re-quantizes the weights.

Solution

  • Pack weights once at load time. Block-wise 4-bit and 8-bit experts are packed for the same MLAS kernels that MatMulNBits uses. The weights aren't re-quantized, and no fp32 copy is kept. Zero points work too, because QMoE stores them the same way MatMulNBits does.
  • Use the threads better. When fewer experts are active than there are threads, experts run one after another and each matrix multiply gets the whole thread pool.
  • New accuracy_level attribute on QMoE, with the same meaning as in MatMulNBits:
    • 4-bit: the default keeps fp32 activations. accuracy_level=4 switches to int8 activations, which is faster but slightly less accurate.
    • 8-bit: MLAS has no fp32 kernel, so int8 activations are used at every level.
  • Environment override: ORT_QMOE_CPU_QNBIT_GEMM takes precedence over the attribute.
    • fp32 forces fp32 activations, so 8-bit experts go back to the old path.
    • int8 forces int8 activations.
    • 0 turns the new path off.
    • Any other value is an error.
  • Shared pre-packed weights across sessions. The packed buffer is tagged with how it was packed. A buffer packed differently is rejected instead of being used.
  • Fallback warning. If a block-wise node can't use the new path, a warning with the reason is logged once, because the old path is about 10x slower for decode.
  • Unchanged: row-wise scales, 2-bit weights, and other execution providers.

Testing

  • New MoETest.QMoETest_CPU_Int4_BlockWise_* and QMoETest_CPU_Int8_BlockWise_* tests compare against an fp32 reference. They cover decode, prefill, bias with block size 64, fp16, accuracy_level=4, zero points, each environment variable setting, and shared pre-packed weights across two sessions. All MoETest.* and MatMulNBits* tests pass in onnxruntime_provider_test on Linux x64 (AVX-512).

  • Model benchmark:

    • Model: LFM2.5-8B-A1B (32 experts, top-4, int4 with block size 32), exported with feat: add LFM2-MoE support onnxruntime-genai#2575.
    • Machine: Xeon 8358, 30 threads, same build for every row.
    • Decode: genai greedy generation.
    • Prefill: a single InferenceSession.run.
    • Accuracy: KL divergence against the Hugging Face fp32 logits on an 81-token prompt.
    ORT_QMOE_CPU_QNBIT_GEMM decode tok/s prefill 256 tok/s prefill 16 tok/s KL vs HF fp32 top-1 agree
    0 (old path) 3.2 147 27 0.096 0.901
    unset (fp32, the default) 55.2 346 148 0.098 0.889
    int8 (accuracy_level=4) 60.7 364 103 0.106 0.889

    An earlier int8-only run on an M3 Ultra gave:

    • Decode: 4 → 125 tok/s.
    • Prefill (256 tokens): 190 → 855 tok/s.
    • KL divergence: 0.112 → 0.101.

    The fp32 default hasn't been measured on Apple Silicon yet. There it uses the NEON fp32 kernels, because KleidiAI only provides int8.

Motivation and Context

This makes block-wise quantized MoE models fast enough for CPU decode by reusing the MatMulNBits kernels instead of converting weights back to fp32 on every call. fp32 activations stay the default where MLAS supports them because int8 loses a little accuracy. Models can opt in to int8 with accuracy_level=4, as with MatMulNBits. This pairs with microsoft/onnxruntime-genai#2575, which exports LFM2-MoE models in this format.

🤖 Generated with Claude Code

…M kernels

The CPU QMoE kernel dequantized block-wise experts to fp32 and ran SGEMM,
one expert per thread, so decode (top_k active experts) left most of the
pool idle. Experts are now pre-packed once for the MatMulNBits kernels (no
re-quantization) and each expert GEMM runs on them, sequentially with the
whole pool when fewer experts are active than threads.
ORT_QMOE_CPU_QNBIT_GEMM selects fp32 (default) or int8 activations, or
disables the path.
Copilot AI balanced review requested due to automatic review settings September 16, 2026 15:09
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The 8-bit default contradicts the documented behavior, and packed-buffer sizing can overflow.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds an MLAS QNBit GEMM fast path for block-wise quantized CPU QMoE experts.

Changes:

  • Pre-packs int4/int8 expert weights.
  • Adds QNBit execution, threading, and sharing support.
  • Adds CPU correctness and sharing tests.
File summaries
File Description
moe_quantization_cpu.h Declares QNBit packed state and helpers.
moe_quantization_cpu.cc Implements packing, execution, policy, and sharing.
moe_test.cc Adds block-wise QMoE tests.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread onnxruntime/contrib_ops/cpu/moe/moe_quantization_cpu.cc Outdated
Comment thread onnxruntime/contrib_ops/cpu/moe/moe_quantization_cpu.cc Outdated
Comment thread onnxruntime/contrib_ops/cpu/moe/moe_quantization_cpu.cc Outdated
Make int8 activations opt-in (ORT_QMOE_CPU_QNBIT_GEMM=int8), matching
MatMulNBits without accuracy_level=4; 8-bit experts keep the dequantize
path by default. Record the compute type in the tagged prepacked shape
buffer and reject shared buffers packed for another compute type. Share
one shape-buffer writer across the three prepack layouts, return the
packed size from the eligibility check instead of recomputing it, pass
fp32 scales to the GEMM lambda by expert index, and only spin up the
load-time pack pool when there is more than one expert. Split the 8-bit
test into default (dequantize) and int8-activation cases.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ng on the CPU QNBit path

Add an accuracy_level attribute to QMoE with the MatMulNBits meaning so a
model can opt into int8 activations (4) instead of relying on the
ORT_QMOE_CPU_QNBIT_GEMM environment variable, which now only overrides
the attribute. Pack constant block-wise zero points into the QNBit
layout (QMoE's per-expert zero point layout is the MatMulNBits one) so
asymmetric checkpoints take the same kernels. Log once when a block-wise
node cannot use the path and why, since the dequantize fallback is an
order of magnitude slower for decode. Record the zero point flag next to
the compute type in the prepacked shape buffer, and cover accuracy_level
and zero points (4-bit fp32 and int8, 8-bit int8) in the tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…Bit prepack

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@mirounga
mirounga self-requested a review September 16, 2026 16:58
Use int8 activations where MLAS has no fp32 kernel for the bit width (8-bit),
so 8-bit experts reach the QNBit path by default as documented; explicit fp32
still forces the previous behavior. Guard the packed-buffer size with SafeInt
and size the load-time pack pool by the expert count.
8-bit experts now run with int8 activations by default, so the block-wise 8-bit
case carries their quantization error; match the tolerance of the other int8
cases and add a case that forces fp32 back onto the dequantize path.
@ykhrustalev

Copy link
Copy Markdown
Contributor Author

Thanks — addressed all three.

  • 8-bit default: correct, MLAS has no SQNBIT_CompFp32 variant for 8 bits (only SQ8BitGemmVariant_CompInt8), so the old policy silently kept 8-bit experts on dequantize+SGEMM. The selection now falls back to int8 when the bit width has no fp32 kernel and fp32 was not explicitly forced; ORT_QMOE_CPU_QNBIT_GEMM=fp32 still pins the dequantize path. QMoETest_CPU_Int8_BlockWise_SwiGLU now exercises the QNBit path (its tolerance moved to 0.05, matching the other int8-activation cases — the previous 0.01 was exceeded by 0.0003), and a new ..._ForcedFp32FallsBackToDequantize case pins the fallback at the tighter tolerance.
  • Packed-buffer sizing: now SafeInt<size_t>, matching the packed-cache path.
  • Pack pool: sized by min(num_experts, physical cores), so it is skipped entirely for a single expert.

All 17 MoETest.* cases pass on macOS arm64 (M3 Ultra).

@tianleiwu Tianlei Wu (tianleiwu) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at d026ecb0. The QNBit packing, compute, zero-point, and shared-buffer plumbing follows the established MatMulNBits contracts. The earlier packed-size overflow, int8-default routing, and pack-thread-count concerns are fixed at this head. I left two non-blocking inline suggestions for route coverage and comment accuracy.

Comment thread onnxruntime/test/contrib_ops/moe_test.cc Outdated
Comment thread onnxruntime/contrib_ops/cpu/moe/moe_quantization_cpu.h Outdated
…check, assert pre-packing in 8-bit QNBit tests

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@tianleiwu Tianlei Wu (tianleiwu) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at 61173ff78c76a4d2072c2555e8b8dde297a8f830. Requesting changes because the new accuracy_level=4 policy still selects fp32 when both 4-bit kernels are available, so the per-model int8 opt-in does not work as documented. A second inline comment is a non-blocking suggestion to keep per-call QNBit scratch arena-backed.

The packing/finalization, shared-buffer metadata, bias handling, and non-nested thread-pool usage otherwise follow the existing contracts. The earlier 8-bit route-coverage and zero-point-comment suggestions are addressed at this head.

Validation: source review and a source-verified dispatch-policy model; no local C++ tests or benchmarks were run because no provider-test executable was available. CI was still running at review time.

Comment thread onnxruntime/contrib_ops/cpu/moe/moe_quantization_cpu.cc Outdated
Comment thread onnxruntime/contrib_ops/cpu/moe/moe_quantization_cpu.cc Outdated
…call workspace arena-backed

accuracy_level=4 (or the int8 env override) now selects the int8 kernels
ahead of fp32 when both are available, as MatMulNBits does; fp32 stays the
fallback when int8 is missing and the explicit fp32 override still pins it.
A regression test compares raw outputs so level 4 is proven to run the
int8 kernels rather than merely meeting the looser tolerance. The QNBit
scratch buffer no longer uses the reserve allocation path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…elper

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Levels other than 4 and the fp32 override must match the 4-bit default
bitwise; the 0 override keeps the previous paths (never pre-packing 8-bit
experts); the shared-prepack case asserts both weights pre-packed instead
of returning early.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ykhrustalev

Copy link
Copy Markdown
Contributor Author

Thanks, both addressed in acebeebc8d:

  • accuracy_level=4: int8 is now selected ahead of fp32 when available, fp32 remains the fallback, and the explicit fp32 override still pins it. QMoETest_CPU_Int4_BlockWise_SwiGLU_AccuracyLevel4SelectsInt8 asserts level-4 outputs are bitwise identical to forced int8 and differ from forced fp32; it fails against the previous fp32-first ordering.
  • Arena: the QNBit scratch allocation no longer uses reserve.

All MoETest.* pass locally on macOS arm64.

@tianleiwu Tianlei Wu (tianleiwu) left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at bc5f446b. The earlier findings on int8 route proof, zero-point documentation, accuracy_level=4 selection, and arena-backed QNBit workspace are addressed. The packing, shared-buffer compatibility, overflow checks, and non-nested thread-pool use follow the established MatMulNBits/MLAS contracts, and I found no new actionable issues.

Validation: rebuilt onnxruntime_provider_test from this head and ran all 16 QMoETest_CPU_Int4_BlockWise* / QMoETest_CPU_Int8_BlockWise* tests; all passed.

@tianleiwu
Tianlei Wu (tianleiwu) merged commit c18808b into microsoft:main Sep 17, 2026
90 checks passed
@ykhrustalev
Yuri Khrustalev (ykhrustalev) deleted the ykhrustalev/qmoe-cpu-qnbit-gemm branch September 17, 2026 10:15
Tianlei Wu (tianleiwu) added a commit that referenced this pull request Sep 17, 2026
### Description

Follow-up to #32644. That PR put block-wise int4/int8 QMoE CPU experts
on the MLAS QNBit (MatMulNBits) kernels, but still issues one
`MlasQNBitGemmBatch` call per active expert, so a layer costs `2 *
num_active_experts` thread-pool barriers.

MLAS already flattens a batch into a single parallel domain over (expert
× N-block), so this batches the active experts instead: they are
bucketed by token count and each bucket runs as one batched dispatch.
Decode (one token per active expert) collapses to a single bucket,
turning `2 * top_k` dispatches per layer into 2.

This only applies where the expert loop is already serialized (fewer
active experts than threads). With at least as many active experts as
threads, one expert per thread remains faster and that path is
unchanged.

### Key changes

- Bucket the active experts by token count and run one batched
`MlasQNBitGemmBatch` per bucket for FC1 and FC2, with SwiGLU applied
across all gathered rows in between.
- fp32 bias is passed straight from the initializer; only fp16 bias
needs a converted copy.
- Tests gain an intra-op thread-count knob so the same shapes can be
driven through either dispatch path.

### Motivation

CPU decode of MoE models is dominated by the per-expert GEMMs. The
per-expert dispatch pattern is barrier-bound rather than
bandwidth-bound: it sits near 30 GB/s regardless of expert count and
gets *worse* above 32 threads, while the batched form scales to 68-126
GB/s.

Measured on a 96-core x64 host.

End-to-end on LFM2.5-8B-A1B int4 (22 QMoE layers, hidden 2048,
moe_intermediate 1792, 32 experts, top-k 4) through onnxruntime-genai
`model_benchmark`, 630 tokens per mode:

| Metric | before | after |
|---|---|---|
| Token generation | 36.5 ms/token (27.4 tok/s) | **30.3 ms/token (33.0
tok/s)** |
| Prompt processing (16 tok) | 249 ms | **142 ms** |

A single QMoE layer at those dimensions: 0.749 -> 0.438 ms at 32
threads.

The gain scales with `threads / active_experts`, since it is barrier
cost that is being removed. At top-k 4 decode: 1.20x at 8 threads, 1.38x
at 16, 1.78x at 32, 2.11x at 64. With few threads relative to active
experts the expert-parallel path is selected instead and nothing
changes.

### Testing

- `MoETest.*` passes (24 tests).
- New `*_PerExpertDispatch` and `*_SingleThread` cases cap the intra-op
pool so the same shapes and reference exercise the per-expert path and a
single-threaded pool, checking the two dispatch paths against each
other.
- Verified with a temporary probe that the grouped cases really take the
new path and the `PerExpertDispatch` cases really do not.
- Greedy generation on LFM2.5-8B-A1B produced identical token IDs and
identical text before and after.

### Note on reproducibility

Bucketing sorts experts by token count, which changes the cross-expert
accumulation order. Decode output is bit-identical; with multiple tokens
per expert results can differ at float rounding level (observed in the
8th significant digit at 16 tokens).
Franklin Moormann (ooples) pushed a commit to ooples/AiDotNet that referenced this pull request Oct 13, 2026
Updated
[Microsoft.ML.OnnxRuntime](https://github.com/Microsoft/onnxruntime)
from 1.30.0 to 1.31.0.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.ML.OnnxRuntime's
releases](https://github.com/Microsoft/onnxruntime/releases)._

## 1.31.0

ONNX Runtime 1.31.0 stabilizes model-package and EPContext data APIs,
improves CPU model loading and quantized MoE inference, expands
device-based execution-provider selection, and strengthens model-loading
and runtime reliability. These notes cover changes since ONNX Runtime
1.30.0. CUDA and WebGPU kernel updates are summarized here; detailed
provider notes are covered by their separate plugin EP releases.

## Highlights

- Promoted the model-package API and EPContext data callbacks to stable
C and C++ APIs, with EPContext callback support added across language
bindings
([#​33166](microsoft/onnxruntime#33166),
[#​32265](microsoft/onnxruntime#32265)).
- Reduced CPU session-initialization overhead by parallelizing eligible
weight prepacking, and accelerated block-wise INT4/INT8 QMoE experts
with MLAS QNBit kernels and grouped expert dispatch
([#​31691](microsoft/onnxruntime#31691),
[#​32644](microsoft/onnxruntime#32644),
[#​32668](microsoft/onnxruntime#32668)).
- Added x86 FP16 LayerNorm/RMSNorm acceleration, Arm KleidiAI SVE2.1
FP16 GEMM support, and additional RISC-V vector kernels
([#​32715](microsoft/onnxruntime#32715),
[#​32670](microsoft/onnxruntime#32670),
[#​32710](microsoft/onnxruntime#32710)).
- Enabled CoreML participation in device-based EP selection, including
Apple Neural Engine selection through `PREFER_NPU`
([#​31975](microsoft/onnxruntime#31975)).
- Added workspace-memory accounting and verification for
constrained-memory graph partitioning, alongside broader model, graph,
and tensor validation
([#​31962](microsoft/onnxruntime#31962),
[#​32189](microsoft/onnxruntime#32189)).

## Announcements & Compatibility

- [CUDA Plugin EP
v0.3.0](https://github.com/microsoft/onnxruntime/releases/tag/plugin-ep-cuda/v0.3.0)
and WebGPU Plugin EP v0.5.0 are matched with ONNX Runtime 1.31.0. For
**contrib-op compatibility**, core and the plugin must use the same
contributed-operator schemas; building both from the **same ONNX Runtime
revision** is recommended. Schema mismatches can cause incorrect
execution or crashes and may not be detected during plugin registration.
- **`GatherBlockQuantized` behavior change:** out-of-range indices now
produce zero output slices across CPU, CUDA, and WebGPU for both integer
and floating-point quantized formats. This deliberately differs from
ONNX `Gather` error semantics
([#​32480](microsoft/onnxruntime#32480)).
- **Experimental API migration:** model-package consumers should use
`OrtApi::GetModelPackageApi` and the stable `OrtModelPackageApi` table.
EPContext callback registration now uses stable APIs; the corresponding
experimental lookup entries were removed. The stable native callback
contract carries callback/state pointers, with payload limits enforced
by application callbacks, bindings, or EPs rather than a native
read-options policy
([#​33166](microsoft/onnxruntime#33166),
[#​32265](microsoft/onnxruntime#32265)).
- **ACL EP is deprecated** and will be removed in a future release.
Source builds using `--use_acl` now receive a deprecation warning
([#​32564](microsoft/onnxruntime#32564)).
- The ONNX dependency remains at **1.22.0**. The initial ONNX 1.23
integration was reverted pending upstream fixes; this release does not
ship that integration's new opset or FLOAT6 support
([#​33073](microsoft/onnxruntime#33073)).
- Supported native builds now default to **1DS telemetry**. Windows
TraceLogging remains selectable with `--use_windows_telemetry` or
`onnxruntime_USE_WINDOWS_TELEMETRY=ON`; telemetry can be disabled with
`--no_telemetry`
([#​32384](microsoft/onnxruntime#32384)).

## New Features

### Core APIs & Runtime

- Stabilized the model-package C and C++ APIs for loading packages,
selecting compatible model variants, and creating sessions
([#​33166](microsoft/onnxruntime#33166)).
- Stabilized named EPContext data read/write callbacks for
application-managed storage. Added EP support negotiation so registered
callbacks do not silently fall back to filesystem I/O
([#​32265](microsoft/onnxruntime#32265)).
- Extended the Compile API to write external initializers and EP context
data to buffers, enabling in-memory compilation workflows for models
larger than 2 GB
([#​31347](microsoft/onnxruntime#31347)).
- Added workspace-memory accounting, source reporting, and
post-partition reservation verification. Set
`session.strict_workspace_verification=1` to fail initialization on
workspace declaration overruns or nonzero orphaned reservations; strict
verification is not supported for ORT-format model loads
([#​31962](microsoft/onnxruntime#31962),
[#​32189](microsoft/onnxruntime#32189)).
- Added the opt-in source-build option
`onnxruntime_DISABLE_DEVICE_DISCOVERY=ON` to skip platform accelerator
probing and report only the CPU device
([#​32592](microsoft/onnxruntime#32592)).

### Shared Contrib Operators

These updates affect core operator schemas and cross-provider behavior,
not only the separately released GPU plugins.

- Extended `EngramGate` and `NGramHashMapping` for Qwen4-Exp, and added
`VarlenNGramHashMapping` for packed DeepSeek Engram batching with
request-local history. Packed hashing also supports Qwen4-Exp head
offsets, EOS resets, and segment boundaries
([#​32285](microsoft/onnxruntime#32285),
[#​32358](microsoft/onnxruntime#32358),
[#​32467](microsoft/onnxruntime#32467)).
- Added the `activation` attribute to `GatedRMSNorm` for SiLU, its Swish
alias, and Sigmoid gating across CPU, CUDA, and WebGPU. SiLU remains the
default
([#​32512](microsoft/onnxruntime#32512)).
- Added weightless hyper-connection operators `BranchwiseRMSNorm`,
`ScaledSiLU`, `HyperConnectionPreMix`, and `HyperConnectionPostMix`,
with CPU, CUDA, and WebGPU implementations
([#​32687](microsoft/onnxruntime#32687)).
- Extended `GatherBlockQuantized` to FP8/FP4 data, full-axis blocks, and
scale broadcasting, and improved CPU UINT8 dequantization. See the
out-of-range-index behavior change above
([#​32480](microsoft/onnxruntime#32480),
[#​32621](microsoft/onnxruntime#32621)).

### Plugin Execution Provider Infrastructure

- Added support for statically linked plugin EPs, with a static WebGPU
plugin build configuration
([#​32395](microsoft/onnxruntime#32395)).
- Preserved caller streams in single-tensor plugin data transfers and
handled null plugin allocator returns
([#​32666](microsoft/onnxruntime#32666),
[#​32683](microsoft/onnxruntime#32683)).
- Deduplicated AutoEP custom-op domains and corrected EP assignment
reporting after NHWC layout transformation
([#​32711](microsoft/onnxruntime#32711),
[#​33184](microsoft/onnxruntime#33184)).
- Added reference guidance for compiled-model compatibility validation,
including multi-device scenarios
([#​29168](microsoft/onnxruntime#29168)).

## Execution Provider Updates

### CUDA EP

CUDA updates include sparse attention and packed continuous batching,
expanded paged speculative decoding, INT2 and mixed-width QMoE
execution, opt-in MatMul auto-tuning, improved MXFP4 support on
Blackwell consumer GPUs, and reliability fixes.
 ... (truncated)

Commits viewable in [compare
view](microsoft/onnxruntime@v1.30.0...v1.31.0).
</details>

Updated
[Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)
from 10.2.3 to 10.3.0.

<details>
<summary>Release notes</summary>

_Sourced from [Swashbuckle.AspNetCore's
releases](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases)._

## 10.3.0

## What's Changed

* Change nullability handling for schemas with `anyOf`, `allOf`, `oneOf`
or `type = null` by @​ldeluigi in
domaindrivendev/Swashbuckle.AspNetCore#3976
* Handle leading slash on route prefix during index.html GET by
@​superstator in
domaindrivendev/Swashbuckle.AspNetCore#4084
* Move NRT handling to DataContractResolver by @​desjoerd in
domaindrivendev/Swashbuckle.AspNetCore#3998
* Clarify nullable behaviour by @​martincostello in
domaindrivendev/Swashbuckle.AspNetCore#4094
* Fix review issues for caching and encoding by @​martincostello in
domaindrivendev/Swashbuckle.AspNetCore#4093
* Support multiple response types per status code by @​DeagleGross in
domaindrivendev/Swashbuckle.AspNetCore#4100
* Add coverage for `AddServer` across multiple documents by
@​RaphaelFakhri in
domaindrivendev/Swashbuckle.AspNetCore#4115
* Fix Swagger sort key fallback for empty endpoint tags metadata by
@​AleksaMCode in
domaindrivendev/Swashbuckle.AspNetCore#4125
* Fix parameter matching to consider location when names collide by
@​nonsoikem in
domaindrivendev/Swashbuckle.AspNetCore#4126
* Emit form encoding for properties of a complex form parameter by
@​RaphaelFakhri in
domaindrivendev/Swashbuckle.AspNetCore#4128
* Keep a property's own summary when comments come from several XML
files by @​RaphaelFakhri in
domaindrivendev/Swashbuckle.AspNetCore#4129
* Strip query string from relative document URLs in Swagger UI by
@​GabeSilvaDev in
domaindrivendev/Swashbuckle.AspNetCore#4152
* Document that System.Text.Json ignores `[EnumMember]` by
@​RaphaelFakhri in
domaindrivendev/Swashbuckle.AspNetCore#4127
* Drop the regex route constraint from the default Swagger route by
@​RaphaelFakhri in
domaindrivendev/Swashbuckle.AspNetCore#4130
* Serialize custom values in the UI `ConfigObject.AdditionalItems` by
@​RaphaelFakhri in
domaindrivendev/Swashbuckle.AspNetCore#4132
* Use Minimal APIs JSON options when MVC is not registered by
@​RaphaelFakhri in
domaindrivendev/Swashbuckle.AspNetCore#4116
* Resolve enum contracts against the underlying type under source
generation by @​RaphaelFakhri in
domaindrivendev/Swashbuckle.AspNetCore#4131

## New Contributors

* @​ldeluigi made their first contribution in
domaindrivendev/Swashbuckle.AspNetCore#3976
* @​superstator made their first contribution in
domaindrivendev/Swashbuckle.AspNetCore#4084
* @​desjoerd made their first contribution in
domaindrivendev/Swashbuckle.AspNetCore#3998
* @​RaphaelFakhri made their first contribution in
domaindrivendev/Swashbuckle.AspNetCore#4115
* @​AleksaMCode made their first contribution in
domaindrivendev/Swashbuckle.AspNetCore#4125
* @​nonsoikem made their first contribution in
domaindrivendev/Swashbuckle.AspNetCore#4126
* @​GabeSilvaDev made their first contribution in
domaindrivendev/Swashbuckle.AspNetCore#4152
* @​JulianThoms made their first contribution in
domaindrivendev/Swashbuckle.AspNetCore#4182

**Full Changelog**:
domaindrivendev/Swashbuckle.AspNetCore@v10.2.3...v10.3.0


Commits viewable in [compare
view](domaindrivendev/Swashbuckle.AspNetCore@v10.2.3...v10.3.0).
</details>

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants