Clarify AI cost estimates with pricing coverage

This commit is contained in:
rcourtman 2025-12-12 13:19:03 +00:00
parent 11ebc0e91c
commit 5f2c240480
6 changed files with 36 additions and 0 deletions

View file

@ -65,6 +65,12 @@ export const AICostDashboard: Component = () => {
return data.provider_models.some((pm) => pm.pricing_known);
});
const unpricedProviderModels = createMemo(() => {
const data = summary();
if (!data) return [];
return (data.provider_models ?? []).filter((pm) => !pm.pricing_known && (pm.total_tokens ?? 0) > 0);
});
const estimatedTotalUSD = createMemo(() => {
const data = summary();
if (!data || !anyPricingKnown()) return null;
@ -412,6 +418,25 @@ export const AICostDashboard: Component = () => {
<div class="text-xs text-gray-500 dark:text-gray-400">
USD is an estimate based on public list prices. It may differ from billing.
<Show when={unpricedProviderModels().length > 0}>
<span class="ml-2">
Estimated spend is partial. Pricing is unknown for{' '}
{unpricedProviderModels()
.slice(0, 6)
.map(
(pm) =>
`${PROVIDER_NAMES[pm.provider as keyof typeof PROVIDER_NAMES] || pm.provider}/${pm.model}`,
)
.join(', ')}
<Show when={unpricedProviderModels().length > 6}>
<span> (+{unpricedProviderModels().length - 6} more)</span>
</Show>
.
</span>
</Show>
<Show when={summary()?.pricing_as_of}>
<span class="ml-2">Prices as of {summary()?.pricing_as_of}.</span>
</Show>
</div>
<div class="flex items-center justify-between gap-3">

View file

@ -220,6 +220,7 @@ export interface AICostSummary {
retention_days: number;
effective_days: number;
truncated: boolean;
pricing_as_of?: string;
provider_models: AICostProviderModelSummary[];
use_cases: AICostUseCaseSummary[];
targets: AICostTargetSummary[];

View file

@ -31,6 +31,11 @@ type modelPrice struct {
const pricingAsOf = "2025-12"
// PricingAsOf indicates the effective date of the pricing table used for estimation.
func PricingAsOf() string {
return pricingAsOf
}
// NOTE: Keep this table small and conservative.
// The goal is quick estimation and relative comparisons, not exact billing.
var providerPrices = map[string][]modelPrice{

View file

@ -237,6 +237,7 @@ func (s *Store) GetSummary(days int) Summary {
RetentionDays: retentionDays,
EffectiveDays: effectiveDays,
Truncated: truncated,
PricingAsOf: PricingAsOf(),
ProviderModels: providerModels,
UseCases: summarizeUseCases(events),
Targets: summarizeTargets(events),
@ -356,6 +357,8 @@ type Summary struct {
EffectiveDays int `json:"effective_days"`
Truncated bool `json:"truncated"`
PricingAsOf string `json:"pricing_as_of,omitempty"`
ProviderModels []ProviderModelSummary `json:"provider_models"`
UseCases []UseCaseSummary `json:"use_cases"`
Targets []TargetSummary `json:"targets"`

View file

@ -138,6 +138,7 @@ func (s *Service) GetCostSummary(days int) cost.Summary {
RetentionDays: cost.DefaultMaxDays,
EffectiveDays: effectiveDays,
Truncated: truncated,
PricingAsOf: cost.PricingAsOf(),
ProviderModels: []cost.ProviderModelSummary{},
UseCases: []cost.UseCaseSummary{},
DailyTotals: []cost.DailySummary{},

View file

@ -2441,6 +2441,7 @@ func (h *AISettingsHandler) HandleGetAICostSummary(w http.ResponseWriter, r *htt
} else {
summary = cost.Summary{
Days: days,
PricingAsOf: cost.PricingAsOf(),
ProviderModels: []cost.ProviderModelSummary{},
DailyTotals: []cost.DailySummary{},
Totals: cost.ProviderModelSummary{Provider: "all"},