@bivelio/savings-layer 0.4.25 → 0.4.28

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/dist/index.d.ts CHANGED
@@ -142,6 +142,14 @@ declare class SavingsLedger {
142
142
  * not yet billed as verifiable against the invoice).
143
143
  */
144
144
  elididoLocal: number;
145
+ /**
146
+ * Tokens (LOCAL ruler) that tool-output compaction removed from the sent
147
+ * prompt. Subtracted from `removedInputLocal` for the same reason as
148
+ * {@link elididoLocal}: the saving is a COUNT in ledger 3 (`compressedInput`)
149
+ * and never enters the reconstructed baseline or the commission, so `basis`
150
+ * stays "measured" and the provider's invoice shows the effect on its own.
151
+ */
152
+ compactedLocal: number;
145
153
  /**
146
154
  * ¿El estimador que produjo las dos cifras anteriores cuenta con la regla del
147
155
  * proveedor? Lo fija el pipeline al resolver el tokenizador del modelo.
@@ -303,7 +311,7 @@ declare function precioDeLista(model: string, at?: Date | number): CostModel | u
303
311
  * on the customer's machine. `test/version-declarada.test.ts` ties this
304
312
  * constant to package.json so the two can never drift.
305
313
  */
306
- declare const BV_SALA_VERSION = "0.4.25";
314
+ declare const BV_SALA_VERSION = "0.4.28";
307
315
 
308
316
  /**
309
317
  * Environment-agnostic UUID generation.
@@ -449,16 +457,30 @@ interface ChatMessage {
449
457
  * The provider tool loop needs it: OpenAI's Chat Completions rejects a
450
458
  * `role: "tool"` message that does not carry `tool_call_id` and does not follow
451
459
  * an assistant turn with `tool_calls` (a 400). Pass the `id` from the
452
- * {@link ToolCallRequest} the model asked for. Ignored by providers that model
453
- * tool results differently.
460
+ * {@link ToolCallRequest} the model asked for.
461
+ *
462
+ * Anthropic's Messages API needs it too: with it, the Anthropic adapter sends
463
+ * the result as a `tool_result` block paired with the call's `tool_use` block.
464
+ * Without it (or without the ids in the assistant turn's `toolCalls`), the
465
+ * exchange is sent as plain text and the adapter warns once.
454
466
  */
455
467
  toolCallId?: string;
468
+ /**
469
+ * For `role: "tool"`: the tool failed, and `content` says how.
470
+ *
471
+ * The Anthropic adapter sends it as `is_error: true` on the `tool_result`, so
472
+ * the model reads the content as an error rather than as data. Providers
473
+ * without such a flag (OpenAI's Chat Completions) send the content unchanged,
474
+ * so write the error into `content` either way.
475
+ */
476
+ isError?: boolean;
456
477
  /**
457
478
  * For `role: "assistant"`: the tool calls the model requested on that turn.
458
479
  *
459
480
  * Send it back with the assistant turn when you continue a tool conversation,
460
481
  * so the following `role: "tool"` messages are valid. These are the same
461
- * {@link ToolCallRequest}s that came back in {@link SavingsResult.toolCalls}.
482
+ * {@link ToolCallRequest}s that came back in {@link SavingsResult.toolCalls},
483
+ * ids included.
462
484
  */
463
485
  toolCalls?: ToolCallRequest[];
464
486
  /** Opaque provider metadata; never used for the exact-cache key. */
@@ -517,6 +539,35 @@ interface RetrievalOptions {
517
539
  maxRounds?: number;
518
540
  maxDocuments?: number;
519
541
  }
542
+ /**
543
+ * How a request chooses between the provider's native tool search and the layer's
544
+ * tool selector. See {@link SavingsToggles.toolSearch}.
545
+ *
546
+ * - `"off"` (default): the layer's tool selector runs, as without this option.
547
+ * - `"auto"`: native tool search when the adapter talks to the provider's own API host,
548
+ * the catalogue reaches the provider's documented size threshold, and deferring it
549
+ * pays for one search on every request even with a warm prompt cache. Otherwise the
550
+ * selector runs.
551
+ * - `"always"`: native tool search whenever the adapter and model support it, with no
552
+ * size or cost check. Use it when a proxy in front of the provider forwards the
553
+ * search blocks, or when you have measured it on your own traffic.
554
+ */
555
+ type ToolSearchMode = "off" | "auto" | "always";
556
+ /** The long form of {@link SavingsToggles.toolSearch}. */
557
+ interface ToolSearchOptions {
558
+ mode: ToolSearchMode;
559
+ /**
560
+ * Tool ids sent without deferred loading, so the model can call them without
561
+ * searching first. They are part of the cached prefix: keep the list short and the
562
+ * same on every request. Ids that are not in the request's tools are ignored.
563
+ */
564
+ alwaysLoaded?: string[];
565
+ /**
566
+ * The provider's search: `"bm25"` (the model writes natural-language queries; the
567
+ * default) or `"regex"` (the model writes regular expressions).
568
+ */
569
+ variant?: "bm25" | "regex";
570
+ }
520
571
  interface SavingsToggles {
521
572
  exactCache?: boolean;
522
573
  /**
@@ -563,6 +614,30 @@ interface SavingsToggles {
563
614
  * and never enters your commission — your invoice stays the source of truth.
564
615
  */
565
616
  elision?: boolean;
617
+ /**
618
+ * OPT-IN. Compact the text of `role: "tool"` messages before the request is
619
+ * sent: terminal escape sequences and superseded progress-bar states are
620
+ * removed, whitespace between JSON tokens is removed when the whole output is
621
+ * JSON, and runs of identical consecutive lines are folded into one line plus
622
+ * a count. Nothing is summarised and no word is dropped.
623
+ *
624
+ * IT NEVER REWRITES WHAT THE PROVIDER HAS ALREADY CACHED. The decision for each
625
+ * tool output depends only on its own text and on the call that produced it,
626
+ * so an output compacted on the turn it arrives is re-sent with the same bytes
627
+ * on every later turn and the prefix cache keeps matching. Turn it on for a
628
+ * whole conversation (or layer-wide with `toolOutputCompaction` in
629
+ * `createSavingsLayer`), not halfway through one: the first compacted request
630
+ * of a conversation that started without it rewrites the history once.
631
+ *
632
+ * Outputs of calls whose arguments name a file are left verbatim by default,
633
+ * because an agent that edits files by exact string match copies what it read.
634
+ * `false` switches it off for this request even when the layer enables it.
635
+ *
636
+ * What it removes is a COUNT in `report.tokens.compressedInput`; it is never
637
+ * priced into `cost.netSavings` or your commission — your provider's invoice
638
+ * shows the effect.
639
+ */
640
+ toolOutputCompaction?: boolean;
566
641
  serialization?: boolean | "auto";
567
642
  singleflight?: boolean;
568
643
  deterministicResolvers?: boolean;
@@ -656,6 +731,37 @@ interface SavingsToggles {
656
731
  * action can be lost to a discovery step.
657
732
  */
658
733
  toolDiscovery?: boolean;
734
+ /**
735
+ * OPT-IN. Let the PROVIDER search your tool catalogue instead of the layer's tool
736
+ * selector. Defaults to `"off"`. Read {@link ToolSearchMode} for the three modes.
737
+ *
738
+ * With it applied, every tool you pass travels on every request, marked for deferred
739
+ * loading, next to the provider's search tool. The provider keeps deferred definitions
740
+ * out of the prompt prefix; the model searches the catalogue when it needs a tool, and
741
+ * the definitions it finds are added after the prefix. No tool you declared becomes
742
+ * unreachable, and the cached prefix stays the same when the question changes or the
743
+ * catalogue grows.
744
+ *
745
+ * WHERE IT APPLIES. The Anthropic adapter of this package, on a model listed in the
746
+ * provider's tool search compatibility table, with at least one tool to defer.
747
+ * Requests with an output contract, or with the model cascade on, keep the selector.
748
+ * OpenAI documents its tool search for the Responses and Agents APIs, not for Chat
749
+ * Completions, which the OpenAI-compatible adapter speaks, so it keeps the selector too.
750
+ * `report.traces` says which path ran and why (entry `tool-search`).
751
+ *
752
+ * WHAT IT IS WORTH BELONGS TO THE PROVIDER. Your provider's usage counter reflects it
753
+ * directly. The layer credits nothing for it, adds nothing to any ledger for it and
754
+ * charges no commission on it, and a request served this way is never used as the
755
+ * reference to value a later repeat.
756
+ *
757
+ * WHAT CHANGES FOR YOU. Relevance pruning of read tools is off: the model chooses.
758
+ * When your catalogue declares `risk`, write and destructive tools are still withheld
759
+ * from requests the router does not classify as actions, as with the selector. Each
760
+ * search is a short extra step inside the same call, and the definitions it loads are
761
+ * billed as normal input. This adapter sends earlier turns as text, so a later turn
762
+ * that needs a tool searches again.
763
+ */
764
+ toolSearch?: ToolSearchMode | ToolSearchOptions;
659
765
  /**
660
766
  * Send the request to a CHEAPER model of the same family, check the answer
661
767
  * with a deterministic gate, and fall back to the model you asked for only
@@ -1189,8 +1295,11 @@ interface QualityRepairCheck {
1189
1295
  * sending the request without the layer (opt-in, `skipRepairAboveBaseline`).
1190
1296
  * - `decode_time_constraint`: generation was already constrained, so a retry
1191
1297
  * would not change the outcome.
1298
+ * - `provider_refusal`: the provider refused the request (`finishReason:
1299
+ * "content_filter"`, Anthropic's `stop_reason: "refusal"`); a repair on the
1300
+ * same model would be refused, and may be billed, again.
1192
1301
  */
1193
- skipReason?: "output_truncated" | "schema_defect" | "would_cost_more_than_no_layer" | "decode_time_constraint";
1302
+ skipReason?: "output_truncated" | "schema_defect" | "would_cost_more_than_no_layer" | "decode_time_constraint" | "provider_refusal";
1194
1303
  }
1195
1304
  /** Whether the provider stopped because it reached the output limit. */
1196
1305
  interface QualityTruncationCheck {
@@ -1236,7 +1345,31 @@ type QualityCacheMatch = {
1236
1345
  * negation parity and content-word overlap. A hit is never served without it.
1237
1346
  */
1238
1347
  factGuard: "passed";
1348
+ /**
1349
+ * Present when the cache runs with `calibration`: the bound this reuse was
1350
+ * served under, and the labels behind it.
1351
+ */
1352
+ calibration?: SemanticCalibrationBound;
1239
1353
  };
1354
+ /**
1355
+ * The bound a calibrated semantic reuse was served under: with probability at
1356
+ * least `confidence`, at most `maxErrorRate` of the reuses served at or above
1357
+ * `floor` are wrong, where "wrong" is what the calibration's judge rejects.
1358
+ * It rests on the labels counted here, which your own traffic produced.
1359
+ */
1360
+ interface SemanticCalibrationBound {
1361
+ maxErrorRate: number;
1362
+ confidence: number;
1363
+ /** The certified floor this reuse's score cleared. */
1364
+ floor: number;
1365
+ /** Held-out labels at or above the floor that certified it, and how many were wrong reuses. */
1366
+ heldOutAtFloor: number;
1367
+ wrongHeldOutAtFloor: number;
1368
+ /** Labels collected in this traffic segment. */
1369
+ labeled: number;
1370
+ /** Calibrations are kept apart for single-shot and agentic traffic. */
1371
+ segment: "single_shot" | "agentic";
1372
+ }
1240
1373
  /** What the model cascade decided on this request. See {@link ModelCascadeReport}. */
1241
1374
  interface QualityCascadeCheck {
1242
1375
  mode: "shadow" | "on";
@@ -1402,7 +1535,8 @@ interface SavingsReport {
1402
1535
  deferred?: DeferredExecutionReport;
1403
1536
  /**
1404
1537
  * Where the provider was allowed to write its prompt cache. Present whenever
1405
- * `savings.standalone` is `true`, including when it changed nothing.
1538
+ * `savings.standalone` is `true`, including when it changed nothing, and when
1539
+ * `cacheBreakpoints: "auto"` switched this request to explicit mode.
1406
1540
  */
1407
1541
  cacheWrites?: CacheWriteReport;
1408
1542
  /** How a streamed request was delivered. Present only on results of `generateStream`. */
@@ -1761,6 +1895,176 @@ declare function stableStringify(value: unknown): string;
1761
1895
  declare function modelFamilyOf(model: string): string;
1762
1896
  declare function canonicalize(request: SavingsRequest): CanonicalRequest;
1763
1897
 
1898
+ /**
1899
+ * Thrown by `importCalibration` / `exportCalibration` when a calibration state
1900
+ * cannot be used as-is: another embedder, verifier, judge or configuration, a
1901
+ * newer format, or numbers that do not add up.
1902
+ */
1903
+ declare class CalibrationStateError extends Error {
1904
+ constructor(message: string);
1905
+ }
1906
+
1907
+ /**
1908
+ * Decides whether a stored answer would have been right for a new request,
1909
+ * given the answer your provider just produced for it. `true` = the stored
1910
+ * answer was reusable.
1911
+ *
1912
+ * It is the definition of "correct" behind every calibrated floor: the bound
1913
+ * holds for what your judge accepts, nothing more. A judge that calls a model
1914
+ * costs money this layer does not see and never subtracts from a saving.
1915
+ */
1916
+ type AnswerJudge = (stored: CachedAnswer, fresh: CachedAnswer) => boolean | Promise<boolean>;
1917
+ /**
1918
+ * Opt-in calibration of the semantic cache: instead of a fixed acceptance floor,
1919
+ * the cache serves a reworded request only at or above a floor that labeled
1920
+ * traffic has certified, so that at most `maxErrorRate` of the reuses served
1921
+ * from that floor up are wrong, at the stated `confidence`.
1922
+ *
1923
+ * Until a floor is certified the cache serves nothing: every candidate goes to
1924
+ * your provider, exactly as it would without the cache, and the fresh answer is
1925
+ * compared with the stored one to produce a label. Calibration costs reuses,
1926
+ * never an extra provider call.
1927
+ *
1928
+ * Only candidates at or above `similarityThreshold` are labeled, so no floor
1929
+ * below it can be certified: lower it to let the calibration look further down.
1930
+ * Your verifier, if any, still scores every candidate, and the score that is
1931
+ * calibrated is the lower of similarity and its score.
1932
+ */
1933
+ interface SemanticCalibrationOptions {
1934
+ /** Highest share of wrong reuses you accept, in (0, 0.5]. E.g. 0.02. */
1935
+ maxErrorRate: number;
1936
+ /** Confidence of the bound, in (0.5, 1). Default 0.95. */
1937
+ confidence?: number;
1938
+ /**
1939
+ * How a label is produced. Default {@link sameAnswer}: identical structured
1940
+ * data, or identical text after trimming and collapsing whitespace. For free
1941
+ * text, pass your own: with the default, two differently worded correct
1942
+ * answers count as a wrong reuse, and the floor stays uncertified.
1943
+ */
1944
+ judge?: AnswerJudge;
1945
+ /**
1946
+ * Share of reuses that clear the floor but are sent to your provider anyway,
1947
+ * to keep labeling above it. In [0, 1). Default 0.05. Without these labels
1948
+ * the floor cannot be re-tested where it serves.
1949
+ */
1950
+ auditRate?: number;
1951
+ /**
1952
+ * Stop serving a stored answer at or below a score where it was already
1953
+ * judged wrong for another request. It only ever removes reuses. Default true.
1954
+ */
1955
+ perRecordFloors?: boolean;
1956
+ /** Source of randomness for audits, in [0, 1). Default `Math.random`. */
1957
+ random?: () => number;
1958
+ /**
1959
+ * Names your `judge` (e.g. `"support-answers-v2"`), so that a saved
1960
+ * calibration is only ever restored under the same definition of a wrong
1961
+ * reuse. Required to export a calibration when you pass your own judge.
1962
+ */
1963
+ judgeId?: string;
1964
+ }
1965
+
1966
+ /**
1967
+ * A calibration saved with `exportCalibration`, to be restored with
1968
+ * `importCalibration` after a restart. Plain JSON: store it as is.
1969
+ *
1970
+ * It carries the whole experiment, not a summary: both halves of the labels
1971
+ * (the half that proposes floors and the half kept apart to test them), how
1972
+ * many tests have already spent the confidence budget, and the floor in force.
1973
+ * Restoring it continues the same calibration, so the bound keeps holding
1974
+ * across restarts; it never starts the budget over.
1975
+ *
1976
+ * It describes itself: who scored the requests (`embedder`, `verifier`), who
1977
+ * judged the answers (`judge`) and the numbers of the test (`config`). A cache
1978
+ * with any of them different refuses it.
1979
+ */
1980
+ interface SemanticCalibrationState {
1981
+ format: "bvsala.semantic-calibration";
1982
+ /** Format and method version. A newer SDK may read an older state; never the other way round. */
1983
+ version: 1;
1984
+ /** `built_in:<version>` for the embedder shipped with the SDK, `custom:<embedderId>` for yours. */
1985
+ embedder: string;
1986
+ /** `null` without a verifier, `custom:<verifierId>` with yours. */
1987
+ verifier: string | null;
1988
+ /** `built_in:<version>` for {@link sameAnswer}, `custom:<judgeId>` for yours. */
1989
+ judge: string;
1990
+ config: {
1991
+ maxErrorRate: number;
1992
+ confidence: number;
1993
+ /** Only candidates at or above it are labeled, so it is part of what the labels mean. */
1994
+ similarityThreshold: number;
1995
+ /** Candidate floors are tested on a fixed grid of `grid + 1` values in [0, 1]. */
1996
+ grid: number;
1997
+ };
1998
+ /** One calibration per traffic segment; a segment with no state yet is absent. */
1999
+ segments: {
2000
+ single_shot?: SemanticCalibrationSegmentState;
2001
+ agentic?: SemanticCalibrationSegmentState;
2002
+ };
2003
+ }
2004
+ /** The calibration of one traffic segment. Grid indices are scores times `grid`. */
2005
+ interface SemanticCalibrationSegmentState {
2006
+ /** Labels recorded. They alternate by arrival between the two halves below. */
2007
+ labeled: number;
2008
+ /** Tests already run. It follows from `labeled`, and a state where it does not is refused. */
2009
+ looks: number;
2010
+ /** The floor the next test starts from (a grid index), or null. */
2011
+ previousCandidate: number | null;
2012
+ /** The floor in force and the held-out labels that certified it, or null. */
2013
+ certified: {
2014
+ floorIndex: number;
2015
+ heldOut: number;
2016
+ wrong: number;
2017
+ } | null;
2018
+ /** The half that proposes floors: `[gridIndex, labels, wrong]` where there are labels. */
2019
+ selection: [number, number, number][];
2020
+ /** The half kept apart to test them, in the same shape. */
2021
+ heldOut: [number, number, number][];
2022
+ }
2023
+ /** One labeled reuse decision: the score the cache would have served on, and whether the stored answer was right. */
2024
+ interface CalibrationObservation {
2025
+ score: number;
2026
+ correct: boolean;
2027
+ }
2028
+ /** Where a calibration stands. */
2029
+ interface SemanticCalibrationStatus {
2030
+ maxErrorRate: number;
2031
+ confidence: number;
2032
+ /** Lowest certified score; null while the labels certify none. */
2033
+ floor: number | null;
2034
+ /** Labels collected. */
2035
+ labeled: number;
2036
+ /** Labels kept apart to test the floor (about half). */
2037
+ heldOut: number;
2038
+ /** Held-out labels at or above the floor, and how many of them were wrong reuses. 0 without a floor. */
2039
+ heldOutAtFloor: number;
2040
+ wrongHeldOutAtFloor: number;
2041
+ /**
2042
+ * Held-out labels at or above a floor, all correct, that the next test needs
2043
+ * to certify it: the shortest possible wait, not a promise.
2044
+ */
2045
+ minHeldOutToCertify: number;
2046
+ /** Online only: the label count at which the floor is re-tested next. Each test comes when the labels have grown by half. */
2047
+ nextTestAt?: number;
2048
+ }
2049
+ /** Default {@link AnswerJudge}: the stored answer counts as right only if it is the same answer. */
2050
+ declare function sameAnswer(stored: CachedAnswer, fresh: CachedAnswer): boolean;
2051
+ /**
2052
+ * Certifies an acceptance floor from labels you already have (your own traffic,
2053
+ * or a labeled set scored with your embedder), with the same test the cache
2054
+ * runs online. Pass the result's `floor` as `acceptThreshold`, or `null` means
2055
+ * these labels do not support any floor at this bound.
2056
+ *
2057
+ * The labels are split at random (fixed seed, independent of their content):
2058
+ * one half proposes candidate floors, the other half tests them, most
2059
+ * conservative first, with an exact binomial test. With probability at least `confidence`, the share of wrong
2060
+ * reuses at or above the returned floor is at most `maxErrorRate`, provided new
2061
+ * requests come from the same distribution as these labels.
2062
+ */
2063
+ declare function calibrateSemanticFloor(observations: readonly CalibrationObservation[], options: {
2064
+ maxErrorRate: number;
2065
+ confidence?: number;
2066
+ }): SemanticCalibrationStatus;
2067
+
1764
2068
  /**
1765
2069
  * Semantic cache (safe-by-construction).
1766
2070
  *
@@ -1823,6 +2127,11 @@ interface SemanticRecord {
1823
2127
  /** Sampling seed of the request, or null when it set none. */
1824
2128
  seed: number | null;
1825
2129
  storedAtMs: number;
2130
+ /**
2131
+ * With calibration on: the highest score at which this stored answer was
2132
+ * judged wrong for another request. It is never served at or below it.
2133
+ */
2134
+ failedAtScore?: number;
1826
2135
  }
1827
2136
  interface SemanticHit {
1828
2137
  record: SemanticRecord;
@@ -1840,6 +2149,30 @@ interface SemanticHit {
1840
2149
  embedder: "built_in" | "custom";
1841
2150
  /** Whether a verifier scored this hit. When false, `verifierScore` equals `similarity`. */
1842
2151
  verifierRan: boolean;
2152
+ /** With calibration on: the bound this hit is served under. `acceptFloor` is then the certified floor. */
2153
+ calibration?: SemanticCalibrationBound;
2154
+ }
2155
+ /**
2156
+ * With calibration on, a candidate the cache did not serve: the request goes to
2157
+ * your provider, and {@link InMemorySemanticCache.observe} turns the answer into
2158
+ * a label.
2159
+ *
2160
+ * - `calibrating`: no floor is certified yet in this traffic segment.
2161
+ * - `below_floor`: the candidate's score is under the certified floor.
2162
+ * - `record_floor`: this stored answer was already judged wrong at this score or higher.
2163
+ * - `audit`: the candidate cleared the floor and was sampled to keep testing it.
2164
+ */
2165
+ interface SemanticProbe {
2166
+ reason: "calibrating" | "below_floor" | "record_floor" | "audit";
2167
+ /** The score the decision was made on: the lower of similarity and your verifier's score. */
2168
+ score: number;
2169
+ segment: "single_shot" | "agentic";
2170
+ record: SemanticRecord;
2171
+ }
2172
+ /** What {@link InMemorySemanticCache.lookup} found: a hit to serve, a candidate to label, or neither. */
2173
+ interface SemanticLookup {
2174
+ hit: SemanticHit | null;
2175
+ probe: SemanticProbe | null;
1843
2176
  }
1844
2177
  interface SemanticCacheOptions {
1845
2178
  /**
@@ -1857,7 +2190,16 @@ interface SemanticCacheOptions {
1857
2190
  * cache miss — never a wrong answer.
1858
2191
  */
1859
2192
  embed?: EmbeddingFn;
2193
+ /**
2194
+ * Names your `embed` function: model and version, e.g.
2195
+ * `"text-embedding-3-small@1536"`. A saved calibration carries it, and one
2196
+ * recorded under another embedder is refused on import. Required to export a
2197
+ * calibration when you pass your own `embed`.
2198
+ */
2199
+ embedderId?: string;
1860
2200
  verify?: SemanticVerifier;
2201
+ /** Names your `verify` function, like {@link embedderId}. Required to export a calibration when you pass one. */
2202
+ verifierId?: string;
1861
2203
  /** Cosine similarity floor for a candidate to be considered at all. */
1862
2204
  similarityThreshold?: number;
1863
2205
  /** Verifier score floor for acceptance. */
@@ -1865,6 +2207,12 @@ interface SemanticCacheOptions {
1865
2207
  ttlMs?: number;
1866
2208
  maxRecords?: number;
1867
2209
  now?: () => number;
2210
+ /**
2211
+ * Opt-in: replace the fixed acceptance floor with one your own labeled
2212
+ * traffic certifies for a wrong-reuse rate you choose. Off by default. See
2213
+ * {@link SemanticCalibrationOptions}.
2214
+ */
2215
+ calibration?: SemanticCalibrationOptions;
1868
2216
  }
1869
2217
  declare function cosine(a: number[], b: number[]): number;
1870
2218
  declare function numericTokens(s: string): string[];
@@ -1889,19 +2237,141 @@ declare class InMemorySemanticCache {
1889
2237
  private readonly maxRecords;
1890
2238
  private readonly now;
1891
2239
  private readonly embebedorPropio;
2240
+ private readonly calibracion;
2241
+ private readonly estados;
2242
+ private readonly nombres;
2243
+ private readonly pendientes;
2244
+ private readonly aceptacionExplicita;
1892
2245
  constructor(opts?: SemanticCacheOptions);
2246
+ /** Whether this cache runs with `calibration`. */
2247
+ get calibrated(): boolean;
1893
2248
  /**
1894
2249
  * Find a safe hit for a canonical request, or null. Metadata filters run
1895
2250
  * FIRST and hard — a candidate from another tenant, scope, or knowledge epoch
1896
2251
  * is never even scored.
2252
+ *
2253
+ * With calibration on, it returns only a hit served under a certified floor
2254
+ * and labels nothing: without {@link lookup} and {@link observe} (the layer
2255
+ * calls both) the calibration never gets a label and never certifies.
1897
2256
  */
1898
2257
  find(c: CanonicalRequest): Promise<SemanticHit | null>;
2258
+ /**
2259
+ * The best candidate that passes every hard filter, the similarity floor and
2260
+ * the fact guard. With `conAceptacion`, also the confidence floor (the path
2261
+ * without calibration, unchanged); without it, calibration decides.
2262
+ */
2263
+ private mejorCandidata;
2264
+ /**
2265
+ * Like {@link find}, and with calibration on it also returns the candidate it
2266
+ * did not serve, so the caller can label it with {@link observe} once the
2267
+ * provider has answered. Without calibration, `probe` is always null.
2268
+ *
2269
+ * `agentic` picks the traffic segment: calibrations are kept apart for
2270
+ * single-shot and agentic requests.
2271
+ */
2272
+ lookup(c: CanonicalRequest, opts?: {
2273
+ agentic?: boolean;
2274
+ }): Promise<SemanticLookup>;
2275
+ /**
2276
+ * Label a candidate {@link lookup} did not serve, with the answer your provider
2277
+ * produced for that request. Resolves to the judge's verdict, or null when
2278
+ * there is no calibration or the judge failed (no label is recorded then).
2279
+ * It never rejects.
2280
+ */
2281
+ observe(probe: SemanticProbe, fresh: CachedAnswer): Promise<boolean | null>;
2282
+ private etiquetar;
2283
+ /** Resolves once every pending {@link observe} has recorded its label. */
2284
+ settled(): Promise<void>;
2285
+ /** Where the calibration of a traffic segment stands, or null without calibration. */
2286
+ calibrationStatus(segment?: SemanticProbe["segment"]): SemanticCalibrationStatus | null;
2287
+ /**
2288
+ * The calibration's full state, to restore it with {@link importCalibration}
2289
+ * in a later process: after a deploy, or in the next serverless invocation.
2290
+ * Plain JSON. Call it after `await cache.settled()` so labels still being
2291
+ * judged are included.
2292
+ *
2293
+ * The bound holds along one line of history: restore the latest state you
2294
+ * saved and keep saving from where it went. Processes that continue the same
2295
+ * state at the same time each spend its remaining confidence again. Each one
2296
+ * keeps its own bound, but the chance that some process certifies a wrong
2297
+ * floor grows with how many do it, and whichever saves last overwrites the
2298
+ * labels of the others.
2299
+ *
2300
+ * Throws {@link CalibrationStateError} without calibration, and when you
2301
+ * passed your own `embed`, `verify` or `judge` without naming it
2302
+ * (`embedderId`, `verifierId`, `calibration.judgeId`).
2303
+ */
2304
+ exportCalibration(): SemanticCalibrationState;
2305
+ /**
2306
+ * Restore a state saved with {@link exportCalibration} (the object, or its
2307
+ * JSON text), right after constructing the cache. The calibration continues
2308
+ * exactly where it was: the same labels in the same two halves, the same
2309
+ * tests already run and the same floor in force.
2310
+ *
2311
+ * All or nothing. Throws {@link CalibrationStateError}, and changes nothing,
2312
+ * when the state comes from another embedder, verifier, judge or
2313
+ * configuration, from a newer SDK, when its numbers do not add up, or when
2314
+ * this cache has already recorded labels in a segment the state carries
2315
+ * (merging two histories would count some labels twice).
2316
+ */
2317
+ importCalibration(state: SemanticCalibrationState | string | unknown): void;
2318
+ private identidad;
2319
+ private estadoDe;
1899
2320
  store(c: CanonicalRequest, answer: CachedAnswer): Promise<void>;
1900
2321
  /** Drop every record for a knowledge epoch (invalidation on re-index). */
1901
2322
  invalidateEpoch(knowledgeEpoch: string): number;
1902
2323
  get size(): number;
1903
2324
  }
1904
2325
 
2326
+ /**
2327
+ * Where the layer keeps what it learned about your prompt-cache reuse
2328
+ * (`cacheBreakpoints: "auto"`), when it must outlive a single instance.
2329
+ *
2330
+ * By default that measurement lives in the layer's memory, which is enough for
2331
+ * a long-running server. On serverless platforms, where an instance serves one
2332
+ * request or lives a few minutes, memory is gone before the measurement means
2333
+ * anything, so the layer never gets past its fixed placement. Pass a store
2334
+ * shared by your instances (Redis, a KV) and every instance reads and extends
2335
+ * the same measurement.
2336
+ *
2337
+ * What is stored, per traffic class (model, tools, output contract and system
2338
+ * header): prompt-prefix fingerprints, the times they were sent, reuse counts
2339
+ * and how many predicted cache reads the provider confirmed (with
2340
+ * `cacheMissDiagnostics`, also the provider's response ids). Never prompt text
2341
+ * or any other content. Values are opaque JSON strings, one key per traffic
2342
+ * class, rewritten after each request of that class. A client that hands them
2343
+ * back already parsed (as some Redis and KV clients do) works as well.
2344
+ *
2345
+ * The interface is deliberately minimal so any key-value store can back it.
2346
+ * The layer awaits `get` before placing breakpoints and `get` + `set` after the
2347
+ * provider answers, so a slow store adds its latency to those requests. A store
2348
+ * that fails is treated as empty for that request, and nothing is written
2349
+ * back: the placement falls back to what a fresh process would do.
2350
+ */
2351
+ interface CacheBreakpointStore {
2352
+ /** The value last `set` for `key` (the string, or the same JSON already parsed), or null/undefined when there is none. */
2353
+ get(key: string): Promise<unknown>;
2354
+ /** `ttlMs`: drop the value if no request of its traffic class rewrites it for this long. What it resolves to is ignored. */
2355
+ set(key: string, value: string, ttlMs: number): Promise<unknown>;
2356
+ }
2357
+ /**
2358
+ * In-memory {@link CacheBreakpointStore}, with TTL and a size bound. Useful for
2359
+ * tests and for several layers in one process; across processes, back the
2360
+ * interface with a shared store.
2361
+ */
2362
+ declare class InMemoryCacheBreakpointStore implements CacheBreakpointStore {
2363
+ private readonly entradas;
2364
+ private readonly maxEntries;
2365
+ private readonly now;
2366
+ constructor(opts?: {
2367
+ maxEntries?: number;
2368
+ now?: () => number;
2369
+ });
2370
+ get(key: string): Promise<string | null>;
2371
+ set(key: string, value: string, ttlMs: number): Promise<void>;
2372
+ get size(): number;
2373
+ }
2374
+
1905
2375
  /**
1906
2376
  * Singleflight (in-flight coalescing).
1907
2377
  *
@@ -2310,6 +2780,69 @@ declare const noopCompressor: Compressor;
2310
2780
  */
2311
2781
  declare function compresionFiel(original: string, comprimido: string): boolean;
2312
2782
 
2783
+ /** A deterministic, content-only transformation applied to a tool output. */
2784
+ type CompactionTransform = "ansi" | "carriage-return" | "json" | "repeated-lines";
2785
+ /** Every transformation, in the order they are applied. */
2786
+ declare const COMPACTION_TRANSFORMS: readonly CompactionTransform[];
2787
+ /**
2788
+ * How tool outputs are compacted. Every field is optional.
2789
+ *
2790
+ * The decision for each tool output depends only on its own text and the tool
2791
+ * call that produced it — never on its position, the size of the prompt or the
2792
+ * model — so an output compacted when it first arrives is re-sent with the same
2793
+ * bytes on every later turn and the provider's prefix cache keeps matching.
2794
+ * Change these settings between conversations, not in the middle of one: a
2795
+ * change rewrites outputs the provider has already cached.
2796
+ */
2797
+ interface ToolOutputCompactionSettings {
2798
+ /** Which transformations may run. Default: all of {@link COMPACTION_TRANSFORMS}. */
2799
+ transforms?: readonly CompactionTransform[];
2800
+ /**
2801
+ * Leave an output verbatim unless compaction removes at least this many
2802
+ * characters (default 256). Small gains are not worth changing the text the
2803
+ * model sees.
2804
+ */
2805
+ minSavedChars?: number;
2806
+ /** Tool names whose outputs are never compacted. */
2807
+ exceptTools?: readonly string[];
2808
+ /**
2809
+ * Also compact outputs of tool calls whose arguments name a file (`path`,
2810
+ * `file_path`, `filename`…). Default `false`: an agent that edits files by
2811
+ * exact string match copies what it read, and a compacted view of a file
2812
+ * would not match the file on disk.
2813
+ */
2814
+ compactFileViews?: boolean;
2815
+ }
2816
+ /** The outcome of compacting one tool output. */
2817
+ interface ToolOutputCompaction {
2818
+ /** The text to send: compacted when `changed`, otherwise the input as-is. */
2819
+ text: string;
2820
+ changed: boolean;
2821
+ /** Transformations that changed the text, in the order they ran. */
2822
+ applied: CompactionTransform[];
2823
+ originalChars: number;
2824
+ compactChars: number;
2825
+ /** Why the output was, or was not, compacted. */
2826
+ reason: string;
2827
+ }
2828
+ /** Default minimum saving, in characters, for an output to be compacted. */
2829
+ declare const DEFAULT_MIN_SAVED_CHARS = 256;
2830
+ /**
2831
+ * Compact one tool output with deterministic, content-only transformations.
2832
+ *
2833
+ * Nothing is summarised and no word is dropped: terminal escape sequences and
2834
+ * superseded progress-bar states are removed, whitespace between JSON tokens is
2835
+ * removed (strings and numbers are kept byte for byte, and the result must parse
2836
+ * to the same value), and runs of identical consecutive lines are folded into
2837
+ * the first line plus a count that expands back to the exact original.
2838
+ *
2839
+ * The same input always produces the same output. Call it where a tool result
2840
+ * is produced, before you append it to your conversation history, and the
2841
+ * history itself holds the compacted text — the provider's prefix cache is
2842
+ * never disturbed.
2843
+ */
2844
+ declare function compactToolOutput(text: string, settings?: ToolOutputCompactionSettings): ToolOutputCompaction;
2845
+
2313
2846
  /** What the layer needs to know about a provider's prefix cache to price a decision. */
2314
2847
  interface CacheTier {
2315
2848
  /**
@@ -2468,6 +3001,72 @@ interface ProviderRequest {
2468
3001
  * letting the provider choose. Others ignore it.
2469
3002
  */
2470
3003
  explicitCacheWrites?: boolean;
3004
+ /**
3005
+ * Provider-native tool search, decided by the layer for this request. An adapter that
3006
+ * supports it sends every tool with deferred loading except `alwaysLoaded`, plus the
3007
+ * provider's search tool. The layer sets it only for adapters of this package that
3008
+ * declared the capability; other providers never receive it.
3009
+ */
3010
+ toolSearch?: ProviderToolSearch;
3011
+ /**
3012
+ * Where the layer's breakpoint placer (`cacheBreakpoints: "auto"`) wants the
3013
+ * provider's prompt cache written, on top of `stablePrefixMessages`. Only
3014
+ * adapters with explicit breakpoints (Anthropic) act on it; others ignore it.
3015
+ * Absent: the adapter's fixed placement.
3016
+ */
3017
+ cachePlacement?: CachePlacement;
3018
+ /**
3019
+ * Ask the provider to compare this request's prompt prefix with the one of
3020
+ * an earlier response and report where they diverged (Anthropic cache
3021
+ * diagnostics, Claude API only). `previousMessageId: null` opts in without a
3022
+ * comparison, so that a later request can be compared with this one.
3023
+ * Adapters without the feature, or talking to another host, ignore it.
3024
+ */
3025
+ cacheDiagnostics?: {
3026
+ previousMessageId: string | null;
3027
+ };
3028
+ }
3029
+ /** See {@link ProviderRequest.toolSearch}. */
3030
+ interface ProviderToolSearch {
3031
+ /** The provider's search: natural-language queries (`"bm25"`) or regular expressions. */
3032
+ variant: "bm25" | "regex";
3033
+ /** Tool ids sent without deferred loading. */
3034
+ alwaysLoaded: string[];
3035
+ }
3036
+ /** What the provider's native tool search did in one call, as its response shows it. */
3037
+ interface ProviderToolSearchUsage {
3038
+ /** Searches the model ran. */
3039
+ searches: number;
3040
+ /** Tool names the searches loaded, in order, without repeats. */
3041
+ loaded: string[];
3042
+ /** Searches that came back as an error result instead of references. */
3043
+ errors: number;
3044
+ /**
3045
+ * Search results from earlier turns that the adapter sent back in this request's history,
3046
+ * so the model could call the tools they loaded without searching again: how many results,
3047
+ * and which tools. Absent when none were sent back.
3048
+ */
3049
+ resent?: {
3050
+ results: number;
3051
+ tools: string[];
3052
+ };
3053
+ }
3054
+ /**
3055
+ * Breakpoint placement decided by the layer from the reuse it measured on your
3056
+ * traffic. See `cacheBreakpoints` in the layer configuration.
3057
+ */
3058
+ interface CachePlacement {
3059
+ /**
3060
+ * Also mark the last block of the request, so that a continuation of this
3061
+ * exact prompt (the next turn of a conversation or an agent loop) reads all
3062
+ * of it from cache instead of writing it again.
3063
+ */
3064
+ tail: boolean;
3065
+ /**
3066
+ * Cache lifetime for every breakpoint of this request. On Anthropic a
3067
+ * one-hour write is billed at 2x the input rate, a five-minute one at 1.25x.
3068
+ */
3069
+ ttl: "5m" | "1h";
2471
3070
  }
2472
3071
  interface ProviderUsage {
2473
3072
  inputTokens: number;
@@ -2486,6 +3085,13 @@ interface ProviderUsage {
2486
3085
  * bill INCREASE as a saving.
2487
3086
  */
2488
3087
  cacheWriteInputTokens?: number;
3088
+ /**
3089
+ * The part of `cacheWriteInputTokens` written with a ONE-HOUR lifetime
3090
+ * (Anthropic `cache_creation.ephemeral_1h_input_tokens`), billed at 2x the
3091
+ * input rate instead of 1.25x. Absent when the provider did not break the
3092
+ * writes down by lifetime.
3093
+ */
3094
+ cacheWrite1hInputTokens?: number;
2489
3095
  /**
2490
3096
  * Output tokens the provider billed for hidden reasoning, already included in
2491
3097
  * `outputTokens` (OpenAI `completion_tokens_details.reasoning_tokens`).
@@ -2575,6 +3181,27 @@ interface ProviderResponse {
2575
3181
  * replayable.
2576
3182
  */
2577
3183
  replayable?: boolean;
3184
+ /**
3185
+ * Present when the request carried {@link ProviderRequest.toolSearch} and the adapter can
3186
+ * read what the search did. The layer only reports it; it credits nothing for it.
3187
+ */
3188
+ toolSearch?: ProviderToolSearchUsage;
3189
+ /** The provider's own id for this response (Anthropic `id`), when it gave one. */
3190
+ providerMessageId?: string;
3191
+ /**
3192
+ * The provider's answer to {@link ProviderRequest.cacheDiagnostics}, when the
3193
+ * request asked for a comparison. `missReason` is the provider's
3194
+ * `cache_miss_reason.type` (`model_changed`, `system_changed`,
3195
+ * `tools_changed`, `messages_changed`, `previous_message_not_found`,
3196
+ * `unavailable`), `"none"` when it compared and found no divergence, or
3197
+ * `"pending"` when the comparison had not finished. `missedInputTokens` is the
3198
+ * provider's estimate of the input after the divergence: a magnitude, not a
3199
+ * billing number.
3200
+ */
3201
+ cacheDiagnostics?: {
3202
+ missReason: string;
3203
+ missedInputTokens?: number;
3204
+ };
2578
3205
  raw?: unknown;
2579
3206
  }
2580
3207
  /** One event of a provider-level stream. */
@@ -2819,6 +3446,13 @@ interface SavingsLayerConfig {
2819
3446
  * with your own measurements to unlock the full decision in both directions.
2820
3447
  */
2821
3448
  compressionEconomics?: CompressionEconomics;
3449
+ /**
3450
+ * Compact tool outputs on every request (`true`, or settings), unless a
3451
+ * request sets `savings.toolOutputCompaction: false`. Off by default; a request
3452
+ * can also switch it on alone with `savings.toolOutputCompaction: true`.
3453
+ * See {@link ToolOutputCompactionSettings}.
3454
+ */
3455
+ toolOutputCompaction?: boolean | ToolOutputCompactionSettings;
2822
3456
  retriever?: Retriever;
2823
3457
  reranker?: Reranker;
2824
3458
  /**
@@ -2832,6 +3466,62 @@ interface SavingsLayerConfig {
2832
3466
  * All off by default; a request's own `savings` value always wins.
2833
3467
  */
2834
3468
  priceLevers?: PriceLeverSettings;
3469
+ /**
3470
+ * Where the layer writes the provider's prompt cache. Defaults to `"fixed"`.
3471
+ *
3472
+ * `"fixed"`: one breakpoint at the end of the system header and one at the
3473
+ * end of the history before the last user message, subject to the measured
3474
+ * hit-rate gate. Nothing else.
3475
+ *
3476
+ * `"auto"`: the layer watches, per model, tool set and system header, how
3477
+ * often your traffic re-sends each part of the prompt and how long after.
3478
+ * Watching costs nothing — it compares prefixes locally, it does not mark
3479
+ * anything to find out. Once it has enough samples it places breakpoints only
3480
+ * where the reuse beats the write premium: the tail of the prompt when turns
3481
+ * continue each other (agent loops, conversations), the history only when it
3482
+ * is re-sent, and a one-hour lifetime on Anthropic only when your gaps and the
3483
+ * size of this request make it pay. On OpenAI models that bill cache writes
3484
+ * (GPT-5.6 and later, on OpenAI's own API) it switches to explicit mode, with
3485
+ * no write on your question, when continuations are rare; `savings.standalone`
3486
+ * still wins when you set it. Until it has samples it behaves exactly like
3487
+ * `"fixed"`.
3488
+ *
3489
+ * What it did and why is in `report.traces` (`prefix-cache-placement`), and
3490
+ * the effect is read from the provider's own counters (`cachedInputTokens`,
3491
+ * `cacheWriteInputTokens`): if they do not confirm the reads the layer
3492
+ * predicted, it falls back to `"fixed"` for that traffic. The write premium it
3493
+ * causes, one-hour writes at 2x included, is charged to `cost.actual`.
3494
+ */
3495
+ cacheBreakpoints?: "fixed" | "auto";
3496
+ /**
3497
+ * Where `cacheBreakpoints: "auto"` keeps its reuse measurement when it must
3498
+ * outlive a single instance. Optional: without it the measurement lives in this
3499
+ * layer's memory, exactly as before.
3500
+ *
3501
+ * On serverless platforms (one instance per request, or instances that live
3502
+ * a few minutes) memory never gathers enough samples, so `"auto"` stays on
3503
+ * the fixed placement. Pass a store your instances share and each one reads
3504
+ * and extends the same measurement: what one instance learned, the next one
3505
+ * uses. Only prompt-prefix fingerprints, timestamps and counts are stored,
3506
+ * never content. See {@link CacheBreakpointStore}.
3507
+ */
3508
+ cacheBreakpointStore?: CacheBreakpointStore;
3509
+ /**
3510
+ * With `cacheBreakpoints: "auto"` on Anthropic's own API: when the layer
3511
+ * expects a request to read the cache entry an earlier response wrote, ask
3512
+ * Anthropic to compare the two requests (cache diagnostics), and when that
3513
+ * read does not arrive, publish the provider's `cache_miss_reason` in the
3514
+ * `prefix-cache-placement` trace (`system_changed`, `tools_changed`,
3515
+ * `messages_changed`…, or `none` when the requests matched and the entry was
3516
+ * simply gone). Off by default.
3517
+ *
3518
+ * Each request that opts in makes Anthropic keep a fingerprint of it for a
3519
+ * short time (hashes and token-count estimates, never prompt text), scoped to
3520
+ * your organization and workspace; check it against your data-retention
3521
+ * terms before switching this on. Not sent through Bedrock, Vertex or any
3522
+ * other host. It costs nothing and changes no bill.
3523
+ */
3524
+ cacheMissDiagnostics?: boolean;
2835
3525
  /**
2836
3526
  * Signed BiVelio license token (from your paid subscription). This is the
2837
3527
  * primary way to license the SDK: `createSavingsLayer` verifies it offline
@@ -3160,6 +3850,195 @@ interface SavingsCallOptions {
3160
3850
  savings?: SavingsAdapterLevers;
3161
3851
  }
3162
3852
 
3853
+ /** One request of a batch: your id for it, and the same request you would pass to `generate()`. */
3854
+ interface BatchRequest {
3855
+ /**
3856
+ * Your id for this request, unique within one `submit()`: 1 to 64 letters, digits,
3857
+ * `_` or `-`. Results come back in any order and are matched by this id.
3858
+ */
3859
+ customId: string;
3860
+ /**
3861
+ * The request, as for `generate()`. It is sent as written, translated by the same
3862
+ * provider adapter: the layer's per-request savings (caching, compression, model
3863
+ * cascade, retrieval) do not run on batches, so `retrieval` and `structuredData`
3864
+ * are rejected and `savings` is ignored.
3865
+ */
3866
+ request: SavingsRequest;
3867
+ }
3868
+ /** Options for `layer.batches.submit()`. */
3869
+ interface BatchSubmitOptions {
3870
+ /**
3871
+ * Anthropic only. Mark the leading system messages of each request as a prompt-cache
3872
+ * prefix, so requests that share them can read it at the cache rate. A cache write
3873
+ * costs more than plain input and, in a batch, hits are best-effort, so this pays
3874
+ * only when many requests share a long system prompt. Defaults to false. OpenAI
3875
+ * caches on its own and ignores it.
3876
+ */
3877
+ cacheSharedPrefix?: boolean;
3878
+ /**
3879
+ * Upper bound on requests per provider batch, below the provider's own limit, to get
3880
+ * partial results sooner. The layer always splits at the provider limits (and, on
3881
+ * OpenAI, by model).
3882
+ */
3883
+ maxRequestsPerBatch?: number;
3884
+ /** Cancels the HTTP calls of this submission. Batches already created stay created. */
3885
+ signal?: AbortSignal;
3886
+ }
3887
+ /**
3888
+ * A submitted batch job. Plain JSON: store it and pass it back to `status()`,
3889
+ * `results()` or `cancel()`, from this process or another one. It holds batch ids,
3890
+ * your request ids, model ids and output schemas; never prompts or answers.
3891
+ */
3892
+ interface BatchJob {
3893
+ kind: "bvsala.batch";
3894
+ version: 1;
3895
+ /** Which batch API holds the job. */
3896
+ provider: "anthropic" | "openai";
3897
+ /** The provider base URL the job was submitted to, without credentials. */
3898
+ endpoint: string;
3899
+ /** One provider batch id per chunk, in submission order. */
3900
+ batchIds: string[];
3901
+ /** ISO 8601. */
3902
+ submittedAt: string;
3903
+ /**
3904
+ * Every submitted request: your id, its model, the index in `batchIds` of the provider
3905
+ * batch that carries it, and its output schema as an index into `schemas`.
3906
+ */
3907
+ requests: {
3908
+ customId: string;
3909
+ model: string;
3910
+ batch: number;
3911
+ schema?: number;
3912
+ }[];
3913
+ /** The distinct output schemas of the job, stored once each. */
3914
+ schemas: JsonSchema[];
3915
+ }
3916
+ /** Request counts of a job, summed over its provider batches. */
3917
+ interface BatchCounts {
3918
+ processing: number;
3919
+ succeeded: number;
3920
+ errored: number;
3921
+ canceled: number;
3922
+ expired: number;
3923
+ }
3924
+ /** Where a job stands, as its provider reports it. */
3925
+ interface BatchJobStatus {
3926
+ /** `"ended"` once every provider batch of the job has finished, expired, failed or been canceled. */
3927
+ state: "in_progress" | "canceling" | "ended";
3928
+ requestCount: number;
3929
+ /**
3930
+ * As the provider reports them. OpenAI reports only completed and failed while a
3931
+ * batch runs; its canceled and expired requests are counted once it has ended.
3932
+ */
3933
+ counts: BatchCounts;
3934
+ /** Each provider batch with the status its provider gave, verbatim. */
3935
+ batches: {
3936
+ id: string;
3937
+ status: string;
3938
+ }[];
3939
+ }
3940
+ /** One request's outcome. */
3941
+ interface BatchItemResult<T = unknown> {
3942
+ customId: string;
3943
+ /** The model you asked for. */
3944
+ model: string;
3945
+ /**
3946
+ * `"succeeded"` ran and is billed by the provider. `"errored"`, `"canceled"` and
3947
+ * `"expired"` returned no answer and no usage (Anthropic documents that it does not
3948
+ * bill them). `"missing"` means the provider returned no line for a request of an
3949
+ * ended batch, which should not happen: nothing is assumed about it.
3950
+ */
3951
+ outcome: "succeeded" | "errored" | "canceled" | "expired" | "missing";
3952
+ text?: string;
3953
+ /** Present when the request had an output contract and the answer met it. */
3954
+ data?: T;
3955
+ /** Whether the answer met the output contract, when the request had one and no tool was called. */
3956
+ contract?: "met" | "missed";
3957
+ toolCalls?: ToolCallRequest[];
3958
+ finishReason?: string;
3959
+ /** The provider's own counter for this request. */
3960
+ usage?: ProviderUsage;
3961
+ /** The processing tier this result was billed at, as read from the provider. */
3962
+ servedTier?: string;
3963
+ /**
3964
+ * What the batch tier was worth on this request: its usage at the standard rate card
3965
+ * minus the same usage at the published batch rate card, component by component.
3966
+ * Present only when credited. It is reported, never metered or billed.
3967
+ */
3968
+ tierSaving?: PriceLeverSaving;
3969
+ /** Why it was or was not credited, or what failed. One sentence, never empty. */
3970
+ reason: string;
3971
+ /** The provider's error, for `"errored"` results. */
3972
+ error?: {
3973
+ type: string;
3974
+ message: string;
3975
+ };
3976
+ }
3977
+ /** The whole job, added up. */
3978
+ interface BatchSummary {
3979
+ requestCount: number;
3980
+ succeeded: number;
3981
+ errored: number;
3982
+ canceled: number;
3983
+ expired: number;
3984
+ missing: number;
3985
+ /** Requests whose provider batch has not ended yet: not in `items`. */
3986
+ pending: number;
3987
+ /** Succeeded requests with a `tierSaving`. */
3988
+ credited: number;
3989
+ /** Sum of every `tierSaving.amount`. Never more than the sum of each request's published difference. */
3990
+ tierSaving: number;
3991
+ /** The credited requests' usage at the standard rate card. */
3992
+ standardCost: number;
3993
+ /** The same usage at the batch rate card. */
3994
+ appliedCost: number;
3995
+ /** `"measured"` only when every credited request is; `"none"` when nothing was credited. */
3996
+ basis: "measured" | "estimated" | "none";
3997
+ /** YYYY-MM-DD: the oldest check date of the rate cards used. */
3998
+ pricesCheckedOn?: string;
3999
+ /** Always `"none"`: a batch saving is reported next to the ledgers, never folded into them. */
4000
+ creditedLedger: "none";
4001
+ /** One sentence. Never empty. */
4002
+ reason: string;
4003
+ }
4004
+ /** What `layer.batches.results()` returns. */
4005
+ interface BatchResults<T = unknown> {
4006
+ /** False while any provider batch of the job is still running; results then cover only the ones that ended. */
4007
+ ended: boolean;
4008
+ items: BatchItemResult<T>[];
4009
+ summary: BatchSummary;
4010
+ }
4011
+ /**
4012
+ * The provider's asynchronous batch API: requests that can wait hours, billed at the
4013
+ * provider's batch rate card. Anthropic's Message Batches and OpenAI's Batch API
4014
+ * (Chat Completions) through the built-in providers; other providers throw.
4015
+ */
4016
+ interface SavingsBatches {
4017
+ /**
4018
+ * Send requests to the provider's batch API. Returns as soon as the provider has
4019
+ * accepted them, with nothing priced: a batch is valued only when its results are
4020
+ * read. Throws before sending anything when a request cannot be batched. If a later
4021
+ * chunk fails after earlier ones were created, the thrown error carries the partial
4022
+ * job as `error.job`, so no created batch is lost.
4023
+ */
4024
+ submit(requests: BatchRequest[], options?: BatchSubmitOptions): Promise<BatchJob>;
4025
+ /** Where the job stands. */
4026
+ status(job: BatchJob, options?: {
4027
+ signal?: AbortSignal;
4028
+ }): Promise<BatchJobStatus>;
4029
+ /**
4030
+ * The results of every provider batch of the job that has ended, one per request,
4031
+ * each valued at the two published rate cards, and the total.
4032
+ */
4033
+ results<T = unknown>(job: BatchJob, options?: {
4034
+ signal?: AbortSignal;
4035
+ }): Promise<BatchResults<T>>;
4036
+ /** Ask the provider to stop the job. Requests it already processed are still billed. */
4037
+ cancel(job: BatchJob, options?: {
4038
+ signal?: AbortSignal;
4039
+ }): Promise<BatchJobStatus>;
4040
+ }
4041
+
3163
4042
  /**
3164
4043
  * Tokenizador EXACTO — la regla del proveedor, no una aproximación.
3165
4044
  *
@@ -3660,8 +4539,10 @@ declare class OpenAICompatibleProvider implements Provider {
3660
4539
  */
3661
4540
  generateStream(req: ProviderRequest, options?: ProviderStreamOptions): AsyncGenerator<ProviderStreamEvent, void, undefined>;
3662
4541
  private enviar;
4542
+ private construirCuerpo;
3663
4543
  /** Translate a 2xx endpoint response into the provider-neutral shape. */
3664
4544
  private leer;
4545
+ private leerJson;
3665
4546
  }
3666
4547
 
3667
4548
  /**
@@ -3713,10 +4594,13 @@ interface AnthropicOptions {
3713
4594
  /**
3714
4595
  * How many `cache_control` breakpoints the adapter may spend.
3715
4596
  *
3716
- * The API caps them at 4 per request. The adapter never needs more than 2
3717
- * (end of system, end of the stable message prefix) and the cap is exposed so
3718
- * a gateway with a stricter limit can lower it. Raising it above 4 is
3719
- * pointless: the extra breakpoints are rejected by the API, not ignored.
4597
+ * The API caps them at 4 per request. The fixed placement never needs more
4598
+ * than 2 (end of system, end of the stable message prefix); the layer's
4599
+ * placer (`cacheBreakpoints: "auto"`) may add a third on the last block. The
4600
+ * cap is exposed so a gateway with a stricter limit can lower it; when it is
4601
+ * short, the system block goes first and the last block second. Raising it
4602
+ * above 4 is pointless: the extra breakpoints are rejected by the API, not
4603
+ * ignored.
3720
4604
  */
3721
4605
  maxCacheBreakpoints?: number;
3722
4606
  /**
@@ -3733,6 +4617,10 @@ interface AnthropicOptions {
3733
4617
  * the whole history at 0.1x — and loses money on one-shot traffic where
3734
4618
  * nothing ever reads it back. It is also rejected by legacy Bedrock
3735
4619
  * deployments. Turn it on when your traffic is multi-turn.
4620
+ *
4621
+ * Ignored on requests whose breakpoints were placed by the layer's placer
4622
+ * (`cacheBreakpoints: "auto"`): the placer already decides whether the tail is
4623
+ * marked, from the reuse it measured on your traffic.
3736
4624
  */
3737
4625
  automaticCache?: boolean;
3738
4626
  /**
@@ -3752,6 +4640,7 @@ declare class AnthropicProvider implements Provider {
3752
4640
  readonly id: string;
3753
4641
  private readonly opts;
3754
4642
  private readonly fetchImpl;
4643
+ private readonly busquedas;
3755
4644
  constructor(opts?: AnthropicOptions);
3756
4645
  /** The id Anthropic's API sees: bare at `api.anthropic.com`, as written elsewhere. */
3757
4646
  private modeloEnElCable;
@@ -3768,8 +4657,13 @@ declare class AnthropicProvider implements Provider {
3768
4657
  generateStream(req: ProviderRequest, options?: ProviderStreamOptions): AsyncGenerator<ProviderStreamEvent, void, undefined>;
3769
4658
  private enviar;
3770
4659
  private construirCuerpo;
4660
+ private reenvioDeBusquedas;
4661
+ private recordarBusquedas;
4662
+ private avisar;
3771
4663
  private marcarCache;
3772
4664
  private leer;
4665
+ private diagnosticoDe;
4666
+ private leerMensaje;
3773
4667
  }
3774
4668
 
3775
4669
  /**
@@ -4210,6 +5104,15 @@ interface SavingsLayer {
4210
5104
  * Await `result` before calling close(), or its usage record may be lost.
4211
5105
  */
4212
5106
  generateStream<T = unknown>(request: SavingsRequest, options?: SavingsStreamOptions): SavingsStream<T>;
5107
+ /**
5108
+ * The provider's asynchronous batch API, for requests that can wait hours: submit
5109
+ * them, check on them, and read their results valued at the provider's batch rate
5110
+ * card. Requests are sent as written (the layer's per-request savings do not run on
5111
+ * batches), nothing is metered, and the batch saving is reported, never billed.
5112
+ * Works with the built-in Anthropic and OpenAI providers; needs the same license as
5113
+ * generate().
5114
+ */
5115
+ readonly batches: SavingsBatches;
4213
5116
  /**
4214
5117
  * Stop the background revocation poller (when the online call-home was enabled
4215
5118
  * via `licenseServerUrl`) and abort any in-flight request, and settle every
@@ -4253,6 +5156,20 @@ declare class StreamAbortedError extends Error {
4253
5156
  readonly requestSent: boolean;
4254
5157
  constructor(requestSent: boolean);
4255
5158
  }
5159
+ /**
5160
+ * Thrown by `layer.batches.submit()` when a chunk fails after earlier chunks were
5161
+ * already created at the provider. Those batches run and are billed: keep `job` and
5162
+ * read it with `results()`, or pass it to `cancel()`.
5163
+ */
5164
+ declare class BatchSubmitError extends Error {
5165
+ /** The batches that were created, as a job you can read or cancel. `batchIds` may be empty. */
5166
+ readonly job: BatchJob;
5167
+ /** Your ids of the requests that were not sent. */
5168
+ readonly notSubmitted: string[];
5169
+ constructor(message: string, job: BatchJob, notSubmitted: string[], options?: {
5170
+ cause?: unknown;
5171
+ });
5172
+ }
4256
5173
 
4257
5174
  /**
4258
5175
  * The parts of a SavingsReport the exporters read. A full SavingsReport
@@ -4623,4 +5540,4 @@ declare function toCsv(rows: readonly Readonly<Record<string, CsvValue>>[], opti
4623
5540
  columns?: readonly string[];
4624
5541
  }): string;
4625
5542
 
4626
- export { type AnswerSource, type AnthropicOptions, AnthropicProvider, BV_SALA_VERSION, type BivelioHandoff, CATALOGO, COMPACT_ROWS_INSTRUCTION, type CacheOutcome, type CacheTier, type CacheWriteReport, type CachedAnswer, type CanonicalRequest, type ChatMessage, type CompactRowsDecodeOptions, type CompactRowsOptions, type CompactVerification, type CompressionEconomics, type CompressionPolicyInput, type CompressionRequest, type CompressionResult, type Compressor, type ConstraintBackend, type ConstraintBackendId, type ConstraintPlan, type ConversationState, type CostBasis, type CostModel, type CsvValue, DEFAULT_COST_MODEL, DEFAULT_MAX_STALENESS_SECONDS, type DeferredExecutionReport, type DeterministicResolver, EMBEDDED_PUBLIC_KEY, type EmbeddingFn, type EvidenceDoc, type ExactCache, ExactTokenizer, type ExecutionClass, ExecutionRouter, type FocusExportOptions, type FocusRow, type FormatChoice, FormatSelector, type FormatSelectorOptions, type HandoffFact, HeuristicEstimator, InMemoryExactCache, InMemorySemanticCache, InMemoryToolSetFreezer, type JsonSchema, type LangfuseGenerationUpdate, LicenseError, type LicenseGate, type LicenseKeyShape, type LicenseKeyset, type LicenseStatus, type LicenseTokenPayload, type LocalClassifier, MODELOS_POR_CODIFICACION, MODELS_BY_ENCODING, type MemoryDelta, type MessageRole, MockProvider, type MockProviderOptions, type ModelCascadeMode, type ModelCascadeReport, type ModelCascadeSettings, type OpenAICompatibleOptions, OpenAICompatibleProvider, type OutputContract, PRECIOS_AL_DIA, type Policy, type PriceLeverSaving, type PriceLeverSettings, type ProtectedSpan, type Provider, type ProviderRequest, type ProviderResponse, type ProviderStreamEvent, type ProviderStreamOptions, type ProviderUsage, QUALITY_STATUSES, type QualityCacheMatch, type QualityCascadeCheck, type QualityLedger, type QualityRepairCheck, type QualitySchemaCheck, type QualityStatus, type QualityTruncationCheck, REFERENCE_MAX_AGE_MS, REFERENCE_MESSAGE_ALLOWANCE, REFERENCE_OUTPUT_SHARE, type ReferenceCredit, type ReferenceRejection, type RequestContext, type Reranker, type ResolvedDeps, type ResolvedGate, type ResolvedLicense, ResolverRegistry, type ResolverResult, type RetrievalOptions, type RetrievalOutcome, type RetrievalPlan, type Retriever, type RevocationListBody, type RevocationListVerification, type RevocationPollEvent, RevocationPoller, type RevocationPollerOptions, RevocationSnapshot, type RouteDecision, type SavingsAdapterLevers, type SavingsAdapterOptions, type SavingsAttributeOptions, type SavingsCallOptions, type SavingsLayer, type SavingsLayerConfig, SavingsLedger, type SavingsRecord, type SavingsReport, type SavingsReportForExport, type SavingsRequest, type SavingsResult, type SavingsSeriesOptions, type SavingsSeriesRow, type SavingsSpanAttributes, type SavingsSpanOptions, type SavingsStream, type SavingsStreamEvent, type SavingsStreamOptions, type SavingsToggles, type SavingsTracingOptions, SchemaRegistry, type SemanticCacheOptions, type SemanticHit, type SemanticRecord, type SemanticVerifier, type SerializationFormat, type Serializer, Singleflight, type SpanAttributeValue, type SpanLike, type SpanStartOptionsLike, StreamAbortedError, type StreamingReport, type TokenEstimator, TokenizerRegistry, type ToolCallRequest, type ToolDescriptor, type ToolRisk, type ToolSelection, ToolSelector, type ToolSelectorOptions, type ToolSetFreezer, TraceCollector, type TraceEvent, type TracerLike, type TracerProviderLike, UMBRAL_LOCAL, UsageEmitter, type UsageEmitterOptions, type ValidationError, type ValidationResult, type VerificationCheck, type VerificationExclusion, type VerificationOutcome, type VerificationReport, type VerificationSettings, type VerificationSide, type VerificationStratum, adaptiveK, arithmeticResolver, buildExactKey, buildInflightKey, canonicalize, compactRowsSerializer, compresionFiel, computeNextDelay, cosine, createCompactRowsSerializer, createDefaultResolvers, createHandoff, createMockProvider, createSavingsLayer, csvSerializer, decodeCompactRows, dedupe, defaultTokenizerRegistry, deriveSeatId, deriveStatus, detectCi, embebedorLocal, emptyState, encodeCompactRows, evalArithmetic, evaluatePolicy, executeRetrievalPlan, gateFromKey, heuristicEstimator, instrumentSavingsLayer, isKeyset, isStrictCompatible, jsonCompactSerializer, keyTokensMatch, licenseKeyShape, liveGateFromKey, mergeDelta, modelFamilyOf, multisetEqual, negationCount, noopCompressor, normalizeLicenseKey, normalizeText, numericTokens, precioDeLista, projectContext, providerNativeBackend, quitarPrefijoDeProveedor, randomId, recordSavingsOnSpan, registerExactTokenizer, registrarTokenizadorExacto, renderState, resolveConstraintBackend, resolveDependencies, resolveLicense, savingsSpanAttributes, sha256, shouldCompress, stableStringify, toCsv, toFocusRows, toLangfuseGeneration, toSavingsSeries, uniformColumns, validateAgainstSchema, vectorLocal, verifyCompactSignature, verifyLicenseToken, verifyRevocationList, withProtectedSpans, words };
5543
+ export { type AnswerJudge, type AnswerSource, type AnthropicOptions, AnthropicProvider, BV_SALA_VERSION, type BatchCounts, type BatchItemResult, type BatchJob, type BatchJobStatus, type BatchRequest, type BatchResults, BatchSubmitError, type BatchSubmitOptions, type BatchSummary, type BivelioHandoff, CATALOGO, COMPACTION_TRANSFORMS, COMPACT_ROWS_INSTRUCTION, type CacheBreakpointStore, type CacheOutcome, type CachePlacement, type CacheTier, type CacheWriteReport, type CachedAnswer, type CalibrationObservation, CalibrationStateError, type CanonicalRequest, type ChatMessage, type CompactRowsDecodeOptions, type CompactRowsOptions, type CompactVerification, type CompactionTransform, type CompressionEconomics, type CompressionPolicyInput, type CompressionRequest, type CompressionResult, type Compressor, type ConstraintBackend, type ConstraintBackendId, type ConstraintPlan, type ConversationState, type CostBasis, type CostModel, type CsvValue, DEFAULT_COST_MODEL, DEFAULT_MAX_STALENESS_SECONDS, DEFAULT_MIN_SAVED_CHARS, type DeferredExecutionReport, type DeterministicResolver, EMBEDDED_PUBLIC_KEY, type EmbeddingFn, type EvidenceDoc, type ExactCache, ExactTokenizer, type ExecutionClass, ExecutionRouter, type FocusExportOptions, type FocusRow, type FormatChoice, FormatSelector, type FormatSelectorOptions, type HandoffFact, HeuristicEstimator, InMemoryCacheBreakpointStore, InMemoryExactCache, InMemorySemanticCache, InMemoryToolSetFreezer, type JsonSchema, type LangfuseGenerationUpdate, LicenseError, type LicenseGate, type LicenseKeyShape, type LicenseKeyset, type LicenseStatus, type LicenseTokenPayload, type LocalClassifier, MODELOS_POR_CODIFICACION, MODELS_BY_ENCODING, type MemoryDelta, type MessageRole, MockProvider, type MockProviderOptions, type ModelCascadeMode, type ModelCascadeReport, type ModelCascadeSettings, type OpenAICompatibleOptions, OpenAICompatibleProvider, type OutputContract, PRECIOS_AL_DIA, type Policy, type PriceLeverSaving, type PriceLeverSettings, type ProtectedSpan, type Provider, type ProviderRequest, type ProviderResponse, type ProviderStreamEvent, type ProviderStreamOptions, type ProviderToolSearch, type ProviderToolSearchUsage, type ProviderUsage, QUALITY_STATUSES, type QualityCacheMatch, type QualityCascadeCheck, type QualityLedger, type QualityRepairCheck, type QualitySchemaCheck, type QualityStatus, type QualityTruncationCheck, REFERENCE_MAX_AGE_MS, REFERENCE_MESSAGE_ALLOWANCE, REFERENCE_OUTPUT_SHARE, type ReferenceCredit, type ReferenceRejection, type RequestContext, type Reranker, type ResolvedDeps, type ResolvedGate, type ResolvedLicense, ResolverRegistry, type ResolverResult, type RetrievalOptions, type RetrievalOutcome, type RetrievalPlan, type Retriever, type RevocationListBody, type RevocationListVerification, type RevocationPollEvent, RevocationPoller, type RevocationPollerOptions, RevocationSnapshot, type RouteDecision, type SavingsAdapterLevers, type SavingsAdapterOptions, type SavingsAttributeOptions, type SavingsBatches, type SavingsCallOptions, type SavingsLayer, type SavingsLayerConfig, SavingsLedger, type SavingsRecord, type SavingsReport, type SavingsReportForExport, type SavingsRequest, type SavingsResult, type SavingsSeriesOptions, type SavingsSeriesRow, type SavingsSpanAttributes, type SavingsSpanOptions, type SavingsStream, type SavingsStreamEvent, type SavingsStreamOptions, type SavingsToggles, type SavingsTracingOptions, SchemaRegistry, type SemanticCacheOptions, type SemanticCalibrationBound, type SemanticCalibrationOptions, type SemanticCalibrationSegmentState, type SemanticCalibrationState, type SemanticCalibrationStatus, type SemanticHit, type SemanticLookup, type SemanticProbe, type SemanticRecord, type SemanticVerifier, type SerializationFormat, type Serializer, Singleflight, type SpanAttributeValue, type SpanLike, type SpanStartOptionsLike, StreamAbortedError, type StreamingReport, type TokenEstimator, TokenizerRegistry, type ToolCallRequest, type ToolDescriptor, type ToolOutputCompaction, type ToolOutputCompactionSettings, type ToolRisk, type ToolSearchMode, type ToolSearchOptions, type ToolSelection, ToolSelector, type ToolSelectorOptions, type ToolSetFreezer, TraceCollector, type TraceEvent, type TracerLike, type TracerProviderLike, UMBRAL_LOCAL, UsageEmitter, type UsageEmitterOptions, type ValidationError, type ValidationResult, type VerificationCheck, type VerificationExclusion, type VerificationOutcome, type VerificationReport, type VerificationSettings, type VerificationSide, type VerificationStratum, adaptiveK, arithmeticResolver, buildExactKey, buildInflightKey, calibrateSemanticFloor, canonicalize, compactRowsSerializer, compactToolOutput, compresionFiel, computeNextDelay, cosine, createCompactRowsSerializer, createDefaultResolvers, createHandoff, createMockProvider, createSavingsLayer, csvSerializer, decodeCompactRows, dedupe, defaultTokenizerRegistry, deriveSeatId, deriveStatus, detectCi, embebedorLocal, emptyState, encodeCompactRows, evalArithmetic, evaluatePolicy, executeRetrievalPlan, gateFromKey, heuristicEstimator, instrumentSavingsLayer, isKeyset, isStrictCompatible, jsonCompactSerializer, keyTokensMatch, licenseKeyShape, liveGateFromKey, mergeDelta, modelFamilyOf, multisetEqual, negationCount, noopCompressor, normalizeLicenseKey, normalizeText, numericTokens, precioDeLista, projectContext, providerNativeBackend, quitarPrefijoDeProveedor, randomId, recordSavingsOnSpan, registerExactTokenizer, registrarTokenizadorExacto, renderState, resolveConstraintBackend, resolveDependencies, resolveLicense, sameAnswer, savingsSpanAttributes, sha256, shouldCompress, stableStringify, toCsv, toFocusRows, toLangfuseGeneration, toSavingsSeries, uniformColumns, validateAgainstSchema, vectorLocal, verifyCompactSignature, verifyLicenseToken, verifyRevocationList, withProtectedSpans, words };