File: GitHubModelsHealthCheck.cs
Web Access
Project: src\src\Aspire.Hosting.GitHub.Models\Aspire.Hosting.GitHub.Models.csproj (Aspire.Hosting.GitHub.Models)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Data.Common;
using System.Net;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Diagnostics.HealthChecks;
 
namespace Aspire.Hosting.GitHub.Models;
 
/// <summary>
/// A health check for GitHub Models resources.
/// </summary>
/// <param name="httpClient">The HttpClient to use.</param>
/// <param name="connectionString">The connection string.</param>
internal sealed class GitHubModelsHealthCheck(HttpClient httpClient, Func<ValueTask<string?>> connectionString) : IHealthCheck
{
    private HealthCheckResult? _result;
 
    /// <summary>
    /// Checks the health of the GitHub Models endpoint by sending a test request.
    /// </summary>
    /// <param name="context">The health check context.</param>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <returns>A task that represents the asynchronous health check operation.</returns>
    public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        if (_result is not null)
        {
            return _result.Value;
        }
 
        try
        {
            var builder = new DbConnectionStringBuilder() { ConnectionString = await connectionString().ConfigureAwait(false) };
 
            using var request = new HttpRequestMessage(HttpMethod.Post, new Uri($"{builder["Endpoint"]}/chat/completions"));
 
            // Add required headers
            request.Headers.Add("Accept", "application/vnd.github+json");
            request.Headers.Add("Authorization", $"Bearer {builder["Key"]}");
            request.Headers.Add("X-GitHub-Api-Version", "2022-11-28");
 
            // Create test payload with empty messages to minimize API usage
            var payload = new
            {
                model = builder["Model"]?.ToString(),
                messages = Array.Empty<object>()
            };
 
            var jsonPayload = JsonSerializer.Serialize(payload);
            request.Content = new StringContent(jsonPayload, System.Text.Encoding.UTF8, "application/json");
 
            using var response = await httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
 
            _result = response.StatusCode switch
            {
                HttpStatusCode.Unauthorized => HealthCheckResult.Unhealthy("GitHub Models API key is invalid or has insufficient permissions"),
                HttpStatusCode.NotFound or HttpStatusCode.Forbidden or HttpStatusCode.BadRequest => await HandleErrorCode(response, cancellationToken).ConfigureAwait(false),
                _ => HealthCheckResult.Unhealthy($"GitHub Models endpoint returned unexpected status code: {response.StatusCode}")
            };
        }
        catch (Exception ex)
        {
            _result = HealthCheckResult.Unhealthy($"Failed to check GitHub Models endpoint: {ex.Message}", ex);
        }
 
        return _result.Value;
    }
 
    private static async Task<HealthCheckResult> HandleErrorCode(HttpResponseMessage response, CancellationToken cancellationToken)
    {
        GitHubErrorResponse? errorResponse = null;
 
        try
        {
            var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
            errorResponse = JsonSerializer.Deserialize<GitHubErrorResponse>(content);
 
            if (errorResponse?.Error?.Code == "unknown_model")
            {
                var message = !string.IsNullOrEmpty(errorResponse.Error.Message)
                    ? errorResponse.Error.Message
                    : "Unknown model";
                return HealthCheckResult.Unhealthy($"GitHub Models: {message}");
            }
            else if (errorResponse?.Error?.Code == "empty_array")
            {
                return HealthCheckResult.Healthy();
            }
            else if (errorResponse?.Error?.Code == "no_access")
            {
                return HealthCheckResult.Unhealthy($"GitHub Models: {errorResponse.Error.Message}");
            }
        }
        catch
        {
        }
 
        return HealthCheckResult.Unhealthy($"GitHub Models returned an unsupported response: ({response.StatusCode}) {errorResponse?.Error?.Message}");
    }
 
    /// <summary>
    /// Represents the error response from GitHub Models API.
    /// </summary>
    private sealed class GitHubErrorResponse
    {
        [JsonPropertyName("error")]
        public GitHubError? Error { get; set; }
    }
 
    /// <summary>
    /// Represents an error from GitHub Models API.
    /// </summary>
    private sealed class GitHubError
    {
        [JsonPropertyName("code")]
        public string? Code { get; set; }
 
        [JsonPropertyName("message")]
        public string? Message { get; set; }
 
        [JsonPropertyName("details")]
        public string? Details { get; set; }
    }
}