< Summary

Information
Class: Orchestrator.Infrastructure.Factories.OpenAiServiceFactory
Assembly: Orchestrator
File(s): /home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Infrastructure/Factories/OpenAiServiceFactory.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 44
Coverable lines: 44
Total lines: 110
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 16
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)0%620%
CreatePredictionService(...)100%210%
GetTokenUsageTracker()0%2040%
GetApiKeyFromEnvironment()0%620%
GetOrCreateCostCalculationService()0%2040%
GetOrCreateInstructionsTemplateProvider()0%2040%

File(s)

/home/runner/work/KicktippAi/KicktippAi/src/Orchestrator/Infrastructure/Factories/OpenAiServiceFactory.cs

#LineLine coverage
 1using System.Collections.Concurrent;
 2using EHonda.KicktippAi.Core;
 3using Microsoft.Extensions.Logging;
 4using OpenAI.Chat;
 5using OpenAiIntegration;
 6
 7namespace Orchestrator.Infrastructure.Factories;
 8
 9/// <summary>
 10/// Default implementation of <see cref="IOpenAiServiceFactory"/>.
 11/// </summary>
 12/// <remarks>
 13/// Reads the API key from OPENAI_API_KEY environment variable.
 14/// Caches services by model to avoid recreating them for repeated requests.
 15/// The <see cref="ITokenUsageTracker"/> is shared across all prediction services.
 16/// </remarks>
 17public sealed class OpenAiServiceFactory : IOpenAiServiceFactory
 18{
 19    private readonly ILoggerFactory _loggerFactory;
 20    private readonly Lazy<string> _apiKey;
 021    private readonly ConcurrentDictionary<string, IPredictionService> _predictionServiceCache = new();
 22    private ITokenUsageTracker? _tokenUsageTracker;
 23    private ICostCalculationService? _costCalculationService;
 24    private IInstructionsTemplateProvider? _instructionsTemplateProvider;
 025    private readonly object _lock = new();
 26
 027    public OpenAiServiceFactory(ILoggerFactory loggerFactory)
 28    {
 029        _loggerFactory = loggerFactory;
 030        _apiKey = new Lazy<string>(GetApiKeyFromEnvironment);
 031    }
 32
 33    /// <inheritdoc />
 34    public IPredictionService CreatePredictionService(string model)
 35    {
 036        ArgumentException.ThrowIfNullOrWhiteSpace(model);
 37
 038        var apiKey = _apiKey.Value;
 39
 40        // Cache key includes model to handle different configurations
 041        return _predictionServiceCache.GetOrAdd(model, _ =>
 042        {
 043            var logger = _loggerFactory.CreateLogger<PredictionService>();
 044            var chatClient = new ChatClient(model, apiKey);
 045
 046            return new PredictionService(
 047                chatClient,
 048                logger,
 049                GetOrCreateCostCalculationService(),
 050                GetTokenUsageTracker(),
 051                GetOrCreateInstructionsTemplateProvider(),
 052                model);
 053        });
 54    }
 55
 56    /// <inheritdoc />
 57    public ITokenUsageTracker GetTokenUsageTracker()
 58    {
 059        if (_tokenUsageTracker == null)
 60        {
 061            lock (_lock)
 62            {
 063                _tokenUsageTracker ??= new TokenUsageTracker(
 064                    _loggerFactory.CreateLogger<TokenUsageTracker>(),
 065                    GetOrCreateCostCalculationService());
 066            }
 67        }
 68
 069        return _tokenUsageTracker;
 70    }
 71
 72    private static string GetApiKeyFromEnvironment()
 73    {
 074        var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");
 075        if (string.IsNullOrWhiteSpace(apiKey))
 76        {
 077            throw new InvalidOperationException("OPENAI_API_KEY environment variable is required");
 78        }
 79
 080        return apiKey;
 81    }
 82
 83    private ICostCalculationService GetOrCreateCostCalculationService()
 84    {
 085        if (_costCalculationService == null)
 86        {
 087            lock (_lock)
 88            {
 089                _costCalculationService ??= new CostCalculationService(
 090                    _loggerFactory.CreateLogger<CostCalculationService>());
 091            }
 92        }
 93
 094        return _costCalculationService;
 95    }
 96
 97    private IInstructionsTemplateProvider GetOrCreateInstructionsTemplateProvider()
 98    {
 099        if (_instructionsTemplateProvider == null)
 100        {
 0101            lock (_lock)
 102            {
 0103                _instructionsTemplateProvider ??= new InstructionsTemplateProvider(
 0104                    PromptsFileProvider.Create());
 0105            }
 106        }
 107
 0108        return _instructionsTemplateProvider;
 109    }
 110}