< Summary

Information
Class: GistBackend.Handlers.AIHandler.SummaryForRecap
Assembly: GistBackend
File(s): /home/runner/work/the-gist-of-it-sec/the-gist-of-it-sec/backend/GistBackend/Handlers/AIHandler/AIHandler.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 1
Coverable lines: 1
Total lines: 87
Line coverage: 0%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Title()100%210%

File(s)

/home/runner/work/the-gist-of-it-sec/the-gist-of-it-sec/backend/GistBackend/Handlers/AIHandler/AIHandler.cs

#LineLine coverage
 1using System.Net.Http.Json;
 2using GistBackend.Exceptions;
 3using GistBackend.Types;
 4using GistBackend.Utils;
 5using Microsoft.Extensions.Options;
 6using SharpToken;
 7
 8namespace GistBackend.Handlers.AIHandler;
 9
 10public record SummarizeRequest(string Title, string Article, string Language);
 11
 012public record SummaryForRecap(string Title, string Summary, int Id);
 13
 14public record RecapRequest(List<SummaryForRecap> Summaries, string RecapType);
 15
 16public interface IAIHandler
 17{
 18    public Task<float[]> GenerateEmbeddingAsync(string input, CancellationToken ct);
 19    public Task<SummaryAIResponse> GenerateSummaryAIResponseAsync(Language feedLanguage, string title, string article,
 20        CancellationToken ct);
 21    public Task<RecapAIResponse> GenerateDailyRecapAsync(IEnumerable<ConstructedGist> gists, CancellationToken ct);
 22    public Task<RecapAIResponse> GenerateWeeklyRecapAsync(IEnumerable<ConstructedGist> gists, CancellationToken ct);
 23}
 24
 25public class AIHandler : IAIHandler
 26{
 27    private readonly IEmbeddingClientHandler _embeddingClientHandler;
 28    private readonly HttpClient _httpClient;
 29    private readonly GptEncoding _encoding;
 30
 31    public AIHandler(IEmbeddingClientHandler embeddingClientHandler, HttpClient httpClient,
 32        IOptions<AIHandlerOptions> options)
 33    {
 34        _embeddingClientHandler = embeddingClientHandler;
 35        _encoding = GptEncoding.GetEncodingForModel(embeddingClientHandler.Model);
 36        _httpClient = httpClient;
 37        _httpClient.BaseAddress = new Uri(options.Value.Host);
 38    }
 39
 40    public Task<float[]> GenerateEmbeddingAsync(string input, CancellationToken ct)
 41    {
 42        // Tokenize and truncate to stay under 8k context; keep a small buffer for prompts.
 43        const int maxTokens = 7500;
 44        var tokens = _encoding.Encode(input);
 45        var safeInput = tokens.Count > maxTokens ? _encoding.Decode(tokens.Take(maxTokens)) : input;
 46
 47        return _embeddingClientHandler.GenerateEmbeddingAsync(safeInput, ct);
 48    }
 49
 50    public async Task<SummaryAIResponse> GenerateSummaryAIResponseAsync(Language feedLanguage, string title,
 51        string article, CancellationToken ct)
 52    {
 53        var request = new SummarizeRequest(title, article, feedLanguage.ToString());
 54        var response = await _httpClient.PostAsJsonAsync("/summarize", request, SerializerDefaults.JsonOptions, ct);
 55        if (!response.IsSuccessStatusCode)
 56        {
 57            throw new ExternalServiceException($"Failed to get summary from AI API: {response.StatusCode}");
 58        }
 59
 60        var aiResponse =
 61            await response.Content.ReadFromJsonAsync<SummaryAIResponse>(cancellationToken: ct,
 62                options: SerializerDefaults.JsonOptions);
 63        return aiResponse ?? throw new ExternalServiceException("Could not parse summary AI response");
 64    }
 65
 66    public Task<RecapAIResponse> GenerateDailyRecapAsync(IEnumerable<ConstructedGist> gists, CancellationToken ct) =>
 67        GenerateRecapAsync(gists, RecapType.Daily, ct);
 68
 69    public Task<RecapAIResponse> GenerateWeeklyRecapAsync(IEnumerable<ConstructedGist> gists, CancellationToken ct) =>
 70        GenerateRecapAsync(gists, RecapType.Weekly, ct);
 71
 72    private async Task<RecapAIResponse> GenerateRecapAsync(IEnumerable<ConstructedGist> gists, RecapType recapType,
 73        CancellationToken ct)
 74    {
 75        var summaries = gists.Select(gist => new SummaryForRecap(gist.Title, gist.Summary, gist.Id)).ToList();
 76        var request = new RecapRequest(summaries, recapType.ToString());
 77        var response = await _httpClient.PostAsJsonAsync("/recap", request, SerializerDefaults.JsonOptions, ct);
 78        if (!response.IsSuccessStatusCode)
 79        {
 80            throw new ExternalServiceException($"Failed to get recap from AI API: {response.StatusCode}");
 81        }
 82        var aiResponse =
 83            await response.Content.ReadFromJsonAsync<RecapAIResponse>(cancellationToken: ct,
 84                options: SerializerDefaults.JsonOptions);
 85        return aiResponse ?? throw new ExternalServiceException("Could not parse recap AI response");
 86    }
 87}

Methods/Properties

get_Title()