@convex-dev/ai-budget 0.0.2-alpha.4 → 0.0.2-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -6
- package/dist/client/dashboard.d.ts +1 -0
- package/dist/client/dashboard.js +215 -0
- package/dist/client/index.d.ts +347 -44
- package/dist/client/index.js +222 -68
- package/dist/component/_generated/component.d.ts +34 -0
- package/dist/component/lib.d.ts +75 -0
- package/dist/component/lib.js +309 -32
- package/dist/component/schema.d.ts +76 -3
- package/dist/component/schema.js +56 -2
- package/package.json +1 -1
- package/src/client/dashboard.ts +215 -0
- package/src/client/index.ts +334 -132
- package/src/component/_generated/component.ts +56 -3
- package/src/component/lib.test.ts +118 -0
- package/src/component/lib.ts +380 -48
- package/src/component/schema.ts +59 -2
package/dist/client/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Expand, type FunctionReference, type HttpRouter } from "convex/server";
|
|
2
2
|
import { type GenericId } from "convex/values";
|
|
3
3
|
import { type LanguageModel } from "ai";
|
|
4
4
|
import type { api } from "../component/_generated/api";
|
|
@@ -21,22 +21,34 @@ export type Tag = {
|
|
|
21
21
|
dimension: string;
|
|
22
22
|
value: string;
|
|
23
23
|
};
|
|
24
|
-
/**
|
|
25
|
-
export type
|
|
24
|
+
/** Common shape for budget-event callbacks. */
|
|
25
|
+
export type BudgetEventInfo = {
|
|
26
26
|
userId: string;
|
|
27
27
|
action?: string;
|
|
28
28
|
tags?: Tag[];
|
|
29
|
-
requestId
|
|
29
|
+
requestId?: string;
|
|
30
|
+
/** soft-cap warnings (onSoftLimit) or approaching-cap notices (onThreshold). */
|
|
31
|
+
messages: string[];
|
|
32
|
+
/** rejection code/reason (onLimitReached only). */
|
|
33
|
+
code?: string;
|
|
34
|
+
reason?: string;
|
|
35
|
+
};
|
|
36
|
+
/** @deprecated use BudgetEventInfo */
|
|
37
|
+
export type SoftLimitInfo = BudgetEventInfo & {
|
|
30
38
|
warnings: string[];
|
|
31
39
|
};
|
|
32
40
|
export type AIBudgetOptions = {
|
|
33
41
|
defaultModel?: string;
|
|
34
42
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
43
|
+
* A *soft* limit was exceeded (request still allowed). Lets you surface budget
|
|
44
|
+
* warnings even on the languageModel/Agent path where they can't be returned.
|
|
45
|
+
* Errors thrown in any of these callbacks are swallowed.
|
|
38
46
|
*/
|
|
39
47
|
onSoftLimit?: (info: SoftLimitInfo) => void | Promise<void>;
|
|
48
|
+
/** Usage crossed a bucket's warnAtPct threshold (approaching a cap). */
|
|
49
|
+
onThreshold?: (info: BudgetEventInfo) => void | Promise<void>;
|
|
50
|
+
/** A *hard* limit blocked the request (fires just before chat/model throws). */
|
|
51
|
+
onLimitReached?: (info: BudgetEventInfo) => void | Promise<void>;
|
|
40
52
|
};
|
|
41
53
|
type RunQueryCtx = {
|
|
42
54
|
runQuery: <Query extends FunctionReference<"query", "internal">>(query: Query, args: Query["_args"]) => Promise<Query["_returnType"]>;
|
|
@@ -67,13 +79,39 @@ export type ChatResult = {
|
|
|
67
79
|
cachedTokens: number;
|
|
68
80
|
/** Soft-limit warnings raised at admission (empty unless a soft cap was hit). */
|
|
69
81
|
warnings: string[];
|
|
82
|
+
/** Approaching-cap notices (empty unless a warnAtPct threshold was crossed). */
|
|
83
|
+
notices: string[];
|
|
84
|
+
};
|
|
85
|
+
/** Limits/controls settable on any budget bucket (user, action, or tag). */
|
|
86
|
+
export type BucketLimits = {
|
|
87
|
+
requestsPerMinute?: number;
|
|
88
|
+
maxConcurrent?: number;
|
|
89
|
+
dailySpendLimitNanos?: number;
|
|
90
|
+
monthlySpendLimitNanos?: number;
|
|
91
|
+
lifetimeSpendLimitNanos?: number;
|
|
92
|
+
dailyTokenLimit?: number;
|
|
93
|
+
monthlyTokenLimit?: number;
|
|
94
|
+
lifetimeTokenLimit?: number;
|
|
95
|
+
/** Fire an approaching-limit alert at this fraction of a cap (e.g. 0.8). */
|
|
96
|
+
warnAtPct?: number;
|
|
97
|
+
enforcement?: "hard" | "soft";
|
|
98
|
+
blocked?: boolean;
|
|
99
|
+
};
|
|
100
|
+
/** One-time bump amounts, added on top of a standing cap. */
|
|
101
|
+
export type BumpArgs = {
|
|
102
|
+
dailyNanos?: number;
|
|
103
|
+
monthlyNanos?: number;
|
|
104
|
+
lifetimeNanos?: number;
|
|
70
105
|
};
|
|
71
106
|
export declare class AIBudget {
|
|
72
107
|
component: AIBudgetApi;
|
|
73
108
|
defaultModel: string;
|
|
74
109
|
private onSoftLimit?;
|
|
110
|
+
private onThreshold?;
|
|
111
|
+
private onLimitReached?;
|
|
75
112
|
constructor(component: AIBudgetApi, options?: AIBudgetOptions);
|
|
76
|
-
private
|
|
113
|
+
private fireBudgetEvents;
|
|
114
|
+
private fireLimitReached;
|
|
77
115
|
/**
|
|
78
116
|
* One-shot chat through the AI Gateway with tracking + limits.
|
|
79
117
|
* Call from an action. `userId` defaults to the authenticated caller.
|
|
@@ -106,8 +144,11 @@ export declare class AIBudget {
|
|
|
106
144
|
private rerunImpl;
|
|
107
145
|
/** The request audit log, replay, and re-run lineage. */
|
|
108
146
|
get requests(): {
|
|
147
|
+
/** Filter by userId, or by any {dimension, value} (incl. custom tags). */
|
|
109
148
|
list: (ctx: RunQueryCtx, args?: {
|
|
110
149
|
userId?: string;
|
|
150
|
+
dimension?: string;
|
|
151
|
+
value?: string;
|
|
111
152
|
limit?: number;
|
|
112
153
|
}) => Promise<{
|
|
113
154
|
_id: string;
|
|
@@ -213,8 +254,8 @@ export declare class AIBudget {
|
|
|
213
254
|
* generalization of `users`/`actions`. Give it any dimension name (team,
|
|
214
255
|
* project, tenant, customer, env, feature, …) and set caps per value:
|
|
215
256
|
*
|
|
216
|
-
* ai.tag("customer").setLimits(ctx, { value: "acme",
|
|
217
|
-
* ai.tag("customer").
|
|
257
|
+
* ai.tag("customer").setLimits(ctx, { value: "acme", monthlySpendLimitNanos });
|
|
258
|
+
* ai.tag("customer").history(ctx, { value: "acme", period: "day" });
|
|
218
259
|
*
|
|
219
260
|
* Attribute a call to it by passing `tags` to `chat`/`languageModel`.
|
|
220
261
|
*/
|
|
@@ -222,22 +263,33 @@ export declare class AIBudget {
|
|
|
222
263
|
/** All buckets in this dimension. */
|
|
223
264
|
list: (ctx: RunQueryCtx) => Promise<{
|
|
224
265
|
spendTodayNanos: number;
|
|
266
|
+
spendThisMonthNanos: number;
|
|
225
267
|
_id: string;
|
|
226
268
|
_creationTime: number;
|
|
227
269
|
requestsPerMinute?: number | undefined;
|
|
270
|
+
maxConcurrent?: number | undefined;
|
|
228
271
|
dailySpendLimitNanos?: number | undefined;
|
|
272
|
+
monthlySpendLimitNanos?: number | undefined;
|
|
229
273
|
lifetimeSpendLimitNanos?: number | undefined;
|
|
230
274
|
dailyTokenLimit?: number | undefined;
|
|
275
|
+
monthlyTokenLimit?: number | undefined;
|
|
231
276
|
lifetimeTokenLimit?: number | undefined;
|
|
232
277
|
blocked?: boolean | undefined;
|
|
278
|
+
warnAtPct?: number | undefined;
|
|
233
279
|
enforcement?: "hard" | "soft" | undefined;
|
|
234
280
|
dailyBumpNanos?: number | undefined;
|
|
281
|
+
monthlyBumpNanos?: number | undefined;
|
|
235
282
|
lifetimeBumpNanos?: number | undefined;
|
|
236
283
|
bumpDayStamp?: string | undefined;
|
|
284
|
+
bumpMonthStamp?: string | undefined;
|
|
237
285
|
tokensToday?: number | undefined;
|
|
286
|
+
monthStamp?: string | undefined;
|
|
287
|
+
tokensThisMonth?: number | undefined;
|
|
238
288
|
reservedTodayNanos?: number | undefined;
|
|
289
|
+
reservedMonthNanos?: number | undefined;
|
|
239
290
|
reservedTotalNanos?: number | undefined;
|
|
240
291
|
reservedTodayTokens?: number | undefined;
|
|
292
|
+
reservedMonthTokens?: number | undefined;
|
|
241
293
|
reservedTotalTokens?: number | undefined;
|
|
242
294
|
pendingCount?: number | undefined;
|
|
243
295
|
dimension: string;
|
|
@@ -252,22 +304,33 @@ export declare class AIBudget {
|
|
|
252
304
|
value: string;
|
|
253
305
|
}) => Promise<{
|
|
254
306
|
spendTodayNanos: number;
|
|
307
|
+
spendThisMonthNanos: number;
|
|
255
308
|
_id: string;
|
|
256
309
|
_creationTime: number;
|
|
257
310
|
requestsPerMinute?: number | undefined;
|
|
311
|
+
maxConcurrent?: number | undefined;
|
|
258
312
|
dailySpendLimitNanos?: number | undefined;
|
|
313
|
+
monthlySpendLimitNanos?: number | undefined;
|
|
259
314
|
lifetimeSpendLimitNanos?: number | undefined;
|
|
260
315
|
dailyTokenLimit?: number | undefined;
|
|
316
|
+
monthlyTokenLimit?: number | undefined;
|
|
261
317
|
lifetimeTokenLimit?: number | undefined;
|
|
262
318
|
blocked?: boolean | undefined;
|
|
319
|
+
warnAtPct?: number | undefined;
|
|
263
320
|
enforcement?: "hard" | "soft" | undefined;
|
|
264
321
|
dailyBumpNanos?: number | undefined;
|
|
322
|
+
monthlyBumpNanos?: number | undefined;
|
|
265
323
|
lifetimeBumpNanos?: number | undefined;
|
|
266
324
|
bumpDayStamp?: string | undefined;
|
|
325
|
+
bumpMonthStamp?: string | undefined;
|
|
267
326
|
tokensToday?: number | undefined;
|
|
327
|
+
monthStamp?: string | undefined;
|
|
328
|
+
tokensThisMonth?: number | undefined;
|
|
268
329
|
reservedTodayNanos?: number | undefined;
|
|
330
|
+
reservedMonthNanos?: number | undefined;
|
|
269
331
|
reservedTotalNanos?: number | undefined;
|
|
270
332
|
reservedTodayTokens?: number | undefined;
|
|
333
|
+
reservedMonthTokens?: number | undefined;
|
|
271
334
|
reservedTotalTokens?: number | undefined;
|
|
272
335
|
pendingCount?: number | undefined;
|
|
273
336
|
dimension: string;
|
|
@@ -279,20 +342,50 @@ export declare class AIBudget {
|
|
|
279
342
|
} | null>;
|
|
280
343
|
setLimits: (ctx: RunMutationCtx, args: {
|
|
281
344
|
value: string;
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
lifetimeSpendLimitNanos?: number;
|
|
285
|
-
dailyTokenLimit?: number;
|
|
286
|
-
lifetimeTokenLimit?: number;
|
|
287
|
-
enforcement?: "hard" | "soft";
|
|
288
|
-
blocked?: boolean;
|
|
289
|
-
}) => Promise<null>;
|
|
290
|
-
/** One-time "approve another $X" bump (daily is today-only). */
|
|
345
|
+
} & BucketLimits) => Promise<null>;
|
|
346
|
+
/** One-time "approve another $X" bump (daily/monthly reset with the window). */
|
|
291
347
|
bump: (ctx: RunMutationCtx, args: {
|
|
292
348
|
value: string;
|
|
293
|
-
|
|
294
|
-
|
|
349
|
+
} & BumpArgs) => Promise<null>;
|
|
350
|
+
/** Manually credit (negative) or debit (positive) this bucket. */
|
|
351
|
+
adjust: (ctx: RunMutationCtx, args: {
|
|
352
|
+
value: string;
|
|
353
|
+
} & {
|
|
354
|
+
deltaNanos: number;
|
|
355
|
+
tokens?: number;
|
|
356
|
+
reason?: string;
|
|
295
357
|
}) => Promise<null>;
|
|
358
|
+
/** Durable spend history for this bucket (per day or per month). */
|
|
359
|
+
history: (ctx: RunQueryCtx, args: {
|
|
360
|
+
value: string;
|
|
361
|
+
} & {
|
|
362
|
+
period?: "day" | "month";
|
|
363
|
+
limit?: number;
|
|
364
|
+
}) => Promise<{
|
|
365
|
+
_id: string;
|
|
366
|
+
_creationTime: number;
|
|
367
|
+
dimension: string;
|
|
368
|
+
value: string;
|
|
369
|
+
period: "day" | "month";
|
|
370
|
+
stamp: string;
|
|
371
|
+
spendNanos: number;
|
|
372
|
+
tokens: number;
|
|
373
|
+
requests: number;
|
|
374
|
+
}[]>;
|
|
375
|
+
/** Manual-adjustment audit log for this bucket. */
|
|
376
|
+
adjustments: (ctx: RunQueryCtx, args: {
|
|
377
|
+
value: string;
|
|
378
|
+
} & {
|
|
379
|
+
limit?: number;
|
|
380
|
+
}) => Promise<{
|
|
381
|
+
_id: string;
|
|
382
|
+
_creationTime: number;
|
|
383
|
+
tokens?: number | undefined;
|
|
384
|
+
reason?: string | undefined;
|
|
385
|
+
dimension: string;
|
|
386
|
+
value: string;
|
|
387
|
+
deltaNanos: number;
|
|
388
|
+
}[]>;
|
|
296
389
|
/** Delete the bucket (for "user", also its request rows). */
|
|
297
390
|
delete: (ctx: RunMutationCtx, args: {
|
|
298
391
|
value: string;
|
|
@@ -301,26 +394,39 @@ export declare class AIBudget {
|
|
|
301
394
|
done: boolean;
|
|
302
395
|
}>;
|
|
303
396
|
};
|
|
397
|
+
private dimensionApi;
|
|
304
398
|
/** Per-user budgets and controls — sugar over the "user" dimension. */
|
|
305
399
|
get users(): {
|
|
400
|
+
/** All buckets in this dimension. */
|
|
306
401
|
list: (ctx: RunQueryCtx) => Promise<{
|
|
307
402
|
spendTodayNanos: number;
|
|
403
|
+
spendThisMonthNanos: number;
|
|
308
404
|
_id: string;
|
|
309
405
|
_creationTime: number;
|
|
310
406
|
requestsPerMinute?: number | undefined;
|
|
407
|
+
maxConcurrent?: number | undefined;
|
|
311
408
|
dailySpendLimitNanos?: number | undefined;
|
|
409
|
+
monthlySpendLimitNanos?: number | undefined;
|
|
312
410
|
lifetimeSpendLimitNanos?: number | undefined;
|
|
313
411
|
dailyTokenLimit?: number | undefined;
|
|
412
|
+
monthlyTokenLimit?: number | undefined;
|
|
314
413
|
lifetimeTokenLimit?: number | undefined;
|
|
315
414
|
blocked?: boolean | undefined;
|
|
415
|
+
warnAtPct?: number | undefined;
|
|
316
416
|
enforcement?: "hard" | "soft" | undefined;
|
|
317
417
|
dailyBumpNanos?: number | undefined;
|
|
418
|
+
monthlyBumpNanos?: number | undefined;
|
|
318
419
|
lifetimeBumpNanos?: number | undefined;
|
|
319
420
|
bumpDayStamp?: string | undefined;
|
|
421
|
+
bumpMonthStamp?: string | undefined;
|
|
320
422
|
tokensToday?: number | undefined;
|
|
423
|
+
monthStamp?: string | undefined;
|
|
424
|
+
tokensThisMonth?: number | undefined;
|
|
321
425
|
reservedTodayNanos?: number | undefined;
|
|
426
|
+
reservedMonthNanos?: number | undefined;
|
|
322
427
|
reservedTotalNanos?: number | undefined;
|
|
323
428
|
reservedTodayTokens?: number | undefined;
|
|
429
|
+
reservedMonthTokens?: number | undefined;
|
|
324
430
|
reservedTotalTokens?: number | undefined;
|
|
325
431
|
pendingCount?: number | undefined;
|
|
326
432
|
dimension: string;
|
|
@@ -330,23 +436,94 @@ export declare class AIBudget {
|
|
|
330
436
|
totalTokens: number;
|
|
331
437
|
dayStamp: string;
|
|
332
438
|
}[]>;
|
|
439
|
+
/** One bucket's limits + spend (null if it has none yet). */
|
|
440
|
+
get: (ctx: RunQueryCtx, args: {
|
|
441
|
+
userId: string;
|
|
442
|
+
}) => Promise<{
|
|
443
|
+
spendTodayNanos: number;
|
|
444
|
+
spendThisMonthNanos: number;
|
|
445
|
+
_id: string;
|
|
446
|
+
_creationTime: number;
|
|
447
|
+
requestsPerMinute?: number | undefined;
|
|
448
|
+
maxConcurrent?: number | undefined;
|
|
449
|
+
dailySpendLimitNanos?: number | undefined;
|
|
450
|
+
monthlySpendLimitNanos?: number | undefined;
|
|
451
|
+
lifetimeSpendLimitNanos?: number | undefined;
|
|
452
|
+
dailyTokenLimit?: number | undefined;
|
|
453
|
+
monthlyTokenLimit?: number | undefined;
|
|
454
|
+
lifetimeTokenLimit?: number | undefined;
|
|
455
|
+
blocked?: boolean | undefined;
|
|
456
|
+
warnAtPct?: number | undefined;
|
|
457
|
+
enforcement?: "hard" | "soft" | undefined;
|
|
458
|
+
dailyBumpNanos?: number | undefined;
|
|
459
|
+
monthlyBumpNanos?: number | undefined;
|
|
460
|
+
lifetimeBumpNanos?: number | undefined;
|
|
461
|
+
bumpDayStamp?: string | undefined;
|
|
462
|
+
bumpMonthStamp?: string | undefined;
|
|
463
|
+
tokensToday?: number | undefined;
|
|
464
|
+
monthStamp?: string | undefined;
|
|
465
|
+
tokensThisMonth?: number | undefined;
|
|
466
|
+
reservedTodayNanos?: number | undefined;
|
|
467
|
+
reservedMonthNanos?: number | undefined;
|
|
468
|
+
reservedTotalNanos?: number | undefined;
|
|
469
|
+
reservedTodayTokens?: number | undefined;
|
|
470
|
+
reservedMonthTokens?: number | undefined;
|
|
471
|
+
reservedTotalTokens?: number | undefined;
|
|
472
|
+
pendingCount?: number | undefined;
|
|
473
|
+
dimension: string;
|
|
474
|
+
value: string;
|
|
475
|
+
totalSpendNanos: number;
|
|
476
|
+
totalRequests: number;
|
|
477
|
+
totalTokens: number;
|
|
478
|
+
dayStamp: string;
|
|
479
|
+
} | null>;
|
|
333
480
|
setLimits: (ctx: RunMutationCtx, args: {
|
|
334
481
|
userId: string;
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
lifetimeSpendLimitNanos?: number;
|
|
338
|
-
dailyTokenLimit?: number;
|
|
339
|
-
lifetimeTokenLimit?: number;
|
|
340
|
-
enforcement?: "hard" | "soft";
|
|
341
|
-
blocked?: boolean;
|
|
342
|
-
}) => Promise<null>;
|
|
343
|
-
/** One-time "approve another $X" bump (daily is today-only). */
|
|
482
|
+
} & BucketLimits) => Promise<null>;
|
|
483
|
+
/** One-time "approve another $X" bump (daily/monthly reset with the window). */
|
|
344
484
|
bump: (ctx: RunMutationCtx, args: {
|
|
345
485
|
userId: string;
|
|
346
|
-
|
|
347
|
-
|
|
486
|
+
} & BumpArgs) => Promise<null>;
|
|
487
|
+
/** Manually credit (negative) or debit (positive) this bucket. */
|
|
488
|
+
adjust: (ctx: RunMutationCtx, args: {
|
|
489
|
+
userId: string;
|
|
490
|
+
} & {
|
|
491
|
+
deltaNanos: number;
|
|
492
|
+
tokens?: number;
|
|
493
|
+
reason?: string;
|
|
348
494
|
}) => Promise<null>;
|
|
349
|
-
/**
|
|
495
|
+
/** Durable spend history for this bucket (per day or per month). */
|
|
496
|
+
history: (ctx: RunQueryCtx, args: {
|
|
497
|
+
userId: string;
|
|
498
|
+
} & {
|
|
499
|
+
period?: "day" | "month";
|
|
500
|
+
limit?: number;
|
|
501
|
+
}) => Promise<{
|
|
502
|
+
_id: string;
|
|
503
|
+
_creationTime: number;
|
|
504
|
+
dimension: string;
|
|
505
|
+
value: string;
|
|
506
|
+
period: "day" | "month";
|
|
507
|
+
stamp: string;
|
|
508
|
+
spendNanos: number;
|
|
509
|
+
tokens: number;
|
|
510
|
+
requests: number;
|
|
511
|
+
}[]>;
|
|
512
|
+
/** Manual-adjustment audit log for this bucket. */
|
|
513
|
+
adjustments: (ctx: RunQueryCtx, args: {
|
|
514
|
+
userId: string;
|
|
515
|
+
} & {
|
|
516
|
+
limit?: number;
|
|
517
|
+
}) => Promise<{
|
|
518
|
+
_id: string;
|
|
519
|
+
_creationTime: number;
|
|
520
|
+
tokens?: number | undefined;
|
|
521
|
+
reason?: string | undefined;
|
|
522
|
+
dimension: string;
|
|
523
|
+
value: string;
|
|
524
|
+
deltaNanos: number;
|
|
525
|
+
}[]>;
|
|
526
|
+
/** Delete the bucket (for "user", also its request rows). */
|
|
350
527
|
delete: (ctx: RunMutationCtx, args: {
|
|
351
528
|
userId: string;
|
|
352
529
|
}) => Promise<{
|
|
@@ -356,24 +533,36 @@ export declare class AIBudget {
|
|
|
356
533
|
};
|
|
357
534
|
/** Per-action (per-feature) budgets — sugar over the "action" dimension. */
|
|
358
535
|
get actions(): {
|
|
536
|
+
/** All buckets in this dimension. */
|
|
359
537
|
list: (ctx: RunQueryCtx) => Promise<{
|
|
360
538
|
spendTodayNanos: number;
|
|
539
|
+
spendThisMonthNanos: number;
|
|
361
540
|
_id: string;
|
|
362
541
|
_creationTime: number;
|
|
363
542
|
requestsPerMinute?: number | undefined;
|
|
543
|
+
maxConcurrent?: number | undefined;
|
|
364
544
|
dailySpendLimitNanos?: number | undefined;
|
|
545
|
+
monthlySpendLimitNanos?: number | undefined;
|
|
365
546
|
lifetimeSpendLimitNanos?: number | undefined;
|
|
366
547
|
dailyTokenLimit?: number | undefined;
|
|
548
|
+
monthlyTokenLimit?: number | undefined;
|
|
367
549
|
lifetimeTokenLimit?: number | undefined;
|
|
368
550
|
blocked?: boolean | undefined;
|
|
551
|
+
warnAtPct?: number | undefined;
|
|
369
552
|
enforcement?: "hard" | "soft" | undefined;
|
|
370
553
|
dailyBumpNanos?: number | undefined;
|
|
554
|
+
monthlyBumpNanos?: number | undefined;
|
|
371
555
|
lifetimeBumpNanos?: number | undefined;
|
|
372
556
|
bumpDayStamp?: string | undefined;
|
|
557
|
+
bumpMonthStamp?: string | undefined;
|
|
373
558
|
tokensToday?: number | undefined;
|
|
559
|
+
monthStamp?: string | undefined;
|
|
560
|
+
tokensThisMonth?: number | undefined;
|
|
374
561
|
reservedTodayNanos?: number | undefined;
|
|
562
|
+
reservedMonthNanos?: number | undefined;
|
|
375
563
|
reservedTotalNanos?: number | undefined;
|
|
376
564
|
reservedTodayTokens?: number | undefined;
|
|
565
|
+
reservedMonthTokens?: number | undefined;
|
|
377
566
|
reservedTotalTokens?: number | undefined;
|
|
378
567
|
pendingCount?: number | undefined;
|
|
379
568
|
dimension: string;
|
|
@@ -383,23 +572,102 @@ export declare class AIBudget {
|
|
|
383
572
|
totalTokens: number;
|
|
384
573
|
dayStamp: string;
|
|
385
574
|
}[]>;
|
|
575
|
+
/** One bucket's limits + spend (null if it has none yet). */
|
|
576
|
+
get: (ctx: RunQueryCtx, args: {
|
|
577
|
+
name: string;
|
|
578
|
+
}) => Promise<{
|
|
579
|
+
spendTodayNanos: number;
|
|
580
|
+
spendThisMonthNanos: number;
|
|
581
|
+
_id: string;
|
|
582
|
+
_creationTime: number;
|
|
583
|
+
requestsPerMinute?: number | undefined;
|
|
584
|
+
maxConcurrent?: number | undefined;
|
|
585
|
+
dailySpendLimitNanos?: number | undefined;
|
|
586
|
+
monthlySpendLimitNanos?: number | undefined;
|
|
587
|
+
lifetimeSpendLimitNanos?: number | undefined;
|
|
588
|
+
dailyTokenLimit?: number | undefined;
|
|
589
|
+
monthlyTokenLimit?: number | undefined;
|
|
590
|
+
lifetimeTokenLimit?: number | undefined;
|
|
591
|
+
blocked?: boolean | undefined;
|
|
592
|
+
warnAtPct?: number | undefined;
|
|
593
|
+
enforcement?: "hard" | "soft" | undefined;
|
|
594
|
+
dailyBumpNanos?: number | undefined;
|
|
595
|
+
monthlyBumpNanos?: number | undefined;
|
|
596
|
+
lifetimeBumpNanos?: number | undefined;
|
|
597
|
+
bumpDayStamp?: string | undefined;
|
|
598
|
+
bumpMonthStamp?: string | undefined;
|
|
599
|
+
tokensToday?: number | undefined;
|
|
600
|
+
monthStamp?: string | undefined;
|
|
601
|
+
tokensThisMonth?: number | undefined;
|
|
602
|
+
reservedTodayNanos?: number | undefined;
|
|
603
|
+
reservedMonthNanos?: number | undefined;
|
|
604
|
+
reservedTotalNanos?: number | undefined;
|
|
605
|
+
reservedTodayTokens?: number | undefined;
|
|
606
|
+
reservedMonthTokens?: number | undefined;
|
|
607
|
+
reservedTotalTokens?: number | undefined;
|
|
608
|
+
pendingCount?: number | undefined;
|
|
609
|
+
dimension: string;
|
|
610
|
+
value: string;
|
|
611
|
+
totalSpendNanos: number;
|
|
612
|
+
totalRequests: number;
|
|
613
|
+
totalTokens: number;
|
|
614
|
+
dayStamp: string;
|
|
615
|
+
} | null>;
|
|
386
616
|
setLimits: (ctx: RunMutationCtx, args: {
|
|
387
617
|
name: string;
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
dailyTokenLimit?: number;
|
|
391
|
-
lifetimeTokenLimit?: number;
|
|
392
|
-
enforcement?: "hard" | "soft";
|
|
393
|
-
/** Hard-block this action (was `disabled`). */
|
|
394
|
-
blocked?: boolean;
|
|
395
|
-
}) => Promise<null>;
|
|
618
|
+
} & BucketLimits) => Promise<null>;
|
|
619
|
+
/** One-time "approve another $X" bump (daily/monthly reset with the window). */
|
|
396
620
|
bump: (ctx: RunMutationCtx, args: {
|
|
397
621
|
name: string;
|
|
398
|
-
|
|
399
|
-
|
|
622
|
+
} & BumpArgs) => Promise<null>;
|
|
623
|
+
/** Manually credit (negative) or debit (positive) this bucket. */
|
|
624
|
+
adjust: (ctx: RunMutationCtx, args: {
|
|
625
|
+
name: string;
|
|
626
|
+
} & {
|
|
627
|
+
deltaNanos: number;
|
|
628
|
+
tokens?: number;
|
|
629
|
+
reason?: string;
|
|
400
630
|
}) => Promise<null>;
|
|
631
|
+
/** Durable spend history for this bucket (per day or per month). */
|
|
632
|
+
history: (ctx: RunQueryCtx, args: {
|
|
633
|
+
name: string;
|
|
634
|
+
} & {
|
|
635
|
+
period?: "day" | "month";
|
|
636
|
+
limit?: number;
|
|
637
|
+
}) => Promise<{
|
|
638
|
+
_id: string;
|
|
639
|
+
_creationTime: number;
|
|
640
|
+
dimension: string;
|
|
641
|
+
value: string;
|
|
642
|
+
period: "day" | "month";
|
|
643
|
+
stamp: string;
|
|
644
|
+
spendNanos: number;
|
|
645
|
+
tokens: number;
|
|
646
|
+
requests: number;
|
|
647
|
+
}[]>;
|
|
648
|
+
/** Manual-adjustment audit log for this bucket. */
|
|
649
|
+
adjustments: (ctx: RunQueryCtx, args: {
|
|
650
|
+
name: string;
|
|
651
|
+
} & {
|
|
652
|
+
limit?: number;
|
|
653
|
+
}) => Promise<{
|
|
654
|
+
_id: string;
|
|
655
|
+
_creationTime: number;
|
|
656
|
+
tokens?: number | undefined;
|
|
657
|
+
reason?: string | undefined;
|
|
658
|
+
dimension: string;
|
|
659
|
+
value: string;
|
|
660
|
+
deltaNanos: number;
|
|
661
|
+
}[]>;
|
|
662
|
+
/** Delete the bucket (for "user", also its request rows). */
|
|
663
|
+
delete: (ctx: RunMutationCtx, args: {
|
|
664
|
+
name: string;
|
|
665
|
+
}) => Promise<{
|
|
666
|
+
deletedThisBatch: number;
|
|
667
|
+
done: boolean;
|
|
668
|
+
}>;
|
|
401
669
|
};
|
|
402
|
-
/** The deployment-wide budget and retention config. */
|
|
670
|
+
/** The deployment-wide budget, alerts, and retention config. */
|
|
403
671
|
get global(): {
|
|
404
672
|
/** Limits + spend today/total. */
|
|
405
673
|
status: (ctx: RunQueryCtx) => Promise<{
|
|
@@ -408,6 +676,8 @@ export declare class AIBudget {
|
|
|
408
676
|
enforcement: "hard" | "soft";
|
|
409
677
|
spentTodayNanos: number;
|
|
410
678
|
spentTotalNanos: number;
|
|
679
|
+
retentionMs: number | null;
|
|
680
|
+
defaultWarnAtPct: number | null;
|
|
411
681
|
}>;
|
|
412
682
|
/** A killswitch spend cap across all users/actions (enforced approximately). */
|
|
413
683
|
setLimits: (ctx: RunMutationCtx, args: {
|
|
@@ -419,6 +689,10 @@ export declare class AIBudget {
|
|
|
419
689
|
dailyNanos?: number;
|
|
420
690
|
lifetimeNanos?: number;
|
|
421
691
|
}) => Promise<null>;
|
|
692
|
+
/** Default approaching-limit alert threshold (fraction of a cap, e.g. 0.8). */
|
|
693
|
+
setAlertDefaults: (ctx: RunMutationCtx, args: {
|
|
694
|
+
warnAtPct?: number;
|
|
695
|
+
}) => Promise<null>;
|
|
422
696
|
/** Request-row retention window in ms (default 1h; 0 disables). */
|
|
423
697
|
setRetention: (ctx: RunMutationCtx, args: {
|
|
424
698
|
retentionMs: number;
|
|
@@ -442,6 +716,7 @@ export declare class AIBudget {
|
|
|
442
716
|
[x: string]: {
|
|
443
717
|
input: number;
|
|
444
718
|
output: number;
|
|
719
|
+
cached?: number | undefined;
|
|
445
720
|
overridden: boolean;
|
|
446
721
|
};
|
|
447
722
|
}>;
|
|
@@ -449,8 +724,36 @@ export declare class AIBudget {
|
|
|
449
724
|
model: string;
|
|
450
725
|
inputNanosPerMTok: number;
|
|
451
726
|
outputNanosPerMTok: number;
|
|
727
|
+
/** Cache-read rate; defaults to a discount off input if omitted. */
|
|
728
|
+
cachedNanosPerMTok?: number;
|
|
452
729
|
}) => Promise<null>;
|
|
453
730
|
};
|
|
731
|
+
/**
|
|
732
|
+
* Mount the built-in admin dashboard on your app's HTTP router with one call.
|
|
733
|
+
* Serves a self-contained HTML dashboard (buckets, requests, usage history,
|
|
734
|
+
* settings) plus a small JSON API, all backed by the component — no extra
|
|
735
|
+
* queries to write.
|
|
736
|
+
*
|
|
737
|
+
* // convex/http.ts
|
|
738
|
+
* import { httpRouter } from "convex/server";
|
|
739
|
+
* const http = httpRouter();
|
|
740
|
+
* ai.registerRoutes(http, { authorize: async (ctx) =>
|
|
741
|
+
* (await ctx.auth.getUserIdentity())?.role === "admin" });
|
|
742
|
+
* export default http;
|
|
743
|
+
*
|
|
744
|
+
* It then lives at `https://<deployment>.convex.site/aibudget`.
|
|
745
|
+
*
|
|
746
|
+
* SECURITY: the endpoint is public on the internet. You MUST gate it — either
|
|
747
|
+
* pass `authorize` (recommended: check the caller is a deployment admin) or
|
|
748
|
+
* set the `AI_BUDGET_DASHBOARD_TOKEN` env var (a bearer token / `?token=`).
|
|
749
|
+
* With neither, every route returns 401.
|
|
750
|
+
*/
|
|
751
|
+
registerRoutes(http: HttpRouter, opts?: {
|
|
752
|
+
/** Mount path (default "/aibudget"). */
|
|
753
|
+
path?: string;
|
|
754
|
+
/** Return true to allow the request. Runs on the HTML page and every API call. */
|
|
755
|
+
authorize?: (ctx: any, request: Request) => boolean | Promise<boolean>;
|
|
756
|
+
}): void;
|
|
454
757
|
}
|
|
455
758
|
/** @deprecated Renamed to `AIBudget`. */
|
|
456
759
|
export declare const WorryFreeAI: typeof AIBudget;
|