-
Notifications
You must be signed in to change notification settings - Fork 235
Description
The IModel.DownloadAsync(Action<float>, CancellationToken) method accepts a CancellationToken parameter but does not properly check or honor cancellation requests during the download operation. This results in downloads continuing to completion even after cancellation has been explicitly requested, making it impossible to reliably cancel long-running model downloads.
Environment
- SDK: Microsoft.AI.Foundry.Local.WinML v0.8.2.1
- Platform: Windows
- Application: WinUI 3 Desktop App
- .NET Version: .NET 8.0
Problem
When calling model.DownloadAsync(progressCallback, cancellationToken) and subsequently canceling the operation via CancellationTokenSource.Cancel(), the download operation continues to execute until completion (100%) despite the cancellation request. The SDK only appears to check the cancellation token during the subsequent LoadAsync() operation, not during the actual download phase.
This behavior violates the standard .NET async cancellation pattern where operations accepting a CancellationToken should periodically check token.IsCancellationRequested and throw OperationCanceledException when cancellation is requested.
Repro Step
- Initialize a FoundryLocal catalog and obtain a model reference:
var catalog = await foundryManager.GetCatalogAsync();
var model = await catalog.GetModelAsync(alias);- Start a model download with cancellation support:
var cts = new CancellationTokenSource();
var downloadTask = model.DownloadAsync(
progressPercent => {
Debug.WriteLine($"Download progress: {progressPercent}%");
},
cts.Token
);- During download (e.g., at 50-60% progress), request cancellation:
cts.Cancel();- Observe that the download continues to 100% completion despite cancellation request.
Actual Behavior
Debug Output Evidence:
[FoundryClient] Download progress: 42.44%
[FoundryClient] Download progress: 49.79%
[App] User clicked cancel button
[App] Calling CancellationTokenSource.Cancel()
[App] CancellationToken AFTER Cancel: IsCancellationRequested: True
[FoundryClient] Progress callback - CancellationToken is REQUESTED!
[FoundryClient] Download progress: 57.19%
[FoundryClient] Download progress: 65.31%
[FoundryClient] Download progress: 73.44%
[FoundryClient] Download progress: 81.56%
[FoundryClient] Download progress: 89.69%
[FoundryClient] Download progress: 97.81%
[FoundryClient] Download progress: 100.00%
[FoundryClient] ========== SDK DownloadAsync COMPLETED ==========
[FoundryClient] Starting model preparation...
[FoundryClient] OperationCanceledException in PrepareModelAsync: The operation was canceled.
Key Observations:
CancellationToken.IsCancellationRequestedreturnsTrueimmediately afterCancel()is called- The progress callback continues to be invoked with increasing percentages (57% → 100%)
- Within the progress callback, we can verify
token.IsCancellationRequested == True, but the SDK ignores it DownloadAsynccompletes successfully without throwingOperationCanceledException- Only the subsequent
LoadAsync()operation respects the cancellation token
Expected Behavior
When CancellationToken.Cancel() is called:
- The SDK should detect
cancellationToken.IsCancellationRequested == Trueduring the download loop DownloadAsyncshould stop the download operation promptlyDownloadAsyncshould throwOperationCanceledExceptionto signal cancellation- Partial downloads should be cleaned up appropriately
- The operation should not continue to 100% completion
This aligns with the standard .NET async cancellation pattern documented in Microsoft's async programming guidelines.
Impact
- Users cannot cancel large model downloads, leading to wasted bandwidth and storage
- UI "Cancel" buttons appear non-functional, creating confusion
- No way to interrupt mistaken downloads of large models (multi-GB files)
- Applications cannot implement reliable cancellation for download operations
- Poor user experience in applications using Foundry Local
Related Code References
IModel.DownloadAsync(Action<float>, CancellationToken)- Primary method affectedIModel.LoadAsync(CancellationToken)- Correctly honors cancellation
Additional Context
This issue was discovered during user testing when users attempted to cancel large model downloads. The debug logging was added specifically to trace the cancellation flow, confirming that the application code correctly propagates the cancellation token through all layers, but the SDK's DownloadAsync method does not check it.
The fact that LoadAsync() properly throws OperationCanceledException when the token is canceled demonstrates that the SDK infrastructure supports cancellation - it just needs to be implemented consistently across all async operations, particularly DownloadAsync.
Thank you for considering this issue. Proper cancellation support is critical for production applications managing large model downloads. Please let me know if you need any additional information or testing.