Skip to content

Commit 97ccf3f

Browse files
edgchen1skottmckay
andauthored
Add OrtEpFactory::GetVersion and store EP version in EP metadata. (microsoft#25272)
### Description <!-- Describe your changes. --> Add OrtEpFactory::GetVersion and store EP version in EP metadata. ### 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. --> Enforce plugin EP version specification and make it accessible from EP metadata. --------- Co-authored-by: Scott McKay <Scott.McKay@microsoft.com>
1 parent dfc27cd commit 97ccf3f

File tree

16 files changed

+222
-10
lines changed

16 files changed

+222
-10
lines changed

cmake/onnxruntime.cmake

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ endif()
2222
function(get_c_cxx_api_headers HEADERS_VAR)
2323
set(_headers
2424
"${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_c_api.h"
25-
"${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_ep_c_api.h"
2625
"${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_cxx_api.h"
2726
"${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_cxx_inline.h"
27+
"${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_ep_c_api.h"
28+
"${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_ep_device_ep_metadata_keys.h"
2829
"${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_float16.h"
30+
"${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_lite_custom_op.h"
2931
"${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_run_options_config_keys.h"
3032
"${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_session_options_config_keys.h"
31-
"${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_lite_custom_op.h"
3233
)
3334

3435
if (onnxruntime_ENABLE_TRAINING_APIS)

include/onnxruntime/core/session/onnxruntime_ep_c_api.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,12 +350,12 @@ struct OrtEp {
350350
uint32_t ort_version_supported;
351351

352352
/** \brief Get the execution provider name.
353+
*
354+
* The returned string should be a null-terminated, UTF-8 encoded string. ORT will copy it.
353355
*
354356
* \param[in] this_ptr The OrtEp instance.
355357
* \return The execution provider name.
356358
*
357-
* \note Returned string is owned by ORT and valid until UnregisterExecutionProviderLibrary is called.
358-
*
359359
* \since Version 1.22.
360360
*/
361361
const char*(ORT_API_CALL* GetName)(_In_ const OrtEp* this_ptr);
@@ -578,6 +578,8 @@ struct OrtEpFactory {
578578
uint32_t ort_version_supported;
579579

580580
/** \brief Get the name of the execution provider that the factory creates.
581+
*
582+
* The returned string should be a null-terminated, UTF-8 encoded string. ORT will copy it.
581583
*
582584
* \param[in] this_ptr The OrtEpFactory instance.
583585
* \return The name of the execution provider the factory creates.
@@ -587,6 +589,8 @@ struct OrtEpFactory {
587589
const char*(ORT_API_CALL* GetName)(const OrtEpFactory* this_ptr);
588590

589591
/** \brief Get the name of vendor who owns the execution provider that the factory creates.
592+
*
593+
* The returned string should be a null-terminated, UTF-8 encoded string. ORT will copy it.
590594
*
591595
* \param[in] this_ptr The OrtEpFactory instance.
592596
* \return vendor The vendor name of the execution provider the factory creates.
@@ -659,6 +663,20 @@ struct OrtEpFactory {
659663
*/
660664
void(ORT_API_CALL* ReleaseEp)(OrtEpFactory* this_ptr, struct OrtEp* ep);
661665

666+
/** \brief Get the version of the execution provider that the factory creates.
667+
*
668+
* The version string should adhere to the Semantic Versioning 2.0 specification
669+
* (https://github.com/semver/semver/blob/v2.0.0/semver.md).
670+
*
671+
* The returned string should be a null-terminated, UTF-8 encoded string. ORT will copy it.
672+
*
673+
* \param[in] this_ptr The OrtEpFactory instance.
674+
* \return The execution provider version string.
675+
*
676+
* \since Version 1.23.
677+
*/
678+
const char*(ORT_API_CALL* GetVersion)(_In_ const OrtEpFactory* this_ptr);
679+
662680
/** \brief Create an OrtAllocator for the given OrtMemoryInfo.
663681
*
664682
* This is used to create an allocator that an execution provider requires. The factory that creates the EP is
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
#pragma once
5+
6+
// This file contains well-known keys for OrtEpDevice EP metadata entries.
7+
// It does NOT specify all available metadata keys.
8+
9+
// Key for the execution provider version string. This should be available for all plugin EPs.
10+
static const char* const kOrtEpDevice_EpMetadataKey_Version = "version";

onnxruntime/core/common/semver.cc

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
#include "core/common/semver.h"
5+
6+
#include <regex>
7+
8+
#include "core/common/common.h"
9+
#include "core/common/narrow.h"
10+
#include "core/common/parse_string.h"
11+
12+
namespace onnxruntime {
13+
14+
Status ParseSemVerVersion(std::string_view version_string, SemVerVersion* semver_version_out) {
15+
// Semantic Versioning version regex was copied from here:
16+
// https://github.com/semver/semver/blob/d58db1686379c8c6d52e32d42d3a530a964264e5/semver.md?plain=1#L357
17+
static const std::regex semver_pattern{
18+
R"(^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$)"};
19+
20+
std::cmatch match_result{};
21+
ORT_RETURN_IF_NOT(std::regex_match(version_string.data(), version_string.data() + version_string.size(),
22+
match_result, semver_pattern),
23+
"Version string is not in semantic versioning format: '", version_string, "'");
24+
25+
auto sub_match_to_string_view = [](const std::csub_match& sub_match) -> std::optional<std::string_view> {
26+
if (!sub_match.matched) {
27+
return std::nullopt;
28+
}
29+
return std::string_view{sub_match.first, narrow<size_t>(sub_match.length())};
30+
};
31+
32+
auto parse_version_component =
33+
[&sub_match_to_string_view](const std::csub_match& sub_match, uint32_t& component) -> Status {
34+
const auto component_str = sub_match_to_string_view(sub_match);
35+
ORT_RETURN_IF_NOT(component_str.has_value(), "sub_match does not match anything.");
36+
return ParseStringWithClassicLocale(*component_str, component);
37+
};
38+
39+
SemVerVersion semver_version{};
40+
41+
ORT_RETURN_IF_ERROR(parse_version_component(match_result[1], semver_version.major));
42+
ORT_RETURN_IF_ERROR(parse_version_component(match_result[2], semver_version.minor));
43+
ORT_RETURN_IF_ERROR(parse_version_component(match_result[3], semver_version.patch));
44+
45+
semver_version.prerelease = sub_match_to_string_view(match_result[4]);
46+
semver_version.build_metadata = sub_match_to_string_view(match_result[5]);
47+
48+
if (semver_version_out) {
49+
*semver_version_out = std::move(semver_version);
50+
}
51+
return Status::OK();
52+
}
53+
54+
SemVerVersion ParseSemVerVersion(std::string_view version_string) {
55+
SemVerVersion result{};
56+
ORT_THROW_IF_ERROR(ParseSemVerVersion(version_string, &result));
57+
return result;
58+
}
59+
60+
} // namespace onnxruntime

onnxruntime/core/common/semver.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
#pragma once
5+
6+
#include <optional>
7+
#include <string_view>
8+
9+
#include "core/common/status.h"
10+
11+
namespace onnxruntime {
12+
13+
// Semantic Versioning version utilities.
14+
// See https://github.com/semver/semver/blob/v2.0.0/semver.md.
15+
16+
// Semantic Versioning version components.
17+
struct SemVerVersion {
18+
uint32_t major{};
19+
uint32_t minor{};
20+
uint32_t patch{};
21+
std::optional<std::string_view> prerelease{};
22+
std::optional<std::string_view> build_metadata{};
23+
};
24+
25+
// Parse a Semantic Versioning version from `version_string`.
26+
// If provided, the parsed version components will be written to `semver_version`.
27+
Status ParseSemVerVersion(std::string_view version_string, SemVerVersion* semver_version);
28+
29+
// Parse a Semantic Versioning version from `version_string`.
30+
SemVerVersion ParseSemVerVersion(std::string_view version_string);
31+
32+
} // namespace onnxruntime

onnxruntime/core/providers/cuda/cuda_provider_factory.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,12 +308,14 @@ CUDA_Provider* GetProvider() {
308308
} // namespace onnxruntime
309309

310310
#include "core/framework/error_code_helper.h"
311+
#include "onnxruntime_config.h" // for ORT_VERSION
311312

312313
// OrtEpApi infrastructure to be able to use the CUDA EP as an OrtEpFactory for auto EP selection.
313314
struct CudaEpFactory : OrtEpFactory {
314315
CudaEpFactory(const OrtApi& ort_api_in) : ort_api{ort_api_in} {
315316
GetName = GetNameImpl;
316317
GetVendor = GetVendorImpl;
318+
GetVersion = GetVersionImpl;
317319
GetSupportedDevices = GetSupportedDevicesImpl;
318320
CreateEp = CreateEpImpl;
319321
ReleaseEp = ReleaseEpImpl;
@@ -329,6 +331,10 @@ struct CudaEpFactory : OrtEpFactory {
329331
return factory->vendor.c_str();
330332
}
331333

334+
static const char* ORT_API_CALL GetVersionImpl(const OrtEpFactory* /*this_ptr*/) noexcept {
335+
return ORT_VERSION;
336+
}
337+
332338
static OrtStatus* GetSupportedDevicesImpl(OrtEpFactory* this_ptr,
333339
const OrtHardwareDevice* const* devices,
334340
size_t num_devices,

onnxruntime/core/providers/qnn/qnn_provider_factory.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ ORT_API(onnxruntime::Provider*, GetProvider) {
116116
}
117117

118118
#include "core/framework/error_code_helper.h"
119+
#include "onnxruntime_config.h" // for ORT_VERSION
119120

120121
// OrtEpApi infrastructure to be able to use the QNN EP as an OrtEpFactory for auto EP selection.
121122
struct QnnEpFactory : OrtEpFactory {
@@ -126,6 +127,7 @@ struct QnnEpFactory : OrtEpFactory {
126127
: ort_api{ort_api_in}, ep_name{ep_name}, ort_hw_device_type{hw_type}, qnn_backend_type{qnn_backend_type} {
127128
GetName = GetNameImpl;
128129
GetVendor = GetVendorImpl;
130+
GetVersion = GetVersionImpl;
129131
GetSupportedDevices = GetSupportedDevicesImpl;
130132
CreateEp = CreateEpImpl;
131133
ReleaseEp = ReleaseEpImpl;
@@ -143,6 +145,10 @@ struct QnnEpFactory : OrtEpFactory {
143145
return factory->vendor.c_str();
144146
}
145147

148+
static const char* ORT_API_CALL GetVersionImpl(const OrtEpFactory* /*this_ptr*/) noexcept {
149+
return ORT_VERSION;
150+
}
151+
146152
// Creates and returns OrtEpDevice instances for all OrtHardwareDevices that this factory supports.
147153
// An EP created with this factory is expected to be able to execute a model with *all* supported
148154
// hardware devices at once. A single instance of QNN EP is not currently setup to partition a model among

onnxruntime/core/session/ep_api.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
#include <algorithm>
77
#include <vector>
8+
9+
#include "core/common/semver.h"
810
#include "core/framework/error_code_helper.h"
911
#include "core/framework/func_api.h"
1012
#include "core/framework/ort_value.h"
@@ -14,6 +16,7 @@
1416
#include "core/graph/ep_api_types.h"
1517
#include "core/session/abi_devices.h"
1618
#include "core/session/abi_ep_types.h"
19+
#include "core/session/onnxruntime_ep_device_ep_metadata_keys.h"
1720
#include "core/session/ort_apis.h"
1821

1922
using namespace onnxruntime;
@@ -34,6 +37,21 @@ ORT_API_STATUS_IMPL(CreateEpDevice, _In_ OrtEpFactory* ep_factory,
3437
ep_device->ep_metadata = *ep_metadata;
3538
}
3639

40+
// Add EP version from OrtEpFactory to metadata. OrtEpFactory::GetVersion is supported since 1.23.
41+
if (ep_factory->ort_version_supported >= uint32_t{23}) {
42+
if (ep_device->ep_metadata.Entries().find(kOrtEpDevice_EpMetadataKey_Version) !=
43+
ep_device->ep_metadata.Entries().end()) {
44+
return OrtApis::CreateStatus(ORT_INVALID_ARGUMENT,
45+
"The provided EP metadata should not explicitly specify the EP version.");
46+
}
47+
48+
{
49+
std::string ep_version = ep_factory->GetVersion(ep_factory);
50+
ORT_API_RETURN_IF_STATUS_NOT_OK(ParseSemVerVersion(ep_version, nullptr));
51+
ep_device->ep_metadata.Add(kOrtEpDevice_EpMetadataKey_Version, std::move(ep_version));
52+
}
53+
}
54+
3755
if (ep_options) {
3856
ep_device->ep_options = *ep_options;
3957
}

onnxruntime/core/session/ep_api_utils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ struct ForwardToFactory {
1616
return static_cast<const TFactory*>(this_ptr)->GetVendor();
1717
}
1818

19+
static const char* ORT_API_CALL GetVersion(const OrtEpFactory* this_ptr) noexcept {
20+
return static_cast<const TFactory*>(this_ptr)->GetVersion();
21+
}
22+
1923
static OrtStatus* ORT_API_CALL GetSupportedDevices(OrtEpFactory* this_ptr,
2024
const OrtHardwareDevice* const* devices,
2125
size_t num_devices,

onnxruntime/core/session/ep_factory_internal.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "core/session/abi_session_options_impl.h"
99
#include "core/session/ep_api_utils.h"
1010
#include "core/session/ort_apis.h"
11+
#include "onnxruntime_config.h" // for ORT_VERSION
1112

1213
namespace onnxruntime {
1314

@@ -24,11 +25,16 @@ EpFactoryInternal::EpFactoryInternal(const std::string& ep_name, const std::stri
2425

2526
OrtEpFactory::GetName = Forward::GetFactoryName;
2627
OrtEpFactory::GetVendor = Forward::GetVendor;
28+
OrtEpFactory::GetVersion = Forward::GetVersion;
2729
OrtEpFactory::GetSupportedDevices = Forward::GetSupportedDevices;
2830
OrtEpFactory::CreateEp = Forward::CreateEp;
2931
OrtEpFactory::ReleaseEp = Forward::ReleaseEp;
3032
}
3133

34+
const char* EpFactoryInternal::GetVersion() const noexcept {
35+
return ORT_VERSION;
36+
}
37+
3238
OrtStatus* EpFactoryInternal::GetSupportedDevices(const OrtHardwareDevice* const* devices,
3339
size_t num_devices,
3440
OrtEpDevice** ep_devices,

0 commit comments

Comments
 (0)