From 938595aab2b182388ba5d6c7c3a6f0febc8d5fca Mon Sep 17 00:00:00 2001 From: Samrat Jha <43038847+samratjha96@users.noreply.github.com> Date: Tue, 9 Sep 2025 01:52:12 -0400 Subject: [PATCH] Add support for Amazon Bedrock provider (#1185) - follows existing patterns for AI SDK to provide Bedrock integration - Uses Bedrock's API token feature for authentication which provides a standard experience - bedrock provided models match the Anthropic provided models (for now) **Disclaimer**: The contributing docs are extremely sparse. I don't actually know how to build this and get this running in Electron ## Testing - AWS Bedrock provider is available for selection image - The provider settings also show the right models and offer the right env variable to use image --- ## Summary by cubic Adds AWS Bedrock as a provider so users can run Claude models via Bedrock with API token authentication. The settings now list Bedrock with supported models and a new env var. - New Features - New provider: bedrock using @ai-sdk/amazon-bedrock, wired into model client and schemas. - Models: Claude 4 Sonnet, Claude 3.7 Sonnet, Claude 3.5 Sonnet (Bedrock model IDs). - Settings: shows AWS Bedrock with correct models and env var AWS_BEARER_TOKEN_BEDROCK. - Default region: us-east-1. - Migration - Set AWS_BEARER_TOKEN_BEDROCK with your Bedrock API token. - Select AWS Bedrock in settings and pick a model. Co-authored-by: Samrat Jha Co-authored-by: Will Chen --- package-lock.json | 205 ++++++++++++++++++++++- package.json | 1 + src/ipc/shared/language_model_helpers.ts | 33 ++++ src/ipc/utils/get_model_client.ts | 16 ++ src/lib/schemas.ts | 1 + 5 files changed, 252 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index c6fa805..bd437e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,9 +6,10 @@ "packages": { "": { "name": "dyad", - "version": "0.20.0-beta.1", + "version": "0.20.0-beta.1", "license": "MIT", "dependencies": { + "@ai-sdk/amazon-bedrock": "^3.0.15", "@ai-sdk/anthropic": "^2.0.4", "@ai-sdk/azure": "^2.0.17", "@ai-sdk/google": "^2.0.6", @@ -129,10 +130,47 @@ "node": ">=20" } }, + "node_modules/@ai-sdk/amazon-bedrock": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/amazon-bedrock/-/amazon-bedrock-3.0.15.tgz", + "integrity": "sha512-gRzjgyeaavYO4Vq4/yILZxinGrXTN1AGaEYAvFQ7fn1CP4kAreLEVDTh2du85WTQHxFck7f3OGKRWXNqQZ7OCA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/anthropic": "2.0.11", + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.7", + "@smithy/eventstream-codec": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "aws4fetch": "^1.0.20" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4" + } + }, + "node_modules/@ai-sdk/amazon-bedrock/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.7.tgz", + "integrity": "sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4" + } + }, "node_modules/@ai-sdk/anthropic": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-2.0.9.tgz", - "integrity": "sha512-1kQgL2A3PeqfEcHHmqy4NxRc8rbgLS71bHBuvDFfDz3VAAyndkilPMCLNDSP2mJVGAej2EMWJ1sShRAxzn70jA==", + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-2.0.11.tgz", + "integrity": "sha512-NCHWE/BnHm9CBw2M17kPwe4bKEUBtntzY26uvoZmWZfCvu2muYBR03pN2pdYmRRZlhpKk1kAOwPOzs9OW8ZC9Q==" "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "2.0.0", @@ -373,6 +411,82 @@ "node": ">=6.0.0" } }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.862.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.862.0.tgz", + "integrity": "sha512-Bei+RL0cDxxV+lW2UezLbCYYNeJm6Nzee0TpW0FfyTRBhH9C1XQh4+x+IClriXvgBnRquTMMYsmJfvx8iyLKrg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -6100,6 +6214,83 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.5.tgz", + "integrity": "sha512-miEUN+nz2UTNoRYRhRqVTJCx7jMeILdAurStT2XoS+mhokkmz1xAPp95DFW9Gxt4iF2VBqpeF9HbTQ3kY1viOA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.3.2", + "@smithy/util-hex-encoding": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.2.tgz", + "integrity": "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@standard-schema/spec": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", @@ -7875,6 +8066,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws4fetch": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/aws4fetch/-/aws4fetch-1.0.20.tgz", + "integrity": "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==", + "license": "MIT" + }, "node_modules/axios": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", diff --git a/package.json b/package.json index 998ce18..2391be9 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "vitest": "^3.1.1" }, "dependencies": { + "@ai-sdk/amazon-bedrock": "^3.0.15", "@ai-sdk/anthropic": "^2.0.4", "@ai-sdk/azure": "^2.0.17", "@ai-sdk/google": "^2.0.6", diff --git a/src/ipc/shared/language_model_helpers.ts b/src/ipc/shared/language_model_helpers.ts index 625ebf0..9714fda 100644 --- a/src/ipc/shared/language_model_helpers.ts +++ b/src/ipc/shared/language_model_helpers.ts @@ -253,6 +253,32 @@ export const MODEL_OPTIONS: Record = { temperature: 0, }, ], + bedrock: [ + { + name: "us.anthropic.claude-sonnet-4-20250514-v1:0", + displayName: "Claude 4 Sonnet", + description: "Excellent coder", + maxOutputTokens: 16_000, + contextWindow: 200_000, + temperature: 0, + }, + { + name: "us.anthropic.claude-3-7-sonnet-20250219-v1:0", + displayName: "Claude 3.7 Sonnet", + description: "Excellent coder", + maxOutputTokens: 16_000, + contextWindow: 200_000, + temperature: 0, + }, + { + name: "us.anthropic.claude-3-5-sonnet-20241022-v2:0", + displayName: "Claude 3.5 Sonnet", + description: "Good coder, excellent at following instructions", + maxOutputTokens: 8_000, + contextWindow: 200_000, + temperature: 0, + }, + ], }; export const PROVIDER_TO_ENV_VAR: Record = { @@ -261,6 +287,7 @@ export const PROVIDER_TO_ENV_VAR: Record = { google: "GEMINI_API_KEY", openrouter: "OPENROUTER_API_KEY", azure: "AZURE_API_KEY", + bedrock: "AWS_BEARER_TOKEN_BEDROCK", }; export const CLOUD_PROVIDERS: Record< @@ -317,6 +344,12 @@ export const CLOUD_PROVIDERS: Record< gatewayPrefix: "", secondary: true, }, + bedrock: { + displayName: "AWS Bedrock", + hasFreeTier: false, + websiteUrl: "https://console.aws.amazon.com/bedrock/", + gatewayPrefix: "bedrock/", + }, }; const LOCAL_PROVIDERS: Record< diff --git a/src/ipc/utils/get_model_client.ts b/src/ipc/utils/get_model_client.ts index 5e8c5e7..f3c7355 100644 --- a/src/ipc/utils/get_model_client.ts +++ b/src/ipc/utils/get_model_client.ts @@ -5,6 +5,7 @@ import { createVertex as createGoogleVertex } from "@ai-sdk/google-vertex"; import { azure } from "@ai-sdk/azure"; import { createOpenRouter } from "@openrouter/ai-sdk-provider"; import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; +import { createAmazonBedrock } from "@ai-sdk/amazon-bedrock"; import type { LargeLanguageModel, UserSettings, @@ -342,6 +343,21 @@ function getRegularModelClient( backupModelClients: [], }; } + case "bedrock": { + // AWS Bedrock supports API key authentication using AWS_BEARER_TOKEN_BEDROCK + // See: https://sdk.vercel.ai/providers/ai-sdk-providers/amazon-bedrock#api-key-authentication + const provider = createAmazonBedrock({ + apiKey: apiKey, + region: getEnvVar("AWS_REGION") || "us-east-1", + }); + return { + modelClient: { + model: provider(model.name), + builtinProviderId: providerId, + }, + backupModelClients: [], + }; + } default: { // Handle custom providers if (providerConfig.type === "custom") { diff --git a/src/lib/schemas.ts b/src/lib/schemas.ts index 9dca45c..09fbeef 100644 --- a/src/lib/schemas.ts +++ b/src/lib/schemas.ts @@ -36,6 +36,7 @@ const providers = [ "ollama", "lmstudio", "azure", + "bedrock", ] as const; export const cloudProviders = providers.filter(