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();
}
}