Modellpreise live vom Anbieter beziehen

Zweiter Teil von B4. Die Preise standen fest im Code — eine Tabelle mit einer
Handvoll Modelle, die bereits veraltet war. Ausgerechnet das Standardmodell der
Agenten fehlte darin, und CalculateCost gab fuer unbekannte Modelle
stillschweigend 0 zurueck. Die Kostenanzeige war damit nicht bloss ungenau,
sondern blind: Sie meldete 0 Euro, waehrend echte Kosten anfielen.

ModelInfo traegt jetzt die Preisangaben aus dem /models-Endpunkt. OpenRouter
liefert sie als Text und pro einzelnem Token; die Umrechnung auf eine Million
Token laeuft ueber InvariantCulture, sonst wuerde eine deutsche Systemsprache
0.000003 als drei lesen.

Halbe Angaben werden verworfen: Ein Modell, bei dem nur der Eingabepreis
vorliegt, ergaebe eine plausibel aussehende, aber falsche Summe.

ModelPricingCatalog haelt die Preise und liefert eine Kostenschaetzung, die
ausweist, ob sie belastbar ist. Der Statusdienst laedt den Katalog beim Start und
markiert Laeufe ohne Preisangabe sichtbar — in der Zeile mit einem Warnzeichen,
im Tooltip mit den betroffenen Modellnamen und dem Hinweis, dass die Summe
unvollstaendig ist.

284 Tests gruen (136 Core, 148 Tools).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-07-28 10:09:39 +02:00
co-authored by Claude Opus 4.8
parent cbb8ac22bc
commit 93f47aca3a
6 changed files with 352 additions and 29 deletions
@@ -11,5 +11,36 @@ public sealed class ModelInfo
/// <summary>Anzeigename des Modells</summary>
public string Name { get; set; } = "";
/// <summary>
/// Preise laut OpenRouter. Null, wenn der Endpunkt für dieses Modell keine
/// Angaben liefert — dann darf keine Kostenschätzung vorgetäuscht werden.
/// </summary>
public ModelPricing? Pricing { get; set; }
public override string ToString() => Id;
}
/// <summary>
/// Preise pro einer Million Token. OpenRouter liefert sie im /models-Endpunkt als
/// Preis pro einzelnem Token in Textform.
/// </summary>
public sealed record ModelPricing(decimal InputPer1M, decimal OutputPer1M)
{
/// <summary>
/// Wandelt eine OpenRouter-Preisangabe ("0.000003" pro Token) in den Preis pro
/// einer Million Token um. Gibt null zurück, wenn der Wert unbrauchbar ist.
/// </summary>
public static decimal? ParsePerToken(string? value)
{
if (string.IsNullOrWhiteSpace(value))
return null;
if (!decimal.TryParse(value, System.Globalization.NumberStyles.Float,
System.Globalization.CultureInfo.InvariantCulture, out var perToken))
{
return null;
}
return perToken < 0 ? null : perToken * 1_000_000m;
}
}