fix: improve analytics accuracy and cost calculation
Refined CalculateCost to correctly handle cached token discounts. Added fuzzy matching to model lookup. Robustified SQL date extraction using SUBSTR and LIKE for better SQLite compatibility.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package models
|
||||
|
||||
import "strings"
|
||||
|
||||
type ModelRegistry struct {
|
||||
Providers map[string]ProviderInfo `json:"-"`
|
||||
}
|
||||
@@ -54,5 +56,14 @@ func (r *ModelRegistry) FindModel(modelID string) *ModelMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
// Try fuzzy matching (e.g. gpt-4o-2024-05-13 matching gpt-4o)
|
||||
for _, provider := range r.Providers {
|
||||
for id, model := range provider.Models {
|
||||
if strings.HasPrefix(modelID, id) {
|
||||
return &model
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -254,9 +254,10 @@ func (s *Server) handleUsageSummary(c *gin.Context) {
|
||||
COUNT(*) as today_requests,
|
||||
COALESCE(SUM(cost), 0.0) as today_cost
|
||||
FROM llm_requests
|
||||
WHERE strftime('%Y-%m-%d', timestamp) = ?
|
||||
`, today)
|
||||
WHERE timestamp LIKE ?
|
||||
`, today+"%")
|
||||
if err != nil {
|
||||
fmt.Printf("[ERROR] Failed to fetch today stats: %v\n", err)
|
||||
todayStats.TodayRequests = 0
|
||||
todayStats.TodayCost = 0.0
|
||||
}
|
||||
@@ -305,7 +306,8 @@ func (s *Server) handleTimeSeries(c *gin.Context) {
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT
|
||||
COALESCE(strftime('%%Y-%%m-%%d', timestamp), 'unknown') as bucket,
|
||||
COALESCE(SUBSTR(timestamp, 1, 10), 'unknown') as bucket,
|
||||
|
||||
COUNT(*) as requests,
|
||||
COALESCE(SUM(total_tokens), 0) as tokens,
|
||||
COALESCE(SUM(cost), 0.0) as cost
|
||||
@@ -468,7 +470,7 @@ func (s *Server) handleDetailedUsage(c *gin.Context) {
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT
|
||||
COALESCE(strftime('%%Y-%%m-%%d', timestamp), 'unknown') as date,
|
||||
COALESCE(SUBSTR(timestamp, 1, 10), 'unknown') as date,
|
||||
COALESCE(client_id, 'unknown') as client,
|
||||
COALESCE(provider, 'unknown') as provider,
|
||||
COALESCE(model, 'unknown') as model,
|
||||
|
||||
@@ -376,6 +376,8 @@ func (s *Server) logRequest(start time.Time, clientID, provider, model string, u
|
||||
|
||||
// Calculate cost using registry
|
||||
entry.Cost = utils.CalculateCost(s.registry, model, entry.PromptTokens, entry.CompletionTokens, entry.CacheReadTokens, entry.CacheWriteTokens)
|
||||
fmt.Printf("[DEBUG] Request logged: model=%s, prompt=%d, completion=%d, cache_read=%d, cost=%f\n",
|
||||
model, entry.PromptTokens, entry.CompletionTokens, entry.CacheReadTokens, entry.Cost)
|
||||
}
|
||||
|
||||
s.logger.LogRequest(entry)
|
||||
|
||||
@@ -40,7 +40,18 @@ func CalculateCost(registry *models.ModelRegistry, modelID string, promptTokens,
|
||||
return 0.0
|
||||
}
|
||||
|
||||
cost := (float64(promptTokens) * meta.Cost.Input / 1000000.0) +
|
||||
// promptTokens is usually the TOTAL prompt size.
|
||||
// We subtract cacheRead from it to get the uncached part.
|
||||
uncachedTokens := promptTokens
|
||||
if cacheRead > 0 {
|
||||
if cacheRead > promptTokens {
|
||||
uncachedTokens = 0
|
||||
} else {
|
||||
uncachedTokens = promptTokens - cacheRead
|
||||
}
|
||||
}
|
||||
|
||||
cost := (float64(uncachedTokens) * meta.Cost.Input / 1000000.0) +
|
||||
(float64(completionTokens) * meta.Cost.Output / 1000000.0)
|
||||
|
||||
if meta.Cost.CacheRead != nil {
|
||||
|
||||
Reference in New Issue
Block a user