@openclaw/amazon-bedrock-provider 2026.5.12-beta.7

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 ADDED
@@ -0,0 +1,3 @@
1
+ import { mergeImplicitBedrockProvider, resolveBedrockConfigApiKey } from "./discovery-shared.js";
2
+ import { discoverBedrockModels, resetBedrockDiscoveryCacheForTest, resolveImplicitBedrockProvider } from "./discovery.js";
3
+ export { discoverBedrockModels, mergeImplicitBedrockProvider, resetBedrockDiscoveryCacheForTest, resolveBedrockConfigApiKey, resolveImplicitBedrockProvider };
@@ -0,0 +1,25 @@
1
+ //#region extensions/amazon-bedrock/aws-credential-refresh.ts
2
+ let sharedIniFileLoaderForTest;
3
+ function hasStaticAwsCredentialEnv(env) {
4
+ return Boolean(env.AWS_ACCESS_KEY_ID && env.AWS_SECRET_ACCESS_KEY);
5
+ }
6
+ function shouldRefreshAwsSharedConfigCacheForBedrock(env) {
7
+ if (env.AWS_BEDROCK_SKIP_AUTH === "1" || env.AWS_BEARER_TOKEN_BEDROCK) return false;
8
+ return !hasStaticAwsCredentialEnv(env);
9
+ }
10
+ async function loadSharedIniFileLoader() {
11
+ if (sharedIniFileLoaderForTest !== void 0) {
12
+ if (!sharedIniFileLoaderForTest) throw new Error("AWS shared INI file loader unavailable");
13
+ return sharedIniFileLoaderForTest;
14
+ }
15
+ return await import("@smithy/shared-ini-file-loader");
16
+ }
17
+ async function refreshAwsSharedConfigCacheForBedrock(env = process.env) {
18
+ if (!shouldRefreshAwsSharedConfigCacheForBedrock(env)) return;
19
+ await (await loadSharedIniFileLoader()).loadSharedConfigFiles({ ignoreCache: true });
20
+ }
21
+ function setAwsSharedIniFileLoaderForTest(loader) {
22
+ sharedIniFileLoaderForTest = loader;
23
+ }
24
+ //#endregion
25
+ export { refreshAwsSharedConfigCacheForBedrock, setAwsSharedIniFileLoaderForTest, shouldRefreshAwsSharedConfigCacheForBedrock };
@@ -0,0 +1,94 @@
1
+ import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
2
+ //#region extensions/amazon-bedrock/config-compat.ts
3
+ const LEGACY_PATH = "models.bedrockDiscovery";
4
+ const TARGET_PATH = "plugins.entries.amazon-bedrock.config.discovery";
5
+ const BLOCKED_OBJECT_KEYS = new Set([
6
+ "__proto__",
7
+ "prototype",
8
+ "constructor"
9
+ ]);
10
+ function isBlockedObjectKey(key) {
11
+ return BLOCKED_OBJECT_KEYS.has(key);
12
+ }
13
+ function getRecord(value) {
14
+ return isRecord(value) ? value : null;
15
+ }
16
+ function ensureRecord(root, key) {
17
+ const existing = root[key];
18
+ if (isRecord(existing)) return existing;
19
+ const next = {};
20
+ root[key] = next;
21
+ return next;
22
+ }
23
+ function mergeMissing(target, source) {
24
+ for (const [key, value] of Object.entries(source)) {
25
+ if (value === void 0 || isBlockedObjectKey(key)) continue;
26
+ const existing = target[key];
27
+ if (existing === void 0) {
28
+ target[key] = value;
29
+ continue;
30
+ }
31
+ if (isRecord(existing) && isRecord(value)) mergeMissing(existing, value);
32
+ }
33
+ }
34
+ function cloneRecord(value) {
35
+ return { ...value };
36
+ }
37
+ function resolveLegacyBedrockDiscoveryConfig(raw) {
38
+ if (!isRecord(raw)) return;
39
+ return getRecord(getRecord(raw.models)?.bedrockDiscovery) ?? void 0;
40
+ }
41
+ function pruneEmptyModelsRoot(root) {
42
+ const models = getRecord(root.models);
43
+ if (models && Object.keys(models).length === 0) delete root.models;
44
+ }
45
+ function migrateAmazonBedrockLegacyConfig(raw) {
46
+ if (!isRecord(raw)) return {
47
+ config: raw,
48
+ changes: []
49
+ };
50
+ const legacy = resolveLegacyBedrockDiscoveryConfig(raw);
51
+ if (!legacy) return {
52
+ config: raw,
53
+ changes: []
54
+ };
55
+ const nextRoot = structuredClone(raw);
56
+ const models = ensureRecord(nextRoot, "models");
57
+ delete models.bedrockDiscovery;
58
+ pruneEmptyModelsRoot(nextRoot);
59
+ const changes = [];
60
+ if (Object.keys(legacy).length === 0) {
61
+ changes.push(`Removed empty ${LEGACY_PATH}.`);
62
+ return {
63
+ config: nextRoot,
64
+ changes
65
+ };
66
+ }
67
+ const config = ensureRecord(ensureRecord(ensureRecord(ensureRecord(nextRoot, "plugins"), "entries"), "amazon-bedrock"), "config");
68
+ const existing = getRecord(config.discovery) ?? void 0;
69
+ if (!existing) {
70
+ config.discovery = cloneRecord(legacy);
71
+ changes.push(`Moved ${LEGACY_PATH} → ${TARGET_PATH}.`);
72
+ return {
73
+ config: nextRoot,
74
+ changes
75
+ };
76
+ }
77
+ const merged = cloneRecord(existing);
78
+ mergeMissing(merged, legacy);
79
+ config.discovery = merged;
80
+ if (JSON.stringify(merged) !== JSON.stringify(existing)) {
81
+ changes.push(`Merged ${LEGACY_PATH} → ${TARGET_PATH} (filled missing fields from legacy; kept explicit plugin config values).`);
82
+ return {
83
+ config: nextRoot,
84
+ changes
85
+ };
86
+ }
87
+ changes.push(`Removed ${LEGACY_PATH} (${TARGET_PATH} already set).`);
88
+ return {
89
+ config: nextRoot,
90
+ changes
91
+ };
92
+ }
93
+ //#endregion
94
+ export { migrateAmazonBedrockLegacyConfig };
@@ -0,0 +1,16 @@
1
+ import { resolveAwsSdkEnvVarName } from "openclaw/plugin-sdk/provider-auth-runtime";
2
+ //#region extensions/amazon-bedrock/discovery-shared.ts
3
+ function resolveBedrockConfigApiKey(env = process.env) {
4
+ return resolveAwsSdkEnvVarName(env);
5
+ }
6
+ function mergeImplicitBedrockProvider(params) {
7
+ const { existing, implicit } = params;
8
+ if (!existing) return implicit;
9
+ return {
10
+ ...implicit,
11
+ ...existing,
12
+ models: Array.isArray(existing.models) && existing.models.length > 0 ? existing.models : implicit.models
13
+ };
14
+ }
15
+ //#endregion
16
+ export { mergeImplicitBedrockProvider, resolveBedrockConfigApiKey };
@@ -0,0 +1,379 @@
1
+ import { refreshAwsSharedConfigCacheForBedrock } from "./aws-credential-refresh.js";
2
+ import { resolveBedrockConfigApiKey } from "./discovery-shared.js";
3
+ import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
4
+ import { createSubsystemLogger } from "openclaw/plugin-sdk/core";
5
+ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
6
+ //#region extensions/amazon-bedrock/discovery.ts
7
+ const log = createSubsystemLogger("bedrock-discovery");
8
+ const DEFAULT_REFRESH_INTERVAL_SECONDS = 3600;
9
+ const DEFAULT_CONTEXT_WINDOW = 32e3;
10
+ const DEFAULT_MAX_TOKENS = 4096;
11
+ /**
12
+ * Bedrock's ListFoundationModels and GetFoundationModel APIs return no token
13
+ * limit information — only model ID, name, modalities, and lifecycle status.
14
+ * There is currently no Bedrock API to discover context windows or max output
15
+ * tokens programmatically.
16
+ *
17
+ * This map provides correct context window values for known models so that
18
+ * session management, compaction thresholds, and context overflow detection
19
+ * work correctly. If AWS adds token metadata to the API in the future, this
20
+ * table should become a fallback rather than the primary source.
21
+ *
22
+ * Inference profile prefixes (us., eu., ap., global.) are stripped before lookup.
23
+ *
24
+ * Sources: https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html
25
+ * https://platform.claude.com/docs/en/about-claude/models
26
+ */
27
+ const KNOWN_CONTEXT_WINDOWS = {
28
+ "anthropic.claude-3-7-sonnet-20250219-v1:0": 2e5,
29
+ "anthropic.claude-opus-4-7": 1e6,
30
+ "anthropic.claude-opus-4-6-v1": 1e6,
31
+ "anthropic.claude-opus-4-6-v1:0": 1e6,
32
+ "anthropic.claude-sonnet-4-6": 1e6,
33
+ "anthropic.claude-sonnet-4-6-v1:0": 1e6,
34
+ "anthropic.claude-sonnet-4-5-20250929-v1:0": 2e5,
35
+ "anthropic.claude-sonnet-4-20250514-v1:0": 2e5,
36
+ "anthropic.claude-opus-4-5-20251101-v1:0": 2e5,
37
+ "anthropic.claude-opus-4-1-20250805-v1:0": 2e5,
38
+ "anthropic.claude-haiku-4-5-20251001-v1:0": 2e5,
39
+ "anthropic.claude-3-5-haiku-20241022-v1:0": 2e5,
40
+ "anthropic.claude-3-haiku-20240307-v1:0": 2e5,
41
+ "amazon.nova-premier-v1:0": 1e6,
42
+ "amazon.nova-pro-v1:0": 3e5,
43
+ "amazon.nova-lite-v1:0": 3e5,
44
+ "amazon.nova-micro-v1:0": 128e3,
45
+ "amazon.nova-2-lite-v1:0": 3e5,
46
+ "minimax.minimax-m2.5": 1e6,
47
+ "minimax.minimax-m2.1": 1e6,
48
+ "minimax.minimax-m2": 1e6,
49
+ "meta.llama4-maverick-17b-instruct-v1:0": 1e6,
50
+ "meta.llama4-scout-17b-instruct-v1:0": 512e3,
51
+ "meta.llama3-3-70b-instruct-v1:0": 128e3,
52
+ "meta.llama3-2-90b-instruct-v1:0": 128e3,
53
+ "meta.llama3-2-11b-instruct-v1:0": 128e3,
54
+ "meta.llama3-2-3b-instruct-v1:0": 128e3,
55
+ "meta.llama3-2-1b-instruct-v1:0": 128e3,
56
+ "meta.llama3-1-405b-instruct-v1:0": 128e3,
57
+ "meta.llama3-1-70b-instruct-v1:0": 128e3,
58
+ "meta.llama3-1-8b-instruct-v1:0": 128e3,
59
+ "nvidia.nemotron-super-3-120b": 256e3,
60
+ "nvidia.nemotron-nano-3-30b": 128e3,
61
+ "nvidia.nemotron-nano-12b-v2": 128e3,
62
+ "nvidia.nemotron-nano-9b-v2": 128e3,
63
+ "mistral.mistral-large-3-675b-instruct": 128e3,
64
+ "mistral.mistral-large-2407-v1:0": 128e3,
65
+ "mistral.mistral-small-2402-v1:0": 32e3,
66
+ "deepseek.r1-v1:0": 128e3,
67
+ "deepseek.v3.2": 128e3,
68
+ "cohere.command-r-plus-v1:0": 128e3,
69
+ "cohere.command-r-v1:0": 128e3,
70
+ "ai21.jamba-1-5-large-v1:0": 256e3,
71
+ "ai21.jamba-1-5-mini-v1:0": 256e3,
72
+ "google.gemma-3-27b-it": 128e3,
73
+ "google.gemma-3-12b-it": 128e3,
74
+ "google.gemma-3-4b-it": 128e3,
75
+ "zai.glm-5": 128e3,
76
+ "zai.glm-4.7": 128e3,
77
+ "zai.glm-4.7-flash": 128e3,
78
+ "qwen.qwen3-coder-next": 256e3,
79
+ "qwen.qwen3-coder-30b-a3b-v1:0": 256e3,
80
+ "qwen.qwen3-32b-v1:0": 128e3,
81
+ "qwen.qwen3-vl-235b-a22b": 128e3
82
+ };
83
+ /**
84
+ * Resolve the real context window for a Bedrock model ID.
85
+ * Strips inference profile prefixes (us., eu., ap., global.) before lookup.
86
+ */
87
+ function resolveKnownContextWindow(modelId) {
88
+ const candidates = [modelId, modelId.replace(/^(?:us|eu|ap|apac|au|jp|global)\./, "")];
89
+ for (const candidate of candidates) {
90
+ if (KNOWN_CONTEXT_WINDOWS[candidate] !== void 0) return KNOWN_CONTEXT_WINDOWS[candidate];
91
+ const withoutVersionSuffix = candidate.replace(/:0$/, "");
92
+ if (withoutVersionSuffix !== candidate && KNOWN_CONTEXT_WINDOWS[withoutVersionSuffix] !== void 0) return KNOWN_CONTEXT_WINDOWS[withoutVersionSuffix];
93
+ }
94
+ }
95
+ const DEFAULT_COST = {
96
+ input: 0,
97
+ output: 0,
98
+ cacheRead: 0,
99
+ cacheWrite: 0
100
+ };
101
+ async function loadBedrockDiscoverySdk() {
102
+ const { BedrockClient, ListFoundationModelsCommand, ListInferenceProfilesCommand } = await import("@aws-sdk/client-bedrock");
103
+ return {
104
+ createClient: (region) => new BedrockClient({ region }),
105
+ createListFoundationModelsCommand: () => new ListFoundationModelsCommand({}),
106
+ createListInferenceProfilesCommand: (input) => new ListInferenceProfilesCommand(input)
107
+ };
108
+ }
109
+ function createInjectedClientDiscoverySdk() {
110
+ class ListFoundationModelsCommand {
111
+ constructor(input = {}) {
112
+ this.input = input;
113
+ }
114
+ }
115
+ class ListInferenceProfilesCommand {
116
+ constructor(input = {}) {
117
+ this.input = input;
118
+ }
119
+ }
120
+ return {
121
+ createClient() {
122
+ throw new Error("clientFactory is required for injected Bedrock discovery commands");
123
+ },
124
+ createListFoundationModelsCommand: () => new ListFoundationModelsCommand({}),
125
+ createListInferenceProfilesCommand: (input) => new ListInferenceProfilesCommand(input)
126
+ };
127
+ }
128
+ const discoveryCache = /* @__PURE__ */ new Map();
129
+ let hasLoggedBedrockError = false;
130
+ function normalizeProviderFilter(filter) {
131
+ if (!filter || filter.length === 0) return [];
132
+ const normalized = new Set(filter.map((entry) => normalizeOptionalLowercaseString(entry)).filter((entry) => Boolean(entry)));
133
+ return Array.from(normalized).toSorted();
134
+ }
135
+ function buildCacheKey(params) {
136
+ return JSON.stringify(params);
137
+ }
138
+ function includesTextModalities(modalities) {
139
+ return (modalities ?? []).some((entry) => normalizeOptionalLowercaseString(entry) === "text");
140
+ }
141
+ function isActive(summary) {
142
+ const status = summary.modelLifecycle?.status;
143
+ return typeof status === "string" ? status.toUpperCase() === "ACTIVE" : false;
144
+ }
145
+ function mapInputModalities(summary) {
146
+ const inputs = summary.inputModalities ?? [];
147
+ const mapped = /* @__PURE__ */ new Set();
148
+ for (const modality of inputs) {
149
+ const lower = normalizeOptionalLowercaseString(modality);
150
+ if (lower === "text") mapped.add("text");
151
+ if (lower === "image") mapped.add("image");
152
+ }
153
+ if (mapped.size === 0) mapped.add("text");
154
+ return Array.from(mapped);
155
+ }
156
+ function inferReasoningSupport(summary) {
157
+ const haystack = normalizeLowercaseStringOrEmpty(`${summary.modelId ?? ""} ${summary.modelName ?? ""}`);
158
+ return haystack.includes("reasoning") || haystack.includes("thinking");
159
+ }
160
+ function resolveDefaultContextWindow(config) {
161
+ const value = Math.floor(config?.defaultContextWindow ?? DEFAULT_CONTEXT_WINDOW);
162
+ return value > 0 ? value : DEFAULT_CONTEXT_WINDOW;
163
+ }
164
+ function resolveDefaultMaxTokens(config) {
165
+ const value = Math.floor(config?.defaultMaxTokens ?? DEFAULT_MAX_TOKENS);
166
+ return value > 0 ? value : DEFAULT_MAX_TOKENS;
167
+ }
168
+ function matchesProviderFilter(summary, filter) {
169
+ if (filter.length === 0) return true;
170
+ const normalized = normalizeOptionalLowercaseString(summary.providerName ?? (typeof summary.modelId === "string" ? summary.modelId.split(".")[0] : void 0));
171
+ if (!normalized) return false;
172
+ return filter.includes(normalized);
173
+ }
174
+ function shouldIncludeSummary(summary, filter) {
175
+ if (!summary.modelId?.trim()) return false;
176
+ if (!matchesProviderFilter(summary, filter)) return false;
177
+ if (summary.responseStreamingSupported !== true) return false;
178
+ if (!includesTextModalities(summary.outputModalities)) return false;
179
+ if (!isActive(summary)) return false;
180
+ return true;
181
+ }
182
+ function toModelDefinition(summary, defaults) {
183
+ const id = summary.modelId?.trim() ?? "";
184
+ return {
185
+ id,
186
+ name: summary.modelName?.trim() || id,
187
+ reasoning: inferReasoningSupport(summary),
188
+ input: mapInputModalities(summary),
189
+ cost: DEFAULT_COST,
190
+ contextWindow: resolveKnownContextWindow(id) ?? defaults.contextWindow,
191
+ maxTokens: defaults.maxTokens
192
+ };
193
+ }
194
+ /**
195
+ * Resolve the base foundation model ID from an inference profile.
196
+ *
197
+ * System-defined profiles use a region prefix:
198
+ * "us.anthropic.claude-sonnet-4-6" → "anthropic.claude-sonnet-4-6"
199
+ *
200
+ * Application profiles carry the model ARN in their models[] array:
201
+ * models[0].modelArn = "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-sonnet-4-6"
202
+ * → "anthropic.claude-sonnet-4-6"
203
+ */
204
+ function resolveBaseModelId(profile) {
205
+ const firstArn = profile.models?.[0]?.modelArn;
206
+ if (firstArn) {
207
+ const arnMatch = /foundation-model\/(.+)$/.exec(firstArn);
208
+ if (arnMatch) return arnMatch[1];
209
+ }
210
+ if (profile.type === "SYSTEM_DEFINED") {
211
+ const id = profile.inferenceProfileId ?? "";
212
+ const prefixMatch = /^(?:us|eu|ap|apac|au|jp|global)\.(.+)$/i.exec(id);
213
+ if (prefixMatch) return prefixMatch[1];
214
+ }
215
+ }
216
+ /**
217
+ * Fetch raw inference profile summaries from the Bedrock control plane.
218
+ * Handles pagination. Best-effort: silently returns empty array if IAM lacks
219
+ * bedrock:ListInferenceProfiles permission.
220
+ */
221
+ async function fetchInferenceProfileSummaries(client, createListInferenceProfilesCommand) {
222
+ try {
223
+ const profiles = [];
224
+ let nextToken;
225
+ do {
226
+ const response = await client.send(createListInferenceProfilesCommand({ nextToken }));
227
+ for (const summary of response.inferenceProfileSummaries ?? []) profiles.push(summary);
228
+ nextToken = response.nextToken;
229
+ } while (nextToken);
230
+ return profiles;
231
+ } catch (error) {
232
+ log.debug?.("Skipping inference profile discovery", { error: formatErrorMessage(error) });
233
+ return [];
234
+ }
235
+ }
236
+ /**
237
+ * Convert raw inference profile summaries into model definitions.
238
+ *
239
+ * Each profile inherits capabilities (modalities, reasoning, context window,
240
+ * cost) from its underlying foundation model. This ensures that
241
+ * "us.anthropic.claude-sonnet-4-6" has the same capabilities as
242
+ * "anthropic.claude-sonnet-4-6" — including image input, reasoning support,
243
+ * and token limits.
244
+ *
245
+ * When the foundation model isn't found in the map (e.g. the model is only
246
+ * available via inference profiles in this region), safe defaults are used.
247
+ */
248
+ function resolveInferenceProfiles(profiles, defaults, providerFilter, foundationModels) {
249
+ const discovered = [];
250
+ for (const profile of profiles) {
251
+ if (!profile.inferenceProfileId?.trim()) continue;
252
+ if (profile.status !== "ACTIVE") continue;
253
+ if (providerFilter.length > 0) {
254
+ if (!(profile.models ?? []).some((m) => {
255
+ const provider = m.modelArn?.split("/")?.[1]?.split(".")?.[0];
256
+ return provider ? providerFilter.includes(normalizeOptionalLowercaseString(provider) ?? "") : false;
257
+ })) continue;
258
+ }
259
+ const baseModelId = resolveBaseModelId(profile);
260
+ const baseModel = baseModelId ? foundationModels.get(normalizeLowercaseStringOrEmpty(baseModelId)) : void 0;
261
+ discovered.push({
262
+ id: profile.inferenceProfileId,
263
+ name: profile.inferenceProfileName?.trim() || profile.inferenceProfileId,
264
+ reasoning: baseModel?.reasoning ?? false,
265
+ input: baseModel?.input ?? ["text"],
266
+ cost: baseModel?.cost ?? DEFAULT_COST,
267
+ contextWindow: baseModel?.contextWindow ?? resolveKnownContextWindow(baseModelId ?? profile.inferenceProfileId ?? "") ?? defaults.contextWindow,
268
+ maxTokens: baseModel?.maxTokens ?? defaults.maxTokens
269
+ });
270
+ }
271
+ return discovered;
272
+ }
273
+ function resetBedrockDiscoveryCacheForTest() {
274
+ discoveryCache.clear();
275
+ hasLoggedBedrockError = false;
276
+ }
277
+ async function discoverBedrockModels(params) {
278
+ const refreshIntervalSeconds = Math.max(0, Math.floor(params.config?.refreshInterval ?? DEFAULT_REFRESH_INTERVAL_SECONDS));
279
+ const providerFilter = normalizeProviderFilter(params.config?.providerFilter);
280
+ const defaultContextWindow = resolveDefaultContextWindow(params.config);
281
+ const defaultMaxTokens = resolveDefaultMaxTokens(params.config);
282
+ const cacheKey = buildCacheKey({
283
+ region: params.region,
284
+ providerFilter,
285
+ refreshIntervalSeconds,
286
+ defaultContextWindow,
287
+ defaultMaxTokens
288
+ });
289
+ const now = params.now?.() ?? Date.now();
290
+ if (refreshIntervalSeconds > 0) {
291
+ const cached = discoveryCache.get(cacheKey);
292
+ if (cached?.value && cached.expiresAt > now) return cached.value;
293
+ if (cached?.inFlight) return cached.inFlight;
294
+ }
295
+ const sdk = params.clientFactory ? createInjectedClientDiscoverySdk() : await loadBedrockDiscoverySdk();
296
+ const clientFactory = params.clientFactory ?? ((region) => sdk.createClient(region));
297
+ if (!params.clientFactory) await refreshAwsSharedConfigCacheForBedrock();
298
+ const client = clientFactory(params.region);
299
+ const discoveryPromise = (async () => {
300
+ const [rawFoundationResponse, profileSummaries] = await Promise.all([client.send(sdk.createListFoundationModelsCommand()), fetchInferenceProfileSummaries(client, (input) => sdk.createListInferenceProfilesCommand(input))]);
301
+ const foundationResponse = rawFoundationResponse;
302
+ const discovered = [];
303
+ const seenIds = /* @__PURE__ */ new Set();
304
+ const foundationModels = /* @__PURE__ */ new Map();
305
+ for (const summary of foundationResponse.modelSummaries ?? []) {
306
+ if (!shouldIncludeSummary(summary, providerFilter)) continue;
307
+ const def = toModelDefinition(summary, {
308
+ contextWindow: defaultContextWindow,
309
+ maxTokens: defaultMaxTokens
310
+ });
311
+ discovered.push(def);
312
+ const normalizedId = normalizeLowercaseStringOrEmpty(def.id);
313
+ seenIds.add(normalizedId);
314
+ foundationModels.set(normalizedId, def);
315
+ }
316
+ const inferenceProfiles = resolveInferenceProfiles(profileSummaries, {
317
+ contextWindow: defaultContextWindow,
318
+ maxTokens: defaultMaxTokens
319
+ }, providerFilter, foundationModels);
320
+ for (const profile of inferenceProfiles) {
321
+ const normalizedId = normalizeLowercaseStringOrEmpty(profile.id);
322
+ if (!seenIds.has(normalizedId)) {
323
+ discovered.push(profile);
324
+ seenIds.add(normalizedId);
325
+ }
326
+ }
327
+ return discovered.toSorted((a, b) => {
328
+ const aGlobal = a.id.startsWith("global.") ? 0 : 1;
329
+ const bGlobal = b.id.startsWith("global.") ? 0 : 1;
330
+ if (aGlobal !== bGlobal) return aGlobal - bGlobal;
331
+ return a.name.localeCompare(b.name);
332
+ });
333
+ })();
334
+ if (refreshIntervalSeconds > 0) discoveryCache.set(cacheKey, {
335
+ expiresAt: now + refreshIntervalSeconds * 1e3,
336
+ inFlight: discoveryPromise
337
+ });
338
+ try {
339
+ const value = await discoveryPromise;
340
+ if (refreshIntervalSeconds > 0) discoveryCache.set(cacheKey, {
341
+ expiresAt: now + refreshIntervalSeconds * 1e3,
342
+ value
343
+ });
344
+ return value;
345
+ } catch (error) {
346
+ if (refreshIntervalSeconds > 0) discoveryCache.delete(cacheKey);
347
+ if (!hasLoggedBedrockError) {
348
+ hasLoggedBedrockError = true;
349
+ log.warn("Failed to discover Bedrock models", { error: formatErrorMessage(error) });
350
+ }
351
+ return [];
352
+ }
353
+ }
354
+ async function resolveImplicitBedrockProvider(params) {
355
+ const env = params.env ?? process.env;
356
+ const discoveryConfig = {
357
+ ...params.config?.models?.bedrockDiscovery,
358
+ ...params.pluginConfig?.discovery
359
+ };
360
+ const enabled = discoveryConfig?.enabled;
361
+ const hasAwsCreds = resolveBedrockConfigApiKey(env) !== void 0;
362
+ if (enabled === false) return null;
363
+ if (enabled !== true && !hasAwsCreds) return null;
364
+ const region = discoveryConfig?.region ?? env.AWS_REGION ?? env.AWS_DEFAULT_REGION ?? "us-east-1";
365
+ const models = await discoverBedrockModels({
366
+ region,
367
+ config: discoveryConfig,
368
+ clientFactory: params.clientFactory
369
+ });
370
+ if (models.length === 0) return null;
371
+ return {
372
+ baseUrl: `https://bedrock-runtime.${region}.amazonaws.com`,
373
+ api: "bedrock-converse-stream",
374
+ auth: "aws-sdk",
375
+ models
376
+ };
377
+ }
378
+ //#endregion
379
+ export { discoverBedrockModels, resetBedrockDiscoveryCacheForTest, resolveImplicitBedrockProvider };