@convex-dev/ai-budget 0.0.2-alpha.3 → 0.0.2-alpha.5
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 +116 -17
- package/dist/client/index.d.ts +433 -34
- package/dist/client/index.js +141 -33
- package/dist/component/_generated/component.d.ts +54 -24
- package/dist/component/lib.d.ts +130 -40
- package/dist/component/lib.js +550 -290
- package/dist/component/schema.d.ts +103 -53
- package/dist/component/schema.js +81 -38
- package/package.json +1 -1
- package/src/client/index.ts +241 -71
- package/src/component/_generated/component.ts +77 -29
- package/src/component/lib.test.ts +167 -15
- package/src/component/lib.ts +640 -313
- package/src/component/schema.ts +84 -38
package/dist/client/index.js
CHANGED
|
@@ -39,14 +39,30 @@ function extractUsage(usage) {
|
|
|
39
39
|
return {
|
|
40
40
|
promptTokens: toTokenCount(usage?.inputTokens ?? usage?.promptTokens),
|
|
41
41
|
completionTokens: toTokenCount(usage?.outputTokens ?? usage?.completionTokens),
|
|
42
|
-
// cached prompt tokens
|
|
43
|
-
// `
|
|
44
|
-
|
|
42
|
+
// cached prompt tokens. The Convex gateway reports these at
|
|
43
|
+
// `usage.inputTokenDetails.cacheReadTokens`; the other paths cover AI SDK v5
|
|
44
|
+
// (`cachedInputTokens`) and raw OpenAI-compatible shapes.
|
|
45
|
+
cachedTokens: toTokenCount(usage?.inputTokenDetails?.cacheReadTokens ??
|
|
46
|
+
usage?.cachedInputTokens ??
|
|
45
47
|
usage?.promptTokensDetails?.cachedTokens ??
|
|
46
48
|
usage?.prompt_tokens_details?.cached_tokens ??
|
|
47
49
|
usage?.cached_tokens),
|
|
48
50
|
};
|
|
49
51
|
}
|
|
52
|
+
// The gateway doesn't report a dollar cost today, but if a future response ever
|
|
53
|
+
// carries an unambiguous nanodollar cost we pass it straight through as
|
|
54
|
+
// authoritative (finishRequest prefers it over the token-based estimate). Only
|
|
55
|
+
// an explicitly nano-denominated field is trusted — a bare `cost` could be in
|
|
56
|
+
// dollars and silently mis-bill by 1e9×.
|
|
57
|
+
function extractGatewayCostNanos(result) {
|
|
58
|
+
const meta = result?.providerMetadata?.convexGateway;
|
|
59
|
+
const candidates = [meta?.costNanos, result?.usage?.costNanos];
|
|
60
|
+
for (const c of candidates) {
|
|
61
|
+
if (typeof c === "number" && Number.isFinite(c) && c >= 0)
|
|
62
|
+
return c;
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
50
66
|
// Flatten an AI SDK prompt (roles + content parts) into simple storable messages.
|
|
51
67
|
function simplifyPrompt(prompt) {
|
|
52
68
|
if (!Array.isArray(prompt))
|
|
@@ -78,24 +94,46 @@ function extractText(result) {
|
|
|
78
94
|
}
|
|
79
95
|
return "";
|
|
80
96
|
}
|
|
81
|
-
// ---------- client ----------
|
|
82
97
|
export class AIBudget {
|
|
83
98
|
component;
|
|
84
99
|
defaultModel;
|
|
85
100
|
onSoftLimit;
|
|
101
|
+
onThreshold;
|
|
102
|
+
onLimitReached;
|
|
86
103
|
constructor(component, options) {
|
|
87
104
|
this.component = component;
|
|
88
105
|
this.defaultModel = options?.defaultModel ?? "openai/gpt-4o-mini";
|
|
89
106
|
this.onSoftLimit = options?.onSoftLimit;
|
|
107
|
+
this.onThreshold = options?.onThreshold;
|
|
108
|
+
this.onLimitReached = options?.onLimitReached;
|
|
109
|
+
}
|
|
110
|
+
// Fire the soft-limit + threshold callbacks from a startRequest result.
|
|
111
|
+
async fireBudgetEvents(base, warnings, notices) {
|
|
112
|
+
if (warnings.length > 0 && this.onSoftLimit) {
|
|
113
|
+
try {
|
|
114
|
+
await this.onSoftLimit({ ...base, messages: warnings, warnings });
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
/* never let a callback error break a request */
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (notices.length > 0 && this.onThreshold) {
|
|
121
|
+
try {
|
|
122
|
+
await this.onThreshold({ ...base, messages: notices });
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
/* swallow */
|
|
126
|
+
}
|
|
127
|
+
}
|
|
90
128
|
}
|
|
91
|
-
async
|
|
92
|
-
if (
|
|
129
|
+
async fireLimitReached(info) {
|
|
130
|
+
if (!this.onLimitReached)
|
|
93
131
|
return;
|
|
94
132
|
try {
|
|
95
|
-
await this.
|
|
133
|
+
await this.onLimitReached(info);
|
|
96
134
|
}
|
|
97
135
|
catch {
|
|
98
|
-
|
|
136
|
+
/* swallow */
|
|
99
137
|
}
|
|
100
138
|
}
|
|
101
139
|
/**
|
|
@@ -110,11 +148,20 @@ export class AIBudget {
|
|
|
110
148
|
const started = await ctx.runMutation(this.component.lib.startRequest, {
|
|
111
149
|
userId,
|
|
112
150
|
actionName,
|
|
151
|
+
tags: args.tags,
|
|
113
152
|
model,
|
|
114
153
|
messages,
|
|
115
154
|
rerunOf: args.rerunOf,
|
|
116
155
|
});
|
|
117
156
|
if (!started.allowed) {
|
|
157
|
+
await this.fireLimitReached({
|
|
158
|
+
userId,
|
|
159
|
+
action: actionName,
|
|
160
|
+
tags: args.tags,
|
|
161
|
+
messages: [started.reason],
|
|
162
|
+
code: started.code,
|
|
163
|
+
reason: started.reason,
|
|
164
|
+
});
|
|
118
165
|
throw new ConvexError({
|
|
119
166
|
kind: "AIBudgetLimit",
|
|
120
167
|
code: started.code,
|
|
@@ -123,7 +170,8 @@ export class AIBudget {
|
|
|
123
170
|
}
|
|
124
171
|
const requestId = started.requestId;
|
|
125
172
|
const warnings = started.warnings;
|
|
126
|
-
|
|
173
|
+
const notices = started.notices;
|
|
174
|
+
await this.fireBudgetEvents({ userId, action: actionName, tags: args.tags, requestId }, warnings, notices);
|
|
127
175
|
const start = Date.now();
|
|
128
176
|
try {
|
|
129
177
|
// The full chain (incl. system) is stored on the request for audit/replay,
|
|
@@ -143,9 +191,10 @@ export class AIBudget {
|
|
|
143
191
|
requestId,
|
|
144
192
|
responseText: result.text,
|
|
145
193
|
...usage,
|
|
194
|
+
costNanos: extractGatewayCostNanos(result),
|
|
146
195
|
latencyMs: Date.now() - start,
|
|
147
196
|
});
|
|
148
|
-
return { text: result.text, requestId, costNanos, warnings, ...usage };
|
|
197
|
+
return { text: result.text, requestId, costNanos, warnings, notices, ...usage };
|
|
149
198
|
}
|
|
150
199
|
catch (e) {
|
|
151
200
|
await ctx.runMutation(this.component.lib.finishRequest, {
|
|
@@ -165,29 +214,33 @@ export class AIBudget {
|
|
|
165
214
|
languageModel(ctx, opts = {}) {
|
|
166
215
|
const modelId = opts.model ?? this.defaultModel;
|
|
167
216
|
const component = this.component;
|
|
168
|
-
const
|
|
217
|
+
const fireBudgetEvents = this.fireBudgetEvents.bind(this);
|
|
218
|
+
const fireLimitReached = this.fireLimitReached.bind(this);
|
|
169
219
|
const begin = async (params) => {
|
|
170
220
|
const userId = await resolveUserId(ctx, opts.userId);
|
|
171
221
|
const actionName = await resolveActionName(ctx, opts.action);
|
|
222
|
+
const base = { userId, action: actionName, tags: opts.tags };
|
|
172
223
|
const started = await ctx.runMutation(component.lib.startRequest, {
|
|
173
224
|
userId,
|
|
174
225
|
actionName,
|
|
226
|
+
tags: opts.tags,
|
|
175
227
|
model: modelId,
|
|
176
228
|
messages: simplifyPrompt(params.prompt),
|
|
177
229
|
});
|
|
178
230
|
if (!started.allowed) {
|
|
231
|
+
await fireLimitReached({
|
|
232
|
+
...base,
|
|
233
|
+
messages: [started.reason],
|
|
234
|
+
code: started.code,
|
|
235
|
+
reason: started.reason,
|
|
236
|
+
});
|
|
179
237
|
throw new ConvexError({
|
|
180
238
|
kind: "AIBudgetLimit",
|
|
181
239
|
code: started.code,
|
|
182
240
|
reason: started.reason,
|
|
183
241
|
});
|
|
184
242
|
}
|
|
185
|
-
await
|
|
186
|
-
userId,
|
|
187
|
-
action: actionName,
|
|
188
|
-
requestId: started.requestId,
|
|
189
|
-
warnings: started.warnings,
|
|
190
|
-
});
|
|
243
|
+
await fireBudgetEvents({ ...base, requestId: started.requestId }, started.warnings, started.notices);
|
|
191
244
|
return started.requestId;
|
|
192
245
|
};
|
|
193
246
|
const finish = async (requestId, fields) => ctx.runMutation(component.lib.finishRequest, { requestId, ...fields });
|
|
@@ -202,6 +255,7 @@ export class AIBudget {
|
|
|
202
255
|
await finish(requestId, {
|
|
203
256
|
responseText: extractText(result),
|
|
204
257
|
...extractUsage(result.usage),
|
|
258
|
+
costNanos: extractGatewayCostNanos(result),
|
|
205
259
|
latencyMs: Date.now() - start,
|
|
206
260
|
});
|
|
207
261
|
return result;
|
|
@@ -278,6 +332,7 @@ export class AIBudget {
|
|
|
278
332
|
messages: args.messages ?? original.messages,
|
|
279
333
|
rerunOf: args.requestId,
|
|
280
334
|
action: original.actionName,
|
|
335
|
+
tags: original.tags,
|
|
281
336
|
});
|
|
282
337
|
}
|
|
283
338
|
// ---------- namespaced admin API ----------
|
|
@@ -285,6 +340,7 @@ export class AIBudget {
|
|
|
285
340
|
get requests() {
|
|
286
341
|
const c = this.component;
|
|
287
342
|
return {
|
|
343
|
+
/** Filter by userId, or by any {dimension, value} (incl. custom tags). */
|
|
288
344
|
list: (ctx, args = {}) => ctx.runQuery(c.lib.listRequests, args),
|
|
289
345
|
/** Ancestors up to the original, plus direct re-runs. */
|
|
290
346
|
lineage: (ctx, args) => ctx.runQuery(c.lib.lineage, { requestId: args.requestId }),
|
|
@@ -292,28 +348,78 @@ export class AIBudget {
|
|
|
292
348
|
rerun: (ctx, args) => this.rerunImpl(ctx, args),
|
|
293
349
|
};
|
|
294
350
|
}
|
|
295
|
-
/**
|
|
296
|
-
|
|
351
|
+
/**
|
|
352
|
+
* Budgets and controls for an arbitrary attribution dimension — the
|
|
353
|
+
* generalization of `users`/`actions`. Give it any dimension name (team,
|
|
354
|
+
* project, tenant, customer, env, feature, …) and set caps per value:
|
|
355
|
+
*
|
|
356
|
+
* ai.tag("customer").setLimits(ctx, { value: "acme", monthlySpendLimitNanos });
|
|
357
|
+
* ai.tag("customer").history(ctx, { value: "acme", period: "day" });
|
|
358
|
+
*
|
|
359
|
+
* Attribute a call to it by passing `tags` to `chat`/`languageModel`.
|
|
360
|
+
*/
|
|
361
|
+
tag(dimension) {
|
|
362
|
+
return this.dimensionApi(dimension, (a) => a.value);
|
|
363
|
+
}
|
|
364
|
+
// Shared implementation behind tag()/users/actions. `key` maps the namespace's
|
|
365
|
+
// id field (value/userId/name) to the bucket value.
|
|
366
|
+
dimensionApi(dimension, key) {
|
|
297
367
|
const c = this.component;
|
|
298
368
|
return {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
/** One
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
369
|
+
/** All buckets in this dimension. */
|
|
370
|
+
list: (ctx) => ctx.runQuery(c.lib.listBuckets, { dimension }),
|
|
371
|
+
/** One bucket's limits + spend (null if it has none yet). */
|
|
372
|
+
get: (ctx, args) => ctx.runQuery(c.lib.getBucket, { dimension, value: key(args) }),
|
|
373
|
+
setLimits: (ctx, args) => {
|
|
374
|
+
const { value, userId, name, ...limits } = args;
|
|
375
|
+
return ctx.runMutation(c.lib.setBucketLimits, {
|
|
376
|
+
dimension,
|
|
377
|
+
value: key(args),
|
|
378
|
+
...limits,
|
|
379
|
+
});
|
|
380
|
+
},
|
|
381
|
+
/** One-time "approve another $X" bump (daily/monthly reset with the window). */
|
|
382
|
+
bump: (ctx, args) => ctx.runMutation(c.lib.bumpBucket, {
|
|
383
|
+
dimension,
|
|
384
|
+
value: key(args),
|
|
385
|
+
dailyNanos: args.dailyNanos,
|
|
386
|
+
monthlyNanos: args.monthlyNanos,
|
|
387
|
+
lifetimeNanos: args.lifetimeNanos,
|
|
388
|
+
}),
|
|
389
|
+
/** Manually credit (negative) or debit (positive) this bucket. */
|
|
390
|
+
adjust: (ctx, args) => ctx.runMutation(c.lib.adjustBucket, {
|
|
391
|
+
dimension,
|
|
392
|
+
value: key(args),
|
|
393
|
+
deltaNanos: args.deltaNanos,
|
|
394
|
+
tokens: args.tokens,
|
|
395
|
+
reason: args.reason,
|
|
396
|
+
}),
|
|
397
|
+
/** Durable spend history for this bucket (per day or per month). */
|
|
398
|
+
history: (ctx, args) => ctx.runQuery(c.lib.usageHistory, {
|
|
399
|
+
dimension,
|
|
400
|
+
value: key(args),
|
|
401
|
+
period: args.period ?? "day",
|
|
402
|
+
limit: args.limit,
|
|
403
|
+
}),
|
|
404
|
+
/** Manual-adjustment audit log for this bucket. */
|
|
405
|
+
adjustments: (ctx, args) => ctx.runQuery(c.lib.listAdjustments, {
|
|
406
|
+
dimension,
|
|
407
|
+
value: key(args),
|
|
408
|
+
limit: args.limit,
|
|
409
|
+
}),
|
|
410
|
+
/** Delete the bucket (for "user", also its request rows). */
|
|
411
|
+
delete: (ctx, args) => ctx.runMutation(c.lib.deleteBucket, { dimension, value: key(args) }),
|
|
305
412
|
};
|
|
306
413
|
}
|
|
307
|
-
/** Per-
|
|
414
|
+
/** Per-user budgets and controls — sugar over the "user" dimension. */
|
|
415
|
+
get users() {
|
|
416
|
+
return this.dimensionApi("user", (a) => a.userId);
|
|
417
|
+
}
|
|
418
|
+
/** Per-action (per-feature) budgets — sugar over the "action" dimension. */
|
|
308
419
|
get actions() {
|
|
309
|
-
|
|
310
|
-
return {
|
|
311
|
-
list: (ctx) => ctx.runQuery(c.lib.listActions, {}),
|
|
312
|
-
setLimits: (ctx, args) => ctx.runMutation(c.lib.setActionLimits, args),
|
|
313
|
-
bump: (ctx, args) => ctx.runMutation(c.lib.bumpAction, args),
|
|
314
|
-
};
|
|
420
|
+
return this.dimensionApi("action", (a) => a.name);
|
|
315
421
|
}
|
|
316
|
-
/** The deployment-wide budget and retention config. */
|
|
422
|
+
/** The deployment-wide budget, alerts, and retention config. */
|
|
317
423
|
get global() {
|
|
318
424
|
const c = this.component;
|
|
319
425
|
return {
|
|
@@ -322,6 +428,8 @@ export class AIBudget {
|
|
|
322
428
|
/** A killswitch spend cap across all users/actions (enforced approximately). */
|
|
323
429
|
setLimits: (ctx, args) => ctx.runMutation(c.lib.setGlobalLimits, args),
|
|
324
430
|
bump: (ctx, args) => ctx.runMutation(c.lib.bumpGlobal, args),
|
|
431
|
+
/** Default approaching-limit alert threshold (fraction of a cap, e.g. 0.8). */
|
|
432
|
+
setAlertDefaults: (ctx, args) => ctx.runMutation(c.lib.setAlertDefaults, args),
|
|
325
433
|
/** Request-row retention window in ms (default 1h; 0 disables). */
|
|
326
434
|
setRetention: (ctx, args) => ctx.runMutation(c.lib.setRetention, args),
|
|
327
435
|
};
|
|
@@ -20,22 +20,28 @@ import type { FunctionReference } from "convex/server";
|
|
|
20
20
|
*/
|
|
21
21
|
export type ComponentApi<Name extends string | undefined = string | undefined> = {
|
|
22
22
|
lib: {
|
|
23
|
-
|
|
23
|
+
adjustBucket: FunctionReference<"mutation", "internal", {
|
|
24
|
+
deltaNanos: number;
|
|
25
|
+
dimension: string;
|
|
26
|
+
reason?: string;
|
|
27
|
+
tokens?: number;
|
|
28
|
+
value: string;
|
|
29
|
+
}, null, Name>;
|
|
30
|
+
bumpBucket: FunctionReference<"mutation", "internal", {
|
|
24
31
|
dailyNanos?: number;
|
|
32
|
+
dimension: string;
|
|
25
33
|
lifetimeNanos?: number;
|
|
26
|
-
|
|
34
|
+
monthlyNanos?: number;
|
|
35
|
+
value: string;
|
|
27
36
|
}, null, Name>;
|
|
28
37
|
bumpGlobal: FunctionReference<"mutation", "internal", {
|
|
29
38
|
dailyNanos?: number;
|
|
30
39
|
lifetimeNanos?: number;
|
|
40
|
+
monthlyNanos?: number;
|
|
31
41
|
}, null, Name>;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
userId: string;
|
|
36
|
-
}, null, Name>;
|
|
37
|
-
deleteUser: FunctionReference<"mutation", "internal", {
|
|
38
|
-
userId: string;
|
|
42
|
+
deleteBucket: FunctionReference<"mutation", "internal", {
|
|
43
|
+
dimension: string;
|
|
44
|
+
value: string;
|
|
39
45
|
}, {
|
|
40
46
|
deletedThisBatch: number;
|
|
41
47
|
done: boolean;
|
|
@@ -43,6 +49,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
43
49
|
finishRequest: FunctionReference<"mutation", "internal", {
|
|
44
50
|
cachedTokens?: number;
|
|
45
51
|
completionTokens?: number;
|
|
52
|
+
costNanos?: number;
|
|
46
53
|
error?: string;
|
|
47
54
|
latencyMs?: number;
|
|
48
55
|
promptTokens?: number;
|
|
@@ -51,6 +58,10 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
51
58
|
}, {
|
|
52
59
|
costNanos: number;
|
|
53
60
|
}, Name>;
|
|
61
|
+
getBucket: FunctionReference<"query", "internal", {
|
|
62
|
+
dimension: string;
|
|
63
|
+
value: string;
|
|
64
|
+
}, any, Name>;
|
|
54
65
|
getGlobalStatus: FunctionReference<"query", "internal", {}, {
|
|
55
66
|
dailySpendLimitNanos: number | null;
|
|
56
67
|
enforcement: "hard" | "soft";
|
|
@@ -68,42 +79,50 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
68
79
|
lineage: FunctionReference<"query", "internal", {
|
|
69
80
|
requestId: string;
|
|
70
81
|
}, any, Name>;
|
|
71
|
-
|
|
82
|
+
listAdjustments: FunctionReference<"query", "internal", {
|
|
83
|
+
dimension: string;
|
|
84
|
+
limit?: number;
|
|
85
|
+
value: string;
|
|
86
|
+
}, any, Name>;
|
|
87
|
+
listBuckets: FunctionReference<"query", "internal", {
|
|
88
|
+
dimension?: string;
|
|
89
|
+
}, any, Name>;
|
|
72
90
|
listPrices: FunctionReference<"query", "internal", {}, any, Name>;
|
|
73
91
|
listRequests: FunctionReference<"query", "internal", {
|
|
92
|
+
dimension?: string;
|
|
74
93
|
limit?: number;
|
|
75
94
|
userId?: string;
|
|
95
|
+
value?: string;
|
|
76
96
|
}, any, Name>;
|
|
77
|
-
|
|
78
|
-
|
|
97
|
+
setAlertDefaults: FunctionReference<"mutation", "internal", {
|
|
98
|
+
warnAtPct?: number;
|
|
99
|
+
}, null, Name>;
|
|
100
|
+
setBucketLimits: FunctionReference<"mutation", "internal", {
|
|
101
|
+
blocked?: boolean;
|
|
79
102
|
dailySpendLimitNanos?: number;
|
|
80
103
|
dailyTokenLimit?: number;
|
|
81
|
-
|
|
104
|
+
dimension: string;
|
|
82
105
|
enforcement?: "hard" | "soft";
|
|
83
106
|
lifetimeSpendLimitNanos?: number;
|
|
84
107
|
lifetimeTokenLimit?: number;
|
|
85
|
-
|
|
108
|
+
maxConcurrent?: number;
|
|
109
|
+
monthlySpendLimitNanos?: number;
|
|
110
|
+
monthlyTokenLimit?: number;
|
|
111
|
+
requestsPerMinute?: number;
|
|
112
|
+
value: string;
|
|
113
|
+
warnAtPct?: number;
|
|
86
114
|
}, null, Name>;
|
|
87
115
|
setGlobalLimits: FunctionReference<"mutation", "internal", {
|
|
88
116
|
dailySpendLimitNanos?: number;
|
|
89
117
|
enforcement?: "hard" | "soft";
|
|
90
118
|
lifetimeSpendLimitNanos?: number;
|
|
91
119
|
}, null, Name>;
|
|
92
|
-
setLimits: FunctionReference<"mutation", "internal", {
|
|
93
|
-
blocked?: boolean;
|
|
94
|
-
dailySpendLimitNanos?: number;
|
|
95
|
-
dailyTokenLimit?: number;
|
|
96
|
-
enforcement?: "hard" | "soft";
|
|
97
|
-
lifetimeSpendLimitNanos?: number;
|
|
98
|
-
lifetimeTokenLimit?: number;
|
|
99
|
-
requestsPerMinute?: number;
|
|
100
|
-
userId: string;
|
|
101
|
-
}, null, Name>;
|
|
102
120
|
setModelPolicy: FunctionReference<"mutation", "internal", {
|
|
103
121
|
mode: "open" | "allowlist" | "denylist";
|
|
104
122
|
models: Array<string>;
|
|
105
123
|
}, null, Name>;
|
|
106
124
|
setPrice: FunctionReference<"mutation", "internal", {
|
|
125
|
+
cachedNanosPerMTok?: number;
|
|
107
126
|
inputNanosPerMTok: number;
|
|
108
127
|
model: string;
|
|
109
128
|
outputNanosPerMTok: number;
|
|
@@ -119,9 +138,14 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
119
138
|
}>;
|
|
120
139
|
model: string;
|
|
121
140
|
rerunOf?: string;
|
|
141
|
+
tags?: Array<{
|
|
142
|
+
dimension: string;
|
|
143
|
+
value: string;
|
|
144
|
+
}>;
|
|
122
145
|
userId: string;
|
|
123
146
|
}, {
|
|
124
147
|
allowed: true;
|
|
148
|
+
notices: Array<string>;
|
|
125
149
|
requestId: string;
|
|
126
150
|
warnings: Array<string>;
|
|
127
151
|
} | {
|
|
@@ -129,5 +153,11 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
129
153
|
code: string;
|
|
130
154
|
reason: string;
|
|
131
155
|
}, Name>;
|
|
156
|
+
usageHistory: FunctionReference<"query", "internal", {
|
|
157
|
+
dimension: string;
|
|
158
|
+
limit?: number;
|
|
159
|
+
period: "day" | "month";
|
|
160
|
+
value: string;
|
|
161
|
+
}, any, Name>;
|
|
132
162
|
};
|
|
133
163
|
};
|