// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Modifications Copyright(C) 2024-2025 Advanced Micro Devices, Inc. All rights reserved.

// Do not include this file directly. Please include "onnxruntime_cxx_api.h" instead.
// If interested in trying out features of the new experimental C++ API, include "experimental_onnxruntime_cxx_api.h" instead.
//
// These are the inline implementations of the C++ header APIs. They're in this separate file as to not clutter
// the main C++ file with implementation details.

namespace Ort {

#ifndef ORT_CXX_API_THROW
inline void ThrowOnError(OrtStatus* ort_status) {
  if (ort_status) {
    throw Exception(std::unique_ptr<OrtStatus>{ort_status});
  }
}
#else
ORT_CXX_API_THROW
#endif

inline Exception::Exception(const Exception& e) : ort_status_{api->CreateStatus(e.GetOrtErrorCode(), e.what())} {}
inline OrtErrorCode Exception::GetOrtErrorCode() const { return ort_status_->GetErrorCode(); }
inline const char* Exception::what() const noexcept { return api->GetErrorMessage(ort_status_.get()); }

struct StandardAllocator : OrtAllocator {
  StandardAllocator() : OrtAllocator{} {
    version = ORT_API_VERSION;
    OrtAllocator::Alloc = [](OrtAllocator* /*this_*/, size_t size) -> void* { return new std::byte[size]; };
    OrtAllocator::Free = [](OrtAllocator* /*this_*/, void* p) { delete reinterpret_cast<std::byte*>(p); };
  }
};

inline StandardAllocator standard_allocator;

// Used on methods that return a std::string. This implements an OrtAllocator that Allocates memory directly
// from a std::string.
struct StringAllocator : OrtAllocator {
  StringAllocator() : OrtAllocator{} {
    version = ORT_API_VERSION;
    OrtAllocator::Alloc = [](OrtAllocator* this_, size_t size) { return static_cast<StringAllocator*>(this_)->Alloc(size); };
  }

  void* Alloc(size_t size) {
    string_.resize(size);
    return string_.data();
  }

  operator std::string&&() {
    string_.resize(string_.size() - 1);  // Remove the trailing null
    return std::move(string_);
  }

  char* out;

 private:
  std::string string_;
};

template <typename... Types>
struct TypeList {};

using TensorTypes = TypeList<bool, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, float, double, Float16_t, BFloat16_t>;

// Variable templates to convert a C++ type into it's ONNXTensorElementDataType
template <typename T>
inline constexpr ONNXTensorElementDataType TypeToTensorType = T::Unsupported_Type;  // Force a compile error if hit, please add specialized version if type is valid
template <>
inline constexpr ONNXTensorElementDataType TypeToTensorType<bool> = ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL;
template <>
inline constexpr ONNXTensorElementDataType TypeToTensorType<int8_t> = ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8;
template <>
inline constexpr ONNXTensorElementDataType TypeToTensorType<uint8_t> = ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8;
template <>
inline constexpr ONNXTensorElementDataType TypeToTensorType<int16_t> = ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16;
template <>
inline constexpr ONNXTensorElementDataType TypeToTensorType<uint16_t> = ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16;
template <>
inline constexpr ONNXTensorElementDataType TypeToTensorType<int32_t> = ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32;
template <>
inline constexpr ONNXTensorElementDataType TypeToTensorType<uint32_t> = ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32;
template <>
inline constexpr ONNXTensorElementDataType TypeToTensorType<int64_t> = ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64;
template <>
inline constexpr ONNXTensorElementDataType TypeToTensorType<uint64_t> = ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64;
template <>
inline constexpr ONNXTensorElementDataType TypeToTensorType<float> = ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT;
template <>
inline constexpr ONNXTensorElementDataType TypeToTensorType<double> = ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE;
template <>
inline constexpr ONNXTensorElementDataType TypeToTensorType<Float16_t> = ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16;
template <>
inline constexpr ONNXTensorElementDataType TypeToTensorType<BFloat16_t> = ONNX_TENSOR_ELEMENT_DATA_TYPE_BFLOAT16;

inline size_t SizeOf(ONNXTensorElementDataType type) {
  switch (type) {
    case Ort::TypeToTensorType<uint8_t>:
      return sizeof(uint8_t);
    case Ort::TypeToTensorType<int8_t>:
      return sizeof(int8_t);
    case Ort::TypeToTensorType<uint16_t>:
      return sizeof(uint16_t);
    case Ort::TypeToTensorType<int16_t>:
      return sizeof(int16_t);
    case Ort::TypeToTensorType<uint32_t>:
      return sizeof(uint32_t);
    case Ort::TypeToTensorType<int32_t>:
      return sizeof(int32_t);
    case Ort::TypeToTensorType<uint64_t>:
      return sizeof(int64_t);
    case Ort::TypeToTensorType<int64_t>:
      return sizeof(int64_t);
    case Ort::TypeToTensorType<bool>:
      return sizeof(bool);
    case Ort::TypeToTensorType<float>:
      return sizeof(float);
    case Ort::TypeToTensorType<double>:
      return sizeof(double);
    case Ort::TypeToTensorType<Ort::Float16_t>:
      return sizeof(Ort::Float16_t);
    case Ort::TypeToTensorType<Ort::BFloat16_t>:
      return sizeof(Ort::BFloat16_t);
    // FP8 KV caches are stored as single-byte elements. There is no Ort wrapper type for them,
    // so match the ONNX element type directly.
    case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT8E4M3FN:
      return sizeof(uint8_t);
    default:
      throw std::runtime_error("Unsupported ONNXTensorElementDataType in GetTypeSize");
  }
}

inline std::vector<std::string> GetAvailableProviders() {
  int len;
  char** providers;
  ThrowOnError(api->GetAvailableProviders(&providers, &len));
  std::vector<std::string> available_providers(providers, providers + len);
  ThrowOnError(api->ReleaseAvailableProviders(providers, len));
  return available_providers;
}

inline void GetEpDevices(OrtEnv* env, const OrtEpDevice* const** device_ptrs, size_t* num_devices) {
  ThrowOnError(api->GetEpDevices(env, device_ptrs, num_devices));
}

inline const OrtKeyValuePairs* GetEpDeviceMetadata(const OrtEpDevice* device) {
  return api->EpDevice_EpMetadata(device);
}

inline void GetKeyValuePairs(const OrtKeyValuePairs* keyvals, const char* const** keys, const char* const** values, size_t* num_entries) {
  api->GetKeyValuePairs(keyvals, keys, values, num_entries);
}

inline void* Allocator::Alloc(size_t size) {
  void* out;
  ThrowOnError(api->AllocatorAlloc(this, size, &out));
  return out;
}

inline void Allocator::Free(void* p) {
  ThrowOnError(api->AllocatorFree(this, p));
}

inline const OrtMemoryInfo& Allocator::GetInfo() const {
  const OrtMemoryInfo* out;
  ThrowOnError(api->AllocatorGetInfo(this, &out));
  return *out;
}

inline Allocator& Allocator::GetWithDefaultOptions() {
  OrtAllocator* p;
  ThrowOnError(api->GetAllocatorWithDefaultOptions(&p));
  return *static_cast<Allocator*>(p);
}

inline std::unique_ptr<Allocator> Allocator::Create(const OrtSession& sess, const OrtMemoryInfo& mem_info) {
  OrtAllocator* p;
  ThrowOnError(api->CreateAllocator(&sess, &mem_info, &p));
  return std::unique_ptr<Allocator>{static_cast<Ort::Allocator*>(p)};
}

inline void SetCurrentGpuDeviceId(int device_id) {
  ThrowOnError(api->SetCurrentGpuDeviceId(device_id));
}

inline int GetCurrentGpuDeviceId() {
  int id;
  ThrowOnError(api->GetCurrentGpuDeviceId(&id));
  return id;
}

inline void RegisterExecutionProviderLibrary(OrtEnv* env, const char* registration_name, const ORTCHAR_T* path) {
  ThrowOnError(Ort::api->RegisterExecutionProviderLibrary(env, registration_name, path));
}

inline void UnregisterExecutionProviderLibrary(OrtEnv* env, const char* registration_name) {
  ThrowOnError(Ort::api->UnregisterExecutionProviderLibrary(env, registration_name));
}

}  // namespace Ort

inline std::unique_ptr<OrtStatus> OrtStatus::Create(OrtErrorCode code, const std::string& what) {
  return std::unique_ptr<OrtStatus>{Ort::api->CreateStatus(code, what.c_str())};
}

inline std::string OrtStatus::GetErrorMessage() const {
  std::string message(Ort::api->GetErrorMessage(this));
  return message;
}

inline OrtErrorCode OrtStatus::GetErrorCode() const {
  return Ort::api->GetErrorCode(this);
}

inline std::string OrtMemoryInfo::GetAllocatorName() const {
  const char* name = nullptr;
  Ort::ThrowOnError(Ort::api->MemoryInfoGetName(this, &name));
  return std::string(name);
}

inline OrtAllocatorType OrtMemoryInfo::GetAllocatorType() const {
  OrtAllocatorType type;
  Ort::ThrowOnError(Ort::api->MemoryInfoGetType(this, &type));
  return type;
}

inline int OrtMemoryInfo::GetDeviceId() const {
  int id = 0;
  Ort::ThrowOnError(Ort::api->MemoryInfoGetId(this, &id));
  return id;
}

inline OrtMemoryInfoDeviceType OrtMemoryInfo::GetDeviceType() const {
  OrtMemoryInfoDeviceType type;
  Ort::api->MemoryInfoGetDeviceType(this, &type);
  return type;
}

inline OrtMemType OrtMemoryInfo::GetMemoryType() const {
  OrtMemType type;
  Ort::ThrowOnError(Ort::api->MemoryInfoGetMemType(this, &type));
  return type;
}

inline bool OrtMemoryInfo::operator==(const OrtMemoryInfo& o) const {
  int comp_result = 0;
  Ort::ThrowOnError(Ort::api->CompareMemoryInfo(this, &o, &comp_result));
  return comp_result == 0;
}

inline std::unique_ptr<OrtMemoryInfo> OrtMemoryInfo::CreateCpu(OrtAllocatorType type, OrtMemType mem_type) {
  OrtMemoryInfo* p;
  Ort::ThrowOnError(Ort::api->CreateCpuMemoryInfo(type, mem_type, &p));
  return std::unique_ptr<OrtMemoryInfo>{p};
}

inline std::unique_ptr<OrtMemoryInfo> OrtMemoryInfo::Create(const char* name, OrtAllocatorType type, int id, OrtMemType mem_type) {
  OrtMemoryInfo* p;
  Ort::ThrowOnError(Ort::api->CreateMemoryInfo(name, type, id, mem_type, &p));
  return std::unique_ptr<OrtMemoryInfo>{p};
}

inline std::unique_ptr<OrtSyncStream> OrtSyncStream::Create(const OrtEpDevice* ep_device, const OrtKeyValuePairs* stream_options) {
  OrtSyncStream* p_stream = nullptr;
  Ort::ThrowOnError(Ort::api->CreateSyncStreamForEpDevice(ep_device, stream_options, &p_stream));
  return std::unique_ptr<OrtSyncStream>(p_stream);
}

inline void* OrtSyncStream::GetHandle() const {
  return Ort::api->SyncStream_GetHandle(const_cast<OrtSyncStream*>(this));
}

inline std::unique_ptr<OrtIoBinding> OrtIoBinding::Create(OrtSession& session) {
  OrtIoBinding* p;
  Ort::ThrowOnError(Ort::api->CreateIoBinding(&session, &p));
  return std::unique_ptr<OrtIoBinding>{p};
}

inline std::vector<std::string> OrtIoBinding::GetOutputNames() const {
  char* buffer{};
  size_t* lengths{};
  size_t count{};
  Ort::ThrowOnError(Ort::api->GetBoundOutputNames(this, &Ort::standard_allocator, &buffer, &lengths, &count));

  std::unique_ptr<size_t> lengths_owned{lengths};
  std::unique_ptr<char> buffer_owned{buffer};

  std::vector<std::string> result;
  for (size_t i = 0; i < count; ++i) {
    auto sz = *lengths;
    result.emplace_back(buffer, sz);
    buffer += sz;
    ++lengths;
  }
  return result;
}

inline std::vector<std::unique_ptr<OrtValue>> OrtIoBinding::GetOutputValues() const {
  size_t owned = 0;
  size_t output_count = 0;
  OrtValue** output_buffer{};
  Ort::ThrowOnError(Ort::api->GetBoundOutputValues(this, &Ort::standard_allocator, &output_buffer, &output_count));
  std::unique_ptr<OrtValue*> owned_output_buffer{output_buffer};

  try {
    std::vector<std::unique_ptr<OrtValue>> result;
    for (size_t i = 0; i < output_count; ++i) {
      result.emplace_back(output_buffer[i]);
      ++owned;
    }
    return result;
  } catch (...) {  // delete any untransferred OrtValues
    while (owned < output_count)
      delete output_buffer[owned++];
    throw;
  }
}

inline void OrtIoBinding::BindInput(const char* name, const OrtValue& value) {
  Ort::ThrowOnError(Ort::api->BindInput(this, name, &value));
}

inline void OrtIoBinding::BindOutput(const char* name, const OrtValue& value) {
  Ort::ThrowOnError(Ort::api->BindOutput(this, name, &value));
}

inline void OrtIoBinding::BindOutput(const char* name, const OrtMemoryInfo& mem_info) {
  Ort::ThrowOnError(Ort::api->BindOutputToDevice(this, name, &mem_info));
}

inline void OrtIoBinding::ClearBoundInputs() {
  Ort::api->ClearBoundInputs(this);
}

inline void OrtIoBinding::ClearBoundOutputs() {
  Ort::api->ClearBoundOutputs(this);
}

inline void OrtIoBinding::SynchronizeInputs() {
  Ort::ThrowOnError(Ort::api->SynchronizeBoundInputs(this));
}

inline void OrtIoBinding::SynchronizeOutputs() {
  Ort::ThrowOnError(Ort::api->SynchronizeBoundOutputs(this));
}

inline std::unique_ptr<OrtArenaCfg> OrtArenaCfg::Create(const char* const* keys, const size_t* values, size_t count) {
  OrtArenaCfg* p;
  Ort::ThrowOnError(Ort::api->CreateArenaCfgV2(keys, values, count, &p));
  return std::unique_ptr<OrtArenaCfg>{p};
}

inline std::string OrtEpDevice::Name() const {
  const char* name = Ort::api->EpDevice_EpName(this);
  return std::string(name);
}

inline std::string OrtEpDevice::Vendor() const {
  const char* vendor = Ort::api->EpDevice_EpVendor(this);
  return std::string(vendor);
}

inline const OrtHardwareDevice* OrtEpDevice::Device() const {
  return Ort::api->EpDevice_Device(this);
}

inline OrtHardwareDeviceType OrtHardwareDevice::Type() const {
  return Ort::api->HardwareDevice_Type(this);
}

inline uint32_t OrtHardwareDevice::VendorId() const {
  return Ort::api->HardwareDevice_VendorId(this);
}

inline uint32_t OrtHardwareDevice::DeviceId() const {
  return Ort::api->HardwareDevice_DeviceId(this);
}

inline std::string OrtHardwareDevice::Vendor() const {
  const char* vendor = Ort::api->HardwareDevice_Vendor(this);
  return std::string(vendor);
}

inline const OrtKeyValuePairs* OrtHardwareDevice::Metadata() const {
  return Ort::api->HardwareDevice_Metadata(this);
}

inline void OrtCommonEnvInit(OrtEnv& v, _In_ const char* logid) {
  if (strcmp(logid, "onnxruntime-node") == 0) {
    Ort::ThrowOnError(Ort::api->SetLanguageProjection(&v, OrtLanguageProjection::ORT_PROJECTION_NODEJS));
  } else {
    Ort::ThrowOnError(Ort::api->SetLanguageProjection(&v, OrtLanguageProjection::ORT_PROJECTION_CPLUSPLUS));
  }
}

inline std::unique_ptr<OrtEnv> OrtEnv::Create(OrtLoggingLevel logging_level, _In_ const char* logid) {
  OrtEnv* p;
  Ort::ThrowOnError(Ort::api->CreateEnv(logging_level, logid, &p));
  OrtCommonEnvInit(*p, logid);
  return std::unique_ptr<OrtEnv>(p);
}

inline std::unique_ptr<OrtEnv> OrtEnv::Create(OrtLoggingLevel logging_level, const char* logid, OrtLoggingFunction logging_function, void* logger_param) {
  OrtEnv* p;
  Ort::ThrowOnError(Ort::api->CreateEnvWithCustomLogger(logging_function, logger_param, logging_level, logid, &p));
  OrtCommonEnvInit(*p, logid);
  return std::unique_ptr<OrtEnv>(p);
}

inline std::unique_ptr<OrtEnv> OrtEnv::Create(const OrtThreadingOptions* tp_options, OrtLoggingLevel logging_level, _In_ const char* logid) {
  OrtEnv* p;
  Ort::ThrowOnError(Ort::api->CreateEnvWithGlobalThreadPools(logging_level, logid, tp_options, &p));
  OrtCommonEnvInit(*p, logid);
  return std::unique_ptr<OrtEnv>(p);
}

inline std::unique_ptr<OrtEnv> OrtEnv::Create(const OrtThreadingOptions* tp_options, OrtLoggingFunction logging_function, void* logger_param,
                                              OrtLoggingLevel logging_level, _In_ const char* logid) {
  OrtEnv* p;
  Ort::ThrowOnError(Ort::api->CreateEnvWithCustomLoggerAndGlobalThreadPools(logging_function, logger_param, logging_level, logid, tp_options, &p));
  OrtCommonEnvInit(*p, logid);
  return std::unique_ptr<OrtEnv>(p);
}

inline OrtEnv& OrtEnv::EnableTelemetryEvents() {
  Ort::ThrowOnError(Ort::api->EnableTelemetryEvents(this));
  return *this;
}

inline OrtEnv& OrtEnv::DisableTelemetryEvents() {
  Ort::ThrowOnError(Ort::api->DisableTelemetryEvents(this));
  return *this;
}

inline OrtEnv& OrtEnv::CreateAndRegisterAllocator(const OrtMemoryInfo& mem_info, const OrtArenaCfg& arena_cfg) {
  Ort::ThrowOnError(Ort::api->CreateAndRegisterAllocator(this, &mem_info, &arena_cfg));
  return *this;
}

inline void OrtEnv::CopyTensors(const std::vector<const OrtValue*>& src_tensors,
                                const std::vector<OrtValue*>& dst_tensors,
                                OrtSyncStream* stream) const {
  if (src_tensors.size() != dst_tensors.size()) {
    throw std::runtime_error("Number of source and destination tensors must match");
  }
  Ort::ThrowOnError(Ort::api->CopyTensors(this, src_tensors.data(), dst_tensors.data(), stream, src_tensors.size()));
}

inline std::vector<const OrtEpDevice*> OrtEnv::GetEpDevices() {
  size_t num_devices = 0;
  const OrtEpDevice* const* device_ptrs = nullptr;
  Ort::ThrowOnError(Ort::api->GetEpDevices(this, &device_ptrs, &num_devices));

  std::vector<const OrtEpDevice*> devices;
  for (size_t i = 0; i < num_devices; ++i) {
    devices.emplace_back(device_ptrs[i]);
  }
  return devices;
}

inline std::unique_ptr<OrtThreadingOptions> OrtThreadingOptions::Create() {
  OrtThreadingOptions* p;
  Ort::ThrowOnError(Ort::api->CreateThreadingOptions(&p));
  return std::unique_ptr<OrtThreadingOptions>{p};
}

inline void OrtThreadingOptions::SetGlobalIntraOpNumThreads(int intra_op_num_threads) {
  Ort::ThrowOnError(Ort::api->SetGlobalIntraOpNumThreads(this, intra_op_num_threads));
}

inline void OrtThreadingOptions::SetGlobalInterOpNumThreads(int inter_op_num_threads) {
  Ort::ThrowOnError(Ort::api->SetGlobalInterOpNumThreads(this, inter_op_num_threads));
}

inline void OrtThreadingOptions::SetGlobalSpinControl(bool allow_spinning) {
  Ort::ThrowOnError(Ort::api->SetGlobalSpinControl(this, allow_spinning));
}

inline void OrtThreadingOptions::SetGlobalDenormalAsZero() {
  Ort::ThrowOnError(Ort::api->SetGlobalDenormalAsZero(this));
}

inline void OrtThreadingOptions::SetGlobalCustomCreateThreadFn(OrtCustomCreateThreadFn ort_custom_create_thread_fn) {
  Ort::ThrowOnError(Ort::api->SetGlobalCustomCreateThreadFn(this, ort_custom_create_thread_fn));
}

inline void OrtThreadingOptions::SetGlobalCustomThreadCreationOptions(void* ort_custom_thread_creation_options) {
  Ort::ThrowOnError(Ort::api->SetGlobalCustomThreadCreationOptions(this, ort_custom_thread_creation_options));
}

inline void OrtThreadingOptions::SetGlobalCustomJoinThreadFn(OrtCustomJoinThreadFn ort_custom_join_thread_fn) {
  Ort::ThrowOnError(Ort::api->SetGlobalCustomJoinThreadFn(this, ort_custom_join_thread_fn));
}

inline std::unique_ptr<OrtCustomOpDomain> OrtCustomOpDomain::Create(const char* domain) {
  OrtCustomOpDomain* p;
  Ort::ThrowOnError(Ort::api->CreateCustomOpDomain(domain, &p));
  return std::unique_ptr<OrtCustomOpDomain>{p};
}

inline void OrtCustomOpDomain::Add(const OrtCustomOp& op) {
  Ort::ThrowOnError(Ort::api->CustomOpDomain_Add(this, &op));
}

inline std::unique_ptr<OrtRunOptions> OrtRunOptions::Create() {
  OrtRunOptions* p;
  Ort::ThrowOnError(Ort::api->CreateRunOptions(&p));
  return std::unique_ptr<OrtRunOptions>{p};
}

inline OrtRunOptions& OrtRunOptions::SetRunLogVerbosityLevel(int level) {
  Ort::ThrowOnError(Ort::api->RunOptionsSetRunLogVerbosityLevel(this, level));
  return *this;
}

inline OrtRunOptions& OrtRunOptions::SetRunLogSeverityLevel(int level) {
  Ort::ThrowOnError(Ort::api->RunOptionsSetRunLogSeverityLevel(this, level));
  return *this;
}

inline int OrtRunOptions::GetRunLogVerbosityLevel() const {
  int out;
  Ort::ThrowOnError(Ort::api->RunOptionsGetRunLogVerbosityLevel(this, &out));
  return out;
}

inline int OrtRunOptions::GetRunLogSeverityLevel() const {
  int out;
  Ort::ThrowOnError(Ort::api->RunOptionsGetRunLogSeverityLevel(this, &out));
  return out;
}

inline OrtRunOptions& OrtRunOptions::SetRunTag(const char* run_tag) {
  Ort::ThrowOnError(Ort::api->RunOptionsSetRunTag(this, run_tag));
  return *this;
}

inline const char* OrtRunOptions::GetRunTag() const {
  const char* out;
  Ort::ThrowOnError(Ort::api->RunOptionsGetRunTag(this, &out));
  return out;
}

inline OrtRunOptions& OrtRunOptions::AddConfigEntry(const char* config_key, const char* config_value) {
  Ort::ThrowOnError(Ort::api->AddRunConfigEntry(this, config_key, config_value));
  return *this;
}

inline OrtRunOptions& OrtRunOptions::SetTerminate() {
  Ort::ThrowOnError(Ort::api->RunOptionsSetTerminate(this));
  return *this;
}

inline OrtRunOptions& OrtRunOptions::UnsetTerminate() {
  Ort::ThrowOnError(Ort::api->RunOptionsUnsetTerminate(this));
  return *this;
}

inline OrtRunOptions& OrtRunOptions::AddActiveLoraAdapter(const OrtLoraAdapter& adapter) {
  Ort::ThrowOnError(Ort::api->RunOptionsAddActiveLoraAdapter(this, &adapter));
  return *this;
}

#if ORT_API_VERSION >= 25
inline OrtRunOptions& OrtRunOptions::EnableProfiling(const ORTCHAR_T* profile_file_prefix) {
  Ort::ThrowOnError(Ort::api->RunOptionsEnableProfiling(this, profile_file_prefix));
  return *this;
}

inline OrtRunOptions& OrtRunOptions::DisableProfiling() {
  Ort::ThrowOnError(Ort::api->RunOptionsDisableProfiling(this));
  return *this;
}
#endif

inline std::unique_ptr<OrtCUDAProviderOptionsV2> OrtCUDAProviderOptionsV2::Create() {
  OrtCUDAProviderOptionsV2* p;
  Ort::ThrowOnError(Ort::api->CreateCUDAProviderOptions(&p));
  return std::unique_ptr<OrtCUDAProviderOptionsV2>{p};
}

inline void OrtCUDAProviderOptionsV2::Update(const char* const* keys, const char* const* values, size_t count) {
  Ort::ThrowOnError(Ort::api->UpdateCUDAProviderOptions(this, keys, values, count));
}

inline void OrtCUDAProviderOptionsV2::UpdateValue(const char* key, void* value) {
  Ort::ThrowOnError(Ort::api->UpdateCUDAProviderOptionsWithValue(this, key, value));
}

inline std::unique_ptr<OrtSessionOptions> OrtSessionOptions::Create() {
  OrtSessionOptions* p;
  Ort::ThrowOnError(Ort::api->CreateSessionOptions(&p));
  return std::unique_ptr<OrtSessionOptions>{p};
}

inline std::unique_ptr<OrtSessionOptions> OrtSessionOptions::Clone() const {
  OrtSessionOptions* out;
  Ort::ThrowOnError(Ort::api->CloneSessionOptions(this, &out));
  return std::unique_ptr<OrtSessionOptions>{out};
}

inline OrtSessionOptions& OrtSessionOptions::SetIntraOpNumThreads(int intra_op_num_threads) {
  Ort::ThrowOnError(Ort::api->SetIntraOpNumThreads(this, intra_op_num_threads));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::SetInterOpNumThreads(int inter_op_num_threads) {
  Ort::ThrowOnError(Ort::api->SetInterOpNumThreads(this, inter_op_num_threads));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::SetGraphOptimizationLevel(GraphOptimizationLevel graph_optimization_level) {
  Ort::ThrowOnError(Ort::api->SetSessionGraphOptimizationLevel(this, graph_optimization_level));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::SetOptimizedModelFilePath(const ORTCHAR_T* optimized_model_filepath) {
  Ort::ThrowOnError(Ort::api->SetOptimizedModelFilePath(this, optimized_model_filepath));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::EnableProfiling(const ORTCHAR_T* profile_file_prefix) {
  Ort::ThrowOnError(Ort::api->EnableProfiling(this, profile_file_prefix));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::DisableProfiling() {
  Ort::ThrowOnError(Ort::api->DisableProfiling(this));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::EnableOrtCustomOps() {
  Ort::ThrowOnError(Ort::api->EnableOrtCustomOps(this));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::EnableMemPattern() {
  Ort::ThrowOnError(Ort::api->EnableMemPattern(this));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::DisableMemPattern() {
  Ort::ThrowOnError(Ort::api->DisableMemPattern(this));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::EnableCpuMemArena() {
  Ort::ThrowOnError(Ort::api->EnableCpuMemArena(this));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::DisableCpuMemArena() {
  Ort::ThrowOnError(Ort::api->DisableCpuMemArena(this));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::SetExecutionMode(ExecutionMode execution_mode) {
  Ort::ThrowOnError(Ort::api->SetSessionExecutionMode(this, execution_mode));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::SetLogId(const char* logid) {
  Ort::ThrowOnError(Ort::api->SetSessionLogId(this, logid));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::SetLogSeverityLevel(int level) {
  Ort::ThrowOnError(Ort::api->SetSessionLogSeverityLevel(this, level));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::SetLogVerbosityLevel(int level) {
  Ort::ThrowOnError(Ort::api->SetSessionLogVerbosityLevel(this, level));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::Add(OrtCustomOpDomain& custom_op_domain) {
  Ort::ThrowOnError(Ort::api->AddCustomOpDomain(this, &custom_op_domain));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::AddConfigEntry(const char* config_key, const char* config_value) {
  Ort::ThrowOnError(Ort::api->AddSessionConfigEntry(this, config_key, config_value));
  return *this;
}

inline bool OrtSessionOptions::HasConfigEntry(const char* config_key) const {
  int out = 0;
  Ort::ThrowOnError(Ort::api->HasSessionConfigEntry(this, config_key, &out));
  return out != 0;
}

inline OrtSessionOptions& OrtSessionOptions::AddInitializer(const char* name, const OrtValue& ort_val) {
  Ort::ThrowOnError(Ort::api->AddInitializer(this, name, &ort_val));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::DisablePerSessionThreads() {
  Ort::ThrowOnError(Ort::api->DisablePerSessionThreads(this));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::AddExternalInitializers(const std::vector<std::string>& names,
                                                                     const std::vector<std::unique_ptr<OrtValue>>& ort_values) {
  const size_t inputs_num = names.size();
  if (inputs_num != ort_values.size()) {
    Ort::ThrowOnError(OrtStatus::Create(ORT_INVALID_ARGUMENT, "Expecting names and ort_values to have the same length").get());
  }
  std::vector<const char*> names_ptr;
  std::vector<const OrtValue*> ort_values_ptrs;
  names_ptr.reserve(inputs_num);
  ort_values_ptrs.reserve(inputs_num);
  for (size_t i = 0; i < inputs_num; ++i) {
    names_ptr.push_back(names[i].c_str());
    ort_values_ptrs.push_back(ort_values[i].get());
  }
  Ort::ThrowOnError(Ort::api->AddExternalInitializers(this, names_ptr.data(), ort_values_ptrs.data(), inputs_num));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::AppendExecutionProvider_CUDA(const OrtCUDAProviderOptions& provider_options) {
  Ort::ThrowOnError(Ort::api->SessionOptionsAppendExecutionProvider_CUDA(this, &provider_options));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::AppendExecutionProvider_CUDA_V2(const OrtCUDAProviderOptionsV2& provider_options) {
  Ort::ThrowOnError(Ort::api->SessionOptionsAppendExecutionProvider_CUDA_V2(this, &provider_options));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::AppendExecutionProvider_TensorRT(const OrtTensorRTProviderOptions& provider_options) {
  Ort::ThrowOnError(Ort::api->SessionOptionsAppendExecutionProvider_TensorRT(this, &provider_options));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::AppendExecutionProvider_TensorRT_V2(const OrtTensorRTProviderOptionsV2& provider_options) {
  Ort::ThrowOnError(Ort::api->SessionOptionsAppendExecutionProvider_TensorRT_V2(this, &provider_options));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::AppendExecutionProvider_MIGraphX(const OrtMIGraphXProviderOptions& provider_options) {
  Ort::ThrowOnError(Ort::api->SessionOptionsAppendExecutionProvider_MIGraphX(this, &provider_options));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::AppendExecutionProvider_CANN(const OrtCANNProviderOptions& provider_options) {
  Ort::ThrowOnError(Ort::api->SessionOptionsAppendExecutionProvider_CANN(this, &provider_options));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::AppendExecutionProvider(const std::string& provider_name, const char* const* keys, const char* const* values, size_t num_keys) {
  Ort::ThrowOnError(Ort::api->SessionOptionsAppendExecutionProvider(this, provider_name.c_str(), keys, values, num_keys));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::SetCustomCreateThreadFn(OrtCustomCreateThreadFn ort_custom_create_thread_fn) {
  Ort::ThrowOnError(Ort::api->SessionOptionsSetCustomCreateThreadFn(this, ort_custom_create_thread_fn));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::SetCustomThreadCreationOptions(void* ort_custom_thread_creation_options) {
  Ort::ThrowOnError(Ort::api->SessionOptionsSetCustomThreadCreationOptions(this, ort_custom_thread_creation_options));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::SetCustomJoinThreadFn(OrtCustomJoinThreadFn ort_custom_join_thread_fn) {
  Ort::ThrowOnError(Ort::api->SessionOptionsSetCustomJoinThreadFn(this, ort_custom_join_thread_fn));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::AppendExecutionProvider_OpenVINO(const OrtOpenVINOProviderOptions& provider_options) {
  Ort::ThrowOnError(Ort::api->SessionOptionsAppendExecutionProvider_OpenVINO(this, &provider_options));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::RegisterCustomOpsLibrary(const ORTCHAR_T* library_file_prefix) {
  Ort::ThrowOnError(Ort::api->RegisterCustomOpsLibrary_V2(this, library_file_prefix));
  return *this;
}

inline OrtSessionOptions& OrtSessionOptions::AppendExecutionProvider_V2(OrtEnv& env, const std::vector<const OrtEpDevice*>& ep_devices,
                                                                        const std::unordered_map<std::string, std::string>& options) {
  std::vector<const char*> keys, values;
  keys.reserve(options.size());
  values.reserve(options.size());
  for (const auto& kv : options) {
    keys.push_back(kv.first.c_str());
    values.push_back(kv.second.c_str());
  }
  Ort::ThrowOnError(Ort::api->SessionOptionsAppendExecutionProvider_V2(this, &env, ep_devices.data(), ep_devices.size(), keys.data(), values.data(), keys.size()));
  return *this;
}

/// Session
inline std::unique_ptr<OrtSession> OrtSession::Create(OrtEnv& env, const ORTCHAR_T* model_path, const OrtSessionOptions* options) {
  OrtSession* p;
  Ort::ThrowOnError(Ort::api->CreateSession(&env, model_path, options, &p));
  return std::unique_ptr<OrtSession>(p);
}

inline std::unique_ptr<OrtSession> OrtSession::Create(OrtEnv& env, const ORTCHAR_T* model_path, const OrtSessionOptions* options,
                                                      OrtPrepackedWeightsContainer& prepacked_weights_container) {
  OrtSession* p;
  Ort::ThrowOnError(Ort::api->CreateSessionWithPrepackedWeightsContainer(&env, model_path, options, &prepacked_weights_container, &p));
  return std::unique_ptr<OrtSession>(p);
}

inline std::unique_ptr<OrtSession> OrtSession::Create(OrtEnv& env, const void* model_data, size_t model_data_length, const OrtSessionOptions* options) {
  OrtSession* p;
  Ort::ThrowOnError(Ort::api->CreateSessionFromArray(&env, model_data, model_data_length, options, &p));
  return std::unique_ptr<OrtSession>(p);
}

inline std::unique_ptr<OrtSession> OrtSession::Create(OrtEnv& env, const void* model_data, size_t model_data_length,
                                                      const OrtSessionOptions* options, OrtPrepackedWeightsContainer& prepacked_weights_container) {
  OrtSession* p;
  Ort::ThrowOnError(Ort::api->CreateSessionFromArrayWithPrepackedWeightsContainer(&env, model_data, model_data_length, options,
                                                                                  &prepacked_weights_container, &p));
  return std::unique_ptr<OrtSession>(p);
}

inline size_t OrtSession::GetInputCount() const {
  size_t out;
  Ort::ThrowOnError(Ort::api->SessionGetInputCount(this, &out));
  return out;
}

inline size_t OrtSession::GetOutputCount() const {
  size_t out;
  Ort::ThrowOnError(Ort::api->SessionGetOutputCount(this, &out));
  return out;
}

inline size_t OrtSession::GetOverridableInitializerCount() const {
  size_t out;
  Ort::ThrowOnError(Ort::api->SessionGetOverridableInitializerCount(this, &out));
  return out;
}

inline std::string OrtSession::GetInputName(size_t index) const {
  Ort::StringAllocator string_allocator;
  Ort::ThrowOnError(Ort::api->SessionGetInputName(this, index, &string_allocator, &string_allocator.out));
  return string_allocator;
}

inline std::vector<std::string> OrtSession::GetInputNames() const {
  std::vector<std::string> out;
  for (size_t i = 0, count = GetInputCount(); i < count; i++) {
    out.emplace_back(GetInputName(i));
  }
  return out;
}

inline std::string OrtSession::GetOutputName(size_t index) const {
  Ort::StringAllocator string_allocator;
  Ort::ThrowOnError(Ort::api->SessionGetOutputName(this, index, &string_allocator, &string_allocator.out));
  return string_allocator;
}

inline std::vector<std::string> OrtSession::GetOutputNames() const {
  std::vector<std::string> out;
  for (size_t i = 0, count = GetOutputCount(); i < count; i++) {
    out.emplace_back(GetOutputName(i));
  }
  return out;
}

inline std::string OrtSession::GetOverridableInitializerName(size_t index) const {
  Ort::StringAllocator string_allocator;
  Ort::ThrowOnError(Ort::api->SessionGetOverridableInitializerName(this, index, &string_allocator, &string_allocator.out));
  return string_allocator;
}

inline std::vector<std::string> OrtSession::GetOverridableInitializerNames() const {
  std::vector<std::string> out;
  for (size_t i = 0, count = GetOverridableInitializerCount(); i < count; i++) {
    out.emplace_back(GetOverridableInitializerName(i));
  }
  return out;
}

inline std::string OrtSession::EndProfiling() {
  Ort::StringAllocator string_allocator;
  Ort::ThrowOnError(Ort::api->SessionEndProfiling(this, &string_allocator, &string_allocator.out));
  return string_allocator;
}

inline uint64_t OrtSession::GetProfilingStartTimeNs() const {
  uint64_t out;
  Ort::ThrowOnError(Ort::api->SessionGetProfilingStartTimeNs(this, &out));
  return out;
}

inline std::unique_ptr<OrtModelMetadata> OrtSession::GetModelMetadata() const {
  OrtModelMetadata* out;
  Ort::ThrowOnError(Ort::api->SessionGetModelMetadata(this, &out));
  return std::unique_ptr<OrtModelMetadata>(out);
}

inline std::unique_ptr<OrtTypeInfo> OrtSession::GetInputTypeInfo(size_t index) const {
  OrtTypeInfo* out;
  Ort::ThrowOnError(Ort::api->SessionGetInputTypeInfo(this, index, &out));
  return std::unique_ptr<OrtTypeInfo>(out);
}

inline std::unique_ptr<OrtTypeInfo> OrtSession::GetOutputTypeInfo(size_t index) const {
  OrtTypeInfo* out;
  Ort::ThrowOnError(Ort::api->SessionGetOutputTypeInfo(this, index, &out));
  return std::unique_ptr<OrtTypeInfo>(out);
}

inline std::unique_ptr<OrtTypeInfo> OrtSession::GetOverridableInitializerTypeInfo(size_t index) const {
  OrtTypeInfo* out;
  Ort::ThrowOnError(Ort::api->SessionGetOverridableInitializerTypeInfo(this, index, &out));
  return std::unique_ptr<OrtTypeInfo>(out);
}

inline std::vector<std::unique_ptr<OrtValue>> OrtSession::Run(const OrtRunOptions* run_options, const char* const* input_names, const OrtValue* const* input_values, size_t input_count,
                                                              const char* const* output_names, size_t output_count) {
  std::vector<OrtValue*> output_values(output_count);
  std::vector<std::unique_ptr<OrtValue>> results(output_count);  // Allocate before Run() so that if it fails, it fails before we have unowned OrtValue*s in output_values
  Run(run_options, input_names, input_values, input_count, output_names, output_values.data(), output_count);

  for (auto i = 0; i < output_count; i++)
    results[i] = std::unique_ptr<OrtValue>{output_values[i]};

  return results;
}

inline void OrtSession::Run(const OrtRunOptions* run_options, const char* const* input_names, const OrtValue* const* input_values, size_t input_count,
                            const char* const* output_names, OrtValue** output_values, size_t output_count) {
  Ort::ThrowOnError(Ort::api->Run(this, run_options, input_names, input_values, input_count, output_names, output_count, output_values));
}

inline void OrtSession::Run(const OrtRunOptions* run_options, const OrtIoBinding& io_binding) {
  Ort::ThrowOnError(Ort::api->RunWithBinding(this, run_options, &io_binding));
}

inline void OrtSession::SetEpDynamicOptions(const char* const* keys, const char* const* values, size_t kv_len) {
  Ort::ThrowOnError(Ort::api->SetEpDynamicOptions(this, keys, values, kv_len));
}

#if ORT_API_VERSION >= 27
inline void OrtSession::ReleaseCapturedGraph(int graph_annotation_id) {
  Ort::ThrowOnError(Ort::api->SessionReleaseCapturedGraph(this, graph_annotation_id));
}
#endif

inline std::string OrtModelMetadata::GetProducerName() const {
  Ort::StringAllocator string_allocator;
  Ort::ThrowOnError(Ort::api->ModelMetadataGetProducerName(this, &string_allocator, &string_allocator.out));
  return string_allocator;
}

inline std::string OrtModelMetadata::GetGraphName() const {
  Ort::StringAllocator string_allocator;
  Ort::ThrowOnError(Ort::api->ModelMetadataGetGraphName(this, &string_allocator, &string_allocator.out));
  return string_allocator;
}

inline std::string OrtModelMetadata::GetDomain() const {
  Ort::StringAllocator string_allocator;
  Ort::ThrowOnError(Ort::api->ModelMetadataGetDomain(this, &string_allocator, &string_allocator.out));
  return string_allocator;
}

inline std::string OrtModelMetadata::GetDescription() const {
  Ort::StringAllocator string_allocator;
  Ort::ThrowOnError(Ort::api->ModelMetadataGetDescription(this, &string_allocator, &string_allocator.out));
  return string_allocator;
}

inline std::string OrtModelMetadata::GetGraphDescription() const {
  Ort::StringAllocator string_allocator;
  Ort::ThrowOnError(Ort::api->ModelMetadataGetGraphDescription(this, &string_allocator, &string_allocator.out));
  return string_allocator;
}

inline std::string OrtModelMetadata::LookupCustomMetadataMap(const char* key) const {
  Ort::StringAllocator string_allocator;
  Ort::ThrowOnError(Ort::api->ModelMetadataLookupCustomMetadataMap(this, &string_allocator, key, &string_allocator.out));
  return string_allocator;
}

inline std::vector<std::string> OrtModelMetadata::GetCustomMetadataMapKeysAllocated() const {
  char** out;
  int64_t num_keys = 0;
  Ort::ThrowOnError(Ort::api->ModelMetadataGetCustomMetadataMapKeys(this, &Ort::standard_allocator, &out, &num_keys));
  if (num_keys <= 0) {
    return {};
  }

  // Own the returned memory so it will be freed
  auto strings_deletor = [num_keys](char** out) { for(int64_t i = 0; i < num_keys; ++i) delete out[i]; };
  std::unique_ptr<char*, decltype(strings_deletor)> strings_guard(out, strings_deletor);

  std::vector<std::string> result;
  for (int64_t i = 0; i < num_keys; ++i)
    result.push_back(out[i]);
  return result;
}

inline int64_t OrtModelMetadata::GetVersion() const {
  int64_t out;
  Ort::ThrowOnError(Ort::api->ModelMetadataGetVersion(this, &out));
  return out;
}

inline ONNXTensorElementDataType OrtTensorTypeAndShapeInfo::GetElementType() const {
  ONNXTensorElementDataType out;
  Ort::ThrowOnError(Ort::api->GetTensorElementType(this, &out));
  return out;
}

inline size_t OrtTensorTypeAndShapeInfo::GetElementCount() const {
  size_t out;
  Ort::ThrowOnError(Ort::api->GetTensorShapeElementCount(this, &out));
  return static_cast<size_t>(out);
}

inline size_t GetDimensionsCount(const OrtTensorTypeAndShapeInfo* p) {
  size_t out;
  Ort::ThrowOnError(Ort::api->GetDimensionsCount(p, &out));
  return out;
}

inline std::vector<const char*> OrtTensorTypeAndShapeInfo::GetSymbolicDimensions() const {
  std::vector<const char*> out(GetDimensionsCount(this), nullptr);
  Ort::ThrowOnError(Ort::api->GetSymbolicDimensions(this, out.data(), out.size()));
  return out;
}

inline std::vector<int64_t> OrtTensorTypeAndShapeInfo::GetShape() const {
  std::vector<int64_t> out(GetDimensionsCount(this), 0);
  Ort::ThrowOnError(Ort::api->GetDimensions(this, out.data(), out.size()));
  return out;
}

inline std::unique_ptr<OrtTypeInfo> OrtSequenceTypeInfo::GetSequenceElementType() const {
  OrtTypeInfo* output;
  Ort::ThrowOnError(Ort::api->GetSequenceElementType(this, &output));
  return std::unique_ptr<OrtTypeInfo>{output};
}

inline ONNXTensorElementDataType OrtMapTypeInfo::GetMapKeyType() const {
  ONNXTensorElementDataType out;
  Ort::ThrowOnError(Ort::api->GetMapKeyType(this, &out));
  return out;
}

inline std::unique_ptr<OrtTypeInfo> OrtMapTypeInfo::GetMapValueType() const {
  OrtTypeInfo* output;
  Ort::ThrowOnError(Ort::api->GetMapValueType(this, &output));
  return std::unique_ptr<OrtTypeInfo>{output};
}

inline const OrtTensorTypeAndShapeInfo& OrtTypeInfo::GetTensorTypeAndShapeInfo() const {
  const OrtTensorTypeAndShapeInfo* out;
  Ort::ThrowOnError(Ort::api->CastTypeInfoToTensorInfo(this, &out));
  return *out;
}

inline const OrtSequenceTypeInfo& OrtTypeInfo::GetSequenceTypeInfo() const {
  const OrtSequenceTypeInfo* out;
  Ort::ThrowOnError(Ort::api->CastTypeInfoToSequenceTypeInfo(this, &out));
  return *out;
}

inline const OrtMapTypeInfo& OrtTypeInfo::GetMapTypeInfo() const {
  const OrtMapTypeInfo* out;
  Ort::ThrowOnError(Ort::api->CastTypeInfoToMapTypeInfo(this, &out));
  return *out;
}

inline ONNXType OrtTypeInfo::GetONNXType() const {
  ONNXType out;
  Ort::ThrowOnError(Ort::api->GetOnnxTypeFromTypeInfo(this, &out));
  return out;
}

template <typename T>
inline void OrtValue::GetOpaqueData(const char* domain, const char* type_name, T& out) const {
  Ort::ThrowOnError(Ort::api->GetOpaqueValue(domain, type_name, this, &out, sizeof(T)));
}

inline bool OrtValue::IsTensor() const {
  int out;
  Ort::ThrowOnError(Ort::api->IsTensor(this, &out));
  return out != 0;
}

inline bool OrtValue::HasValue() const {
  int out;
  Ort::ThrowOnError(Ort::api->HasValue(this, &out));
  return out != 0;
}

inline size_t OrtValue::GetCount() const {
  size_t out;
  Ort::ThrowOnError(Ort::api->GetValueCount(this, &out));
  return out;
}

inline std::unique_ptr<OrtValue> OrtValue::GetValue(int index) const {
  OrtValue* out;
  Ort::ThrowOnError(Ort::api->GetValue(this, index, &Ort::standard_allocator, &out));
  return std::unique_ptr<OrtValue>{out};
}

inline size_t OrtValue::GetStringTensorDataLength() const {
  size_t out;
  Ort::ThrowOnError(Ort::api->GetStringTensorDataLength(this, &out));
  return out;
}

inline size_t OrtValue::GetStringTensorElementLength(size_t element_index) const {
  size_t out;
  Ort::ThrowOnError(Ort::api->GetStringTensorElementLength(this, element_index, &out));
  return out;
}

template <typename T>
inline const T* OrtValue::GetTensorData() const {
  T* out;
  Ort::ThrowOnError(Ort::api->GetTensorMutableData(const_cast<OrtValue*>(this), (void**)&out));
  return out;
}

inline const void* OrtValue::GetTensorRawData() const {
  void* out;
  Ort::ThrowOnError(Ort::api->GetTensorMutableData(const_cast<OrtValue*>(this), &out));
  return out;
}

inline std::unique_ptr<OrtTypeInfo> OrtValue::GetTypeInfo() const {
  OrtTypeInfo* output;
  Ort::ThrowOnError(Ort::api->GetTypeInfo(this, &output));
  return std::unique_ptr<OrtTypeInfo>{output};
}

inline std::unique_ptr<OrtTensorTypeAndShapeInfo> OrtValue::GetTensorTypeAndShapeInfo() const {
  OrtTensorTypeAndShapeInfo* output;
  Ort::ThrowOnError(Ort::api->GetTensorTypeAndShape(this, &output));
  return std::unique_ptr<OrtTensorTypeAndShapeInfo>{output};
}

inline const OrtMemoryInfo& OrtValue::GetTensorMemoryInfo() const {
  const OrtMemoryInfo* mem_info;
  Ort::ThrowOnError(Ort::api->GetTensorMemoryInfo(this, &mem_info));
  return *mem_info;
}

inline void OrtValue::GetStringTensorElement(size_t buffer_length, size_t element_index, void* buffer) const {
  Ort::ThrowOnError(Ort::api->GetStringTensorElement(this, buffer_length, element_index, buffer));
}

inline void OrtValue::GetStringTensorContent(void* buffer, size_t buffer_length, size_t* offsets, size_t offsets_count) const {
  Ort::ThrowOnError(Ort::api->GetStringTensorContent(this, buffer, buffer_length, offsets, offsets_count));
}

#if !defined(DISABLE_SPARSE_TENSORS)
inline OrtSparseFormat OrtValue::GetSparseFormat() const {
  OrtSparseFormat format;
  Ort::ThrowOnError(Ort::api->GetSparseTensorFormat(this, &format));
  return format;
}

inline std::unique_ptr<OrtTensorTypeAndShapeInfo> OrtValue::GetSparseTensorValuesTypeAndShapeInfo() const {
  OrtTensorTypeAndShapeInfo* output;
  Ort::ThrowOnError(Ort::api->GetSparseTensorValuesTypeAndShape(this, &output));
  return std::unique_ptr<OrtTensorTypeAndShapeInfo>{output};
}

inline std::unique_ptr<OrtTensorTypeAndShapeInfo> OrtValue::GetSparseTensorIndicesTypeShapeInfo(OrtSparseIndicesFormat indices_format) const {
  OrtTensorTypeAndShapeInfo* output;
  Ort::ThrowOnError(Ort::api->GetSparseTensorIndicesTypeShape(this, indices_format, &output));
  return std::unique_ptr<OrtTensorTypeAndShapeInfo>{output};
}

template <typename T>
inline const T* OrtValue::GetSparseTensorIndicesData(OrtSparseIndicesFormat indices_format, size_t& num_indices) const {
  const void* out;
  Ort::ThrowOnError(Ort::api->GetSparseTensorIndices(this, indices_format, &num_indices, &out));
  return reinterpret_cast<const T*>(out);
}

inline bool OrtValue::IsSparseTensor() const {
  int out;
  Ort::ThrowOnError(Ort::api->IsSparseTensor(this, &out));
  return out != 0;
}

template <typename T>
inline const T* OrtValue::GetSparseTensorValues() const {
  const void* out;
  Ort::ThrowOnError(Ort::api->GetSparseTensorValues(this, &out));
  return reinterpret_cast<const T*>(out);
}

#endif

inline void OrtValue::FillStringTensor(const char* const* s, size_t s_len) {
  Ort::ThrowOnError(Ort::api->FillStringTensor(this, s, s_len));
}

inline void OrtValue::FillStringTensorElement(const char* s, size_t index) {
  Ort::ThrowOnError(Ort::api->FillStringTensorElement(this, s, index));
}

inline void* OrtValue::GetTensorMutableRawData() {
  void* out;
  Ort::ThrowOnError(Ort::api->GetTensorMutableData(this, &out));
  return out;
}

template <typename T>
T* OrtValue::GetTensorMutableData() {
  T* out;
  Ort::ThrowOnError(Ort::api->GetTensorMutableData(this, (void**)&out));
  return out;
}

template <typename T>
T& OrtValue::At(const std::vector<int64_t>& location) {
  static_assert(!std::is_same<T, std::string>::value, "this api does not support std::string");
  T* out;
  Ort::ThrowOnError(Ort::api->TensorAt(this, location.data(), location.size(), (void**)&out));
  return *out;
}

#if !defined(DISABLE_SPARSE_TENSORS)
inline void OrtValue::UseCooIndices(int64_t* indices_data, size_t indices_num) {
  Ort::ThrowOnError(Ort::api->UseCooIndices(this, indices_data, indices_num));
}

inline void OrtValue::UseCsrIndices(int64_t* inner_data, size_t inner_num, int64_t* outer_data, size_t outer_num) {
  Ort::ThrowOnError(Ort::api->UseCsrIndices(this, inner_data, inner_num, outer_data, outer_num));
}

inline void OrtValue::UseBlockSparseIndices(const OrtShape& indices_shape, int32_t* indices_data) {
  Ort::ThrowOnError(Ort::api->UseBlockSparseIndices(this, indices_shape.shape, indices_shape.shape_len, indices_data));
}

inline void OrtValue::FillSparseTensorCoo(const OrtMemoryInfo& mem_info, const OrtSparseValuesParam& values_param,
                                          const int64_t* indices_data, size_t indices_num) {
  Ort::ThrowOnError(Ort::api->FillSparseTensorCoo(this, &mem_info, values_param.values_shape,
                                                  values_param.values_shape_len, values_param.data.p_data,
                                                  indices_data, indices_num));
}

inline void OrtValue::FillSparseTensorCsr(const OrtMemoryInfo& data_mem_info,
                                          const OrtSparseValuesParam& values,
                                          const int64_t* inner_indices_data, size_t inner_indices_num,
                                          const int64_t* outer_indices_data, size_t outer_indices_num) {
  Ort::ThrowOnError(Ort::api->FillSparseTensorCsr(this, &data_mem_info, values.values_shape, values.values_shape_len, values.data.p_data,
                                                  inner_indices_data, inner_indices_num,
                                                  outer_indices_data, outer_indices_num));
}

inline void OrtValue::FillSparseTensorBlockSparse(const OrtMemoryInfo& data_mem_info,
                                                  const OrtSparseValuesParam& values,
                                                  const OrtShape& indices_shape,
                                                  const int32_t* indices_data) {
  Ort::ThrowOnError(Ort::api->FillSparseTensorBlockSparse(this, &data_mem_info, values.values_shape, values.values_shape_len, values.data.p_data,
                                                          indices_shape.shape, indices_shape.shape_len,
                                                          indices_data));
}

#endif  // !defined(DISABLE_SPARSE_TENSORS)

template <typename T>
inline std::unique_ptr<OrtValue> OrtValue::CreateTensor(const OrtMemoryInfo& info, std::span<T> p_data, std::span<const int64_t> shape) {
  return CreateTensor(info, p_data.data(), p_data.size_bytes(), shape, Ort::TypeToTensorType<T>);
}

inline std::unique_ptr<OrtValue> OrtValue::CreateTensor(const OrtMemoryInfo& info, void* p_data, size_t p_data_byte_count, std::span<const int64_t> shape,
                                                        ONNXTensorElementDataType type) {
  OrtValue* out;
  Ort::ThrowOnError(Ort::api->CreateTensorWithDataAsOrtValue(&info, p_data, p_data_byte_count, shape.data(), shape.size(), type, &out));
  return std::unique_ptr<OrtValue>{out};
}

template <typename T>
inline std::unique_ptr<OrtValue> OrtValue::CreateTensor(OrtAllocator& allocator, std::span<const int64_t> shape) {
  return CreateTensor(allocator, shape, Ort::TypeToTensorType<T>);
}

inline std::unique_ptr<OrtValue> OrtValue::CreateTensor(OrtAllocator& allocator, std::span<const int64_t> shape, ONNXTensorElementDataType type) {
  OrtValue* out;
  Ort::ThrowOnError(Ort::api->CreateTensorAsOrtValue(&allocator, shape.data(), shape.size(), type, &out));
  return std::unique_ptr<OrtValue>{out};
}

#if !defined(DISABLE_SPARSE_TENSORS)

template <typename T>
inline std::unique_ptr<OrtValue> OrtValue::CreateSparseTensor(const OrtMemoryInfo& info, T* p_data, const OrtShape& dense_shape,
                                                              const OrtShape& values_shape) {
  return CreateSparseTensor(info, p_data, dense_shape, values_shape, Ort::TypeToTensorType<T>);
}

inline std::unique_ptr<OrtValue> OrtValue::CreateSparseTensor(const OrtMemoryInfo& info, void* p_data, const OrtShape& dense_shape,
                                                              const OrtShape& values_shape, ONNXTensorElementDataType type) {
  OrtValue* out;
  Ort::ThrowOnError(Ort::api->CreateSparseTensorWithValuesAsOrtValue(&info, p_data, dense_shape.shape, dense_shape.shape_len,
                                                                     values_shape.shape, values_shape.shape_len, type, &out));
  return std::unique_ptr<OrtValue>{out};
}

template <typename T>
inline std::unique_ptr<OrtValue> OrtValue::CreateSparseTensor(OrtAllocator* allocator, const OrtShape& dense_shape) {
  return CreateSparseTensor(allocator, dense_shape, Ort::TypeToTensorType<T>);
}

inline std::unique_ptr<OrtValue> OrtValue::CreateSparseTensor(OrtAllocator* allocator, const OrtShape& dense_shape,
                                                              ONNXTensorElementDataType type) {
  OrtValue* out;
  Ort::ThrowOnError(Ort::api->CreateSparseTensorAsOrtValue(allocator, dense_shape.shape, dense_shape.shape_len, type, &out));
  return std::unique_ptr<OrtValue>{out};
}
#endif  // !defined(DISABLE_SPARSE_TENSORS)

inline std::unique_ptr<OrtValue> OrtValue::CreateMap(OrtValue& keys, OrtValue& values) {
  OrtValue* out;
  OrtValue* inputs[2] = {&keys, &values};
  Ort::ThrowOnError(Ort::api->CreateValue(inputs, 2, ONNX_TYPE_MAP, &out));
  return std::unique_ptr<OrtValue>{out};
}

inline std::unique_ptr<OrtValue> OrtValue::CreateSequence(const OrtValue* const* values, size_t count) {
  OrtValue* out;

  Ort::ThrowOnError(Ort::api->CreateValue(values, count, ONNX_TYPE_SEQUENCE, &out));
  return std::unique_ptr<OrtValue>{out};
}

template <typename T>
inline std::unique_ptr<OrtValue> OrtValue::CreateOpaque(const char* domain, const char* type_name, const T& data_container) {
  OrtValue* out;
  Ort::ThrowOnError(Ort::api->CreateOpaqueValue(domain, type_name, &data_container, sizeof(T), &out));
  return std::unique_ptr<OrtValue>{out};
}

//
// Custom OP Inlines
//
inline size_t OrtKernelContext::GetInputCount() const {
  size_t out;
  Ort::ThrowOnError(Ort::api->KernelContext_GetInputCount(this, &out));
  return out;
}

inline size_t OrtKernelContext::GetOutputCount() const {
  size_t out;
  Ort::ThrowOnError(Ort::api->KernelContext_GetOutputCount(this, &out));
  return out;
}

inline const OrtValue* OrtKernelContext::GetInput(size_t index) const {
  const OrtValue* out;
  Ort::ThrowOnError(Ort::api->KernelContext_GetInput(this, index, &out));
  return out;
}

inline OrtValue* OrtKernelContext::GetOutput(size_t index, const int64_t* dim_values, size_t dim_count) {
  OrtValue* out;
  Ort::ThrowOnError(Ort::api->KernelContext_GetOutput(this, index, dim_values, dim_count, &out));
  return out;
}

inline OrtValue* OrtKernelContext::GetOutput(size_t index, const std::vector<int64_t>& dims) {
  OrtValue* out;
  Ort::ThrowOnError(Ort::api->KernelContext_GetOutput(this, index, dims.data(), dims.size(), &out));
  return out;
}

inline void* OrtKernelContext::GetGPUComputeStream() const {
  void* out;
  Ort::ThrowOnError(Ort::api->KernelContext_GetGPUComputeStream(this, &out));
  return out;
}

inline std::unique_ptr<OrtOpAttr> OrtOpAttr::Create(const char* name, const void* data, int len, OrtOpAttrType type) {
  OrtOpAttr* p;
  Ort::ThrowOnError(Ort::api->CreateOpAttr(name, data, len, type, &p));
  return std::unique_ptr<OrtOpAttr>{p};
}

inline std::unique_ptr<OrtGraph> OrtGraph::Create() {
  OrtGraph* p;
  Ort::ThrowOnError(Ort::GetModelEditorApi().CreateGraph(&p));
  return std::unique_ptr<OrtGraph>{p};
}

inline void OrtGraph::SetInputs(OrtValueInfo** inputs, size_t input_count) {
  Ort::ThrowOnError(Ort::GetModelEditorApi().SetGraphInputs(this, inputs, input_count));
}

inline void OrtGraph::SetOutputs(OrtValueInfo** outputs, size_t output_count) {
  Ort::ThrowOnError(Ort::GetModelEditorApi().SetGraphOutputs(this, outputs, output_count));
}

inline void OrtGraph::AddNode(OrtNode* node) {
  Ort::ThrowOnError(Ort::GetModelEditorApi().AddNodeToGraph(this, node));
}

inline std::unique_ptr<OrtModel> OrtModel::Create(const char** domain_names, const int* opset_versions, size_t num_domains) {
  OrtModel* p;
  Ort::ThrowOnError(Ort::GetModelEditorApi().CreateModel(domain_names, opset_versions, num_domains, &p));
  return std::unique_ptr<OrtModel>{p};
}

inline void OrtModel::AddGraph(OrtGraph* graph) {
  Ort::ThrowOnError(Ort::GetModelEditorApi().AddGraphToModel(this, graph));
}

inline std::unique_ptr<OrtValueInfo> OrtValueInfo::Create(const char* name, const OrtTensorTypeAndShapeInfo* tensor_info) {
  const auto& model_editor_api = Ort::GetModelEditorApi();

  OrtTypeInfo* type_info;
  Ort::ThrowOnError(model_editor_api.CreateTensorTypeInfo(tensor_info, &type_info));

  OrtValueInfo* p;
  auto status = model_editor_api.CreateValueInfo(name, type_info, &p);
  Ort::api->ReleaseTypeInfo(type_info);
  Ort::ThrowOnError(status);

  return std::unique_ptr<OrtValueInfo>{p};
}

inline std::unique_ptr<OrtNode> OrtNode::Create(const char* op_type, const char* domain, const char* name,
                                                const char** input_names, size_t num_inputs,
                                                const char** output_names, size_t num_outputs,
                                                OrtOpAttr** attributes, size_t num_attributes) {
  OrtNode* p;
  Ort::ThrowOnError(Ort::GetModelEditorApi().CreateNode(
      op_type, domain, name,
      input_names, num_inputs,
      output_names, num_outputs,
      attributes, num_attributes,
      &p));
  return std::unique_ptr<OrtNode>{p};
}

inline std::unique_ptr<OrtTensorTypeAndShapeInfo> OrtTensorTypeAndShapeInfo::Create() {
  OrtTensorTypeAndShapeInfo* p;
  Ort::ThrowOnError(Ort::api->CreateTensorTypeAndShapeInfo(&p));
  return std::unique_ptr<OrtTensorTypeAndShapeInfo>{p};
}

inline void OrtTensorTypeAndShapeInfo::SetElementType(ONNXTensorElementDataType type) {
  Ort::ThrowOnError(Ort::api->SetTensorElementType(this, type));
}

inline void OrtTensorTypeAndShapeInfo::SetDimensions(const int64_t* dim_values, size_t dim_count) {
  Ort::ThrowOnError(Ort::api->SetDimensions(this, dim_values, dim_count));
}

inline std::unique_ptr<OrtKernelInfo> OrtKernelInfo::Clone() const {
  OrtKernelInfo* p;
  Ort::ThrowOnError(Ort::api->CopyKernelInfo(this, &p));
  return std::unique_ptr<OrtKernelInfo>{p};
}

inline void OrtKernelInfo::GetAttr(const char* name, float& out) {
  Ort::ThrowOnError(Ort::api->KernelInfoGetAttribute_float(this, name, &out));
}

inline void OrtKernelInfo::GetAttr(const char* name, int64_t& out) {
  Ort::ThrowOnError(Ort::api->KernelInfoGetAttribute_int64(this, name, &out));
}

inline void OrtKernelInfo::GetAttr(const char* name, std::string& result) {
  size_t size = 0;
  // Feed nullptr for the data buffer to query the true size of the string attribute
  Ort::ThrowOnError(Ort::api->KernelInfoGetAttribute_string(this, name, nullptr, &size));

  std::string out;
  out.resize(size);
  Ort::ThrowOnError(Ort::api->KernelInfoGetAttribute_string(this, name, &out[0], &size));
  out.resize(size - 1);  // remove the terminating character '\0'
  out.swap(result);
}

inline void OrtKernelInfo::GetAttrs(const char* name, std::vector<float>& result) {
  size_t size = 0;
  // Feed nullptr for the data buffer to query the true size of the attribute
  Ort::ThrowOnError(Ort::api->KernelInfoGetAttributeArray_float(this, name, nullptr, &size));

  std::vector<float> out;
  out.resize(size);
  Ort::ThrowOnError(Ort::api->KernelInfoGetAttributeArray_float(this, name, out.data(), &size));
  out.swap(result);
}

inline void OrtKernelInfo::GetAttrs(const char* name, std::vector<int64_t>& result) {
  size_t size = 0;

  // Feed nullptr for the data buffer to query the true size of the attribute
  Ort::ThrowOnError(Ort::api->KernelInfoGetAttributeArray_int64(this, name, nullptr, &size));

  std::vector<int64_t> out;
  out.resize(size);
  Ort::ThrowOnError(Ort::api->KernelInfoGetAttributeArray_int64(this, name, out.data(), &size));
  out.swap(result);
}

inline std::unique_ptr<OrtOp> OrtOp::Create(const OrtKernelInfo* info, const char* op_name, const char* domain, int version,
                                            const char** type_constraint_names,
                                            const ONNXTensorElementDataType* type_constraint_values,
                                            size_t type_constraint_count,
                                            const OrtOpAttr* const* attr_values, size_t attr_count,
                                            size_t input_count, size_t output_count) {
  OrtOp* p;
  Ort::ThrowOnError(Ort::api->CreateOp(info, op_name, domain, version, type_constraint_names, type_constraint_values,
                                       static_cast<int>(type_constraint_count),
                                       attr_values,
                                       static_cast<int>(attr_count),
                                       static_cast<int>(input_count),
                                       static_cast<int>(output_count), &p));
  return std::unique_ptr<OrtOp>{p};
}

inline void OrtOp::Invoke(const OrtKernelContext* context,
                          const OrtValue* const* input_values,
                          size_t input_count,
                          OrtValue* const* output_values,
                          size_t output_count) {
  Ort::ThrowOnError(Ort::api->InvokeOp(context, this, input_values, static_cast<int>(input_count),
                                       output_values, static_cast<int>(output_count)));
}

inline std::unique_ptr<OrtLoraAdapter> OrtLoraAdapter::Create(const ORTCHAR_T* adapter_file_path, OrtAllocator& allocator) {
  OrtLoraAdapter* p;
  Ort::ThrowOnError(Ort::api->CreateLoraAdapter(adapter_file_path, &allocator, &p));
  return std::unique_ptr<OrtLoraAdapter>{p};
}

#if ORT_GENAI_HAS_MODEL_PACKAGE

namespace Ort {

inline const ModelPackageApi& GetModelPackageApi() {
  static const ModelPackageApi fns = []() {
    ModelPackageApi f;
    if (api == nullptr) {
      return f;
    }
    // Resolve OrtModelPackageApi entries via the C API to avoid including
    // onnxruntime_experimental_cxx_api.h. That header defines the
    // Ort::Experimental::Get_OrtModelPackageApi_*_SinceV28_Fn accessors but transitively
    // pulls in onnxruntime_cxx_api.h, which redefines the Ort:: types that genai has
    // vendored in onnxruntime_api.h.
#define GENAI_MP_V28_FN(NAME)                                                \
  reinterpret_cast<OrtExperimental_OrtModelPackageApi_##NAME##_SinceV28_Fn>( \
      api->GetExperimentalFunction(                                          \
          kOrtExperimental_OrtModelPackageApi_##NAME##_SinceV28_FnName))

    f.CreateModelPackageOptionsFromSessionOptions = GENAI_MP_V28_FN(CreateModelPackageOptionsFromSessionOptions);
    f.ReleaseModelPackageOptions = GENAI_MP_V28_FN(ReleaseModelPackageOptions);
    f.CreateModelPackageContext = GENAI_MP_V28_FN(CreateModelPackageContext);
    f.ReleaseModelPackageContext = GENAI_MP_V28_FN(ReleaseModelPackageContext);
    f.ModelPackage_GetComponentCount = GENAI_MP_V28_FN(ModelPackage_GetComponentCount);
    f.ModelPackage_GetComponentNames = GENAI_MP_V28_FN(ModelPackage_GetComponentNames);
    f.ModelPackage_GetVariantCount = GENAI_MP_V28_FN(ModelPackage_GetVariantCount);
    f.ModelPackage_GetVariantNames = GENAI_MP_V28_FN(ModelPackage_GetVariantNames);
    f.ModelPackage_GetVariantEpName = GENAI_MP_V28_FN(ModelPackage_GetVariantEpName);
    f.ModelPackage_ResolveStringRef = GENAI_MP_V28_FN(ModelPackage_ResolveStringRef);
    f.SelectComponent = GENAI_MP_V28_FN(SelectComponent);
    f.ReleaseModelPackageComponentContext = GENAI_MP_V28_FN(ReleaseModelPackageComponentContext);
    f.ModelPackageComponent_GetSelectedVariantFolderPath =
        GENAI_MP_V28_FN(ModelPackageComponent_GetSelectedVariantFolderPath);

#undef GENAI_MP_V28_FN
    return f;
  }();
  if (fns.CreateModelPackageContext == nullptr) {
    throw std::runtime_error(
        "Model package API is not available in this ONNX Runtime build. "
        "Model packages require ONNX Runtime with the OrtModelPackageApi experimental functions.");
  }
  return fns;
}

}  // namespace Ort

inline std::unique_ptr<OrtModelPackageOptions> OrtModelPackageOptions::Create(const OrtEnv& env,
                                                                              const OrtSessionOptions& session_options) {
  OrtModelPackageOptions* p = nullptr;
  Ort::ThrowOnError(Ort::GetModelPackageApi().CreateModelPackageOptionsFromSessionOptions(&env, &session_options, &p));
  return std::unique_ptr<OrtModelPackageOptions>{p};
}

inline std::unique_ptr<OrtModelPackageContext> OrtModelPackageContext::Create(const ORTCHAR_T* package_root) {
  OrtModelPackageContext* p = nullptr;
  Ort::ThrowOnError(Ort::GetModelPackageApi().CreateModelPackageContext(package_root, &p));
  return std::unique_ptr<OrtModelPackageContext>{p};
}

inline std::vector<std::string> OrtModelPackageContext::GetComponentNames() const {
  const char* const* names = nullptr;
  size_t count = 0;
  Ort::ThrowOnError(Ort::GetModelPackageApi().ModelPackage_GetComponentNames(this, &names, &count));
  std::vector<std::string> result;
  result.reserve(count);
  for (size_t i = 0; i < count; ++i) {
    result.emplace_back(names[i]);
  }
  return result;
}

inline std::vector<std::string> OrtModelPackageContext::GetVariantNames(const char* component_name) const {
  const char* const* names = nullptr;
  size_t count = 0;
  Ort::ThrowOnError(Ort::GetModelPackageApi().ModelPackage_GetVariantNames(this, component_name, &names, &count));
  std::vector<std::string> result;
  result.reserve(count);
  for (size_t i = 0; i < count; ++i) {
    result.emplace_back(names[i]);
  }
  return result;
}

inline std::string OrtModelPackageContext::GetVariantEpName(const char* component_name,
                                                            const char* variant_name) const {
  const char* ep = nullptr;
  Ort::ThrowOnError(Ort::GetModelPackageApi().ModelPackage_GetVariantEpName(this, component_name, variant_name, &ep));
  return (ep == nullptr) ? std::string{} : std::string{ep};
}

inline std::string OrtModelPackageContext::ResolveStringRef(const std::string& base_dir,
                                                            const std::string& input,
                                                            bool must_exist) const {
  const char* resolved = nullptr;
  Ort::ThrowOnError(Ort::GetModelPackageApi().ModelPackage_ResolveStringRef(
      this, base_dir.empty() ? nullptr : base_dir.c_str(), input.c_str(), must_exist ? 1 : 0, &resolved));
  return (resolved == nullptr) ? std::string{} : std::string{resolved};
}

inline std::unique_ptr<OrtModelPackageComponentContext> OrtModelPackageContext::SelectComponent(
    const char* component_name, const OrtModelPackageOptions& options) const {
  OrtModelPackageComponentContext* p = nullptr;
  Ort::ThrowOnError(Ort::GetModelPackageApi().SelectComponent(this, component_name, &options, &p));
  return std::unique_ptr<OrtModelPackageComponentContext>{p};
}

inline std::basic_string<ORTCHAR_T> OrtModelPackageComponentContext::GetSelectedVariantFolderPath() const {
  const ORTCHAR_T* path = nullptr;
  Ort::ThrowOnError(Ort::GetModelPackageApi().ModelPackageComponent_GetSelectedVariantFolderPath(this, &path));
  return path == nullptr ? std::basic_string<ORTCHAR_T>{} : std::basic_string<ORTCHAR_T>{path};
}

#endif  // ORT_GENAI_HAS_MODEL_PACKAGE
