@bivelio/savings-layer 0.4.30 → 0.4.32
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 +171 -8
- package/dist/adapters/anthropic-sdk.js +75 -69
- package/dist/gateway/bin.js +893 -719
- package/dist/index.d.ts +193 -22
- package/dist/index.js +1597 -1363
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -211,7 +211,8 @@ import { createSavingsLayer, OpenAICompatibleProvider } from "@bivelio/savings-l
|
|
|
211
211
|
// Your provider. LLM traffic goes straight to it — the SDK never proxies it.
|
|
212
212
|
// Any OpenAI-compatible endpoint works: OpenAI, Azure, vLLM, SGLang, a gateway.
|
|
213
213
|
// Model ids are qualified (`openai/gpt-4o-mini`); the prefix is stripped on the
|
|
214
|
-
// wire for OpenAI and
|
|
214
|
+
// wire for OpenAI, Azure and Google's Gemini API and kept for gateways — see
|
|
215
|
+
// `stripProviderPrefix`.
|
|
215
216
|
const provider = new OpenAICompatibleProvider({
|
|
216
217
|
baseUrl: "https://api.openai.com/v1",
|
|
217
218
|
apiKey: process.env.OPENAI_API_KEY,
|
|
@@ -282,6 +283,46 @@ nothing either. The same rule applies in the gateway. Any other refusal is price
|
|
|
282
283
|
its `usage` as usual. That covers `bio`, `frontier_llm` and `reasoning_extraction`, a
|
|
283
284
|
refusal after partial output, and a category the table does not list.
|
|
284
285
|
|
|
286
|
+
Most refusals carry no signal from the API: the model writes "I can't help with that."
|
|
287
|
+
as ordinary text and ends with `end_turn` or `stop`. The layer also looks for these,
|
|
288
|
+
with a conservative text heuristic. It checks only the opening of the answer, or the
|
|
289
|
+
whole answer when it is short, and it needs a real refusal clause, such as "I'm sorry,
|
|
290
|
+
but I can't help with that", "I cannot assist", "I can't determine…" or "As an AI, I
|
|
291
|
+
cannot provide". After an apology, any verb counts ("I'm sorry, but I can't
|
|
292
|
+
determine…"), and so does a stated reason with no "I can't" ("I'm sorry, but
|
|
293
|
+
accessing … is illegal", "… would be a violation of…"). When the answer opens with
|
|
294
|
+
the refusal itself ("I can't", "I won't", "I'm not going to", "I'm unable to", and
|
|
295
|
+
"No puedo", "Je ne peux pas", "Ich kann … nicht"…), any verb counts too ("I can't
|
|
296
|
+
argue that…"), except for a tested list of idioms that are not refusals ("I can't
|
|
297
|
+
wait", "I won't lie", "I can't find any issues", "I can't be sure, but…").
|
|
298
|
+
A single word is not enough.
|
|
299
|
+
An answer that opens with empathy and, in its first two sentences, sends the user to
|
|
300
|
+
a mental health professional, a crisis line, emergency services or someone they
|
|
301
|
+
trust, instead of answering, counts too (rule `redirection`). It covers English,
|
|
302
|
+
Spanish, Catalan, French, German, Italian and Portuguese. A match is measured and
|
|
303
|
+
served as usual. It is not written to the exact or semantic cache, and it is not
|
|
304
|
+
replayed by reference. A concurrent identical request served from it is `estimated`.
|
|
305
|
+
The trace says why (`skipReason: "text_refusal"`, with the `rule`). A refusal
|
|
306
|
+
already stored by an earlier version is not served either. In the gateway, the
|
|
307
|
+
coalesced twin of such a response is `estimated` (`primary-text-refusal`). A false
|
|
308
|
+
positive costs one cache hit. A normal answer that starts with "Sorry for the delay,
|
|
309
|
+
here is…", praises with "I can't recommend this enough", has "I can't" later on, or
|
|
310
|
+
offers empathy with practical help and no referral ("I'm sorry to hear about your
|
|
311
|
+
flight; here are three options…") is cached as before.
|
|
312
|
+
|
|
313
|
+
**Sensitive topics.** A request whose user turns touch self-harm or suicide, a mental
|
|
314
|
+
health crisis or a medical emergency ("How do I commit suicide?", "I'm having a panic
|
|
315
|
+
attack", "my friend is not breathing") is never read from or written to the exact or
|
|
316
|
+
semantic cache, even when the answer is useful, and leaves no reference record. It is
|
|
317
|
+
measured and served as usual; a concurrent identical request served from it is
|
|
318
|
+
`estimated`. The `policy` and `cache-write` traces say why
|
|
319
|
+
(`skipReason: "sensitive_topic"`, with the `category`). The check is lexical and
|
|
320
|
+
conservative: phrases, not single words ("kill a Python process", "Suicide Squad" and
|
|
321
|
+
"cut my hair" do not count). It covers the same twelve languages as the freshness
|
|
322
|
+
and personalization checks, with shorter lists outside English, Spanish, Catalan,
|
|
323
|
+
French, German, Italian and Portuguese. In the gateway, the coalesced twin of such a
|
|
324
|
+
request is `estimated` (`sensitive-topic`). A false positive costs one cache hit.
|
|
325
|
+
|
|
285
326
|
**Streaming.** `generateStream()` takes the same request and hands you the answer as
|
|
286
327
|
the provider writes it, then the same `SavingsReport`:
|
|
287
328
|
|
|
@@ -438,6 +479,45 @@ It runs once per HTTP attempt, inside the SDK's retries: an attempt the provider
|
|
|
438
479
|
rejects reaches the SDK unchanged and is not recorded. Only message creation goes
|
|
439
480
|
through the layer; `countTokens`, batches, models and files pass untouched.
|
|
440
481
|
|
|
482
|
+
The middleware forwards your request body unchanged, history included, so it never
|
|
483
|
+
edits a turn before a thinking block. It also does not remove parameters the model
|
|
484
|
+
rejects. On Claude Sonnet 5.5, Opus 5.5, Fable 5.1 and Mythos 5.1 a forced
|
|
485
|
+
`tool_choice` (`any` or `tool`) returns a 400. On those models, and on Opus 4.7/4.8,
|
|
486
|
+
Opus 5, Sonnet 5 and Fable 5, so does a non-default `temperature`, `top_p` or `top_k`.
|
|
487
|
+
The middleware warns once per model and cause (`silenceWarnings` turns it off) and
|
|
488
|
+
the API's 400 reaches you unchanged.
|
|
489
|
+
|
|
490
|
+
<details>
|
|
491
|
+
<summary><b>Claude Sonnet 5.5, Opus 5.5, Fable 5.1: what <code>AnthropicProvider</code> sends instead</b></summary>
|
|
492
|
+
|
|
493
|
+
These models reject some request parameters with a 400 ([forcing tool
|
|
494
|
+
use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools#forcing-tool-use),
|
|
495
|
+
[Sonnet 5.5](https://platform.claude.com/docs/en/models/sonnet-5-5/overview)).
|
|
496
|
+
`AnthropicProvider` sends a request they accept instead of failing:
|
|
497
|
+
|
|
498
|
+
- **Output contract.** A forced `tool_choice` returns a 400 on these models, with or
|
|
499
|
+
without thinking. When your schema meets `strict` mode, the contract travels as
|
|
500
|
+
structured output (`output_config.format`). Without your tools, the provider still
|
|
501
|
+
enforces the contract. With your tools, they stay callable with `tool_choice: auto`
|
|
502
|
+
and the final text answer comes back in the contract's shape. When your schema does
|
|
503
|
+
not meet `strict`, the contract travels as a tool the model is not forced to call,
|
|
504
|
+
with one sentence in the system prompt that tells it to answer through that tool.
|
|
505
|
+
Models that support forced tool use (Opus 5, Sonnet 5 and the 4.x models) get the
|
|
506
|
+
same request as before.
|
|
507
|
+
- **`temperature`.** A non-default value is dropped, with one warning per model,
|
|
508
|
+
because it can only return a 400. The answer is then sampled, not deterministic.
|
|
509
|
+
`temperature: 1`, the API default, is sent as is.
|
|
510
|
+
- **A model the table does not list** (a new model or a gateway alias): when the API
|
|
511
|
+
rejects either parameter, the provider repeats the request once without it and
|
|
512
|
+
remembers the model for later requests. A 400 is not billed, so the retry is not
|
|
513
|
+
counted in `retries`.
|
|
514
|
+
- **Thinking blocks.** `ChatMessage` does not carry thinking blocks, so this adapter
|
|
515
|
+
never sends one. The preserved-thinking check, which returns a 400 when a turn
|
|
516
|
+
before a thinking block has been edited, has nothing to check on its requests, even
|
|
517
|
+
when the levers that rewrite history are on.
|
|
518
|
+
|
|
519
|
+
</details>
|
|
520
|
+
|
|
441
521
|
## What you get on every request
|
|
442
522
|
|
|
443
523
|
A `SavingsReport` with the numbers behind every claim, kept in **four separate
|
|
@@ -666,7 +746,16 @@ const agentLayer = createSavingsLayer({
|
|
|
666
746
|
|
|
667
747
|
By default the layer writes the provider's prompt cache at two fixed places: the end
|
|
668
748
|
of the system header and the end of the history before your last user message, and
|
|
669
|
-
stops marking a prefix whose measured hit rate does not pay the write premium.
|
|
749
|
+
stops marking a prefix whose measured hit rate does not pay the write premium. On
|
|
750
|
+
Anthropic, where a write costs 1.25x, the history and a tool catalogue sent without a
|
|
751
|
+
system header are marked only when the request itself shows they will be sent again:
|
|
752
|
+
a tool-loop step after your last user message, or a conversation already past its
|
|
753
|
+
second turn. The first follow-up of a conversation (`[question, answer, question]`)
|
|
754
|
+
marks only the system header, so two-turn chats pay no premium that nothing reads.
|
|
755
|
+
In an agent loop with no system header, the end of the tool list is marked once the
|
|
756
|
+
catalogue reaches the model's minimum cacheable size (512 tokens on Claude Sonnet 5.5
|
|
757
|
+
and Opus 5.5, 1,024 on Sonnet 5, 4,096 on Haiku 4.5). That discount is the provider's
|
|
758
|
+
cache on your bytes: it is never credited as the layer's saving.
|
|
670
759
|
|
|
671
760
|
`cacheBreakpoints: "auto"` places them from what your traffic actually does. The layer
|
|
672
761
|
compares each request's prefix with the ones it sent before, per model, tool set and
|
|
@@ -711,7 +800,11 @@ const placedLayer = createSavingsLayer({
|
|
|
711
800
|
Calibrated reuse and `cacheBreakpoints: "auto"` learn from your traffic, and by default
|
|
712
801
|
what they learn lives in the process. On serverless platforms (Vercel Functions, AWS
|
|
713
802
|
Lambda) an instance may serve one request or live a few minutes, so neither would ever
|
|
714
|
-
get past its starting point. Give each one somewhere to keep it
|
|
803
|
+
get past its starting point. Give each one somewhere to keep it. Giving the layer a
|
|
804
|
+
`semanticCache` only says where reworded questions are kept; `semanticCacheDefault: true`
|
|
805
|
+
is what switches reuse on for every request (a request's own `savings.semanticCache`
|
|
806
|
+
still wins), and without it, or `savings.semanticCache` on each request, the cache
|
|
807
|
+
stays empty and learns nothing:
|
|
715
808
|
|
|
716
809
|
```ts
|
|
717
810
|
import { InMemorySemanticCache } from "@bivelio/savings-layer";
|
|
@@ -731,6 +824,8 @@ async function serverlessLayer(kv: SharedKeyValue) {
|
|
|
731
824
|
provider,
|
|
732
825
|
licenseKey: process.env.BIVELIO_LICENSE_KEY,
|
|
733
826
|
semanticCache,
|
|
827
|
+
// Passing the cache does not switch it on; this asks for it on every request.
|
|
828
|
+
semanticCacheDefault: true,
|
|
734
829
|
cacheBreakpoints: "auto",
|
|
735
830
|
cacheBreakpointStore: {
|
|
736
831
|
get: (key) => kv.get(key),
|
|
@@ -802,6 +897,54 @@ console.log(data.answer); // 8
|
|
|
802
897
|
|
|
803
898
|
</details>
|
|
804
899
|
|
|
900
|
+
<details>
|
|
901
|
+
<summary><b>Output cap — with or without a contract</b></summary>
|
|
902
|
+
|
|
903
|
+
`maxOutputTokens` on the request caps the answer of any request, free text
|
|
904
|
+
included. With `response.maxOutputTokens` as well, the smaller of the two is sent.
|
|
905
|
+
|
|
906
|
+
```ts
|
|
907
|
+
const { text, finishReason } = await layer.generate({
|
|
908
|
+
model: "openai/gpt-5-mini",
|
|
909
|
+
messages: [{ role: "user", content: "Summarize this in two sentences: …" }],
|
|
910
|
+
maxOutputTokens: 300,
|
|
911
|
+
});
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
On the wire it becomes Anthropic's `max_tokens`; on Chat Completions,
|
|
915
|
+
`max_completion_tokens` when `baseUrl` is OpenAI's own API (`max_tokens` is
|
|
916
|
+
deprecated there and its reasoning models, o-series and gpt-5.x, reject it) and
|
|
917
|
+
`max_tokens` on any other OpenAI-compatible endpoint, which is the field they all
|
|
918
|
+
understand. If an endpoint answers 400 naming the field it got as unsupported, the
|
|
919
|
+
request is repeated once with the other one (nothing was generated, so nothing is
|
|
920
|
+
billed twice) and the model is remembered. To choose the field yourself:
|
|
921
|
+
`new OpenAICompatibleProvider({ baseUrl, maxTokensParam: "max_completion_tokens" })`.
|
|
922
|
+
|
|
923
|
+
**OpenAI-compatible endpoints that are not OpenAI** (Gemini's
|
|
924
|
+
`generativelanguage.googleapis.com/v1beta/openai`, LiteLLM, vLLM, Ollama, a router).
|
|
925
|
+
`prompt_cache_key`, OpenAI's prefix-cache routing hint, goes only to OpenAI's own API:
|
|
926
|
+
Gemini answers 400 to a field it does not know, and up to 0.4.31 every request with a
|
|
927
|
+
cached prefix failed there. A proxy in front of OpenAI that forwards it can opt in with
|
|
928
|
+
`sendPromptCacheKey: true`. Any optional field an endpoint still rejects by name
|
|
929
|
+
(`prompt_cache_key`, `service_tier`, `seed`, `reasoning_effort`) is dropped, the request
|
|
930
|
+
repeated once and the model remembered; `reasoning_effort` is never dropped on OpenAI's
|
|
931
|
+
own API, where its 400 is the answer. Gemini leaves its thinking tokens out of
|
|
932
|
+
`completion_tokens` and bills them as output, so the layer counts output as
|
|
933
|
+
`total_tokens − prompt_tokens` whenever the total is larger (and reports the difference
|
|
934
|
+
as reasoning); on OpenAI's API `completion_tokens` already include reasoning and nothing
|
|
935
|
+
is added. Gemini stops a filtered answer with `finish_reason: "content_filter: OTHER"`
|
|
936
|
+
(or another reason): it is `finishReason: "content_filter"` (the adapter keeps the
|
|
937
|
+
reason in `finishReasonDetail` and the report's cache trace names it), and like every
|
|
938
|
+
provider refusal it is measured, never
|
|
939
|
+
cached and never credited. Google does not document that such refusals are free, so
|
|
940
|
+
they are costed from their `usage` like any call.
|
|
941
|
+
|
|
942
|
+
The cap is yours, not a saving: it is never credited as output avoided. An answer
|
|
943
|
+
it cut (`finishReason: "length"`) is not cached, and
|
|
944
|
+
`report.qualityLedger.truncation` says the limit was yours.
|
|
945
|
+
|
|
946
|
+
</details>
|
|
947
|
+
|
|
805
948
|
<details>
|
|
806
949
|
<summary><b>Tool calls</b></summary>
|
|
807
950
|
|
|
@@ -854,6 +997,18 @@ console.log(res.text);
|
|
|
854
997
|
the SDK warns once.
|
|
855
998
|
- **Errors.** When a tool fails, put the error in `content` and set `isError: true`.
|
|
856
999
|
Anthropic receives it as `is_error`; providers without such a flag get the text.
|
|
1000
|
+
- **Gemini 3's thought signature.** Through its OpenAI-compatible endpoint Gemini
|
|
1001
|
+
returns each call with `extra_content.google.thought_signature` and rejects the next
|
|
1002
|
+
turn with a 400 if the call comes back without it. It arrives in `call.extraContent`;
|
|
1003
|
+
pushing `res.toolCalls` back as they came (as above) sends it back untouched. It is
|
|
1004
|
+
opaque and not part of any cache key.
|
|
1005
|
+
- **gpt-5.6-sol, gpt-5.6-terra and gpt-5.6-luna with tools need `reasoningEffort: "none"`.**
|
|
1006
|
+
On Chat Completions OpenAI rejects function tools for these models unless
|
|
1007
|
+
`reasoning_effort` is `"none"` («Function tools with reasoning_effort are not supported for gpt-5.6-sol in
|
|
1008
|
+
/v1/chat/completions»). Declare it on the request:
|
|
1009
|
+
`layer.generate({ model: "openai/gpt-5.6-sol", messages, tools, reasoningEffort: "none" })`.
|
|
1010
|
+
The layer never picks an effort itself: without the field nothing is sent. It is part
|
|
1011
|
+
of the cache key, like `temperature` and `seed`.
|
|
857
1012
|
|
|
858
1013
|
A tool‑call turn is never served from cache and never counted as an avoided call:
|
|
859
1014
|
an action that has not run yet cannot be a saving. The answer that follows a tool loop
|
|
@@ -862,13 +1017,21 @@ each result answers and its `isError`. Two conversations that differ only there
|
|
|
862
1017
|
share a cached answer; the fresh call ids of a new run of the same loop are not a
|
|
863
1018
|
difference.
|
|
864
1019
|
|
|
1020
|
+
**Which tools reach the model.** The layer never cuts a catalogue it cannot rank. If
|
|
1021
|
+
your tools declare no `intents` and no `risk` (the usual shape of MCP and OpenAI tool
|
|
1022
|
+
lists), or you pass `savings: { router: false }`, every tool goes to the provider
|
|
1023
|
+
exactly as you sent it, and the layer credits nothing for the tool block. Only when
|
|
1024
|
+
your tools declare `intents`/`risk` does the selector keep the relevant ones, up to
|
|
1025
|
+
`new ToolSelector({ maxTools })` (8 by default), and hold back write tools from a
|
|
1026
|
+
request that is not an action. Whatever it leaves out, the model cannot call in that
|
|
1027
|
+
request: the report's `tool-selector` trace lists it, the tokens count as eliminated
|
|
1028
|
+
input, and the SDK warns once per process (`silenceWarnings` turns the warning off).
|
|
1029
|
+
|
|
865
1030
|
**Large catalogues: let the provider search them (opt‑in).** With hundreds of tools
|
|
866
1031
|
(several MCP servers, say), `savings: { toolSearch: "auto" }` sends every tool deferred
|
|
867
|
-
behind the provider's own tool search instead of
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
search the model looks up what it needs, no tool you declared is out of reach, and the
|
|
871
|
-
cached prompt prefix stays the same from one question to the next.
|
|
1032
|
+
behind the provider's own tool search instead of putting the whole catalogue in every
|
|
1033
|
+
request's prompt. The model looks up what it needs, no tool you declared is out of
|
|
1034
|
+
reach, and the cached prompt prefix stays the same from one question to the next.
|
|
872
1035
|
|
|
873
1036
|
- **Where.** The Anthropic adapter, on models in Anthropic's tool search compatibility
|
|
874
1037
|
table. OpenAI documents its tool search for the Responses and Agents APIs, not for
|
|
@@ -1,67 +1,73 @@
|
|
|
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 W(t,e,n){let r=t??{},o=J(r.iterations),s=F(r.cache_read_input_tokens,o?.cacheRead),a=F(r.cache_creation_input_tokens,o?.cacheWrite),i=r.cache_creation?.ephemeral_1h_input_tokens,l=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
|
-
"number",h=typeof e!="number";return {usage:{inputTokens:d,outputTokens:g,...s!=null?{cachedInputTokens:s}:{},...a!=null?{cacheWriteInputTokens:a}:{},...
|
|
7
|
-
h||o?{}:{reasoningTokens:0},...m||h?{complete:false}:{}},entradaAusente:m,salidaAusente:h}}function
|
|
6
|
+
"number",h=typeof e!="number";return {usage:{inputTokens:d,outputTokens:g,...s!=null?{cachedInputTokens:s}:{},...a!=null?{cacheWriteInputTokens:a}:{},...l!==void 0?{cacheWrite1hInputTokens:l}:{},...n||
|
|
7
|
+
h||o?{}:{reasoningTokens:0},...m||h?{complete:false}:{}},entradaAusente:m,salidaAusente:h}}function F(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
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
|
-
"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"||!
|
|
12
|
-
isArray(t.iterations)&&t.iterations.length>1)}var
|
|
13
|
-
|
|
10
|
+
tool_calls";case "refusal":return "content_filter";default:return t}}var be=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"||!be.has(r)||t.bloques!==0||t.outputTokens!==0||Array.
|
|
12
|
+
isArray(t.iterations)&&t.iterations.length>1)}var Re=["claude-opus-5-5","claude-sonnet-5-5","claude-fable-5-1","claude-mythos-5-1"],we=["claude-opus-4-7","claude-opus-4-8","claude-opus-5","claude-sonnet-5","claude-fable-5","claude-mythos-5"];function Se(t){return /claude-[a-z]+-\d[a-z0-9-]*/.exec(t.toLowerCase())?.[0]}function K(t,e){let n=Se(t);return n===void 0?false:e.some(r=>n===r||n.startsWith(`${r}-`))}function Z(t){return K(t,Re)}function Q(t){
|
|
13
|
+
return K(t,we)}var xe={"api.anthropic.com":"anthropic","api.openai.com":"openai","generativelanguage.googleapis.com":"google"};function Ae(t){if(typeof t!="string")return null;try{let e=new URL(t);return e.protocol!==
|
|
14
|
+
"https:"||e.port!==""?null:xe[e.hostname.toLowerCase()]??null}catch{return null}}function X(t){return Ae(t)!==null?"oficial":"no-oficial"}var Y=Symbol.for("@bivelio/savings-layer:provider-origin");var O=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(
|
|
14
15
|
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,
|
|
15
16
|
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(
|
|
16
17
|
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.
|
|
17
18
|
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),
|
|
18
19
|
s=e.slice(r+1),s.charCodeAt(0)===32&&(s=s.slice(1))),o){case "data":this.datos+=s+`
|
|
19
20
|
`;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":
|
|
20
|
-
this.evento,data:this.datos.slice(0,-1),...this.ultimoId!==void 0?{id:this.ultimoId}:{}}),this.datos="",this.evento="";}};var
|
|
21
|
-
nothing more was sent.",
|
|
22
|
-
completada:false,...r!==void 0?{origen:r}:{}}}function
|
|
23
|
-
e.model&&
|
|
24
|
-
|
|
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,
|
|
26
|
-
ultCostModel","exactCache","exactCacheTtlMs","resolvers","exactTokenizer","silenceWarnings","failOpen"];function
|
|
27
|
-
t.completada}var
|
|
28
|
-
let n=
|
|
29
|
-
try{let
|
|
30
|
-
let g=n.run(i,()=>o.generateStream(a,
|
|
31
|
-
"sin-informe",
|
|
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
|
|
21
|
+
this.evento,data:this.datos.slice(0,-1),...this.ultimoId!==void 0?{id:this.ultimoId}:{}}),this.datos="",this.evento="";}};var ee=new Set;function te(t,e,n){t===true||ee.has(e)||(ee.add(e),console.warn(n));}var De="[bvsala] internal: the layer changed a request this adapter must forward unchanged; nothing was sent.",Ce="[bvsala] internal: the layer asked for a second model call within one framework call;\
|
|
22
|
+
nothing more was sent.",Pe="[bvsala] internal: the layer called the model outside a framework call; nothing was sent.";function I(t,e,n,r){return {renderizado:t,generar:e,...n?{transmitir:n}:{},invocada:false,
|
|
23
|
+
completada:false,...r!==void 0?{origen:r}:{}}}function Oe(t,e){if(t===e)return true;try{return stableStringify(t)===stableStringify(e)}catch{return false}}function re(t,e){let n=t??[],r=e??[];return n.length===r.length&&n.every((o,s)=>Oe(o,r[s]))}function Te(t,e){return t.model===
|
|
24
|
+
e.model&&re(t.messages,e.messages)&&re(t.tools,e.tools)}function oe(t,e){let n=o=>{let s=e.getStore();if(!s)throw new Error(Pe);if(s.invocada)throw new Error(Ce);if(!Te(o,s.renderizado))throw new Error(
|
|
25
|
+
De);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}
|
|
26
|
+
let i=await a.generar(s?.signal);i.text!==""&&(yield {type:"text",delta:i.text}),yield {type:"done",response:i};}};return Object.defineProperty(r,Y,{value:()=>e.getStore()?.origen}),r}function se(){return new AsyncLocalStorage}var Le=["licenseKey","licenseServerUrl","serviceAccountKey","licenseRefresh","licenseRevocation","licensePollIntervalSeconds","licenseFetch","metering","meteringUrl","meteringFetch","costModels","defa\
|
|
27
|
+
ultCostModel","exactCache","exactCacheTtlMs","resolvers","exactTokenizer","silenceWarnings","failOpen"];function ae(t,e){return e instanceof LicenseError||t.errorDelMarco&&t.errorDelMarco.error===e?false:t.invocada&&
|
|
28
|
+
t.completada}var ie="[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 ce(t,e){
|
|
29
|
+
let n=se(),r={};for(let a of Le)e[a]!==void 0&&(r[a]=e[a]);let o=createSavingsLayer({...r,provider:oe(t,n)}),s=(a,i)=>te(e.silenceWarnings,`${t}:${a}`,i);return {ejecutar(a,i){return n.run(i,()=>o.generate(a))},async generar(a,i){
|
|
30
|
+
try{let l=await n.run(i,()=>o.generate(a));return i.invocada?{tipo:"vivo",result:l}:{tipo:"atajo",result:l}}catch(l){if(ae(i,l))return s("sin-informe",ie),{tipo:"sin-informe"};throw l}},async transmitir(a,i,l){
|
|
31
|
+
let g=n.run(i,()=>o.generateStream(a,l?{signal:l}:{})).result;i.resultado=g,g.catch(()=>{});let m;try{m=await Promise.race([i.abierta.then(()=>"abierta"),g.then(()=>"resuelta")]);}catch(b){if(ae(i,b))return s(
|
|
32
|
+
"sin-informe",ie),{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.
|
|
33
|
+
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 le(){
|
|
33
34
|
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.
|
|
34
35
|
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||
|
|
35
36
|
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](){
|
|
36
37
|
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(
|
|
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
|
|
38
|
-
function
|
|
39
|
-
erministicResolvers")&&n.sinResolvers!==true}}function
|
|
40
|
-
let n=e.delProveedor!==true&&
|
|
38
|
+
(e,n)=>{this.esperando={resolver:e,rechazar:n};}),return:()=>(this.terminada=true,this.pendientes.length=0,Promise.resolve({value:void 0,done:true}))}}};var qe={router:false,compression:false,elision:false,serialization:false,toolDiscovery:false,modelCascade:"off",outputCodebook:false,deferrable:false,semanticCache:false,verification:false};
|
|
39
|
+
function Ie(t,e,n={}){let r=o=>t?.[o]!==false&&e?.[o]!==false;return {...qe,exactCache:r("exactCache")&&n.sinReutilizar!==true,singleflight:r("singleflight")&&n.sinReutilizar!==true,deterministicResolvers:r("det\
|
|
40
|
+
erministicResolvers")&&n.sinResolvers!==true}}function Ue(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 de(t,e){
|
|
41
|
+
let n=e.delProveedor!==true&&Ue(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}:{},
|
|
41
42
|
...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)?
|
|
42
43
|
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)
|
|
43
44
|
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};
|
|
44
45
|
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:
|
|
45
46
|
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);
|
|
46
|
-
a!==void 0&&(o[s]=a);}return o}finally{n.delete(r);}}async function
|
|
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
|
|
48
|
-
all(e.map(
|
|
47
|
+
a!==void 0&&(o[s]=a);}return o}finally{n.delete(r);}}async function ue(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(
|
|
48
|
+
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 ze(t){try{let e=[],n=JSON.stringify(P(t,e,new WeakSet))??"null",r=await Promise.
|
|
49
|
+
all(e.map(ue)),o=new TextEncoder().encode(`${n}|${r.join(",")}`);return await ue(o)}catch{return null}}async function pe(t,e){let n=await ze(e.proyeccion),r=`${e.context?.authorizationScope??"default"}\
|
|
49
50
|
|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"?
|
|
50
|
-
{seed:e.seed}:{},context:{...e.context??{},authorizationScope:r},savings:
|
|
51
|
-
{}}}function
|
|
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
|
|
51
|
+
{seed:e.seed}:{},context:{...e.context??{},authorizationScope:r},savings:Ie(t.savings,e.savings,{...e.restricciones,...n===null?{sinReutilizar:true}:{}})}}function fe(t,e){if(!(!t&&!e))return {...t??{},...e??
|
|
52
|
+
{}}}function ge(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 U="[bvsala] could not read the request body; nothing was sent.";function p(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function Ne(t){if((t.method??"GET").toUpperCase()!=="POST")return false;
|
|
53
|
+
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 T(t){
|
|
53
54
|
return t.filter(e=>e.type==="text"&&typeof e.text=="string").map(e=>e.text).join(`
|
|
54
|
-
`)}function
|
|
55
|
-
m",content:typeof n=="string"?n:
|
|
56
|
-
i=>({...typeof i.id=="string"?{id:i.id}:{},name:String(i.name??""),arguments:
|
|
57
|
-
l_result"&&e.push({role:"tool",content:typeof a.content=="string"?a.content:
|
|
58
|
-
ool_result"))&&e.push({role:"user",content:s});}return e}function
|
|
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(
|
|
60
|
-
t.messages)?t.messages:[]).filter(p).map(r=>({role:r.role,content:A(r.content).map(
|
|
61
|
-
p(t.output_config)&&t.output_config.format!==void 0?true:t.output_format!==void 0}
|
|
55
|
+
`)}function ve(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\
|
|
56
|
+
m",content:typeof n=="string"?n:T(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(
|
|
57
|
+
i=>({...typeof i.id=="string"?{id:i.id}:{},name:String(i.name??""),arguments:ve(i.input)}));e.push({role:"assistant",content:T(o),...a.length>0?{toolCalls:a}:{}});continue}for(let a of o)a.type==="too\
|
|
58
|
+
l_result"&&e.push({role:"tool",content:typeof a.content=="string"?a.content:T(A(a.content)),...typeof a.tool_use_id=="string"?{toolCallId:a.tool_use_id}:{}});let s=T(o);(s!==""||!o.some(a=>a.type==="t\
|
|
59
|
+
ool_result"))&&e.push({role:"user",content:s});}return e}function me(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 Fe(t,e){
|
|
60
|
+
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(me),n.messages=(Array.isArray(
|
|
61
|
+
t.messages)?t.messages:[]).filter(p).map(r=>({role:r.role,content:A(r.content).map(me)})),e&&(n["anthropic-beta"]=e),n}function We(t){let e=t.tool_choice;return p(e)&&(e.type==="any"||e.type==="tool")||
|
|
62
|
+
p(t.output_config)&&t.output_config.format!==void 0?true:t.output_format!==void 0}function Be(t){let e=typeof t.model=="string"?t.model:"";if(e==="")return [];let n=[],r=t.tool_choice;p(r)&&(r.type==="an\
|
|
63
|
+
y"||r.type==="tool")&&Z(e)&&n.push({clave:`tool-choice-forzado:${e}`,mensaje:`[bvsala] Model "${e}" rejects a forced \`tool_choice\` (\`any\` or \`tool\`) with a 400, with or without thinking. This adapter \
|
|
64
|
+
forwards your request unchanged, so the API will reject it: use \`tool_choice: {type: "auto"}\` with \`strict: true\` tools, or \`output_config.format\` for a fixed JSON shape. See https://platform.claude.c\
|
|
65
|
+
om/docs/en/agents-and-tools/tool-use/define-tools#forcing-tool-use`});let o=["temperature","top_p","top_k"].filter(s=>typeof t[s]=="number"&&!(s==="temperature"&&t[s]===1));return o.length>0&&Q(e)&&n.
|
|
66
|
+
push({clave:`muestreo:${e}`,mensaje:`[bvsala] Model "${e}" rejects a non-default ${o.map(s=>`\`${s}\``).join(", ")} with a 400. This adapter forwards your request unchanged, so the API will reject it:\
|
|
67
|
+
drop the parameter for this model. See https://platform.claude.com/docs/en/models/sonnet-5-5/overview`}),n}var M=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
68
|
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
69
|
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:
|
|
70
|
+
id=="string"?{id:n.id}:{},name:String(n.name??""),arguments:ve(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
71
|
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
72
|
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
73
|
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(
|
|
@@ -69,36 +75,36 @@ n.delta)?n.delta:{};if(o.type==="text_delta"&&typeof o.text=="string")return thi
|
|
|
69
75
|
"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
76
|
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
77
|
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:
|
|
73
|
-
n=this.vioDelta?
|
|
78
|
+
[...this.herramientas.entries()].sort((e,n)=>e[0]-n[0]).map(([,e])=>({...e.id?{id:e.id}:{},name:e.name,arguments:Ve(e.json)})).filter(e=>e.name!=="")}respuesta(){let e=q(this.usoInicio,this.usoDelta),
|
|
79
|
+
n=this.vioDelta?W(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=B(e);
|
|
74
80
|
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
|
|
76
|
-
try{return JSON.stringify(JSON.parse(t===""?"{}":t))}catch{return t}}function
|
|
81
|
+
outputTokens:this.usoDelta?.output_tokens,iterations:r})?{unbilledRefusal:true}:{},...s?{servedServiceTier:s}:{},usage:n,...this.reproducible&&this.parada==="end_turn"?{}:{replayable:false}}}};function Ve(t){
|
|
82
|
+
try{return JSON.stringify(JSON.parse(t===""?"{}":t))}catch{return t}}function Ge(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
83
|
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
|
|
79
|
-
if(!n)return new Response(JSON.stringify(r),{status:200,headers:{...o,"content-type":"application/json"}});let s=(i,
|
|
80
|
-
data: ${JSON.stringify(
|
|
84
|
+
content:n,stop_reason:(t.toolCalls?.length??0)>0?"tool_use":"end_turn",stop_sequence:null,usage:{input_tokens:0,output_tokens:0}}}function he(t,e,n){let r=Ge(t,e),o={"request-id":`bvsala_${t.requestId}`};
|
|
85
|
+
if(!n)return new Response(JSON.stringify(r),{status:200,headers:{...o,"content-type":"application/json"}});let s=(i,l)=>`event: ${i}
|
|
86
|
+
data: ${JSON.stringify(l)}
|
|
81
87
|
|
|
82
|
-
`,a=s("message_start",{type:"message_start",message:{...r,content:[],stop_reason:null,stop_sequence:null}});return r.content.forEach((i,
|
|
83
|
-
block_start",index:
|
|
84
|
-
ntent_block_start",index:
|
|
85
|
-
stringify(i.input)}})),a+=s("content_block_stop",{type:"content_block_stop",index:
|
|
86
|
-
a+=s("message_stop",{type:"message_stop"}),new Response(a,{status:200,headers:{...o,"content-type":"text/event-stream"}})}var
|
|
87
|
-
status}`);this.respuesta=n;}respuesta};function
|
|
88
|
-
r.keys().next().value);};return {middleware:async(a,i,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
d),tools:
|
|
92
|
-
v={model:
|
|
93
|
-
{error:y},y}return c.completada=true,f},z=c=>{if(c instanceof
|
|
94
|
-
D.respuesta()},void 0,
|
|
95
|
-
N),
|
|
96
|
-
throw new Error("[bvsala] the model stream ended before completion; nothing was measured.")},()=>{let c=new C;return R(k).then(f=>{
|
|
97
|
-
transmitir(
|
|
98
|
-
d.model,true)}return
|
|
99
|
-
resultado;o(s.id,n.informe(
|
|
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("
|
|
101
|
-
then(
|
|
102
|
-
|
|
103
|
-
read();}catch(R){e.fail(R),
|
|
104
|
-
|
|
88
|
+
`,a=s("message_start",{type:"message_start",message:{...r,content:[],stop_reason:null,stop_sequence:null}});return r.content.forEach((i,l)=>{i.type==="text"?(a+=s("content_block_start",{type:"content_\
|
|
89
|
+
block_start",index:l,content_block:{type:"text",text:""}}),a+=s("content_block_delta",{type:"content_block_delta",index:l,delta:{type:"text_delta",text:i.text}})):(a+=s("content_block_start",{type:"co\
|
|
90
|
+
ntent_block_start",index:l,content_block:{type:"tool_use",id:i.id,name:i.name,input:{}}}),a+=s("content_block_delta",{type:"content_block_delta",index:l,delta:{type:"input_json_delta",partial_json:JSON.
|
|
91
|
+
stringify(i.input)}})),a+=s("content_block_stop",{type:"content_block_stop",index:l});}),a+=s("message_delta",{type:"message_delta",delta:{stop_reason:r.stop_reason,stop_sequence:null},usage:{output_tokens:0}}),
|
|
92
|
+
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.
|
|
93
|
+
status}`);this.respuesta=n;}respuesta};function bt(t){let e=ce("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(
|
|
94
|
+
r.keys().next().value);};return {middleware:async(a,i,l)=>{if(!Ne(a))return i(a);if(typeof a.body!="string")throw new Error(U);let d;try{let c=JSON.parse(a.body);if(!p(c))throw new Error(U);d=c;}catch{throw new Error(
|
|
95
|
+
U)}for(let c of Be(d))e.avisar(c.clave,c.mensaje);let g=l.options,m=ge(g?.bivelio),h=t.context?.({body:d,requestOptions:g}),b=(Array.isArray(d.tools)?d.tools:[]).filter(p).map(c=>de(t,{nombre:String(c.
|
|
96
|
+
name??""),descripcion:typeof c.description=="string"?c.description:void 0,parametros:c.input_schema,delProveedor:c.input_schema===void 0&&typeof c.type=="string"&&c.type!=="custom"})),u=await pe(t,{model:`\
|
|
97
|
+
anthropic/${String(d.model??"")}`,messages:Je(d),tools:b,temperature:typeof d.temperature=="number"?d.temperature:void 0,context:fe(h,m.context),savings:m.savings,proyeccion:Fe(d,a.headers.get("anthro\
|
|
98
|
+
pic-beta")),restricciones:{sinResolvers:We(d)}}),v={model:u.model,messages:u.messages,...u.tools?{tools:u.tools}:{}},_=X(a.url),R=async c=>{let f;try{f=await i(a);}catch(y){throw c.errorDelMarco={error:y},
|
|
99
|
+
y}if(!f.ok){let y=new E(f);throw c.errorDelMarco={error:y},y}return c.completada=true,f},z=c=>{if(c instanceof E)return c.respuesta;throw c};if(d.stream!==true){let c,f,y=I(v,async()=>{c=await R(y);let D=new M;
|
|
100
|
+
return D.json(await c.clone().json()),f=D.id,D.respuesta()},void 0,_),S;try{S=await e.generar(u,y);}catch(D){return z(D)}if(S.tipo==="sin-informe")return c;let N=e.informe(S.result.report);return S.tipo===
|
|
101
|
+
"atajo"?(o(`msg_bvsala_${S.result.requestId}`,N),he(S.result,d.model,false)):(o(f,N),c)}let{abierta:_e,abrir:$}=le(),L,j,k=Object.assign(I(v,async()=>{let c=await R(k),f=new C;j=ye(c,f,e,k,o),$();for await(let y of f)
|
|
102
|
+
if(y.type==="done")return y.response;throw new Error("[bvsala] the model stream ended before completion; nothing was measured.")},()=>{let c=new C;return R(k).then(f=>{L=ye(f,c,e,k,o),$();},f=>c.fail(f)),
|
|
103
|
+
c},_),{abierta:_e}),w;try{w=await e.transmitir(u,k,a.signal??void 0);}catch(c){return z(c)}if(w.tipo==="abierto")return L??j;if(w.tipo==="atajo"){let c=e.informe(w.result.report);return o(`msg_bvsala_${w.
|
|
104
|
+
result.requestId}`,c),he(w.result,d.model,true)}return L??j},reportFor(a){let i=typeof a=="string"?a:a?.id;return typeof i=="string"?r.get(i):void 0},close(){e.close();}}}function ye(t,e,n,r,o){let s=new M,
|
|
105
|
+
a,i=async()=>{try{let u=await r.resultado;o(s.id,n.informe(u.report));}catch{}},l=()=>{e.cerrada||(s.error?e.fail(new Error(`[bvsala] provider stream error: ${s.error}`)):s.vioDelta||s.vioStop?(e.push(
|
|
106
|
+
{type:"done",response:s.respuesta()}),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("ap\
|
|
107
|
+
plication/json"))return t.clone().json().then(u=>{s.json(p(u)?u:{}),s.texto!==""&&e.push({type:"text",delta:s.texto}),l();},u=>e.fail(u)),t;let d=t.body;if(!d)return e.fail(new Error("[bvsala] the mode\
|
|
108
|
+
l stream had no body; nothing was measured.")),t;let g=d.getReader(),m=new O,h=u=>{for(let v of u){let _=s.evento(v);_!==void 0&&e.push({type:"text",delta:_}),(s.vioStop||s.error)&&l();}},b=new ReadableStream(
|
|
109
|
+
{async pull(u){let v;try{v=await g.read();}catch(R){e.fail(R),u.error(R);return}if(v.done){h(m.end()),l(),await a,u.close();return}let _=v.value;u.enqueue(_),h(m.push(_));},cancel(u){return e.fail(new Error(
|
|
110
|
+
"[bvsala] the stream was cancelled by its reader; nothing was measured.")),g.cancel(u)}});return new Response(b,{status:t.status,statusText:t.statusText,headers:t.headers})}export{bt as anthropicSavings};
|