feat: show current-cycle daily token peak
This commit is contained in:
+1
-1
@@ -39,7 +39,7 @@
|
||||
fetchedAt, stale, lastError?}
|
||||
```
|
||||
|
||||
时间字段为 Unix 秒。`currentCycle` 是按当前仍有效的最长限额窗口计算的当前重置周期;`totalTokens` 由 `account/usage/read` 的每日 Token bucket 汇总,受每日粒度影响,无法精确切分周期起止日期内的单日数据。没有有效窗口或用量接口失败时省略该字段。app-server 未提供的摘要字段可以是 `null`;列表应返回数组而非 `null`。
|
||||
时间字段为 Unix 秒。`currentCycle` 是按当前仍有效的最长限额窗口计算的当前重置周期;`totalTokens` 由 `account/usage/read` 的每日 Token bucket 汇总,受每日粒度影响,无法精确切分周期起止日期内的单日数据。`summary.peakDailyTokens` 是同一当前重置周期内每日 Token bucket 的最大值,不再直接采用 app-server 未限定口径的峰值摘要;没有有效窗口或周期内没有每日数据时为 `null`。app-server 未提供的其他摘要字段可以是 `null`;列表应返回数组而非 `null`。
|
||||
|
||||
## 3. 系统、初始化与会话
|
||||
|
||||
|
||||
@@ -533,6 +533,7 @@ func (a *App) syncAccount(ctx context.Context, id int64) error {
|
||||
_, _ = a.store.DB.Exec("INSERT INTO daily_usage(account_id,date,total_tokens,fetched_at) VALUES(?,?,?,?) ON CONFLICT(account_id,date) DO UPDATE SET total_tokens=excluded.total_tokens,fetched_at=excluded.fetched_at", id, x.StartDate, x.Tokens, d.FetchedAt)
|
||||
}
|
||||
d.CurrentCycle = currentTokenCycle(d.Limits, d.Usage, d.FetchedAt)
|
||||
d.Summary.PeakDailyTokens = peakDailyTokensForCycle(d.CurrentCycle, d.Usage, d.FetchedAt)
|
||||
}
|
||||
resetDetected, e := a.storeLimitSnapshots(d)
|
||||
if e != nil {
|
||||
@@ -642,6 +643,29 @@ func currentTokenCycle(limits []LimitBucket, usage []UsagePoint, fetchedAt int64
|
||||
}
|
||||
}
|
||||
|
||||
func peakDailyTokensForCycle(cycle *TokenCycle, usage []UsagePoint, fetchedAt int64) *int64 {
|
||||
if cycle == nil {
|
||||
return nil
|
||||
}
|
||||
startDate := time.Unix(cycle.StartedAt, 0).UTC().Format("2006-01-02")
|
||||
endDate := time.Unix(fetchedAt, 0).UTC().Format("2006-01-02")
|
||||
var peak int64
|
||||
found := false
|
||||
for _, point := range usage {
|
||||
if _, err := time.Parse("2006-01-02", point.Date); err != nil || point.Date < startDate || point.Date > endDate {
|
||||
continue
|
||||
}
|
||||
if !found || point.TotalTokens > peak {
|
||||
peak = point.TotalTokens
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
return &peak
|
||||
}
|
||||
|
||||
func betterTokenCycleLimit(candidate, current LimitBucket) bool {
|
||||
if candidate.WindowDurationMinutes != current.WindowDurationMinutes {
|
||||
return candidate.WindowDurationMinutes > current.WindowDurationMinutes
|
||||
|
||||
@@ -331,6 +331,24 @@ func TestCurrentTokenCycleRequiresAValidFutureResetWindow(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPeakDailyTokensUsesOnlyTheCurrentTokenCycle(t *testing.T) {
|
||||
now := time.Date(2026, time.August, 14, 12, 0, 0, 0, time.UTC)
|
||||
cycle := &TokenCycle{
|
||||
StartedAt: time.Date(2026, time.August, 8, 0, 0, 0, 0, time.UTC).Unix(),
|
||||
ResetsAt: time.Date(2026, time.August, 15, 0, 0, 0, 0, time.UTC).Unix(),
|
||||
}
|
||||
peak := peakDailyTokensForCycle(cycle, []UsagePoint{
|
||||
{Date: "2026-08-07", TotalTokens: 900},
|
||||
{Date: "2026-08-08", TotalTokens: 100},
|
||||
{Date: "2026-08-10", TotalTokens: 200},
|
||||
{Date: "2026-08-14", TotalTokens: 300},
|
||||
{Date: "2026-08-15", TotalTokens: 800},
|
||||
}, now.Unix())
|
||||
if peak == nil || *peak != 300 {
|
||||
t.Fatalf("cycle peak = %v; want 300", peak)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlattenLimitReadsAppServerWindowDuration(t *testing.T) {
|
||||
var limit rawLimit
|
||||
if err := json.Unmarshal([]byte(`{
|
||||
|
||||
Reference in New Issue
Block a user