summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGrĂ©goire Morpain <[email protected]>2025-12-29 05:44:15 +0100
committerGitHub <[email protected]>2025-12-28 22:44:15 -0600
commit893888536a75a8e84831c1fa6d426377e4e2d326 (patch)
tree3c05f8fdb18d816f1a18adc946d0a5c522d92b7c
parentc6221fc8b3cc3d63db0270e8f207bd3a33d39034 (diff)
downloadopencode-893888536a75a8e84831c1fa6d426377e4e2d326.tar.gz
opencode-893888536a75a8e84831c1fa6d426377e4e2d326.zip
fix(bedrock): support region and bearer token configuration (#6332)
Co-authored-by: Aiden Cline <[email protected]> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
-rw-r--r--packages/opencode/src/provider/provider.ts33
-rw-r--r--packages/opencode/test/provider/amazon-bedrock.test.ts236
2 files changed, 260 insertions, 9 deletions
diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts
index 0fdf26392..0f00c3a30 100644
--- a/packages/opencode/src/provider/provider.ts
+++ b/packages/opencode/src/provider/provider.ts
@@ -165,29 +165,44 @@ export namespace Provider {
}
},
"amazon-bedrock": async () => {
- const [awsProfile, awsAccessKeyId, awsBearerToken, awsRegion] = await Promise.all([
- Env.get("AWS_PROFILE"),
- Env.get("AWS_ACCESS_KEY_ID"),
- Env.get("AWS_BEARER_TOKEN_BEDROCK"),
- Env.get("AWS_REGION"),
- ])
+ const auth = await Auth.get("amazon-bedrock")
+ const awsProfile = Env.get("AWS_PROFILE")
+ const awsAccessKeyId = Env.get("AWS_ACCESS_KEY_ID")
+ const awsRegion = Env.get("AWS_REGION")
+
+ const awsBearerToken = iife(() => {
+ const envToken = Env.get("AWS_BEARER_TOKEN_BEDROCK")
+ if (envToken) return envToken
+ if (auth?.type === "api") {
+ Env.set("AWS_BEARER_TOKEN_BEDROCK", auth.key)
+ return auth.key
+ }
+ return undefined
+ })
+
if (!awsProfile && !awsAccessKeyId && !awsBearerToken) return { autoload: false }
- const region = awsRegion ?? "us-east-1"
+ const defaultRegion = awsRegion ?? "us-east-1"
const { fromNodeProviderChain } = await import(await BunProc.install("@aws-sdk/credential-providers"))
return {
autoload: true,
options: {
- region,
+ region: defaultRegion,
credentialProvider: fromNodeProviderChain(),
},
- async getModel(sdk: any, modelID: string, _options?: Record<string, any>) {
+ async getModel(sdk: any, modelID: string, options?: Record<string, any>) {
// Skip region prefixing if model already has global prefix
if (modelID.startsWith("global.")) {
return sdk.languageModel(modelID)
}
+ // Region resolution precedence (highest to lowest):
+ // 1. options.region from opencode.json provider config
+ // 2. defaultRegion from AWS_REGION environment variable
+ // 3. Default "us-east-1" (baked into defaultRegion)
+ const region = options?.region ?? defaultRegion
+
let regionPrefix = region.split("-")[0]
switch (regionPrefix) {
diff --git a/packages/opencode/test/provider/amazon-bedrock.test.ts b/packages/opencode/test/provider/amazon-bedrock.test.ts
new file mode 100644
index 000000000..30cd2d0b6
--- /dev/null
+++ b/packages/opencode/test/provider/amazon-bedrock.test.ts
@@ -0,0 +1,236 @@
+import { test, expect } from "bun:test"
+import path from "path"
+import { tmpdir } from "../fixture/fixture"
+import { Instance } from "../../src/project/instance"
+import { Provider } from "../../src/provider/provider"
+import { Env } from "../../src/env"
+import { Auth } from "../../src/auth"
+import { Global } from "../../src/global"
+
+test("Bedrock: config region takes precedence over AWS_REGION env var", async () => {
+ await using tmp = await tmpdir({
+ init: async (dir) => {
+ await Bun.write(
+ path.join(dir, "opencode.json"),
+ JSON.stringify({
+ $schema: "https://opencode.ai/config.json",
+ provider: {
+ "amazon-bedrock": {
+ options: {
+ region: "eu-west-1",
+ },
+ },
+ },
+ }),
+ )
+ },
+ })
+ await Instance.provide({
+ directory: tmp.path,
+ init: async () => {
+ Env.set("AWS_REGION", "us-east-1")
+ Env.set("AWS_PROFILE", "default")
+ },
+ fn: async () => {
+ const providers = await Provider.list()
+ expect(providers["amazon-bedrock"]).toBeDefined()
+ // Region from config should be used (not env var)
+ expect(providers["amazon-bedrock"].options?.region).toBe("eu-west-1")
+ },
+ })
+})
+
+test("Bedrock: falls back to AWS_REGION env var when no config", async () => {
+ await using tmp = await tmpdir({
+ init: async (dir) => {
+ await Bun.write(
+ path.join(dir, "opencode.json"),
+ JSON.stringify({
+ $schema: "https://opencode.ai/config.json",
+ }),
+ )
+ },
+ })
+ await Instance.provide({
+ directory: tmp.path,
+ init: async () => {
+ Env.set("AWS_REGION", "eu-west-1")
+ Env.set("AWS_PROFILE", "default")
+ },
+ fn: async () => {
+ const providers = await Provider.list()
+ expect(providers["amazon-bedrock"]).toBeDefined()
+ expect(providers["amazon-bedrock"].options?.region).toBe("eu-west-1")
+ },
+ })
+})
+
+test("Bedrock: without explicit region config, uses AWS_REGION env or defaults", async () => {
+ await using tmp = await tmpdir({
+ init: async (dir) => {
+ await Bun.write(
+ path.join(dir, "opencode.json"),
+ JSON.stringify({
+ $schema: "https://opencode.ai/config.json",
+ }),
+ )
+ },
+ })
+ await Instance.provide({
+ directory: tmp.path,
+ init: async () => {
+ Env.set("AWS_PROFILE", "default")
+ // AWS_REGION might be set in the environment, use that or default
+ },
+ fn: async () => {
+ const providers = await Provider.list()
+ expect(providers["amazon-bedrock"]).toBeDefined()
+ // Should have some region set (either from env or default)
+ expect(providers["amazon-bedrock"].options?.region).toBeDefined()
+ expect(typeof providers["amazon-bedrock"].options?.region).toBe("string")
+ },
+ })
+})
+
+test("Bedrock: uses config region in provider options", async () => {
+ await using tmp = await tmpdir({
+ init: async (dir) => {
+ await Bun.write(
+ path.join(dir, "opencode.json"),
+ JSON.stringify({
+ $schema: "https://opencode.ai/config.json",
+ provider: {
+ "amazon-bedrock": {
+ options: {
+ region: "eu-north-1",
+ },
+ },
+ },
+ }),
+ )
+ },
+ })
+ await Instance.provide({
+ directory: tmp.path,
+ init: async () => {
+ Env.set("AWS_PROFILE", "default")
+ },
+ fn: async () => {
+ const providers = await Provider.list()
+ const bedrockProvider = providers["amazon-bedrock"]
+ expect(bedrockProvider).toBeDefined()
+ expect(bedrockProvider.options?.region).toBe("eu-north-1")
+ },
+ })
+})
+
+test("Bedrock: respects config region for different instances", async () => {
+ // First instance with EU config
+ await using tmp1 = await tmpdir({
+ init: async (dir) => {
+ await Bun.write(
+ path.join(dir, "opencode.json"),
+ JSON.stringify({
+ $schema: "https://opencode.ai/config.json",
+ provider: {
+ "amazon-bedrock": {
+ options: {
+ region: "eu-west-1",
+ },
+ },
+ },
+ }),
+ )
+ },
+ })
+
+ await Instance.provide({
+ directory: tmp1.path,
+ init: async () => {
+ Env.set("AWS_PROFILE", "default")
+ Env.set("AWS_REGION", "us-east-1")
+ },
+ fn: async () => {
+ const providers1 = await Provider.list()
+ expect(providers1["amazon-bedrock"].options?.region).toBe("eu-west-1")
+ },
+ })
+
+ // Second instance with US config
+ await using tmp2 = await tmpdir({
+ init: async (dir) => {
+ await Bun.write(
+ path.join(dir, "opencode.json"),
+ JSON.stringify({
+ $schema: "https://opencode.ai/config.json",
+ provider: {
+ "amazon-bedrock": {
+ options: {
+ region: "us-west-2",
+ },
+ },
+ },
+ }),
+ )
+ },
+ })
+
+ await Instance.provide({
+ directory: tmp2.path,
+ init: async () => {
+ Env.set("AWS_PROFILE", "default")
+ Env.set("AWS_REGION", "eu-west-1")
+ },
+ fn: async () => {
+ const providers2 = await Provider.list()
+ expect(providers2["amazon-bedrock"].options?.region).toBe("us-west-2")
+ },
+ })
+})
+
+test("Bedrock: loads when bearer token from auth.json is present", async () => {
+ await using tmp = await tmpdir({
+ init: async (dir) => {
+ await Bun.write(
+ path.join(dir, "opencode.json"),
+ JSON.stringify({
+ $schema: "https://opencode.ai/config.json",
+ provider: {
+ "amazon-bedrock": {
+ options: {
+ region: "eu-west-1",
+ },
+ },
+ },
+ }),
+ )
+ },
+ })
+
+ // Setup auth.json with bearer token for amazon-bedrock
+ const authPath = path.join(Global.Path.data, "auth.json")
+ await Bun.write(
+ authPath,
+ JSON.stringify({
+ "amazon-bedrock": {
+ type: "api",
+ key: "test-bearer-token",
+ },
+ }),
+ )
+
+ await Instance.provide({
+ directory: tmp.path,
+ init: async () => {
+ // Clear env vars so only auth.json should trigger autoload
+ Env.set("AWS_PROFILE", "")
+ Env.set("AWS_ACCESS_KEY_ID", "")
+ Env.set("AWS_BEARER_TOKEN_BEDROCK", "")
+ },
+ fn: async () => {
+ const providers = await Provider.list()
+ expect(providers["amazon-bedrock"]).toBeDefined()
+ expect(providers["amazon-bedrock"].options?.region).toBe("eu-west-1")
+ },
+ })
+})