@openclaw/amazon-bedrock-provider 2026.7.2-beta.7 → 2026.8.1-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +2 -2
- package/dist/discovery.js +1 -6
- package/dist/embedding-provider.js +43 -11
- package/dist/memory-embedding-adapter.js +2 -1
- package/dist/register.sync.runtime.js +15 -19
- package/dist/stream.runtime.js +97 -44
- package/openclaw.plugin.json +1 -1
- package/package.json +4 -4
package/dist/api.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { mergeImplicitBedrockProvider, resolveBedrockConfigApiKey } from "./discovery-shared.js";
|
|
2
|
-
import { discoverBedrockModels,
|
|
3
|
-
export { discoverBedrockModels, mergeImplicitBedrockProvider,
|
|
2
|
+
import { discoverBedrockModels, resolveImplicitBedrockProvider } from "./discovery.js";
|
|
3
|
+
export { discoverBedrockModels, mergeImplicitBedrockProvider, resolveBedrockConfigApiKey, resolveImplicitBedrockProvider };
|
package/dist/discovery.js
CHANGED
|
@@ -278,11 +278,6 @@ function resolveInferenceProfiles(profiles, defaults, providerFilter, foundation
|
|
|
278
278
|
}
|
|
279
279
|
return discovered;
|
|
280
280
|
}
|
|
281
|
-
/** Reset Bedrock discovery cache for tests. */
|
|
282
|
-
function resetBedrockDiscoveryCacheForTest() {
|
|
283
|
-
discoveryCache.clear();
|
|
284
|
-
hasLoggedBedrockError = false;
|
|
285
|
-
}
|
|
286
281
|
/** Discover Bedrock models and inference profiles for one region/config. */
|
|
287
282
|
async function discoverBedrockModels(params) {
|
|
288
283
|
const refreshIntervalSeconds = Math.max(0, Math.floor(params.config?.refreshInterval ?? DEFAULT_REFRESH_INTERVAL_SECONDS));
|
|
@@ -400,4 +395,4 @@ async function resolveImplicitBedrockProvider(params) {
|
|
|
400
395
|
};
|
|
401
396
|
}
|
|
402
397
|
//#endregion
|
|
403
|
-
export { discoverBedrockModels,
|
|
398
|
+
export { discoverBedrockModels, resolveImplicitBedrockProvider };
|
|
@@ -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
|
|
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({
|
|
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
|
};
|
|
@@ -5,9 +5,10 @@ import { mergeImplicitBedrockProvider, resolveBedrockConfigApiKey } from "./disc
|
|
|
5
5
|
import { bedrockMemoryEmbeddingProviderAdapter } from "./memory-embedding-adapter.js";
|
|
6
6
|
import { isLatestAdaptiveBedrockModelRef, isOpus47OrNewerBedrockModelRef, resolveBedrockClaudeThinkingProfile, resolveBedrockNativeThinkingLevelMap, supportsBedrockNativeMaxEffort } from "./thinking-policy.js";
|
|
7
7
|
import { streamSimpleBedrock } from "./stream.runtime.js";
|
|
8
|
+
import { adaptMemoryEmbeddingProviderAdapter } from "openclaw/plugin-sdk/memory-core-host-engine-embeddings";
|
|
8
9
|
import { resolvePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
|
|
9
10
|
import { buildProviderReplayFamilyHooks, normalizeProviderId, resolveClaudeFable5ModelIdentity, resolveClaudeModelIdentity, resolveClaudeMythos5ModelIdentity, resolveClaudeOpus5ModelIdentity, resolveClaudeSonnet5ModelIdentity } from "openclaw/plugin-sdk/provider-model-shared";
|
|
10
|
-
import {
|
|
11
|
+
import { createPayloadPatchStreamWrapper } from "openclaw/plugin-sdk/provider-stream-shared";
|
|
11
12
|
//#region extensions/amazon-bedrock/register.sync.runtime.ts
|
|
12
13
|
function normalizeBedrockResolvedModel({ modelId, model }) {
|
|
13
14
|
const thinkingLevelMap = resolveBedrockNativeThinkingLevelMap(modelId, model.params);
|
|
@@ -68,28 +69,23 @@ function resolveBedrockServiceTier(extraParams, warn) {
|
|
|
68
69
|
warn(`ignoring invalid Bedrock service_tier param: ${raw}`);
|
|
69
70
|
}
|
|
70
71
|
function createBedrockServiceTierWrapper(underlying, serviceTier) {
|
|
71
|
-
return (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
payloadObj.serviceTier ??= { type: serviceTier };
|
|
75
|
-
});
|
|
76
|
-
};
|
|
72
|
+
return createPayloadPatchStreamWrapper(underlying, ({ payload }) => {
|
|
73
|
+
payload.serviceTier ??= { type: serviceTier };
|
|
74
|
+
}, { shouldPatch: ({ model }) => model.api === "bedrock-converse-stream" });
|
|
77
75
|
}
|
|
78
76
|
function createGuardrailWrapStreamFn(innerWrapStreamFn, guardrailConfig) {
|
|
79
77
|
return (ctx) => {
|
|
80
78
|
const inner = innerWrapStreamFn(ctx);
|
|
81
79
|
if (!inner) return inner;
|
|
82
|
-
return (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
});
|
|
92
|
-
};
|
|
80
|
+
return createPayloadPatchStreamWrapper(inner, ({ payload }) => {
|
|
81
|
+
const gc = {
|
|
82
|
+
guardrailIdentifier: guardrailConfig.guardrailIdentifier,
|
|
83
|
+
guardrailVersion: guardrailConfig.guardrailVersion
|
|
84
|
+
};
|
|
85
|
+
if (guardrailConfig.streamProcessingMode) gc.streamProcessingMode = guardrailConfig.streamProcessingMode;
|
|
86
|
+
if (guardrailConfig.trace) gc.trace = guardrailConfig.trace;
|
|
87
|
+
payload.guardrailConfig = gc;
|
|
88
|
+
});
|
|
93
89
|
};
|
|
94
90
|
}
|
|
95
91
|
function sharedRuntimeWouldInjectCachePoints(modelId) {
|
|
@@ -220,7 +216,7 @@ function registerAmazonBedrockPlugin(api) {
|
|
|
220
216
|
function resolveCurrentPluginConfig(config) {
|
|
221
217
|
return resolvePluginConfigObject(config, providerId) ?? (config ? void 0 : startupPluginConfig);
|
|
222
218
|
}
|
|
223
|
-
api.
|
|
219
|
+
api.registerEmbeddingProvider(adaptMemoryEmbeddingProviderAdapter(bedrockMemoryEmbeddingProviderAdapter));
|
|
224
220
|
const baseWrapStreamFn = ({ modelId, model, streamFn }) => {
|
|
225
221
|
const modelRef = {
|
|
226
222
|
id: modelId,
|
package/dist/stream.runtime.js
CHANGED
|
@@ -2,13 +2,13 @@ import { supportsBedrockPromptCaching } from "./bedrock-options.js";
|
|
|
2
2
|
import { supportsBedrockNativeMaxEffort } from "./thinking-policy.js";
|
|
3
3
|
import { requiresClaudeMandatoryAdaptiveThinking, resolveClaudeFable5ModelIdentity, resolveClaudeModelIdentity, resolveClaudeMythos5ModelIdentity, resolveClaudeOpus5ModelIdentity, resolveClaudeSonnet5ModelIdentity, supportsClaudeAdaptiveThinking, supportsClaudeNativeXhighEffort } from "openclaw/plugin-sdk/provider-model-shared";
|
|
4
4
|
import { applyAnthropicRefusal, createDeferredEventBuffer, notifyLlmRequestActivity } from "openclaw/plugin-sdk/provider-stream-shared";
|
|
5
|
-
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
5
|
+
import { isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
6
|
import { BedrockRuntimeClient, BedrockRuntimeServiceException, CachePointType, CacheTTL, ConversationRole, ConverseStreamCommand, ImageFormat, StopReason, ToolResultStatus } from "@aws-sdk/client-bedrock-runtime";
|
|
7
7
|
import { NodeHttpHandler } from "@smithy/node-http-handler";
|
|
8
8
|
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
9
9
|
import { AssistantMessageEventStream, adjustMaxTokensForThinking, buildBaseOptions, calculateCost, clampReasoning, createHttpProxyAgentsForTarget, parseStreamingJson, sanitizeSurrogates, transformMessages } from "openclaw/plugin-sdk/llm";
|
|
10
10
|
import { canonicalizeBase64 } from "openclaw/plugin-sdk/media-runtime";
|
|
11
|
-
import { describeToolResultMediaPlaceholder } from "openclaw/plugin-sdk/provider-transport-runtime";
|
|
11
|
+
import { describeToolResultMediaPlaceholder, finalizeTerminalToolCallArguments, notifyProviderHttpMetadata } from "openclaw/plugin-sdk/provider-transport-runtime";
|
|
12
12
|
//#region extensions/amazon-bedrock/stream.runtime.ts
|
|
13
13
|
/**
|
|
14
14
|
* Amazon Bedrock Converse streaming runtime. It maps OpenClaw messages/tools,
|
|
@@ -72,6 +72,8 @@ const streamBedrock = (model, context, options = {}) => {
|
|
|
72
72
|
timestamp: Date.now()
|
|
73
73
|
};
|
|
74
74
|
const blocks = output.content;
|
|
75
|
+
const pendingToolCallEnds = [];
|
|
76
|
+
const redactedReasoningChunks = /* @__PURE__ */ new Map();
|
|
75
77
|
const fable5 = usesClaudeFable5BedrockContract(model);
|
|
76
78
|
const refusalBuffer = usesClaudeStreamingRefusalBedrockContract(model) ? createDeferredEventBuffer(stream, () => notifyLlmRequestActivity(options.signal)) : void 0;
|
|
77
79
|
const eventSink = refusalBuffer ?? stream;
|
|
@@ -100,8 +102,9 @@ const streamBedrock = (model, context, options = {}) => {
|
|
|
100
102
|
config.token = { token: bearerToken };
|
|
101
103
|
config.authSchemePreference = ["httpBearerAuth"];
|
|
102
104
|
}
|
|
105
|
+
let client;
|
|
103
106
|
try {
|
|
104
|
-
|
|
107
|
+
client = new BedrockRuntimeClient(config);
|
|
105
108
|
const cacheRetention = resolveCacheRetention(options.cacheRetention);
|
|
106
109
|
const additionalModelRequestFields = buildAdditionalModelRequestFields(model, options);
|
|
107
110
|
const thinking = additionalModelRequestFields?.thinking;
|
|
@@ -123,24 +126,32 @@ const streamBedrock = (model, context, options = {}) => {
|
|
|
123
126
|
if (nextCommandInput !== void 0) commandInput = nextCommandInput;
|
|
124
127
|
const command = new ConverseStreamCommand(commandInput);
|
|
125
128
|
const response = await client.send(command, { abortSignal: options.signal });
|
|
129
|
+
const responseIterator = response.stream[Symbol.asyncIterator]();
|
|
126
130
|
if (response.$metadata.httpStatusCode !== void 0) {
|
|
127
131
|
const responseHeaders = {};
|
|
128
132
|
if (response.$metadata.requestId) responseHeaders["x-amzn-requestid"] = response.$metadata.requestId;
|
|
129
|
-
await
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
+
await notifyProviderHttpMetadata({
|
|
134
|
+
options,
|
|
135
|
+
response: {
|
|
136
|
+
status: response.$metadata.httpStatusCode,
|
|
137
|
+
headers: responseHeaders
|
|
138
|
+
},
|
|
139
|
+
model,
|
|
140
|
+
cancelStream: async () => {
|
|
141
|
+
await responseIterator.return?.();
|
|
142
|
+
}
|
|
143
|
+
});
|
|
133
144
|
}
|
|
134
145
|
let sawMessageStop = false;
|
|
135
|
-
for await (const item of
|
|
146
|
+
for await (const item of { [Symbol.asyncIterator]: () => responseIterator }) if (item.messageStart) {
|
|
136
147
|
if (item.messageStart.role !== ConversationRole.ASSISTANT) throw new Error("Unexpected assistant message start but got user message start instead");
|
|
137
148
|
eventSink.push({
|
|
138
149
|
type: "start",
|
|
139
150
|
partial: output
|
|
140
151
|
});
|
|
141
152
|
} 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);
|
|
153
|
+
else if (item.contentBlockDelta) handleContentBlockDelta(item.contentBlockDelta, blocks, output, eventSink, redactedReasoningChunks);
|
|
154
|
+
else if (item.contentBlockStop) handleContentBlockStop(item.contentBlockStop, blocks, output, eventSink, redactedReasoningChunks, pendingToolCallEnds);
|
|
144
155
|
else if (item.messageStop) {
|
|
145
156
|
sawMessageStop = true;
|
|
146
157
|
if (item.messageStop.stopReason === "refusal") applyAnthropicRefusal(output, readBedrockStopDetails(item.messageStop.additionalModelResponseFields), model.provider);
|
|
@@ -155,9 +166,11 @@ const streamBedrock = (model, context, options = {}) => {
|
|
|
155
166
|
else if (item.validationException) throw item.validationException;
|
|
156
167
|
else if (item.throttlingException) throw item.throttlingException;
|
|
157
168
|
else if (item.serviceUnavailableException) throw item.serviceUnavailableException;
|
|
158
|
-
if (
|
|
169
|
+
if (!sawMessageStop) throw new Error("Bedrock stream ended before messageStop");
|
|
159
170
|
if (options.signal?.aborted) throw new Error("Request was aborted");
|
|
160
171
|
if (output.stopReason === "error" || output.stopReason === "aborted") throw new Error(output.errorMessage ?? "An unknown error occurred");
|
|
172
|
+
for (const block of blocks) if (block.index !== void 0 && block.type !== "toolCall") handleContentBlockStop({ contentBlockIndex: block.index }, blocks, output, eventSink, redactedReasoningChunks, pendingToolCallEnds);
|
|
173
|
+
flushPendingBedrockToolCalls(pendingToolCallEnds, blocks, output, eventSink);
|
|
161
174
|
refusalBuffer?.flush();
|
|
162
175
|
stream.push({
|
|
163
176
|
type: "done",
|
|
@@ -166,6 +179,7 @@ const streamBedrock = (model, context, options = {}) => {
|
|
|
166
179
|
});
|
|
167
180
|
stream.end();
|
|
168
181
|
} catch (error) {
|
|
182
|
+
output.content = output.content.filter((block) => block.type !== "toolCall");
|
|
169
183
|
for (const block of output.content) {
|
|
170
184
|
delete block.index;
|
|
171
185
|
delete block.partialJson;
|
|
@@ -182,6 +196,8 @@ const streamBedrock = (model, context, options = {}) => {
|
|
|
182
196
|
error: output
|
|
183
197
|
});
|
|
184
198
|
stream.end();
|
|
199
|
+
} finally {
|
|
200
|
+
client?.destroy();
|
|
185
201
|
}
|
|
186
202
|
})();
|
|
187
203
|
return stream;
|
|
@@ -269,11 +285,12 @@ function handleContentBlockStart(event, blocks, output, stream) {
|
|
|
269
285
|
const index = event.contentBlockIndex;
|
|
270
286
|
const start = event.start;
|
|
271
287
|
if (start?.toolUse) {
|
|
288
|
+
const startArguments = isRecord(start.toolUse) ? start.toolUse.input : void 0;
|
|
272
289
|
const block = {
|
|
273
290
|
type: "toolCall",
|
|
274
291
|
id: start.toolUse.toolUseId || "",
|
|
275
292
|
name: start.toolUse.name || "",
|
|
276
|
-
arguments: {},
|
|
293
|
+
arguments: isRecord(startArguments) ? startArguments : {},
|
|
277
294
|
partialJson: "",
|
|
278
295
|
index
|
|
279
296
|
};
|
|
@@ -285,7 +302,7 @@ function handleContentBlockStart(event, blocks, output, stream) {
|
|
|
285
302
|
});
|
|
286
303
|
}
|
|
287
304
|
}
|
|
288
|
-
function handleContentBlockDelta(event, blocks, output, stream) {
|
|
305
|
+
function handleContentBlockDelta(event, blocks, output, stream, redactedReasoningChunks) {
|
|
289
306
|
const contentBlockIndex = event.contentBlockIndex;
|
|
290
307
|
const delta = event.delta;
|
|
291
308
|
let index = blocks.findIndex((b) => b.index === contentBlockIndex);
|
|
@@ -354,6 +371,13 @@ function handleContentBlockDelta(event, blocks, output, stream) {
|
|
|
354
371
|
});
|
|
355
372
|
}
|
|
356
373
|
if (delta.reasoningContent.signature) thinkingBlock.thinkingSignature = (thinkingBlock.thinkingSignature || "") + delta.reasoningContent.signature;
|
|
374
|
+
if (delta.reasoningContent.redactedContent) {
|
|
375
|
+
const chunks = redactedReasoningChunks.get(contentBlockIndex);
|
|
376
|
+
if (chunks) chunks.push(delta.reasoningContent.redactedContent);
|
|
377
|
+
else redactedReasoningChunks.set(contentBlockIndex, [delta.reasoningContent.redactedContent]);
|
|
378
|
+
thinkingBlock.thinking = "[Reasoning redacted]";
|
|
379
|
+
thinkingBlock.redacted = true;
|
|
380
|
+
}
|
|
357
381
|
}
|
|
358
382
|
}
|
|
359
383
|
}
|
|
@@ -363,17 +387,25 @@ function handleMetadata(event, model, output) {
|
|
|
363
387
|
output.usage.output = event.usage.outputTokens || 0;
|
|
364
388
|
output.usage.cacheRead = event.usage.cacheReadInputTokens || 0;
|
|
365
389
|
output.usage.cacheWrite = event.usage.cacheWriteInputTokens || 0;
|
|
366
|
-
|
|
390
|
+
const promptTokens = output.usage.input + output.usage.cacheRead + output.usage.cacheWrite;
|
|
391
|
+
output.usage.totalTokens = Math.max(event.usage.totalTokens || 0, promptTokens + output.usage.output);
|
|
392
|
+
output.usage.contextUsage = {
|
|
393
|
+
state: "available",
|
|
394
|
+
promptTokens,
|
|
395
|
+
totalTokens: promptTokens + output.usage.output
|
|
396
|
+
};
|
|
397
|
+
const cacheWrite1h = event.usage.cacheDetails?.reduce((total, detail) => detail.ttl === CacheTTL.ONE_HOUR ? total + (detail.inputTokens ?? 0) : total, 0);
|
|
398
|
+
if (cacheWrite1h) output.usage.cacheWrite1h = cacheWrite1h;
|
|
367
399
|
calculateCost(model, output.usage);
|
|
368
400
|
}
|
|
369
401
|
}
|
|
370
|
-
function handleContentBlockStop(event, blocks, output, stream) {
|
|
402
|
+
function handleContentBlockStop(event, blocks, output, stream, redactedReasoningChunks, pendingToolCallEnds) {
|
|
371
403
|
const index = blocks.findIndex((b) => b.index === event.contentBlockIndex);
|
|
372
404
|
const block = blocks[index];
|
|
373
405
|
if (!block) return;
|
|
374
|
-
delete block.index;
|
|
375
406
|
switch (block.type) {
|
|
376
407
|
case "text":
|
|
408
|
+
delete block.index;
|
|
377
409
|
stream.push({
|
|
378
410
|
type: "text_end",
|
|
379
411
|
contentIndex: index,
|
|
@@ -382,6 +414,16 @@ function handleContentBlockStop(event, blocks, output, stream) {
|
|
|
382
414
|
});
|
|
383
415
|
break;
|
|
384
416
|
case "thinking":
|
|
417
|
+
delete block.index;
|
|
418
|
+
if (block.redacted) {
|
|
419
|
+
const chunks = redactedReasoningChunks.get(event.contentBlockIndex);
|
|
420
|
+
if (chunks) {
|
|
421
|
+
let opaqueReasoning = "";
|
|
422
|
+
for (const chunk of chunks) for (const byte of chunk) opaqueReasoning += String.fromCharCode(byte);
|
|
423
|
+
block.thinkingSignature = btoa(opaqueReasoning);
|
|
424
|
+
redactedReasoningChunks.delete(event.contentBlockIndex);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
385
427
|
stream.push({
|
|
386
428
|
type: "thinking_end",
|
|
387
429
|
contentIndex: index,
|
|
@@ -390,16 +432,27 @@ function handleContentBlockStop(event, blocks, output, stream) {
|
|
|
390
432
|
});
|
|
391
433
|
break;
|
|
392
434
|
case "toolCall":
|
|
393
|
-
delete block.
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
contentIndex: index
|
|
397
|
-
toolCall: block,
|
|
398
|
-
partial: output
|
|
435
|
+
delete block.index;
|
|
436
|
+
pendingToolCallEnds.push({
|
|
437
|
+
block,
|
|
438
|
+
contentIndex: index
|
|
399
439
|
});
|
|
400
440
|
break;
|
|
401
441
|
}
|
|
402
442
|
}
|
|
443
|
+
function flushPendingBedrockToolCalls(pending, blocks, output, stream) {
|
|
444
|
+
if (blocks.some((block) => block.type === "toolCall" && block.index !== void 0)) throw new Error("Provider completed stream with an incomplete tool call");
|
|
445
|
+
finalizeTerminalToolCallArguments(pending.map(({ block }) => block), (block) => block.partialJson && block.partialJson.length > 0 ? block.partialJson : block.arguments);
|
|
446
|
+
for (const toolCall of pending) {
|
|
447
|
+
delete toolCall.block.partialJson;
|
|
448
|
+
stream.push({
|
|
449
|
+
type: "toolcall_end",
|
|
450
|
+
contentIndex: toolCall.contentIndex,
|
|
451
|
+
toolCall: toolCall.block,
|
|
452
|
+
partial: output
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
403
456
|
function resolveClaudeProfileNameModelId(modelName) {
|
|
404
457
|
const normalized = modelName?.trim().toLowerCase().replace(/[\s_.:]+/g, "-") ?? "";
|
|
405
458
|
if (!normalized.includes("claude")) return;
|
|
@@ -495,16 +548,17 @@ function createBedrockToolResult(message) {
|
|
|
495
548
|
content.push({ text: sanitizeSurrogates(block.text) });
|
|
496
549
|
continue;
|
|
497
550
|
}
|
|
498
|
-
if (describeToolResultMediaPlaceholder([block])) content.push({ image: createImageBlock(block.mimeType, block.data) });
|
|
551
|
+
if (block.type === "image" && describeToolResultMediaPlaceholder([block])) content.push({ image: createImageBlock(block.mimeType, block.data) });
|
|
499
552
|
}
|
|
500
553
|
return { toolResult: {
|
|
501
554
|
toolUseId: message.toolCallId,
|
|
502
|
-
content: content.length > 0 ? content : [{ text: "(no output)" }],
|
|
555
|
+
content: content.length > 0 ? content : [{ text: describeToolResultMediaPlaceholder(message.content) ?? "(no output)" }],
|
|
503
556
|
status: message.isError ? ToolResultStatus.ERROR : ToolResultStatus.SUCCESS
|
|
504
557
|
} };
|
|
505
558
|
}
|
|
506
559
|
function convertMessages(context, model, cacheRetention) {
|
|
507
560
|
const result = [];
|
|
561
|
+
let firstVolatileMessageIndex;
|
|
508
562
|
const transformedMessages = transformMessages(context.messages, model, normalizeToolCallId);
|
|
509
563
|
for (let i = 0; i < transformedMessages.length; i++) {
|
|
510
564
|
const m = expectDefined(transformedMessages[i], "message conversion index is in bounds");
|
|
@@ -522,6 +576,7 @@ function convertMessages(context, model, cacheRetention) {
|
|
|
522
576
|
default: continue;
|
|
523
577
|
}
|
|
524
578
|
if (content.length === 0) continue;
|
|
579
|
+
if (m.runtimeContextCarrier === true && firstVolatileMessageIndex === void 0) firstVolatileMessageIndex = result.length;
|
|
525
580
|
result.push({
|
|
526
581
|
role: ConversationRole.USER,
|
|
527
582
|
content
|
|
@@ -544,6 +599,12 @@ function convertMessages(context, model, cacheRetention) {
|
|
|
544
599
|
} });
|
|
545
600
|
break;
|
|
546
601
|
case "thinking": {
|
|
602
|
+
if (c.redacted) {
|
|
603
|
+
if (!supportsThinkingSignature(model)) continue;
|
|
604
|
+
if (!c.thinkingSignature) throw new Error("Bedrock redacted reasoning block is missing its opaque signature");
|
|
605
|
+
contentBlocks.push({ reasoningContent: { redactedContent: decodeBedrockBase64(c.thinkingSignature, "Bedrock redacted reasoning block has a malformed opaque signature") } });
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
547
608
|
const thinkingSignature = c.thinkingSignature;
|
|
548
609
|
const normalizedThinkingSignature = thinkingSignature?.trim();
|
|
549
610
|
const supportsSignature = supportsThinkingSignature(model);
|
|
@@ -588,9 +649,9 @@ function convertMessages(context, model, cacheRetention) {
|
|
|
588
649
|
default: continue;
|
|
589
650
|
}
|
|
590
651
|
}
|
|
591
|
-
if (cacheRetention !== "none" && supportsPromptCaching(model) && result.
|
|
592
|
-
const
|
|
593
|
-
if (
|
|
652
|
+
if (cacheRetention !== "none" && supportsPromptCaching(model) && result.at(-1)?.role === ConversationRole.USER) {
|
|
653
|
+
const cacheAnchor = result.findLast((message, index) => message.role === ConversationRole.USER && (firstVolatileMessageIndex === void 0 || index < firstVolatileMessageIndex));
|
|
654
|
+
if (cacheAnchor?.content) cacheAnchor.content.push({ cachePoint: {
|
|
594
655
|
type: CachePointType.DEFAULT,
|
|
595
656
|
...cacheRetention === "long" ? { ttl: CacheTTL.ONE_HOUR } : {}
|
|
596
657
|
} });
|
|
@@ -602,7 +663,7 @@ function convertToolConfig(tools, toolChoice) {
|
|
|
602
663
|
const bedrockTools = tools.map((tool) => ({ toolSpec: {
|
|
603
664
|
name: tool.name,
|
|
604
665
|
description: tool.description,
|
|
605
|
-
inputSchema: { json: tool.parameters }
|
|
666
|
+
inputSchema: { json: { ...tool.parameters } }
|
|
606
667
|
} }));
|
|
607
668
|
let bedrockToolChoice;
|
|
608
669
|
switch (toolChoice) {
|
|
@@ -720,26 +781,18 @@ function createImageBlock(mimeType, data) {
|
|
|
720
781
|
break;
|
|
721
782
|
default: throw new Error(`Unknown image type: ${mimeType}`);
|
|
722
783
|
}
|
|
784
|
+
return {
|
|
785
|
+
source: { bytes: decodeBedrockBase64(data, "Amazon Bedrock image content has malformed base64") },
|
|
786
|
+
format
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
function decodeBedrockBase64(data, errorMessage) {
|
|
723
790
|
const canonicalBase64 = canonicalizeBase64(data);
|
|
724
|
-
if (!canonicalBase64) throw new Error(
|
|
791
|
+
if (!canonicalBase64) throw new Error(errorMessage);
|
|
725
792
|
const binaryString = atob(canonicalBase64);
|
|
726
793
|
const bytes = new Uint8Array(binaryString.length);
|
|
727
794
|
for (let i = 0; i < binaryString.length; i++) bytes[i] = binaryString.charCodeAt(i);
|
|
728
|
-
return
|
|
729
|
-
source: { bytes },
|
|
730
|
-
format
|
|
731
|
-
};
|
|
795
|
+
return bytes;
|
|
732
796
|
}
|
|
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
797
|
//#endregion
|
|
745
798
|
export { streamSimpleBedrock };
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/amazon-bedrock-provider",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.8.1-beta.3",
|
|
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.
|
|
28
|
+
"pluginApi": ">=2026.8.1-beta.3"
|
|
29
29
|
},
|
|
30
30
|
"build": {
|
|
31
|
-
"openclawVersion": "2026.
|
|
31
|
+
"openclawVersion": "2026.8.1-beta.3",
|
|
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.
|
|
48
|
+
"openclaw": ">=2026.8.1-beta.3"
|
|
49
49
|
},
|
|
50
50
|
"peerDependenciesMeta": {
|
|
51
51
|
"openclaw": {
|