Clarify AI cost estimates with pricing coverage
This commit is contained in:
parent
11ebc0e91c
commit
5f2c240480
6 changed files with 36 additions and 0 deletions
|
|
@ -65,6 +65,12 @@ export const AICostDashboard: Component = () => {
|
||||||
return data.provider_models.some((pm) => pm.pricing_known);
|
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 estimatedTotalUSD = createMemo(() => {
|
||||||
const data = summary();
|
const data = summary();
|
||||||
if (!data || !anyPricingKnown()) return null;
|
if (!data || !anyPricingKnown()) return null;
|
||||||
|
|
@ -412,6 +418,25 @@ export const AICostDashboard: Component = () => {
|
||||||
|
|
||||||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
<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.
|
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>
|
||||||
|
|
||||||
<div class="flex items-center justify-between gap-3">
|
<div class="flex items-center justify-between gap-3">
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,7 @@ export interface AICostSummary {
|
||||||
retention_days: number;
|
retention_days: number;
|
||||||
effective_days: number;
|
effective_days: number;
|
||||||
truncated: boolean;
|
truncated: boolean;
|
||||||
|
pricing_as_of?: string;
|
||||||
provider_models: AICostProviderModelSummary[];
|
provider_models: AICostProviderModelSummary[];
|
||||||
use_cases: AICostUseCaseSummary[];
|
use_cases: AICostUseCaseSummary[];
|
||||||
targets: AICostTargetSummary[];
|
targets: AICostTargetSummary[];
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,11 @@ type modelPrice struct {
|
||||||
|
|
||||||
const pricingAsOf = "2025-12"
|
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.
|
// NOTE: Keep this table small and conservative.
|
||||||
// The goal is quick estimation and relative comparisons, not exact billing.
|
// The goal is quick estimation and relative comparisons, not exact billing.
|
||||||
var providerPrices = map[string][]modelPrice{
|
var providerPrices = map[string][]modelPrice{
|
||||||
|
|
|
||||||
|
|
@ -237,6 +237,7 @@ func (s *Store) GetSummary(days int) Summary {
|
||||||
RetentionDays: retentionDays,
|
RetentionDays: retentionDays,
|
||||||
EffectiveDays: effectiveDays,
|
EffectiveDays: effectiveDays,
|
||||||
Truncated: truncated,
|
Truncated: truncated,
|
||||||
|
PricingAsOf: PricingAsOf(),
|
||||||
ProviderModels: providerModels,
|
ProviderModels: providerModels,
|
||||||
UseCases: summarizeUseCases(events),
|
UseCases: summarizeUseCases(events),
|
||||||
Targets: summarizeTargets(events),
|
Targets: summarizeTargets(events),
|
||||||
|
|
@ -356,6 +357,8 @@ type Summary struct {
|
||||||
EffectiveDays int `json:"effective_days"`
|
EffectiveDays int `json:"effective_days"`
|
||||||
Truncated bool `json:"truncated"`
|
Truncated bool `json:"truncated"`
|
||||||
|
|
||||||
|
PricingAsOf string `json:"pricing_as_of,omitempty"`
|
||||||
|
|
||||||
ProviderModels []ProviderModelSummary `json:"provider_models"`
|
ProviderModels []ProviderModelSummary `json:"provider_models"`
|
||||||
UseCases []UseCaseSummary `json:"use_cases"`
|
UseCases []UseCaseSummary `json:"use_cases"`
|
||||||
Targets []TargetSummary `json:"targets"`
|
Targets []TargetSummary `json:"targets"`
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,7 @@ func (s *Service) GetCostSummary(days int) cost.Summary {
|
||||||
RetentionDays: cost.DefaultMaxDays,
|
RetentionDays: cost.DefaultMaxDays,
|
||||||
EffectiveDays: effectiveDays,
|
EffectiveDays: effectiveDays,
|
||||||
Truncated: truncated,
|
Truncated: truncated,
|
||||||
|
PricingAsOf: cost.PricingAsOf(),
|
||||||
ProviderModels: []cost.ProviderModelSummary{},
|
ProviderModels: []cost.ProviderModelSummary{},
|
||||||
UseCases: []cost.UseCaseSummary{},
|
UseCases: []cost.UseCaseSummary{},
|
||||||
DailyTotals: []cost.DailySummary{},
|
DailyTotals: []cost.DailySummary{},
|
||||||
|
|
|
||||||
|
|
@ -2441,6 +2441,7 @@ func (h *AISettingsHandler) HandleGetAICostSummary(w http.ResponseWriter, r *htt
|
||||||
} else {
|
} else {
|
||||||
summary = cost.Summary{
|
summary = cost.Summary{
|
||||||
Days: days,
|
Days: days,
|
||||||
|
PricingAsOf: cost.PricingAsOf(),
|
||||||
ProviderModels: []cost.ProviderModelSummary{},
|
ProviderModels: []cost.ProviderModelSummary{},
|
||||||
DailyTotals: []cost.DailySummary{},
|
DailyTotals: []cost.DailySummary{},
|
||||||
Totals: cost.ProviderModelSummary{Provider: "all"},
|
Totals: cost.ProviderModelSummary{Provider: "all"},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue