mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-21 00:21:25 +00:00
Merge b16fae960b into 9cfaf38d8a
This commit is contained in:
commit
4e84f81d38
5 changed files with 120 additions and 2 deletions
|
|
@ -207,6 +207,7 @@ const anthropicSchema = apiModelIdProviderModelSchema.extend({
|
|||
anthropicBaseUrl: z.string().optional(),
|
||||
anthropicUseAuthToken: z.boolean().optional(),
|
||||
anthropicBeta1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window.
|
||||
anthropicHeaders: z.record(z.string(), z.string()).optional(), // Custom headers for Anthropic API (e.g., for API gateways like Portkey).
|
||||
})
|
||||
|
||||
const openRouterSchema = baseProviderSettingsSchema.extend({
|
||||
|
|
|
|||
|
|
@ -144,6 +144,29 @@ describe("AnthropicHandler", () => {
|
|||
expect(mockAnthropicConstructor.mock.calls[0]![0]!.authToken).toEqual("test-api-key")
|
||||
expect(mockAnthropicConstructor.mock.calls[0]![0]!.apiKey).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should pass custom headers to the SDK when anthropicHeaders is provided", () => {
|
||||
const customHeaders = {
|
||||
"x-portkey-metadata": "test-metadata",
|
||||
"x-custom-header": "custom-value",
|
||||
}
|
||||
const handlerWithHeaders = new AnthropicHandler({
|
||||
...mockOptions,
|
||||
anthropicHeaders: customHeaders,
|
||||
})
|
||||
expect(handlerWithHeaders).toBeInstanceOf(AnthropicHandler)
|
||||
expect(mockAnthropicConstructor).toHaveBeenCalledTimes(1)
|
||||
expect(mockAnthropicConstructor.mock.calls[0]![0]!.defaultHeaders).toEqual(customHeaders)
|
||||
})
|
||||
|
||||
it("should pass undefined defaultHeaders when anthropicHeaders is not provided", () => {
|
||||
const handlerWithoutHeaders = new AnthropicHandler({
|
||||
...mockOptions,
|
||||
})
|
||||
expect(handlerWithoutHeaders).toBeInstanceOf(AnthropicHandler)
|
||||
expect(mockAnthropicConstructor).toHaveBeenCalledTimes(1)
|
||||
expect(mockAnthropicConstructor.mock.calls[0]![0]!.defaultHeaders).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("createMessage", () => {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
|
|||
this.client = new Anthropic({
|
||||
baseURL: this.options.anthropicBaseUrl || undefined,
|
||||
[apiKeyFieldName]: this.options.apiKey,
|
||||
defaultHeaders: this.options.anthropicHeaders || undefined,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,8 @@ vi.mock("@src/components/ui", () => ({
|
|||
CollapsibleContent: ({ children }: any) => <div>{children}</div>,
|
||||
Slider: ({ children, ...props }: any) => <div {...props}>{children}</div>,
|
||||
Button: ({ children, ...props }: any) => <button {...props}>{children}</button>,
|
||||
// Add StandardTooltip for Anthropic custom headers UI
|
||||
StandardTooltip: ({ children, content }: any) => <div title={content}>{children}</div>,
|
||||
// Add Popover components for ModelPicker
|
||||
Popover: ({ children }: any) => <div>{children}</div>,
|
||||
PopoverTrigger: ({ children }: any) => <div>{children}</div>,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
import { useCallback, useState } from "react"
|
||||
import { useCallback, useState, useEffect } from "react"
|
||||
import { Checkbox } from "vscrui"
|
||||
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import { VSCodeButton, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
|
||||
import type { ProviderSettings } from "@roo-code/types"
|
||||
|
||||
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
||||
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
|
||||
import { useSelectedModel } from "@src/components/ui/hooks/useSelectedModel"
|
||||
import { StandardTooltip } from "@src/components/ui"
|
||||
|
||||
import { convertHeadersToObject } from "../utils/headers"
|
||||
import { inputEventTransform, noTransform } from "../transforms"
|
||||
|
||||
type AnthropicProps = {
|
||||
|
|
@ -22,6 +24,11 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro
|
|||
|
||||
const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl)
|
||||
|
||||
const [customHeaders, setCustomHeaders] = useState<[string, string][]>(() => {
|
||||
const headers = apiConfiguration?.anthropicHeaders || {}
|
||||
return Object.entries(headers)
|
||||
})
|
||||
|
||||
// Check if the current model supports 1M context beta
|
||||
const supports1MContextBeta =
|
||||
selectedModel?.id === "claude-sonnet-4-20250514" ||
|
||||
|
|
@ -40,6 +47,50 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro
|
|||
[setApiConfigurationField],
|
||||
)
|
||||
|
||||
const handleAddCustomHeader = useCallback(() => {
|
||||
// Only update the local state to show the new row in the UI.
|
||||
setCustomHeaders((prev) => [...prev, ["", ""]])
|
||||
// Do not update the main configuration yet, wait for user input.
|
||||
}, [])
|
||||
|
||||
const handleUpdateHeaderKey = useCallback((index: number, newKey: string) => {
|
||||
setCustomHeaders((prev) => {
|
||||
const updated = [...prev]
|
||||
|
||||
if (updated[index]) {
|
||||
updated[index] = [newKey, updated[index][1]]
|
||||
}
|
||||
|
||||
return updated
|
||||
})
|
||||
}, [])
|
||||
|
||||
const handleUpdateHeaderValue = useCallback((index: number, newValue: string) => {
|
||||
setCustomHeaders((prev) => {
|
||||
const updated = [...prev]
|
||||
|
||||
if (updated[index]) {
|
||||
updated[index] = [updated[index][0], newValue]
|
||||
}
|
||||
|
||||
return updated
|
||||
})
|
||||
}, [])
|
||||
|
||||
const handleRemoveCustomHeader = useCallback((index: number) => {
|
||||
setCustomHeaders((prev) => prev.filter((_, i) => i !== index))
|
||||
}, [])
|
||||
|
||||
// Add effect to update the parent component's state when local headers change
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
const headerObject = convertHeadersToObject(customHeaders)
|
||||
setApiConfigurationField("anthropicHeaders", headerObject)
|
||||
}, 300)
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}, [customHeaders, setApiConfigurationField])
|
||||
|
||||
return (
|
||||
<>
|
||||
<VSCodeTextField
|
||||
|
|
@ -89,6 +140,46 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro
|
|||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Custom Headers UI */}
|
||||
<div className="mb-4">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<label className="block font-medium">{t("settings:providers.customHeaders")}</label>
|
||||
<StandardTooltip content={t("settings:common.add")}>
|
||||
<VSCodeButton appearance="icon" onClick={handleAddCustomHeader}>
|
||||
<span className="codicon codicon-add"></span>
|
||||
</VSCodeButton>
|
||||
</StandardTooltip>
|
||||
</div>
|
||||
{!customHeaders.length ? (
|
||||
<div className="text-sm text-vscode-descriptionForeground">
|
||||
{t("settings:providers.noCustomHeaders")}
|
||||
</div>
|
||||
) : (
|
||||
customHeaders.map(([key, value], index) => (
|
||||
<div key={index} className="flex items-center mb-2">
|
||||
<VSCodeTextField
|
||||
value={key}
|
||||
className="flex-1 mr-2"
|
||||
placeholder={t("settings:providers.headerName")}
|
||||
onInput={(e: any) => handleUpdateHeaderKey(index, e.target.value)}
|
||||
/>
|
||||
<VSCodeTextField
|
||||
value={value}
|
||||
className="flex-1 mr-2"
|
||||
placeholder={t("settings:providers.headerValue")}
|
||||
onInput={(e: any) => handleUpdateHeaderValue(index, e.target.value)}
|
||||
/>
|
||||
<StandardTooltip content={t("settings:common.remove")}>
|
||||
<VSCodeButton appearance="icon" onClick={() => handleRemoveCustomHeader(index)}>
|
||||
<span className="codicon codicon-trash"></span>
|
||||
</VSCodeButton>
|
||||
</StandardTooltip>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
{supports1MContextBeta && (
|
||||
<div>
|
||||
<Checkbox
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue