mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat(site): improve cost analytics view (#23069)
Surfaces cache token data in the analytics views and fixes table spacing. ### Changes - **Cache token columns**: Added cache read and cache write token counts to all analytics views (user and admin), from SQL queries through Go SDK types to the frontend tables and summary cards. - **Table spacing fix**: Replaced the bare React fragment in `ChatCostSummaryView` with a `space-y-6` container so the model and chat breakdown tables no longer overlap. ### Data flow `chat_messages` table already stores `cache_read_tokens` and `cache_creation_tokens` (and uses them for cost calculation). This PR aggregates and displays them alongside input/output tokens in: - Summary cards (6 cards: Total Cost, Input, Output, Cache Read, Cache Write, Messages) - Per-model breakdown table - Per-chat breakdown table - Admin per-user table
This commit is contained in:
+40
-32
@@ -432,15 +432,17 @@ func (api *API) chatCostSummary(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, codersdk.ChatCostSummary{
|
||||
StartDate: startDate,
|
||||
EndDate: endDate,
|
||||
TotalCostMicros: summary.TotalCostMicros,
|
||||
PricedMessageCount: summary.PricedMessageCount,
|
||||
UnpricedMessageCount: summary.UnpricedMessageCount,
|
||||
TotalInputTokens: summary.TotalInputTokens,
|
||||
TotalOutputTokens: summary.TotalOutputTokens,
|
||||
ByModel: modelBreakdowns,
|
||||
ByChat: chatBreakdowns,
|
||||
StartDate: startDate,
|
||||
EndDate: endDate,
|
||||
TotalCostMicros: summary.TotalCostMicros,
|
||||
PricedMessageCount: summary.PricedMessageCount,
|
||||
UnpricedMessageCount: summary.UnpricedMessageCount,
|
||||
TotalInputTokens: summary.TotalInputTokens,
|
||||
TotalOutputTokens: summary.TotalOutputTokens,
|
||||
TotalCacheReadTokens: summary.TotalCacheReadTokens,
|
||||
TotalCacheCreationTokens: summary.TotalCacheCreationTokens,
|
||||
ByModel: modelBreakdowns,
|
||||
ByChat: chatBreakdowns,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2488,39 +2490,45 @@ func convertChatCostModelBreakdown(model database.GetChatCostPerModelRow) coders
|
||||
displayName = model.Model
|
||||
}
|
||||
return codersdk.ChatCostModelBreakdown{
|
||||
ModelConfigID: model.ModelConfigID,
|
||||
DisplayName: displayName,
|
||||
Provider: model.Provider,
|
||||
Model: model.Model,
|
||||
TotalCostMicros: model.TotalCostMicros,
|
||||
MessageCount: model.MessageCount,
|
||||
TotalInputTokens: model.TotalInputTokens,
|
||||
TotalOutputTokens: model.TotalOutputTokens,
|
||||
ModelConfigID: model.ModelConfigID,
|
||||
DisplayName: displayName,
|
||||
Provider: model.Provider,
|
||||
Model: model.Model,
|
||||
TotalCostMicros: model.TotalCostMicros,
|
||||
MessageCount: model.MessageCount,
|
||||
TotalInputTokens: model.TotalInputTokens,
|
||||
TotalOutputTokens: model.TotalOutputTokens,
|
||||
TotalCacheReadTokens: model.TotalCacheReadTokens,
|
||||
TotalCacheCreationTokens: model.TotalCacheCreationTokens,
|
||||
}
|
||||
}
|
||||
|
||||
func convertChatCostChatBreakdown(chat database.GetChatCostPerChatRow) codersdk.ChatCostChatBreakdown {
|
||||
return codersdk.ChatCostChatBreakdown{
|
||||
RootChatID: chat.RootChatID,
|
||||
ChatTitle: chat.ChatTitle,
|
||||
TotalCostMicros: chat.TotalCostMicros,
|
||||
MessageCount: chat.MessageCount,
|
||||
TotalInputTokens: chat.TotalInputTokens,
|
||||
TotalOutputTokens: chat.TotalOutputTokens,
|
||||
RootChatID: chat.RootChatID,
|
||||
ChatTitle: chat.ChatTitle,
|
||||
TotalCostMicros: chat.TotalCostMicros,
|
||||
MessageCount: chat.MessageCount,
|
||||
TotalInputTokens: chat.TotalInputTokens,
|
||||
TotalOutputTokens: chat.TotalOutputTokens,
|
||||
TotalCacheReadTokens: chat.TotalCacheReadTokens,
|
||||
TotalCacheCreationTokens: chat.TotalCacheCreationTokens,
|
||||
}
|
||||
}
|
||||
|
||||
func convertChatCostUserRollup(user database.GetChatCostPerUserRow) codersdk.ChatCostUserRollup {
|
||||
return codersdk.ChatCostUserRollup{
|
||||
UserID: user.UserID,
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
AvatarURL: user.AvatarURL,
|
||||
TotalCostMicros: user.TotalCostMicros,
|
||||
MessageCount: user.MessageCount,
|
||||
ChatCount: user.ChatCount,
|
||||
TotalInputTokens: user.TotalInputTokens,
|
||||
TotalOutputTokens: user.TotalOutputTokens,
|
||||
UserID: user.UserID,
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
AvatarURL: user.AvatarURL,
|
||||
TotalCostMicros: user.TotalCostMicros,
|
||||
MessageCount: user.MessageCount,
|
||||
ChatCount: user.ChatCount,
|
||||
TotalInputTokens: user.TotalInputTokens,
|
||||
TotalOutputTokens: user.TotalOutputTokens,
|
||||
TotalCacheReadTokens: user.TotalCacheReadTokens,
|
||||
TotalCacheCreationTokens: user.TotalCacheCreationTokens,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3259,7 +3259,9 @@ WITH chat_costs AS (
|
||||
OR cm.cache_read_tokens IS NOT NULL
|
||||
)::bigint AS message_count,
|
||||
COALESCE(SUM(cm.input_tokens), 0)::bigint AS total_input_tokens,
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens,
|
||||
COALESCE(SUM(cm.cache_read_tokens), 0)::bigint AS total_cache_read_tokens,
|
||||
COALESCE(SUM(cm.cache_creation_tokens), 0)::bigint AS total_cache_creation_tokens
|
||||
FROM chat_messages cm
|
||||
JOIN chats c ON c.id = cm.chat_id
|
||||
WHERE c.owner_id = $1::uuid
|
||||
@@ -3274,7 +3276,9 @@ SELECT
|
||||
cc.total_cost_micros,
|
||||
cc.message_count,
|
||||
cc.total_input_tokens,
|
||||
cc.total_output_tokens
|
||||
cc.total_output_tokens,
|
||||
cc.total_cache_read_tokens,
|
||||
cc.total_cache_creation_tokens
|
||||
FROM chat_costs cc
|
||||
LEFT JOIN chats rc ON rc.id = cc.root_chat_id
|
||||
ORDER BY cc.total_cost_micros DESC
|
||||
@@ -3287,12 +3291,14 @@ type GetChatCostPerChatParams struct {
|
||||
}
|
||||
|
||||
type GetChatCostPerChatRow struct {
|
||||
RootChatID uuid.UUID `db:"root_chat_id" json:"root_chat_id"`
|
||||
ChatTitle string `db:"chat_title" json:"chat_title"`
|
||||
TotalCostMicros int64 `db:"total_cost_micros" json:"total_cost_micros"`
|
||||
MessageCount int64 `db:"message_count" json:"message_count"`
|
||||
TotalInputTokens int64 `db:"total_input_tokens" json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `db:"total_output_tokens" json:"total_output_tokens"`
|
||||
RootChatID uuid.UUID `db:"root_chat_id" json:"root_chat_id"`
|
||||
ChatTitle string `db:"chat_title" json:"chat_title"`
|
||||
TotalCostMicros int64 `db:"total_cost_micros" json:"total_cost_micros"`
|
||||
MessageCount int64 `db:"message_count" json:"message_count"`
|
||||
TotalInputTokens int64 `db:"total_input_tokens" json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `db:"total_output_tokens" json:"total_output_tokens"`
|
||||
TotalCacheReadTokens int64 `db:"total_cache_read_tokens" json:"total_cache_read_tokens"`
|
||||
TotalCacheCreationTokens int64 `db:"total_cache_creation_tokens" json:"total_cache_creation_tokens"`
|
||||
}
|
||||
|
||||
// Per-root-chat cost breakdown for a single user within a date range.
|
||||
@@ -3314,6 +3320,8 @@ func (q *sqlQuerier) GetChatCostPerChat(ctx context.Context, arg GetChatCostPerC
|
||||
&i.MessageCount,
|
||||
&i.TotalInputTokens,
|
||||
&i.TotalOutputTokens,
|
||||
&i.TotalCacheReadTokens,
|
||||
&i.TotalCacheCreationTokens,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -3343,7 +3351,9 @@ SELECT
|
||||
OR cm.cache_read_tokens IS NOT NULL
|
||||
)::bigint AS message_count,
|
||||
COALESCE(SUM(cm.input_tokens), 0)::bigint AS total_input_tokens,
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens,
|
||||
COALESCE(SUM(cm.cache_read_tokens), 0)::bigint AS total_cache_read_tokens,
|
||||
COALESCE(SUM(cm.cache_creation_tokens), 0)::bigint AS total_cache_creation_tokens
|
||||
FROM
|
||||
chat_messages cm
|
||||
JOIN
|
||||
@@ -3368,14 +3378,16 @@ type GetChatCostPerModelParams struct {
|
||||
}
|
||||
|
||||
type GetChatCostPerModelRow struct {
|
||||
ModelConfigID uuid.UUID `db:"model_config_id" json:"model_config_id"`
|
||||
DisplayName string `db:"display_name" json:"display_name"`
|
||||
Provider string `db:"provider" json:"provider"`
|
||||
Model string `db:"model" json:"model"`
|
||||
TotalCostMicros int64 `db:"total_cost_micros" json:"total_cost_micros"`
|
||||
MessageCount int64 `db:"message_count" json:"message_count"`
|
||||
TotalInputTokens int64 `db:"total_input_tokens" json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `db:"total_output_tokens" json:"total_output_tokens"`
|
||||
ModelConfigID uuid.UUID `db:"model_config_id" json:"model_config_id"`
|
||||
DisplayName string `db:"display_name" json:"display_name"`
|
||||
Provider string `db:"provider" json:"provider"`
|
||||
Model string `db:"model" json:"model"`
|
||||
TotalCostMicros int64 `db:"total_cost_micros" json:"total_cost_micros"`
|
||||
MessageCount int64 `db:"message_count" json:"message_count"`
|
||||
TotalInputTokens int64 `db:"total_input_tokens" json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `db:"total_output_tokens" json:"total_output_tokens"`
|
||||
TotalCacheReadTokens int64 `db:"total_cache_read_tokens" json:"total_cache_read_tokens"`
|
||||
TotalCacheCreationTokens int64 `db:"total_cache_creation_tokens" json:"total_cache_creation_tokens"`
|
||||
}
|
||||
|
||||
// Per-model cost breakdown for a single user within a date range.
|
||||
@@ -3398,6 +3410,8 @@ func (q *sqlQuerier) GetChatCostPerModel(ctx context.Context, arg GetChatCostPer
|
||||
&i.MessageCount,
|
||||
&i.TotalInputTokens,
|
||||
&i.TotalOutputTokens,
|
||||
&i.TotalCacheReadTokens,
|
||||
&i.TotalCacheCreationTokens,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -3429,7 +3443,9 @@ WITH chat_cost_users AS (
|
||||
)::bigint AS message_count,
|
||||
COUNT(DISTINCT COALESCE(c.root_chat_id, c.id))::bigint AS chat_count,
|
||||
COALESCE(SUM(cm.input_tokens), 0)::bigint AS total_input_tokens,
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens,
|
||||
COALESCE(SUM(cm.cache_read_tokens), 0)::bigint AS total_cache_read_tokens,
|
||||
COALESCE(SUM(cm.cache_creation_tokens), 0)::bigint AS total_cache_creation_tokens
|
||||
FROM
|
||||
chat_messages cm
|
||||
JOIN
|
||||
@@ -3460,6 +3476,8 @@ SELECT
|
||||
chat_count,
|
||||
total_input_tokens,
|
||||
total_output_tokens,
|
||||
total_cache_read_tokens,
|
||||
total_cache_creation_tokens,
|
||||
COUNT(*) OVER()::bigint AS total_count
|
||||
FROM
|
||||
chat_cost_users
|
||||
@@ -3481,16 +3499,18 @@ type GetChatCostPerUserParams struct {
|
||||
}
|
||||
|
||||
type GetChatCostPerUserRow struct {
|
||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||
Username string `db:"username" json:"username"`
|
||||
Name string `db:"name" json:"name"`
|
||||
AvatarURL string `db:"avatar_url" json:"avatar_url"`
|
||||
TotalCostMicros int64 `db:"total_cost_micros" json:"total_cost_micros"`
|
||||
MessageCount int64 `db:"message_count" json:"message_count"`
|
||||
ChatCount int64 `db:"chat_count" json:"chat_count"`
|
||||
TotalInputTokens int64 `db:"total_input_tokens" json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `db:"total_output_tokens" json:"total_output_tokens"`
|
||||
TotalCount int64 `db:"total_count" json:"total_count"`
|
||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||
Username string `db:"username" json:"username"`
|
||||
Name string `db:"name" json:"name"`
|
||||
AvatarURL string `db:"avatar_url" json:"avatar_url"`
|
||||
TotalCostMicros int64 `db:"total_cost_micros" json:"total_cost_micros"`
|
||||
MessageCount int64 `db:"message_count" json:"message_count"`
|
||||
ChatCount int64 `db:"chat_count" json:"chat_count"`
|
||||
TotalInputTokens int64 `db:"total_input_tokens" json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `db:"total_output_tokens" json:"total_output_tokens"`
|
||||
TotalCacheReadTokens int64 `db:"total_cache_read_tokens" json:"total_cache_read_tokens"`
|
||||
TotalCacheCreationTokens int64 `db:"total_cache_creation_tokens" json:"total_cache_creation_tokens"`
|
||||
TotalCount int64 `db:"total_count" json:"total_count"`
|
||||
}
|
||||
|
||||
// Deployment-wide per-user cost rollup within a date range.
|
||||
@@ -3520,6 +3540,8 @@ func (q *sqlQuerier) GetChatCostPerUser(ctx context.Context, arg GetChatCostPerU
|
||||
&i.ChatCount,
|
||||
&i.TotalInputTokens,
|
||||
&i.TotalOutputTokens,
|
||||
&i.TotalCacheReadTokens,
|
||||
&i.TotalCacheCreationTokens,
|
||||
&i.TotalCount,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
@@ -3552,7 +3574,9 @@ SELECT
|
||||
)
|
||||
)::bigint AS unpriced_message_count,
|
||||
COALESCE(SUM(cm.input_tokens), 0)::bigint AS total_input_tokens,
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens,
|
||||
COALESCE(SUM(cm.cache_read_tokens), 0)::bigint AS total_cache_read_tokens,
|
||||
COALESCE(SUM(cm.cache_creation_tokens), 0)::bigint AS total_cache_creation_tokens
|
||||
FROM
|
||||
chat_messages cm
|
||||
JOIN
|
||||
@@ -3571,11 +3595,13 @@ type GetChatCostSummaryParams struct {
|
||||
}
|
||||
|
||||
type GetChatCostSummaryRow struct {
|
||||
TotalCostMicros int64 `db:"total_cost_micros" json:"total_cost_micros"`
|
||||
PricedMessageCount int64 `db:"priced_message_count" json:"priced_message_count"`
|
||||
UnpricedMessageCount int64 `db:"unpriced_message_count" json:"unpriced_message_count"`
|
||||
TotalInputTokens int64 `db:"total_input_tokens" json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `db:"total_output_tokens" json:"total_output_tokens"`
|
||||
TotalCostMicros int64 `db:"total_cost_micros" json:"total_cost_micros"`
|
||||
PricedMessageCount int64 `db:"priced_message_count" json:"priced_message_count"`
|
||||
UnpricedMessageCount int64 `db:"unpriced_message_count" json:"unpriced_message_count"`
|
||||
TotalInputTokens int64 `db:"total_input_tokens" json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `db:"total_output_tokens" json:"total_output_tokens"`
|
||||
TotalCacheReadTokens int64 `db:"total_cache_read_tokens" json:"total_cache_read_tokens"`
|
||||
TotalCacheCreationTokens int64 `db:"total_cache_creation_tokens" json:"total_cache_creation_tokens"`
|
||||
}
|
||||
|
||||
// Aggregate cost summary for a single user within a date range.
|
||||
@@ -3589,6 +3615,8 @@ func (q *sqlQuerier) GetChatCostSummary(ctx context.Context, arg GetChatCostSumm
|
||||
&i.UnpricedMessageCount,
|
||||
&i.TotalInputTokens,
|
||||
&i.TotalOutputTokens,
|
||||
&i.TotalCacheReadTokens,
|
||||
&i.TotalCacheCreationTokens,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
@@ -526,7 +526,9 @@ SELECT
|
||||
)
|
||||
)::bigint AS unpriced_message_count,
|
||||
COALESCE(SUM(cm.input_tokens), 0)::bigint AS total_input_tokens,
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens,
|
||||
COALESCE(SUM(cm.cache_read_tokens), 0)::bigint AS total_cache_read_tokens,
|
||||
COALESCE(SUM(cm.cache_creation_tokens), 0)::bigint AS total_cache_creation_tokens
|
||||
FROM
|
||||
chat_messages cm
|
||||
JOIN
|
||||
@@ -554,7 +556,9 @@ SELECT
|
||||
OR cm.cache_read_tokens IS NOT NULL
|
||||
)::bigint AS message_count,
|
||||
COALESCE(SUM(cm.input_tokens), 0)::bigint AS total_input_tokens,
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens,
|
||||
COALESCE(SUM(cm.cache_read_tokens), 0)::bigint AS total_cache_read_tokens,
|
||||
COALESCE(SUM(cm.cache_creation_tokens), 0)::bigint AS total_cache_creation_tokens
|
||||
FROM
|
||||
chat_messages cm
|
||||
JOIN
|
||||
@@ -587,7 +591,9 @@ WITH chat_costs AS (
|
||||
OR cm.cache_read_tokens IS NOT NULL
|
||||
)::bigint AS message_count,
|
||||
COALESCE(SUM(cm.input_tokens), 0)::bigint AS total_input_tokens,
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens,
|
||||
COALESCE(SUM(cm.cache_read_tokens), 0)::bigint AS total_cache_read_tokens,
|
||||
COALESCE(SUM(cm.cache_creation_tokens), 0)::bigint AS total_cache_creation_tokens
|
||||
FROM chat_messages cm
|
||||
JOIN chats c ON c.id = cm.chat_id
|
||||
WHERE c.owner_id = @owner_id::uuid
|
||||
@@ -602,7 +608,9 @@ SELECT
|
||||
cc.total_cost_micros,
|
||||
cc.message_count,
|
||||
cc.total_input_tokens,
|
||||
cc.total_output_tokens
|
||||
cc.total_output_tokens,
|
||||
cc.total_cache_read_tokens,
|
||||
cc.total_cache_creation_tokens
|
||||
FROM chat_costs cc
|
||||
LEFT JOIN chats rc ON rc.id = cc.root_chat_id
|
||||
ORDER BY cc.total_cost_micros DESC;
|
||||
@@ -626,7 +634,9 @@ WITH chat_cost_users AS (
|
||||
)::bigint AS message_count,
|
||||
COUNT(DISTINCT COALESCE(c.root_chat_id, c.id))::bigint AS chat_count,
|
||||
COALESCE(SUM(cm.input_tokens), 0)::bigint AS total_input_tokens,
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens
|
||||
COALESCE(SUM(cm.output_tokens), 0)::bigint AS total_output_tokens,
|
||||
COALESCE(SUM(cm.cache_read_tokens), 0)::bigint AS total_cache_read_tokens,
|
||||
COALESCE(SUM(cm.cache_creation_tokens), 0)::bigint AS total_cache_creation_tokens
|
||||
FROM
|
||||
chat_messages cm
|
||||
JOIN
|
||||
@@ -657,6 +667,8 @@ SELECT
|
||||
chat_count,
|
||||
total_input_tokens,
|
||||
total_output_tokens,
|
||||
total_cache_read_tokens,
|
||||
total_cache_creation_tokens,
|
||||
COUNT(*) OVER()::bigint AS total_count
|
||||
FROM
|
||||
chat_cost_users
|
||||
|
||||
+40
-32
@@ -733,50 +733,58 @@ type ChatCostUsersOptions struct {
|
||||
|
||||
// ChatCostSummary is the response from the chat cost summary endpoint.
|
||||
type ChatCostSummary struct {
|
||||
StartDate time.Time `json:"start_date" format:"date-time"`
|
||||
EndDate time.Time `json:"end_date" format:"date-time"`
|
||||
TotalCostMicros int64 `json:"total_cost_micros"`
|
||||
PricedMessageCount int64 `json:"priced_message_count"`
|
||||
UnpricedMessageCount int64 `json:"unpriced_message_count"`
|
||||
TotalInputTokens int64 `json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `json:"total_output_tokens"`
|
||||
ByModel []ChatCostModelBreakdown `json:"by_model"`
|
||||
ByChat []ChatCostChatBreakdown `json:"by_chat"`
|
||||
StartDate time.Time `json:"start_date" format:"date-time"`
|
||||
EndDate time.Time `json:"end_date" format:"date-time"`
|
||||
TotalCostMicros int64 `json:"total_cost_micros"`
|
||||
PricedMessageCount int64 `json:"priced_message_count"`
|
||||
UnpricedMessageCount int64 `json:"unpriced_message_count"`
|
||||
TotalInputTokens int64 `json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `json:"total_output_tokens"`
|
||||
TotalCacheReadTokens int64 `json:"total_cache_read_tokens"`
|
||||
TotalCacheCreationTokens int64 `json:"total_cache_creation_tokens"`
|
||||
ByModel []ChatCostModelBreakdown `json:"by_model"`
|
||||
ByChat []ChatCostChatBreakdown `json:"by_chat"`
|
||||
}
|
||||
|
||||
// ChatCostModelBreakdown contains per-model cost aggregation.
|
||||
type ChatCostModelBreakdown struct {
|
||||
ModelConfigID uuid.UUID `json:"model_config_id" format:"uuid"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Provider string `json:"provider"`
|
||||
Model string `json:"model"`
|
||||
TotalCostMicros int64 `json:"total_cost_micros"`
|
||||
MessageCount int64 `json:"message_count"`
|
||||
TotalInputTokens int64 `json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `json:"total_output_tokens"`
|
||||
ModelConfigID uuid.UUID `json:"model_config_id" format:"uuid"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Provider string `json:"provider"`
|
||||
Model string `json:"model"`
|
||||
TotalCostMicros int64 `json:"total_cost_micros"`
|
||||
MessageCount int64 `json:"message_count"`
|
||||
TotalInputTokens int64 `json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `json:"total_output_tokens"`
|
||||
TotalCacheReadTokens int64 `json:"total_cache_read_tokens"`
|
||||
TotalCacheCreationTokens int64 `json:"total_cache_creation_tokens"`
|
||||
}
|
||||
|
||||
// ChatCostChatBreakdown contains per-root-chat cost aggregation.
|
||||
type ChatCostChatBreakdown struct {
|
||||
RootChatID uuid.UUID `json:"root_chat_id" format:"uuid"`
|
||||
ChatTitle string `json:"chat_title"`
|
||||
TotalCostMicros int64 `json:"total_cost_micros"`
|
||||
MessageCount int64 `json:"message_count"`
|
||||
TotalInputTokens int64 `json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `json:"total_output_tokens"`
|
||||
RootChatID uuid.UUID `json:"root_chat_id" format:"uuid"`
|
||||
ChatTitle string `json:"chat_title"`
|
||||
TotalCostMicros int64 `json:"total_cost_micros"`
|
||||
MessageCount int64 `json:"message_count"`
|
||||
TotalInputTokens int64 `json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `json:"total_output_tokens"`
|
||||
TotalCacheReadTokens int64 `json:"total_cache_read_tokens"`
|
||||
TotalCacheCreationTokens int64 `json:"total_cache_creation_tokens"`
|
||||
}
|
||||
|
||||
// ChatCostUserRollup contains per-user cost aggregation for admin views.
|
||||
type ChatCostUserRollup struct {
|
||||
UserID uuid.UUID `json:"user_id" format:"uuid"`
|
||||
Username string `json:"username"`
|
||||
Name string `json:"name"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
TotalCostMicros int64 `json:"total_cost_micros"`
|
||||
MessageCount int64 `json:"message_count"`
|
||||
ChatCount int64 `json:"chat_count"`
|
||||
TotalInputTokens int64 `json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `json:"total_output_tokens"`
|
||||
UserID uuid.UUID `json:"user_id" format:"uuid"`
|
||||
Username string `json:"username"`
|
||||
Name string `json:"name"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
TotalCostMicros int64 `json:"total_cost_micros"`
|
||||
MessageCount int64 `json:"message_count"`
|
||||
ChatCount int64 `json:"chat_count"`
|
||||
TotalInputTokens int64 `json:"total_input_tokens"`
|
||||
TotalOutputTokens int64 `json:"total_output_tokens"`
|
||||
TotalCacheReadTokens int64 `json:"total_cache_read_tokens"`
|
||||
TotalCacheCreationTokens int64 `json:"total_cache_creation_tokens"`
|
||||
}
|
||||
|
||||
// ChatCostUsersResponse is the response from the admin chat cost users endpoint.
|
||||
|
||||
Generated
+8
@@ -1082,6 +1082,8 @@ export interface ChatCostChatBreakdown {
|
||||
readonly message_count: number;
|
||||
readonly total_input_tokens: number;
|
||||
readonly total_output_tokens: number;
|
||||
readonly total_cache_read_tokens: number;
|
||||
readonly total_cache_creation_tokens: number;
|
||||
}
|
||||
|
||||
// From codersdk/chats.go
|
||||
@@ -1097,6 +1099,8 @@ export interface ChatCostModelBreakdown {
|
||||
readonly message_count: number;
|
||||
readonly total_input_tokens: number;
|
||||
readonly total_output_tokens: number;
|
||||
readonly total_cache_read_tokens: number;
|
||||
readonly total_cache_creation_tokens: number;
|
||||
}
|
||||
|
||||
// From codersdk/chats.go
|
||||
@@ -1111,6 +1115,8 @@ export interface ChatCostSummary {
|
||||
readonly unpriced_message_count: number;
|
||||
readonly total_input_tokens: number;
|
||||
readonly total_output_tokens: number;
|
||||
readonly total_cache_read_tokens: number;
|
||||
readonly total_cache_creation_tokens: number;
|
||||
readonly by_model: readonly ChatCostModelBreakdown[];
|
||||
readonly by_chat: readonly ChatCostChatBreakdown[];
|
||||
}
|
||||
@@ -1138,6 +1144,8 @@ export interface ChatCostUserRollup {
|
||||
readonly chat_count: number;
|
||||
readonly total_input_tokens: number;
|
||||
readonly total_output_tokens: number;
|
||||
readonly total_cache_read_tokens: number;
|
||||
readonly total_cache_creation_tokens: number;
|
||||
}
|
||||
|
||||
// From codersdk/chats.go
|
||||
|
||||
@@ -49,6 +49,8 @@ const mockAnalyticsSummary: TypesGen.ChatCostSummary = {
|
||||
unpriced_message_count: 1,
|
||||
total_input_tokens: 123_456,
|
||||
total_output_tokens: 654_321,
|
||||
total_cache_read_tokens: 9_876,
|
||||
total_cache_creation_tokens: 5_432,
|
||||
by_model: [
|
||||
{
|
||||
model_config_id: "model-config-1",
|
||||
@@ -59,6 +61,8 @@ const mockAnalyticsSummary: TypesGen.ChatCostSummary = {
|
||||
message_count: 9,
|
||||
total_input_tokens: 100_000,
|
||||
total_output_tokens: 200_000,
|
||||
total_cache_read_tokens: 7_654,
|
||||
total_cache_creation_tokens: 3_210,
|
||||
},
|
||||
],
|
||||
by_chat: [
|
||||
@@ -69,6 +73,8 @@ const mockAnalyticsSummary: TypesGen.ChatCostSummary = {
|
||||
message_count: 5,
|
||||
total_input_tokens: 60_000,
|
||||
total_output_tokens: 80_000,
|
||||
total_cache_read_tokens: 4_321,
|
||||
total_cache_creation_tokens: 1_234,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -88,6 +94,8 @@ const mockUsageUsers: TypesGen.ChatCostUsersResponse = {
|
||||
chat_count: 3,
|
||||
total_input_tokens: 120_000,
|
||||
total_output_tokens: 45_000,
|
||||
total_cache_read_tokens: 6_789,
|
||||
total_cache_creation_tokens: 2_468,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -13,6 +13,8 @@ const buildSummary = (
|
||||
unpriced_message_count: 0,
|
||||
total_input_tokens: 123_456,
|
||||
total_output_tokens: 654_321,
|
||||
total_cache_read_tokens: 9_876,
|
||||
total_cache_creation_tokens: 5_432,
|
||||
by_model: [
|
||||
{
|
||||
model_config_id: "model-config-1",
|
||||
@@ -23,6 +25,8 @@ const buildSummary = (
|
||||
message_count: 9,
|
||||
total_input_tokens: 100_000,
|
||||
total_output_tokens: 200_000,
|
||||
total_cache_read_tokens: 7_654,
|
||||
total_cache_creation_tokens: 3_210,
|
||||
},
|
||||
],
|
||||
by_chat: [
|
||||
@@ -33,6 +37,8 @@ const buildSummary = (
|
||||
message_count: 5,
|
||||
total_input_tokens: 60_000,
|
||||
total_output_tokens: 80_000,
|
||||
total_cache_read_tokens: 4_321,
|
||||
total_cache_creation_tokens: 1_234,
|
||||
},
|
||||
],
|
||||
...overrides,
|
||||
|
||||
@@ -61,8 +61,8 @@ export const ChatCostSummaryView: FC<ChatCostSummaryViewProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-2 gap-4 md:grid-cols-4">
|
||||
<div className="space-y-6">
|
||||
<div className="grid grid-cols-2 gap-4 md:grid-cols-3">
|
||||
<div className="rounded-lg border border-border-default bg-surface-secondary p-4">
|
||||
<p className="text-xs font-medium uppercase tracking-wide text-content-secondary">
|
||||
Total Cost
|
||||
@@ -87,6 +87,22 @@ export const ChatCostSummaryView: FC<ChatCostSummaryViewProps> = ({
|
||||
{formatTokenCount(summary.total_output_tokens)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-border-default bg-surface-secondary p-4">
|
||||
<p className="text-xs font-medium uppercase tracking-wide text-content-secondary">
|
||||
Cache Read
|
||||
</p>
|
||||
<p className="mt-1 text-2xl font-semibold text-content-primary">
|
||||
{formatTokenCount(summary.total_cache_read_tokens)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-border-default bg-surface-secondary p-4">
|
||||
<p className="text-xs font-medium uppercase tracking-wide text-content-secondary">
|
||||
Cache Write
|
||||
</p>
|
||||
<p className="mt-1 text-2xl font-semibold text-content-primary">
|
||||
{formatTokenCount(summary.total_cache_creation_tokens)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-border-default bg-surface-secondary p-4">
|
||||
<p className="text-xs font-medium uppercase tracking-wide text-content-secondary">
|
||||
Messages
|
||||
@@ -128,6 +144,12 @@ export const ChatCostSummaryView: FC<ChatCostSummaryViewProps> = ({
|
||||
</TableHead>
|
||||
<TableHead className="px-4 py-3 text-right">Input</TableHead>
|
||||
<TableHead className="px-4 py-3 text-right">Output</TableHead>
|
||||
<TableHead className="px-4 py-3 text-right">
|
||||
Cache Read
|
||||
</TableHead>
|
||||
<TableHead className="px-4 py-3 text-right">
|
||||
Cache Write
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
@@ -154,6 +176,12 @@ export const ChatCostSummaryView: FC<ChatCostSummaryViewProps> = ({
|
||||
<TableCell className="px-4 py-3 text-right">
|
||||
{formatTokenCount(model.total_output_tokens)}
|
||||
</TableCell>
|
||||
<TableCell className="px-4 py-3 text-right">
|
||||
{formatTokenCount(model.total_cache_read_tokens)}
|
||||
</TableCell>
|
||||
<TableCell className="px-4 py-3 text-right">
|
||||
{formatTokenCount(model.total_cache_creation_tokens)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
@@ -171,6 +199,12 @@ export const ChatCostSummaryView: FC<ChatCostSummaryViewProps> = ({
|
||||
</TableHead>
|
||||
<TableHead className="px-4 py-3 text-right">Input</TableHead>
|
||||
<TableHead className="px-4 py-3 text-right">Output</TableHead>
|
||||
<TableHead className="px-4 py-3 text-right">
|
||||
Cache Read
|
||||
</TableHead>
|
||||
<TableHead className="px-4 py-3 text-right">
|
||||
Cache Write
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
@@ -198,6 +232,12 @@ export const ChatCostSummaryView: FC<ChatCostSummaryViewProps> = ({
|
||||
<TableCell className="px-4 py-3 text-right">
|
||||
{formatTokenCount(chat.total_output_tokens)}
|
||||
</TableCell>
|
||||
<TableCell className="px-4 py-3 text-right">
|
||||
{formatTokenCount(chat.total_cache_read_tokens)}
|
||||
</TableCell>
|
||||
<TableCell className="px-4 py-3 text-right">
|
||||
{formatTokenCount(chat.total_cache_creation_tokens)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
@@ -205,6 +245,6 @@ export const ChatCostSummaryView: FC<ChatCostSummaryViewProps> = ({
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -90,6 +90,8 @@ const buildUsageUser = (
|
||||
chat_count: 3,
|
||||
total_input_tokens: 120_000,
|
||||
total_output_tokens: 45_000,
|
||||
total_cache_read_tokens: 6_789,
|
||||
total_cache_creation_tokens: 2_468,
|
||||
...overrides,
|
||||
});
|
||||
|
||||
@@ -109,6 +111,8 @@ const mockUsageUsers: ChatCostUsersResponse = {
|
||||
chat_count: 2,
|
||||
total_input_tokens: 80_000,
|
||||
total_output_tokens: 30_000,
|
||||
total_cache_read_tokens: 4_321,
|
||||
total_cache_creation_tokens: 1_234,
|
||||
}),
|
||||
],
|
||||
};
|
||||
@@ -121,6 +125,8 @@ const mockUsageSummary: ChatCostSummary = {
|
||||
unpriced_message_count: 0,
|
||||
total_input_tokens: 120_000,
|
||||
total_output_tokens: 45_000,
|
||||
total_cache_read_tokens: 6_789,
|
||||
total_cache_creation_tokens: 2_468,
|
||||
by_model: [
|
||||
{
|
||||
model_config_id: "model-cfg-1",
|
||||
@@ -131,6 +137,8 @@ const mockUsageSummary: ChatCostSummary = {
|
||||
message_count: 12,
|
||||
total_input_tokens: 120_000,
|
||||
total_output_tokens: 45_000,
|
||||
total_cache_read_tokens: 6_789,
|
||||
total_cache_creation_tokens: 2_468,
|
||||
},
|
||||
],
|
||||
by_chat: [
|
||||
@@ -141,6 +149,8 @@ const mockUsageSummary: ChatCostSummary = {
|
||||
message_count: 12,
|
||||
total_input_tokens: 120_000,
|
||||
total_output_tokens: 45_000,
|
||||
total_cache_read_tokens: 6_789,
|
||||
total_cache_creation_tokens: 2_468,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -129,6 +129,12 @@ const UserRow: FC<{
|
||||
<TableCell className="px-4 py-3 text-right">
|
||||
{formatTokenCount(user.total_output_tokens)}
|
||||
</TableCell>
|
||||
<TableCell className="px-4 py-3 text-right">
|
||||
{formatTokenCount(user.total_cache_read_tokens)}
|
||||
</TableCell>
|
||||
<TableCell className="px-4 py-3 text-right">
|
||||
{formatTokenCount(user.total_cache_creation_tokens)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
};
|
||||
@@ -306,6 +312,12 @@ const UsageContent: FC = () => {
|
||||
<TableHead className="px-4 py-3 text-right">
|
||||
Output Tokens
|
||||
</TableHead>
|
||||
<TableHead className="px-4 py-3 text-right">
|
||||
Cache Read
|
||||
</TableHead>
|
||||
<TableHead className="px-4 py-3 text-right">
|
||||
Cache Write
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
|
||||
@@ -14,6 +14,8 @@ const mockSummary: TypesGen.ChatCostSummary = {
|
||||
unpriced_message_count: 1,
|
||||
total_input_tokens: 123_456,
|
||||
total_output_tokens: 654_321,
|
||||
total_cache_read_tokens: 9_876,
|
||||
total_cache_creation_tokens: 5_432,
|
||||
by_model: [
|
||||
{
|
||||
model_config_id: "model-config-1",
|
||||
@@ -24,6 +26,8 @@ const mockSummary: TypesGen.ChatCostSummary = {
|
||||
message_count: 9,
|
||||
total_input_tokens: 100_000,
|
||||
total_output_tokens: 200_000,
|
||||
total_cache_read_tokens: 7_654,
|
||||
total_cache_creation_tokens: 3_210,
|
||||
},
|
||||
],
|
||||
by_chat: [
|
||||
@@ -34,6 +38,8 @@ const mockSummary: TypesGen.ChatCostSummary = {
|
||||
message_count: 5,
|
||||
total_input_tokens: 60_000,
|
||||
total_output_tokens: 80_000,
|
||||
total_cache_read_tokens: 4_321,
|
||||
total_cache_creation_tokens: 1_234,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user