feat: notify users when AI spend crosses the budget threshold (#27346)

Implements:
https://linear.app/codercom/issue/AIGOV-289/notify-users-and-admins-on-budget-warning-and-limit-reached

Notify users when their AI spend crosses a budget threshold for their
effective group. Two thresholds are covered: a warning at 85%, and a
limit-reached notification at 100%.

Detection runs on the post-response path, right after the interception's
cost is added to the user's daily spend. It reads the user's AI spend on
the same transaction where token usage is recorded and AI daily spend is
incremented, and derives the pre-interception total by subtracting this
interception's cost. In case of `oldSpend < threshold && newSpend >=
threshold` - notification is sent. A single interception that crosses
both thresholds enqueues both notifications.

Detection and delivery are best-effort: a failure is logged and never
fails usage recording. The payload uses only stable values (the
threshold percentage and the spend limit, not the exact spend), so
duplicate enqueues are deduplicated by the notification system.

The two templates are added via migration and appear in each user's
notification settings under the "AI Budget" group.

Admin notifications (owners and user admins) are a follow-up: #27415.

## Screenshots:
<img width="1102" height="252" alt="image"
src="https://github.com/user-attachments/assets/62291510-09ca-4cdf-a1f5-4bdc11a1db4b"
/>

<img width="466" height="384" alt="image"
src="https://github.com/user-attachments/assets/030460ff-6fe2-4d59-b247-3550c543ef30"
/>

---------

Co-authored-by: Cian Johnston <cian@coder.com>
This commit is contained in:
Yevhenii Shcherbina
2026-07-27 12:09:21 -04:00
committed by GitHub
co-authored by Cian Johnston
parent c9e68987c1
commit ce4ee923c2
18 changed files with 940 additions and 3 deletions
+1
View File
@@ -696,6 +696,7 @@ var (
rbac.ResourceAiModelPrice.Type: {policy.ActionRead, policy.ActionUpdate}, // Read: per-interception cost lookup. Update: startup price seeder.
rbac.ResourceAiSeat.Type: {policy.ActionCreate}, // Required for UpsertAISeatState.
rbac.ResourceAIProvider.Type: {policy.ActionRead}, // Required to load the provider snapshot (and per-provider keys) at startup.
rbac.ResourceGroup.Type: {policy.ActionRead}, // Required to read the effective group.
}),
User: []rbac.Permission{},
ByOrgID: map[string]rbac.OrgPermissions{},
@@ -0,0 +1,5 @@
DELETE FROM notification_templates
WHERE id IN (
'b5db9597-de2a-4dea-87e9-25cee6906b86',
'cdcf2ecd-f003-4169-9800-abb2661ea522'
);
@@ -0,0 +1,53 @@
INSERT INTO notification_templates (
id,
name,
title_template,
body_template,
actions,
"group",
method,
kind,
enabled_by_default
)
VALUES (
'b5db9597-de2a-4dea-87e9-25cee6906b86',
'AI Budget Warning',
E'You''re approaching your {{.Labels.period}} AI budget limit',
$$You have used more than {{.Labels.threshold}}% of your {{.Labels.period}} AI budget ({{.Labels.limit}}).
Effective group: **{{.Labels.effective_group_name}}**
AI budget period: {{.Labels.period_start}} - {{.Labels.period_end}}$$,
'[]'::jsonb,
'AI Cost Control Events',
NULL,
'system'::notification_template_kind,
true
);
INSERT INTO notification_templates (
id,
name,
title_template,
body_template,
actions,
"group",
method,
kind,
enabled_by_default
)
VALUES (
'cdcf2ecd-f003-4169-9800-abb2661ea522',
'AI Budget Limit Reached',
E'You''ve reached your {{.Labels.period}} AI budget limit',
$$You have reached your {{.Labels.period}} AI budget limit ({{.Labels.limit}}). Subsequent requests will be blocked.
Effective group: **{{.Labels.effective_group_name}}**
AI budget period: {{.Labels.period_start}} - {{.Labels.period_end}}$$,
'[]'::jsonb,
'AI Cost Control Events',
NULL,
'system'::notification_template_kind,
true
);