mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
refactor: unify token usage stats across all session types (#274)
- Rewrite SQL queries to join bot_history_messages with bot_sessions, supporting chat/heartbeat/schedule usage from a single source - Update Go handler and CLI command to use unified queries - Fix daily chart stacking: each session type gets its own bar group - Add total input/output trend lines to the daily token chart - Fix summary cards reactivity by restricting aggregation to allDays range - Fix cache chart reactive dependency tracking by inlining data access - Add i18n keys for schedule, totalInput, totalOutput - Default time range changed to 7 days - Regenerate sqlc, swagger, and SDK
This commit is contained in:
+34
-47
@@ -28,46 +28,52 @@ func (h *Handler) buildUsageGroup() *CommandGroup {
|
||||
toTS := pgtype.Timestamptz{Time: now, Valid: true}
|
||||
nullModel := pgtype.UUID{Valid: false}
|
||||
|
||||
chatRows, err := h.queries.GetMessageTokenUsageByDay(cc.Ctx, dbsqlc.GetMessageTokenUsageByDayParams{
|
||||
rows, err := h.queries.GetTokenUsageByDayAndType(cc.Ctx, dbsqlc.GetTokenUsageByDayAndTypeParams{
|
||||
BotID: botUUID, FromTime: fromTS, ToTime: toTS, ModelID: nullModel,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
hbRows, err := h.queries.GetHeartbeatTokenUsageByDay(cc.Ctx, dbsqlc.GetHeartbeatTokenUsageByDayParams{
|
||||
BotID: botUUID, FromTime: fromTS, ToTime: toTS, ModelID: nullModel,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(chatRows) == 0 && len(hbRows) == 0 {
|
||||
if len(rows) == 0 {
|
||||
return "No token usage in the last 7 days.", nil
|
||||
}
|
||||
|
||||
type bucket struct {
|
||||
label string
|
||||
rows []dbsqlc.GetTokenUsageByDayAndTypeRow
|
||||
}
|
||||
buckets := []bucket{
|
||||
{label: "Chat"},
|
||||
{label: "Heartbeat"},
|
||||
{label: "Schedule"},
|
||||
}
|
||||
for _, r := range rows {
|
||||
switch r.SessionType {
|
||||
case "heartbeat":
|
||||
buckets[1].rows = append(buckets[1].rows, r)
|
||||
case "schedule":
|
||||
buckets[2].rows = append(buckets[2].rows, r)
|
||||
default:
|
||||
buckets[0].rows = append(buckets[0].rows, r)
|
||||
}
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString("Token usage (last 7 days):\n\n")
|
||||
|
||||
if len(chatRows) > 0 {
|
||||
b.WriteString("Chat:\n")
|
||||
var totalIn, totalOut int64
|
||||
for _, r := range chatRows {
|
||||
day := r.Day.Time.Format("01-02")
|
||||
fmt.Fprintf(&b, " %s: in=%d out=%d\n", day, r.InputTokens, r.OutputTokens)
|
||||
totalIn += r.InputTokens
|
||||
totalOut += r.OutputTokens
|
||||
first := true
|
||||
for _, bk := range buckets {
|
||||
if len(bk.rows) == 0 {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(&b, " Total: in=%d out=%d\n", totalIn, totalOut)
|
||||
}
|
||||
|
||||
if len(hbRows) > 0 {
|
||||
if len(chatRows) > 0 {
|
||||
if !first {
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
b.WriteString("Heartbeat:\n")
|
||||
first = false
|
||||
b.WriteString(bk.label + ":\n")
|
||||
var totalIn, totalOut int64
|
||||
for _, r := range hbRows {
|
||||
for _, r := range bk.rows {
|
||||
day := r.Day.Time.Format("01-02")
|
||||
fmt.Fprintf(&b, " %s: in=%d out=%d\n", day, r.InputTokens, r.OutputTokens)
|
||||
totalIn += r.InputTokens
|
||||
@@ -92,41 +98,22 @@ func (h *Handler) buildUsageGroup() *CommandGroup {
|
||||
fromTS := pgtype.Timestamptz{Time: from, Valid: true}
|
||||
toTS := pgtype.Timestamptz{Time: now, Valid: true}
|
||||
|
||||
chatRows, err := h.queries.GetMessageTokenUsageByModel(cc.Ctx, dbsqlc.GetMessageTokenUsageByModelParams{
|
||||
BotID: botUUID, FromTime: fromTS, ToTime: toTS,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
hbRows, err := h.queries.GetHeartbeatTokenUsageByModel(cc.Ctx, dbsqlc.GetHeartbeatTokenUsageByModelParams{
|
||||
rows, err := h.queries.GetTokenUsageByModel(cc.Ctx, dbsqlc.GetTokenUsageByModelParams{
|
||||
BotID: botUUID, FromTime: fromTS, ToTime: toTS,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(chatRows) == 0 && len(hbRows) == 0 {
|
||||
if len(rows) == 0 {
|
||||
return "No token usage in the last 7 days.", nil
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString("Token usage by model (last 7 days):\n\n")
|
||||
|
||||
if len(chatRows) > 0 {
|
||||
b.WriteString("Chat:\n")
|
||||
for _, r := range chatRows {
|
||||
fmt.Fprintf(&b, " %s (%s): in=%d out=%d\n", r.ModelName, r.ProviderName, r.InputTokens, r.OutputTokens)
|
||||
}
|
||||
}
|
||||
|
||||
if len(hbRows) > 0 {
|
||||
if len(chatRows) > 0 {
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
b.WriteString("Heartbeat:\n")
|
||||
for _, r := range hbRows {
|
||||
fmt.Fprintf(&b, " %s (%s): in=%d out=%d\n", r.ModelName, r.ProviderName, r.InputTokens, r.OutputTokens)
|
||||
}
|
||||
for _, r := range rows {
|
||||
fmt.Fprintf(&b, " %s (%s): in=%d out=%d\n", r.ModelName, r.ProviderName, r.InputTokens, r.OutputTokens)
|
||||
}
|
||||
|
||||
return strings.TrimRight(b.String(), "\n"), nil
|
||||
|
||||
Reference in New Issue
Block a user