floe-guard 0.3.0 → 0.5.0
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 +19 -3
- package/dist/index.cjs +206 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +166 -15
- package/dist/index.d.ts +166 -15
- package/dist/index.js +204 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
[](../LICENSE)
|
|
5
5
|
|
|
6
6
|
**A local budget guardrail for AI agents** — the TypeScript counterpart to the
|
|
7
|
-
[Python `floe-guard`](../README.md). It hard-stops your agent *before its next
|
|
8
|
-
call* when it would cross a USD spend ceiling
|
|
9
|
-
|
|
7
|
+
[Python `floe-guard`](../README.md). It hard-stops your agent *before its next
|
|
8
|
+
LLM or paid tool call* when it would cross a USD spend ceiling — tokens and
|
|
9
|
+
tool calls under one local ceiling. No account, no signup, no network. Runs in
|
|
10
|
+
your process.
|
|
10
11
|
|
|
11
12
|
Works with both **AI SDK v4 and v5** (`ai@4` / `ai@5`).
|
|
12
13
|
|
|
@@ -63,6 +64,21 @@ const adv = guard.advisory();
|
|
|
63
64
|
const model = adv.nearLimit ? openai("gpt-4o-mini") : openai("gpt-4o");
|
|
64
65
|
```
|
|
65
66
|
|
|
67
|
+
## Tool spend under the same ceiling
|
|
68
|
+
|
|
69
|
+
Paid tool calls (Apollo, Exa, scrapers) burn the same budget as tokens. The
|
|
70
|
+
full reserve/settle contract applies — and the price is known *before* the
|
|
71
|
+
call, so the pre-call hard-stop is exact:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
const handle = guard.reserveTool(0.02); // throws BudgetExceeded BEFORE the call
|
|
75
|
+
const result = await apollo.peopleLookup(...);
|
|
76
|
+
guard.settleTool("apollo.people_lookup", 0.02, { reserved: handle });
|
|
77
|
+
|
|
78
|
+
guard.recordTool("exa.search", 0.004); // post-hoc, for metered APIs
|
|
79
|
+
guard.toolCosts; // { "apollo.people_lookup": 0.42, "exa.search": 0.11 }
|
|
80
|
+
```
|
|
81
|
+
|
|
66
82
|
## Per-call spend log
|
|
67
83
|
|
|
68
84
|
The guard keeps a typed, in-memory ledger of everything it priced: each
|
package/dist/index.cjs
CHANGED
|
@@ -22,7 +22,9 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
BudgetExceeded: () => BudgetExceeded,
|
|
24
24
|
BudgetGuard: () => BudgetGuard,
|
|
25
|
+
DeadlineExceeded: () => DeadlineExceeded,
|
|
25
26
|
FloeGuardError: () => FloeGuardError,
|
|
27
|
+
LatencyBudget: () => LatencyBudget,
|
|
26
28
|
UnpriceableModelError: () => UnpriceableModelError,
|
|
27
29
|
budgetGuardMiddleware: () => budgetGuardMiddleware,
|
|
28
30
|
priceTokens: () => priceTokens,
|
|
@@ -61,6 +63,21 @@ var UnpriceableModelError = class extends FloeGuardError {
|
|
|
61
63
|
this.model = model;
|
|
62
64
|
}
|
|
63
65
|
};
|
|
66
|
+
function roundHalfUp(ms) {
|
|
67
|
+
return Math.floor(ms + 0.5);
|
|
68
|
+
}
|
|
69
|
+
var DeadlineExceeded = class extends FloeGuardError {
|
|
70
|
+
elapsedMs;
|
|
71
|
+
slaMs;
|
|
72
|
+
constructor(elapsedMs, slaMs) {
|
|
73
|
+
super(
|
|
74
|
+
`DEADLINE EXCEEDED \u2014 call blocked (elapsed ${roundHalfUp(elapsedMs)}ms of ${roundHalfUp(slaMs)}ms SLA)`
|
|
75
|
+
);
|
|
76
|
+
this.name = "DeadlineExceeded";
|
|
77
|
+
this.elapsedMs = elapsedMs;
|
|
78
|
+
this.slaMs = slaMs;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
64
81
|
|
|
65
82
|
// src/pricing.ts
|
|
66
83
|
var pricing_exports = {};
|
|
@@ -975,13 +992,26 @@ var BudgetGuard = class {
|
|
|
975
992
|
failClosed;
|
|
976
993
|
nearLimitBps;
|
|
977
994
|
onBlock;
|
|
978
|
-
/**
|
|
979
|
-
|
|
995
|
+
/**
|
|
996
|
+
* Costs of the most recent priced LLM call and tool call, tracked
|
|
997
|
+
* SEPARATELY: the default next-call prediction is the max of the two, so a
|
|
998
|
+
* cheap tool call can't shrink the estimate right before an expensive LLM
|
|
999
|
+
* call (or vice versa) — conservative beats one-call-too-late.
|
|
1000
|
+
*/
|
|
1001
|
+
lastLlmCost = 0;
|
|
1002
|
+
lastToolCost = 0;
|
|
980
1003
|
/** USD held for in-flight calls (reserved, not yet settled). Counts toward the ceiling. */
|
|
981
1004
|
reserved = 0;
|
|
982
1005
|
/** Per-call ledger, oldest first; a ring buffer when maxLogEvents is set. */
|
|
983
1006
|
spendEvents = [];
|
|
984
1007
|
maxLogEvents;
|
|
1008
|
+
/**
|
|
1009
|
+
* Per-tool running totals (settleTool/recordTool) — the tool side of the one
|
|
1010
|
+
* shared ceiling, exposed via the toolCosts getter. null-prototype: tool
|
|
1011
|
+
* names are caller-supplied strings, so a "__proto__" name is stored as
|
|
1012
|
+
* plain data instead of mutating the object's prototype.
|
|
1013
|
+
*/
|
|
1014
|
+
toolCostTotals = /* @__PURE__ */ Object.create(null);
|
|
985
1015
|
/**
|
|
986
1016
|
* @param limitUsd the spend ceiling, in USD. `0` blocks the very first call.
|
|
987
1017
|
*/
|
|
@@ -1013,7 +1043,8 @@ var BudgetGuard = class {
|
|
|
1013
1043
|
* Throw {@link BudgetExceeded} if the next call would cross the ceiling.
|
|
1014
1044
|
*
|
|
1015
1045
|
* Call this immediately before each LLM request. The "next call" is estimated
|
|
1016
|
-
*
|
|
1046
|
+
* conservatively as the costlier of the last LLM call and the last tool call
|
|
1047
|
+
* (override with `estimatedNextCost`); the
|
|
1017
1048
|
* first call is always allowed unless the ceiling is already met. In-flight
|
|
1018
1049
|
* reservations count toward the total, so this stays correct alongside
|
|
1019
1050
|
* {@link BudgetGuard.reserve}.
|
|
@@ -1022,7 +1053,7 @@ var BudgetGuard = class {
|
|
|
1022
1053
|
* `settle()`, which hold the estimate across the await.
|
|
1023
1054
|
*/
|
|
1024
1055
|
check(estimatedNextCost) {
|
|
1025
|
-
const rawEstimate = estimatedNextCost === void 0 ? this.
|
|
1056
|
+
const rawEstimate = estimatedNextCost === void 0 ? this.defaultEstimate() : estimatedNextCost;
|
|
1026
1057
|
if (!Number.isFinite(rawEstimate)) {
|
|
1027
1058
|
throw new RangeError(
|
|
1028
1059
|
`estimatedNextCost must be a finite number, got ${rawEstimate}`
|
|
@@ -1043,10 +1074,11 @@ var BudgetGuard = class {
|
|
|
1043
1074
|
* the same stale total. Throws {@link BudgetExceeded} (without reserving) if
|
|
1044
1075
|
* the reservation would cross the ceiling. Returns the reservation handle to
|
|
1045
1076
|
* pass to {@link BudgetGuard.settle} (or {@link BudgetGuard.release} on error).
|
|
1046
|
-
* `estimatedCost` defaults to the last call
|
|
1077
|
+
* `estimatedCost` defaults to the costlier of the last LLM call and the last
|
|
1078
|
+
* tool call.
|
|
1047
1079
|
*/
|
|
1048
1080
|
reserve(estimatedCost) {
|
|
1049
|
-
const rawEstimate = estimatedCost === void 0 ? this.
|
|
1081
|
+
const rawEstimate = estimatedCost === void 0 ? this.defaultEstimate() : estimatedCost;
|
|
1050
1082
|
if (!Number.isFinite(rawEstimate)) {
|
|
1051
1083
|
throw new RangeError(
|
|
1052
1084
|
`estimatedCost must be a finite number, got ${rawEstimate}`
|
|
@@ -1098,13 +1130,13 @@ var BudgetGuard = class {
|
|
|
1098
1130
|
throw err;
|
|
1099
1131
|
}
|
|
1100
1132
|
if (reserved) {
|
|
1101
|
-
this.
|
|
1133
|
+
this.consumeReservation(reserved);
|
|
1102
1134
|
}
|
|
1103
1135
|
this.spentUsd += cost;
|
|
1104
1136
|
if (this.spentUsd - this.limitUsd > 0 && this.spentUsd - this.limitUsd < EPS) {
|
|
1105
1137
|
this.spentUsd = this.limitUsd;
|
|
1106
1138
|
}
|
|
1107
|
-
this.
|
|
1139
|
+
this.lastLlmCost = cost;
|
|
1108
1140
|
this.appendEvent({
|
|
1109
1141
|
timestamp: Date.now() / 1e3,
|
|
1110
1142
|
kind: "llm",
|
|
@@ -1134,24 +1166,64 @@ var BudgetGuard = class {
|
|
|
1134
1166
|
});
|
|
1135
1167
|
}
|
|
1136
1168
|
/**
|
|
1137
|
-
*
|
|
1169
|
+
* Atomically check the ceiling AND hold a tool call's cost in flight.
|
|
1170
|
+
*
|
|
1171
|
+
* The tool-spend counterpart of {@link BudgetGuard.reserve} — and STRONGER
|
|
1172
|
+
* than the LLM path, because a paid tool's price is usually known exactly
|
|
1173
|
+
* before the call, so the pre-call hard-stop is precise rather than
|
|
1174
|
+
* estimated:
|
|
1138
1175
|
*
|
|
1139
|
-
*
|
|
1140
|
-
*
|
|
1141
|
-
*
|
|
1142
|
-
*
|
|
1143
|
-
*
|
|
1144
|
-
*
|
|
1145
|
-
*
|
|
1176
|
+
* const handle = guard.reserveTool(0.02); // throws BEFORE Apollo runs
|
|
1177
|
+
* const result = await apollo.peopleLookup(...);
|
|
1178
|
+
* guard.settleTool("apollo.people_lookup", 0.02, { reserved: handle });
|
|
1179
|
+
*
|
|
1180
|
+
* Throws {@link BudgetExceeded} (without reserving) if the call would cross
|
|
1181
|
+
* the ceiling. The estimate is required — tools have no last-cost prediction
|
|
1182
|
+
* worth falling back to. Pass the returned handle to
|
|
1183
|
+
* {@link BudgetGuard.settleTool}, or {@link BudgetGuard.release} on failure.
|
|
1146
1184
|
*/
|
|
1147
|
-
|
|
1185
|
+
reserveTool(estimatedCost) {
|
|
1186
|
+
if (estimatedCost === void 0) {
|
|
1187
|
+
throw new RangeError("reserveTool requires an estimated cost, got undefined");
|
|
1188
|
+
}
|
|
1189
|
+
if (!Number.isFinite(estimatedCost) || estimatedCost < 0) {
|
|
1190
|
+
throw new RangeError(
|
|
1191
|
+
`estimatedCost must be a finite, non-negative number, got ${estimatedCost}`
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
return this.reserve(estimatedCost);
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* Release a reservation and record a tool call's actual cost.
|
|
1198
|
+
*
|
|
1199
|
+
* `recordTool` is `settleTool` with no reservation. The caller supplies the
|
|
1200
|
+
* cost — tools have no token usage to price. Accrues into the same
|
|
1201
|
+
* `spentUsd` ceiling as tokens, tallies the per-tool total
|
|
1202
|
+
* ({@link BudgetGuard.toolCosts}), updates the tool side of the next-call
|
|
1203
|
+
* estimate (tracked separately from the LLM side; the default prediction is
|
|
1204
|
+
* the max of the two, so a tool-hammering loop's plain `check()` stops
|
|
1205
|
+
* BEFORE the crossing call without a cheap tool shrinking the LLM
|
|
1206
|
+
* prediction), and appends
|
|
1207
|
+
* a `kind: "tool"` {@link SpendEvent} to {@link BudgetGuard.spendLog}.
|
|
1208
|
+
* Returns `costUsd`.
|
|
1209
|
+
*/
|
|
1210
|
+
settleTool(tool, costUsd, options = {}) {
|
|
1148
1211
|
if (!Number.isFinite(costUsd) || costUsd < 0) {
|
|
1149
1212
|
throw new RangeError(`costUsd must be a finite, non-negative number, got ${costUsd}`);
|
|
1150
1213
|
}
|
|
1214
|
+
const reserved = options.reserved ?? 0;
|
|
1215
|
+
if (!Number.isFinite(reserved) || reserved < 0) {
|
|
1216
|
+
throw new RangeError(`reserved must be a finite, non-negative number, got ${reserved}`);
|
|
1217
|
+
}
|
|
1218
|
+
if (reserved) {
|
|
1219
|
+
this.consumeReservation(reserved);
|
|
1220
|
+
}
|
|
1151
1221
|
this.spentUsd += costUsd;
|
|
1152
1222
|
if (this.spentUsd - this.limitUsd > 0 && this.spentUsd - this.limitUsd < EPS) {
|
|
1153
1223
|
this.spentUsd = this.limitUsd;
|
|
1154
1224
|
}
|
|
1225
|
+
this.lastToolCost = costUsd;
|
|
1226
|
+
this.toolCostTotals[tool] = (this.toolCostTotals[tool] ?? 0) + costUsd;
|
|
1155
1227
|
this.appendEvent({
|
|
1156
1228
|
timestamp: Date.now() / 1e3,
|
|
1157
1229
|
kind: "tool",
|
|
@@ -1159,10 +1231,22 @@ var BudgetGuard = class {
|
|
|
1159
1231
|
promptTokens: null,
|
|
1160
1232
|
completionTokens: null,
|
|
1161
1233
|
costUsd,
|
|
1162
|
-
...options.label !== void 0 ? { label: options.label } : {}
|
|
1234
|
+
...options.label !== void 0 ? { label: options.label } : {},
|
|
1235
|
+
...reserved ? { reserved } : {}
|
|
1163
1236
|
});
|
|
1164
1237
|
return costUsd;
|
|
1165
1238
|
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Accrue a non-LLM cost (a paid tool/API call) against the same ceiling.
|
|
1241
|
+
*
|
|
1242
|
+
* Post-hoc accrual for costs only known after the call (metered APIs); when
|
|
1243
|
+
* the price is known up front, {@link BudgetGuard.reserveTool} /
|
|
1244
|
+
* {@link BudgetGuard.settleTool} give the stronger pre-call hard-stop. See
|
|
1245
|
+
* `settleTool` for the full contract. Returns `costUsd`.
|
|
1246
|
+
*/
|
|
1247
|
+
recordTool(tool, costUsd, options = {}) {
|
|
1248
|
+
return this.settleTool(tool, costUsd, { reserved: 0, label: options.label });
|
|
1249
|
+
}
|
|
1166
1250
|
/**
|
|
1167
1251
|
* Drop an in-flight reservation without recording spend (e.g. the call failed
|
|
1168
1252
|
* before producing usage). Safe to call with `0`.
|
|
@@ -1172,16 +1256,25 @@ var BudgetGuard = class {
|
|
|
1172
1256
|
throw new RangeError(`reserved must be a finite, non-negative number, got ${reserved}`);
|
|
1173
1257
|
}
|
|
1174
1258
|
if (!reserved) return;
|
|
1175
|
-
this.
|
|
1259
|
+
this.consumeReservation(reserved);
|
|
1176
1260
|
}
|
|
1177
1261
|
/** USD left before the ceiling, net of in-flight reservations (never negative). */
|
|
1178
1262
|
get remainingUsd() {
|
|
1179
1263
|
return Math.max(0, this.limitUsd - this.spentUsd - this.reserved);
|
|
1180
1264
|
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Per-tool running USD totals, keyed by the name given to `settleTool()` /
|
|
1267
|
+
* `recordTool()` — e.g. `{"apollo.people_lookup": 0.42, "exa.search": 0.11}`.
|
|
1268
|
+
* Makes the token/tool split of the one shared ceiling inspectable
|
|
1269
|
+
* (`spentUsd - sum of toolCosts` is the token side). Returns a snapshot copy.
|
|
1270
|
+
*/
|
|
1271
|
+
get toolCosts() {
|
|
1272
|
+
return { ...this.toolCostTotals };
|
|
1273
|
+
}
|
|
1181
1274
|
/**
|
|
1182
1275
|
* The per-call spend ledger, oldest first — one {@link SpendEvent} per priced
|
|
1183
|
-
* `record()` / `settle()` / `recordTool()`. Returns a
|
|
1184
|
-
* it cannot corrupt the ledger.
|
|
1276
|
+
* `record()` / `settle()` / `recordTool()` / `settleTool()`. Returns a
|
|
1277
|
+
* snapshot copy: mutating it cannot corrupt the ledger.
|
|
1185
1278
|
*/
|
|
1186
1279
|
get spendLog() {
|
|
1187
1280
|
return [...this.spendEvents];
|
|
@@ -1212,6 +1305,34 @@ var BudgetGuard = class {
|
|
|
1212
1305
|
`;
|
|
1213
1306
|
}).join("");
|
|
1214
1307
|
}
|
|
1308
|
+
/**
|
|
1309
|
+
* The default next-call prediction when the caller supplies no estimate.
|
|
1310
|
+
* Conservative: the costlier of the last LLM call and the last tool call — a
|
|
1311
|
+
* mixed loop predicts the pricier kind, which at worst blocks one call early
|
|
1312
|
+
* (fail-closed) rather than letting a crossing call through because the LAST
|
|
1313
|
+
* event happened to be cheap.
|
|
1314
|
+
*/
|
|
1315
|
+
defaultEstimate() {
|
|
1316
|
+
return Math.max(this.lastLlmCost, this.lastToolCost);
|
|
1317
|
+
}
|
|
1318
|
+
/**
|
|
1319
|
+
* Subtract a settled/released hold from the in-flight tally. A handle larger
|
|
1320
|
+
* than EVERYTHING currently held cannot have come from a matching
|
|
1321
|
+
* `reserve()` — throwing beats silently clamping, which would free OTHER
|
|
1322
|
+
* callers' holds and fail the ceiling open. The epsilon absorbs float dust
|
|
1323
|
+
* from accumulating and draining many holds; per-caller over-release (a
|
|
1324
|
+
* handle within the total but larger than the caller's own hold) is
|
|
1325
|
+
* undetectable without per-handle tracking and remains the caller's
|
|
1326
|
+
* responsibility.
|
|
1327
|
+
*/
|
|
1328
|
+
consumeReservation(reserved) {
|
|
1329
|
+
if (reserved > this.reserved + EPS) {
|
|
1330
|
+
throw new RangeError(
|
|
1331
|
+
`reserved handle (${reserved}) exceeds total in-flight reservations (${this.reserved}) \u2014 a handle must come from a matching reserve()`
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1334
|
+
this.reserved = Math.max(0, this.reserved - reserved);
|
|
1335
|
+
}
|
|
1215
1336
|
appendEvent(event) {
|
|
1216
1337
|
this.spendEvents.push(Object.freeze(event));
|
|
1217
1338
|
if (this.maxLogEvents !== void 0 && this.spendEvents.length > this.maxLogEvents) {
|
|
@@ -1249,6 +1370,68 @@ function defaultOnBlock(spentUsd, limitUsd) {
|
|
|
1249
1370
|
);
|
|
1250
1371
|
}
|
|
1251
1372
|
|
|
1373
|
+
// src/latency.ts
|
|
1374
|
+
var LatencyBudget = class {
|
|
1375
|
+
slaMs;
|
|
1376
|
+
nearDeadlineBps;
|
|
1377
|
+
onBlock;
|
|
1378
|
+
clock;
|
|
1379
|
+
startedAt;
|
|
1380
|
+
/** The budget starts counting at construction — build it when the request
|
|
1381
|
+
* (and its SLA) starts. */
|
|
1382
|
+
constructor(slaMs, options = {}) {
|
|
1383
|
+
if (!(Number.isFinite(slaMs) && slaMs > 0)) {
|
|
1384
|
+
throw new RangeError("slaMs must be > 0");
|
|
1385
|
+
}
|
|
1386
|
+
const nearDeadlineBps = options.nearDeadlineBps ?? 8e3;
|
|
1387
|
+
if (!Number.isInteger(nearDeadlineBps) || nearDeadlineBps < 0 || nearDeadlineBps > 1e4) {
|
|
1388
|
+
throw new RangeError("nearDeadlineBps must be an integer 0..10000");
|
|
1389
|
+
}
|
|
1390
|
+
this.slaMs = slaMs;
|
|
1391
|
+
this.nearDeadlineBps = nearDeadlineBps;
|
|
1392
|
+
this.onBlock = options.onBlock;
|
|
1393
|
+
this.clock = options.clock ?? (() => performance.now());
|
|
1394
|
+
this.startedAt = this.clock();
|
|
1395
|
+
}
|
|
1396
|
+
/** Milliseconds since construction (monotonic). */
|
|
1397
|
+
get elapsedMs() {
|
|
1398
|
+
return this.clock() - this.startedAt;
|
|
1399
|
+
}
|
|
1400
|
+
/** Milliseconds left before the SLA, floored at 0 — the readable signal a
|
|
1401
|
+
* router uses to pick a faster fallback or truncate work mid-chain. */
|
|
1402
|
+
get remainingMs() {
|
|
1403
|
+
return Math.max(0, this.slaMs - this.elapsedMs);
|
|
1404
|
+
}
|
|
1405
|
+
/**
|
|
1406
|
+
* Throw {@link DeadlineExceeded} when the projected elapsed time (now +
|
|
1407
|
+
* `expectedMs` for the upcoming call) would blow the SLA. Call it
|
|
1408
|
+
* immediately before each tool/model call; pass 0 to only gate on time
|
|
1409
|
+
* already spent.
|
|
1410
|
+
*/
|
|
1411
|
+
check(expectedMs = 0) {
|
|
1412
|
+
if (!(Number.isFinite(expectedMs) && expectedMs >= 0)) {
|
|
1413
|
+
throw new RangeError("expectedMs must be >= 0");
|
|
1414
|
+
}
|
|
1415
|
+
const elapsed = this.elapsedMs;
|
|
1416
|
+
if (elapsed + expectedMs > this.slaMs) {
|
|
1417
|
+
this.onBlock?.(elapsed, this.slaMs);
|
|
1418
|
+
throw new DeadlineExceeded(elapsed, this.slaMs);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
/** The soft near-deadline signal — symmetric to `BudgetGuard.advisory()`. */
|
|
1422
|
+
advisory() {
|
|
1423
|
+
const elapsed = this.elapsedMs;
|
|
1424
|
+
const usedBps = elapsed > 0 ? Math.min(1e4, Math.round(elapsed * 1e4 / this.slaMs)) : 0;
|
|
1425
|
+
return {
|
|
1426
|
+
nearDeadline: usedBps >= this.nearDeadlineBps,
|
|
1427
|
+
usedBps,
|
|
1428
|
+
remainingMs: Math.max(0, this.slaMs - elapsed),
|
|
1429
|
+
slaMs: this.slaMs,
|
|
1430
|
+
elapsedMs: elapsed
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
|
|
1252
1435
|
// src/middleware.ts
|
|
1253
1436
|
function usageTokens(modelId, usage) {
|
|
1254
1437
|
const u = usage;
|
|
@@ -1342,7 +1525,9 @@ function budgetGuardMiddleware(guard) {
|
|
|
1342
1525
|
0 && (module.exports = {
|
|
1343
1526
|
BudgetExceeded,
|
|
1344
1527
|
BudgetGuard,
|
|
1528
|
+
DeadlineExceeded,
|
|
1345
1529
|
FloeGuardError,
|
|
1530
|
+
LatencyBudget,
|
|
1346
1531
|
UnpriceableModelError,
|
|
1347
1532
|
budgetGuardMiddleware,
|
|
1348
1533
|
priceTokens,
|