Skip to content

Commit f481b17

Browse files
adrastogiAditya Rastogigithub-actions[bot]Copilot
authored
Add dedicated API to support extracting compatibility string from model metadata (#27015)
### Description This change proposes a new helper ORT API for callers that need to extract the model compatibility string from a precompiled model. ### Motivation and Context <!-- - Why is this change required? What problem does it solve? - If it fixes an open issue, please link to the issue here. --> See #25749 for more background on the model compatibility concept and infrastructure; #25841 provides a related helper API for an application to call to do a validation check using the compatibility info string. However, there is no direct way to get to the model metadata without creating a session (which some callers may prefer to avoid) or by taking a dependency on a separate library to parse the model's protobuf (which again callers may prefer to avoid). This change proposes a separate helper API which can be used to retrieve the compatibility info string, thereby avoiding session creation or an external dependency. This does incur some amount of redundant work in that the model protobuf will be parsed again during session creation- but for some callers, this tradeoff may be acceptable. --------- Co-authored-by: Aditya Rastogi <adityar@ntdev.microsoft.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: adrastogi <8368026+adrastogi@users.noreply.github.com>
1 parent 347b990 commit f481b17

File tree

6 files changed

+492
-1
lines changed

6 files changed

+492
-1
lines changed

include/onnxruntime/core/session/onnxruntime_c_api.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7003,6 +7003,77 @@ struct OrtApi {
70037003

70047004
/// @}
70057005

7006+
/// \name Model Compatibility APIs
7007+
/// @{
7008+
7009+
/** \brief Extract EP compatibility info from a precompiled model file.
7010+
*
7011+
* Parses the model file to extract the compatibility info string for a specific execution provider
7012+
* from the model's metadata properties. This is only applicable to models that have been precompiled
7013+
* for an EP (e.g., via OrtCompileApi). Standard ONNX models do not contain this information.
7014+
*
7015+
* The compatibility info string must be valid UTF-8 without embedded NUL characters.
7016+
*
7017+
* \note This API performs standalone model parsing, separate from session creation. This means
7018+
* the protobuf parsing cost is incurred here and again during session creation. It is intended
7019+
* for scenarios where applications need to check compatibility before deciding whether to proceed
7020+
* with session creation, such as providing early user feedback.
7021+
*
7022+
* \note This operation parses the full ONNX ModelProto from disk. For very large models, consider
7023+
* using GetCompatibilityInfoFromModelBytes with a pre-loaded buffer if the model is already in memory.
7024+
*
7025+
* The compatibility info can then be passed to GetModelCompatibilityForEpDevices to check if a
7026+
* precompiled model is compatible with the current system.
7027+
*
7028+
* \param[in] model_path Path to the ONNX model file.
7029+
* \param[in] ep_type The execution provider type string. Must be non-empty.
7030+
* Use OrtApi::EpDevice_EpName to get this value from an OrtEpDevice.
7031+
* \param[in] allocator Allocator to use for the output string. Use OrtApi::GetAllocatorWithDefaultOptions.
7032+
* \param[out] compatibility_info Output pointer to the compatibility info string.
7033+
* Returns nullptr if no compatibility info exists for the specified EP.
7034+
* Caller must free with OrtApi::AllocatorFree when non-null.
7035+
*
7036+
* \snippet{doc} snippets.dox OrtStatus Return Value
7037+
*
7038+
* \since Version 1.24.
7039+
*/
7040+
ORT_API2_STATUS(GetCompatibilityInfoFromModel,
7041+
_In_ const ORTCHAR_T* model_path,
7042+
_In_ const char* ep_type,
7043+
_Inout_ OrtAllocator* allocator,
7044+
_Outptr_result_maybenull_ char** compatibility_info);
7045+
7046+
/** \brief Extract EP compatibility info from precompiled model bytes in memory.
7047+
*
7048+
* Same as GetCompatibilityInfoFromModel but reads from a memory buffer instead of a file.
7049+
* Useful when precompiled models are loaded from encrypted storage, network, or other non-file sources.
7050+
*
7051+
* \note This API performs standalone model parsing, separate from session creation. This means
7052+
* the protobuf parsing cost is incurred here and again during session creation. It is intended
7053+
* for scenarios where applications need to check compatibility before deciding whether to proceed
7054+
* with session creation, such as providing early user feedback.
7055+
*
7056+
* \param[in] model_data Pointer to the model data in memory.
7057+
* \param[in] model_data_length Size of the model data in bytes.
7058+
* \param[in] ep_type The execution provider type string. Must be non-empty.
7059+
* \param[in] allocator Allocator to use for the output string. Use OrtApi::GetAllocatorWithDefaultOptions.
7060+
* \param[out] compatibility_info Output pointer to the compatibility info string.
7061+
* Returns nullptr if no compatibility info exists for the specified EP.
7062+
* Caller must free with OrtApi::AllocatorFree when non-null.
7063+
*
7064+
* \snippet{doc} snippets.dox OrtStatus Return Value
7065+
*
7066+
* \since Version 1.24.
7067+
*/
7068+
ORT_API2_STATUS(GetCompatibilityInfoFromModelBytes,
7069+
_In_reads_(model_data_length) const void* model_data,
7070+
_In_ size_t model_data_length,
7071+
_In_ const char* ep_type,
7072+
_Inout_ OrtAllocator* allocator,
7073+
_Outptr_result_maybenull_ char** compatibility_info);
7074+
7075+
/// @}
7076+
70067077
/** \brief Create an OrtEnv instance with the given options.
70077078
*
70087079
* \note Invoking this function will return the same instance of the environment as that returned by a previous call

include/onnxruntime/core/session/onnxruntime_cxx_api.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,39 @@ OrtCompiledModelCompatibility GetModelCompatibilityForEpDevices(
11641164
const std::vector<ConstEpDevice>& ep_devices,
11651165
const char* compatibility_info);
11661166

1167+
/** \brief Extract EP compatibility info from a precompiled model file.
1168+
*
1169+
* Parses the model file to extract the compatibility info string for a specific execution provider
1170+
* from the model's metadata properties. This is only applicable to models that have been precompiled
1171+
* for an EP. Standard ONNX models do not contain this information.
1172+
*
1173+
* \note This operation parses the full ONNX ModelProto from disk.
1174+
*
1175+
* \param model_path Path to the ONNX model file.
1176+
* \param ep_type The execution provider type string. Must be non-empty.
1177+
* Use ConstEpDevice::EpName() to get this value.
1178+
* \param allocator Allocator to use for the output string.
1179+
* \return The compatibility info string, or nullptr if not found for this EP. Caller must free via allocator.
1180+
* \throws Ort::Exception on error.
1181+
*/
1182+
AllocatedStringPtr GetCompatibilityInfoFromModelAllocated(const ORTCHAR_T* model_path, const char* ep_type,
1183+
OrtAllocator* allocator);
1184+
1185+
/** \brief Extract EP compatibility info from precompiled model bytes in memory.
1186+
*
1187+
* Same as GetCompatibilityInfoFromModelAllocated but reads from a memory buffer.
1188+
* Useful when precompiled models are loaded from encrypted storage, network, or other non-file sources.
1189+
*
1190+
* \param model_data Pointer to the model data in memory.
1191+
* \param model_data_length Size of the model data in bytes.
1192+
* \param ep_type The execution provider type string. Must be non-empty.
1193+
* \param allocator Allocator to use for the output string.
1194+
* \return The compatibility info string, or nullptr if not found for this EP. Caller must free via allocator.
1195+
* \throws Ort::Exception on error.
1196+
*/
1197+
AllocatedStringPtr GetCompatibilityInfoFromModelBytesAllocated(const void* model_data, size_t model_data_length,
1198+
const char* ep_type, OrtAllocator* allocator);
1199+
11671200
namespace detail {
11681201
template <typename T>
11691202
struct EpAssignedNodeImpl : Ort::detail::Base<T> {

include/onnxruntime/core/session/onnxruntime_cxx_inline.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,20 @@ inline OrtCompiledModelCompatibility GetModelCompatibilityForEpDevices(
983983
return status;
984984
}
985985

986+
inline AllocatedStringPtr GetCompatibilityInfoFromModelAllocated(const ORTCHAR_T* model_path, const char* ep_type,
987+
OrtAllocator* allocator) {
988+
char* compat_info = nullptr;
989+
ThrowOnError(GetApi().GetCompatibilityInfoFromModel(model_path, ep_type, allocator, &compat_info));
990+
return AllocatedStringPtr(compat_info, detail::AllocatedFree(allocator));
991+
}
992+
993+
inline AllocatedStringPtr GetCompatibilityInfoFromModelBytesAllocated(const void* model_data, size_t model_data_length,
994+
const char* ep_type, OrtAllocator* allocator) {
995+
char* compat_info = nullptr;
996+
ThrowOnError(GetApi().GetCompatibilityInfoFromModelBytes(model_data, model_data_length, ep_type, allocator, &compat_info));
997+
return AllocatedStringPtr(compat_info, detail::AllocatedFree(allocator));
998+
}
999+
9861000
inline LoraAdapter LoraAdapter::CreateLoraAdapter(const std::basic_string<ORTCHAR_T>& adapter_path,
9871001
OrtAllocator* allocator) {
9881002
OrtLoraAdapter* p;

onnxruntime/core/session/onnxruntime_c_api.cc

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33

44
#include <algorithm>
55
#include <cassert>
6+
#include <climits>
67
#include <cstring>
78
#include <functional>
89
#include <mutex>
9-
#include <vector>
1010
#include <sstream>
11+
#include <vector>
1112

1213
#include "core/common/common.h"
1314
#include "core/common/logging/logging.h"
@@ -30,8 +31,10 @@
3031
#include "core/framework/utils.h"
3132
#include "core/graph/constants.h"
3233
#include "core/graph/graph.h"
34+
#include "core/graph/model.h"
3335
#include "core/graph/model_editor_api_types.h"
3436
#include "core/graph/ep_api_types.h"
37+
#include "core/graph/onnx_protobuf.h"
3538
#include "core/providers/get_execution_providers.h"
3639
#include "core/session/abi_devices.h"
3740
#include "core/session/abi_session_options_impl.h"
@@ -40,6 +43,7 @@
4043
#include "core/session/environment.h"
4144
#include "core/session/ep_graph_assignment_info.h"
4245
#include "core/session/interop_api.h"
46+
#include "core/session/onnxruntime_ep_device_ep_metadata_keys.h"
4347
#include "core/session/plugin_ep/ep_api.h"
4448
#include "core/session/plugin_ep/ep_library_internal.h"
4549
#include "core/session/inference_session.h"
@@ -3943,6 +3947,93 @@ ORT_API_STATUS_IMPL(OrtApis::GetModelCompatibilityForEpDevices,
39433947
API_IMPL_END
39443948
}
39453949

3950+
// Helper function to extract compatibility info from model metadata
3951+
static OrtStatus* ExtractCompatibilityInfoFromModelProto(
3952+
const ONNX_NAMESPACE::ModelProto& model_proto,
3953+
const char* ep_type,
3954+
OrtAllocator* allocator,
3955+
char** compatibility_info) {
3956+
// Build the key we're looking for
3957+
std::string target_key = std::string(kOrtModelMetadata_EpCompatibilityInfoPrefix) + ep_type;
3958+
3959+
// Search through metadata_props for the matching key
3960+
for (const auto& prop : model_proto.metadata_props()) {
3961+
if (prop.key() == target_key) {
3962+
// Found it - allocate and copy the value using the provided allocator
3963+
*compatibility_info = onnxruntime::StrDup(prop.value(), allocator);
3964+
if (*compatibility_info == nullptr) {
3965+
return OrtApis::CreateStatus(ORT_FAIL, "Failed to allocate memory for compatibility info.");
3966+
}
3967+
return nullptr;
3968+
}
3969+
}
3970+
3971+
// Key not found - return nullptr (not an error, just means no compat info for this EP)
3972+
*compatibility_info = nullptr;
3973+
return nullptr;
3974+
}
3975+
3976+
// Extract EP compatibility info from a model file
3977+
ORT_API_STATUS_IMPL(OrtApis::GetCompatibilityInfoFromModel,
3978+
_In_ const ORTCHAR_T* model_path,
3979+
_In_ const char* ep_type,
3980+
_Inout_ OrtAllocator* allocator,
3981+
_Outptr_result_maybenull_ char** compatibility_info) {
3982+
API_IMPL_BEGIN
3983+
if (model_path == nullptr || ep_type == nullptr || ep_type[0] == '\0' ||
3984+
allocator == nullptr || compatibility_info == nullptr) {
3985+
return OrtApis::CreateStatus(ORT_INVALID_ARGUMENT,
3986+
"Invalid argument provided to GetCompatibilityInfoFromModel.");
3987+
}
3988+
3989+
*compatibility_info = nullptr;
3990+
3991+
// Use Model::Load for proper cross-platform path handling via file descriptor
3992+
ONNX_NAMESPACE::ModelProto model_proto;
3993+
auto status = Model::Load(PathString(model_path), model_proto);
3994+
if (!status.IsOK()) {
3995+
if (status.Code() == common::NO_SUCHFILE) {
3996+
return OrtApis::CreateStatus(ORT_NO_SUCHFILE, status.ErrorMessage().c_str());
3997+
}
3998+
return OrtApis::CreateStatus(ORT_INVALID_GRAPH, status.ErrorMessage().c_str());
3999+
}
4000+
4001+
return ExtractCompatibilityInfoFromModelProto(model_proto, ep_type, allocator, compatibility_info);
4002+
API_IMPL_END
4003+
}
4004+
4005+
// Extract EP compatibility info from model bytes in memory
4006+
ORT_API_STATUS_IMPL(OrtApis::GetCompatibilityInfoFromModelBytes,
4007+
_In_reads_(model_data_length) const void* model_data,
4008+
_In_ size_t model_data_length,
4009+
_In_ const char* ep_type,
4010+
_Inout_ OrtAllocator* allocator,
4011+
_Outptr_result_maybenull_ char** compatibility_info) {
4012+
API_IMPL_BEGIN
4013+
if (model_data == nullptr || model_data_length == 0 || ep_type == nullptr || ep_type[0] == '\0' ||
4014+
allocator == nullptr || compatibility_info == nullptr) {
4015+
return OrtApis::CreateStatus(ORT_INVALID_ARGUMENT,
4016+
"Invalid argument provided to GetCompatibilityInfoFromModelBytes.");
4017+
}
4018+
4019+
*compatibility_info = nullptr;
4020+
4021+
// Explicit check for size limit - Model::LoadFromBytes uses int for size due to protobuf API
4022+
if (model_data_length > static_cast<size_t>(INT_MAX)) {
4023+
return OrtApis::CreateStatus(ORT_INVALID_ARGUMENT,
4024+
"Model data size exceeds maximum supported size (2GB). Use GetCompatibilityInfoFromModel with a file path instead.");
4025+
}
4026+
4027+
ONNX_NAMESPACE::ModelProto model_proto;
4028+
auto status = Model::LoadFromBytes(static_cast<int>(model_data_length), model_data, model_proto);
4029+
if (!status.IsOK()) {
4030+
return OrtApis::CreateStatus(ORT_INVALID_GRAPH, status.ErrorMessage().c_str());
4031+
}
4032+
4033+
return ExtractCompatibilityInfoFromModelProto(model_proto, ep_type, allocator, compatibility_info);
4034+
API_IMPL_END
4035+
}
4036+
39464037
// GetInteropApi - returns the Interop API struct
39474038
ORT_API(const OrtInteropApi*, OrtApis::GetInteropApi) {
39484039
return OrtInteropAPI::GetInteropApi();
@@ -3981,6 +4072,29 @@ ORT_API_STATUS_IMPL(OrtApis::GetModelCompatibilityForEpDevices,
39814072
API_IMPL_END
39824073
}
39834074

4075+
// Minimal build stub for GetCompatibilityInfoFromModel
4076+
ORT_API_STATUS_IMPL(OrtApis::GetCompatibilityInfoFromModel,
4077+
_In_ const ORTCHAR_T* /*model_path*/,
4078+
_In_ const char* /*ep_type*/,
4079+
_Inout_ OrtAllocator* /*allocator*/,
4080+
_Outptr_result_maybenull_ char** /*compatibility_info*/) {
4081+
API_IMPL_BEGIN
4082+
return OrtApis::CreateStatus(ORT_NOT_IMPLEMENTED, "GetCompatibilityInfoFromModel is not supported in a minimal build.");
4083+
API_IMPL_END
4084+
}
4085+
4086+
// Minimal build stub for GetCompatibilityInfoFromModelBytes
4087+
ORT_API_STATUS_IMPL(OrtApis::GetCompatibilityInfoFromModelBytes,
4088+
_In_reads_(model_data_length) const void* /*model_data*/,
4089+
_In_ size_t /*model_data_length*/,
4090+
_In_ const char* /*ep_type*/,
4091+
_Inout_ OrtAllocator* /*allocator*/,
4092+
_Outptr_result_maybenull_ char** /*compatibility_info*/) {
4093+
API_IMPL_BEGIN
4094+
return OrtApis::CreateStatus(ORT_NOT_IMPLEMENTED, "GetCompatibilityInfoFromModelBytes is not supported in a minimal build.");
4095+
API_IMPL_END
4096+
}
4097+
39844098
ORT_API_STATUS_IMPL(OrtApis::SessionOptionsAppendExecutionProvider_V2, _In_ OrtSessionOptions* /*session_options*/,
39854099
_In_ OrtEnv* /*env*/,
39864100
_In_reads_(num_ep_devices) const OrtEpDevice* const* /*ep_devices*/,
@@ -4677,6 +4791,9 @@ static constexpr OrtApi ort_api_1_to_25 = {
46774791
&OrtApis::DeviceEpIncompatibilityDetails_GetNotes,
46784792
&OrtApis::DeviceEpIncompatibilityDetails_GetErrorCode,
46794793
&OrtApis::ReleaseDeviceEpIncompatibilityDetails,
4794+
&OrtApis::GetCompatibilityInfoFromModel,
4795+
&OrtApis::GetCompatibilityInfoFromModelBytes,
4796+
46804797
&OrtApis::CreateEnvWithOptions,
46814798
&OrtApis::Session_GetEpGraphAssignmentInfo,
46824799
&OrtApis::EpAssignedSubgraph_GetEpName,

onnxruntime/core/session/ort_apis.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,17 @@ ORT_API_STATUS_IMPL(GetModelCompatibilityForEpDevices,
660660
_In_ size_t num_ep_devices,
661661
_In_ const char* compatibility_info,
662662
_Out_ OrtCompiledModelCompatibility* out_status);
663+
ORT_API_STATUS_IMPL(GetCompatibilityInfoFromModel,
664+
_In_ const ORTCHAR_T* model_path,
665+
_In_ const char* ep_type,
666+
_Inout_ OrtAllocator* allocator,
667+
_Outptr_result_maybenull_ char** compatibility_info);
668+
ORT_API_STATUS_IMPL(GetCompatibilityInfoFromModelBytes,
669+
_In_reads_(model_data_length) const void* model_data,
670+
_In_ size_t model_data_length,
671+
_In_ const char* ep_type,
672+
_Inout_ OrtAllocator* allocator,
673+
_Outptr_result_maybenull_ char** compatibility_info);
663674
ORT_API_STATUS_IMPL(Graph_GetModelPath, _In_ const OrtGraph* graph, _Outptr_ const ORTCHAR_T** model_path);
664675
ORT_API_STATUS_IMPL(Graph_GetOnnxIRVersion, _In_ const OrtGraph* graph, _Out_ int64_t* onnx_ir_version);
665676
ORT_API_STATUS_IMPL(Graph_GetNumOperatorSets, _In_ const OrtGraph* graph, _Out_ size_t* num_operator_sets);

0 commit comments

Comments
 (0)