@bivelio/savings-layer 0.4.29 → 0.4.31
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 +80 -6
- package/dist/adapters/anthropic-sdk.js +69 -66
- package/dist/gateway/bin.js +759 -698
- package/dist/index.d.ts +131 -17
- package/dist/index.js +1306 -1225
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -269,6 +269,34 @@ does not expose its base URL), the rule applies as it always has. The layer read
|
|
|
269
269
|
configured base URL, not the wire: a custom `fetch` that sends the request elsewhere
|
|
270
270
|
is not seen.
|
|
271
271
|
|
|
272
|
+
**Provider refusals.** A refusal is measured like any other call and is never cached,
|
|
273
|
+
repaired or replayed by reference. One kind is priced at 0: an Anthropic
|
|
274
|
+
`stop_reason: "refusal"` that arrives before any output (no content block, zero output
|
|
275
|
+
tokens) with `stop_details.category` set to `cyber`, `general_harms` or `null`. Anthropic
|
|
276
|
+
documents these as
|
|
277
|
+
[not billed](https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback#how-refusals-are-billed).
|
|
278
|
+
Its tokens still appear in `report.tokens`. `cost.actual`, `cost.baselineEstimate` and
|
|
279
|
+
`cost.netSavings` are all 0, so the request credits no saving: not measured, not
|
|
280
|
+
estimated and not by reference. A concurrent identical request served from it credits
|
|
281
|
+
nothing either. The same rule applies in the gateway. Any other refusal is priced from
|
|
282
|
+
its `usage` as usual. That covers `bio`, `frontier_llm` and `reasoning_extraction`, a
|
|
283
|
+
refusal after partial output, and a category the table does not list.
|
|
284
|
+
|
|
285
|
+
Most refusals carry no signal from the API: the model writes "I can't help with that."
|
|
286
|
+
as ordinary text and ends with `end_turn` or `stop`. The layer also looks for these,
|
|
287
|
+
with a conservative text heuristic. It checks only the opening of the answer, or the
|
|
288
|
+
whole answer when it is short, and it needs a real refusal clause, such as "I'm sorry,
|
|
289
|
+
but I can't help with that", "I cannot assist" or "As an AI, I cannot provide". A
|
|
290
|
+
single word is not enough. It covers English, Spanish, Catalan, French, German,
|
|
291
|
+
Italian and Portuguese. A match is measured and served as usual. It is not written to
|
|
292
|
+
the exact or semantic cache, and it is not replayed by reference. A concurrent
|
|
293
|
+
identical request served from it is `estimated`. The trace says why
|
|
294
|
+
(`skipReason: "text_refusal"`). A refusal already stored by an earlier version is not
|
|
295
|
+
served either. In the gateway, the coalesced twin of such a response is `estimated`
|
|
296
|
+
(`primary-text-refusal`). A false positive costs one cache hit. A normal answer that
|
|
297
|
+
starts with "Sorry for the delay, here is…", or has "I can't" later on, is cached as
|
|
298
|
+
before.
|
|
299
|
+
|
|
272
300
|
**Streaming.** `generateStream()` takes the same request and hands you the answer as
|
|
273
301
|
the provider writes it, then the same `SavingsReport`:
|
|
274
302
|
|
|
@@ -698,7 +726,11 @@ const placedLayer = createSavingsLayer({
|
|
|
698
726
|
Calibrated reuse and `cacheBreakpoints: "auto"` learn from your traffic, and by default
|
|
699
727
|
what they learn lives in the process. On serverless platforms (Vercel Functions, AWS
|
|
700
728
|
Lambda) an instance may serve one request or live a few minutes, so neither would ever
|
|
701
|
-
get past its starting point. Give each one somewhere to keep it
|
|
729
|
+
get past its starting point. Give each one somewhere to keep it. Giving the layer a
|
|
730
|
+
`semanticCache` only says where reworded questions are kept; `semanticCacheDefault: true`
|
|
731
|
+
is what switches reuse on for every request (a request's own `savings.semanticCache`
|
|
732
|
+
still wins), and without it, or `savings.semanticCache` on each request, the cache
|
|
733
|
+
stays empty and learns nothing:
|
|
702
734
|
|
|
703
735
|
```ts
|
|
704
736
|
import { InMemorySemanticCache } from "@bivelio/savings-layer";
|
|
@@ -718,6 +750,8 @@ async function serverlessLayer(kv: SharedKeyValue) {
|
|
|
718
750
|
provider,
|
|
719
751
|
licenseKey: process.env.BIVELIO_LICENSE_KEY,
|
|
720
752
|
semanticCache,
|
|
753
|
+
// Passing the cache does not switch it on; this asks for it on every request.
|
|
754
|
+
semanticCacheDefault: true,
|
|
721
755
|
cacheBreakpoints: "auto",
|
|
722
756
|
cacheBreakpointStore: {
|
|
723
757
|
get: (key) => kv.get(key),
|
|
@@ -789,6 +823,35 @@ console.log(data.answer); // 8
|
|
|
789
823
|
|
|
790
824
|
</details>
|
|
791
825
|
|
|
826
|
+
<details>
|
|
827
|
+
<summary><b>Output cap — with or without a contract</b></summary>
|
|
828
|
+
|
|
829
|
+
`maxOutputTokens` on the request caps the answer of any request, free text
|
|
830
|
+
included. With `response.maxOutputTokens` as well, the smaller of the two is sent.
|
|
831
|
+
|
|
832
|
+
```ts
|
|
833
|
+
const { text, finishReason } = await layer.generate({
|
|
834
|
+
model: "openai/gpt-5-mini",
|
|
835
|
+
messages: [{ role: "user", content: "Summarize this in two sentences: …" }],
|
|
836
|
+
maxOutputTokens: 300,
|
|
837
|
+
});
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
On the wire it becomes Anthropic's `max_tokens`; on Chat Completions,
|
|
841
|
+
`max_completion_tokens` when `baseUrl` is OpenAI's own API (`max_tokens` is
|
|
842
|
+
deprecated there and its reasoning models, o-series and gpt-5.x, reject it) and
|
|
843
|
+
`max_tokens` on any other OpenAI-compatible endpoint, which is the field they all
|
|
844
|
+
understand. If an endpoint answers 400 naming the field it got as unsupported, the
|
|
845
|
+
request is repeated once with the other one (nothing was generated, so nothing is
|
|
846
|
+
billed twice) and the model is remembered. To choose the field yourself:
|
|
847
|
+
`new OpenAICompatibleProvider({ baseUrl, maxTokensParam: "max_completion_tokens" })`.
|
|
848
|
+
|
|
849
|
+
The cap is yours, not a saving: it is never credited as output avoided. An answer
|
|
850
|
+
it cut (`finishReason: "length"`) is not cached, and
|
|
851
|
+
`report.qualityLedger.truncation` says the limit was yours.
|
|
852
|
+
|
|
853
|
+
</details>
|
|
854
|
+
|
|
792
855
|
<details>
|
|
793
856
|
<summary><b>Tool calls</b></summary>
|
|
794
857
|
|
|
@@ -849,13 +912,21 @@ each result answers and its `isError`. Two conversations that differ only there
|
|
|
849
912
|
share a cached answer; the fresh call ids of a new run of the same loop are not a
|
|
850
913
|
difference.
|
|
851
914
|
|
|
915
|
+
**Which tools reach the model.** The layer never cuts a catalogue it cannot rank. If
|
|
916
|
+
your tools declare no `intents` and no `risk` (the usual shape of MCP and OpenAI tool
|
|
917
|
+
lists), or you pass `savings: { router: false }`, every tool goes to the provider
|
|
918
|
+
exactly as you sent it, and the layer credits nothing for the tool block. Only when
|
|
919
|
+
your tools declare `intents`/`risk` does the selector keep the relevant ones, up to
|
|
920
|
+
`new ToolSelector({ maxTools })` (8 by default), and hold back write tools from a
|
|
921
|
+
request that is not an action. Whatever it leaves out, the model cannot call in that
|
|
922
|
+
request: the report's `tool-selector` trace lists it, the tokens count as eliminated
|
|
923
|
+
input, and the SDK warns once per process (`silenceWarnings` turns the warning off).
|
|
924
|
+
|
|
852
925
|
**Large catalogues: let the provider search them (opt‑in).** With hundreds of tools
|
|
853
926
|
(several MCP servers, say), `savings: { toolSearch: "auto" }` sends every tool deferred
|
|
854
|
-
behind the provider's own tool search instead of
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
search the model looks up what it needs, no tool you declared is out of reach, and the
|
|
858
|
-
cached prompt prefix stays the same from one question to the next.
|
|
927
|
+
behind the provider's own tool search instead of putting the whole catalogue in every
|
|
928
|
+
request's prompt. The model looks up what it needs, no tool you declared is out of
|
|
929
|
+
reach, and the cached prompt prefix stays the same from one question to the next.
|
|
859
930
|
|
|
860
931
|
- **Where.** The Anthropic adapter, on models in Anthropic's tool search compatibility
|
|
861
932
|
table. OpenAI documents its tool search for the Responses and Agents APIs, not for
|
|
@@ -1044,6 +1115,9 @@ provider invoice; `estimated` has its own column on purpose.
|
|
|
1044
1115
|
- **Set `serviceAccountKey` on every long‑running deployment.** Renewals mint a new
|
|
1045
1116
|
license token each billing period; with the service‑account key (`bvk_…`, same
|
|
1046
1117
|
dashboard) the SDK picks the renewed token up by itself, without a restart.
|
|
1118
|
+
If that pickup is rejected (HTTP 401/403/404: rotated key, key from another
|
|
1119
|
+
organization, wrong `licenseServerUrl`), the SDK warns once per cause even with
|
|
1120
|
+
`silenceWarnings: true`, because the license will stop renewing.
|
|
1047
1121
|
- **Set `BVSALA_SEAT_ID` (one stable value per replica) on Kubernetes or any fleet
|
|
1048
1122
|
with dynamic hostnames**, so each replica counts as what it is:
|
|
1049
1123
|
|
|
@@ -1,101 +1,104 @@
|
|
|
1
1
|
import {createSavingsLayer,LicenseError,sha256,stableStringify}from'@bivelio/savings-layer';import {AsyncLocalStorage}from'async_hooks';var x=t=>typeof t=="number"&&Number.isFinite(t)&&t>0?t:0;function J(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+=x(r.input_tokens),e.output+=x(r.output_tokens),e.cacheRead+=x(r.cache_read_input_tokens);let o=r.cache_creation,
|
|
3
3
|
s=x(o?.ephemeral_5m_input_tokens),a=x(o?.ephemeral_1h_input_tokens),i=typeof r.cache_creation_input_tokens=="number"?x(r.cache_creation_input_tokens):s+a;e.cacheWrite+=i,e.cacheWrite1h+=Math.min(a,i);}
|
|
4
|
-
return e.iteraciones>0?e:null}function
|
|
4
|
+
return e.iteraciones>0?e:null}function B(t,e,n){let r=t??{},o=J(r.iterations),s=W(r.cache_read_input_tokens,o?.cacheRead),a=W(r.cache_creation_input_tokens,o?.cacheWrite),i=r.cache_creation?.ephemeral_1h_input_tokens,u=typeof i=="\
|
|
5
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
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 W(t,e){return e?(t??0)+e:t}function
|
|
8
|
-
function
|
|
7
|
+
h||o?{}:{reasoningTokens:0},...m||h?{complete:false}:{}},entradaAusente:m,salidaAusente:h}}function W(t,e){return e?(t??0)+e:t}function F(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
9
|
...n,...r?{cache_creation:r}:{}}}function V(t){return t==="thinking"||t==="redacted_thinking"}function G(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
|
|
11
|
-
"
|
|
10
|
+
tool_calls";case "refusal":return "content_filter";default:return t}}var ye=new Set([null,"cyber","general_harms"]);function H(t){if(t.stopReason!=="refusal")return false;let e=t.stopDetails;if(e===null||typeof e!=
|
|
11
|
+
"object"||Array.isArray(e))return false;let n=e;if(n.type!=="refusal"||!("category"in n))return false;let r=n.category;return !(r!==null&&typeof r!="string"||!ye.has(r)||t.bloques!==0||t.outputTokens!==0||Array.
|
|
12
|
+
isArray(t.iterations)&&t.iterations.length>1)}var ve={"api.anthropic.com":"anthropic","api.openai.com":"openai","generativelanguage.googleapis.com":"google"};function be(t){if(typeof t!="string")return null;try{let e=new URL(t);return e.protocol!==
|
|
13
|
+
"https:"||e.port!==""?null:ve[e.hostname.toLowerCase()]??null}catch{return null}}function K(t){return be(t)!==null?"oficial":"no-oficial"}var Z=Symbol.for("@bivelio/savings-layer:provider-origin");var L=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(
|
|
12
14
|
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,
|
|
13
15
|
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(
|
|
14
16
|
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.
|
|
15
17
|
crPendiente=true),o++,s=o;}return this.resto+=r.slice(s),n}linea(e,n){if(e===""){this.despachar(n);return}if(e.charCodeAt(0)===58)return;let r=e.indexOf(":"),o,s;switch(r===-1?(o=e,s=""):(o=e.slice(0,r),
|
|
16
18
|
s=e.slice(r+1),s.charCodeAt(0)===32&&(s=s.slice(1))),o){case "data":this.datos+=s+`
|
|
17
19
|
`;break;case "event":this.evento=s;break;case "id":s.includes("\0")||(this.ultimoId=s);break;}}despachar(e){if(this.datos===""){this.evento="";return}e.push({event:this.evento===""?"message":
|
|
18
|
-
this.evento,data:this.datos.slice(0,-1),...this.ultimoId!==void 0?{id:this.ultimoId}:{}}),this.datos="",this.evento="";}};var
|
|
19
|
-
nothing more was sent.",
|
|
20
|
-
completada:false,...r!==void 0?{origen:r}:{}}}function
|
|
21
|
-
model&&
|
|
22
|
-
return s.invocada=true,s},r={id:`bivelio-adapter:${t}`,async generate(o,s){return n(o).generar(s?.signal)},async*generateStream(o,s){let a=n(o);if(a.transmitir){yield*a.transmitir(s?.signal);return}
|
|
23
|
-
generar(s?.signal);i.text!==""&&(yield {type:"text",delta:i.text}),yield {type:"done",response:i};}};return Object.defineProperty(r,
|
|
24
|
-
ultCostModel","exactCache","exactCacheTtlMs","resolvers","exactTokenizer","silenceWarnings","failOpen"];function
|
|
25
|
-
t.completada}var
|
|
26
|
-
let n=
|
|
27
|
-
try{let u=await n.run(i,()=>o.generate(a));return i.invocada?{tipo:"vivo",result:u}:{tipo:"atajo",result:u}}catch(u){if(
|
|
28
|
-
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(
|
|
29
|
-
"sin-informe",
|
|
30
|
-
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
|
|
20
|
+
this.evento,data:this.datos.slice(0,-1),...this.ultimoId!==void 0?{id:this.ultimoId}:{}}),this.datos="",this.evento="";}};var Q=new Set;function X(t,e,n){t===true||Q.has(e)||(Q.add(e),console.warn(n));}var Re="[bvsala] internal: the layer changed a request this adapter must forward unchanged; nothing was sent.",Se="[bvsala] internal: the layer asked for a second model call within one framework call;\
|
|
21
|
+
nothing more was sent.",we="[bvsala] internal: the layer called the model outside a framework call; nothing was sent.";function U(t,e,n,r){return {renderizado:t,generar:e,...n?{transmitir:n}:{},invocada:false,
|
|
22
|
+
completada:false,...r!==void 0?{origen:r}:{}}}function xe(t,e){if(t===e)return true;try{return stableStringify(t)===stableStringify(e)}catch{return false}}function ee(t,e){let n=t??[],r=e??[];return n.length===r.length&&n.every((o,s)=>xe(o,r[s]))}function Ae(t,e){return t.model===
|
|
23
|
+
e.model&&ee(t.messages,e.messages)&&ee(t.tools,e.tools)}function te(t,e){let n=o=>{let s=e.getStore();if(!s)throw new Error(we);if(s.invocada)throw new Error(Se);if(!Ae(o,s.renderizado))throw new Error(
|
|
24
|
+
Re);return s.invocada=true,s},r={id:`bivelio-adapter:${t}`,async generate(o,s){return n(o).generar(s?.signal)},async*generateStream(o,s){let a=n(o);if(a.transmitir){yield*a.transmitir(s?.signal);return}
|
|
25
|
+
let i=await a.generar(s?.signal);i.text!==""&&(yield {type:"text",delta:i.text}),yield {type:"done",response:i};}};return Object.defineProperty(r,Z,{value:()=>e.getStore()?.origen}),r}function ne(){return new AsyncLocalStorage}var Ce=["licenseKey","licenseServerUrl","serviceAccountKey","licenseRefresh","licenseRevocation","licensePollIntervalSeconds","licenseFetch","metering","meteringUrl","meteringFetch","costModels","defa\
|
|
26
|
+
ultCostModel","exactCache","exactCacheTtlMs","resolvers","exactTokenizer","silenceWarnings","failOpen"];function re(t,e){return e instanceof LicenseError||t.errorDelMarco&&t.errorDelMarco.error===e?false:t.invocada&&
|
|
27
|
+
t.completada}var oe="[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 se(t,e){
|
|
28
|
+
let n=ne(),r={};for(let a of Ce)e[a]!==void 0&&(r[a]=e[a]);let o=createSavingsLayer({...r,provider:te(t,n)}),s=(a,i)=>X(e.silenceWarnings,`${t}:${a}`,i);return {ejecutar(a,i){return n.run(i,()=>o.generate(a))},async generar(a,i){
|
|
29
|
+
try{let u=await n.run(i,()=>o.generate(a));return i.invocada?{tipo:"vivo",result:u}:{tipo:"atajo",result:u}}catch(u){if(re(i,u))return s("sin-informe",oe),{tipo:"sin-informe"};throw u}},async transmitir(a,i,u){
|
|
30
|
+
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(_){if(re(i,_))return s(
|
|
31
|
+
"sin-informe",oe),{tipo:"sin-informe"};throw _}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.
|
|
32
|
+
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 ae(){
|
|
31
33
|
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.
|
|
32
34
|
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||
|
|
33
35
|
this.fallo)return;this.fallo={error:e};let n=this.esperando;n&&this.pendientes.length===0&&(this.esperando=void 0,n.rechazar(e));}get cerrada(){return this.terminada||this.fallo!==void 0}[Symbol.asyncIterator](){
|
|
34
36
|
return {next:()=>this.pendientes.length>0?Promise.resolve({value:this.pendientes.shift(),done:false}):this.fallo?Promise.reject(this.fallo.error):this.terminada?Promise.resolve({value:void 0,done:true}):new Promise(
|
|
35
|
-
(e,n)=>{this.esperando={resolver:e,rechazar:n};}),return:()=>(this.terminada=true,this.pendientes.length=0,Promise.resolve({value:void 0,done:true}))}}};var
|
|
36
|
-
function
|
|
37
|
-
erministicResolvers")&&n.sinResolvers!==true}}function
|
|
38
|
-
let n=e.delProveedor!==true&&
|
|
37
|
+
(e,n)=>{this.esperando={resolver:e,rechazar:n};}),return:()=>(this.terminada=true,this.pendientes.length=0,Promise.resolve({value:void 0,done:true}))}}};var Le={router:false,compression:false,elision:false,serialization:false,toolDiscovery:false,modelCascade:"off",outputCodebook:false,deferrable:false,semanticCache:false,verification:false};
|
|
38
|
+
function Me(t,e,n={}){let r=o=>t?.[o]!==false&&e?.[o]!==false;return {...Le,exactCache:r("exactCache")&&n.sinReutilizar!==true,singleflight:r("singleflight")&&n.sinReutilizar!==true,deterministicResolvers:r("det\
|
|
39
|
+
erministicResolvers")&&n.sinResolvers!==true}}function Oe(t,e){let n=t.readOnlyTools;if(typeof n=="function")try{return n(e)===!0}catch{return false}return Array.isArray(n)?n.includes(e):false}function ce(t,e){
|
|
40
|
+
let n=e.delProveedor!==true&&Oe(t,e.nombre);return {id:e.nombre,intents:[],risk:n?"read":"write",...e.parametros!==void 0&&e.parametros!==null&&typeof e.parametros=="object"?{parameters:e.parametros}:{},
|
|
39
41
|
...typeof e.descripcion=="string"?{description:e.descripcion}:{}}}function P(t,e,n){if(t===null)return null;switch(typeof t){case "string":case "boolean":return t;case "number":return Number.isFinite(t)?
|
|
40
42
|
t:String(t);case "bigint":return `${t.toString()}n`;case "undefined":case "function":case "symbol":return}let r=t;if(r instanceof Uint8Array)return e.push(r),{$bytes:e.length-1};if(r instanceof ArrayBuffer)
|
|
41
43
|
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};
|
|
42
44
|
if(r instanceof Date)return {$date:Number.isNaN(r.getTime())?"invalid":r.toISOString()};if(n.has(r))return "$cycle";n.add(r);try{if(Array.isArray(r))return r.map(s=>{let a=P(s,e,n);return a===void 0?null:
|
|
43
45
|
a});if(r instanceof Map)return {$map:[...r.entries()].map(s=>P(s,e,n))};if(r instanceof Set)return {$set:[...r.values()].map(s=>P(s,e,n))};let o={};for(let s of Object.keys(r).sort()){let a=P(r[s],e,n);
|
|
44
|
-
a!==void 0&&(o[s]=a);}return o}finally{n.delete(r);}}async function
|
|
45
|
-
2,"0")).join("")}let n="";for(let r=0;r<t.length;r+=32768)n+=String.fromCharCode(...t.subarray(r,r+32768));return sha256(n)}async function
|
|
46
|
-
all(e.map(
|
|
46
|
+
a!==void 0&&(o[s]=a);}return o}finally{n.delete(r);}}async function ie(t){let e=globalThis.crypto?.subtle;if(e){let r=new Uint8Array(await e.digest("SHA-256",t));return Array.from(r,o=>o.toString(16).padStart(
|
|
47
|
+
2,"0")).join("")}let n="";for(let r=0;r<t.length;r+=32768)n+=String.fromCharCode(...t.subarray(r,r+32768));return sha256(n)}async function Te(t){try{let e=[],n=JSON.stringify(P(t,e,new WeakSet))??"null",r=await Promise.
|
|
48
|
+
all(e.map(ie)),o=new TextEncoder().encode(`${n}|${r.join(",")}`);return await ie(o)}catch{return null}}async function ue(t,e){let n=await Te(e.proyeccion),r=`${e.context?.authorizationScope??"default"}\
|
|
47
49
|
|bvsala-adapter:${n??"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"?
|
|
48
|
-
{seed:e.seed}:{},context:{...e.context??{},authorizationScope:r},savings:
|
|
49
|
-
{}}}function
|
|
50
|
+
{seed:e.seed}:{},context:{...e.context??{},authorizationScope:r},savings:Me(t.savings,e.savings,{...e.restricciones,...n===null?{sinReutilizar:true}:{}})}}function le(t,e){if(!(!t&&!e))return {...t??{},...e??
|
|
51
|
+
{}}}function de(t){if(!t||typeof t!="object")return {};let e=t,n={};return e.context&&typeof e.context=="object"&&(n.context=e.context),e.savings&&typeof e.savings=="object"&&(n.savings=e.savings),n}var I="[bvsala] could not read the request body; nothing was sent.";function p(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function Ee(t){if((t.method??"GET").toUpperCase()!=="POST")return false;
|
|
50
52
|
try{return new URL(t.url).pathname.replace(/\/+$/,"").endsWith("/v1/messages")}catch{return false}}function A(t){return typeof t=="string"?[{type:"text",text:t}]:Array.isArray(t)?t.filter(p):[]}function M(t){
|
|
51
53
|
return t.filter(e=>e.type==="text"&&typeof e.text=="string").map(e=>e.text).join(`
|
|
52
|
-
`)}function
|
|
54
|
+
`)}function me(t){if(typeof t=="string")return t;try{return JSON.stringify(t??{})}catch{return "{}"}}function je(t){let e=[],n=t.system;(typeof n=="string"?n!=="":Array.isArray(n))&&e.push({role:"syste\
|
|
53
55
|
m",content:typeof n=="string"?n:M(A(n))});for(let r of Array.isArray(t.messages)?t.messages:[]){if(!p(r))continue;let o=A(r.content);if(r.role==="assistant"){let a=o.filter(i=>i.type==="tool_use").map(
|
|
54
|
-
i=>({...typeof i.id=="string"?{id:i.id}:{},name:String(i.name??""),arguments:
|
|
56
|
+
i=>({...typeof i.id=="string"?{id:i.id}:{},name:String(i.name??""),arguments:me(i.input)}));e.push({role:"assistant",content:M(o),...a.length>0?{toolCalls:a}:{}});continue}for(let a of o)a.type==="too\
|
|
55
57
|
l_result"&&e.push({role:"tool",content:typeof a.content=="string"?a.content:M(A(a.content)),...typeof a.tool_use_id=="string"?{toolCallId:a.tool_use_id}:{}});let s=M(o);(s!==""||!o.some(a=>a.type==="t\
|
|
56
|
-
ool_result"))&&e.push({role:"user",content:s});}return e}function
|
|
57
|
-
let n={};for(let[r,o]of Object.entries(t))r==="stream"||r==="metadata"||r==="messages"||r==="system"||(n[r]=o);return n.system=typeof t.system=="string"?"$text":A(t.system).map(
|
|
58
|
-
t.messages)?t.messages:[]).filter(p).map(r=>({role:r.role,content:A(r.content).map(
|
|
59
|
-
p(t.output_config)&&t.output_config.format!==void 0?true:t.output_format!==void 0}var O=class{id;texto="";razono=false;reproducible=true;parada;usoInicio;usoDelta;vioDelta=false;vioStop=false;error;herramientas=new Map;toolCallsJson=[];bloque(e){
|
|
60
|
-
V(e.type)?(this.razono=true,this.reproducible=false):e.type==="text"?Array.isArray(e.citations)&&e.citations.length>0&&(this.reproducible=false):e.type!=="tool_use"&&(this.reproducible=false);}json(e){
|
|
61
|
-
id=="string"?e.id:void 0;for(let n of A(e.content))this.bloque(n),n.type==="text"&&typeof n.text=="string"&&(this.texto+=n.text),n.type==="tool_use"&&this.toolCallsJson.push({...typeof n.
|
|
62
|
-
{id:n.id}:{},name:String(n.name??""),arguments:
|
|
63
|
-
vioDelta=true,this.vioStop=true;}evento(e){let n;try{n=JSON.parse(e.data);}catch{return}if(!p(n))return;switch(e.event!=="message"?e.event:String(n.type??e.event)){case "
|
|
64
|
-
message:{};typeof o.id=="string"&&(this.id=o.id),p(o.usage)&&(this.usoInicio=o.usage);return}case "content_block_start":{let o=p(n.content_block)?n.content_block:{};
|
|
65
|
-
typeof n.index=="number"&&this.herramientas.set(n.index,{...typeof o.id=="string"?{id:o.id}:{},name:String(o.name??""),json:""});return}case "content_block_delta":{let o=p(
|
|
66
|
-
"text_delta"&&typeof o.text=="string")return this.texto+=o.text,o.text===""?void 0:o.text;if(o.type==="input_json_delta"&&typeof o.partial_json=="string"&&typeof n.index==
|
|
67
|
-
get(n.index);s&&(s.json+=o.partial_json);}o.type==="citations_delta"&&(this.reproducible=false);return}case "message_delta":{this.vioDelta=true;let o=p(n.delta)?n.delta:{};typeof o.
|
|
68
|
-
parada=o.stop_reason),p(n.usage)&&(this.usoDelta=
|
|
69
|
-
: ${String(o.message??"")}`;return}default:return}}get toolCalls(){return this.toolCallsJson.length>0?this.toolCallsJson:
|
|
70
|
-
id:e.id}:{},name:e.name,arguments:
|
|
71
|
-
outputTokens:0,complete:false},r=e.iterations;Array.isArray(r)&&r.length>1&&(n.complete=false);let o=this.toolCalls,s=
|
|
72
|
-
this.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
58
|
+
ool_result"))&&e.push({role:"user",content:s});}return e}function pe(t){let e={};for(let[n,r]of Object.entries(t))n==="id"||n==="tool_use_id"||n==="text"&&t.type==="text"||(e[n]=r);return e}function qe(t,e){
|
|
59
|
+
let n={};for(let[r,o]of Object.entries(t))r==="stream"||r==="metadata"||r==="messages"||r==="system"||(n[r]=o);return n.system=typeof t.system=="string"?"$text":A(t.system).map(pe),n.messages=(Array.isArray(
|
|
60
|
+
t.messages)?t.messages:[]).filter(p).map(r=>({role:r.role,content:A(r.content).map(pe)})),e&&(n["anthropic-beta"]=e),n}function Ue(t){let e=t.tool_choice;return p(e)&&(e.type==="any"||e.type==="tool")||
|
|
61
|
+
p(t.output_config)&&t.output_config.format!==void 0?true:t.output_format!==void 0}var O=class{id;texto="";razono=false;reproducible=true;parada;detallesDeParada;bloquesVistos=0;usoInicio;usoDelta;vioDelta=false;vioStop=false;error;herramientas=new Map;toolCallsJson=[];bloque(e){
|
|
62
|
+
this.bloquesVistos+=1,V(e.type)?(this.razono=true,this.reproducible=false):e.type==="text"?Array.isArray(e.citations)&&e.citations.length>0&&(this.reproducible=false):e.type!=="tool_use"&&(this.reproducible=false);}json(e){
|
|
63
|
+
this.id=typeof e.id=="string"?e.id:void 0;for(let n of A(e.content))this.bloque(n),n.type==="text"&&typeof n.text=="string"&&(this.texto+=n.text),n.type==="tool_use"&&this.toolCallsJson.push({...typeof n.
|
|
64
|
+
id=="string"?{id:n.id}:{},name:String(n.name??""),arguments:me(n.input)});this.parada=typeof e.stop_reason=="string"?e.stop_reason:void 0,this.detallesDeParada=e.stop_details,this.usoInicio=p(e.usage)?
|
|
65
|
+
e.usage:void 0,this.usoDelta=this.usoInicio,this.vioDelta=true,this.vioStop=true;}evento(e){let n;try{n=JSON.parse(e.data);}catch{return}if(!p(n))return;switch(e.event!=="message"?e.event:String(n.type??e.event)){case "\
|
|
66
|
+
message_start":{let o=p(n.message)?n.message:{};typeof o.id=="string"&&(this.id=o.id),p(o.usage)&&(this.usoInicio=o.usage);return}case "content_block_start":{let o=p(n.content_block)?n.content_block:{};
|
|
67
|
+
this.bloque(o),o.type==="tool_use"&&typeof n.index=="number"&&this.herramientas.set(n.index,{...typeof o.id=="string"?{id:o.id}:{},name:String(o.name??""),json:""});return}case "content_block_delta":{let o=p(
|
|
68
|
+
n.delta)?n.delta:{};if(o.type==="text_delta"&&typeof o.text=="string")return this.texto+=o.text,o.text===""?void 0:o.text;if(o.type==="input_json_delta"&&typeof o.partial_json=="string"&&typeof n.index==
|
|
69
|
+
"number"){let s=this.herramientas.get(n.index);s&&(s.json+=o.partial_json);}o.type==="citations_delta"&&(this.reproducible=false);return}case "message_delta":{this.vioDelta=true;let o=p(n.delta)?n.delta:{};typeof o.
|
|
70
|
+
stop_reason=="string"&&(this.parada=o.stop_reason),"stop_details"in o&&(this.detallesDeParada=o.stop_details),p(n.usage)&&(this.usoDelta=q(this.usoDelta,n.usage));return}case "message_stop":this.vioStop=
|
|
71
|
+
true;return;case "error":{let o=p(n.error)?n.error:{};this.error=`${String(o.type??"error")}: ${String(o.message??"")}`;return}default:return}}get toolCalls(){return this.toolCallsJson.length>0?this.toolCallsJson:
|
|
72
|
+
[...this.herramientas.entries()].sort((e,n)=>e[0]-n[0]).map(([,e])=>({...e.id?{id:e.id}:{},name:e.name,arguments:Ie(e.json)})).filter(e=>e.name!=="")}respuesta(){let e=q(this.usoInicio,this.usoDelta),
|
|
73
|
+
n=this.vioDelta?B(e,this.usoDelta?.output_tokens,this.razono).usage:{inputTokens:0,outputTokens:0,complete:false},r=e.iterations;Array.isArray(r)&&r.length>1&&(n.complete=false);let o=this.toolCalls,s=F(e);
|
|
74
|
+
return {text:this.texto,...o.length>0?{toolCalls:o}:{},...this.parada?{finishReason:G(this.parada)}:{},...this.vioDelta&&H({stopReason:this.parada,stopDetails:this.detallesDeParada,bloques:this.bloquesVistos,
|
|
75
|
+
outputTokens:this.usoDelta?.output_tokens,iterations:r})?{unbilledRefusal:true}:{},...s?{servedServiceTier:s}:{},usage:n,...this.reproducible&&this.parada==="end_turn"?{}:{replayable:false}}}};function Ie(t){
|
|
76
|
+
try{return JSON.stringify(JSON.parse(t===""?"{}":t))}catch{return t}}function ze(t,e){let n=[];return t.text!==""&&n.push({type:"text",text:t.text}),(t.toolCalls??[]).forEach((r,o)=>{let s={};try{s=JSON.
|
|
77
|
+
parse(r.arguments||"{}");}catch{s={};}n.push({type:"tool_use",id:r.id??`toolu_bvsala_${t.requestId}_${o}`,name:r.name,input:s});}),{id:`msg_bvsala_${t.requestId}`,type:"message",role:"assistant",model:e,
|
|
78
|
+
content:n,stop_reason:(t.toolCalls?.length??0)>0?"tool_use":"end_turn",stop_sequence:null,usage:{input_tokens:0,output_tokens:0}}}function fe(t,e,n){let r=ze(t,e),o={"request-id":`bvsala_${t.requestId}`};
|
|
79
|
+
if(!n)return new Response(JSON.stringify(r),{status:200,headers:{...o,"content-type":"application/json"}});let s=(i,u)=>`event: ${i}
|
|
77
80
|
data: ${JSON.stringify(u)}
|
|
78
81
|
|
|
79
82
|
`,a=s("message_start",{type:"message_start",message:{...r,content:[],stop_reason:null,stop_sequence:null}});return r.content.forEach((i,u)=>{i.type==="text"?(a+=s("content_block_start",{type:"content_\
|
|
80
83
|
block_start",index:u,content_block:{type:"text",text:""}}),a+=s("content_block_delta",{type:"content_block_delta",index:u,delta:{type:"text_delta",text:i.text}})):(a+=s("content_block_start",{type:"co\
|
|
81
84
|
ntent_block_start",index:u,content_block:{type:"tool_use",id:i.id,name:i.name,input:{}}}),a+=s("content_block_delta",{type:"content_block_delta",index:u,delta:{type:"input_json_delta",partial_json:JSON.
|
|
82
85
|
stringify(i.input)}})),a+=s("content_block_stop",{type:"content_block_stop",index:u});}),a+=s("message_delta",{type:"message_delta",delta:{stop_reason:r.stop_reason,stop_sequence:null},usage:{output_tokens:0}}),
|
|
83
|
-
a+=s("message_stop",{type:"message_stop"}),new Response(a,{status:200,headers:{...o,"content-type":"text/event-stream"}})}var
|
|
84
|
-
status}`);this.respuesta=n;}respuesta};function
|
|
85
|
-
r.keys().next().value);};return {middleware:async(a,i,u)=>{if(!
|
|
86
|
-
I)}let g=u.options,m=
|
|
87
|
-
tring"?c.description:void 0,parametros:c.input_schema,delProveedor:c.input_schema===void 0&&typeof c.type=="string"&&c.type!=="custom"})),l=await
|
|
88
|
-
d),tools:
|
|
89
|
-
v={model:l.model,messages:l.messages,...l.tools?{tools:l.tools}:{}},
|
|
90
|
-
{error:y},y}return c.completada=true,f},z=c=>{if(c instanceof
|
|
91
|
-
D.respuesta()},void 0,
|
|
92
|
-
N),
|
|
93
|
-
throw new Error("[bvsala] the model stream ended before completion; nothing was measured.")},()=>{let c=new C;return
|
|
94
|
-
transmitir(l,k,a.signal??void 0);}catch(c){return z(c)}if(
|
|
95
|
-
d.model,true)}return
|
|
86
|
+
a+=s("message_stop",{type:"message_stop"}),new Response(a,{status:200,headers:{...o,"content-type":"text/event-stream"}})}var T=class extends Error{constructor(n){super(`[bvsala] provider responded ${n.
|
|
87
|
+
status}`);this.respuesta=n;}respuesta};function lt(t){let e=se("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(
|
|
88
|
+
r.keys().next().value);};return {middleware:async(a,i,u)=>{if(!Ee(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(
|
|
89
|
+
I)}let g=u.options,m=de(g?.bivelio),h=t.context?.({body:d,requestOptions:g}),_=(Array.isArray(d.tools)?d.tools:[]).filter(p).map(c=>ce(t,{nombre:String(c.name??""),descripcion:typeof c.description=="s\
|
|
90
|
+
tring"?c.description:void 0,parametros:c.input_schema,delProveedor:c.input_schema===void 0&&typeof c.type=="string"&&c.type!=="custom"})),l=await ue(t,{model:`anthropic/${String(d.model??"")}`,messages:je(
|
|
91
|
+
d),tools:_,temperature:typeof d.temperature=="number"?d.temperature:void 0,context:le(h,m.context),savings:m.savings,proyeccion:qe(d,a.headers.get("anthropic-beta")),restricciones:{sinResolvers:Ue(d)}}),
|
|
92
|
+
v={model:l.model,messages:l.messages,...l.tools?{tools:l.tools}:{}},b=K(a.url),R=async c=>{let f;try{f=await i(a);}catch(y){throw c.errorDelMarco={error:y},y}if(!f.ok){let y=new T(f);throw c.errorDelMarco=
|
|
93
|
+
{error:y},y}return c.completada=true,f},z=c=>{if(c instanceof T)return c.respuesta;throw c};if(d.stream!==true){let c,f,y=U(v,async()=>{c=await R(y);let D=new O;return D.json(await c.clone().json()),f=D.id,
|
|
94
|
+
D.respuesta()},void 0,b),w;try{w=await e.generar(l,y);}catch(D){return z(D)}if(w.tipo==="sin-informe")return c;let N=e.informe(w.result.report);return w.tipo==="atajo"?(o(`msg_bvsala_${w.result.requestId}`,
|
|
95
|
+
N),fe(w.result,d.model,false)):(o(f,N),c)}let{abierta:he,abrir:$}=ae(),E,j,k=Object.assign(U(v,async()=>{let c=await R(k),f=new C;j=ge(c,f,e,k,o),$();for await(let y of f)if(y.type==="done")return y.response;
|
|
96
|
+
throw new Error("[bvsala] the model stream ended before completion; nothing was measured.")},()=>{let c=new C;return R(k).then(f=>{E=ge(f,c,e,k,o),$();},f=>c.fail(f)),c},b),{abierta:he}),S;try{S=await e.
|
|
97
|
+
transmitir(l,k,a.signal??void 0);}catch(c){return z(c)}if(S.tipo==="abierto")return E??j;if(S.tipo==="atajo"){let c=e.informe(S.result.report);return o(`msg_bvsala_${S.result.requestId}`,c),fe(S.result,
|
|
98
|
+
d.model,true)}return E??j},reportFor(a){let i=typeof a=="string"?a:a?.id;return typeof i=="string"?r.get(i):void 0},close(){e.close();}}}function ge(t,e,n,r,o){let s=new O,a,i=async()=>{try{let l=await r.
|
|
96
99
|
resultado;o(s.id,n.informe(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()}),
|
|
97
100
|
e.end(),a=i()):e.fail(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().
|
|
98
101
|
then(l=>{s.json(p(l)?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 measure\
|
|
99
|
-
d.")),t;let g=d.getReader(),m=new L,h=l=>{for(let v of l){let
|
|
100
|
-
read();}catch(
|
|
101
|
-
lled by its reader; nothing was measured.")),g.cancel(l)}});return new Response(
|
|
102
|
+
d.")),t;let g=d.getReader(),m=new L,h=l=>{for(let v of l){let b=s.evento(v);b!==void 0&&e.push({type:"text",delta:b}),(s.vioStop||s.error)&&u();}},_=new ReadableStream({async pull(l){let v;try{v=await g.
|
|
103
|
+
read();}catch(R){e.fail(R),l.error(R);return}if(v.done){h(m.end()),u(),await a,l.close();return}let b=v.value;l.enqueue(b),h(m.push(b));},cancel(l){return e.fail(new Error("[bvsala] the stream was cance\
|
|
104
|
+
lled by its reader; nothing was measured.")),g.cancel(l)}});return new Response(_,{status:t.status,statusText:t.statusText,headers:t.headers})}export{lt as anthropicSavings};
|