diff --git a/src/Components/Aspire.Azure.AI.Inference/AspireAzureAIInferenceExtensions.cs b/src/Components/Aspire.Azure.AI.Inference/AspireAzureAIInferenceExtensions.cs index 9324efc1e6f..93a87d9e0a6 100644 --- a/src/Components/Aspire.Azure.AI.Inference/AspireAzureAIInferenceExtensions.cs +++ b/src/Components/Aspire.Azure.AI.Inference/AspireAzureAIInferenceExtensions.cs @@ -186,10 +186,10 @@ protected override void BindSettingsToConfiguration(ChatCompletionsClientSetting => configuration.Bind(settings); protected override IHealthCheck CreateHealthCheck(ChatCompletionsClient client, ChatCompletionsClientSettings settings) - => throw new NotImplementedException(); + => new AzureAIInferenceChatCompletionsHealthCheck(client); protected override bool GetHealthCheckEnabled(ChatCompletionsClientSettings settings) - => false; + => !settings.DisableHealthChecks; protected override bool GetMetricsEnabled(ChatCompletionsClientSettings settings) => !settings.DisableMetrics; @@ -439,10 +439,10 @@ protected override void BindSettingsToConfiguration(ChatCompletionsClientSetting => configuration.Bind(settings); protected override IHealthCheck CreateHealthCheck(EmbeddingsClient client, ChatCompletionsClientSettings settings) - => throw new NotImplementedException(); + => new AzureAIInferenceEmbeddingsHealthCheck(client); protected override bool GetHealthCheckEnabled(ChatCompletionsClientSettings settings) - => false; + => !settings.DisableHealthChecks; protected override bool GetMetricsEnabled(ChatCompletionsClientSettings settings) => !settings.DisableMetrics; diff --git a/src/Components/Aspire.Azure.AI.Inference/AzureAIInferenceChatCompletionsHealthCheck.cs b/src/Components/Aspire.Azure.AI.Inference/AzureAIInferenceChatCompletionsHealthCheck.cs new file mode 100644 index 00000000000..1f67869a5a9 --- /dev/null +++ b/src/Components/Aspire.Azure.AI.Inference/AzureAIInferenceChatCompletionsHealthCheck.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Azure.AI.Inference; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace Aspire.Azure.AI.Inference; + +internal sealed class AzureAIInferenceChatCompletionsHealthCheck : IHealthCheck +{ + private readonly ChatCompletionsClient _client; + + public AzureAIInferenceChatCompletionsHealthCheck(ChatCompletionsClient client) + => _client = client; + + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + await _client.GetModelInfoAsync(cancellationToken).ConfigureAwait(false); + return HealthCheckResult.Healthy(); + } + catch (Exception ex) + { + return new HealthCheckResult(context.Registration.FailureStatus, exception: ex); + } + } +} diff --git a/src/Components/Aspire.Azure.AI.Inference/AzureAIInferenceEmbeddingsHealthCheck.cs b/src/Components/Aspire.Azure.AI.Inference/AzureAIInferenceEmbeddingsHealthCheck.cs new file mode 100644 index 00000000000..b4c42a6d5ae --- /dev/null +++ b/src/Components/Aspire.Azure.AI.Inference/AzureAIInferenceEmbeddingsHealthCheck.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Azure.AI.Inference; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace Aspire.Azure.AI.Inference; + +internal sealed class AzureAIInferenceEmbeddingsHealthCheck : IHealthCheck +{ + private readonly EmbeddingsClient _client; + + public AzureAIInferenceEmbeddingsHealthCheck(EmbeddingsClient client) + => _client = client; + + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + await _client.GetModelInfoAsync(cancellationToken).ConfigureAwait(false); + return HealthCheckResult.Healthy(); + } + catch (Exception ex) + { + return new HealthCheckResult(context.Registration.FailureStatus, exception: ex); + } + } +} diff --git a/src/Components/Aspire.Azure.AI.Inference/ChatCompletionsClientSettings.cs b/src/Components/Aspire.Azure.AI.Inference/ChatCompletionsClientSettings.cs index e9f0a355c21..d77844f33c1 100644 --- a/src/Components/Aspire.Azure.AI.Inference/ChatCompletionsClientSettings.cs +++ b/src/Components/Aspire.Azure.AI.Inference/ChatCompletionsClientSettings.cs @@ -40,6 +40,14 @@ public sealed class ChatCompletionsClientSettings : IConnectionStringSettings /// public string? Key { get; set; } + /// + /// Gets or sets a boolean value that indicates whether the health check is disabled or not. + /// + /// + /// The default value is . + /// + public bool DisableHealthChecks { get; set; } + /// /// Gets or sets a boolean value that indicates whether the OpenTelemetry metrics are enabled or not. /// diff --git a/src/Components/Aspire.Azure.AI.Inference/ConfigurationSchema.json b/src/Components/Aspire.Azure.AI.Inference/ConfigurationSchema.json index 19784762f63..c71a8c27e62 100644 --- a/src/Components/Aspire.Azure.AI.Inference/ConfigurationSchema.json +++ b/src/Components/Aspire.Azure.AI.Inference/ConfigurationSchema.json @@ -122,6 +122,11 @@ "type": "string", "description": "Gets or sets the name of the AI model deployment to use for chat completions." }, + "DisableHealthChecks": { + "type": "boolean", + "description": "Gets or sets a boolean value that indicates whether the health check is disabled or not.", + "default": false + }, "DisableMetrics": { "type": "boolean", "description": "Gets or sets a boolean value that indicates whether the OpenTelemetry metrics are enabled or not.", diff --git a/src/Components/Aspire.Azure.AI.Inference/api/Aspire.Azure.AI.Inference.cs b/src/Components/Aspire.Azure.AI.Inference/api/Aspire.Azure.AI.Inference.cs index 99136031095..97e793d61f1 100644 --- a/src/Components/Aspire.Azure.AI.Inference/api/Aspire.Azure.AI.Inference.cs +++ b/src/Components/Aspire.Azure.AI.Inference/api/Aspire.Azure.AI.Inference.cs @@ -44,6 +44,8 @@ public sealed partial class ChatCompletionsClientSettings public string? DeploymentName { get { throw null; } set { } } + public bool DisableHealthChecks { get { throw null; } set { } } + public bool DisableMetrics { get { throw null; } set { } } public bool DisableTracing { get { throw null; } set { } } diff --git a/src/Components/Aspire.Azure.AI.OpenAI/AspireAzureOpenAIExtensions.cs b/src/Components/Aspire.Azure.AI.OpenAI/AspireAzureOpenAIExtensions.cs index 76e07b2f791..d0451765642 100644 --- a/src/Components/Aspire.Azure.AI.OpenAI/AspireAzureOpenAIExtensions.cs +++ b/src/Components/Aspire.Azure.AI.OpenAI/AspireAzureOpenAIExtensions.cs @@ -131,7 +131,11 @@ protected override void BindSettingsToConfiguration(AzureOpenAISettings settings protected override IHealthCheck CreateHealthCheck(AzureOpenAIClient client, AzureOpenAISettings settings) { - throw new NotImplementedException(); + // Azure OpenAI does not expose a lightweight read-only health check endpoint. + // GetOpenAIModelClient() explicitly throws NotSupportedException in AzureOpenAIClient, + // and all other sub-clients require a deployment name and initiate inference operations. + // Health checks remain disabled until a suitable API is available in the Azure.AI.OpenAI SDK. + throw new NotSupportedException("Health checks are not supported for AzureOpenAIClient."); } protected override bool GetHealthCheckEnabled(AzureOpenAISettings settings) diff --git a/tests/Aspire.Azure.AI.Inference.Tests/ConformanceTests.cs b/tests/Aspire.Azure.AI.Inference.Tests/ConformanceTests.cs index 537366d0089..6ae22e616a1 100644 --- a/tests/Aspire.Azure.AI.Inference.Tests/ConformanceTests.cs +++ b/tests/Aspire.Azure.AI.Inference.Tests/ConformanceTests.cs @@ -70,7 +70,7 @@ void ConfigureCredentials(ChatCompletionsClientSettings settings) } protected override void SetHealthCheck(ChatCompletionsClientSettings options, bool enabled) - => throw new NotImplementedException(); + => options.DisableHealthChecks = !enabled; protected override void SetMetrics(ChatCompletionsClientSettings options, bool enabled) => options.DisableMetrics = !enabled;