Skip to content

Commit c878bae

Browse files
Add model management sample and update package versions (#572)
* update winml version * Add ModelCatalog samples to WinML (#541) * cp * v update * raw links * update the APIs and json file * update cs files * update package versions * update * update to show error if json file is missing --------- Co-authored-by: Darshak Bhatti <[email protected]>
1 parent 70e13b7 commit c878bae

File tree

29 files changed

+467
-205
lines changed

29 files changed

+467
-205
lines changed

Samples/WindowsML/Directory.Packages.props

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@
55
<ItemGroup>
66
<!-- WindowsAppSDK packages -->
77
<PackageVersion Include="Microsoft.ML.OnnxRuntimeGenAI.WinML" Version="0.10.0" />
8-
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.250916003" />
9-
<PackageVersion Include="Microsoft.WindowsAppSDK.AI" Version="1.8.38" />
8+
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
9+
<PackageVersion Include="Microsoft.WindowsAppSDK.AI" Version="1.8.39" />
1010
<PackageVersion Include="Microsoft.WindowsAppSDK.Base" Version="1.8.250831001" />
1111
<PackageVersion Include="Microsoft.WindowsAppSDK.DWrite" Version="1.8.25090401" />
12-
<PackageVersion Include="Microsoft.WindowsAppSDK.Foundation" Version="1.8.250906002" />
13-
<PackageVersion Include="Microsoft.WindowsAppSDK.InteractiveExperiences" Version="1.8.250906004" />
14-
<PackageVersion Include="Microsoft.WindowsAppSDK.ML" Version="1.8.2091" />
15-
<PackageVersion Include="Microsoft.WindowsAppSDK.Runtime" Version="1.8.250916003" />
12+
<PackageVersion Include="Microsoft.WindowsAppSDK.Foundation" Version="1.8.251104000" />
13+
<PackageVersion Include="Microsoft.WindowsAppSDK.InteractiveExperiences" Version="1.8.251104001" />
14+
<PackageVersion Include="Microsoft.WindowsAppSDK.ML" Version="1.8.2109" />
15+
<PackageVersion Include="Microsoft.WindowsAppSDK.Runtime" Version="1.8.251106002" />
1616
<PackageVersion Include="Microsoft.WindowsAppSDK.Widgets" Version="1.8.250904007" />
17-
<PackageVersion Include="Microsoft.WindowsAppSDK.WinUI" Version="1.8.250906003" />
17+
<PackageVersion Include="Microsoft.WindowsAppSDK.WinUI" Version="1.8.251105000" />
1818
<!-- Other dependencies -->
1919
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.3179.45" />
2020
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4654" />

Samples/WindowsML/Resources/SqueezeNetModel.targets

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
<!-- Resources folder files (checked in) -->
2121
<SqueezeNetModelFileDefault>$(MSBuildThisFileDirectory)SqueezeNet.onnx</SqueezeNetModelFileDefault>
2222
<SqueezeNetLicenseFile>$(MSBuildThisFileDirectory)SqueezeNet.LICENSE.txt</SqueezeNetLicenseFile>
23+
24+
<SqueezeNetModelCatalogFile>$(MSBuildThisFileDirectory)SqueezeNetModelCatalog.json</SqueezeNetModelCatalogFile>
2325
</PropertyGroup>
2426

2527
<!-- Create temp directory for downloaded model -->
@@ -80,6 +82,9 @@
8082

8183
<!-- Copy image file -->
8284
<Copy SourceFiles="$(SqueezeNetImageFile)" DestinationFiles="$(OutputPath)\image.png" SkipUnchangedFiles="true" Retries="5" RetryDelayMilliseconds="1000" />
85+
86+
<!-- Copy sample catalog file from Resources folder -->
87+
<Copy SourceFiles="$(SqueezeNetModelCatalogFile)" DestinationFiles="$(OutputPath)\SqueezeNetModelCatalog.json" SkipUnchangedFiles="true" Retries="5" RetryDelayMilliseconds="1000" />
8388
</Target>
8489

8590
</Project>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{
2+
"base": "https://learn.microsoft.com/azure/ai-foundry/foundry-local/reference/reference-catalog-api",
3+
"models": [
4+
{
5+
"id": "squeezenet",
6+
"name": "squeezenet",
7+
"version": "1",
8+
"publisher": "microsoft",
9+
"executionProviders": [
10+
{
11+
"name": "CPUExecutionProvider"
12+
}
13+
],
14+
"modelSizeBytes": 1250000,
15+
"license": "BSD",
16+
"licenseUri": "https://github.com/microsoft/WindowsAppSDK-Samples/raw/refs/heads/release/experimental/Samples/WindowsML/cpp/CppConsoleDesktop/CppConsoleDesktop/SqueezeNet.LICENSE.txt",
17+
"licenseText": "This model is provided under the License Terms available at \u003Chttps://github.com/microsoft/WindowsAppSDK-Samples/raw/refs/heads/release/experimental/Samples/WindowsML/cpp/CppConsoleDesktop/CppConsoleDesktop/SqueezeNet.LICENSE.txt\u003E",
18+
"uri": "https://github.com/microsoft/WindowsAppSDK-Samples/raw/refs/heads/release/experimental/Samples/WindowsML/cpp/CppConsoleDesktop/CppConsoleDesktop",
19+
"files": [
20+
{
21+
"name": "SqueezeNet.onnx",
22+
"uri": "https://github.com/microsoft/WindowsAppSDK-Samples/raw/8adfdf35d8bb88114490ea3ba795b99bc52250a0/Samples/WindowsML/Resources/SqueezeNet.onnx",
23+
"sha256": "d7f93e79ba1284a3ff2b4cea317d79f3e98e64acfce725ad5f4e8197864aef73"
24+
},
25+
{
26+
"name": "SqueezeNet.Labels.txt",
27+
"uri": "https://github.com/microsoft/Windows-Machine-Learning/raw/02b586811c8beb1ae2208c8605393267051257ae/Samples/SqueezeNetObjectDetection/Desktop/cpp/Labels.txt",
28+
"sha256": "dc1fd0d4747096d3aa690bd65ec2f51fdb3e5117535bfbce46fa91088a8f93a9"
29+
},
30+
{
31+
"name": "image.png",
32+
"uri": "https://github.com/microsoft/Windows-Machine-Learning/raw/638cd40abc408e7049219e5d7b4fe164c304f2a2/SharedContent/media/kitten_224.png",
33+
"sha256": "e49fb5cd0c31b07984784e50a809825370d8adeac31c9f86ff396f64da635010"
34+
}
35+
]
36+
},
37+
{
38+
"id": "squeezenet-fp32",
39+
"name": "squeezenet-fp32",
40+
"version": "1.1-7",
41+
"publisher": "microsoft",
42+
"executionProviders": [
43+
{
44+
"name": "NvTensorRTRTXExecutionProvider"
45+
}
46+
],
47+
"modelSizeBytes": 4730000,
48+
"license": "BSD",
49+
"licenseUri": "https://github.com/microsoft/WindowsAppSDK-Samples/blob/main/Samples/WindowsML/Resources/SqueezeNet.LICENSE.txt",
50+
"licenseText": "This model is provided under the License Terms available at \u003Chttps://github.com/microsoft/WindowsAppSDK-Samples/blob/main/Samples/WindowsML/Resources/SqueezeNet.LICENSE.txt\u003E",
51+
"uri": "https://github.com/onnx/models/raw/b1eeaa1ac722dcc1cd1a8284bde34393dab61c3d/validated/vision/classification/squeezenet/model",
52+
"files": [
53+
{
54+
"name": "SqueezeNet-fp32.onnx",
55+
"uri": "https://github.com/onnx/models/raw/b1eeaa1ac722dcc1cd1a8284bde34393dab61c3d/validated/vision/classification/squeezenet/model/squeezenet1.1-7.onnx",
56+
"sha256": "1eeff551a67ae8d565ca33b572fc4b66e3ef357b0eb2863bb9ff47a918cc4088"
57+
},
58+
{
59+
"name": "SqueezeNet.Labels.txt",
60+
"uri": "https://github.com/microsoft/Windows-Machine-Learning/raw/02b586811c8beb1ae2208c8605393267051257ae/Samples/SqueezeNetObjectDetection/Desktop/cpp/Labels.txt",
61+
"sha256": "dc1fd0d4747096d3aa690bd65ec2f51fdb3e5117535bfbce46fa91088a8f93a9"
62+
},
63+
{
64+
"name": "image.png",
65+
"uri": "https://github.com/microsoft/Windows-Machine-Learning/raw/638cd40abc408e7049219e5d7b4fe164c304f2a2/SharedContent/media/kitten_224.png",
66+
"sha256": "e49fb5cd0c31b07984784e50a809825370d8adeac31c9f86ff396f64da635010"
67+
}
68+
]
69+
}
70+
]
71+
}

Samples/WindowsML/Shared/cpp/ArgumentParser.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ namespace Shared
3030
{
3131
options.download_packages = true;
3232
}
33+
else if (arguments[i] == L"--use_model_catalog")
34+
{
35+
options.use_model_catalog = true;
36+
}
3337
else if (arguments[i] == L"--model" && i + 1 < arguments.size())
3438
{
3539
options.model_path = arguments[++i];
@@ -99,6 +103,14 @@ namespace Shared
99103
return false;
100104
}
101105

106+
// Mutual exclusivity validation between use_model_catalog and model_path
107+
if (options.use_model_catalog && !options.model_path.empty())
108+
{
109+
std::wcout << L"ERROR: Specify only one of --use_model_catalog or --model.\n";
110+
PrintUsage();
111+
return false;
112+
}
113+
102114
// Optional early validation of device_type token
103115
if (options.device_type.has_value())
104116
{
@@ -154,11 +166,13 @@ namespace Shared
154166
<< L" --device_type <type> Device type for OpenVINOExecutionProvider (NPU, GPU, CPU) when multiple present\n"
155167
<< L" --compile Compile the model\n"
156168
<< L" --download Download required packages\n"
169+
<< L" --use_model_catalog Use the model catalog for model selection\n"
157170
<< L" --model <path> Path to the input ONNX model (default: SqueezeNet.onnx in executable directory)\n"
158171
<< L" --compiled_output <path> Path for compiled output model (default: SqueezeNet_ctx.onnx)\n"
159172
<< L" --image_path <path> Path to the input image (default: sample kitten image)\n"
160173
<< L"\n"
161174
<< L"Exactly one of --ep_policy or --ep_name must be specified.\n"
175+
<< L"--use_model_catalog and --model are mutually exclusive.\n"
162176
<< L"\nAvailable execution providers (name, vendor, device type):\n";
163177
ExecutionProviderManager::PrintExecutionProviderHelpTable();
164178
}

Samples/WindowsML/Shared/cpp/ArgumentParser.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation. All rights reserved.
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License. See LICENSE.md in the repo root for license information.
33
#pragma once
44

@@ -28,6 +28,7 @@ namespace Shared
2828
{
2929
bool compile_model = false;
3030
bool download_packages = false;
31+
bool use_model_catalog = false;
3132
// Policy-based EP selection (mutually exclusive with explicit ep_name)
3233
std::optional<OrtExecutionProviderDevicePolicy> ep_policy;
3334
// Explicit EP selection

Samples/WindowsML/Shared/cs/ArgumentParser.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class Options
2626
public string? DeviceType { get; set; } = null; // Optional value to declare CPU | GPU | NPU
2727
public bool Compile { get; set; } = false;
2828
public bool Download { get; set; } = false;
29+
public bool UseModelCatalog { get; set; } = false;
2930
public string ModelPath { get; set; } = string.Empty;
3031
public string OutputPath { get; set; } = "SqueezeNet_ctx.onnx";
3132
public string ImagePath { get; set; } = string.Empty;
@@ -95,6 +96,10 @@ public static Options ParseOptions(string[] args)
9596
case "--download":
9697
options.Download = true;
9798
break;
99+
100+
case "--use_model_catalog":
101+
options.UseModelCatalog = true;
102+
break;
98103

99104
case "--model":
100105
if (i + 1 < args.Length)
@@ -179,6 +184,7 @@ public static void PrintHelp()
179184
Console.WriteLine(" --device_type <type> Optional hardware device type to use when EP supports multiple (e.g. CPU, GPU, NPU)");
180185
Console.WriteLine(" --compile Compile the model");
181186
Console.WriteLine(" --download Download required packages");
187+
Console.WriteLine(" --use_model_catalog Use the model catalog for model discovery");
182188
Console.WriteLine(" --model <path> Path to input ONNX model (default: SqueezeNet.onnx)");
183189
Console.WriteLine(" --compiled_output <path> Path for compiled output model (default: SqueezeNet_ctx.onnx)");
184190
Console.WriteLine(" --image_path <path> Path to the input image (default: sample kitten image)");

Samples/WindowsML/Shared/cs/ModelManager.cs

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Microsoft.ML.OnnxRuntime;
55
using System.IO;
66
using System.Reflection;
7+
using Microsoft.Windows.AI.MachineLearning;
78

89
namespace WindowsML.Shared
910
{
@@ -112,14 +113,73 @@ public static string GetModelVariantPath(string executableFolder, ModelVariant v
112113
/// <summary>
113114
/// Resolve model paths with intelligent variant selection
114115
/// </summary>
115-
public static (string modelPath, string compiledModelPath, string labelsPath) ResolvePaths(Options options, OrtEnv ortEnv)
116+
public static async Task<(string modelPath, string compiledModelPath, string labelsPath)> ResolvePaths(Options options, OrtEnv ortEnv)
116117
{
117118
string executableFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!;
118-
119119
string modelPath;
120-
121-
// Check if user specified a custom model path
122-
if (!string.IsNullOrWhiteSpace(options.ModelPath))
120+
string labelsPath = Path.Combine(executableFolder, "SqueezeNet.Labels.txt");
121+
122+
if (options.UseModelCatalog)
123+
{
124+
Console.WriteLine("Using model catalog...");
125+
126+
// Build source
127+
string sampleCatalogJsonPath = Path.Combine(executableFolder, "SqueezeNetModelCatalog.json");
128+
129+
// Use intelligent model variant selection based on execution provider and device capabilities
130+
ModelVariant actualVariant = DetermineModelVariant(options, ortEnv);
131+
if (File.Exists(sampleCatalogJsonPath))
132+
{
133+
var uri = new System.Uri(sampleCatalogJsonPath);
134+
var sampleCatalogSource = await ModelCatalogSource.CreateFromUriAsync(uri);
135+
ModelCatalog modelCatalog = new ModelCatalog(new[] { sampleCatalogSource });
136+
CatalogModelInfo modelFromCatalog;
137+
string modelVariantName = (actualVariant == ModelVariant.FP32) ? "squeezenet-fp32" : "squeezenet";
138+
modelFromCatalog = await modelCatalog.FindModelAsync(modelVariantName);
139+
if (modelFromCatalog != null)
140+
{
141+
var additionalHeaders = new Dictionary<string, string>();
142+
var catalogModelInstanceOp = modelFromCatalog.GetInstanceAsync(additionalHeaders);
143+
144+
catalogModelInstanceOp.Progress += (operation, progress) =>
145+
{
146+
Console.Write($"Model download progress: {progress}%\r");
147+
};
148+
149+
var catalogModelInstanceResult = await catalogModelInstanceOp;
150+
151+
if (catalogModelInstanceResult.Status == CatalogModelInstanceStatus.Available)
152+
{
153+
using var catalogModelInstance = catalogModelInstanceResult.GetInstance();
154+
var modelPaths = catalogModelInstance.ModelPaths;
155+
156+
string modelFolderPath = modelPaths[0];
157+
string modelName = $"{modelVariantName}.onnx";
158+
modelPath = Path.Combine(modelFolderPath, modelName);
159+
Console.WriteLine($"Using model from catalog at: {modelPath}");
160+
161+
// Get labels
162+
labelsPath = Path.Combine(modelFolderPath, "SqueezeNet.Labels.txt");
163+
}
164+
else
165+
{
166+
Console.WriteLine("Model download failed. Falling back to executableFolder");
167+
modelPath = GetModelVariantPath(executableFolder, actualVariant);
168+
}
169+
}
170+
else
171+
{
172+
Console.WriteLine($"Model with alias or ID '{modelVariantName}' not found in catalog. Falling back to executableFolder");
173+
modelPath = GetModelVariantPath(executableFolder, actualVariant);
174+
}
175+
}
176+
else
177+
{
178+
Console.WriteLine("Model catalog JSON file not found. Falling back to executableFolder");
179+
modelPath = GetModelVariantPath(executableFolder, actualVariant);
180+
}
181+
}
182+
else if (!string.IsNullOrWhiteSpace(options.ModelPath))
123183
{
124184
// User provided custom model path - use it as-is
125185
modelPath = options.ModelPath.Contains(Path.DirectorySeparatorChar) ?
@@ -135,8 +195,6 @@ public static (string modelPath, string compiledModelPath, string labelsPath) Re
135195
string compiledModelPath = options.OutputPath.Contains(Path.DirectorySeparatorChar) ?
136196
options.OutputPath : Path.Combine(executableFolder, options.OutputPath);
137197

138-
string labelsPath = Path.Combine(executableFolder, "SqueezeNet.Labels.txt");
139-
140198
if (!File.Exists(labelsPath))
141199
{
142200
throw new FileNotFoundException($"Labels file not found: {labelsPath}");

0 commit comments

Comments
 (0)