Skip to content

feat(friendli): fetch model list dynamically from /v1/models - #1219

Open
Lee-Si-Yoon wants to merge 8 commits into
Zoo-Code-Org:mainfrom
Lee-Si-Yoon:feat/friendli-dynamic-models
Open

feat(friendli): fetch model list dynamically from /v1/models#1219
Lee-Si-Yoon wants to merge 8 commits into
Zoo-Code-Org:mainfrom
Lee-Si-Yoon:feat/friendli-dynamic-models

Conversation

@Lee-Si-Yoon

@Lee-Si-Yoon Lee-Si-Yoon commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Related GitHub Issue

Closes: #

Description

The Friendli /v1/models endpoint has been updated and is now the source of truth for the available model list. This PR reduces the static seed to only the default model (GLM-5.2) and lets all other models come from the live /v1/models fetcher at runtime.

Static seed reduction:

  • FriendliModelId narrowed to "zai-org/GLM-5.2" only
  • friendliModels reduced to a single entry with corrected values from the live API (maxTokens: 1_048_576, contextWindow: 1_048_576)
  • Removed static entries for GLM-5.1, DeepSeek-V3.2, MiniMax-M2.5 — these are now served exclusively via the dynamic fetcher

Why only GLM-5.2 in the seed: the static map is a cold-start fallback for when the dynamic fetch hasn't completed or has failed. Only the default model needs to be available in that window; all other models are populated from /v1/models within seconds of handler construction.

Test updates:

  • Removed it.each static model assertions for GLM-5.1, DeepSeek-V3.2, MiniMax-M2.5
  • DeepSeek-V3.2 binary reasoning test now provides a dynamic model mock with vi.waitFor
  • useSelectedModel test uses routerModels.friendli for dynamic-only models
  • GLM-5.2 clamp test updated (maxTokens now equals contextWindow, so clamp binds at 209_716)
  • Removed GLM-5.1 clamp tests (no longer static)

Test Procedure

  • pnpm vitest run api/providers/__tests__/friendli.spec.ts api/providers/fetchers/__tests__/friendli.spec.ts — 44 tests pass
  • pnpm vitest run src/components/ui/hooks/__tests__/useSelectedModel.spec.ts — 42 tests pass
  • pnpm vitest run src/components/settings/providers/__tests__/Friendli.spec.tsx — 3 tests pass
  • pnpm vitest run core/webview/__tests__/ClineProvider.spec.ts core/webview/__tests__/webviewMessageHandler.spec.ts — 184 tests pass
  • pnpm check-types — pass
  • pnpm build — pass
  • pnpm lint — pass

Pre-Submission Checklist

  • Issue Linked: This PR is linked to an approved GitHub Issue (see "Related GitHub Issue" above).
  • Scope: My changes are focused on the linked issue (one major feature/fix per PR).
  • Self-Review: I have performed a thorough self-review of my code.
  • Testing: New and/or updated tests have been added to cover my changes (if applicable).
  • Visual Snapshot (UI changes only): N/A — no UI rendering changes.
  • Documentation Impact: No documentation updates required.
  • Contribution Guidelines: I have read and agree to the Contributor Guidelines.

Documentation Updates

  • No documentation updates are required.

Additional Notes

Supersedes #1152 (closed) and #1028 (closed). The /v1/models response was updated since #1152 was opened; this PR follows the latest response.

Summary by CodeRabbit

  • New Features

    • Added dynamic Friendli model discovery with metadata such as pricing, image support, reasoning capabilities, deprecation status, and token limits.
    • Added Friendli models to router results and settings-based model selection.
    • Added a Friendli model picker with dynamically loaded models and static fallback options.
  • Bug Fixes

    • Improved fallback behavior when Friendli model loading is empty or unsuccessful.
    • Preserved selected models while dynamic model data loads.
    • Improved handling of dynamic model reasoning and request token limits.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: df772ac1-3da4-4611-9505-fa2881dff896

📥 Commits

Reviewing files that changed from the base of the PR and between 70cfac6 and 16a4270.

📒 Files selected for processing (2)
  • src/api/providers/fetchers/__tests__/friendli.spec.ts
  • webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts

📝 Walkthrough

Walkthrough

Friendli now discovers model metadata through the Friendli API, caches it, and uses it for provider requests, routing, and settings model selection. Static metadata remains as a fallback. Reasoning and model-fetching tests cover the new behavior.

Changes

Friendli dynamic model discovery

Layer / File(s) Summary
Provider contracts
packages/types/src/providers/friendli.ts, packages/types/src/provider-settings.ts, packages/types/src/__tests__/provider-identifiers.test.ts, src/shared/api.ts
Friendli is classified as a dynamic provider. Its static fallback contains only GLM-5.2.
Model fetching and caching
src/api/providers/fetchers/friendli.ts, src/api/providers/fetchers/modelCache.ts, src/api/providers/fetchers/__tests__/friendli.spec.ts
Friendli API responses are validated, filtered, parsed into ModelInfo, and loaded through the shared model cache.
Provider runtime and routing
src/api/providers/friendli.ts, src/api/providers/__tests__/friendli.spec.ts, src/core/webview/webviewMessageHandler.ts, src/core/webview/__tests__/*
Friendli requests use dynamic metadata with static fallback. Binary and controllable reasoning modes are handled. Router aggregation includes Friendli.
Settings model selection
webview-ui/src/components/settings/providers/Friendli.tsx, webview-ui/src/components/settings/ApiOptions.tsx, webview-ui/src/components/ui/hooks/useSelectedModel.ts, webview-ui/src/components/settings/utils/providerModelConfig.ts, webview-ui/src/components/settings/providers/__tests__/Friendli.spec.tsx, webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts, webview-ui/src/utils/__tests__/validate.spec.ts
Friendli settings use dynamic router models with static fallback, validation props, and provider-specific model selection.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SettingsUI
  participant WebviewMessageHandler
  participant FriendliProvider
  participant modelCache
  participant FriendliAPI
  SettingsUI->>WebviewMessageHandler: request router models
  WebviewMessageHandler->>FriendliProvider: request Friendli models
  FriendliProvider->>modelCache: load dynamic metadata
  modelCache->>FriendliAPI: request model list
  FriendliAPI-->>modelCache: model response
  modelCache-->>FriendliProvider: ModelInfo records
  FriendliProvider-->>WebviewMessageHandler: Friendli models
  WebviewMessageHandler-->>SettingsUI: router model data
Loading

Possibly related PRs

Suggested reviewers: edelauna

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers implementation details, tests, checklist items, and documentation, but it omits the required approved GitHub issue link. Add the approved GitHub issue number after “Closes:” and mark the Issue Linked checklist item as complete.
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% 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 states that Friendli models are fetched dynamically from the /v1/models endpoint, which is the main change.
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 unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/api/providers/fetchers/__tests__/friendli.spec.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.


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

🧹 Nitpick comments (1)
src/api/providers/__tests__/friendli.spec.ts (1)

422-423: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove private-state double assertions from dynamic-load tests.

Wait for observable getModel() results after resolving or rejecting a controlled mockGetModels promise. Do not inspect dynamicModelsLoaded through as unknown as Record<string, unknown>.

  • src/api/providers/__tests__/friendli.spec.ts#L422-L423: wait until the dynamic model is selected.
  • src/api/providers/__tests__/friendli.spec.ts#L525-L526: wait until the unknown model falls back to the default.
  • src/api/providers/__tests__/friendli.spec.ts#L551-L552: wait until dynamic metadata appears in getModel().
  • src/api/providers/__tests__/friendli.spec.ts#L573-L575: configure an unknown requested ID, then wait until rejection causes fallback.

As per coding guidelines, “Use double assertions only as a last resort and explain them with a comment.”

🤖 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/__tests__/friendli.spec.ts` around lines 422 - 423, Replace
the private dynamicModelsLoaded double assertions in
src/api/providers/__tests__/friendli.spec.ts at lines 422-423, 525-526, 551-552,
and 573-575 with waits for observable getModel() outcomes: confirm the dynamic
model is selected, the unknown model falls back to the default, dynamic metadata
appears, and rejection after configuring an unknown requested ID causes
fallback. Do not inspect handler private state or use the double assertion.

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/fetchers/friendli.ts`:
- Around line 235-237: Update the supportsMaxTokens assignment in the model
metadata construction to depend only on model.max_completion_tokens, removing
the model.reasoning requirement so non-reasoning chat models expose max-token
controls.
- Around line 106-122: Update the reasoning-effort handling in the Friendli
fetcher so that when the API provides effort values but filtering leaves no
known values, it returns binary reasoning support instead of an empty effort
list that later defaults to unsupported "high". Preserve the existing
filtered-effort behavior when known values remain, and update the Friendli
regression test to expect binary support for this case.

In `@src/api/providers/friendli.ts`:
- Around line 136-144: Update the dynamic-only branch in the provider
model-selection flow, near thisProviderModelId handling, so it does not assign
defaultProviderModelId metadata to the requested model. Ensure createStream does
not derive max_tokens or reasoning parameters from fallback metadata: wait for
the initial dynamic model load or omit those parameters until the selected
model’s metadata is available. Add a regression test covering a request made
while the dynamic fetch is pending.

In `@webview-ui/src/components/settings/ApiOptions.tsx`:
- Around line 701-704: Update the Friendli model binding around routerModels and
requestRouterModels so an absent or empty Friendli record falls back to the
existing static friendliModels list, while populated records remain active. Add
focused UI tests covering missing, empty, and populated Friendli router models.

In `@webview-ui/src/components/settings/providers/__tests__/Friendli.spec.tsx`:
- Around line 30-32: Update the ModelPicker mock and the Friendli test to
capture its props, then assert the dynamic models, static fallback,
defaultModelId, and errorMessage bindings. Add a model-selection interaction
assertion verifying the callback writes through setApiConfigurationField,
covering both UI binding and save behavior.

In `@webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts`:
- Around line 1082-1091: Replace the as any cast in the mockUseRouterModels
fixture with a fully typed React Query result matching useRouterModels,
including a complete RouterModels provider map and all required query-result
fields. Use the hook’s return type or a typed fixture helper so TypeScript
validates both the provider data and query state.

In `@webview-ui/src/components/ui/hooks/useSelectedModel.ts`:
- Around line 366-372: Preserve the saved Friendli model selection while router
data is loading by updating the Friendli branch in useSelectedModel to avoid
returning a default ID that ApiOptions can persist over
apiConfiguration.apiModelId; retain the saved ID until dynamic data is
available. In webview-ui/src/components/ui/hooks/Friendli.tsx lines 76-76, use
friendliModels whenever routerModels.friendli is absent or empty so ModelPicker
receives the static fallback.

---

Nitpick comments:
In `@src/api/providers/__tests__/friendli.spec.ts`:
- Around line 422-423: Replace the private dynamicModelsLoaded double assertions
in src/api/providers/__tests__/friendli.spec.ts at lines 422-423, 525-526,
551-552, and 573-575 with waits for observable getModel() outcomes: confirm the
dynamic model is selected, the unknown model falls back to the default, dynamic
metadata appears, and rejection after configuring an unknown requested ID causes
fallback. Do not inspect handler private state or use the double assertion.
🪄 Autofix

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: 7e06fa35-05a8-4f1d-8a96-78d71bd14073

📥 Commits

Reviewing files that changed from the base of the PR and between abaf732 and 3129853.

📒 Files selected for processing (20)
  • packages/types/src/__tests__/provider-identifiers.test.ts
  • packages/types/src/provider-settings.ts
  • packages/types/src/providers/friendli.ts
  • src/api/providers/__tests__/friendli.spec.ts
  • src/api/providers/fetchers/__tests__/friendli.spec.ts
  • src/api/providers/fetchers/friendli.ts
  • src/api/providers/fetchers/modelCache.ts
  • src/api/providers/friendli.ts
  • src/core/webview/__tests__/ClineProvider.spec.ts
  • src/core/webview/__tests__/webviewMessageHandler.spec.ts
  • src/core/webview/webviewMessageHandler.ts
  • src/shared/api.ts
  • webview-ui/src/components/settings/ApiOptions.tsx
  • webview-ui/src/components/settings/constants.ts
  • webview-ui/src/components/settings/providers/Friendli.tsx
  • webview-ui/src/components/settings/providers/__tests__/Friendli.spec.tsx
  • 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
💤 Files with no reviewable changes (1)
  • webview-ui/src/components/settings/constants.ts

Comment thread src/api/providers/fetchers/friendli.ts Outdated
Comment thread src/api/providers/fetchers/friendli.ts Outdated
Comment thread src/api/providers/friendli.ts Outdated
Comment on lines +701 to +704
routerModels={routerModels}
organizationAllowList={organizationAllowList}
modelValidationError={modelValidationError}
simplifySettings={fromWelcomeView}

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

Keep the static fallback when Friendli returns no models.

The new routerModels prop activates routerModels?.["friendli"] ?? friendliModels in Friendli. requestRouterModels returns {} for an empty or failed Friendli fetch. Since {} is non-nullish, the picker receives no models instead of the static GLM-5.2 fallback.

Normalize an empty Friendli record to undefined, or make Friendli fall back when the record has zero keys. Add a UI test for missing, empty, and populated Friendli router models.

As per coding guidelines, add focused tests for UI binding behavior.

🤖 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/ApiOptions.tsx` around lines 701 - 704,
Update the Friendli model binding around routerModels and requestRouterModels so
an absent or empty Friendli record falls back to the existing static
friendliModels list, while populated records remain active. Add focused UI tests
covering missing, empty, and populated Friendli router models.

Source: Coding guidelines

Comment thread webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts Outdated
Comment thread webview-ui/src/components/ui/hooks/useSelectedModel.ts Outdated

@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: 1

🤖 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 `@webview-ui/src/components/settings/providers/__tests__/Friendli.spec.tsx`:
- Around line 30-35: Update the ModelPicker test double in the vi.mock factory
to replace props: any and the untyped mockModelPickerProps with
React.ComponentProps<typeof ModelPicker> or the exported ModelPickerProps type,
keeping the mock aligned with the component contract.
🪄 Autofix

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: 67e8c45e-7201-4938-818c-a316235e8393

📥 Commits

Reviewing files that changed from the base of the PR and between 3129853 and 081d9f6.

📒 Files selected for processing (8)
  • src/api/providers/__tests__/friendli.spec.ts
  • src/api/providers/fetchers/__tests__/friendli.spec.ts
  • src/api/providers/fetchers/friendli.ts
  • src/api/providers/friendli.ts
  • webview-ui/src/components/settings/providers/Friendli.tsx
  • webview-ui/src/components/settings/providers/__tests__/Friendli.spec.tsx
  • webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts
  • webview-ui/src/components/ui/hooks/useSelectedModel.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • webview-ui/src/components/ui/hooks/useSelectedModel.ts
  • webview-ui/src/components/ui/hooks/tests/useSelectedModel.spec.ts
  • src/api/providers/fetchers/tests/friendli.spec.ts
  • webview-ui/src/components/settings/providers/Friendli.tsx
  • src/api/providers/fetchers/friendli.ts
  • src/api/providers/tests/friendli.spec.ts

Comment on lines +30 to +35
let mockModelPickerProps: Record<string, unknown> = {}

vi.mock("../../ModelPicker", () => ({
ModelPicker: (props: any) => {
mockModelPickerProps = props
return <div data-testid="friendli-model-picker-mock" />

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- candidate test structure ---'
ast-grep outline webview-ui/src/components/settings/providers/__tests__/Friendli.spec.tsx
printf '%s\n' '--- relevant test lines ---'
cat -n webview-ui/src/components/settings/providers/__tests__/Friendli.spec.tsx | sed -n '1,180p'
printf '%s\n' '--- ModelPicker declarations and usages ---'
rg -n --glob '*.{ts,tsx}' 'interface .*ModelPicker|type .*ModelPicker|function ModelPicker|const ModelPicker|ModelPicker\(' webview-ui/src | head -120
printf '%s\n' '--- package and lint scripts ---'
fd -i 'package.json' -a . | xargs -r rg -n '"(test|lint|typecheck|vitest|eslint)[^"]*"'

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 11691


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- ModelPicker props and implementation ---'
cat -n webview-ui/src/components/settings/ModelPicker.tsx | sed -n '35,125p'
printf '%s\n' '--- Friendli implementation ---'
cat -n webview-ui/src/components/settings/providers/Friendli.tsx | sed -n '1,180p'
printf '%s\n' '--- TypeScript config and available dependencies ---'
for f in webview-ui/tsconfig.json webview-ui/tsconfig.*.json webview-ui/package.json; do
  if [ -f "$f" ]; then
    echo "--- $f"
    cat -n "$f" | sed -n '1,180p'
  fi
done
printf '%s\n' '--- all ModelPicker mock prop typing patterns ---'
rg -n -C 4 'mockModelPickerProps|vi\.mock\(".*ModelPicker|ModelPicker:.*props' webview-ui/src/components/settings/providers webview-ui/src/components/settings --glob '*.{spec,test}.{ts,tsx}'

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 34202


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
import re

model_picker = Path("webview-ui/src/components/settings/ModelPicker.tsx").read_text()
friendli_test = Path("webview-ui/src/components/settings/providers/__tests__/Friendli.spec.tsx").read_text()

props_decl = re.search(r"(?m)^interface\s+ModelPickerProps\s*\{", model_picker)
props_exported = bool(re.search(r"(?m)^export\s+interface\s+ModelPickerProps\s*\{", model_picker))
mock_any = bool(re.search(r"ModelPicker:\s*\(props:\s*any\)", friendli_test))
print(f"ModelPickerProps_declared={bool(props_decl)}")
print(f"ModelPickerProps_exported={props_exported}")
print(f"Friendli_ModelPicker_mock_uses_any={mock_any}")
print("Friendli_ModelPicker_mock_capture_type=Record<string, unknown>")
PY

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 325


Type the ModelPicker test double.

Use React.ComponentProps<typeof ModelPicker> or export and import ModelPickerProps instead of props: any. This keeps the mock aligned with the component contract.

🤖 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/__tests__/Friendli.spec.tsx`
around lines 30 - 35, Update the ModelPicker test double in the vi.mock factory
to replace props: any and the untyped mockModelPickerProps with
React.ComponentProps<typeof ModelPicker> or the exported ModelPickerProps type,
keeping the mock aligned with the component contract.

Source: Coding guidelines

Convert Friendli from a static provider (4 hardcoded models) to a dynamic
provider that fetches the live model list from the public
https://api.friendli.ai/serverless/v1/models endpoint at runtime.

- Add getFriendliModels() fetcher with zod schema validation
- Wire friendli into modelCache, webviewMessageHandler, and dynamicProviders
- FriendliHandler loads dynamic models in constructor, falls back to static
  friendliModels for cold-start and API lag
- UI model picker uses routerModels.friendli instead of static list
- Add fetcher spec (14 tests) and update Friendli.spec.tsx with ModelPicker mock
- fetcher: return empty list when safeParse fails instead of consuming
  unvalidated response data
- fetcher: add 10s timeout to /models axios request
- fetcher: preserve API-provided reasoning effort values verbatim,
  dropping only "default" and unknown values like "ultracode" instead of
  merging hardcoded extra efforts
- handler: track dynamicModelsLoaded and preserve a dynamic-only
  requestedId during the initial load window so the first request after
  construction doesn't silently fall back to the default model
- shared/api.ts: use object type instead of eslint-disable suppression
- Friendli.tsx: fall back to static friendliModels when routerModels is
  unavailable so the picker always has selectable models
Add friendli to routerModels expectations and mock sequences — it was
added to the handler's provider list but the existing tests weren't
updated, causing the mock call order to shift and expectations to miss.
Same issue as webviewMessageHandler spec — routerModels expectations
and mock sequences needed friendli added to match the handler's provider
list.
…oading

- fetcher: test ultracode/unknown effort value dropping + de-duplication
- handler: mock getModels, test dynamicModelsLoaded branches — pending
  load preserves dynamic-only id, completed load falls back to default,
  dynamic info used when available, rejection sets loaded flag
…enum

Friendli API returns reasoning: true for all models, but only GLM-5.2
has a discrete effort enum (["high","max"]). Other models (DeepSeek-V3.2,
MiniMax-M2.5, GLM-5.1, gemma, K-EXAONE) only support on/off thinking
toggle via chat_template_kwargs.enable_thinking.

Previously these models got supportsReasoningEffort: true (boolean),
which made the UI show a full effort dropdown (low/medium/high/...) even
though the API ignores reasoning_effort for them. Now they get
supportsReasoningBinary: true, which shows a simple on/off checkbox.

Also fixes max tokens: all Friendli reasoning models with
max_completion_tokens now get supportsMaxTokens: true (the fetcher
already did this, but the static fallback also needs it — it already
has it, so dynamic + static are now consistent).

Handler updated to send enable_thinking + parse_reasoning for binary
reasoning models when reasoning is enabled, and nothing when disabled.
The /v1/models endpoint has been updated. Static seed now only contains
GLM-5.2 (default model) with corrected maxTokens (1_048_576) and context
window (1_048_576) matching the live API. All other models (GLM-5.1,
DeepSeek-V3.2, MiniMax-M2.5) are removed from the static seed and are
served exclusively from the dynamic /v1/models fetcher at runtime.

- packages/types: FriendliModelId narrowed to "zai-org/GLM-5.2";
  friendliModels reduced to single entry with live API values
- src/api/providers/friendli.ts: updated comment about binary reasoning
  models to reference the live /v1/models list
- Tests updated: removed static model assertions for GLM-5.1/DeepSeek-V3.2/
  MiniMax-M2.5; DeepSeek-V3.2 binary reasoning test now provides a dynamic
  model mock; useSelectedModel test uses routerModels for dynamic models;
  clamp tests updated for new GLM-5.2 maxTokens
@Lee-Si-Yoon
Lee-Si-Yoon force-pushed the feat/friendli-dynamic-models branch from 081d9f6 to 70cfac6 Compare August 11, 2026 04:14
@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Aug 11, 2026
@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

7 actionable comments + 1 nitpick resolved:

1. (Major) fetchers/friendli.ts: empty effort array now falls back to
   binary reasoning instead of returning [] that defaults to unsupported
   reasoning_effort="high"
2. (Minor) fetchers/friendli.ts: supportsMaxTokens no longer requires
   model.reasoning — all chat models with max_completion_tokens get the
   max-token slider
3. (Major) friendli.ts: dynamic-loading fallback now uses
   openAiModelInfoSaneDefaults instead of GLM-5.2-specific metadata,
   preventing wrong reasoning params and max_tokens during cold start;
   createStream and completePrompt call getModel() once and pass the
   result to buildFriendliReasoningParams
4. (Major) Friendli.tsx: empty {} routerModels.friendli now falls back
   to static friendliModels (checks Object.keys length, not truthiness)
5. (Minor) Friendli.spec.tsx: ModelPicker mock now captures props and
   asserts bindings (defaultModelId, models, errorMessage)
6. (Minor) useSelectedModel.spec.ts: replaced `as any` with typed
   `as unknown as ReturnType<typeof useRouterModels>` cast
7. (Major) useSelectedModel.ts: Friendli branch preserves saved model
   selection during dynamic catalog loading instead of substituting
   the default ID
8. (Nitpick) friendli.spec.ts: removed all dynamicModelsLoaded double
   assertions — tests now wait for observable getModel() outcomes
@Lee-Si-Yoon
Lee-Si-Yoon force-pushed the feat/friendli-dynamic-models branch from 70cfac6 to 16a4270 Compare August 11, 2026 05:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-review PR changes are ready and waiting for maintainer re-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant