mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(amazon-bedrock): ignore blank region env overrides (#110676)
* fix(amazon-bedrock): ignore blank region env overrides * fix(amazon-bedrock): normalize region envs across runtimes Co-authored-by: ZengWen-DT <ceng.wen@xydigit.com> --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
co-authored by
Peter Steinberger
parent
fc777a35bb
commit
7e4a76a6d0
@@ -710,6 +710,38 @@ describe("bedrock mantle discovery", () => {
|
||||
objectArgAt(mockFetch, 0, 1);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
name: "the fallback region when the primary env is blank",
|
||||
env: { AWS_REGION: " ", AWS_DEFAULT_REGION: "eu-west-1" },
|
||||
expectedRegion: "eu-west-1",
|
||||
},
|
||||
{
|
||||
name: "the default region when both env values are blank",
|
||||
env: { AWS_REGION: "", AWS_DEFAULT_REGION: "\t" },
|
||||
expectedRegion: "us-east-1",
|
||||
},
|
||||
])("uses $name", async ({ env, expectedRegion }) => {
|
||||
const mockFetch = vi
|
||||
.fn()
|
||||
.mockResolvedValue(
|
||||
modelDiscoveryResponse({ data: [{ id: "openai.gpt-oss-120b", object: "model" }] }),
|
||||
);
|
||||
|
||||
const provider = await resolveImplicitMantleProvider({
|
||||
env: {
|
||||
AWS_BEARER_TOKEN_BEDROCK: MANTLE_IAM_TOKEN_MARKER,
|
||||
...env,
|
||||
} as NodeJS.ProcessEnv,
|
||||
fetchFn: mockFetch as unknown as typeof fetch,
|
||||
});
|
||||
|
||||
expect(provider?.baseUrl).toBe(`https://bedrock-mantle.${expectedRegion}.api.aws/v1`);
|
||||
expect(stringArgAt(mockFetch, 0, 0)).toBe(
|
||||
`https://bedrock-mantle.${expectedRegion}.api.aws/v1/models`,
|
||||
);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Provider merging
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -13,7 +13,10 @@ import type {
|
||||
ModelProviderConfig,
|
||||
} from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
|
||||
const log = createSubsystemLogger("bedrock-mantle-discovery");
|
||||
|
||||
@@ -116,7 +119,11 @@ const iamTokenCache = new Map<string, { token: string; expiresAt: number }>();
|
||||
const IAM_TOKEN_TTL_MS = 7200_000; // Matches the 2h token lifetime we request below.
|
||||
|
||||
function resolveMantleRegion(env: NodeJS.ProcessEnv): string {
|
||||
return env.AWS_REGION ?? env.AWS_DEFAULT_REGION ?? "us-east-1";
|
||||
return (
|
||||
normalizeOptionalString(env.AWS_REGION) ??
|
||||
normalizeOptionalString(env.AWS_DEFAULT_REGION) ??
|
||||
"us-east-1"
|
||||
);
|
||||
}
|
||||
|
||||
function getCachedIamTokenEntry(
|
||||
|
||||
@@ -795,6 +795,34 @@ describe("bedrock discovery", () => {
|
||||
expect(sendMock).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
name: "secondary region when the primary env override is blank",
|
||||
env: { AWS_REGION: " ", AWS_DEFAULT_REGION: "eu-west-1" },
|
||||
expectedRegion: "eu-west-1",
|
||||
},
|
||||
{
|
||||
name: "plugin default when both region env overrides are blank",
|
||||
env: { AWS_REGION: "", AWS_DEFAULT_REGION: " " },
|
||||
expectedRegion: "us-east-1",
|
||||
},
|
||||
{
|
||||
name: "primary region when both env overrides are nonblank",
|
||||
env: { AWS_REGION: "ap-southeast-2", AWS_DEFAULT_REGION: "eu-west-1" },
|
||||
expectedRegion: "ap-southeast-2",
|
||||
},
|
||||
])("uses $name", async ({ env, expectedRegion }) => {
|
||||
mockSingleActiveSummary();
|
||||
|
||||
const provider = await resolveImplicitBedrockProvider({
|
||||
pluginConfig: { discovery: { enabled: true } },
|
||||
env,
|
||||
clientFactory,
|
||||
});
|
||||
|
||||
expect(provider?.baseUrl).toBe(`https://bedrock-runtime.${expectedRegion}.amazonaws.com`);
|
||||
});
|
||||
|
||||
// Ported from #65449 by @alickgithub2 — extended to also cover apac. prefix
|
||||
it("resolves au. and apac. prefixes for regional inference profiles", async () => {
|
||||
sendMock
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalLowercaseString,
|
||||
normalizeOptionalString,
|
||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import { refreshAwsSharedConfigCacheForBedrock } from "./aws-credential-refresh.js";
|
||||
import {
|
||||
@@ -675,7 +676,11 @@ export async function resolveImplicitBedrockProvider(params: {
|
||||
return null;
|
||||
}
|
||||
|
||||
const region = discoveryConfig?.region ?? env.AWS_REGION ?? env.AWS_DEFAULT_REGION ?? "us-east-1";
|
||||
const region =
|
||||
discoveryConfig?.region ??
|
||||
normalizeOptionalString(env.AWS_REGION) ??
|
||||
normalizeOptionalString(env.AWS_DEFAULT_REGION) ??
|
||||
"us-east-1";
|
||||
const models = await discoverBedrockModels({
|
||||
region,
|
||||
config: discoveryConfig,
|
||||
|
||||
@@ -1,8 +1,45 @@
|
||||
// Amazon Bedrock tests cover embedding provider plugin behavior.
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { hasAwsCredentials } from "./embedding-provider.js";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { createBedrockEmbeddingProvider, hasAwsCredentials } from "./embedding-provider.js";
|
||||
import { embeddingTesting as testing } from "./test-support.js";
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
describe("bedrock embedding region resolution", () => {
|
||||
it.each([
|
||||
{
|
||||
name: "secondary region when the primary env override is blank",
|
||||
primary: " ",
|
||||
secondary: "eu-west-1",
|
||||
expected: "eu-west-1",
|
||||
},
|
||||
{
|
||||
name: "plugin default when both env overrides are blank",
|
||||
primary: "",
|
||||
secondary: " ",
|
||||
expected: "us-east-1",
|
||||
},
|
||||
{
|
||||
name: "primary region when both env overrides are nonblank",
|
||||
primary: "ap-southeast-2",
|
||||
secondary: "eu-west-1",
|
||||
expected: "ap-southeast-2",
|
||||
},
|
||||
])("uses $name", async ({ primary, secondary, expected }) => {
|
||||
vi.stubEnv("AWS_REGION", primary);
|
||||
vi.stubEnv("AWS_DEFAULT_REGION", secondary);
|
||||
|
||||
const { client } = await createBedrockEmbeddingProvider({
|
||||
config: {},
|
||||
model: "",
|
||||
});
|
||||
|
||||
expect(client.region).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("hasAwsCredentials", () => {
|
||||
it("accepts static AWS key credentials without loading the credential chain", async () => {
|
||||
const loadCredentialProvider = vi.fn();
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import {
|
||||
asOptionalRecord as asRecord,
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import { refreshAwsSharedConfigCacheForBedrock } from "./aws-credential-refresh.js";
|
||||
|
||||
@@ -406,8 +407,8 @@ function resolveBedrockEmbeddingClient(
|
||||
const region =
|
||||
regionFromUrl(options.remote?.baseUrl) ??
|
||||
regionFromUrl(providerConfig?.baseUrl) ??
|
||||
process.env.AWS_REGION ??
|
||||
process.env.AWS_DEFAULT_REGION ??
|
||||
normalizeOptionalString(process.env.AWS_REGION) ??
|
||||
normalizeOptionalString(process.env.AWS_DEFAULT_REGION) ??
|
||||
"us-east-1";
|
||||
|
||||
let dimensions: number | undefined;
|
||||
|
||||
@@ -238,6 +238,20 @@ describe("Bedrock profile endpoint resolution", () => {
|
||||
ambientRegion: "eu-west-1",
|
||||
expectedRegion: "eu-west-1",
|
||||
},
|
||||
{
|
||||
name: "blank primary region with a fallback env",
|
||||
modelId: "amazon.nova-micro-v1:0",
|
||||
ambientRegion: " ",
|
||||
fallbackRegion: "eu-west-1",
|
||||
expectedRegion: "eu-west-1",
|
||||
},
|
||||
{
|
||||
name: "blank region env vars",
|
||||
modelId: "amazon.nova-micro-v1:0",
|
||||
ambientRegion: " ",
|
||||
fallbackRegion: "\t",
|
||||
expectedRegion: "us-east-1",
|
||||
},
|
||||
{
|
||||
name: "application inference-profile ARN",
|
||||
modelId: "arn:aws:bedrock:us-west-2:123456789012:application-inference-profile/profile-abc",
|
||||
@@ -260,8 +274,11 @@ describe("Bedrock profile endpoint resolution", () => {
|
||||
},
|
||||
])(
|
||||
"resolves $name to $expectedRegion",
|
||||
async ({ modelId, ambientRegion, explicitRegion, expectedRegion }) => {
|
||||
async ({ modelId, ambientRegion, fallbackRegion, explicitRegion, expectedRegion }) => {
|
||||
vi.stubEnv("AWS_REGION", ambientRegion);
|
||||
if (fallbackRegion !== undefined) {
|
||||
vi.stubEnv("AWS_DEFAULT_REGION", fallbackRegion);
|
||||
}
|
||||
|
||||
await expect(
|
||||
captureClientRegion(
|
||||
|
||||
@@ -69,6 +69,7 @@ import {
|
||||
notifyLlmRequestActivity,
|
||||
} from "openclaw/plugin-sdk/provider-stream-shared";
|
||||
import { describeToolResultMediaPlaceholder } from "openclaw/plugin-sdk/provider-transport-runtime";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import { supportsBedrockPromptCaching, type BedrockOptions } from "./bedrock-options.js";
|
||||
import { supportsBedrockNativeMaxEffort } from "./thinking-policy.js";
|
||||
|
||||
@@ -1038,7 +1039,11 @@ function getConfiguredBedrockRegion(options: BedrockOptions): string | undefined
|
||||
return options.region;
|
||||
}
|
||||
|
||||
return options.region || process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || undefined;
|
||||
return (
|
||||
options.region ||
|
||||
normalizeOptionalString(process.env.AWS_REGION) ||
|
||||
normalizeOptionalString(process.env.AWS_DEFAULT_REGION)
|
||||
);
|
||||
}
|
||||
|
||||
function hasConfiguredBedrockProfile(options: BedrockOptions): boolean {
|
||||
|
||||
Reference in New Issue
Block a user