using Microsoft.Extensions.Options; using CvMatcher.Models.Settings; using CvMatcher.Data.Repositories.Contracts; using Api.Clients.Ai.Contracts; using CommonHelpers; namespace Api.Clients.Ai; public sealed class CachedMatcherAiClient : IMatcherAiClient { private readonly MatcherAiClient _client; private readonly IMatcherRepository _repository; private readonly AiSettings _settings; public CachedMatcherAiClient(MatcherAiClient client, IMatcherRepository repository, IOptions options) { _client = client; _repository = repository; _settings = options.Value; } public async Task CreateChatCompletionAsync(string systemPrompt, string userPrompt, decimal temperature, CancellationToken ct) { var model = GetChatModel(); var cacheKey = HashHelper.Compute($"chat:{_settings.Provider}:{model}:{temperature:0.00}:{systemPrompt}:{userPrompt}"); var cached = await _repository.GetChatCompletionAsync(cacheKey, ct); if (cached is not null) return cached; var response = await _client.CreateChatCompletionAsync(systemPrompt, userPrompt, temperature, ct); await _repository.SaveChatCompletionAsync(cacheKey, model, temperature, response, ct); return response; } private string GetChatModel() => string.Equals(_settings.Provider, "Ollama", StringComparison.OrdinalIgnoreCase) ? _settings.Ollama.ChatModel : _settings.OpenAI.ChatModel; }