@convex-dev/ai-budget 0.0.2-alpha.17 → 0.0.2-alpha.19
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 +73 -13
- package/dist/client/index.d.ts +38 -5
- package/dist/client/index.js +56 -8
- package/dist/component/_generated/component.d.ts +8 -4
- package/dist/component/lib.d.ts +19 -3
- package/dist/component/lib.js +233 -80
- package/dist/component/schema.d.ts +20 -4
- package/dist/component/schema.js +28 -2
- package/package.json +1 -1
- package/src/client/index.ts +65 -15
- package/src/component/_generated/component.ts +11 -4
- package/src/component/lib.test.ts +109 -9
- package/src/component/lib.ts +249 -88
- package/src/component/schema.ts +28 -2
package/README.md
CHANGED
|
@@ -251,7 +251,10 @@ await ai.meter(ctx,
|
|
|
251
251
|
});
|
|
252
252
|
```
|
|
253
253
|
|
|
254
|
-
### `ai.decisions` — structured decisions (Jev)
|
|
254
|
+
### `ai.decisions` — structured decisions (Jev) · **experimental**
|
|
255
|
+
|
|
256
|
+
> **Experimental** — the Decisions endpoint is alpha on the gateway; this API may
|
|
257
|
+
> change without a major version bump.
|
|
255
258
|
|
|
256
259
|
Budget the gateway's Decisions endpoint ([Jev](https://docs.typesafe.ai)) — typed
|
|
257
260
|
`choice` / `score` / `boolean` questions evaluated against a `state` — with the
|
|
@@ -280,7 +283,10 @@ defaults to `defaultEvalModel` (`"typesafe/jev-1.13"`). Requires
|
|
|
280
283
|
`experimental_evaluate` — both imported lazily, so callers who don't use
|
|
281
284
|
`decisions` are unaffected.
|
|
282
285
|
|
|
283
|
-
### `ai.begin` / `ai.settle` — long async jobs (video)
|
|
286
|
+
### `ai.begin` / `ai.settle` — long async jobs (video) · **experimental**
|
|
287
|
+
|
|
288
|
+
> **Experimental** — video generation depends on alpha gateway features; the
|
|
289
|
+
> async surface (`reserveTtlMs`, `registerWebhook`) may change without a major bump.
|
|
284
290
|
|
|
285
291
|
A video job is submit → wait minutes → poll/webhook → done, spanning multiple
|
|
286
292
|
Convex functions, so the synchronous `meter` bracket doesn't fit. Reserve with
|
|
@@ -432,8 +438,11 @@ await ai.users.adjust(ctx, { userId, deltaNanos: -5 * 1_000_000_000, reason: "go
|
|
|
432
438
|
await ai.users.adjustments(ctx, { userId }); // the audit log
|
|
433
439
|
```
|
|
434
440
|
|
|
435
|
-
|
|
436
|
-
|
|
441
|
+
Positive = an extra charge (adds to **gross** spend). Negative = a credit/refund,
|
|
442
|
+
which accrues in a **separate** credits balance and never reduces gross spend — so
|
|
443
|
+
gross spend and durable history stay consistent. Caps enforce on **net = gross −
|
|
444
|
+
credits**, so a credit gives the bucket real headroom. Both are written to the
|
|
445
|
+
audit log.
|
|
437
446
|
|
|
438
447
|
### Alerts
|
|
439
448
|
|
|
@@ -490,9 +499,12 @@ ai.requests.get(ctx, { requestId }) // one request
|
|
|
490
499
|
### Global cap
|
|
491
500
|
|
|
492
501
|
```ts
|
|
502
|
+
// enforcement: "approximate" (default, best-effort block) | "soft" (warn only).
|
|
503
|
+
// Pass null to clear a field; only the fields you pass change.
|
|
493
504
|
ai.global.setLimits(ctx, { dailySpendLimitNanos?, lifetimeSpendLimitNanos?, enforcement? })
|
|
494
|
-
ai.global.status(ctx) // {
|
|
505
|
+
ai.global.status(ctx) // { dailySpendLimitNanos, lifetimeSpendLimitNanos, enforcement, spentTodayNanos, spentTotalNanos, … }
|
|
495
506
|
ai.global.bump(ctx, { dailyNanos?, lifetimeNanos? })
|
|
507
|
+
ai.global.setPolicy(ctx, { allowUnpricedModels?, storeContent? }) // deployment data/pricing policy
|
|
496
508
|
```
|
|
497
509
|
|
|
498
510
|
A best-effort killswitch across everything. Backed by a sharded counter for
|
|
@@ -584,19 +596,29 @@ import { AIBudget } from "@convex-dev/ai-budget";
|
|
|
584
596
|
const ai = new AIBudget(components.aiBudget);
|
|
585
597
|
const http = httpRouter();
|
|
586
598
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
});
|
|
599
|
+
// Recommended: token mode. Set AI_BUDGET_DASHBOARD_TOKEN in the deployment env;
|
|
600
|
+
// open the dashboard with ?token=<it> once and the page keeps using it.
|
|
601
|
+
ai.registerRoutes(http);
|
|
591
602
|
|
|
592
603
|
export default http;
|
|
593
604
|
```
|
|
594
605
|
|
|
595
606
|
It lives at `https://<deployment>.convex.site/aibudget` (override with `path`).
|
|
596
|
-
**It is a public internet endpoint, so you must gate it
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
607
|
+
**It is a public internet endpoint, so you must gate it**, one of two ways:
|
|
608
|
+
|
|
609
|
+
- **Token (recommended, works end-to-end):** set the `AI_BUDGET_DASHBOARD_TOKEN`
|
|
610
|
+
env var. Open `…/aibudget?token=<token>` once; the page strips it from the URL
|
|
611
|
+
and sends it as a bearer on every API call. `?token=` is accepted only on the
|
|
612
|
+
page navigation, and the compare is constant-time.
|
|
613
|
+
- **`authorize(ctx, request)` callback:** must authenticate from something the
|
|
614
|
+
**browser sends on a top-level navigation** — a cookie or a header *you*
|
|
615
|
+
control — because a page load carries no `Authorization` bearer. `ctx.auth`
|
|
616
|
+
(deployment JWT) is `null` for the HTML page, so `authorize: (ctx) => (await
|
|
617
|
+
ctx.auth.getUserIdentity())?.role === "admin"` will 401 the page. Use it only
|
|
618
|
+
when you have your own session cookie to check.
|
|
619
|
+
|
|
620
|
+
With neither configured, every route returns 401. Everything the page shows is
|
|
621
|
+
backed by the component's own functions — nothing else to wire up.
|
|
600
622
|
|
|
601
623
|
---
|
|
602
624
|
|
|
@@ -641,6 +663,19 @@ more tokens or costs more than estimated, settlement records the real amount and
|
|
|
641
663
|
the final total can exceed a hard cap by that request's estimation delta. The next
|
|
642
664
|
admission sees the settled total and blocks until there is headroom again.
|
|
643
665
|
|
|
666
|
+
**Throughput characteristics (know these before you turn on a global cap).**
|
|
667
|
+
Reconciliation runs as small, independently-rescheduling phases, so folding,
|
|
668
|
+
expiry, and retention can't stall each other and each drains its own backlog.
|
|
669
|
+
Two shared-write hot spots remain, by design:
|
|
670
|
+
- The **global cap** reads a sharded counter *inside* admission, which contends
|
|
671
|
+
with every fold that writes it — so a configured global cap adds contention on
|
|
672
|
+
the hot path. It's a deployment-wide killswitch, not a high-throughput per-call
|
|
673
|
+
limit; prefer per-bucket caps for the common case.
|
|
674
|
+
- **Settlement writes every attributed bucket**, including the per-request
|
|
675
|
+
`action` bucket that every call shares. That single row is written by every
|
|
676
|
+
settle, so an extremely high single-action settle rate can lag totals. Spread
|
|
677
|
+
load across naturally-sharded dimensions (per user/customer) where you can.
|
|
678
|
+
|
|
644
679
|
The `error.md` file documents the adversarial audits this design survived, with
|
|
645
680
|
live repros.
|
|
646
681
|
|
|
@@ -732,6 +767,31 @@ metadata use their creation period and current capped buckets as a compatibility
|
|
|
732
767
|
fallback. Exact historical hold ownership cannot be reconstructed if those caps
|
|
733
768
|
changed before the upgrade. New requests always store explicit ownership.
|
|
734
769
|
|
|
770
|
+
## Stability (v1)
|
|
771
|
+
|
|
772
|
+
**Stable (frozen for 1.0)** — these keep backward compatibility within the 1.x line:
|
|
773
|
+
`ai.chat`, `ai.meter`, `ai.begin`/`ai.settle`, `ai.languageModel`, and the admin
|
|
774
|
+
namespaces `ai.users` / `ai.actions` / `ai.tag` / `ai.global` / `ai.models` /
|
|
775
|
+
`ai.prices` / `ai.requests`, plus `ai.registerRoutes`. The stored schema is frozen;
|
|
776
|
+
new fields will only be added as optional.
|
|
777
|
+
|
|
778
|
+
**Experimental (may change without a major bump):** `ai.decisions` (the Jev /
|
|
779
|
+
Decisions endpoint) and video generation (`begin`/`settle` with `reserveTtlMs` +
|
|
780
|
+
`registerWebhook`). Both depend on gateway features that are still alpha.
|
|
781
|
+
|
|
782
|
+
**Semantics worth knowing:**
|
|
783
|
+
- **Spend caps** admit on an *estimate*, so a token-priced cap can be exceeded by
|
|
784
|
+
one request's estimate-vs-actual delta; pass `estimatedCostNanos` for an exact
|
|
785
|
+
reservation. The **global cap** is a best-effort killswitch (`enforcement:
|
|
786
|
+
"approximate"` | `"soft"`), not a to-the-dollar ceiling.
|
|
787
|
+
- **Credits** (`ai.tag(d).adjust` / negative `deltaNanos`) accrue in a separate
|
|
788
|
+
balance and never reduce gross spend; caps enforce on **net = gross − credits**,
|
|
789
|
+
so a credit grants real headroom while spend history stays consistent.
|
|
790
|
+
- **Deployment policy** (`ai.global.setPolicy`): `allowUnpricedModels: false`
|
|
791
|
+
rejects models with no configured price under hard enforcement (default charges
|
|
792
|
+
the conservative fallback); `storeContent: false` persists metadata but no
|
|
793
|
+
prompt/response content (for teams that want zero prompt retention).
|
|
794
|
+
|
|
735
795
|
## Development
|
|
736
796
|
|
|
737
797
|
```sh
|
package/dist/client/index.d.ts
CHANGED
|
@@ -495,6 +495,9 @@ export declare class AIBudget {
|
|
|
495
495
|
lifetimeBumpNanos?: number | undefined;
|
|
496
496
|
bumpDayStamp?: string | undefined;
|
|
497
497
|
bumpMonthStamp?: string | undefined;
|
|
498
|
+
creditsNanos?: number | undefined;
|
|
499
|
+
creditsTodayNanos?: number | undefined;
|
|
500
|
+
creditsThisMonthNanos?: number | undefined;
|
|
498
501
|
tokensToday?: number | undefined;
|
|
499
502
|
monthStamp?: string | undefined;
|
|
500
503
|
tokensThisMonth?: number | undefined;
|
|
@@ -536,6 +539,9 @@ export declare class AIBudget {
|
|
|
536
539
|
lifetimeBumpNanos?: number | undefined;
|
|
537
540
|
bumpDayStamp?: string | undefined;
|
|
538
541
|
bumpMonthStamp?: string | undefined;
|
|
542
|
+
creditsNanos?: number | undefined;
|
|
543
|
+
creditsTodayNanos?: number | undefined;
|
|
544
|
+
creditsThisMonthNanos?: number | undefined;
|
|
539
545
|
tokensToday?: number | undefined;
|
|
540
546
|
monthStamp?: string | undefined;
|
|
541
547
|
tokensThisMonth?: number | undefined;
|
|
@@ -632,6 +638,9 @@ export declare class AIBudget {
|
|
|
632
638
|
lifetimeBumpNanos?: number | undefined;
|
|
633
639
|
bumpDayStamp?: string | undefined;
|
|
634
640
|
bumpMonthStamp?: string | undefined;
|
|
641
|
+
creditsNanos?: number | undefined;
|
|
642
|
+
creditsTodayNanos?: number | undefined;
|
|
643
|
+
creditsThisMonthNanos?: number | undefined;
|
|
635
644
|
tokensToday?: number | undefined;
|
|
636
645
|
monthStamp?: string | undefined;
|
|
637
646
|
tokensThisMonth?: number | undefined;
|
|
@@ -673,6 +682,9 @@ export declare class AIBudget {
|
|
|
673
682
|
lifetimeBumpNanos?: number | undefined;
|
|
674
683
|
bumpDayStamp?: string | undefined;
|
|
675
684
|
bumpMonthStamp?: string | undefined;
|
|
685
|
+
creditsNanos?: number | undefined;
|
|
686
|
+
creditsTodayNanos?: number | undefined;
|
|
687
|
+
creditsThisMonthNanos?: number | undefined;
|
|
676
688
|
tokensToday?: number | undefined;
|
|
677
689
|
monthStamp?: string | undefined;
|
|
678
690
|
tokensThisMonth?: number | undefined;
|
|
@@ -768,6 +780,9 @@ export declare class AIBudget {
|
|
|
768
780
|
lifetimeBumpNanos?: number | undefined;
|
|
769
781
|
bumpDayStamp?: string | undefined;
|
|
770
782
|
bumpMonthStamp?: string | undefined;
|
|
783
|
+
creditsNanos?: number | undefined;
|
|
784
|
+
creditsTodayNanos?: number | undefined;
|
|
785
|
+
creditsThisMonthNanos?: number | undefined;
|
|
771
786
|
tokensToday?: number | undefined;
|
|
772
787
|
monthStamp?: string | undefined;
|
|
773
788
|
tokensThisMonth?: number | undefined;
|
|
@@ -809,6 +824,9 @@ export declare class AIBudget {
|
|
|
809
824
|
lifetimeBumpNanos?: number | undefined;
|
|
810
825
|
bumpDayStamp?: string | undefined;
|
|
811
826
|
bumpMonthStamp?: string | undefined;
|
|
827
|
+
creditsNanos?: number | undefined;
|
|
828
|
+
creditsTodayNanos?: number | undefined;
|
|
829
|
+
creditsThisMonthNanos?: number | undefined;
|
|
812
830
|
tokensToday?: number | undefined;
|
|
813
831
|
monthStamp?: string | undefined;
|
|
814
832
|
tokensThisMonth?: number | undefined;
|
|
@@ -886,17 +904,21 @@ export declare class AIBudget {
|
|
|
886
904
|
status: (ctx: RunQueryCtx) => Promise<{
|
|
887
905
|
dailySpendLimitNanos: number | null;
|
|
888
906
|
lifetimeSpendLimitNanos: number | null;
|
|
889
|
-
enforcement: "
|
|
907
|
+
enforcement: "soft" | "approximate";
|
|
890
908
|
spentTodayNanos: number;
|
|
891
909
|
spentTotalNanos: number;
|
|
892
910
|
retentionMs: number | null;
|
|
893
911
|
defaultWarnAtPct: number | null;
|
|
894
912
|
}>;
|
|
895
|
-
/**
|
|
913
|
+
/**
|
|
914
|
+
* A killswitch spend cap across all users/actions. Enforced
|
|
915
|
+
* **approximately** (bounded overshoot, no per-request reservation) — for
|
|
916
|
+
* an exact ceiling use a per-bucket cap. Pass `null` to clear a field.
|
|
917
|
+
*/
|
|
896
918
|
setLimits: (ctx: RunMutationCtx, args: {
|
|
897
|
-
dailySpendLimitNanos?: number;
|
|
898
|
-
lifetimeSpendLimitNanos?: number;
|
|
899
|
-
enforcement?: "
|
|
919
|
+
dailySpendLimitNanos?: number | null;
|
|
920
|
+
lifetimeSpendLimitNanos?: number | null;
|
|
921
|
+
enforcement?: "approximate" | "soft" | null;
|
|
900
922
|
}) => Promise<null>;
|
|
901
923
|
bump: (ctx: RunMutationCtx, args: {
|
|
902
924
|
dailyNanos?: number;
|
|
@@ -910,6 +932,17 @@ export declare class AIBudget {
|
|
|
910
932
|
setRetention: (ctx: RunMutationCtx, args: {
|
|
911
933
|
retentionMs: number;
|
|
912
934
|
}) => Promise<null>;
|
|
935
|
+
/**
|
|
936
|
+
* Deployment-wide data/pricing policy (only the fields you pass change):
|
|
937
|
+
* - `allowUnpricedModels: false` rejects models with no configured price
|
|
938
|
+
* under hard enforcement (default true — charge the conservative fallback).
|
|
939
|
+
* - `storeContent: false` stops persisting prompt/response content on
|
|
940
|
+
* request rows (default true).
|
|
941
|
+
*/
|
|
942
|
+
setPolicy: (ctx: RunMutationCtx, args: {
|
|
943
|
+
allowUnpricedModels?: boolean;
|
|
944
|
+
storeContent?: boolean;
|
|
945
|
+
}) => Promise<null>;
|
|
913
946
|
};
|
|
914
947
|
/** Model allow/deny policy. */
|
|
915
948
|
get models(): {
|
package/dist/client/index.js
CHANGED
|
@@ -39,10 +39,17 @@ function toTokenCount(x) {
|
|
|
39
39
|
}
|
|
40
40
|
function extractUsage(usage) {
|
|
41
41
|
return {
|
|
42
|
-
// Cover AI SDK camelCase (v5/v7)
|
|
43
|
-
// `
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
// Cover AI SDK camelCase (v5/v7), raw OpenAI-compatible snake_case, AND raw
|
|
43
|
+
// Anthropic (`input_tokens`/`output_tokens`), so a `meter` caller passing any
|
|
44
|
+
// provider's raw `usage` object still gets counts.
|
|
45
|
+
promptTokens: toTokenCount(usage?.inputTokens ??
|
|
46
|
+
usage?.promptTokens ??
|
|
47
|
+
usage?.prompt_tokens ??
|
|
48
|
+
usage?.input_tokens),
|
|
49
|
+
completionTokens: toTokenCount(usage?.outputTokens ??
|
|
50
|
+
usage?.completionTokens ??
|
|
51
|
+
usage?.completion_tokens ??
|
|
52
|
+
usage?.output_tokens),
|
|
46
53
|
// cached prompt tokens. The Convex gateway reports these at
|
|
47
54
|
// `usage.inputTokenDetails.cacheReadTokens`; the other paths cover AI SDK v5
|
|
48
55
|
// (`cachedInputTokens`) and raw OpenAI-compatible shapes.
|
|
@@ -50,7 +57,8 @@ function extractUsage(usage) {
|
|
|
50
57
|
usage?.cachedInputTokens ??
|
|
51
58
|
usage?.promptTokensDetails?.cachedTokens ??
|
|
52
59
|
usage?.prompt_tokens_details?.cached_tokens ??
|
|
53
|
-
usage?.cached_tokens
|
|
60
|
+
usage?.cached_tokens ??
|
|
61
|
+
usage?.cache_read_input_tokens),
|
|
54
62
|
};
|
|
55
63
|
}
|
|
56
64
|
// The AI Gateway reports the authoritative dollar cost of each request.
|
|
@@ -72,6 +80,10 @@ function extractGatewayCostNanos(result) {
|
|
|
72
80
|
return undefined;
|
|
73
81
|
}
|
|
74
82
|
const NANOS_PER_DOLLAR = 1e9;
|
|
83
|
+
// Cap stored prompt/response content so one row can't approach Convex's 1 MiB
|
|
84
|
+
// document limit (which would fail the call) or bloat the reconciler's scans.
|
|
85
|
+
const MAX_STORED_CONTENT = 32 * 1024;
|
|
86
|
+
const capContent = (s) => s.length > MAX_STORED_CONTENT ? s.slice(0, MAX_STORED_CONTENT) + "…[truncated]" : s;
|
|
75
87
|
// Flatten an AI SDK prompt (roles + content parts) into simple storable messages.
|
|
76
88
|
function simplifyPrompt(prompt) {
|
|
77
89
|
if (!Array.isArray(prompt))
|
|
@@ -83,13 +95,22 @@ function simplifyPrompt(prompt) {
|
|
|
83
95
|
}
|
|
84
96
|
else if (Array.isArray(m.content)) {
|
|
85
97
|
content = m.content
|
|
86
|
-
.map((part) =>
|
|
98
|
+
.map((part) => {
|
|
99
|
+
if (part?.type === "text")
|
|
100
|
+
return part.text ?? "";
|
|
101
|
+
// NEVER inline a base64 image/file part — a data URL or Uint8Array here
|
|
102
|
+
// becomes megabytes, pushing the stored row past the 1 MiB doc limit
|
|
103
|
+
// (failing the call) and turning a 200 KB image into a ~50k-token
|
|
104
|
+
// estimate. Store a compact placeholder.
|
|
105
|
+
const bytes = part?.data?.length ?? part?.image?.length ?? part?.data?.byteLength;
|
|
106
|
+
return `[${part?.type ?? "part"}${typeof bytes === "number" ? ` ${bytes}b` : ""}]`;
|
|
107
|
+
})
|
|
87
108
|
.join("");
|
|
88
109
|
}
|
|
89
110
|
else {
|
|
90
111
|
content = JSON.stringify(m.content);
|
|
91
112
|
}
|
|
92
|
-
return { role: String(m.role), content };
|
|
113
|
+
return { role: String(m.role), content: capContent(content) };
|
|
93
114
|
});
|
|
94
115
|
}
|
|
95
116
|
function extractText(result) {
|
|
@@ -491,6 +512,21 @@ export class AIBudget {
|
|
|
491
512
|
async flush() {
|
|
492
513
|
await settle();
|
|
493
514
|
},
|
|
515
|
+
// A cancelled/aborted stream (client disconnect, AbortSignal, or
|
|
516
|
+
// breaking out of `for await`) does NOT run `flush`. Without this
|
|
517
|
+
// the request would sit pending until the 30-min reservation
|
|
518
|
+
// expiry and be recorded as $0 though the provider charged for
|
|
519
|
+
// what streamed. Settle here with the text so far, estimating the
|
|
520
|
+
// completion tokens when the provider gave no usage.
|
|
521
|
+
// `cancel` is a newer Streams-spec transformer hook not yet in
|
|
522
|
+
// the TS DOM lib types; cast the literal so it compiles (runtimes
|
|
523
|
+
// without it simply won't call it, and the reconciler backstops).
|
|
524
|
+
async cancel(reason) {
|
|
525
|
+
if (usage === undefined && text) {
|
|
526
|
+
usage = { outputTokens: Math.ceil(text.length / 4) };
|
|
527
|
+
}
|
|
528
|
+
await settle(`cancelled: ${String(reason)}`);
|
|
529
|
+
},
|
|
494
530
|
}));
|
|
495
531
|
return { ...result, stream: tapped };
|
|
496
532
|
}
|
|
@@ -612,13 +648,25 @@ export class AIBudget {
|
|
|
612
648
|
return {
|
|
613
649
|
/** Limits + spend today/total. */
|
|
614
650
|
status: (ctx) => ctx.runQuery(c.lib.getGlobalStatus, {}),
|
|
615
|
-
/**
|
|
651
|
+
/**
|
|
652
|
+
* A killswitch spend cap across all users/actions. Enforced
|
|
653
|
+
* **approximately** (bounded overshoot, no per-request reservation) — for
|
|
654
|
+
* an exact ceiling use a per-bucket cap. Pass `null` to clear a field.
|
|
655
|
+
*/
|
|
616
656
|
setLimits: (ctx, args) => ctx.runMutation(c.lib.setGlobalLimits, args),
|
|
617
657
|
bump: (ctx, args) => ctx.runMutation(c.lib.bumpGlobal, args),
|
|
618
658
|
/** Default approaching-limit alert threshold (fraction of a cap, e.g. 0.8). */
|
|
619
659
|
setAlertDefaults: (ctx, args) => ctx.runMutation(c.lib.setAlertDefaults, args),
|
|
620
660
|
/** Request-row retention window in ms (default 1h; 0 disables). */
|
|
621
661
|
setRetention: (ctx, args) => ctx.runMutation(c.lib.setRetention, args),
|
|
662
|
+
/**
|
|
663
|
+
* Deployment-wide data/pricing policy (only the fields you pass change):
|
|
664
|
+
* - `allowUnpricedModels: false` rejects models with no configured price
|
|
665
|
+
* under hard enforcement (default true — charge the conservative fallback).
|
|
666
|
+
* - `storeContent: false` stops persisting prompt/response content on
|
|
667
|
+
* request rows (default true).
|
|
668
|
+
*/
|
|
669
|
+
setPolicy: (ctx, args) => ctx.runMutation(c.lib.setDeploymentPolicy, args),
|
|
622
670
|
};
|
|
623
671
|
}
|
|
624
672
|
/** Model allow/deny policy. */
|
|
@@ -66,7 +66,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
66
66
|
getGlobalStatus: FunctionReference<"query", "internal", {}, {
|
|
67
67
|
dailySpendLimitNanos: number | null;
|
|
68
68
|
defaultWarnAtPct: number | null;
|
|
69
|
-
enforcement: "
|
|
69
|
+
enforcement: "approximate" | "soft";
|
|
70
70
|
lifetimeSpendLimitNanos: number | null;
|
|
71
71
|
retentionMs: number | null;
|
|
72
72
|
spentTodayNanos: number;
|
|
@@ -116,10 +116,14 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
116
116
|
value: string;
|
|
117
117
|
warnAtPct?: number;
|
|
118
118
|
}, null, Name>;
|
|
119
|
+
setDeploymentPolicy: FunctionReference<"mutation", "internal", {
|
|
120
|
+
allowUnpricedModels?: boolean;
|
|
121
|
+
storeContent?: boolean;
|
|
122
|
+
}, null, Name>;
|
|
119
123
|
setGlobalLimits: FunctionReference<"mutation", "internal", {
|
|
120
|
-
dailySpendLimitNanos?: number;
|
|
121
|
-
enforcement?: "
|
|
122
|
-
lifetimeSpendLimitNanos?: number;
|
|
124
|
+
dailySpendLimitNanos?: number | null;
|
|
125
|
+
enforcement?: "approximate" | "soft" | null;
|
|
126
|
+
lifetimeSpendLimitNanos?: number | null;
|
|
123
127
|
}, null, Name>;
|
|
124
128
|
setModelPolicy: FunctionReference<"mutation", "internal", {
|
|
125
129
|
mode: "open" | "allowlist" | "denylist";
|
package/dist/component/lib.d.ts
CHANGED
|
@@ -39,9 +39,15 @@ export declare const finishRequest: import("convex/server").RegisteredMutation<"
|
|
|
39
39
|
export declare const foldTotals: import("convex/server").RegisteredMutation<"internal", {
|
|
40
40
|
requestId: import("convex/values").GenericId<"requests">;
|
|
41
41
|
}, Promise<null>>;
|
|
42
|
-
export declare const reconcile: import("convex/server").RegisteredMutation<"internal", {}, Promise<
|
|
42
|
+
export declare const reconcile: import("convex/server").RegisteredMutation<"internal", {}, Promise<null>>;
|
|
43
|
+
export declare const globalPhase: import("convex/server").RegisteredMutation<"internal", {}, Promise<null>>;
|
|
44
|
+
export declare const foldPhase: import("convex/server").RegisteredMutation<"internal", {}, Promise<{
|
|
43
45
|
folded: number;
|
|
46
|
+
}>>;
|
|
47
|
+
export declare const expirePhase: import("convex/server").RegisteredMutation<"internal", {}, Promise<{
|
|
44
48
|
expired: number;
|
|
49
|
+
}>>;
|
|
50
|
+
export declare const retentionPhase: import("convex/server").RegisteredMutation<"internal", {}, Promise<{
|
|
45
51
|
purged: number;
|
|
46
52
|
}>>;
|
|
47
53
|
export declare const setRetention: import("convex/server").RegisteredMutation<"public", {
|
|
@@ -237,6 +243,9 @@ export declare const listBuckets: import("convex/server").RegisteredQuery<"publi
|
|
|
237
243
|
lifetimeBumpNanos?: number | undefined;
|
|
238
244
|
bumpDayStamp?: string | undefined;
|
|
239
245
|
bumpMonthStamp?: string | undefined;
|
|
246
|
+
creditsNanos?: number | undefined;
|
|
247
|
+
creditsTodayNanos?: number | undefined;
|
|
248
|
+
creditsThisMonthNanos?: number | undefined;
|
|
240
249
|
tokensToday?: number | undefined;
|
|
241
250
|
monthStamp?: string | undefined;
|
|
242
251
|
tokensThisMonth?: number | undefined;
|
|
@@ -278,6 +287,9 @@ export declare const getBucket: import("convex/server").RegisteredQuery<"public"
|
|
|
278
287
|
lifetimeBumpNanos?: number | undefined;
|
|
279
288
|
bumpDayStamp?: string | undefined;
|
|
280
289
|
bumpMonthStamp?: string | undefined;
|
|
290
|
+
creditsNanos?: number | undefined;
|
|
291
|
+
creditsTodayNanos?: number | undefined;
|
|
292
|
+
creditsThisMonthNanos?: number | undefined;
|
|
281
293
|
tokensToday?: number | undefined;
|
|
282
294
|
monthStamp?: string | undefined;
|
|
283
295
|
tokensThisMonth?: number | undefined;
|
|
@@ -356,6 +368,10 @@ export declare const usageHistory: import("convex/server").RegisteredQuery<"publ
|
|
|
356
368
|
export declare const setAlertDefaults: import("convex/server").RegisteredMutation<"public", {
|
|
357
369
|
warnAtPct?: number | undefined;
|
|
358
370
|
}, Promise<null>>;
|
|
371
|
+
export declare const setDeploymentPolicy: import("convex/server").RegisteredMutation<"public", {
|
|
372
|
+
allowUnpricedModels?: boolean | undefined;
|
|
373
|
+
storeContent?: boolean | undefined;
|
|
374
|
+
}, Promise<null>>;
|
|
359
375
|
export declare const deleteBucket: import("convex/server").RegisteredMutation<"public", {
|
|
360
376
|
dimension: string;
|
|
361
377
|
value: string;
|
|
@@ -370,7 +386,7 @@ export declare const getModelPolicy: import("convex/server").RegisteredQuery<"pu
|
|
|
370
386
|
export declare const getGlobalStatus: import("convex/server").RegisteredQuery<"public", {}, Promise<{
|
|
371
387
|
dailySpendLimitNanos: number | null;
|
|
372
388
|
lifetimeSpendLimitNanos: number | null;
|
|
373
|
-
enforcement: "
|
|
389
|
+
enforcement: "soft" | "approximate";
|
|
374
390
|
spentTodayNanos: number;
|
|
375
391
|
spentTotalNanos: number;
|
|
376
392
|
retentionMs: number | null;
|
|
@@ -379,7 +395,7 @@ export declare const getGlobalStatus: import("convex/server").RegisteredQuery<"p
|
|
|
379
395
|
export declare const setGlobalLimits: import("convex/server").RegisteredMutation<"public", {
|
|
380
396
|
dailySpendLimitNanos?: number | null | undefined;
|
|
381
397
|
lifetimeSpendLimitNanos?: number | null | undefined;
|
|
382
|
-
enforcement?: "
|
|
398
|
+
enforcement?: "soft" | "approximate" | null | undefined;
|
|
383
399
|
}, Promise<null>>;
|
|
384
400
|
export declare const bumpGlobal: import("convex/server").RegisteredMutation<"public", {
|
|
385
401
|
dailyNanos?: number | undefined;
|