From f068ed0abf60126a85b3a27a94c7c8d745ec7132 Mon Sep 17 00:00:00 2001 From: Prathik Rao Date: Thu, 4 Jun 2026 10:45:24 -0700 Subject: [PATCH 01/15] update --- .../src/ep_detection/cuda_ep_bootstrapper.cc | 102 +++++++++++------- 1 file changed, 64 insertions(+), 38 deletions(-) diff --git a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc index 55ac8cd2a..3e3c80abc 100644 --- a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc +++ b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc @@ -2,19 +2,23 @@ // Licensed under the MIT License. #include "ep_detection/cuda_ep_bootstrapper.h" +#include "http/http_client.h" +#include "http/http_download.h" #include "logger.h" #include "util/file_lock.h" -#include "http/http_download.h" #include "util/sha256.h" #include "util/zip_extract.h" #include +#include #include +#include #include #include #include #include +#include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -28,43 +32,60 @@ constexpr const char* kLockFileName = "cuda-ep.lock"; constexpr const char* kUserAgent = "FoundryLocal"; constexpr int kMaxInstallAttempts = 5; -// CUDA EP package is built against the ONNX Runtime version we link against, so -// WinML and non-WinML builds need separate downloads. Hashes mirror the C# core -// (see neutron.main/src/Service/Providers/Detector/CudaEpBootstrapper.cs). -// WinML build -> ORT 1.23.2 (cuda-ep-20260501-182408.zip) -// Non-WinML -> ORT 1.25.1 (cuda-ep-20260501-062935.zip) -#if defined(FOUNDRY_LOCAL_USE_WINML) && FOUNDRY_LOCAL_USE_WINML -constexpr const char* kDownloadUrl = - "https://foundrypackages-ffhrdhbxb7gpdreh.b02.azurefd.net/cuda-ep-20260501-182408.zip"; +// Manifest URL on the CDN — published by the CUDA EP upload pipeline. +constexpr const char* kManifestUrl = + "https://foundrypackages-ffhrdhbxb7gpdreh.b02.azurefd.net/cuda_ep_prod.json"; + +// Platform key used to look up the current platform in the manifest. +#if defined(_WIN32) +constexpr const char* kPlatformKey = "win-x64"; +constexpr const char* kCudaProviderLib = "onnxruntime_providers_cuda.dll"; #else -constexpr const char* kDownloadUrl = - "https://foundrypackages-ffhrdhbxb7gpdreh.b02.azurefd.net/cuda-ep-20260501-062935.zip"; +constexpr const char* kPlatformKey = "linux-x64"; +constexpr const char* kCudaProviderLib = "libonnxruntime_providers_cuda.so"; #endif -struct ExpectedBinary { - const char* filename; - const char* sha256; -}; +constexpr const char* kRegistrationName = "Foundry.CUDA"; -#if defined(FOUNDRY_LOCAL_USE_WINML) && FOUNDRY_LOCAL_USE_WINML -constexpr ExpectedBinary kExpectedBinaries[] = { - {"onnxruntime_providers_cuda.dll", "4CEF18654878CEFCFCF8488E9C3A705EB5327AA9B5556155C319C9CBB2D98FCF"}, - {"onnxruntime-genai-cuda.dll", "BC953F8E2AAFC6219B2D723B65AB8F1A9426A6B7724D6A01ED756FAE8C3DE6AE"}, +struct ManifestInfo { + std::string version; + std::string download_url; + std::unordered_map sha256; // filename -> expected hash }; -#else -constexpr ExpectedBinary kExpectedBinaries[] = { - {"onnxruntime_providers_cuda.dll", "DD540FCFECFBC68B4675C9ADF09C2858CF6B054563859D79598AA2524406A76F"}, - {"onnxruntime-genai-cuda.dll", "BC953F8E2AAFC6219B2D723B65AB8F1A9426A6B7724D6A01ED756FAE8C3DE6AE"}, -}; -#endif -constexpr const char* kRegistrationName = "Foundry.CUDA"; -constexpr const char* kCudaProviderDll = "onnxruntime_providers_cuda.dll"; +/// Fetch and parse the CUDA EP manifest from the CDN. +ManifestInfo FetchManifest(fl::ILogger& logger) { + logger.Log(fl::LogLevel::Information, + fmt::format("CUDA EP: fetching manifest from {}", kManifestUrl)); + + auto body = fl::http::HttpGetWithRetry(kManifestUrl, kUserAgent, logger); + auto j = nlohmann::json::parse(body); + + ManifestInfo info; + info.version = j.at("version").get(); + + auto& packages = j.at("packages"); + if (!packages.contains(kPlatformKey)) { + throw std::runtime_error( + fmt::format("CUDA EP manifest has no entry for platform '{}'", kPlatformKey)); + } + + auto& pkg = packages.at(kPlatformKey); + info.download_url = pkg.at("url").get(); + + for (auto& [filename, hash] : pkg.at("sha256").items()) { + info.sha256[filename] = hash.get(); + } + + return info; +} /// Verify all expected binaries exist and have correct SHA256 hashes. -bool VerifyPackage(const std::filesystem::path& dir, fl::ILogger& logger) { - for (const auto& expected : kExpectedBinaries) { - auto file_path = dir / expected.filename; +bool VerifyPackage(const std::filesystem::path& dir, + const std::unordered_map& expected_hashes, + fl::ILogger& logger) { + for (const auto& [filename, expected_hash] : expected_hashes) { + auto file_path = dir / filename; if (!std::filesystem::exists(file_path)) { return false; @@ -73,12 +94,11 @@ bool VerifyPackage(const std::filesystem::path& dir, fl::ILogger& logger) { auto hash = fl::Sha256File(file_path); // Case-insensitive comparison - std::string expected_hash(expected.sha256); if (!std::equal(hash.begin(), hash.end(), expected_hash.begin(), expected_hash.end(), [](char a, char b) { return std::toupper(a) == std::toupper(b); })) { logger.Log(fl::LogLevel::Warning, fmt::format("CUDA EP: hash mismatch for {}: got {}, expected {}", - expected.filename, hash, expected.sha256)); + filename, hash, expected_hash)); return false; } } @@ -123,11 +143,16 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, auto zip_path = ep_dir.parent_path() / kPackageFileName; try { + // Fetch the manifest to get the download URL and expected hashes + auto manifest = FetchManifest(logger); + logger.Log(LogLevel::Information, + fmt::format("CUDA EP: manifest version={}, url={}", manifest.version, manifest.download_url)); + // Cross-process lock to prevent concurrent installs FileLock lock(lock_path); // Check if package already exists and is valid - if (VerifyPackage(ep_dir, logger)) { + if (VerifyPackage(ep_dir, manifest.sha256, logger)) { logger.Log(LogLevel::Information, "CUDA EP: package already valid, skipping download"); } else { // Clean up any partial install @@ -138,7 +163,8 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, std::filesystem::create_directories(ep_dir); // Download - logger.Log(LogLevel::Information, "CUDA EP: downloading from CDN..."); + logger.Log(LogLevel::Information, + fmt::format("CUDA EP: downloading from {}", manifest.download_url)); // Bridge callback-based cancellation to the atomic flag HttpDownloadFile expects std::atomic cancel_flag{false}; @@ -152,7 +178,7 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, } }; - if (!HttpDownloadFile(kDownloadUrl, zip_path, kUserAgent, + if (!HttpDownloadFile(manifest.download_url, zip_path, kUserAgent, &cancel_flag, download_progress, logger)) { logger.Log(LogLevel::Warning, "CUDA EP: download failed (see prior log for details)"); return false; @@ -170,7 +196,7 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, std::filesystem::remove(zip_path); // Verify - if (!VerifyPackage(ep_dir, logger)) { + if (!VerifyPackage(ep_dir, manifest.sha256, logger)) { logger.Log(LogLevel::Warning, "CUDA EP: verification failed after download"); return false; } @@ -202,9 +228,9 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, } #endif - auto cuda_dll_path = ep_dir / kCudaProviderDll; + auto cuda_lib_path = ep_dir / kCudaProviderLib; - if (!register_ep_(kRegistrationName, cuda_dll_path)) { + if (!register_ep_(kRegistrationName, cuda_lib_path)) { logger.Log(LogLevel::Warning, "CUDA EP: ORT registration failed"); return false; } From 0a09b10573f3098e136fcd38499e245794a42ca2 Mon Sep 17 00:00:00 2001 From: Prathik Rao Date: Mon, 8 Jun 2026 16:38:11 -0700 Subject: [PATCH 02/15] fixes --- .../src/ep_detection/cuda_ep_bootstrapper.cc | 135 ++++++++++++------ 1 file changed, 94 insertions(+), 41 deletions(-) diff --git a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc index 3e3c80abc..6e0766bf8 100644 --- a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc +++ b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include #include @@ -29,6 +32,7 @@ namespace { constexpr const char* kPackageFileName = "cuda-ep.zip"; constexpr const char* kLockFileName = "cuda-ep.lock"; +constexpr const char* kStagingDirName = "cuda-ep-staging"; constexpr const char* kUserAgent = "FoundryLocal"; constexpr int kMaxInstallAttempts = 5; @@ -36,14 +40,42 @@ constexpr int kMaxInstallAttempts = 5; constexpr const char* kManifestUrl = "https://foundrypackages-ffhrdhbxb7gpdreh.b02.azurefd.net/cuda_ep_prod.json"; -// Platform key used to look up the current platform in the manifest. -#if defined(_WIN32) -constexpr const char* kPlatformKey = "win-x64"; -constexpr const char* kCudaProviderLib = "onnxruntime_providers_cuda.dll"; +// ----------------------------------------------------------------------- +// Platform detection +// +// Returns the manifest platform key and ORT registration library filename +// for the current build target, or std::nullopt if unsupported. +// +// To add a platform: +// 1. Uncomment its #elif block below. +// 2. Uncomment its entry in $binaryNames / $expectedPlatforms in +// cuda-ep-upload.yml and update $platformPattern there too. +// ----------------------------------------------------------------------- +struct PlatformInfo { + const char* key; // manifest lookup key, e.g. "win-x64" + const char* ep_lib; // ORT registration library filename +}; + +std::optional GetPlatformInfo() { +#if defined(_WIN32) && !defined(_M_ARM64) + return PlatformInfo{"win-x64", "onnxruntime_providers_cuda.dll"}; + +// Uncomment when win-arm64 CUDA EP build is available (see cuda-ep-upload.yml): +// #elif defined(_WIN32) && defined(_M_ARM64) +// return PlatformInfo{"win-arm64", "onnxruntime_providers_cuda.dll"}; + +// Uncomment when linux-x64 CUDA EP build is available (see cuda-ep-upload.yml): +// #elif defined(__linux__) && defined(__x86_64__) +// return PlatformInfo{"linux-x64", "libonnxruntime_providers_cuda.so"}; + +// Uncomment when linux-arm64 CUDA EP build is available (see cuda-ep-upload.yml): +// #elif defined(__linux__) && defined(__aarch64__) +// return PlatformInfo{"linux-arm64", "libonnxruntime_providers_cuda.so"}; + #else -constexpr const char* kPlatformKey = "linux-x64"; -constexpr const char* kCudaProviderLib = "libonnxruntime_providers_cuda.so"; + return std::nullopt; // Platform not yet supported — graceful no-op. #endif +} constexpr const char* kRegistrationName = "Foundry.CUDA"; @@ -54,8 +86,9 @@ struct ManifestInfo { }; /// Fetch and parse the CUDA EP manifest from the CDN. -ManifestInfo FetchManifest(fl::ILogger& logger) { - logger.Log(fl::LogLevel::Information, +/// Returns the package entry for the given platform key. +ManifestInfo FetchManifest(const char* platform_key, fl::ILogger& logger) { + logger.Log(fl::LogLevel::Debug, fmt::format("CUDA EP: fetching manifest from {}", kManifestUrl)); auto body = fl::http::HttpGetWithRetry(kManifestUrl, kUserAgent, logger); @@ -65,12 +98,12 @@ ManifestInfo FetchManifest(fl::ILogger& logger) { info.version = j.at("version").get(); auto& packages = j.at("packages"); - if (!packages.contains(kPlatformKey)) { + if (!packages.contains(platform_key)) { throw std::runtime_error( - fmt::format("CUDA EP manifest has no entry for platform '{}'", kPlatformKey)); + fmt::format("CUDA EP manifest has no entry for platform '{}'", platform_key)); } - auto& pkg = packages.at(kPlatformKey); + auto& pkg = packages.at(platform_key); info.download_url = pkg.at("url").get(); for (auto& [filename, hash] : pkg.at("sha256").items()) { @@ -138,40 +171,49 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, attempts_++; + // Bail out early if this platform is not yet in the manifest. + auto platform_info = GetPlatformInfo(); + if (!platform_info) { + logger.Log(LogLevel::Information, "CUDA EP: current platform is not yet supported"); + return false; + } + auto ep_dir = std::filesystem::path(ep_dir_); - auto lock_path = ep_dir.parent_path() / kLockFileName; - auto zip_path = ep_dir.parent_path() / kPackageFileName; + auto parent_dir = ep_dir.parent_path(); try { - // Fetch the manifest to get the download URL and expected hashes - auto manifest = FetchManifest(logger); + // Fetch the manifest before acquiring the lock to avoid holding it during network I/O. + auto manifest = FetchManifest(platform_info->key, logger); logger.Log(LogLevel::Information, - fmt::format("CUDA EP: manifest version={}, url={}", manifest.version, manifest.download_url)); + fmt::format("CUDA EP: manifest fetched (version={}, platform={})", + manifest.version, platform_info->key)); - // Cross-process lock to prevent concurrent installs - FileLock lock(lock_path); + // Cross-process lock to prevent concurrent installs. + std::filesystem::create_directories(parent_dir); + FileLock lock(parent_dir / kLockFileName); - // Check if package already exists and is valid - if (VerifyPackage(ep_dir, manifest.sha256, logger)) { + // Re-check after acquiring the lock — another process may have already updated. + if (!force && VerifyPackage(ep_dir, manifest.sha256, logger)) { logger.Log(LogLevel::Information, "CUDA EP: package already valid, skipping download"); } else { - // Clean up any partial install - if (std::filesystem::exists(ep_dir)) { - std::filesystem::remove_all(ep_dir); + // Download to a staging directory so a failure never corrupts the existing install. + auto staging_dir = parent_dir / kStagingDirName; + if (std::filesystem::exists(staging_dir)) { + std::filesystem::remove_all(staging_dir); } + std::filesystem::create_directories(staging_dir); - std::filesystem::create_directories(ep_dir); + auto zip_path = staging_dir / kPackageFileName; - // Download logger.Log(LogLevel::Information, - fmt::format("CUDA EP: downloading from {}", manifest.download_url)); + fmt::format("CUDA EP: downloading for {}...", platform_info->key)); + logger.Log(LogLevel::Debug, + fmt::format("CUDA EP: download URL is {}", manifest.download_url)); - // Bridge callback-based cancellation to the atomic flag HttpDownloadFile expects std::atomic cancel_flag{false}; - auto download_progress = [&](float pct) { if (progress_cb) { - // 0-80% for download phase + // 0–80% for the download phase. if (!progress_cb(name_, pct * 0.8f)) { cancel_flag.store(true); } @@ -181,32 +223,44 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, if (!HttpDownloadFile(manifest.download_url, zip_path, kUserAgent, &cancel_flag, download_progress, logger)) { logger.Log(LogLevel::Warning, "CUDA EP: download failed (see prior log for details)"); + std::filesystem::remove_all(staging_dir); return false; } - // Extract - logger.Log(LogLevel::Information, "CUDA EP: extracting..."); + logger.Log(LogLevel::Information, + fmt::format("CUDA EP: extracting package to {}", staging_dir.string())); - if (!ExtractZip(zip_path, ep_dir, logger)) { + if (!ExtractZip(zip_path, staging_dir, logger)) { logger.Log(LogLevel::Warning, "CUDA EP: extraction failed"); + std::filesystem::remove_all(staging_dir); return false; } - // Clean up zip std::filesystem::remove(zip_path); - // Verify - if (!VerifyPackage(ep_dir, manifest.sha256, logger)) { - logger.Log(LogLevel::Warning, "CUDA EP: verification failed after download"); + if (!VerifyPackage(staging_dir, manifest.sha256, logger)) { + logger.Log(LogLevel::Warning, "CUDA EP: verification failed after extraction"); + std::filesystem::remove_all(staging_dir); return false; } + + logger.Log(LogLevel::Debug, + fmt::format("CUDA EP: staging verification succeeded, promoting to {}", + ep_dir.string())); + + // Atomic swap: delete old install, rename staging to target. + if (std::filesystem::exists(ep_dir)) { + std::filesystem::remove_all(ep_dir); + } + std::filesystem::rename(staging_dir, ep_dir); + logger.Log(LogLevel::Information, "CUDA EP: successfully installed."); } if (progress_cb) { progress_cb(name_, 90.0f); } - // Register with ORT + // Register with ORT. #ifdef _WIN32 // Permanently prepend the EP directory to PATH. The zip bundles all // required CUDA/cuDNN DLLs, so no system CUDA install is needed. @@ -228,7 +282,7 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, } #endif - auto cuda_lib_path = ep_dir / kCudaProviderLib; + auto cuda_lib_path = ep_dir / platform_info->ep_lib; if (!register_ep_(kRegistrationName, cuda_lib_path)) { logger.Log(LogLevel::Warning, "CUDA EP: ORT registration failed"); @@ -241,10 +295,9 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, progress_cb(name_, 100.0f); } - // Bootstrapper-side log — captures the install dir, which the central - // register_ep callback (logs library + version) doesn't have. logger.Log(LogLevel::Information, - fmt::format("CUDA EP: ready (install_path={})", ep_dir.string())); + fmt::format("CUDA EP: ready (install_path={}, version={})", + ep_dir.string(), manifest.version)); return true; } catch (const std::exception& e) { logger.Log(LogLevel::Warning, fmt::format("CUDA EP: error: {}", e.what())); From 4b9159a217696f5fe0f90dc133c536a8497a8388 Mon Sep 17 00:00:00 2001 From: Prathik Rao Date: Tue, 9 Jun 2026 16:26:42 -0700 Subject: [PATCH 03/15] naming --- .../src/ep_detection/cuda_ep_bootstrapper.cc | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc index 6e0766bf8..bfece0efa 100644 --- a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc +++ b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc @@ -58,19 +58,19 @@ struct PlatformInfo { std::optional GetPlatformInfo() { #if defined(_WIN32) && !defined(_M_ARM64) - return PlatformInfo{"win-x64", "onnxruntime_providers_cuda.dll"}; + return PlatformInfo{"win-x64", "onnxruntime_providers_cuda_plugin.dll"}; // Uncomment when win-arm64 CUDA EP build is available (see cuda-ep-upload.yml): // #elif defined(_WIN32) && defined(_M_ARM64) -// return PlatformInfo{"win-arm64", "onnxruntime_providers_cuda.dll"}; +// return PlatformInfo{"win-arm64", "onnxruntime_providers_cuda_plugin.dll"}; // Uncomment when linux-x64 CUDA EP build is available (see cuda-ep-upload.yml): // #elif defined(__linux__) && defined(__x86_64__) -// return PlatformInfo{"linux-x64", "libonnxruntime_providers_cuda.so"}; +// return PlatformInfo{"linux-x64", "libonnxruntime_providers_cuda_plugin.so"}; // Uncomment when linux-arm64 CUDA EP build is available (see cuda-ep-upload.yml): // #elif defined(__linux__) && defined(__aarch64__) -// return PlatformInfo{"linux-arm64", "libonnxruntime_providers_cuda.so"}; +// return PlatformInfo{"linux-arm64", "libonnxruntime_providers_cuda_plugin.so"}; #else return std::nullopt; // Platform not yet supported — graceful no-op. @@ -261,11 +261,18 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, } // Register with ORT. + // NOTE: RegisterExecutionProviderLibrary loads the CUDA plugin DLL, which + // initializes the CUDA runtime and cuDNN. This can take 30–60 seconds on + // first use — especially on machines with large cuDNN caches or slow VRAM + // init. This is normal; it is NOT a hang in the bootstrapper itself. + logger.Log(LogLevel::Information, + fmt::format("CUDA EP: registering provider library {} (CUDA init may take ~30s)...", + cuda_lib_path.string())); #ifdef _WIN32 // Permanently prepend the EP directory to PATH. The zip bundles all // required CUDA/cuDNN DLLs, so no system CUDA install is needed. // PATH must stay modified for the process lifetime because: - // - onnxruntime_providers_cuda.dll delay-loads some dependencies + // - onnxruntime_providers_cuda_plugin.dll delay-loads CUDA dependencies // - onnxruntime-genai-cuda.dll is loaded later at model-load time // - ORT creates CUDA sessions after registration { From c739199161c1fc3b7b90a7dc4f0781e0be8b56cd Mon Sep 17 00:00:00 2001 From: Prathik Rao Date: Tue, 9 Jun 2026 16:29:17 -0700 Subject: [PATCH 04/15] log --- .../cpp/src/ep_detection/cuda_ep_bootstrapper.cc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc index bfece0efa..19db151bb 100644 --- a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc +++ b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc @@ -261,13 +261,6 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, } // Register with ORT. - // NOTE: RegisterExecutionProviderLibrary loads the CUDA plugin DLL, which - // initializes the CUDA runtime and cuDNN. This can take 30–60 seconds on - // first use — especially on machines with large cuDNN caches or slow VRAM - // init. This is normal; it is NOT a hang in the bootstrapper itself. - logger.Log(LogLevel::Information, - fmt::format("CUDA EP: registering provider library {} (CUDA init may take ~30s)...", - cuda_lib_path.string())); #ifdef _WIN32 // Permanently prepend the EP directory to PATH. The zip bundles all // required CUDA/cuDNN DLLs, so no system CUDA install is needed. @@ -291,6 +284,14 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, auto cuda_lib_path = ep_dir / platform_info->ep_lib; + // NOTE: RegisterExecutionProviderLibrary loads the CUDA plugin DLL, which + // initializes the CUDA runtime and cuDNN. This can take 30–60 seconds on + // first use — especially on machines with large cuDNN caches or slow VRAM + // init. This is normal; it is NOT a hang in the bootstrapper itself. + logger.Log(LogLevel::Information, + fmt::format("CUDA EP: registering provider library {} (CUDA init may take ~30s)...", + cuda_lib_path.string())); + if (!register_ep_(kRegistrationName, cuda_lib_path)) { logger.Log(LogLevel::Warning, "CUDA EP: ORT registration failed"); return false; From 1acf3de38fbce8ffffb724586805fc3f00d20812 Mon Sep 17 00:00:00 2001 From: Prathik Rao Date: Wed, 10 Jun 2026 10:36:45 -0700 Subject: [PATCH 05/15] logs --- sdk_v2/cpp/src/spdlog_logger.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sdk_v2/cpp/src/spdlog_logger.cc b/sdk_v2/cpp/src/spdlog_logger.cc index c49ff3549..cf9c0574d 100644 --- a/sdk_v2/cpp/src/spdlog_logger.cc +++ b/sdk_v2/cpp/src/spdlog_logger.cc @@ -73,7 +73,11 @@ SpdlogLogger::SpdlogLogger(LogLevel min_level, const std::string& logs_dir) { spdlog::async_overflow_policy::block); logger_->set_level(ToSpdlogLevel(min_level)); - logger_->flush_on(spdlog::level::warn); + // Flush immediately at the configured level so every message that passes the + // level filter is guaranteed to reach the file sink. Without this, the async + // queue only flushes on warn+, causing debug/info messages to be lost if the + // process exits before the next warning. + logger_->flush_on(ToSpdlogLevel(min_level)); spdlog::register_logger(logger_); } From 14d894b71f75a8fa56ee07d0c9a2ecde3f299954 Mon Sep 17 00:00:00 2001 From: Prathik Rao Date: Wed, 10 Jun 2026 11:20:18 -0700 Subject: [PATCH 06/15] logs --- sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc index 19db151bb..d886efab9 100644 --- a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc +++ b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc @@ -114,13 +114,23 @@ ManifestInfo FetchManifest(const char* platform_key, fl::ILogger& logger) { } /// Verify all expected binaries exist and have correct SHA256 hashes. +/// Logs the name of the first missing or mismatched file to aid diagnosis. bool VerifyPackage(const std::filesystem::path& dir, const std::unordered_map& expected_hashes, fl::ILogger& logger) { + // Quick sentinel check before the expensive SHA256 work. + if (!std::filesystem::exists(dir)) { + logger.Log(fl::LogLevel::Debug, + fmt::format("CUDA EP: package directory does not exist: {}", dir.string())); + return false; + } + for (const auto& [filename, expected_hash] : expected_hashes) { auto file_path = dir / filename; if (!std::filesystem::exists(file_path)) { + logger.Log(fl::LogLevel::Debug, + fmt::format("CUDA EP: package file missing: {}", file_path.string())); return false; } From 78eb082846f98bf576d68671cc26a2fa57abbac1 Mon Sep 17 00:00:00 2001 From: Prathik Rao Date: Wed, 10 Jun 2026 11:32:17 -0700 Subject: [PATCH 07/15] add mapping --- sdk_v2/cpp/src/catalog/static_catalog_client.cc | 8 ++++++++ sdk_v2/cpp/src/inferencing/model_load_manager.cc | 16 ++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/sdk_v2/cpp/src/catalog/static_catalog_client.cc b/sdk_v2/cpp/src/catalog/static_catalog_client.cc index af9ebb5f6..82f8a6aa0 100644 --- a/sdk_v2/cpp/src/catalog/static_catalog_client.cc +++ b/sdk_v2/cpp/src/catalog/static_catalog_client.cc @@ -51,6 +51,14 @@ class StaticCatalogClient : public ICatalogClient { for (const auto& [device, eps] : devices_to_eps) { for (const auto& ep : eps) { allowed.emplace(to_lower(device), to_lower(ep)); + + // CudaPluginExecutionProvider is the ORT registration name for the + // downloadable CUDA plugin EP, but catalog models are tagged with + // CudaExecutionProvider. Add the canonical name as an alias so + // plugin-EP machines can see and load CUDA catalog models. + if (to_lower(ep) == "cudapluginexecutionprovider") { + allowed.emplace(to_lower(device), "cudaexecutionprovider"); + } } } diff --git a/sdk_v2/cpp/src/inferencing/model_load_manager.cc b/sdk_v2/cpp/src/inferencing/model_load_manager.cc index 0bc321b9b..4b25ce9d5 100644 --- a/sdk_v2/cpp/src/inferencing/model_load_manager.cc +++ b/sdk_v2/cpp/src/inferencing/model_load_manager.cc @@ -36,6 +36,16 @@ constexpr ModelIdEpRequirement kModelIdEpRequirements[] = { {"vitis-npu", "VitisAIExecutionProvider"}, }; +/// Returns true if the registered EP name satisfies the catalog requirement. +/// CudaPluginExecutionProvider is treated as equivalent to CUDAExecutionProvider +/// because catalog models are tagged with the canonical name, not the plugin name. +bool EpSatisfiesRequirement(std::string_view registered_ep, std::string_view required_ep) { + if (registered_ep == required_ep) return true; + if (required_ep == "CUDAExecutionProvider" && registered_ep == "CudaPluginExecutionProvider") + return true; + return false; +} + /// Returns the required EP registration name for a model_id, or empty if none required. std::string_view RequiredEpForModelId(std::string_view model_id) { for (const auto& req : kModelIdEpRequirements) { @@ -65,8 +75,10 @@ ModelLoadManager::~ModelLoadManager() { bool ModelLoadManager::HasEP(const std::string& ep_name) const { const auto& device_map = ep_detector_.GetAvailableDevicesToEPs(); for (const auto& [device, eps] : device_map) { - if (std::find(eps.begin(), eps.end(), ep_name) != eps.end()) { - return true; + for (const auto& registered : eps) { + if (EpSatisfiesRequirement(registered, ep_name)) { + return true; + } } } From b0b6460bbb33ad04a837ba32eb8c7e16964f866f Mon Sep 17 00:00:00 2001 From: Prathik Rao Date: Wed, 10 Jun 2026 11:47:07 -0700 Subject: [PATCH 08/15] rev --- sdk_v2/cpp/src/spdlog_logger.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sdk_v2/cpp/src/spdlog_logger.cc b/sdk_v2/cpp/src/spdlog_logger.cc index cf9c0574d..c49ff3549 100644 --- a/sdk_v2/cpp/src/spdlog_logger.cc +++ b/sdk_v2/cpp/src/spdlog_logger.cc @@ -73,11 +73,7 @@ SpdlogLogger::SpdlogLogger(LogLevel min_level, const std::string& logs_dir) { spdlog::async_overflow_policy::block); logger_->set_level(ToSpdlogLevel(min_level)); - // Flush immediately at the configured level so every message that passes the - // level filter is guaranteed to reach the file sink. Without this, the async - // queue only flushes on warn+, causing debug/info messages to be lost if the - // process exits before the next warning. - logger_->flush_on(ToSpdlogLevel(min_level)); + logger_->flush_on(spdlog::level::warn); spdlog::register_logger(logger_); } From faea292db5f2fae0ce18e40068e345c1d4a5e0c8 Mon Sep 17 00:00:00 2001 From: Prathik Rao Date: Wed, 10 Jun 2026 15:30:33 -0700 Subject: [PATCH 09/15] force false --- sdk_v2/cpp/src/ep_detection/ep_detector.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk_v2/cpp/src/ep_detection/ep_detector.cc b/sdk_v2/cpp/src/ep_detection/ep_detector.cc index e086c0b48..8055b1316 100644 --- a/sdk_v2/cpp/src/ep_detection/ep_detector.cc +++ b/sdk_v2/cpp/src/ep_detection/ep_detector.cc @@ -170,7 +170,7 @@ EpDownloadResult EpDetector::DownloadAndRegisterEps(const std::vectorName()); - if (bs->DownloadAndRegister(/*force=*/true, wrapped_cb, logger_)) { + if (bs->DownloadAndRegister(/*force=*/false, wrapped_cb, logger_)) { result.registered_eps.push_back(bs->Name()); // Update cached registration state in place under the cache lock so From 6c63851c1bc281f255f66c23abfb65ccb247a120 Mon Sep 17 00:00:00 2001 From: Prathik Rao Date: Wed, 10 Jun 2026 16:03:48 -0700 Subject: [PATCH 10/15] copilot Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- sdk_v2/cpp/src/catalog/static_catalog_client.cc | 2 +- sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sdk_v2/cpp/src/catalog/static_catalog_client.cc b/sdk_v2/cpp/src/catalog/static_catalog_client.cc index 82f8a6aa0..de722a06d 100644 --- a/sdk_v2/cpp/src/catalog/static_catalog_client.cc +++ b/sdk_v2/cpp/src/catalog/static_catalog_client.cc @@ -54,7 +54,7 @@ class StaticCatalogClient : public ICatalogClient { // CudaPluginExecutionProvider is the ORT registration name for the // downloadable CUDA plugin EP, but catalog models are tagged with - // CudaExecutionProvider. Add the canonical name as an alias so + // CUDAExecutionProvider. Add the canonical name as an alias so // plugin-EP machines can see and load CUDA catalog models. if (to_lower(ep) == "cudapluginexecutionprovider") { allowed.emplace(to_lower(device), "cudaexecutionprovider"); diff --git a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc index d886efab9..85cb23307 100644 --- a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc +++ b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc @@ -125,6 +125,11 @@ bool VerifyPackage(const std::filesystem::path& dir, return false; } + if (expected_hashes.empty()) { + logger.Log(fl::LogLevel::Warning, "CUDA EP: manifest contains no expected SHA256 hashes"); + return false; + } + for (const auto& [filename, expected_hash] : expected_hashes) { auto file_path = dir / filename; From fc3d3cda433c7984db1b7a5d13e3c3af3f1ba0ac Mon Sep 17 00:00:00 2001 From: Prathik Rao Date: Wed, 10 Jun 2026 16:11:47 -0700 Subject: [PATCH 11/15] copilot --- .../cpp/src/ep_detection/cuda_ep_bootstrapper.cc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc index 85cb23307..e961ad487 100644 --- a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc +++ b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc @@ -141,8 +141,18 @@ bool VerifyPackage(const std::filesystem::path& dir, auto hash = fl::Sha256File(file_path); - // Case-insensitive comparison - if (!std::equal(hash.begin(), hash.end(), expected_hash.begin(), expected_hash.end(), + // Explicit length guard before comparison — expected_hash comes from a CDN + // manifest (untrusted), so a malformed/truncated hash must be rejected clearly + // rather than relying on the 4-iterator std::equal form to return false quietly. + if (hash.size() != expected_hash.size()) { + logger.Log(fl::LogLevel::Warning, + fmt::format("CUDA EP: hash length mismatch for {} (got {} chars, expected {})", + filename, hash.size(), expected_hash.size())); + return false; + } + + // Case-insensitive comparison (lengths are equal at this point) + if (!std::equal(hash.begin(), hash.end(), expected_hash.begin(), [](char a, char b) { return std::toupper(a) == std::toupper(b); })) { logger.Log(fl::LogLevel::Warning, fmt::format("CUDA EP: hash mismatch for {}: got {}, expected {}", From 3ce171e33e9ce9a6e9a323e7364ac28d79bfb6c1 Mon Sep 17 00:00:00 2001 From: Prathik Rao Date: Thu, 11 Jun 2026 11:49:17 -0700 Subject: [PATCH 12/15] new arch --- .../src/ep_detection/cuda_ep_bootstrapper.cc | 202 ++++++++++++------ sdk_v2/cpp/src/ep_detection/ep_utils.cc | 8 +- sdk_v2/cpp/src/ep_detection/ep_utils.h | 10 +- .../ep_detection/webgpu_ep_bootstrapper.cc | 9 +- 4 files changed, 159 insertions(+), 70 deletions(-) diff --git a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc index 07882ba29..7a8820aef 100644 --- a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc +++ b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc @@ -3,9 +3,10 @@ #include "ep_detection/cuda_ep_bootstrapper.h" #include "ep_detection/ep_utils.h" +#include "http/http_client.h" +#include "http/http_download.h" #include "logger.h" #include "util/file_lock.h" -#include "http/http_download.h" #include "util/zip_extract.h" #include @@ -29,7 +30,8 @@ namespace { -constexpr const char* kPackageFileName = "cuda-ep.zip"; +constexpr const char* kOrtPackageFileName = "cuda-ep-ort.zip"; +constexpr const char* kCudaDepsPackageFileName = "cuda-ep-cuda-deps.zip"; constexpr const char* kLockFileName = "cuda-ep.lock"; constexpr const char* kStagingDirName = "cuda-ep-staging"; constexpr const char* kUserAgent = "FoundryLocal"; @@ -37,7 +39,7 @@ constexpr int kMaxInstallAttempts = 5; // Manifest URL on the CDN — published by the CUDA EP upload pipeline. constexpr const char* kManifestUrl = - "https://foundrypackages-ffhrdhbxb7gpdreh.b02.azurefd.net/cuda_ep_prod.json"; + "https://foundrypackages-ffhrdhbxb7gpdreh.b02.azurefd.net/cuda_ep_dev.json"; // ----------------------------------------------------------------------- // Platform detection @@ -79,9 +81,77 @@ std::optional GetPlatformInfo() { constexpr const char* kRegistrationName = "Foundry.CUDA"; struct ManifestInfo { - std::string version; - std::string download_url; - std::unordered_map sha256; // filename -> expected hash + struct PackageInfo { + std::string download_url; + std::unordered_map sha256; // filename -> expected hash + }; + + std::string ort_version; + std::string cuda_deps_version; + PackageInfo ort; + PackageInfo cuda_deps; +}; + +ManifestInfo::PackageInfo ParsePackage(const nlohmann::json& package_json, + const char* package_name) { + ManifestInfo::PackageInfo info; + + info.download_url = package_json.at("url").get(); + auto& sha256 = package_json.at("sha256"); + if (!sha256.is_object() || sha256.empty()) { + throw std::runtime_error( + fmt::format("CUDA EP manifest '{}' entry has invalid/empty 'sha256'", package_name)); + } + + for (auto& [filename, hash] : sha256.items()) { + info.sha256[filename] = hash.get(); + } + + return info; +} + +bool DownloadAndExtractPackage(const ManifestInfo::PackageInfo& package, + const std::filesystem::path& staging_dir, + const std::filesystem::path& zip_path, + const std::string& package_name, + const std::string& ep_name, + const fl::ProgressCallback& progress_cb, + float progress_base, + float progress_span, + fl::ILogger& logger) { + logger.Log(fl::LogLevel::Information, + fmt::format("CUDA EP: downloading {} package...", package_name)); + logger.Log(fl::LogLevel::Debug, + fmt::format("CUDA EP: {} download URL is {}", package_name, package.download_url)); + + std::atomic cancel_flag{false}; + auto download_progress = [&](float pct) { + if (progress_cb) { + if (!progress_cb(ep_name, progress_base + (pct * progress_span))) { + cancel_flag.store(true); + } + } + }; + + if (!HttpDownloadFile(package.download_url, zip_path, kUserAgent, + &cancel_flag, download_progress, logger)) { + logger.Log(fl::LogLevel::Warning, + fmt::format("CUDA EP: {} package download failed", package_name)); + return false; + } + + logger.Log(fl::LogLevel::Information, + fmt::format("CUDA EP: extracting {} package to {}", + package_name, staging_dir.string())); + + if (!ExtractZip(zip_path, staging_dir, logger)) { + logger.Log(fl::LogLevel::Warning, + fmt::format("CUDA EP: {} package extraction failed", package_name)); + return false; + } + + std::filesystem::remove(zip_path); + return true; }; /// Fetch and parse the CUDA EP manifest from the CDN. @@ -94,7 +164,8 @@ ManifestInfo FetchManifest(const char* platform_key, fl::ILogger& logger) { auto j = nlohmann::json::parse(body); ManifestInfo info; - info.version = j.at("version").get(); + info.ort_version = j.at("ort_version").get(); + info.cuda_deps_version = j.at("cuda_deps_version").get(); auto& packages = j.at("packages"); if (!packages.contains(platform_key)) { @@ -102,13 +173,16 @@ ManifestInfo FetchManifest(const char* platform_key, fl::ILogger& logger) { fmt::format("CUDA EP manifest has no entry for platform '{}'", platform_key)); } - auto& pkg = packages.at(platform_key); - info.download_url = pkg.at("url").get(); - - for (auto& [filename, hash] : pkg.at("sha256").items()) { - info.sha256[filename] = hash.get(); + auto& platform_package = packages.at(platform_key); + if (!platform_package.contains("ort") || !platform_package.contains("cuda_deps")) { + throw std::runtime_error( + fmt::format("CUDA EP manifest platform '{}' is missing 'ort' or 'cuda_deps'", + platform_key)); } + info.ort = ParsePackage(platform_package.at("ort"), "ort"); + info.cuda_deps = ParsePackage(platform_package.at("cuda_deps"), "cuda_deps"); + return info; } @@ -158,64 +232,66 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, // Fetch the manifest before acquiring the lock to avoid holding it during network I/O. auto manifest = FetchManifest(platform_info->key, logger); logger.Log(LogLevel::Information, - fmt::format("CUDA EP: manifest fetched (version={}, platform={})", - manifest.version, platform_info->key)); - - // Check if package already exists and is valid - if (fl::VerifyEpPackage(ep_dir, - {{kExpectedBinaries[0].filename, kExpectedBinaries[0].sha256}, - {kExpectedBinaries[1].filename, kExpectedBinaries[1].sha256}}, - "CUDA EP", logger)) { - logger.Log(LogLevel::Information, "CUDA EP: package already valid, skipping download"); + fmt::format("CUDA EP: manifest fetched (ort_version={}, cuda_deps_version={}, platform={})", + manifest.ort_version, manifest.cuda_deps_version, platform_info->key)); + + // Cross-process lock to prevent concurrent installs. + std::filesystem::create_directories(parent_dir); + FileLock lock(parent_dir / kLockFileName); + + bool needs_ort = force || !VerifyEpPackage(ep_dir, manifest.ort.sha256, "CUDA EP (ort)", logger); + bool needs_cuda_deps = force || !VerifyEpPackage(ep_dir, manifest.cuda_deps.sha256, "CUDA EP (cuda_deps)", logger); + + if (!needs_ort && !needs_cuda_deps) { + logger.Log(LogLevel::Information, + "CUDA EP: ORT and CUDA deps packages already valid, skipping download"); } else { - // Download to a staging directory so a failure never corrupts the existing install. + // Download only outdated package(s) into staging, then atomically swap. auto staging_dir = parent_dir / kStagingDirName; if (std::filesystem::exists(staging_dir)) { std::filesystem::remove_all(staging_dir); } - std::filesystem::create_directories(staging_dir); - auto zip_path = staging_dir / kPackageFileName; + if (std::filesystem::exists(ep_dir)) { + std::filesystem::copy(ep_dir, staging_dir, + std::filesystem::copy_options::recursive | + std::filesystem::copy_options::overwrite_existing); + } else { + std::filesystem::create_directories(staging_dir); + } - logger.Log(LogLevel::Information, - fmt::format("CUDA EP: downloading for {}...", platform_info->key)); - logger.Log(LogLevel::Debug, - fmt::format("CUDA EP: download URL is {}", manifest.download_url)); - - std::atomic cancel_flag{false}; - auto download_progress = [&](float pct) { - if (progress_cb) { - // 0–80% for the download phase. - if (!progress_cb(name_, pct * 0.8f)) { - cancel_flag.store(true); - } + const int download_count = (needs_ort ? 1 : 0) + (needs_cuda_deps ? 1 : 0); + float progress_base = 0.0f; + const float progress_span = download_count > 0 ? (80.0f / download_count) : 0.0f; + + if (needs_ort) { + auto ort_zip_path = staging_dir / kOrtPackageFileName; + if (!DownloadAndExtractPackage(manifest.ort, staging_dir, ort_zip_path, + "ort", name_, progress_cb, + progress_base, progress_span, logger)) { + std::filesystem::remove_all(staging_dir); + return false; } - }; - - if (!HttpDownloadFile(manifest.download_url, zip_path, kUserAgent, - &cancel_flag, download_progress, logger)) { - logger.Log(LogLevel::Warning, "CUDA EP: download failed (see prior log for details)"); - std::filesystem::remove_all(staging_dir); - return false; + progress_base += progress_span; } - logger.Log(LogLevel::Information, - fmt::format("CUDA EP: extracting package to {}", staging_dir.string())); - - if (!ExtractZip(zip_path, staging_dir, logger)) { - logger.Log(LogLevel::Warning, "CUDA EP: extraction failed"); - std::filesystem::remove_all(staging_dir); - return false; + if (needs_cuda_deps) { + auto cuda_deps_zip_path = staging_dir / kCudaDepsPackageFileName; + if (!DownloadAndExtractPackage(manifest.cuda_deps, staging_dir, cuda_deps_zip_path, + "cuda_deps", name_, progress_cb, + progress_base, progress_span, logger)) { + std::filesystem::remove_all(staging_dir); + return false; + } + progress_base += progress_span; } - std::filesystem::remove(zip_path); - - // Verify - if (!fl::VerifyEpPackage(ep_dir, - {{kExpectedBinaries[0].filename, kExpectedBinaries[0].sha256}, - {kExpectedBinaries[1].filename, kExpectedBinaries[1].sha256}}, - "CUDA EP", logger)) { - logger.Log(LogLevel::Warning, "CUDA EP: verification failed after download"); + // Verify both package subsets in staging before promotion. + if (!VerifyEpPackage(staging_dir, manifest.ort.sha256, "CUDA EP (ort)", logger) || + !VerifyEpPackage(staging_dir, manifest.cuda_deps.sha256, "CUDA EP (cuda_deps)", logger)) { + logger.Log(LogLevel::Warning, + "CUDA EP: verification failed after downloading updated package(s)"); + std::filesystem::remove_all(staging_dir); return false; } @@ -228,7 +304,11 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, std::filesystem::remove_all(ep_dir); } std::filesystem::rename(staging_dir, ep_dir); - logger.Log(LogLevel::Information, "CUDA EP: successfully installed."); + + logger.Log(LogLevel::Information, + fmt::format("CUDA EP: successfully installed (updated: ort={}, cuda_deps={})", + needs_ort ? "yes" : "no", + needs_cuda_deps ? "yes" : "no")); } if (progress_cb) { @@ -279,8 +359,8 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, } logger.Log(LogLevel::Information, - fmt::format("CUDA EP: ready (install_path={}, version={})", - ep_dir.string(), manifest.version)); + fmt::format("CUDA EP: ready (install_path={}, ort_version={}, cuda_deps_version={})", + ep_dir.string(), manifest.ort_version, manifest.cuda_deps_version)); return true; } catch (const std::exception& e) { logger.Log(LogLevel::Warning, fmt::format("CUDA EP: error: {}", e.what())); diff --git a/sdk_v2/cpp/src/ep_detection/ep_utils.cc b/sdk_v2/cpp/src/ep_detection/ep_utils.cc index 7fa6524f4..fe04e4221 100644 --- a/sdk_v2/cpp/src/ep_detection/ep_utils.cc +++ b/sdk_v2/cpp/src/ep_detection/ep_utils.cc @@ -15,9 +15,15 @@ namespace fl { bool VerifyEpPackage( const std::filesystem::path& dir, - std::initializer_list> expected, + const std::unordered_map& expected, std::string_view ep_name, ILogger& logger) { + if (expected.empty()) { + logger.Log(LogLevel::Warning, + fmt::format("{}: expected hash map is empty", ep_name)); + return false; + } + for (const auto& [filename, expected_hash] : expected) { auto file_path = dir / filename; diff --git a/sdk_v2/cpp/src/ep_detection/ep_utils.h b/sdk_v2/cpp/src/ep_detection/ep_utils.h index 634bb517e..4caf179cf 100644 --- a/sdk_v2/cpp/src/ep_detection/ep_utils.h +++ b/sdk_v2/cpp/src/ep_detection/ep_utils.h @@ -3,8 +3,8 @@ #pragma once #include -#include -#include +#include +#include #include namespace fl { @@ -14,13 +14,13 @@ class ILogger; /// Verify a set of binaries in @p dir all exist and match their expected SHA-256 hashes. /// /// @param dir Directory containing the extracted EP binaries. -/// @param expected List of (filename, expected_sha256_hex) pairs. -/// @param ep_name EP name used in warning log messages (e.g. "CUDA EP"). +/// @param expected Map of filename -> expected_sha256_hex. +/// @param ep_name EP name used in warning log messages (e.g. "CUDA EP (ort)"). /// @param logger Logger for diagnostic output. /// @return true if every file exists and its hash matches; false otherwise. bool VerifyEpPackage( const std::filesystem::path& dir, - std::initializer_list> expected, + const std::unordered_map& expected, std::string_view ep_name, ILogger& logger); diff --git a/sdk_v2/cpp/src/ep_detection/webgpu_ep_bootstrapper.cc b/sdk_v2/cpp/src/ep_detection/webgpu_ep_bootstrapper.cc index afdb7da9f..3725bf209 100644 --- a/sdk_v2/cpp/src/ep_detection/webgpu_ep_bootstrapper.cc +++ b/sdk_v2/cpp/src/ep_detection/webgpu_ep_bootstrapper.cc @@ -18,6 +18,7 @@ #include #include #include +#include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -139,9 +140,11 @@ bool WebGpuEpBootstrapper::DownloadAndRegister(bool force, try { // Fetch manifest before acquiring lock (avoid holding lock during network I/O) auto manifest = FetchManifest(logger); + const std::unordered_map expected_hashes = { + {kWebGpuProviderLib, manifest.sha256}}; // Check if package already exists and is valid - if (!force && VerifyEpPackage(ep_dir, {{kWebGpuProviderLib, manifest.sha256}}, "WebGPU EP", logger)) { + if (!force && VerifyEpPackage(ep_dir, expected_hashes, "WebGPU EP", logger)) { logger.Log(LogLevel::Debug, "WebGPU EP: local binaries match manifest, skipping download"); } else { // Ensure parent directory exists for the lock file @@ -152,7 +155,7 @@ bool WebGpuEpBootstrapper::DownloadAndRegister(bool force, FileLock lock(lock_path); // Re-check after acquiring lock (another process may have completed the update) - if (!force && VerifyEpPackage(ep_dir, {{kWebGpuProviderLib, manifest.sha256}}, "WebGPU EP", logger)) { + if (!force && VerifyEpPackage(ep_dir, expected_hashes, "WebGPU EP", logger)) { logger.Log(LogLevel::Debug, "WebGPU EP: another process already completed the update"); } else { // Download and extract to staging directory for atomic swap @@ -200,7 +203,7 @@ bool WebGpuEpBootstrapper::DownloadAndRegister(bool force, std::filesystem::remove(zip_path); // Verify staging - if (!VerifyEpPackage(staging_dir, {{kWebGpuProviderLib, manifest.sha256}}, "WebGPU EP", logger)) { + if (!VerifyEpPackage(staging_dir, expected_hashes, "WebGPU EP", logger)) { logger.Log(LogLevel::Warning, fmt::format("WebGPU EP: verification failed after extraction (attempt {})", attempts_)); From faacd17f6a05b055e946055e9e6f3b933e898ead Mon Sep 17 00:00:00 2001 From: Prathik Rao Date: Thu, 11 Jun 2026 11:51:25 -0700 Subject: [PATCH 13/15] bug fix --- sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc index 7a8820aef..d04558bc1 100644 --- a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc +++ b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc @@ -115,7 +115,7 @@ bool DownloadAndExtractPackage(const ManifestInfo::PackageInfo& package, const std::filesystem::path& zip_path, const std::string& package_name, const std::string& ep_name, - const fl::ProgressCallback& progress_cb, + const fl::IEpBootstrapper::ProgressCallback& progress_cb, float progress_base, float progress_span, fl::ILogger& logger) { From 208c240aca69caa5e73ca6975b14f8e7ef530512 Mon Sep 17 00:00:00 2001 From: Prathik Rao Date: Thu, 11 Jun 2026 12:10:14 -0700 Subject: [PATCH 14/15] version --- .../src/ep_detection/cuda_ep_bootstrapper.cc | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc index d04558bc1..3cb98c2cf 100644 --- a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc +++ b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,13 @@ constexpr int kMaxInstallAttempts = 5; constexpr const char* kManifestUrl = "https://foundrypackages-ffhrdhbxb7gpdreh.b02.azurefd.net/cuda_ep_dev.json"; +// Install flow: +// 1. Fetch the manifest and check the existing cuda-ep directory against it. +// 2. If any package is stale, copy the existing cuda-ep directory into staging. +// 3. Use that staged copy as the base and download only the stale package(s). +// 4. Re-write version.json with the ORT version, verify the staged files, then +// atomically rename staging into place. + // ----------------------------------------------------------------------- // Platform detection // @@ -154,6 +162,28 @@ bool DownloadAndExtractPackage(const ManifestInfo::PackageInfo& package, return true; }; +void WriteVersionJson(const std::filesystem::path& staging_dir, + const std::string& ort_version, + fl::ILogger& logger) { + auto version_path = staging_dir / "version.json"; + auto version_json = nlohmann::json{{"version", ort_version}}; + + std::ofstream out(version_path, std::ios::trunc | std::ios::binary); + if (!out) { + throw std::runtime_error( + fmt::format("CUDA EP: failed to open {} for writing", version_path.string())); + } + + out << version_json.dump(); + if (!out) { + throw std::runtime_error( + fmt::format("CUDA EP: failed to write {}", version_path.string())); + } + + logger.Log(fl::LogLevel::Debug, + fmt::format("CUDA EP: wrote version.json with ort_version={}", ort_version)); +} + /// Fetch and parse the CUDA EP manifest from the CDN. /// Returns the package entry for the given platform key. ManifestInfo FetchManifest(const char* platform_key, fl::ILogger& logger) { @@ -286,6 +316,10 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force, progress_base += progress_span; } + // CUDA has two install steps (ORT package and CUDA deps), so always stamp + // the final package with the ORT version after both steps complete. + WriteVersionJson(staging_dir, manifest.ort_version, logger); + // Verify both package subsets in staging before promotion. if (!VerifyEpPackage(staging_dir, manifest.ort.sha256, "CUDA EP (ort)", logger) || !VerifyEpPackage(staging_dir, manifest.cuda_deps.sha256, "CUDA EP (cuda_deps)", logger)) { From 24360a931275e6bcf8474d219726ba7c05eaca9c Mon Sep 17 00:00:00 2001 From: Prathik Rao Date: Thu, 11 Jun 2026 12:15:35 -0700 Subject: [PATCH 15/15] comment --- sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc index 3cb98c2cf..a2eb12d67 100644 --- a/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc +++ b/sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc @@ -54,11 +54,6 @@ constexpr const char* kManifestUrl = // // Returns the manifest platform key and ORT registration library filename // for the current build target, or std::nullopt if unsupported. -// -// To add a platform: -// 1. Uncomment its #elif block below. -// 2. Uncomment its entry in $binaryNames / $expectedPlatforms in -// cuda-ep-upload.yml and update $platformPattern there too. // ----------------------------------------------------------------------- struct PlatformInfo { const char* key; // manifest lookup key, e.g. "win-x64"