@bivelio/savings-layer 0.5.2 → 1.1.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 CHANGED
@@ -11,50 +11,124 @@
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-commercial-0F1218" alt="Commercial 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
- ### In plain words
18
-
19
- Every request your app sends to an AI is paid for by the text going in and out.
20
- **Savings** sits on that path and lets through only what's needed — skipping calls
21
- that don't have to happen, and trimming what does — so you pay for less. It is not a
22
- promise you take on faith: **every saving we report is measured with your provider's
23
- own usage counter, the one your bill is built from** (for a repeat served without
24
- calling the provider, the counter of the identical call that ran), and every saving —
25
- measured or estimated, and labelled as such — is handed back to you request by request.
26
-
27
- **Measure for free, then pay per seat — with no commission on your savings.** The
28
- free **Measure** license (no card) runs the layer in shadow mode on your own traffic and
29
- shows what it would have saved, valued with your provider's counter. To turn the levers
30
- on, **Pro** is **€15 / seat / month** or **€150 / seat / year**, and **Team** is **€129 / month
31
- with 10 seats included** (€12 per extra seat; €1,290 / year, €120 per extra seat), EUR,
32
- VAT not included — a fixed fee, paid whether or not you save, and no commission on what
33
- you save. Enterprise (more seats, your own contract or invoicing terms) is by contract.
34
- And your prompts and completions **never reach BiVelio**: what BiVelio receives is per‑request
35
- usage — token counts, cost, the model id and opaque ids — never your content
36
- ([LICENSE](./LICENSE) §5 lists every field; [savings.bivelio.com/privacy](https://savings.bivelio.com/privacy)
37
- explains each one and how long it is kept).
38
-
39
- ### For engineers, in one line
40
-
41
- `@bivelio/savings-layer` (**BV‑SALA**) is one layer you add inside your code, right
42
- where your app calls the OpenAI‑compatible or Anthropic API. For every request it
43
- picks the cheapest execution that still meets your quality bar and hands you an
44
- auditable `SavingsReport` — the same numbers your dashboard reads. Traffic still
45
- goes straight from your app to your provider; the SDK is never a middleman server.
46
-
47
- > **Commercial software — © 2026 BiVelio Inc.** Free to install, **paid to save**:
48
- > BV‑SALA is fail‑closed and executes only with an active BiVelio license key. The
49
- > free **Measure** license (no card) runs it in shadow mode: nothing you send is
50
- > changed, nothing is served from cache, and you see what it would have saved.
51
- > The license signature is verified **offline**; with `licenseServerUrl` set, the SDK
52
- > also calls `savings.bivelio.com` for the revocation list, license renewal and usage
53
- > metering. **Your prompts, completions and provider keys never reach BiVelio** — what
54
- > travels is per‑request usage: token counts, cost, the model id, a request id and
55
- > opaque seat and session ids (the exact list is in [LICENSE](./LICENSE) §5). Use is
56
- > governed by the LICENSE, which also sets out the fees, and the
57
- > BiVelio Terms. Not a wrapper around any third‑party library.
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
+ 📄 **The paper:** [Verifiable Measurement of Cost Savings in LLM Applications
101
+ (open-core edition, v2)](./docs/PAPER.md) — live double-runs, percentile cluster
102
+ bootstrap, four ledgers, and every limitation stated. Also at
103
+ **[savings.bivelio.com/paper](https://savings.bivelio.com/paper)**.
104
+
105
+ ### Community vs Cloud
106
+
107
+ | | Community (default, no keys) | Cloud (optional `BIVELIO_*` keys) |
108
+ | --- | --- | --- |
109
+ | Exact + semantic cache, deduplication of identical requests | ✅ | ✅ |
110
+ | Model cascade, sibling routing, prompt-cache placement | ✅ | ✅ |
111
+ | Compression, output contracts, four savings ledgers | ✅ | ✅ |
112
+ | Gateway + `bvsala` CLI + MCP server | ✅ | ✅ |
113
+ | Team dashboard, seat metering, shared caches | — | ✅ |
114
+ | 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 |
115
+
116
+ A license key that **is** configured but invalid or expired still fails closed with
117
+ `LicenseError` — only its absence is a working product.
118
+
119
+ ### Cloud pricing (the optional, paid part)
120
+
121
+ The cloud subscription is a fixed fee, paid whether or not you save: **Pro
122
+ €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
123
+ commission on measured savings runs from **10 % down to 4 %** by volume —
124
+ keeping you **at least 90 % of every euro saved**, never from estimates, and
125
+ shadow-mode usage is never part of any commission. No trial. The free
126
+ **Measure** license (no card) still exists for the cloud: it runs the layer in
127
+ **shadow mode** — nothing is served from any cache, nothing you send is
128
+ changed — and shows what the levers would have saved, request by request. The
129
+ older commission-only modality is **closed to new customers**. Your
130
+ prompts, completions and provider keys never reach BiVelio: a cloud key sends
131
+ only the per-request usage fields listed in [LICENSE-CLOUD.md](./LICENSE-CLOUD.md) §5.
58
132
 
59
133
  ## Where it installs
60
134
 
@@ -74,25 +148,22 @@ products charge a flat subscription, so there is no per‑token bill to cut. The
74
148
  story, in plain words:
75
149
  **[Where does the saving install?](https://savings.bivelio.com/en/how-it-works)**
76
150
 
77
- **A coding agent with an API key?** The package also ships a local measuring
78
- gateway: `bvsala claude` (or `bvsala codex`, `goose`, `qwen`, `opencode`, `kilo`,
79
- `copilot` with your own Anthropic key, `aider`) launches the agent through it and your real
80
- spend shows up in your dashboard, token by token — nothing to configure, nothing
81
- persisted. On exit it tells you how many requests it metered and which ones it
82
- could not, and why. For Crush, Droid, Cline, Continue or Copilot in VS Code,
83
- `bvsala doctor` prints the exact config block. Gemini CLI and Cursor cannot be
84
- metered today, and the doctor says so. Like the SDK, the gateway is
85
- fail‑closed: it starts only with your service key (`BIVELIO_SERVICE_KEY`) and an
86
- active BiVelio license, which it collects with that key and verifies against the
87
- key embedded in the package. Once running it never interrupts a request in flight:
88
- if the license lapses, only new requests are refused (HTTP 402) until it is
89
- renewed. It measures every request. With a PRO license it also applies one saving
90
- by default: it shortens the cache-write TTL while your session stays hot, which
91
- changes the price, never the content the model reads. The other levers (sibling
92
- model, per-turn effort) stay off until you switch them on. On a Claude or ChatGPT
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.
151
+ **A coding agent with an API key?** The package also ships a local gateway:
152
+ `bvsala claude` (or `bvsala codex`, `goose`, `qwen`, `opencode`, `kilo`, `gemini`,
153
+ `copilot` with your own Anthropic key, `aider`) launches the agent through it and
154
+ your real spend is metered locally, token by token — nothing to configure,
155
+ nothing persisted. On exit it tells you how many requests it metered and which
156
+ ones it could not, and why. For Crush, Droid, Cline, Continue, Cursor or Copilot
157
+ in VS Code, `bvsala doctor` prints the exact config block. The gateway starts
158
+ with **no keys at all** (COMMUNITY mode: every local saving lever armed, zero
159
+ telemetry); `BIVELIO_SERVICE_KEY` adds the cloud dashboard and org entitlements.
160
+ Once running it never interrupts a request in flight: with a live license that
161
+ lapses, only new requests are refused (HTTP 402) until it is renewed. The
162
+ cache-write TTL lever (1h→5m, the saving that changes the rate, never the
163
+ content the model reads) is armed by default; the other levers (sibling model,
164
+ per-turn effort) switch on with their flags. On a Claude or ChatGPT subscription
165
+ there is no per-token invoice, so that traffic is metered at list-price
166
+ equivalent and never carries commission.
96
167
 
97
168
  **The gateway forwards what your terminal sends.** With no lever armed, your
98
169
  provider receives the request body byte for byte and every header your client
@@ -117,6 +188,8 @@ the language:
117
188
  ```bash
118
189
  bvsala gateway --upstream openai --port 8403 # one gateway per upstream
119
190
  bvsala gateway # Anthropic, the default, on 8402
191
+ bvsala gateway --upstream deepseek # DeepSeek: /v1/messages → its /anthropic endpoint
192
+ bvsala gateway --upstream gemini # Gemini protocol (bvsala gemini wraps the CLI)
120
193
  ```
121
194
 
122
195
  Flags and environment variables are in English (`--port`, `--project`, `--force`,
@@ -142,13 +215,14 @@ claude = Anthropic(base_url="http://127.0.0.1:8402") # NO /v1
142
215
  ```
143
216
 
144
217
  With `BIVELIO_SERVICE_KEY` set, the gateway measures every request whose usage the
145
- provider reports — Anthropic Messages, OpenAI Chat Completions and Responses, streamed
146
- or not — for models with a list price in its catalog, priced at that list price. A
218
+ provider reports — Anthropic Messages, OpenAI Chat Completions and Responses, and
219
+ the Gemini protocol (`:generateContent` / `:streamGenerateContent`), streamed or
220
+ not — for models with a list price in its catalog, priced at that list price. A
147
221
  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. That
149
- is measurement, not savings: the saving levers of the SDK are TypeScript, and the
150
- gateway's own levers apply to Anthropic `/v1/messages` traffic with a PRO license.
151
- `bvsala doctor` prints the recipe for each SDK.
222
+ that never asked for usage) is still forwarded, and its log line says so and why.
223
+ Without a key (COMMUNITY mode) the metering numbers stay local: the local report
224
+ routes below work the same, and nothing is sent to BiVelio.
225
+ `bvsala doctor` prints the recipe for each SDK and agent.
152
226
 
153
227
  Each request can also bring its report back to your code. Send the header
154
228
  `x-bvsala-report: 1` (in Python, `default_headers={"x-bvsala-report": "1"}` on
@@ -163,12 +237,11 @@ provider, and a request without it is not changed at all:
163
237
 
164
238
  ## Check it before you install
165
239
 
166
- What you can check before paying is what we have measured: the published benchmark,
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
- and the free **Measure** license, which runs the layer in shadow mode on your own
170
- traffic and shows what it would have saved, valued with your provider's counter. The verifier that replays the optimizer on your own data
171
- lives in your dashboard once you sign in.
240
+ What you can check before installing is what we have measured: the published
241
+ benchmark, trial by trial, with the raw CSV linked, at
242
+ **[savings.bivelio.com/en/how-it-works](https://savings.bivelio.com/en/how-it-works)**.
243
+ With no keys at all, the layer runs the full savings stack locally on your own
244
+ traffic and reports per request — nothing leaves your machine.
172
245
 
173
246
  **Upgrading from 0.4?** Every breaking change of 0.5, with a before and after for
174
247
  each, is in [Migrating from 0.4 to 0.5](https://savings.bivelio.com/en/migrate-to-0.5).
@@ -204,13 +277,13 @@ to a built‑in estimate and the reconstructed savings are less accurate. Either
204
277
  saving where the layer removed something is reported as `estimated`, and estimated
205
278
  savings are never billed — the SDK warns once when the tokenizer is missing.
206
279
 
207
- **2 · The license** — from your [dashboard](https://savings.bivelio.com/dashboard/install)
208
- (free **Measure** license, no card; **Pro** €15 / seat / month or €150 / seat / year;
209
- **Team** €129 / month with 10 seats included — EUR)
280
+ **2 · The cloud (optional)** — a [BiVelio key](https://savings.bivelio.com/dashboard/install)
281
+ adds the team dashboard, shared caches and license management. Skip this step and
282
+ nothing changes except the dashboard: the layer keeps working, fully, offline.
210
283
 
211
284
  ```bash
212
- export BIVELIO_LICENSE_KEY=… # your license key
213
- export BIVELIO_SERVICE_KEY=… # service-account key: auto-renewal + seat metering (required by the gateway)
285
+ export BIVELIO_LICENSE_KEY=… # optional: a key that IS set but invalid still fails closed
286
+ export BIVELIO_SERVICE_KEY=… # optional: cloud metering + license auto-renewal
214
287
  ```
215
288
 
216
289
  **3 · Wrap the call** — this is the entire code change. The package is ESM only:
@@ -232,9 +305,11 @@ const provider = new OpenAICompatibleProvider({
232
305
 
233
306
  const layer = createSavingsLayer({
234
307
  provider,
235
- licenseKey: process.env.BIVELIO_LICENSE_KEY, // required — from your savings.bivelio.com dashboard
236
- licenseServerUrl: "https://savings.bivelio.com", // revocation list, renewal + metering — omit it and the two lines below do nothing
237
- serviceAccountKey: process.env.BIVELIO_SERVICE_KEY, // seat metering + AUTO-RENEWAL pickup
308
+ // OPEN CORE: nothing else is required — COMMUNITY mode, zero telemetry.
309
+ // For the cloud dashboard + team metering, add (all optional):
310
+ // // licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
311
+ // // licenseServerUrl: "https://savings.bivelio.com",
312
+ // serviceAccountKey: process.env.BIVELIO_SERVICE_KEY,
238
313
  });
239
314
 
240
315
  const { text, report } = await layer.generate({
@@ -372,7 +447,7 @@ point, from a subpath of this same package. The four adapters behave alike:
372
447
  - **Tools are treated as having side effects** unless you list them in
373
448
  `readOnlyTools`: a request that offers any other tool is never answered from a
374
449
  cache, because the action has to run.
375
- - **Fail‑closed, like the SDK**: without an active license the call fails with
450
+ - **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
451
  `LicenseError` (a framework may wrap it, keeping its name and message) and the
377
452
  model is not called. **Put the adapter last**, so nothing sits between it and the
378
453
  model.
@@ -392,8 +467,8 @@ import { openai } from "@ai-sdk/openai";
392
467
  import { savingsMiddleware, getSavingsReport } from "@bivelio/savings-layer/ai-sdk";
393
468
 
394
469
  const savings = savingsMiddleware({
395
- licenseKey: process.env.BIVELIO_LICENSE_KEY,
396
- licenseServerUrl: "https://savings.bivelio.com",
470
+ // licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
471
+ // licenseServerUrl: "https://savings.bivelio.com",
397
472
  serviceAccountKey: process.env.BIVELIO_SERVICE_KEY,
398
473
  readOnlyTools: ["get_weather"],
399
474
  });
@@ -419,8 +494,8 @@ import { Agent } from "@mastra/core/agent";
419
494
  import { savingsProcessor } from "@bivelio/savings-layer/mastra";
420
495
 
421
496
  const bivelio = savingsProcessor({
422
- licenseKey: process.env.BIVELIO_LICENSE_KEY,
423
- licenseServerUrl: "https://savings.bivelio.com",
497
+ // licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
498
+ // licenseServerUrl: "https://savings.bivelio.com",
424
499
  serviceAccountKey: process.env.BIVELIO_SERVICE_KEY,
425
500
  onReport: (r) => console.log(r.cost.basis),
426
501
  });
@@ -451,8 +526,8 @@ import { createAgent } from "langchain";
451
526
  import { savingsAgentMiddleware, getSavingsReportFromMessage } from "@bivelio/savings-layer/langchain";
452
527
 
453
528
  const agentSavings = savingsAgentMiddleware({
454
- licenseKey: process.env.BIVELIO_LICENSE_KEY,
455
- licenseServerUrl: "https://savings.bivelio.com",
529
+ // licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
530
+ // licenseServerUrl: "https://savings.bivelio.com",
456
531
  serviceAccountKey: process.env.BIVELIO_SERVICE_KEY,
457
532
  });
458
533
  const lcAgent = createAgent({ model: "openai:gpt-4o-mini", tools: [], middleware: [agentSavings] }); // last
@@ -474,8 +549,8 @@ import Anthropic from "@anthropic-ai/sdk";
474
549
  import { anthropicSavings } from "@bivelio/savings-layer/anthropic-sdk";
475
550
 
476
551
  const claudeSavings = anthropicSavings({
477
- licenseKey: process.env.BIVELIO_LICENSE_KEY,
478
- licenseServerUrl: "https://savings.bivelio.com",
552
+ // licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
553
+ // licenseServerUrl: "https://savings.bivelio.com",
479
554
  serviceAccountKey: process.env.BIVELIO_SERVICE_KEY,
480
555
  });
481
556
  const client = new Anthropic({ middleware: [claudeSavings.middleware] }); // last in the list
@@ -578,18 +653,20 @@ single-shot requests only: requests that declare tools, carry tool calls or resu
578
653
  hold more than one user turn are left out unless you set
579
654
  `savings.semanticCache: "include-agentic"`.
580
655
 
581
- **Calibrated reuse (opt-in).** A fixed similarity floor means something different for
582
- every embedder and every workload, so you can make the cache earn its floor on your own
583
- traffic instead: `new InMemorySemanticCache({ embed, calibration: { maxErrorRate: 0.02 } })`.
584
- It then serves no reworded answer until your labels show, at 95 % confidence, that at
585
- most 2 % of the answers it would reuse from some score up are wrong, and it re-tests that
586
- floor as labels accumulate. Until then every candidate goes to your provider, exactly as
587
- it would without the cache, and the fresh answer is compared with the stored one by your
588
- `judge` (by default only identical answers count, which suits classification and
589
- structured output; pass your own for free text). Each calibrated reuse carries its bound
590
- and the labels behind it in `report.qualityLedger.cacheMatch.calibration`. Calibration
591
- costs reuses, never an extra call, and it does not change how anything is billed. If you
592
- already have labeled pairs, `calibrateSemanticFloor()` runs the same test offline.
656
+ **Calibrated reuse (required to serve).** A fixed similarity floor means something different
657
+ for every embedder and every workload, so the cache must earn its floor on your own
658
+ traffic: `new InMemorySemanticCache({ embed, calibration: { maxErrorRate: 0.02 } })`.
659
+ Without `calibration`, it runs in shadow mode: it may store answers for future calibration,
660
+ but `find()` and `lookup()` never return a hit. With calibration it serves no reworded answer
661
+ until your labels show, at 95 % confidence, that at most 2 % of the answers it would reuse
662
+ from some score up are wrong, and it re-tests that floor as labels accumulate. Until then
663
+ every candidate goes to your provider, exactly as it would without the cache, and the fresh
664
+ answer is compared with the stored one by your `judge` (by default only identical answers
665
+ count, which suits classification and structured output; pass your own for free text). Each
666
+ calibrated reuse carries its bound and the labels behind it in
667
+ `report.qualityLedger.cacheMatch.calibration`. Calibration costs reuses, never an extra call,
668
+ and it does not change how anything is billed. If you already have labeled pairs,
669
+ `calibrateSemanticFloor()` runs the same test offline.
593
670
 
594
671
  And a promise you can hold it to: **never worse than your JSON.** Payload encodings
595
672
  are chosen by counting real tokens with the destination model's own tokenizer, and
@@ -606,8 +683,8 @@ your provider, turn on verification sampling:
606
683
  ```ts
607
684
  const checkedLayer = createSavingsLayer({
608
685
  provider,
609
- licenseKey: process.env.BIVELIO_LICENSE_KEY,
610
- licenseServerUrl: "https://savings.bivelio.com",
686
+ // licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
687
+ // licenseServerUrl: "https://savings.bivelio.com",
611
688
  serviceAccountKey: process.env.BIVELIO_SERVICE_KEY,
612
689
  verification: { sampleRate: 0.02 }, // about 2 in every 100 eligible requests
613
690
  });
@@ -640,9 +717,11 @@ and every pair as a CSV download.
640
717
  - **Only counts leave your infrastructure.** The copy goes to your own provider, like
641
718
  the original; BiVelio receives token counts, costs, model ids and outcomes — never
642
719
  prompts or answers.
643
- - **`close()` settles every copy still running:** it is recorded as abandoned, never
644
- as a pair. On serverless platforms, pass your runtime's `waitUntil` as
645
- `verification.waitUntil` so checks can finish.
720
+ - **`await close()` settles every copy still running and drains usage:** an
721
+ unfinished copy is recorded as abandoned, never as a pair. Awaiting close makes
722
+ a bounded final delivery attempt; calling it without await remains safe. On
723
+ serverless platforms, pass your runtime's `waitUntil` as `verification.waitUntil`
724
+ so checks can finish.
646
725
 
647
726
  </details>
648
727
 
@@ -686,7 +765,7 @@ default was not run.
686
765
  ```ts
687
766
  const leveredLayer = createSavingsLayer({
688
767
  provider,
689
- licenseKey: process.env.BIVELIO_LICENSE_KEY,
768
+ // licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
690
769
  priceLevers: { deferrable: true, onCapacityError: "standard" }, // layer-wide defaults
691
770
  });
692
771
 
@@ -757,7 +836,7 @@ verbatim, so an agent that edits by exact match still sees the file as it is.
757
836
  ```ts
758
837
  const agentLayer = createSavingsLayer({
759
838
  provider,
760
- licenseKey: process.env.BIVELIO_LICENSE_KEY,
839
+ // licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
761
840
  toolOutputCompaction: true, // or { exceptTools: ["read_logs"], minSavedChars: 512 }
762
841
  });
763
842
  // or per request: savings: { toolOutputCompaction: true }
@@ -825,7 +904,7 @@ saving and never part of any commission.
825
904
  ```ts
826
905
  const placedLayer = createSavingsLayer({
827
906
  provider, // Anthropic, or OpenAI GPT-5.6+ on OpenAI's own API
828
- licenseKey: process.env.BIVELIO_LICENSE_KEY,
907
+ // licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
829
908
  cacheBreakpoints: "auto",
830
909
  });
831
910
  ```
@@ -860,7 +939,7 @@ async function serverlessLayer(kv: SharedKeyValue) {
860
939
 
861
940
  const layer = createSavingsLayer({
862
941
  provider,
863
- licenseKey: process.env.BIVELIO_LICENSE_KEY,
942
+ // licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
864
943
  semanticCacheStore: semanticCache,
865
944
  // Passing the cache does not switch it on; this asks for it on every request.
866
945
  semanticCacheDefault: true,
@@ -893,13 +972,14 @@ async function serverlessLayer(kv: SharedKeyValue) {
893
972
  verifier, judge or configuration is refused with `CalibrationStateError`. If you pass
894
973
  your own `embed`, `verify` or `judge`, name them (`embedderId`, `verifierId`,
895
974
  `calibration.judgeId`) so a state can say what it was recorded with.
896
- - **One line of history.** The bound holds along one line: restore the latest state and
897
- save from where it went. Instances that continue the same state at the same time
898
- each spend its remaining confidence again, and the last one to save overwrites the
899
- others' labels. For one bound across many concurrent instances, calibrate in a single
900
- long-lived process, or offline with `calibrateSemanticFloor()`, and give every
901
- instance the certified floor as `acceptThreshold`. The cached answers themselves
902
- still live in each instance's memory.
975
+ - **One line of history for in-memory snapshots.** Restoring and saving the latest
976
+ state continues one line. Instances that manually continue the same snapshot at
977
+ the same time each spend its remaining confidence again, and the last save
978
+ overwrites the others. Use one long-lived process or offline calibration for that
979
+ manual model. For shared records and one coordinated history across instances,
980
+ use `PgSemanticCache`: its transactions and observation IDs prevent lost labels
981
+ and repeated confidence spending. The in-memory cached answers still live in
982
+ each instance unless you inject a persistent store.
903
983
  - **Why a predicted cache read did not happen** (Anthropic's own API). With
904
984
  `cacheMissDiagnostics: true`, the layer asks Anthropic to compare each marked request
905
985
  with the one whose cache entry it expected to read, and puts the provider's
@@ -909,6 +989,32 @@ async function serverlessLayer(kv: SharedKeyValue) {
909
989
 
910
990
  </details>
911
991
 
992
+ <details>
993
+ <summary><b>Customer-operated PostgreSQL for caches and learning</b></summary>
994
+
995
+ The optional `@bivelio/persistence-postgres` package provides `PgExactCache`,
996
+ `PgSemanticCache`, `PgCacheBreakpointStore` and `PgConstraintMemoryStore` without
997
+ adding a database driver to the core SDK. You supply the `pg` pool, apply its
998
+ versioned SQL migrations yourself, and inject the stores; nothing connects or
999
+ changes your schema automatically. Exact and semantic entries contain customer
1000
+ queries, answers or derived embeddings and need the same access, retention and
1001
+ deletion controls as the original data. Breakpoints contain fingerprints,
1002
+ timestamps and counters, never prompt text. Constraint learning stores only a
1003
+ hash of tenant, scope, provider, served model and contract.
1004
+
1005
+ The breakpoint adapter uses revisions and compare-and-set. Semantic calibration
1006
+ uses short transactions and idempotent observation IDs, so concurrent instances
1007
+ continue one history instead of spending the same confidence budget independently.
1008
+ Both use the same deterministic candidate guards and statistical journal as memory.
1009
+ The semantic backend requires the PostgreSQL vector extension and is opt-in; existing key-value stores
1010
+ and in-memory defaults keep their previous behavior. Removing `constraintMemoryStore`
1011
+ rolls constraint learning back to process memory without reading the new table.
1012
+
1013
+ See [`packages/persistence-postgres/README.md`](packages/persistence-postgres/README.md)
1014
+ for setup, ownership, cleanup, benchmark evidence and shutdown.
1015
+
1016
+ </details>
1017
+
912
1018
  <details>
913
1019
  <summary><b>Structured output — validated <code>data</code>, not just text</b></summary>
914
1020
 
@@ -953,7 +1059,7 @@ import { GoogleProvider } from "@bivelio/savings-layer";
953
1059
 
954
1060
  const gemini = createSavingsLayer({
955
1061
  provider: new GoogleProvider({ apiKey: process.env.GEMINI_API_KEY, requireUsage: true }),
956
- licenseKey: process.env.BIVELIO_LICENSE_KEY,
1062
+ // licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
957
1063
  });
958
1064
 
959
1065
  const { text: answer, report: geminiReport } = await gemini.generate({
@@ -1159,7 +1265,7 @@ import { AnthropicProvider } from "@bivelio/savings-layer";
1159
1265
 
1160
1266
  const claude = createSavingsLayer({
1161
1267
  provider: new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
1162
- licenseKey: process.env.BIVELIO_LICENSE_KEY,
1268
+ // licenseKey: process.env.BIVELIO_LICENSE_KEY, (optional: cloud)
1163
1269
  });
1164
1270
 
1165
1271
  const mcpTools: ToolDescriptor[] = []; // every tool your MCP servers list
@@ -1359,7 +1465,7 @@ BVSALA_SEAT_ID=worker-03 # stable per replica; [A-Za-z0-9._:/-], max 128 chars
1359
1465
  BV‑SALA is commercial software. The plans are **Measure** (free), **Pro** (per seat),
1360
1466
  **Team** (a number of seats included, plus each extra seat) and **Enterprise** (by
1361
1467
  contract); Pro and Team are paid whether or not you save and carry **no commission** on
1362
- your savings. The prices are at the top of this page, in [LICENSE](./LICENSE) §6 and in
1468
+ your savings. The prices are at the top of this page, in [LICENSE-CLOUD.md](./LICENSE-CLOUD.md) §6 and in
1363
1469
  the Published Pricing it refers to,
1364
1470
  **[savings.bivelio.com/pricing](https://savings.bivelio.com/pricing)**. No trial. There is one free license,
1365
1471
  **Measure** — no card, one organization per verified account, up to 5 seats and 20,000
@@ -1383,7 +1489,7 @@ The license is a signed token your SDK and the
1383
1489
  gateway verify **offline**; the managed control plane and the revocation kill‑switch
1384
1490
  are served from BiVelio. Your LLM traffic goes **straight to your provider** —
1385
1491
  BiVelio receives per‑request usage (counts, cost, the model id and opaque ids), never
1386
- prompts, completions or provider keys; [LICENSE](./LICENSE) §5 lists every field, and
1492
+ prompts, completions or provider keys; [LICENSE-CLOUD.md](./LICENSE-CLOUD.md) §5 lists every field, and
1387
1493
  **[savings.bivelio.com/privacy](https://savings.bivelio.com/privacy)** says how long
1388
1494
  each one is kept.
1389
1495