Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public struct OrtCompileApi
public IntPtr ModelCompilationOptions_SetGraphOptimizationLevel;
public IntPtr ModelCompilationOptions_SetOutputModelWriteFunc;
public IntPtr ModelCompilationOptions_SetOutputModelGetInitializerLocationFunc;
public IntPtr ModelCompilationOptions_SetInputModel;
Copy link
Contributor Author

@adrastogi adrastogi Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debating whether or not we need this, ultimately... I realized that there aren't managed versions of the model editor APIs. But since I am touching the API table, it seemed mandatory(?).

}

internal class NativeMethods
Expand Down Expand Up @@ -136,6 +137,12 @@ public DOrtModelCompilationOptions_SetOutputModelWriteFunc
public DOrtModelCompilationOptions_SetOutputModelGetInitializerLocationFunc
OrtModelCompilationOptions_SetOutputModelGetInitializerLocationFunc;

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate IntPtr /* OrtStatus* */ DOrtModelCompilationOptions_SetInputModel(
IntPtr /* OrtModelCompilationOptions* */ options,
IntPtr /* const OrtModel* */ inputModel);
public DOrtModelCompilationOptions_SetInputModel OrtModelCompilationOptions_SetInputModel;

internal NativeMethods(OnnxRuntime.NativeMethods.DOrtGetCompileApi getCompileApi)
{

Expand Down Expand Up @@ -217,6 +224,11 @@ internal NativeMethods(OnnxRuntime.NativeMethods.DOrtGetCompileApi getCompileApi
_compileApi.ModelCompilationOptions_SetOutputModelGetInitializerLocationFunc,
typeof(DOrtModelCompilationOptions_SetOutputModelGetInitializerLocationFunc));

OrtModelCompilationOptions_SetInputModel =
(DOrtModelCompilationOptions_SetInputModel)Marshal.GetDelegateForFunctionPointer(
_compileApi.ModelCompilationOptions_SetInputModel,
typeof(DOrtModelCompilationOptions_SetInputModel));

}
}
}
23 changes: 23 additions & 0 deletions include/onnxruntime/core/session/onnxruntime_c_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -8037,6 +8037,29 @@ struct OrtCompileApi {
ORT_API2_STATUS(ModelCompilationOptions_SetOutputModelGetInitializerLocationFunc,
_In_ OrtModelCompilationOptions* model_compile_options,
_In_ OrtGetInitializerLocationFunc get_initializer_location_func, _In_ void* state);

/** \brief Sets the OrtModel to compile.
*
* Sets an OrtModel created via the Model Editor API as the input for compilation.
*
* The input model's source (file path, memory buffer, or OrtModel) must be set with
* one of: ModelCompilationOptions_SetInputModelPath, ModelCompilationOptions_SetInputModelFromBuffer,
* or ModelCompilationOptions_SetInputModel.
*
* The OrtModel must have a complete graph with inputs, outputs, and nodes defined.
* The caller retains ownership of the OrtModel and must not release it until after
* CompileModel returns.
*
* \param[in] model_compile_options The OrtModelCompilationOptions instance.
* \param[in] model The OrtModel to compile. The model is borrowed (not copied or owned).
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
* \since Version 1.24.
*/
ORT_API2_STATUS(ModelCompilationOptions_SetInputModel,
_In_ OrtModelCompilationOptions* model_compile_options,
_In_ const OrtModel* model);
};

/**
Expand Down
2 changes: 2 additions & 0 deletions include/onnxruntime/core/session/onnxruntime_cxx_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -1612,6 +1612,8 @@ struct ModelCompilationOptions : detail::Base<OrtModelCompilationOptions> {
ModelCompilationOptions& SetFlags(uint32_t flags); ///< Wraps OrtApi::ModelCompilationOptions_SetFlags

ModelCompilationOptions& SetGraphOptimizationLevel(GraphOptimizationLevel graph_optimization_level); ///< Wraps OrtApi::ModelCompilationOptions_SetGraphOptimizationLevel

ModelCompilationOptions& SetInputModel(const OrtModel* model); ///< Wraps OrtCompileApi::ModelCompilationOptions_SetInputModel
};

/** \brief Compiles an input model to generate a model with EPContext nodes that execute EP-specific kernels. Wraps OrtApi::CompileModels.
Expand Down
5 changes: 5 additions & 0 deletions include/onnxruntime/core/session/onnxruntime_cxx_inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,11 @@ inline ModelCompilationOptions& ModelCompilationOptions::SetGraphOptimizationLev
return *this;
}

inline ModelCompilationOptions& ModelCompilationOptions::SetInputModel(const OrtModel* model) {
Ort::ThrowOnError(GetCompileApi().ModelCompilationOptions_SetInputModel(this->p_, model));
return *this;
}

namespace detail {

template <typename T>
Expand Down
26 changes: 26 additions & 0 deletions onnxruntime/core/session/compile_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,27 @@ ORT_API_STATUS_IMPL(OrtCompileAPI::ModelCompilationOptions_SetGraphOptimizationL
API_IMPL_END
}

ORT_API_STATUS_IMPL(OrtCompileAPI::ModelCompilationOptions_SetInputModel,
_In_ OrtModelCompilationOptions* ort_model_compile_options,
_In_ const OrtModel* model) {
API_IMPL_BEGIN
#if !defined(ORT_MINIMAL_BUILD)
auto model_compile_options = reinterpret_cast<onnxruntime::ModelCompilationOptions*>(ort_model_compile_options);

if (model == nullptr) {
return OrtApis::CreateStatus(ORT_INVALID_ARGUMENT, "Invalid input model: OrtModel pointer is null");
}

model_compile_options->SetInputModel(model);
return nullptr;
#else
ORT_UNUSED_PARAMETER(ort_model_compile_options);
ORT_UNUSED_PARAMETER(model);
return OrtApis::CreateStatus(ORT_NOT_IMPLEMENTED, "Compile API is not supported in this build");
#endif // !defined(ORT_MINIMAL_BUILD)
API_IMPL_END
}

ORT_API_STATUS_IMPL(OrtCompileAPI::CompileModel, _In_ const OrtEnv* env,
_In_ const OrtModelCompilationOptions* ort_model_compile_options) {
API_IMPL_BEGIN
Expand Down Expand Up @@ -343,13 +364,18 @@ static constexpr OrtCompileApi ort_compile_api = {
&OrtCompileAPI::ModelCompilationOptions_SetOutputModelWriteFunc,
&OrtCompileAPI::ModelCompilationOptions_SetOutputModelGetInitializerLocationFunc,
// End of Version 23 - DO NOT MODIFY ABOVE

&OrtCompileAPI::ModelCompilationOptions_SetInputModel,
// End of Version 24 - DO NOT MODIFY ABOVE
};

// checks that we don't violate the rule that the functions must remain in the slots they were originally assigned
static_assert(offsetof(OrtCompileApi, CompileModel) / sizeof(void*) == 8,
"Size of version 22 Api cannot change"); // initial version in ORT 1.22
static_assert(offsetof(OrtCompileApi, ModelCompilationOptions_SetOutputModelGetInitializerLocationFunc) / sizeof(void*) == 13,
"Size of version 23 of Api cannot change");
static_assert(offsetof(OrtCompileApi, ModelCompilationOptions_SetInputModel) / sizeof(void*) == 14,
"Size of version 24 of Api cannot change");

ORT_API(const OrtCompileApi*, OrtCompileAPI::GetCompileApi) {
return &ort_compile_api;
Expand Down
3 changes: 3 additions & 0 deletions onnxruntime/core/session/compile_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,8 @@ ORT_API_STATUS_IMPL(ModelCompilationOptions_SetOutputModelWriteFunc,
ORT_API_STATUS_IMPL(ModelCompilationOptions_SetOutputModelGetInitializerLocationFunc,
_In_ OrtModelCompilationOptions* model_compile_options,
_In_ OrtGetInitializerLocationFunc get_initializer_location_func, _In_ void* state);
ORT_API_STATUS_IMPL(ModelCompilationOptions_SetInputModel,
_In_ OrtModelCompilationOptions* model_compile_options,
_In_ const OrtModel* model);

} // namespace OrtCompileAPI
106 changes: 99 additions & 7 deletions onnxruntime/core/session/model_compilation_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include "core/common/path_string.h"
#include "core/framework/allocator.h"
#include "core/framework/ep_context_options.h"
#include "core/platform/env.h"
#include "core/session/inference_session_utils.h"
#include "core/session/onnxruntime_session_options_config_keys.h"
#include "core/session/environment.h"

Expand Down Expand Up @@ -45,6 +47,11 @@ void ModelCompilationOptions::SetInputModelFromBuffer(const void* input_model_da
input_model_data_size_ = input_model_data_size;
}

void ModelCompilationOptions::SetInputModel(const OrtModel* model) {
ResetInputModelSettings();
input_model_ = model;
}

Status ModelCompilationOptions::SetOutputModelPath(const std::filesystem::path& output_model_path) {
ConfigOptions& config_options = session_options_.value.config_options;
epctx::ModelGenOptions& ep_context_gen_options = session_options_.value.ep_context_gen_options;
Expand Down Expand Up @@ -186,10 +193,19 @@ size_t ModelCompilationOptions::GetInputModelDataSize() const {
return input_model_data_size_;
}

bool ModelCompilationOptions::InputModelComesFromOrtModel() const {
return input_model_ != nullptr;
}

const OrtModel* ModelCompilationOptions::GetInputModel() const {
return input_model_;
}

void ModelCompilationOptions::ResetInputModelSettings() {
input_model_path_.clear();
input_model_data_ = nullptr;
input_model_data_size_ = 0;
input_model_ = nullptr;
}

Status ModelCompilationOptions::SetGraphOptimizationLevel(GraphOptimizationLevel graph_optimization_level) {
Expand Down Expand Up @@ -229,16 +245,21 @@ Status ModelCompilationOptions::Check() const {
// Check input model settings.
const bool input_from_file = !input_model_path_.empty();
const bool input_from_memory = input_model_data_ != nullptr;
const bool input_from_model = input_model_ != nullptr;

int input_source_count = (input_from_file ? 1 : 0) +
(input_from_memory ? 1 : 0) +
(input_from_model ? 1 : 0);

if (!input_from_file && !input_from_memory) {
if (input_source_count == 0) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Input model to compile must be loaded from either a file or a memory buffer");
"Input model to compile must be specified via file path, memory buffer, or OrtModel");
}

if (input_from_file && input_from_memory) {
if (input_source_count > 1) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Input model to compile must be loaded from either a file or a memory buffer, ",
"but not both.");
"Input model to compile must be specified via exactly one of: ",
"file path, memory buffer, or OrtModel");
}

if (input_from_file && !std::filesystem::exists(input_model_path_)) {
Expand All @@ -249,12 +270,77 @@ Status ModelCompilationOptions::Check() const {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Buffer for input model data has size 0");
}

// Validate OrtModel input
if (input_from_model) {
if (input_model_->graph == nullptr) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"OrtModel has no graph. Call AddGraphToModel before compilation.");
}

if (input_model_->graph->GetNumInputs() == 0 || input_model_->graph->GetNumOutputs() == 0) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"OrtModel graph must have at least one input and one output defined.");
}

if (input_model_->domain_to_version.empty()) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"OrtModel must specify at least one opset domain/version.");
}

// Note: Additional validation (node connections, schema) happens during
// Model::LoadFromModelEditorApiModel -> Graph::Resolve()
}

// ORT_LOAD_CONFIG_FROM_MODEL is not supported for OrtModel input.
// Check early so we fail before session construction.
if (input_from_model) {
const Env& os_env = Env::Default();
if (os_env.GetEnvironmentVar(inference_session_utils::kOrtLoadConfigFromModelEnvVar) == "1") {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"The environment variable ORT_LOAD_CONFIG_FROM_MODEL=1 is set, but loading "
"config from model is not supported for in-memory OrtModel input. "
"OrtModel is programmatically constructed and has no embedded ORT config. "
"Unset ORT_LOAD_CONFIG_FROM_MODEL or use file/buffer input instead.");
}
}
Comment on lines +294 to +305
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: not sure this is worth the extra code here given (afaik) reading the config from the model is a very niche usage.


// Check output model settings.
const epctx::ModelGenOptions& ep_context_gen_options = session_options_.value.ep_context_gen_options;
bool has_no_output_model_location = std::holds_alternative<std::monostate>(
ep_context_gen_options.output_model_location);

if (has_no_output_model_location && input_from_file) {
// Also treat an empty output file path as "no location" since it's not usable.
const auto* output_path = ep_context_gen_options.TryGetOutputModelPath();
if (!has_no_output_model_location && output_path != nullptr && output_path->empty()) {
has_no_output_model_location = true;
}
Comment on lines +313 to +316
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we instead prevent an empty path being set?


// Determine if we can derive an output path from the input
bool can_derive_output_path = input_from_file;
bool model_has_path = false;

// For OrtModel input, check if model_path is set in the graph using the virtual GetModelPath() method
// (avoids dynamic_cast which requires RTTI)
if (input_from_model && input_model_->graph) {
const ORTCHAR_T* model_path_cstr = input_model_->graph->GetModelPath();
if (model_path_cstr && model_path_cstr[0] != ORT_TSTR('\0')) {
can_derive_output_path = true;
model_has_path = true;
}
}

// Fast-fail: If OrtModel has no model_path and user hasn't specified output location or embed mode,
// EPs that need to write context binaries will fail later. Fail early with a clear error.
if (input_from_model && !model_has_path && has_no_output_model_location &&
!ep_context_gen_options.embed_ep_context_in_model) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"OrtModel has no model_path set and no output location was specified. "
"Please either: (1) set the model_path on the OrtGraph before adding to OrtModel, "
"(2) call SetOutputModelPath/SetOutputModelBuffer to specify an output location, or "
"(3) call SetEpContextEmbedMode(true) to embed EP context in the model.");
}
Comment on lines +332 to +341
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth duplicating code for this? Not sure we need a 'fast fail' path if the issue is a usage error as that should be caught and fixed during development.


if (has_no_output_model_location && can_derive_output_path) {
// User did not specify an output file, an output buffer, or an output write function. We default to generating an
// output file with a name based on the input file name, so do not return an error.
return Status::OK();
Expand Down Expand Up @@ -294,7 +380,13 @@ Status ModelCompilationOptions::Check() const {
}

std::string ModelCompilationOptions::GetInputSourceForTelemetry() const {
return InputModelComesFromFile() ? "file" : "buffer";
if (InputModelComesFromFile()) {
return "file";
}
if (InputModelComesFromOrtModel()) {
return "ort_model";
}
return "buffer";
}

std::string ModelCompilationOptions::GetOutputTargetForTelemetry() const {
Expand Down
26 changes: 24 additions & 2 deletions onnxruntime/core/session/model_compilation_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "core/common/status.h"
#include "core/common/path_string.h"
#include "core/framework/allocator.h"
#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_session_options_config_keys.h"
Expand Down Expand Up @@ -45,6 +46,14 @@ class ModelCompilationOptions {
/// <param name="input_model_data_size">The size in bytes of the input model's buffer</param>
void SetInputModelFromBuffer(const void* input_model_data, size_t input_model_data_size);

/// <summary>
/// Sets the OrtModel to compile.
/// The OrtModel is borrowed (not copied) - caller must keep it alive until CompileModel returns.
/// Overrides any previous call to SetInputModelPath(), SetInputModelFromBuffer(), or SetInputModel().
/// </summary>
/// <param name="model">The OrtModel to compile</param>
void SetInputModel(const OrtModel* model);

/// <summary>
/// Sets the file path to store the output/compiled ONNX model.
/// Overrides any previous call to SetOutputModelPath() or SetOutputModelBuffer().
Expand Down Expand Up @@ -132,6 +141,18 @@ class ModelCompilationOptions {
/// <returns>true if input model comes from a file</returns>
bool InputModelComesFromFile() const;

/// <summary>
/// Returns true if the input model comes from an OrtModel pointer.
/// </summary>
/// <returns>true if input model comes from an OrtModel</returns>
bool InputModelComesFromOrtModel() const;

/// <summary>
/// Returns the OrtModel to compile, or nullptr if not set.
/// </summary>
/// <returns>pointer to the OrtModel or nullptr</returns>
const OrtModel* GetInputModel() const;

/// <summary>
/// Returns the buffer that contains the bytes for the input ONNX model.
/// Returns nullptr if the input model is not stored in a buffer.
Expand Down Expand Up @@ -162,9 +183,9 @@ class ModelCompilationOptions {
// Telemetry helper methods

/// <summary>
/// Returns a string describing the input source type: "file" or "buffer".
/// Returns a string describing the input source type: "file", "buffer", or "ort_model".
/// </summary>
/// <returns>"file" or "buffer"</returns>
/// <returns>"file", "buffer", or "ort_model"</returns>
std::string GetInputSourceForTelemetry() const;

/// <summary>
Expand Down Expand Up @@ -205,6 +226,7 @@ class ModelCompilationOptions {
std::filesystem::path input_model_path_;
const void* input_model_data_ = nullptr;
size_t input_model_data_size_ = 0;
const OrtModel* input_model_ = nullptr; // Borrowed pointer
};
} // namespace onnxruntime
#endif // !defined(ORT_MINIMAL_BUILD)
Loading
Loading