Files
goclaw/internal/agent/loop_pricing.go
T
Duc Nguyen 747b58d24c fix(usage): repair cost analytics and display precision (#1330)
fix(usage): repair cost analytics and display precision

- Automatic OpenRouter pricing sync with cost backfill for traces/snapshots/events
- Live usage data merging for current-hour dashboard accuracy
- 2-decimal API cost formatting across usage/overview pages
- Comprehensive test coverage (PG, SQLite, HTTP, UI)

Merged by github-maintain automation.
2026-07-03 06:24:21 +07:00

46 lines
1.4 KiB
Go

package agent
import (
"context"
"database/sql"
"errors"
"log/slog"
"time"
"github.com/google/uuid"
"github.com/nextlevelbuilder/goclaw/internal/providers"
"github.com/nextlevelbuilder/goclaw/internal/store"
"github.com/nextlevelbuilder/goclaw/internal/tracing"
)
func (l *Loop) calculateLLMCost(ctx context.Context, providerName, model string, usage *providers.Usage) float64 {
if usage == nil {
return 0
}
tenantID := store.TenantIDFromContext(ctx)
if tenantID == uuid.Nil {
tenantID = l.tenantID
}
if tenantID == uuid.Nil {
tenantID = store.MasterTenantID
}
if l.usageCaps != nil {
resolveCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second)
defer cancel()
resolved, err := l.usageCaps.ResolvePricing(resolveCtx, tenantID, providerName, model)
if err == nil && resolved != nil {
cost, calcErr := tracing.CalculateCostFromUsagePricing(resolved.Pricing, usage)
if calcErr == nil {
return cost
}
slog.Warn("usage_pricing.trace_cost_calculation_failed", "provider", providerName, "model", model, "source", resolved.Source, "error", calcErr)
} else if err != nil && !errors.Is(err, sql.ErrNoRows) {
slog.Warn("usage_pricing.trace_pricing_resolve_failed", "provider", providerName, "model", model, "error", err)
}
}
if pricing := tracing.LookupPricing(l.modelPricing, providerName, model); pricing != nil {
return tracing.CalculateCost(pricing, usage)
}
return 0
}