using System.Globalization; using System.Net.Http.Json; using System.Text.Json; namespace AnchorCli.OpenRouter; /// /// Fetches and caches model pricing from the OpenRouter API. /// internal sealed class PricingProvider { private const string ModelsUrl = "https://openrouter.ai/api/v1/models"; private static readonly HttpClient Http = new(); private Dictionary? _models; /// /// Fetches the full model list from OpenRouter (cached after first call). /// public async Task> GetAllModelsAsync( CancellationToken ct = default) { if (_models != null) return _models; var response = await Http.GetAsync(ModelsUrl, ct); response.EnsureSuccessStatusCode(); var json = await response.Content.ReadAsStringAsync(ct); var result = JsonSerializer.Deserialize(json, AppJsonContext.Default.ModelsResponse); _models = result?.Data?.ToDictionary(m => m.Id) ?? []; return _models; } /// /// Looks up pricing for a specific model ID. Returns null if not found. /// public async Task GetModelInfoAsync( string modelId, CancellationToken ct = default) { var models = await GetAllModelsAsync(ct); return models.GetValueOrDefault(modelId); } /// /// Parses a pricing string (USD per token) to decimal. Returns 0 on failure. /// public static decimal ParsePrice(string? priceStr) => decimal.TryParse(priceStr, NumberStyles.Float, CultureInfo.InvariantCulture, out var v) ? v : 0m; }