feat: add Umans provider#755
Conversation
- 移除自定义 Headers UI 和流式传输复选框 - 基础 URL 改为可选(复选框启用) - 模型选择器使用 anthropicModels 列表 - 自动从选中模型填充模型信息 - 校验只需要 API 密钥和模型 ID(基础 URL 可选)
📝 WalkthroughWalkthroughAdds ChangesUmans + anthropic-custom provider integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/types/src/global-settings.ts (1)
290-323: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winAdd
anthropicCustomApiKeytoSECRET_STATE_KEYS.
packages/types/src/provider-settings.tsLines 271-278 introduceanthropicCustomApiKey, but this list still only registersumansApiKey. That leaves the new Anthropic custom credential in global settings instead of secret storage.Suggested follow-up
export const SECRET_STATE_KEYS = [ "apiKey", "openRouterApiKey", "umansApiKey", + "anthropicCustomApiKey", "awsAccessKey",it("should not contain Umans API key (secret)", () => { expect(GLOBAL_STATE_KEYS).not.toContain("umansApiKey") }) + + it("should not contain Anthropic Custom API key (secret)", () => { + expect(GLOBAL_STATE_KEYS).not.toContain("anthropicCustomApiKey") + })🤖 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 `@packages/types/src/global-settings.ts` around lines 290 - 323, `SECRET_STATE_KEYS` is missing the new Anthropic custom credential, so update the list in `global-settings.ts` to include `anthropicCustomApiKey` alongside the other secret API key entries. Make sure the added key matches the identifier introduced in `provider-settings.ts` (`anthropicCustomApiKey`) so it is treated as secret state consistently.
🧹 Nitpick comments (3)
src/shared/ProfileValidator.ts (1)
56-57: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a local unit test for
anthropic-custommodel extraction.This branch is now part of the allow-list gate, but
src/shared/__tests__/ProfileValidator.spec.tsonly adds coverage forumans. A regression here would reject otherwise allowedanthropic-customprofiles with no test catching it.Suggested test
+ it("should extract anthropicCustomModelId for anthropic-custom provider", () => { + const allowList: OrganizationAllowList = { + allowAll: false, + providers: { + "anthropic-custom": { allowAll: false, models: ["claude-custom"] }, + }, + } + const profile: ProviderSettings = { + apiProvider: "anthropic-custom", + anthropicCustomModelId: "claude-custom", + } + + expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true) + })As per coding guidelines,
**/*.{test,spec}.{ts,tsx,js}: Use package-local unit tests for pure logic, parsing, state transitions, validation, serialization, request construction, retry decisions, and error handling.🤖 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/shared/ProfileValidator.ts` around lines 56 - 57, Add a package-local unit test for the new anthropic-custom branch in ProfileValidator’s model extraction logic. Update src/shared/__tests__/ProfileValidator.spec.ts to cover the getModelId/profile validation path for an anthropic-custom profile and assert it returns profile.anthropicCustomModelId, alongside the existing umans coverage. This will ensure the allow-list gate continues to accept anthropic-custom profiles and prevents regressions in the ProfileValidator branch handling.Source: Coding guidelines
webview-ui/src/utils/__tests__/validate.spec.ts (1)
159-167: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winPlease add the matching
anthropic-customvalidation case here too.This cohort adds validation for both providers, but this spec only locks down the Umans branch. A small unit test for the custom Anthropic required-field path would keep that new validation logic from regressing. As per coding guidelines, use package-local unit tests for validation.
🤖 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/utils/__tests__/validate.spec.ts` around lines 159 - 167, Add a matching unit test in validate.spec.ts for the anthropic-custom provider missing API key case, alongside the existing validateApiConfigurationExcludingModelErrors coverage. Reuse the same test pattern and assertions as the Umans branch, but set apiProvider to anthropic-custom and omit the required Anthropic custom fields so the validation returns settings:validation.apiKey. Use the existing validateApiConfigurationExcludingModelErrors helper and ProviderSettings fixture style to keep the spec package-local and consistent.Source: Coding guidelines
webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts (1)
648-722: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a focused test for the new
anthropic-customhook path.
useSelectedModelnow has provider-specific fallback logic foranthropic-custom, but this file only exercises Umans. A case that provesanthropicCustomModelIdwins over any staleapiModelIdand returnsanthropicCustomModelInfowould lock down the new behavior. As per coding guidelines, prefer localwebview-uitests for hooks and use package-local unit tests for state transitions.🤖 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/__tests__/useSelectedModel.spec.ts` around lines 648 - 722, The current hook tests cover Umans fallback behavior but miss the new anthropic-custom path in useSelectedModel. Add a focused test in useSelectedModel.spec.ts that sets apiProvider to anthropic-custom, provides both a stale apiModelId and a valid anthropicCustomModelId, and asserts the hook returns the anthropicCustomModelId plus anthropicCustomModelInfo. Use the existing mockUseRouterModels setup and mirror the style of the Umans cases so the provider-specific fallback logic is locked down.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 applying anthropicCustom*
overrides unconditionally, so normal anthropic profiles can inherit stale custom
base URL, API key, headers, or model settings. Update Anthrop
icHandler/buildApiHandler handling so these overrides are only used for the
anthropic-custom path, either by preserving apiProvider in the handler options
or by passing an explicit custom-mode flag before constructing the Anthropic
client in anthropic.ts and any related model selection logic.
- Around line 366-368: The custom-model fallback in anthropic.ts currently uses
openAiModelInfoSaneDefaults, which can leave maxTokens at -1 and flow into
max_tokens for anthropicCustomModelId. Update the ModelInfo selection in the
Anthropic provider to use an Anthropic-safe default, such as
ANTHROPIC_DEFAULT_MAX_TOKENS, or clamp any negative maxTokens before
getModelMaxOutputTokens() builds the request. Keep the fix localized around the
customModelId branch and the Anthropic model info lookup.
In `@src/api/providers/fetchers/umans.ts`:
- Around line 50-63: The Umans fetcher in getModels() is treating the optional
/models pricing request as fatal because Promise.all() rejects both calls when
pricing fails. Update the logic in umans.ts so the /models/info request can
still succeed independently, and only use /models to enrich
inputPrice/outputPrice when it resolves and parses successfully. Add a
package-local unit regression spec around getModels()/the Umans fetcher covering
“pricing request rejects but model info still returns models,” and verify the
catalog is still returned and cached.
In `@webview-ui/src/components/settings/providers/AnthropicCustom.tsx`:
- Line 42: The Anthropic custom base URL toggle is only initialized once from
apiConfiguration, so it can get out of sync when the settings object changes.
Update AnthropicCustom to derive anthropicBaseUrlSelected from
apiConfiguration?.anthropicCustomBaseUrl or resync it in an effect keyed on that
prop, and make sure the checkbox/field visibility always reflects the current
configuration.
- Around line 44-55: Seed and reset anthropicCustomModelInfo from the currently
active Anthropic model id instead of always using anthropicCustomDefaultModelId.
Update the useEffect in AnthropicCustom to derive the hydrated model info from
anthropicCustomModelId (falling back to the default only when no id is set), and
make the model-picker logic around anthropicCustomModelId use the same source so
the cached capability/pricing data always matches the selected model.
In `@webview-ui/src/components/ui/hooks/useSelectedModel.ts`:
- Around line 378-385: The `useSelectedModel` hook is picking the wrong model id
for the `anthropic-custom` provider because it prefers
`apiConfiguration.apiModelId` before the custom field. Update the selection
logic in `useSelectedModel` so the `anthropic-custom` branch uses
`apiConfiguration.anthropicCustomModelId` first (falling back only if needed),
while still returning `anthropicCustomModelInfo` for that provider. Keep the
existing provider check and adjust the `id` resolution to avoid stale ids from
other providers.
---
Outside diff comments:
In `@packages/types/src/global-settings.ts`:
- Around line 290-323: `SECRET_STATE_KEYS` is missing the new Anthropic custom
credential, so update the list in `global-settings.ts` to include
`anthropicCustomApiKey` alongside the other secret API key entries. Make sure
the added key matches the identifier introduced in `provider-settings.ts`
(`anthropicCustomApiKey`) so it is treated as secret state consistently.
---
Nitpick comments:
In `@src/shared/ProfileValidator.ts`:
- Around line 56-57: Add a package-local unit test for the new anthropic-custom
branch in ProfileValidator’s model extraction logic. Update
src/shared/__tests__/ProfileValidator.spec.ts to cover the getModelId/profile
validation path for an anthropic-custom profile and assert it returns
profile.anthropicCustomModelId, alongside the existing umans coverage. This will
ensure the allow-list gate continues to accept anthropic-custom profiles and
prevents regressions in the ProfileValidator branch handling.
In `@webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts`:
- Around line 648-722: The current hook tests cover Umans fallback behavior but
miss the new anthropic-custom path in useSelectedModel. Add a focused test in
useSelectedModel.spec.ts that sets apiProvider to anthropic-custom, provides
both a stale apiModelId and a valid anthropicCustomModelId, and asserts the hook
returns the anthropicCustomModelId plus anthropicCustomModelInfo. Use the
existing mockUseRouterModels setup and mirror the style of the Umans cases so
the provider-specific fallback logic is locked down.
In `@webview-ui/src/utils/__tests__/validate.spec.ts`:
- Around line 159-167: Add a matching unit test in validate.spec.ts for the
anthropic-custom provider missing API key case, alongside the existing
validateApiConfigurationExcludingModelErrors coverage. Reuse the same test
pattern and assertions as the Umans branch, but set apiProvider to
anthropic-custom and omit the required Anthropic custom fields so the validation
returns settings:validation.apiKey. Use the existing
validateApiConfigurationExcludingModelErrors helper and ProviderSettings fixture
style to keep the spec package-local and consistent.
🪄 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: 63d16cb7-a1d4-4eb9-8f97-31019ced3166
📒 Files selected for processing (33)
packages/types/src/__tests__/index.test.tspackages/types/src/global-settings.tspackages/types/src/provider-settings.tspackages/types/src/providers/index.tspackages/types/src/providers/umans.tssrc/api/index.tssrc/api/providers/__tests__/umans.spec.tssrc/api/providers/anthropic.tssrc/api/providers/fetchers/__tests__/modelCache.spec.tssrc/api/providers/fetchers/__tests__/umans.spec.tssrc/api/providers/fetchers/modelCache.tssrc/api/providers/fetchers/umans.tssrc/api/providers/index.tssrc/api/providers/umans.tssrc/core/webview/__tests__/ClineProvider.spec.tssrc/core/webview/__tests__/webviewMessageHandler.routerModels.spec.tssrc/core/webview/__tests__/webviewMessageHandler.spec.tssrc/core/webview/webviewMessageHandler.tssrc/shared/ProfileValidator.tssrc/shared/__tests__/ProfileValidator.spec.tssrc/shared/api.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/Umans.tsxwebview-ui/src/components/settings/providers/index.tswebview-ui/src/components/settings/utils/__tests__/providerModelConfig.spec.tswebview-ui/src/components/settings/utils/providerModelConfig.tswebview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.tswebview-ui/src/components/ui/hooks/useSelectedModel.tswebview-ui/src/utils/__tests__/validate.spec.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.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Gate anthropicCustom* overrides to the anthropic-custom provider path.
buildApiHandler constructs AnthropicHandler(options) for both "anthropic" and "anthropic-custom" after removing apiProvider, so these unconditional preferences can make a normal Anthropic profile use stale custom base URL/API key/headers/model settings. Pass an explicit custom-mode flag or retain apiProvider in the handler options before applying these fields.
Possible direction
- case "anthropic":
- case "anthropic-custom":
- return new AnthropicHandler(options)
+ case "anthropic":
+ return new AnthropicHandler(options, { useCustomAnthropic: false })
+ case "anthropic-custom":
+ return new AnthropicHandler(options, { useCustomAnthropic: true })- const baseURL = this.options.anthropicCustomBaseUrl || this.options.anthropicBaseUrl || undefined
- const apiKey = this.options.anthropicCustomApiKey || this.options.apiKey
+ const baseURL = useCustomAnthropic
+ ? this.options.anthropicCustomBaseUrl || undefined
+ : this.options.anthropicBaseUrl || undefined
+ const apiKey = useCustomAnthropic ? this.options.anthropicCustomApiKey : this.options.apiKeyAlso applies to: 361-368
🤖 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 applying anthropicCustom* overrides unconditionally, so normal
anthropic profiles can inherit stale custom base URL, API key, headers, or model
settings. Update Anthrop icHandler/buildApiHandler handling so these overrides
are only used for the anthropic-custom path, either by preserving apiProvider in
the handler options or by passing an explicit custom-mode flag before
constructing the Anthropic client in anthropic.ts and any related model
selection logic.
| let info: ModelInfo = customModelId | ||
| ? this.options.anthropicCustomModelInfo || openAiModelInfoSaneDefaults | ||
| : anthropicModels[id as AnthropicModelId] |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect getModelParams handling for ModelInfo.maxTokens === -1.
rg -n -C 8 'function getModelParams|const getModelParams|export .*getModelParams|maxTokens' src packagesRepository: Zoo-Code-Org/Zoo-Code
Length of output: 50378
🏁 Script executed:
ast-grep outline src/api/providers/anthropic.ts --view expandedRepository: Zoo-Code-Org/Zoo-Code
Length of output: 553
🏁 Script executed:
rg -n -C 6 'getModelParams|maxTokens\s*[:=]|max_tokens|anthropicCustomModelInfo|openAiModelInfoSaneDefaults' src packages/typesRepository: Zoo-Code-Org/Zoo-Code
Length of output: 50378
🏁 Script executed:
sed -n '340,470p' src/api/providers/anthropic.tsRepository: Zoo-Code-Org/Zoo-Code
Length of output: 2856
🏁 Script executed:
rg -n -C 6 'getModelParams|maxTokens|max_tokens|anthropicCustomModelInfo|openAiModelInfoSaneDefaults' src/api/providers/anthropic.ts packages/types/src/providers/openai.tsRepository: Zoo-Code-Org/Zoo-Code
Length of output: 44934
🏁 Script executed:
ast-grep outline src/api/transform/model-params.ts --view expandedRepository: Zoo-Code-Org/Zoo-Code
Length of output: 625
🏁 Script executed:
sed -n '1,260p' src/api/transform/model-params.tsRepository: Zoo-Code-Org/Zoo-Code
Length of output: 6545
🏁 Script executed:
ast-grep outline src/shared/api.ts --view expandedRepository: Zoo-Code-Org/Zoo-Code
Length of output: 483
🏁 Script executed:
rg -n -C 8 'function getModelMaxOutputTokens|getModelMaxOutputTokens|maxTokens === -1|modelMaxTokens' src/shared packages/typesRepository: Zoo-Code-Org/Zoo-Code
Length of output: 30796
🏁 Script executed:
sed -n '120,180p' src/shared/api.tsRepository: Zoo-Code-Org/Zoo-Code
Length of output: 2536
Use an Anthropic-safe fallback for custom models. openAiModelInfoSaneDefaults.maxTokens is -1, and getModelMaxOutputTokens() will pass that through to max_tokens for anthropicCustomModelId when no custom model info is provided. Default to ANTHROPIC_DEFAULT_MAX_TOKENS or clamp negative values before building the request.
🤖 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 366 - 368, The custom-model
fallback in anthropic.ts currently uses openAiModelInfoSaneDefaults, which can
leave maxTokens at -1 and flow into max_tokens for anthropicCustomModelId.
Update the ModelInfo selection in the Anthropic provider to use an
Anthropic-safe default, such as ANTHROPIC_DEFAULT_MAX_TOKENS, or clamp any
negative maxTokens before getModelMaxOutputTokens() builds the request. Keep the
fix localized around the customModelId branch and the Anthropic model info
lookup.
| const [infoResponse, pricingResponse] = await Promise.all([ | ||
| axios.get(`${UMANS_DEFAULT_BASE_URL}/models/info`), | ||
| axios.get(`${UMANS_DEFAULT_BASE_URL}/models`), | ||
| ]) | ||
|
|
||
| const infoResult = umansModelsInfoResponseSchema.safeParse(infoResponse.data) | ||
| if (!infoResult.success) { | ||
| return models | ||
| } | ||
|
|
||
| const pricingResult = umansPricingResponseSchema.safeParse(pricingResponse.data) | ||
| const pricingById = new Map( | ||
| (pricingResult.success ? pricingResult.data.data : []).map((entry) => [entry.id, entry.pricing]), | ||
| ) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Treat the pricing call as optional.
/models is only enriching inputPrice/outputPrice, but Promise.all() makes a pricing outage discard the already-fetched /models/info payload and return an empty catalog. Because getModels() only caches non-empty results, a transient pricing failure will make Umans look like it has no models at all.
Suggested fix
- const [infoResponse, pricingResponse] = await Promise.all([
- axios.get(`${UMANS_DEFAULT_BASE_URL}/models/info`),
- axios.get(`${UMANS_DEFAULT_BASE_URL}/models`),
- ])
+ const [infoResponse, pricingResponse] = await Promise.allSettled([
+ axios.get(`${UMANS_DEFAULT_BASE_URL}/models/info`),
+ axios.get(`${UMANS_DEFAULT_BASE_URL}/models`),
+ ])
+
+ if (infoResponse.status !== "fulfilled") {
+ throw infoResponse.reason
+ }
- const infoResult = umansModelsInfoResponseSchema.safeParse(infoResponse.data)
+ const infoResult = umansModelsInfoResponseSchema.safeParse(infoResponse.value.data)
if (!infoResult.success) {
return models
}
- const pricingResult = umansPricingResponseSchema.safeParse(pricingResponse.data)
+ const pricingResult =
+ pricingResponse.status === "fulfilled"
+ ? umansPricingResponseSchema.safeParse(pricingResponse.value.data)
+ : { success: false as const }Please add a package-local regression spec for “pricing request rejects but model info still returns models.” As per coding guidelines, use package-local unit tests for pure logic and error handling.
📝 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 [infoResponse, pricingResponse] = await Promise.all([ | |
| axios.get(`${UMANS_DEFAULT_BASE_URL}/models/info`), | |
| axios.get(`${UMANS_DEFAULT_BASE_URL}/models`), | |
| ]) | |
| const infoResult = umansModelsInfoResponseSchema.safeParse(infoResponse.data) | |
| if (!infoResult.success) { | |
| return models | |
| } | |
| const pricingResult = umansPricingResponseSchema.safeParse(pricingResponse.data) | |
| const pricingById = new Map( | |
| (pricingResult.success ? pricingResult.data.data : []).map((entry) => [entry.id, entry.pricing]), | |
| ) | |
| const [infoResponse, pricingResponse] = await Promise.allSettled([ | |
| axios.get(`${UMANS_DEFAULT_BASE_URL}/models/info`), | |
| axios.get(`${UMANS_DEFAULT_BASE_URL}/models`), | |
| ]) | |
| if (infoResponse.status !== "fulfilled") { | |
| throw infoResponse.reason | |
| } | |
| const infoResult = umansModelsInfoResponseSchema.safeParse(infoResponse.value.data) | |
| if (!infoResult.success) { | |
| return models | |
| } | |
| const pricingResult = | |
| pricingResponse.status === "fulfilled" | |
| ? umansPricingResponseSchema.safeParse(pricingResponse.value.data) | |
| : { success: false as const } |
🤖 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/fetchers/umans.ts` around lines 50 - 63, The Umans fetcher
in getModels() is treating the optional /models pricing request as fatal because
Promise.all() rejects both calls when pricing fails. Update the logic in
umans.ts so the /models/info request can still succeed independently, and only
use /models to enrich inputPrice/outputPrice when it resolves and parses
successfully. Add a package-local unit regression spec around getModels()/the
Umans fetcher covering “pricing request rejects but model info still returns
models,” and verify the catalog is still returned and cached.
Source: Coding guidelines
| }: AnthropicCustomProps) => { | ||
| const { t } = useAppTranslation() | ||
|
|
||
| const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicCustomBaseUrl) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Keep the custom-base-URL toggle in sync with apiConfiguration.
Line 42 snapshots anthropicCustomBaseUrl once. If the settings object is replaced later, the checkbox state and field visibility can drift from the real config value. Derive this from the prop or resync it in an effect.
🤖 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 is only initialized once from
apiConfiguration, so it can get out of sync when the settings object changes.
Update AnthropicCustom to derive anthropicBaseUrlSelected from
apiConfiguration?.anthropicCustomBaseUrl or resync it in an effect keyed on that
prop, and make sure the checkbox/field visibility always reflects the current
configuration.
| 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 and reset anthropicCustomModelInfo from the active model id.
Line 45 always hydrates from anthropicCustomDefaultModelId, while Line 330 only looks at anthropicCustomModelId. That can persist capability/pricing data for the wrong model when a saved config already points at a different Anthropic model, or when no id is stored yet and the picker is showing its default.
Suggested fix
+ const getDefaultAnthropicCustomModelInfo = (
+ modelId = apiConfiguration.anthropicCustomModelId ?? anthropicCustomDefaultModelId,
+ ) => ({
+ ...openAiModelInfoSaneDefaults,
+ ...(anthropicModels[modelId as keyof typeof anthropicModels] || {}),
+ })
+
useEffect(() => {
if (!apiConfiguration.anthropicCustomModelInfo) {
setApiConfigurationField(
"anthropicCustomModelInfo",
- {
- ...openAiModelInfoSaneDefaults,
- ...(anthropicModels[anthropicCustomDefaultModelId] || {}),
- },
+ getDefaultAnthropicCustomModelInfo(),
false,
)
}
- }, [apiConfiguration.anthropicCustomModelInfo, setApiConfigurationField])
+ }, [
+ apiConfiguration.anthropicCustomModelId,
+ apiConfiguration.anthropicCustomModelInfo,
+ setApiConfigurationField,
+ ])
…
<Button
variant="secondary"
onClick={() =>
- setApiConfigurationField("anthropicCustomModelInfo", {
- ...openAiModelInfoSaneDefaults,
- ...(anthropicModels[
- apiConfiguration.anthropicCustomModelId as keyof typeof anthropicModels
- ] || {}),
- })
+ setApiConfigurationField(
+ "anthropicCustomModelInfo",
+ getDefaultAnthropicCustomModelInfo(),
+ )
}>Also applies to: 327-335
🤖 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 and reset anthropicCustomModelInfo from the currently active
Anthropic model id instead of always using anthropicCustomDefaultModelId. Update
the useEffect in AnthropicCustom to derive the hydrated model info from
anthropicCustomModelId (falling back to the default only when no id is set), and
make the model-picker logic around anthropicCustomModelId use the same source so
the cached capability/pricing data always matches the selected model.
| 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.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Prefer anthropicCustomModelId in the anthropic-custom branch.
Line 379 currently picks apiModelId first. Because provider settings retain fields from other providers, a stale apiModelId can win here and leave this hook returning an Anthropic Custom info object with the wrong id.
Suggested fix
default: {
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 id = apiConfiguration.anthropicCustomModelId ?? defaultModelId
const info = apiConfiguration.anthropicCustomModelInfo || undefined
return { id, info }
}
+
+ const id = apiConfiguration.apiModelId ?? defaultModelId📝 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.
| 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 } | |
| } | |
| provider satisfies "anthropic" | "anthropic-custom" | "gemini-cli" | "fake-ai" | |
| // For anthropic-custom, use custom model info if available | |
| if (provider === "anthropic-custom") { | |
| const id = apiConfiguration.anthropicCustomModelId ?? defaultModelId | |
| const info = apiConfiguration.anthropicCustomModelInfo || undefined | |
| return { id, info } | |
| } | |
| const id = apiConfiguration.apiModelId ?? defaultModelId |
🤖 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 378 -
385, The `useSelectedModel` hook is picking the wrong model id for the
`anthropic-custom` provider because it prefers `apiConfiguration.apiModelId`
before the custom field. Update the selection logic in `useSelectedModel` so the
`anthropic-custom` branch uses `apiConfiguration.anthropicCustomModelId` first
(falling back only if needed), while still returning `anthropicCustomModelInfo`
for that provider. Keep the existing provider check and adjust the `id`
resolution to avoid stale ids from other providers.
Summary
Testing
Summary by CodeRabbit
New Features
Bug Fixes