@warlock.js/ai-bedrock 5.23.2 → 5.24.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 CHANGED
@@ -4,6 +4,16 @@ 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.24.0 - 2026-09-27
8
+
9
+ ### Fixed
10
+
11
+ - Preserve Claude extended-thinking reasoning blocks across Bedrock Converse tool-use follow-ups, and enforce valid thinking budgets and output-token ceilings.
12
+
13
+ ### Changed
14
+
15
+ - Lockstep release; package APIs are unchanged.
16
+
7
17
  ## 5.23.2 - 2026-09-26
8
18
 
9
19
  ### Changed
package/cjs/index.cjs CHANGED
@@ -31,6 +31,44 @@ function mapStopReason(raw) {
31
31
  return stopReasonMap[raw ?? ""] ?? "error";
32
32
  }
33
33
 
34
+ //#endregion
35
+ //#region ../ai-bedrock/src/utils/reasoning-blocks.ts
36
+ /** Convert a Converse reasoning content block to persistence-safe metadata. */
37
+ function toReasoningMetadata(block) {
38
+ const reasoning = block.reasoningContent;
39
+ if (!reasoning) return;
40
+ if (reasoning.reasoningText) {
41
+ const { text, signature } = reasoning.reasoningText;
42
+ if (typeof text !== "string") return;
43
+ return {
44
+ type: "reasoning",
45
+ text,
46
+ ...typeof signature === "string" ? { signature } : {}
47
+ };
48
+ }
49
+ if (reasoning.redactedContent instanceof Uint8Array) return {
50
+ type: "redacted",
51
+ data: Buffer.from(reasoning.redactedContent).toString("base64")
52
+ };
53
+ }
54
+ /** Restore one persistence-safe metadata entry to a Converse reasoning block. */
55
+ function fromReasoningMetadata(value) {
56
+ if (!isRecord$1(value) || typeof value.type !== "string") return;
57
+ if (value.type === "reasoning" && typeof value.text === "string") return { reasoningContent: { reasoningText: {
58
+ text: value.text,
59
+ ...typeof value.signature === "string" ? { signature: value.signature } : {}
60
+ } } };
61
+ if (value.type === "redacted" && typeof value.data === "string" && isBase64(value.data)) return { reasoningContent: { redactedContent: new Uint8Array(Buffer.from(value.data, "base64")) } };
62
+ }
63
+ function isRecord$1(value) {
64
+ return typeof value === "object" && value !== null;
65
+ }
66
+ /** Require canonical base64, avoiding Buffer's permissive decoding of garbage. */
67
+ function isBase64(value) {
68
+ if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) return false;
69
+ return Buffer.from(value, "base64").toString("base64") === value;
70
+ }
71
+
34
72
  //#endregion
35
73
  //#region ../ai-bedrock/src/utils/to-bedrock-messages.ts
36
74
  const MEDIA_TYPE_TO_FORMAT = {
@@ -81,6 +119,8 @@ function toBedrockMessages(messages) {
81
119
  if (message.role === "assistant" && message.toolCalls && message.toolCalls.length > 0) {
82
120
  const blocks = [];
83
121
  const text = stringifyContent(message.content);
122
+ const reasoningBlocks = getReasoningBlocks(message.toolCalls);
123
+ blocks.push(...reasoningBlocks);
84
124
  if (text) blocks.push({ text });
85
125
  for (const toolCall of message.toolCalls) blocks.push({ toolUse: {
86
126
  toolUseId: toolCall.id,
@@ -111,6 +151,23 @@ function toBedrockMessages(messages) {
111
151
  };
112
152
  }
113
153
  /**
154
+ * Recover opaque Converse reasoning blocks retained on a prior tool call.
155
+ * Bedrock requires them to be replayed before the assistant's text and
156
+ * tool-use blocks on the follow-up request.
157
+ */
158
+ function getReasoningBlocks(toolCalls) {
159
+ for (const toolCall of toolCalls) {
160
+ const bedrock = toolCall.providerMetadata?.bedrock;
161
+ if (!isRecord(bedrock) || !Array.isArray(bedrock.reasoningBlocks)) continue;
162
+ return bedrock.reasoningBlocks.map(fromReasoningMetadata).filter((block) => block !== void 0);
163
+ }
164
+ return [];
165
+ }
166
+ /** Narrow an opaque metadata value to an object without altering its contents. */
167
+ function isRecord(value) {
168
+ return typeof value === "object" && value !== null;
169
+ }
170
+ /**
114
171
  * Multipart content is only meaningful on user messages — for any other
115
172
  * role collapse a `ContentPart[]` to its concatenated text. Plain
116
173
  * strings pass through unchanged.
@@ -565,6 +622,9 @@ function inferVisionCapability(modelId) {
565
622
  //#endregion
566
623
  //#region ../ai-bedrock/src/model.ts
567
624
  const LOG_MODULE = "ai.bedrock";
625
+ /** Default output allowance added to a thinking budget when maxTokens is unset. */
626
+ const DEFAULT_MAX_TOKENS = 4096;
627
+ const MIN_THINKING_BUDGET = 1024;
568
628
  /**
569
629
  * Conventional extended-thinking token budgets for the neutral
570
630
  * `reasoning.effort` levels, used when the caller asks for an effort
@@ -689,6 +749,8 @@ var BedrockModel = class {
689
749
  total: 0
690
750
  };
691
751
  const toolBlocks = /* @__PURE__ */ new Map();
752
+ const reasoningBlocks = /* @__PURE__ */ new Map();
753
+ const completedReasoningBlocks = [];
692
754
  try {
693
755
  for await (const event of response.stream ?? []) {
694
756
  if (event.contentBlockStart?.start?.toolUse) {
@@ -709,19 +771,34 @@ var BedrockModel = class {
709
771
  else if (delta.toolUse) {
710
772
  const accumulator = toolBlocks.get(event.contentBlockDelta.contentBlockIndex ?? 0);
711
773
  if (accumulator) accumulator.json += delta.toolUse.input ?? "";
774
+ } else if (delta.reasoningContent) {
775
+ const index = event.contentBlockDelta.contentBlockIndex ?? 0;
776
+ const accumulator = reasoningBlocks.get(index) ?? {};
777
+ if (delta.reasoningContent.text !== void 0) accumulator.text = (accumulator.text ?? "") + delta.reasoningContent.text;
778
+ if (delta.reasoningContent.signature !== void 0) accumulator.signature = delta.reasoningContent.signature;
779
+ if (delta.reasoningContent.redactedContent !== void 0) accumulator.redactedContent = delta.reasoningContent.redactedContent;
780
+ reasoningBlocks.set(index, accumulator);
712
781
  }
713
782
  continue;
714
783
  }
715
784
  if (event.contentBlockStop) {
716
- const accumulator = toolBlocks.get(event.contentBlockStop.contentBlockIndex ?? 0);
785
+ const index = event.contentBlockStop.contentBlockIndex ?? 0;
786
+ const accumulator = toolBlocks.get(index);
717
787
  if (accumulator) {
718
788
  yield {
719
789
  type: "tool-call",
720
790
  id: accumulator.id,
721
791
  name: accumulator.name,
722
- input: (0, _warlock_js_ai.safeJsonParse)(accumulator.json, {})
792
+ input: (0, _warlock_js_ai.safeJsonParse)(accumulator.json, {}),
793
+ ...completedReasoningBlocks.length > 0 ? { providerMetadata: { bedrock: { reasoningBlocks: completedReasoningBlocks } } } : {}
723
794
  };
724
- toolBlocks.delete(event.contentBlockStop.contentBlockIndex ?? 0);
795
+ toolBlocks.delete(index);
796
+ }
797
+ const reasoningAccumulator = reasoningBlocks.get(index);
798
+ if (reasoningAccumulator) {
799
+ const reasoningBlock = this.toReasoningMetadata(reasoningAccumulator);
800
+ if (reasoningBlock) completedReasoningBlocks.push(reasoningBlock);
801
+ reasoningBlocks.delete(index);
725
802
  }
726
803
  continue;
727
804
  }
@@ -757,7 +834,9 @@ var BedrockModel = class {
757
834
  */
758
835
  buildRequest(messages, options) {
759
836
  const { system, messages: bedrockMessages } = toBedrockMessages(messages);
760
- const maxTokens = options?.maxTokens ?? this.config.maxTokens;
837
+ const thinkingBudget = this.resolveThinkingBudget(options?.reasoning);
838
+ const configuredMaxTokens = options?.maxTokens ?? this.config.maxTokens;
839
+ const maxTokens = this.resolveMaxTokens(configuredMaxTokens, thinkingBudget);
761
840
  const temperature = options?.temperature ?? this.config.temperature;
762
841
  const cachedMessages = this.applyCacheBreakpoints(bedrockMessages, options?.cacheControl);
763
842
  return {
@@ -770,7 +849,7 @@ var BedrockModel = class {
770
849
  },
771
850
  ...this.buildToolConfig(options?.tools),
772
851
  ...this.buildOutputConfig(options?.responseSchema),
773
- ...this.buildReasoningConfig(options?.reasoning)
852
+ ...this.buildReasoningConfig(thinkingBudget)
774
853
  };
775
854
  }
776
855
  /**
@@ -809,9 +888,20 @@ var BedrockModel = class {
809
888
  * when no reasoning option was supplied, so unsupported params never
810
889
  * reach the wire.
811
890
  */
812
- buildReasoningConfig(reasoning) {
813
- if (!this.capabilities.reasoning || !reasoning) return {};
891
+ resolveThinkingBudget(reasoning) {
892
+ if (!this.capabilities.reasoning || !reasoning || reasoning.effort === "none") return;
814
893
  const budgetTokens = reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[reasoning.effort ?? ""];
894
+ if (budgetTokens !== void 0 && budgetTokens < MIN_THINKING_BUDGET) throw new Error(`thinking budget (${budgetTokens}) must be at least ${MIN_THINKING_BUDGET}`);
895
+ return budgetTokens;
896
+ }
897
+ /** Resolve Bedrock's output ceiling while reserving room for extended thinking. */
898
+ resolveMaxTokens(maxTokens, thinkingBudget) {
899
+ if (thinkingBudget === void 0) return maxTokens;
900
+ if (maxTokens === void 0) return thinkingBudget + DEFAULT_MAX_TOKENS;
901
+ if (maxTokens <= thinkingBudget) throw new Error(`maxTokens (${maxTokens}) must be greater than thinking budget (${thinkingBudget})`);
902
+ return maxTokens;
903
+ }
904
+ buildReasoningConfig(budgetTokens) {
815
905
  if (budgetTokens === void 0) return {};
816
906
  return { additionalModelRequestFields: { thinking: {
817
907
  type: "enabled",
@@ -861,13 +951,23 @@ var BedrockModel = class {
861
951
  */
862
952
  extractToolCalls(blocks) {
863
953
  const toolCalls = [];
954
+ const reasoningBlocks = blocks.map(toReasoningMetadata).filter((block) => block !== void 0);
864
955
  for (const block of blocks) if ("toolUse" in block && block.toolUse) toolCalls.push({
865
956
  id: block.toolUse.toolUseId ?? "",
866
957
  name: block.toolUse.name ?? "",
867
- input: block.toolUse.input ?? {}
958
+ input: block.toolUse.input ?? {},
959
+ ...reasoningBlocks.length > 0 ? { providerMetadata: { bedrock: { reasoningBlocks } } } : {}
868
960
  });
869
961
  return toolCalls.length > 0 ? toolCalls : void 0;
870
962
  }
963
+ /** Encode accumulated stream deltas as persistence-safe reasoning metadata. */
964
+ toReasoningMetadata(block) {
965
+ if (block.redactedContent !== void 0) return toReasoningMetadata({ reasoningContent: { redactedContent: block.redactedContent } });
966
+ return toReasoningMetadata({ reasoningContent: { reasoningText: {
967
+ text: block.text ?? "",
968
+ ...block.signature ? { signature: block.signature } : {}
969
+ } } });
970
+ }
871
971
  /**
872
972
  * Normalize Converse's `TokenUsage` into the neutral `Usage` shape.
873
973
  * Bedrock supplies a pre-summed `totalTokens`; cache-read and
package/cjs/index.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["InvalidRequestError","AIError","ProviderTimeoutError","ProviderAuthError","QuotaExceededError","ProviderRateLimitError","ContextLengthExceededError","InvalidRequestError","ProviderError","LOG_MODULE","log","InvokeModelCommand","log","ConverseCommand","ConverseStreamCommand","BedrockRuntimeClient"],"sources":["../../../../../../ai-bedrock/src/utils/map-stop-reason.ts","../../../../../../ai-bedrock/src/utils/to-bedrock-messages.ts","../../../../../../ai-bedrock/src/utils/to-bedrock-tools.ts","../../../../../../ai-bedrock/src/utils/wrap-bedrock-error.ts","../../../../../../ai-bedrock/src/embedder.ts","../../../../../../ai-bedrock/src/known-capabilities.ts","../../../../../../ai-bedrock/src/known-vision-models.ts","../../../../../../ai-bedrock/src/model.ts","../../../../../../ai-bedrock/src/sdk.ts"],"sourcesContent":["import type { FinishReason } from \"@warlock.js/ai\";\n\nconst stopReasonMap: Record<string, FinishReason> = {\n end_turn: \"stop\",\n stop_sequence: \"stop\",\n max_tokens: \"length\",\n tool_use: \"tool_calls\",\n};\n\n/**\n * Map Bedrock Converse's `stopReason` to the normalized `FinishReason`\n * union.\n *\n * `end_turn` / `stop_sequence` are natural stops. `max_tokens` maps to\n * `length`. `tool_use` maps to `tool_calls`. Everything else —\n * `content_filtered`, `guardrail_intervened`, `malformed_tool_use`,\n * `malformed_model_output`, `model_context_window_exceeded`, `null`,\n * or any future value — falls through to `\"error\"`: none produced a\n * clean terminal answer, so the agent must not treat them as success.\n *\n * @example\n * mapStopReason(\"end_turn\"); // \"stop\"\n * mapStopReason(\"tool_use\"); // \"tool_calls\"\n * mapStopReason(\"guardrail_intervened\"); // \"error\"\n * mapStopReason(undefined); // \"error\"\n */\nexport function mapStopReason(raw: string | null | undefined): FinishReason {\n return stopReasonMap[raw ?? \"\"] ?? \"error\";\n}\n","import { InvalidRequestError, type ContentPart, type Message } from \"@warlock.js/ai\";\nimport type {\n ContentBlock,\n ImageFormat,\n Message as BedrockMessage,\n SystemContentBlock,\n} from \"@aws-sdk/client-bedrock-runtime\";\n\n/**\n * Result of splitting a vendor-neutral `Message[]` for the Bedrock\n * Converse API: system prompts hoist to a separate `SystemContentBlock[]`\n * (Converse has no `\"system\"` role inside `messages`), and the\n * remaining turns map to Bedrock `Message[]`.\n */\nexport type BedrockMessages = {\n system: SystemContentBlock[] | undefined;\n messages: BedrockMessage[];\n};\n\nconst MEDIA_TYPE_TO_FORMAT: Record<string, ImageFormat> = {\n \"image/jpeg\": \"jpeg\",\n \"image/png\": \"png\",\n \"image/gif\": \"gif\",\n \"image/webp\": \"webp\",\n};\n\n/**\n * Convert vendor-neutral `Message[]` into Bedrock Converse's request\n * shape.\n *\n * Converse differs from the OpenAI Chat protocol in three ways this\n * function absorbs:\n *\n * 1. **No `system` role.** System messages become a separate\n * `SystemContentBlock[]` (one `{ text }` block each).\n * 2. **Tool results are `user` turns.** A neutral `tool` message\n * becomes a `user` message carrying a single `toolResult` block.\n * 3. **Tool calls are `toolUse` content blocks.** An assistant message\n * with `toolCalls` becomes an `assistant` message: an optional\n * leading `text` block followed by one `toolUse` block per call.\n *\n * @example\n * const { system, messages } = toBedrockMessages([\n * { role: \"system\", content: \"Be concise.\" },\n * { role: \"user\", content: \"Hi\" },\n * ]);\n */\nexport function toBedrockMessages(messages: Message[]): BedrockMessages {\n const system: SystemContentBlock[] = [];\n const mapped: BedrockMessage[] = [];\n\n for (const message of messages) {\n if (message.role === \"system\") {\n system.push({ text: stringifyContent(message.content) });\n\n continue;\n }\n\n if (message.role === \"tool\") {\n mapped.push({\n role: \"user\",\n content: [\n {\n toolResult: {\n toolUseId: message.toolCallId ?? \"\",\n content: [{ text: stringifyContent(message.content) }],\n },\n },\n ],\n });\n\n continue;\n }\n\n if (message.role === \"assistant\" && message.toolCalls && message.toolCalls.length > 0) {\n const blocks: ContentBlock[] = [];\n const text = stringifyContent(message.content);\n\n if (text) {\n blocks.push({ text });\n }\n\n for (const toolCall of message.toolCalls) {\n blocks.push({\n toolUse: {\n toolUseId: toolCall.id,\n name: toolCall.name,\n input: toolCall.input ?? {},\n },\n } as ContentBlock);\n }\n\n mapped.push({ role: \"assistant\", content: blocks });\n\n continue;\n }\n\n if (message.role === \"user\" && Array.isArray(message.content)) {\n mapped.push({\n role: \"user\",\n content: message.content.map(toBedrockContentBlock),\n });\n\n continue;\n }\n\n mapped.push({\n role: message.role === \"assistant\" ? \"assistant\" : \"user\",\n content: [{ text: stringifyContent(message.content) }],\n });\n }\n\n return {\n system: system.length > 0 ? system : undefined,\n messages: mapped,\n };\n}\n\n/**\n * Multipart content is only meaningful on user messages — for any other\n * role collapse a `ContentPart[]` to its concatenated text. Plain\n * strings pass through unchanged.\n */\nfunction stringifyContent(content: string | ContentPart[]): string {\n if (typeof content === \"string\") {\n return content;\n }\n\n return content\n .filter((part): part is { type: \"text\"; text: string } => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\");\n}\n\n/**\n * Map a resolved `ContentPart` to a Bedrock `ContentBlock`. Bedrock's\n * `ImageSource` only accepts raw bytes or an S3 location — there is no\n * remote-URL source. A neutral `{ url }` image therefore cannot be\n * sent and surfaces a typed `InvalidRequestError` upfront rather than\n * a downstream Bedrock validation fault. The agent has already\n * resolved attachments, so this never fetches or reads anything.\n */\nfunction toBedrockContentBlock(part: ContentPart): ContentBlock {\n if (part.type === \"text\") {\n return { text: part.text };\n }\n\n if (\"url\" in part.source) {\n throw new InvalidRequestError(\n \"Bedrock Converse does not support remote-URL sources; supply base64 bytes instead.\",\n );\n }\n\n // PDF → Bedrock `document` content block (A2). Converse accepts a\n // document block with raw bytes; the agent gates this on the model's\n // `pdf` capability before it reaches here.\n if (part.type === \"pdf\") {\n return {\n document: {\n format: \"pdf\",\n name: \"attachment\",\n source: { bytes: Buffer.from(part.source.base64, \"base64\") },\n },\n } as unknown as ContentBlock;\n }\n\n // Bedrock Converse has no audio content block (capability stays false).\n if (part.type === \"audio\") {\n throw new InvalidRequestError(\n \"Bedrock Converse does not support audio attachments.\",\n );\n }\n\n const format = MEDIA_TYPE_TO_FORMAT[part.source.mediaType];\n\n if (!format) {\n throw new InvalidRequestError(\n `Unsupported image media type for Bedrock: \"${part.source.mediaType}\" (expected image/jpeg, image/png, image/gif, or image/webp).`,\n );\n }\n\n return {\n image: {\n format,\n source: { bytes: Buffer.from(part.source.base64, \"base64\") },\n },\n };\n}\n","import { extractJsonSchema, type ToolConfig } from \"@warlock.js/ai\";\nimport type { Tool, ToolConfiguration, ToolInputSchema } from \"@aws-sdk/client-bedrock-runtime\";\n\n/**\n * Convert vendor-neutral `ToolConfig[]` into Bedrock Converse's\n * `ToolConfiguration`. Each tool becomes a `toolSpec` with a JSON\n * `inputSchema`. Bedrock requires the schema root to be an object —\n * a non-object extraction degrades to a parameterless object schema\n * so registration never fails.\n *\n * Returns `undefined` when there are no tools so the caller can omit\n * `toolConfig` from the request entirely (Bedrock rejects an empty\n * `tools` array).\n *\n * @example\n * const toolConfig = toBedrockToolConfig([weatherTool]);\n * await client.send(new ConverseCommand({ modelId, messages, toolConfig }));\n */\nexport function toBedrockToolConfig(\n tools: ToolConfig<unknown, unknown>[] | undefined,\n): ToolConfiguration | undefined {\n if (!tools || tools.length === 0) {\n return undefined;\n }\n\n return {\n tools: tools.map(\n (tool): Tool => ({\n toolSpec: {\n name: tool.name,\n description: tool.description,\n inputSchema: { json: toJsonSchema(tool.input) } as ToolInputSchema,\n },\n }),\n ),\n };\n}\n\n/**\n * Resolve a tool's input schema to a JSON-Schema object. Bedrock's\n * `ToolInputSchema.json` requires an object root; anything else (or a\n * failed extraction) degrades to a parameterless object so the tool\n * still registers.\n */\nfunction toJsonSchema(input: ToolConfig<unknown, unknown>[\"input\"]): Record<string, unknown> {\n const schema = extractJsonSchema(input);\n\n if (schema && schema.type === \"object\") {\n return schema;\n }\n\n return { type: \"object\" };\n}\n","import {\n AIError,\n ContextLengthExceededError,\n InvalidRequestError,\n ProviderAuthError,\n ProviderError,\n ProviderRateLimitError,\n ProviderTimeoutError,\n QuotaExceededError,\n} from \"@warlock.js/ai\";\n\n/**\n * Raw-error fields the wrapper reads off an AWS SDK exception. Every\n * Bedrock error is a Smithy `__BaseException` with a stable `name`\n * (`\"ThrottlingException\"`, `\"ValidationException\"`, …) and a\n * `$metadata` carrying `httpStatusCode` + `requestId`. We duck-type\n * because retries and proxies sometimes flatten the prototype chain.\n */\ntype BedrockErrorShape = {\n name?: string;\n message?: string;\n httpStatusCode?: number;\n requestId?: string;\n code?: string;\n};\n\nconst TIMEOUT_NAMES = new Set([\n \"ModelTimeoutException\",\n \"TimeoutError\",\n \"RequestTimeout\",\n \"RequestTimeoutException\",\n]);\n\n/**\n * Wrap any thrown value caught inside the Bedrock adapter into the\n * appropriate `@warlock.js/ai` `AIError` subclass.\n *\n * **Dispatch strategy.** AWS errors carry no provider machine `code`;\n * the stable identifier is the Smithy exception `name`. Dispatch keys\n * on `name`, falls back to `$metadata.httpStatusCode` when the name is\n * missing (flattened/proxied errors). `ValidationException` is split:\n * the \"input is too long / exceeds context window\" phrasing maps to\n * `ContextLengthExceededError`, everything else to\n * `InvalidRequestError`.\n *\n * `AIError` instances pass through unchanged so `catch/throw wrap(e)`\n * pipelines never double-wrap.\n *\n * @example\n * try {\n * return await this.client.send(new ConverseCommand(...));\n * } catch (thrown) {\n * throw wrapBedrockError(thrown);\n * }\n */\nexport function wrapBedrockError(thrown: unknown): AIError {\n if (thrown instanceof AIError) {\n return thrown;\n }\n\n const shape = toShape(thrown);\n const context = buildContext(shape);\n const message = shape.message ?? (thrown instanceof Error ? thrown.message : String(thrown));\n\n if (isTimeout(shape)) {\n return new ProviderTimeoutError(message, { cause: thrown, context });\n }\n\n if (shape.name === \"AccessDeniedException\" || shape.httpStatusCode === 403) {\n return new ProviderAuthError(message, { cause: thrown, context });\n }\n\n if (shape.httpStatusCode === 401) {\n return new ProviderAuthError(message, { cause: thrown, context });\n }\n\n if (shape.name === \"ServiceQuotaExceededException\") {\n return new QuotaExceededError(message, { cause: thrown, context });\n }\n\n if (shape.name === \"ThrottlingException\" || shape.httpStatusCode === 429) {\n return new ProviderRateLimitError(message, { cause: thrown, context });\n }\n\n if (shape.name === \"ValidationException\") {\n if (/too long|context window|maximum context|exceeds the maximum/i.test(message)) {\n return new ContextLengthExceededError(message, { cause: thrown, context });\n }\n\n return new InvalidRequestError(message, { cause: thrown, context });\n }\n\n if (\n shape.name === \"ResourceNotFoundException\" ||\n shape.name === \"ConflictException\" ||\n isClientStatus(shape.httpStatusCode)\n ) {\n return new InvalidRequestError(message, { cause: thrown, context });\n }\n\n return new ProviderError(message, { cause: thrown, context });\n}\n\n/**\n * Read the raw error shape without depending on `instanceof`. AWS\n * exceptions expose `$metadata`; plain/proxied errors may carry\n * `status` / `code` instead.\n */\nfunction toShape(thrown: unknown): BedrockErrorShape {\n if (typeof thrown !== \"object\" || thrown === null) {\n return {};\n }\n\n const raw = thrown as Record<string, unknown>;\n const metadata = raw.$metadata as { httpStatusCode?: number; requestId?: string } | undefined;\n\n return {\n name: typeof raw.name === \"string\" ? raw.name : undefined,\n message: typeof raw.message === \"string\" ? raw.message : undefined,\n httpStatusCode:\n metadata && typeof metadata.httpStatusCode === \"number\"\n ? metadata.httpStatusCode\n : typeof raw.status === \"number\"\n ? (raw.status as number)\n : undefined,\n requestId: metadata && typeof metadata.requestId === \"string\" ? metadata.requestId : undefined,\n code: typeof raw.code === \"string\" ? raw.code : undefined,\n };\n}\n\n/**\n * Decide whether the error is a timeout. Bedrock surfaces\n * `ModelTimeoutException`; the AWS transport layer surfaces\n * `TimeoutError` / `ETIMEDOUT` / `ECONNABORTED`.\n */\nfunction isTimeout(shape: BedrockErrorShape): boolean {\n if (shape.name && TIMEOUT_NAMES.has(shape.name)) {\n return true;\n }\n\n return shape.code === \"ETIMEDOUT\" || shape.code === \"ECONNABORTED\";\n}\n\n/** True for HTTP 4xx — a client-side request problem, not a server fault. */\nfunction isClientStatus(status: number | undefined): boolean {\n return typeof status === \"number\" && status >= 400 && status < 500;\n}\n\n/**\n * Attach the raw diagnostic fields to `error.context`. The Smithy\n * exception `name` is the closest thing Bedrock has to a stable error\n * code, so it lands on `context.code`.\n */\nfunction buildContext(shape: BedrockErrorShape): Record<string, unknown> {\n const context: Record<string, unknown> = {};\n\n if (shape.httpStatusCode !== undefined) {\n context.status = shape.httpStatusCode;\n }\n\n if (shape.name) {\n context.code = shape.name;\n }\n\n if (shape.requestId) {\n context.requestId = shape.requestId;\n }\n\n return context;\n}\n","import {\n type EmbeddingBatchResult,\n type EmbeddingResult,\n type EmbeddingUsage,\n type EmbedderContract,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport { InvokeModelCommand, type BedrockRuntimeClient } from \"@aws-sdk/client-bedrock-runtime\";\nimport type { BedrockEmbedderConfig } from \"./config.type\";\nimport { wrapBedrockError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.bedrock\";\n\n/** Shape of the Amazon Titan Text Embeddings response body. */\ntype TitanEmbeddingResponse = {\n embedding: number[];\n inputTextTokenCount: number;\n};\n\n/**\n * Bedrock-backed implementation of `EmbedderContract`, targeting the\n * Amazon Titan Text Embeddings family\n * (`amazon.titan-embed-text-v2:0` / v1) via `InvokeModel`.\n *\n * **Role.** Converts text into floating-point vectors. Standalone\n * primitive — unrelated to Converse / tools / the agent loop.\n *\n * **Single-input only upstream.** Titan's `InvokeModel` body accepts\n * one `inputText` per call — there is no batch endpoint. `embedMany`\n * therefore issues one request per input sequentially and aggregates\n * token usage. This is a deliberate, documented trade-off: a real\n * batch API does not exist for Titan on Bedrock, so the alternative\n * (failing `embedMany`) would be worse. Cohere embeddings on Bedrock\n * *do* batch but use an incompatible body shape — out of scope; use\n * the OpenAI adapter or a future Cohere adapter when batch throughput\n * matters.\n *\n * **Dimensions.** When no `dimensions` override is given,\n * `this.dimensions` starts at `0` and is populated from the first\n * response's vector length, then cached. Passing `dimensions` forwards\n * Titan v2's truncation hint (256 / 512 / 1024) and sets the initial\n * value immediately.\n *\n * @example\n * const embedder = new BedrockEmbedder(client, { name: \"amazon.titan-embed-text-v2:0\" });\n * const { vector } = await embedder.embed(\"Hello world\");\n * const { vectors } = await embedder.embedMany([\"doc 1\", \"doc 2\"]);\n */\nexport class BedrockEmbedder implements EmbedderContract {\n public readonly name: string;\n public readonly provider: string;\n public dimensions: number;\n\n private readonly client: BedrockRuntimeClient;\n private readonly configuredDimensions: number | undefined;\n private readonly logger: Logger = log;\n\n public constructor(\n client: BedrockRuntimeClient,\n config: BedrockEmbedderConfig,\n provider: string = \"bedrock\",\n ) {\n this.client = client;\n this.name = config.name;\n this.provider = provider;\n this.configuredDimensions = config.dimensions;\n this.dimensions = config.dimensions ?? 0;\n }\n\n public async embed(input: string): Promise<EmbeddingResult> {\n const { vector, tokens } = await this.invoke(input);\n\n return {\n vector,\n dimensions: this.dimensions,\n usage: { promptTokens: tokens, totalTokens: tokens },\n };\n }\n\n public async embedMany(inputs: string[]): Promise<EmbeddingBatchResult> {\n const vectors: number[][] = [];\n let tokens = 0;\n\n for (const input of inputs) {\n const result = await this.invoke(input);\n\n vectors.push(result.vector);\n tokens += result.tokens;\n }\n\n const usage: EmbeddingUsage = { promptTokens: tokens, totalTokens: tokens };\n\n return { vectors, dimensions: this.dimensions, usage };\n }\n\n /**\n * Issue a single Titan `InvokeModel` embedding request: encode the\n * JSON body, send, wrap provider errors, decode the response, and\n * cache `dimensions` on the first successful call.\n */\n private async invoke(input: string): Promise<{ vector: number[]; tokens: number }> {\n this.logger.debug(LOG_MODULE, \"embedder.request\", \"InvokeModel embeddings\", {\n model: this.name,\n });\n\n const body = JSON.stringify({\n inputText: input,\n ...(this.configuredDimensions !== undefined\n ? { dimensions: this.configuredDimensions }\n : {}),\n });\n\n let raw;\n\n try {\n raw = await this.client.send(\n new InvokeModelCommand({\n modelId: this.name,\n contentType: \"application/json\",\n accept: \"application/json\",\n body: new TextEncoder().encode(body),\n }),\n );\n } catch (thrown) {\n const wrapped = wrapBedrockError(thrown);\n\n this.logger.error(LOG_MODULE, \"embedder.error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n const decoded = JSON.parse(new TextDecoder().decode(raw.body)) as TitanEmbeddingResponse;\n\n if (this.dimensions === 0) {\n this.dimensions = decoded.embedding.length;\n }\n\n this.logger.debug(LOG_MODULE, \"embedder.response\", \"InvokeModel embeddings returned\", {\n dimensions: decoded.embedding.length,\n tokens: decoded.inputTextTokenCount,\n });\n\n return { vector: decoded.embedding, tokens: decoded.inputTextTokenCount };\n }\n}\n","/**\n * Cost-truth capability inference for Bedrock Converse model ids.\n *\n * Bedrock model ids are provider-prefixed and version-suffixed\n * (`anthropic.claude-3-7-sonnet-20250219-v1:0`, `us.amazon.nova-pro-v1:0`),\n * so — exactly like `known-vision-models.ts` — a lowercase substring scan\n * is the only robust check across cross-region inference-profile prefixes\n * (`us.`, `eu.`, `apac.`) and date/version tags.\n *\n * Each predicate answers a single `ModelCapabilities` flag the agent reads\n * to decide whether to forward a cost-truth option (`reasoning`,\n * `cacheControl`) or up-front-reject an attachment (`pdf`). Unknown ids\n * default to `false` so an unsupported request fails fast with a clear\n * capability error instead of an opaque Bedrock `ValidationException`.\n * Every inference is overridable per-model via `bedrock.model({ name, … })`.\n */\n\n/**\n * Families that expose Anthropic-style extended thinking on Bedrock\n * Converse via `additionalModelRequestFields.thinking`. Only Claude 3.7\n * and the Claude 4 line (Sonnet / Opus / Haiku) support a configurable\n * thinking budget; earlier Claude, Nova, Llama, Mistral and Cohere do\n * not, so they are intentionally absent.\n */\nconst REASONING_CAPABLE_SUBSTRINGS = [\n \"claude-3-7\",\n \"claude-sonnet-4\",\n \"claude-opus-4\",\n \"claude-haiku-4\",\n];\n\n/**\n * Families that honor Converse `cachePoint` prompt-cache breakpoints.\n * Anthropic Claude 3.5+ / 3.7 / 4 and the Amazon Nova line support\n * cache points; text-only legacy families do not.\n */\nconst PROMPT_CACHING_CAPABLE_SUBSTRINGS = [\n \"claude-3-5\",\n \"claude-3-7\",\n \"claude-sonnet-4\",\n \"claude-opus-4\",\n \"claude-haiku-4\",\n \"nova-lite\",\n \"nova-pro\",\n \"nova-premier\",\n \"nova-micro\",\n];\n\n/**\n * Families that accept Converse `document` content blocks (PDF / docx /\n * txt input). The multimodal Claude 3+ and Nova families support\n * document blocks; the substring set mirrors the vision-capable list\n * minus the image-only Llama entries (Llama on Bedrock takes images but\n * not document blocks via Converse).\n */\nconst PDF_CAPABLE_SUBSTRINGS = [\n \"claude-3\",\n \"claude-sonnet-4\",\n \"claude-opus-4\",\n \"claude-haiku-4\",\n \"nova-lite\",\n \"nova-pro\",\n \"nova-premier\",\n];\n\nfunction matchesAny(modelId: string, fragments: string[]): boolean {\n const normalized = modelId.toLowerCase();\n\n return fragments.some((fragment) => normalized.includes(fragment));\n}\n\n/**\n * Infer whether a Bedrock model id exposes extended-thinking / reasoning\n * (Claude 3.7 + Claude 4). When true the adapter forwards\n * `ModelCallOptions.reasoning` as Converse\n * `additionalModelRequestFields.thinking`.\n *\n * @example\n * inferReasoningCapability(\"anthropic.claude-3-7-sonnet-20250219-v1:0\"); // → true\n * inferReasoningCapability(\"us.amazon.nova-pro-v1:0\"); // → false\n */\nexport function inferReasoningCapability(modelId: string): boolean {\n return matchesAny(modelId, REASONING_CAPABLE_SUBSTRINGS);\n}\n\n/**\n * Infer whether a Bedrock model id honors Converse `cachePoint`\n * breakpoints (Claude 3.5+ / Nova). When true the adapter both maps\n * `ModelCallOptions.cacheControl` write breakpoints to cache points and\n * reports `Usage.cachedTokens` / `Usage.cacheWriteTokens`.\n *\n * @example\n * inferPromptCachingCapability(\"us.amazon.nova-pro-v1:0\"); // → true\n * inferPromptCachingCapability(\"meta.llama3-1-8b-instruct-v1:0\"); // → false\n */\nexport function inferPromptCachingCapability(modelId: string): boolean {\n return matchesAny(modelId, PROMPT_CACHING_CAPABLE_SUBSTRINGS);\n}\n\n/**\n * Infer whether a Bedrock model id accepts Converse `document` content\n * blocks (PDF / document input — Claude 3+ / Nova). When false the agent\n * rejects a PDF attachment up front instead of dropping it at the wire.\n *\n * @example\n * inferPdfCapability(\"anthropic.claude-3-5-sonnet-20240620-v1:0\"); // → true\n * inferPdfCapability(\"meta.llama3-2-90b-instruct-v1:0\"); // → false\n */\nexport function inferPdfCapability(modelId: string): boolean {\n return matchesAny(modelId, PDF_CAPABLE_SUBSTRINGS);\n}\n","/**\n * Substrings that identify Bedrock model ids whose family accepts image\n * input on the Converse API.\n *\n * Bedrock model ids are provider-prefixed and version-suffixed\n * (`anthropic.claude-3-5-sonnet-20240620-v1:0`, `us.amazon.nova-pro-v1:0`,\n * `meta.llama3-2-90b-instruct-v1:0`), so a substring match is the only\n * robust check across the cross-region inference-profile prefixes\n * (`us.`, `eu.`, `apac.`) and date/version tags.\n *\n * Multimodal families covered: Anthropic Claude 3 / 3.5 / 3.7 / 4,\n * Amazon Nova Lite/Pro/Premier, Meta Llama 3.2 (11B/90B) and Llama 4.\n * Text-only families (Llama 3/3.1, Titan Text, Mistral 7B, Cohere\n * Command) are intentionally absent. Override per-model via\n * `bedrock.model({ name, vision: true | false })`.\n */\nconst VISION_CAPABLE_SUBSTRINGS = [\n \"claude-3\",\n \"claude-sonnet-4\",\n \"claude-opus-4\",\n \"claude-haiku-4\",\n \"nova-lite\",\n \"nova-pro\",\n \"nova-premier\",\n \"llama3-2-11b\",\n \"llama3-2-90b\",\n \"llama4\",\n];\n\n/**\n * Infer whether a Bedrock model id supports vision based on the known\n * multimodal-family substrings. Unknown ids default to `false` so that\n * passing an image attachment to an unsupported model surfaces a clear,\n * agent-side capability error instead of an opaque Bedrock validation\n * fault.\n *\n * @example\n * inferVisionCapability(\"anthropic.claude-3-5-sonnet-20240620-v1:0\"); // → true\n * inferVisionCapability(\"us.amazon.nova-pro-v1:0\"); // → true\n * inferVisionCapability(\"meta.llama3-1-8b-instruct-v1:0\"); // → false\n * inferVisionCapability(\"amazon.titan-text-express-v1\"); // → false\n */\nexport function inferVisionCapability(modelId: string): boolean {\n const normalized = modelId.toLowerCase();\n\n return VISION_CAPABLE_SUBSTRINGS.some((fragment) => normalized.includes(fragment));\n}\n","import {\n safeJsonParse,\n type Message,\n type ModelCallOptions,\n type ModelCapabilities,\n type ModelContract,\n type ModelPricing,\n type ModelResponse,\n type ModelStreamChunk,\n type ModelToolCallRequest,\n type Usage,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport {\n ConverseCommand,\n ConverseStreamCommand,\n type BedrockRuntimeClient,\n type ContentBlock,\n type ConverseRequest,\n type TokenUsage,\n} from \"@aws-sdk/client-bedrock-runtime\";\nimport type { BedrockModelConfig } from \"./config.type\";\nimport {\n inferPdfCapability,\n inferPromptCachingCapability,\n inferReasoningCapability,\n} from \"./known-capabilities\";\nimport { inferVisionCapability } from \"./known-vision-models\";\nimport { mapStopReason, toBedrockMessages, toBedrockToolConfig, wrapBedrockError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.bedrock\";\n\n/**\n * Conventional extended-thinking token budgets for the neutral\n * `reasoning.effort` levels, used when the caller asks for an effort\n * tier without naming an explicit `reasoning.maxTokens` budget. Mirrors\n * the low / medium / high spread other reasoning adapters expose so the\n * vendor-neutral option behaves consistently across providers.\n */\nconst EFFORT_THINKING_BUDGET: Record<string, number | undefined> = {\n low: 1024,\n medium: 4096,\n high: 16384,\n};\n\n/**\n * Bedrock-backed implementation of `ModelContract`.\n *\n * **Role.** The provider-facing bridge between the vendor-neutral\n * `@warlock.js/ai` agent runtime and AWS Bedrock's Converse /\n * ConverseStream API. Converse is the model-agnostic surface — one\n * wire mapping covers every Bedrock-hosted family (Anthropic Claude,\n * Amazon Nova, Meta Llama, Mistral, Cohere) instead of per-family\n * `InvokeModel` body shapes.\n *\n * **Responsibility.**\n * - Owns: a long-lived `BedrockRuntimeClient` + frozen `ModelConfig`\n * (modelId, temperature, maxTokens) used as per-call defaults.\n * - Owns: translating vendor-neutral `Message[]` / `ToolConfig[]` into\n * Converse shapes (system hoisting, `toolUse` / `toolResult` blocks,\n * image bytes) on the way out, and Converse's content-block response\n * (text, tool calls, stop reason, token usage) back into the neutral\n * shapes on the way in.\n * - Does NOT own: dispatching tools, looping, history, retries — those\n * are agent concerns. The model is a per-call protocol adapter.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across calls\"): the AWS client is heavy to construct and reused for\n * the SDK's lifetime.\n *\n * @example\n * import { BedrockRuntimeClient } from \"@aws-sdk/client-bedrock-runtime\";\n * const client = new BedrockRuntimeClient({ region: \"us-east-1\" });\n * const model = new BedrockModel(client, {\n * name: \"anthropic.claude-sonnet-4-5-20250929-v1:0\",\n * });\n *\n * const myAgent = agent({ model, tools: [searchTool] });\n * const result = await myAgent.execute(\"Summarize today's news.\");\n */\nexport class BedrockModel implements ModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly capabilities: ModelCapabilities;\n public readonly pricing?: ModelPricing;\n\n private readonly client: BedrockRuntimeClient;\n private readonly config: BedrockModelConfig;\n private readonly logger: Logger = log;\n\n public constructor(\n client: BedrockRuntimeClient,\n config: BedrockModelConfig,\n provider: string = \"bedrock\",\n ) {\n this.client = client;\n this.config = config;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n this.capabilities = {\n structuredOutput: config.structuredOutput ?? true,\n vision: config.vision ?? inferVisionCapability(config.name),\n reasoning: config.reasoning ?? inferReasoningCapability(config.name),\n promptCaching: config.promptCaching ?? inferPromptCachingCapability(config.name),\n pdf: config.pdf ?? inferPdfCapability(config.name),\n audio: config.audio ?? false,\n };\n }\n\n /**\n * Single-shot completion via the Converse API. Sends the full\n * message list, waits for the terminal response, and reshapes it\n * into a vendor-neutral `ModelResponse`. Per-call `options` override\n * the instance defaults for this call only.\n */\n public async complete(messages: Message[], options?: ModelCallOptions): Promise<ModelResponse> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting Converse call\", {\n model: this.name,\n messageCount: messages.length,\n streaming: false,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response;\n\n try {\n response = await this.client.send(\n new ConverseCommand(this.buildRequest(messages, options)),\n options?.signal ? { abortSignal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const blocks = response.output?.message?.content ?? [];\n const finishReason = mapStopReason(response.stopReason);\n const usage = this.extractUsage(response.usage);\n const toolCalls = this.extractToolCalls(blocks);\n\n this.logger.debug(LOG_MODULE, \"response\", \"Converse call succeeded\", { finishReason, usage });\n\n return {\n content: this.extractText(blocks),\n finishReason,\n usage,\n toolCalls,\n };\n }\n\n /**\n * Incremental streaming completion via ConverseStream. Yields neutral\n * `ModelStreamChunk`s — `delta` for text, `tool-call` once a\n * `toolUse` block's accumulated input JSON is complete, and a\n * terminal `done` with the final finish reason + usage totals.\n */\n public async *stream(\n messages: Message[],\n options?: ModelCallOptions,\n ): AsyncIterable<ModelStreamChunk> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting ConverseStream call\", {\n model: this.name,\n messageCount: messages.length,\n streaming: true,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response;\n\n try {\n response = await this.client.send(\n new ConverseStreamCommand(this.buildRequest(messages, options)),\n options?.signal ? { abortSignal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n let rawStopReason: string | undefined;\n const usage: Usage = { input: 0, output: 0, total: 0 };\n const toolBlocks = new Map<number, { id: string; name: string; json: string }>();\n\n try {\n for await (const event of response.stream ?? []) {\n if (event.contentBlockStart?.start?.toolUse) {\n const start = event.contentBlockStart.start.toolUse;\n\n toolBlocks.set(event.contentBlockStart.contentBlockIndex ?? 0, {\n id: start.toolUseId ?? \"\",\n name: start.name ?? \"\",\n json: \"\",\n });\n\n continue;\n }\n\n if (event.contentBlockDelta?.delta) {\n const delta = event.contentBlockDelta.delta;\n\n if (delta.text) {\n yield { type: \"delta\", content: delta.text };\n } else if (delta.toolUse) {\n const accumulator = toolBlocks.get(event.contentBlockDelta.contentBlockIndex ?? 0);\n\n if (accumulator) {\n accumulator.json += delta.toolUse.input ?? \"\";\n }\n }\n\n continue;\n }\n\n if (event.contentBlockStop) {\n const accumulator = toolBlocks.get(event.contentBlockStop.contentBlockIndex ?? 0);\n\n if (accumulator) {\n yield {\n type: \"tool-call\",\n id: accumulator.id,\n name: accumulator.name,\n input: safeJsonParse<Record<string, unknown>>(accumulator.json, {}),\n };\n\n toolBlocks.delete(event.contentBlockStop.contentBlockIndex ?? 0);\n }\n\n continue;\n }\n\n if (event.messageStop) {\n rawStopReason = event.messageStop.stopReason;\n }\n\n if (event.metadata?.usage) {\n const raw = event.metadata.usage;\n\n usage.input = raw.inputTokens ?? 0;\n usage.output = raw.outputTokens ?? 0;\n usage.total = raw.totalTokens ?? usage.input + usage.output;\n\n if (raw.cacheReadInputTokens && raw.cacheReadInputTokens > 0) {\n usage.cachedTokens = raw.cacheReadInputTokens;\n }\n\n if (raw.cacheWriteInputTokens && raw.cacheWriteInputTokens > 0) {\n usage.cacheWriteTokens = raw.cacheWriteInputTokens;\n }\n }\n }\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const finishReason = mapStopReason(rawStopReason);\n\n this.logger.debug(LOG_MODULE, \"response\", \"ConverseStream call succeeded\", {\n finishReason,\n usage,\n });\n\n yield { type: \"done\", finishReason, usage };\n }\n\n /**\n * Assemble the Converse request shared by `complete()` and\n * `stream()` (both command shapes take the same input). Hoists the\n * system prompt, maps inference params, and conditionally attaches\n * tools and native structured output.\n */\n private buildRequest(\n messages: Message[],\n options: ModelCallOptions | undefined,\n ): ConverseRequest {\n const { system, messages: bedrockMessages } = toBedrockMessages(messages);\n const maxTokens = options?.maxTokens ?? this.config.maxTokens;\n const temperature = options?.temperature ?? this.config.temperature;\n const cachedMessages = this.applyCacheBreakpoints(bedrockMessages, options?.cacheControl);\n\n return {\n modelId: this.name,\n messages: cachedMessages,\n ...(system ? { system } : {}),\n inferenceConfig: {\n ...(maxTokens !== undefined ? { maxTokens } : {}),\n ...(temperature !== undefined ? { temperature } : {}),\n },\n ...this.buildToolConfig(options?.tools),\n ...this.buildOutputConfig(options?.responseSchema),\n ...this.buildReasoningConfig(options?.reasoning),\n };\n }\n\n /**\n * Append a Converse `cachePoint` block to the LAST message when the\n * caller supplies a `cacheControl` write breakpoint and the model is\n * `promptCaching`-capable. A cache point tells Bedrock to cache the\n * whole prefix up to that block, so subsequent calls reusing the same\n * prefix bill the cached portion at the discounted read rate\n * (surfaced as `Usage.cachedTokens`). No-ops gracefully when caching\n * is unsupported, no breakpoint was requested, or there are no\n * messages to mark — Bedrock then prices the call normally.\n *\n * Bedrock only honors `CachePointType.DEFAULT`; the neutral\n * `breakpoints` count is a presence hint (one trailing breakpoint is\n * the only placement Converse supports without manual block surgery),\n * so any positive value marks the trailing message.\n */\n private applyCacheBreakpoints(\n messages: ConverseRequest[\"messages\"],\n cacheControl: ModelCallOptions[\"cacheControl\"],\n ): ConverseRequest[\"messages\"] {\n const breakpoints = cacheControl?.breakpoints ?? 0;\n\n if (!this.capabilities.promptCaching || breakpoints <= 0 || !messages || messages.length === 0) {\n return messages;\n }\n\n const last = messages.length - 1;\n const lastMessage = messages[last];\n\n return [\n ...messages.slice(0, last),\n {\n ...lastMessage,\n content: [...(lastMessage.content ?? []), { cachePoint: { type: \"default\" } }],\n },\n ];\n }\n\n /**\n * Translate the neutral `reasoning` option into Claude-on-Bedrock's\n * extended-thinking control, carried in Converse's escape hatch\n * `additionalModelRequestFields.thinking`. Emitted only when the model\n * is `reasoning`-capable and a budget can be resolved — `maxTokens`\n * (explicit thinking budget) wins, otherwise `effort` maps to a\n * conventional token budget so callers can opt in without picking a\n * number. Returns an empty object (no-op) for non-reasoning models or\n * when no reasoning option was supplied, so unsupported params never\n * reach the wire.\n */\n private buildReasoningConfig(\n reasoning: ModelCallOptions[\"reasoning\"],\n ): Pick<ConverseRequest, \"additionalModelRequestFields\"> {\n if (!this.capabilities.reasoning || !reasoning) {\n return {};\n }\n\n const budgetTokens = reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[reasoning.effort ?? \"\"];\n\n if (budgetTokens === undefined) {\n return {};\n }\n\n return {\n additionalModelRequestFields: {\n thinking: { type: \"enabled\", budget_tokens: budgetTokens },\n },\n };\n }\n\n /**\n * Spread-friendly tool fragment. Returns an empty object when no\n * tools were supplied (Bedrock rejects an empty `tools` array).\n */\n private buildToolConfig(tools: ModelCallOptions[\"tools\"]): Pick<ConverseRequest, \"toolConfig\"> {\n const toolConfig = toBedrockToolConfig(tools);\n\n return toolConfig ? { toolConfig } : {};\n }\n\n /**\n * Translate the neutral `responseSchema` into Converse's native\n * `outputConfig.textFormat` (JSON-schema structured output). Bedrock\n * requires the schema as a stringified JSON document and only\n * accepts an object root. Emitted only when the model is\n * `structuredOutput`-capable and the schema is an object — otherwise\n * the agent's soft system-prompt hint + client-side `validate()`\n * carry shape (same degradation philosophy as the OpenAI adapter).\n */\n private buildOutputConfig(\n responseSchema: Record<string, unknown> | undefined,\n ): Pick<ConverseRequest, \"outputConfig\"> {\n if (!responseSchema || !this.capabilities.structuredOutput) {\n return {};\n }\n\n if (responseSchema.type !== \"object\" || typeof responseSchema.properties !== \"object\") {\n return {};\n }\n\n return {\n outputConfig: {\n textFormat: {\n type: \"json_schema\",\n structure: {\n jsonSchema: { name: \"response\", schema: JSON.stringify(responseSchema) },\n },\n },\n },\n };\n }\n\n /**\n * Concatenate every `text` content block into the single neutral\n * `content` string. `toolUse` and other block types are surfaced\n * separately via `extractToolCalls`.\n */\n private extractText(blocks: ContentBlock[]): string {\n return blocks\n .map((block) => (\"text\" in block && typeof block.text === \"string\" ? block.text : \"\"))\n .join(\"\");\n }\n\n /**\n * Reshape Converse `toolUse` content blocks into the neutral\n * `ModelToolCallRequest[]`. Returns `undefined` when no tools were\n * requested so callers can branch on presence.\n */\n private extractToolCalls(blocks: ContentBlock[]): ModelToolCallRequest[] | undefined {\n const toolCalls: ModelToolCallRequest[] = [];\n\n for (const block of blocks) {\n if (\"toolUse\" in block && block.toolUse) {\n toolCalls.push({\n id: block.toolUse.toolUseId ?? \"\",\n name: block.toolUse.name ?? \"\",\n input: (block.toolUse.input ?? {}) as Record<string, unknown>,\n });\n }\n }\n\n return toolCalls.length > 0 ? toolCalls : undefined;\n }\n\n /**\n * Normalize Converse's `TokenUsage` into the neutral `Usage` shape.\n * Bedrock supplies a pre-summed `totalTokens`; cache-read and\n * cache-write tokens are surfaced as `cachedTokens` /\n * `cacheWriteTokens` only when non-zero so callers can price the\n * discounted read rate and the one-time write cost separately.\n * Bedrock's Converse `TokenUsage` carries no reasoning-token channel,\n * so `Usage.reasoningTokens` is intentionally left unset here.\n */\n private extractUsage(raw: TokenUsage | undefined): Usage {\n if (!raw) {\n return { input: 0, output: 0, total: 0 };\n }\n\n const input = raw.inputTokens ?? 0;\n const output = raw.outputTokens ?? 0;\n const cached = raw.cacheReadInputTokens;\n const cacheWrite = raw.cacheWriteInputTokens;\n\n return {\n input,\n output,\n total: raw.totalTokens ?? input + output,\n ...(cached && cached > 0 ? { cachedTokens: cached } : {}),\n ...(cacheWrite && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {}),\n };\n }\n\n /**\n * Wrap a thrown provider error into the typed `AIError` hierarchy\n * and emit the standard error log line before it propagates. Shared\n * by every catch site so the log shape stays identical.\n */\n private logAndWrap(thrown: unknown) {\n const wrapped = wrapBedrockError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n return wrapped;\n }\n}\n","import { BedrockRuntimeClient } from \"@aws-sdk/client-bedrock-runtime\";\nimport type {\n EmbedderContract,\n ModelContract,\n ModelPricing,\n SDKAdapterContract,\n} from \"@warlock.js/ai\";\nimport { approximateTokenCount } from \"@warlock.js/ai\";\nimport type {\n BedrockEmbedderConfig,\n BedrockModelConfig,\n BedrockSDKConfig,\n} from \"./config.type\";\nimport { BedrockEmbedder } from \"./embedder\";\nimport { BedrockModel } from \"./model\";\n\n/**\n * AWS Bedrock-backed implementation of `SDKAdapterContract`.\n *\n * **Role.** The package entry point for any Bedrock-hosted model via\n * the Converse API. A single `BedrockSDK` holds one live\n * `BedrockRuntimeClient`, shared by every `ModelContract` and\n * `EmbedderContract` it produces. Construct one SDK per AWS\n * account/region and reuse it everywhere.\n *\n * **Responsibility.**\n * - Owns: a long-lived `BedrockRuntimeClient` (region, credential\n * chain) and its lifetime. Factory for `BedrockModel` /\n * `BedrockEmbedder` instances sharing that client.\n * - Does NOT own: anything per-call — those live in `BedrockModel` /\n * `BedrockEmbedder` and the agent runtime.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across many calls\"): the AWS client is heavy to construct and\n * designed for reuse; keeping it on `this` aligns with the\n * `new BedrockRuntimeClient(...)` upstream convention.\n *\n * @example\n * const bedrock = new BedrockSDK({ region: \"us-east-1\" });\n * const model = bedrock.model({ name: \"anthropic.claude-sonnet-4-5-20250929-v1:0\" });\n * const embedder = bedrock.embedder({ name: \"amazon.titan-embed-text-v2:0\" });\n */\nexport class BedrockSDK implements SDKAdapterContract {\n private readonly client: BedrockRuntimeClient;\n private readonly provider: string;\n private readonly pricing?: Record<string, ModelPricing>;\n\n public constructor(config: BedrockSDKConfig) {\n const { provider, pricing, ...clientConfig } = config;\n\n this.client = new BedrockRuntimeClient(clientConfig);\n this.provider = provider ?? \"bedrock\";\n this.pricing = pricing;\n }\n\n /**\n * Build a `BedrockModel` bound to this SDK's client. Each call\n * returns a fresh instance; all instances share the underlying AWS\n * client so connection pools, credential refresh, and retry config\n * stay unified. The SDK's `provider` label is forwarded.\n *\n * Pricing resolution: per-model `config.pricing` wins; otherwise the\n * SDK-level registry entry keyed by `config.name`; otherwise\n * `undefined` (no cost computed).\n */\n public model(config: BedrockModelConfig): ModelContract {\n const resolvedPricing = config.pricing ?? this.pricing?.[config.name];\n const resolvedConfig: BedrockModelConfig =\n resolvedPricing === config.pricing ? config : { ...config, pricing: resolvedPricing };\n\n return new BedrockModel(this.client, resolvedConfig, this.provider);\n }\n\n /**\n * Rough token-count estimate. Uses the character-heuristic\n * (`approximateTokenCount`) from the core package — Bedrock has no\n * offline tokenizer and the per-model tokenizers differ; good enough\n * for budgeting and quota guards, not for billing.\n */\n public async count(text: string, _model?: string): Promise<number> {\n return approximateTokenCount(text);\n }\n\n /**\n * Build a `BedrockEmbedder` (Amazon Titan Text Embeddings) bound to\n * this SDK's client.\n *\n * @example\n * const embedder = bedrock.embedder({ name: \"amazon.titan-embed-text-v2:0\" });\n * const { vector } = await embedder.embed(\"Hello world\");\n */\n public embedder(config: BedrockEmbedderConfig): EmbedderContract {\n return new BedrockEmbedder(this.client, config, this.provider);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["isRecord","InvalidRequestError","AIError","ProviderTimeoutError","ProviderAuthError","QuotaExceededError","ProviderRateLimitError","ContextLengthExceededError","InvalidRequestError","ProviderError","LOG_MODULE","log","InvokeModelCommand","log","ConverseCommand","ConverseStreamCommand","BedrockRuntimeClient"],"sources":["../../../../../../ai-bedrock/src/utils/map-stop-reason.ts","../../../../../../ai-bedrock/src/utils/reasoning-blocks.ts","../../../../../../ai-bedrock/src/utils/to-bedrock-messages.ts","../../../../../../ai-bedrock/src/utils/to-bedrock-tools.ts","../../../../../../ai-bedrock/src/utils/wrap-bedrock-error.ts","../../../../../../ai-bedrock/src/embedder.ts","../../../../../../ai-bedrock/src/known-capabilities.ts","../../../../../../ai-bedrock/src/known-vision-models.ts","../../../../../../ai-bedrock/src/model.ts","../../../../../../ai-bedrock/src/sdk.ts"],"sourcesContent":["import type { FinishReason } from \"@warlock.js/ai\";\n\nconst stopReasonMap: Record<string, FinishReason> = {\n end_turn: \"stop\",\n stop_sequence: \"stop\",\n max_tokens: \"length\",\n tool_use: \"tool_calls\",\n};\n\n/**\n * Map Bedrock Converse's `stopReason` to the normalized `FinishReason`\n * union.\n *\n * `end_turn` / `stop_sequence` are natural stops. `max_tokens` maps to\n * `length`. `tool_use` maps to `tool_calls`. Everything else —\n * `content_filtered`, `guardrail_intervened`, `malformed_tool_use`,\n * `malformed_model_output`, `model_context_window_exceeded`, `null`,\n * or any future value — falls through to `\"error\"`: none produced a\n * clean terminal answer, so the agent must not treat them as success.\n *\n * @example\n * mapStopReason(\"end_turn\"); // \"stop\"\n * mapStopReason(\"tool_use\"); // \"tool_calls\"\n * mapStopReason(\"guardrail_intervened\"); // \"error\"\n * mapStopReason(undefined); // \"error\"\n */\nexport function mapStopReason(raw: string | null | undefined): FinishReason {\n return stopReasonMap[raw ?? \"\"] ?? \"error\";\n}\n","import type { ContentBlock } from \"@aws-sdk/client-bedrock-runtime\";\n\n/**\n * JSON-safe representation of a Bedrock Converse reasoning block retained in\n * tool-call provider metadata. Binary redacted reasoning is base64 encoded so\n * it survives message-history persistence unchanged.\n */\nexport type BedrockReasoningBlock =\n | { type: \"reasoning\"; text: string; signature?: string }\n | { type: \"redacted\"; data: string };\n\n/** Convert a Converse reasoning content block to persistence-safe metadata. */\nexport function toReasoningMetadata(block: ContentBlock): BedrockReasoningBlock | undefined {\n const reasoning = block.reasoningContent;\n\n if (!reasoning) {\n return undefined;\n }\n\n if (reasoning.reasoningText) {\n const { text, signature } = reasoning.reasoningText;\n\n if (typeof text !== \"string\") {\n return undefined;\n }\n\n return { type: \"reasoning\", text, ...(typeof signature === \"string\" ? { signature } : {}) };\n }\n\n if (reasoning.redactedContent instanceof Uint8Array) {\n return { type: \"redacted\", data: Buffer.from(reasoning.redactedContent).toString(\"base64\") };\n }\n\n return undefined;\n}\n\n/** Restore one persistence-safe metadata entry to a Converse reasoning block. */\nexport function fromReasoningMetadata(value: unknown): ContentBlock | undefined {\n if (!isRecord(value) || typeof value.type !== \"string\") {\n return undefined;\n }\n\n if (value.type === \"reasoning\" && typeof value.text === \"string\") {\n return {\n reasoningContent: {\n reasoningText: {\n text: value.text,\n ...(typeof value.signature === \"string\" ? { signature: value.signature } : {}),\n },\n },\n };\n }\n\n if (value.type === \"redacted\" && typeof value.data === \"string\" && isBase64(value.data)) {\n return { reasoningContent: { redactedContent: new Uint8Array(Buffer.from(value.data, \"base64\")) } };\n }\n\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/** Require canonical base64, avoiding Buffer's permissive decoding of garbage. */\nfunction isBase64(value: string): boolean {\n if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) {\n return false;\n }\n\n return Buffer.from(value, \"base64\").toString(\"base64\") === value;\n}\n","import { InvalidRequestError, type ContentPart, type Message } from \"@warlock.js/ai\";\nimport type {\n ContentBlock,\n ImageFormat,\n Message as BedrockMessage,\n SystemContentBlock,\n} from \"@aws-sdk/client-bedrock-runtime\";\nimport { fromReasoningMetadata } from \"./reasoning-blocks\";\n\n/**\n * Result of splitting a vendor-neutral `Message[]` for the Bedrock\n * Converse API: system prompts hoist to a separate `SystemContentBlock[]`\n * (Converse has no `\"system\"` role inside `messages`), and the\n * remaining turns map to Bedrock `Message[]`.\n */\nexport type BedrockMessages = {\n system: SystemContentBlock[] | undefined;\n messages: BedrockMessage[];\n};\n\nconst MEDIA_TYPE_TO_FORMAT: Record<string, ImageFormat> = {\n \"image/jpeg\": \"jpeg\",\n \"image/png\": \"png\",\n \"image/gif\": \"gif\",\n \"image/webp\": \"webp\",\n};\n\n/**\n * Convert vendor-neutral `Message[]` into Bedrock Converse's request\n * shape.\n *\n * Converse differs from the OpenAI Chat protocol in three ways this\n * function absorbs:\n *\n * 1. **No `system` role.** System messages become a separate\n * `SystemContentBlock[]` (one `{ text }` block each).\n * 2. **Tool results are `user` turns.** A neutral `tool` message\n * becomes a `user` message carrying a single `toolResult` block.\n * 3. **Tool calls are `toolUse` content blocks.** An assistant message\n * with `toolCalls` becomes an `assistant` message: an optional\n * leading `text` block followed by one `toolUse` block per call.\n *\n * @example\n * const { system, messages } = toBedrockMessages([\n * { role: \"system\", content: \"Be concise.\" },\n * { role: \"user\", content: \"Hi\" },\n * ]);\n */\nexport function toBedrockMessages(messages: Message[]): BedrockMessages {\n const system: SystemContentBlock[] = [];\n const mapped: BedrockMessage[] = [];\n\n for (const message of messages) {\n if (message.role === \"system\") {\n system.push({ text: stringifyContent(message.content) });\n\n continue;\n }\n\n if (message.role === \"tool\") {\n mapped.push({\n role: \"user\",\n content: [\n {\n toolResult: {\n toolUseId: message.toolCallId ?? \"\",\n content: [{ text: stringifyContent(message.content) }],\n },\n },\n ],\n });\n\n continue;\n }\n\n if (message.role === \"assistant\" && message.toolCalls && message.toolCalls.length > 0) {\n const blocks: ContentBlock[] = [];\n const text = stringifyContent(message.content);\n const reasoningBlocks = getReasoningBlocks(message.toolCalls);\n\n blocks.push(...reasoningBlocks);\n\n if (text) {\n blocks.push({ text });\n }\n\n for (const toolCall of message.toolCalls) {\n blocks.push({\n toolUse: {\n toolUseId: toolCall.id,\n name: toolCall.name,\n input: toolCall.input ?? {},\n },\n } as ContentBlock);\n }\n\n mapped.push({ role: \"assistant\", content: blocks });\n\n continue;\n }\n\n if (message.role === \"user\" && Array.isArray(message.content)) {\n mapped.push({\n role: \"user\",\n content: message.content.map(toBedrockContentBlock),\n });\n\n continue;\n }\n\n mapped.push({\n role: message.role === \"assistant\" ? \"assistant\" : \"user\",\n content: [{ text: stringifyContent(message.content) }],\n });\n }\n\n return {\n system: system.length > 0 ? system : undefined,\n messages: mapped,\n };\n}\n\n/**\n * Recover opaque Converse reasoning blocks retained on a prior tool call.\n * Bedrock requires them to be replayed before the assistant's text and\n * tool-use blocks on the follow-up request.\n */\nfunction getReasoningBlocks(toolCalls: NonNullable<Message[\"toolCalls\"]>): ContentBlock[] {\n for (const toolCall of toolCalls) {\n const bedrock = toolCall.providerMetadata?.bedrock;\n\n if (!isRecord(bedrock) || !Array.isArray(bedrock.reasoningBlocks)) {\n continue;\n }\n\n return bedrock.reasoningBlocks.map(fromReasoningMetadata).filter(\n (block): block is ContentBlock => block !== undefined,\n );\n }\n\n return [];\n}\n\n/** Narrow an opaque metadata value to an object without altering its contents. */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * Multipart content is only meaningful on user messages — for any other\n * role collapse a `ContentPart[]` to its concatenated text. Plain\n * strings pass through unchanged.\n */\nfunction stringifyContent(content: string | ContentPart[]): string {\n if (typeof content === \"string\") {\n return content;\n }\n\n return content\n .filter((part): part is { type: \"text\"; text: string } => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\");\n}\n\n/**\n * Map a resolved `ContentPart` to a Bedrock `ContentBlock`. Bedrock's\n * `ImageSource` only accepts raw bytes or an S3 location — there is no\n * remote-URL source. A neutral `{ url }` image therefore cannot be\n * sent and surfaces a typed `InvalidRequestError` upfront rather than\n * a downstream Bedrock validation fault. The agent has already\n * resolved attachments, so this never fetches or reads anything.\n */\nfunction toBedrockContentBlock(part: ContentPart): ContentBlock {\n if (part.type === \"text\") {\n return { text: part.text };\n }\n\n if (\"url\" in part.source) {\n throw new InvalidRequestError(\n \"Bedrock Converse does not support remote-URL sources; supply base64 bytes instead.\",\n );\n }\n\n // PDF → Bedrock `document` content block (A2). Converse accepts a\n // document block with raw bytes; the agent gates this on the model's\n // `pdf` capability before it reaches here.\n if (part.type === \"pdf\") {\n return {\n document: {\n format: \"pdf\",\n name: \"attachment\",\n source: { bytes: Buffer.from(part.source.base64, \"base64\") },\n },\n } as unknown as ContentBlock;\n }\n\n // Bedrock Converse has no audio content block (capability stays false).\n if (part.type === \"audio\") {\n throw new InvalidRequestError(\n \"Bedrock Converse does not support audio attachments.\",\n );\n }\n\n const format = MEDIA_TYPE_TO_FORMAT[part.source.mediaType];\n\n if (!format) {\n throw new InvalidRequestError(\n `Unsupported image media type for Bedrock: \"${part.source.mediaType}\" (expected image/jpeg, image/png, image/gif, or image/webp).`,\n );\n }\n\n return {\n image: {\n format,\n source: { bytes: Buffer.from(part.source.base64, \"base64\") },\n },\n };\n}\n","import { extractJsonSchema, type ToolConfig } from \"@warlock.js/ai\";\nimport type { Tool, ToolConfiguration, ToolInputSchema } from \"@aws-sdk/client-bedrock-runtime\";\n\n/**\n * Convert vendor-neutral `ToolConfig[]` into Bedrock Converse's\n * `ToolConfiguration`. Each tool becomes a `toolSpec` with a JSON\n * `inputSchema`. Bedrock requires the schema root to be an object —\n * a non-object extraction degrades to a parameterless object schema\n * so registration never fails.\n *\n * Returns `undefined` when there are no tools so the caller can omit\n * `toolConfig` from the request entirely (Bedrock rejects an empty\n * `tools` array).\n *\n * @example\n * const toolConfig = toBedrockToolConfig([weatherTool]);\n * await client.send(new ConverseCommand({ modelId, messages, toolConfig }));\n */\nexport function toBedrockToolConfig(\n tools: ToolConfig<unknown, unknown>[] | undefined,\n): ToolConfiguration | undefined {\n if (!tools || tools.length === 0) {\n return undefined;\n }\n\n return {\n tools: tools.map(\n (tool): Tool => ({\n toolSpec: {\n name: tool.name,\n description: tool.description,\n inputSchema: { json: toJsonSchema(tool.input) } as ToolInputSchema,\n },\n }),\n ),\n };\n}\n\n/**\n * Resolve a tool's input schema to a JSON-Schema object. Bedrock's\n * `ToolInputSchema.json` requires an object root; anything else (or a\n * failed extraction) degrades to a parameterless object so the tool\n * still registers.\n */\nfunction toJsonSchema(input: ToolConfig<unknown, unknown>[\"input\"]): Record<string, unknown> {\n const schema = extractJsonSchema(input);\n\n if (schema && schema.type === \"object\") {\n return schema;\n }\n\n return { type: \"object\" };\n}\n","import {\n AIError,\n ContextLengthExceededError,\n InvalidRequestError,\n ProviderAuthError,\n ProviderError,\n ProviderRateLimitError,\n ProviderTimeoutError,\n QuotaExceededError,\n} from \"@warlock.js/ai\";\n\n/**\n * Raw-error fields the wrapper reads off an AWS SDK exception. Every\n * Bedrock error is a Smithy `__BaseException` with a stable `name`\n * (`\"ThrottlingException\"`, `\"ValidationException\"`, …) and a\n * `$metadata` carrying `httpStatusCode` + `requestId`. We duck-type\n * because retries and proxies sometimes flatten the prototype chain.\n */\ntype BedrockErrorShape = {\n name?: string;\n message?: string;\n httpStatusCode?: number;\n requestId?: string;\n code?: string;\n};\n\nconst TIMEOUT_NAMES = new Set([\n \"ModelTimeoutException\",\n \"TimeoutError\",\n \"RequestTimeout\",\n \"RequestTimeoutException\",\n]);\n\n/**\n * Wrap any thrown value caught inside the Bedrock adapter into the\n * appropriate `@warlock.js/ai` `AIError` subclass.\n *\n * **Dispatch strategy.** AWS errors carry no provider machine `code`;\n * the stable identifier is the Smithy exception `name`. Dispatch keys\n * on `name`, falls back to `$metadata.httpStatusCode` when the name is\n * missing (flattened/proxied errors). `ValidationException` is split:\n * the \"input is too long / exceeds context window\" phrasing maps to\n * `ContextLengthExceededError`, everything else to\n * `InvalidRequestError`.\n *\n * `AIError` instances pass through unchanged so `catch/throw wrap(e)`\n * pipelines never double-wrap.\n *\n * @example\n * try {\n * return await this.client.send(new ConverseCommand(...));\n * } catch (thrown) {\n * throw wrapBedrockError(thrown);\n * }\n */\nexport function wrapBedrockError(thrown: unknown): AIError {\n if (thrown instanceof AIError) {\n return thrown;\n }\n\n const shape = toShape(thrown);\n const context = buildContext(shape);\n const message = shape.message ?? (thrown instanceof Error ? thrown.message : String(thrown));\n\n if (isTimeout(shape)) {\n return new ProviderTimeoutError(message, { cause: thrown, context });\n }\n\n if (shape.name === \"AccessDeniedException\" || shape.httpStatusCode === 403) {\n return new ProviderAuthError(message, { cause: thrown, context });\n }\n\n if (shape.httpStatusCode === 401) {\n return new ProviderAuthError(message, { cause: thrown, context });\n }\n\n if (shape.name === \"ServiceQuotaExceededException\") {\n return new QuotaExceededError(message, { cause: thrown, context });\n }\n\n if (shape.name === \"ThrottlingException\" || shape.httpStatusCode === 429) {\n return new ProviderRateLimitError(message, { cause: thrown, context });\n }\n\n if (shape.name === \"ValidationException\") {\n if (/too long|context window|maximum context|exceeds the maximum/i.test(message)) {\n return new ContextLengthExceededError(message, { cause: thrown, context });\n }\n\n return new InvalidRequestError(message, { cause: thrown, context });\n }\n\n if (\n shape.name === \"ResourceNotFoundException\" ||\n shape.name === \"ConflictException\" ||\n isClientStatus(shape.httpStatusCode)\n ) {\n return new InvalidRequestError(message, { cause: thrown, context });\n }\n\n return new ProviderError(message, { cause: thrown, context });\n}\n\n/**\n * Read the raw error shape without depending on `instanceof`. AWS\n * exceptions expose `$metadata`; plain/proxied errors may carry\n * `status` / `code` instead.\n */\nfunction toShape(thrown: unknown): BedrockErrorShape {\n if (typeof thrown !== \"object\" || thrown === null) {\n return {};\n }\n\n const raw = thrown as Record<string, unknown>;\n const metadata = raw.$metadata as { httpStatusCode?: number; requestId?: string } | undefined;\n\n return {\n name: typeof raw.name === \"string\" ? raw.name : undefined,\n message: typeof raw.message === \"string\" ? raw.message : undefined,\n httpStatusCode:\n metadata && typeof metadata.httpStatusCode === \"number\"\n ? metadata.httpStatusCode\n : typeof raw.status === \"number\"\n ? (raw.status as number)\n : undefined,\n requestId: metadata && typeof metadata.requestId === \"string\" ? metadata.requestId : undefined,\n code: typeof raw.code === \"string\" ? raw.code : undefined,\n };\n}\n\n/**\n * Decide whether the error is a timeout. Bedrock surfaces\n * `ModelTimeoutException`; the AWS transport layer surfaces\n * `TimeoutError` / `ETIMEDOUT` / `ECONNABORTED`.\n */\nfunction isTimeout(shape: BedrockErrorShape): boolean {\n if (shape.name && TIMEOUT_NAMES.has(shape.name)) {\n return true;\n }\n\n return shape.code === \"ETIMEDOUT\" || shape.code === \"ECONNABORTED\";\n}\n\n/** True for HTTP 4xx — a client-side request problem, not a server fault. */\nfunction isClientStatus(status: number | undefined): boolean {\n return typeof status === \"number\" && status >= 400 && status < 500;\n}\n\n/**\n * Attach the raw diagnostic fields to `error.context`. The Smithy\n * exception `name` is the closest thing Bedrock has to a stable error\n * code, so it lands on `context.code`.\n */\nfunction buildContext(shape: BedrockErrorShape): Record<string, unknown> {\n const context: Record<string, unknown> = {};\n\n if (shape.httpStatusCode !== undefined) {\n context.status = shape.httpStatusCode;\n }\n\n if (shape.name) {\n context.code = shape.name;\n }\n\n if (shape.requestId) {\n context.requestId = shape.requestId;\n }\n\n return context;\n}\n","import {\n type EmbeddingBatchResult,\n type EmbeddingResult,\n type EmbeddingUsage,\n type EmbedderContract,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport { InvokeModelCommand, type BedrockRuntimeClient } from \"@aws-sdk/client-bedrock-runtime\";\nimport type { BedrockEmbedderConfig } from \"./config.type\";\nimport { wrapBedrockError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.bedrock\";\n\n/** Shape of the Amazon Titan Text Embeddings response body. */\ntype TitanEmbeddingResponse = {\n embedding: number[];\n inputTextTokenCount: number;\n};\n\n/**\n * Bedrock-backed implementation of `EmbedderContract`, targeting the\n * Amazon Titan Text Embeddings family\n * (`amazon.titan-embed-text-v2:0` / v1) via `InvokeModel`.\n *\n * **Role.** Converts text into floating-point vectors. Standalone\n * primitive — unrelated to Converse / tools / the agent loop.\n *\n * **Single-input only upstream.** Titan's `InvokeModel` body accepts\n * one `inputText` per call — there is no batch endpoint. `embedMany`\n * therefore issues one request per input sequentially and aggregates\n * token usage. This is a deliberate, documented trade-off: a real\n * batch API does not exist for Titan on Bedrock, so the alternative\n * (failing `embedMany`) would be worse. Cohere embeddings on Bedrock\n * *do* batch but use an incompatible body shape — out of scope; use\n * the OpenAI adapter or a future Cohere adapter when batch throughput\n * matters.\n *\n * **Dimensions.** When no `dimensions` override is given,\n * `this.dimensions` starts at `0` and is populated from the first\n * response's vector length, then cached. Passing `dimensions` forwards\n * Titan v2's truncation hint (256 / 512 / 1024) and sets the initial\n * value immediately.\n *\n * @example\n * const embedder = new BedrockEmbedder(client, { name: \"amazon.titan-embed-text-v2:0\" });\n * const { vector } = await embedder.embed(\"Hello world\");\n * const { vectors } = await embedder.embedMany([\"doc 1\", \"doc 2\"]);\n */\nexport class BedrockEmbedder implements EmbedderContract {\n public readonly name: string;\n public readonly provider: string;\n public dimensions: number;\n\n private readonly client: BedrockRuntimeClient;\n private readonly configuredDimensions: number | undefined;\n private readonly logger: Logger = log;\n\n public constructor(\n client: BedrockRuntimeClient,\n config: BedrockEmbedderConfig,\n provider: string = \"bedrock\",\n ) {\n this.client = client;\n this.name = config.name;\n this.provider = provider;\n this.configuredDimensions = config.dimensions;\n this.dimensions = config.dimensions ?? 0;\n }\n\n public async embed(input: string): Promise<EmbeddingResult> {\n const { vector, tokens } = await this.invoke(input);\n\n return {\n vector,\n dimensions: this.dimensions,\n usage: { promptTokens: tokens, totalTokens: tokens },\n };\n }\n\n public async embedMany(inputs: string[]): Promise<EmbeddingBatchResult> {\n const vectors: number[][] = [];\n let tokens = 0;\n\n for (const input of inputs) {\n const result = await this.invoke(input);\n\n vectors.push(result.vector);\n tokens += result.tokens;\n }\n\n const usage: EmbeddingUsage = { promptTokens: tokens, totalTokens: tokens };\n\n return { vectors, dimensions: this.dimensions, usage };\n }\n\n /**\n * Issue a single Titan `InvokeModel` embedding request: encode the\n * JSON body, send, wrap provider errors, decode the response, and\n * cache `dimensions` on the first successful call.\n */\n private async invoke(input: string): Promise<{ vector: number[]; tokens: number }> {\n this.logger.debug(LOG_MODULE, \"embedder.request\", \"InvokeModel embeddings\", {\n model: this.name,\n });\n\n const body = JSON.stringify({\n inputText: input,\n ...(this.configuredDimensions !== undefined\n ? { dimensions: this.configuredDimensions }\n : {}),\n });\n\n let raw;\n\n try {\n raw = await this.client.send(\n new InvokeModelCommand({\n modelId: this.name,\n contentType: \"application/json\",\n accept: \"application/json\",\n body: new TextEncoder().encode(body),\n }),\n );\n } catch (thrown) {\n const wrapped = wrapBedrockError(thrown);\n\n this.logger.error(LOG_MODULE, \"embedder.error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n const decoded = JSON.parse(new TextDecoder().decode(raw.body)) as TitanEmbeddingResponse;\n\n if (this.dimensions === 0) {\n this.dimensions = decoded.embedding.length;\n }\n\n this.logger.debug(LOG_MODULE, \"embedder.response\", \"InvokeModel embeddings returned\", {\n dimensions: decoded.embedding.length,\n tokens: decoded.inputTextTokenCount,\n });\n\n return { vector: decoded.embedding, tokens: decoded.inputTextTokenCount };\n }\n}\n","/**\n * Cost-truth capability inference for Bedrock Converse model ids.\n *\n * Bedrock model ids are provider-prefixed and version-suffixed\n * (`anthropic.claude-3-7-sonnet-20250219-v1:0`, `us.amazon.nova-pro-v1:0`),\n * so — exactly like `known-vision-models.ts` — a lowercase substring scan\n * is the only robust check across cross-region inference-profile prefixes\n * (`us.`, `eu.`, `apac.`) and date/version tags.\n *\n * Each predicate answers a single `ModelCapabilities` flag the agent reads\n * to decide whether to forward a cost-truth option (`reasoning`,\n * `cacheControl`) or up-front-reject an attachment (`pdf`). Unknown ids\n * default to `false` so an unsupported request fails fast with a clear\n * capability error instead of an opaque Bedrock `ValidationException`.\n * Every inference is overridable per-model via `bedrock.model({ name, … })`.\n */\n\n/**\n * Families that expose Anthropic-style extended thinking on Bedrock\n * Converse via `additionalModelRequestFields.thinking`. Only Claude 3.7\n * and the Claude 4 line (Sonnet / Opus / Haiku) support a configurable\n * thinking budget; earlier Claude, Nova, Llama, Mistral and Cohere do\n * not, so they are intentionally absent.\n */\nconst REASONING_CAPABLE_SUBSTRINGS = [\n \"claude-3-7\",\n \"claude-sonnet-4\",\n \"claude-opus-4\",\n \"claude-haiku-4\",\n];\n\n/**\n * Families that honor Converse `cachePoint` prompt-cache breakpoints.\n * Anthropic Claude 3.5+ / 3.7 / 4 and the Amazon Nova line support\n * cache points; text-only legacy families do not.\n */\nconst PROMPT_CACHING_CAPABLE_SUBSTRINGS = [\n \"claude-3-5\",\n \"claude-3-7\",\n \"claude-sonnet-4\",\n \"claude-opus-4\",\n \"claude-haiku-4\",\n \"nova-lite\",\n \"nova-pro\",\n \"nova-premier\",\n \"nova-micro\",\n];\n\n/**\n * Families that accept Converse `document` content blocks (PDF / docx /\n * txt input). The multimodal Claude 3+ and Nova families support\n * document blocks; the substring set mirrors the vision-capable list\n * minus the image-only Llama entries (Llama on Bedrock takes images but\n * not document blocks via Converse).\n */\nconst PDF_CAPABLE_SUBSTRINGS = [\n \"claude-3\",\n \"claude-sonnet-4\",\n \"claude-opus-4\",\n \"claude-haiku-4\",\n \"nova-lite\",\n \"nova-pro\",\n \"nova-premier\",\n];\n\nfunction matchesAny(modelId: string, fragments: string[]): boolean {\n const normalized = modelId.toLowerCase();\n\n return fragments.some((fragment) => normalized.includes(fragment));\n}\n\n/**\n * Infer whether a Bedrock model id exposes extended-thinking / reasoning\n * (Claude 3.7 + Claude 4). When true the adapter forwards\n * `ModelCallOptions.reasoning` as Converse\n * `additionalModelRequestFields.thinking`.\n *\n * @example\n * inferReasoningCapability(\"anthropic.claude-3-7-sonnet-20250219-v1:0\"); // → true\n * inferReasoningCapability(\"us.amazon.nova-pro-v1:0\"); // → false\n */\nexport function inferReasoningCapability(modelId: string): boolean {\n return matchesAny(modelId, REASONING_CAPABLE_SUBSTRINGS);\n}\n\n/**\n * Infer whether a Bedrock model id honors Converse `cachePoint`\n * breakpoints (Claude 3.5+ / Nova). When true the adapter both maps\n * `ModelCallOptions.cacheControl` write breakpoints to cache points and\n * reports `Usage.cachedTokens` / `Usage.cacheWriteTokens`.\n *\n * @example\n * inferPromptCachingCapability(\"us.amazon.nova-pro-v1:0\"); // → true\n * inferPromptCachingCapability(\"meta.llama3-1-8b-instruct-v1:0\"); // → false\n */\nexport function inferPromptCachingCapability(modelId: string): boolean {\n return matchesAny(modelId, PROMPT_CACHING_CAPABLE_SUBSTRINGS);\n}\n\n/**\n * Infer whether a Bedrock model id accepts Converse `document` content\n * blocks (PDF / document input — Claude 3+ / Nova). When false the agent\n * rejects a PDF attachment up front instead of dropping it at the wire.\n *\n * @example\n * inferPdfCapability(\"anthropic.claude-3-5-sonnet-20240620-v1:0\"); // → true\n * inferPdfCapability(\"meta.llama3-2-90b-instruct-v1:0\"); // → false\n */\nexport function inferPdfCapability(modelId: string): boolean {\n return matchesAny(modelId, PDF_CAPABLE_SUBSTRINGS);\n}\n","/**\n * Substrings that identify Bedrock model ids whose family accepts image\n * input on the Converse API.\n *\n * Bedrock model ids are provider-prefixed and version-suffixed\n * (`anthropic.claude-3-5-sonnet-20240620-v1:0`, `us.amazon.nova-pro-v1:0`,\n * `meta.llama3-2-90b-instruct-v1:0`), so a substring match is the only\n * robust check across the cross-region inference-profile prefixes\n * (`us.`, `eu.`, `apac.`) and date/version tags.\n *\n * Multimodal families covered: Anthropic Claude 3 / 3.5 / 3.7 / 4,\n * Amazon Nova Lite/Pro/Premier, Meta Llama 3.2 (11B/90B) and Llama 4.\n * Text-only families (Llama 3/3.1, Titan Text, Mistral 7B, Cohere\n * Command) are intentionally absent. Override per-model via\n * `bedrock.model({ name, vision: true | false })`.\n */\nconst VISION_CAPABLE_SUBSTRINGS = [\n \"claude-3\",\n \"claude-sonnet-4\",\n \"claude-opus-4\",\n \"claude-haiku-4\",\n \"nova-lite\",\n \"nova-pro\",\n \"nova-premier\",\n \"llama3-2-11b\",\n \"llama3-2-90b\",\n \"llama4\",\n];\n\n/**\n * Infer whether a Bedrock model id supports vision based on the known\n * multimodal-family substrings. Unknown ids default to `false` so that\n * passing an image attachment to an unsupported model surfaces a clear,\n * agent-side capability error instead of an opaque Bedrock validation\n * fault.\n *\n * @example\n * inferVisionCapability(\"anthropic.claude-3-5-sonnet-20240620-v1:0\"); // → true\n * inferVisionCapability(\"us.amazon.nova-pro-v1:0\"); // → true\n * inferVisionCapability(\"meta.llama3-1-8b-instruct-v1:0\"); // → false\n * inferVisionCapability(\"amazon.titan-text-express-v1\"); // → false\n */\nexport function inferVisionCapability(modelId: string): boolean {\n const normalized = modelId.toLowerCase();\n\n return VISION_CAPABLE_SUBSTRINGS.some((fragment) => normalized.includes(fragment));\n}\n","import {\n safeJsonParse,\n type Message,\n type ModelCallOptions,\n type ModelCapabilities,\n type ModelContract,\n type ModelPricing,\n type ModelResponse,\n type ModelStreamChunk,\n type ModelToolCallRequest,\n type Usage,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport {\n ConverseCommand,\n ConverseStreamCommand,\n type BedrockRuntimeClient,\n type ContentBlock,\n type ConverseRequest,\n type TokenUsage,\n} from \"@aws-sdk/client-bedrock-runtime\";\nimport type { BedrockModelConfig } from \"./config.type\";\nimport {\n inferPdfCapability,\n inferPromptCachingCapability,\n inferReasoningCapability,\n} from \"./known-capabilities\";\nimport { inferVisionCapability } from \"./known-vision-models\";\nimport { toReasoningMetadata, type BedrockReasoningBlock } from \"./utils/reasoning-blocks\";\nimport { mapStopReason, toBedrockMessages, toBedrockToolConfig, wrapBedrockError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.bedrock\";\n\n/** Default output allowance added to a thinking budget when maxTokens is unset. */\nconst DEFAULT_MAX_TOKENS = 4096;\nconst MIN_THINKING_BUDGET = 1024;\n\n/**\n * Conventional extended-thinking token budgets for the neutral\n * `reasoning.effort` levels, used when the caller asks for an effort\n * tier without naming an explicit `reasoning.maxTokens` budget. Mirrors\n * the low / medium / high spread other reasoning adapters expose so the\n * vendor-neutral option behaves consistently across providers.\n */\nconst EFFORT_THINKING_BUDGET: Record<string, number | undefined> = {\n low: 1024,\n medium: 4096,\n high: 16384,\n};\n\n/**\n * Bedrock-backed implementation of `ModelContract`.\n *\n * **Role.** The provider-facing bridge between the vendor-neutral\n * `@warlock.js/ai` agent runtime and AWS Bedrock's Converse /\n * ConverseStream API. Converse is the model-agnostic surface — one\n * wire mapping covers every Bedrock-hosted family (Anthropic Claude,\n * Amazon Nova, Meta Llama, Mistral, Cohere) instead of per-family\n * `InvokeModel` body shapes.\n *\n * **Responsibility.**\n * - Owns: a long-lived `BedrockRuntimeClient` + frozen `ModelConfig`\n * (modelId, temperature, maxTokens) used as per-call defaults.\n * - Owns: translating vendor-neutral `Message[]` / `ToolConfig[]` into\n * Converse shapes (system hoisting, `toolUse` / `toolResult` blocks,\n * image bytes) on the way out, and Converse's content-block response\n * (text, tool calls, stop reason, token usage) back into the neutral\n * shapes on the way in.\n * - Does NOT own: dispatching tools, looping, history, retries — those\n * are agent concerns. The model is a per-call protocol adapter.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across calls\"): the AWS client is heavy to construct and reused for\n * the SDK's lifetime.\n *\n * @example\n * import { BedrockRuntimeClient } from \"@aws-sdk/client-bedrock-runtime\";\n * const client = new BedrockRuntimeClient({ region: \"us-east-1\" });\n * const model = new BedrockModel(client, {\n * name: \"anthropic.claude-sonnet-4-5-20250929-v1:0\",\n * });\n *\n * const myAgent = agent({ model, tools: [searchTool] });\n * const result = await myAgent.execute(\"Summarize today's news.\");\n */\nexport class BedrockModel implements ModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly capabilities: ModelCapabilities;\n public readonly pricing?: ModelPricing;\n\n private readonly client: BedrockRuntimeClient;\n private readonly config: BedrockModelConfig;\n private readonly logger: Logger = log;\n\n public constructor(\n client: BedrockRuntimeClient,\n config: BedrockModelConfig,\n provider: string = \"bedrock\",\n ) {\n this.client = client;\n this.config = config;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n this.capabilities = {\n structuredOutput: config.structuredOutput ?? true,\n vision: config.vision ?? inferVisionCapability(config.name),\n reasoning: config.reasoning ?? inferReasoningCapability(config.name),\n promptCaching: config.promptCaching ?? inferPromptCachingCapability(config.name),\n pdf: config.pdf ?? inferPdfCapability(config.name),\n audio: config.audio ?? false,\n };\n }\n\n /**\n * Single-shot completion via the Converse API. Sends the full\n * message list, waits for the terminal response, and reshapes it\n * into a vendor-neutral `ModelResponse`. Per-call `options` override\n * the instance defaults for this call only.\n */\n public async complete(messages: Message[], options?: ModelCallOptions): Promise<ModelResponse> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting Converse call\", {\n model: this.name,\n messageCount: messages.length,\n streaming: false,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response;\n\n try {\n response = await this.client.send(\n new ConverseCommand(this.buildRequest(messages, options)),\n options?.signal ? { abortSignal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const blocks = response.output?.message?.content ?? [];\n const finishReason = mapStopReason(response.stopReason);\n const usage = this.extractUsage(response.usage);\n const toolCalls = this.extractToolCalls(blocks);\n\n this.logger.debug(LOG_MODULE, \"response\", \"Converse call succeeded\", { finishReason, usage });\n\n return {\n content: this.extractText(blocks),\n finishReason,\n usage,\n toolCalls,\n };\n }\n\n /**\n * Incremental streaming completion via ConverseStream. Yields neutral\n * `ModelStreamChunk`s — `delta` for text, `tool-call` once a\n * `toolUse` block's accumulated input JSON is complete, and a\n * terminal `done` with the final finish reason + usage totals.\n */\n public async *stream(\n messages: Message[],\n options?: ModelCallOptions,\n ): AsyncIterable<ModelStreamChunk> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting ConverseStream call\", {\n model: this.name,\n messageCount: messages.length,\n streaming: true,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response;\n\n try {\n response = await this.client.send(\n new ConverseStreamCommand(this.buildRequest(messages, options)),\n options?.signal ? { abortSignal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n let rawStopReason: string | undefined;\n const usage: Usage = { input: 0, output: 0, total: 0 };\n const toolBlocks = new Map<number, { id: string; name: string; json: string }>();\n const reasoningBlocks = new Map<number, { text?: string; signature?: string; redactedContent?: Uint8Array }>();\n const completedReasoningBlocks: BedrockReasoningBlock[] = [];\n\n try {\n for await (const event of response.stream ?? []) {\n if (event.contentBlockStart?.start?.toolUse) {\n const start = event.contentBlockStart.start.toolUse;\n\n toolBlocks.set(event.contentBlockStart.contentBlockIndex ?? 0, {\n id: start.toolUseId ?? \"\",\n name: start.name ?? \"\",\n json: \"\",\n });\n\n continue;\n }\n\n if (event.contentBlockDelta?.delta) {\n const delta = event.contentBlockDelta.delta;\n\n if (delta.text) {\n yield { type: \"delta\", content: delta.text };\n } else if (delta.toolUse) {\n const accumulator = toolBlocks.get(event.contentBlockDelta.contentBlockIndex ?? 0);\n\n if (accumulator) {\n accumulator.json += delta.toolUse.input ?? \"\";\n }\n } else if (delta.reasoningContent) {\n const index = event.contentBlockDelta.contentBlockIndex ?? 0;\n const accumulator = reasoningBlocks.get(index) ?? {};\n\n if (delta.reasoningContent.text !== undefined) {\n accumulator.text = (accumulator.text ?? \"\") + delta.reasoningContent.text;\n }\n\n if (delta.reasoningContent.signature !== undefined) {\n accumulator.signature = delta.reasoningContent.signature;\n }\n\n if (delta.reasoningContent.redactedContent !== undefined) {\n accumulator.redactedContent = delta.reasoningContent.redactedContent;\n }\n\n reasoningBlocks.set(index, accumulator);\n }\n\n continue;\n }\n\n if (event.contentBlockStop) {\n const index = event.contentBlockStop.contentBlockIndex ?? 0;\n const accumulator = toolBlocks.get(index);\n\n if (accumulator) {\n yield {\n type: \"tool-call\",\n id: accumulator.id,\n name: accumulator.name,\n input: safeJsonParse<Record<string, unknown>>(accumulator.json, {}),\n ...(completedReasoningBlocks.length > 0\n ? { providerMetadata: { bedrock: { reasoningBlocks: completedReasoningBlocks } } }\n : {}),\n };\n\n toolBlocks.delete(index);\n }\n\n const reasoningAccumulator = reasoningBlocks.get(index);\n\n if (reasoningAccumulator) {\n const reasoningBlock = this.toReasoningMetadata(reasoningAccumulator);\n\n if (reasoningBlock) {\n completedReasoningBlocks.push(reasoningBlock);\n }\n reasoningBlocks.delete(index);\n }\n\n continue;\n }\n\n if (event.messageStop) {\n rawStopReason = event.messageStop.stopReason;\n }\n\n if (event.metadata?.usage) {\n const raw = event.metadata.usage;\n\n usage.input = raw.inputTokens ?? 0;\n usage.output = raw.outputTokens ?? 0;\n usage.total = raw.totalTokens ?? usage.input + usage.output;\n\n if (raw.cacheReadInputTokens && raw.cacheReadInputTokens > 0) {\n usage.cachedTokens = raw.cacheReadInputTokens;\n }\n\n if (raw.cacheWriteInputTokens && raw.cacheWriteInputTokens > 0) {\n usage.cacheWriteTokens = raw.cacheWriteInputTokens;\n }\n }\n }\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const finishReason = mapStopReason(rawStopReason);\n\n this.logger.debug(LOG_MODULE, \"response\", \"ConverseStream call succeeded\", {\n finishReason,\n usage,\n });\n\n yield { type: \"done\", finishReason, usage };\n }\n\n /**\n * Assemble the Converse request shared by `complete()` and\n * `stream()` (both command shapes take the same input). Hoists the\n * system prompt, maps inference params, and conditionally attaches\n * tools and native structured output.\n */\n private buildRequest(\n messages: Message[],\n options: ModelCallOptions | undefined,\n ): ConverseRequest {\n const { system, messages: bedrockMessages } = toBedrockMessages(messages);\n const thinkingBudget = this.resolveThinkingBudget(options?.reasoning);\n const configuredMaxTokens = options?.maxTokens ?? this.config.maxTokens;\n const maxTokens = this.resolveMaxTokens(configuredMaxTokens, thinkingBudget);\n const temperature = options?.temperature ?? this.config.temperature;\n const cachedMessages = this.applyCacheBreakpoints(bedrockMessages, options?.cacheControl);\n\n return {\n modelId: this.name,\n messages: cachedMessages,\n ...(system ? { system } : {}),\n inferenceConfig: {\n ...(maxTokens !== undefined ? { maxTokens } : {}),\n ...(temperature !== undefined ? { temperature } : {}),\n },\n ...this.buildToolConfig(options?.tools),\n ...this.buildOutputConfig(options?.responseSchema),\n ...this.buildReasoningConfig(thinkingBudget),\n };\n }\n\n /**\n * Append a Converse `cachePoint` block to the LAST message when the\n * caller supplies a `cacheControl` write breakpoint and the model is\n * `promptCaching`-capable. A cache point tells Bedrock to cache the\n * whole prefix up to that block, so subsequent calls reusing the same\n * prefix bill the cached portion at the discounted read rate\n * (surfaced as `Usage.cachedTokens`). No-ops gracefully when caching\n * is unsupported, no breakpoint was requested, or there are no\n * messages to mark — Bedrock then prices the call normally.\n *\n * Bedrock only honors `CachePointType.DEFAULT`; the neutral\n * `breakpoints` count is a presence hint (one trailing breakpoint is\n * the only placement Converse supports without manual block surgery),\n * so any positive value marks the trailing message.\n */\n private applyCacheBreakpoints(\n messages: ConverseRequest[\"messages\"],\n cacheControl: ModelCallOptions[\"cacheControl\"],\n ): ConverseRequest[\"messages\"] {\n const breakpoints = cacheControl?.breakpoints ?? 0;\n\n if (!this.capabilities.promptCaching || breakpoints <= 0 || !messages || messages.length === 0) {\n return messages;\n }\n\n const last = messages.length - 1;\n const lastMessage = messages[last];\n\n return [\n ...messages.slice(0, last),\n {\n ...lastMessage,\n content: [...(lastMessage.content ?? []), { cachePoint: { type: \"default\" } }],\n },\n ];\n }\n\n /**\n * Translate the neutral `reasoning` option into Claude-on-Bedrock's\n * extended-thinking control, carried in Converse's escape hatch\n * `additionalModelRequestFields.thinking`. Emitted only when the model\n * is `reasoning`-capable and a budget can be resolved — `maxTokens`\n * (explicit thinking budget) wins, otherwise `effort` maps to a\n * conventional token budget so callers can opt in without picking a\n * number. Returns an empty object (no-op) for non-reasoning models or\n * when no reasoning option was supplied, so unsupported params never\n * reach the wire.\n */\n private resolveThinkingBudget(reasoning: ModelCallOptions[\"reasoning\"]): number | undefined {\n if (!this.capabilities.reasoning || !reasoning || reasoning.effort === \"none\") {\n return undefined;\n }\n\n const budgetTokens = reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[reasoning.effort ?? \"\"];\n\n if (budgetTokens !== undefined && budgetTokens < MIN_THINKING_BUDGET) {\n throw new Error(`thinking budget (${budgetTokens}) must be at least ${MIN_THINKING_BUDGET}`);\n }\n\n return budgetTokens;\n }\n\n /** Resolve Bedrock's output ceiling while reserving room for extended thinking. */\n private resolveMaxTokens(maxTokens: number | undefined, thinkingBudget: number | undefined): number | undefined {\n if (thinkingBudget === undefined) {\n return maxTokens;\n }\n\n if (maxTokens === undefined) {\n return thinkingBudget + DEFAULT_MAX_TOKENS;\n }\n\n if (maxTokens <= thinkingBudget) {\n throw new Error(`maxTokens (${maxTokens}) must be greater than thinking budget (${thinkingBudget})`);\n }\n\n return maxTokens;\n }\n\n private buildReasoningConfig(\n budgetTokens: number | undefined,\n ): Pick<ConverseRequest, \"additionalModelRequestFields\"> {\n if (budgetTokens === undefined) {\n return {};\n }\n\n return {\n additionalModelRequestFields: {\n thinking: { type: \"enabled\", budget_tokens: budgetTokens },\n },\n };\n }\n\n /**\n * Spread-friendly tool fragment. Returns an empty object when no\n * tools were supplied (Bedrock rejects an empty `tools` array).\n */\n private buildToolConfig(tools: ModelCallOptions[\"tools\"]): Pick<ConverseRequest, \"toolConfig\"> {\n const toolConfig = toBedrockToolConfig(tools);\n\n return toolConfig ? { toolConfig } : {};\n }\n\n /**\n * Translate the neutral `responseSchema` into Converse's native\n * `outputConfig.textFormat` (JSON-schema structured output). Bedrock\n * requires the schema as a stringified JSON document and only\n * accepts an object root. Emitted only when the model is\n * `structuredOutput`-capable and the schema is an object — otherwise\n * the agent's soft system-prompt hint + client-side `validate()`\n * carry shape (same degradation philosophy as the OpenAI adapter).\n */\n private buildOutputConfig(\n responseSchema: Record<string, unknown> | undefined,\n ): Pick<ConverseRequest, \"outputConfig\"> {\n if (!responseSchema || !this.capabilities.structuredOutput) {\n return {};\n }\n\n if (responseSchema.type !== \"object\" || typeof responseSchema.properties !== \"object\") {\n return {};\n }\n\n return {\n outputConfig: {\n textFormat: {\n type: \"json_schema\",\n structure: {\n jsonSchema: { name: \"response\", schema: JSON.stringify(responseSchema) },\n },\n },\n },\n };\n }\n\n /**\n * Concatenate every `text` content block into the single neutral\n * `content` string. `toolUse` and other block types are surfaced\n * separately via `extractToolCalls`.\n */\n private extractText(blocks: ContentBlock[]): string {\n return blocks\n .map((block) => (\"text\" in block && typeof block.text === \"string\" ? block.text : \"\"))\n .join(\"\");\n }\n\n /**\n * Reshape Converse `toolUse` content blocks into the neutral\n * `ModelToolCallRequest[]`. Returns `undefined` when no tools were\n * requested so callers can branch on presence.\n */\n private extractToolCalls(blocks: ContentBlock[]): ModelToolCallRequest[] | undefined {\n const toolCalls: ModelToolCallRequest[] = [];\n const reasoningBlocks = blocks.map(toReasoningMetadata).filter(\n (block): block is BedrockReasoningBlock => block !== undefined,\n );\n\n for (const block of blocks) {\n if (\"toolUse\" in block && block.toolUse) {\n toolCalls.push({\n id: block.toolUse.toolUseId ?? \"\",\n name: block.toolUse.name ?? \"\",\n input: (block.toolUse.input ?? {}) as Record<string, unknown>,\n ...(reasoningBlocks.length > 0\n ? { providerMetadata: { bedrock: { reasoningBlocks } } }\n : {}),\n });\n }\n }\n\n return toolCalls.length > 0 ? toolCalls : undefined;\n }\n\n /** Encode accumulated stream deltas as persistence-safe reasoning metadata. */\n private toReasoningMetadata(block: {\n text?: string;\n signature?: string;\n redactedContent?: Uint8Array;\n }): BedrockReasoningBlock | undefined {\n if (block.redactedContent !== undefined) {\n return toReasoningMetadata({ reasoningContent: { redactedContent: block.redactedContent } });\n }\n\n return toReasoningMetadata({\n reasoningContent: {\n reasoningText: { text: block.text ?? \"\", ...(block.signature ? { signature: block.signature } : {}) },\n },\n });\n }\n\n /**\n * Normalize Converse's `TokenUsage` into the neutral `Usage` shape.\n * Bedrock supplies a pre-summed `totalTokens`; cache-read and\n * cache-write tokens are surfaced as `cachedTokens` /\n * `cacheWriteTokens` only when non-zero so callers can price the\n * discounted read rate and the one-time write cost separately.\n * Bedrock's Converse `TokenUsage` carries no reasoning-token channel,\n * so `Usage.reasoningTokens` is intentionally left unset here.\n */\n private extractUsage(raw: TokenUsage | undefined): Usage {\n if (!raw) {\n return { input: 0, output: 0, total: 0 };\n }\n\n const input = raw.inputTokens ?? 0;\n const output = raw.outputTokens ?? 0;\n const cached = raw.cacheReadInputTokens;\n const cacheWrite = raw.cacheWriteInputTokens;\n\n return {\n input,\n output,\n total: raw.totalTokens ?? input + output,\n ...(cached && cached > 0 ? { cachedTokens: cached } : {}),\n ...(cacheWrite && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {}),\n };\n }\n\n /**\n * Wrap a thrown provider error into the typed `AIError` hierarchy\n * and emit the standard error log line before it propagates. Shared\n * by every catch site so the log shape stays identical.\n */\n private logAndWrap(thrown: unknown) {\n const wrapped = wrapBedrockError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n return wrapped;\n }\n}\n","import { BedrockRuntimeClient } from \"@aws-sdk/client-bedrock-runtime\";\nimport type {\n EmbedderContract,\n ModelContract,\n ModelPricing,\n SDKAdapterContract,\n} from \"@warlock.js/ai\";\nimport { approximateTokenCount } from \"@warlock.js/ai\";\nimport type {\n BedrockEmbedderConfig,\n BedrockModelConfig,\n BedrockSDKConfig,\n} from \"./config.type\";\nimport { BedrockEmbedder } from \"./embedder\";\nimport { BedrockModel } from \"./model\";\n\n/**\n * AWS Bedrock-backed implementation of `SDKAdapterContract`.\n *\n * **Role.** The package entry point for any Bedrock-hosted model via\n * the Converse API. A single `BedrockSDK` holds one live\n * `BedrockRuntimeClient`, shared by every `ModelContract` and\n * `EmbedderContract` it produces. Construct one SDK per AWS\n * account/region and reuse it everywhere.\n *\n * **Responsibility.**\n * - Owns: a long-lived `BedrockRuntimeClient` (region, credential\n * chain) and its lifetime. Factory for `BedrockModel` /\n * `BedrockEmbedder` instances sharing that client.\n * - Does NOT own: anything per-call — those live in `BedrockModel` /\n * `BedrockEmbedder` and the agent runtime.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across many calls\"): the AWS client is heavy to construct and\n * designed for reuse; keeping it on `this` aligns with the\n * `new BedrockRuntimeClient(...)` upstream convention.\n *\n * @example\n * const bedrock = new BedrockSDK({ region: \"us-east-1\" });\n * const model = bedrock.model({ name: \"anthropic.claude-sonnet-4-5-20250929-v1:0\" });\n * const embedder = bedrock.embedder({ name: \"amazon.titan-embed-text-v2:0\" });\n */\nexport class BedrockSDK implements SDKAdapterContract {\n private readonly client: BedrockRuntimeClient;\n private readonly provider: string;\n private readonly pricing?: Record<string, ModelPricing>;\n\n public constructor(config: BedrockSDKConfig) {\n const { provider, pricing, ...clientConfig } = config;\n\n this.client = new BedrockRuntimeClient(clientConfig);\n this.provider = provider ?? \"bedrock\";\n this.pricing = pricing;\n }\n\n /**\n * Build a `BedrockModel` bound to this SDK's client. Each call\n * returns a fresh instance; all instances share the underlying AWS\n * client so connection pools, credential refresh, and retry config\n * stay unified. The SDK's `provider` label is forwarded.\n *\n * Pricing resolution: per-model `config.pricing` wins; otherwise the\n * SDK-level registry entry keyed by `config.name`; otherwise\n * `undefined` (no cost computed).\n */\n public model(config: BedrockModelConfig): ModelContract {\n const resolvedPricing = config.pricing ?? this.pricing?.[config.name];\n const resolvedConfig: BedrockModelConfig =\n resolvedPricing === config.pricing ? config : { ...config, pricing: resolvedPricing };\n\n return new BedrockModel(this.client, resolvedConfig, this.provider);\n }\n\n /**\n * Rough token-count estimate. Uses the character-heuristic\n * (`approximateTokenCount`) from the core package — Bedrock has no\n * offline tokenizer and the per-model tokenizers differ; good enough\n * for budgeting and quota guards, not for billing.\n */\n public async count(text: string, _model?: string): Promise<number> {\n return approximateTokenCount(text);\n }\n\n /**\n * Build a `BedrockEmbedder` (Amazon Titan Text Embeddings) bound to\n * this SDK's client.\n *\n * @example\n * const embedder = bedrock.embedder({ name: \"amazon.titan-embed-text-v2:0\" });\n * const { vector } = await embedder.embed(\"Hello world\");\n */\n public embedder(config: BedrockEmbedderConfig): EmbedderContract {\n return new BedrockEmbedder(this.client, config, this.provider);\n }\n}\n"],"mappings":"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"}
package/esm/model.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { mapStopReason } from "./utils/map-stop-reason.mjs";
2
+ import { toReasoningMetadata } from "./utils/reasoning-blocks.mjs";
2
3
  import { toBedrockMessages } from "./utils/to-bedrock-messages.mjs";
3
4
  import { toBedrockToolConfig } from "./utils/to-bedrock-tools.mjs";
4
5
  import { wrapBedrockError } from "./utils/wrap-bedrock-error.mjs";
@@ -11,6 +12,9 @@ import { log } from "@warlock.js/logger";
11
12
 
12
13
  //#region ../ai-bedrock/src/model.ts
13
14
  const LOG_MODULE = "ai.bedrock";
15
+ /** Default output allowance added to a thinking budget when maxTokens is unset. */
16
+ const DEFAULT_MAX_TOKENS = 4096;
17
+ const MIN_THINKING_BUDGET = 1024;
14
18
  /**
15
19
  * Conventional extended-thinking token budgets for the neutral
16
20
  * `reasoning.effort` levels, used when the caller asks for an effort
@@ -135,6 +139,8 @@ var BedrockModel = class {
135
139
  total: 0
136
140
  };
137
141
  const toolBlocks = /* @__PURE__ */ new Map();
142
+ const reasoningBlocks = /* @__PURE__ */ new Map();
143
+ const completedReasoningBlocks = [];
138
144
  try {
139
145
  for await (const event of response.stream ?? []) {
140
146
  if (event.contentBlockStart?.start?.toolUse) {
@@ -155,19 +161,34 @@ var BedrockModel = class {
155
161
  else if (delta.toolUse) {
156
162
  const accumulator = toolBlocks.get(event.contentBlockDelta.contentBlockIndex ?? 0);
157
163
  if (accumulator) accumulator.json += delta.toolUse.input ?? "";
164
+ } else if (delta.reasoningContent) {
165
+ const index = event.contentBlockDelta.contentBlockIndex ?? 0;
166
+ const accumulator = reasoningBlocks.get(index) ?? {};
167
+ if (delta.reasoningContent.text !== void 0) accumulator.text = (accumulator.text ?? "") + delta.reasoningContent.text;
168
+ if (delta.reasoningContent.signature !== void 0) accumulator.signature = delta.reasoningContent.signature;
169
+ if (delta.reasoningContent.redactedContent !== void 0) accumulator.redactedContent = delta.reasoningContent.redactedContent;
170
+ reasoningBlocks.set(index, accumulator);
158
171
  }
159
172
  continue;
160
173
  }
161
174
  if (event.contentBlockStop) {
162
- const accumulator = toolBlocks.get(event.contentBlockStop.contentBlockIndex ?? 0);
175
+ const index = event.contentBlockStop.contentBlockIndex ?? 0;
176
+ const accumulator = toolBlocks.get(index);
163
177
  if (accumulator) {
164
178
  yield {
165
179
  type: "tool-call",
166
180
  id: accumulator.id,
167
181
  name: accumulator.name,
168
- input: safeJsonParse(accumulator.json, {})
182
+ input: safeJsonParse(accumulator.json, {}),
183
+ ...completedReasoningBlocks.length > 0 ? { providerMetadata: { bedrock: { reasoningBlocks: completedReasoningBlocks } } } : {}
169
184
  };
170
- toolBlocks.delete(event.contentBlockStop.contentBlockIndex ?? 0);
185
+ toolBlocks.delete(index);
186
+ }
187
+ const reasoningAccumulator = reasoningBlocks.get(index);
188
+ if (reasoningAccumulator) {
189
+ const reasoningBlock = this.toReasoningMetadata(reasoningAccumulator);
190
+ if (reasoningBlock) completedReasoningBlocks.push(reasoningBlock);
191
+ reasoningBlocks.delete(index);
171
192
  }
172
193
  continue;
173
194
  }
@@ -203,7 +224,9 @@ var BedrockModel = class {
203
224
  */
204
225
  buildRequest(messages, options) {
205
226
  const { system, messages: bedrockMessages } = toBedrockMessages(messages);
206
- const maxTokens = options?.maxTokens ?? this.config.maxTokens;
227
+ const thinkingBudget = this.resolveThinkingBudget(options?.reasoning);
228
+ const configuredMaxTokens = options?.maxTokens ?? this.config.maxTokens;
229
+ const maxTokens = this.resolveMaxTokens(configuredMaxTokens, thinkingBudget);
207
230
  const temperature = options?.temperature ?? this.config.temperature;
208
231
  const cachedMessages = this.applyCacheBreakpoints(bedrockMessages, options?.cacheControl);
209
232
  return {
@@ -216,7 +239,7 @@ var BedrockModel = class {
216
239
  },
217
240
  ...this.buildToolConfig(options?.tools),
218
241
  ...this.buildOutputConfig(options?.responseSchema),
219
- ...this.buildReasoningConfig(options?.reasoning)
242
+ ...this.buildReasoningConfig(thinkingBudget)
220
243
  };
221
244
  }
222
245
  /**
@@ -255,9 +278,20 @@ var BedrockModel = class {
255
278
  * when no reasoning option was supplied, so unsupported params never
256
279
  * reach the wire.
257
280
  */
258
- buildReasoningConfig(reasoning) {
259
- if (!this.capabilities.reasoning || !reasoning) return {};
281
+ resolveThinkingBudget(reasoning) {
282
+ if (!this.capabilities.reasoning || !reasoning || reasoning.effort === "none") return;
260
283
  const budgetTokens = reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[reasoning.effort ?? ""];
284
+ if (budgetTokens !== void 0 && budgetTokens < MIN_THINKING_BUDGET) throw new Error(`thinking budget (${budgetTokens}) must be at least ${MIN_THINKING_BUDGET}`);
285
+ return budgetTokens;
286
+ }
287
+ /** Resolve Bedrock's output ceiling while reserving room for extended thinking. */
288
+ resolveMaxTokens(maxTokens, thinkingBudget) {
289
+ if (thinkingBudget === void 0) return maxTokens;
290
+ if (maxTokens === void 0) return thinkingBudget + DEFAULT_MAX_TOKENS;
291
+ if (maxTokens <= thinkingBudget) throw new Error(`maxTokens (${maxTokens}) must be greater than thinking budget (${thinkingBudget})`);
292
+ return maxTokens;
293
+ }
294
+ buildReasoningConfig(budgetTokens) {
261
295
  if (budgetTokens === void 0) return {};
262
296
  return { additionalModelRequestFields: { thinking: {
263
297
  type: "enabled",
@@ -307,13 +341,23 @@ var BedrockModel = class {
307
341
  */
308
342
  extractToolCalls(blocks) {
309
343
  const toolCalls = [];
344
+ const reasoningBlocks = blocks.map(toReasoningMetadata).filter((block) => block !== void 0);
310
345
  for (const block of blocks) if ("toolUse" in block && block.toolUse) toolCalls.push({
311
346
  id: block.toolUse.toolUseId ?? "",
312
347
  name: block.toolUse.name ?? "",
313
- input: block.toolUse.input ?? {}
348
+ input: block.toolUse.input ?? {},
349
+ ...reasoningBlocks.length > 0 ? { providerMetadata: { bedrock: { reasoningBlocks } } } : {}
314
350
  });
315
351
  return toolCalls.length > 0 ? toolCalls : void 0;
316
352
  }
353
+ /** Encode accumulated stream deltas as persistence-safe reasoning metadata. */
354
+ toReasoningMetadata(block) {
355
+ if (block.redactedContent !== void 0) return toReasoningMetadata({ reasoningContent: { redactedContent: block.redactedContent } });
356
+ return toReasoningMetadata({ reasoningContent: { reasoningText: {
357
+ text: block.text ?? "",
358
+ ...block.signature ? { signature: block.signature } : {}
359
+ } } });
360
+ }
317
361
  /**
318
362
  * Normalize Converse's `TokenUsage` into the neutral `Usage` shape.
319
363
  * Bedrock supplies a pre-summed `totalTokens`; cache-read and
package/esm/model.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"model.mjs","names":[],"sources":["../../../../../../ai-bedrock/src/model.ts"],"sourcesContent":["import {\n safeJsonParse,\n type Message,\n type ModelCallOptions,\n type ModelCapabilities,\n type ModelContract,\n type ModelPricing,\n type ModelResponse,\n type ModelStreamChunk,\n type ModelToolCallRequest,\n type Usage,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport {\n ConverseCommand,\n ConverseStreamCommand,\n type BedrockRuntimeClient,\n type ContentBlock,\n type ConverseRequest,\n type TokenUsage,\n} from \"@aws-sdk/client-bedrock-runtime\";\nimport type { BedrockModelConfig } from \"./config.type\";\nimport {\n inferPdfCapability,\n inferPromptCachingCapability,\n inferReasoningCapability,\n} from \"./known-capabilities\";\nimport { inferVisionCapability } from \"./known-vision-models\";\nimport { mapStopReason, toBedrockMessages, toBedrockToolConfig, wrapBedrockError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.bedrock\";\n\n/**\n * Conventional extended-thinking token budgets for the neutral\n * `reasoning.effort` levels, used when the caller asks for an effort\n * tier without naming an explicit `reasoning.maxTokens` budget. Mirrors\n * the low / medium / high spread other reasoning adapters expose so the\n * vendor-neutral option behaves consistently across providers.\n */\nconst EFFORT_THINKING_BUDGET: Record<string, number | undefined> = {\n low: 1024,\n medium: 4096,\n high: 16384,\n};\n\n/**\n * Bedrock-backed implementation of `ModelContract`.\n *\n * **Role.** The provider-facing bridge between the vendor-neutral\n * `@warlock.js/ai` agent runtime and AWS Bedrock's Converse /\n * ConverseStream API. Converse is the model-agnostic surface — one\n * wire mapping covers every Bedrock-hosted family (Anthropic Claude,\n * Amazon Nova, Meta Llama, Mistral, Cohere) instead of per-family\n * `InvokeModel` body shapes.\n *\n * **Responsibility.**\n * - Owns: a long-lived `BedrockRuntimeClient` + frozen `ModelConfig`\n * (modelId, temperature, maxTokens) used as per-call defaults.\n * - Owns: translating vendor-neutral `Message[]` / `ToolConfig[]` into\n * Converse shapes (system hoisting, `toolUse` / `toolResult` blocks,\n * image bytes) on the way out, and Converse's content-block response\n * (text, tool calls, stop reason, token usage) back into the neutral\n * shapes on the way in.\n * - Does NOT own: dispatching tools, looping, history, retries — those\n * are agent concerns. The model is a per-call protocol adapter.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across calls\"): the AWS client is heavy to construct and reused for\n * the SDK's lifetime.\n *\n * @example\n * import { BedrockRuntimeClient } from \"@aws-sdk/client-bedrock-runtime\";\n * const client = new BedrockRuntimeClient({ region: \"us-east-1\" });\n * const model = new BedrockModel(client, {\n * name: \"anthropic.claude-sonnet-4-5-20250929-v1:0\",\n * });\n *\n * const myAgent = agent({ model, tools: [searchTool] });\n * const result = await myAgent.execute(\"Summarize today's news.\");\n */\nexport class BedrockModel implements ModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly capabilities: ModelCapabilities;\n public readonly pricing?: ModelPricing;\n\n private readonly client: BedrockRuntimeClient;\n private readonly config: BedrockModelConfig;\n private readonly logger: Logger = log;\n\n public constructor(\n client: BedrockRuntimeClient,\n config: BedrockModelConfig,\n provider: string = \"bedrock\",\n ) {\n this.client = client;\n this.config = config;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n this.capabilities = {\n structuredOutput: config.structuredOutput ?? true,\n vision: config.vision ?? inferVisionCapability(config.name),\n reasoning: config.reasoning ?? inferReasoningCapability(config.name),\n promptCaching: config.promptCaching ?? inferPromptCachingCapability(config.name),\n pdf: config.pdf ?? inferPdfCapability(config.name),\n audio: config.audio ?? false,\n };\n }\n\n /**\n * Single-shot completion via the Converse API. Sends the full\n * message list, waits for the terminal response, and reshapes it\n * into a vendor-neutral `ModelResponse`. Per-call `options` override\n * the instance defaults for this call only.\n */\n public async complete(messages: Message[], options?: ModelCallOptions): Promise<ModelResponse> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting Converse call\", {\n model: this.name,\n messageCount: messages.length,\n streaming: false,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response;\n\n try {\n response = await this.client.send(\n new ConverseCommand(this.buildRequest(messages, options)),\n options?.signal ? { abortSignal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const blocks = response.output?.message?.content ?? [];\n const finishReason = mapStopReason(response.stopReason);\n const usage = this.extractUsage(response.usage);\n const toolCalls = this.extractToolCalls(blocks);\n\n this.logger.debug(LOG_MODULE, \"response\", \"Converse call succeeded\", { finishReason, usage });\n\n return {\n content: this.extractText(blocks),\n finishReason,\n usage,\n toolCalls,\n };\n }\n\n /**\n * Incremental streaming completion via ConverseStream. Yields neutral\n * `ModelStreamChunk`s — `delta` for text, `tool-call` once a\n * `toolUse` block's accumulated input JSON is complete, and a\n * terminal `done` with the final finish reason + usage totals.\n */\n public async *stream(\n messages: Message[],\n options?: ModelCallOptions,\n ): AsyncIterable<ModelStreamChunk> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting ConverseStream call\", {\n model: this.name,\n messageCount: messages.length,\n streaming: true,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response;\n\n try {\n response = await this.client.send(\n new ConverseStreamCommand(this.buildRequest(messages, options)),\n options?.signal ? { abortSignal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n let rawStopReason: string | undefined;\n const usage: Usage = { input: 0, output: 0, total: 0 };\n const toolBlocks = new Map<number, { id: string; name: string; json: string }>();\n\n try {\n for await (const event of response.stream ?? []) {\n if (event.contentBlockStart?.start?.toolUse) {\n const start = event.contentBlockStart.start.toolUse;\n\n toolBlocks.set(event.contentBlockStart.contentBlockIndex ?? 0, {\n id: start.toolUseId ?? \"\",\n name: start.name ?? \"\",\n json: \"\",\n });\n\n continue;\n }\n\n if (event.contentBlockDelta?.delta) {\n const delta = event.contentBlockDelta.delta;\n\n if (delta.text) {\n yield { type: \"delta\", content: delta.text };\n } else if (delta.toolUse) {\n const accumulator = toolBlocks.get(event.contentBlockDelta.contentBlockIndex ?? 0);\n\n if (accumulator) {\n accumulator.json += delta.toolUse.input ?? \"\";\n }\n }\n\n continue;\n }\n\n if (event.contentBlockStop) {\n const accumulator = toolBlocks.get(event.contentBlockStop.contentBlockIndex ?? 0);\n\n if (accumulator) {\n yield {\n type: \"tool-call\",\n id: accumulator.id,\n name: accumulator.name,\n input: safeJsonParse<Record<string, unknown>>(accumulator.json, {}),\n };\n\n toolBlocks.delete(event.contentBlockStop.contentBlockIndex ?? 0);\n }\n\n continue;\n }\n\n if (event.messageStop) {\n rawStopReason = event.messageStop.stopReason;\n }\n\n if (event.metadata?.usage) {\n const raw = event.metadata.usage;\n\n usage.input = raw.inputTokens ?? 0;\n usage.output = raw.outputTokens ?? 0;\n usage.total = raw.totalTokens ?? usage.input + usage.output;\n\n if (raw.cacheReadInputTokens && raw.cacheReadInputTokens > 0) {\n usage.cachedTokens = raw.cacheReadInputTokens;\n }\n\n if (raw.cacheWriteInputTokens && raw.cacheWriteInputTokens > 0) {\n usage.cacheWriteTokens = raw.cacheWriteInputTokens;\n }\n }\n }\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const finishReason = mapStopReason(rawStopReason);\n\n this.logger.debug(LOG_MODULE, \"response\", \"ConverseStream call succeeded\", {\n finishReason,\n usage,\n });\n\n yield { type: \"done\", finishReason, usage };\n }\n\n /**\n * Assemble the Converse request shared by `complete()` and\n * `stream()` (both command shapes take the same input). Hoists the\n * system prompt, maps inference params, and conditionally attaches\n * tools and native structured output.\n */\n private buildRequest(\n messages: Message[],\n options: ModelCallOptions | undefined,\n ): ConverseRequest {\n const { system, messages: bedrockMessages } = toBedrockMessages(messages);\n const maxTokens = options?.maxTokens ?? this.config.maxTokens;\n const temperature = options?.temperature ?? this.config.temperature;\n const cachedMessages = this.applyCacheBreakpoints(bedrockMessages, options?.cacheControl);\n\n return {\n modelId: this.name,\n messages: cachedMessages,\n ...(system ? { system } : {}),\n inferenceConfig: {\n ...(maxTokens !== undefined ? { maxTokens } : {}),\n ...(temperature !== undefined ? { temperature } : {}),\n },\n ...this.buildToolConfig(options?.tools),\n ...this.buildOutputConfig(options?.responseSchema),\n ...this.buildReasoningConfig(options?.reasoning),\n };\n }\n\n /**\n * Append a Converse `cachePoint` block to the LAST message when the\n * caller supplies a `cacheControl` write breakpoint and the model is\n * `promptCaching`-capable. A cache point tells Bedrock to cache the\n * whole prefix up to that block, so subsequent calls reusing the same\n * prefix bill the cached portion at the discounted read rate\n * (surfaced as `Usage.cachedTokens`). No-ops gracefully when caching\n * is unsupported, no breakpoint was requested, or there are no\n * messages to mark — Bedrock then prices the call normally.\n *\n * Bedrock only honors `CachePointType.DEFAULT`; the neutral\n * `breakpoints` count is a presence hint (one trailing breakpoint is\n * the only placement Converse supports without manual block surgery),\n * so any positive value marks the trailing message.\n */\n private applyCacheBreakpoints(\n messages: ConverseRequest[\"messages\"],\n cacheControl: ModelCallOptions[\"cacheControl\"],\n ): ConverseRequest[\"messages\"] {\n const breakpoints = cacheControl?.breakpoints ?? 0;\n\n if (!this.capabilities.promptCaching || breakpoints <= 0 || !messages || messages.length === 0) {\n return messages;\n }\n\n const last = messages.length - 1;\n const lastMessage = messages[last];\n\n return [\n ...messages.slice(0, last),\n {\n ...lastMessage,\n content: [...(lastMessage.content ?? []), { cachePoint: { type: \"default\" } }],\n },\n ];\n }\n\n /**\n * Translate the neutral `reasoning` option into Claude-on-Bedrock's\n * extended-thinking control, carried in Converse's escape hatch\n * `additionalModelRequestFields.thinking`. Emitted only when the model\n * is `reasoning`-capable and a budget can be resolved — `maxTokens`\n * (explicit thinking budget) wins, otherwise `effort` maps to a\n * conventional token budget so callers can opt in without picking a\n * number. Returns an empty object (no-op) for non-reasoning models or\n * when no reasoning option was supplied, so unsupported params never\n * reach the wire.\n */\n private buildReasoningConfig(\n reasoning: ModelCallOptions[\"reasoning\"],\n ): Pick<ConverseRequest, \"additionalModelRequestFields\"> {\n if (!this.capabilities.reasoning || !reasoning) {\n return {};\n }\n\n const budgetTokens = reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[reasoning.effort ?? \"\"];\n\n if (budgetTokens === undefined) {\n return {};\n }\n\n return {\n additionalModelRequestFields: {\n thinking: { type: \"enabled\", budget_tokens: budgetTokens },\n },\n };\n }\n\n /**\n * Spread-friendly tool fragment. Returns an empty object when no\n * tools were supplied (Bedrock rejects an empty `tools` array).\n */\n private buildToolConfig(tools: ModelCallOptions[\"tools\"]): Pick<ConverseRequest, \"toolConfig\"> {\n const toolConfig = toBedrockToolConfig(tools);\n\n return toolConfig ? { toolConfig } : {};\n }\n\n /**\n * Translate the neutral `responseSchema` into Converse's native\n * `outputConfig.textFormat` (JSON-schema structured output). Bedrock\n * requires the schema as a stringified JSON document and only\n * accepts an object root. Emitted only when the model is\n * `structuredOutput`-capable and the schema is an object — otherwise\n * the agent's soft system-prompt hint + client-side `validate()`\n * carry shape (same degradation philosophy as the OpenAI adapter).\n */\n private buildOutputConfig(\n responseSchema: Record<string, unknown> | undefined,\n ): Pick<ConverseRequest, \"outputConfig\"> {\n if (!responseSchema || !this.capabilities.structuredOutput) {\n return {};\n }\n\n if (responseSchema.type !== \"object\" || typeof responseSchema.properties !== \"object\") {\n return {};\n }\n\n return {\n outputConfig: {\n textFormat: {\n type: \"json_schema\",\n structure: {\n jsonSchema: { name: \"response\", schema: JSON.stringify(responseSchema) },\n },\n },\n },\n };\n }\n\n /**\n * Concatenate every `text` content block into the single neutral\n * `content` string. `toolUse` and other block types are surfaced\n * separately via `extractToolCalls`.\n */\n private extractText(blocks: ContentBlock[]): string {\n return blocks\n .map((block) => (\"text\" in block && typeof block.text === \"string\" ? block.text : \"\"))\n .join(\"\");\n }\n\n /**\n * Reshape Converse `toolUse` content blocks into the neutral\n * `ModelToolCallRequest[]`. Returns `undefined` when no tools were\n * requested so callers can branch on presence.\n */\n private extractToolCalls(blocks: ContentBlock[]): ModelToolCallRequest[] | undefined {\n const toolCalls: ModelToolCallRequest[] = [];\n\n for (const block of blocks) {\n if (\"toolUse\" in block && block.toolUse) {\n toolCalls.push({\n id: block.toolUse.toolUseId ?? \"\",\n name: block.toolUse.name ?? \"\",\n input: (block.toolUse.input ?? {}) as Record<string, unknown>,\n });\n }\n }\n\n return toolCalls.length > 0 ? toolCalls : undefined;\n }\n\n /**\n * Normalize Converse's `TokenUsage` into the neutral `Usage` shape.\n * Bedrock supplies a pre-summed `totalTokens`; cache-read and\n * cache-write tokens are surfaced as `cachedTokens` /\n * `cacheWriteTokens` only when non-zero so callers can price the\n * discounted read rate and the one-time write cost separately.\n * Bedrock's Converse `TokenUsage` carries no reasoning-token channel,\n * so `Usage.reasoningTokens` is intentionally left unset here.\n */\n private extractUsage(raw: TokenUsage | undefined): Usage {\n if (!raw) {\n return { input: 0, output: 0, total: 0 };\n }\n\n const input = raw.inputTokens ?? 0;\n const output = raw.outputTokens ?? 0;\n const cached = raw.cacheReadInputTokens;\n const cacheWrite = raw.cacheWriteInputTokens;\n\n return {\n input,\n output,\n total: raw.totalTokens ?? input + output,\n ...(cached && cached > 0 ? { cachedTokens: cached } : {}),\n ...(cacheWrite && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {}),\n };\n }\n\n /**\n * Wrap a thrown provider error into the typed `AIError` hierarchy\n * and emit the standard error log line before it propagates. Shared\n * by every catch site so the log shape stays identical.\n */\n private logAndWrap(thrown: unknown) {\n const wrapped = wrapBedrockError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n return wrapped;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"model.mjs","names":[],"sources":["../../../../../../ai-bedrock/src/model.ts"],"sourcesContent":["import {\n safeJsonParse,\n type Message,\n type ModelCallOptions,\n type ModelCapabilities,\n type ModelContract,\n type ModelPricing,\n type ModelResponse,\n type ModelStreamChunk,\n type ModelToolCallRequest,\n type Usage,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport {\n ConverseCommand,\n ConverseStreamCommand,\n type BedrockRuntimeClient,\n type ContentBlock,\n type ConverseRequest,\n type TokenUsage,\n} from \"@aws-sdk/client-bedrock-runtime\";\nimport type { BedrockModelConfig } from \"./config.type\";\nimport {\n inferPdfCapability,\n inferPromptCachingCapability,\n inferReasoningCapability,\n} from \"./known-capabilities\";\nimport { inferVisionCapability } from \"./known-vision-models\";\nimport { toReasoningMetadata, type BedrockReasoningBlock } from \"./utils/reasoning-blocks\";\nimport { mapStopReason, toBedrockMessages, toBedrockToolConfig, wrapBedrockError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.bedrock\";\n\n/** Default output allowance added to a thinking budget when maxTokens is unset. */\nconst DEFAULT_MAX_TOKENS = 4096;\nconst MIN_THINKING_BUDGET = 1024;\n\n/**\n * Conventional extended-thinking token budgets for the neutral\n * `reasoning.effort` levels, used when the caller asks for an effort\n * tier without naming an explicit `reasoning.maxTokens` budget. Mirrors\n * the low / medium / high spread other reasoning adapters expose so the\n * vendor-neutral option behaves consistently across providers.\n */\nconst EFFORT_THINKING_BUDGET: Record<string, number | undefined> = {\n low: 1024,\n medium: 4096,\n high: 16384,\n};\n\n/**\n * Bedrock-backed implementation of `ModelContract`.\n *\n * **Role.** The provider-facing bridge between the vendor-neutral\n * `@warlock.js/ai` agent runtime and AWS Bedrock's Converse /\n * ConverseStream API. Converse is the model-agnostic surface — one\n * wire mapping covers every Bedrock-hosted family (Anthropic Claude,\n * Amazon Nova, Meta Llama, Mistral, Cohere) instead of per-family\n * `InvokeModel` body shapes.\n *\n * **Responsibility.**\n * - Owns: a long-lived `BedrockRuntimeClient` + frozen `ModelConfig`\n * (modelId, temperature, maxTokens) used as per-call defaults.\n * - Owns: translating vendor-neutral `Message[]` / `ToolConfig[]` into\n * Converse shapes (system hoisting, `toolUse` / `toolResult` blocks,\n * image bytes) on the way out, and Converse's content-block response\n * (text, tool calls, stop reason, token usage) back into the neutral\n * shapes on the way in.\n * - Does NOT own: dispatching tools, looping, history, retries — those\n * are agent concerns. The model is a per-call protocol adapter.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across calls\"): the AWS client is heavy to construct and reused for\n * the SDK's lifetime.\n *\n * @example\n * import { BedrockRuntimeClient } from \"@aws-sdk/client-bedrock-runtime\";\n * const client = new BedrockRuntimeClient({ region: \"us-east-1\" });\n * const model = new BedrockModel(client, {\n * name: \"anthropic.claude-sonnet-4-5-20250929-v1:0\",\n * });\n *\n * const myAgent = agent({ model, tools: [searchTool] });\n * const result = await myAgent.execute(\"Summarize today's news.\");\n */\nexport class BedrockModel implements ModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly capabilities: ModelCapabilities;\n public readonly pricing?: ModelPricing;\n\n private readonly client: BedrockRuntimeClient;\n private readonly config: BedrockModelConfig;\n private readonly logger: Logger = log;\n\n public constructor(\n client: BedrockRuntimeClient,\n config: BedrockModelConfig,\n provider: string = \"bedrock\",\n ) {\n this.client = client;\n this.config = config;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n this.capabilities = {\n structuredOutput: config.structuredOutput ?? true,\n vision: config.vision ?? inferVisionCapability(config.name),\n reasoning: config.reasoning ?? inferReasoningCapability(config.name),\n promptCaching: config.promptCaching ?? inferPromptCachingCapability(config.name),\n pdf: config.pdf ?? inferPdfCapability(config.name),\n audio: config.audio ?? false,\n };\n }\n\n /**\n * Single-shot completion via the Converse API. Sends the full\n * message list, waits for the terminal response, and reshapes it\n * into a vendor-neutral `ModelResponse`. Per-call `options` override\n * the instance defaults for this call only.\n */\n public async complete(messages: Message[], options?: ModelCallOptions): Promise<ModelResponse> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting Converse call\", {\n model: this.name,\n messageCount: messages.length,\n streaming: false,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response;\n\n try {\n response = await this.client.send(\n new ConverseCommand(this.buildRequest(messages, options)),\n options?.signal ? { abortSignal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const blocks = response.output?.message?.content ?? [];\n const finishReason = mapStopReason(response.stopReason);\n const usage = this.extractUsage(response.usage);\n const toolCalls = this.extractToolCalls(blocks);\n\n this.logger.debug(LOG_MODULE, \"response\", \"Converse call succeeded\", { finishReason, usage });\n\n return {\n content: this.extractText(blocks),\n finishReason,\n usage,\n toolCalls,\n };\n }\n\n /**\n * Incremental streaming completion via ConverseStream. Yields neutral\n * `ModelStreamChunk`s — `delta` for text, `tool-call` once a\n * `toolUse` block's accumulated input JSON is complete, and a\n * terminal `done` with the final finish reason + usage totals.\n */\n public async *stream(\n messages: Message[],\n options?: ModelCallOptions,\n ): AsyncIterable<ModelStreamChunk> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting ConverseStream call\", {\n model: this.name,\n messageCount: messages.length,\n streaming: true,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response;\n\n try {\n response = await this.client.send(\n new ConverseStreamCommand(this.buildRequest(messages, options)),\n options?.signal ? { abortSignal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n let rawStopReason: string | undefined;\n const usage: Usage = { input: 0, output: 0, total: 0 };\n const toolBlocks = new Map<number, { id: string; name: string; json: string }>();\n const reasoningBlocks = new Map<number, { text?: string; signature?: string; redactedContent?: Uint8Array }>();\n const completedReasoningBlocks: BedrockReasoningBlock[] = [];\n\n try {\n for await (const event of response.stream ?? []) {\n if (event.contentBlockStart?.start?.toolUse) {\n const start = event.contentBlockStart.start.toolUse;\n\n toolBlocks.set(event.contentBlockStart.contentBlockIndex ?? 0, {\n id: start.toolUseId ?? \"\",\n name: start.name ?? \"\",\n json: \"\",\n });\n\n continue;\n }\n\n if (event.contentBlockDelta?.delta) {\n const delta = event.contentBlockDelta.delta;\n\n if (delta.text) {\n yield { type: \"delta\", content: delta.text };\n } else if (delta.toolUse) {\n const accumulator = toolBlocks.get(event.contentBlockDelta.contentBlockIndex ?? 0);\n\n if (accumulator) {\n accumulator.json += delta.toolUse.input ?? \"\";\n }\n } else if (delta.reasoningContent) {\n const index = event.contentBlockDelta.contentBlockIndex ?? 0;\n const accumulator = reasoningBlocks.get(index) ?? {};\n\n if (delta.reasoningContent.text !== undefined) {\n accumulator.text = (accumulator.text ?? \"\") + delta.reasoningContent.text;\n }\n\n if (delta.reasoningContent.signature !== undefined) {\n accumulator.signature = delta.reasoningContent.signature;\n }\n\n if (delta.reasoningContent.redactedContent !== undefined) {\n accumulator.redactedContent = delta.reasoningContent.redactedContent;\n }\n\n reasoningBlocks.set(index, accumulator);\n }\n\n continue;\n }\n\n if (event.contentBlockStop) {\n const index = event.contentBlockStop.contentBlockIndex ?? 0;\n const accumulator = toolBlocks.get(index);\n\n if (accumulator) {\n yield {\n type: \"tool-call\",\n id: accumulator.id,\n name: accumulator.name,\n input: safeJsonParse<Record<string, unknown>>(accumulator.json, {}),\n ...(completedReasoningBlocks.length > 0\n ? { providerMetadata: { bedrock: { reasoningBlocks: completedReasoningBlocks } } }\n : {}),\n };\n\n toolBlocks.delete(index);\n }\n\n const reasoningAccumulator = reasoningBlocks.get(index);\n\n if (reasoningAccumulator) {\n const reasoningBlock = this.toReasoningMetadata(reasoningAccumulator);\n\n if (reasoningBlock) {\n completedReasoningBlocks.push(reasoningBlock);\n }\n reasoningBlocks.delete(index);\n }\n\n continue;\n }\n\n if (event.messageStop) {\n rawStopReason = event.messageStop.stopReason;\n }\n\n if (event.metadata?.usage) {\n const raw = event.metadata.usage;\n\n usage.input = raw.inputTokens ?? 0;\n usage.output = raw.outputTokens ?? 0;\n usage.total = raw.totalTokens ?? usage.input + usage.output;\n\n if (raw.cacheReadInputTokens && raw.cacheReadInputTokens > 0) {\n usage.cachedTokens = raw.cacheReadInputTokens;\n }\n\n if (raw.cacheWriteInputTokens && raw.cacheWriteInputTokens > 0) {\n usage.cacheWriteTokens = raw.cacheWriteInputTokens;\n }\n }\n }\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const finishReason = mapStopReason(rawStopReason);\n\n this.logger.debug(LOG_MODULE, \"response\", \"ConverseStream call succeeded\", {\n finishReason,\n usage,\n });\n\n yield { type: \"done\", finishReason, usage };\n }\n\n /**\n * Assemble the Converse request shared by `complete()` and\n * `stream()` (both command shapes take the same input). Hoists the\n * system prompt, maps inference params, and conditionally attaches\n * tools and native structured output.\n */\n private buildRequest(\n messages: Message[],\n options: ModelCallOptions | undefined,\n ): ConverseRequest {\n const { system, messages: bedrockMessages } = toBedrockMessages(messages);\n const thinkingBudget = this.resolveThinkingBudget(options?.reasoning);\n const configuredMaxTokens = options?.maxTokens ?? this.config.maxTokens;\n const maxTokens = this.resolveMaxTokens(configuredMaxTokens, thinkingBudget);\n const temperature = options?.temperature ?? this.config.temperature;\n const cachedMessages = this.applyCacheBreakpoints(bedrockMessages, options?.cacheControl);\n\n return {\n modelId: this.name,\n messages: cachedMessages,\n ...(system ? { system } : {}),\n inferenceConfig: {\n ...(maxTokens !== undefined ? { maxTokens } : {}),\n ...(temperature !== undefined ? { temperature } : {}),\n },\n ...this.buildToolConfig(options?.tools),\n ...this.buildOutputConfig(options?.responseSchema),\n ...this.buildReasoningConfig(thinkingBudget),\n };\n }\n\n /**\n * Append a Converse `cachePoint` block to the LAST message when the\n * caller supplies a `cacheControl` write breakpoint and the model is\n * `promptCaching`-capable. A cache point tells Bedrock to cache the\n * whole prefix up to that block, so subsequent calls reusing the same\n * prefix bill the cached portion at the discounted read rate\n * (surfaced as `Usage.cachedTokens`). No-ops gracefully when caching\n * is unsupported, no breakpoint was requested, or there are no\n * messages to mark — Bedrock then prices the call normally.\n *\n * Bedrock only honors `CachePointType.DEFAULT`; the neutral\n * `breakpoints` count is a presence hint (one trailing breakpoint is\n * the only placement Converse supports without manual block surgery),\n * so any positive value marks the trailing message.\n */\n private applyCacheBreakpoints(\n messages: ConverseRequest[\"messages\"],\n cacheControl: ModelCallOptions[\"cacheControl\"],\n ): ConverseRequest[\"messages\"] {\n const breakpoints = cacheControl?.breakpoints ?? 0;\n\n if (!this.capabilities.promptCaching || breakpoints <= 0 || !messages || messages.length === 0) {\n return messages;\n }\n\n const last = messages.length - 1;\n const lastMessage = messages[last];\n\n return [\n ...messages.slice(0, last),\n {\n ...lastMessage,\n content: [...(lastMessage.content ?? []), { cachePoint: { type: \"default\" } }],\n },\n ];\n }\n\n /**\n * Translate the neutral `reasoning` option into Claude-on-Bedrock's\n * extended-thinking control, carried in Converse's escape hatch\n * `additionalModelRequestFields.thinking`. Emitted only when the model\n * is `reasoning`-capable and a budget can be resolved — `maxTokens`\n * (explicit thinking budget) wins, otherwise `effort` maps to a\n * conventional token budget so callers can opt in without picking a\n * number. Returns an empty object (no-op) for non-reasoning models or\n * when no reasoning option was supplied, so unsupported params never\n * reach the wire.\n */\n private resolveThinkingBudget(reasoning: ModelCallOptions[\"reasoning\"]): number | undefined {\n if (!this.capabilities.reasoning || !reasoning || reasoning.effort === \"none\") {\n return undefined;\n }\n\n const budgetTokens = reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[reasoning.effort ?? \"\"];\n\n if (budgetTokens !== undefined && budgetTokens < MIN_THINKING_BUDGET) {\n throw new Error(`thinking budget (${budgetTokens}) must be at least ${MIN_THINKING_BUDGET}`);\n }\n\n return budgetTokens;\n }\n\n /** Resolve Bedrock's output ceiling while reserving room for extended thinking. */\n private resolveMaxTokens(maxTokens: number | undefined, thinkingBudget: number | undefined): number | undefined {\n if (thinkingBudget === undefined) {\n return maxTokens;\n }\n\n if (maxTokens === undefined) {\n return thinkingBudget + DEFAULT_MAX_TOKENS;\n }\n\n if (maxTokens <= thinkingBudget) {\n throw new Error(`maxTokens (${maxTokens}) must be greater than thinking budget (${thinkingBudget})`);\n }\n\n return maxTokens;\n }\n\n private buildReasoningConfig(\n budgetTokens: number | undefined,\n ): Pick<ConverseRequest, \"additionalModelRequestFields\"> {\n if (budgetTokens === undefined) {\n return {};\n }\n\n return {\n additionalModelRequestFields: {\n thinking: { type: \"enabled\", budget_tokens: budgetTokens },\n },\n };\n }\n\n /**\n * Spread-friendly tool fragment. Returns an empty object when no\n * tools were supplied (Bedrock rejects an empty `tools` array).\n */\n private buildToolConfig(tools: ModelCallOptions[\"tools\"]): Pick<ConverseRequest, \"toolConfig\"> {\n const toolConfig = toBedrockToolConfig(tools);\n\n return toolConfig ? { toolConfig } : {};\n }\n\n /**\n * Translate the neutral `responseSchema` into Converse's native\n * `outputConfig.textFormat` (JSON-schema structured output). Bedrock\n * requires the schema as a stringified JSON document and only\n * accepts an object root. Emitted only when the model is\n * `structuredOutput`-capable and the schema is an object — otherwise\n * the agent's soft system-prompt hint + client-side `validate()`\n * carry shape (same degradation philosophy as the OpenAI adapter).\n */\n private buildOutputConfig(\n responseSchema: Record<string, unknown> | undefined,\n ): Pick<ConverseRequest, \"outputConfig\"> {\n if (!responseSchema || !this.capabilities.structuredOutput) {\n return {};\n }\n\n if (responseSchema.type !== \"object\" || typeof responseSchema.properties !== \"object\") {\n return {};\n }\n\n return {\n outputConfig: {\n textFormat: {\n type: \"json_schema\",\n structure: {\n jsonSchema: { name: \"response\", schema: JSON.stringify(responseSchema) },\n },\n },\n },\n };\n }\n\n /**\n * Concatenate every `text` content block into the single neutral\n * `content` string. `toolUse` and other block types are surfaced\n * separately via `extractToolCalls`.\n */\n private extractText(blocks: ContentBlock[]): string {\n return blocks\n .map((block) => (\"text\" in block && typeof block.text === \"string\" ? block.text : \"\"))\n .join(\"\");\n }\n\n /**\n * Reshape Converse `toolUse` content blocks into the neutral\n * `ModelToolCallRequest[]`. Returns `undefined` when no tools were\n * requested so callers can branch on presence.\n */\n private extractToolCalls(blocks: ContentBlock[]): ModelToolCallRequest[] | undefined {\n const toolCalls: ModelToolCallRequest[] = [];\n const reasoningBlocks = blocks.map(toReasoningMetadata).filter(\n (block): block is BedrockReasoningBlock => block !== undefined,\n );\n\n for (const block of blocks) {\n if (\"toolUse\" in block && block.toolUse) {\n toolCalls.push({\n id: block.toolUse.toolUseId ?? \"\",\n name: block.toolUse.name ?? \"\",\n input: (block.toolUse.input ?? {}) as Record<string, unknown>,\n ...(reasoningBlocks.length > 0\n ? { providerMetadata: { bedrock: { reasoningBlocks } } }\n : {}),\n });\n }\n }\n\n return toolCalls.length > 0 ? toolCalls : undefined;\n }\n\n /** Encode accumulated stream deltas as persistence-safe reasoning metadata. */\n private toReasoningMetadata(block: {\n text?: string;\n signature?: string;\n redactedContent?: Uint8Array;\n }): BedrockReasoningBlock | undefined {\n if (block.redactedContent !== undefined) {\n return toReasoningMetadata({ reasoningContent: { redactedContent: block.redactedContent } });\n }\n\n return toReasoningMetadata({\n reasoningContent: {\n reasoningText: { text: block.text ?? \"\", ...(block.signature ? { signature: block.signature } : {}) },\n },\n });\n }\n\n /**\n * Normalize Converse's `TokenUsage` into the neutral `Usage` shape.\n * Bedrock supplies a pre-summed `totalTokens`; cache-read and\n * cache-write tokens are surfaced as `cachedTokens` /\n * `cacheWriteTokens` only when non-zero so callers can price the\n * discounted read rate and the one-time write cost separately.\n * Bedrock's Converse `TokenUsage` carries no reasoning-token channel,\n * so `Usage.reasoningTokens` is intentionally left unset here.\n */\n private extractUsage(raw: TokenUsage | undefined): Usage {\n if (!raw) {\n return { input: 0, output: 0, total: 0 };\n }\n\n const input = raw.inputTokens ?? 0;\n const output = raw.outputTokens ?? 0;\n const cached = raw.cacheReadInputTokens;\n const cacheWrite = raw.cacheWriteInputTokens;\n\n return {\n input,\n output,\n total: raw.totalTokens ?? input + output,\n ...(cached && cached > 0 ? { cachedTokens: cached } : {}),\n ...(cacheWrite && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {}),\n };\n }\n\n /**\n * Wrap a thrown provider error into the typed `AIError` hierarchy\n * and emit the standard error log line before it propagates. Shared\n * by every catch site so the log shape stays identical.\n */\n private logAndWrap(thrown: unknown) {\n const wrapped = wrapBedrockError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n return wrapped;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ //#region ../ai-bedrock/src/utils/reasoning-blocks.ts
2
+ /** Convert a Converse reasoning content block to persistence-safe metadata. */
3
+ function toReasoningMetadata(block) {
4
+ const reasoning = block.reasoningContent;
5
+ if (!reasoning) return;
6
+ if (reasoning.reasoningText) {
7
+ const { text, signature } = reasoning.reasoningText;
8
+ if (typeof text !== "string") return;
9
+ return {
10
+ type: "reasoning",
11
+ text,
12
+ ...typeof signature === "string" ? { signature } : {}
13
+ };
14
+ }
15
+ if (reasoning.redactedContent instanceof Uint8Array) return {
16
+ type: "redacted",
17
+ data: Buffer.from(reasoning.redactedContent).toString("base64")
18
+ };
19
+ }
20
+ /** Restore one persistence-safe metadata entry to a Converse reasoning block. */
21
+ function fromReasoningMetadata(value) {
22
+ if (!isRecord(value) || typeof value.type !== "string") return;
23
+ if (value.type === "reasoning" && typeof value.text === "string") return { reasoningContent: { reasoningText: {
24
+ text: value.text,
25
+ ...typeof value.signature === "string" ? { signature: value.signature } : {}
26
+ } } };
27
+ if (value.type === "redacted" && typeof value.data === "string" && isBase64(value.data)) return { reasoningContent: { redactedContent: new Uint8Array(Buffer.from(value.data, "base64")) } };
28
+ }
29
+ function isRecord(value) {
30
+ return typeof value === "object" && value !== null;
31
+ }
32
+ /** Require canonical base64, avoiding Buffer's permissive decoding of garbage. */
33
+ function isBase64(value) {
34
+ if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) return false;
35
+ return Buffer.from(value, "base64").toString("base64") === value;
36
+ }
37
+
38
+ //#endregion
39
+ export { fromReasoningMetadata, toReasoningMetadata };
40
+ //# sourceMappingURL=reasoning-blocks.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reasoning-blocks.mjs","names":[],"sources":["../../../../../../../ai-bedrock/src/utils/reasoning-blocks.ts"],"sourcesContent":["import type { ContentBlock } from \"@aws-sdk/client-bedrock-runtime\";\n\n/**\n * JSON-safe representation of a Bedrock Converse reasoning block retained in\n * tool-call provider metadata. Binary redacted reasoning is base64 encoded so\n * it survives message-history persistence unchanged.\n */\nexport type BedrockReasoningBlock =\n | { type: \"reasoning\"; text: string; signature?: string }\n | { type: \"redacted\"; data: string };\n\n/** Convert a Converse reasoning content block to persistence-safe metadata. */\nexport function toReasoningMetadata(block: ContentBlock): BedrockReasoningBlock | undefined {\n const reasoning = block.reasoningContent;\n\n if (!reasoning) {\n return undefined;\n }\n\n if (reasoning.reasoningText) {\n const { text, signature } = reasoning.reasoningText;\n\n if (typeof text !== \"string\") {\n return undefined;\n }\n\n return { type: \"reasoning\", text, ...(typeof signature === \"string\" ? { signature } : {}) };\n }\n\n if (reasoning.redactedContent instanceof Uint8Array) {\n return { type: \"redacted\", data: Buffer.from(reasoning.redactedContent).toString(\"base64\") };\n }\n\n return undefined;\n}\n\n/** Restore one persistence-safe metadata entry to a Converse reasoning block. */\nexport function fromReasoningMetadata(value: unknown): ContentBlock | undefined {\n if (!isRecord(value) || typeof value.type !== \"string\") {\n return undefined;\n }\n\n if (value.type === \"reasoning\" && typeof value.text === \"string\") {\n return {\n reasoningContent: {\n reasoningText: {\n text: value.text,\n ...(typeof value.signature === \"string\" ? { signature: value.signature } : {}),\n },\n },\n };\n }\n\n if (value.type === \"redacted\" && typeof value.data === \"string\" && isBase64(value.data)) {\n return { reasoningContent: { redactedContent: new Uint8Array(Buffer.from(value.data, \"base64\")) } };\n }\n\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/** Require canonical base64, avoiding Buffer's permissive decoding of garbage. */\nfunction isBase64(value: string): boolean {\n if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) {\n return false;\n }\n\n return Buffer.from(value, \"base64\").toString(\"base64\") === value;\n}\n"],"mappings":";;AAYA,SAAgB,oBAAoB,OAAwD;CAC1F,MAAM,YAAY,MAAM;CAExB,IAAI,CAAC,WACH;CAGF,IAAI,UAAU,eAAe;EAC3B,MAAM,EAAE,MAAM,cAAc,UAAU;EAEtC,IAAI,OAAO,SAAS,UAClB;EAGF,OAAO;GAAE,MAAM;GAAa;GAAM,GAAI,OAAO,cAAc,WAAW,EAAE,UAAU,IAAI,CAAC;EAAG;CAC5F;CAEA,IAAI,UAAU,2BAA2B,YACvC,OAAO;EAAE,MAAM;EAAY,MAAM,OAAO,KAAK,UAAU,eAAe,CAAC,CAAC,SAAS,QAAQ;CAAE;AAI/F;;AAGA,SAAgB,sBAAsB,OAA0C;CAC9E,IAAI,CAAC,SAAS,KAAK,KAAK,OAAO,MAAM,SAAS,UAC5C;CAGF,IAAI,MAAM,SAAS,eAAe,OAAO,MAAM,SAAS,UACtD,OAAO,EACL,kBAAkB,EAChB,eAAe;EACb,MAAM,MAAM;EACZ,GAAI,OAAO,MAAM,cAAc,WAAW,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;CAC9E,EACF,EACF;CAGF,IAAI,MAAM,SAAS,cAAc,OAAO,MAAM,SAAS,YAAY,SAAS,MAAM,IAAI,GACpF,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,IAAI,WAAW,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC,EAAE,EAAE;AAItG;AAEA,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU;AAChD;;AAGA,SAAS,SAAS,OAAwB;CACxC,IAAI,CAAC,mEAAmE,KAAK,KAAK,GAChF,OAAO;CAGT,OAAO,OAAO,KAAK,OAAO,QAAQ,CAAC,CAAC,SAAS,QAAQ,MAAM;AAC7D"}
@@ -1,3 +1,4 @@
1
+ import { fromReasoningMetadata } from "./reasoning-blocks.mjs";
1
2
  import { InvalidRequestError } from "@warlock.js/ai";
2
3
 
3
4
  //#region ../ai-bedrock/src/utils/to-bedrock-messages.ts
@@ -49,6 +50,8 @@ function toBedrockMessages(messages) {
49
50
  if (message.role === "assistant" && message.toolCalls && message.toolCalls.length > 0) {
50
51
  const blocks = [];
51
52
  const text = stringifyContent(message.content);
53
+ const reasoningBlocks = getReasoningBlocks(message.toolCalls);
54
+ blocks.push(...reasoningBlocks);
52
55
  if (text) blocks.push({ text });
53
56
  for (const toolCall of message.toolCalls) blocks.push({ toolUse: {
54
57
  toolUseId: toolCall.id,
@@ -79,6 +82,23 @@ function toBedrockMessages(messages) {
79
82
  };
80
83
  }
81
84
  /**
85
+ * Recover opaque Converse reasoning blocks retained on a prior tool call.
86
+ * Bedrock requires them to be replayed before the assistant's text and
87
+ * tool-use blocks on the follow-up request.
88
+ */
89
+ function getReasoningBlocks(toolCalls) {
90
+ for (const toolCall of toolCalls) {
91
+ const bedrock = toolCall.providerMetadata?.bedrock;
92
+ if (!isRecord(bedrock) || !Array.isArray(bedrock.reasoningBlocks)) continue;
93
+ return bedrock.reasoningBlocks.map(fromReasoningMetadata).filter((block) => block !== void 0);
94
+ }
95
+ return [];
96
+ }
97
+ /** Narrow an opaque metadata value to an object without altering its contents. */
98
+ function isRecord(value) {
99
+ return typeof value === "object" && value !== null;
100
+ }
101
+ /**
82
102
  * Multipart content is only meaningful on user messages — for any other
83
103
  * role collapse a `ContentPart[]` to its concatenated text. Plain
84
104
  * strings pass through unchanged.
@@ -1 +1 @@
1
- {"version":3,"file":"to-bedrock-messages.mjs","names":[],"sources":["../../../../../../../ai-bedrock/src/utils/to-bedrock-messages.ts"],"sourcesContent":["import { InvalidRequestError, type ContentPart, type Message } from \"@warlock.js/ai\";\nimport type {\n ContentBlock,\n ImageFormat,\n Message as BedrockMessage,\n SystemContentBlock,\n} from \"@aws-sdk/client-bedrock-runtime\";\n\n/**\n * Result of splitting a vendor-neutral `Message[]` for the Bedrock\n * Converse API: system prompts hoist to a separate `SystemContentBlock[]`\n * (Converse has no `\"system\"` role inside `messages`), and the\n * remaining turns map to Bedrock `Message[]`.\n */\nexport type BedrockMessages = {\n system: SystemContentBlock[] | undefined;\n messages: BedrockMessage[];\n};\n\nconst MEDIA_TYPE_TO_FORMAT: Record<string, ImageFormat> = {\n \"image/jpeg\": \"jpeg\",\n \"image/png\": \"png\",\n \"image/gif\": \"gif\",\n \"image/webp\": \"webp\",\n};\n\n/**\n * Convert vendor-neutral `Message[]` into Bedrock Converse's request\n * shape.\n *\n * Converse differs from the OpenAI Chat protocol in three ways this\n * function absorbs:\n *\n * 1. **No `system` role.** System messages become a separate\n * `SystemContentBlock[]` (one `{ text }` block each).\n * 2. **Tool results are `user` turns.** A neutral `tool` message\n * becomes a `user` message carrying a single `toolResult` block.\n * 3. **Tool calls are `toolUse` content blocks.** An assistant message\n * with `toolCalls` becomes an `assistant` message: an optional\n * leading `text` block followed by one `toolUse` block per call.\n *\n * @example\n * const { system, messages } = toBedrockMessages([\n * { role: \"system\", content: \"Be concise.\" },\n * { role: \"user\", content: \"Hi\" },\n * ]);\n */\nexport function toBedrockMessages(messages: Message[]): BedrockMessages {\n const system: SystemContentBlock[] = [];\n const mapped: BedrockMessage[] = [];\n\n for (const message of messages) {\n if (message.role === \"system\") {\n system.push({ text: stringifyContent(message.content) });\n\n continue;\n }\n\n if (message.role === \"tool\") {\n mapped.push({\n role: \"user\",\n content: [\n {\n toolResult: {\n toolUseId: message.toolCallId ?? \"\",\n content: [{ text: stringifyContent(message.content) }],\n },\n },\n ],\n });\n\n continue;\n }\n\n if (message.role === \"assistant\" && message.toolCalls && message.toolCalls.length > 0) {\n const blocks: ContentBlock[] = [];\n const text = stringifyContent(message.content);\n\n if (text) {\n blocks.push({ text });\n }\n\n for (const toolCall of message.toolCalls) {\n blocks.push({\n toolUse: {\n toolUseId: toolCall.id,\n name: toolCall.name,\n input: toolCall.input ?? {},\n },\n } as ContentBlock);\n }\n\n mapped.push({ role: \"assistant\", content: blocks });\n\n continue;\n }\n\n if (message.role === \"user\" && Array.isArray(message.content)) {\n mapped.push({\n role: \"user\",\n content: message.content.map(toBedrockContentBlock),\n });\n\n continue;\n }\n\n mapped.push({\n role: message.role === \"assistant\" ? \"assistant\" : \"user\",\n content: [{ text: stringifyContent(message.content) }],\n });\n }\n\n return {\n system: system.length > 0 ? system : undefined,\n messages: mapped,\n };\n}\n\n/**\n * Multipart content is only meaningful on user messages — for any other\n * role collapse a `ContentPart[]` to its concatenated text. Plain\n * strings pass through unchanged.\n */\nfunction stringifyContent(content: string | ContentPart[]): string {\n if (typeof content === \"string\") {\n return content;\n }\n\n return content\n .filter((part): part is { type: \"text\"; text: string } => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\");\n}\n\n/**\n * Map a resolved `ContentPart` to a Bedrock `ContentBlock`. Bedrock's\n * `ImageSource` only accepts raw bytes or an S3 location — there is no\n * remote-URL source. A neutral `{ url }` image therefore cannot be\n * sent and surfaces a typed `InvalidRequestError` upfront rather than\n * a downstream Bedrock validation fault. The agent has already\n * resolved attachments, so this never fetches or reads anything.\n */\nfunction toBedrockContentBlock(part: ContentPart): ContentBlock {\n if (part.type === \"text\") {\n return { text: part.text };\n }\n\n if (\"url\" in part.source) {\n throw new InvalidRequestError(\n \"Bedrock Converse does not support remote-URL sources; supply base64 bytes instead.\",\n );\n }\n\n // PDF → Bedrock `document` content block (A2). Converse accepts a\n // document block with raw bytes; the agent gates this on the model's\n // `pdf` capability before it reaches here.\n if (part.type === \"pdf\") {\n return {\n document: {\n format: \"pdf\",\n name: \"attachment\",\n source: { bytes: Buffer.from(part.source.base64, \"base64\") },\n },\n } as unknown as ContentBlock;\n }\n\n // Bedrock Converse has no audio content block (capability stays false).\n if (part.type === \"audio\") {\n throw new InvalidRequestError(\n \"Bedrock Converse does not support audio attachments.\",\n );\n }\n\n const format = MEDIA_TYPE_TO_FORMAT[part.source.mediaType];\n\n if (!format) {\n throw new InvalidRequestError(\n `Unsupported image media type for Bedrock: \"${part.source.mediaType}\" (expected image/jpeg, image/png, image/gif, or image/webp).`,\n );\n }\n\n return {\n image: {\n format,\n source: { bytes: Buffer.from(part.source.base64, \"base64\") },\n },\n };\n}\n"],"mappings":";;;AAmBA,MAAM,uBAAoD;CACxD,cAAc;CACd,aAAa;CACb,aAAa;CACb,cAAc;AAChB;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,kBAAkB,UAAsC;CACtE,MAAM,SAA+B,CAAC;CACtC,MAAM,SAA2B,CAAC;CAElC,KAAK,MAAM,WAAW,UAAU;EAC9B,IAAI,QAAQ,SAAS,UAAU;GAC7B,OAAO,KAAK,EAAE,MAAM,iBAAiB,QAAQ,OAAO,EAAE,CAAC;GAEvD;EACF;EAEA,IAAI,QAAQ,SAAS,QAAQ;GAC3B,OAAO,KAAK;IACV,MAAM;IACN,SAAS,CACP,EACE,YAAY;KACV,WAAW,QAAQ,cAAc;KACjC,SAAS,CAAC,EAAE,MAAM,iBAAiB,QAAQ,OAAO,EAAE,CAAC;IACvD,EACF,CACF;GACF,CAAC;GAED;EACF;EAEA,IAAI,QAAQ,SAAS,eAAe,QAAQ,aAAa,QAAQ,UAAU,SAAS,GAAG;GACrF,MAAM,SAAyB,CAAC;GAChC,MAAM,OAAO,iBAAiB,QAAQ,OAAO;GAE7C,IAAI,MACF,OAAO,KAAK,EAAE,KAAK,CAAC;GAGtB,KAAK,MAAM,YAAY,QAAQ,WAC7B,OAAO,KAAK,EACV,SAAS;IACP,WAAW,SAAS;IACpB,MAAM,SAAS;IACf,OAAO,SAAS,SAAS,CAAC;GAC5B,EACF,CAAiB;GAGnB,OAAO,KAAK;IAAE,MAAM;IAAa,SAAS;GAAO,CAAC;GAElD;EACF;EAEA,IAAI,QAAQ,SAAS,UAAU,MAAM,QAAQ,QAAQ,OAAO,GAAG;GAC7D,OAAO,KAAK;IACV,MAAM;IACN,SAAS,QAAQ,QAAQ,IAAI,qBAAqB;GACpD,CAAC;GAED;EACF;EAEA,OAAO,KAAK;GACV,MAAM,QAAQ,SAAS,cAAc,cAAc;GACnD,SAAS,CAAC,EAAE,MAAM,iBAAiB,QAAQ,OAAO,EAAE,CAAC;EACvD,CAAC;CACH;CAEA,OAAO;EACL,QAAQ,OAAO,SAAS,IAAI,SAAS;EACrC,UAAU;CACZ;AACF;;;;;;AAOA,SAAS,iBAAiB,SAAyC;CACjE,IAAI,OAAO,YAAY,UACrB,OAAO;CAGT,OAAO,QACJ,QAAQ,SAAiD,KAAK,SAAS,MAAM,CAAC,CAC9E,KAAK,SAAS,KAAK,IAAI,CAAC,CACxB,KAAK,EAAE;AACZ;;;;;;;;;AAUA,SAAS,sBAAsB,MAAiC;CAC9D,IAAI,KAAK,SAAS,QAChB,OAAO,EAAE,MAAM,KAAK,KAAK;CAG3B,IAAI,SAAS,KAAK,QAChB,MAAM,IAAI,oBACR,oFACF;CAMF,IAAI,KAAK,SAAS,OAChB,OAAO,EACL,UAAU;EACR,QAAQ;EACR,MAAM;EACN,QAAQ,EAAE,OAAO,OAAO,KAAK,KAAK,OAAO,QAAQ,QAAQ,EAAE;CAC7D,EACF;CAIF,IAAI,KAAK,SAAS,SAChB,MAAM,IAAI,oBACR,sDACF;CAGF,MAAM,SAAS,qBAAqB,KAAK,OAAO;CAEhD,IAAI,CAAC,QACH,MAAM,IAAI,oBACR,8CAA8C,KAAK,OAAO,UAAU,8DACtE;CAGF,OAAO,EACL,OAAO;EACL;EACA,QAAQ,EAAE,OAAO,OAAO,KAAK,KAAK,OAAO,QAAQ,QAAQ,EAAE;CAC7D,EACF;AACF"}
1
+ {"version":3,"file":"to-bedrock-messages.mjs","names":[],"sources":["../../../../../../../ai-bedrock/src/utils/to-bedrock-messages.ts"],"sourcesContent":["import { InvalidRequestError, type ContentPart, type Message } from \"@warlock.js/ai\";\nimport type {\n ContentBlock,\n ImageFormat,\n Message as BedrockMessage,\n SystemContentBlock,\n} from \"@aws-sdk/client-bedrock-runtime\";\nimport { fromReasoningMetadata } from \"./reasoning-blocks\";\n\n/**\n * Result of splitting a vendor-neutral `Message[]` for the Bedrock\n * Converse API: system prompts hoist to a separate `SystemContentBlock[]`\n * (Converse has no `\"system\"` role inside `messages`), and the\n * remaining turns map to Bedrock `Message[]`.\n */\nexport type BedrockMessages = {\n system: SystemContentBlock[] | undefined;\n messages: BedrockMessage[];\n};\n\nconst MEDIA_TYPE_TO_FORMAT: Record<string, ImageFormat> = {\n \"image/jpeg\": \"jpeg\",\n \"image/png\": \"png\",\n \"image/gif\": \"gif\",\n \"image/webp\": \"webp\",\n};\n\n/**\n * Convert vendor-neutral `Message[]` into Bedrock Converse's request\n * shape.\n *\n * Converse differs from the OpenAI Chat protocol in three ways this\n * function absorbs:\n *\n * 1. **No `system` role.** System messages become a separate\n * `SystemContentBlock[]` (one `{ text }` block each).\n * 2. **Tool results are `user` turns.** A neutral `tool` message\n * becomes a `user` message carrying a single `toolResult` block.\n * 3. **Tool calls are `toolUse` content blocks.** An assistant message\n * with `toolCalls` becomes an `assistant` message: an optional\n * leading `text` block followed by one `toolUse` block per call.\n *\n * @example\n * const { system, messages } = toBedrockMessages([\n * { role: \"system\", content: \"Be concise.\" },\n * { role: \"user\", content: \"Hi\" },\n * ]);\n */\nexport function toBedrockMessages(messages: Message[]): BedrockMessages {\n const system: SystemContentBlock[] = [];\n const mapped: BedrockMessage[] = [];\n\n for (const message of messages) {\n if (message.role === \"system\") {\n system.push({ text: stringifyContent(message.content) });\n\n continue;\n }\n\n if (message.role === \"tool\") {\n mapped.push({\n role: \"user\",\n content: [\n {\n toolResult: {\n toolUseId: message.toolCallId ?? \"\",\n content: [{ text: stringifyContent(message.content) }],\n },\n },\n ],\n });\n\n continue;\n }\n\n if (message.role === \"assistant\" && message.toolCalls && message.toolCalls.length > 0) {\n const blocks: ContentBlock[] = [];\n const text = stringifyContent(message.content);\n const reasoningBlocks = getReasoningBlocks(message.toolCalls);\n\n blocks.push(...reasoningBlocks);\n\n if (text) {\n blocks.push({ text });\n }\n\n for (const toolCall of message.toolCalls) {\n blocks.push({\n toolUse: {\n toolUseId: toolCall.id,\n name: toolCall.name,\n input: toolCall.input ?? {},\n },\n } as ContentBlock);\n }\n\n mapped.push({ role: \"assistant\", content: blocks });\n\n continue;\n }\n\n if (message.role === \"user\" && Array.isArray(message.content)) {\n mapped.push({\n role: \"user\",\n content: message.content.map(toBedrockContentBlock),\n });\n\n continue;\n }\n\n mapped.push({\n role: message.role === \"assistant\" ? \"assistant\" : \"user\",\n content: [{ text: stringifyContent(message.content) }],\n });\n }\n\n return {\n system: system.length > 0 ? system : undefined,\n messages: mapped,\n };\n}\n\n/**\n * Recover opaque Converse reasoning blocks retained on a prior tool call.\n * Bedrock requires them to be replayed before the assistant's text and\n * tool-use blocks on the follow-up request.\n */\nfunction getReasoningBlocks(toolCalls: NonNullable<Message[\"toolCalls\"]>): ContentBlock[] {\n for (const toolCall of toolCalls) {\n const bedrock = toolCall.providerMetadata?.bedrock;\n\n if (!isRecord(bedrock) || !Array.isArray(bedrock.reasoningBlocks)) {\n continue;\n }\n\n return bedrock.reasoningBlocks.map(fromReasoningMetadata).filter(\n (block): block is ContentBlock => block !== undefined,\n );\n }\n\n return [];\n}\n\n/** Narrow an opaque metadata value to an object without altering its contents. */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * Multipart content is only meaningful on user messages — for any other\n * role collapse a `ContentPart[]` to its concatenated text. Plain\n * strings pass through unchanged.\n */\nfunction stringifyContent(content: string | ContentPart[]): string {\n if (typeof content === \"string\") {\n return content;\n }\n\n return content\n .filter((part): part is { type: \"text\"; text: string } => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\");\n}\n\n/**\n * Map a resolved `ContentPart` to a Bedrock `ContentBlock`. Bedrock's\n * `ImageSource` only accepts raw bytes or an S3 location — there is no\n * remote-URL source. A neutral `{ url }` image therefore cannot be\n * sent and surfaces a typed `InvalidRequestError` upfront rather than\n * a downstream Bedrock validation fault. The agent has already\n * resolved attachments, so this never fetches or reads anything.\n */\nfunction toBedrockContentBlock(part: ContentPart): ContentBlock {\n if (part.type === \"text\") {\n return { text: part.text };\n }\n\n if (\"url\" in part.source) {\n throw new InvalidRequestError(\n \"Bedrock Converse does not support remote-URL sources; supply base64 bytes instead.\",\n );\n }\n\n // PDF → Bedrock `document` content block (A2). Converse accepts a\n // document block with raw bytes; the agent gates this on the model's\n // `pdf` capability before it reaches here.\n if (part.type === \"pdf\") {\n return {\n document: {\n format: \"pdf\",\n name: \"attachment\",\n source: { bytes: Buffer.from(part.source.base64, \"base64\") },\n },\n } as unknown as ContentBlock;\n }\n\n // Bedrock Converse has no audio content block (capability stays false).\n if (part.type === \"audio\") {\n throw new InvalidRequestError(\n \"Bedrock Converse does not support audio attachments.\",\n );\n }\n\n const format = MEDIA_TYPE_TO_FORMAT[part.source.mediaType];\n\n if (!format) {\n throw new InvalidRequestError(\n `Unsupported image media type for Bedrock: \"${part.source.mediaType}\" (expected image/jpeg, image/png, image/gif, or image/webp).`,\n );\n }\n\n return {\n image: {\n format,\n source: { bytes: Buffer.from(part.source.base64, \"base64\") },\n },\n };\n}\n"],"mappings":"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"}
package/llms-full.txt CHANGED
@@ -72,6 +72,8 @@ await model.complete(messages, { reasoning: { effort: "high" } });
72
72
 
73
73
  `maxTokens` wins over `effort` when both are set. For non-reasoning models — or when neither `effort` nor `maxTokens` is given — the option is a no-op, so unsupported params never reach the wire. Bedrock's Converse `TokenUsage` reports no reasoning-token channel, so `usage.reasoningTokens` is left unset (reasoning text arrives as `reasoningContent` content blocks, not a token count).
74
74
 
75
+ A thinking budget below `1024` throws before the request. When thinking is enabled and no output cap is configured, Converse receives `maxTokens = thinking budget + 4096`; an explicit `maxTokens` must be greater than the budget. On tool calls, Converse `reasoningContent` is retained and replayed automatically on the next turn through provider metadata. The stored shape is JSON-safe: signed reasoning text stays text, while redacted reasoning bytes are base64 encoded, so persisted history preserves the opaque block exactly.
76
+
75
77
  ## Prompt caching
76
78
 
77
79
  For a `promptCaching`-capable model, `ModelCallOptions.cacheControl.breakpoints` (any positive value) appends one Converse `cachePoint: { type: "default" }` block to the LAST message — Bedrock then caches the whole prefix up to that point:
package/package.json CHANGED
@@ -14,13 +14,13 @@
14
14
  "url": "https://github.com/warlockjs/ai-bedrock"
15
15
  },
16
16
  "peerDependencies": {
17
- "@warlock.js/ai": "5.23.2"
17
+ "@warlock.js/ai": "5.24.0"
18
18
  },
19
19
  "dependencies": {
20
20
  "@aws-sdk/client-bedrock-runtime": "^3.1048.0",
21
- "@warlock.js/logger": "5.23.2"
21
+ "@warlock.js/logger": "5.24.0"
22
22
  },
23
- "version": "5.23.2",
23
+ "version": "5.24.0",
24
24
  "main": "./cjs/index.cjs",
25
25
  "module": "./esm/index.mjs",
26
26
  "types": "./esm/index.d.mts",
@@ -64,6 +64,8 @@ await model.complete(messages, { reasoning: { effort: "high" } });
64
64
 
65
65
  `maxTokens` wins over `effort` when both are set. For non-reasoning models — or when neither `effort` nor `maxTokens` is given — the option is a no-op, so unsupported params never reach the wire. Bedrock's Converse `TokenUsage` reports no reasoning-token channel, so `usage.reasoningTokens` is left unset (reasoning text arrives as `reasoningContent` content blocks, not a token count).
66
66
 
67
+ A thinking budget below `1024` throws before the request. When thinking is enabled and no output cap is configured, Converse receives `maxTokens = thinking budget + 4096`; an explicit `maxTokens` must be greater than the budget. On tool calls, Converse `reasoningContent` is retained and replayed automatically on the next turn through provider metadata. The stored shape is JSON-safe: signed reasoning text stays text, while redacted reasoning bytes are base64 encoded, so persisted history preserves the opaque block exactly.
68
+
67
69
  ## Prompt caching
68
70
 
69
71
  For a `promptCaching`-capable model, `ModelCallOptions.cacheControl.breakpoints` (any positive value) appends one Converse `cachePoint: { type: "default" }` block to the LAST message — Bedrock then caches the whole prefix up to that point: