feat: Add Anthropic Custom provider with configurable model parameters#754
feat: Add Anthropic Custom provider with configurable model parameters#754ygzzfyh123 wants to merge 2 commits into
Conversation
- 移除自定义 Headers UI 和流式传输复选框 - 基础 URL 改为可选(复选框启用) - 模型选择器使用 anthropicModels 列表 - 自动从选中模型填充模型信息 - 校验只需要 API 密钥和模型 ID(基础 URL 可选)
📝 WalkthroughWalkthroughAdds a new ChangesAnthropic Custom Provider
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
webview-ui/src/components/settings/providers/AnthropicCustom.tsx (1)
33-342: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftAdd colocated Vitest coverage for this provider UI.
This component introduces local state, effects, conditional rendering, and model-default reset behavior. Please cover the base-URL toggle, model-info auto-fill/reset, and prompt-cache pricing visibility under
webview-ui/src/**/__tests__in this PR. As per coding guidelines,webview-ui/src/**/*.{ts,tsx}should prefer localwebview-uitests for React/webview behavior and add or update Vitest coverage underwebview-ui/src/**/__tests__instead of reaching forapps/vscode-e2e.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@webview-ui/src/components/settings/providers/AnthropicCustom.tsx` around lines 33 - 342, Add colocated Vitest tests for AnthropicCustom to cover its local state, effect, and conditional UI behavior instead of using vscode-e2e. Focus on AnthropicCustom, ModelPicker integration, and the reset button: verify the custom base-URL checkbox hides/clears the URL field, model selection auto-fills anthopicCustomModelInfo and the reset action restores defaults, and enabling supportsPromptCache reveals the cacheReads/cacheWrites pricing inputs. Place or update these tests under webview-ui/src/**/__tests__.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/api/providers/anthropic.ts`:
- Around line 42-52: The Anthropic client setup is mixing standard and custom
provider settings, so `AnthropicApiHandler` should derive `baseURL`, auth field
selection, and headers strictly from the active provider namespace instead of
falling back between `anthropic*` and `anthropicCustom*`. Update the constructor
logic in `src/api/providers/anthropic.ts` so `anthropic-custom` only reads
`anthropicCustomBaseUrl`, `anthropicCustomApiKey`, and `anthropicCustomHeaders`,
while the standard Anthropic path only uses the non-custom fields; avoid using
the old fallback chain in `this.client = new Anthropic(...)`, and make sure any
provider selection passed from `buildApiHandler()` is used to choose the correct
source of truth.
- Around line 361-368: The model metadata fallback in the Anthropics provider is
skipping built-in metadata whenever anthropicCustomModelId is set, so saved
profiles can lose the real Claude defaults. Update the logic around the custom
model path in this block to first look up anthropicModels[customModelId] when
the ID is a known Anthropic model, then merge anthropicCustomModelInfo over
that, and only use openAiModelInfoSaneDefaults when no built-in metadata exists.
Keep the change localized to the custom model handling in the Anthropics
provider where modelId/id/info are computed.
In `@webview-ui/src/components/settings/providers/AnthropicCustom.tsx`:
- Line 42: The anthropic custom base URL toggle in AnthropicCustom should stay
in sync when apiConfiguration is later hydrated or updated externally, because
the current useState initialization only snapshots the value once. Update the
anthropicBaseUrlSelected state in response to
apiConfiguration.anthropicCustomBaseUrl changes, and make sure the
checkbox/field visibility in AnthropicCustom reflects the latest config,
including the logic around the related toggle and input handling in the
component.
- Around line 44-55: Seed anthropicCustomModelInfo from the currently selected
anthropicCustomModelId instead of always using anthropicCustomDefaultModelId.
Update the initialization logic in AnthropicCustom’s useEffect so it reads
apiConfiguration.anthropicCustomModelId (with a fallback to the default only
when no custom model is selected) before calling setApiConfigurationField,
ensuring the hydrated capability/pricing data matches the active model
selection.
In `@webview-ui/src/components/ui/hooks/useSelectedModel.ts`:
- Around line 373-379: The anthropic-custom branch in useSelectedModel is
choosing apiModelId before anthropicCustomModelId, which can surface a stale
standard model instead of the custom one. Update the id selection logic so the
anthropic-custom provider prefers anthropicCustomModelId first, with apiModelId
only as a fallback, and keep the existing info handling for
anthropicCustomModelInfo in that branch.
---
Nitpick comments:
In `@webview-ui/src/components/settings/providers/AnthropicCustom.tsx`:
- Around line 33-342: Add colocated Vitest tests for AnthropicCustom to cover
its local state, effect, and conditional UI behavior instead of using
vscode-e2e. Focus on AnthropicCustom, ModelPicker integration, and the reset
button: verify the custom base-URL checkbox hides/clears the URL field, model
selection auto-fills anthopicCustomModelInfo and the reset action restores
defaults, and enabling supportsPromptCache reveals the cacheReads/cacheWrites
pricing inputs. Place or update these tests under webview-ui/src/**/__tests__.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 9826bf4f-8323-4c30-9e4f-e438ea972906
📒 Files selected for processing (11)
packages/types/src/provider-settings.tssrc/api/index.tssrc/api/providers/anthropic.tssrc/shared/ProfileValidator.tswebview-ui/src/components/settings/ApiOptions.tsxwebview-ui/src/components/settings/ModelPicker.tsxwebview-ui/src/components/settings/constants.tswebview-ui/src/components/settings/providers/AnthropicCustom.tsxwebview-ui/src/components/settings/providers/index.tswebview-ui/src/components/ui/hooks/useSelectedModel.tswebview-ui/src/utils/validate.ts
| const baseURL = this.options.anthropicCustomBaseUrl || this.options.anthropicBaseUrl || undefined | ||
| const apiKey = this.options.anthropicCustomApiKey || this.options.apiKey | ||
| const apiKeyFieldName = | ||
| this.options.anthropicBaseUrl && this.options.anthropicUseAuthToken ? "authToken" : "apiKey" | ||
| baseURL && this.options.anthropicUseAuthToken && !this.options.anthropicCustomApiKey | ||
| ? "authToken" | ||
| : "apiKey" | ||
|
|
||
| this.client = new Anthropic({ | ||
| baseURL: this.options.anthropicBaseUrl || undefined, | ||
| [apiKeyFieldName]: this.options.apiKey, | ||
| baseURL, | ||
| [apiKeyFieldName]: apiKey, | ||
| defaultHeaders: this.options.anthropicCustomHeaders, |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Keep anthropic-custom isolated from the standard Anthropic connection fields.
This fallback chain mixes the two provider namespaces. If a profile still has anthropicBaseUrl populated from the standard Anthropic setup, selecting anthropic-custom with its custom base URL disabled will still route prompts to that old proxy instead of Anthropic’s default endpoint. The same bleed-through can happen with credentials because buildApiHandler() no longer passes apiProvider into the handler, so this constructor has no way to tell which namespace should win. Please derive endpoint/auth strictly from the active provider instead of falling back across anthropic* and anthropicCustom*.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/api/providers/anthropic.ts` around lines 42 - 52, The Anthropic client
setup is mixing standard and custom provider settings, so `AnthropicApiHandler`
should derive `baseURL`, auth field selection, and headers strictly from the
active provider namespace instead of falling back between `anthropic*` and
`anthropicCustom*`. Update the constructor logic in
`src/api/providers/anthropic.ts` so `anthropic-custom` only reads
`anthropicCustomBaseUrl`, `anthropicCustomApiKey`, and `anthropicCustomHeaders`,
while the standard Anthropic path only uses the non-custom fields; avoid using
the old fallback chain in `this.client = new Anthropic(...)`, and make sure any
provider selection passed from `buildApiHandler()` is used to choose the correct
source of truth.
| const customModelId = this.options.anthropicCustomModelId | ||
| const modelId = customModelId || this.options.apiModelId | ||
| const id = | ||
| customModelId || | ||
| (modelId && modelId in anthropicModels ? (modelId as AnthropicModelId) : anthropicDefaultModelId) | ||
| let info: ModelInfo = customModelId | ||
| ? this.options.anthropicCustomModelInfo || openAiModelInfoSaneDefaults | ||
| : anthropicModels[id as AnthropicModelId] |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Fallback to built-in Anthropic metadata for known custom model IDs.
anthropicCustomModelInfo is optional, but this path drops straight to openAiModelInfoSaneDefaults whenever a custom model ID is set. For any saved/imported profile that picks a stock Claude model but lacks the hydrated anthropicCustomModelInfo, you'll silently lose the real context window, prompt-cache support, and pricing. Please seed from anthropicModels[customModelId] when the ID is known, then layer user overrides on top.
Suggested patch
const customModelId = this.options.anthropicCustomModelId
const modelId = customModelId || this.options.apiModelId
const id =
customModelId ||
(modelId && modelId in anthropicModels ? (modelId as AnthropicModelId) : anthropicDefaultModelId)
- let info: ModelInfo = customModelId
- ? this.options.anthropicCustomModelInfo || openAiModelInfoSaneDefaults
- : anthropicModels[id as AnthropicModelId]
+ const builtInCustomInfo =
+ customModelId && customModelId in anthropicModels
+ ? anthropicModels[customModelId as AnthropicModelId]
+ : undefined
+ let info: ModelInfo = customModelId
+ ? {
+ ...(builtInCustomInfo ?? openAiModelInfoSaneDefaults),
+ ...(this.options.anthropicCustomModelInfo ?? {}),
+ }
+ : anthropicModels[id as AnthropicModelId]📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const customModelId = this.options.anthropicCustomModelId | |
| const modelId = customModelId || this.options.apiModelId | |
| const id = | |
| customModelId || | |
| (modelId && modelId in anthropicModels ? (modelId as AnthropicModelId) : anthropicDefaultModelId) | |
| let info: ModelInfo = customModelId | |
| ? this.options.anthropicCustomModelInfo || openAiModelInfoSaneDefaults | |
| : anthropicModels[id as AnthropicModelId] | |
| const customModelId = this.options.anthropicCustomModelId | |
| const modelId = customModelId || this.options.apiModelId | |
| const id = | |
| customModelId || | |
| (modelId && modelId in anthropicModels ? (modelId as AnthropicModelId) : anthropicDefaultModelId) | |
| const builtInCustomInfo = | |
| customModelId && customModelId in anthropicModels | |
| ? anthropicModels[customModelId as AnthropicModelId] | |
| : undefined | |
| let info: ModelInfo = customModelId | |
| ? { | |
| ...(builtInCustomInfo ?? openAiModelInfoSaneDefaults), | |
| ...(this.options.anthropicCustomModelInfo ?? {}), | |
| } | |
| : anthropicModels[id as AnthropicModelId] |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/api/providers/anthropic.ts` around lines 361 - 368, The model metadata
fallback in the Anthropics provider is skipping built-in metadata whenever
anthropicCustomModelId is set, so saved profiles can lose the real Claude
defaults. Update the logic around the custom model path in this block to first
look up anthropicModels[customModelId] when the ID is a known Anthropic model,
then merge anthropicCustomModelInfo over that, and only use
openAiModelInfoSaneDefaults when no built-in metadata exists. Keep the change
localized to the custom model handling in the Anthropics provider where
modelId/id/info are computed.
| }: AnthropicCustomProps) => { | ||
| const { t } = useAppTranslation() | ||
|
|
||
| const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicCustomBaseUrl) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Resync the custom-base-URL toggle when config is hydrated externally.
Line 42 snapshots anthropicBaseUrlSelected once, so a later profile/config update with a saved anthropicCustomBaseUrl can leave the checkbox unchecked and the field hidden even though the value exists.
Suggested fix
const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicCustomBaseUrl)
+
+useEffect(() => {
+ setAnthropicBaseUrlSelected(!!apiConfiguration?.anthropicCustomBaseUrl)
+}, [apiConfiguration?.anthropicCustomBaseUrl])Also applies to: 89-109
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@webview-ui/src/components/settings/providers/AnthropicCustom.tsx` at line 42,
The anthropic custom base URL toggle in AnthropicCustom should stay in sync when
apiConfiguration is later hydrated or updated externally, because the current
useState initialization only snapshots the value once. Update the
anthropicBaseUrlSelected state in response to
apiConfiguration.anthropicCustomBaseUrl changes, and make sure the
checkbox/field visibility in AnthropicCustom reflects the latest config,
including the logic around the related toggle and input handling in the
component.
| useEffect(() => { | ||
| if (!apiConfiguration.anthropicCustomModelInfo) { | ||
| setApiConfigurationField( | ||
| "anthropicCustomModelInfo", | ||
| { | ||
| ...openAiModelInfoSaneDefaults, | ||
| ...(anthropicModels[anthropicCustomDefaultModelId] || {}), | ||
| }, | ||
| false, | ||
| ) | ||
| } | ||
| }, [apiConfiguration.anthropicCustomModelInfo, setApiConfigurationField]) |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Seed anthropicCustomModelInfo from the selected model ID.
Line 50 always hydrates anthropicCustomModelInfo from anthropicCustomDefaultModelId. If apiConfiguration.anthropicCustomModelId is already set while model info is absent, this writes Sonnet 4.5 capability/pricing data under a different model selection.
Suggested fix
useEffect(() => {
if (!apiConfiguration.anthropicCustomModelInfo) {
+ const modelId =
+ (apiConfiguration.anthropicCustomModelId as keyof typeof anthropicModels | undefined) ??
+ anthropicCustomDefaultModelId
+
setApiConfigurationField(
"anthropicCustomModelInfo",
{
...openAiModelInfoSaneDefaults,
- ...(anthropicModels[anthropicCustomDefaultModelId] || {}),
+ ...(anthropicModels[modelId] || {}),
},
false,
)
}
-}, [apiConfiguration.anthropicCustomModelInfo, setApiConfigurationField])
+}, [
+ apiConfiguration.anthropicCustomModelId,
+ apiConfiguration.anthropicCustomModelInfo,
+ setApiConfigurationField,
+])📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| useEffect(() => { | |
| if (!apiConfiguration.anthropicCustomModelInfo) { | |
| setApiConfigurationField( | |
| "anthropicCustomModelInfo", | |
| { | |
| ...openAiModelInfoSaneDefaults, | |
| ...(anthropicModels[anthropicCustomDefaultModelId] || {}), | |
| }, | |
| false, | |
| ) | |
| } | |
| }, [apiConfiguration.anthropicCustomModelInfo, setApiConfigurationField]) | |
| useEffect(() => { | |
| if (!apiConfiguration.anthropicCustomModelInfo) { | |
| const modelId = | |
| (apiConfiguration.anthropicCustomModelId as keyof typeof anthropicModels | undefined) ?? | |
| anthropicCustomDefaultModelId | |
| setApiConfigurationField( | |
| "anthropicCustomModelInfo", | |
| { | |
| ...openAiModelInfoSaneDefaults, | |
| ...(anthropicModels[modelId] || {}), | |
| }, | |
| false, | |
| ) | |
| } | |
| }, [ | |
| apiConfiguration.anthropicCustomModelId, | |
| apiConfiguration.anthropicCustomModelInfo, | |
| setApiConfigurationField, | |
| ]) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@webview-ui/src/components/settings/providers/AnthropicCustom.tsx` around
lines 44 - 55, Seed anthropicCustomModelInfo from the currently selected
anthropicCustomModelId instead of always using anthropicCustomDefaultModelId.
Update the initialization logic in AnthropicCustom’s useEffect so it reads
apiConfiguration.anthropicCustomModelId (with a fallback to the default only
when no custom model is selected) before calling setApiConfigurationField,
ensuring the hydrated capability/pricing data matches the active model
selection.
| provider satisfies "anthropic" | "anthropic-custom" | "gemini-cli" | "fake-ai" | ||
| const id = apiConfiguration.apiModelId ?? apiConfiguration.anthropicCustomModelId ?? defaultModelId | ||
|
|
||
| // For anthropic-custom, use custom model info if available | ||
| if (provider === "anthropic-custom") { | ||
| const info = apiConfiguration.anthropicCustomModelInfo || undefined | ||
| return { id, info } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Prefer anthropicCustomModelId before apiModelId in the anthropic-custom branch.
For this provider, validation and profile handling both use anthropicCustomModelId as the selected model, but this hook lets a stale apiModelId win first. If a user switches from the standard Anthropic provider and both fields are populated, the picker will display/edit the wrong model and capability set.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@webview-ui/src/components/ui/hooks/useSelectedModel.ts` around lines 373 -
379, The anthropic-custom branch in useSelectedModel is choosing apiModelId
before anthropicCustomModelId, which can surface a stale standard model instead
of the custom one. Update the id selection logic so the anthropic-custom
provider prefers anthropicCustomModelId first, with apiModelId only as a
fallback, and keep the existing info handling for anthropicCustomModelInfo in
that branch.
|
Reasons for creating this PR: The main reason for opening this PR is to address compatibility issues caused by certain third-party API vendors and to provide a more flexible configuration option. The specific reasons are as follows: Protocol Compatibility and Errors: Some third-party API providers have poor support for Claude and protocol conversion. Using OpenAI-compatible protocols with them results in errors indicating that tools cannot be called. Context Length Limitations: Although switching to the Anthropic protocol resolves the tool-calling errors, the default Anthropic protocol does not support custom context lengths. Non-Destructive Solution: To resolve this dilemma, this PR adds a new option in a non-destructive manner. It does not directly modify the original Anthropic implementation, ensuring no negative impact while perfectly adapting to the limitations of third-party APIs. |

Related GitHub Issue
Closes: #753
Description
This PR adds a new Anthropic Custom provider that allows users to customize model parameters while using the Anthropic Messages API protocol. This addresses the limitation that the standard Anthropic provider doesn't allow customization of context window size and pricing.
Key implementation details:
anthropic-customto the provider listAnthropicHandlerwith custom configuration fieldsanthropicModelslistChanges made:
anthropic-customto provider settingsanthropicCustomBaseUrl,anthropicCustomApiKey,anthropicCustomModelId,anthropicCustomModelInfo,anthropicCustomHeadersAnthropicHandlerto use custom fields when provider isanthropic-customAnthropicCustom.tsxsettings componentModelPicker,ProfileValidator, and routing logicTest Procedure
Manual testing steps:
Testing environment:
Pre-Submission Checklist
Screenshots / Videos
Settings UI showing Anthropic Custom provider with customizable options


Documentation Updates
The feature is self-documenting through the UI with tooltips explaining each field.
Additional Notes
This implementation intentionally reuses the existing
AnthropicHandlerto minimize code duplication and ensure consistency with the standard Anthropic provider. The custom headers field was added to the type definitions but the UI for it was removed in the final version to keep the interface simple.Get in Touch
Discord: g_avery114
Summary by CodeRabbit
New Features
Bug Fixes