@bivelio/savings-layer 0.4.25 → 0.4.26

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
@@ -74,12 +74,14 @@ products charge a flat subscription, so there is no per‑token bill to cut. The
74
74
  story, in plain words:
75
75
  **[Where does the saving install?](https://savings.bivelio.com/en/how-it-works)**
76
76
 
77
- **Claude Code, Codex CLI, Goose or Qwen Code with an API key?** The package also
78
- ships a local measuring gateway: `bvsala claude` (or `bvsala codex`, `bvsala
79
- goose`, `bvsala qwen`) launches the terminal through it and your real spend shows
80
- up in your dashboard, token by token — nothing to configure, nothing persisted.
81
- For opencode, Crush, Droid, Cline or Kilo Code, `bvsala doctor` prints the exact
82
- config block for each one you have installed. Like the SDK, the gateway is
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
83
85
  fail‑closed: it starts only with your service key (`BIVELIO_SERVICE_KEY`) and an
84
86
  active BiVelio license, which it collects with that key and verifies against the
85
87
  key embedded in the package. Once running it never interrupts a request in flight:
@@ -94,7 +96,8 @@ as for any other use.
94
96
 
95
97
  **The gateway forwards what your terminal sends.** With no lever armed, your
96
98
  provider receives the request body byte for byte and every header your client
97
- sends (see the limits): `cache_control` and its 1-hour TTL, tool search and
99
+ sends except `x-bvsala-report`, which asks the gateway itself for a report (see
100
+ the limits): `cache_control` and its 1-hour TTL, tool search and
98
101
  `tool_reference` blocks, the advisor, every `anthropic-beta` value. An armed lever
99
102
  changes only what it declares, and a request it cannot rewrite exactly travels
100
103
  untouched. A test suite checks this against a fake upstream on every change
@@ -140,6 +143,17 @@ is measurement, not savings: the saving levers of the SDK are TypeScript, and th
140
143
  gateway's own levers apply to Anthropic `/v1/messages` traffic with a PRO license.
141
144
  `bvsala doctor` prints the recipe for each SDK.
142
145
 
146
+ Each request can also bring its report back to your code. Send the header
147
+ `x-bvsala-report: 1` (in Python, `default_headers={"x-bvsala-report": "1"}` on
148
+ the client) and the gateway answers with `x-bvsala-request-id`. On the same
149
+ gateway, `GET /_bvsala/v1/reports/<that id>` returns what it recorded for your
150
+ dashboard: `metered` with the `SavingsReport` (the provider's token counts, the
151
+ cost at list price and `cost.basis`), or `not_metered` with the reason from the
152
+ log line. `?response_id=<the provider's response id>` finds it too, and
153
+ `wait_ms` waits for a request still in flight. The header never reaches your
154
+ provider, and a request without it is not changed at all:
155
+ [the details](./docs/terminales/README.md#what-the-gateway-guarantees-it-forwards-what-your-client-sends).
156
+
143
157
  ## Check it before you install
144
158
 
145
159
  What you can check before paying is what we have measured: the published benchmark,
@@ -149,6 +163,22 @@ and the commission calculator on the front page, which runs on the same price
149
163
  catalogue that bills you. The verifier that replays the optimizer on your own data
150
164
  lives in your dashboard once you sign in.
151
165
 
166
+ ## Set it up in one command
167
+
168
+ ```bash
169
+ npx @bivelio/savings-layer@latest init
170
+ ```
171
+
172
+ It reads your `package.json` (Next.js, Express, the Vercel AI SDK, Mastra,
173
+ LangChain.js, the Anthropic SDK or the OpenAI SDK) and prints the three steps below,
174
+ written for your stack — the install line for your package manager, which keys are
175
+ missing, the exact code for your adapter — plus a free check: a question the layer
176
+ answers on your machine, so the model is not called. It reads `package.json` files only
177
+ (yours, and those in `node_modules` for the installed versions), never a `.env` file or a
178
+ key's value; it uses no network of its own and writes nothing.
179
+ `--write` creates the new files after asking you, and never overwrites one; `--json`
180
+ prints the same plan for a script or a coding agent.
181
+
152
182
  ## Install in three steps
153
183
 
154
184
  **1 · The package**
@@ -332,8 +362,10 @@ shared here. `emitReportPart: true` also streams each report as a
332
362
 
333
363
  ### LangChain.js
334
364
 
335
- `pnpm add @bivelio/savings-layer langchain @langchain/core` — an agent middleware for
336
- `createAgent` in `langchain` 1.x:
365
+ `pnpm add @bivelio/savings-layer langchain @langchain/core @langchain/openai` — an agent
366
+ middleware for `createAgent` in `langchain` 1.x. The model string (`"openai:…"`) is loaded
367
+ through its integration package, so `@langchain/openai` (or `@langchain/anthropic`) has
368
+ to be installed:
337
369
 
338
370
  ```ts
339
371
  import { createAgent } from "langchain";
@@ -402,6 +434,19 @@ single-shot requests only: requests that declare tools, carry tool calls or resu
402
434
  hold more than one user turn are left out unless you set
403
435
  `savings.semanticCache: "include-agentic"`.
404
436
 
437
+ **Calibrated reuse (opt-in).** A fixed similarity floor means something different for
438
+ every embedder and every workload, so you can make the cache earn its floor on your own
439
+ traffic instead: `new InMemorySemanticCache({ embed, calibration: { maxErrorRate: 0.02 } })`.
440
+ It then serves no reworded answer until your labels show, at 95 % confidence, that at
441
+ most 2 % of the answers it would reuse from some score up are wrong, and it re-tests that
442
+ floor as labels accumulate. Until then every candidate goes to your provider, exactly as
443
+ it would without the cache, and the fresh answer is compared with the stored one by your
444
+ `judge` (by default only identical answers count, which suits classification and
445
+ structured output; pass your own for free text). Each calibrated reuse carries its bound
446
+ and the labels behind it in `report.qualityLedger.cacheMatch.calibration`. Calibration
447
+ costs reuses, never an extra call, and it does not change how anything is billed. If you
448
+ already have labeled pairs, `calibrateSemanticFloor()` runs the same test offline.
449
+
405
450
  And a promise you can hold it to: **never worse than your JSON.** Payload encodings
406
451
  are chosen by counting real tokens with the destination model's own tokenizer, and
407
452
  BV‑SALA only moves away from plain JSON when the saving clearly clears the bar —
@@ -465,8 +510,8 @@ changes the answer, and what they are worth is reported next to the four ledgers
465
510
  never folded into `cost.*`, never part of any commission.
466
511
 
467
512
  **`savings.deferrable`** asks for the provider's Flex tier: OpenAI, and Gemini through
468
- its OpenAI‑compatible endpoint. Anthropic has no cheaper synchronous tier (its
469
- discount lives only in Message Batches, which the layer does not support yet).
513
+ its OpenAI‑compatible endpoint. Anthropic has no cheaper synchronous tier: its
514
+ discount lives only in Message Batches, which `layer.batches` (below) sends to.
470
515
  Bedrock and DeepSeek off‑peak pricing are not supported.
471
516
 
472
517
  - **Latency.** OpenAI publishes no latency commitment for Flex; Google publishes a
@@ -514,6 +559,124 @@ report.deferred?.tierSaving; // { amount, basis, standardCost, appliedCost, pric
514
559
  report.cacheWrites; // { mode, breakpoints, avoidedWritePremium?, reason }
515
560
  ```
516
561
 
562
+ **`layer.batches`** sends requests that can wait hours to the provider's own batch API,
563
+ billed at its published batch rate card: Anthropic's Message Batches through
564
+ `AnthropicProvider`, and OpenAI's Batch API through `OpenAICompatibleProvider` on
565
+ `api.openai.com`. Gemini's batch mode is not supported yet.
566
+
567
+ - **What runs.** Each request is translated by the same provider adapter as
568
+ `generate()` and sent as you wrote it: the layer's own per‑request savings (caching,
569
+ compression, model cascade, retrieval) do not run on batches. The layer splits the
570
+ job at the provider's limits (and, on OpenAI, one model per file).
571
+ - **Latency.** The provider's: results arrive within its batch window, not on a
572
+ request/response path. `submit()` returns a plain‑JSON job you can store and read
573
+ from another process.
574
+ - **What it is worth.** Each result's usage counter at the standard and at the batch
575
+ published rate cards, component by component, with the same `measured` /
576
+ `estimated` rule as above. The tier is read from the provider: Anthropic reports it
577
+ on every result; on OpenAI it is the batch the result came from. Nothing is
578
+ metered and the batch saving is never billed.
579
+
580
+ ```ts
581
+ const tickets = [{ id: "t-1", text: "My invoice is wrong." }]; // your deferrable work
582
+
583
+ const job = await layer.batches.submit(
584
+ tickets.map((t) => ({
585
+ customId: t.id,
586
+ request: { model: "openai/gpt-4o-mini", messages: [{ role: "user", content: t.text }] },
587
+ })),
588
+ );
589
+ // …later, even from another process (store `job` as JSON):
590
+ const results = await layer.batches.results(job);
591
+ results.ended; // false while the provider is still working
592
+ results.items; // one per request: { customId, outcome, text, usage, tierSaving?, reason }
593
+ results.summary; // { succeeded, errored, expired, credited, tierSaving, basis, pricesCheckedOn, reason, … }
594
+ ```
595
+
596
+ </details>
597
+
598
+ <details>
599
+ <summary><b>Tool outputs, compacted where they are born (opt-in, off by default)</b></summary>
600
+
601
+ In an agent loop your history is re-sent on every turn and your provider reads it
602
+ from its prompt cache at a fraction of the input price. Rewriting anything it has
603
+ already cached turns everything after it back into full-price cache writes, which is
604
+ how "fewer tokens" ends up as a bigger bill. So this lever only ever changes a tool
605
+ output on the turn it arrives, and re-sends exactly the same bytes afterwards.
606
+
607
+ It removes terminal colour codes, superseded progress-bar states, whitespace between
608
+ JSON tokens and runs of identical lines (folded into one line plus a count), and
609
+ nothing else: no summaries, no dropped words. Each change either reverses exactly or
610
+ leaves what a terminal would display. Outputs of calls that name a file are left
611
+ verbatim, so an agent that edits by exact match still sees the file as it is.
612
+
613
+ ```ts
614
+ const agentLayer = createSavingsLayer({
615
+ provider,
616
+ licenseKey: process.env.BIVELIO_LICENSE_KEY,
617
+ toolOutputCompaction: true, // or { exceptTools: ["read_logs"], minSavedChars: 512 }
618
+ });
619
+ // or per request: savings: { toolOutputCompaction: true }
620
+ ```
621
+
622
+ - **Switch it on for whole conversations.** The decision depends only on each
623
+ output's own text, so every process and every turn produces the same bytes. A
624
+ conversation that started without it is rewritten once, on its first compacted
625
+ request.
626
+ - **Counted, not billed.** What it removes shows in `report.tokens.compressedInput`
627
+ and in `report.traces`; it never enters `cost.*` or any commission. Your provider's
628
+ invoice shows the effect.
629
+ - **Inside a framework,** call `compactToolOutput(text)` in your tool before you
630
+ return its result: your history then holds the compacted text itself.
631
+ - **Measure it by conversation, not by request.** A verification copy re-sends one
632
+ request with the original history, which your provider has not cached, so on
633
+ turns that re-send compacted outputs the copy costs more than going without the
634
+ layer would have. Compare whole conversations with and without it.
635
+
636
+ </details>
637
+
638
+ <details>
639
+ <summary><b>Prompt-cache breakpoints placed from your traffic (opt-in)</b></summary>
640
+
641
+ By default the layer writes the provider's prompt cache at two fixed places: the end
642
+ of the system header and the end of the history before your last user message, and
643
+ stops marking a prefix whose measured hit rate does not pay the write premium.
644
+
645
+ `cacheBreakpoints: "auto"` places them from what your traffic actually does. The layer
646
+ compares each request's prefix with the ones it sent before, per model, tool set and
647
+ system header, and learns how often each part is sent again and after how long. That
648
+ costs nothing: it compares locally and marks nothing to find out. Once the evidence
649
+ is conclusive:
650
+
651
+ - **Tail.** When requests continue each other (agent tool loops, conversations), it
652
+ also marks the last block, so the next turn reads the whole prompt from cache.
653
+ Tool results arrive after your last user message, so the fixed placement never
654
+ caches them.
655
+ - **History.** When the history before the question is not sent again (one-off
656
+ requests with their own document), it moves the breakpoint back to the system
657
+ header, so no write premium is paid on a prefix nobody reads.
658
+ - **One-hour lifetime** (Anthropic). When turns come back after more than five
659
+ minutes but within the hour, and the size of the request makes the 2x write pay.
660
+ - **Explicit mode** (GPT‑5.6 and later, on OpenAI's own API). When continuations are
661
+ rare, your question is not written to the cache. `savings.standalone` still wins
662
+ when you set it, and `report.cacheWrites` says when the layer chose it.
663
+
664
+ Until then it behaves exactly like the default. Every decision and its reason is in
665
+ `report.traces` (`prefix-cache-placement`), and the effect is read from the provider's
666
+ own counters: if they do not confirm the cache reads the layer predicted, that
667
+ traffic goes back to the default placement. The write premium it causes, one-hour
668
+ writes at 2x included, is charged to `cost.actual`; the cache discount it earns is
669
+ counted in ledger 4 like any provider cache read, never credited as the layer's
670
+ saving and never part of any commission.
671
+
672
+ ```ts
673
+ const placedLayer = createSavingsLayer({
674
+ provider, // Anthropic, or OpenAI GPT-5.6+ on OpenAI's own API
675
+ licenseKey: process.env.BIVELIO_LICENSE_KEY,
676
+ cacheBreakpoints: "auto",
677
+ });
678
+ ```
679
+
517
680
  </details>
518
681
 
519
682
  <details>
@@ -587,6 +750,47 @@ if (res.toolCalls?.length) {
587
750
  A tool‑call turn is never served from cache and never counted as an avoided call:
588
751
  an action that has not run yet cannot be a saving.
589
752
 
753
+ **Large catalogues: let the provider search them (opt‑in).** With hundreds of tools
754
+ (several MCP servers, say), `savings: { toolSearch: "auto" }` sends every tool deferred
755
+ behind the provider's own tool search instead of letting the layer's selector keep a
756
+ few. A catalogue that declares no `intents` or `risk` gives the selector nothing to go
757
+ on, so it keeps the first ones and the model cannot call the rest; with the provider's
758
+ search the model looks up what it needs, no tool you declared is out of reach, and the
759
+ cached prompt prefix stays the same from one question to the next.
760
+
761
+ - **Where.** The Anthropic adapter, on models in Anthropic's tool search compatibility
762
+ table. OpenAI documents its tool search for the Responses and Agents APIs, not for
763
+ Chat Completions, which is what this SDK's OpenAI adapter speaks, so OpenAI requests
764
+ keep the selector.
765
+ - **When.** `"auto"` applies it when the adapter talks to Anthropic's own API, the
766
+ catalogue is over the provider's documented size, and deferring it pays for one
767
+ search on every request even with a warm prompt cache. `"always"` skips those checks
768
+ (for example behind a proxy that forwards the search blocks). Requests with an output
769
+ contract, or with the model cascade on, keep the selector.
770
+ - **Whose saving.** The provider's. Your usage counter shows it; the layer credits
771
+ nothing for it and charges no commission on it.
772
+
773
+ ```ts
774
+ import { AnthropicProvider } from "@bivelio/savings-layer";
775
+
776
+ const claude = createSavingsLayer({
777
+ provider: new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
778
+ licenseKey: process.env.BIVELIO_LICENSE_KEY,
779
+ });
780
+
781
+ const mcpTools: ToolDescriptor[] = []; // every tool your MCP servers list
782
+
783
+ const { report: searched } = await claude.generate({
784
+ model: "anthropic/claude-opus-5-5",
785
+ messages: [{ role: "user", content: "Which orders from service 17 are still open?" }],
786
+ tools: mcpTools,
787
+ savings: { toolSearch: { mode: "auto", alwaysLoaded: ["read_file"] } },
788
+ });
789
+
790
+ // Which path ran and why, and how many searches the provider ran.
791
+ console.log(searched.traces.filter((t) => t.stage === "tool-search"));
792
+ ```
793
+
590
794
  </details>
591
795
 
592
796
  <details>
@@ -1,12 +1,13 @@
1
1
  import {createSavingsLayer,LicenseError,sha256,stableStringify}from'@bivelio/savings-layer';import {AsyncLocalStorage}from'async_hooks';var w=t=>typeof t=="number"&&Number.isFinite(t)&&t>0?t:0;function N(t){if(!Array.isArray(t))return null;let e={input:0,output:0,cacheRead:0,cacheWrite:0,cacheWrite1h:0,iteraciones:0};for(let n of t){if(n===
2
2
  null||typeof n!="object")continue;let r=n;if(r.type!=="compaction")continue;e.iteraciones++,e.input+=w(r.input_tokens),e.output+=w(r.output_tokens),e.cacheRead+=w(r.cache_read_input_tokens);let o=r.cache_creation,
3
3
  s=w(o?.ephemeral_5m_input_tokens),a=w(o?.ephemeral_1h_input_tokens),i=typeof r.cache_creation_input_tokens=="number"?w(r.cache_creation_input_tokens):s+a;e.cacheWrite+=i,e.cacheWrite1h+=Math.min(a,i);}
4
- return e.iteraciones>0?e:null}function W(t,e,n){let r=t??{},o=N(r.iterations),s=J(r.cache_read_input_tokens,o?.cacheRead),a=J(r.cache_creation_input_tokens,o?.cacheWrite),i=(r.input_tokens??0)+(o?.input??0)+(a??0)+(s??0),u=(e??0)+
5
- (o?.output??0),d=typeof r.input_tokens!="number",f=typeof e!="number";return {usage:{inputTokens:i,outputTokens:u,...s!=null?{cachedInputTokens:s}:{},...a!=null?{cacheWriteInputTokens:a}:{},...n||f||o?
6
- {}:{reasoningTokens:0},...d||f?{complete:false}:{}},entradaAusente:d,salidaAusente:f}}function J(t,e){return e?(t??0)+e:t}function B(t){let e=t?.service_tier;return typeof e=="string"&&e!==""?e:void 0}function q(t,e){
7
- let n=Object.fromEntries(Object.entries(e??{}).filter(([,o])=>o!=null)),r=t?.cache_creation||e?.cache_creation?{...t?.cache_creation??{},...e?.cache_creation??{}}:void 0;return {...t??{},...n,...r?{cache_creation:r}:
8
- {}}}function F(t){return t==="thinking"||t==="redacted_thinking"}function V(t){switch(t){case "end_turn":return "stop";case "max_tokens":return "length";case "tool_use":return "tool_calls";case "refusal":return "\
9
- content_filter";default:return t}}var M=class{decoder;resto="";crPendiente=false;alPrincipio=true;datos="";evento="";ultimoId;push(e){let n=typeof e=="string"?e:(this.decoder??=new TextDecoder("utf-8")).decode(e,{stream:true});return this.procesar(
4
+ return e.iteraciones>0?e:null}function W(t,e,n){let r=t??{},o=N(r.iterations),s=J(r.cache_read_input_tokens,o?.cacheRead),a=J(r.cache_creation_input_tokens,o?.cacheWrite),i=r.cache_creation?.ephemeral_1h_input_tokens,u=typeof i=="\
5
+ number"||(o?.cacheWrite1h??0)>0?Math.min(Math.max(0,(typeof i=="number"?i:0)+(o?.cacheWrite1h??0)),a??0):void 0,d=(r.input_tokens??0)+(o?.input??0)+(a??0)+(s??0),g=(e??0)+(o?.output??0),m=typeof r.input_tokens!=
6
+ "number",h=typeof e!="number";return {usage:{inputTokens:d,outputTokens:g,...s!=null?{cachedInputTokens:s}:{},...a!=null?{cacheWriteInputTokens:a}:{},...u!==void 0?{cacheWrite1hInputTokens:u}:{},...n||
7
+ h||o?{}:{reasoningTokens:0},...m||h?{complete:false}:{}},entradaAusente:m,salidaAusente:h}}function J(t,e){return e?(t??0)+e:t}function B(t){let e=t?.service_tier;return typeof e=="string"&&e!==""?e:void 0}
8
+ function q(t,e){let n=Object.fromEntries(Object.entries(e??{}).filter(([,o])=>o!=null)),r=t?.cache_creation||e?.cache_creation?{...t?.cache_creation??{},...e?.cache_creation??{}}:void 0;return {...t??{},
9
+ ...n,...r?{cache_creation:r}:{}}}function F(t){return t==="thinking"||t==="redacted_thinking"}function V(t){switch(t){case "end_turn":return "stop";case "max_tokens":return "length";case "tool_use":return "\
10
+ tool_calls";case "refusal":return "content_filter";default:return t}}var M=class{decoder;resto="";crPendiente=false;alPrincipio=true;datos="";evento="";ultimoId;push(e){let n=typeof e=="string"?e:(this.decoder??=new TextDecoder("utf-8")).decode(e,{stream:true});return this.procesar(
10
11
  n)}end(){let e=[];if(this.decoder){let n=this.decoder.decode();n!==""&&e.push(...this.procesar(n));}return this.resto!==""&&(this.linea(this.resto,e),this.resto=""),this.despachar(e),this.crPendiente=false,
11
12
  e}procesar(e){let n=[];if(e==="")return n;let r=e;if(this.alPrincipio&&(this.alPrincipio=false,r.charCodeAt(0)===65279&&(r=r.slice(1)),r===""))return n;let o=0;this.crPendiente&&(this.crPendiente=false,r.charCodeAt(
12
13
  0)===10&&(o=1));let s=o;for(;o<r.length;){let a=r.charCodeAt(o);if(a!==10&&a!==13){o++;continue}this.linea(this.resto+r.slice(s,o),n),this.resto="",a===13&&(o+1<r.length?r.charCodeAt(o+1)===10&&o++:this.
@@ -23,8 +24,8 @@ ultCostModel","exactCache","exactCacheTtlMs","resolvers","exactTokenizer","silen
23
24
  t.completada}var ee="[bvsala] the model answered, but the savings layer failed while accounting for it; the answer is returned unchanged, without a savings report and without a usage record.";function te(t,e){
24
25
  let n=X(),r={};for(let a of Se)e[a]!==void 0&&(r[a]=e[a]);let o=createSavingsLayer({...r,provider:Q(t,n)}),s=(a,i)=>H(e.silenceWarnings,`${t}:${a}`,i);return {ejecutar(a,i){return n.run(i,()=>o.generate(a))},async generar(a,i){
25
26
  try{let u=await n.run(i,()=>o.generate(a));return i.invocada?{tipo:"vivo",result:u}:{tipo:"atajo",result:u}}catch(u){if(Y(i,u))return s("sin-informe",ee),{tipo:"sin-informe"};throw u}},async transmitir(a,i,u){
26
- let f=n.run(i,()=>o.generateStream(a,u?{signal:u}:{})).result;i.resultado=f,f.catch(()=>{});let y;try{y=await Promise.race([i.abierta.then(()=>"abierta"),f.then(()=>"resuelta")]);}catch(_){if(Y(i,_))return s(
27
- "sin-informe",ee),{tipo:"sin-informe"};throw _}if(y==="abierta")return {tipo:"abierto"};let b=await f;return i.invocada?{tipo:"entero",result:b}:{tipo:"atajo",result:b}},informe(a){let i=JSON.parse(JSON.
27
+ let g=n.run(i,()=>o.generateStream(a,u?{signal:u}:{})).result;i.resultado=g,g.catch(()=>{});let m;try{m=await Promise.race([i.abierta.then(()=>"abierta"),g.then(()=>"resuelta")]);}catch(b){if(Y(i,b))return s(
28
+ "sin-informe",ee),{tipo:"sin-informe"};throw b}if(m==="abierta")return {tipo:"abierto"};let h=await g;return i.invocada?{tipo:"entero",result:h}:{tipo:"atajo",result:h}},informe(a){let i=JSON.parse(JSON.
28
29
  stringify(a));if(e.onReport)try{e.onReport(i);}catch{s("onreport","[bvsala] `onReport` threw; the error was caught and the model call was not affected.");}return i},avisar:s,close(){o.close();}}}function ne(){
29
30
  let t;return {abierta:new Promise(n=>{t=n;}),abrir:t}}var C=class{pendientes=[];esperando;terminada=false;fallo;push(e){if(this.terminada||this.fallo)return;let n=this.esperando;if(n){this.esperando=void 0,n.resolver({value:e,done:false});return}this.pendientes.
30
31
  push(e);}end(){if(this.terminada||this.fallo)return;this.terminada=true;let e=this.esperando;e&&this.pendientes.length===0&&(this.esperando=void 0,e.resolver({value:void 0,done:true}));}fail(e){if(this.terminada||
@@ -81,19 +82,19 @@ stringify(i.input)}})),a+=s("content_block_stop",{type:"content_block_stop",inde
81
82
  a+=s("message_stop",{type:"message_stop"}),new Response(a,{status:200,headers:{...o,"content-type":"text/event-stream"}})}var E=class extends Error{constructor(n){super(`[bvsala] provider responded ${n.
82
83
  status}`);this.respuesta=n;}respuesta};function et(t){let e=te("anthropic-sdk",t),n=Math.max(1,Math.floor(t.reportCacheSize??1e3)),r=new Map,o=(a,i)=>{if(a)for(r.delete(a),r.set(a,i);r.size>n;)r.delete(
83
84
  r.keys().next().value);};return {middleware:async(a,i,u)=>{if(!De(a))return i(a);if(typeof a.body!="string")throw new Error(I);let d;try{let c=JSON.parse(a.body);if(!p(c))throw new Error(I);d=c;}catch{throw new Error(
84
- I)}let f=u.options,y=ie(f?.bivelio),b=t.context?.({body:d,requestOptions:f}),_=(Array.isArray(d.tools)?d.tools:[]).filter(p).map(c=>oe(t,{nombre:String(c.name??""),descripcion:typeof c.description=="s\
85
+ I)}let g=u.options,m=ie(g?.bivelio),h=t.context?.({body:d,requestOptions:g}),b=(Array.isArray(d.tools)?d.tools:[]).filter(p).map(c=>oe(t,{nombre:String(c.name??""),descripcion:typeof c.description=="s\
85
86
  tring"?c.description:void 0,parametros:c.input_schema,delProveedor:c.input_schema===void 0&&typeof c.type=="string"&&c.type!=="custom"})),l=await se(t,{model:`anthropic/${String(d.model??"")}`,messages:Ce(
86
- d),tools:_,temperature:typeof d.temperature=="number"?d.temperature:void 0,context:ae(b,y.context),savings:y.savings,proyeccion:Pe(d,a.headers.get("anthropic-beta")),restricciones:{sinResolvers:Me(d)}}),
87
- v={model:l.model,messages:l.messages,...l.tools?{tools:l.tools}:{}},h=async c=>{let g;try{g=await i(a);}catch(m){throw c.errorDelMarco={error:m},m}if(!g.ok){let m=new E(g);throw c.errorDelMarco={error:m},
88
- m}return c.completada=true,g},A=c=>{if(c instanceof E)return c.respuesta;throw c};if(d.stream!==true){let c,g,m=U(v,async()=>{c=await h(m);let D=new T;return D.json(await c.clone().json()),g=D.id,D.respuesta()}),
89
- R;try{R=await e.generar(l,m);}catch(D){return A(D)}if(R.tipo==="sin-informe")return c;let $=e.informe(R.result.report);return R.tipo==="atajo"?(o(`msg_bvsala_${R.result.requestId}`,$),ue(R.result,d.model,
90
- false)):(o(g,$),c)}let{abierta:pe,abrir:z}=ne(),j,O,k=Object.assign(U(v,async()=>{let c=await h(k),g=new C;O=le(c,g,e,k,o),z();for await(let m of g)if(m.type==="done")return m.response;throw new Error("[\
91
- bvsala] the model stream ended before completion; nothing was measured.")},()=>{let c=new C;return h(k).then(g=>{j=le(g,c,e,k,o),z();},g=>c.fail(g)),c}),{abierta:pe}),S;try{S=await e.transmitir(l,k,a.signal??
87
+ d),tools:b,temperature:typeof d.temperature=="number"?d.temperature:void 0,context:ae(h,m.context),savings:m.savings,proyeccion:Pe(d,a.headers.get("anthropic-beta")),restricciones:{sinResolvers:Me(d)}}),
88
+ _={model:l.model,messages:l.messages,...l.tools?{tools:l.tools}:{}},v=async c=>{let f;try{f=await i(a);}catch(y){throw c.errorDelMarco={error:y},y}if(!f.ok){let y=new E(f);throw c.errorDelMarco={error:y},
89
+ y}return c.completada=true,f},A=c=>{if(c instanceof E)return c.respuesta;throw c};if(d.stream!==true){let c,f,y=U(_,async()=>{c=await v(y);let D=new T;return D.json(await c.clone().json()),f=D.id,D.respuesta()}),
90
+ R;try{R=await e.generar(l,y);}catch(D){return A(D)}if(R.tipo==="sin-informe")return c;let $=e.informe(R.result.report);return R.tipo==="atajo"?(o(`msg_bvsala_${R.result.requestId}`,$),ue(R.result,d.model,
91
+ false)):(o(f,$),c)}let{abierta:pe,abrir:z}=ne(),j,O,k=Object.assign(U(_,async()=>{let c=await v(k),f=new C;O=le(c,f,e,k,o),z();for await(let y of f)if(y.type==="done")return y.response;throw new Error("[\
92
+ bvsala] the model stream ended before completion; nothing was measured.")},()=>{let c=new C;return v(k).then(f=>{j=le(f,c,e,k,o),z();},f=>c.fail(f)),c}),{abierta:pe}),S;try{S=await e.transmitir(l,k,a.signal??
92
93
  void 0);}catch(c){return A(c)}if(S.tipo==="abierto")return j??O;if(S.tipo==="atajo"){let c=e.informe(S.result.report);return o(`msg_bvsala_${S.result.requestId}`,c),ue(S.result,d.model,true)}return j??O},
93
94
  reportFor(a){let i=typeof a=="string"?a:a?.id;return typeof i=="string"?r.get(i):void 0},close(){e.close();}}}function le(t,e,n,r,o){let s=new T,a,i=async()=>{try{let l=await r.resultado;o(s.id,n.informe(
94
95
  l.report));}catch{}},u=()=>{e.cerrada||(s.error?e.fail(new Error(`[bvsala] provider stream error: ${s.error}`)):s.vioDelta||s.vioStop?(e.push({type:"done",response:s.respuesta()}),e.end(),a=i()):e.fail(
95
96
  new Error("[bvsala] the model stream ended before completion; nothing was measured.")));};if((t.headers.get("content-type")??"").includes("application/json"))return t.clone().json().then(l=>{s.json(p(l)?
96
- l:{}),s.texto!==""&&e.push({type:"text",delta:s.texto}),u();},l=>e.fail(l)),t;let d=t.body;if(!d)return e.fail(new Error("[bvsala] the model stream had no body; nothing was measured.")),t;let f=d.getReader(),
97
- y=new M,b=l=>{for(let v of l){let h=s.evento(v);h!==void 0&&e.push({type:"text",delta:h}),(s.vioStop||s.error)&&u();}},_=new ReadableStream({async pull(l){let v;try{v=await f.read();}catch(A){e.fail(A),
98
- l.error(A);return}if(v.done){b(y.end()),u(),await a,l.close();return}let h=v.value;l.enqueue(h),b(y.push(h));},cancel(l){return e.fail(new Error("[bvsala] the stream was cancelled by its reader; nothin\
99
- g was measured.")),f.cancel(l)}});return new Response(_,{status:t.status,statusText:t.statusText,headers:t.headers})}export{et as anthropicSavings};
97
+ l:{}),s.texto!==""&&e.push({type:"text",delta:s.texto}),u();},l=>e.fail(l)),t;let d=t.body;if(!d)return e.fail(new Error("[bvsala] the model stream had no body; nothing was measured.")),t;let g=d.getReader(),
98
+ m=new M,h=l=>{for(let _ of l){let v=s.evento(_);v!==void 0&&e.push({type:"text",delta:v}),(s.vioStop||s.error)&&u();}},b=new ReadableStream({async pull(l){let _;try{_=await g.read();}catch(A){e.fail(A),
99
+ l.error(A);return}if(_.done){h(m.end()),u(),await a,l.close();return}let v=_.value;l.enqueue(v),h(m.push(v));},cancel(l){return e.fail(new Error("[bvsala] the stream was cancelled by its reader; nothin\
100
+ g was measured.")),g.cancel(l)}});return new Response(b,{status:t.status,statusText:t.statusText,headers:t.headers})}export{et as anthropicSavings};
@@ -1,57 +1,58 @@
1
- import {createMiddleware}from'langchain';import {AIMessage}from'@langchain/core/messages';import {toJsonSchema}from'@langchain/core/utils/json_schema';import {createSavingsLayer,LicenseError,sha256,stableStringify}from'@bivelio/savings-layer';import {AsyncLocalStorage}from'async_hooks';var D=new Set;function _(e,n,t){e===true||D.has(n)||(D.add(n),console.warn(t));}var K="[bvsala] internal: the layer changed a request this adapter must forward unchanged; noth\
1
+ import {createMiddleware}from'langchain';import {AIMessage}from'@langchain/core/messages';import {toJsonSchema}from'@langchain/core/utils/json_schema';import {createSavingsLayer,LicenseError,sha256,stableStringify}from'@bivelio/savings-layer';import {AsyncLocalStorage}from'async_hooks';var D=new Set;function _(n,e,t){n===true||D.has(e)||(D.add(e),console.warn(t));}var K="[bvsala] internal: the layer changed a request this adapter must forward unchanged; noth\
2
2
  ing was sent.",H="[bvsala] internal: the layer asked for a second model call within one framework call; nothing more was sent.",G="[bvsala] internal: the layer called the model outside a framework cal\
3
- l; nothing was sent.";function L(e,n,t){return {renderizado:e,generar:n,...{},invocada:false,completada:false}}async function P(e,n){try{let t=await n();return e.completada=!0,t}catch(t){throw e.
4
- errorDelMarco={error:t},t}}function X(e,n){if(e===n)return true;try{return stableStringify(e)===stableStringify(n)}catch{return false}}function C(e,n){let t=e??[],r=n??[];return t.length===r.length&&t.every((o,s)=>X(o,r[s]))}function Z(e,n){
5
- return e.model===n.model&&C(e.messages,n.messages)&&C(e.tools,n.tools)}function T(e,n){let t=r=>{let o=n.getStore();if(!o)throw new Error(G);if(o.invocada)throw new Error(H);if(!Z(r,o.renderizado))throw new Error(
6
- K);return o.invocada=true,o};return {id:`bivelio-adapter:${e}`,async generate(r,o){return t(r).generar(o?.signal)},async*generateStream(r,o){let s=t(r);if(s.transmitir){yield*s.transmitir(o?.signal);return}
3
+ l; nothing was sent.";function P(n,e,t){return {renderizado:n,generar:e,...{},invocada:false,completada:false}}async function L(n,e){try{let t=await e();return n.completada=!0,t}catch(t){throw n.
4
+ errorDelMarco={error:t},t}}function X(n,e){if(n===e)return true;try{return stableStringify(n)===stableStringify(e)}catch{return false}}function C(n,e){let t=n??[],r=e??[];return t.length===r.length&&t.every((o,s)=>X(o,r[s]))}function Z(n,e){
5
+ return n.model===e.model&&C(n.messages,e.messages)&&C(n.tools,e.tools)}function T(n,e){let t=r=>{let o=e.getStore();if(!o)throw new Error(G);if(o.invocada)throw new Error(H);if(!Z(r,o.renderizado))throw new Error(
6
+ K);return o.invocada=true,o};return {id:`bivelio-adapter:${n}`,async generate(r,o){return t(r).generar(o?.signal)},async*generateStream(r,o){let s=t(r);if(s.transmitir){yield*s.transmitir(o?.signal);return}
7
7
  let a=await s.generar(o?.signal);a.text!==""&&(yield {type:"text",delta:a.text}),yield {type:"done",response:a};}}}function O(){return new AsyncLocalStorage}var ee=["licenseKey","licenseServerUrl","serviceAccountKey","licenseRefresh","licenseRevocation","licensePollIntervalSeconds","licenseFetch","metering","meteringUrl","meteringFetch","costModels","defa\
8
- ultCostModel","exactCache","exactCacheTtlMs","resolvers","exactTokenizer","silenceWarnings","failOpen"];function I(e,n){return n instanceof LicenseError||e.errorDelMarco&&e.errorDelMarco.error===n?false:e.invocada&&
9
- e.completada}var j="[bvsala] the model answered, but the savings layer failed while accounting for it; the answer is returned unchanged, without a savings report and without a usage record.";function q(e,n){
10
- let t=O(),r={};for(let a of ee)n[a]!==void 0&&(r[a]=n[a]);let o=createSavingsLayer({...r,provider:T(e,t)}),s=(a,i)=>_(n.silenceWarnings,`${e}:${a}`,i);return {ejecutar(a,i){return t.run(i,()=>o.generate(a))},async generar(a,i){
11
- try{let l=await t.run(i,()=>o.generate(a));return i.invocada?{tipo:"vivo",result:l}:{tipo:"atajo",result:l}}catch(l){if(I(i,l))return s("sin-informe",j),{tipo:"sin-informe"};throw l}},async transmitir(a,i,l){
12
- let u=t.run(i,()=>o.generateStream(a,l?{signal:l}:{})).result;i.resultado=u,u.catch(()=>{});let f;try{f=await Promise.race([i.abierta.then(()=>"abierta"),u.then(()=>"resuelta")]);}catch(h){if(I(i,h))return s(
13
- "sin-informe",j),{tipo:"sin-informe"};throw h}if(f==="abierta")return {tipo:"abierto"};let g=await u;return i.invocada?{tipo:"entero",result:g}:{tipo:"atajo",result:g}},informe(a){let i=JSON.parse(JSON.
14
- stringify(a));if(n.onReport)try{n.onReport(i);}catch{s("onreport","[bvsala] `onReport` threw; the error was caught and the model call was not affected.");}return i},avisar:s,close(){o.close();}}}var te={router:false,compression:false,elision:false,serialization:false,toolDiscovery:false,modelCascade:"off",outputCodebook:false,deferrable:false,semanticCache:false,verification:false};
15
- function re(e,n,t={}){let r=o=>e?.[o]!==false&&n?.[o]!==false;return {...te,exactCache:r("exactCache")&&t.sinReutilizar!==true,singleflight:r("singleflight")&&t.sinReutilizar!==true,deterministicResolvers:r("det\
16
- erministicResolvers")&&t.sinResolvers!==true}}function oe(e,n){let t=e.readOnlyTools;if(typeof t=="function")try{return t(n)===!0}catch{return false}return Array.isArray(t)?t.includes(n):false}function R(e,n){
17
- let t=n.delProveedor!==true&&oe(e,n.nombre);return {id:n.nombre,intents:[],risk:t?"read":"write",...n.parametros!==void 0&&n.parametros!==null&&typeof n.parametros=="object"?{parameters:n.parametros}:{},
18
- ...typeof n.descripcion=="string"?{description:n.descripcion}:{}}}function y(e,n,t){if(e===null)return null;switch(typeof e){case "string":case "boolean":return e;case "number":return Number.isFinite(e)?
19
- e:String(e);case "bigint":return `${e.toString()}n`;case "undefined":case "function":case "symbol":return}let r=e;if(r instanceof Uint8Array)return n.push(r),{$bytes:n.length-1};if(r instanceof ArrayBuffer)
20
- return n.push(new Uint8Array(r)),{$bytes:n.length-1};if(ArrayBuffer.isView(r))return n.push(new Uint8Array(r.buffer,r.byteOffset,r.byteLength)),{$bytes:n.length-1};if(r instanceof URL)return {$url:r.href};
21
- if(r instanceof Date)return {$date:Number.isNaN(r.getTime())?"invalid":r.toISOString()};if(t.has(r))return "$cycle";t.add(r);try{if(Array.isArray(r))return r.map(s=>{let a=y(s,n,t);return a===void 0?null:
22
- a});if(r instanceof Map)return {$map:[...r.entries()].map(s=>y(s,n,t))};if(r instanceof Set)return {$set:[...r.values()].map(s=>y(s,n,t))};let o={};for(let s of Object.keys(r).sort()){let a=y(r[s],n,t);
23
- a!==void 0&&(o[s]=a);}return o}finally{t.delete(r);}}async function E(e){let n=globalThis.crypto?.subtle;if(n){let r=new Uint8Array(await n.digest("SHA-256",e));return Array.from(r,o=>o.toString(16).padStart(
24
- 2,"0")).join("")}let t="";for(let r=0;r<e.length;r+=32768)t+=String.fromCharCode(...e.subarray(r,r+32768));return sha256(t)}async function ae(e){try{let n=[],t=JSON.stringify(y(e,n,new WeakSet))??"null",r=await Promise.
25
- all(n.map(E)),o=new TextEncoder().encode(`${t}|${r.join(",")}`);return await E(o)}catch{return null}}async function z(e,n){let t=await ae(n.proyeccion),r=`${n.context?.authorizationScope??"default"}|b\
26
- vsala-adapter:${t??"none"}`;return {model:n.model,messages:n.messages,...n.tools.length>0?{tools:n.tools}:{},...typeof n.temperature=="number"?{temperature:n.temperature}:{},...typeof n.seed=="number"?
27
- {seed:n.seed}:{},context:{...n.context??{},authorizationScope:r},savings:re(e.savings,n.savings,{...n.restricciones,...t===null?{sinReutilizar:true}:{}})}}function c(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function N(e){let n=e;try{if(typeof n.getType=="function")return n.getType();if(typeof n._getType=="function")return n._getType()}catch{}
28
- return typeof n.type=="string"?n.type:""}function v(e){return typeof e=="string"?e:Array.isArray(e)?e.filter(n=>c(n)&&n.type==="text"&&typeof n.text=="string").map(n=>n.text).join(`
29
- `):""}function U(e){if(typeof e=="string")return e;try{return JSON.stringify(e??{})}catch{return "{}"}}function b(e){return (Array.isArray(e.tool_calls)?e.tool_calls:[]).filter(c).map(t=>({...typeof t.id==
30
- "string"?{id:t.id}:{},name:String(t.name??""),arguments:U(t.args)}))}function le(e,n){let t=[],r=v(e?.content);r!==""&&t.push({role:"system",content:r});for(let o of n){if(!c(o))continue;let s=N(o);if(s===
8
+ ultCostModel","exactCache","exactCacheTtlMs","resolvers","exactTokenizer","silenceWarnings","failOpen"];function j(n,e){return e instanceof LicenseError||n.errorDelMarco&&n.errorDelMarco.error===e?false:n.invocada&&
9
+ n.completada}var I="[bvsala] the model answered, but the savings layer failed while accounting for it; the answer is returned unchanged, without a savings report and without a usage record.";function q(n,e){
10
+ let t=O(),r={};for(let a of ee)e[a]!==void 0&&(r[a]=e[a]);let o=createSavingsLayer({...r,provider:T(n,t)}),s=(a,i)=>_(e.silenceWarnings,`${n}:${a}`,i);return {ejecutar(a,i){return t.run(i,()=>o.generate(a))},async generar(a,i){
11
+ try{let l=await t.run(i,()=>o.generate(a));return i.invocada?{tipo:"vivo",result:l}:{tipo:"atajo",result:l}}catch(l){if(j(i,l))return s("sin-informe",I),{tipo:"sin-informe"};throw l}},async transmitir(a,i,l){
12
+ let u=t.run(i,()=>o.generateStream(a,l?{signal:l}:{})).result;i.resultado=u,u.catch(()=>{});let f;try{f=await Promise.race([i.abierta.then(()=>"abierta"),u.then(()=>"resuelta")]);}catch(h){if(j(i,h))return s(
13
+ "sin-informe",I),{tipo:"sin-informe"};throw h}if(f==="abierta")return {tipo:"abierto"};let g=await u;return i.invocada?{tipo:"entero",result:g}:{tipo:"atajo",result:g}},informe(a){let i=JSON.parse(JSON.
14
+ stringify(a));if(e.onReport)try{e.onReport(i);}catch{s("onreport","[bvsala] `onReport` threw; the error was caught and the model call was not affected.");}return i},avisar:s,close(){o.close();}}}var te={router:false,compression:false,elision:false,serialization:false,toolDiscovery:false,modelCascade:"off",outputCodebook:false,deferrable:false,semanticCache:false,verification:false};
15
+ function re(n,e,t={}){let r=o=>n?.[o]!==false&&e?.[o]!==false;return {...te,exactCache:r("exactCache")&&t.sinReutilizar!==true,singleflight:r("singleflight")&&t.sinReutilizar!==true,deterministicResolvers:r("det\
16
+ erministicResolvers")&&t.sinResolvers!==true}}function oe(n,e){let t=n.readOnlyTools;if(typeof t=="function")try{return t(e)===!0}catch{return false}return Array.isArray(t)?t.includes(e):false}function R(n,e){
17
+ let t=e.delProveedor!==true&&oe(n,e.nombre);return {id:e.nombre,intents:[],risk:t?"read":"write",...e.parametros!==void 0&&e.parametros!==null&&typeof e.parametros=="object"?{parameters:e.parametros}:{},
18
+ ...typeof e.descripcion=="string"?{description:e.descripcion}:{}}}function y(n,e,t){if(n===null)return null;switch(typeof n){case "string":case "boolean":return n;case "number":return Number.isFinite(n)?
19
+ n:String(n);case "bigint":return `${n.toString()}n`;case "undefined":case "function":case "symbol":return}let r=n;if(r instanceof Uint8Array)return e.push(r),{$bytes:e.length-1};if(r instanceof ArrayBuffer)
20
+ return e.push(new Uint8Array(r)),{$bytes:e.length-1};if(ArrayBuffer.isView(r))return e.push(new Uint8Array(r.buffer,r.byteOffset,r.byteLength)),{$bytes:e.length-1};if(r instanceof URL)return {$url:r.href};
21
+ if(r instanceof Date)return {$date:Number.isNaN(r.getTime())?"invalid":r.toISOString()};if(t.has(r))return "$cycle";t.add(r);try{if(Array.isArray(r))return r.map(s=>{let a=y(s,e,t);return a===void 0?null:
22
+ a});if(r instanceof Map)return {$map:[...r.entries()].map(s=>y(s,e,t))};if(r instanceof Set)return {$set:[...r.values()].map(s=>y(s,e,t))};let o={};for(let s of Object.keys(r).sort()){let a=y(r[s],e,t);
23
+ a!==void 0&&(o[s]=a);}return o}finally{t.delete(r);}}async function E(n){let e=globalThis.crypto?.subtle;if(e){let r=new Uint8Array(await e.digest("SHA-256",n));return Array.from(r,o=>o.toString(16).padStart(
24
+ 2,"0")).join("")}let t="";for(let r=0;r<n.length;r+=32768)t+=String.fromCharCode(...n.subarray(r,r+32768));return sha256(t)}async function ae(n){try{let e=[],t=JSON.stringify(y(n,e,new WeakSet))??"null",r=await Promise.
25
+ all(e.map(E)),o=new TextEncoder().encode(`${t}|${r.join(",")}`);return await E(o)}catch{return null}}async function $(n,e){let t=await ae(e.proyeccion),r=`${e.context?.authorizationScope??"default"}|b\
26
+ vsala-adapter:${t??"none"}`;return {model:e.model,messages:e.messages,...e.tools.length>0?{tools:e.tools}:{},...typeof e.temperature=="number"?{temperature:e.temperature}:{},...typeof e.seed=="number"?
27
+ {seed:e.seed}:{},context:{...e.context??{},authorizationScope:r},savings:re(n.savings,e.savings,{...e.restricciones,...t===null?{sinReutilizar:true}:{}})}}function c(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function N(n){let e=n;try{if(typeof e.getType=="function")return e.getType();if(typeof e._getType=="function")return e._getType()}catch{}
28
+ return typeof e.type=="string"?e.type:""}function v(n){return typeof n=="string"?n:Array.isArray(n)?n.filter(e=>c(e)&&e.type==="text"&&typeof e.text=="string").map(e=>e.text).join(`
29
+ `):""}function U(n){if(typeof n=="string")return n;try{return JSON.stringify(n??{})}catch{return "{}"}}function b(n){return (Array.isArray(n.tool_calls)?n.tool_calls:[]).filter(c).map(t=>({...typeof t.id==
30
+ "string"?{id:t.id}:{},name:String(t.name??""),arguments:U(t.args)}))}function le(n,e){let t=[],r=v(n?.content);r!==""&&t.push({role:"system",content:r});for(let o of e){if(!c(o))continue;let s=N(o);if(s===
31
31
  "system")t.push({role:"system",content:v(o.content)});else if(s==="human")t.push({role:"user",content:v(o.content)});else if(s==="ai"){let a=b(o);t.push({role:"assistant",content:v(o.content),...a.length>
32
32
  0?{toolCalls:a}:{}});}else s==="tool"&&t.push({role:"tool",content:typeof o.content=="string"?o.content:U(o.content),...typeof o.tool_call_id=="string"?{toolCallId:o.tool_call_id}:{},...typeof o.name==
33
- "string"?{name:o.name}:{}});}return t}function $(e){if(!c(e))return e;let n=e.content,t=N(e),r={};for(let[o,s]of Object.entries(c(e.additional_kwargs)?e.additional_kwargs:{}))o==="tool_calls"||o==="fun\
34
- ction_call"||o.startsWith("__")||(r[o]=s);return {type:t,name:e.name,content:t==="tool"?n:typeof n=="string"?"$text":Array.isArray(n)?n.map(o=>c(o)&&o.type==="text"?{...o,text:void 0,id:void 0}:o):n,tool_calls:b(
35
- e).map(o=>({name:o.name,arguments:o.arguments})),status:e.status,additional_kwargs:r}}function ue(e,n){let t=[],r=[];for(let o of n){if(!c(o))continue;if(typeof o.name=="string"&&"schema"in o){let a,i=true;
36
- try{a=toJsonSchema(o.schema);}catch{i=false;}let l=R(e,{nombre:o.name,descripcion:typeof o.description=="string"?o.description:void 0,parametros:a});t.push(i?l:{...l,risk:"write"}),r.push({name:o.name,description:o.
37
- description,parametros:a,returnDirect:o.returnDirect});continue}let s=c(o.function)?o.function:void 0;if(o.type==="function"&&s&&typeof s.name=="string"){t.push(R(e,{nombre:s.name,descripcion:typeof s.
38
- description=="string"?s.description:void 0,parametros:s.parameters})),r.push(o);continue}t.push(R(e,{nombre:String(o.name??o.type??"server-tool"),delProveedor:true})),r.push(o);}return {tools:t,definiciones:r}}
39
- function F(e){try{let n=e._llmType?.();return typeof n=="string"?n:""}catch{return ""}}function de(e){if(typeof e=="string")return e.replace(":","/");let n=e,t=typeof n.model=="string"?n.model:typeof n.
40
- modelName=="string"?n.modelName:"";if(t.includes("/"))return t;let r=F(e);return r!==""?`${r}/${t}`:t}function fe(e){try{return e.invocationParams?.({})??null}catch{return null}}function pe(e){let n=e.
41
- toolChoice;return n==="required"||c(n)?true:e.responseFormat!==void 0}function p(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function ge(e,n){let t=c(e.usage_metadata)?e.usage_metadata:void 0,
42
- r=p(t?.input_tokens),o=p(t?.output_tokens),s=c(t?.input_token_details)?t.input_token_details:{},a=c(t?.output_token_details)?t.output_token_details:{},i=p(s.cache_read),l=p(s.cache_creation),d=p(a.reasoning),
43
- u=r!==void 0&&(i??0)+(l??0)<=r;return {inputTokens:r??0,outputTokens:o??0,...i!==void 0?{cachedInputTokens:i}:{},...l!==void 0?{cacheWriteInputTokens:l}:{},...d!==void 0?{reasoningTokens:d}:{},...n&&r!==
44
- void 0&&o!==void 0&&u?{}:{complete:false}}}function me(e){let n=c(e.response_metadata)?e.response_metadata:{},t=n.finish_reason??n.stop_reason??n.finishReason;switch(t){case "end_turn":case "stop":case "STO\
45
- P":return "stop";case "max_tokens":case "length":case "MAX_TOKENS":return "length";case "tool_use":case "tool_calls":return "tool_calls";default:return typeof t=="string"?t:void 0}}function ye(e,n){if(n!=="st\
46
- op")return false;if(Array.isArray(e.content)){for(let r of e.content)if(!c(r)||r.type!=="text"||Array.isArray(r.citations)&&r.citations.length>0||Array.isArray(r.annotations)&&r.annotations.length>0)return false}
47
- let t=c(e.additional_kwargs)?e.additional_kwargs:{};return !("reasoning"in t||"reasoning_content"in t||"audio"in t)}function ve(e,n){let t=me(e),r=b(e);return {text:v(e.content),...r.length>0?{toolCalls:r}:
48
- {},...t?{finishReason:t}:{},usage:ge(e,n),...ye(e,t)?{}:{replayable:false}}}function he(e,n){return new AIMessage({content:e.text,tool_calls:(e.toolCalls??[]).map((t,r)=>{let o={};try{let s=JSON.parse(t.arguments||
49
- "{}");c(s)&&(o=s);}catch{o={};}return {id:t.id??`bvsala-${e.requestId}-${r}`,name:t.name,args:o,type:"tool_call"}}),usage_metadata:{input_tokens:0,output_tokens:0,total_tokens:0},response_metadata:n?{bivelio:{
50
- savingsReport:n}}:{}})}var we="[bvsala] this LangChain model has its own `cache` set, so a usage counter can be a cached copy rather than a provider bill; its calls are reported as unmeasured.";function qe(e){
51
- let n=q("langchain",e),t=e.reportInMetadata!==false,r=createMiddleware({name:"BivelioSavings",wrapModelCall:async(o,s)=>{let a=o,i=a.model,l=F(i),d=c(i)&&i.cache!==void 0&&i.cache!==null&&i.cache!==false;d&&n.avisar("cac\
52
- he-del-modelo",we);let u=!l.toLowerCase().includes("fake")&&!d,f=e.modelId?e.modelId(i):de(i),g=Array.isArray(a.messages)?a.messages:[],{tools:h,definiciones:J}=ue(e,Array.isArray(a.tools)?a.tools:[]),
53
- B=pe(a),W=p(i?.temperature),m=await z(e,{model:f,messages:le(a.systemMessage,g),tools:h,temperature:W,context:e.context?.(o),proyeccion:{modelo:f,parametros:fe(i),sistema:$(a.systemMessage),mensajes:g.
54
- map($),herramientas:J,toolChoice:a.toolChoice,responseFormat:a.responseFormat,modelSettings:a.modelSettings},restricciones:{sinResolvers:B,sinReutilizar:a.responseFormat!==void 0}}),w,x=L({model:m.model,
55
- messages:m.messages,...m.tools?{tools:m.tools}:{}},async()=>(w=await P(x,async()=>s(o)),ve(w,u))),S=await n.generar(m,x);if(S.tipo==="sin-informe")return w;let k=n.informe(S.result.report);if(S.tipo===
56
- "atajo")return he(S.result,t?k:void 0);let A=w;return t&&(A.response_metadata={...A.response_metadata??{},bivelio:{savingsReport:k}}),A}});return Object.assign(r,{close:()=>n.close()})}function Ee(e){
57
- let n=e?.response_metadata,t=c(n)?n.bivelio:void 0,r=c(t)?t.savingsReport:void 0;if(!(!c(r)||typeof r.requestId!="string"))return r}export{Ee as getSavingsReportFromMessage,qe as savingsAgentMiddleware};
33
+ "string"?{name:o.name}:{}});}return t}function z(n){if(!c(n))return n;let e=n.content,t=N(n),r={};for(let[o,s]of Object.entries(c(n.additional_kwargs)?n.additional_kwargs:{}))o==="tool_calls"||o==="fun\
34
+ ction_call"||o.startsWith("__")||(r[o]=s);return {type:t,name:n.name,content:t==="tool"?e:typeof e=="string"?"$text":Array.isArray(e)?e.map(o=>c(o)&&o.type==="text"?{...o,text:void 0,id:void 0}:o):e,tool_calls:b(
35
+ n).map(o=>({name:o.name,arguments:o.arguments})),status:n.status,additional_kwargs:r}}function ue(n,e){let t=[],r=[];for(let o of e){if(!c(o))continue;if(typeof o.name=="string"&&"schema"in o){let a,i=true;
36
+ try{a=toJsonSchema(o.schema);}catch{i=false;}let l=R(n,{nombre:o.name,descripcion:typeof o.description=="string"?o.description:void 0,parametros:a});t.push(i?l:{...l,risk:"write"}),r.push({name:o.name,description:o.
37
+ description,parametros:a,returnDirect:o.returnDirect});continue}let s=c(o.function)?o.function:void 0;if(o.type==="function"&&s&&typeof s.name=="string"){t.push(R(n,{nombre:s.name,descripcion:typeof s.
38
+ description=="string"?s.description:void 0,parametros:s.parameters})),r.push(o);continue}t.push(R(n,{nombre:String(o.name??o.type??"server-tool"),delProveedor:true})),r.push(o);}return {tools:t,definiciones:r}}
39
+ function F(n){try{let e=n._llmType?.();return typeof e=="string"?e:""}catch{return ""}}function de(n){if(typeof n=="string")return n.replace(":","/");let e=n,t=e._defaultConfig;if(t&&typeof t=="object"&&
40
+ typeof t.model=="string"&&t.model!=="")return t.model.includes("/")?t.model:typeof t.modelProvider=="string"&&t.modelProvider!==""?`${t.modelProvider}/${t.model}`:t.model;let r=typeof e.model=="string"?
41
+ e.model:typeof e.modelName=="string"?e.modelName:"";if(r.includes("/"))return r;let o=F(n);return o!==""?`${o}/${r}`:r}function fe(n){try{return n.invocationParams?.({})??null}catch{return null}}function pe(n){
42
+ let e=n.toolChoice;return e==="required"||c(e)?true:n.responseFormat!==void 0}function p(n){return typeof n=="number"&&Number.isFinite(n)?n:void 0}function ge(n,e){let t=c(n.usage_metadata)?n.usage_metadata:
43
+ void 0,r=p(t?.input_tokens),o=p(t?.output_tokens),s=c(t?.input_token_details)?t.input_token_details:{},a=c(t?.output_token_details)?t.output_token_details:{},i=p(s.cache_read),l=p(s.cache_creation),d=p(
44
+ a.reasoning),u=r!==void 0&&(i??0)+(l??0)<=r;return {inputTokens:r??0,outputTokens:o??0,...i!==void 0?{cachedInputTokens:i}:{},...l!==void 0?{cacheWriteInputTokens:l}:{},...d!==void 0?{reasoningTokens:d}:
45
+ {},...e&&r!==void 0&&o!==void 0&&u?{}:{complete:false}}}function me(n){let e=c(n.response_metadata)?n.response_metadata:{},t=e.finish_reason??e.stop_reason??e.finishReason;switch(t){case "end_turn":case "s\
46
+ top":case "STOP":return "stop";case "max_tokens":case "length":case "MAX_TOKENS":return "length";case "tool_use":case "tool_calls":return "tool_calls";default:return typeof t=="string"?t:void 0}}function ye(n,e){
47
+ if(e!=="stop")return false;if(Array.isArray(n.content)){for(let r of n.content)if(!c(r)||r.type!=="text"||Array.isArray(r.citations)&&r.citations.length>0||Array.isArray(r.annotations)&&r.annotations.length>
48
+ 0)return false}let t=c(n.additional_kwargs)?n.additional_kwargs:{};return !("reasoning"in t||"reasoning_content"in t||"audio"in t)}function ve(n,e){let t=me(n),r=b(n);return {text:v(n.content),...r.length>0?
49
+ {toolCalls:r}:{},...t?{finishReason:t}:{},usage:ge(n,e),...ye(n,t)?{}:{replayable:false}}}function he(n,e){return new AIMessage({content:n.text,tool_calls:(n.toolCalls??[]).map((t,r)=>{let o={};try{let s=JSON.parse(
50
+ t.arguments||"{}");c(s)&&(o=s);}catch{o={};}return {id:t.id??`bvsala-${n.requestId}-${r}`,name:t.name,args:o,type:"tool_call"}}),usage_metadata:{input_tokens:0,output_tokens:0,total_tokens:0},response_metadata:e?
51
+ {bivelio:{savingsReport:e}}:{}})}var we="[bvsala] this LangChain model has its own `cache` set, so a usage counter can be a cached copy rather than a provider bill; its calls are reported as unmeasure\
52
+ d.";function qe(n){let e=q("langchain",n),t=n.reportInMetadata!==false,r=createMiddleware({name:"BivelioSavings",wrapModelCall:async(o,s)=>{let a=o,i=a.model,l=F(i),d=c(i)&&i.cache!==void 0&&i.cache!==null&&i.cache!==
53
+ false;d&&e.avisar("cache-del-modelo",we);let u=!l.toLowerCase().includes("fake")&&!d,f=n.modelId?n.modelId(i):de(i),g=Array.isArray(a.messages)?a.messages:[],{tools:h,definiciones:J}=ue(n,Array.isArray(a.
54
+ tools)?a.tools:[]),B=pe(a),W=p(i?.temperature),m=await $(n,{model:f,messages:le(a.systemMessage,g),tools:h,temperature:W,context:n.context?.(o),proyeccion:{modelo:f,parametros:fe(i),sistema:z(a.systemMessage),
55
+ mensajes:g.map(z),herramientas:J,toolChoice:a.toolChoice,responseFormat:a.responseFormat,modelSettings:a.modelSettings},restricciones:{sinResolvers:B,sinReutilizar:a.responseFormat!==void 0}}),w,k=P({
56
+ model:m.model,messages:m.messages,...m.tools?{tools:m.tools}:{}},async()=>(w=await L(k,async()=>s(o)),ve(w,u))),S=await e.generar(m,k);if(S.tipo==="sin-informe")return w;let x=e.informe(S.result.report);
57
+ if(S.tipo==="atajo")return he(S.result,t?x:void 0);let A=w;return t&&(A.response_metadata={...A.response_metadata??{},bivelio:{savingsReport:x}}),A}});return Object.assign(r,{close:()=>e.close()})}function Ee(n){
58
+ let e=n?.response_metadata,t=c(e)?e.bivelio:void 0,r=c(t)?t.savingsReport:void 0;if(!(!c(r)||typeof r.requestId!="string"))return r}export{Ee as getSavingsReportFromMessage,qe as savingsAgentMiddleware};