MCPcopy
hub / github.com/mudler/LocalAI / computeSourceTotals

Function computeSourceTotals

core/http/auth/usage.go:298–387  ·  view source on GitHub ↗

computeSourceTotals rolls up by_source / by_key / grand_total. userID/apiKeyID are optional filters. includeLegacy controls whether the legacy bucket is exposed (admin-only).

(db *gorm.DB, userID, apiKeyID string, since time.Time, includeLegacy bool)

Source from the content-addressed store, hash-verified

296// userID/apiKeyID are optional filters. includeLegacy controls whether the
297// legacy bucket is exposed (admin-only).
298func computeSourceTotals(db *gorm.DB, userID, apiKeyID string, since time.Time, includeLegacy bool) SourceTotals {
299 totals := SourceTotals{BySource: map[string]TotalsEntry{}}
300
301 bySourceQ := db.Model(&UsageRecord{}).
302 Select("source, SUM(total_tokens) as tokens, COUNT(*) as requests").
303 Group("source")
304 bySourceQ = applyFilters(bySourceQ, userID, apiKeyID, since, includeLegacy)
305
306 var bySourceRows []struct {
307 Source string
308 Tokens int64
309 Requests int64
310 }
311 if err := bySourceQ.Scan(&bySourceRows).Error; err != nil {
312 xlog.Warn("computeSourceTotals: by-source Scan failed", "error", err)
313 return totals
314 }
315 for _, r := range bySourceRows {
316 totals.BySource[r.Source] = TotalsEntry{Tokens: r.Tokens, Requests: r.Requests}
317 totals.GrandTotal.Tokens += r.Tokens
318 totals.GrandTotal.Requests += r.Requests
319 }
320
321 byKeyQ := db.Model(&UsageRecord{}).
322 Select("COALESCE(api_key_id, '') as api_key_id, api_key_name, "+
323 "user_id, user_name, "+
324 "SUM(total_tokens) as tokens, COUNT(*) as requests, MAX(created_at) as last_used").
325 Where("api_key_id IS NOT NULL AND api_key_id <> ''").
326 Group("api_key_id, api_key_name, user_id, user_name").
327 Order("tokens DESC").
328 Limit(maxKeyTotals)
329 byKeyQ = applyFilters(byKeyQ, userID, apiKeyID, since, includeLegacy)
330
331 // Iterate Rows() manually because MAX(created_at) is returned as a string by
332 // the SQLite driver, and Go's database/sql refuses to scan that into
333 // *time.Time. Postgres returns a proper timestamp. We accept both shapes
334 // via a Rows.Scan into a string column, then parse uniformly.
335 rows, err := byKeyQ.Rows()
336 if err != nil {
337 xlog.Warn("computeSourceTotals: by-key Rows() failed", "error", err)
338 } else {
339 defer func() { _ = rows.Close() }()
340 out := make([]KeyTotal, 0)
341 for rows.Next() {
342 var (
343 apiKeyID, apiKeyName, userIDCol, userName, lastUsedRaw string
344 tokens, requests int64
345 )
346 if scanErr := rows.Scan(&apiKeyID, &apiKeyName, &userIDCol, &userName, &tokens, &requests, &lastUsedRaw); scanErr != nil {
347 continue
348 }
349 out = append(out, KeyTotal{
350 APIKeyID: apiKeyID,
351 APIKeyName: apiKeyName,
352 UserID: userIDCol,
353 UserName: userName,
354 Tokens: tokens,
355 Requests: requests,

Callers 2

GetUserUsageBySourceFunction · 0.85
GetAllUsageBySourceFunction · 0.85

Calls 6

applyFiltersFunction · 0.85
parseLastUsedStringFunction · 0.85
SelectMethod · 0.80
ErrMethod · 0.80
CloseMethod · 0.65
ScanMethod · 0.45

Tested by

no test coverage detected