Skip to content

fix: /v1/models 返回 modelRedirects key,修复模型调度误判 (#786)#787

Open
tesgth032 wants to merge 3 commits intoding113:devfrom
tesgth032:fix/issue-786-model-redirect-keys
Open

fix: /v1/models 返回 modelRedirects key,修复模型调度误判 (#786)#787
tesgth032 wants to merge 3 commits intoding113:devfrom
tesgth032:fix/issue-786-model-redirect-keys

Conversation

@tesgth032
Copy link
Contributor

@tesgth032 tesgth032 commented Feb 14, 2026

Summary

Fixes model scheduling misjudgment when provider uses modelRedirects with allowedModels whitelist, and exposes modelRedirects keys in /v1/models endpoint.

Fixes #786

Problem

When a Claude provider is configured with:

  • allowedModels: ["claude-opus-4-6-think"] (only redirect target)
  • modelRedirects: { "claude-opus-4-6": "claude-opus-4-6-think" } (redirect mapping)

The provider selection logic incorrectly filters out this provider when users request claude-opus-4-6, because:

  1. The scheduling filter checks if claude-opus-4-6 is in allowedModels - it's not
  2. It doesn't consider that modelRedirects makes this model requestable as an "entry alias"

Additionally, /v1/models endpoint doesn't expose the redirect source model names (keys), causing clients/frontend to incorrectly judge entry models as unavailable.

Solution

  1. Extract unified model support check: Create src/app/v1/_lib/proxy/provider-supports-model.ts to centralize the logic
  2. Fix scheduling logic: For Claude providers, if modelRedirects[requestedModel] exists, treat it as supported even if not in allowedModels
  3. Fix /v1/models: Merge modelRedirects keys into provider model list (deduplicated, using same filtering rules to avoid exposing claude-* on non-Claude providers)

Changes

Core Changes

  • src/app/v1/_lib/proxy/provider-supports-model.ts (+60 lines): New module with unified providerSupportsModel() function
  • src/app/v1/_lib/proxy/provider-selector.ts (-52/+1 lines): Extract model support logic to new module
  • src/app/v1/_lib/models/available-models.ts (+47/-18 lines): Include modelRedirects keys in model list

Supporting Changes

  • biome.json: Update schema to 2.3.15 (match current CLI version)

Tests

  • tests/unit/proxy/provider-selector-model-redirect.test.ts (+70 lines): Test redirect key as supported with whitelist
  • tests/unit/models/available-models-include-redirect-keys.test.ts (+120 lines): Test /v1/models includes redirect keys

Testing

  • bun run build - Production build
  • bun run lint / bun run lint:fix - Biome check
  • bun run typecheck - TypeScript check
  • bun run test - Vitest tests

Related


Background (中文)

issue #786:当 Claude provider 配置了 modelRedirects(例如把客户端请求的 claude-opus-4-6 重定向到上游实际可用的 claude-opus-4-6-think),但该 provider 的 allowedModels 只包含重定向后的模型时,调度筛选会误判为"不支持模型",从而把该 provider 过滤掉。

另外,/v1/models 目前不会暴露 modelRedirects 的 key(映射前模型名)。这会导致依赖模型列表的客户端/前端误判入口模型不可用。

变更 (中文)

  • 抽离并统一模型支持判断:新增 src/app/v1/_lib/proxy/provider-supports-model.ts
  • Fix 重定向的模型(可供用户请求的模型名),在"决策链"中,未被识别为可用模型 #786:Claude 模型请求时,若存在 modelRedirects[requestedModel],即使 requestedModel 不在 allowedModels 中也视为支持(入口别名语义)
  • /v1/models:在 provider 模型列表中合并 modelRedirects 的 key(去重,并复用同一规则过滤,避免在非 Claude provider 暴露 claude-* key)
  • 更新 biome.json$schema 到 2.3.15(与当前 CLI 版本匹配)

测试 (中文)

  • bun run build
  • bun run lint / bun run lint:fix
  • bun run typecheck
  • bun run test

Description enhanced by Claude AI

Greptile Overview

Greptile Summary

Fixes model scheduling misjudgment when providers use modelRedirects with allowedModels whitelist, and exposes redirect keys in /v1/models endpoint.

Key Changes:

  • Extracted unified model support logic into provider-supports-model.ts module for better code reuse
  • Fixed scheduling filter to treat modelRedirects keys as supported entry models, even when only redirect targets are in allowedModels
  • Enhanced /v1/models to include redirect source model names (keys), filtered by same providerSupportsModel logic to maintain consistency
  • Added comprehensive test coverage for both provider selection and model listing scenarios

Impact:
Resolves issue #786 where Claude providers configured with modelRedirects: { "claude-opus-4-6": "claude-opus-4-6-think" } and allowedModels: ["claude-opus-4-6-think"] were incorrectly filtered out when users requested claude-opus-4-6. Both the scheduling logic and model listing endpoint now correctly recognize redirect keys as valid entry points.

Confidence Score: 5/5

  • Safe to merge with high confidence - clean refactoring with comprehensive test coverage
  • The implementation is well-structured with proper separation of concerns, consistent logic across provider selection and model listing, and thorough test coverage for the fix. The refactoring extracts duplicate code into a reusable module while maintaining backward compatibility.
  • No files require special attention

Important Files Changed

Filename Overview
src/app/v1/_lib/proxy/provider-supports-model.ts New module extracting unified model support logic with proper handling of modelRedirects
src/app/v1/_lib/proxy/provider-selector.ts Refactored to import providerSupportsModel from dedicated module, removing duplicate logic
src/app/v1/_lib/models/available-models.ts Enhanced to include modelRedirects keys in response, properly filtered by providerSupportsModel

Flowchart

flowchart TD
    A[Client requests claude-opus-4-6] --> B[Provider Selection]
    B --> C[providerSupportsModel check]
    C --> D{Check explicitlyDeclared}
    D -->|modelRedirects key exists| E[Provider matches]
    D -->|allowedModels includes requested| E
    D -->|Not explicitly declared| F{Check implicit rules}
    F -->|Claude model on Claude provider| G{allowedModels set?}
    G -->|No or empty| E
    G -->|Yes but not in list| H[Provider filtered out]
    
    I[v1 models endpoint] --> J[fetchModelsFromProvider]
    J --> K[Get base models from allowedModels or upstream]
    K --> L{Has modelRedirects?}
    L -->|Yes| M[Add redirect keys to model list]
    M --> N{providerSupportsModel for each key}
    N -->|Supported| O[Include redirect key in response]
    N -->|Not supported| P[Skip redirect key]
    L -->|No| Q[Return base models]
    O --> Q
    P --> Q
Loading

Last reviewed commit: d1d4bc4

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @tesgth032, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求旨在优化模型调度和模型列表暴露机制,解决了一个关键问题:当使用模型重定向时,调度器可能错误地将提供商标记为不支持模型。通过引入统一的模型支持判断逻辑,并确保 /v1/models 接口能够正确地包含重定向的原始模型名称,此更改显著提升了系统的鲁棒性和用户体验。同时,还进行了配置文件的常规更新。

Highlights

  • 统一模型支持判断逻辑: 将模型支持判断逻辑抽离并统一到新的 provider-supports-model.ts 文件中,提高了代码的可维护性和复用性。
  • 修复模型调度误判 (重定向的模型(可供用户请求的模型名),在"决策链"中,未被识别为可用模型 #786): 解决了当 Claude provider 配置了 modelRedirectsallowedModels 中不包含原始请求模型时,调度器会错误地过滤掉该 provider 的问题。现在,modelRedirects 的键(映射前模型名)也被视为支持的模型。
  • /v1/models 接口增强: /v1/models 接口现在会在返回的 provider 模型列表中合并 modelRedirects 的键,确保客户端/前端能够正确识别可用的入口模型,同时避免在非 Claude provider 中暴露 claude-* 键。
  • Biome 配置更新: 更新了 biome.json 中的 $schema 版本至 2.3.15,以匹配当前的 Biome CLI 版本。
  • 新增及更新单元测试: 增加了新的单元测试文件,并更新了现有测试,以验证 modelRedirects 键在模型列表和调度逻辑中的正确行为。
Changelog
  • biome.json
    • 更新了 Biome 配置文件的 $schema 版本至 2.3.15。
  • src/app/v1/_lib/models/available-models.ts
    • 引入了新的 providerSupportsModel 工具函数。
    • 修改了 fetchModelsFromProvider 函数,使其在获取模型列表时,能够合并并暴露 modelRedirects 中定义的原始模型键,同时进行去重和类型过滤。
  • src/app/v1/_lib/proxy/provider-selector.ts
    • 移除了原有的 providerSupportsModel 函数,并从新的专用文件中导入了该函数。
  • src/app/v1/_lib/proxy/provider-supports-model.ts
    • 新增了此文件,用于集中管理 providerSupportsModel 函数。
    • 重构了模型支持判断逻辑,明确处理了 modelRedirects 场景,确保即使原始请求模型不在 allowedModels 中,只要存在重定向映射,Claude provider 也能被视为支持。
  • tests/unit/models/available-models-include-redirect-keys.test.ts
    • 新增了单元测试文件,验证 /v1/models 接口在 Claude provider 中正确包含重定向源模型键。
    • 新增了单元测试,验证非 Claude provider 不会错误地包含 claude-* 重定向键。
  • tests/unit/proxy/provider-selector-model-redirect.test.ts
    • 新增了测试用例,确认对于配置了 allowedModelsmodelRedirects 的 Claude provider,调度器能正确识别重定向键为支持模型。
    • 新增了测试用例,验证当请求模型不在 allowedModels 且无重定向配置时,Claude provider 会被正确过滤。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Feb 14, 2026

📝 Walkthrough

Walkthrough

引入并使用新的工具函数 providerSupportsModel,扩展可用模型枚举以包含经 modelRedirects 映射且与提供商兼容的别名;同时更新 biome.json schema 版本并补充针对重定向与提供商选择行为的单元测试覆盖。

Changes

Cohort / File(s) Summary
配置更新
biome.json
将 schema 引用从 2.3.14 更新至 2.3.15
提供商模型匹配核心逻辑
src/app/v1/_lib/proxy/provider-supports-model.ts
新增并导出 providerSupportsModel(provider: Provider, requestedModel: string): boolean,实现 Claude 与非-Claude 的分支规则、allowedModels/modelRedirects 检查和隐式支持策略。
提供商选择器重构
src/app/v1/_lib/proxy/provider-selector.ts
移除内联实现,改为从 provider-supports-model 模块导入并使用 providerSupportsModel
可用模型列表扩展
src/app/v1/_lib/models/available-models.ts
导入 providerSupportsModel;在无 allowedModels 时根据 providerType 从上游拉取模型;将与提供商兼容的 modelRedirects 别名合并到返回模型列表(包含重复检查、计数与调试日志);新增对未知 providerType 的错误处理。
单元测试覆盖
tests/unit/models/available-models-include-redirect-keys.test.ts, tests/unit/proxy/provider-selector-model-redirect.test.ts
新增测试:校验在 Anthropic/Claude 与 OpenAI-compatible 提供商场景下 modelRedirects 的暴露行为;以及提供商选择器在有/无重定向情况下的过滤/选择结果。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Linked Issues check ✅ Passed PR 完整解决 #786 中的编程需求:修复调度逻辑识别 modelRedirects 键、在 /v1/models 暴露重定向源模型名、提取统一的模型支持检查逻辑。
Out of Scope Changes check ✅ Passed 所有改动均在 #786 范围内:provider-supports-model 新模块、provider-selector 和 available-models 的重构、相关单元测试、biome.json 版本更新。无超出范围的改动。
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into dev
Description check ✅ Passed Pull request description clearly relates to changeset: introduces modelRedirects support, fixes scheduling logic, exposes redirect keys in /v1/models endpoint, matches all modified files.
Title check ✅ Passed 标题用中文编写,与变更集中的关键改进直接相关——引入modelRedirects key支持和修复模型调度逻辑。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

这个 Pull Request 旨在修复模型调度中的一个错误,并增强了 /v1/models 端点的功能。主要变更包括:

  1. 修复调度逻辑:当 Claude provider 使用 modelRedirects 时,即使原始模型名不在 allowedModels 中,调度器现在也能正确识别其为支持的模型。
  2. 增强模型列表/v1/models 端点现在会返回 modelRedirects 中定义的模型别名,使得客户端能看到所有可用的入口模型。
  3. 代码重构:将模型支持的判断逻辑抽离到独立的 provider-supports-model.ts 文件中,提高了代码的模块化和可维护性。

整体来看,这些变更是非常积极的,解决了 issue #786 中描述的问题,并且附带了相应的单元测试来验证修复。代码质量很高。

我只对新抽离的 providerSupportsModel 函数提出了一点重构建议,旨在进一步提升其可读性。除此之外,没有发现其他问题。

Comment on lines 11 to 60
export function providerSupportsModel(provider: Provider, requestedModel: string): boolean {
const isClaudeModel = requestedModel.startsWith("claude-");
const isClaudeProvider =
provider.providerType === "claude" || provider.providerType === "claude-auth";

// Case 1: Claude 模型请求
if (isClaudeModel) {
// 1a. Anthropic 提供商
if (isClaudeProvider) {
// 未设置 allowedModels 或为空数组:允许所有 claude 模型
if (!provider.allowedModels || provider.allowedModels.length === 0) {
return true;
}

// Fix #786:当存在 modelRedirects 映射时,映射前模型名(key)也应视为可请求模型
return (
provider.allowedModels.includes(requestedModel) ||
!!provider.modelRedirects?.[requestedModel]
);
}

// 1b. 非 Anthropic 提供商不支持 Claude 模型调度
return false;
}

// Case 2: 非 Claude 模型请求(gpt-*, gemini-*, 或其他任意模型)
// 2a. 优先检查显式声明(支持跨类型别名)
const explicitlyDeclared = !!(
provider.allowedModels?.includes(requestedModel) || provider.modelRedirects?.[requestedModel]
);

if (explicitlyDeclared) {
return true; // 显式声明优先级最高,允许跨类型别名
}

// 2b. Anthropic 提供商不支持非声明的非 Claude 模型
// 保护机制:防止将非 Claude 模型误路由到 Anthropic API
if (isClaudeProvider) {
return false;
}

// 2c. 非 Anthropic 提供商(codex, gemini, gemini-cli, openai-compatible)
// 未设置 allowedModels 或为空数组:接受任意模型(由上游提供商判断)
if (!provider.allowedModels || provider.allowedModels.length === 0) {
return true;
}

// 不在声明列表中且无重定向配置(前面已检查 explicitlyDeclared)
return false;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

当前函数的逻辑有些复杂,分支较多,可以重构以提高可读性。我们可以将逻辑分为两个主要部分:

  1. 显式声明: 如果模型在 allowedModelsmodelRedirects 中明确列出,则始终支持。这具有最高优先级,并允许跨提供商类型的别名。
  2. 隐式规则: 对于未显式声明的模型,我们根据提供商和模型的类型应用默认规则。

这样的结构可以使逻辑更清晰,更容易理解和维护。

export function providerSupportsModel(provider: Provider, requestedModel: string): boolean {
  const isClaudeModel = requestedModel.startsWith("claude-");
  const isClaudeProvider =
    provider.providerType === "claude" || provider.providerType === "claude-auth";

  // 1. 显式声明的模型(在 allowedModels 或 modelRedirects 中)始终被支持。
  // 这是最高优先级,允许跨类型代理。
  const isExplicitlyDeclared =
    provider.allowedModels?.includes(requestedModel) || !!provider.modelRedirects?.[requestedModel];
  if (isExplicitlyDeclared) {
    return true;
  }

  // 2. 对于未显式声明的模型,根据 provider 和 model 类型进行隐式规则判断。

  // 2a. Claude 模型只能由 Claude provider 支持。
  if (isClaudeModel) {
    // 如果 Claude provider 没有设置 allowedModels,则默认支持所有 Claude 模型。
    if (isClaudeProvider) {
      return !provider.allowedModels || provider.allowedModels.length === 0;
    }
    // 非 Claude provider 不支持 Claude 模型。
    return false;
  }

  // 2b. 非 Claude 模型。

  // Claude provider 不支持未声明的非 Claude 模型。
  if (isClaudeProvider) {
    return false;
  }

  // 非 Claude provider,如果未设置 allowedModels,则默认支持所有非 Claude 模型。
  return !provider.allowedModels || provider.allowedModels.length === 0;
}

@github-actions github-actions bot added bug Something isn't working area:core area:provider labels Feb 14, 2026
@github-actions github-actions bot added the size/S Small PR (< 200 lines) label Feb 14, 2026
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review Summary

This PR properly fixes issue #786 by treating modelRedirects keys as supported models when the target model is in allowedModels. The extraction of providerSupportsModel to a dedicated module improves code reusability and the /v1/models endpoint now correctly exposes redirect source models. Implementation is clean with appropriate test coverage.

PR Size: S

  • Lines changed: 370 (299 additions, 71 deletions)
  • Files changed: 6

Issues Found

Category Critical High Medium Low
Logic/Bugs 0 0 0 0
Security 0 0 0 0
Error Handling 0 0 0 0
Types 0 0 0 0
Comments/Docs 0 0 0 0
Tests 0 0 0 0
Simplification 0 0 0 0

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Proper logging and fallback behavior
  • Type safety - Proper TypeScript usage
  • Documentation accuracy - Comments match implementation
  • Test coverage - Adequate (4 new tests covering fix and edge cases)
  • Code clarity - Good (function extraction improves maintainability)

Automated review by Claude AI

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

6 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link

greptile-apps bot commented Feb 14, 2026

Additional Comments (1)

src/app/v1/_lib/proxy/provider-selector.ts
Stale JSDoc comment left behind

The providerSupportsModel function body was extracted to its own module, but its JSDoc comment block (lines 96–114) was left behind. This creates two consecutive JSDoc blocks — the orphaned one followed by the checkFormatProviderTypeCompatibility JSDoc — which is confusing and misleading.

/**
 * 根据原始请求格式限制可选供应商类型
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/v1/_lib/proxy/provider-selector.ts
Line: 96:115

Comment:
**Stale JSDoc comment left behind**

The `providerSupportsModel` function body was extracted to its own module, but its JSDoc comment block (lines 96–114) was left behind. This creates two consecutive JSDoc blocks — the orphaned one followed by the `checkFormatProviderTypeCompatibility` JSDoc — which is confusing and misleading.

```suggestion
/**
 * 根据原始请求格式限制可选供应商类型
```

How can I resolve this? If you propose a fix, please make it concise.

@tesgth032 tesgth032 changed the title fix: /v1/models 返回 modelRedirects key,修复模型调度误判 (#786) [未完成] fix: /v1/models 返回 modelRedirects key,修复模型调度误判 (#786) Feb 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core area:provider bug Something isn't working size/S Small PR (< 200 lines)

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

1 participant