feat: add total_runtime_ms to chat cost analytics endpoints (#24050)

Surface the aggregated `runtime_ms` from `chat_messages` through all
four cost analytics queries (summary, per-model, per-chat, per-user).
This is the key billing metric for agent compute time.

The per-chat breakdown already groups by `root_chat_id`, so subagent
runtime is automatically rolled up under the parent chat — no additional
query changes needed.

<details>
<summary>Implementation details</summary>

**SQL** (`coderd/database/queries/chats.sql`): Added
`COALESCE(SUM(cm.runtime_ms), 0)::bigint AS total_runtime_ms` to
`GetChatCostSummary`, `GetChatCostPerModel`, `GetChatCostPerChat`, and
`GetChatCostPerUser`.

**Go SDK** (`codersdk/chats.go`): Added `TotalRuntimeMs int64` to
`ChatCostSummary`, `ChatCostModelBreakdown`, `ChatCostChatBreakdown`,
and `ChatCostUserRollup`.

**Handler** (`coderd/exp_chats.go`): Wired the new field through all
converter functions and the response assembly.

**Tests** (`coderd/exp_chats_test.go`): Updated fixture to seed non-zero
`runtime_ms` values and added assertions for the new field at summary,
per-model, and per-chat levels.
</details>

> 🤖 Generated by Coder Agents
This commit is contained in:
Kyle Carberry
2026-04-06 12:10:57 -04:00
committed by GitHub
parent 0060dee222
commit a2ce74f398
9 changed files with 58 additions and 11 deletions
+4
View File
@@ -974,6 +974,7 @@ type ChatCostSummary struct {
TotalOutputTokens int64 `json:"total_output_tokens"`
TotalCacheReadTokens int64 `json:"total_cache_read_tokens"`
TotalCacheCreationTokens int64 `json:"total_cache_creation_tokens"`
TotalRuntimeMs int64 `json:"total_runtime_ms"`
ByModel []ChatCostModelBreakdown `json:"by_model"`
ByChat []ChatCostChatBreakdown `json:"by_chat"`
UsageLimit *ChatUsageLimitStatus `json:"usage_limit,omitempty"`
@@ -991,6 +992,7 @@ type ChatCostModelBreakdown struct {
TotalOutputTokens int64 `json:"total_output_tokens"`
TotalCacheReadTokens int64 `json:"total_cache_read_tokens"`
TotalCacheCreationTokens int64 `json:"total_cache_creation_tokens"`
TotalRuntimeMs int64 `json:"total_runtime_ms"`
}
// ChatCostChatBreakdown contains per-root-chat cost aggregation.
@@ -1003,6 +1005,7 @@ type ChatCostChatBreakdown struct {
TotalOutputTokens int64 `json:"total_output_tokens"`
TotalCacheReadTokens int64 `json:"total_cache_read_tokens"`
TotalCacheCreationTokens int64 `json:"total_cache_creation_tokens"`
TotalRuntimeMs int64 `json:"total_runtime_ms"`
}
// ChatCostUserRollup contains per-user cost aggregation for admin views.
@@ -1018,6 +1021,7 @@ type ChatCostUserRollup struct {
TotalOutputTokens int64 `json:"total_output_tokens"`
TotalCacheReadTokens int64 `json:"total_cache_read_tokens"`
TotalCacheCreationTokens int64 `json:"total_cache_creation_tokens"`
TotalRuntimeMs int64 `json:"total_runtime_ms"`
}
// ChatCostUsersResponse is the response from the admin chat cost users endpoint.