@openclaw/amazon-bedrock-provider 2026.7.2-beta.7 → 2026.8.1-beta.2

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.
@@ -118,7 +118,7 @@ function loadDefaultCredentialProvider() {
118
118
  return credentialProviderPromise ??= import("@aws-sdk/credential-provider-node").then(({ defaultProvider }) => defaultProvider).catch(() => null);
119
119
  }
120
120
  const MODEL_PREFIX_RE = /^(?:bedrock|amazon-bedrock|aws)\//;
121
- const REGION_RE = /bedrock-runtime\.([a-z0-9-]+)\./;
121
+ const REGION_RE = /bedrock-runtime(?:-fips)?\.([a-z0-9-]+)\./;
122
122
  function normalizeBedrockEmbeddingModel(model) {
123
123
  const trimmed = model.trim();
124
124
  return trimmed ? trimmed.replace(MODEL_PREFIX_RE, "") : DEFAULT_BEDROCK_EMBEDDING_MODEL;
@@ -211,15 +211,9 @@ function parseCohereBatch(family, raw) {
211
211
  }
212
212
  return asNumberArrayBatch(embeddings);
213
213
  }
214
- const testing = {
215
- parseCohereBatch,
216
- parseSingle,
217
- stripInferenceProfilePrefix
218
- };
219
- if (process.env.VITEST === "true") Reflect.set(globalThis, Symbol.for("openclaw.amazonBedrockEmbeddingTestApi"), testing);
220
214
  async function createBedrockEmbeddingProvider(options) {
221
- const client = resolveBedrockEmbeddingClient(options);
222
215
  const { BedrockRuntimeClient, InvokeModelCommand } = await loadSdk();
216
+ const client = resolveBedrockEmbeddingClient(options, BedrockRuntimeClient);
223
217
  const spec = resolveSpec(client.model);
224
218
  const family = spec?.family ?? inferFamily(client.model);
225
219
  debugEmbeddingsLog("memory embeddings: bedrock client", {
@@ -230,7 +224,12 @@ async function createBedrockEmbeddingProvider(options) {
230
224
  });
231
225
  const invoke = async (body, signal) => {
232
226
  await refreshAwsSharedConfigCacheForBedrock();
233
- const sdk = new BedrockRuntimeClient({ region: client.region });
227
+ const sdk = new BedrockRuntimeClient({
228
+ region: client.region,
229
+ endpoint: client.endpoint,
230
+ useFipsEndpoint: client.useFipsEndpoint,
231
+ useDualstackEndpoint: client.useDualstackEndpoint
232
+ });
234
233
  try {
235
234
  const res = await sdk.send(new InvokeModelCommand({
236
235
  modelId: client.model,
@@ -273,11 +272,41 @@ async function createBedrockEmbeddingProvider(options) {
273
272
  client
274
273
  };
275
274
  }
276
- function resolveBedrockEmbeddingClient(options) {
275
+ function resolveBedrockEmbeddingClient(options, BedrockRuntimeClient) {
277
276
  const model = normalizeBedrockEmbeddingModel(options.model);
278
277
  const spec = resolveSpec(model);
279
278
  const providerConfig = options.config.models?.providers?.["amazon-bedrock"];
279
+ let endpoint = normalizeOptionalString(options.remote?.baseUrl) ?? normalizeOptionalString(providerConfig?.baseUrl);
280
+ let useFipsEndpoint;
281
+ let useDualstackEndpoint;
280
282
  const region = regionFromUrl(options.remote?.baseUrl) ?? regionFromUrl(providerConfig?.baseUrl) ?? normalizeOptionalString(process.env.AWS_REGION) ?? normalizeOptionalString(process.env.AWS_DEFAULT_REGION) ?? "us-east-1";
283
+ if (endpoint) {
284
+ const sdk = new BedrockRuntimeClient({ region });
285
+ try {
286
+ const normalizedEndpoint = new URL(endpoint).href;
287
+ for (const fips of [false, true]) {
288
+ for (const dualstack of [false, true]) {
289
+ const endpointModes = {
290
+ Region: region,
291
+ UseFIPS: fips,
292
+ UseDualStack: dualstack
293
+ };
294
+ try {
295
+ if (sdk.config.endpointProvider(endpointModes).url.href !== normalizedEndpoint) continue;
296
+ } catch {
297
+ continue;
298
+ }
299
+ endpoint = void 0;
300
+ useFipsEndpoint = fips || void 0;
301
+ useDualstackEndpoint = dualstack || void 0;
302
+ break;
303
+ }
304
+ if (!endpoint) break;
305
+ }
306
+ } finally {
307
+ sdk.destroy();
308
+ }
309
+ }
281
310
  let dimensions;
282
311
  if (options.outputDimensionality != null) {
283
312
  if (spec?.validDims && !spec.validDims.includes(options.outputDimensionality)) throw new Error(`Invalid dimensions ${options.outputDimensionality} for ${model}. Valid values: ${spec.validDims.join(", ")}`);
@@ -286,7 +315,10 @@ function resolveBedrockEmbeddingClient(options) {
286
315
  return {
287
316
  region,
288
317
  model,
289
- dimensions
318
+ dimensions,
319
+ ...endpoint ? { endpoint } : {},
320
+ ...useFipsEndpoint ? { useFipsEndpoint } : {},
321
+ ...useDualstackEndpoint ? { useDualstackEndpoint } : {}
290
322
  };
291
323
  }
292
324
  async function hasAwsCredentials(env = process.env, loadCredentialProvider = loadDefaultCredentialProvider) {
@@ -29,7 +29,8 @@ const bedrockMemoryEmbeddingProviderAdapter = {
29
29
  provider: "bedrock",
30
30
  region: client.region,
31
31
  model: client.model,
32
- dimensions: client.dimensions
32
+ dimensions: client.dimensions,
33
+ ...client.endpoint ? { endpoint: client.endpoint } : {}
33
34
  }
34
35
  }
35
36
  };
@@ -7,7 +7,7 @@ import { isLatestAdaptiveBedrockModelRef, isOpus47OrNewerBedrockModelRef, resolv
7
7
  import { streamSimpleBedrock } from "./stream.runtime.js";
8
8
  import { resolvePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
9
9
  import { buildProviderReplayFamilyHooks, normalizeProviderId, resolveClaudeFable5ModelIdentity, resolveClaudeModelIdentity, resolveClaudeMythos5ModelIdentity, resolveClaudeOpus5ModelIdentity, resolveClaudeSonnet5ModelIdentity } from "openclaw/plugin-sdk/provider-model-shared";
10
- import { streamWithPayloadPatch } from "openclaw/plugin-sdk/provider-stream-shared";
10
+ import { createPayloadPatchStreamWrapper } from "openclaw/plugin-sdk/provider-stream-shared";
11
11
  //#region extensions/amazon-bedrock/register.sync.runtime.ts
12
12
  function normalizeBedrockResolvedModel({ modelId, model }) {
13
13
  const thinkingLevelMap = resolveBedrockNativeThinkingLevelMap(modelId, model.params);
@@ -68,28 +68,23 @@ function resolveBedrockServiceTier(extraParams, warn) {
68
68
  warn(`ignoring invalid Bedrock service_tier param: ${raw}`);
69
69
  }
70
70
  function createBedrockServiceTierWrapper(underlying, serviceTier) {
71
- return (model, context, options) => {
72
- if (model.api !== "bedrock-converse-stream") return underlying(model, context, options);
73
- return streamWithPayloadPatch(underlying, model, context, options, (payloadObj) => {
74
- payloadObj.serviceTier ??= { type: serviceTier };
75
- });
76
- };
71
+ return createPayloadPatchStreamWrapper(underlying, ({ payload }) => {
72
+ payload.serviceTier ??= { type: serviceTier };
73
+ }, { shouldPatch: ({ model }) => model.api === "bedrock-converse-stream" });
77
74
  }
78
75
  function createGuardrailWrapStreamFn(innerWrapStreamFn, guardrailConfig) {
79
76
  return (ctx) => {
80
77
  const inner = innerWrapStreamFn(ctx);
81
78
  if (!inner) return inner;
82
- return (model, context, options) => {
83
- return streamWithPayloadPatch(inner, model, context, options, (payload) => {
84
- const gc = {
85
- guardrailIdentifier: guardrailConfig.guardrailIdentifier,
86
- guardrailVersion: guardrailConfig.guardrailVersion
87
- };
88
- if (guardrailConfig.streamProcessingMode) gc.streamProcessingMode = guardrailConfig.streamProcessingMode;
89
- if (guardrailConfig.trace) gc.trace = guardrailConfig.trace;
90
- payload.guardrailConfig = gc;
91
- });
92
- };
79
+ return createPayloadPatchStreamWrapper(inner, ({ payload }) => {
80
+ const gc = {
81
+ guardrailIdentifier: guardrailConfig.guardrailIdentifier,
82
+ guardrailVersion: guardrailConfig.guardrailVersion
83
+ };
84
+ if (guardrailConfig.streamProcessingMode) gc.streamProcessingMode = guardrailConfig.streamProcessingMode;
85
+ if (guardrailConfig.trace) gc.trace = guardrailConfig.trace;
86
+ payload.guardrailConfig = gc;
87
+ });
93
88
  };
94
89
  }
95
90
  function sharedRuntimeWouldInjectCachePoints(modelId) {
@@ -72,6 +72,7 @@ const streamBedrock = (model, context, options = {}) => {
72
72
  timestamp: Date.now()
73
73
  };
74
74
  const blocks = output.content;
75
+ const redactedReasoningChunks = /* @__PURE__ */ new Map();
75
76
  const fable5 = usesClaudeFable5BedrockContract(model);
76
77
  const refusalBuffer = usesClaudeStreamingRefusalBedrockContract(model) ? createDeferredEventBuffer(stream, () => notifyLlmRequestActivity(options.signal)) : void 0;
77
78
  const eventSink = refusalBuffer ?? stream;
@@ -100,8 +101,9 @@ const streamBedrock = (model, context, options = {}) => {
100
101
  config.token = { token: bearerToken };
101
102
  config.authSchemePreference = ["httpBearerAuth"];
102
103
  }
104
+ let client;
103
105
  try {
104
- const client = new BedrockRuntimeClient(config);
106
+ client = new BedrockRuntimeClient(config);
105
107
  const cacheRetention = resolveCacheRetention(options.cacheRetention);
106
108
  const additionalModelRequestFields = buildAdditionalModelRequestFields(model, options);
107
109
  const thinking = additionalModelRequestFields?.thinking;
@@ -139,8 +141,8 @@ const streamBedrock = (model, context, options = {}) => {
139
141
  partial: output
140
142
  });
141
143
  } else if (item.contentBlockStart) handleContentBlockStart(item.contentBlockStart, blocks, output, eventSink);
142
- else if (item.contentBlockDelta) handleContentBlockDelta(item.contentBlockDelta, blocks, output, eventSink);
143
- else if (item.contentBlockStop) handleContentBlockStop(item.contentBlockStop, blocks, output, eventSink);
144
+ else if (item.contentBlockDelta) handleContentBlockDelta(item.contentBlockDelta, blocks, output, eventSink, redactedReasoningChunks);
145
+ else if (item.contentBlockStop) handleContentBlockStop(item.contentBlockStop, blocks, output, eventSink, redactedReasoningChunks);
144
146
  else if (item.messageStop) {
145
147
  sawMessageStop = true;
146
148
  if (item.messageStop.stopReason === "refusal") applyAnthropicRefusal(output, readBedrockStopDetails(item.messageStop.additionalModelResponseFields), model.provider);
@@ -155,9 +157,10 @@ const streamBedrock = (model, context, options = {}) => {
155
157
  else if (item.validationException) throw item.validationException;
156
158
  else if (item.throttlingException) throw item.throttlingException;
157
159
  else if (item.serviceUnavailableException) throw item.serviceUnavailableException;
158
- if (refusalBuffer && !sawMessageStop) throw new Error("Bedrock stream ended before messageStop");
160
+ if (!sawMessageStop) throw new Error("Bedrock stream ended before messageStop");
159
161
  if (options.signal?.aborted) throw new Error("Request was aborted");
160
162
  if (output.stopReason === "error" || output.stopReason === "aborted") throw new Error(output.errorMessage ?? "An unknown error occurred");
163
+ for (const block of blocks) if (block.index !== void 0) handleContentBlockStop({ contentBlockIndex: block.index }, blocks, output, eventSink, redactedReasoningChunks);
161
164
  refusalBuffer?.flush();
162
165
  stream.push({
163
166
  type: "done",
@@ -182,6 +185,8 @@ const streamBedrock = (model, context, options = {}) => {
182
185
  error: output
183
186
  });
184
187
  stream.end();
188
+ } finally {
189
+ client?.destroy();
185
190
  }
186
191
  })();
187
192
  return stream;
@@ -285,7 +290,7 @@ function handleContentBlockStart(event, blocks, output, stream) {
285
290
  });
286
291
  }
287
292
  }
288
- function handleContentBlockDelta(event, blocks, output, stream) {
293
+ function handleContentBlockDelta(event, blocks, output, stream, redactedReasoningChunks) {
289
294
  const contentBlockIndex = event.contentBlockIndex;
290
295
  const delta = event.delta;
291
296
  let index = blocks.findIndex((b) => b.index === contentBlockIndex);
@@ -354,6 +359,13 @@ function handleContentBlockDelta(event, blocks, output, stream) {
354
359
  });
355
360
  }
356
361
  if (delta.reasoningContent.signature) thinkingBlock.thinkingSignature = (thinkingBlock.thinkingSignature || "") + delta.reasoningContent.signature;
362
+ if (delta.reasoningContent.redactedContent) {
363
+ const chunks = redactedReasoningChunks.get(contentBlockIndex);
364
+ if (chunks) chunks.push(delta.reasoningContent.redactedContent);
365
+ else redactedReasoningChunks.set(contentBlockIndex, [delta.reasoningContent.redactedContent]);
366
+ thinkingBlock.thinking = "[Reasoning redacted]";
367
+ thinkingBlock.redacted = true;
368
+ }
357
369
  }
358
370
  }
359
371
  }
@@ -363,11 +375,19 @@ function handleMetadata(event, model, output) {
363
375
  output.usage.output = event.usage.outputTokens || 0;
364
376
  output.usage.cacheRead = event.usage.cacheReadInputTokens || 0;
365
377
  output.usage.cacheWrite = event.usage.cacheWriteInputTokens || 0;
366
- output.usage.totalTokens = event.usage.totalTokens || output.usage.input + output.usage.output;
378
+ const promptTokens = output.usage.input + output.usage.cacheRead + output.usage.cacheWrite;
379
+ output.usage.totalTokens = Math.max(event.usage.totalTokens || 0, promptTokens + output.usage.output);
380
+ output.usage.contextUsage = {
381
+ state: "available",
382
+ promptTokens,
383
+ totalTokens: promptTokens + output.usage.output
384
+ };
385
+ const cacheWrite1h = event.usage.cacheDetails?.reduce((total, detail) => detail.ttl === CacheTTL.ONE_HOUR ? total + (detail.inputTokens ?? 0) : total, 0);
386
+ if (cacheWrite1h) output.usage.cacheWrite1h = cacheWrite1h;
367
387
  calculateCost(model, output.usage);
368
388
  }
369
389
  }
370
- function handleContentBlockStop(event, blocks, output, stream) {
390
+ function handleContentBlockStop(event, blocks, output, stream, redactedReasoningChunks) {
371
391
  const index = blocks.findIndex((b) => b.index === event.contentBlockIndex);
372
392
  const block = blocks[index];
373
393
  if (!block) return;
@@ -382,6 +402,15 @@ function handleContentBlockStop(event, blocks, output, stream) {
382
402
  });
383
403
  break;
384
404
  case "thinking":
405
+ if (block.redacted) {
406
+ const chunks = redactedReasoningChunks.get(event.contentBlockIndex);
407
+ if (chunks) {
408
+ let opaqueReasoning = "";
409
+ for (const chunk of chunks) for (const byte of chunk) opaqueReasoning += String.fromCharCode(byte);
410
+ block.thinkingSignature = btoa(opaqueReasoning);
411
+ redactedReasoningChunks.delete(event.contentBlockIndex);
412
+ }
413
+ }
385
414
  stream.push({
386
415
  type: "thinking_end",
387
416
  contentIndex: index,
@@ -495,16 +524,17 @@ function createBedrockToolResult(message) {
495
524
  content.push({ text: sanitizeSurrogates(block.text) });
496
525
  continue;
497
526
  }
498
- if (describeToolResultMediaPlaceholder([block])) content.push({ image: createImageBlock(block.mimeType, block.data) });
527
+ if (block.type === "image" && describeToolResultMediaPlaceholder([block])) content.push({ image: createImageBlock(block.mimeType, block.data) });
499
528
  }
500
529
  return { toolResult: {
501
530
  toolUseId: message.toolCallId,
502
- content: content.length > 0 ? content : [{ text: "(no output)" }],
531
+ content: content.length > 0 ? content : [{ text: describeToolResultMediaPlaceholder(message.content) ?? "(no output)" }],
503
532
  status: message.isError ? ToolResultStatus.ERROR : ToolResultStatus.SUCCESS
504
533
  } };
505
534
  }
506
535
  function convertMessages(context, model, cacheRetention) {
507
536
  const result = [];
537
+ let firstVolatileMessageIndex;
508
538
  const transformedMessages = transformMessages(context.messages, model, normalizeToolCallId);
509
539
  for (let i = 0; i < transformedMessages.length; i++) {
510
540
  const m = expectDefined(transformedMessages[i], "message conversion index is in bounds");
@@ -522,6 +552,7 @@ function convertMessages(context, model, cacheRetention) {
522
552
  default: continue;
523
553
  }
524
554
  if (content.length === 0) continue;
555
+ if (m.runtimeContextCarrier === true && firstVolatileMessageIndex === void 0) firstVolatileMessageIndex = result.length;
525
556
  result.push({
526
557
  role: ConversationRole.USER,
527
558
  content
@@ -544,6 +575,12 @@ function convertMessages(context, model, cacheRetention) {
544
575
  } });
545
576
  break;
546
577
  case "thinking": {
578
+ if (c.redacted) {
579
+ if (!supportsThinkingSignature(model)) continue;
580
+ if (!c.thinkingSignature) throw new Error("Bedrock redacted reasoning block is missing its opaque signature");
581
+ contentBlocks.push({ reasoningContent: { redactedContent: decodeBedrockBase64(c.thinkingSignature, "Bedrock redacted reasoning block has a malformed opaque signature") } });
582
+ break;
583
+ }
547
584
  const thinkingSignature = c.thinkingSignature;
548
585
  const normalizedThinkingSignature = thinkingSignature?.trim();
549
586
  const supportsSignature = supportsThinkingSignature(model);
@@ -588,9 +625,9 @@ function convertMessages(context, model, cacheRetention) {
588
625
  default: continue;
589
626
  }
590
627
  }
591
- if (cacheRetention !== "none" && supportsPromptCaching(model) && result.length > 0) {
592
- const lastMessage = expectDefined(result.at(-1), "non-empty converted message list");
593
- if (lastMessage.role === ConversationRole.USER && lastMessage.content) lastMessage.content.push({ cachePoint: {
628
+ if (cacheRetention !== "none" && supportsPromptCaching(model) && result.at(-1)?.role === ConversationRole.USER) {
629
+ const cacheAnchor = result.findLast((message, index) => message.role === ConversationRole.USER && (firstVolatileMessageIndex === void 0 || index < firstVolatileMessageIndex));
630
+ if (cacheAnchor?.content) cacheAnchor.content.push({ cachePoint: {
594
631
  type: CachePointType.DEFAULT,
595
632
  ...cacheRetention === "long" ? { ttl: CacheTTL.ONE_HOUR } : {}
596
633
  } });
@@ -720,26 +757,18 @@ function createImageBlock(mimeType, data) {
720
757
  break;
721
758
  default: throw new Error(`Unknown image type: ${mimeType}`);
722
759
  }
760
+ return {
761
+ source: { bytes: decodeBedrockBase64(data, "Amazon Bedrock image content has malformed base64") },
762
+ format
763
+ };
764
+ }
765
+ function decodeBedrockBase64(data, errorMessage) {
723
766
  const canonicalBase64 = canonicalizeBase64(data);
724
- if (!canonicalBase64) throw new Error("Amazon Bedrock image content has malformed base64");
767
+ if (!canonicalBase64) throw new Error(errorMessage);
725
768
  const binaryString = atob(canonicalBase64);
726
769
  const bytes = new Uint8Array(binaryString.length);
727
770
  for (let i = 0; i < binaryString.length; i++) bytes[i] = binaryString.charCodeAt(i);
728
- return {
729
- source: { bytes },
730
- format
731
- };
771
+ return bytes;
732
772
  }
733
- /** Test-only hooks for Bedrock runtime conversion and endpoint policy. */
734
- const testing = {
735
- buildAdditionalModelRequestFields,
736
- convertMessages,
737
- getConfiguredBedrockRegion,
738
- hasConfiguredBedrockProfile,
739
- mapThinkingLevelToEffort,
740
- resolveSimpleBedrockOptions,
741
- shouldUseExplicitBedrockEndpoint
742
- };
743
- if (process.env.VITEST === "true") Reflect.set(globalThis, Symbol.for("openclaw.amazonBedrockStreamTestApi"), testing);
744
773
  //#endregion
745
774
  export { streamSimpleBedrock };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/amazon-bedrock-provider",
3
- "version": "2026.7.2-beta.7",
3
+ "version": "2026.8.1-beta.2",
4
4
  "description": "OpenClaw Amazon Bedrock provider plugin with model discovery, embeddings, and guardrail support.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,10 +25,10 @@
25
25
  "minHostVersion": ">=2026.5.12-beta.1"
26
26
  },
27
27
  "compat": {
28
- "pluginApi": ">=2026.7.2-beta.7"
28
+ "pluginApi": ">=2026.8.1-beta.2"
29
29
  },
30
30
  "build": {
31
- "openclawVersion": "2026.7.2-beta.7",
31
+ "openclawVersion": "2026.8.1-beta.2",
32
32
  "bundledDist": false
33
33
  },
34
34
  "release": {
@@ -45,7 +45,7 @@
45
45
  "README.md"
46
46
  ],
47
47
  "peerDependencies": {
48
- "openclaw": ">=2026.7.2-beta.7"
48
+ "openclaw": ">=2026.8.1-beta.2"
49
49
  },
50
50
  "peerDependenciesMeta": {
51
51
  "openclaw": {