@bivelio/savings-layer 0.5.1 → 1.0.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/CHANGELOG.md +115 -0
- package/LICENSE +183 -163
- package/LICENSE-CLOUD.md +174 -0
- package/NOTICE +11 -0
- package/README.md +228 -126
- package/dist/adapters/ai-sdk.js +78 -78
- package/dist/adapters/anthropic-sdk.js +88 -88
- package/dist/adapters/langchain.js +58 -58
- package/dist/adapters/mastra.js +70 -70
- package/dist/gateway/bin.js +1291 -1082
- package/dist/index.d.ts +367 -197
- package/dist/index.js +1717 -1641
- package/docs/terminales/README.md +39 -33
- package/docs/terminales/aider.md +1 -1
- package/docs/terminales/claude-code.md +2 -2
- package/docs/terminales/cline.md +1 -1
- package/docs/terminales/codex.md +3 -3
- package/docs/terminales/copilot.md +1 -1
- package/docs/terminales/crush.md +1 -1
- package/docs/terminales/cursor.md +32 -0
- package/docs/terminales/deepseek.md +28 -0
- package/docs/terminales/droid.md +1 -1
- package/docs/terminales/gemini.md +38 -0
- package/docs/terminales/goose.md +2 -2
- package/docs/terminales/kilo.md +2 -2
- package/docs/terminales/no-soportados.md +16 -27
- package/docs/terminales/ollama.md +19 -0
- package/docs/terminales/opencode.md +2 -2
- package/docs/terminales/qwen-code.md +2 -2
- package/package.json +48 -20
package/README.md
CHANGED
|
@@ -11,50 +11,119 @@
|
|
|
11
11
|
<p align="center">
|
|
12
12
|
<a href="https://www.npmjs.com/package/@bivelio/savings-layer"><img src="https://img.shields.io/npm/v/%40bivelio%2Fsavings-layer?color=2F63D8&label=npm" alt="npm version"></a>
|
|
13
13
|
<img src="https://img.shields.io/badge/node-%E2%89%A520-14A06F" alt="Node ≥ 20">
|
|
14
|
-
<img src="https://img.shields.io/badge/license-
|
|
14
|
+
<img src="https://img.shields.io/badge/license-Apache--2.0-2F63D8" alt="Apache-2.0">
|
|
15
|
+
<img src="https://img.shields.io/badge/runtime%20dependencies-0-14A06F" alt="0 runtime dependencies">
|
|
16
|
+
<img src="https://img.shields.io/badge/telemetry-off%20by%20default-0F1218" alt="Telemetry off by default">
|
|
15
17
|
</p>
|
|
16
18
|
|
|
17
|
-
###
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
that don't have to happen
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
19
|
+
### Open core: install it and it saves. No key. No signup. No telemetry.
|
|
20
|
+
|
|
21
|
+
`@bivelio/savings-layer` (**BV‑SALA**) sits between your app — or your coding agent —
|
|
22
|
+
and the LLM provider, and lets through only what has to be paid for: it skips calls
|
|
23
|
+
that don't have to happen (exact + semantic cache, deduplication), trims what does
|
|
24
|
+
(prompt-cache placement, context and tool-output compaction, serialization), and
|
|
25
|
+
routes each request to the cheapest execution that meets your quality bar (model
|
|
26
|
+
cascade, sibling models, provider failover) — on OpenAI, Anthropic, Gemini,
|
|
27
|
+
**DeepSeek, Qwen, GLM, Moonshot**, any OpenAI‑compatible endpoint, and local
|
|
28
|
+
servers (Ollama, vLLM, LM Studio).
|
|
29
|
+
|
|
30
|
+
It is not a promise you take on faith: **every saving is reported request by
|
|
31
|
+
request, valued with your provider's own usage counter at versioned list prices**,
|
|
32
|
+
split into four ledgers (`avoided_calls`, `eliminated_tokens`, `compressed_tokens`,
|
|
33
|
+
`provider_cached_tokens`) and labelled `measured`, `reference` or `estimated`.
|
|
34
|
+
Nothing is sent to BiVelio unless you choose to add a key: **zero telemetry by
|
|
35
|
+
default**, and prompts, completions and provider keys never reach BiVelio
|
|
36
|
+
(the exact per-request fields a cloud key sends are in [LICENSE-CLOUD.md](./LICENSE-CLOUD.md) §5).
|
|
37
|
+
|
|
38
|
+
The free part is the whole savings layer. What a (paid, optional) BiVelio key adds
|
|
39
|
+
is the cloud: the team dashboard, shared semantic caches across seats, org
|
|
40
|
+
entitlements and license management. The cloud subscription is a fixed fee, paid
|
|
41
|
+
whether or not you save — Pro €15 / seat / month or €150 / seat / year; Team
|
|
42
|
+
€129 / month with 10 seats included (€12 per extra seat; €1,290 / year, €120 per
|
|
43
|
+
extra seat), EUR, VAT not included — and there is never a commission on what you
|
|
44
|
+
save (see [LICENSE-CLOUD.md](./LICENSE-CLOUD.md) §6).
|
|
45
|
+
|
|
46
|
+
### Install anywhere, in 60 seconds
|
|
47
|
+
|
|
48
|
+
```shell
|
|
49
|
+
# 1 · Any OpenAI SDK, any language — point it at the local gateway (Python, Node, Cursor, Aider, Cline, OpenWebUI…)
|
|
50
|
+
npx @bivelio/savings-layer gateway --upstream openai --port 8403 # or: deepseek | qwen | glm | moonshot | gemini | ollama | vllm | lmstudio
|
|
51
|
+
# python: OpenAI(base_url="http://127.0.0.1:8403/v1")
|
|
52
|
+
|
|
53
|
+
# 2 · Coding agents, one command, zero configuration
|
|
54
|
+
npx @bivelio/savings-layer claude # Claude Code through the gateway
|
|
55
|
+
npx @bivelio/savings-layer gemini # Gemini CLI (native Gemini protocol)
|
|
56
|
+
npx @bivelio/savings-layer codex # Codex CLI (OpenAI or ChatGPT auth)
|
|
57
|
+
npx @bivelio/savings-layer qwen # Qwen Code
|
|
58
|
+
npx @bivelio/savings-layer aider # Aider
|
|
59
|
+
|
|
60
|
+
# Claude Code with DeepSeek's Anthropic-format endpoint (cache reads at $0.003/1M):
|
|
61
|
+
# ANTHROPIC_BASE_URL=http://127.0.0.1:8402 ANTHROPIC_AUTH_TOKEN=<deepseek key> claude
|
|
62
|
+
# with: npx @bivelio/savings-layer gateway --upstream deepseek
|
|
63
|
+
|
|
64
|
+
# 3 · Your app, TypeScript/JavaScript — the SDK in your code (zero runtime dependencies)
|
|
65
|
+
npm i @bivelio/savings-layer
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
// The imports come from the "Wrap the call" block below; this block compiles
|
|
70
|
+
// together with it (the repo test concatenates every README block into one
|
|
71
|
+
// program, as if you pasted them in order).
|
|
72
|
+
const provider = new OpenAICompatibleProvider({
|
|
73
|
+
baseUrl: "https://api.openai.com/v1",
|
|
74
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
75
|
+
});
|
|
76
|
+
const layer = createSavingsLayer({
|
|
77
|
+
provider,
|
|
78
|
+
// OPEN CORE: nothing else is required — COMMUNITY mode, zero telemetry.
|
|
79
|
+
// For the cloud dashboard + team metering, add (all optional):
|
|
80
|
+
// // licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
|
|
81
|
+
// // licenseServerUrl: "https://savings.bivelio.com",
|
|
82
|
+
// serviceAccountKey: process.env.BIVELIO_SERVICE_KEY,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const { text, report } = await layer.generate({
|
|
86
|
+
model: "openai/gpt-4o-mini",
|
|
87
|
+
messages: [{ role: "user", content: "What is 2 + 2 * 3?" }],
|
|
88
|
+
});
|
|
89
|
+
console.log(text); // "8" — computed locally, no LLM call
|
|
90
|
+
console.log(report.calls.avoidedLlm); // → 1: a whole call you never paid for
|
|
91
|
+
console.log(report.cost.basis); // "estimated": nothing ran, so there is no provider counter
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`npx @bivelio/savings-layer init` prints the exact three steps for your stack
|
|
95
|
+
(Next.js, Express, Vercel AI SDK, Mastra, LangChain.js, Anthropic/OpenAI SDK) and
|
|
96
|
+
`npx @bivelio/savings-layer doctor` prints the exact configuration for every
|
|
97
|
+
installed coding agent. The published benchmark, trial by trial, with the raw CSV,
|
|
98
|
+
is at **[savings.bivelio.com/en/how-it-works](https://savings.bivelio.com/en/how-it-works)**.
|
|
99
|
+
|
|
100
|
+
### Community vs Cloud
|
|
101
|
+
|
|
102
|
+
| | Community (default, no keys) | Cloud (optional `BIVELIO_*` keys) |
|
|
103
|
+
| --- | --- | --- |
|
|
104
|
+
| Exact + semantic cache, deduplication of identical requests | ✅ | ✅ |
|
|
105
|
+
| Model cascade, sibling routing, prompt-cache placement | ✅ | ✅ |
|
|
106
|
+
| Compression, output contracts, four savings ledgers | ✅ | ✅ |
|
|
107
|
+
| Gateway + `bvsala` CLI + MCP server | ✅ | ✅ |
|
|
108
|
+
| Team dashboard, seat metering, shared caches | — | ✅ |
|
|
109
|
+
| Anything sent to BiVelio | **Nothing** | Per-request usage only (counts, cost, model id, opaque ids — [LICENSE-CLOUD.md](./LICENSE-CLOUD.md) §5), never prompts or keys |
|
|
110
|
+
|
|
111
|
+
A license key that **is** configured but invalid or expired still fails closed with
|
|
112
|
+
`LicenseError` — only its absence is a working product.
|
|
113
|
+
|
|
114
|
+
### Cloud pricing (the optional, paid part)
|
|
115
|
+
|
|
116
|
+
The cloud subscription is a fixed fee, paid whether or not you save: **Pro
|
|
117
|
+
€15 / seat / month** or **€150 / seat / year**; **Team €129 / month with 10 seats included** (**€12 per extra seat**; **€1,290 / year, €120 per extra seat**), EUR, VAT not included. The first month carries no commission, and the
|
|
118
|
+
commission on measured savings runs from **10 % down to 4 %** by volume —
|
|
119
|
+
keeping you **at least 90 % of every euro saved**, never from estimates, and
|
|
120
|
+
shadow-mode usage is never part of any commission. No trial. The free
|
|
121
|
+
**Measure** license (no card) still exists for the cloud: it runs the layer in
|
|
122
|
+
**shadow mode** — nothing is served from any cache, nothing you send is
|
|
123
|
+
changed — and shows what the levers would have saved, request by request. The
|
|
124
|
+
older commission-only modality is **closed to new customers**. Your
|
|
125
|
+
prompts, completions and provider keys never reach BiVelio: a cloud key sends
|
|
126
|
+
only the per-request usage fields listed in [LICENSE-CLOUD.md](./LICENSE-CLOUD.md) §5.
|
|
58
127
|
|
|
59
128
|
## Where it installs
|
|
60
129
|
|
|
@@ -74,25 +143,22 @@ products charge a flat subscription, so there is no per‑token bill to cut. The
|
|
|
74
143
|
story, in plain words:
|
|
75
144
|
**[Where does the saving install?](https://savings.bivelio.com/en/how-it-works)**
|
|
76
145
|
|
|
77
|
-
**A coding agent with an API key?** The package also ships a local
|
|
78
|
-
|
|
79
|
-
`copilot` with your own Anthropic key, `aider`) launches the agent through it and
|
|
80
|
-
|
|
81
|
-
persisted. On exit it tells you how many requests it metered and which
|
|
82
|
-
could not, and why. For Crush, Droid, Cline, Continue or Copilot
|
|
83
|
-
`bvsala doctor` prints the exact config block.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
subscription there is no per-token invoice, so that traffic is metered at
|
|
94
|
-
list-price equivalent and never carries commission; the per-seat license applies
|
|
95
|
-
as for any other use.
|
|
146
|
+
**A coding agent with an API key?** The package also ships a local gateway:
|
|
147
|
+
`bvsala claude` (or `bvsala codex`, `goose`, `qwen`, `opencode`, `kilo`, `gemini`,
|
|
148
|
+
`copilot` with your own Anthropic key, `aider`) launches the agent through it and
|
|
149
|
+
your real spend is metered locally, token by token — nothing to configure,
|
|
150
|
+
nothing persisted. On exit it tells you how many requests it metered and which
|
|
151
|
+
ones it could not, and why. For Crush, Droid, Cline, Continue, Cursor or Copilot
|
|
152
|
+
in VS Code, `bvsala doctor` prints the exact config block. The gateway starts
|
|
153
|
+
with **no keys at all** (COMMUNITY mode: every local saving lever armed, zero
|
|
154
|
+
telemetry); `BIVELIO_SERVICE_KEY` adds the cloud dashboard and org entitlements.
|
|
155
|
+
Once running it never interrupts a request in flight: with a live license that
|
|
156
|
+
lapses, only new requests are refused (HTTP 402) until it is renewed. The
|
|
157
|
+
cache-write TTL lever (1h→5m, the saving that changes the rate, never the
|
|
158
|
+
content the model reads) is armed by default; the other levers (sibling model,
|
|
159
|
+
per-turn effort) switch on with their flags. On a Claude or ChatGPT subscription
|
|
160
|
+
there is no per-token invoice, so that traffic is metered at list-price
|
|
161
|
+
equivalent and never carries commission.
|
|
96
162
|
|
|
97
163
|
**The gateway forwards what your terminal sends.** With no lever armed, your
|
|
98
164
|
provider receives the request body byte for byte and every header your client
|
|
@@ -117,6 +183,8 @@ the language:
|
|
|
117
183
|
```bash
|
|
118
184
|
bvsala gateway --upstream openai --port 8403 # one gateway per upstream
|
|
119
185
|
bvsala gateway # Anthropic, the default, on 8402
|
|
186
|
+
bvsala gateway --upstream deepseek # DeepSeek: /v1/messages → its /anthropic endpoint
|
|
187
|
+
bvsala gateway --upstream gemini # Gemini protocol (bvsala gemini wraps the CLI)
|
|
120
188
|
```
|
|
121
189
|
|
|
122
190
|
Flags and environment variables are in English (`--port`, `--project`, `--force`,
|
|
@@ -142,13 +210,14 @@ claude = Anthropic(base_url="http://127.0.0.1:8402") # NO /v1
|
|
|
142
210
|
```
|
|
143
211
|
|
|
144
212
|
With `BIVELIO_SERVICE_KEY` set, the gateway measures every request whose usage the
|
|
145
|
-
provider reports — Anthropic Messages, OpenAI Chat Completions and Responses,
|
|
146
|
-
|
|
213
|
+
provider reports — Anthropic Messages, OpenAI Chat Completions and Responses, and
|
|
214
|
+
the Gemini protocol (`:generateContent` / `:streamGenerateContent`), streamed or
|
|
215
|
+
not — for models with a list price in its catalog, priced at that list price. A
|
|
147
216
|
request it cannot meter (a model without a list price, a body it cannot read, a stream
|
|
148
|
-
that never asked for usage) is still forwarded, and its log line says so and why.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
`bvsala doctor` prints the recipe for each SDK.
|
|
217
|
+
that never asked for usage) is still forwarded, and its log line says so and why.
|
|
218
|
+
Without a key (COMMUNITY mode) the metering numbers stay local: the local report
|
|
219
|
+
routes below work the same, and nothing is sent to BiVelio.
|
|
220
|
+
`bvsala doctor` prints the recipe for each SDK and agent.
|
|
152
221
|
|
|
153
222
|
Each request can also bring its report back to your code. Send the header
|
|
154
223
|
`x-bvsala-report: 1` (in Python, `default_headers={"x-bvsala-report": "1"}` on
|
|
@@ -163,12 +232,11 @@ provider, and a request without it is not changed at all:
|
|
|
163
232
|
|
|
164
233
|
## Check it before you install
|
|
165
234
|
|
|
166
|
-
What you can check before
|
|
167
|
-
trial by trial, with the raw CSV linked, at
|
|
168
|
-
**[savings.bivelio.com/en/how-it-works](https://savings.bivelio.com/en/how-it-works)
|
|
169
|
-
|
|
170
|
-
traffic and
|
|
171
|
-
lives in your dashboard once you sign in.
|
|
235
|
+
What you can check before installing is what we have measured: the published
|
|
236
|
+
benchmark, trial by trial, with the raw CSV linked, at
|
|
237
|
+
**[savings.bivelio.com/en/how-it-works](https://savings.bivelio.com/en/how-it-works)**.
|
|
238
|
+
With no keys at all, the layer runs the full savings stack locally on your own
|
|
239
|
+
traffic and reports per request — nothing leaves your machine.
|
|
172
240
|
|
|
173
241
|
**Upgrading from 0.4?** Every breaking change of 0.5, with a before and after for
|
|
174
242
|
each, is in [Migrating from 0.4 to 0.5](https://savings.bivelio.com/en/migrate-to-0.5).
|
|
@@ -204,13 +272,13 @@ to a built‑in estimate and the reconstructed savings are less accurate. Either
|
|
|
204
272
|
saving where the layer removed something is reported as `estimated`, and estimated
|
|
205
273
|
savings are never billed — the SDK warns once when the tokenizer is missing.
|
|
206
274
|
|
|
207
|
-
**2 · The
|
|
208
|
-
|
|
209
|
-
|
|
275
|
+
**2 · The cloud (optional)** — a [BiVelio key](https://savings.bivelio.com/dashboard/install)
|
|
276
|
+
adds the team dashboard, shared caches and license management. Skip this step and
|
|
277
|
+
nothing changes except the dashboard: the layer keeps working, fully, offline.
|
|
210
278
|
|
|
211
279
|
```bash
|
|
212
|
-
export BIVELIO_LICENSE_KEY=… #
|
|
213
|
-
export BIVELIO_SERVICE_KEY=… #
|
|
280
|
+
export BIVELIO_LICENSE_KEY=… # optional: a key that IS set but invalid still fails closed
|
|
281
|
+
export BIVELIO_SERVICE_KEY=… # optional: cloud metering + license auto-renewal
|
|
214
282
|
```
|
|
215
283
|
|
|
216
284
|
**3 · Wrap the call** — this is the entire code change. The package is ESM only:
|
|
@@ -232,9 +300,11 @@ const provider = new OpenAICompatibleProvider({
|
|
|
232
300
|
|
|
233
301
|
const layer = createSavingsLayer({
|
|
234
302
|
provider,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
303
|
+
// OPEN CORE: nothing else is required — COMMUNITY mode, zero telemetry.
|
|
304
|
+
// For the cloud dashboard + team metering, add (all optional):
|
|
305
|
+
// // licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
|
|
306
|
+
// // licenseServerUrl: "https://savings.bivelio.com",
|
|
307
|
+
// serviceAccountKey: process.env.BIVELIO_SERVICE_KEY,
|
|
238
308
|
});
|
|
239
309
|
|
|
240
310
|
const { text, report } = await layer.generate({
|
|
@@ -372,7 +442,7 @@ point, from a subpath of this same package. The four adapters behave alike:
|
|
|
372
442
|
- **Tools are treated as having side effects** unless you list them in
|
|
373
443
|
`readOnlyTools`: a request that offers any other tool is never answered from a
|
|
374
444
|
cache, because the action has to run.
|
|
375
|
-
- **
|
|
445
|
+
- **Open core, like the SDK**: with no key the adapter runs in COMMUNITY mode; a key that is set but invalid fails closed with
|
|
376
446
|
`LicenseError` (a framework may wrap it, keeping its name and message) and the
|
|
377
447
|
model is not called. **Put the adapter last**, so nothing sits between it and the
|
|
378
448
|
model.
|
|
@@ -392,8 +462,8 @@ import { openai } from "@ai-sdk/openai";
|
|
|
392
462
|
import { savingsMiddleware, getSavingsReport } from "@bivelio/savings-layer/ai-sdk";
|
|
393
463
|
|
|
394
464
|
const savings = savingsMiddleware({
|
|
395
|
-
licenseKey: process.env.BIVELIO_LICENSE_KEY,
|
|
396
|
-
licenseServerUrl: "https://savings.bivelio.com",
|
|
465
|
+
// licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
|
|
466
|
+
// licenseServerUrl: "https://savings.bivelio.com",
|
|
397
467
|
serviceAccountKey: process.env.BIVELIO_SERVICE_KEY,
|
|
398
468
|
readOnlyTools: ["get_weather"],
|
|
399
469
|
});
|
|
@@ -419,8 +489,8 @@ import { Agent } from "@mastra/core/agent";
|
|
|
419
489
|
import { savingsProcessor } from "@bivelio/savings-layer/mastra";
|
|
420
490
|
|
|
421
491
|
const bivelio = savingsProcessor({
|
|
422
|
-
licenseKey: process.env.BIVELIO_LICENSE_KEY,
|
|
423
|
-
licenseServerUrl: "https://savings.bivelio.com",
|
|
492
|
+
// licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
|
|
493
|
+
// licenseServerUrl: "https://savings.bivelio.com",
|
|
424
494
|
serviceAccountKey: process.env.BIVELIO_SERVICE_KEY,
|
|
425
495
|
onReport: (r) => console.log(r.cost.basis),
|
|
426
496
|
});
|
|
@@ -451,8 +521,8 @@ import { createAgent } from "langchain";
|
|
|
451
521
|
import { savingsAgentMiddleware, getSavingsReportFromMessage } from "@bivelio/savings-layer/langchain";
|
|
452
522
|
|
|
453
523
|
const agentSavings = savingsAgentMiddleware({
|
|
454
|
-
licenseKey: process.env.BIVELIO_LICENSE_KEY,
|
|
455
|
-
licenseServerUrl: "https://savings.bivelio.com",
|
|
524
|
+
// licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
|
|
525
|
+
// licenseServerUrl: "https://savings.bivelio.com",
|
|
456
526
|
serviceAccountKey: process.env.BIVELIO_SERVICE_KEY,
|
|
457
527
|
});
|
|
458
528
|
const lcAgent = createAgent({ model: "openai:gpt-4o-mini", tools: [], middleware: [agentSavings] }); // last
|
|
@@ -474,8 +544,8 @@ import Anthropic from "@anthropic-ai/sdk";
|
|
|
474
544
|
import { anthropicSavings } from "@bivelio/savings-layer/anthropic-sdk";
|
|
475
545
|
|
|
476
546
|
const claudeSavings = anthropicSavings({
|
|
477
|
-
licenseKey: process.env.BIVELIO_LICENSE_KEY,
|
|
478
|
-
licenseServerUrl: "https://savings.bivelio.com",
|
|
547
|
+
// licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
|
|
548
|
+
// licenseServerUrl: "https://savings.bivelio.com",
|
|
479
549
|
serviceAccountKey: process.env.BIVELIO_SERVICE_KEY,
|
|
480
550
|
});
|
|
481
551
|
const client = new Anthropic({ middleware: [claudeSavings.middleware] }); // last in the list
|
|
@@ -578,18 +648,20 @@ single-shot requests only: requests that declare tools, carry tool calls or resu
|
|
|
578
648
|
hold more than one user turn are left out unless you set
|
|
579
649
|
`savings.semanticCache: "include-agentic"`.
|
|
580
650
|
|
|
581
|
-
**Calibrated reuse (
|
|
582
|
-
every embedder and every workload, so
|
|
583
|
-
traffic
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
and
|
|
591
|
-
|
|
592
|
-
|
|
651
|
+
**Calibrated reuse (required to serve).** A fixed similarity floor means something different
|
|
652
|
+
for every embedder and every workload, so the cache must earn its floor on your own
|
|
653
|
+
traffic: `new InMemorySemanticCache({ embed, calibration: { maxErrorRate: 0.02 } })`.
|
|
654
|
+
Without `calibration`, it runs in shadow mode: it may store answers for future calibration,
|
|
655
|
+
but `find()` and `lookup()` never return a hit. With calibration it serves no reworded answer
|
|
656
|
+
until your labels show, at 95 % confidence, that at most 2 % of the answers it would reuse
|
|
657
|
+
from some score up are wrong, and it re-tests that floor as labels accumulate. Until then
|
|
658
|
+
every candidate goes to your provider, exactly as it would without the cache, and the fresh
|
|
659
|
+
answer is compared with the stored one by your `judge` (by default only identical answers
|
|
660
|
+
count, which suits classification and structured output; pass your own for free text). Each
|
|
661
|
+
calibrated reuse carries its bound and the labels behind it in
|
|
662
|
+
`report.qualityLedger.cacheMatch.calibration`. Calibration costs reuses, never an extra call,
|
|
663
|
+
and it does not change how anything is billed. If you already have labeled pairs,
|
|
664
|
+
`calibrateSemanticFloor()` runs the same test offline.
|
|
593
665
|
|
|
594
666
|
And a promise you can hold it to: **never worse than your JSON.** Payload encodings
|
|
595
667
|
are chosen by counting real tokens with the destination model's own tokenizer, and
|
|
@@ -606,8 +678,8 @@ your provider, turn on verification sampling:
|
|
|
606
678
|
```ts
|
|
607
679
|
const checkedLayer = createSavingsLayer({
|
|
608
680
|
provider,
|
|
609
|
-
licenseKey: process.env.BIVELIO_LICENSE_KEY,
|
|
610
|
-
licenseServerUrl: "https://savings.bivelio.com",
|
|
681
|
+
// licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
|
|
682
|
+
// licenseServerUrl: "https://savings.bivelio.com",
|
|
611
683
|
serviceAccountKey: process.env.BIVELIO_SERVICE_KEY,
|
|
612
684
|
verification: { sampleRate: 0.02 }, // about 2 in every 100 eligible requests
|
|
613
685
|
});
|
|
@@ -640,9 +712,11 @@ and every pair as a CSV download.
|
|
|
640
712
|
- **Only counts leave your infrastructure.** The copy goes to your own provider, like
|
|
641
713
|
the original; BiVelio receives token counts, costs, model ids and outcomes — never
|
|
642
714
|
prompts or answers.
|
|
643
|
-
- **`close()` settles every copy still running
|
|
644
|
-
|
|
645
|
-
|
|
715
|
+
- **`await close()` settles every copy still running and drains usage:** an
|
|
716
|
+
unfinished copy is recorded as abandoned, never as a pair. Awaiting close makes
|
|
717
|
+
a bounded final delivery attempt; calling it without await remains safe. On
|
|
718
|
+
serverless platforms, pass your runtime's `waitUntil` as `verification.waitUntil`
|
|
719
|
+
so checks can finish.
|
|
646
720
|
|
|
647
721
|
</details>
|
|
648
722
|
|
|
@@ -686,7 +760,7 @@ default was not run.
|
|
|
686
760
|
```ts
|
|
687
761
|
const leveredLayer = createSavingsLayer({
|
|
688
762
|
provider,
|
|
689
|
-
licenseKey: process.env.BIVELIO_LICENSE_KEY,
|
|
763
|
+
// licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
|
|
690
764
|
priceLevers: { deferrable: true, onCapacityError: "standard" }, // layer-wide defaults
|
|
691
765
|
});
|
|
692
766
|
|
|
@@ -757,7 +831,7 @@ verbatim, so an agent that edits by exact match still sees the file as it is.
|
|
|
757
831
|
```ts
|
|
758
832
|
const agentLayer = createSavingsLayer({
|
|
759
833
|
provider,
|
|
760
|
-
licenseKey: process.env.BIVELIO_LICENSE_KEY,
|
|
834
|
+
// licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
|
|
761
835
|
toolOutputCompaction: true, // or { exceptTools: ["read_logs"], minSavedChars: 512 }
|
|
762
836
|
});
|
|
763
837
|
// or per request: savings: { toolOutputCompaction: true }
|
|
@@ -825,7 +899,7 @@ saving and never part of any commission.
|
|
|
825
899
|
```ts
|
|
826
900
|
const placedLayer = createSavingsLayer({
|
|
827
901
|
provider, // Anthropic, or OpenAI GPT-5.6+ on OpenAI's own API
|
|
828
|
-
licenseKey: process.env.BIVELIO_LICENSE_KEY,
|
|
902
|
+
// licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
|
|
829
903
|
cacheBreakpoints: "auto",
|
|
830
904
|
});
|
|
831
905
|
```
|
|
@@ -860,7 +934,7 @@ async function serverlessLayer(kv: SharedKeyValue) {
|
|
|
860
934
|
|
|
861
935
|
const layer = createSavingsLayer({
|
|
862
936
|
provider,
|
|
863
|
-
licenseKey: process.env.BIVELIO_LICENSE_KEY,
|
|
937
|
+
// licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
|
|
864
938
|
semanticCacheStore: semanticCache,
|
|
865
939
|
// Passing the cache does not switch it on; this asks for it on every request.
|
|
866
940
|
semanticCacheDefault: true,
|
|
@@ -893,13 +967,14 @@ async function serverlessLayer(kv: SharedKeyValue) {
|
|
|
893
967
|
verifier, judge or configuration is refused with `CalibrationStateError`. If you pass
|
|
894
968
|
your own `embed`, `verify` or `judge`, name them (`embedderId`, `verifierId`,
|
|
895
969
|
`calibration.judgeId`) so a state can say what it was recorded with.
|
|
896
|
-
- **One line of history
|
|
897
|
-
|
|
898
|
-
each spend its remaining confidence again, and the last
|
|
899
|
-
others
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
970
|
+
- **One line of history for in-memory snapshots.** Restoring and saving the latest
|
|
971
|
+
state continues one line. Instances that manually continue the same snapshot at
|
|
972
|
+
the same time each spend its remaining confidence again, and the last save
|
|
973
|
+
overwrites the others. Use one long-lived process or offline calibration for that
|
|
974
|
+
manual model. For shared records and one coordinated history across instances,
|
|
975
|
+
use `PgSemanticCache`: its transactions and observation IDs prevent lost labels
|
|
976
|
+
and repeated confidence spending. The in-memory cached answers still live in
|
|
977
|
+
each instance unless you inject a persistent store.
|
|
903
978
|
- **Why a predicted cache read did not happen** (Anthropic's own API). With
|
|
904
979
|
`cacheMissDiagnostics: true`, the layer asks Anthropic to compare each marked request
|
|
905
980
|
with the one whose cache entry it expected to read, and puts the provider's
|
|
@@ -909,6 +984,32 @@ async function serverlessLayer(kv: SharedKeyValue) {
|
|
|
909
984
|
|
|
910
985
|
</details>
|
|
911
986
|
|
|
987
|
+
<details>
|
|
988
|
+
<summary><b>Customer-operated PostgreSQL for caches and learning</b></summary>
|
|
989
|
+
|
|
990
|
+
The optional `@bivelio/persistence-postgres` package provides `PgExactCache`,
|
|
991
|
+
`PgSemanticCache`, `PgCacheBreakpointStore` and `PgConstraintMemoryStore` without
|
|
992
|
+
adding a database driver to the core SDK. You supply the `pg` pool, apply its
|
|
993
|
+
versioned SQL migrations yourself, and inject the stores; nothing connects or
|
|
994
|
+
changes your schema automatically. Exact and semantic entries contain customer
|
|
995
|
+
queries, answers or derived embeddings and need the same access, retention and
|
|
996
|
+
deletion controls as the original data. Breakpoints contain fingerprints,
|
|
997
|
+
timestamps and counters, never prompt text. Constraint learning stores only a
|
|
998
|
+
hash of tenant, scope, provider, served model and contract.
|
|
999
|
+
|
|
1000
|
+
The breakpoint adapter uses revisions and compare-and-set. Semantic calibration
|
|
1001
|
+
uses short transactions and idempotent observation IDs, so concurrent instances
|
|
1002
|
+
continue one history instead of spending the same confidence budget independently.
|
|
1003
|
+
Both use the same deterministic candidate guards and statistical journal as memory.
|
|
1004
|
+
The semantic backend requires the PostgreSQL vector extension and is opt-in; existing key-value stores
|
|
1005
|
+
and in-memory defaults keep their previous behavior. Removing `constraintMemoryStore`
|
|
1006
|
+
rolls constraint learning back to process memory without reading the new table.
|
|
1007
|
+
|
|
1008
|
+
See [`packages/persistence-postgres/README.md`](packages/persistence-postgres/README.md)
|
|
1009
|
+
for setup, ownership, cleanup, benchmark evidence and shutdown.
|
|
1010
|
+
|
|
1011
|
+
</details>
|
|
1012
|
+
|
|
912
1013
|
<details>
|
|
913
1014
|
<summary><b>Structured output — validated <code>data</code>, not just text</b></summary>
|
|
914
1015
|
|
|
@@ -933,10 +1034,11 @@ const { data, text } = await layer.generate<{ answer: number }>({
|
|
|
933
1034
|
console.log(data.answer); // 8
|
|
934
1035
|
```
|
|
935
1036
|
|
|
936
|
-
A question the layer
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1037
|
+
A question the layer can answer on its own (the arithmetic of the first
|
|
1038
|
+
example) is only served without the model when that answer meets your schema.
|
|
1039
|
+
The arithmetic resolver returns `{ value: 8 }`: with a `{ answer: number }`
|
|
1040
|
+
contract it steps aside and the request goes to the model, which answers in
|
|
1041
|
+
your contract's shape.
|
|
940
1042
|
|
|
941
1043
|
</details>
|
|
942
1044
|
|
|
@@ -952,7 +1054,7 @@ import { GoogleProvider } from "@bivelio/savings-layer";
|
|
|
952
1054
|
|
|
953
1055
|
const gemini = createSavingsLayer({
|
|
954
1056
|
provider: new GoogleProvider({ apiKey: process.env.GEMINI_API_KEY, requireUsage: true }),
|
|
955
|
-
licenseKey: process.env.BIVELIO_LICENSE_KEY,
|
|
1057
|
+
// licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
|
|
956
1058
|
});
|
|
957
1059
|
|
|
958
1060
|
const { text: answer, report: geminiReport } = await gemini.generate({
|
|
@@ -1158,7 +1260,7 @@ import { AnthropicProvider } from "@bivelio/savings-layer";
|
|
|
1158
1260
|
|
|
1159
1261
|
const claude = createSavingsLayer({
|
|
1160
1262
|
provider: new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
|
|
1161
|
-
licenseKey: process.env.BIVELIO_LICENSE_KEY,
|
|
1263
|
+
// licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
|
|
1162
1264
|
});
|
|
1163
1265
|
|
|
1164
1266
|
const mcpTools: ToolDescriptor[] = []; // every tool your MCP servers list
|
|
@@ -1358,7 +1460,7 @@ BVSALA_SEAT_ID=worker-03 # stable per replica; [A-Za-z0-9._:/-], max 128 chars
|
|
|
1358
1460
|
BV‑SALA is commercial software. The plans are **Measure** (free), **Pro** (per seat),
|
|
1359
1461
|
**Team** (a number of seats included, plus each extra seat) and **Enterprise** (by
|
|
1360
1462
|
contract); Pro and Team are paid whether or not you save and carry **no commission** on
|
|
1361
|
-
your savings. The prices are at the top of this page, in [LICENSE](./LICENSE) §6 and in
|
|
1463
|
+
your savings. The prices are at the top of this page, in [LICENSE-CLOUD.md](./LICENSE-CLOUD.md) §6 and in
|
|
1362
1464
|
the Published Pricing it refers to,
|
|
1363
1465
|
**[savings.bivelio.com/pricing](https://savings.bivelio.com/pricing)**. No trial. There is one free license,
|
|
1364
1466
|
**Measure** — no card, one organization per verified account, up to 5 seats and 20,000
|
|
@@ -1382,7 +1484,7 @@ The license is a signed token your SDK and the
|
|
|
1382
1484
|
gateway verify **offline**; the managed control plane and the revocation kill‑switch
|
|
1383
1485
|
are served from BiVelio. Your LLM traffic goes **straight to your provider** —
|
|
1384
1486
|
BiVelio receives per‑request usage (counts, cost, the model id and opaque ids), never
|
|
1385
|
-
prompts, completions or provider keys; [LICENSE](./LICENSE) §5 lists every field, and
|
|
1487
|
+
prompts, completions or provider keys; [LICENSE-CLOUD.md](./LICENSE-CLOUD.md) §5 lists every field, and
|
|
1386
1488
|
**[savings.bivelio.com/privacy](https://savings.bivelio.com/privacy)** says how long
|
|
1387
1489
|
each one is kept.
|
|
1388
1490
|
|