@warlock.js/ai-bedrock 5.30.0 → 5.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/cjs/index.cjs +340 -47
- package/cjs/index.cjs.map +1 -1
- package/esm/config.type.d.mts +30 -5
- package/esm/config.type.d.mts.map +1 -1
- package/esm/embedder.mjs.map +1 -1
- package/esm/index.d.mts +3 -1
- package/esm/index.mjs +2 -1
- package/esm/known-capabilities.mjs +41 -3
- package/esm/known-capabilities.mjs.map +1 -1
- package/esm/known-pricing.d.mts +48 -0
- package/esm/known-pricing.d.mts.map +1 -0
- package/esm/known-pricing.mjs +99 -0
- package/esm/known-pricing.mjs.map +1 -0
- package/esm/model.mjs +121 -45
- package/esm/model.mjs.map +1 -1
- package/esm/provider-options.d.mts +8 -0
- package/esm/provider-options.d.mts.map +1 -0
- package/esm/provider-options.mjs +36 -0
- package/esm/provider-options.mjs.map +1 -0
- package/esm/sdk.d.mts +4 -0
- package/esm/sdk.d.mts.map +1 -1
- package/esm/sdk.mjs +34 -3
- package/esm/sdk.mjs.map +1 -1
- package/esm/utils/reasoning-blocks.mjs.map +1 -1
- package/esm/utils/to-bedrock-messages.mjs +14 -1
- package/esm/utils/to-bedrock-messages.mjs.map +1 -1
- package/esm/utils/to-bedrock-tools.mjs.map +1 -1
- package/esm/utils/wrap-bedrock-error.mjs +5 -1
- package/esm/utils/wrap-bedrock-error.mjs.map +1 -1
- package/llms-full.txt +152 -38
- package/llms.txt +1 -1
- package/package.json +3 -3
- package/skills/setup-bedrock/SKILL.md +152 -38
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,33 @@ All notable changes to `@warlock.js/ai-bedrock` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
|
|
6
6
|
|
|
7
|
+
## 5.31.0 - 2026-10-18
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Usage is marked `inputIncludesCache: false` (Converse `inputTokens` excludes cache reads and writes) and reports 1-hour cache writes from `cacheDetails` in `cacheWrite1hTokens`, so cost bills each cache token once at its own rate.
|
|
12
|
+
- A `toolUse` block with `type: "server_tool_use"` (a system tool run by Bedrock) is no longer returned as a tool call for the agent to dispatch.
|
|
13
|
+
- Converse conversion compiles with strict indexed-access checks while preserving empty and system-only histories.
|
|
14
|
+
- Provider body options merge plain-object fields one level deep, preserving neutral reasoning, generation and structured-output settings unless the caller overrides their keys.
|
|
15
|
+
- Converse conversion combines adjacent same-role turns in content order, preserving parallel tool results and automatic compaction summaries beside assistant tool calls.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- With a `cacheControl` breakpoint, a `cachePoint` is also placed after the system prompt and, for Claude, after the tool definitions — the prefix segments Bedrock caches in `tools` → `system` → `messages` order. `ttl: "1h"` (per call, or the new `promptCacheTtl` model option) is sent only to models that accept it.
|
|
20
|
+
- Built-in Claude prices for `global.` and `us.` inference profiles (`BEDROCK_GLOBAL_PRICING`, `BEDROCK_US_PRICING`, `bedrockPricingFor`), from AWS's Bedrock price list as of 2026-10-17; caller pricing for Claude models gets the 1.25x / 2x cache-write rates filled (`withClaudeCacheWriteRates`). `complete()` and the streaming `done` chunk attach `usage.cost` when priced.
|
|
21
|
+
- Claude 5 families and Nova 2 Lite are recognised as prompt-caching capable.
|
|
22
|
+
- `ServiceUnavailableException` throws `ProviderOverloadedError`; wrapped errors carry `status`, `billed` and `retryable`.
|
|
23
|
+
- `new BedrockSDK({ apiKey, region })` authenticates with an Amazon Bedrock API key: requests carry `Authorization: Bearer <apiKey>` instead of a SigV4 signature and the AWS credential chain is not consulted (the adapter sets the AWS SDK's `token` and `authSchemePreference: ["httpBearerAuth"]`). An empty key, or a key combined with `credentials` or `token`, throws `InvalidRequestError`.
|
|
24
|
+
- An adapter contract test verifies that aborting `signal` rejects `complete()` and `stream()` and cancels the HTTP/2 request.
|
|
25
|
+
- With `streamChunks`, `stream()` yields `reasoning` for `reasoningContent.text` deltas and `tool-args` for client `toolUse.input` deltas.
|
|
26
|
+
- `ai.providerTool({ provider: "bedrock", definition })` appends a Converse `Tool` member such as `{ systemTool: { name: "nova_grounding" } }` verbatim; other providers' tools are skipped with a warning.
|
|
27
|
+
- `providerOptions.bedrock.body` forwards native per-call request options for completions and streams while protecting the model, conversation and tool definitions.
|
|
28
|
+
- Models expose native `countTokens()` preflight using the generation request's Converse conversation, tools and additional model fields.
|
|
29
|
+
|
|
30
|
+
### Upgrading
|
|
31
|
+
|
|
32
|
+
- Calls that ask for `cacheControl` breakpoints now also write the system prompt and (Claude) tool definitions to the cache. Writes cost more than plain input on the first call; repeated prefixes then read at the cache rate.
|
|
33
|
+
|
|
7
34
|
## 5.30.0 - 2026-10-16
|
|
8
35
|
|
|
9
36
|
### Changed
|
|
@@ -85,6 +112,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
85
112
|
### Changed
|
|
86
113
|
|
|
87
114
|
- Lockstep release maintenance and dependency refresh.
|
|
115
|
+
|
|
88
116
|
## 5.20.0 - 2026-09-24
|
|
89
117
|
|
|
90
118
|
### Changed
|
package/cjs/index.cjs
CHANGED
|
@@ -147,9 +147,22 @@ function toBedrockMessages(messages) {
|
|
|
147
147
|
}
|
|
148
148
|
return {
|
|
149
149
|
system: system.length > 0 ? system : void 0,
|
|
150
|
-
messages: mapped
|
|
150
|
+
messages: mergeAdjacentTurns(mapped)
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
|
+
/** Converse requires alternating turns; preserve every content block in order. */
|
|
154
|
+
function mergeAdjacentTurns(messages) {
|
|
155
|
+
const merged = [];
|
|
156
|
+
for (const message of messages) {
|
|
157
|
+
const previous = merged[merged.length - 1];
|
|
158
|
+
if (previous && previous.role === message.role) previous.content.push(...message.content ?? []);
|
|
159
|
+
else merged.push({
|
|
160
|
+
...message,
|
|
161
|
+
content: [...message.content ?? []]
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return merged;
|
|
165
|
+
}
|
|
153
166
|
/**
|
|
154
167
|
* Recover opaque Converse reasoning blocks retained on a prior tool call.
|
|
155
168
|
* Bedrock requires them to be replayed before the assistant's text and
|
|
@@ -293,6 +306,10 @@ function wrapBedrockError(thrown) {
|
|
|
293
306
|
cause: thrown,
|
|
294
307
|
context
|
|
295
308
|
});
|
|
309
|
+
if (shape.name === "ServiceUnavailableException" || shape.httpStatusCode === 503) return new _warlock_js_ai.ProviderOverloadedError(message, {
|
|
310
|
+
cause: thrown,
|
|
311
|
+
context
|
|
312
|
+
});
|
|
296
313
|
if (shape.name === "ValidationException") {
|
|
297
314
|
if (/too long|context window|maximum context|exceeds the maximum/i.test(message)) return new _warlock_js_ai.ContextLengthExceededError(message, {
|
|
298
315
|
cause: thrown,
|
|
@@ -465,6 +482,38 @@ var BedrockEmbedder = class {
|
|
|
465
482
|
}
|
|
466
483
|
};
|
|
467
484
|
|
|
485
|
+
//#endregion
|
|
486
|
+
//#region ../../packages/ai-bedrock/src/provider-options.ts
|
|
487
|
+
const RESERVED_FIELDS = new Set([
|
|
488
|
+
"modelId",
|
|
489
|
+
"messages",
|
|
490
|
+
"system",
|
|
491
|
+
"toolConfig"
|
|
492
|
+
]);
|
|
493
|
+
function mergeProviderBody(built, options) {
|
|
494
|
+
const body = (options?.providerOptions?.bedrock)?.body ?? {};
|
|
495
|
+
const reserved = Object.keys(body).filter((key) => RESERVED_FIELDS.has(key));
|
|
496
|
+
if (reserved.length > 0) throw new _warlock_js_ai.InvalidRequestError(`providerOptions.bedrock.body cannot set ${reserved.join(", ")}: use the call's messages, tools and model options.`, { context: { fields: reserved } });
|
|
497
|
+
const defaults = built;
|
|
498
|
+
const overrides = Object.fromEntries(Object.entries(body).map(([key, value]) => {
|
|
499
|
+
const previous = defaults[key];
|
|
500
|
+
return [key, isPlainObject(previous) && isPlainObject(value) ? {
|
|
501
|
+
...previous,
|
|
502
|
+
...value
|
|
503
|
+
} : value];
|
|
504
|
+
}));
|
|
505
|
+
return {
|
|
506
|
+
...built,
|
|
507
|
+
...overrides
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
/** Merge only plain objects; arrays, null and provider scalar values replace the field. */
|
|
511
|
+
function isPlainObject(value) {
|
|
512
|
+
if (value === null || typeof value !== "object") return false;
|
|
513
|
+
const prototype = Object.getPrototypeOf(value);
|
|
514
|
+
return prototype === Object.prototype || prototype === null;
|
|
515
|
+
}
|
|
516
|
+
|
|
468
517
|
//#endregion
|
|
469
518
|
//#region ../../packages/ai-bedrock/src/known-capabilities.ts
|
|
470
519
|
/**
|
|
@@ -498,8 +547,10 @@ const REASONING_CAPABLE_SUBSTRINGS = [
|
|
|
498
547
|
];
|
|
499
548
|
/**
|
|
500
549
|
* Families that honor Converse `cachePoint` prompt-cache breakpoints.
|
|
501
|
-
* Anthropic Claude 3.5+ / 3.7 / 4 and the Amazon Nova line support
|
|
502
|
-
* cache points; text-only legacy families do not.
|
|
550
|
+
* Anthropic Claude 3.5+ / 3.7 / 4 / 5 and the Amazon Nova line support
|
|
551
|
+
* cache points; text-only legacy families do not. Per the "Supported
|
|
552
|
+
* models" table of https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html
|
|
553
|
+
* (checked 2026-10-17).
|
|
503
554
|
*/
|
|
504
555
|
const PROMPT_CACHING_CAPABLE_SUBSTRINGS = [
|
|
505
556
|
"claude-3-5",
|
|
@@ -507,6 +558,12 @@ const PROMPT_CACHING_CAPABLE_SUBSTRINGS = [
|
|
|
507
558
|
"claude-sonnet-4",
|
|
508
559
|
"claude-opus-4",
|
|
509
560
|
"claude-haiku-4",
|
|
561
|
+
"claude-sonnet-5",
|
|
562
|
+
"claude-opus-5",
|
|
563
|
+
"claude-haiku-5",
|
|
564
|
+
"claude-fable-5",
|
|
565
|
+
"claude-mythos-5",
|
|
566
|
+
"nova-2-lite",
|
|
510
567
|
"nova-lite",
|
|
511
568
|
"nova-pro",
|
|
512
569
|
"nova-premier",
|
|
@@ -570,6 +627,133 @@ function inferPromptCachingCapability(modelId) {
|
|
|
570
627
|
function inferPdfCapability(modelId) {
|
|
571
628
|
return matchesAny(modelId, PDF_CAPABLE_SUBSTRINGS);
|
|
572
629
|
}
|
|
630
|
+
/**
|
|
631
|
+
* Claude on Bedrock accepts a `cachePoint` in `system`, `messages` and
|
|
632
|
+
* `tools`; Amazon Nova accepts one only in `system` and `messages`
|
|
633
|
+
* (https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html,
|
|
634
|
+
* "Fields that accept prompt cache checkpoints", checked 2026-10-17).
|
|
635
|
+
* Unknown families get no tool cache point.
|
|
636
|
+
*
|
|
637
|
+
* @example
|
|
638
|
+
* supportsToolCachePoint("global.anthropic.claude-sonnet-4-6"); // true
|
|
639
|
+
* supportsToolCachePoint("amazon.nova-pro-v1:0"); // false
|
|
640
|
+
*/
|
|
641
|
+
function supportsToolCachePoint(modelId) {
|
|
642
|
+
return modelId.toLowerCase().includes("anthropic.claude");
|
|
643
|
+
}
|
|
644
|
+
/** Claude families whose cache points accept the 1-hour TTL. */
|
|
645
|
+
const ONE_HOUR_TTL_PATTERN = /anthropic\.claude-(?:(?:opus|sonnet|haiku|fable|mythos)-5|opus-4-[5-9]|sonnet-4-[5-9]|haiku-4-5)/;
|
|
646
|
+
/**
|
|
647
|
+
* Claude models whose Converse `cachePoint` accepts `ttl: "1h"`: Claude
|
|
648
|
+
* Haiku 4.5, the 4.5+ Sonnet / Opus line and every Claude 5 family. Any
|
|
649
|
+
* other model gets no `ttl` field (the 5-minute default), since AWS
|
|
650
|
+
* warns that sending it to a 5-minute-only model "can result in a
|
|
651
|
+
* ValidationException" (same page, checked 2026-10-17).
|
|
652
|
+
*
|
|
653
|
+
* @example
|
|
654
|
+
* supportsOneHourCacheTtl("anthropic.claude-haiku-4-5-20251001-v1:0"); // true
|
|
655
|
+
* supportsOneHourCacheTtl("anthropic.claude-3-7-sonnet-20250219-v1:0"); // false
|
|
656
|
+
*/
|
|
657
|
+
function supportsOneHourCacheTtl(modelId) {
|
|
658
|
+
return ONE_HOUR_TTL_PATTERN.test(modelId.toLowerCase());
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
//#endregion
|
|
662
|
+
//#region ../../packages/ai-bedrock/src/known-pricing.ts
|
|
663
|
+
/**
|
|
664
|
+
* Bedrock on-demand list prices for Anthropic Claude, USD per 1,000,000
|
|
665
|
+
* tokens, keyed by Bedrock model id (without an inference-profile
|
|
666
|
+
* prefix). `cacheWrite` is the 5-minute write rate, `cacheWrite1h` the
|
|
667
|
+
* 1-hour write rate, `cachedInput` the cache-read rate.
|
|
668
|
+
*
|
|
669
|
+
* Source: https://aws.amazon.com/bedrock/pricing/ — the page's own price
|
|
670
|
+
* feed for "US East (N. Virginia)", fetched 2026-10-17 (Sonnet 5.5 cache
|
|
671
|
+
* reads already at the rate effective 2026-10-07). Model ids from
|
|
672
|
+
* https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html.
|
|
673
|
+
*
|
|
674
|
+
* Two tables, because AWS prices them differently:
|
|
675
|
+
* - {@link BEDROCK_GLOBAL_PRICING} — `global.` cross-Region inference
|
|
676
|
+
* profiles.
|
|
677
|
+
* - {@link BEDROCK_US_PRICING} — `us.` geographic profiles (the
|
|
678
|
+
* "Geo / In-region" rates, 10% above global for these models).
|
|
679
|
+
*
|
|
680
|
+
* In-Region ids without a profile prefix, other geographies (`eu.`,
|
|
681
|
+
* `apac.`, …), Claude Haiku 5.5 and non-Claude models stay unpriced —
|
|
682
|
+
* their rates vary by Region or were not verified — so pass `pricing`
|
|
683
|
+
* yourself for them.
|
|
684
|
+
*/
|
|
685
|
+
const BEDROCK_GLOBAL_PRICING = {
|
|
686
|
+
"anthropic.claude-sonnet-5-5": claude(2, 10, .1),
|
|
687
|
+
"anthropic.claude-opus-5-5": claude(4, 20, .2),
|
|
688
|
+
"anthropic.claude-fable-5-1": claude(10, 50, .25),
|
|
689
|
+
"anthropic.claude-opus-5": claude(5, 25, .5),
|
|
690
|
+
"anthropic.claude-sonnet-5": claude(2, 10, .2),
|
|
691
|
+
"anthropic.claude-fable-5": claude(10, 50, 1),
|
|
692
|
+
"anthropic.claude-opus-4-8": claude(5, 25, .5),
|
|
693
|
+
"anthropic.claude-opus-4-7": claude(5, 25, .5),
|
|
694
|
+
"anthropic.claude-opus-4-6-v1": claude(5, 25, .5),
|
|
695
|
+
"anthropic.claude-sonnet-4-6": claude(3, 15, .3),
|
|
696
|
+
"anthropic.claude-opus-4-5-20251101-v1:0": claude(5, 25, .5),
|
|
697
|
+
"anthropic.claude-sonnet-4-5-20250929-v1:0": claude(3, 15, .3),
|
|
698
|
+
"anthropic.claude-haiku-4-5-20251001-v1:0": claude(1, 5, .1)
|
|
699
|
+
};
|
|
700
|
+
/** `us.` geographic-profile prices — see {@link BEDROCK_GLOBAL_PRICING}. */
|
|
701
|
+
const BEDROCK_US_PRICING = {
|
|
702
|
+
"anthropic.claude-sonnet-5-5": claude(2.2, 11, .11),
|
|
703
|
+
"anthropic.claude-opus-5-5": claude(4.4, 22, .22),
|
|
704
|
+
"anthropic.claude-fable-5-1": claude(11, 55, .275),
|
|
705
|
+
"anthropic.claude-opus-5": claude(5.5, 27.5, .55),
|
|
706
|
+
"anthropic.claude-sonnet-5": claude(2.2, 11, .22),
|
|
707
|
+
"anthropic.claude-fable-5": claude(11, 55, 1.1),
|
|
708
|
+
"anthropic.claude-opus-4-8": claude(5.5, 27.5, .55),
|
|
709
|
+
"anthropic.claude-opus-4-7": claude(5.5, 27.5, .55),
|
|
710
|
+
"anthropic.claude-opus-4-6-v1": claude(5.5, 27.5, .55),
|
|
711
|
+
"anthropic.claude-sonnet-4-6": claude(3.3, 16.5, .33),
|
|
712
|
+
"anthropic.claude-opus-4-5-20251101-v1:0": claude(5.5, 27.5, .55),
|
|
713
|
+
"anthropic.claude-sonnet-4-5-20250929-v1:0": claude(3.3, 16.5, .33),
|
|
714
|
+
"anthropic.claude-haiku-4-5-20251001-v1:0": claude(1.1, 5.5, .11)
|
|
715
|
+
};
|
|
716
|
+
/**
|
|
717
|
+
* Look up the built-in price for a Bedrock model id or inference-profile
|
|
718
|
+
* id: `global.<model>` reads {@link BEDROCK_GLOBAL_PRICING}, `us.<model>`
|
|
719
|
+
* reads {@link BEDROCK_US_PRICING}; anything else is `undefined`.
|
|
720
|
+
*
|
|
721
|
+
* @example
|
|
722
|
+
* bedrockPricingFor("global.anthropic.claude-sonnet-4-6"); // { input: 3, output: 15, … }
|
|
723
|
+
* bedrockPricingFor("anthropic.claude-sonnet-4-6"); // undefined — in-Region rates vary
|
|
724
|
+
*/
|
|
725
|
+
function bedrockPricingFor(modelId) {
|
|
726
|
+
if (modelId.startsWith("global.")) return BEDROCK_GLOBAL_PRICING[modelId.slice(7)];
|
|
727
|
+
if (modelId.startsWith("us.")) return BEDROCK_US_PRICING[modelId.slice(3)];
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Fill the Claude cache-write rates (5-minute 1.25x, 1-hour 2x the input
|
|
731
|
+
* rate — the ratio of every Claude row in the tables above) into a
|
|
732
|
+
* caller-supplied entry for a Claude model that omits them. Non-Claude
|
|
733
|
+
* models are returned unchanged.
|
|
734
|
+
*/
|
|
735
|
+
function withClaudeCacheWriteRates(modelId, pricing) {
|
|
736
|
+
if (!pricing || !modelId.toLowerCase().includes("anthropic.claude")) return pricing;
|
|
737
|
+
if (pricing.cacheWrite !== void 0 && pricing.cacheWrite1h !== void 0) return pricing;
|
|
738
|
+
return {
|
|
739
|
+
...pricing,
|
|
740
|
+
cacheWrite: pricing.cacheWrite ?? pricing.cachedOutput ?? round(pricing.input * 1.25),
|
|
741
|
+
cacheWrite1h: pricing.cacheWrite1h ?? round(pricing.input * 2)
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
function claude(input, output, cachedInput) {
|
|
745
|
+
return {
|
|
746
|
+
input,
|
|
747
|
+
output,
|
|
748
|
+
cachedInput,
|
|
749
|
+
cacheWrite: round(input * 1.25),
|
|
750
|
+
cacheWrite1h: round(input * 2)
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
/** Trim binary floating-point noise (2.2 * 1.25 = 2.7500000000000004). */
|
|
754
|
+
function round(value) {
|
|
755
|
+
return Math.round(value * 1e9) / 1e9;
|
|
756
|
+
}
|
|
573
757
|
|
|
574
758
|
//#endregion
|
|
575
759
|
//#region ../../packages/ai-bedrock/src/known-vision-models.ts
|
|
@@ -622,6 +806,10 @@ function inferVisionCapability(modelId) {
|
|
|
622
806
|
//#endregion
|
|
623
807
|
//#region ../../packages/ai-bedrock/src/model.ts
|
|
624
808
|
const LOG_MODULE = "ai.bedrock";
|
|
809
|
+
/** `provider` value of `ai.providerTool()` entries this adapter sends verbatim. */
|
|
810
|
+
const BEDROCK_WIRE_FORMAT = "bedrock";
|
|
811
|
+
/** Converse `ToolUseBlock.type` of a tool use run by Bedrock itself (SDK `ToolUseType`). */
|
|
812
|
+
const SERVER_TOOL_USE = "server_tool_use";
|
|
625
813
|
/** Default output allowance added to a thinking budget when maxTokens is unset. */
|
|
626
814
|
const DEFAULT_MAX_TOKENS = 4096;
|
|
627
815
|
const MIN_THINKING_BUDGET = 1024;
|
|
@@ -682,7 +870,7 @@ var BedrockModel = class {
|
|
|
682
870
|
this.config = config;
|
|
683
871
|
this.name = config.name;
|
|
684
872
|
this.provider = provider;
|
|
685
|
-
this.pricing = config.pricing;
|
|
873
|
+
this.pricing = withClaudeCacheWriteRates(config.name, config.pricing ?? bedrockPricingFor(config.name));
|
|
686
874
|
this.capabilities = {
|
|
687
875
|
structuredOutput: config.structuredOutput ?? true,
|
|
688
876
|
vision: config.vision ?? inferVisionCapability(config.name),
|
|
@@ -713,7 +901,7 @@ var BedrockModel = class {
|
|
|
713
901
|
}
|
|
714
902
|
const blocks = response.output?.message?.content ?? [];
|
|
715
903
|
const finishReason = mapStopReason(response.stopReason);
|
|
716
|
-
const usage = this.extractUsage(response.usage);
|
|
904
|
+
const usage = (0, _warlock_js_ai.attachCost)(this.extractUsage(response.usage), this.pricing);
|
|
717
905
|
const toolCalls = this.extractToolCalls(blocks);
|
|
718
906
|
this.logger.debug(LOG_MODULE, "response", "Converse call succeeded", {
|
|
719
907
|
finishReason,
|
|
@@ -726,11 +914,36 @@ var BedrockModel = class {
|
|
|
726
914
|
toolCalls
|
|
727
915
|
};
|
|
728
916
|
}
|
|
917
|
+
/** Count native input tokens from the same Converse conversation and tool configuration. */
|
|
918
|
+
async countTokens(messages, options) {
|
|
919
|
+
const request = this.buildRequest(messages, options);
|
|
920
|
+
try {
|
|
921
|
+
const response = await this.client.send(new _aws_sdk_client_bedrock_runtime.CountTokensCommand({
|
|
922
|
+
modelId: request.modelId,
|
|
923
|
+
input: { converse: {
|
|
924
|
+
messages: request.messages,
|
|
925
|
+
system: request.system,
|
|
926
|
+
toolConfig: request.toolConfig,
|
|
927
|
+
additionalModelRequestFields: request.additionalModelRequestFields
|
|
928
|
+
} }
|
|
929
|
+
}), options?.signal ? { abortSignal: options.signal } : void 0);
|
|
930
|
+
if (response.inputTokens === void 0) throw new _warlock_js_ai.InvalidRequestError("Bedrock countTokens returned no inputTokens.");
|
|
931
|
+
return response.inputTokens;
|
|
932
|
+
} catch (thrown) {
|
|
933
|
+
throw this.logAndWrap(thrown);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
729
936
|
/**
|
|
730
937
|
* Incremental streaming completion via ConverseStream. Yields neutral
|
|
731
938
|
* `ModelStreamChunk`s — `delta` for text, `tool-call` once a
|
|
732
939
|
* `toolUse` block's accumulated input JSON is complete, and a
|
|
733
940
|
* terminal `done` with the final finish reason + usage totals.
|
|
941
|
+
*
|
|
942
|
+
* With `options.streamChunks` it also yields `reasoning` for each
|
|
943
|
+
* `reasoningContent.text` delta and `tool-args` for each client
|
|
944
|
+
* `toolUse.input` delta. Usage arrives once, in the final `metadata`
|
|
945
|
+
* event, so no `usage` chunk is emitted. A system tool's
|
|
946
|
+
* `server_tool_use` block never becomes a `tool-call`.
|
|
734
947
|
*/
|
|
735
948
|
async *stream(messages, options) {
|
|
736
949
|
this.logger.debug(LOG_MODULE, "request", "Starting ConverseStream call", {
|
|
@@ -746,11 +959,12 @@ var BedrockModel = class {
|
|
|
746
959
|
throw this.logAndWrap(thrown);
|
|
747
960
|
}
|
|
748
961
|
let rawStopReason;
|
|
749
|
-
|
|
962
|
+
let usage = {
|
|
750
963
|
input: 0,
|
|
751
964
|
output: 0,
|
|
752
965
|
total: 0
|
|
753
966
|
};
|
|
967
|
+
const want = (0, _warlock_js_ai.resolveStreamChunks)(options);
|
|
754
968
|
const toolBlocks = /* @__PURE__ */ new Map();
|
|
755
969
|
const reasoningBlocks = /* @__PURE__ */ new Map();
|
|
756
970
|
const completedReasoningBlocks = [];
|
|
@@ -758,6 +972,7 @@ var BedrockModel = class {
|
|
|
758
972
|
for await (const event of response.stream ?? []) {
|
|
759
973
|
if (event.contentBlockStart?.start?.toolUse) {
|
|
760
974
|
const start = event.contentBlockStart.start.toolUse;
|
|
975
|
+
if (start.type === SERVER_TOOL_USE) continue;
|
|
761
976
|
toolBlocks.set(event.contentBlockStart.contentBlockIndex ?? 0, {
|
|
762
977
|
id: start.toolUseId ?? "",
|
|
763
978
|
name: start.name ?? "",
|
|
@@ -773,11 +988,25 @@ var BedrockModel = class {
|
|
|
773
988
|
};
|
|
774
989
|
else if (delta.toolUse) {
|
|
775
990
|
const accumulator = toolBlocks.get(event.contentBlockDelta.contentBlockIndex ?? 0);
|
|
776
|
-
if (accumulator)
|
|
991
|
+
if (accumulator) {
|
|
992
|
+
accumulator.json += delta.toolUse.input ?? "";
|
|
993
|
+
if (want.toolArgs && delta.toolUse.input) yield {
|
|
994
|
+
type: "tool-args",
|
|
995
|
+
callId: accumulator.id,
|
|
996
|
+
name: accumulator.name,
|
|
997
|
+
delta: delta.toolUse.input
|
|
998
|
+
};
|
|
999
|
+
}
|
|
777
1000
|
} else if (delta.reasoningContent) {
|
|
778
1001
|
const index = event.contentBlockDelta.contentBlockIndex ?? 0;
|
|
779
1002
|
const accumulator = reasoningBlocks.get(index) ?? {};
|
|
780
|
-
if (delta.reasoningContent.text !== void 0)
|
|
1003
|
+
if (delta.reasoningContent.text !== void 0) {
|
|
1004
|
+
accumulator.text = (accumulator.text ?? "") + delta.reasoningContent.text;
|
|
1005
|
+
if (want.reasoning && delta.reasoningContent.text !== "") yield {
|
|
1006
|
+
type: "reasoning",
|
|
1007
|
+
delta: delta.reasoningContent.text
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
781
1010
|
if (delta.reasoningContent.signature !== void 0) accumulator.signature = delta.reasoningContent.signature;
|
|
782
1011
|
if (delta.reasoningContent.redactedContent !== void 0) accumulator.redactedContent = delta.reasoningContent.redactedContent;
|
|
783
1012
|
reasoningBlocks.set(index, accumulator);
|
|
@@ -806,19 +1035,13 @@ var BedrockModel = class {
|
|
|
806
1035
|
continue;
|
|
807
1036
|
}
|
|
808
1037
|
if (event.messageStop) rawStopReason = event.messageStop.stopReason;
|
|
809
|
-
if (event.metadata?.usage)
|
|
810
|
-
const raw = event.metadata.usage;
|
|
811
|
-
usage.input = raw.inputTokens ?? 0;
|
|
812
|
-
usage.output = raw.outputTokens ?? 0;
|
|
813
|
-
usage.total = raw.totalTokens ?? usage.input + usage.output;
|
|
814
|
-
if (raw.cacheReadInputTokens && raw.cacheReadInputTokens > 0) usage.cachedTokens = raw.cacheReadInputTokens;
|
|
815
|
-
if (raw.cacheWriteInputTokens && raw.cacheWriteInputTokens > 0) usage.cacheWriteTokens = raw.cacheWriteInputTokens;
|
|
816
|
-
}
|
|
1038
|
+
if (event.metadata?.usage) usage = this.extractUsage(event.metadata.usage);
|
|
817
1039
|
}
|
|
818
1040
|
} catch (thrown) {
|
|
819
1041
|
throw this.logAndWrap(thrown);
|
|
820
1042
|
}
|
|
821
1043
|
const finishReason = mapStopReason(rawStopReason);
|
|
1044
|
+
(0, _warlock_js_ai.attachCost)(usage, this.pricing);
|
|
822
1045
|
this.logger.debug(LOG_MODULE, "response", "ConverseStream call succeeded", {
|
|
823
1046
|
finishReason,
|
|
824
1047
|
usage
|
|
@@ -841,44 +1064,58 @@ var BedrockModel = class {
|
|
|
841
1064
|
const configuredMaxTokens = options?.maxTokens ?? this.config.maxTokens;
|
|
842
1065
|
const maxTokens = this.resolveMaxTokens(configuredMaxTokens, thinkingBudget);
|
|
843
1066
|
const temperature = options?.temperature ?? this.config.temperature;
|
|
844
|
-
const
|
|
845
|
-
|
|
1067
|
+
const cachePoint = this.resolveCachePoint(options?.cacheControl);
|
|
1068
|
+
const cachedMessages = this.applyCacheBreakpoints(bedrockMessages, cachePoint);
|
|
1069
|
+
const cachedSystem = system && cachePoint ? [...system, { cachePoint }] : system;
|
|
1070
|
+
return mergeProviderBody({
|
|
846
1071
|
modelId: this.name,
|
|
847
1072
|
messages: cachedMessages,
|
|
848
|
-
...
|
|
1073
|
+
...cachedSystem ? { system: cachedSystem } : {},
|
|
849
1074
|
inferenceConfig: {
|
|
850
1075
|
...maxTokens !== void 0 ? { maxTokens } : {},
|
|
851
1076
|
...temperature !== void 0 ? { temperature } : {}
|
|
852
1077
|
},
|
|
853
|
-
...this.buildToolConfig(options
|
|
1078
|
+
...this.buildToolConfig(options, cachePoint),
|
|
854
1079
|
...this.buildOutputConfig(options?.responseSchema),
|
|
855
1080
|
...this.buildReasoningConfig(thinkingBudget)
|
|
856
|
-
};
|
|
1081
|
+
}, options);
|
|
857
1082
|
}
|
|
858
1083
|
/**
|
|
859
|
-
*
|
|
860
|
-
*
|
|
861
|
-
* `promptCaching`-capable
|
|
862
|
-
*
|
|
863
|
-
*
|
|
864
|
-
* (surfaced as `Usage.cachedTokens`). No-ops gracefully when caching
|
|
865
|
-
* is unsupported, no breakpoint was requested, or there are no
|
|
866
|
-
* messages to mark — Bedrock then prices the call normally.
|
|
1084
|
+
* Resolve the Converse `cachePoint` to place for this call, or
|
|
1085
|
+
* `undefined` when no cache point applies (the model is not
|
|
1086
|
+
* `promptCaching`-capable or no `cacheControl` breakpoint was asked
|
|
1087
|
+
* for). Bedrock only honors `type: "default"`; the neutral
|
|
1088
|
+
* `breakpoints` count is a presence hint.
|
|
867
1089
|
*
|
|
868
|
-
*
|
|
869
|
-
* `
|
|
870
|
-
*
|
|
871
|
-
*
|
|
1090
|
+
* The lifetime is the per-call `cacheControl.ttl`, else the model's
|
|
1091
|
+
* `promptCacheTtl`. `ttl: "1h"` is sent only to models that accept it
|
|
1092
|
+
* ({@link supportsOneHourCacheTtl}); any other model gets the 5-minute
|
|
1093
|
+
* default, because AWS rejects the field there.
|
|
872
1094
|
*/
|
|
873
|
-
|
|
1095
|
+
resolveCachePoint(cacheControl) {
|
|
874
1096
|
const breakpoints = cacheControl?.breakpoints ?? 0;
|
|
875
|
-
if (!this.capabilities.promptCaching || breakpoints <= 0
|
|
1097
|
+
if (!this.capabilities.promptCaching || breakpoints <= 0) return;
|
|
1098
|
+
return (cacheControl?.ttl ?? this.config.promptCacheTtl) === "1h" && supportsOneHourCacheTtl(this.name) ? {
|
|
1099
|
+
type: "default",
|
|
1100
|
+
ttl: "1h"
|
|
1101
|
+
} : { type: "default" };
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Append the call's `cachePoint` to the LAST message. Together with the
|
|
1105
|
+
* `system` and `tools` cache points (see `buildRequest` /
|
|
1106
|
+
* `buildToolConfig`) this caches every stable segment of the prefix —
|
|
1107
|
+
* Bedrock processes them in `tools` → `system` → `messages` order, at
|
|
1108
|
+
* most three of the four checkpoints it allows — so later calls reusing
|
|
1109
|
+
* the prefix bill it at the cache-read rate (`Usage.cachedTokens`).
|
|
1110
|
+
*/
|
|
1111
|
+
applyCacheBreakpoints(messages, cachePoint) {
|
|
1112
|
+
if (!cachePoint || !messages || messages.length === 0) return messages;
|
|
876
1113
|
const last = messages.length - 1;
|
|
877
1114
|
const lastMessage = messages[last];
|
|
878
1115
|
if (!lastMessage) return messages;
|
|
879
1116
|
return [...messages.slice(0, last), {
|
|
880
1117
|
...lastMessage,
|
|
881
|
-
content: [...lastMessage.content ?? [], { cachePoint
|
|
1118
|
+
content: [...lastMessage.content ?? [], { cachePoint }]
|
|
882
1119
|
}];
|
|
883
1120
|
}
|
|
884
1121
|
/**
|
|
@@ -914,11 +1151,26 @@ var BedrockModel = class {
|
|
|
914
1151
|
}
|
|
915
1152
|
/**
|
|
916
1153
|
* Spread-friendly tool fragment. Returns an empty object when no
|
|
917
|
-
* tools were supplied (Bedrock rejects an empty `tools` array).
|
|
1154
|
+
* tools were supplied (Bedrock rejects an empty `tools` array). With a
|
|
1155
|
+
* cache point, a trailing `{ cachePoint }` caches every tool definition
|
|
1156
|
+
* — Claude only, since Nova accepts no cache point in `tools`.
|
|
918
1157
|
*/
|
|
919
|
-
buildToolConfig(
|
|
1158
|
+
buildToolConfig(options, cachePoint) {
|
|
1159
|
+
const { tools, definitions } = (0, _warlock_js_ai.selectProviderTools)(options, {
|
|
1160
|
+
accepts: [BEDROCK_WIRE_FORMAT, this.provider],
|
|
1161
|
+
module: LOG_MODULE,
|
|
1162
|
+
model: this.name,
|
|
1163
|
+
logger: this.logger
|
|
1164
|
+
});
|
|
920
1165
|
const toolConfig = toBedrockToolConfig(tools);
|
|
921
|
-
|
|
1166
|
+
const providerDefined = definitions;
|
|
1167
|
+
const allTools = [...toolConfig?.tools ?? [], ...providerDefined];
|
|
1168
|
+
if (allTools.length === 0) return toolConfig ? { toolConfig } : {};
|
|
1169
|
+
const withCachePoint = cachePoint && supportsToolCachePoint(this.name) ? [...allTools, { cachePoint }] : allTools;
|
|
1170
|
+
return { toolConfig: {
|
|
1171
|
+
...toolConfig,
|
|
1172
|
+
tools: withCachePoint
|
|
1173
|
+
} };
|
|
922
1174
|
}
|
|
923
1175
|
/**
|
|
924
1176
|
* Translate the neutral `responseSchema` into Converse's native
|
|
@@ -956,7 +1208,7 @@ var BedrockModel = class {
|
|
|
956
1208
|
extractToolCalls(blocks) {
|
|
957
1209
|
const toolCalls = [];
|
|
958
1210
|
const reasoningBlocks = blocks.map(toReasoningMetadata).filter((block) => block !== void 0);
|
|
959
|
-
for (const block of blocks) if ("toolUse" in block && block.toolUse) toolCalls.push({
|
|
1211
|
+
for (const block of blocks) if ("toolUse" in block && block.toolUse && block.toolUse.type !== SERVER_TOOL_USE) toolCalls.push({
|
|
960
1212
|
id: block.toolUse.toolUseId ?? "",
|
|
961
1213
|
name: block.toolUse.name ?? "",
|
|
962
1214
|
input: block.toolUse.input ?? {},
|
|
@@ -976,10 +1228,13 @@ var BedrockModel = class {
|
|
|
976
1228
|
* Normalize Converse's `TokenUsage` into the neutral `Usage` shape.
|
|
977
1229
|
* Bedrock supplies a pre-summed `totalTokens`; cache-read and
|
|
978
1230
|
* cache-write tokens are surfaced as `cachedTokens` /
|
|
979
|
-
* `cacheWriteTokens` only when non-zero
|
|
980
|
-
*
|
|
981
|
-
*
|
|
982
|
-
*
|
|
1231
|
+
* `cacheWriteTokens` only when non-zero, and the 1-hour share of the
|
|
1232
|
+
* writes (`cacheDetails` entries with `ttl: "1h"`) as
|
|
1233
|
+
* `cacheWrite1hTokens`. With caching, `inputTokens` "represents only
|
|
1234
|
+
* the non-cached input tokens" (AWS prompt-caching guide), so the usage
|
|
1235
|
+
* is marked `inputIncludesCache: false`. Bedrock's Converse `TokenUsage`
|
|
1236
|
+
* carries no reasoning-token channel, so `Usage.reasoningTokens` is
|
|
1237
|
+
* intentionally left unset here.
|
|
983
1238
|
*/
|
|
984
1239
|
extractUsage(raw) {
|
|
985
1240
|
if (!raw) return {
|
|
@@ -991,12 +1246,15 @@ var BedrockModel = class {
|
|
|
991
1246
|
const output = raw.outputTokens ?? 0;
|
|
992
1247
|
const cached = raw.cacheReadInputTokens;
|
|
993
1248
|
const cacheWrite = raw.cacheWriteInputTokens;
|
|
1249
|
+
const cacheWrite1h = (raw.cacheDetails ?? []).filter((detail) => detail.ttl === "1h").reduce((sum, detail) => sum + (detail.inputTokens ?? 0), 0);
|
|
994
1250
|
return {
|
|
995
1251
|
input,
|
|
996
1252
|
output,
|
|
997
1253
|
total: raw.totalTokens ?? input + output,
|
|
1254
|
+
inputIncludesCache: false,
|
|
998
1255
|
...cached && cached > 0 ? { cachedTokens: cached } : {},
|
|
999
|
-
...cacheWrite && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {}
|
|
1256
|
+
...cacheWrite && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {},
|
|
1257
|
+
...cacheWrite && cacheWrite > 0 && cacheWrite1h > 0 ? { cacheWrite1hTokens: Math.min(cacheWrite1h, cacheWrite) } : {}
|
|
1000
1258
|
};
|
|
1001
1259
|
}
|
|
1002
1260
|
/**
|
|
@@ -1041,11 +1299,15 @@ var BedrockModel = class {
|
|
|
1041
1299
|
* const bedrock = new BedrockSDK({ region: "us-east-1" });
|
|
1042
1300
|
* const model = bedrock.model({ name: "anthropic.claude-sonnet-4-5-20250929-v1:0" });
|
|
1043
1301
|
* const embedder = bedrock.embedder({ name: "amazon.titan-embed-text-v2:0" });
|
|
1302
|
+
*
|
|
1303
|
+
* @example
|
|
1304
|
+
* // Amazon Bedrock API key (bearer auth) instead of the AWS credential chain
|
|
1305
|
+
* const bedrock = new BedrockSDK({ apiKey: process.env.BEDROCK_API_KEY!, region: "us-east-1" });
|
|
1044
1306
|
*/
|
|
1045
1307
|
var BedrockSDK = class {
|
|
1046
1308
|
constructor(config) {
|
|
1047
|
-
const { provider, pricing, ...clientConfig } = config;
|
|
1048
|
-
this.client = new _aws_sdk_client_bedrock_runtime.BedrockRuntimeClient(clientConfig);
|
|
1309
|
+
const { provider, pricing, apiKey, ...clientConfig } = config;
|
|
1310
|
+
this.client = new _aws_sdk_client_bedrock_runtime.BedrockRuntimeClient(apiKey === void 0 ? clientConfig : withApiKey(clientConfig, apiKey));
|
|
1049
1311
|
this.provider = provider ?? "bedrock";
|
|
1050
1312
|
this.pricing = pricing;
|
|
1051
1313
|
}
|
|
@@ -1088,7 +1350,38 @@ var BedrockSDK = class {
|
|
|
1088
1350
|
return new BedrockEmbedder(this.client, config, this.provider);
|
|
1089
1351
|
}
|
|
1090
1352
|
};
|
|
1353
|
+
/**
|
|
1354
|
+
* Configure the runtime client for an Amazon Bedrock API key.
|
|
1355
|
+
*
|
|
1356
|
+
* Bedrock API keys are bearer tokens: the request carries
|
|
1357
|
+
* `Authorization: Bearer <key>` instead of a SigV4 signature (AWS docs,
|
|
1358
|
+
* "Use an Amazon Bedrock API key",
|
|
1359
|
+
* https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys-use.html,
|
|
1360
|
+
* checked 2026-10-17). The installed `@aws-sdk/client-bedrock-runtime`
|
|
1361
|
+
* supports this natively: its auth scheme provider offers
|
|
1362
|
+
* `smithy.api#httpBearerAuth` beside SigV4, fed by the client's `token`
|
|
1363
|
+
* option, and `authSchemePreference: ["httpBearerAuth"]` selects it ahead
|
|
1364
|
+
* of SigV4, so the AWS credential chain is never consulted.
|
|
1365
|
+
*
|
|
1366
|
+
* Fails closed on an empty key, and on a key combined with `credentials`
|
|
1367
|
+
* or `token` — two credentials for one client leave which one signs the
|
|
1368
|
+
* request ambiguous.
|
|
1369
|
+
*/
|
|
1370
|
+
function withApiKey(clientConfig, apiKey) {
|
|
1371
|
+
if (typeof apiKey !== "string" || apiKey.trim() === "") throw new _warlock_js_ai.InvalidRequestError("BedrockSDK: `apiKey` must be a non-empty string.");
|
|
1372
|
+
const conflicting = ["credentials", "token"].filter((field) => clientConfig[field] !== void 0);
|
|
1373
|
+
if (conflicting.length > 0) throw new _warlock_js_ai.InvalidRequestError(`BedrockSDK: pass either \`apiKey\` or ${conflicting.map((field) => `\`${field}\``).join(" / ")}, not both.`, { context: { fields: conflicting } });
|
|
1374
|
+
return {
|
|
1375
|
+
...clientConfig,
|
|
1376
|
+
token: { token: apiKey },
|
|
1377
|
+
authSchemePreference: ["httpBearerAuth"]
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1091
1380
|
|
|
1092
1381
|
//#endregion
|
|
1382
|
+
exports.BEDROCK_GLOBAL_PRICING = BEDROCK_GLOBAL_PRICING;
|
|
1383
|
+
exports.BEDROCK_US_PRICING = BEDROCK_US_PRICING;
|
|
1093
1384
|
exports.BedrockSDK = BedrockSDK;
|
|
1385
|
+
exports.bedrockPricingFor = bedrockPricingFor;
|
|
1386
|
+
exports.withClaudeCacheWriteRates = withClaudeCacheWriteRates;
|
|
1094
1387
|
//# sourceMappingURL=index.cjs.map
|