Skip to content
Open
1 change: 1 addition & 0 deletions sdk_v2/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ set(FOUNDRY_LOCAL_SOURCES
src/utils.cc
src/util/file_lock.cc
src/http/http_download.cc
src/util/model_layout.cc
src/util/path_safety.cc
src/util/region_fallback.cc
src/util/sha256.cc
Expand Down
68 changes: 57 additions & 11 deletions sdk_v2/cpp/src/catalog/azure_catalog_models.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,44 @@ int64_t ParseIso8601ToUnix(const std::string& iso_str) {
return t == static_cast<time_t>(-1) ? 0 : static_cast<int64_t>(t);
}

DeviceType ParseDeviceType(const std::string& device) {
const auto lower = ToLower(device);
if (lower == "cpu") {
return DeviceType::kCPU;
std::optional<ModelVariantMetadata> ToModelVariantMetadata(const VariantMetadata& variant_metadata) {
if (!variant_metadata.model_format.has_value() && !variant_metadata.model_package.has_value()) {
return std::nullopt;
}

if (lower == "gpu") {
return DeviceType::kGPU;
}
ModelVariantMetadata metadata;
metadata.model_format = variant_metadata.model_format;

if (variant_metadata.model_package.has_value()) {
ModelPackageMetadata package_metadata;
package_metadata.schema_version = variant_metadata.model_package->schema_version;
package_metadata.variants.reserve(variant_metadata.model_package->variants.size());

for (const auto& catalog_variant : variant_metadata.model_package->variants) {
ModelPackageVariant package_variant;
if (catalog_variant.name.has_value()) {
package_variant.name = *catalog_variant.name;
}

if (catalog_variant.execution_provider.has_value()) {
package_variant.execution_provider = *catalog_variant.execution_provider;
}

if (catalog_variant.device.has_value()) {
package_variant.device_type = DeviceTypeFromString(*catalog_variant.device);
}

if (catalog_variant.compatibility_string.has_value()) {
package_variant.compatibility_string = *catalog_variant.compatibility_string;
}

package_metadata.variants.push_back(std::move(package_variant));
}

if (lower == "npu") {
return DeviceType::kNPU;
metadata.model_package = std::move(package_metadata);
}
return DeviceType::kNotSet;

return metadata;
}

} // anonymous namespace
Expand Down Expand Up @@ -120,11 +144,31 @@ void to_json(nlohmann::json& j, const AzureCatalogRequest& r) {
// Response deserialization (from_json)
// ========================================================================

void from_json(const nlohmann::json& j, CatalogModelPackageVariant& v) {
opt_str(j, "name", v.name);
opt_str(j, "executionProvider", v.execution_provider);
opt_str(j, "device", v.device);
opt_str(j, "compatibilityString", v.compatibility_string);
}

void from_json(const nlohmann::json& j, CatalogModelPackageMetadata& p) {
opt_int(j, "schemaVersion", p.schema_version);

if (j.contains("variants") && j["variants"].is_array()) {
p.variants = j["variants"].get<std::vector<CatalogModelPackageVariant>>();
}
}

void from_json(const nlohmann::json& j, VariantMetadata& v) {
opt_str(j, "modelType", v.model_type);
opt_str(j, "device", v.device);
opt_str(j, "executionProvider", v.execution_provider);
opt_int64(j, "fileSizeBytes", v.file_size_bytes);
opt_str(j, "modelFormat", v.model_format);

if (j.contains("modelPackage") && j["modelPackage"].is_object()) {
v.model_package = j["modelPackage"].get<CatalogModelPackageMetadata>();
}
}

void from_json(const nlohmann::json& j, VariantParent& v) {
Expand Down Expand Up @@ -286,13 +330,15 @@ std::optional<ModelInfo> CatalogModelToModelInfo(const CatalogLocalModel& cm) {
if (props.variant_info && props.variant_info->variant_metadata) {
const auto& vm = *props.variant_info->variant_metadata;
if (vm.device) {
info.device_type = ParseDeviceType(*vm.device);
info.device_type = DeviceTypeFromString(*vm.device);
}

if (vm.execution_provider) {
info.execution_provider = *vm.execution_provider;
}

info.variant_metadata = ToModelVariantMetadata(vm);

// ModelType — defaults to "ONNX" (matches C# ToAzureFoundryLocalModel)
info.string_properties[FOUNDRY_LOCAL_MODEL_PROP_MODEL_TYPE_STR] =
vm.model_type.value_or("ONNX");
Expand Down
16 changes: 16 additions & 0 deletions sdk_v2/cpp/src/catalog/azure_catalog_models.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,26 @@ struct AzureCatalogRequest {

// --- Response types ---

struct CatalogModelPackageVariant {
std::optional<std::string> name;
std::optional<std::string> execution_provider;
std::optional<std::string> device;
std::optional<std::string> compatibility_string;
};

struct CatalogModelPackageMetadata {
std::optional<int> schema_version;
std::vector<CatalogModelPackageVariant> variants;
};

/// Variant metadata nested inside Properties → VariantInfo.
struct VariantMetadata {
std::optional<std::string> model_type;
std::optional<std::string> device;
std::optional<std::string> execution_provider;
std::optional<int64_t> file_size_bytes;
std::optional<std::string> model_format;
std::optional<CatalogModelPackageMetadata> model_package;
};

struct VariantParent {
Expand Down Expand Up @@ -148,6 +162,8 @@ void to_json(nlohmann::json& j, const AzureCatalogRequest& r);

// --- Response deserialization (from_json) ---

void from_json(const nlohmann::json& j, CatalogModelPackageVariant& v);
void from_json(const nlohmann::json& j, CatalogModelPackageMetadata& p);
void from_json(const nlohmann::json& j, VariantMetadata& v);
void from_json(const nlohmann::json& j, VariantParent& v);
void from_json(const nlohmann::json& j, VariantInfo& v);
Expand Down
147 changes: 143 additions & 4 deletions sdk_v2/cpp/src/catalog/azure_model_catalog.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,137 @@ std::vector<ModelInfo> DeduplicateByModelId(std::vector<ModelInfo> model_infos)
return deduplicated;
}

bool IsCompatible(CompiledModelCompatibility compatibility) {
return compatibility == CompiledModelCompatibility::kSupportedOptimal ||
compatibility == CompiledModelCompatibility::kSupportedPreferRecompilation;
}

bool IsRelevantPackageVariant(const ModelInfo& info, const ModelPackageVariant& package_variant) {
if (!info.execution_provider.empty() && !package_variant.execution_provider.empty() &&
info.execution_provider != package_variant.execution_provider) {
return false;
}

if (info.device_type != DeviceType::kNotSet &&
package_variant.device_type != DeviceType::kNotSet &&
info.device_type != package_variant.device_type) {
return false;
}

return true;
}

std::optional<DeviceType> GetDeviceConstraint(const ModelInfo& info,
const ModelPackageVariant& package_variant) {
if (package_variant.device_type != DeviceType::kNotSet) {
return package_variant.device_type;
}

if (info.device_type != DeviceType::kNotSet) {
return info.device_type;
}

return std::nullopt;
}

std::string_view GetRequestedExecutionProvider(const ModelInfo& info,
const ModelPackageVariant& package_variant) {
if (!package_variant.execution_provider.empty()) {
return package_variant.execution_provider;
}

return info.execution_provider;
}

bool ShouldExposeModelInfo(const ModelInfo& info,
const IEpDetector& ep_detector,
ILogger& logger) {
if (!info.variant_metadata.has_value() ||
!info.variant_metadata->model_package.has_value() ||
info.variant_metadata->model_package->variants.empty()) {
return true;
}

bool saw_relevant_variant = false;
bool saw_unknown_compatibility = false;

for (const auto& package_variant : info.variant_metadata->model_package->variants) {
if (!IsRelevantPackageVariant(info, package_variant)) {
continue;
}

saw_relevant_variant = true;

const auto execution_provider = GetRequestedExecutionProvider(info, package_variant);
if (execution_provider.empty() || package_variant.compatibility_string.empty()) {
logger.Log(LogLevel::Debug,
fmt::format("Keeping catalog model '{}' because package variant '{}' has incomplete "
"compatibility metadata.",
info.model_id,
package_variant.name));
saw_unknown_compatibility = true;
continue;
}

const auto compatibility = ep_detector.GetModelCompatibilityForEpDevices(
execution_provider,
GetDeviceConstraint(info, package_variant),
package_variant.compatibility_string);

if (IsCompatible(compatibility)) {
return true;
}

if (compatibility == CompiledModelCompatibility::kUnknown) {
logger.Log(LogLevel::Debug,
fmt::format("Keeping catalog model '{}' because compatibility is unknown for package "
"variant '{}'.",
info.model_id,
package_variant.name));
saw_unknown_compatibility = true;
}
}

if (!saw_relevant_variant) {
logger.Log(LogLevel::Debug,
fmt::format("Keeping catalog model '{}' because no package variant definitively matches "
"its catalog EP/device.",
info.model_id));
return true;
}

if (saw_unknown_compatibility) {
return true;
}

logger.Log(LogLevel::Debug,
fmt::format("Filtering catalog model '{}' because all relevant ORT model package variants "
"are unsupported.",
info.model_id));
return false;
}

/// Drops catalog entries whose ORT model package variants are all known to be unsupported
/// on the current hardware. Entries without package metadata are always kept. Ids of the
/// dropped entries are collected in `hidden_model_ids` so they aren't resurrected elsewhere.
std::vector<ModelInfo> FilterVisibleInfos(std::vector<ModelInfo> model_infos,
const IEpDetector& ep_detector,
ILogger& logger,
std::unordered_set<std::string>* hidden_model_ids = nullptr) {
std::vector<ModelInfo> visible_infos;
visible_infos.reserve(model_infos.size());

for (auto& info : model_infos) {
if (ShouldExposeModelInfo(info, ep_detector, logger)) {
visible_infos.push_back(std::move(info));
} else if (hidden_model_ids != nullptr) {
hidden_model_ids->insert(info.model_id);
}
}

return visible_infos;
}

} // namespace

AzureModelCatalog::AzureModelCatalog(std::vector<std::pair<std::string, std::optional<std::string>>> catalog_urls,
Expand Down Expand Up @@ -123,12 +254,15 @@ AzureModelCatalog::CatalogResult AzureModelCatalog::GetLiveCatalogOrLocalSnapsho
}

std::vector<Model> AzureModelCatalog::AddLocalModels(std::vector<ModelInfo>& model_infos,
const LocalModels& local_models) const {
const LocalModels& local_models,
const std::unordered_set<std::string>& hidden_model_ids) const {
std::vector<Model> models;
models.reserve(model_infos.size() + local_models.size());

std::unordered_set<std::string> model_ids;
model_ids.reserve(model_infos.size() + local_models.size());
// Seeding with the hidden ids keeps catalog models that were filtered out as unsupported from
// being re-added as synthesized local BYOM entries.
std::unordered_set<std::string> model_ids(hidden_model_ids);
model_ids.reserve(model_ids.size() + model_infos.size() + local_models.size());
for (const auto& info : model_infos) {
model_ids.insert(info.model_id);

Expand Down Expand Up @@ -162,7 +296,10 @@ std::vector<Model> AzureModelCatalog::FetchModels() const {
logger_.Log(LogLevel::Information, fmt::format("Found {} locally cached models.", cached_model_ids.size()));

auto catalog_result = GetLiveCatalogOrLocalSnapshot(cached_model_ids);
auto models = AddLocalModels(catalog_result.model_infos, local_models);
std::unordered_set<std::string> hidden_model_ids;
catalog_result.model_infos =
FilterVisibleInfos(std::move(catalog_result.model_infos), ep_detector_, logger_, &hidden_model_ids);
auto models = AddLocalModels(catalog_result.model_infos, local_models, hidden_model_ids);

logger_.Log(LogLevel::Information, fmt::format("Populated model info for {} models.", models.size()));

Expand All @@ -189,6 +326,7 @@ std::vector<Model> AzureModelCatalog::FetchModelVersions(
try {
auto client = CreateCatalogClient(url, filter.value_or(""));
auto model_infos = client->FetchAllVersionsByAlias(model_alias, model_name);
model_infos = FilterVisibleInfos(std::move(model_infos), ep_detector_, logger_);

out.reserve(out.size() + model_infos.size());
for (auto& info : model_infos) {
Expand Down Expand Up @@ -233,6 +371,7 @@ std::vector<Model> AzureModelCatalog::FetchModelsByIds(const std::vector<std::st
try {
auto client = CreateCatalogClient(url, filter.value_or(""));
auto model_infos = client->FetchModelsByIds(remaining);
model_infos = FilterVisibleInfos(std::move(model_infos), ep_detector_, logger_);

for (auto& info : model_infos) {
std::string local_path;
Expand Down
5 changes: 4 additions & 1 deletion sdk_v2/cpp/src/catalog/azure_model_catalog.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <memory>
#include <optional>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -60,7 +61,9 @@ class AzureModelCatalog : public BaseModelCatalog {
static constexpr const char* kDefaultCatalogFilter = "''";

CatalogResult GetLiveCatalogOrLocalSnapshot(const std::vector<std::string>& cached_model_ids) const;
std::vector<Model> AddLocalModels(std::vector<ModelInfo>& model_infos, const LocalModels& local_models) const;
std::vector<Model> AddLocalModels(std::vector<ModelInfo>& model_infos,
const LocalModels& local_models,
const std::unordered_set<std::string>& hidden_model_ids) const;

std::vector<std::pair<std::string, std::optional<std::string>>> catalog_urls_;
std::string cache_dir_;
Expand Down
Loading
Loading