floe-guard 0.15.6 → 0.16.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 +73 -0
- package/dist/adapters/livekit.cjs +7 -1
- 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 +7 -1
- 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 +7 -1
- 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-BMRQIQXH.js → chunk-5V3ZR6GE.js} +8 -2
- package/dist/chunk-5V3ZR6GE.js.map +1 -0
- package/dist/{guard-BPIqdSVt.d.cts → guard-Bbs5OxAF.d.cts} +17 -1
- package/dist/{guard-BPIqdSVt.d.ts → guard-Bbs5OxAF.d.ts} +17 -1
- package/dist/index.cjs +178 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -3
- package/dist/index.d.ts +68 -3
- package/dist/index.js +169 -4
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/src/cost_map.json +7 -1
- package/dist/chunk-BMRQIQXH.js.map +0 -1
package/README.md
CHANGED
|
@@ -34,6 +34,79 @@ The middleware sits in the call path: it `check()`s before `doGenerate` /
|
|
|
34
34
|
`doStream` (throwing `BudgetExceeded` to halt the run) and `record()`s priced
|
|
35
35
|
token usage after — for streaming it reads usage from the `finish` part.
|
|
36
36
|
|
|
37
|
+
## Mid-stream budget enforcement
|
|
38
|
+
|
|
39
|
+
`StreamGuard` and `guardStream` port Python's streaming USD guard. They price
|
|
40
|
+
each text delta before it reaches the consumer. When a chunk crosses the
|
|
41
|
+
shared ceiling, its partial spend is recorded in `guard.spendLog` **before**
|
|
42
|
+
`BudgetExceeded` is thrown. Active streams on the same guard share the ceiling.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { BudgetGuard, guardStream } from "floe-guard";
|
|
46
|
+
|
|
47
|
+
const guard = new BudgetGuard(0.01);
|
|
48
|
+
// textDeltas is your provider's Iterable<string> or AsyncIterable<string>.
|
|
49
|
+
for await (const text of guardStream(guard, "gpt-4o", textDeltas)) {
|
|
50
|
+
consume(text);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
For structured chunks, supply `{ getText: chunk => chunk.delta.text ?? "" }`
|
|
55
|
+
using your provider's actual shape. Without an extractor, non-string chunks
|
|
56
|
+
throw instead of silently recording zero. Synchronous inputs return a
|
|
57
|
+
synchronous iterator; asynchronous inputs return an asynchronous iterator.
|
|
58
|
+
|
|
59
|
+
Use `StreamGuard` directly when the provider reports final usage:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { StreamGuard } from "floe-guard";
|
|
63
|
+
|
|
64
|
+
const reserved = guard.reserve(guard.estimateCall("gpt-4o", 100, 200));
|
|
65
|
+
const stream = new StreamGuard(guard, "gpt-4o", { promptTokens: 100, reserved });
|
|
66
|
+
try {
|
|
67
|
+
for await (const text of textDeltas) {
|
|
68
|
+
stream.feedText(text); // or feedTokens(n) with known per-chunk counts
|
|
69
|
+
consume(text);
|
|
70
|
+
}
|
|
71
|
+
stream.finish({ promptTokens: reportedPromptTokens, completionTokens: reportedCompletionTokens });
|
|
72
|
+
} finally {
|
|
73
|
+
stream.close(); // idempotent; settles estimates if finish() was not reached
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- Options: `promptTokens`, `reserved`, `price`, `label`, and `countTokens(delta)`.
|
|
78
|
+
`guardStream` additionally accepts `getText(chunk)`.
|
|
79
|
+
Configuration is captured at construction; changing the caller's options or
|
|
80
|
+
manual price object later does not change an existing stream.
|
|
81
|
+
- `approxTokens` defaults to roughly four Unicode characters per token, with
|
|
82
|
+
a minimum of one for a non-empty delta. A custom tokenizer can replace it.
|
|
83
|
+
`finish()` reconciles to reported usage; `completionTokens` exposes the running
|
|
84
|
+
estimate. Mid-stream checks cover the aggregate USD ceiling, matching Python;
|
|
85
|
+
token reservations are reconciled at settlement.
|
|
86
|
+
- An unpriceable model fails at construction and releases its reservation.
|
|
87
|
+
With `failClosed: false`, unpriceable streams pass through and settlement
|
|
88
|
+
warns and skips accounting, matching the existing guard policy.
|
|
89
|
+
- The wrapper settles on exhaustion, source/consumer errors, and early `break`,
|
|
90
|
+
and closes the source iterator when iteration ends early. Direct users must
|
|
91
|
+
call `close()` in `finally`. If a wrapper is **never iterated**, its reservation
|
|
92
|
+
remains the caller's responsibility: call `guard.release(reserved)`.
|
|
93
|
+
- The crossing chunk has already been generated. With accurate counts, the
|
|
94
|
+
local cutoff can overshoot by that chunk; heuristic error, parallel streams,
|
|
95
|
+
provider buffering and delayed cancellation can cause additional actual
|
|
96
|
+
spend. Stopping iteration requests iterator cleanup, not guaranteed remote
|
|
97
|
+
cancellation. Connect cleanup to your provider's abort mechanism where needed.
|
|
98
|
+
- Existing Vapi, LiveKit and middleware streaming behavior is unchanged; this
|
|
99
|
+
is the standalone primitive requested in issue #124.
|
|
100
|
+
|
|
101
|
+
Run the no-key example from a repository checkout:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
cd js
|
|
105
|
+
npm ci
|
|
106
|
+
npm run build
|
|
107
|
+
node ../examples/streaming_guard.mjs
|
|
108
|
+
```
|
|
109
|
+
|
|
37
110
|
## Pricing
|
|
38
111
|
|
|
39
112
|
Tokens are priced **offline** from a bundled
|
|
@@ -76,7 +76,7 @@ function preCall(guard, options = {}) {
|
|
|
76
76
|
// src/cost_map.json
|
|
77
77
|
var cost_map_default = {
|
|
78
78
|
__meta__: {
|
|
79
|
-
generated_at: "2026-09-
|
|
79
|
+
generated_at: "2026-09-14",
|
|
80
80
|
source: "LiteLLM public model prices (bundled snapshot); voice rates verified from vendor list pages"
|
|
81
81
|
},
|
|
82
82
|
"chat-latest": {
|
|
@@ -334,6 +334,12 @@ var cost_map_default = {
|
|
|
334
334
|
cache_read_input_token_cost: 2e-7,
|
|
335
335
|
cache_creation_input_token_cost: 25e-7
|
|
336
336
|
},
|
|
337
|
+
"computer-use-preview": {
|
|
338
|
+
input_cost_per_token: 3e-6,
|
|
339
|
+
output_cost_per_token: 12e-6,
|
|
340
|
+
litellm_provider: "openai",
|
|
341
|
+
mode: "chat"
|
|
342
|
+
},
|
|
337
343
|
"daybreak-blue-latest": {
|
|
338
344
|
input_cost_per_token: 4e-6,
|
|
339
345
|
output_cost_per_token: 2e-5,
|