using MarketAlly.GitCommitEditor.Models; using MarketAlly.GitCommitEditor.Options; using MarketAlly.GitCommitEditor.Resources; using MarketAlly.GitCommitEditor.Services; namespace MarketAlly.GitCommitEditor.Rewriters; /// /// Exception thrown when AI features are used without an API key configured. /// public class ApiKeyNotConfiguredException : InvalidOperationException { public ApiKeyNotConfiguredException() : base(Str.Service_ApiKeyNotConfigured) { } } /// /// Dynamic commit rewriter that uses AI when configured, or throws if not. /// public sealed class DynamicCommitRewriter : ICommitMessageRewriter, IDisposable { private readonly AiOptions _options; private readonly ICostTrackingService? _costTracker; private AICommitRewriter? _aiRewriter; private string? _lastApiKey; private string? _lastProvider; private string? _lastModel; public DynamicCommitRewriter(AiOptions options, ICostTrackingService? costTracker = null) { _options = options ?? throw new ArgumentNullException(nameof(options)); _costTracker = costTracker; } /// /// Returns true if an API key is configured and AI features are available. /// public bool IsConfigured => !string.IsNullOrEmpty(_options.ApiKey); private AICommitRewriter GetRewriter() { // If no API key, throw exception if (string.IsNullOrEmpty(_options.ApiKey)) { throw new ApiKeyNotConfiguredException(); } // Check if we need to create/recreate the AI rewriter // (API key, provider, or model changed) if (_aiRewriter == null || _lastApiKey != _options.ApiKey || _lastProvider != _options.Provider || _lastModel != _options.Model) { // Dispose old rewriter if exists _aiRewriter?.Dispose(); // Create new rewriter with current settings _aiRewriter = new AICommitRewriter(_options, _costTracker); _lastApiKey = _options.ApiKey; _lastProvider = _options.Provider; _lastModel = _options.Model; } return _aiRewriter; } public Task SuggestImprovedMessageAsync(CommitAnalysis analysis, CancellationToken ct = default) { return GetRewriter().SuggestImprovedMessageAsync(analysis, ct); } public Task> SuggestBatchAsync( IEnumerable analyses, IProgress? progress = null, CancellationToken ct = default) { return GetRewriter().SuggestBatchAsync(analyses, progress, ct); } public void Dispose() { _aiRewriter?.Dispose(); } }