feat: Introduce a pluggable LLM provider system with token extraction, pricing, and updated setup configuration.
This commit is contained in:
48
Program.cs
48
Program.cs
@@ -1,4 +1,5 @@
|
||||
using System.ClientModel;
|
||||
using AnchorCli.Providers;
|
||||
using Microsoft.Extensions.AI;
|
||||
using OpenAI;
|
||||
using AnchorCli;
|
||||
@@ -15,10 +16,11 @@ if (args.Length > 0 && args[0].Equals("setup", StringComparison.OrdinalIgnoreCas
|
||||
}
|
||||
|
||||
// ── Config ──────────────────────────────────────────────────────────────
|
||||
const string endpoint = "https://openrouter.ai/api/v1";
|
||||
var cfg = AnchorConfig.Load();
|
||||
string apiKey = cfg.ApiKey;
|
||||
string model = cfg.Model;
|
||||
string provider = cfg.Provider ?? "openrouter";
|
||||
string endpoint = cfg.Endpoint ?? "https://openrouter.ai/api/v1";
|
||||
|
||||
if (string.IsNullOrWhiteSpace(apiKey))
|
||||
{
|
||||
@@ -26,28 +28,33 @@ if (string.IsNullOrWhiteSpace(apiKey))
|
||||
return;
|
||||
}
|
||||
|
||||
// ── Fetch model pricing from OpenRouter ─────────────────────────────────
|
||||
var pricingProvider = new PricingProvider();
|
||||
var tokenTracker = new TokenTracker();
|
||||
// ── Create token extractor for this provider ───────────────────────────
|
||||
var tokenExtractor = ProviderFactory.CreateTokenExtractorForEndpoint(endpoint);
|
||||
var tokenTracker = new TokenTracker { Provider = tokenExtractor.ProviderName };
|
||||
|
||||
// ── Fetch model pricing (only for supported providers) ─────────────────
|
||||
ModelInfo? modelInfo = null;
|
||||
await AnsiConsole.Status()
|
||||
.Spinner(Spinner.Known.BouncingBar)
|
||||
.SpinnerStyle(Style.Parse("cornflowerblue"))
|
||||
.StartAsync("Fetching model pricing...", async ctx =>
|
||||
{
|
||||
try
|
||||
if (ProviderFactory.IsOpenRouter(endpoint))
|
||||
{
|
||||
await AnsiConsole.Status()
|
||||
.Spinner(Spinner.Known.BouncingBar)
|
||||
.SpinnerStyle(Style.Parse("cornflowerblue"))
|
||||
.StartAsync("Fetching model pricing...", async ctx =>
|
||||
{
|
||||
modelInfo = await pricingProvider.GetModelInfoAsync(model);
|
||||
if (modelInfo?.Pricing != null)
|
||||
try
|
||||
{
|
||||
tokenTracker.InputPrice = PricingProvider.ParsePrice(modelInfo.Pricing.Prompt);
|
||||
tokenTracker.OutputPrice = PricingProvider.ParsePrice(modelInfo.Pricing.Completion);
|
||||
tokenTracker.RequestPrice = PricingProvider.ParsePrice(modelInfo.Pricing.Request);
|
||||
var pricingProvider = new OpenRouterProvider();
|
||||
modelInfo = await pricingProvider.GetModelInfoAsync(model);
|
||||
if (modelInfo?.Pricing != null)
|
||||
{
|
||||
tokenTracker.InputPrice = PricingProvider.ParsePrice(modelInfo.Pricing.Prompt);
|
||||
tokenTracker.OutputPrice = PricingProvider.ParsePrice(modelInfo.Pricing.Completion);
|
||||
tokenTracker.RequestPrice = PricingProvider.ParsePrice(modelInfo.Pricing.Request);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { /* pricing is best-effort */ }
|
||||
});
|
||||
catch { /* pricing is best-effort */ }
|
||||
});
|
||||
}
|
||||
|
||||
// ── Pretty header ───────────────────────────────────────────────────────
|
||||
AnsiConsole.Write(
|
||||
@@ -68,9 +75,12 @@ var infoTable = new Table()
|
||||
.AddColumn(new TableColumn("[dim]Value[/]"));
|
||||
|
||||
infoTable.AddRow("[grey]Model[/]", $"[cyan]{Markup.Escape(modelInfo?.Name ?? model)}[/]");
|
||||
infoTable.AddRow("[grey]Endpoint[/]", $"[blue]OpenRouter[/]");
|
||||
infoTable.AddRow("[grey]Provider[/]", $"[blue]{tokenExtractor.ProviderName}[/]");
|
||||
infoTable.AddRow("[grey]Endpoint[/]", $"[dim]{endpoint}[/]");
|
||||
infoTable.AddRow("[grey]CWD[/]", $"[green]{Markup.Escape(Environment.CurrentDirectory)}[/]");
|
||||
|
||||
if (modelInfo?.Pricing != null)
|
||||
|
||||
if (modelInfo?.Pricing != null)
|
||||
{
|
||||
var inM = tokenTracker.InputPrice * 1_000_000m;
|
||||
|
||||
Reference in New Issue
Block a user