floe-guard 0.13.0 → 0.13.2
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/dist/adapters/livekit.cjs +24 -0
- package/dist/adapters/livekit.cjs.map +1 -1
- package/dist/adapters/livekit.d.cts +1 -1
- package/dist/adapters/livekit.d.ts +1 -1
- package/dist/adapters/livekit.js +1 -1
- package/dist/adapters/retell.cjs +24 -0
- package/dist/adapters/retell.cjs.map +1 -1
- package/dist/adapters/retell.d.cts +1 -1
- package/dist/adapters/retell.d.ts +1 -1
- package/dist/adapters/retell.js +1 -1
- package/dist/adapters/vapi.cjs +24 -0
- package/dist/adapters/vapi.cjs.map +1 -1
- package/dist/adapters/vapi.d.cts +2 -2
- package/dist/adapters/vapi.d.ts +2 -2
- package/dist/adapters/vapi.js +1 -1
- package/dist/{chunk-5Q5VQC6J.js → chunk-TAK6I7CN.js} +25 -1
- package/dist/chunk-TAK6I7CN.js.map +1 -0
- package/dist/{guard-BXtXoark.d.cts → guard-Ct38zQZS.d.cts} +6 -4
- package/dist/{guard-BXtXoark.d.ts → guard-Ct38zQZS.d.ts} +6 -4
- package/dist/index.cjs +42 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -4
- package/dist/index.d.ts +13 -4
- package/dist/index.js +19 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cost_map.json +24 -0
- package/dist/chunk-5Q5VQC6J.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { B as BudgetGuard, b as BudgetAdvisory } from './guard-
|
|
2
|
-
export { a as BudgetExceeded, c as BudgetGuardOptions, d as BudgetReservation, D as DeadlineExceeded, F as FloeGuardError, L as LedgerSyncError, M as ManualPrice, P as PricedModel, R as ReservationHandle, S as SpendEvent, T as TokenBudgetExceeded, U as UnpriceableModelError, e as UnpriceableVoiceError, p as priceTokens, f as pricing, r as resolvePrice } from './guard-
|
|
1
|
+
import { B as BudgetGuard, b as BudgetAdvisory } from './guard-Ct38zQZS.cjs';
|
|
2
|
+
export { a as BudgetExceeded, c as BudgetGuardOptions, d as BudgetReservation, D as DeadlineExceeded, F as FloeGuardError, L as LedgerSyncError, M as ManualPrice, P as PricedModel, R as ReservationHandle, S as SpendEvent, T as TokenBudgetExceeded, U as UnpriceableModelError, e as UnpriceableVoiceError, p as priceTokens, f as pricing, r as resolvePrice } from './guard-Ct38zQZS.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* LatencyBudget — a cumulative tool-chain deadline, sibling to BudgetGuard.
|
|
@@ -142,7 +142,9 @@ declare function pushLedger(jsonl: string, apiKey?: string, opts?: {
|
|
|
142
142
|
* Reserving before the await is what makes parallel calls (`Promise.all` over
|
|
143
143
|
* several generations) honour the ceiling: each holds its slice instead of all
|
|
144
144
|
* reading the same stale total (issue #18). The reservation is released if the
|
|
145
|
-
* call throws, or if a stream
|
|
145
|
+
* call throws, is cancelled by the consumer, or if a stream completes without a
|
|
146
|
+
* usage-bearing finish part — in the last case the stream is also rejected with
|
|
147
|
+
* an error rather than treated as a free ($0) call (fail-closed).
|
|
146
148
|
*
|
|
147
149
|
* The model id used for pricing comes from `model.modelId`.
|
|
148
150
|
*/
|
|
@@ -205,7 +207,14 @@ interface BudgetRetryOptions<T> {
|
|
|
205
207
|
maxAttempts?: number;
|
|
206
208
|
/** Choose a cheaper retry plan when `guard.advisory().nearLimit` is true. */
|
|
207
209
|
onDegrade?: (error: unknown, advisory: BudgetAdvisory) => RetryPlan<T> | Promise<RetryPlan<T> | undefined> | undefined;
|
|
208
|
-
/**
|
|
210
|
+
/**
|
|
211
|
+
* Decide whether an error is retryable.
|
|
212
|
+
*
|
|
213
|
+
* Defaults to all non-{@link FloeGuardError} errors — every
|
|
214
|
+
* {@link FloeGuardError} (including {@link BudgetExceeded} and
|
|
215
|
+
* {@link UnpriceableModelError}) is treated as terminal because retrying a
|
|
216
|
+
* deterministic guard error cannot fix it and would only multiply LLM spend.
|
|
217
|
+
*/
|
|
209
218
|
retryIf?: (error: unknown) => boolean;
|
|
210
219
|
}
|
|
211
220
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { B as BudgetGuard, b as BudgetAdvisory } from './guard-
|
|
2
|
-
export { a as BudgetExceeded, c as BudgetGuardOptions, d as BudgetReservation, D as DeadlineExceeded, F as FloeGuardError, L as LedgerSyncError, M as ManualPrice, P as PricedModel, R as ReservationHandle, S as SpendEvent, T as TokenBudgetExceeded, U as UnpriceableModelError, e as UnpriceableVoiceError, p as priceTokens, f as pricing, r as resolvePrice } from './guard-
|
|
1
|
+
import { B as BudgetGuard, b as BudgetAdvisory } from './guard-Ct38zQZS.js';
|
|
2
|
+
export { a as BudgetExceeded, c as BudgetGuardOptions, d as BudgetReservation, D as DeadlineExceeded, F as FloeGuardError, L as LedgerSyncError, M as ManualPrice, P as PricedModel, R as ReservationHandle, S as SpendEvent, T as TokenBudgetExceeded, U as UnpriceableModelError, e as UnpriceableVoiceError, p as priceTokens, f as pricing, r as resolvePrice } from './guard-Ct38zQZS.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* LatencyBudget — a cumulative tool-chain deadline, sibling to BudgetGuard.
|
|
@@ -142,7 +142,9 @@ declare function pushLedger(jsonl: string, apiKey?: string, opts?: {
|
|
|
142
142
|
* Reserving before the await is what makes parallel calls (`Promise.all` over
|
|
143
143
|
* several generations) honour the ceiling: each holds its slice instead of all
|
|
144
144
|
* reading the same stale total (issue #18). The reservation is released if the
|
|
145
|
-
* call throws, or if a stream
|
|
145
|
+
* call throws, is cancelled by the consumer, or if a stream completes without a
|
|
146
|
+
* usage-bearing finish part — in the last case the stream is also rejected with
|
|
147
|
+
* an error rather than treated as a free ($0) call (fail-closed).
|
|
146
148
|
*
|
|
147
149
|
* The model id used for pricing comes from `model.modelId`.
|
|
148
150
|
*/
|
|
@@ -205,7 +207,14 @@ interface BudgetRetryOptions<T> {
|
|
|
205
207
|
maxAttempts?: number;
|
|
206
208
|
/** Choose a cheaper retry plan when `guard.advisory().nearLimit` is true. */
|
|
207
209
|
onDegrade?: (error: unknown, advisory: BudgetAdvisory) => RetryPlan<T> | Promise<RetryPlan<T> | undefined> | undefined;
|
|
208
|
-
/**
|
|
210
|
+
/**
|
|
211
|
+
* Decide whether an error is retryable.
|
|
212
|
+
*
|
|
213
|
+
* Defaults to all non-{@link FloeGuardError} errors — every
|
|
214
|
+
* {@link FloeGuardError} (including {@link BudgetExceeded} and
|
|
215
|
+
* {@link UnpriceableModelError}) is treated as terminal because retrying a
|
|
216
|
+
* deterministic guard error cannot fix it and would only multiply LLM spend.
|
|
217
|
+
*/
|
|
209
218
|
retryIf?: (error: unknown) => boolean;
|
|
210
219
|
}
|
|
211
220
|
/**
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
priceVoiceLeg,
|
|
14
14
|
resolveVoiceRate,
|
|
15
15
|
voiceLegCost
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-TAK6I7CN.js";
|
|
17
17
|
|
|
18
18
|
// src/sync.ts
|
|
19
19
|
var FLOE_API_KEY_ENV = "FLOE_API_KEY";
|
|
@@ -772,19 +772,21 @@ var BudgetGuard = class {
|
|
|
772
772
|
}
|
|
773
773
|
/**
|
|
774
774
|
* The ONE choke point, across two dimensions and two scopes. Returns the first
|
|
775
|
-
* ceiling that blocks as `[dimension, scope]` — dimension
|
|
776
|
-
* scope `"aggregate" | "step"` — or `null` if the call
|
|
777
|
-
* Aggregate is checked before step (the guard-wide ceiling
|
|
775
|
+
* ceiling that blocks as `[dimension, scope, spent, limit]` — dimension
|
|
776
|
+
* `"usd" | "tokens"`, scope `"aggregate" | "step"` — or `null` if the call
|
|
777
|
+
* fits everywhere. Aggregate is checked before step (the guard-wide ceiling
|
|
778
|
+
* is the hard limit). `spent` / `limit` are captured here so `raiseBlock`
|
|
779
|
+
* (called outside the lock) reads no shared state and stays race-free.
|
|
778
780
|
*/
|
|
779
781
|
blockingCross(estimateUsd, estimateTokens) {
|
|
780
782
|
const committed = this.spentUsd + this.reserved;
|
|
781
783
|
if (committed > this.limitUsd - EPS || committed + estimateUsd > this.limitUsd + EPS) {
|
|
782
|
-
return ["usd", "aggregate"];
|
|
784
|
+
return ["usd", "aggregate", this.spentUsd, this.limitUsd];
|
|
783
785
|
}
|
|
784
786
|
if (this.tokenLimit !== null) {
|
|
785
787
|
const committedT = this.spentTokens + this.reservedTokens;
|
|
786
788
|
if (committedT >= this.tokenLimit || committedT + estimateTokens > this.tokenLimit) {
|
|
787
|
-
return ["tokens", "aggregate"];
|
|
789
|
+
return ["tokens", "aggregate", committedT, this.tokenLimit];
|
|
788
790
|
}
|
|
789
791
|
}
|
|
790
792
|
const step = this.steps.length ? this.steps[this.steps.length - 1] : null;
|
|
@@ -792,36 +794,26 @@ var BudgetGuard = class {
|
|
|
792
794
|
if (step.maxUsd !== null) {
|
|
793
795
|
const sCommitted = step.spentUsd + step.reservedUsd;
|
|
794
796
|
if (sCommitted > step.maxUsd - EPS || sCommitted + estimateUsd > step.maxUsd + EPS) {
|
|
795
|
-
return ["usd", "step"];
|
|
797
|
+
return ["usd", "step", sCommitted, step.maxUsd];
|
|
796
798
|
}
|
|
797
799
|
}
|
|
798
800
|
if (step.maxTokens !== null) {
|
|
799
801
|
const sCommittedT = step.spentTokens + step.reservedTokens;
|
|
800
802
|
if (sCommittedT >= step.maxTokens || sCommittedT + estimateTokens > step.maxTokens) {
|
|
801
|
-
return ["tokens", "step"];
|
|
803
|
+
return ["tokens", "step", sCommittedT, step.maxTokens];
|
|
802
804
|
}
|
|
803
805
|
}
|
|
804
806
|
}
|
|
805
807
|
return null;
|
|
806
808
|
}
|
|
807
|
-
/** Notify + throw the right error for a [dimension, scope] block. */
|
|
809
|
+
/** Notify + throw the right error for a [dimension, scope, spent, limit] block. */
|
|
808
810
|
raiseBlock(blocked) {
|
|
809
|
-
const [dimension, scope] = blocked;
|
|
811
|
+
const [dimension, scope, spent, limit] = blocked;
|
|
810
812
|
if (dimension === "usd") {
|
|
811
|
-
this.onBlock(
|
|
812
|
-
throw new BudgetExceeded(
|
|
813
|
+
this.onBlock(spent, limit);
|
|
814
|
+
throw new BudgetExceeded(spent, limit);
|
|
813
815
|
}
|
|
814
|
-
|
|
815
|
-
let limitT;
|
|
816
|
-
if (scope === "step") {
|
|
817
|
-
const step = this.steps[this.steps.length - 1];
|
|
818
|
-
spentT = step.spentTokens + step.reservedTokens;
|
|
819
|
-
limitT = step.maxTokens ?? 0;
|
|
820
|
-
} else {
|
|
821
|
-
spentT = this.spentTokens + this.reservedTokens;
|
|
822
|
-
limitT = this.tokenLimit ?? 0;
|
|
823
|
-
}
|
|
824
|
-
throw new TokenBudgetExceeded(spentT, limitT, scope);
|
|
816
|
+
throw new TokenBudgetExceeded(Math.trunc(spent), Math.trunc(limit), scope);
|
|
825
817
|
}
|
|
826
818
|
/**
|
|
827
819
|
* Scope a per-step USD and/or token cap for a **sequential agent loop**.
|
|
@@ -1090,6 +1082,9 @@ function budgetGuardMiddleware(guard) {
|
|
|
1090
1082
|
if (!handled) {
|
|
1091
1083
|
handled = true;
|
|
1092
1084
|
guard.release(reserved);
|
|
1085
|
+
throw new Error(
|
|
1086
|
+
`Model '${model.modelId}' stream completed without a token-usage finish part \u2014 the budget guard cannot meter spend it cannot see, so this call is rejected rather than treated as free.`
|
|
1087
|
+
);
|
|
1093
1088
|
}
|
|
1094
1089
|
},
|
|
1095
1090
|
cancel() {
|
|
@@ -1109,7 +1104,7 @@ function budgetGuardMiddleware(guard) {
|
|
|
1109
1104
|
|
|
1110
1105
|
// src/retry.ts
|
|
1111
1106
|
function defaultRetryIf(error) {
|
|
1112
|
-
return !(error instanceof
|
|
1107
|
+
return !(error instanceof FloeGuardError);
|
|
1113
1108
|
}
|
|
1114
1109
|
async function withBudgetRetry(guard, call, options = {}) {
|
|
1115
1110
|
const maxAttempts = options.maxAttempts ?? 2;
|