Skip to content

feat: add Umans provider#755

Open
taltas wants to merge 3 commits into
mainfrom
feat/add-umans-provider
Open

feat: add Umans provider#755
taltas wants to merge 3 commits into
mainfrom
feat/add-umans-provider

Conversation

@taltas

@taltas taltas commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add the Umans provider to provider/type registries and shared settings types
  • integrate the Umans runtime handler, provider wiring, and model fetcher/cache support
  • wire the settings UI, model selection behavior, validation, and focused provider tests

Testing

  • focused Umans provider tests were run successfully before handoff
  • pre-commit lint and push-time type checks passed during commit/push flow

Summary by CodeRabbit

  • New Features

    • Added support for a new provider option with its own settings, default model, and model picker integration.
    • Added support for a custom Anthropic provider configuration, including custom model details and provider-specific settings.
  • Bug Fixes

    • Improved model selection so the correct provider/model is chosen more reliably across settings and router model lists.
    • Updated validation to require the right API keys and model fields for the new provider options.
    • Refresh and model loading now include the new provider in aggregated results.

AlgerMusic Build Bot and others added 3 commits June 28, 2026 15:58
- 移除自定义 Headers UI 和流式传输复选框
- 基础 URL 改为可选(复选框启用)
- 模型选择器使用 anthropicModels 列表
- 自动从选中模型填充模型信息
- 校验只需要 API 密钥和模型 ID(基础 URL 可选)
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds umans as a new dynamic provider and anthropic-custom as a custom Anthropic variant. Changes span shared type schemas, a new UmansHandler (OpenAI-compatible), a model fetcher (getUmansModels), AnthropicHandler extension for custom base URL/key/model, webview router model wiring, profile validation, settings UI components, and API key validation.

Changes

Umans + anthropic-custom provider integration

Layer / File(s) Summary
Shared type contracts
packages/types/src/providers/umans.ts, packages/types/src/provider-settings.ts, packages/types/src/global-settings.ts, packages/types/src/providers/index.ts, packages/types/src/__tests__/index.test.ts
Introduces umansSchema and anthropicCustomSchema Zod objects; adds both to the discriminated union and flat shape; extends modelIdKeys, ANTHROPIC_STYLE_PROVIDERS, MODELS_BY_PROVIDER, and SECRET_STATE_KEYS; exports the new umans.ts constants module.
Umans model fetcher and cache wiring
src/api/providers/fetchers/umans.ts, src/api/providers/fetchers/modelCache.ts, src/api/providers/fetchers/__tests__/umans.spec.ts, src/api/providers/fetchers/__tests__/modelCache.spec.ts, src/shared/api.ts
Implements getUmansModels() with concurrent zod-validated fetches of model metadata and pricing endpoints; routes "umans" in fetchModelsFromProvider and adds it to the public background-refresh list.
UmansHandler API handler
src/api/providers/umans.ts, src/api/providers/index.ts, src/api/index.ts, src/api/providers/__tests__/umans.spec.ts
Adds UmansHandler wrapping the OpenAI SDK with configurable base URL/key/headers, model selection with fallbacks, streaming createMessage (text/reasoning/tool-call deltas, usage tracking), and non-streaming completePrompt.
AnthropicHandler extended for anthropic-custom
src/api/providers/anthropic.ts
Updates AnthropicHandler constructor to derive baseURL, apiKey, and auth field name from anthropicCustomBaseUrl/anthropicCustomApiKey fallbacks; updates getModel() to prioritize anthropicCustomModelId and anthropicCustomModelInfo.
Profile validation and webview router wiring
src/shared/ProfileValidator.ts, src/shared/__tests__/ProfileValidator.spec.ts, src/core/webview/webviewMessageHandler.ts, src/core/webview/__tests__/*
Adds umans and anthropic-custom cases to ProfileValidator.getModelIdFromProfile; adds "umans" to the routerModels initialization map and Promise.allSettled candidate list in webviewMessageHandler.
Settings UI components
webview-ui/src/components/settings/providers/Umans.tsx, webview-ui/src/components/settings/providers/AnthropicCustom.tsx, webview-ui/src/components/settings/providers/index.ts, webview-ui/src/components/settings/ApiOptions.tsx, webview-ui/src/components/settings/constants.ts, webview-ui/src/components/settings/ModelPicker.tsx, webview-ui/src/components/settings/utils/providerModelConfig.ts, webview-ui/src/components/settings/utils/__tests__/providerModelConfig.spec.ts
Adds Umans (API key field, refresh button, ModelPicker) and AnthropicCustom (API key, optional base URL, ModelPicker, full capability/pricing fields, reset defaults) components; registers both in constants, ApiOptions, and providerModelConfig registries.
Validation and model selection
webview-ui/src/utils/validate.ts, webview-ui/src/utils/__tests__/validate.spec.ts, webview-ui/src/components/ui/hooks/useSelectedModel.ts, webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts
Adds umans (requires umansApiKey) and anthropic-custom (requires anthropicCustomApiKey + anthropicCustomModelId) validation branches; adds "umans" and "anthropic-custom" cases to useSelectedModel model resolution.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Zoo-Code-Org/Zoo-Code#319: Adds a new dynamic provider (opencode-go) through the same SECRET_STATE_KEYS, modelCache.ts, and webviewMessageHandler.ts plumbing used here.
  • Zoo-Code-Org/Zoo-Code#344: Extends provider-settings.ts and fetchers/modelCache.ts with a new provider case (zoo-gateway), directly analogous to the umans additions here.
  • Zoo-Code-Org/Zoo-Code#608: Adds "max" reasoning-effort tier to the shared model schema that UmansHandler (for umans-glm-5.2) depends on.

Suggested labels

awaiting-review

Suggested reviewers

  • JamesRobert20
  • navedmerchant
  • hannesrudolph
  • edelauna

Poem

🐇 A new path through the warren grows,
Where Umans models join the flow.
With custom Anthropic dressed just right,
Each API key secured from sight.
The bunny hops—new providers appear,
Making the model menu clearer! 🌟

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers summary and testing, but it omits the required issue link and most template sections. Add the linked issue, detailed implementation description, test procedure, checklist, screenshots/docs notes, and contact info per the template.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely names the main change: adding the Umans provider.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add-umans-provider

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Add anthropicCustomApiKey to SECRET_STATE_KEYS.

packages/types/src/provider-settings.ts Lines 271-278 introduce anthropicCustomApiKey, but this list still only registers umansApiKey. 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 win

Add a local unit test for anthropic-custom model extraction.

This branch is now part of the allow-list gate, but src/shared/__tests__/ProfileValidator.spec.ts only adds coverage for umans. A regression here would reject otherwise allowed anthropic-custom profiles 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 win

Please add the matching anthropic-custom validation 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 win

Add a focused test for the new anthropic-custom hook path.

useSelectedModel now has provider-specific fallback logic for anthropic-custom, but this file only exercises Umans. A case that proves anthropicCustomModelId wins over any stale apiModelId and returns anthropicCustomModelInfo would lock down the new behavior. As per coding guidelines, prefer local webview-ui tests 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

📥 Commits

Reviewing files that changed from the base of the PR and between 83fc6bb and 6b1c9b2.

📒 Files selected for processing (33)
  • packages/types/src/__tests__/index.test.ts
  • packages/types/src/global-settings.ts
  • packages/types/src/provider-settings.ts
  • packages/types/src/providers/index.ts
  • packages/types/src/providers/umans.ts
  • src/api/index.ts
  • src/api/providers/__tests__/umans.spec.ts
  • src/api/providers/anthropic.ts
  • src/api/providers/fetchers/__tests__/modelCache.spec.ts
  • src/api/providers/fetchers/__tests__/umans.spec.ts
  • src/api/providers/fetchers/modelCache.ts
  • src/api/providers/fetchers/umans.ts
  • src/api/providers/index.ts
  • src/api/providers/umans.ts
  • src/core/webview/__tests__/ClineProvider.spec.ts
  • src/core/webview/__tests__/webviewMessageHandler.routerModels.spec.ts
  • src/core/webview/__tests__/webviewMessageHandler.spec.ts
  • src/core/webview/webviewMessageHandler.ts
  • src/shared/ProfileValidator.ts
  • src/shared/__tests__/ProfileValidator.spec.ts
  • src/shared/api.ts
  • webview-ui/src/components/settings/ApiOptions.tsx
  • webview-ui/src/components/settings/ModelPicker.tsx
  • webview-ui/src/components/settings/constants.ts
  • webview-ui/src/components/settings/providers/AnthropicCustom.tsx
  • webview-ui/src/components/settings/providers/Umans.tsx
  • webview-ui/src/components/settings/providers/index.ts
  • webview-ui/src/components/settings/utils/__tests__/providerModelConfig.spec.ts
  • webview-ui/src/components/settings/utils/providerModelConfig.ts
  • webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts
  • webview-ui/src/components/ui/hooks/useSelectedModel.ts
  • webview-ui/src/utils/__tests__/validate.spec.ts
  • webview-ui/src/utils/validate.ts

Comment on lines +42 to +52
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,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.apiKey

Also 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.

Comment on lines +366 to +368
let info: ModelInfo = customModelId
? this.options.anthropicCustomModelInfo || openAiModelInfoSaneDefaults
: anthropicModels[id as AnthropicModelId]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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 packages

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 50378


🏁 Script executed:

ast-grep outline src/api/providers/anthropic.ts --view expanded

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 553


🏁 Script executed:

rg -n -C 6 'getModelParams|maxTokens\s*[:=]|max_tokens|anthropicCustomModelInfo|openAiModelInfoSaneDefaults' src packages/types

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 50378


🏁 Script executed:

sed -n '340,470p' src/api/providers/anthropic.ts

Repository: 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.ts

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 44934


🏁 Script executed:

ast-grep outline src/api/transform/model-params.ts --view expanded

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 625


🏁 Script executed:

sed -n '1,260p' src/api/transform/model-params.ts

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 6545


🏁 Script executed:

ast-grep outline src/shared/api.ts --view expanded

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 483


🏁 Script executed:

rg -n -C 8 'function getModelMaxOutputTokens|getModelMaxOutputTokens|maxTokens === -1|modelMaxTokens' src/shared packages/types

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 30796


🏁 Script executed:

sed -n '120,180p' src/shared/api.ts

Repository: 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.

Comment on lines +50 to +63
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]),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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.

Suggested change
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.

Comment on lines +44 to +55
useEffect(() => {
if (!apiConfiguration.anthropicCustomModelInfo) {
setApiConfigurationField(
"anthropicCustomModelInfo",
{
...openAiModelInfoSaneDefaults,
...(anthropicModels[anthropicCustomDefaultModelId] || {}),
},
false,
)
}
}, [apiConfiguration.anthropicCustomModelInfo, setApiConfigurationField])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ 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.

Comment on lines +378 to +385
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 }
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ 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.

Suggested change
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant