feat: add Prometheus metrics for chatd subsystem (#24371)

Adds 7 Prometheus metrics to the chatd subsystem and introduces typed
`ActivityBumpReason` for deadline bump attribution.

| Metric | Type | Labels |
|--------|------|--------|
| `coderd_chatd_chats` | Gauge | `state` (streaming, waiting) |
| `coderd_chatd_message_count` | Histogram | `provider` |
| `coderd_chatd_prompt_size_bytes` | Histogram | `provider` |
| `coderd_chatd_tool_result_size_bytes` | Histogram | `provider`,
`tool_name` |
| `coderd_chatd_ttft_seconds` | Histogram | `provider` |
| `coderd_chatd_compaction_total` | Counter | `provider`, `result` |
| `coderd_chatd_steps_total` | Counter | `provider` |

> 🤖
This commit is contained in:
Cian Johnston
2026-04-15 19:53:10 +01:00
committed by GitHub
parent 2b68a1f4bd
commit d7439a9de0
13 changed files with 644 additions and 13 deletions
+18 -1
View File
@@ -11,6 +11,21 @@ import (
"github.com/coder/coder/v2/coderd/database"
)
// ActivityBumpReason represents the reason for an activity bump.
type ActivityBumpReason string
const (
// ActivityBumpReasonWorkspaceStats indicates the bump was triggered
// by SSH or terminal activity reported via workspace stats.
ActivityBumpReasonWorkspaceStats ActivityBumpReason = "workspace_stats"
// ActivityBumpReasonChatHeartbeat indicates the bump was triggered
// by an AI chat heartbeat.
ActivityBumpReasonChatHeartbeat ActivityBumpReason = "chat_heartbeat"
// ActivityBumpReasonAppActivity indicates the bump was triggered
// by app or port-forward activity.
ActivityBumpReasonAppActivity ActivityBumpReason = "app_activity"
)
// ActivityBumpWorkspace automatically bumps the workspace's auto-off timer
// if it is set to expire soon. The deadline will be bumped by 1 hour*.
// If the bump crosses over an autostart time, the workspace will be
@@ -36,7 +51,7 @@ import (
// A way to avoid this is to configure the max deadline to something that will not
// span more than 1 day. This will force the workspace to restart and reset the deadline
// each morning when it autostarts.
func ActivityBumpWorkspace(ctx context.Context, log slog.Logger, db database.Store, workspaceID uuid.UUID, nextAutostart time.Time) {
func ActivityBumpWorkspace(ctx context.Context, log slog.Logger, db database.Store, workspaceID uuid.UUID, nextAutostart time.Time, reason ActivityBumpReason) {
// We set a short timeout so if the app is under load, these
// low priority operations fail first.
ctx, cancel := context.WithTimeout(ctx, time.Second*15)
@@ -50,6 +65,7 @@ func ActivityBumpWorkspace(ctx context.Context, log slog.Logger, db database.Sto
// Bump will fail if the context is canceled, but this is ok.
log.Error(ctx, "activity bump failed", slog.Error(err),
slog.F("workspace_id", workspaceID),
slog.F("reason", reason),
)
}
return
@@ -57,5 +73,6 @@ func ActivityBumpWorkspace(ctx context.Context, log slog.Logger, db database.Sto
log.Debug(ctx, "bumped deadline from activity",
slog.F("workspace_id", workspaceID),
slog.F("reason", reason),
)
}
+2 -1
View File
@@ -268,13 +268,14 @@ func Test_ActivityBumpWorkspace(t *testing.T) {
// Bump duration is measured from the time of the bump, so we measure from here.
start := dbtime.Now()
workspacestats.ActivityBumpWorkspace(ctx, log, db, bld.WorkspaceID, nextAutostart(start))
workspacestats.ActivityBumpWorkspace(ctx, log, db, bld.WorkspaceID, nextAutostart(start), workspacestats.ActivityBumpReasonWorkspaceStats)
end := dbtime.Now()
// Validate our state after bump
updatedBuild, err := db.GetLatestWorkspaceBuildByWorkspaceID(ctx, bld.WorkspaceID)
require.NoError(t, err, "unexpected error getting latest workspace build")
require.Equal(t, bld.MaxDeadline.UTC(), updatedBuild.MaxDeadline.UTC(), "max_deadline should not have changed")
if tt.expectedBump == 0 {
assert.Equal(t, bld.UpdatedAt.UTC(), updatedBuild.UpdatedAt.UTC(), "should not have bumped updated_at")
assert.Equal(t, bld.Deadline.UTC(), updatedBuild.Deadline.UTC(), "should not have bumped deadline")
+1 -1
View File
@@ -194,7 +194,7 @@ func (r *Reporter) ReportAgentStats(ctx context.Context, now time.Time, workspac
}
// bump workspace activity
ActivityBumpWorkspace(ctx, r.opts.Logger.Named("activity_bump"), r.opts.Database, workspace.ID, nextAutostart)
ActivityBumpWorkspace(ctx, r.opts.Logger.Named("activity_bump"), r.opts.Database, workspace.ID, nextAutostart, ActivityBumpReasonWorkspaceStats)
}
// bump workspace last_used_at