Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 26 additions & 18 deletions sdk/cs/src/FoundryLocalManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,30 +258,38 @@ public async Task<List<ModelInfo>> ListCachedModelsAsync(CancellationToken ct =
IgnorePipeReport = true
};

var response = await _serviceClient!.PostAsJsonAsync("/openai/download", request, ct);
response.EnsureSuccessStatusCode();
var responseBody = await response.Content.ReadAsStringAsync(ct);

// Find the last '{' to get the start of the JSON object
var jsonStart = responseBody.LastIndexOf('{');
if (jsonStart == -1)
try
{
throw new InvalidOperationException("No JSON object found in response.");
}
var response = await _serviceClient!.PostAsJsonAsync("/openai/download", request, ct);
response.EnsureSuccessStatusCode();
var responseBody = await response.Content.ReadAsStringAsync(ct);

var jsonPart = responseBody[jsonStart..];
// Find the last '{' to get the start of the JSON object
var jsonStart = responseBody.LastIndexOf('{');
if (jsonStart == -1)
{
throw new InvalidOperationException("No JSON object found in response.");
}

// Parse the JSON part
using var jsonDoc = JsonDocument.Parse(jsonPart);
var success = jsonDoc.RootElement.GetProperty("success").GetBoolean();
var errorMessage = jsonDoc.RootElement.GetProperty("errorMessage").GetString();
var jsonPart = responseBody[jsonStart..];

if (!success)
// Parse the JSON part
using var jsonDoc = JsonDocument.Parse(jsonPart);
var success = jsonDoc.RootElement.GetProperty("success").GetBoolean();
var errorMessage = jsonDoc.RootElement.GetProperty("errorMessage").GetString();

if (!success)
{
throw new InvalidOperationException($"Failed to download model: {errorMessage}");
}

return modelInfo;
}
catch (Exception)
{
throw new InvalidOperationException($"Failed to download model: {errorMessage}");
Console.WriteLine($"Failed to download model from: {modelInfo.Uri}");
throw;
}

return modelInfo;
}

public async Task<bool> IsModelUpgradeableAsync(string aliasOrModelId, DeviceType? device = null, CancellationToken ct = default)
Expand Down
64 changes: 64 additions & 0 deletions sdk/cs/test/FoundryLocal.Tests/DownloadExceptionTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright company="Microsoft">
// Copyright (c) Microsoft. All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace Microsoft.AI.Foundry.Local.Tests;

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AI.Foundry.Local;
using RichardSzalay.MockHttp;
using Xunit;

public class DownloadExceptionTest
{
[Fact]
public async Task DownloadModelAsync_RethrowsException_AndPrintsUrl()
{
// Arrange
using var mockHttp = new MockHttpMessageHandler();
var client = mockHttp.ToHttpClient();
client.BaseAddress = new Uri("http://localhost:5272");

using var manager = new FoundryLocalManager();

// Inject the mock client
typeof(FoundryLocalManager)
.GetField("_serviceUri", BindingFlags.NonPublic | BindingFlags.Instance)!
.SetValue(manager, client.BaseAddress);

typeof(FoundryLocalManager)
.GetField("_serviceClient", BindingFlags.NonPublic | BindingFlags.Instance)!
.SetValue(manager, client);

// Mock catalog to return a model
var modelInfo = new ModelInfo
{
ModelId = "test-model:1",
Uri = "https://example.com/model.onnx",
Alias = "test-model",
Runtime = new Runtime { DeviceType = DeviceType.CPU }
};
var catalog = new List<ModelInfo> { modelInfo };
var catalogJson = JsonSerializer.Serialize(catalog, ModelGenerationContext.Default.ListModelInfo);

mockHttp.When(HttpMethod.Get, "/foundry/list").Respond("application/json", catalogJson);
mockHttp.When(HttpMethod.Get, "/openai/models").Respond("application/json", "[]");

// Mock download to throw an exception
mockHttp.When(HttpMethod.Post, "/openai/download").Throw(new HttpRequestException("SSL connection failed"));

// Act & Assert
var ex = await Assert.ThrowsAsync<HttpRequestException>(() => manager.DownloadModelAsync("test-model"));
Assert.Equal("SSL connection failed", ex.Message);

// Note: Verifying Console.WriteLine is difficult in this setup without redirecting Console.Out,
// but we verified the exception is rethrown.
}
}