Skip to content

Commit 3217698

Browse files
Fill dashboard cache rate and first-token stats
1 parent 852a703 commit 3217698

3 files changed

Lines changed: 162 additions & 27 deletions

File tree

database/postgres.go

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,9 @@ func New(driver string, dsn string, schema ...string) (*DB, error) {
309309
prompt_tokens BIGINT NOT NULL DEFAULT 0,
310310
completion_tokens BIGINT NOT NULL DEFAULT 0,
311311
cached_tokens BIGINT NOT NULL DEFAULT 0,
312+
cache_hit_requests BIGINT NOT NULL DEFAULT 0,
313+
first_token_ms_sum DOUBLE PRECISION NOT NULL DEFAULT 0,
314+
first_token_samples BIGINT NOT NULL DEFAULT 0,
312315
account_billed DOUBLE PRECISION NOT NULL DEFAULT 0,
313316
user_billed DOUBLE PRECISION NOT NULL DEFAULT 0
314317
)
@@ -341,6 +344,9 @@ func (db *DB) ensureUsageStatsBaselineBillingColumns(ctx context.Context) error
341344
}{
342345
{name: "account_billed", def: "REAL NOT NULL DEFAULT 0"},
343346
{name: "user_billed", def: "REAL NOT NULL DEFAULT 0"},
347+
{name: "cache_hit_requests", def: "INTEGER NOT NULL DEFAULT 0"},
348+
{name: "first_token_ms_sum", def: "REAL NOT NULL DEFAULT 0"},
349+
{name: "first_token_samples", def: "INTEGER NOT NULL DEFAULT 0"},
344350
} {
345351
if _, ok := columns[column.name]; ok {
346352
continue
@@ -354,6 +360,9 @@ func (db *DB) ensureUsageStatsBaselineBillingColumns(ctx context.Context) error
354360
_, err := db.conn.ExecContext(ctx, `
355361
ALTER TABLE usage_stats_baseline ADD COLUMN IF NOT EXISTS account_billed DOUBLE PRECISION NOT NULL DEFAULT 0;
356362
ALTER TABLE usage_stats_baseline ADD COLUMN IF NOT EXISTS user_billed DOUBLE PRECISION NOT NULL DEFAULT 0;
363+
ALTER TABLE usage_stats_baseline ADD COLUMN IF NOT EXISTS cache_hit_requests BIGINT NOT NULL DEFAULT 0;
364+
ALTER TABLE usage_stats_baseline ADD COLUMN IF NOT EXISTS first_token_ms_sum DOUBLE PRECISION NOT NULL DEFAULT 0;
365+
ALTER TABLE usage_stats_baseline ADD COLUMN IF NOT EXISTS first_token_samples BIGINT NOT NULL DEFAULT 0;
357366
`)
358367
return err
359368
}
@@ -1833,16 +1842,20 @@ type UsageStats struct {
18331842
TotalPrompt int64 `json:"total_prompt_tokens"`
18341843
TotalCompletion int64 `json:"total_completion_tokens"`
18351844
TotalCachedTokens int64 `json:"total_cached_tokens"`
1845+
TotalCacheRate float64 `json:"total_cache_rate"`
18361846
TotalAccountBilled float64 `json:"total_account_billed"`
18371847
TotalUserBilled float64 `json:"total_user_billed"`
18381848
AvgAccountBilled float64 `json:"avg_account_billed_per_request"`
18391849
AvgUserBilled float64 `json:"avg_user_billed_per_request"`
18401850
TodayRequests int64 `json:"today_requests"`
18411851
TodayTokens int64 `json:"today_tokens"`
1852+
TodayCachedTokens int64 `json:"today_cached_tokens"`
1853+
TodayCacheRate float64 `json:"today_cache_rate"`
18421854
TodayAccountBilled float64 `json:"today_account_billed"`
18431855
TodayUserBilled float64 `json:"today_user_billed"`
18441856
RPM float64 `json:"rpm"`
18451857
TPM float64 `json:"tpm"`
1858+
AvgFirstTokenMs float64 `json:"avg_first_token_ms"`
18461859
AvgDurationMs float64 `json:"avg_duration_ms"`
18471860
ErrorRate float64 `json:"error_rate"`
18481861
FeatureStats UsageFeatureStat `json:"feature_stats"`
@@ -1918,27 +1931,32 @@ func (db *DB) GetUsageStats(ctx context.Context) (*UsageStats, error) {
19181931
SELECT
19191932
COUNT(*) AS today_requests,
19201933
COALESCE(SUM(total_tokens), 0) AS today_tokens,
1921-
COALESCE(SUM(prompt_tokens), 0) AS today_prompt,
1922-
COALESCE(SUM(completion_tokens), 0) AS today_completion,
1923-
COALESCE(SUM(cached_tokens), 0) AS today_cached,
1924-
COALESCE(SUM(account_billed), 0) AS today_account_billed,
1925-
COALESCE(SUM(user_billed), 0) AS today_user_billed,
1926-
COALESCE(SUM(CASE WHEN created_at >= $2 THEN 1 ELSE 0 END), 0) AS rpm,
1927-
COALESCE(SUM(CASE WHEN created_at >= $2 THEN total_tokens ELSE 0 END), 0) AS tpm,
1928-
COALESCE(AVG(duration_ms), 0) AS avg_duration_ms,
1934+
COALESCE(SUM(prompt_tokens), 0) AS today_prompt,
1935+
COALESCE(SUM(completion_tokens), 0) AS today_completion,
1936+
COALESCE(SUM(cached_tokens), 0) AS today_cached,
1937+
COALESCE(SUM(account_billed), 0) AS today_account_billed,
1938+
COALESCE(SUM(user_billed), 0) AS today_user_billed,
1939+
COALESCE(SUM(CASE WHEN created_at >= $2 THEN 1 ELSE 0 END), 0) AS rpm,
1940+
COALESCE(SUM(CASE WHEN created_at >= $2 THEN total_tokens ELSE 0 END), 0) AS tpm,
1941+
COALESCE(AVG(NULLIF(first_token_ms, 0)), 0) AS avg_first_token_ms,
1942+
COALESCE(AVG(duration_ms), 0) AS avg_duration_ms,
1943+
COALESCE(SUM(CASE WHEN cached_tokens > 0 THEN 1 ELSE 0 END), 0) AS today_cache_hit_requests,
19291944
COALESCE(SUM(CASE WHEN status_code >= 400 THEN 1 ELSE 0 END), 0) AS today_errors
19301945
FROM usage_logs
19311946
WHERE created_at >= $1
19321947
AND status_code <> 499
19331948
`
19341949

19351950
var todayErrors int64
1951+
var todayCacheHitRequests int64
19361952
var todayPrompt, todayCompletion, todayCached int64
19371953
err := db.conn.QueryRowContext(ctx, todayQuery, todayStart, minuteAgo).Scan(
19381954
&stats.TodayRequests, &stats.TodayTokens, &todayPrompt, &todayCompletion, &todayCached,
19391955
&stats.TodayAccountBilled, &stats.TodayUserBilled,
19401956
&stats.RPM, &stats.TPM,
1957+
&stats.AvgFirstTokenMs,
19411958
&stats.AvgDurationMs,
1959+
&todayCacheHitRequests,
19421960
&todayErrors,
19431961
)
19441962
if err != nil {
@@ -1947,7 +1965,10 @@ func (db *DB) GetUsageStats(ctx context.Context) (*UsageStats, error) {
19471965

19481966
// 统计当前可见请求总数和计费总额(排除 499,保证与使用统计列表口径一致)
19491967
var visibleTotal int64
1968+
var visibleCacheHitRequests int64
1969+
var visibleFirstTokenSamples int64
19501970
var currentTokens, currentPrompt, currentCompletion, currentCached int64
1971+
var currentFirstTokenMsSum float64
19511972
var currentAccountBilled, currentUserBilled float64
19521973
_ = db.conn.QueryRowContext(ctx, `
19531974
SELECT
@@ -1956,27 +1977,41 @@ func (db *DB) GetUsageStats(ctx context.Context) (*UsageStats, error) {
19561977
COALESCE(SUM(prompt_tokens), 0),
19571978
COALESCE(SUM(completion_tokens), 0),
19581979
COALESCE(SUM(cached_tokens), 0),
1980+
COALESCE(SUM(CASE WHEN cached_tokens > 0 THEN 1 ELSE 0 END), 0),
1981+
COALESCE(SUM(CASE WHEN first_token_ms > 0 THEN first_token_ms ELSE 0 END), 0),
1982+
COALESCE(SUM(CASE WHEN first_token_ms > 0 THEN 1 ELSE 0 END), 0),
19591983
COALESCE(SUM(account_billed), 0),
19601984
COALESCE(SUM(user_billed), 0)
19611985
FROM usage_logs
19621986
WHERE status_code <> 499
1963-
`).Scan(&visibleTotal, &currentTokens, &currentPrompt, &currentCompletion, &currentCached, &currentAccountBilled, &currentUserBilled)
1987+
`).Scan(&visibleTotal, &currentTokens, &currentPrompt, &currentCompletion, &currentCached, &visibleCacheHitRequests, &currentFirstTokenMsSum, &visibleFirstTokenSamples, &currentAccountBilled, &currentUserBilled)
19641988

19651989
// 加上基线值(清空日志前保存的累计值)
1966-
var bReq, bTok, bPrompt, bComp, bCached int64
1990+
var bReq, bTok, bPrompt, bComp, bCached, bCacheHitRequests, bFirstTokenSamples int64
1991+
var bFirstTokenMsSum float64
19671992
var bAccountBilled, bUserBilled float64
19681993
_ = db.conn.QueryRowContext(ctx, `
1969-
SELECT total_requests, total_tokens, prompt_tokens, completion_tokens, cached_tokens, account_billed, user_billed
1994+
SELECT total_requests, total_tokens, prompt_tokens, completion_tokens, cached_tokens, cache_hit_requests, first_token_ms_sum, first_token_samples, account_billed, user_billed
19701995
FROM usage_stats_baseline WHERE id = 1
1971-
`).Scan(&bReq, &bTok, &bPrompt, &bComp, &bCached, &bAccountBilled, &bUserBilled)
1996+
`).Scan(&bReq, &bTok, &bPrompt, &bComp, &bCached, &bCacheHitRequests, &bFirstTokenMsSum, &bFirstTokenSamples, &bAccountBilled, &bUserBilled)
19721997

19731998
stats.TotalRequests = visibleTotal + bReq
19741999
stats.TotalTokens = currentTokens + bTok
19752000
stats.TotalPrompt = currentPrompt + bPrompt
19762001
stats.TotalCompletion = currentCompletion + bComp
19772002
stats.TotalCachedTokens = currentCached + bCached
2003+
stats.TodayCachedTokens = todayCached
19782004
stats.TotalAccountBilled = currentAccountBilled + bAccountBilled
19792005
stats.TotalUserBilled = currentUserBilled + bUserBilled
2006+
if stats.TodayRequests > 0 {
2007+
stats.TodayCacheRate = float64(todayCacheHitRequests) / float64(stats.TodayRequests) * 100
2008+
}
2009+
if stats.TotalRequests > 0 {
2010+
stats.TotalCacheRate = float64(visibleCacheHitRequests+bCacheHitRequests) / float64(stats.TotalRequests) * 100
2011+
}
2012+
if totalFirstTokenSamples := visibleFirstTokenSamples + bFirstTokenSamples; totalFirstTokenSamples > 0 {
2013+
stats.AvgFirstTokenMs = (currentFirstTokenMsSum + bFirstTokenMsSum) / float64(totalFirstTokenSamples)
2014+
}
19802015
if stats.TotalRequests > 0 {
19812016
stats.AvgAccountBilled = stats.TotalAccountBilled / float64(stats.TotalRequests)
19822017
stats.AvgUserBilled = stats.TotalUserBilled / float64(stats.TotalRequests)
@@ -2773,12 +2808,15 @@ func (db *DB) ClearUsageLogs(ctx context.Context) error {
27732808
_, err := db.conn.ExecContext(ctx, `
27742809
UPDATE usage_stats_baseline SET
27752810
total_requests = total_requests + COALESCE((SELECT COUNT(*) FROM usage_logs WHERE status_code <> 499), 0),
2776-
total_tokens = total_tokens + COALESCE((SELECT SUM(total_tokens) FROM usage_logs WHERE status_code <> 499), 0),
2777-
prompt_tokens = prompt_tokens + COALESCE((SELECT SUM(prompt_tokens) FROM usage_logs WHERE status_code <> 499), 0),
2778-
completion_tokens = completion_tokens + COALESCE((SELECT SUM(completion_tokens) FROM usage_logs WHERE status_code <> 499), 0),
2779-
cached_tokens = cached_tokens + COALESCE((SELECT SUM(cached_tokens) FROM usage_logs WHERE status_code <> 499), 0),
2780-
account_billed = account_billed + COALESCE((SELECT SUM(account_billed) FROM usage_logs WHERE status_code <> 499), 0),
2781-
user_billed = user_billed + COALESCE((SELECT SUM(user_billed) FROM usage_logs WHERE status_code <> 499), 0)
2811+
total_tokens = total_tokens + COALESCE((SELECT SUM(total_tokens) FROM usage_logs WHERE status_code <> 499), 0),
2812+
prompt_tokens = prompt_tokens + COALESCE((SELECT SUM(prompt_tokens) FROM usage_logs WHERE status_code <> 499), 0),
2813+
completion_tokens = completion_tokens + COALESCE((SELECT SUM(completion_tokens) FROM usage_logs WHERE status_code <> 499), 0),
2814+
cached_tokens = cached_tokens + COALESCE((SELECT SUM(cached_tokens) FROM usage_logs WHERE status_code <> 499), 0),
2815+
cache_hit_requests = cache_hit_requests + COALESCE((SELECT SUM(CASE WHEN cached_tokens > 0 THEN 1 ELSE 0 END) FROM usage_logs WHERE status_code <> 499), 0),
2816+
first_token_ms_sum = first_token_ms_sum + COALESCE((SELECT SUM(CASE WHEN first_token_ms > 0 THEN first_token_ms ELSE 0 END) FROM usage_logs WHERE status_code <> 499), 0),
2817+
first_token_samples = first_token_samples + COALESCE((SELECT SUM(CASE WHEN first_token_ms > 0 THEN 1 ELSE 0 END) FROM usage_logs WHERE status_code <> 499), 0),
2818+
account_billed = account_billed + COALESCE((SELECT SUM(account_billed) FROM usage_logs WHERE status_code <> 499), 0),
2819+
user_billed = user_billed + COALESCE((SELECT SUM(user_billed) FROM usage_logs WHERE status_code <> 499), 0)
27822820
WHERE id = 1
27832821
`)
27842822
if err != nil {

database/sqlite.go

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ func (db *DB) getUsageStatsSQLite(ctx context.Context) (*UsageStats, error) {
703703

704704
rows, err := db.conn.QueryContext(ctx, `
705705
SELECT created_at, total_tokens, prompt_tokens, completion_tokens,
706-
cached_tokens, duration_ms, status_code, account_billed, user_billed
706+
cached_tokens, first_token_ms, duration_ms, status_code, account_billed, user_billed
707707
FROM usage_logs
708708
WHERE created_at >= $1 AND status_code <> 499
709709
`, db.timeArg(todayStart))
@@ -715,15 +715,18 @@ func (db *DB) getUsageStatsSQLite(ctx context.Context) (*UsageStats, error) {
715715
stats := &UsageStats{}
716716
var todayErrors int64
717717
var totalDuration float64
718+
var totalFirstTokenMs float64
719+
var totalFirstTokenSamples int64
720+
var todayCacheHitRequests int64
718721

719722
for rows.Next() {
720723
var createdRaw interface{}
721724
var totalTokens, promptTokens, completionTokens, cachedTokens int64
722-
var durationMs int
725+
var firstTokenMs, durationMs int
723726
var statusCode int
724727
var accountBilled, userBilled float64
725728
if err := rows.Scan(&createdRaw, &totalTokens, &promptTokens, &completionTokens,
726-
&cachedTokens, &durationMs, &statusCode, &accountBilled, &userBilled); err != nil {
729+
&cachedTokens, &firstTokenMs, &durationMs, &statusCode, &accountBilled, &userBilled); err != nil {
727730
return nil, err
728731
}
729732
createdAt, err := parseDBTimeValue(createdRaw)
@@ -736,9 +739,17 @@ func (db *DB) getUsageStatsSQLite(ctx context.Context) (*UsageStats, error) {
736739
stats.TotalPrompt += promptTokens
737740
stats.TotalCompletion += completionTokens
738741
stats.TotalCachedTokens += cachedTokens
742+
stats.TodayCachedTokens += cachedTokens
739743
stats.TodayAccountBilled += accountBilled
740744
stats.TodayUserBilled += userBilled
741745
totalDuration += float64(durationMs)
746+
if firstTokenMs > 0 {
747+
totalFirstTokenMs += float64(firstTokenMs)
748+
totalFirstTokenSamples++
749+
}
750+
if cachedTokens > 0 {
751+
todayCacheHitRequests++
752+
}
742753

743754
if statusCode >= 400 {
744755
todayErrors++
@@ -756,11 +767,16 @@ func (db *DB) getUsageStatsSQLite(ctx context.Context) (*UsageStats, error) {
756767
if stats.TodayRequests > 0 {
757768
stats.AvgDurationMs = totalDuration / float64(stats.TodayRequests)
758769
stats.ErrorRate = float64(todayErrors) / float64(stats.TodayRequests) * 100
770+
stats.TodayCacheRate = float64(todayCacheHitRequests) / float64(stats.TodayRequests) * 100
771+
}
772+
if totalFirstTokenSamples > 0 {
773+
stats.AvgFirstTokenMs = totalFirstTokenMs / float64(totalFirstTokenSamples)
759774
}
760775

761776
// 可见请求总数(排除 499)
762-
var visibleTotal int64
777+
var visibleTotal, visibleCacheHitRequests, visibleFirstTokenSamples int64
763778
var currentTokens, currentPrompt, currentCompletion, currentCached int64
779+
var currentFirstTokenMsSum float64
764780
var currentAccountBilled, currentUserBilled float64
765781
_ = db.conn.QueryRowContext(ctx, `
766782
SELECT
@@ -769,19 +785,23 @@ func (db *DB) getUsageStatsSQLite(ctx context.Context) (*UsageStats, error) {
769785
COALESCE(SUM(prompt_tokens), 0),
770786
COALESCE(SUM(completion_tokens), 0),
771787
COALESCE(SUM(cached_tokens), 0),
788+
COALESCE(SUM(CASE WHEN cached_tokens > 0 THEN 1 ELSE 0 END), 0),
789+
COALESCE(SUM(CASE WHEN first_token_ms > 0 THEN first_token_ms ELSE 0 END), 0),
790+
COALESCE(SUM(CASE WHEN first_token_ms > 0 THEN 1 ELSE 0 END), 0),
772791
COALESCE(SUM(account_billed), 0),
773792
COALESCE(SUM(user_billed), 0)
774793
FROM usage_logs
775794
WHERE status_code <> 499
776-
`).Scan(&visibleTotal, &currentTokens, &currentPrompt, &currentCompletion, &currentCached, &currentAccountBilled, &currentUserBilled)
795+
`).Scan(&visibleTotal, &currentTokens, &currentPrompt, &currentCompletion, &currentCached, &visibleCacheHitRequests, &currentFirstTokenMsSum, &visibleFirstTokenSamples, &currentAccountBilled, &currentUserBilled)
777796

778797
// 基线值
779-
var bReq, bTok, bPrompt, bComp, bCached int64
798+
var bReq, bTok, bPrompt, bComp, bCached, bCacheHitRequests, bFirstTokenSamples int64
799+
var bFirstTokenMsSum float64
780800
var bAccountBilled, bUserBilled float64
781801
_ = db.conn.QueryRowContext(ctx, `
782-
SELECT total_requests, total_tokens, prompt_tokens, completion_tokens, cached_tokens, account_billed, user_billed
802+
SELECT total_requests, total_tokens, prompt_tokens, completion_tokens, cached_tokens, cache_hit_requests, first_token_ms_sum, first_token_samples, account_billed, user_billed
783803
FROM usage_stats_baseline WHERE id = 1
784-
`).Scan(&bReq, &bTok, &bPrompt, &bComp, &bCached, &bAccountBilled, &bUserBilled)
804+
`).Scan(&bReq, &bTok, &bPrompt, &bComp, &bCached, &bCacheHitRequests, &bFirstTokenMsSum, &bFirstTokenSamples, &bAccountBilled, &bUserBilled)
785805

786806
stats.TotalRequests = visibleTotal + bReq
787807
stats.TotalTokens = currentTokens + bTok
@@ -790,6 +810,12 @@ func (db *DB) getUsageStatsSQLite(ctx context.Context) (*UsageStats, error) {
790810
stats.TotalCachedTokens = currentCached + bCached
791811
stats.TotalAccountBilled = currentAccountBilled + bAccountBilled
792812
stats.TotalUserBilled = currentUserBilled + bUserBilled
813+
if stats.TotalRequests > 0 {
814+
stats.TotalCacheRate = float64(visibleCacheHitRequests+bCacheHitRequests) / float64(stats.TotalRequests) * 100
815+
}
816+
if visibleFirstTokenSamples+bFirstTokenSamples > 0 {
817+
stats.AvgFirstTokenMs = (currentFirstTokenMsSum + bFirstTokenMsSum) / float64(visibleFirstTokenSamples+bFirstTokenSamples)
818+
}
793819
if stats.TotalRequests > 0 {
794820
stats.AvgAccountBilled = stats.TotalAccountBilled / float64(stats.TotalRequests)
795821
stats.AvgUserBilled = stats.TotalUserBilled / float64(stats.TotalRequests)

database/sqlite_test.go

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ func TestSQLiteUsageStatsBaselineHasBillingColumns(t *testing.T) {
544544
t.Fatalf("sqliteTableColumns 返回错误: %v", err)
545545
}
546546

547-
for _, name := range []string{"account_billed", "user_billed"} {
547+
for _, name := range []string{"account_billed", "user_billed", "cache_hit_requests", "first_token_ms_sum", "first_token_samples"} {
548548
if _, ok := columns[name]; !ok {
549549
t.Fatalf("usage_stats_baseline 缺少列 %q", name)
550550
}
@@ -798,6 +798,7 @@ func TestUsageStatsIncludeCodex2APIBreakdowns(t *testing.T) {
798798
Stream: true,
799799
ServiceTier: "fast",
800800
CachedTokens: 128,
801+
FirstTokenMs: 820,
801802
ReasoningTokens: 32,
802803
APIKeyID: 7,
803804
APIKeyName: "Claude Code",
@@ -853,6 +854,18 @@ func TestUsageStatsIncludeCodex2APIBreakdowns(t *testing.T) {
853854
if stats.TotalRequests != 3 {
854855
t.Fatalf("TotalRequests = %d, want 3", stats.TotalRequests)
855856
}
857+
if stats.TodayCachedTokens != 128 {
858+
t.Fatalf("TodayCachedTokens = %d, want 128", stats.TodayCachedTokens)
859+
}
860+
if stats.TodayCacheRate < 33.3 || stats.TodayCacheRate > 33.4 {
861+
t.Fatalf("TodayCacheRate = %.4f, want about 33.33", stats.TodayCacheRate)
862+
}
863+
if stats.TotalCacheRate < 33.3 || stats.TotalCacheRate > 33.4 {
864+
t.Fatalf("TotalCacheRate = %.4f, want about 33.33", stats.TotalCacheRate)
865+
}
866+
if stats.AvgFirstTokenMs != 820 {
867+
t.Fatalf("AvgFirstTokenMs = %.2f, want 820", stats.AvgFirstTokenMs)
868+
}
856869
features := stats.FeatureStats
857870
if features.StreamRequests != 1 || features.SyncRequests != 2 || features.FastRequests != 1 ||
858871
features.CacheHitRequests != 1 || features.ReasoningRequests != 1 || features.ImageRequests != 1 ||
@@ -880,6 +893,64 @@ func TestUsageStatsIncludeCodex2APIBreakdowns(t *testing.T) {
880893
}
881894
}
882895

896+
func TestUsageStatsBaselinePreservesCacheRateAndFirstTokenAfterClear(t *testing.T) {
897+
dbPath := filepath.Join(t.TempDir(), "codex2api.db")
898+
899+
db, err := New("sqlite", dbPath)
900+
if err != nil {
901+
t.Fatalf("New(sqlite) 返回错误: %v", err)
902+
}
903+
defer db.Close()
904+
905+
ctx := context.Background()
906+
for _, usageLog := range []*UsageLogInput{
907+
{
908+
AccountID: 1,
909+
Endpoint: "/v1/responses",
910+
Model: "gpt-5.5",
911+
StatusCode: 200,
912+
InputTokens: 100,
913+
OutputTokens: 50,
914+
TotalTokens: 150,
915+
CachedTokens: 32,
916+
FirstTokenMs: 600,
917+
},
918+
{
919+
AccountID: 1,
920+
Endpoint: "/v1/responses",
921+
Model: "gpt-5.5",
922+
StatusCode: 200,
923+
InputTokens: 80,
924+
OutputTokens: 20,
925+
TotalTokens: 100,
926+
FirstTokenMs: 300,
927+
},
928+
} {
929+
if err := db.InsertUsageLog(ctx, usageLog); err != nil {
930+
t.Fatalf("InsertUsageLog 返回错误: %v", err)
931+
}
932+
}
933+
db.flushLogs()
934+
935+
if err := db.ClearUsageLogs(ctx); err != nil {
936+
t.Fatalf("ClearUsageLogs 返回错误: %v", err)
937+
}
938+
939+
stats, err := db.GetUsageStats(ctx)
940+
if err != nil {
941+
t.Fatalf("GetUsageStats 返回错误: %v", err)
942+
}
943+
if stats.TotalRequests != 2 {
944+
t.Fatalf("TotalRequests = %d, want 2", stats.TotalRequests)
945+
}
946+
if stats.TotalCacheRate < 49.9 || stats.TotalCacheRate > 50.1 {
947+
t.Fatalf("TotalCacheRate = %.4f, want about 50.00", stats.TotalCacheRate)
948+
}
949+
if stats.AvgFirstTokenMs < 449.9 || stats.AvgFirstTokenMs > 450.1 {
950+
t.Fatalf("AvgFirstTokenMs = %.4f, want about 450.00", stats.AvgFirstTokenMs)
951+
}
952+
}
953+
883954
func TestSoftDeleteAccountMarksDeletedStatus(t *testing.T) {
884955
dbPath := filepath.Join(t.TempDir(), "codex2api.db")
885956

0 commit comments

Comments
 (0)