@warlock.js/ai-bedrock 4.2.11 → 4.4.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,7 +4,14 @@ 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
- ## [Unreleased]
7
+ ## 4.3.0 - 2026-06-21
8
+
9
+ ### Added
10
+
11
+ - **Cost-truth contract wiring.** Capabilities now report `reasoning`, `promptCaching`, `pdf`, and `audio` truthfully per model family (inferred from the model id, overridable via `bedrock.model({ name, reasoning?, promptCaching?, pdf?, audio? })`): `reasoning` for Claude 3.7 + Claude 4, `promptCaching` for Claude 3.5+/3.7/4 and Nova, `pdf` for Claude 3+ and Nova, `audio` defaulting to `false`.
12
+ - **Reasoning / extended thinking.** `ModelCallOptions.reasoning` maps to Converse `additionalModelRequestFields.thinking = { type: "enabled", budget_tokens }` for reasoning-capable models — `reasoning.maxTokens` is the explicit budget, `reasoning.effort` (low/medium/high) maps to a conventional budget. No-ops for non-reasoning models so unsupported params never reach the wire.
13
+ - **Prompt-cache write breakpoints.** `ModelCallOptions.cacheControl.breakpoints` appends a Converse `cachePoint` block to the last message for `promptCaching`-capable models.
14
+ - **`Usage.cacheWriteTokens`** populated from Converse `usage.cacheWriteInputTokens` on both `complete()` and `stream()` (alongside the existing `cachedTokens` from `cacheReadInputTokens`). `Usage.reasoningTokens` is intentionally left unset — Bedrock's Converse `TokenUsage` reports no reasoning-token channel.
8
15
 
9
16
  ## 4.1.15
10
17
 
package/cjs/index.cjs CHANGED
@@ -402,6 +402,112 @@ var BedrockEmbedder = class {
402
402
  }
403
403
  };
404
404
 
405
+ //#endregion
406
+ //#region ../@warlock.js/ai-bedrock/src/known-capabilities.ts
407
+ /**
408
+ * Cost-truth capability inference for Bedrock Converse model ids.
409
+ *
410
+ * Bedrock model ids are provider-prefixed and version-suffixed
411
+ * (`anthropic.claude-3-7-sonnet-20250219-v1:0`, `us.amazon.nova-pro-v1:0`),
412
+ * so — exactly like `known-vision-models.ts` — a lowercase substring scan
413
+ * is the only robust check across cross-region inference-profile prefixes
414
+ * (`us.`, `eu.`, `apac.`) and date/version tags.
415
+ *
416
+ * Each predicate answers a single `ModelCapabilities` flag the agent reads
417
+ * to decide whether to forward a cost-truth option (`reasoning`,
418
+ * `cacheControl`) or up-front-reject an attachment (`pdf`). Unknown ids
419
+ * default to `false` so an unsupported request fails fast with a clear
420
+ * capability error instead of an opaque Bedrock `ValidationException`.
421
+ * Every inference is overridable per-model via `bedrock.model({ name, … })`.
422
+ */
423
+ /**
424
+ * Families that expose Anthropic-style extended thinking on Bedrock
425
+ * Converse via `additionalModelRequestFields.thinking`. Only Claude 3.7
426
+ * and the Claude 4 line (Sonnet / Opus / Haiku) support a configurable
427
+ * thinking budget; earlier Claude, Nova, Llama, Mistral and Cohere do
428
+ * not, so they are intentionally absent.
429
+ */
430
+ const REASONING_CAPABLE_SUBSTRINGS = [
431
+ "claude-3-7",
432
+ "claude-sonnet-4",
433
+ "claude-opus-4",
434
+ "claude-haiku-4"
435
+ ];
436
+ /**
437
+ * Families that honor Converse `cachePoint` prompt-cache breakpoints.
438
+ * Anthropic Claude 3.5+ / 3.7 / 4 and the Amazon Nova line support
439
+ * cache points; text-only legacy families do not.
440
+ */
441
+ const PROMPT_CACHING_CAPABLE_SUBSTRINGS = [
442
+ "claude-3-5",
443
+ "claude-3-7",
444
+ "claude-sonnet-4",
445
+ "claude-opus-4",
446
+ "claude-haiku-4",
447
+ "nova-lite",
448
+ "nova-pro",
449
+ "nova-premier",
450
+ "nova-micro"
451
+ ];
452
+ /**
453
+ * Families that accept Converse `document` content blocks (PDF / docx /
454
+ * txt input). The multimodal Claude 3+ and Nova families support
455
+ * document blocks; the substring set mirrors the vision-capable list
456
+ * minus the image-only Llama entries (Llama on Bedrock takes images but
457
+ * not document blocks via Converse).
458
+ */
459
+ const PDF_CAPABLE_SUBSTRINGS = [
460
+ "claude-3",
461
+ "claude-sonnet-4",
462
+ "claude-opus-4",
463
+ "claude-haiku-4",
464
+ "nova-lite",
465
+ "nova-pro",
466
+ "nova-premier"
467
+ ];
468
+ function matchesAny(modelId, fragments) {
469
+ const normalized = modelId.toLowerCase();
470
+ return fragments.some((fragment) => normalized.includes(fragment));
471
+ }
472
+ /**
473
+ * Infer whether a Bedrock model id exposes extended-thinking / reasoning
474
+ * (Claude 3.7 + Claude 4). When true the adapter forwards
475
+ * `ModelCallOptions.reasoning` as Converse
476
+ * `additionalModelRequestFields.thinking`.
477
+ *
478
+ * @example
479
+ * inferReasoningCapability("anthropic.claude-3-7-sonnet-20250219-v1:0"); // → true
480
+ * inferReasoningCapability("us.amazon.nova-pro-v1:0"); // → false
481
+ */
482
+ function inferReasoningCapability(modelId) {
483
+ return matchesAny(modelId, REASONING_CAPABLE_SUBSTRINGS);
484
+ }
485
+ /**
486
+ * Infer whether a Bedrock model id honors Converse `cachePoint`
487
+ * breakpoints (Claude 3.5+ / Nova). When true the adapter both maps
488
+ * `ModelCallOptions.cacheControl` write breakpoints to cache points and
489
+ * reports `Usage.cachedTokens` / `Usage.cacheWriteTokens`.
490
+ *
491
+ * @example
492
+ * inferPromptCachingCapability("us.amazon.nova-pro-v1:0"); // → true
493
+ * inferPromptCachingCapability("meta.llama3-1-8b-instruct-v1:0"); // → false
494
+ */
495
+ function inferPromptCachingCapability(modelId) {
496
+ return matchesAny(modelId, PROMPT_CACHING_CAPABLE_SUBSTRINGS);
497
+ }
498
+ /**
499
+ * Infer whether a Bedrock model id accepts Converse `document` content
500
+ * blocks (PDF / document input — Claude 3+ / Nova). When false the agent
501
+ * rejects a PDF attachment up front instead of dropping it at the wire.
502
+ *
503
+ * @example
504
+ * inferPdfCapability("anthropic.claude-3-5-sonnet-20240620-v1:0"); // → true
505
+ * inferPdfCapability("meta.llama3-2-90b-instruct-v1:0"); // → false
506
+ */
507
+ function inferPdfCapability(modelId) {
508
+ return matchesAny(modelId, PDF_CAPABLE_SUBSTRINGS);
509
+ }
510
+
405
511
  //#endregion
406
512
  //#region ../@warlock.js/ai-bedrock/src/known-vision-models.ts
407
513
  /**
@@ -454,6 +560,18 @@ function inferVisionCapability(modelId) {
454
560
  //#region ../@warlock.js/ai-bedrock/src/model.ts
455
561
  const LOG_MODULE = "ai.bedrock";
456
562
  /**
563
+ * Conventional extended-thinking token budgets for the neutral
564
+ * `reasoning.effort` levels, used when the caller asks for an effort
565
+ * tier without naming an explicit `reasoning.maxTokens` budget. Mirrors
566
+ * the low / medium / high spread other reasoning adapters expose so the
567
+ * vendor-neutral option behaves consistently across providers.
568
+ */
569
+ const EFFORT_THINKING_BUDGET = {
570
+ low: 1024,
571
+ medium: 4096,
572
+ high: 16384
573
+ };
574
+ /**
457
575
  * Bedrock-backed implementation of `ModelContract`.
458
576
  *
459
577
  * **Role.** The provider-facing bridge between the vendor-neutral
@@ -498,7 +616,11 @@ var BedrockModel = class {
498
616
  this.pricing = config.pricing;
499
617
  this.capabilities = {
500
618
  structuredOutput: config.structuredOutput ?? true,
501
- vision: config.vision ?? inferVisionCapability(config.name)
619
+ vision: config.vision ?? inferVisionCapability(config.name),
620
+ reasoning: config.reasoning ?? inferReasoningCapability(config.name),
621
+ promptCaching: config.promptCaching ?? inferPromptCachingCapability(config.name),
622
+ pdf: config.pdf ?? inferPdfCapability(config.name),
623
+ audio: config.audio ?? false
502
624
  };
503
625
  }
504
626
  /**
@@ -604,6 +726,7 @@ var BedrockModel = class {
604
726
  usage.output = raw.outputTokens ?? 0;
605
727
  usage.total = raw.totalTokens ?? usage.input + usage.output;
606
728
  if (raw.cacheReadInputTokens && raw.cacheReadInputTokens > 0) usage.cachedTokens = raw.cacheReadInputTokens;
729
+ if (raw.cacheWriteInputTokens && raw.cacheWriteInputTokens > 0) usage.cacheWriteTokens = raw.cacheWriteInputTokens;
607
730
  }
608
731
  }
609
732
  } catch (thrown) {
@@ -630,19 +753,66 @@ var BedrockModel = class {
630
753
  const { system, messages: bedrockMessages } = toBedrockMessages(messages);
631
754
  const maxTokens = options?.maxTokens ?? this.config.maxTokens;
632
755
  const temperature = options?.temperature ?? this.config.temperature;
756
+ const cachedMessages = this.applyCacheBreakpoints(bedrockMessages, options?.cacheControl);
633
757
  return {
634
758
  modelId: this.name,
635
- messages: bedrockMessages,
759
+ messages: cachedMessages,
636
760
  ...system ? { system } : {},
637
761
  inferenceConfig: {
638
762
  ...maxTokens !== void 0 ? { maxTokens } : {},
639
763
  ...temperature !== void 0 ? { temperature } : {}
640
764
  },
641
765
  ...this.buildToolConfig(options?.tools),
642
- ...this.buildOutputConfig(options?.responseSchema)
766
+ ...this.buildOutputConfig(options?.responseSchema),
767
+ ...this.buildReasoningConfig(options?.reasoning)
643
768
  };
644
769
  }
645
770
  /**
771
+ * Append a Converse `cachePoint` block to the LAST message when the
772
+ * caller supplies a `cacheControl` write breakpoint and the model is
773
+ * `promptCaching`-capable. A cache point tells Bedrock to cache the
774
+ * whole prefix up to that block, so subsequent calls reusing the same
775
+ * prefix bill the cached portion at the discounted read rate
776
+ * (surfaced as `Usage.cachedTokens`). No-ops gracefully when caching
777
+ * is unsupported, no breakpoint was requested, or there are no
778
+ * messages to mark — Bedrock then prices the call normally.
779
+ *
780
+ * Bedrock only honors `CachePointType.DEFAULT`; the neutral
781
+ * `breakpoints` count is a presence hint (one trailing breakpoint is
782
+ * the only placement Converse supports without manual block surgery),
783
+ * so any positive value marks the trailing message.
784
+ */
785
+ applyCacheBreakpoints(messages, cacheControl) {
786
+ const breakpoints = cacheControl?.breakpoints ?? 0;
787
+ if (!this.capabilities.promptCaching || breakpoints <= 0 || !messages || messages.length === 0) return messages;
788
+ const last = messages.length - 1;
789
+ const lastMessage = messages[last];
790
+ return [...messages.slice(0, last), {
791
+ ...lastMessage,
792
+ content: [...lastMessage.content ?? [], { cachePoint: { type: "default" } }]
793
+ }];
794
+ }
795
+ /**
796
+ * Translate the neutral `reasoning` option into Claude-on-Bedrock's
797
+ * extended-thinking control, carried in Converse's escape hatch
798
+ * `additionalModelRequestFields.thinking`. Emitted only when the model
799
+ * is `reasoning`-capable and a budget can be resolved — `maxTokens`
800
+ * (explicit thinking budget) wins, otherwise `effort` maps to a
801
+ * conventional token budget so callers can opt in without picking a
802
+ * number. Returns an empty object (no-op) for non-reasoning models or
803
+ * when no reasoning option was supplied, so unsupported params never
804
+ * reach the wire.
805
+ */
806
+ buildReasoningConfig(reasoning) {
807
+ if (!this.capabilities.reasoning || !reasoning) return {};
808
+ const budgetTokens = reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[reasoning.effort ?? ""];
809
+ if (budgetTokens === void 0) return {};
810
+ return { additionalModelRequestFields: { thinking: {
811
+ type: "enabled",
812
+ budget_tokens: budgetTokens
813
+ } } };
814
+ }
815
+ /**
646
816
  * Spread-friendly tool fragment. Returns an empty object when no
647
817
  * tools were supplied (Bedrock rejects an empty `tools` array).
648
818
  */
@@ -694,8 +864,12 @@ var BedrockModel = class {
694
864
  }
695
865
  /**
696
866
  * Normalize Converse's `TokenUsage` into the neutral `Usage` shape.
697
- * Bedrock supplies a pre-summed `totalTokens`; cache-read tokens are
698
- * surfaced as `cachedTokens` only when non-zero.
867
+ * Bedrock supplies a pre-summed `totalTokens`; cache-read and
868
+ * cache-write tokens are surfaced as `cachedTokens` /
869
+ * `cacheWriteTokens` only when non-zero so callers can price the
870
+ * discounted read rate and the one-time write cost separately.
871
+ * Bedrock's Converse `TokenUsage` carries no reasoning-token channel,
872
+ * so `Usage.reasoningTokens` is intentionally left unset here.
699
873
  */
700
874
  extractUsage(raw) {
701
875
  if (!raw) return {
@@ -706,11 +880,13 @@ var BedrockModel = class {
706
880
  const input = raw.inputTokens ?? 0;
707
881
  const output = raw.outputTokens ?? 0;
708
882
  const cached = raw.cacheReadInputTokens;
883
+ const cacheWrite = raw.cacheWriteInputTokens;
709
884
  return {
710
885
  input,
711
886
  output,
712
887
  total: raw.totalTokens ?? input + output,
713
- ...cached && cached > 0 ? { cachedTokens: cached } : {}
888
+ ...cached && cached > 0 ? { cachedTokens: cached } : {},
889
+ ...cacheWrite && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {}
714
890
  };
715
891
  }
716
892
  /**
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":["../../../../../../@warlock.js/ai-bedrock/src/utils/map-stop-reason.ts","../../../../../../@warlock.js/ai-bedrock/src/utils/to-bedrock-messages.ts","../../../../../../@warlock.js/ai-bedrock/src/utils/to-bedrock-tools.ts","../../../../../../@warlock.js/ai-bedrock/src/utils/wrap-bedrock-error.ts","../../../../../../@warlock.js/ai-bedrock/src/embedder.ts","../../../../../../@warlock.js/ai-bedrock/src/known-vision-models.ts","../../../../../../@warlock.js/ai-bedrock/src/model.ts","../../../../../../@warlock.js/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 image sources; supply base64 image bytes instead.\",\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 * 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 { inferVisionCapability } from \"./known-vision-models\";\nimport { mapStopReason, toBedrockMessages, toBedrockToolConfig, wrapBedrockError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.bedrock\";\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 };\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 }\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\n return {\n modelId: this.name,\n messages: bedrockMessages,\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 };\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 tokens are\n * surfaced as `cachedTokens` only when non-zero.\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\n return {\n input,\n output,\n total: raw.totalTokens ?? input + output,\n ...(cached && cached > 0 ? { cachedTokens: cached } : {}),\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":["InvalidRequestError","AIError","ProviderTimeoutError","ProviderAuthError","QuotaExceededError","ProviderRateLimitError","ContextLengthExceededError","InvalidRequestError","ProviderError","LOG_MODULE","log","InvokeModelCommand","log","ConverseCommand","ConverseStreamCommand","BedrockRuntimeClient"],"sources":["../../../../../../@warlock.js/ai-bedrock/src/utils/map-stop-reason.ts","../../../../../../@warlock.js/ai-bedrock/src/utils/to-bedrock-messages.ts","../../../../../../@warlock.js/ai-bedrock/src/utils/to-bedrock-tools.ts","../../../../../../@warlock.js/ai-bedrock/src/utils/wrap-bedrock-error.ts","../../../../../../@warlock.js/ai-bedrock/src/embedder.ts","../../../../../../@warlock.js/ai-bedrock/src/known-capabilities.ts","../../../../../../@warlock.js/ai-bedrock/src/known-vision-models.ts","../../../../../../@warlock.js/ai-bedrock/src/model.ts","../../../../../../@warlock.js/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 image sources; supply base64 image bytes instead.\",\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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"}
@@ -72,6 +72,36 @@ type BedrockModelConfig = ModelConfig & {
72
72
  * hint into the system prompt instead.
73
73
  */
74
74
  structuredOutput?: boolean;
75
+ /**
76
+ * Override the auto-inferred reasoning / extended-thinking capability.
77
+ * When omitted, the adapter infers `true` for Claude 3.7 + Claude 4
78
+ * families (see `known-capabilities.ts`). When capable, the adapter
79
+ * forwards `ModelCallOptions.reasoning` as Converse
80
+ * `additionalModelRequestFields.thinking`.
81
+ */
82
+ reasoning?: boolean;
83
+ /**
84
+ * Override the auto-inferred prompt-caching capability. When omitted,
85
+ * the adapter infers `true` for Claude 3.5+/3.7/4 and Nova families.
86
+ * When capable, the adapter maps `ModelCallOptions.cacheControl` write
87
+ * breakpoints to Converse `cachePoint` blocks and reports
88
+ * `Usage.cachedTokens` / `Usage.cacheWriteTokens` from the response.
89
+ */
90
+ promptCaching?: boolean;
91
+ /**
92
+ * Override the auto-inferred PDF / document-input capability. When
93
+ * omitted, the adapter infers `true` for Claude 3+ / Nova families
94
+ * that accept Converse `document` content blocks. When false the
95
+ * agent rejects a PDF attachment up front.
96
+ */
97
+ pdf?: boolean;
98
+ /**
99
+ * Override the audio-input capability. Defaults to `false` — Bedrock
100
+ * Converse does not accept audio content blocks for the families this
101
+ * adapter targets. Set `true` only for a model id you have confirmed
102
+ * supports audio input on Converse.
103
+ */
104
+ audio?: boolean;
75
105
  };
76
106
  /**
77
107
  * Per-embedder configuration for `BedrockSDK.embedder()`. `name` is the
@@ -1 +1 @@
1
- {"version":3,"file":"config.type.d.mts","names":[],"sources":["../../../../../../@warlock.js/ai-bedrock/src/config.type.ts"],"mappings":";;;;;;AAoCA;;;;;;;;;;;;;;AAQuC;AAavC;;;;;;;;AAgBkB;AAclB;;;;AAAkD;;;KAnDtC,gBAAA,GAAmB,0BAAA;EAC7B,QAAA;;;;;;;EAOA,OAAA,GAAU,MAAA,SAAe,YAAA;AAAA;;;;;;;;;;;KAaf,kBAAA,GAAqB,WAAW;;;;;;;EAO1C,MAAA;;;;;;;;;EASA,gBAAA;AAAA;;;;;;;;;;;;KAcU,qBAAA,GAAwB,cAAc"}
1
+ {"version":3,"file":"config.type.d.mts","names":[],"sources":["../../../../../../@warlock.js/ai-bedrock/src/config.type.ts"],"mappings":";;;;;;AAoCA;;;;;;;;;;;;;;AAQuC;AAavC;;;;;;;;;;;;AA8CO;AAcP;;;KAjFY,gBAAA,GAAmB,0BAAA;EAC7B,QAAA;;;;;;;EAOA,OAAA,GAAU,MAAA,SAAe,YAAA;AAAA;;;;;;;;;;;KAaf,kBAAA,GAAqB,WAAW;;;;;;;EAO1C,MAAA;;;;;;;;;EASA,gBAAA;;;;;;;;EAQA,SAAA;;;;;;;;EAQA,aAAA;;;;;;;EAOA,GAAA;;;;;;;EAOA,KAAA;AAAA;;;;;;;;;;;;KAcU,qBAAA,GAAwB,cAAc"}
@@ -0,0 +1,108 @@
1
+ //#region ../@warlock.js/ai-bedrock/src/known-capabilities.ts
2
+ /**
3
+ * Cost-truth capability inference for Bedrock Converse model ids.
4
+ *
5
+ * Bedrock model ids are provider-prefixed and version-suffixed
6
+ * (`anthropic.claude-3-7-sonnet-20250219-v1:0`, `us.amazon.nova-pro-v1:0`),
7
+ * so — exactly like `known-vision-models.ts` — a lowercase substring scan
8
+ * is the only robust check across cross-region inference-profile prefixes
9
+ * (`us.`, `eu.`, `apac.`) and date/version tags.
10
+ *
11
+ * Each predicate answers a single `ModelCapabilities` flag the agent reads
12
+ * to decide whether to forward a cost-truth option (`reasoning`,
13
+ * `cacheControl`) or up-front-reject an attachment (`pdf`). Unknown ids
14
+ * default to `false` so an unsupported request fails fast with a clear
15
+ * capability error instead of an opaque Bedrock `ValidationException`.
16
+ * Every inference is overridable per-model via `bedrock.model({ name, … })`.
17
+ */
18
+ /**
19
+ * Families that expose Anthropic-style extended thinking on Bedrock
20
+ * Converse via `additionalModelRequestFields.thinking`. Only Claude 3.7
21
+ * and the Claude 4 line (Sonnet / Opus / Haiku) support a configurable
22
+ * thinking budget; earlier Claude, Nova, Llama, Mistral and Cohere do
23
+ * not, so they are intentionally absent.
24
+ */
25
+ const REASONING_CAPABLE_SUBSTRINGS = [
26
+ "claude-3-7",
27
+ "claude-sonnet-4",
28
+ "claude-opus-4",
29
+ "claude-haiku-4"
30
+ ];
31
+ /**
32
+ * Families that honor Converse `cachePoint` prompt-cache breakpoints.
33
+ * Anthropic Claude 3.5+ / 3.7 / 4 and the Amazon Nova line support
34
+ * cache points; text-only legacy families do not.
35
+ */
36
+ const PROMPT_CACHING_CAPABLE_SUBSTRINGS = [
37
+ "claude-3-5",
38
+ "claude-3-7",
39
+ "claude-sonnet-4",
40
+ "claude-opus-4",
41
+ "claude-haiku-4",
42
+ "nova-lite",
43
+ "nova-pro",
44
+ "nova-premier",
45
+ "nova-micro"
46
+ ];
47
+ /**
48
+ * Families that accept Converse `document` content blocks (PDF / docx /
49
+ * txt input). The multimodal Claude 3+ and Nova families support
50
+ * document blocks; the substring set mirrors the vision-capable list
51
+ * minus the image-only Llama entries (Llama on Bedrock takes images but
52
+ * not document blocks via Converse).
53
+ */
54
+ const PDF_CAPABLE_SUBSTRINGS = [
55
+ "claude-3",
56
+ "claude-sonnet-4",
57
+ "claude-opus-4",
58
+ "claude-haiku-4",
59
+ "nova-lite",
60
+ "nova-pro",
61
+ "nova-premier"
62
+ ];
63
+ function matchesAny(modelId, fragments) {
64
+ const normalized = modelId.toLowerCase();
65
+ return fragments.some((fragment) => normalized.includes(fragment));
66
+ }
67
+ /**
68
+ * Infer whether a Bedrock model id exposes extended-thinking / reasoning
69
+ * (Claude 3.7 + Claude 4). When true the adapter forwards
70
+ * `ModelCallOptions.reasoning` as Converse
71
+ * `additionalModelRequestFields.thinking`.
72
+ *
73
+ * @example
74
+ * inferReasoningCapability("anthropic.claude-3-7-sonnet-20250219-v1:0"); // → true
75
+ * inferReasoningCapability("us.amazon.nova-pro-v1:0"); // → false
76
+ */
77
+ function inferReasoningCapability(modelId) {
78
+ return matchesAny(modelId, REASONING_CAPABLE_SUBSTRINGS);
79
+ }
80
+ /**
81
+ * Infer whether a Bedrock model id honors Converse `cachePoint`
82
+ * breakpoints (Claude 3.5+ / Nova). When true the adapter both maps
83
+ * `ModelCallOptions.cacheControl` write breakpoints to cache points and
84
+ * reports `Usage.cachedTokens` / `Usage.cacheWriteTokens`.
85
+ *
86
+ * @example
87
+ * inferPromptCachingCapability("us.amazon.nova-pro-v1:0"); // → true
88
+ * inferPromptCachingCapability("meta.llama3-1-8b-instruct-v1:0"); // → false
89
+ */
90
+ function inferPromptCachingCapability(modelId) {
91
+ return matchesAny(modelId, PROMPT_CACHING_CAPABLE_SUBSTRINGS);
92
+ }
93
+ /**
94
+ * Infer whether a Bedrock model id accepts Converse `document` content
95
+ * blocks (PDF / document input — Claude 3+ / Nova). When false the agent
96
+ * rejects a PDF attachment up front instead of dropping it at the wire.
97
+ *
98
+ * @example
99
+ * inferPdfCapability("anthropic.claude-3-5-sonnet-20240620-v1:0"); // → true
100
+ * inferPdfCapability("meta.llama3-2-90b-instruct-v1:0"); // → false
101
+ */
102
+ function inferPdfCapability(modelId) {
103
+ return matchesAny(modelId, PDF_CAPABLE_SUBSTRINGS);
104
+ }
105
+
106
+ //#endregion
107
+ export { inferPdfCapability, inferPromptCachingCapability, inferReasoningCapability };
108
+ //# sourceMappingURL=known-capabilities.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"known-capabilities.mjs","names":[],"sources":["../../../../../../@warlock.js/ai-bedrock/src/known-capabilities.ts"],"sourcesContent":["/**\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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAM,+BAA+B;CACnC;CACA;CACA;CACA;AACF;;;;;;AAOA,MAAM,oCAAoC;CACxC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;;AASA,MAAM,yBAAyB;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,WAAW,SAAiB,WAA8B;CACjE,MAAM,aAAa,QAAQ,YAAY;CAEvC,OAAO,UAAU,MAAM,aAAa,WAAW,SAAS,QAAQ,CAAC;AACnE;;;;;;;;;;;AAYA,SAAgB,yBAAyB,SAA0B;CACjE,OAAO,WAAW,SAAS,4BAA4B;AACzD;;;;;;;;;;;AAYA,SAAgB,6BAA6B,SAA0B;CACrE,OAAO,WAAW,SAAS,iCAAiC;AAC9D;;;;;;;;;;AAWA,SAAgB,mBAAmB,SAA0B;CAC3D,OAAO,WAAW,SAAS,sBAAsB;AACnD"}
package/esm/model.mjs CHANGED
@@ -3,6 +3,7 @@ import { toBedrockMessages } from "./utils/to-bedrock-messages.mjs";
3
3
  import { toBedrockToolConfig } from "./utils/to-bedrock-tools.mjs";
4
4
  import { wrapBedrockError } from "./utils/wrap-bedrock-error.mjs";
5
5
  import "./utils/index.mjs";
6
+ import { inferPdfCapability, inferPromptCachingCapability, inferReasoningCapability } from "./known-capabilities.mjs";
6
7
  import { inferVisionCapability } from "./known-vision-models.mjs";
7
8
  import { ConverseCommand, ConverseStreamCommand } from "@aws-sdk/client-bedrock-runtime";
8
9
  import { safeJsonParse } from "@warlock.js/ai";
@@ -11,6 +12,18 @@ import { log } from "@warlock.js/logger";
11
12
  //#region ../@warlock.js/ai-bedrock/src/model.ts
12
13
  const LOG_MODULE = "ai.bedrock";
13
14
  /**
15
+ * Conventional extended-thinking token budgets for the neutral
16
+ * `reasoning.effort` levels, used when the caller asks for an effort
17
+ * tier without naming an explicit `reasoning.maxTokens` budget. Mirrors
18
+ * the low / medium / high spread other reasoning adapters expose so the
19
+ * vendor-neutral option behaves consistently across providers.
20
+ */
21
+ const EFFORT_THINKING_BUDGET = {
22
+ low: 1024,
23
+ medium: 4096,
24
+ high: 16384
25
+ };
26
+ /**
14
27
  * Bedrock-backed implementation of `ModelContract`.
15
28
  *
16
29
  * **Role.** The provider-facing bridge between the vendor-neutral
@@ -55,7 +68,11 @@ var BedrockModel = class {
55
68
  this.pricing = config.pricing;
56
69
  this.capabilities = {
57
70
  structuredOutput: config.structuredOutput ?? true,
58
- vision: config.vision ?? inferVisionCapability(config.name)
71
+ vision: config.vision ?? inferVisionCapability(config.name),
72
+ reasoning: config.reasoning ?? inferReasoningCapability(config.name),
73
+ promptCaching: config.promptCaching ?? inferPromptCachingCapability(config.name),
74
+ pdf: config.pdf ?? inferPdfCapability(config.name),
75
+ audio: config.audio ?? false
59
76
  };
60
77
  }
61
78
  /**
@@ -161,6 +178,7 @@ var BedrockModel = class {
161
178
  usage.output = raw.outputTokens ?? 0;
162
179
  usage.total = raw.totalTokens ?? usage.input + usage.output;
163
180
  if (raw.cacheReadInputTokens && raw.cacheReadInputTokens > 0) usage.cachedTokens = raw.cacheReadInputTokens;
181
+ if (raw.cacheWriteInputTokens && raw.cacheWriteInputTokens > 0) usage.cacheWriteTokens = raw.cacheWriteInputTokens;
164
182
  }
165
183
  }
166
184
  } catch (thrown) {
@@ -187,19 +205,66 @@ var BedrockModel = class {
187
205
  const { system, messages: bedrockMessages } = toBedrockMessages(messages);
188
206
  const maxTokens = options?.maxTokens ?? this.config.maxTokens;
189
207
  const temperature = options?.temperature ?? this.config.temperature;
208
+ const cachedMessages = this.applyCacheBreakpoints(bedrockMessages, options?.cacheControl);
190
209
  return {
191
210
  modelId: this.name,
192
- messages: bedrockMessages,
211
+ messages: cachedMessages,
193
212
  ...system ? { system } : {},
194
213
  inferenceConfig: {
195
214
  ...maxTokens !== void 0 ? { maxTokens } : {},
196
215
  ...temperature !== void 0 ? { temperature } : {}
197
216
  },
198
217
  ...this.buildToolConfig(options?.tools),
199
- ...this.buildOutputConfig(options?.responseSchema)
218
+ ...this.buildOutputConfig(options?.responseSchema),
219
+ ...this.buildReasoningConfig(options?.reasoning)
200
220
  };
201
221
  }
202
222
  /**
223
+ * Append a Converse `cachePoint` block to the LAST message when the
224
+ * caller supplies a `cacheControl` write breakpoint and the model is
225
+ * `promptCaching`-capable. A cache point tells Bedrock to cache the
226
+ * whole prefix up to that block, so subsequent calls reusing the same
227
+ * prefix bill the cached portion at the discounted read rate
228
+ * (surfaced as `Usage.cachedTokens`). No-ops gracefully when caching
229
+ * is unsupported, no breakpoint was requested, or there are no
230
+ * messages to mark — Bedrock then prices the call normally.
231
+ *
232
+ * Bedrock only honors `CachePointType.DEFAULT`; the neutral
233
+ * `breakpoints` count is a presence hint (one trailing breakpoint is
234
+ * the only placement Converse supports without manual block surgery),
235
+ * so any positive value marks the trailing message.
236
+ */
237
+ applyCacheBreakpoints(messages, cacheControl) {
238
+ const breakpoints = cacheControl?.breakpoints ?? 0;
239
+ if (!this.capabilities.promptCaching || breakpoints <= 0 || !messages || messages.length === 0) return messages;
240
+ const last = messages.length - 1;
241
+ const lastMessage = messages[last];
242
+ return [...messages.slice(0, last), {
243
+ ...lastMessage,
244
+ content: [...lastMessage.content ?? [], { cachePoint: { type: "default" } }]
245
+ }];
246
+ }
247
+ /**
248
+ * Translate the neutral `reasoning` option into Claude-on-Bedrock's
249
+ * extended-thinking control, carried in Converse's escape hatch
250
+ * `additionalModelRequestFields.thinking`. Emitted only when the model
251
+ * is `reasoning`-capable and a budget can be resolved — `maxTokens`
252
+ * (explicit thinking budget) wins, otherwise `effort` maps to a
253
+ * conventional token budget so callers can opt in without picking a
254
+ * number. Returns an empty object (no-op) for non-reasoning models or
255
+ * when no reasoning option was supplied, so unsupported params never
256
+ * reach the wire.
257
+ */
258
+ buildReasoningConfig(reasoning) {
259
+ if (!this.capabilities.reasoning || !reasoning) return {};
260
+ const budgetTokens = reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[reasoning.effort ?? ""];
261
+ if (budgetTokens === void 0) return {};
262
+ return { additionalModelRequestFields: { thinking: {
263
+ type: "enabled",
264
+ budget_tokens: budgetTokens
265
+ } } };
266
+ }
267
+ /**
203
268
  * Spread-friendly tool fragment. Returns an empty object when no
204
269
  * tools were supplied (Bedrock rejects an empty `tools` array).
205
270
  */
@@ -251,8 +316,12 @@ var BedrockModel = class {
251
316
  }
252
317
  /**
253
318
  * Normalize Converse's `TokenUsage` into the neutral `Usage` shape.
254
- * Bedrock supplies a pre-summed `totalTokens`; cache-read tokens are
255
- * surfaced as `cachedTokens` only when non-zero.
319
+ * Bedrock supplies a pre-summed `totalTokens`; cache-read and
320
+ * cache-write tokens are surfaced as `cachedTokens` /
321
+ * `cacheWriteTokens` only when non-zero so callers can price the
322
+ * discounted read rate and the one-time write cost separately.
323
+ * Bedrock's Converse `TokenUsage` carries no reasoning-token channel,
324
+ * so `Usage.reasoningTokens` is intentionally left unset here.
256
325
  */
257
326
  extractUsage(raw) {
258
327
  if (!raw) return {
@@ -263,11 +332,13 @@ var BedrockModel = class {
263
332
  const input = raw.inputTokens ?? 0;
264
333
  const output = raw.outputTokens ?? 0;
265
334
  const cached = raw.cacheReadInputTokens;
335
+ const cacheWrite = raw.cacheWriteInputTokens;
266
336
  return {
267
337
  input,
268
338
  output,
269
339
  total: raw.totalTokens ?? input + output,
270
- ...cached && cached > 0 ? { cachedTokens: cached } : {}
340
+ ...cached && cached > 0 ? { cachedTokens: cached } : {},
341
+ ...cacheWrite && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {}
271
342
  };
272
343
  }
273
344
  /**
package/esm/model.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"model.mjs","names":[],"sources":["../../../../../../@warlock.js/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 { inferVisionCapability } from \"./known-vision-models\";\nimport { mapStopReason, toBedrockMessages, toBedrockToolConfig, wrapBedrockError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.bedrock\";\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 };\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 }\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\n return {\n modelId: this.name,\n messages: bedrockMessages,\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 };\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 tokens are\n * surfaced as `cachedTokens` only when non-zero.\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\n return {\n input,\n output,\n total: raw.totalTokens ?? input + output,\n ...(cached && cached > 0 ? { cachedTokens: cached } : {}),\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":["../../../../../../@warlock.js/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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"}
package/llms-full.txt CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  ---
10
10
  name: setup-bedrock
11
- description: 'Wire @warlock.js/ai-bedrock — new BedrockSDK({region, credentials?, provider?}) for AWS Bedrock Converse API + Titan embeddings. AWS credential chain (no apiKey). Triggers: `BedrockSDK`, `bedrock.model`, `bedrock.embedder`, `bedrock.count`, `BedrockRuntimeClient`; "how do I use AWS Bedrock", "wire Claude/Nova/Llama on Bedrock", "Titan embeddings", "Bedrock Converse API"; typical import `import { BedrockSDK } from "@warlock.js/ai-bedrock"`. Skip: agent wiring — `@warlock.js/ai/run-ai-agent/SKILL.md`; provider choice — `@warlock.js/ai/pick-ai-provider/SKILL.md`; embeddings concepts — `@warlock.js/ai/embed-text/SKILL.md`; competing libs `@aws-sdk/client-bedrock-runtime`, `@ai-sdk/amazon-bedrock`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`.'
11
+ description: 'Wire @warlock.js/ai-bedrock — new BedrockSDK({region, credentials?, provider?}) for AWS Bedrock Converse API + Titan embeddings. AWS credential chain (no apiKey). .model({name, vision?, reasoning?, promptCaching?, pdf?, audio?}) with cost-truth capabilities inferred per family, extended thinking via options.reasoning → Converse thinking budget_tokens, prompt caching via options.cacheControl → cachePoint, usage cachedTokens/cacheWriteTokens. Triggers: `BedrockSDK`, `bedrock.model`, `bedrock.embedder`, `bedrock.count`, `BedrockRuntimeClient`, `reasoning`, `thinking`, `budget_tokens`, `cachePoint`, `cacheControl`, `promptCaching`, `cachedTokens`, `cacheWriteTokens`; "how do I use AWS Bedrock", "wire Claude/Nova/Llama on Bedrock", "Titan embeddings", "Bedrock Converse API", "Claude 3.7/4 extended thinking on Bedrock", "Bedrock prompt caching cost"; typical import `import { BedrockSDK } from "@warlock.js/ai-bedrock"`. Skip: agent wiring — `@warlock.js/ai/run-ai-agent/SKILL.md`; provider choice — `@warlock.js/ai/pick-ai-provider/SKILL.md`; embeddings concepts — `@warlock.js/ai/embed-text/SKILL.md`; competing libs `@aws-sdk/client-bedrock-runtime`, `@ai-sdk/amazon-bedrock`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`.'
12
12
  ---
13
13
 
14
14
  # `@warlock.js/ai-bedrock`
@@ -51,8 +51,36 @@ Uses the model-agnostic **Converse / ConverseStream** API — one wire mapping c
51
51
  | --- | --- |
52
52
  | `structuredOutput` | `true` (via Converse `outputConfig.textFormat`) |
53
53
  | `vision` | Inferred from model id substring. `true` for Claude 3/3.5/3.7/4, Nova Lite/Pro/Premier, Llama 3.2-11B/90B, Llama 4. |
54
+ | `reasoning` | Inferred from model id. `true` for Claude 3.7 + Claude 4 (Sonnet/Opus/Haiku) — they expose a configurable thinking budget on Converse. |
55
+ | `promptCaching` | Inferred from model id. `true` for Claude 3.5+/3.7/4 and Nova Micro/Lite/Pro/Premier — the families that honor Converse `cachePoint`. |
56
+ | `pdf` | Inferred from model id. `true` for Claude 3+ and Nova Lite/Pro/Premier (Converse `document` blocks). |
57
+ | `audio` | `false` by default — Converse does not accept audio blocks for the families this adapter targets. Pass `audio: true` for a confirmed audio model. |
54
58
 
55
- Explicit config always wins.
59
+ Explicit config always wins — every flag is overridable per model, e.g. `bedrock.model({ name, reasoning: false, promptCaching: true, pdf: false, audio: true })`.
60
+
61
+ ## Reasoning / extended thinking
62
+
63
+ For a `reasoning`-capable model (Claude 3.7 / 4), `ModelCallOptions.reasoning` maps to Converse's escape hatch `additionalModelRequestFields.thinking = { type: "enabled", budget_tokens }`:
64
+
65
+ ```ts
66
+ // Explicit thinking budget (token cap):
67
+ await model.complete(messages, { reasoning: { maxTokens: 2048 } });
68
+
69
+ // Effort tier → conventional budget (low 1024 / medium 4096 / high 16384):
70
+ await model.complete(messages, { reasoning: { effort: "high" } });
71
+ ```
72
+
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
+
75
+ ## Prompt caching
76
+
77
+ 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:
78
+
79
+ ```ts
80
+ await model.complete(messages, { cacheControl: { breakpoints: 1 } });
81
+ ```
82
+
83
+ Earlier turns are untouched; only the trailing message is marked. No-ops gracefully when the model is not caching-capable or no breakpoint was requested. Read-side accounting works without any breakpoint — see the usage section below.
56
84
 
57
85
  ## System prompt
58
86
 
@@ -81,7 +109,7 @@ Object-root `responseSchema` + `structuredOutput`-capable → `outputConfig.text
81
109
 
82
110
  - `contentBlockDelta.delta.text` → `{ type: "delta", content }`
83
111
  - `toolUse`: `contentBlockStart` opens an accumulator, `contentBlockDelta.delta.toolUse.input` fragments append, `contentBlockStop` emits one consolidated `{ type: "tool-call", ... }`
84
- - terminal `{ type: "done", finishReason, usage }` — usage from `metadata.usage` (a `TokenUsage`)
112
+ - terminal `{ type: "done", finishReason, usage }` — usage from `metadata.usage` (a `TokenUsage`), including `cachedTokens` / `cacheWriteTokens` when reported (see the usage table below)
85
113
 
86
114
  ## Finish-reason mapping
87
115
 
@@ -136,7 +164,22 @@ const { usage } = await ai.agent({ model: bedrock.model({ name: "us.amazon.nova-
136
164
  usage.cost; // per-channel USD breakdown of THIS run, computed from tokens × pricing[model]
137
165
  ```
138
166
 
139
- Resolution at `model()` time: per-model `pricing` (`bedrock.model({ name, pricing })`) > SDK registry > `undefined` (no cost computed). When the same model id appears under a cross-region inference profile, list each id you actually invoke (`us.`, `eu.`, `apac.` prefixes are distinct keys). Bedrock meters cache-read tokens via Converse `usage` — those surface as `usage.cachedTokens`, so set `cachedInput` to have them priced at the discounted rate. See [`@warlock.js/ai/pick-ai-provider/SKILL.md`](@warlock.js/ai/pick-ai-provider/SKILL.md).
167
+ Resolution at `model()` time: per-model `pricing` (`bedrock.model({ name, pricing })`) > SDK registry > `undefined` (no cost computed). When the same model id appears under a cross-region inference profile, list each id you actually invoke (`us.`, `eu.`, `apac.` prefixes are distinct keys). See [`@warlock.js/ai/pick-ai-provider/SKILL.md`](@warlock.js/ai/pick-ai-provider/SKILL.md).
168
+
169
+ ### Tokens & usage — cost truth
170
+
171
+ Both `complete()` and `stream()` normalize Converse's `TokenUsage` into the neutral `Usage` shape:
172
+
173
+ | Converse `TokenUsage` field | Neutral `Usage` field | Notes |
174
+ | --- | --- | --- |
175
+ | `inputTokens` | `usage.input` | Includes cached read tokens. |
176
+ | `outputTokens` | `usage.output` | |
177
+ | `totalTokens` | `usage.total` | Falls back to `input + output` when absent. |
178
+ | `cacheReadInputTokens` | `usage.cachedTokens` | Only when `> 0`. Cache-hit reads — price at `pricing.cachedInput`. |
179
+ | `cacheWriteInputTokens` | `usage.cacheWriteTokens` | Only when `> 0`. One-time cache write — price at `pricing.cachedOutput`. |
180
+ | — | `usage.reasoningTokens` | **Never set** — Converse exposes no reasoning-token count. |
181
+
182
+ Set `cachedInput` / `cachedOutput` in `ModelPricing` to have cache reads/writes billed at their distinct rates. Read-side accounting (`cachedTokens` / `cacheWriteTokens`) is populated whenever Bedrock reports it, independent of whether you sent a `cacheControl` breakpoint.
140
183
 
141
184
  ## When NOT to use this skill
142
185
 
package/llms.txt CHANGED
@@ -6,4 +6,4 @@
6
6
 
7
7
  ## Skills
8
8
 
9
- - [setup-bedrock](@warlock.js/ai-bedrock/setup-bedrock/SKILL.md): Wire @warlock.js/ai-bedrock — new BedrockSDK({region, credentials?, provider?}) for AWS Bedrock Converse API + Titan embeddings. AWS credential chain (no apiKey). Triggers: `BedrockSDK`, `bedrock.model`, `bedrock.embedder`, `bedrock.count`, `BedrockRuntimeClient`; "how do I use AWS Bedrock", "wire Claude/Nova/Llama on Bedrock", "Titan embeddings", "Bedrock Converse API"; typical import `import { BedrockSDK } from "@warlock.js/ai-bedrock"`. Skip: agent wiring — `@warlock.js/ai/run-ai-agent/SKILL.md`; provider choice — `@warlock.js/ai/pick-ai-provider/SKILL.md`; embeddings concepts — `@warlock.js/ai/embed-text/SKILL.md`; competing libs `@aws-sdk/client-bedrock-runtime`, `@ai-sdk/amazon-bedrock`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`.
9
+ - [setup-bedrock](@warlock.js/ai-bedrock/setup-bedrock/SKILL.md): Wire @warlock.js/ai-bedrock — new BedrockSDK({region, credentials?, provider?}) for AWS Bedrock Converse API + Titan embeddings. AWS credential chain (no apiKey). .model({name, vision?, reasoning?, promptCaching?, pdf?, audio?}) with cost-truth capabilities inferred per family, extended thinking via options.reasoning → Converse thinking budget_tokens, prompt caching via options.cacheControl → cachePoint, usage cachedTokens/cacheWriteTokens. Triggers: `BedrockSDK`, `bedrock.model`, `bedrock.embedder`, `bedrock.count`, `BedrockRuntimeClient`, `reasoning`, `thinking`, `budget_tokens`, `cachePoint`, `cacheControl`, `promptCaching`, `cachedTokens`, `cacheWriteTokens`; "how do I use AWS Bedrock", "wire Claude/Nova/Llama on Bedrock", "Titan embeddings", "Bedrock Converse API", "Claude 3.7/4 extended thinking on Bedrock", "Bedrock prompt caching cost"; typical import `import { BedrockSDK } from "@warlock.js/ai-bedrock"`. Skip: agent wiring — `@warlock.js/ai/run-ai-agent/SKILL.md`; provider choice — `@warlock.js/ai/pick-ai-provider/SKILL.md`; embeddings concepts — `@warlock.js/ai/embed-text/SKILL.md`; competing libs `@aws-sdk/client-bedrock-runtime`, `@ai-sdk/amazon-bedrock`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`.
package/package.json CHANGED
@@ -15,12 +15,12 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "@aws-sdk/client-bedrock-runtime": "^3.1048.0",
18
- "@warlock.js/logger": "4.2.11"
18
+ "@warlock.js/logger": "4.4.0"
19
19
  },
20
20
  "peerDependencies": {
21
- "@warlock.js/ai": "4.2.11"
21
+ "@warlock.js/ai": "4.4.0"
22
22
  },
23
- "version": "4.2.11",
23
+ "version": "4.4.0",
24
24
  "main": "./cjs/index.cjs",
25
25
  "module": "./esm/index.mjs",
26
26
  "types": "./esm/index.d.mts",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: setup-bedrock
3
- description: 'Wire @warlock.js/ai-bedrock — new BedrockSDK({region, credentials?, provider?}) for AWS Bedrock Converse API + Titan embeddings. AWS credential chain (no apiKey). Triggers: `BedrockSDK`, `bedrock.model`, `bedrock.embedder`, `bedrock.count`, `BedrockRuntimeClient`; "how do I use AWS Bedrock", "wire Claude/Nova/Llama on Bedrock", "Titan embeddings", "Bedrock Converse API"; typical import `import { BedrockSDK } from "@warlock.js/ai-bedrock"`. Skip: agent wiring — `@warlock.js/ai/run-ai-agent/SKILL.md`; provider choice — `@warlock.js/ai/pick-ai-provider/SKILL.md`; embeddings concepts — `@warlock.js/ai/embed-text/SKILL.md`; competing libs `@aws-sdk/client-bedrock-runtime`, `@ai-sdk/amazon-bedrock`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`.'
3
+ description: 'Wire @warlock.js/ai-bedrock — new BedrockSDK({region, credentials?, provider?}) for AWS Bedrock Converse API + Titan embeddings. AWS credential chain (no apiKey). .model({name, vision?, reasoning?, promptCaching?, pdf?, audio?}) with cost-truth capabilities inferred per family, extended thinking via options.reasoning → Converse thinking budget_tokens, prompt caching via options.cacheControl → cachePoint, usage cachedTokens/cacheWriteTokens. Triggers: `BedrockSDK`, `bedrock.model`, `bedrock.embedder`, `bedrock.count`, `BedrockRuntimeClient`, `reasoning`, `thinking`, `budget_tokens`, `cachePoint`, `cacheControl`, `promptCaching`, `cachedTokens`, `cacheWriteTokens`; "how do I use AWS Bedrock", "wire Claude/Nova/Llama on Bedrock", "Titan embeddings", "Bedrock Converse API", "Claude 3.7/4 extended thinking on Bedrock", "Bedrock prompt caching cost"; typical import `import { BedrockSDK } from "@warlock.js/ai-bedrock"`. Skip: agent wiring — `@warlock.js/ai/run-ai-agent/SKILL.md`; provider choice — `@warlock.js/ai/pick-ai-provider/SKILL.md`; embeddings concepts — `@warlock.js/ai/embed-text/SKILL.md`; competing libs `@aws-sdk/client-bedrock-runtime`, `@ai-sdk/amazon-bedrock`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`.'
4
4
  ---
5
5
 
6
6
  # `@warlock.js/ai-bedrock`
@@ -43,8 +43,36 @@ Uses the model-agnostic **Converse / ConverseStream** API — one wire mapping c
43
43
  | --- | --- |
44
44
  | `structuredOutput` | `true` (via Converse `outputConfig.textFormat`) |
45
45
  | `vision` | Inferred from model id substring. `true` for Claude 3/3.5/3.7/4, Nova Lite/Pro/Premier, Llama 3.2-11B/90B, Llama 4. |
46
+ | `reasoning` | Inferred from model id. `true` for Claude 3.7 + Claude 4 (Sonnet/Opus/Haiku) — they expose a configurable thinking budget on Converse. |
47
+ | `promptCaching` | Inferred from model id. `true` for Claude 3.5+/3.7/4 and Nova Micro/Lite/Pro/Premier — the families that honor Converse `cachePoint`. |
48
+ | `pdf` | Inferred from model id. `true` for Claude 3+ and Nova Lite/Pro/Premier (Converse `document` blocks). |
49
+ | `audio` | `false` by default — Converse does not accept audio blocks for the families this adapter targets. Pass `audio: true` for a confirmed audio model. |
46
50
 
47
- Explicit config always wins.
51
+ Explicit config always wins — every flag is overridable per model, e.g. `bedrock.model({ name, reasoning: false, promptCaching: true, pdf: false, audio: true })`.
52
+
53
+ ## Reasoning / extended thinking
54
+
55
+ For a `reasoning`-capable model (Claude 3.7 / 4), `ModelCallOptions.reasoning` maps to Converse's escape hatch `additionalModelRequestFields.thinking = { type: "enabled", budget_tokens }`:
56
+
57
+ ```ts
58
+ // Explicit thinking budget (token cap):
59
+ await model.complete(messages, { reasoning: { maxTokens: 2048 } });
60
+
61
+ // Effort tier → conventional budget (low 1024 / medium 4096 / high 16384):
62
+ await model.complete(messages, { reasoning: { effort: "high" } });
63
+ ```
64
+
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
+
67
+ ## Prompt caching
68
+
69
+ 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:
70
+
71
+ ```ts
72
+ await model.complete(messages, { cacheControl: { breakpoints: 1 } });
73
+ ```
74
+
75
+ Earlier turns are untouched; only the trailing message is marked. No-ops gracefully when the model is not caching-capable or no breakpoint was requested. Read-side accounting works without any breakpoint — see the usage section below.
48
76
 
49
77
  ## System prompt
50
78
 
@@ -73,7 +101,7 @@ Object-root `responseSchema` + `structuredOutput`-capable → `outputConfig.text
73
101
 
74
102
  - `contentBlockDelta.delta.text` → `{ type: "delta", content }`
75
103
  - `toolUse`: `contentBlockStart` opens an accumulator, `contentBlockDelta.delta.toolUse.input` fragments append, `contentBlockStop` emits one consolidated `{ type: "tool-call", ... }`
76
- - terminal `{ type: "done", finishReason, usage }` — usage from `metadata.usage` (a `TokenUsage`)
104
+ - terminal `{ type: "done", finishReason, usage }` — usage from `metadata.usage` (a `TokenUsage`), including `cachedTokens` / `cacheWriteTokens` when reported (see the usage table below)
77
105
 
78
106
  ## Finish-reason mapping
79
107
 
@@ -128,7 +156,22 @@ const { usage } = await ai.agent({ model: bedrock.model({ name: "us.amazon.nova-
128
156
  usage.cost; // per-channel USD breakdown of THIS run, computed from tokens × pricing[model]
129
157
  ```
130
158
 
131
- Resolution at `model()` time: per-model `pricing` (`bedrock.model({ name, pricing })`) > SDK registry > `undefined` (no cost computed). When the same model id appears under a cross-region inference profile, list each id you actually invoke (`us.`, `eu.`, `apac.` prefixes are distinct keys). Bedrock meters cache-read tokens via Converse `usage` — those surface as `usage.cachedTokens`, so set `cachedInput` to have them priced at the discounted rate. See [`@warlock.js/ai/pick-ai-provider/SKILL.md`](@warlock.js/ai/pick-ai-provider/SKILL.md).
159
+ Resolution at `model()` time: per-model `pricing` (`bedrock.model({ name, pricing })`) > SDK registry > `undefined` (no cost computed). When the same model id appears under a cross-region inference profile, list each id you actually invoke (`us.`, `eu.`, `apac.` prefixes are distinct keys). See [`@warlock.js/ai/pick-ai-provider/SKILL.md`](@warlock.js/ai/pick-ai-provider/SKILL.md).
160
+
161
+ ### Tokens & usage — cost truth
162
+
163
+ Both `complete()` and `stream()` normalize Converse's `TokenUsage` into the neutral `Usage` shape:
164
+
165
+ | Converse `TokenUsage` field | Neutral `Usage` field | Notes |
166
+ | --- | --- | --- |
167
+ | `inputTokens` | `usage.input` | Includes cached read tokens. |
168
+ | `outputTokens` | `usage.output` | |
169
+ | `totalTokens` | `usage.total` | Falls back to `input + output` when absent. |
170
+ | `cacheReadInputTokens` | `usage.cachedTokens` | Only when `> 0`. Cache-hit reads — price at `pricing.cachedInput`. |
171
+ | `cacheWriteInputTokens` | `usage.cacheWriteTokens` | Only when `> 0`. One-time cache write — price at `pricing.cachedOutput`. |
172
+ | — | `usage.reasoningTokens` | **Never set** — Converse exposes no reasoning-token count. |
173
+
174
+ Set `cachedInput` / `cachedOutput` in `ModelPricing` to have cache reads/writes billed at their distinct rates. Read-side accounting (`cachedTokens` / `cacheWriteTokens`) is populated whenever Bedrock reports it, independent of whether you sent a `cacheControl` breakpoint.
132
175
 
133
176
  ## When NOT to use this skill
134
177