diff --git a/frontend-modern/src/components/AI/AICostDashboard.tsx b/frontend-modern/src/components/AI/AICostDashboard.tsx
index b290e4b..3a1a28b 100644
--- a/frontend-modern/src/components/AI/AICostDashboard.tsx
+++ b/frontend-modern/src/components/AI/AICostDashboard.tsx
@@ -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 = () => {
USD is an estimate based on public list prices. It may differ from billing.
+ 0}>
+
+ 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(', ')}
+ 6}>
+ (+{unpricedProviderModels().length - 6} more)
+
+ .
+
+
+
+ Prices as of {summary()?.pricing_as_of}.
+
diff --git a/frontend-modern/src/types/ai.ts b/frontend-modern/src/types/ai.ts
index 82d3aa7..d73cde2 100644
--- a/frontend-modern/src/types/ai.ts
+++ b/frontend-modern/src/types/ai.ts
@@ -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[];
diff --git a/internal/ai/cost/pricing.go b/internal/ai/cost/pricing.go
index 95da791..de2585c 100644
--- a/internal/ai/cost/pricing.go
+++ b/internal/ai/cost/pricing.go
@@ -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{
diff --git a/internal/ai/cost/store.go b/internal/ai/cost/store.go
index 0fd6e17..c7c81ce 100644
--- a/internal/ai/cost/store.go
+++ b/internal/ai/cost/store.go
@@ -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"`
diff --git a/internal/ai/service.go b/internal/ai/service.go
index a8670be..8f2fde9 100644
--- a/internal/ai/service.go
+++ b/internal/ai/service.go
@@ -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{},
diff --git a/internal/api/ai_handlers.go b/internal/api/ai_handlers.go
index def16ee..93761dd 100644
--- a/internal/api/ai_handlers.go
+++ b/internal/api/ai_handlers.go
@@ -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"},