mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 12:21:13 -05:00
Add support for OpenRouter and AWS Bedrock
This commit is contained in:
144
webview-ui/src/components/ApiOptions.tsx
Normal file
144
webview-ui/src/components/ApiOptions.tsx
Normal file
@@ -0,0 +1,144 @@
|
||||
import { ApiConfiguration } from "@shared/api"
|
||||
import { VSCodeDropdown, VSCodeLink, VSCodeOption, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import React from "react"
|
||||
|
||||
interface ApiOptionsProps {
|
||||
apiConfiguration?: ApiConfiguration
|
||||
setApiConfiguration: React.Dispatch<React.SetStateAction<ApiConfiguration | undefined>>
|
||||
}
|
||||
|
||||
const ApiOptions: React.FC<ApiOptionsProps> = ({ apiConfiguration, setApiConfiguration }) => {
|
||||
const handleInputChange = (field: keyof ApiConfiguration) => (event: any) => {
|
||||
setApiConfiguration((prev) => ({ ...prev, [field]: event.target.value }))
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
|
||||
<div className="dropdown-container">
|
||||
<label htmlFor="api-provider">
|
||||
<span style={{ fontWeight: 500 }}>API Provider</span>
|
||||
</label>
|
||||
<VSCodeDropdown
|
||||
id="api-provider"
|
||||
value={apiConfiguration?.apiProvider || "anthropic"}
|
||||
onChange={handleInputChange("apiProvider")}>
|
||||
<VSCodeOption value="anthropic">Anthropic</VSCodeOption>
|
||||
<VSCodeOption value="openrouter">OpenRouter</VSCodeOption>
|
||||
<VSCodeOption value="bedrock">AWS Bedrock</VSCodeOption>
|
||||
</VSCodeDropdown>
|
||||
</div>
|
||||
|
||||
{apiConfiguration?.apiProvider === "anthropic" && (
|
||||
<div>
|
||||
<VSCodeTextField
|
||||
value={apiConfiguration?.apiKey || ""}
|
||||
style={{ width: "100%" }}
|
||||
onInput={handleInputChange("apiKey")}
|
||||
placeholder="Enter API Key...">
|
||||
<span style={{ fontWeight: 500 }}>Anthropic API Key</span>
|
||||
</VSCodeTextField>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
marginTop: "5px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
}}>
|
||||
This key is stored locally and only used to make API requests from the extension.
|
||||
<VSCodeLink href="https://console.anthropic.com/" style={{ display: "inline" }}>
|
||||
You can get an Anthropic API key by signing up here.
|
||||
</VSCodeLink>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{apiConfiguration?.apiProvider === "openrouter" && (
|
||||
<div>
|
||||
<VSCodeTextField
|
||||
value={apiConfiguration?.openRouterApiKey || ""}
|
||||
style={{ width: "100%" }}
|
||||
onInput={handleInputChange("openRouterApiKey")}
|
||||
placeholder="Enter API Key...">
|
||||
<span style={{ fontWeight: 500 }}>OpenRouter API Key</span>
|
||||
</VSCodeTextField>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
marginTop: "5px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
}}>
|
||||
This key is stored locally and only used to make API requests from the extension.
|
||||
<VSCodeLink href="https://openrouter.ai/" style={{ display: "inline" }}>
|
||||
You can get an OpenRouter API key by signing up here.
|
||||
</VSCodeLink>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{apiConfiguration?.apiProvider === "bedrock" && (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
|
||||
<VSCodeTextField
|
||||
value={apiConfiguration?.awsAccessKey || ""}
|
||||
style={{ width: "100%" }}
|
||||
onInput={handleInputChange("awsAccessKey")}
|
||||
placeholder="Enter Access Key...">
|
||||
<span style={{ fontWeight: 500 }}>AWS Access Key</span>
|
||||
</VSCodeTextField>
|
||||
<VSCodeTextField
|
||||
value={apiConfiguration?.awsSecretKey || ""}
|
||||
style={{ width: "100%" }}
|
||||
onInput={handleInputChange("awsSecretKey")}
|
||||
placeholder="Enter Secret Key...">
|
||||
<span style={{ fontWeight: 500 }}>AWS Secret Key</span>
|
||||
</VSCodeTextField>
|
||||
<div className="dropdown-container">
|
||||
<label htmlFor="aws-region-dropdown">
|
||||
<span style={{ fontWeight: 500 }}>AWS Region</span>
|
||||
</label>
|
||||
<VSCodeDropdown
|
||||
id="aws-region-dropdown"
|
||||
value={apiConfiguration?.awsRegion || ""}
|
||||
style={{ width: "100%" }}
|
||||
onChange={handleInputChange("awsRegion")}>
|
||||
<VSCodeOption value="">Select a region...</VSCodeOption>
|
||||
<VSCodeOption value="us-east-1">US East (N. Virginia)</VSCodeOption>
|
||||
<VSCodeOption value="us-east-2">US East (Ohio)</VSCodeOption>
|
||||
<VSCodeOption value="us-west-1">US West (N. California)</VSCodeOption>
|
||||
<VSCodeOption value="us-west-2">US West (Oregon)</VSCodeOption>
|
||||
<VSCodeOption value="af-south-1">Africa (Cape Town)</VSCodeOption>
|
||||
<VSCodeOption value="ap-east-1">Asia Pacific (Hong Kong)</VSCodeOption>
|
||||
<VSCodeOption value="ap-south-1">Asia Pacific (Mumbai)</VSCodeOption>
|
||||
<VSCodeOption value="ap-northeast-1">Asia Pacific (Tokyo)</VSCodeOption>
|
||||
<VSCodeOption value="ap-northeast-2">Asia Pacific (Seoul)</VSCodeOption>
|
||||
<VSCodeOption value="ap-northeast-3">Asia Pacific (Osaka)</VSCodeOption>
|
||||
<VSCodeOption value="ap-southeast-1">Asia Pacific (Singapore)</VSCodeOption>
|
||||
<VSCodeOption value="ap-southeast-2">Asia Pacific (Sydney)</VSCodeOption>
|
||||
<VSCodeOption value="ca-central-1">Canada (Central)</VSCodeOption>
|
||||
<VSCodeOption value="eu-central-1">Europe (Frankfurt)</VSCodeOption>
|
||||
<VSCodeOption value="eu-west-1">Europe (Ireland)</VSCodeOption>
|
||||
<VSCodeOption value="eu-west-2">Europe (London)</VSCodeOption>
|
||||
<VSCodeOption value="eu-west-3">Europe (Paris)</VSCodeOption>
|
||||
<VSCodeOption value="eu-north-1">Europe (Stockholm)</VSCodeOption>
|
||||
<VSCodeOption value="me-south-1">Middle East (Bahrain)</VSCodeOption>
|
||||
<VSCodeOption value="sa-east-1">South America (São Paulo)</VSCodeOption>
|
||||
</VSCodeDropdown>
|
||||
</div>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
marginTop: "5px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
}}>
|
||||
These credentials are stored locally and only used to make API requests from the extension.
|
||||
<VSCodeLink
|
||||
href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html"
|
||||
style={{ display: "inline" }}>
|
||||
You can find your AWS access key and secret key here.
|
||||
</VSCodeLink>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ApiOptions
|
||||
@@ -1,64 +1,50 @@
|
||||
import { ApiConfiguration } from "@shared/api"
|
||||
import { VSCodeButton, VSCodeDivider, VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import React, { useState } from "react"
|
||||
import { useEffectOnce } from "react-use"
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { validateApiConfiguration, validateMaxRequestsPerTask } from "../utilities/validate"
|
||||
import { vscode } from "../utilities/vscode"
|
||||
import ApiOptions from "./ApiOptions"
|
||||
|
||||
type SettingsViewProps = {
|
||||
apiKey: string
|
||||
setApiKey: React.Dispatch<React.SetStateAction<string>>
|
||||
apiConfiguration?: ApiConfiguration
|
||||
setApiConfiguration: React.Dispatch<React.SetStateAction<ApiConfiguration | undefined>>
|
||||
maxRequestsPerTask: string
|
||||
setMaxRequestsPerTask: React.Dispatch<React.SetStateAction<string>>
|
||||
onDone: () => void // Define the type of the onDone prop
|
||||
onDone: () => void
|
||||
}
|
||||
|
||||
const SettingsView = ({ apiKey, setApiKey, maxRequestsPerTask, setMaxRequestsPerTask, onDone }: SettingsViewProps) => {
|
||||
const [apiKeyErrorMessage, setApiKeyErrorMessage] = useState<string | undefined>(undefined)
|
||||
const SettingsView = ({
|
||||
apiConfiguration,
|
||||
setApiConfiguration,
|
||||
maxRequestsPerTask,
|
||||
setMaxRequestsPerTask,
|
||||
onDone,
|
||||
}: SettingsViewProps) => {
|
||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
|
||||
const [maxRequestsErrorMessage, setMaxRequestsErrorMessage] = useState<string | undefined>(undefined)
|
||||
|
||||
const disableDoneButton = apiKeyErrorMessage != null || maxRequestsErrorMessage != null
|
||||
|
||||
const handleApiKeyChange = (event: any) => {
|
||||
const input = event.target.value
|
||||
setApiKey(input)
|
||||
validateApiKey(input)
|
||||
}
|
||||
|
||||
const validateApiKey = (value: string) => {
|
||||
if (value.trim() === "") {
|
||||
setApiKeyErrorMessage("API Key cannot be empty")
|
||||
} else {
|
||||
setApiKeyErrorMessage(undefined)
|
||||
}
|
||||
}
|
||||
|
||||
const handleMaxRequestsChange = (event: any) => {
|
||||
const input = event.target.value
|
||||
setMaxRequestsPerTask(input)
|
||||
validateMaxRequests(input)
|
||||
}
|
||||
|
||||
const validateMaxRequests = (value: string | undefined) => {
|
||||
if (value?.trim()) {
|
||||
const num = Number(value)
|
||||
if (isNaN(num)) {
|
||||
setMaxRequestsErrorMessage("Maximum requests must be a number")
|
||||
} else if (num < 3 || num > 100) {
|
||||
setMaxRequestsErrorMessage("Maximum requests must be between 3 and 100")
|
||||
} else {
|
||||
setMaxRequestsErrorMessage(undefined)
|
||||
}
|
||||
} else {
|
||||
setMaxRequestsErrorMessage(undefined)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
vscode.postMessage({ type: "apiKey", text: apiKey })
|
||||
vscode.postMessage({ type: "maxRequestsPerTask", text: maxRequestsPerTask })
|
||||
const apiValidationResult = validateApiConfiguration(apiConfiguration)
|
||||
const maxRequestsValidationResult = validateMaxRequestsPerTask(maxRequestsPerTask)
|
||||
|
||||
onDone()
|
||||
setApiErrorMessage(apiValidationResult)
|
||||
setMaxRequestsErrorMessage(maxRequestsValidationResult)
|
||||
|
||||
if (!apiValidationResult && !maxRequestsValidationResult) {
|
||||
vscode.postMessage({ type: "apiConfiguration", apiConfiguration })
|
||||
vscode.postMessage({ type: "maxRequestsPerTask", text: maxRequestsPerTask })
|
||||
onDone()
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setApiErrorMessage(undefined)
|
||||
}, [apiConfiguration])
|
||||
|
||||
useEffect(() => {
|
||||
setMaxRequestsErrorMessage(undefined)
|
||||
}, [maxRequestsPerTask])
|
||||
|
||||
// validate as soon as the component is mounted
|
||||
/*
|
||||
useEffect will use stale values of variables if they are not included in the dependency array. so trying to use useEffect with a dependency array of only one value for example will use any other variables' old values. In most cases you don't want this, and should opt to use react-use hooks.
|
||||
@@ -70,14 +56,6 @@ const SettingsView = ({ apiKey, setApiKey, maxRequestsPerTask, setMaxRequestsPer
|
||||
|
||||
If we only want to run code once on mount we can use react-use's useEffectOnce or useMount
|
||||
*/
|
||||
useEffectOnce(() => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
validateApiKey(apiKey)
|
||||
validateMaxRequests(maxRequestsPerTask)
|
||||
}, 1000)
|
||||
|
||||
return () => clearTimeout(timeoutId)
|
||||
})
|
||||
|
||||
return (
|
||||
<div style={{ margin: "0 auto", paddingTop: "10px" }}>
|
||||
@@ -89,40 +67,21 @@ const SettingsView = ({ apiKey, setApiKey, maxRequestsPerTask, setMaxRequestsPer
|
||||
marginBottom: "17px",
|
||||
}}>
|
||||
<h3 style={{ color: "var(--vscode-foreground)", margin: 0 }}>Settings</h3>
|
||||
<VSCodeButton onClick={handleSubmit} disabled={disableDoneButton}>
|
||||
Done
|
||||
</VSCodeButton>
|
||||
<VSCodeButton onClick={handleSubmit}>Done</VSCodeButton>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: "20px" }}>
|
||||
<VSCodeTextField
|
||||
value={apiKey}
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Enter your Anthropic API Key"
|
||||
onInput={handleApiKeyChange}>
|
||||
<span style={{ fontWeight: "500" }}>Anthropic API Key</span>
|
||||
</VSCodeTextField>
|
||||
{apiKeyErrorMessage && (
|
||||
<div style={{ marginBottom: 5 }}>
|
||||
<ApiOptions apiConfiguration={apiConfiguration} setApiConfiguration={setApiConfiguration} />
|
||||
{apiErrorMessage && (
|
||||
<p
|
||||
style={{
|
||||
margin: "-5px 0 12px 0",
|
||||
fontSize: "12px",
|
||||
marginTop: "5px",
|
||||
color: "var(--vscode-errorForeground)",
|
||||
}}>
|
||||
{apiKeyErrorMessage}
|
||||
{apiErrorMessage}
|
||||
</p>
|
||||
)}
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
marginTop: "5px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
}}>
|
||||
This key is not shared with anyone and only used to make API requests from the extension.
|
||||
<VSCodeLink href="https://console.anthropic.com/" style={{ display: "inline" }}>
|
||||
You can get an API key by signing up here.
|
||||
</VSCodeLink>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: "20px" }}>
|
||||
@@ -130,9 +89,18 @@ const SettingsView = ({ apiKey, setApiKey, maxRequestsPerTask, setMaxRequestsPer
|
||||
value={maxRequestsPerTask}
|
||||
style={{ width: "100%" }}
|
||||
placeholder="20"
|
||||
onInput={handleMaxRequestsChange}>
|
||||
onInput={(e: any) => setMaxRequestsPerTask(e.target?.value)}>
|
||||
<span style={{ fontWeight: "500" }}>Maximum # Requests Per Task</span>
|
||||
</VSCodeTextField>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
marginTop: "5px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
}}>
|
||||
If Claude Dev reaches this limit, it will pause and ask for your permission before making additional
|
||||
requests.
|
||||
</p>
|
||||
{maxRequestsErrorMessage && (
|
||||
<p
|
||||
style={{
|
||||
@@ -143,15 +111,6 @@ const SettingsView = ({ apiKey, setApiKey, maxRequestsPerTask, setMaxRequestsPer
|
||||
{maxRequestsErrorMessage}
|
||||
</p>
|
||||
)}
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
marginTop: "5px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
}}>
|
||||
If Claude Dev reaches this limit, it will pause and ask for your permission before making additional
|
||||
requests.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<VSCodeDivider />
|
||||
@@ -163,13 +122,14 @@ const SettingsView = ({ apiKey, setApiKey, maxRequestsPerTask, setMaxRequestsPer
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
fontSize: "12px",
|
||||
lineHeight: "1.2",
|
||||
fontStyle: "italic",
|
||||
}}>
|
||||
<p>
|
||||
<VSCodeLink href="https://github.com/saoudrizwan/claude-dev">
|
||||
<p style={{ wordWrap: "break-word" }}>
|
||||
If you have any questions or feedback, feel free to open an issue at{" "}
|
||||
<VSCodeLink href="https://github.com/saoudrizwan/claude-dev" style={{ display: "inline" }}>
|
||||
https://github.com/saoudrizwan/claude-dev
|
||||
</VSCodeLink>
|
||||
</p>
|
||||
<p style={{ fontStyle: "italic" }}>v1.0.86</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,32 +1,27 @@
|
||||
import React, { useState, useEffect } from "react"
|
||||
import { VSCodeButton, VSCodeTextField, VSCodeLink, VSCodeDivider } from "@vscode/webview-ui-toolkit/react"
|
||||
import { ApiConfiguration } from "@shared/api"
|
||||
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { validateApiConfiguration } from "../utilities/validate"
|
||||
import { vscode } from "../utilities/vscode"
|
||||
import ApiOptions from "./ApiOptions"
|
||||
|
||||
interface WelcomeViewProps {
|
||||
apiKey: string
|
||||
setApiKey: React.Dispatch<React.SetStateAction<string>>
|
||||
apiConfiguration?: ApiConfiguration
|
||||
setApiConfiguration: React.Dispatch<React.SetStateAction<ApiConfiguration | undefined>>
|
||||
}
|
||||
|
||||
const WelcomeView: React.FC<WelcomeViewProps> = ({ apiKey, setApiKey }) => {
|
||||
const [apiKeyErrorMessage, setApiKeyErrorMessage] = useState<string | undefined>(undefined)
|
||||
const WelcomeView: React.FC<WelcomeViewProps> = ({ apiConfiguration, setApiConfiguration }) => {
|
||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
|
||||
|
||||
const disableLetsGoButton = apiKeyErrorMessage != null
|
||||
|
||||
const validateApiKey = (value: string) => {
|
||||
if (value.trim() === "") {
|
||||
setApiKeyErrorMessage("API Key cannot be empty")
|
||||
} else {
|
||||
setApiKeyErrorMessage(undefined)
|
||||
}
|
||||
}
|
||||
const disableLetsGoButton = apiErrorMessage != null
|
||||
|
||||
const handleSubmit = () => {
|
||||
vscode.postMessage({ type: "apiKey", text: apiKey })
|
||||
vscode.postMessage({ type: "apiConfiguration", apiConfiguration })
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
validateApiKey(apiKey)
|
||||
}, [apiKey])
|
||||
setApiErrorMessage(validateApiConfiguration(apiConfiguration))
|
||||
}, [apiConfiguration])
|
||||
|
||||
return (
|
||||
<div style={{ position: "fixed", top: 0, left: 0, right: 0, bottom: 0, padding: "0 20px" }}>
|
||||
@@ -42,35 +37,14 @@ const WelcomeView: React.FC<WelcomeViewProps> = ({ apiKey, setApiKey }) => {
|
||||
files, analyze project source code, and execute terminal commands (with your permission, of course).
|
||||
</p>
|
||||
|
||||
<b>To get started, this extension needs an Anthropic API key:</b>
|
||||
<ol style={{ paddingLeft: "15px" }}>
|
||||
<li>
|
||||
Go to{" "}
|
||||
<VSCodeLink href="https://console.anthropic.com" style={{ display: "inline" }}>
|
||||
https://console.anthropic.com
|
||||
</VSCodeLink>
|
||||
</li>
|
||||
<li>You may need to buy some credits (although Anthropic is offering $5 free credit for new users)</li>
|
||||
<li>Click 'Get API Keys' and create a new key (you can delete it any time)</li>
|
||||
</ol>
|
||||
<b>To get started, this extension needs an API key for Claude 3.5 Sonnet:</b>
|
||||
|
||||
<VSCodeDivider />
|
||||
|
||||
<div style={{ marginTop: "20px", display: "flex", alignItems: "center" }}>
|
||||
<VSCodeTextField
|
||||
style={{ flexGrow: 1, marginRight: "10px" }}
|
||||
placeholder="Enter API Key..."
|
||||
value={apiKey}
|
||||
onInput={(e: any) => setApiKey(e.target.value)}
|
||||
/>
|
||||
<VSCodeButton onClick={handleSubmit} disabled={disableLetsGoButton}>
|
||||
Submit
|
||||
<div style={{ marginTop: "15px" }}>
|
||||
<ApiOptions apiConfiguration={apiConfiguration} setApiConfiguration={setApiConfiguration} />
|
||||
<VSCodeButton onClick={handleSubmit} disabled={disableLetsGoButton} style={{ marginTop: "3px" }}>
|
||||
Let's go!
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
|
||||
<p style={{ fontSize: "12px", marginTop: "10px", color: "var(--vscode-descriptionForeground)" }}>
|
||||
Your API key is stored securely on your computer and used only for interacting with the Anthropic API.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user