diff --git a/include/onnxruntime/core/session/onnxruntime_c_api.h b/include/onnxruntime/core/session/onnxruntime_c_api.h index c39ca2a52ee35..2c708ad202242 100644 --- a/include/onnxruntime/core/session/onnxruntime_c_api.h +++ b/include/onnxruntime/core/session/onnxruntime_c_api.h @@ -334,6 +334,9 @@ ORT_RUNTIME_CLASS(ExternalSemaphoreHandle); // EP-imported view of shared exte ORT_RUNTIME_CLASS(DeviceEpIncompatibilityDetails); ORT_RUNTIME_CLASS(EpAssignedSubgraph); ORT_RUNTIME_CLASS(EpAssignedNode); +ORT_RUNTIME_CLASS(ModelPackageOptions); +ORT_RUNTIME_CLASS(ModelPackageContext); +ORT_RUNTIME_CLASS(ModelPackageComponentContext); #ifdef _MSC_VER typedef _Return_type_success_(return == 0) OrtStatus* OrtStatusPtr; @@ -976,6 +979,9 @@ typedef struct OrtCompileApi OrtCompileApi; struct OrtInteropApi; typedef struct OrtInteropApi OrtInteropApi; +struct OrtModelPackageApi; +typedef struct OrtModelPackageApi OrtModelPackageApi; + struct OrtEpApi; typedef struct OrtEpApi OrtEpApi; @@ -7678,6 +7684,17 @@ struct OrtApi { */ ORT_API2_STATUS(SessionOptionsSetEpContextDataReadFunc, _Inout_ OrtSessionOptions* options, _In_opt_ OrtReadNamedBufferFunc read_func, _In_opt_ void* state); + + /** \brief Get the stable model package API table. + * + * The returned table provides model package inspection, variant selection, and session creation APIs. + * The table is owned by ONNX Runtime and must not be released. + * + * \return Pointer to the ::OrtModelPackageApi table. + * + * \since Version 1.31. + */ + const OrtModelPackageApi*(ORT_API_CALL* GetModelPackageApi)(void); }; /* @@ -8833,6 +8850,267 @@ struct OrtInteropApi { /// @} }; +/** \brief Stable API table for model package workflows. + * + * A model package contains one or more components, each with variants targeting execution providers and devices. + * Obtain this table from OrtApi::GetModelPackageApi(). + * + * Typical usage creates model package options from session options, opens a package context, selects a component, + * and creates an OrtSession from the selected variant. Objects returned by this API must be released with the + * corresponding release function in this table. + * + * \since Version 1.31. + */ +struct OrtModelPackageApi { + /** \brief Create model package options from session options. + * + * Captures the execution provider configuration used to select a component variant. + * + * \param[in] env The ORT environment. + * \param[in] session_options Session options containing the execution provider configuration. + * \param[out] out Receives the created options. Release with ReleaseModelPackageOptions(). + * + * \snippet{doc} snippets.dox OrtStatus Return Value + * + * \since Version 1.31. + */ + ORT_API2_STATUS(CreateModelPackageOptionsFromSessionOptions, + _In_ const OrtEnv* env, + _In_ const OrtSessionOptions* session_options, + _Outptr_ OrtModelPackageOptions** out); + + /** \brief Release model package options. + * + * \param[in] input The options to release. May be NULL. + * + * \since Version 1.31. + */ + ORT_CLASS_RELEASE(ModelPackageOptions); + + /** \brief Open a model package. + * + * Parses the package manifest and metadata rooted at `package_root`. + * + * \param[in] package_root Path to the model package root directory. + * \param[out] out Receives the created context. Release with ReleaseModelPackageContext(). + * + * \snippet{doc} snippets.dox OrtStatus Return Value + * + * \since Version 1.31. + */ + ORT_API2_STATUS(CreateModelPackageContext, + _In_ const ORTCHAR_T* package_root, + _Outptr_ OrtModelPackageContext** out); + + /** \brief Release a model package context. + * + * \param[in] input The context to release. May be NULL. + * + * \since Version 1.31. + */ + ORT_CLASS_RELEASE(ModelPackageContext); + + /** \brief Get the model package schema version. + * + * \param[in] ctx The model package context. + * \param[out] out_version Receives the schema version. + * + * \snippet{doc} snippets.dox OrtStatus Return Value + * + * \since Version 1.31. + */ + ORT_API2_STATUS(ModelPackage_GetSchemaVersion, + _In_ const OrtModelPackageContext* ctx, + _Out_ int64_t* out_version); + + /** \brief Get the number of components in a model package. + * + * \param[in] ctx The model package context. + * \param[out] out_count Receives the component count. + * + * \snippet{doc} snippets.dox OrtStatus Return Value + * + * \since Version 1.31. + */ + ORT_API2_STATUS(ModelPackage_GetComponentCount, + _In_ const OrtModelPackageContext* ctx, + _Out_ size_t* out_count); + + /** \brief Get the component names in a model package. + * + * The returned array and UTF-8 strings are owned by `ctx` and remain valid until `ctx` is released. + * + * \param[in] ctx The model package context. + * \param[out] out_names Receives the component name array, or NULL when the package has no components. + * \param[out] out_count Receives the number of names in `out_names`. + * + * \snippet{doc} snippets.dox OrtStatus Return Value + * + * \since Version 1.31. + */ + ORT_API2_STATUS(ModelPackage_GetComponentNames, + _In_ const OrtModelPackageContext* ctx, + _Outptr_result_buffer_maybenull_(*out_count) const char* const** out_names, + _Out_ size_t* out_count); + + /** \brief Get the number of variants for a component. + * + * \param[in] ctx The model package context. + * \param[in] component_name UTF-8 component name. + * \param[out] out_count Receives the variant count. + * + * \snippet{doc} snippets.dox OrtStatus Return Value + * + * \since Version 1.31. + */ + ORT_API2_STATUS(ModelPackage_GetVariantCount, + _In_ const OrtModelPackageContext* ctx, + _In_ const char* component_name, + _Out_ size_t* out_count); + + /** \brief Get the variant names for a component. + * + * The returned array and UTF-8 strings are owned by `ctx` and remain valid until `ctx` is released. + * + * \param[in] ctx The model package context. + * \param[in] component_name UTF-8 component name. + * \param[out] out_variant_names Receives the variant name array, or NULL when the component has no variants. + * \param[out] out_count Receives the number of names in `out_variant_names`. + * + * \snippet{doc} snippets.dox OrtStatus Return Value + * + * \since Version 1.31. + */ + ORT_API2_STATUS(ModelPackage_GetVariantNames, + _In_ const OrtModelPackageContext* ctx, + _In_ const char* component_name, + _Outptr_result_buffer_maybenull_(*out_count) const char* const** out_variant_names, + _Out_ size_t* out_count); + + /** \brief Get the execution provider name declared by a variant. + * + * The returned UTF-8 string is owned by `ctx` and remains valid until `ctx` is released. `out_ep` is set to NULL + * when the variant does not declare an execution provider. + * + * \param[in] ctx The model package context. + * \param[in] component_name UTF-8 component name. + * \param[in] variant_name UTF-8 variant name. + * \param[out] out_ep Receives the execution provider name or NULL. + * + * \snippet{doc} snippets.dox OrtStatus Return Value + * + * \since Version 1.31. + */ + ORT_API2_STATUS(ModelPackage_GetVariantEpName, + _In_ const OrtModelPackageContext* ctx, + _In_ const char* component_name, + _In_ const char* variant_name, + _Outptr_result_maybenull_ const char** out_ep); + + /** \brief Resolve a package path reference. + * + * Resolves content-addressed `sha256:[/path]` references and package-relative paths. When `must_exist` is + * nonzero, the resolved path must exist. The returned UTF-8 path is owned by `ctx` and remains valid until the next + * call to this function on the same context or until `ctx` is released. + * + * \param[in] ctx The model package context. + * \param[in] base_dir Base directory for relative references, or NULL to use the package root. + * \param[in] input UTF-8 path reference to resolve. + * \param[in] must_exist Nonzero to require that the resolved path exists. + * \param[out] out_path Receives the resolved UTF-8 path. + * + * \snippet{doc} snippets.dox OrtStatus Return Value + * + * \since Version 1.31. + */ + ORT_API2_STATUS(ModelPackage_ResolveStringRef, + _In_ const OrtModelPackageContext* ctx, + _In_opt_ const char* base_dir, + _In_ const char* input, + _In_ int must_exist, + _Outptr_ const char** out_path); + + /** \brief Select a component and resolve its best matching variant. + * + * The returned component context is independent of `context` and must be released with + * ReleaseModelPackageComponentContext(). + * + * \param[in] context The model package context. + * \param[in] component_name UTF-8 component name. + * \param[in] options Options that provide the execution provider configuration for variant selection. + * \param[out] out Receives the selected component context. + * + * \snippet{doc} snippets.dox OrtStatus Return Value + * + * \since Version 1.31. + */ + ORT_API2_STATUS(SelectComponent, + _In_ const OrtModelPackageContext* context, + _In_ const char* component_name, + _In_ const OrtModelPackageOptions* options, + _Outptr_ OrtModelPackageComponentContext** out); + + /** \brief Release a selected component context. + * + * \param[in] input The component context to release. May be NULL. + * + * \since Version 1.31. + */ + ORT_CLASS_RELEASE(ModelPackageComponentContext); + + /** \brief Get the selected variant name. + * + * The returned UTF-8 string is owned by `ctx` and remains valid until `ctx` is released. + * + * \param[in] ctx The selected component context. + * \param[out] out_name Receives the selected variant name. + * + * \snippet{doc} snippets.dox OrtStatus Return Value + * + * \since Version 1.31. + */ + ORT_API2_STATUS(ModelPackageComponent_GetSelectedVariantName, + _In_ const OrtModelPackageComponentContext* ctx, + _Outptr_ const char** out_name); + + /** \brief Get the selected variant directory. + * + * The returned path is owned by `ctx` and remains valid until `ctx` is released. + * + * \param[in] ctx The selected component context. + * \param[out] folder_path Receives the selected variant directory path. + * + * \snippet{doc} snippets.dox OrtStatus Return Value + * + * \since Version 1.31. + */ + ORT_API2_STATUS(ModelPackageComponent_GetSelectedVariantFolderPath, + _In_ const OrtModelPackageComponentContext* ctx, + _Outptr_ const ORTCHAR_T** folder_path); + + /** \brief Create a session for the selected component variant. + * + * When `session_options` is NULL, ONNX Runtime creates fresh session options and applies the selected variant's + * session and provider options. When `session_options` is non-NULL, the caller's options are used and package + * provider options are not applied; package path-valued session options are still added when the caller did not + * provide them. + * + * \param[in] env The ORT environment. + * \param[in] context The selected component context. + * \param[in] session_options Optional caller-provided session options. + * \param[out] session Receives the created session. Release with OrtApi::ReleaseSession(). + * + * \snippet{doc} snippets.dox OrtStatus Return Value + * + * \since Version 1.31. + */ + ORT_API2_STATUS(CreateSession, + _In_ const OrtEnv* env, + _In_ OrtModelPackageComponentContext* context, + _In_opt_ const OrtSessionOptions* session_options, + _Outptr_ OrtSession** session); +}; + /* * This is the old way to add the CUDA provider to the session, please use SessionOptionsAppendExecutionProvider_CUDA above to access the latest functionality * This function always exists, but will only succeed if Onnxruntime was built with CUDA support and the CUDA provider shared library exists diff --git a/include/onnxruntime/core/session/onnxruntime_cxx_api.h b/include/onnxruntime/core/session/onnxruntime_cxx_api.h index 284f12d7b975d..d583724c8ccf8 100644 --- a/include/onnxruntime/core/session/onnxruntime_cxx_api.h +++ b/include/onnxruntime/core/session/onnxruntime_cxx_api.h @@ -250,6 +250,19 @@ inline const OrtInteropApi& GetInteropApi() { return *api; } +/// +/// This returns a reference to the stable ORT Model Package API. +/// +/// ORT C Model Package API reference +inline const OrtModelPackageApi& GetModelPackageApi() { + auto* api = GetApi().GetModelPackageApi(); + if (api == nullptr) { + ORT_CXX_API_THROW("Model Package API is not available in this build", ORT_FAIL); + } + + return *api; +} + /// /// This returns a reference to the ORT C EP API. Used if authoring a plugin execution provider. /// @@ -658,6 +671,9 @@ ORT_DEFINE_RELEASE(Value); ORT_DEFINE_RELEASE(ValueInfo); ORT_DEFINE_RELEASE_FROM_API_STRUCT(ModelCompilationOptions, GetCompileApi); +ORT_DEFINE_RELEASE_FROM_API_STRUCT(ModelPackageOptions, GetModelPackageApi); +ORT_DEFINE_RELEASE_FROM_API_STRUCT(ModelPackageContext, GetModelPackageApi); +ORT_DEFINE_RELEASE_FROM_API_STRUCT(ModelPackageComponentContext, GetModelPackageApi); ORT_DEFINE_RELEASE_FROM_API_STRUCT(EpContextConfig, GetEpApi); ORT_DEFINE_RELEASE_FROM_API_STRUCT(EpDevice, GetEpApi); ORT_DEFINE_RELEASE_FROM_API_STRUCT(KernelDef, GetEpApi); @@ -792,6 +808,9 @@ struct EpDevice; struct ExternalInitializerInfo; struct Graph; struct Model; +struct ModelPackageOptions; +struct ModelPackageContext; +struct ModelPackageComponentContext; struct Node; struct ModelMetadata; struct TypeInfo; @@ -1823,6 +1842,52 @@ struct ModelCompilationOptions : detail::Base { */ Status CompileModel(const Env& env, const ModelCompilationOptions& model_compilation_options); +/** \brief Options used to select a component variant from a model package. + * + * The constructor captures execution provider configuration from session options. + */ +struct ModelPackageOptions : detail::Base { + using Base = detail::Base; + using Base::Base; + + explicit ModelPackageOptions(std::nullptr_t) {} + ModelPackageOptions(const Env& env, const SessionOptions& session_options); + ModelPackageOptions(const Env& env, ConstSessionOptions session_options); +}; + +/** \brief Context used to inspect a model package and select one of its components. */ +struct ModelPackageContext : detail::Base { + using Base = detail::Base; + using Base::Base; + + explicit ModelPackageContext(std::nullptr_t) {} + explicit ModelPackageContext(const ORTCHAR_T* package_root); + + int64_t GetSchemaVersion() const; + size_t GetComponentCount() const; + std::vector GetComponentNames() const; + size_t GetVariantCount(const char* component_name) const; + std::vector GetVariantNames(const char* component_name) const; + const char* GetVariantEpName(const char* component_name, const char* variant_name) const; + std::string ResolveStringRef(const char* base_dir, const char* input, bool must_exist) const; + ModelPackageComponentContext SelectComponent(const char* component_name, + const ModelPackageOptions& options) const; +}; + +/** \brief Context for a selected model package component and variant. */ +struct ModelPackageComponentContext : detail::Base { + using Base = detail::Base; + using Base::Base; + + explicit ModelPackageComponentContext(std::nullptr_t) {} + + std::string GetSelectedVariantName() const; + std::basic_string GetSelectedVariantFolderPath() const; + Session CreateSession(const Env& env); + Session CreateSession(const Env& env, const SessionOptions& session_options); + Session CreateSession(const Env& env, ConstSessionOptions session_options); +}; + /** \brief Wrapper around ::OrtModelMetadata * */ diff --git a/include/onnxruntime/core/session/onnxruntime_cxx_inline.h b/include/onnxruntime/core/session/onnxruntime_cxx_inline.h index a324d8c082414..dee0f135654ad 100644 --- a/include/onnxruntime/core/session/onnxruntime_cxx_inline.h +++ b/include/onnxruntime/core/session/onnxruntime_cxx_inline.h @@ -1372,6 +1372,115 @@ inline ModelCompilationOptions& ModelCompilationOptions::SetWeightlessEnabled(bo return *this; } +inline ModelPackageOptions::ModelPackageOptions(const Env& env, const SessionOptions& session_options) { + ThrowOnError(GetModelPackageApi().CreateModelPackageOptionsFromSessionOptions(env, session_options, &this->p_)); +} + +inline ModelPackageOptions::ModelPackageOptions(const Env& env, ConstSessionOptions session_options) { + ThrowOnError(GetModelPackageApi().CreateModelPackageOptionsFromSessionOptions(env, session_options, &this->p_)); +} + +inline ModelPackageContext::ModelPackageContext(const ORTCHAR_T* package_root) { + ThrowOnError(GetModelPackageApi().CreateModelPackageContext(package_root, &this->p_)); +} + +inline int64_t ModelPackageContext::GetSchemaVersion() const { + int64_t version = 0; + ThrowOnError(GetModelPackageApi().ModelPackage_GetSchemaVersion(this->p_, &version)); + return version; +} + +inline size_t ModelPackageContext::GetComponentCount() const { + size_t count = 0; + ThrowOnError(GetModelPackageApi().ModelPackage_GetComponentCount(this->p_, &count)); + return count; +} + +inline std::vector ModelPackageContext::GetComponentNames() const { + const char* const* names = nullptr; + size_t count = 0; + ThrowOnError(GetModelPackageApi().ModelPackage_GetComponentNames(this->p_, &names, &count)); + std::vector result; + result.reserve(count); + for (size_t i = 0; i < count; ++i) { + result.emplace_back(names[i]); + } + return result; +} + +inline size_t ModelPackageContext::GetVariantCount(const char* component_name) const { + size_t count = 0; + ThrowOnError(GetModelPackageApi().ModelPackage_GetVariantCount(this->p_, component_name, &count)); + return count; +} + +inline std::vector ModelPackageContext::GetVariantNames(const char* component_name) const { + const char* const* names = nullptr; + size_t count = 0; + ThrowOnError(GetModelPackageApi().ModelPackage_GetVariantNames(this->p_, component_name, &names, &count)); + std::vector result; + result.reserve(count); + for (size_t i = 0; i < count; ++i) { + result.emplace_back(names[i]); + } + return result; +} + +inline const char* ModelPackageContext::GetVariantEpName(const char* component_name, + const char* variant_name) const { + const char* ep = nullptr; + ThrowOnError(GetModelPackageApi().ModelPackage_GetVariantEpName( + this->p_, component_name, variant_name, &ep)); + return ep; +} + +inline std::string ModelPackageContext::ResolveStringRef(const char* base_dir, const char* input, + bool must_exist) const { + const char* path = nullptr; + ThrowOnError(GetModelPackageApi().ModelPackage_ResolveStringRef( + this->p_, base_dir, input, must_exist ? 1 : 0, &path)); + return std::string{path}; +} + +inline ModelPackageComponentContext ModelPackageContext::SelectComponent( + const char* component_name, const ModelPackageOptions& options) const { + OrtModelPackageComponentContext* out = nullptr; + ThrowOnError(GetModelPackageApi().SelectComponent(this->p_, component_name, options, &out)); + return ModelPackageComponentContext{out}; +} + +inline std::string ModelPackageComponentContext::GetSelectedVariantName() const { + const char* name = nullptr; + ThrowOnError(GetModelPackageApi().ModelPackageComponent_GetSelectedVariantName(this->p_, &name)); + return std::string{name}; +} + +inline std::basic_string ModelPackageComponentContext::GetSelectedVariantFolderPath() const { + const ORTCHAR_T* path = nullptr; + ThrowOnError(GetModelPackageApi().ModelPackageComponent_GetSelectedVariantFolderPath(this->p_, &path)); + return std::basic_string{path}; +} + +inline Session ModelPackageComponentContext::CreateSession(const Env& env) { + OrtSession* out = nullptr; + ThrowOnError(GetModelPackageApi().CreateSession(env, this->p_, nullptr, &out)); + return Session{out}; +} + +inline Session ModelPackageComponentContext::CreateSession(const Env& env, + const SessionOptions& session_options) { + OrtSession* out = nullptr; + ThrowOnError(GetModelPackageApi().CreateSession(env, this->p_, session_options, &out)); + return Session{out}; +} + +inline Session ModelPackageComponentContext::CreateSession(const Env& env, + ConstSessionOptions session_options) { + OrtSession* out = nullptr; + ThrowOnError(GetModelPackageApi().CreateSession(env, this->p_, session_options, &out)); + return Session{out}; +} + inline EpContextConfig::EpContextConfig(const SessionOptions& session_options) : EpContextConfig{session_options.GetConst()} {} diff --git a/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h b/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h index e5b9bd4713a1c..10dbec14e533f 100644 --- a/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h +++ b/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h @@ -42,10 +42,6 @@ // ORT_RUNTIME_CLASS(ExperimentalType); // -ORT_RUNTIME_CLASS(ModelPackageOptions); -ORT_RUNTIME_CLASS(ModelPackageContext); -ORT_RUNTIME_CLASS(ModelPackageComponentContext); - // // C function pointer typedefs and name constants // diff --git a/include/onnxruntime/core/session/onnxruntime_experimental_c_api.inc b/include/onnxruntime/core/session/onnxruntime_experimental_c_api.inc index aae1dd2f8e401..0123b02818584 100644 --- a/include/onnxruntime/core/session/onnxruntime_experimental_c_api.inc +++ b/include/onnxruntime/core/session/onnxruntime_experimental_c_api.inc @@ -35,278 +35,3 @@ * \snippet{doc} snippets.dox OrtStatus Return Value */ ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtApi_ExperimentalApiTest, _Out_ int64_t* out) - -// --------------------------------------------------------------------------- -// OrtModelPackageApi -// -// A model package is a directory containing one or more *components* (logical models). -// Each component has one or more *variants*, where each variant targets a single -// execution provider (EP). The package manifest declares the EP name, device type, -// and an optional compatibility string for every variant so that the runtime can -// automatically select the best variant for the hardware and EPs available in the -// caller's session options. -// -// The functions below support: -// - creating model package options that capture EP configuration from OrtSessionOptions, -// - loading a package context (manifest + metadata) from a package root path, -// - querying component/variant metadata including per-variant EP information, -// - selecting a component (which also resolves the best-matching variant), -// - querying the selected variant's name and folder path, -// - creating an OrtSession from the selected component context. -// -// Typical flow: -// 1) Create model package options: -// - OrtModelPackageApi_CreateModelPackageOptionsFromSessionOptions -// 2) Load package metadata: -// - OrtModelPackageApi_CreateModelPackageContext -// 3) Query metadata (optional): -// - OrtModelPackageApi_ModelPackage_GetSchemaVersion -// - OrtModelPackageApi_ModelPackage_GetComponentCount -// - OrtModelPackageApi_ModelPackage_GetComponentNames -// - OrtModelPackageApi_ModelPackage_GetVariantCount -// - OrtModelPackageApi_ModelPackage_GetVariantNames -// - OrtModelPackageApi_ModelPackage_GetVariantEpName -// - OrtModelPackageApi_ModelPackage_ResolveStringRef -// 4) Select a component and resolve variant: -// - OrtModelPackageApi_SelectComponent -// 5) Query selected variant info (optional): -// - OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantName -// - OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantFolderPath -// 6) Create session: -// - OrtModelPackageApi_CreateSession -// -// Ownership: -// - Release objects created by this API with the corresponding release functions: -// OrtModelPackageApi_ReleaseModelPackageOptions, -// OrtModelPackageApi_ReleaseModelPackageContext, -// OrtModelPackageApi_ReleaseModelPackageComponentContext. -// -// Opaque handles (OrtModelPackageOptions, OrtModelPackageContext, OrtModelPackageComponentContext) -// are declared in onnxruntime_experimental_c_api.h. -// --------------------------------------------------------------------------- - -/** \brief Create model package options from an existing OrtSessionOptions. - * - * Captures EP configuration (registered execution providers and their devices) from - * the session options for use during variant selection. The resulting OrtModelPackageOptions - * is passed to OrtModelPackageApi_SelectComponent to resolve the best variant for the - * available EPs. - * - * \param[in] env The ORT environment. - * \param[in] session_options Session options containing registered EPs. - * \param[out] out Receives the newly created OrtModelPackageOptions. Must be released - * with OrtModelPackageApi_ReleaseModelPackageOptions. - * - * \snippet{doc} snippets.dox OrtStatus Return Value - */ -ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtModelPackageApi_CreateModelPackageOptionsFromSessionOptions, - _In_ const OrtEnv* env, - _In_ const OrtSessionOptions* session_options, - _Outptr_ OrtModelPackageOptions** out) - -/** \brief Release an OrtModelPackageOptions created by - * OrtModelPackageApi_CreateModelPackageOptionsFromSessionOptions. - */ -ORT_EXPERIMENTAL_API(28, void, OrtModelPackageApi_ReleaseModelPackageOptions, - _Frees_ptr_opt_ OrtModelPackageOptions* options) - -/** \brief Create a model package context by parsing the package at the given root path. - * - * Parses the manifest.json and component metadata from the specified directory. - * The returned context provides read-only access to the package structure (components, - * variants, EP declarations). - * - * \param[in] package_root Path to the model package root directory (containing manifest.json). - * \param[out] out Receives the newly created OrtModelPackageContext. Must be released - * with OrtModelPackageApi_ReleaseModelPackageContext. - * - * \snippet{doc} snippets.dox OrtStatus Return Value - */ -ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtModelPackageApi_CreateModelPackageContext, - _In_ const ORTCHAR_T* package_root, - _Outptr_ OrtModelPackageContext** out) - -/** \brief Release an OrtModelPackageContext created by OrtModelPackageApi_CreateModelPackageContext. */ -ORT_EXPERIMENTAL_API(28, void, OrtModelPackageApi_ReleaseModelPackageContext, - _Frees_ptr_opt_ OrtModelPackageContext* ctx) - -/** \brief Get the schema version declared in the model package manifest. - * - * \param[in] ctx The model package context. - * \param[out] out_version Receives the schema version number. - * - * \snippet{doc} snippets.dox OrtStatus Return Value - */ -ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtModelPackageApi_ModelPackage_GetSchemaVersion, - _In_ const OrtModelPackageContext* ctx, - _Out_ int64_t* out_version) - -/** \brief Get the number of components in the model package. - * - * \param[in] ctx The model package context. - * \param[out] out_count Receives the component count. - * - * \snippet{doc} snippets.dox OrtStatus Return Value - */ -ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtModelPackageApi_ModelPackage_GetComponentCount, - _In_ const OrtModelPackageContext* ctx, - _Out_ size_t* out_count) - -/** \brief Get the names of all components in the model package. - * - * Returns a pointer to an array of UTF-8 component name strings. The array and its - * strings are owned by `ctx` and remain valid until the context is released. - * - * \param[in] ctx The model package context. - * \param[out] out_names Receives a pointer to an array of component name strings. - * \param[out] out_count Receives the number of elements in the array. - * - * \snippet{doc} snippets.dox OrtStatus Return Value - */ -ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtModelPackageApi_ModelPackage_GetComponentNames, - _In_ const OrtModelPackageContext* ctx, - _Outptr_result_buffer_maybenull_(*out_count) const char* const** out_names, - _Out_ size_t* out_count) - -/** \brief Get the number of variants for a given component. - * - * \param[in] ctx The model package context. - * \param[in] component_name Name of the component to query. - * \param[out] out_count Receives the variant count. - * - * \snippet{doc} snippets.dox OrtStatus Return Value - */ -ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtModelPackageApi_ModelPackage_GetVariantCount, - _In_ const OrtModelPackageContext* ctx, - _In_ const char* component_name, - _Out_ size_t* out_count) - -/** \brief Get the names of all variants for a given component. - * - * Returns a pointer to an array of UTF-8 variant name strings. The array and its - * strings are owned by `ctx` and remain valid until the context is released. - * - * \param[in] ctx The model package context. - * \param[in] component_name Name of the component to query. - * \param[out] out_variant_names Receives a pointer to an array of variant name strings. - * \param[out] out_count Receives the number of elements in the array. - * - * \snippet{doc} snippets.dox OrtStatus Return Value - */ -ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtModelPackageApi_ModelPackage_GetVariantNames, - _In_ const OrtModelPackageContext* ctx, - _In_ const char* component_name, - _Outptr_result_buffer_maybenull_(*out_count) const char* const** out_variant_names, - _Out_ size_t* out_count) - -/** \brief Get the EP name declared for a (component, variant) pair. - * - * Each variant targets a single EP. `out_ep` receives the EP name string. - * When the variant does not declare an EP, the returned pointer is NULL. - * String memory is owned by `ctx` and remains valid until the context is released. - * - * \snippet{doc} snippets.dox OrtStatus Return Value - */ -ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtModelPackageApi_ModelPackage_GetVariantEpName, - _In_ const OrtModelPackageContext* ctx, - _In_ const char* component_name, - _In_ const char* variant_name, - _Outptr_result_maybenull_ const char** out_ep) - -/** \brief Resolve a path reference declared inside the package against the model_package rules. - * - * Handles the path forms a package may use: - * - "sha256:" or "sha256:/": a content-addressed shared asset. Resolves to - * the asset's on-disk directory (honoring manifest shared_assets overrides), optionally - * joined with the confined tail. Errors if the asset is not declared/discovered. - * - any other value: a relative path resolved against `base_dir` (or the package root when - * `base_dir` is NULL), with portable-layout confinement (no absolute paths, no ".."). - * - * When `must_exist` is non-zero the resolved path must exist on disk. `out_path` is owned by - * `ctx` and remains valid until the next call to this function on the same context. - * - * \param[in] ctx The package context returned by OrtModelPackageApi_CreateModelPackageContext. - * \param[in] base_dir Base directory for relative inputs. May be NULL to use the package root. - * \param[in] input The path reference to resolve. - * \param[in] must_exist Non-zero to require that the resolved path exists on disk. - * \param[out] out_path Receives the resolved path string. - * - * \snippet{doc} snippets.dox OrtStatus Return Value - */ -ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtModelPackageApi_ModelPackage_ResolveStringRef, - _In_ const OrtModelPackageContext* ctx, - _In_opt_ const char* base_dir, - _In_ const char* input, - _In_ int must_exist, - _Outptr_ const char** out_path) - -/** \brief Select a component model and return an opaque component instance. - * - * The variant selection is also performed during this call based on the component - * metadata and the provided options. The returned `OrtModelPackageComponentContext*` is - * independent of `context` lifetime and must be released via - * OrtModelPackageApi_ReleaseModelPackageComponentContext. - * - * \snippet{doc} snippets.dox OrtStatus Return Value - */ -ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtModelPackageApi_SelectComponent, - _In_ const OrtModelPackageContext* context, - _In_ const char* component_name, - _In_ const OrtModelPackageOptions* options, - _Outptr_ OrtModelPackageComponentContext** out) - -/** \brief Release an OrtModelPackageComponentContext created by OrtModelPackageApi_SelectComponent. */ -ORT_EXPERIMENTAL_API(28, void, OrtModelPackageApi_ReleaseModelPackageComponentContext, - _Frees_ptr_opt_ OrtModelPackageComponentContext* ctx) - -/** \brief Get the name of the selected variant after OrtModelPackageApi_SelectComponent has been called. - * - * String memory is owned by `ctx` and remains valid until the context is released. - * - * \param[in] ctx The component context returned by OrtModelPackageApi_SelectComponent. - * \param[out] out_name Receives the selected variant's name string. - * - * \snippet{doc} snippets.dox OrtStatus Return Value - */ -ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantName, - _In_ const OrtModelPackageComponentContext* ctx, - _Outptr_ const char** out_name) - -/** \brief Get the folder path of the selected variant. - * - * Returns the resolved absolute path to the variant's directory on disk. - * The string is owned by `ctx` and remains valid until the context is released. - * - * \param[in] ctx The component context returned by OrtModelPackageApi_SelectComponent. - * \param[out] folder_path Receives the variant folder path string. - * - * \snippet{doc} snippets.dox OrtStatus Return Value - */ -ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantFolderPath, - _In_ const OrtModelPackageComponentContext* ctx, - _Outptr_ const ORTCHAR_T** folder_path) - -/** \brief Create an OrtSession for a selected file within a component model variant. - * - * The chosen variant (and thus its EP selection) is determined by `context`, which - * was built from an OrtSessionOptions via OrtModelPackageApi_CreateModelPackageOptionsFromSessionOptions. - * - * Session options precedence: - * 1. session_options == NULL (default path): - * ORT uses the OrtSessionOptions that was captured when `context` was created. - * Any variant-specific session and provider options declared in the package - * metadata are merged on top. - * - * 2. session_options != NULL (advanced path): - * ORT uses the caller-provided OrtSessionOptions as-is. Variant-specific - * session and provider options from the package metadata are NOT applied. - * Use this when custom EP setup is required (e.g., shared CUDA streams, - * shared QNN EP contexts, custom allocators). - * - * \snippet{doc} snippets.dox OrtStatus Return Value - */ -ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtModelPackageApi_CreateSession, - _In_ const OrtEnv* env, - _In_ OrtModelPackageComponentContext* context, - _In_opt_ const OrtSessionOptions* session_options, - _Outptr_ OrtSession** session) diff --git a/onnxruntime/core/session/model_compilation_options.h b/onnxruntime/core/session/model_compilation_options.h index 1b0eb02595784..3a9c2db252ca8 100644 --- a/onnxruntime/core/session/model_compilation_options.h +++ b/onnxruntime/core/session/model_compilation_options.h @@ -13,7 +13,6 @@ #include "core/graph/model_editor_api_types.h" #include "core/session/abi_session_options_impl.h" #include "core/session/onnxruntime_c_api.h" -#include "core/session/onnxruntime_experimental_c_api.h" #include "core/session/onnxruntime_session_options_config_keys.h" namespace onnxruntime { diff --git a/onnxruntime/core/session/model_package/README.md b/onnxruntime/core/session/model_package/README.md index 5380bb801028c..3eb6317e88557 100644 --- a/onnxruntime/core/session/model_package/README.md +++ b/onnxruntime/core/session/model_package/README.md @@ -13,10 +13,8 @@ adds three things on top**: variant body). 2. The variant selection algorithm, which queries each execution provider factory and picks the highest-scoring variant. -3. The experimental `OrtModelPackageApi_*` C functions that wrap the library - and expose session creation. They are registered in - `include/onnxruntime/core/session/onnxruntime_experimental_c_api.inc` and - resolved by name through `OrtApi::GetExperimentalFunction`. +3. The stable `OrtModelPackageApi` C API table that wraps the library and + exposes session creation through `OrtApi::GetModelPackageApi`. ORT links the `model_package` library as a static archive; the library itself never links against ORT. @@ -28,12 +26,12 @@ itself never links against ORT. | File | Responsibility | | ------------------------------------- | -------------- | | `model_package_context.h/.cc` | Translates the `model_package` library's C info tree into ORT-internal C++ structs (`ModelPackageInfo`, `ComponentInfo`, `VariantInfo`, `VariantModelInfo`). Parses the `executor_info["ort"]` payload. Owns `ModelPackageContext` (package-level) and `ModelPackageComponentContext` (per-component, with selected variant and provider list). | -| `model_package_options.h/.cc` | `ModelPackageOptions` snapshots EP intent (factories, devices, EP-name list) from an `OrtSessionOptions` at the moment `OrtModelPackageApi_CreateModelPackageOptionsFromSessionOptions_SinceV28` is called. Drives variant selection and provider construction. | +| `model_package_options.h/.cc` | `ModelPackageOptions` snapshots EP intent (factories, devices, EP-name list) from an `OrtSessionOptions` when `OrtModelPackageApi::CreateModelPackageOptionsFromSessionOptions` is called. Drives variant selection and provider construction. | | `model_package_variant_selector.h/.cc`| `VariantSelector::SelectVariant` picks the best variant from a component given the EP list. Uses `OrtEpFactory::ValidateCompiledModelCompatibilityInfo`. | The C entry points themselves live in `onnxruntime/core/session/model_package_api.cc` under -`namespace OrtExperimentalApis`. +`namespace OrtModelPackageAPI`. --- @@ -118,7 +116,7 @@ string internally; ORT only round-trips it through the EP callback. ## Session creation contract -`OrtModelPackageApi_CreateSession_SinceV28(env, component_ctx, session_options, &session)`. +`OrtModelPackageApi::CreateSession(env, component_ctx, session_options, &session)`. The `component_ctx` already knows which variant won selection and which provider list it should use. Two paths: @@ -152,20 +150,13 @@ by) the package. ## C API surface -The model package API is exposed via ONNX Runtime's -[experimental C API](../../../../docs/design/Experimental_C_API.md). Each -function is registered as a separate entry in -`include/onnxruntime/core/session/onnxruntime_experimental_c_api.inc` with -prefix `OrtModelPackageApi_` and version suffix `_SinceV28`. Consumers look -the functions up by name through `OrtApi::GetExperimentalFunction`, either -directly or via the typed C++ accessors in `Ort::Experimental::*` generated -from `onnxruntime_experimental_c_api.h`. +The model package API is a stable companion table returned by +`OrtApi::GetModelPackageApi`. Its opaque handles +(`OrtModelPackageOptions`, `OrtModelPackageContext`, and +`OrtModelPackageComponentContext`) and function table are declared in +`onnxruntime_c_api.h`. `onnxruntime_cxx_api.h` provides RAII wrappers. -The opaque handle types (`OrtModelPackageOptions`, `OrtModelPackageContext`, -`OrtModelPackageComponentContext`) are forward-declared at the top of -`onnxruntime_experimental_c_api.h`. - -Registered entries: +API entries: | Function | Notes | | ----------------------------------------------------- | ----- | @@ -179,59 +170,41 @@ Registered entries: | `ModelPackage_GetVariantCount` | | | `ModelPackage_GetVariantNames` | | | `ModelPackage_GetVariantEpName` | | +| `ModelPackage_ResolveStringRef` | Resolves package-relative and content-addressed paths. | | `SelectComponent` | Resolves the best-matching variant. | | `ReleaseModelPackageComponentContext` | | | `ModelPackageComponent_GetSelectedVariantName` | | | `ModelPackageComponent_GetSelectedVariantFolderPath` | | | `CreateSession` | | -> Experimental functions are not part of the stable ABI. Names, signatures -> and behaviour may change between releases until the surface is promoted -> to the stable `OrtApi`. Callers should null-check every lookup. - Typical flow: ```cpp #include "onnxruntime_c_api.h" -#include "onnxruntime_experimental_c_api.h" const OrtApi* ort = OrtGetApiBase()->GetApi(ORT_API_VERSION); - -auto fn_create_opts = - Ort::Experimental::Get_OrtModelPackageApi_CreateModelPackageOptionsFromSessionOptions_SinceV28_Fn(ort); -auto fn_release_opts = - Ort::Experimental::Get_OrtModelPackageApi_ReleaseModelPackageOptions_SinceV28_Fn(ort); -auto fn_create_ctx = - Ort::Experimental::Get_OrtModelPackageApi_CreateModelPackageContext_SinceV28_Fn(ort); -auto fn_release_ctx = - Ort::Experimental::Get_OrtModelPackageApi_ReleaseModelPackageContext_SinceV28_Fn(ort); -auto fn_select = - Ort::Experimental::Get_OrtModelPackageApi_SelectComponent_SinceV28_Fn(ort); -auto fn_release_comp = - Ort::Experimental::Get_OrtModelPackageApi_ReleaseModelPackageComponentContext_SinceV28_Fn(ort); -auto fn_create_session = - Ort::Experimental::Get_OrtModelPackageApi_CreateSession_SinceV28_Fn(ort); +const OrtModelPackageApi* package_api = ort->GetModelPackageApi(); OrtSessionOptions* so = nullptr; ort->CreateSessionOptions(&so); ort->SessionOptionsAppendExecutionProvider(so, "CUDAExecutionProvider", nullptr, nullptr, 0); OrtModelPackageOptions* mp_opts = nullptr; -fn_create_opts(env, so, &mp_opts); +package_api->CreateModelPackageOptionsFromSessionOptions(env, so, &mp_opts); OrtModelPackageContext* ctx = nullptr; -fn_create_ctx(ORT_TSTR("/path/to/pkg"), &ctx); +package_api->CreateModelPackageContext(ORT_TSTR("/path/to/pkg"), &ctx); OrtModelPackageComponentContext* comp_ctx = nullptr; -fn_select(ctx, "decoder", mp_opts, &comp_ctx); +package_api->SelectComponent(ctx, "decoder", mp_opts, &comp_ctx); OrtSession* session = nullptr; -fn_create_session(env, comp_ctx, nullptr, &session); +package_api->CreateSession(env, comp_ctx, nullptr, &session); ort->ReleaseSession(session); -fn_release_comp(comp_ctx); -fn_release_ctx(ctx); -fn_release_opts(mp_opts); +package_api->ReleaseModelPackageComponentContext(comp_ctx); +package_api->ReleaseModelPackageContext(ctx); +package_api->ReleaseModelPackageOptions(mp_opts); ort->ReleaseSessionOptions(so); ``` @@ -246,8 +219,5 @@ context is released. - [`model_package/README.md`](../../../../model_package/README.md): package format, manifest/component schema, shared assets, path resolution, the authoring C API, and the `executor_info` extension point. -- [`docs/design/Experimental_C_API.md`](../../../../docs/design/Experimental_C_API.md): - design and lifecycle rules for the experimental C API mechanism that - hosts these entries. -- `include/onnxruntime/core/session/onnxruntime_experimental_c_api.inc`: - the canonical list of `OrtModelPackageApi_*` entries. +- `include/onnxruntime/core/session/onnxruntime_c_api.h`: the stable + `OrtModelPackageApi` declaration. diff --git a/onnxruntime/core/session/model_package_api.cc b/onnxruntime/core/session/model_package_api.cc index 633d81aa9baf8..03f76c445db71 100644 --- a/onnxruntime/core/session/model_package_api.cc +++ b/onnxruntime/core/session/model_package_api.cc @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "core/session/onnxruntime_experimental_c_api.h" +#include "core/session/model_package_api.h" #include "core/common/common.h" #include "core/framework/error_code_helper.h" @@ -22,9 +22,9 @@ using namespace onnxruntime; return OrtApis::CreateStatus(ORT_NOT_IMPLEMENTED, \ "Model package API is not supported in this build") -namespace OrtExperimentalApis { +namespace OrtModelPackageAPI { -ORT_API(void, OrtModelPackageApi_ReleaseModelPackageOptions_SinceV28, +ORT_API(void, ReleaseModelPackageOptions, _Frees_ptr_opt_ OrtModelPackageOptions* options) { #if !defined(ORT_MINIMAL_BUILD) delete reinterpret_cast(options); @@ -33,7 +33,7 @@ ORT_API(void, OrtModelPackageApi_ReleaseModelPackageOptions_SinceV28, #endif } -ORT_API_STATUS_IMPL(OrtModelPackageApi_CreateModelPackageOptionsFromSessionOptions_SinceV28, +ORT_API_STATUS_IMPL(CreateModelPackageOptionsFromSessionOptions, _In_ const OrtEnv* env, _In_ const OrtSessionOptions* session_options, _Outptr_ OrtModelPackageOptions** out) { @@ -55,7 +55,7 @@ ORT_API_STATUS_IMPL(OrtModelPackageApi_CreateModelPackageOptionsFromSessionOptio API_IMPL_END } -ORT_API(void, OrtModelPackageApi_ReleaseModelPackageContext_SinceV28, +ORT_API(void, ReleaseModelPackageContext, _Frees_ptr_opt_ OrtModelPackageContext* ctx) { #if !defined(ORT_MINIMAL_BUILD) delete reinterpret_cast(ctx); @@ -64,7 +64,7 @@ ORT_API(void, OrtModelPackageApi_ReleaseModelPackageContext_SinceV28, #endif } -ORT_API_STATUS_IMPL(OrtModelPackageApi_CreateModelPackageContext_SinceV28, +ORT_API_STATUS_IMPL(CreateModelPackageContext, _In_ const ORTCHAR_T* package_root, _Outptr_ OrtModelPackageContext** out) { API_IMPL_BEGIN @@ -90,7 +90,7 @@ ORT_API_STATUS_IMPL(OrtModelPackageApi_CreateModelPackageContext_SinceV28, API_IMPL_END } -ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_GetComponentCount_SinceV28, +ORT_API_STATUS_IMPL(ModelPackage_GetComponentCount, _In_ const OrtModelPackageContext* ctx, _Out_ size_t* out_count) { API_IMPL_BEGIN @@ -108,7 +108,7 @@ ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_GetComponentCount_SinceV28, API_IMPL_END } -ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_GetComponentNames_SinceV28, +ORT_API_STATUS_IMPL(ModelPackage_GetComponentNames, _In_ const OrtModelPackageContext* ctx, _Outptr_result_buffer_maybenull_(*out_count) const char* const** out_names, _Out_ size_t* out_count) { @@ -137,7 +137,7 @@ ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_GetComponentNames_SinceV28, API_IMPL_END } -ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_GetVariantCount_SinceV28, +ORT_API_STATUS_IMPL(ModelPackage_GetVariantCount, _In_ const OrtModelPackageContext* ctx, _In_ const char* component_name, _Out_ size_t* out_count) { @@ -147,8 +147,10 @@ ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_GetVariantCount_SinceV28, return OrtApis::CreateStatus(ORT_INVALID_ARGUMENT, "ctx, component_name, and out_count must be non-null"); } + size_t count = 0; ORT_API_RETURN_IF_STATUS_NOT_OK( - reinterpret_cast(ctx)->GetVariantCount(component_name, *out_count)); + reinterpret_cast(ctx)->GetVariantCount(component_name, count)); + *out_count = count; return nullptr; #else ORT_UNUSED_PARAMETER(ctx); @@ -159,7 +161,7 @@ ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_GetVariantCount_SinceV28, API_IMPL_END } -ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_GetVariantNames_SinceV28, +ORT_API_STATUS_IMPL(ModelPackage_GetVariantNames, _In_ const OrtModelPackageContext* ctx, _In_ const char* component_name, _Outptr_result_buffer_maybenull_(*out_count) const char* const** out_variant_names, @@ -191,7 +193,7 @@ ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_GetVariantNames_SinceV28, API_IMPL_END } -ORT_API_STATUS_IMPL(OrtModelPackageApi_SelectComponent_SinceV28, +ORT_API_STATUS_IMPL(SelectComponent, _In_ const OrtModelPackageContext* context, _In_ const char* component_name, _In_ const OrtModelPackageOptions* options, @@ -236,7 +238,7 @@ ORT_API_STATUS_IMPL(OrtModelPackageApi_SelectComponent_SinceV28, API_IMPL_END } -ORT_API(void, OrtModelPackageApi_ReleaseModelPackageComponentContext_SinceV28, +ORT_API(void, ReleaseModelPackageComponentContext, _Frees_ptr_opt_ OrtModelPackageComponentContext* cix) { #if !defined(ORT_MINIMAL_BUILD) delete reinterpret_cast(cix); @@ -245,7 +247,7 @@ ORT_API(void, OrtModelPackageApi_ReleaseModelPackageComponentContext_SinceV28, #endif } -ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantFolderPath_SinceV28, +ORT_API_STATUS_IMPL(ModelPackageComponent_GetSelectedVariantFolderPath, _In_ const OrtModelPackageComponentContext* ctx, _Outptr_ const ORTCHAR_T** folder_path) { API_IMPL_BEGIN @@ -270,7 +272,7 @@ ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantF API_IMPL_END } -ORT_API_STATUS_IMPL(OrtModelPackageApi_CreateSession_SinceV28, +ORT_API_STATUS_IMPL(CreateSession, _In_ const OrtEnv* env, _In_ OrtModelPackageComponentContext* ctx, _In_opt_ const OrtSessionOptions* session_options, @@ -408,29 +410,26 @@ ORT_API_STATUS_IMPL(OrtModelPackageApi_CreateSession_SinceV28, // ---------- API table ------------------------------------------------------ -ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_GetVariantEpName_SinceV28, +ORT_API_STATUS_IMPL(ModelPackage_GetVariantEpName, _In_ const OrtModelPackageContext* ctx, _In_ const char* component_name, _In_ const char* variant_name, _Outptr_result_maybenull_ const char** out_ep) { API_IMPL_BEGIN #if !defined(ORT_MINIMAL_BUILD) - if (ctx == nullptr || component_name == nullptr || variant_name == nullptr) { + if (ctx == nullptr || component_name == nullptr || variant_name == nullptr || out_ep == nullptr) { return OrtApis::CreateStatus(ORT_INVALID_ARGUMENT, - "ctx, component_name, and variant_name must be non-null"); + "ctx, component_name, variant_name, and out_ep must be non-null"); } const onnxruntime::VariantEpCompatibilityInfo* info = nullptr; auto status = reinterpret_cast(ctx)->GetVariantEpCompatibility( component_name, variant_name, info); if (!status.IsOK()) { - if (out_ep != nullptr) *out_ep = nullptr; return onnxruntime::ToOrtStatus(status); } - if (out_ep != nullptr) { - *out_ep = (info != nullptr && info->ep.has_value()) ? info->ep->c_str() : nullptr; - } + *out_ep = (info != nullptr && info->ep.has_value()) ? info->ep->c_str() : nullptr; return nullptr; #else ORT_UNUSED_PARAMETER(ctx); @@ -442,7 +441,7 @@ ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_GetVariantEpName_SinceV28, API_IMPL_END } -ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_ResolveStringRef_SinceV28, +ORT_API_STATUS_IMPL(ModelPackage_ResolveStringRef, _In_ const OrtModelPackageContext* ctx, _In_opt_ const char* base_dir, _In_ const char* input, @@ -453,7 +452,6 @@ ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_ResolveStringRef_SinceV28, if (ctx == nullptr || input == nullptr || out_path == nullptr) { return OrtApis::CreateStatus(ORT_INVALID_ARGUMENT, "ctx, input, and out_path must be non-null"); } - *out_path = nullptr; const char* resolved = nullptr; auto status = reinterpret_cast(ctx)->ResolveStringRef( @@ -475,7 +473,7 @@ ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_ResolveStringRef_SinceV28, API_IMPL_END } -ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_GetSchemaVersion_SinceV28, +ORT_API_STATUS_IMPL(ModelPackage_GetSchemaVersion, _In_ const OrtModelPackageContext* ctx, _Out_ int64_t* out_version) { API_IMPL_BEGIN @@ -495,7 +493,7 @@ ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackage_GetSchemaVersion_SinceV28, API_IMPL_END } -ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantName_SinceV28, +ORT_API_STATUS_IMPL(ModelPackageComponent_GetSelectedVariantName, _In_ const OrtModelPackageComponentContext* ctx, _Outptr_ const char** out_name) { API_IMPL_BEGIN @@ -519,4 +517,27 @@ ORT_API_STATUS_IMPL(OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantN API_IMPL_END } -} // namespace OrtExperimentalApis +static constexpr OrtModelPackageApi ort_model_package_api = { + &CreateModelPackageOptionsFromSessionOptions, + &ReleaseModelPackageOptions, + &CreateModelPackageContext, + &ReleaseModelPackageContext, + &ModelPackage_GetSchemaVersion, + &ModelPackage_GetComponentCount, + &ModelPackage_GetComponentNames, + &ModelPackage_GetVariantCount, + &ModelPackage_GetVariantNames, + &ModelPackage_GetVariantEpName, + &ModelPackage_ResolveStringRef, + &SelectComponent, + &ReleaseModelPackageComponentContext, + &ModelPackageComponent_GetSelectedVariantName, + &ModelPackageComponent_GetSelectedVariantFolderPath, + &CreateSession, +}; + +ORT_API(const OrtModelPackageApi*, GetModelPackageApi) { + return &ort_model_package_api; +} + +} // namespace OrtModelPackageAPI diff --git a/onnxruntime/core/session/model_package_api.h b/onnxruntime/core/session/model_package_api.h new file mode 100644 index 0000000000000..bcb151c18aae8 --- /dev/null +++ b/onnxruntime/core/session/model_package_api.h @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include "core/session/onnxruntime_c_api.h" + +namespace OrtModelPackageAPI { + +ORT_API(const OrtModelPackageApi*, GetModelPackageApi); + +ORT_API_STATUS_IMPL(CreateModelPackageOptionsFromSessionOptions, + _In_ const OrtEnv* env, + _In_ const OrtSessionOptions* session_options, + _Outptr_ OrtModelPackageOptions** out); +ORT_API(void, ReleaseModelPackageOptions, _Frees_ptr_opt_ OrtModelPackageOptions* options); + +ORT_API_STATUS_IMPL(CreateModelPackageContext, + _In_ const ORTCHAR_T* package_root, + _Outptr_ OrtModelPackageContext** out); +ORT_API(void, ReleaseModelPackageContext, _Frees_ptr_opt_ OrtModelPackageContext* ctx); + +ORT_API_STATUS_IMPL(ModelPackage_GetSchemaVersion, + _In_ const OrtModelPackageContext* ctx, + _Out_ int64_t* out_version); +ORT_API_STATUS_IMPL(ModelPackage_GetComponentCount, + _In_ const OrtModelPackageContext* ctx, + _Out_ size_t* out_count); +ORT_API_STATUS_IMPL(ModelPackage_GetComponentNames, + _In_ const OrtModelPackageContext* ctx, + _Outptr_result_buffer_maybenull_(*out_count) const char* const** out_names, + _Out_ size_t* out_count); +ORT_API_STATUS_IMPL(ModelPackage_GetVariantCount, + _In_ const OrtModelPackageContext* ctx, + _In_ const char* component_name, + _Out_ size_t* out_count); +ORT_API_STATUS_IMPL(ModelPackage_GetVariantNames, + _In_ const OrtModelPackageContext* ctx, + _In_ const char* component_name, + _Outptr_result_buffer_maybenull_(*out_count) const char* const** out_variant_names, + _Out_ size_t* out_count); +ORT_API_STATUS_IMPL(ModelPackage_GetVariantEpName, + _In_ const OrtModelPackageContext* ctx, + _In_ const char* component_name, + _In_ const char* variant_name, + _Outptr_result_maybenull_ const char** out_ep); +ORT_API_STATUS_IMPL(ModelPackage_ResolveStringRef, + _In_ const OrtModelPackageContext* ctx, + _In_opt_ const char* base_dir, + _In_ const char* input, + _In_ int must_exist, + _Outptr_ const char** out_path); + +ORT_API_STATUS_IMPL(SelectComponent, + _In_ const OrtModelPackageContext* context, + _In_ const char* component_name, + _In_ const OrtModelPackageOptions* options, + _Outptr_ OrtModelPackageComponentContext** out); +ORT_API(void, ReleaseModelPackageComponentContext, + _Frees_ptr_opt_ OrtModelPackageComponentContext* ctx); +ORT_API_STATUS_IMPL(ModelPackageComponent_GetSelectedVariantName, + _In_ const OrtModelPackageComponentContext* ctx, + _Outptr_ const char** out_name); +ORT_API_STATUS_IMPL(ModelPackageComponent_GetSelectedVariantFolderPath, + _In_ const OrtModelPackageComponentContext* ctx, + _Outptr_ const ORTCHAR_T** folder_path); + +ORT_API_STATUS_IMPL(CreateSession, + _In_ const OrtEnv* env, + _In_ OrtModelPackageComponentContext* ctx, + _In_opt_ const OrtSessionOptions* session_options, + _Outptr_ OrtSession** session); + +} // namespace OrtModelPackageAPI diff --git a/onnxruntime/core/session/onnxruntime_c_api.cc b/onnxruntime/core/session/onnxruntime_c_api.cc index 15896eb03214d..ee046b19b9f39 100644 --- a/onnxruntime/core/session/onnxruntime_c_api.cc +++ b/onnxruntime/core/session/onnxruntime_c_api.cc @@ -43,6 +43,7 @@ #include "core/session/environment.h" #include "core/session/ep_graph_assignment_info.h" #include "core/session/interop_api.h" +#include "core/session/model_package_api.h" #include "core/session/onnxruntime_ep_device_ep_metadata_keys.h" #include "core/session/plugin_ep/ep_api.h" #include "core/session/plugin_ep/ep_library_internal.h" @@ -3715,6 +3716,10 @@ ORT_API(const OrtCompileApi*, OrtApis::GetCompileApi) { return OrtCompileAPI::GetCompileApi(); } +ORT_API(const OrtModelPackageApi*, OrtApis::GetModelPackageApi) { + return OrtModelPackageAPI::GetModelPackageApi(); +} + ORT_API(void, OrtApis::CreateKeyValuePairs, _Outptr_ OrtKeyValuePairs** out) { auto kvps = std::make_unique(); *out = reinterpret_cast(kvps.release()); @@ -4946,6 +4951,7 @@ static constexpr OrtApi ort_api_1_to_29 = { // End of Version 30 - DO NOT MODIFY ABOVE (see above text for more information) &OrtApis::SessionOptionsSetEpContextDataReadFunc, + &OrtApis::GetModelPackageApi, }; // OrtApiBase can never change as there is no way to know what version of OrtApiBase is returned by OrtGetApiBase. diff --git a/onnxruntime/core/session/ort_apis.h b/onnxruntime/core/session/ort_apis.h index e3449f8b39670..c448312884087 100644 --- a/onnxruntime/core/session/ort_apis.h +++ b/onnxruntime/core/session/ort_apis.h @@ -831,6 +831,9 @@ ORT_API_STATUS_IMPL(GetTensorElementTypeAndShapeDataReference, _In_ const OrtVal _Outptr_result_maybenull_ const int64_t** shape_data, _Out_ size_t* shape_data_count); +// Model Package API +ORT_API(const OrtModelPackageApi*, GetModelPackageApi); + // Experimental API ORT_API(OrtExperimentalFnPtr, GetExperimentalFunction, _In_ const char* name); diff --git a/onnxruntime/test/autoep/test_model_package.cc b/onnxruntime/test/autoep/test_model_package.cc index 601364d44fd44..5f5d6f7b539ab 100644 --- a/onnxruntime/test/autoep/test_model_package.cc +++ b/onnxruntime/test/autoep/test_model_package.cc @@ -15,7 +15,7 @@ #include "nlohmann/json.hpp" #include "core/session/model_package/model_package_context.h" -#include "core/session/onnxruntime_experimental_cxx_api.h" +#include "core/session/onnxruntime_cxx_api.h" #include "core/session/onnxruntime_session_options_config_keys.h" #include "core/session/abi_devices.h" #include "test/autoep/test_autoep_utils.h" @@ -28,83 +28,8 @@ namespace onnxruntime { namespace test { namespace { -// Typed function pointers for every OrtModelPackageApi_* experimental entry, -// resolved once via the experimental name-based lookup. -struct ModelPackageFns { - OrtExperimental_OrtModelPackageApi_CreateModelPackageOptionsFromSessionOptions_SinceV28_Fn - CreateModelPackageOptionsFromSessionOptions{nullptr}; - OrtExperimental_OrtModelPackageApi_ReleaseModelPackageOptions_SinceV28_Fn - ReleaseModelPackageOptions{nullptr}; - OrtExperimental_OrtModelPackageApi_CreateModelPackageContext_SinceV28_Fn - CreateModelPackageContext{nullptr}; - OrtExperimental_OrtModelPackageApi_ReleaseModelPackageContext_SinceV28_Fn - ReleaseModelPackageContext{nullptr}; - OrtExperimental_OrtModelPackageApi_ModelPackage_GetSchemaVersion_SinceV28_Fn - ModelPackage_GetSchemaVersion{nullptr}; - OrtExperimental_OrtModelPackageApi_ModelPackage_GetComponentCount_SinceV28_Fn - ModelPackage_GetComponentCount{nullptr}; - OrtExperimental_OrtModelPackageApi_ModelPackage_GetComponentNames_SinceV28_Fn - ModelPackage_GetComponentNames{nullptr}; - OrtExperimental_OrtModelPackageApi_ModelPackage_GetVariantCount_SinceV28_Fn - ModelPackage_GetVariantCount{nullptr}; - OrtExperimental_OrtModelPackageApi_ModelPackage_GetVariantNames_SinceV28_Fn - ModelPackage_GetVariantNames{nullptr}; - OrtExperimental_OrtModelPackageApi_ModelPackage_GetVariantEpName_SinceV28_Fn - ModelPackage_GetVariantEpName{nullptr}; - OrtExperimental_OrtModelPackageApi_ModelPackage_ResolveStringRef_SinceV28_Fn - ModelPackage_ResolveStringRef{nullptr}; - OrtExperimental_OrtModelPackageApi_SelectComponent_SinceV28_Fn - SelectComponent{nullptr}; - OrtExperimental_OrtModelPackageApi_ReleaseModelPackageComponentContext_SinceV28_Fn - ReleaseModelPackageComponentContext{nullptr}; - OrtExperimental_OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantName_SinceV28_Fn - ModelPackageComponent_GetSelectedVariantName{nullptr}; - OrtExperimental_OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantFolderPath_SinceV28_Fn - ModelPackageComponent_GetSelectedVariantFolderPath{nullptr}; - OrtExperimental_OrtModelPackageApi_CreateSession_SinceV28_Fn - CreateSession{nullptr}; -}; - -inline const ModelPackageFns& GetModelPackageFns() { - static const ModelPackageFns fns = []() { - const OrtApi* api = &Ort::GetApi(); - namespace Exp = Ort::Experimental; - ModelPackageFns f; - f.CreateModelPackageOptionsFromSessionOptions = - Exp::Get_OrtModelPackageApi_CreateModelPackageOptionsFromSessionOptions_SinceV28_FnOrThrow(api); - f.ReleaseModelPackageOptions = - Exp::Get_OrtModelPackageApi_ReleaseModelPackageOptions_SinceV28_FnOrThrow(api); - f.CreateModelPackageContext = - Exp::Get_OrtModelPackageApi_CreateModelPackageContext_SinceV28_FnOrThrow(api); - f.ReleaseModelPackageContext = - Exp::Get_OrtModelPackageApi_ReleaseModelPackageContext_SinceV28_FnOrThrow(api); - f.ModelPackage_GetSchemaVersion = - Exp::Get_OrtModelPackageApi_ModelPackage_GetSchemaVersion_SinceV28_FnOrThrow(api); - f.ModelPackage_GetComponentCount = - Exp::Get_OrtModelPackageApi_ModelPackage_GetComponentCount_SinceV28_FnOrThrow(api); - f.ModelPackage_GetComponentNames = - Exp::Get_OrtModelPackageApi_ModelPackage_GetComponentNames_SinceV28_FnOrThrow(api); - f.ModelPackage_GetVariantCount = - Exp::Get_OrtModelPackageApi_ModelPackage_GetVariantCount_SinceV28_FnOrThrow(api); - f.ModelPackage_GetVariantNames = - Exp::Get_OrtModelPackageApi_ModelPackage_GetVariantNames_SinceV28_FnOrThrow(api); - f.ModelPackage_GetVariantEpName = - Exp::Get_OrtModelPackageApi_ModelPackage_GetVariantEpName_SinceV28_FnOrThrow(api); - f.ModelPackage_ResolveStringRef = - Exp::Get_OrtModelPackageApi_ModelPackage_ResolveStringRef_SinceV28_FnOrThrow(api); - f.SelectComponent = - Exp::Get_OrtModelPackageApi_SelectComponent_SinceV28_FnOrThrow(api); - f.ReleaseModelPackageComponentContext = - Exp::Get_OrtModelPackageApi_ReleaseModelPackageComponentContext_SinceV28_FnOrThrow(api); - f.ModelPackageComponent_GetSelectedVariantName = - Exp::Get_OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantName_SinceV28_FnOrThrow(api); - f.ModelPackageComponent_GetSelectedVariantFolderPath = - Exp::Get_OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantFolderPath_SinceV28_FnOrThrow(api); - f.CreateSession = - Exp::Get_OrtModelPackageApi_CreateSession_SinceV28_FnOrThrow(api); - return f; - }(); - return fns; +inline const OrtModelPackageApi& GetModelPackageFns() { + return Ort::GetModelPackageApi(); } // ──────────────────────────────────────────────────────────────────────────── @@ -204,7 +129,7 @@ std::filesystem::path BuildTwoVariantPackage(const std::filesystem::path& packag return BuildPackage(package_root, "model_1", variants); } -// Runs the full experimental OrtModelPackageApi flow for a single-component package and returns the +// Runs the full OrtModelPackageApi flow for a single-component package and returns the // created session: CreateModelPackageOptionsFromSessionOptions -> CreateModelPackageContext -> // SelectComponent -> CreateSession. `session_options` drives both variant/EP selection and session // creation (advanced path), mirroring how a caller loads a package. Throws on any error. @@ -249,7 +174,7 @@ TEST(ModelPackageApiTest, PackageContextQueries) { "testdata/mul_16.onnx"); const auto& pkg_api = GetModelPackageFns(); - ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package experimental API is not available"; + ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package API is not available"; auto context_deleter = [&pkg_api](OrtModelPackageContext* p) { if (p) pkg_api.ReleaseModelPackageContext(p); @@ -278,6 +203,13 @@ TEST(ModelPackageApiTest, PackageContextQueries) { model_pkg_context.get(), "model_1", &variant_count)); ASSERT_EQ(variant_count, 2u); + size_t missing_variant_count = 123; + OrtStatus* status = pkg_api.ModelPackage_GetVariantCount( + model_pkg_context.get(), "missing_component", &missing_variant_count); + ASSERT_NE(status, nullptr); + EXPECT_EQ(missing_variant_count, 123u); + Ort::GetApi().ReleaseStatus(status); + const char* const* variant_names = nullptr; size_t variant_name_count = 0; ASSERT_ORTSTATUS_OK(pkg_api.ModelPackage_GetVariantNames( @@ -296,6 +228,71 @@ TEST(ModelPackageApiTest, PackageContextQueries) { std::filesystem::remove_all(package_root, ec); } +TEST(ModelPackageApiTest, CxxWrappers_MetadataSelectionAndSessionLifetime) { + const auto package_root = std::filesystem::temp_directory_path() / "ort_model_package_cxx_api_test"; + BuildTwoVariantPackage(package_root, + "variant_1", "cpu", "", + "testdata/mul_1.onnx", + "variant_2", "npu", "", + "testdata/mul_16.onnx"); + + RegisteredEpDeviceUniquePtr example_ep; + ASSERT_NO_FATAL_FAILURE(Utils::RegisterAndGetExampleEp(*ort_env, Utils::example_ep_info, example_ep)); + Ort::ConstEpDevice plugin_ep_device(example_ep.get()); + + Ort::SessionOptions session_options; + std::unordered_map ep_options; + session_options.AppendExecutionProvider_V2(*ort_env, {plugin_ep_device}, ep_options); + + Ort::ModelPackageComponentContext component_context{nullptr}; + { + Ort::ModelPackageOptions options_source(*ort_env, session_options.GetConst()); + Ort::ModelPackageOptions package_options = std::move(options_source); + Ort::ModelPackageContext context_source(package_root.c_str()); + Ort::ModelPackageContext package_context = std::move(context_source); + + EXPECT_EQ(package_context.GetSchemaVersion(), 1); + EXPECT_EQ(package_context.GetComponentCount(), 1u); + EXPECT_THAT(package_context.GetComponentNames(), ::testing::ElementsAre("model_1")); + EXPECT_EQ(package_context.GetVariantCount("model_1"), 2u); + EXPECT_THAT(package_context.GetVariantNames("model_1"), + ::testing::UnorderedElementsAre("variant_1", "variant_2")); + + try { + (void)package_context.GetVariantCount("missing_component"); + FAIL() << "Expected missing component lookup to throw"; + } catch (const Ort::Exception& ex) { + EXPECT_EQ(ex.GetOrtErrorCode(), ORT_INVALID_ARGUMENT); + } + + auto selected = package_context.SelectComponent("model_1", package_options); + EXPECT_EQ(selected.GetSelectedVariantName(), "variant_1"); + component_context = std::move(selected); + } + + Ort::Session session = component_context.CreateSession(*ort_env, session_options.GetConst()); + + Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU); + std::vector shape = {3, 2}; + std::vector input_data = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; + Ort::Value input = Ort::Value::CreateTensor(memory_info, input_data.data(), input_data.size(), + shape.data(), shape.size()); + const char* input_names[] = {"X"}; + const char* output_names[] = {"Y"}; + std::vector inputs; + inputs.push_back(std::move(input)); + + auto outputs = session.Run(Ort::RunOptions{nullptr}, input_names, inputs.data(), inputs.size(), + output_names, 1); + ASSERT_EQ(outputs.size(), 1u); + const float* out = outputs[0].GetTensorData(); + gsl::span out_span(out, input_data.size()); + EXPECT_THAT(out_span, ::testing::ElementsAre(1.f, 4.f, 9.f, 16.f, 25.f, 36.f)); + + std::error_code ec; + std::filesystem::remove_all(package_root, ec); +} + TEST(ModelPackageApiTest, ResolveStringRef) { const auto package_root = std::filesystem::temp_directory_path() / "ort_model_package_resolve_test"; std::vector variants; @@ -312,7 +309,7 @@ TEST(ModelPackageApiTest, ResolveStringRef) { } const auto& pkg_api = GetModelPackageFns(); - ASSERT_NE(pkg_api.ModelPackage_ResolveStringRef, nullptr) << "Model package experimental API is not available"; + ASSERT_NE(pkg_api.ModelPackage_ResolveStringRef, nullptr) << "Model package API is not available"; auto context_deleter = [&pkg_api](OrtModelPackageContext* p) { if (p) pkg_api.ReleaseModelPackageContext(p); @@ -345,9 +342,12 @@ TEST(ModelPackageApiTest, ResolveStringRef) { // An undeclared sha256 asset is rejected even when must_exist is false. const std::string missing_digest(64, 'b'); + const char* const sentinel = "unchanged"; + resolved = sentinel; OrtStatus* status = pkg_api.ModelPackage_ResolveStringRef( ctx.get(), nullptr, ("sha256:" + missing_digest).c_str(), /*must_exist=*/0, &resolved); EXPECT_NE(status, nullptr); + EXPECT_EQ(resolved, sentinel); if (status != nullptr) Ort::GetApi().ReleaseStatus(status); std::error_code ec; @@ -382,7 +382,7 @@ TEST(ModelPackageApiTest, SingleFileVariantInComponent_SelectComponentAndCreateS session_options.AppendExecutionProvider_V2(*ort_env, {plugin_ep_device}, ep_options); const auto& pkg_api = GetModelPackageFns(); - ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package experimental API is not available"; + ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package API is not available"; auto options_deleter = [&pkg_api](OrtModelPackageOptions* p) { if (p) pkg_api.ReleaseModelPackageOptions(p); @@ -680,7 +680,7 @@ TEST(ModelPackageApiTest, GetVariantEpName_ReturnsSingleEp) { BuildPackage(package_root, "model_1", variants); const auto& pkg_api = GetModelPackageFns(); - ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package experimental API is not available"; + ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package API is not available"; auto context_deleter = [&pkg_api](OrtModelPackageContext* p) { if (p) pkg_api.ReleaseModelPackageContext(p); @@ -702,9 +702,18 @@ TEST(ModelPackageApiTest, GetVariantEpName_ReturnsSingleEp) { ASSERT_NE(ep2, nullptr); EXPECT_STREQ(ep2, "other_ep"); - // Optional out-parameter: callers can pass NULL. - ASSERT_ORTSTATUS_OK(pkg_api.ModelPackage_GetVariantEpName( - ctx.get(), "model_1", "variant_1", nullptr)); + OrtStatus* status = pkg_api.ModelPackage_GetVariantEpName( + ctx.get(), "model_1", "variant_1", nullptr); + ASSERT_NE(status, nullptr); + EXPECT_EQ(Ort::GetApi().GetErrorCode(status), ORT_INVALID_ARGUMENT); + Ort::GetApi().ReleaseStatus(status); + + const char* missing_ep = "unchanged"; + status = pkg_api.ModelPackage_GetVariantEpName( + ctx.get(), "model_1", "missing_variant", &missing_ep); + ASSERT_NE(status, nullptr); + EXPECT_STREQ(missing_ep, "unchanged"); + Ort::GetApi().ReleaseStatus(status); std::error_code ec; std::filesystem::remove_all(package_root, ec); @@ -733,7 +742,7 @@ TEST(ModelPackageTest, VariantSelector_TieBreakIsDeterministic) { session_options.AppendExecutionProvider_V2(*ort_env, {plugin_ep_device}, ep_options); const auto& pkg_api = GetModelPackageFns(); - ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package experimental API is not available"; + ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package API is not available"; auto options_deleter = [&pkg_api](OrtModelPackageOptions* p) { if (p) pkg_api.ReleaseModelPackageOptions(p); }; auto context_deleter = [&pkg_api](OrtModelPackageContext* p) { if (p) pkg_api.ReleaseModelPackageContext(p); }; @@ -798,7 +807,7 @@ TEST(ModelPackageTest, VariantSessionOptions_DispatchedThroughAddSessionConfigEn session_options.AppendExecutionProvider_V2(*ort_env, {plugin_ep_device}, ep_options); const auto& pkg_api = GetModelPackageFns(); - ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package experimental API is not available"; + ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package API is not available"; auto options_deleter = [&pkg_api](OrtModelPackageOptions* p) { if (p) pkg_api.ReleaseModelPackageOptions(p); }; auto context_deleter = [&pkg_api](OrtModelPackageContext* p) { if (p) pkg_api.ReleaseModelPackageContext(p); }; @@ -875,7 +884,7 @@ TEST(ModelPackageTest, VariantSessionOption_ResolvesExternalInitializersFolder) session_options.AppendExecutionProvider_V2(*ort_env, {plugin_ep_device}, ep_options); const auto& pkg_api = GetModelPackageFns(); - ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package experimental API is not available"; + ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package API is not available"; auto options_deleter = [&pkg_api](OrtModelPackageOptions* p) { if (p) pkg_api.ReleaseModelPackageOptions(p); }; auto context_deleter = [&pkg_api](OrtModelPackageContext* p) { if (p) pkg_api.ReleaseModelPackageContext(p); }; @@ -919,7 +928,7 @@ TEST(ModelPackageTest, VariantSessionOption_ResolvesExternalInitializersFolder) TEST(ModelPackageTest, VariantSessionOption_RejectsOutputFileOptions) { const auto package_root = std::filesystem::temp_directory_path() / "ort_mp_output_file_options"; const auto& pkg_api = GetModelPackageFns(); - ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package experimental API is not available"; + ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package API is not available"; for (const auto* option_key : {kDebugLayoutTransformation, kOrtSessionOptionsCollectNodeMemoryStatsToFile, @@ -957,7 +966,7 @@ TEST(ModelPackageTest, VariantSessionOption_RejectsEmbeddedNulInKey) { BuildPackage(package_root, "model_1", variants); const auto& pkg_api = GetModelPackageFns(); - ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package experimental API is not available"; + ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package API is not available"; OrtModelPackageContext* raw_context = nullptr; OrtStatus* status = pkg_api.CreateModelPackageContext(package_root.c_str(), &raw_context); @@ -997,7 +1006,7 @@ TEST(ModelPackageApiTest, FolderPath_ReturnsCorrectPath_WhenExecutorInfoAbsent) so.AppendExecutionProvider_V2(*ort_env, {plugin_ep_device}, ep_options); const auto& pkg_api = GetModelPackageFns(); - ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package experimental API is not available"; + ASSERT_NE(pkg_api.CreateModelPackageContext, nullptr) << "Model package API is not available"; OrtModelPackageOptions* raw_mp_opts = nullptr; ASSERT_ORTSTATUS_OK(pkg_api.CreateModelPackageOptionsFromSessionOptions(*ort_env, so, &raw_mp_opts));