Improve the app-EP weightless contract (#32899)
### Description
Until now, an EP could report only one weightless mode, and apps could
only turn weightless mode on or off. Each mode has different runtime
requirements: ALL needs the source model at runtime, EXTERNAL_ONLY
doesn't. With this change, EPs report every mode they support and apps
choose the one that fits, without breaking EPs and apps built for 1.29
and 1.30.
### Motivation and Context
Address the following shortcomings from PR 29607:
- After a system update, a plugin EP could switch to a weightless mode
whose requirements the app wasn't prepared to meet.
- An EP that supports both modes could only advertise one. If it
advertised the more demanding one, apps that can't meet those
requirements would skip the EP.
### Changes
- **Modes**: OrtWeightlessSupport stays a sequential enum
(ABI-compatible).
- New value OrtWeightlessSupport_ALL_OR_EXTERNAL_ONLY = 3 describes an
EP capability only; apps can't request it.
- Future versions may add values, and apps should ignore values they
don't recognize.
- **EP reporting**:
- OrtEp::GetWeightlessSupport() keeps its 1.29 signature. EPs return a
single mode or OrtWeightlessSupport_ALL_OR_EXTERNAL_ONLY. ORT versions
before 1.32 only check that the value isn't NONE, so returning 3 is safe
for them too.
- New EP metadata key weightless_supported_modes, with values "none",
"external_only", "all" or "all_or_external_only".
- The existing weightless_support key keeps its 1.29 values. ORT and
apps fall back to it when the new key is absent.
- **App request**:
- New ModelCompilationOptions_SetWeightlessMode(OrtWeightlessSupport)
(C++: SetWeightlessMode()) and session option ep.enable_weightless_mode.
The option accepts exactly "0", "1" or "2".
- Deprecated: ModelCompilationOptions_SetWeightlessEnabled(bool) and
ep.enable_weightless. They keep their previous behavior when the new
option isn't set.
- **Validation**:
- InferenceSession::Initialize() rejects invalid values
(INVALID_ARGUMENT) for every API (C, C++, Python), even without plugin
EPs or compiled nodes. That includes "3", other spellings like "0x2",
and an explicitly empty value.
- Before creating each plugin EP, ORT checks the requested mode against
the EP metadata (EP_FAIL if it isn't supported). The EP then receives a
copy of the session options with ep.enable_weightless set to match ("0"
or "1"), so EPs built for earlier versions behave correctly. The app's
session options aren't modified.
- During Compile(), ORT checks the mode against GetWeightlessSupport().
It logs a warning if that disagrees with the EP metadata, and includes
the mismatch in the error when the mode is rejected.
- Error codes from plugin EP creation are now preserved instead of
becoming ORT_FAIL.
- **Recorded mode**: the chosen mode is written to the compiled model's
metadata (weightless_mode). ORT warns when a model compiled with ALL is
loaded without a source model path or buffer.
- **Fix**: the weightless source model buffer moved into
onnxruntime::SessionOptions. Previously it was lost when session options
were copied or cloned, and the session couldn't see it.
- **Example EP**: reports both metadata keys and honors the selected
mode, both when compiling and when running directly:
- NONE: copies all initializers.
- EXTERNAL_ONLY: copies only internal initializers.
- ALL: copies none.
- **Docs**: updated header docs and a new design doc,
docs/design/Weightless_Mode.md. It covers how EPs report modes, how apps
choose one, compatibility with older EPs and apps, and the runtime and
packaging requirements for each mode.
### Compatibility
- GetWeightlessSupport() and its OrtWeightlessSupport* output are
unchanged.
- weightless_support keeps its values ("none", "external_only", "all").
- SetWeightlessEnabled(bool) and ep.enable_weightless still work. When
ep.enable_weightless_mode is set, it takes precedence, and ORT
translates it for EPs that only read the deprecated option.
### Testing
- onnxruntime_autoep_test:
- Compiling with each mode, including the mode recorded in the compiled
model.
- How the EP handles initializers for internal, external and mixed
models, both compiled and run directly.
- Invalid values and their error codes.
- Rejection when the EP metadata doesn't support the mode.
- ep.enable_weightless being set correctly for older EPs.
- The deprecated APIs.
- onnxruntime_test_all:
PluginExecutionProviderTest.Compile_WeightlessSupportDoesNotMatchEpMetadata
checks the mismatch warning.
- Python (onnxruntime_test_python.py): invalid values are rejected and
the ALL source-model warning is logged on the Python path.