53 lines
1.7 KiB
C#
53 lines
1.7 KiB
C#
using System.Globalization;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
|
|
namespace AnchorCli.OpenRouter;
|
|
|
|
/// <summary>
|
|
/// Fetches and caches model pricing from the OpenRouter API.
|
|
/// </summary>
|
|
internal sealed class PricingProvider
|
|
{
|
|
private const string ModelsUrl = "https://openrouter.ai/api/v1/models";
|
|
|
|
private static readonly HttpClient Http = new();
|
|
private Dictionary<string, ModelInfo>? _models;
|
|
|
|
/// <summary>
|
|
/// Fetches the full model list from OpenRouter (cached after first call).
|
|
/// </summary>
|
|
public async Task<Dictionary<string, ModelInfo>> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Looks up pricing for a specific model ID. Returns null if not found.
|
|
/// </summary>
|
|
public async Task<ModelInfo?> GetModelInfoAsync(
|
|
string modelId, CancellationToken ct = default)
|
|
{
|
|
var models = await GetAllModelsAsync(ct);
|
|
return models.GetValueOrDefault(modelId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses a pricing string (USD per token) to decimal. Returns 0 on failure.
|
|
/// </summary>
|
|
public static decimal ParsePrice(string? priceStr) =>
|
|
decimal.TryParse(priceStr, NumberStyles.Float, CultureInfo.InvariantCulture, out var v)
|
|
? v
|
|
: 0m;
|
|
}
|