Skip to content

Commit 686e05e

Browse files
[EP ABI] Add API RegisterExecutionProviderLibraryWithOptions
1 parent d1857d1 commit 686e05e

File tree

18 files changed

+341
-30
lines changed

18 files changed

+341
-30
lines changed

csharp/src/Microsoft.ML.OnnxRuntime/NativeMethods.shared.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,10 @@ public struct OrtApi
451451
public IntPtr Graph_GetModelMetadata;
452452
public IntPtr GetModelCompatibilityForEpDevices;
453453
public IntPtr CreateExternalInitializerInfo;
454+
455+
public IntPtr TensorTypeAndShape_HasShape;
456+
public IntPtr KernelInfo_GetConfigEntries;
457+
public IntPtr RegisterExecutionProviderLibraryWithOptions;
454458
}
455459

456460
internal static class NativeMethods
@@ -847,7 +851,7 @@ static NativeMethods()
847851
api_.CreateSyncStreamForEpDevice,
848852
typeof(DOrtCreateSyncStreamForEpDevice));
849853

850-
OrtSyncStream_GetHandle =
854+
OrtSyncStream_GetHandle =
851855
(DOrtSyncStream_GetHandle)Marshal.GetDelegateForFunctionPointer(
852856
api_.SyncStream_GetHandle,
853857
typeof(DOrtSyncStream_GetHandle));
@@ -861,6 +865,11 @@ static NativeMethods()
861865
(DOrtCopyTensors)Marshal.GetDelegateForFunctionPointer(
862866
api_.CopyTensors,
863867
typeof(DOrtCopyTensors));
868+
869+
OrtRegisterExecutionProviderLibraryWithOptions =
870+
(DOrtRegisterExecutionProviderLibraryWithOptions)Marshal.GetDelegateForFunctionPointer(
871+
api_.RegisterExecutionProviderLibraryWithOptions,
872+
typeof(DOrtRegisterExecutionProviderLibraryWithOptions));
864873
}
865874

866875
internal class NativeLib
@@ -2780,6 +2789,22 @@ out IntPtr /* OrtSyncStream** */ stream
27802789
byte[] /* const char* */ registration_name,
27812790
byte[] /* const ORTCHAR_T* */ path);
27822791

2792+
/// <summary>
2793+
/// Register an execution provider library. The provided options are passed to EP factories after creation.
2794+
/// The library must implement CreateEpFactories and ReleaseEpFactory.
2795+
/// </summary>
2796+
/// <param name="env">Environment to add the EP library to.</param>
2797+
/// <param name="registration_name">Name to register the library under.</param>
2798+
/// <param name="path">Absolute path to the library.</param>
2799+
/// <param name="options">Options passed to OrtEpFactory::SetEnvironmentOptions after creation.</param>
2800+
/// <returns>OrtStatus*</returns>
2801+
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
2802+
public delegate IntPtr /* OrtStatus* */ DOrtRegisterExecutionProviderLibraryWithOptions(
2803+
IntPtr /* OrtEnv* */ env,
2804+
byte[] /* const char* */ registration_name,
2805+
byte[] /* const ORTCHAR_T* */ path,
2806+
IntPtr /* const OrtKeyValuePairs* */ options);
2807+
27832808
/// <summary>
27842809
/// Unregister an execution provider library.
27852810
/// </summary>
@@ -2792,6 +2817,7 @@ out IntPtr /* OrtSyncStream** */ stream
27922817
byte[] /* const char* */ registration_name);
27932818

27942819
public static DOrtRegisterExecutionProviderLibrary OrtRegisterExecutionProviderLibrary;
2820+
public static DOrtRegisterExecutionProviderLibraryWithOptions OrtRegisterExecutionProviderLibraryWithOptions;
27952821
public static DOrtUnregisterExecutionProviderLibrary OrtUnregisterExecutionProviderLibrary;
27962822

27972823
/// <summary>

csharp/src/Microsoft.ML.OnnxRuntime/OrtEnv.shared.cs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -523,9 +523,6 @@ public OrtLoggingLevel EnvLogLevel
523523
/// A registered execution provider library can be used by all sessions created with the OrtEnv instance.
524524
/// Devices the execution provider can utilize are added to the values returned by GetEpDevices() and can
525525
/// be used in SessionOptions.AppendExecutionProvider to select an execution provider for a device.
526-
///
527-
/// Coming: A selection policy can be specified and ORT will automatically select the best execution providers
528-
/// and devices for the model.
529526
/// </summary>
530527
/// <param name="registrationName">The name to register the library under.</param>
531528
/// <param name="libraryPath">The path to the library to register.</param>
@@ -540,6 +537,48 @@ public void RegisterExecutionProviderLibrary(string registrationName, string lib
540537
NativeMethods.OrtRegisterExecutionProviderLibrary(handle, registrationNameUtf8, pathUtf8));
541538
}
542539

540+
/// <summary>
541+
/// Register an execution provider library with the OrtEnv instance. The provided options are passed to
542+
/// EP factory instances after creation.
543+
///
544+
/// A registered execution provider library can be used by all sessions created with the OrtEnv instance.
545+
/// Devices the execution provider can utilize are added to the values returned by GetEpDevices() and can
546+
/// be used in SessionOptions.AppendExecutionProvider to select an execution provider for a device.
547+
/// </summary>
548+
/// <param name="registrationName">The name to register the library under.</param>
549+
/// <param name="libraryPath">The path to the library to register.</param>
550+
/// <param name="options">Optional options to pass to each EP factory after creation. May be null.</param>
551+
/// <see cref="GetEpDevices"/>
552+
/// <see cref="SessionOptions.AppendExecutionProvider(OrtEnv, IReadOnlyList{OrtEpDevice}, IReadOnlyDictionary{string, string})"/>
553+
public void RegisterExecutionProviderLibrary(string registrationName, string libraryPath,
554+
IReadOnlyDictionary<string, string> options)
555+
{
556+
var registrationNameUtf8 = NativeOnnxValueHelper.StringToZeroTerminatedUtf8(registrationName);
557+
var pathUtf8 = NativeOnnxValueHelper.GetPlatformSerializedString(libraryPath);
558+
559+
if (options != null && options.Count > 0)
560+
{
561+
// this creates an OrtKeyValuePairs instance with a backing native instance
562+
using var optionsKvps = new OrtKeyValuePairs(options);
563+
564+
NativeApiStatus.VerifySuccess(
565+
NativeMethods.OrtRegisterExecutionProviderLibraryWithOptions(
566+
handle,
567+
registrationNameUtf8,
568+
pathUtf8,
569+
optionsKvps.Handle));
570+
}
571+
else
572+
{
573+
NativeApiStatus.VerifySuccess(
574+
NativeMethods.OrtRegisterExecutionProviderLibraryWithOptions(
575+
handle,
576+
registrationNameUtf8,
577+
pathUtf8,
578+
IntPtr.Zero)); // Options OrtKeyValuePairs
579+
}
580+
}
581+
543582
/// <summary>
544583
/// Unregister an execution provider library from the OrtEnv instance.
545584
/// </summary>

csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/OrtAutoEpTests.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,37 @@ public void RegisterUnregisterLibrary()
9393
}
9494
}
9595

96+
[Fact]
97+
public void RegisterLibraryWithOptions()
98+
{
99+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
100+
{
101+
const string registrationName = "example_plugin_ep_kernel_registry";
102+
const string epName = "ExampleKernelEp";
103+
104+
string libFullPath = Path.Combine(Directory.GetCurrentDirectory(), "example_plugin_ep_kernel_registry.dll");
105+
Assert.True(File.Exists(libFullPath), $"Expected lib {libFullPath} does not exist.");
106+
107+
Dictionary<string, string> options = new Dictionary<string, string> { { "some_env_config", "2" } };
108+
ortEnvInstance.RegisterExecutionProviderLibrary(registrationName, libFullPath, options);
109+
try
110+
{
111+
// check OrtEpDevice was found
112+
var epDevices = ortEnvInstance.GetEpDevices();
113+
var epDevice = epDevices.FirstOrDefault(d => string.Equals(epName, d.EpName, StringComparison.OrdinalIgnoreCase));
114+
Assert.NotNull(epDevice);
115+
116+
// The example EP stores the env config in the OrtEpDevice metadata for testing convenience.
117+
var epMetadata = epDevice.EpMetadata.Entries;
118+
Assert.Equal("2", epMetadata["some_env_config"]);
119+
}
120+
finally
121+
{ // unregister
122+
ortEnvInstance.UnregisterExecutionProviderLibrary(registrationName);
123+
}
124+
}
125+
}
126+
96127
[Fact]
97128
public void AppendToSessionOptionsV2()
98129
{
@@ -194,7 +225,7 @@ public void SetEpSelectionPolicyDelegate()
194225

195226
// doesn't matter what the value is. should fallback to ORT CPU EP
196227
sessionOptions.SetEpSelectionPolicyDelegate(SelectionPolicyDelegate);
197-
228+
198229
var model = TestDataLoader.LoadModelFromEmbeddedResource("squeezenet.onnx");
199230

200231
// session should load successfully

include/onnxruntime/core/session/environment.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ class Environment {
132132
const OrtArenaCfg* arena_cfg = nullptr);
133133

134134
#if !defined(ORT_MINIMAL_BUILD)
135-
Status RegisterExecutionProviderLibrary(const std::string& registration_name, const ORTCHAR_T* lib_path);
135+
Status RegisterExecutionProviderLibrary(const std::string& registration_name, const ORTCHAR_T* lib_path,
136+
const OrtKeyValuePairs* options);
136137
Status UnregisterExecutionProviderLibrary(const std::string& registration_name);
137138

138139
// convert an OrtEpFactory* to EpFactoryInternal* if possible.
@@ -206,14 +207,16 @@ class Environment {
206207

207208
Status RegisterExecutionProviderLibrary(const std::string& registration_name,
208209
std::unique_ptr<EpLibrary> ep_library,
209-
const std::vector<EpFactoryInternal*>& internal_factories = {});
210+
const std::vector<EpFactoryInternal*>& internal_factories = {},
211+
const OrtKeyValuePairs* options = nullptr);
210212

211213
struct EpInfo {
212214
// calls EpLibrary::Load
213215
// for each factory gets the OrtEpDevice instances and adds to execution_devices
214216
// internal_factory is set if this is an internal EP
215217
static Status Create(std::unique_ptr<EpLibrary> library_in, std::unique_ptr<EpInfo>& out,
216-
const std::vector<EpFactoryInternal*>& internal_factories = {});
218+
const std::vector<EpFactoryInternal*>& internal_factories = {},
219+
const OrtKeyValuePairs* options = nullptr);
217220

218221
// removes entries for this library from execution_devices
219222
// calls EpLibrary::Unload

include/onnxruntime/core/session/onnxruntime_c_api.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6608,6 +6608,24 @@ struct OrtApi {
66086608
* \since Version 1.24
66096609
*/
66106610
ORT_API2_STATUS(KernelInfo_GetConfigEntries, _In_ const OrtKernelInfo* info, _Outptr_ OrtKeyValuePairs** out);
6611+
6612+
/** \brief Register an execution provider library with ORT. The provided options are passed to
6613+
* OrtEpFactory::SetEnvironmentOptions after factory creation.
6614+
*
6615+
* The library must export 'CreateEpFactories' and 'ReleaseEpFactory' functions.
6616+
* See OrtEpApi for more details.
6617+
*
6618+
* \param[in] env The OrtEnv instance to register the library in.
6619+
* \param[in] registration_name The name to register the execution provider library under.
6620+
* \param[in] path The path to the execution provider library.
6621+
* \param[in] options Map of options to pass to each OrtEpFactory via OrtEpFactory::SetEnvironmentOptions.
6622+
*
6623+
* \snippet{doc} snippets.dox OrtStatus Return Value
6624+
*
6625+
* \since Version 1.24.
6626+
*/
6627+
ORT_API2_STATUS(RegisterExecutionProviderLibraryWithOptions, _In_ OrtEnv* env, _In_ const char* registration_name,
6628+
_In_ const ORTCHAR_T* path, _In_opt_ const OrtKeyValuePairs* options);
66116629
};
66126630

66136631
/*

include/onnxruntime/core/session/onnxruntime_cxx_api.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,8 +1199,14 @@ struct Env : detail::Base<OrtEnv> {
11991199
void ReleaseSharedAllocator(const OrtEpDevice* ep_device,
12001200
OrtDeviceMemoryType mem_type); ///< Wraps OrtApi::ReleaseSharedAllocator
12011201

1202-
Env& RegisterExecutionProviderLibrary(const char* registration_name, const std::basic_string<ORTCHAR_T>& path); ///< Wraps OrtApi::RegisterExecutionProviderLibrary
1203-
Env& UnregisterExecutionProviderLibrary(const char* registration_name); ///< Wraps OrtApi::UnregisterExecutionProviderLibrary
1202+
///< Wraps OrtApi::RegisterExecutionProviderLibrary
1203+
Env& RegisterExecutionProviderLibrary(const char* registration_name, const std::basic_string<ORTCHAR_T>& path);
1204+
1205+
///< Wraps OrtApi::RegisterExecutionProviderLibraryWithOptions
1206+
Env& RegisterExecutionProviderLibraryWithOptions(const char* registration_name, const std::basic_string<ORTCHAR_T>& path,
1207+
const OrtKeyValuePairs* options);
1208+
1209+
Env& UnregisterExecutionProviderLibrary(const char* registration_name); ///< Wraps OrtApi::UnregisterExecutionProviderLibrary
12041210

12051211
std::vector<ConstEpDevice> GetEpDevices() const;
12061212

include/onnxruntime/core/session/onnxruntime_cxx_inline.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,13 @@ inline Env& Env::RegisterExecutionProviderLibrary(const char* registration_name,
835835
return *this;
836836
}
837837

838+
inline Env& Env::RegisterExecutionProviderLibraryWithOptions(const char* registration_name,
839+
const std::basic_string<ORTCHAR_T>& path,
840+
const OrtKeyValuePairs* options) {
841+
ThrowOnError(GetApi().RegisterExecutionProviderLibraryWithOptions(p_, registration_name, path.c_str(), options));
842+
return *this;
843+
}
844+
838845
inline Env& Env::UnregisterExecutionProviderLibrary(const char* registration_name) {
839846
ThrowOnError(GetApi().UnregisterExecutionProviderLibrary(p_, registration_name));
840847
return *this;

include/onnxruntime/core/session/onnxruntime_ep_c_api.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,12 +1510,13 @@ struct OrtEpFactory {
15101510
_In_opt_ const OrtKeyValuePairs* stream_options,
15111511
_Outptr_ OrtSyncStreamImpl** stream);
15121512

1513-
/** \brief Set environment options on this EP factory.
1513+
/** \brief Sets environment options that are provided by the application during EP library registration.
15141514
*
1515-
* Environment options can be set by ORT after calling the library's 'CreateEpFactories' function to
1516-
* create EP factories.
1515+
* If defined, ORT calls this function during EP library registration directly after creating the factory instance.
1516+
* Valid option keys and values are defined by the EP library. However, some common EP-agnostic options are listed
1517+
* below.
15171518
*
1518-
* Supported options:
1519+
* Common EP options:
15191520
* "allow_virtual_devices": Allows EP factory to specify OrtEpDevice instances that use custom
15201521
* virtual OrtHardwareDevices, which can be created via OrtEpApi::CreateHardwareDevice().
15211522
*
@@ -1528,10 +1529,11 @@ struct OrtEpFactory {
15281529
* -# "1": Creation of virtual devices is allowed.
15291530
*
15301531
* \param[in] this_ptr The OrtEpFactory instance.
1531-
* \param[in] options The configuration options.
1532+
* \param[in] options The configuration options. Do not cache pointers to the OrtKeyValuePairs instance or its
1533+
* keys and values. Key and value strings should be copied if necessary.
15321534
*
15331535
* \note Implementation of this function is optional.
1534-
* An EP factory should only implement this if it needs to handle any environment options.
1536+
* An EP factory should implement this if it needs to handle any environment options.
15351537
*
15361538
* \snippet{doc} snippets.dox OrtStatus Return Value
15371539
*

onnxruntime/core/session/abi_key_value_pairs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ struct OrtKeyValuePairs {
3939
Sync();
4040
}
4141

42+
bool HasKey(const std::string& key) const {
43+
return entries_.find(key) != entries_.end();
44+
}
45+
4246
void Add(const char* key, const char* value) {
4347
// ignore if either are nullptr.
4448
if (key && value) {

0 commit comments

Comments
 (0)