Skip to content

Commit 3b110da

Browse files
committed
feat: Add Amazon Bedrock provider support with AWS credentials configuration
1 parent c6743ec commit 3b110da

File tree

17 files changed

+602
-7
lines changed

17 files changed

+602
-7
lines changed

client/public/bedrock_logo.png

614 Bytes
Loading

client/src/components/ChatTab.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,15 @@ export function ChatTab({
345345
className="w-8 h-8 object-contain opacity-70 hover:opacity-100 transition-opacity"
346346
/>
347347
</div>
348+
349+
{/* Amazon Bedrock */}
350+
<div className="flex items-center justify-center">
351+
<img
352+
src="/bedrock_logo.png"
353+
alt="Amazon Bedrock"
354+
className="w-8 h-8 object-contain opacity-70 hover:opacity-100 transition-opacity"
355+
/>
356+
</div>
348357
</div>
349358

350359
{/* CTA */}

client/src/components/ChatTabV2.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ export function ChatTabV2({
9595
getLiteLLMModelAlias,
9696
getOpenRouterSelectedModels,
9797
getOllamaBaseUrl,
98+
getBedrockRegion,
99+
getBedrockSecretKey,
98100
} = useAiProviderKeys();
99101

100102
const [input, setInput] = useState("");
@@ -162,6 +164,9 @@ export function ChatTabV2({
162164
...(isGpt5 ? {} : { temperature }),
163165
systemPrompt,
164166
selectedServers: selectedConnectedServerNames,
167+
ollamaBaseUrl: getOllamaBaseUrl(),
168+
bedrockRegion: getBedrockRegion(),
169+
bedrockSecretKey: getBedrockSecretKey(),
165170
},
166171
headers: authHeaders,
167172
});
@@ -172,6 +177,9 @@ export function ChatTabV2({
172177
temperature,
173178
systemPrompt,
174179
selectedConnectedServerNames,
180+
getOllamaBaseUrl,
181+
getBedrockRegion,
182+
getBedrockSecretKey,
175183
]);
176184

177185
useEffect(() => {

client/src/components/SettingsTab.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { ProviderConfigDialog } from "./setting/ProviderConfigDialog";
55
import { OllamaConfigDialog } from "./setting/OllamaConfigDialog";
66
import { LiteLLMConfigDialog } from "./setting/LiteLLMConfigDialog";
77
import { OpenRouterConfigDialog } from "./setting/OpenRouterConfigDialog";
8+
import { BedrockConfigDialog } from "./setting/BedrockConfigDialog";
89
import { AccountApiKeySection } from "./setting/AccountApiKeySection";
910

1011
interface ProviderConfig {
@@ -31,6 +32,10 @@ export function SettingsTab() {
3132
setLiteLLMModelAlias,
3233
getOpenRouterSelectedModels,
3334
setOpenRouterSelectedModels,
35+
getBedrockRegion,
36+
setBedrockRegion,
37+
getBedrockSecretKey,
38+
setBedrockSecretKey,
3439
} = useAiProviderKeys();
3540

3641
const [editingValue, setEditingValue] = useState("");
@@ -47,6 +52,10 @@ export function SettingsTab() {
4752
const [openRouterApiKeyInput, setOpenRouterApiKeyInput] = useState("");
4853
const [openRouterSelectedModelsInput, setOpenRouterSelectedModelsInput] =
4954
useState<string[]>([]);
55+
const [bedrockDialogOpen, setBedrockDialogOpen] = useState(false);
56+
const [bedrockAccessKeyIdInput, setBedrockAccessKeyIdInput] = useState("");
57+
const [bedrockSecretKeyInput, setBedrockSecretKeyInput] = useState("");
58+
const [bedrockRegionInput, setBedrockRegionInput] = useState("");
5059

5160
const providerConfigs: ProviderConfig[] = [
5261
{
@@ -94,9 +103,23 @@ export function SettingsTab() {
94103
placeholder: "...",
95104
getApiKeyUrl: "https://console.mistral.ai/api-keys/",
96105
},
106+
{
107+
id: "bedrock",
108+
name: "Amazon Bedrock",
109+
logo: "/bedrock_logo.png",
110+
logoAlt: "Amazon Bedrock",
111+
description: "Claude 3.5, Llama 3, Mistral models on AWS Bedrock",
112+
placeholder: "AWS Access Key ID",
113+
getApiKeyUrl: "https://console.aws.amazon.com/iam/",
114+
},
97115
];
98116

99117
const handleEdit = (providerId: string) => {
118+
if (providerId === "bedrock") {
119+
handleBedrockEdit();
120+
return;
121+
}
122+
100123
const provider = providerConfigs.find((p) => p.id === providerId);
101124
if (provider) {
102125
setSelectedProvider(provider);
@@ -129,6 +152,11 @@ export function SettingsTab() {
129152
if (providerId === "openrouter") {
130153
setOpenRouterSelectedModels([]);
131154
}
155+
// Also clear Bedrock credentials if deleting Bedrock provider
156+
if (providerId === "bedrock") {
157+
setBedrockSecretKey("");
158+
setBedrockRegion("");
159+
}
132160
};
133161

134162
const handleOllamaEdit = () => {
@@ -194,6 +222,30 @@ export function SettingsTab() {
194222
setOpenRouterSelectedModelsInput([]);
195223
};
196224

225+
const handleBedrockEdit = () => {
226+
setBedrockAccessKeyIdInput(tokens.bedrock || "");
227+
setBedrockSecretKeyInput(getBedrockSecretKey());
228+
setBedrockRegionInput(getBedrockRegion());
229+
setBedrockDialogOpen(true);
230+
};
231+
232+
const handleBedrockSave = () => {
233+
setToken("bedrock", bedrockAccessKeyIdInput);
234+
setBedrockSecretKey(bedrockSecretKeyInput);
235+
setBedrockRegion(bedrockRegionInput);
236+
setBedrockDialogOpen(false);
237+
setBedrockAccessKeyIdInput("");
238+
setBedrockSecretKeyInput("");
239+
setBedrockRegionInput("");
240+
};
241+
242+
const handleBedrockCancel = () => {
243+
setBedrockDialogOpen(false);
244+
setBedrockAccessKeyIdInput("");
245+
setBedrockSecretKeyInput("");
246+
setBedrockRegionInput("");
247+
};
248+
197249
return (
198250
<div className="container mx-auto p-6 max-w-6xl space-y-8">
199251
<div className="flex items-center gap-3 mb-6">
@@ -268,6 +320,20 @@ export function SettingsTab() {
268320
onSave={handleOpenRouterSave}
269321
onCancel={handleOpenRouterCancel}
270322
/>
323+
324+
{/* Bedrock Configuration Dialog */}
325+
<BedrockConfigDialog
326+
open={bedrockDialogOpen}
327+
onOpenChange={setBedrockDialogOpen}
328+
accessKeyId={bedrockAccessKeyIdInput}
329+
secretKey={bedrockSecretKeyInput}
330+
region={bedrockRegionInput}
331+
onAccessKeyIdChange={setBedrockAccessKeyIdInput}
332+
onSecretKeyChange={setBedrockSecretKeyInput}
333+
onRegionChange={setBedrockRegionInput}
334+
onSave={handleBedrockSave}
335+
onCancel={handleBedrockCancel}
336+
/>
271337
</div>
272338
);
273339
}

client/src/components/chat-v2/chat-helpers.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import openrouterLogo from "/openrouter_logo.png";
1414
import moonshotLightLogo from "/moonshot_light.png";
1515
import moonshotDarkLogo from "/moonshot_dark.png";
1616
import zAiLogo from "/z-ai.png";
17+
import bedrockLogo from "/bedrock_logo.png";
1718

1819
export const getProviderLogoFromProvider = (
1920
provider: string,
@@ -68,6 +69,8 @@ export const getProviderLogoFromProvider = (
6869
return moonshotLightLogo;
6970
case "z-ai":
7071
return zAiLogo;
72+
case "bedrock":
73+
return bedrockLogo;
7174
default:
7275
return null;
7376
}
@@ -104,6 +107,8 @@ export const getProviderColor = (provider: string) => {
104107
return "text-indigo-600 dark:text-indigo-400";
105108
case "meta":
106109
return "text-blue-500 dark:text-blue-400";
110+
case "bedrock":
111+
return "text-orange-500 dark:text-orange-400";
107112
default:
108113
return "text-blue-600 dark:text-blue-400";
109114
}

client/src/components/chat-v2/model-helpers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export function buildAvailableModels(params: {
4646
openrouter: Boolean(
4747
hasToken("openrouter") && getOpenRouterSelectedModels().length > 0,
4848
),
49+
bedrock: hasToken("bedrock"),
4950
meta: false,
5051
"x-ai": false,
5152
} as const;

client/src/components/chat/chat-helpers.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import openrouterLogo from "/openrouter_logo.png";
1414
import moonshotLightLogo from "/moonshot_light.png";
1515
import moonshotDarkLogo from "/moonshot_dark.png";
1616
import zAiLogo from "/z-ai.png";
17+
import bedrockLogo from "/bedrock_logo.png";
1718

1819
export const getProviderLogoFromProvider = (
1920
provider: string,
@@ -68,6 +69,8 @@ export const getProviderLogoFromProvider = (
6869
return moonshotLightLogo;
6970
case "z-ai":
7071
return zAiLogo;
72+
case "bedrock":
73+
return bedrockLogo;
7174
default:
7275
return null;
7376
}
@@ -104,6 +107,8 @@ export const getProviderColor = (provider: string) => {
104107
return "text-indigo-600 dark:text-indigo-400";
105108
case "meta":
106109
return "text-blue-500 dark:text-blue-400";
110+
case "bedrock":
111+
return "text-orange-500 dark:text-orange-400";
107112
default:
108113
return "text-blue-600 dark:text-blue-400";
109114
}
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import { ExternalLink } from "lucide-react";
2+
import { Button } from "../ui/button";
3+
import { Input } from "../ui/input";
4+
import {
5+
Dialog,
6+
DialogContent,
7+
DialogDescription,
8+
DialogFooter,
9+
DialogHeader,
10+
DialogTitle,
11+
} from "../ui/dialog";
12+
import {
13+
Select,
14+
SelectContent,
15+
SelectItem,
16+
SelectTrigger,
17+
SelectValue,
18+
} from "../ui/select";
19+
20+
interface BedrockConfigDialogProps {
21+
open: boolean;
22+
onOpenChange: (open: boolean) => void;
23+
accessKeyId: string;
24+
secretKey: string;
25+
region: string;
26+
onAccessKeyIdChange: (value: string) => void;
27+
onSecretKeyChange: (value: string) => void;
28+
onRegionChange: (value: string) => void;
29+
onSave: () => void;
30+
onCancel: () => void;
31+
}
32+
33+
const AWS_REGIONS = [
34+
// United States
35+
{ value: "us-east-1", label: "US East (N. Virginia)" },
36+
{ value: "us-east-2", label: "US East (Ohio)" },
37+
{ value: "us-west-1", label: "US West (N. California)" },
38+
{ value: "us-west-2", label: "US West (Oregon)" },
39+
// Asia Pacific
40+
{ value: "ap-south-1", label: "Asia Pacific (Mumbai)" },
41+
{ value: "ap-northeast-1", label: "Asia Pacific (Tokyo)" },
42+
{ value: "ap-northeast-2", label: "Asia Pacific (Seoul)" },
43+
{ value: "ap-northeast-3", label: "Asia Pacific (Osaka)" },
44+
{ value: "ap-southeast-1", label: "Asia Pacific (Singapore)" },
45+
{ value: "ap-southeast-2", label: "Asia Pacific (Sydney)" },
46+
// Canada
47+
{ value: "ca-central-1", label: "Canada (Central)" },
48+
// Europe
49+
{ value: "eu-central-1", label: "Europe (Frankfurt)" },
50+
{ value: "eu-west-1", label: "Europe (Ireland)" },
51+
{ value: "eu-west-2", label: "Europe (London)" },
52+
{ value: "eu-west-3", label: "Europe (Paris)" },
53+
{ value: "eu-north-1", label: "Europe (Stockholm)" },
54+
];
55+
56+
export function BedrockConfigDialog({
57+
open,
58+
onOpenChange,
59+
accessKeyId,
60+
secretKey,
61+
region,
62+
onAccessKeyIdChange,
63+
onSecretKeyChange,
64+
onRegionChange,
65+
onSave,
66+
onCancel,
67+
}: BedrockConfigDialogProps) {
68+
return (
69+
<Dialog open={open} onOpenChange={onOpenChange}>
70+
<DialogContent className="max-w-md">
71+
<DialogHeader>
72+
<div className="flex items-center gap-3 mb-4">
73+
<div className="w-12 h-12 rounded-lg bg-white dark:bg-gray-800 p-2 flex items-center justify-center">
74+
<img
75+
src="/bedrock_logo.png"
76+
alt="AWS Bedrock Logo"
77+
className="w-full h-full object-contain"
78+
/>
79+
</div>
80+
<div>
81+
<DialogTitle className="text-left pb-2">
82+
Configure Amazon Bedrock
83+
</DialogTitle>
84+
<DialogDescription className="text-left">
85+
Set up your AWS credentials for Bedrock
86+
</DialogDescription>
87+
</div>
88+
</div>
89+
</DialogHeader>
90+
91+
<div className="space-y-4">
92+
<div>
93+
<label htmlFor="bedrock-access-key" className="text-sm font-medium">
94+
AWS Access Key ID
95+
</label>
96+
<Input
97+
id="bedrock-access-key"
98+
type="text"
99+
value={accessKeyId}
100+
onChange={(e) => onAccessKeyIdChange(e.target.value)}
101+
placeholder="AKIA..."
102+
className="mt-1"
103+
/>
104+
</div>
105+
106+
<div>
107+
<label htmlFor="bedrock-secret-key" className="text-sm font-medium">
108+
AWS Secret Access Key
109+
</label>
110+
<Input
111+
id="bedrock-secret-key"
112+
type="password"
113+
value={secretKey}
114+
onChange={(e) => onSecretKeyChange(e.target.value)}
115+
placeholder="Enter secret key"
116+
className="mt-1"
117+
/>
118+
</div>
119+
120+
<div>
121+
<label htmlFor="bedrock-region" className="text-sm font-medium">
122+
AWS Region
123+
</label>
124+
<Select value={region} onValueChange={onRegionChange}>
125+
<SelectTrigger className="mt-1">
126+
<SelectValue placeholder="Select a region" />
127+
</SelectTrigger>
128+
<SelectContent>
129+
{AWS_REGIONS.map((r) => (
130+
<SelectItem key={r.value} value={r.value}>
131+
{r.label}
132+
</SelectItem>
133+
))}
134+
</SelectContent>
135+
</Select>
136+
</div>
137+
138+
<div className="flex items-center gap-2 p-3 bg-blue-50 dark:bg-blue-950/20 rounded-lg">
139+
<ExternalLink className="w-4 h-4 text-blue-600" />
140+
<span className="text-sm text-blue-600">
141+
Need help?{" "}
142+
<button
143+
onClick={() =>
144+
window.open(
145+
"https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html",
146+
"_blank",
147+
)
148+
}
149+
className="underline hover:no-underline"
150+
>
151+
AWS Bedrock Docs
152+
</button>
153+
</span>
154+
</div>
155+
</div>
156+
157+
<DialogFooter>
158+
<Button variant="outline" onClick={onCancel}>
159+
Cancel
160+
</Button>
161+
<Button
162+
onClick={onSave}
163+
disabled={
164+
!accessKeyId.trim() || !secretKey.trim() || !region.trim()
165+
}
166+
>
167+
Save Configuration
168+
</Button>
169+
</DialogFooter>
170+
</DialogContent>
171+
</Dialog>
172+
);
173+
}

0 commit comments

Comments
 (0)