mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add ai_user_daily_spend table and queries (#26562)
## Description Adds the spend tracking table and queries needed by [AIGOV-427](https://linear.app/codercom/issue/AIGOV-427/add-post-response-spend-accumulation) (post-response accumulation) and [AIGOV-428](https://linear.app/codercom/issue/AIGOV-428/add-pre-request-budget-enforcement) (pre-request enforcement). ## Changes - Add `ai_user_daily_spend` table to aggregate per-user, per-effective-group AI spend by UTC day. - Add `UpsertUserAIDailySpend` and `GetUserAISpendSince` queries. Closes https://linear.app/codercom/issue/AIGOV-426/add-daily-spend-table-and-queries > [!NOTE] > Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira
This commit is contained in:
Generated
+73
@@ -2546,6 +2546,79 @@ func (q *sqlQuerier) GetUserAIBudgetOverride(ctx context.Context, userID uuid.UU
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserAISpendSince = `-- name: GetUserAISpendSince :one
|
||||
SELECT
|
||||
$1::uuid AS user_id,
|
||||
$2::uuid AS effective_group_id,
|
||||
(($3::timestamptz) AT TIME ZONE 'UTC')::date AS period_start,
|
||||
COALESCE(SUM(spend_micros), 0)::BIGINT AS spend_micros
|
||||
FROM ai_user_daily_spend
|
||||
WHERE user_id = $1
|
||||
AND effective_group_id = $2
|
||||
AND day >= (($3::timestamptz) AT TIME ZONE 'UTC')::date
|
||||
`
|
||||
|
||||
type GetUserAISpendSinceParams struct {
|
||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||
EffectiveGroupID uuid.UUID `db:"effective_group_id" json:"effective_group_id"`
|
||||
PeriodStart time.Time `db:"period_start" json:"period_start"`
|
||||
}
|
||||
|
||||
type GetUserAISpendSinceRow struct {
|
||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||
EffectiveGroupID uuid.UUID `db:"effective_group_id" json:"effective_group_id"`
|
||||
PeriodStart time.Time `db:"period_start" json:"period_start"`
|
||||
SpendMicros int64 `db:"spend_micros" json:"spend_micros"`
|
||||
}
|
||||
|
||||
// Total spend for (user_id, effective_group_id) on or after period_start until NOW.
|
||||
// The period_start parameter is normalized to its UTC calendar day.
|
||||
func (q *sqlQuerier) GetUserAISpendSince(ctx context.Context, arg GetUserAISpendSinceParams) (GetUserAISpendSinceRow, error) {
|
||||
row := q.db.QueryRowContext(ctx, getUserAISpendSince, arg.UserID, arg.EffectiveGroupID, arg.PeriodStart)
|
||||
var i GetUserAISpendSinceRow
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.EffectiveGroupID,
|
||||
&i.PeriodStart,
|
||||
&i.SpendMicros,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const incrementUserAIDailySpend = `-- name: IncrementUserAIDailySpend :one
|
||||
INSERT INTO ai_user_daily_spend (user_id, effective_group_id, day, spend_micros)
|
||||
VALUES ($1, $2, (($3::timestamptz) AT TIME ZONE 'UTC')::date, $4)
|
||||
ON CONFLICT (user_id, effective_group_id, day) DO UPDATE SET
|
||||
spend_micros = ai_user_daily_spend.spend_micros + EXCLUDED.spend_micros
|
||||
RETURNING user_id, effective_group_id, day, spend_micros
|
||||
`
|
||||
|
||||
type IncrementUserAIDailySpendParams struct {
|
||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||
EffectiveGroupID uuid.UUID `db:"effective_group_id" json:"effective_group_id"`
|
||||
Day time.Time `db:"day" json:"day"`
|
||||
CostMicros int64 `db:"cost_micros" json:"cost_micros"`
|
||||
}
|
||||
|
||||
// Adds cost_micros to the spend for (user_id, effective_group_id, day).
|
||||
// The day parameter is normalized to its UTC calendar day before storage.
|
||||
func (q *sqlQuerier) IncrementUserAIDailySpend(ctx context.Context, arg IncrementUserAIDailySpendParams) (AIUserDailySpend, error) {
|
||||
row := q.db.QueryRowContext(ctx, incrementUserAIDailySpend,
|
||||
arg.UserID,
|
||||
arg.EffectiveGroupID,
|
||||
arg.Day,
|
||||
arg.CostMicros,
|
||||
)
|
||||
var i AIUserDailySpend
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.EffectiveGroupID,
|
||||
&i.Day,
|
||||
&i.SpendMicros,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const upsertAIModelPrices = `-- name: UpsertAIModelPrices :exec
|
||||
INSERT INTO ai_model_prices (
|
||||
provider, model, input_price, output_price, cache_read_price, cache_write_price
|
||||
|
||||
Reference in New Issue
Block a user