158 lines
5.2 KiB
C#
158 lines
5.2 KiB
C#
using MarketAlly.AIPlugin;
|
|
using MarketAlly.AIPlugin.Agentic;
|
|
using MarketAlly.AIPlugin.Agentic.Models;
|
|
using MarketAlly.AIPlugin.Conversation;
|
|
using MarketAlly.GitCommitEditor.Options;
|
|
|
|
namespace MarketAlly.GitCommitEditor.Services;
|
|
|
|
/// <summary>
|
|
/// Service for managing AI model providers dynamically based on configured API keys.
|
|
/// Only shows providers that the user has actually configured.
|
|
/// </summary>
|
|
public interface IModelProviderService
|
|
{
|
|
/// <summary>
|
|
/// Gets the providers that have API keys configured.
|
|
/// </summary>
|
|
IReadOnlyList<string> AvailableProviders { get; }
|
|
|
|
/// <summary>
|
|
/// Gets models for a provider that support function calling.
|
|
/// </summary>
|
|
IReadOnlyList<string> GetModelsForProvider(string provider);
|
|
|
|
/// <summary>
|
|
/// Gets models with display information for a provider.
|
|
/// </summary>
|
|
IReadOnlyList<ModelDisplayInfo> GetModelsWithInfoForProvider(string provider);
|
|
|
|
/// <summary>
|
|
/// Gets model info by ID.
|
|
/// </summary>
|
|
ModelDisplayInfo? GetModelInfo(string modelId);
|
|
|
|
/// <summary>
|
|
/// Gets the API key for a provider.
|
|
/// </summary>
|
|
string? GetApiKey(AIProvider provider);
|
|
|
|
/// <summary>
|
|
/// Checks if a provider has an API key configured.
|
|
/// </summary>
|
|
bool HasApiKey(AIProvider provider);
|
|
|
|
/// <summary>
|
|
/// Refreshes the available providers based on current API key configuration.
|
|
/// Call this after adding or removing an API key.
|
|
/// </summary>
|
|
void RefreshProviders();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Implementation of IModelProviderService that uses IApiKeyProvider to determine available providers.
|
|
/// Uses the static ModelLibrary.Default for efficient querying.
|
|
/// </summary>
|
|
public class ModelProviderService : IModelProviderService
|
|
{
|
|
private readonly IApiKeyProvider _apiKeyProvider;
|
|
|
|
public ModelProviderService(IApiKeyProvider apiKeyProvider)
|
|
{
|
|
_apiKeyProvider = apiKeyProvider;
|
|
}
|
|
|
|
public IReadOnlyList<string> AvailableProviders =>
|
|
ModelLibrary.Default.GetModelsWithFunctionCalling()
|
|
.Where(m => m.Type != ModelType.Legacy)
|
|
.Select(m => m.Provider.ToString())
|
|
.Distinct()
|
|
.OrderBy(p => p)
|
|
.ToList();
|
|
|
|
public void RefreshProviders()
|
|
{
|
|
// No-op - static library doesn't need refreshing
|
|
// API key changes are handled separately by IApiKeyProvider
|
|
}
|
|
|
|
public IReadOnlyList<string> GetModelsForProvider(string provider)
|
|
{
|
|
var aiProvider = ParseProvider(provider);
|
|
var models = ModelLibrary.Default.GetModelsByProvider(aiProvider)
|
|
.Where(m => m.SupportsFunctionCalling && m.Type != ModelType.Legacy)
|
|
.OrderByDescending(m => m.Tier)
|
|
.ThenBy(m => m.DisplayName)
|
|
.Select(m => m.ModelId)
|
|
.ToList();
|
|
|
|
return models.Count > 0 ? models : new[] { ModelConstants.Claude.Sonnet4 };
|
|
}
|
|
|
|
public IReadOnlyList<ModelDisplayInfo> GetModelsWithInfoForProvider(string provider)
|
|
{
|
|
var aiProvider = ParseProvider(provider);
|
|
var models = ModelLibrary.Default.GetModelsByProvider(aiProvider)
|
|
.Where(m => m.SupportsFunctionCalling && m.Type != ModelType.Legacy)
|
|
.OrderByDescending(m => m.Tier)
|
|
.ThenBy(m => m.DisplayName)
|
|
.Select(m => new ModelDisplayInfo
|
|
{
|
|
ModelId = m.ModelId,
|
|
DisplayName = m.DisplayName,
|
|
Tier = m.Tier.ToString(),
|
|
InputCostPer1MTokens = m.InputCostPer1MTokens,
|
|
OutputCostPer1MTokens = m.OutputCostPer1MTokens,
|
|
Speed = m.Speed.ToString()
|
|
})
|
|
.ToList();
|
|
|
|
return models.Count > 0 ? models : GetDefaultModelInfo();
|
|
}
|
|
|
|
public ModelDisplayInfo? GetModelInfo(string modelId)
|
|
{
|
|
var model = ModelLibrary.Default.GetModel(modelId);
|
|
if (model == null) return null;
|
|
|
|
return new ModelDisplayInfo
|
|
{
|
|
ModelId = model.ModelId,
|
|
DisplayName = model.DisplayName,
|
|
Tier = model.Tier.ToString(),
|
|
InputCostPer1MTokens = model.InputCostPer1MTokens,
|
|
OutputCostPer1MTokens = model.OutputCostPer1MTokens,
|
|
Speed = model.Speed.ToString()
|
|
};
|
|
}
|
|
|
|
public string? GetApiKey(AIProvider provider) => _apiKeyProvider.GetApiKey(provider);
|
|
|
|
public bool HasApiKey(AIProvider provider) => _apiKeyProvider.HasApiKey(provider);
|
|
|
|
private static IReadOnlyList<ModelDisplayInfo> GetDefaultModelInfo() => new[]
|
|
{
|
|
new ModelDisplayInfo
|
|
{
|
|
ModelId = ModelConstants.Claude.Sonnet4,
|
|
DisplayName = "Claude Sonnet 4",
|
|
Tier = "Balanced",
|
|
InputCostPer1MTokens = 3.00m,
|
|
OutputCostPer1MTokens = 15.00m,
|
|
Speed = "Fast"
|
|
}
|
|
};
|
|
|
|
private static AIProvider ParseProvider(string? provider)
|
|
{
|
|
return provider?.ToLowerInvariant() switch
|
|
{
|
|
"claude" or "anthropic" => AIProvider.Claude,
|
|
"openai" or "gpt" => AIProvider.OpenAI,
|
|
"gemini" or "google" => AIProvider.Gemini,
|
|
"qwen" or "alibaba" => AIProvider.Qwen,
|
|
_ => AIProvider.Claude
|
|
};
|
|
}
|
|
}
|