From 21d99a5916ba64a4da7684514e7dd5d54b5a4035 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Tue, 28 Jan 2025 15:53:59 +0000 Subject: [PATCH 1/2] Ensure the Ollama clients validate HTTP status codes. --- .../OllamaChatClient.cs | 11 ++++++ .../OllamaEmbeddingGenerator.cs | 5 +++ .../OllamaUtilities.cs | 38 +++++++++++++++++++ .../OllamaChatClientIntegrationTests.cs | 18 +++++++++ ...llamaEmbeddingGeneratorIntegrationTests.cs | 17 +++++++++ 5 files changed, 89 insertions(+) diff --git a/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaChatClient.cs b/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaChatClient.cs index 608573e05f6..6bdd924b87a 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaChatClient.cs +++ b/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaChatClient.cs @@ -87,6 +87,11 @@ public async Task CompleteAsync(IList chatMessages, JsonContext.Default.OllamaChatRequest, cancellationToken).ConfigureAwait(false); + if (!httpResponse.IsSuccessStatusCode) + { + await OllamaUtilities.ThrowUnsuccessfulOllamaResponseAsync(httpResponse, cancellationToken).ConfigureAwait(false); + } + var response = (await httpResponse.Content.ReadFromJsonAsync( JsonContext.Default.OllamaChatResponse, cancellationToken).ConfigureAwait(false))!; @@ -117,6 +122,12 @@ public async IAsyncEnumerable CompleteStreamingAs Content = JsonContent.Create(ToOllamaChatRequest(chatMessages, options, stream: true), JsonContext.Default.OllamaChatRequest) }; using var httpResponse = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + + if (!httpResponse.IsSuccessStatusCode) + { + await OllamaUtilities.ThrowUnsuccessfulOllamaResponseAsync(httpResponse, cancellationToken).ConfigureAwait(false); + } + using var httpResponseStream = await httpResponse.Content #if NET .ReadAsStreamAsync(cancellationToken) diff --git a/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaEmbeddingGenerator.cs b/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaEmbeddingGenerator.cs index 5377b5f7092..ac5bd608dc7 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaEmbeddingGenerator.cs +++ b/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaEmbeddingGenerator.cs @@ -110,6 +110,11 @@ public async Task>> GenerateAsync( JsonContext.Default.OllamaEmbeddingRequest, cancellationToken).ConfigureAwait(false); + if (!httpResponse.IsSuccessStatusCode) + { + await OllamaUtilities.ThrowUnsuccessfulOllamaResponseAsync(httpResponse, cancellationToken).ConfigureAwait(false); + } + var response = (await httpResponse.Content.ReadFromJsonAsync( JsonContext.Default.OllamaEmbeddingResponse, cancellationToken).ConfigureAwait(false))!; diff --git a/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaUtilities.cs b/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaUtilities.cs index d7db10e5a04..ea2625bd50e 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaUtilities.cs +++ b/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaUtilities.cs @@ -2,8 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Net.Http; +using System.Text.Json; using System.Threading; +using System.Threading.Tasks; namespace Microsoft.Extensions.AI; @@ -31,4 +35,38 @@ public static void TransferNanosecondsTime(TResponse response, Func(() => chatClient.CompleteAsync("Hello, world!")); + Assert.Contains("inexistent-model", ex.Message); + + ex = await Assert.ThrowsAsync(() => chatClient.CompleteStreamingAsync("Hello, world!").ToChatCompletionAsync()); + Assert.Contains("inexistent-model", ex.Message); + } + private sealed class AssertNoToolsDefinedChatClient(IChatClient innerClient) : DelegatingChatClient(innerClient) { public override Task CompleteAsync( diff --git a/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaEmbeddingGeneratorIntegrationTests.cs b/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaEmbeddingGeneratorIntegrationTests.cs index 4333cbde636..086b2a06660 100644 --- a/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaEmbeddingGeneratorIntegrationTests.cs +++ b/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaEmbeddingGeneratorIntegrationTests.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Threading.Tasks; +using Xunit; namespace Microsoft.Extensions.AI; @@ -11,4 +13,19 @@ public class OllamaEmbeddingGeneratorIntegrationTests : EmbeddingGeneratorIntegr IntegrationTestHelpers.GetOllamaUri() is Uri endpoint ? new OllamaEmbeddingGenerator(endpoint, "all-minilm") : null; + + [Fact] + public async Task InvalidModelParameter_ThrowsInvalidOperationException() + { + SkipIfNotEnabled(); + + var endpoint = IntegrationTestHelpers.GetOllamaUri(); + Assert.NotNull(endpoint); + + using var generator = new OllamaEmbeddingGenerator(endpoint, modelId: "inexistent-model"); + + InvalidOperationException ex; + ex = await Assert.ThrowsAsync(() => generator.GenerateAsync(["Hello, world!"])); + Assert.Contains("inexistent-model", ex.Message); + } } From 7e3c93b4bc008d32fdab52861bce3b98369ffe80 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Tue, 28 Jan 2025 17:51:17 +0000 Subject: [PATCH 2/2] Apply the correct test attribute. --- .../OllamaChatClientIntegrationTests.cs | 2 +- .../OllamaEmbeddingGeneratorIntegrationTests.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaChatClientIntegrationTests.cs b/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaChatClientIntegrationTests.cs index ce02bb7c9bd..178411a7d13 100644 --- a/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaChatClientIntegrationTests.cs +++ b/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaChatClientIntegrationTests.cs @@ -87,7 +87,7 @@ public async Task PromptBasedFunctionCalling_WithArgs() Assert.False(didCallIrrelevantTool); } - [Fact] + [ConditionalFact] public async Task InvalidModelParameter_ThrowsInvalidOperationException() { SkipIfNotEnabled(); diff --git a/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaEmbeddingGeneratorIntegrationTests.cs b/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaEmbeddingGeneratorIntegrationTests.cs index 086b2a06660..493c0bf0333 100644 --- a/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaEmbeddingGeneratorIntegrationTests.cs +++ b/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaEmbeddingGeneratorIntegrationTests.cs @@ -3,6 +3,7 @@ using System; using System.Threading.Tasks; +using Microsoft.TestUtilities; using Xunit; namespace Microsoft.Extensions.AI; @@ -14,7 +15,7 @@ public class OllamaEmbeddingGeneratorIntegrationTests : EmbeddingGeneratorIntegr new OllamaEmbeddingGenerator(endpoint, "all-minilm") : null; - [Fact] + [ConditionalFact] public async Task InvalidModelParameter_ThrowsInvalidOperationException() { SkipIfNotEnabled();