Skip to content

feat(provider): 允许的供应商模型支持从上游 API 动态获取#491

Merged
ding113 merged 5 commits into
ding113:devfrom
NieiR:feat/upstream-model-list
Jan 1, 2026
Merged

feat(provider): 允许的供应商模型支持从上游 API 动态获取#491
ding113 merged 5 commits into
ding113:devfrom
NieiR:feat/upstream-model-list

Conversation

@NieiR

@NieiR NieiR commented Dec 31, 2025

Copy link
Copy Markdown
Contributor

Summary

新增或编辑供应商时,允许的模型列表支持从上游 API 动态获取真实可用的模型,失败时回退到本地价格表。

Changes

  • 支持 Claude、Codex、Gemini 三种供应商类型从上游获取模型列表
  • 通过标签指示数据来源(上游 / 本地)
  • 新增刷新按钮,可手动重新获取
  • 修复 Anthropic 代理认证兼容性问题
  • 修复 Gemini 代理返回 null 字段时的过滤逻辑

Screenshots

Claude Codex Gemini
Claude Codex Gemini

github-actions Bot and others added 2 commits December 31, 2025 16:24
新增或编辑供应商时,允许的模型列表优先从上游 API 获取真实可用的模型,
失败时回退到本地价格表,并通过图标指示数据来源(云端/本地)。
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @NieiR, 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!

此拉取请求的核心目标是提升供应商模型管理的灵活性和准确性。通过引入从上游API动态获取模型列表的能力,系统能够实时反映供应商提供的最新模型,并提供清晰的模型来源指示。同时,健壮的回退机制和修复的代理兼容性问题,确保了在各种情况下用户都能获得可靠的模型选择体验。

Highlights

  • 动态模型获取: 新增功能,允许系统从上游供应商API(如Claude、Codex、Gemini)动态获取可用的模型列表,以确保模型信息的实时性和准确性。
  • 模型来源指示与回退机制: 在模型选择界面,通过标签明确指示模型列表是来自上游API还是本地价格表。当上游API获取失败或不支持时,系统将自动回退到使用本地价格表中的模型列表。
  • 手动刷新功能: 模型选择器中新增了刷新按钮,用户可以手动触发重新从上游API获取模型列表的操作。
  • Anthropic代理认证修复: 修复了Anthropic代理认证的兼容性问题,确保通过代理访问Anthropic服务时认证正常工作。
  • Gemini代理响应过滤修复: 修复了Gemini代理在返回null字段时可能导致的过滤逻辑问题,提高了模型列表解析的健壮性。
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.

@github-actions github-actions Bot added enhancement New feature or request area:provider area:UI area:i18n size/M Medium PR (< 500 lines) labels Dec 31, 2025

@gemini-code-assist gemini-code-assist 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.

Code Review

本次 PR 的主要目标是实现供应商模型列表的动态获取功能,当新增或编辑供应商时,允许从上游 API 获取可用模型,并在失败时回退到本地列表。整体实现思路清晰,代码结构良好,特别是在 src/actions/providers.ts 中对不同供应商的 API 进行了模块化处理,并增加了代理和错误处理逻辑,提高了代码的健壮性。在前端 model-multi-select.tsx 组件中,通过 useCallbackuseEffect 实现了模型加载逻辑,并增加了数据来源指示和手动刷新功能,提升了用户体验。

我提出了两点建议:

  1. 在后端的 providers.ts 文件中,建议移除 GET 请求中不必要的 Content-Type 头,以保持 HTTP 请求的准确性。
  2. 在前端的 model-multi-select.tsx 组件中,当前的 useEffect 实现可能存在竞态条件和在组件卸载后更新状态的风险,建议进行重构以提高组件的稳健性。

除此之外,代码质量很高,注释清晰,功能实现完整。合并后将显著改善供应商配置的灵活性和准确性。

Comment thread src/actions/providers.ts Outdated

@github-actions github-actions 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.

Code Review Summary

This PR adds upstream model list fetching functionality for provider configuration. The implementation is functional but has critical error handling issues that need to be addressed before merging.

PR Size: M

  • Lines changed: 579 (536 additions + 43 deletions)
  • Files changed: 9

Issues Found

Category Critical High Medium Low
Error Handling 0 2 0 0
User Feedback 0 2 1 0
Tests 0 1 0 0
Logic/Security 0 1 0 0

Critical & High Priority Issues

1. [HIGH] Silent Gemini Auth Failure (src/actions/providers.ts:2918-2925)

Issue: When GeminiAuth.getAccessToken() fails, the code catches the error, logs a warning, and continues with the potentially invalid original API key. The user is never informed of the auth processing failure.

Location: Lines 2918-2925 in fetchGeminiModels()

Current Code:

try {
  processedApiKey = await GeminiAuth.getAccessToken(data.apiKey);
  isJsonCreds = GeminiAuth.isJson(data.apiKey);
} catch (e) {
  logger.warn("fetchGeminiModels: auth process failed", { error: e });
}

Fix:

try {
  processedApiKey = await GeminiAuth.getAccessToken(data.apiKey);
  isJsonCreds = GeminiAuth.isJson(data.apiKey);
} catch (e) {
  logger.error("fetchGeminiModels: auth processing failed", { 
    error: e,
    keyFormat: GeminiAuth.isJson(data.apiKey) ? 'json' : 'string'
  });
  return { 
    ok: false, 
    error: `认证处理失败: ${e instanceof Error ? e.message : '未知错误'}` 
  };
}

Severity: High - Users will receive confusing 401/403 errors instead of clear auth processing errors


2. [HIGH] Insufficient HTTP Error Context (src/actions/providers.ts:2882-2892)

Issue: The handleHttpError() function returns minimal error information to the user (only HTTP status code), while the actual error message from the API is logged but not shown.

Location: Lines 2882-2892

Current Code:

function handleHttpError(
  response: Response,
  errorText: string,
  logPrefix: string
): FetchUpstreamModelsResult {
  logger.warn(`${logPrefix}: API returned error`, {
    status: response.status,
    errorPreview: errorText.substring(0, 200),
  });
  return { ok: false, error: `API 返回错误: HTTP ${response.status}` };
}

Fix:

function handleHttpError(
  response: Response,
  errorText: string,
  logPrefix: string
): FetchUpstreamModelsResult {
  logger.warn(`${logPrefix}: API returned error`, {
    url: response.url,
    status: response.status,
    statusText: response.statusText,
    errorPreview: errorText.substring(0, 200),
  });
  
  const errorSummary = errorText.trim().split('\n')[0].substring(0, 100);
  const userMessage = errorSummary 
    ? `API 返回错误 (HTTP ${response.status}): ${errorSummary}`
    : `API 返回错误: HTTP ${response.status} ${response.statusText}`;
    
  return { ok: false, error: userMessage };
}

Severity: High - Makes troubleshooting API failures very difficult for admins


3. [HIGH] Generic Exception Handling Without Error Type Discrimination (src/actions/providers.ts:2893-2903)

Issue: The handleFetchException() function treats all errors identically, losing important diagnostic context like timeouts, DNS failures, and connection issues.

Location: Lines 2893-2903

Current Code:

function handleFetchException(
  error: unknown,
  logPrefix: string
): FetchUpstreamModelsResult {
  const err = error as Error & { code?: string };
  logger.warn(`${logPrefix}: request failed`, {
    error: err.message,
    code: err.code,
  });
  return { ok: false, error: `请求失败: ${err.message}` };
}

Fix:

function handleFetchException(
  error: unknown,
  logPrefix: string
): FetchUpstreamModelsResult {
  const err = error as Error & { code?: string };
  
  logger.warn(`${logPrefix}: request failed`, {
    errorName: err.name,
    errorMessage: err.message,
    errorCode: err.code,
    isTimeout: err.name === 'AbortError' || err.name === 'TimeoutError',
  });
  
  let userMessage = '请求失败';
  if (err.name === 'AbortError' || err.name === 'TimeoutError') {
    userMessage = '请求超时,请检查网络连接或增加超时时间';
  } else if (err.code === 'ENOTFOUND') {
    userMessage = 'DNS 解析失败,请检查 URL 是否正确';
  } else if (err.code === 'ECONNREFUSED') {
    userMessage = '连接被拒绝,请检查服务是否运行';
  } else if (err.message) {
    userMessage = `请求失败: ${err.message}`;
  } else {
    userMessage = '请求失败: 未知错误';
  }
  
  return { ok: false, error: userMessage };
}

Severity: High - Timeout and network errors are common but not clearly identified


4. [HIGH] Silent Fallback Without Logging (model-multi-select.tsx:76-117)

Issue: When fetchUpstreamModels() fails, the component silently falls back to local price list without console logging or user notification.

Location: Lines 76-117 in loadModels() callback

Fix: Add logging when fallback occurs:

if (upstreamResult.ok && upstreamResult.data) {
  setAvailableModels(upstreamResult.data.models);
  setModelSource("upstream");
  setLoading(false);
  return;
}

// Log the failure reason for debugging
console.warn('[ModelSelect] Failed to fetch upstream models, falling back to local:', 
  upstreamResult.error);

Severity: High - Makes production debugging very difficult


5. [MEDIUM] Vague Proxy URL Validation Error (src/actions/providers.ts:2837-2838)

Issue: When proxy URL validation fails, the error message is generic and doesn't explain what formats are valid.

Location: Lines 2837-2838

Fix:

if (data.proxyUrl && !isValidProxyUrl(data.proxyUrl)) {
  logger.warn("fetchUpstreamModels: invalid proxy URL", {
    proxyUrl: data.proxyUrl,
  });
  return { 
    ok: false, 
    error: "代理地址格式无效,请使用 http://host:port 或 socks5://host:port 格式" 
  };
}

Severity: Medium - Affects UX but not critical


6. [HIGH] Missing Tests for Critical New Feature

Issue: No tests found for the new fetchUpstreamModels() function, which makes network calls and has complex error handling logic.

Required Test Coverage:

  • ✅ Successful upstream fetch (OpenAI, Gemini, Anthropic)
  • ✅ Network timeout handling
  • ✅ HTTP error responses (401, 403, 500)
  • ✅ Fallback to local price list
  • ✅ Invalid URL/proxy URL validation
  • ✅ Gemini auth failure scenarios
  • ✅ Race condition handling in React component

Severity: High - Untested network code with complex error handling is high-risk


7. [HIGH] Potential Race Condition in loadModels (model-multi-select.tsx)

Issue: The loadModels() function doesn't track which invocation is the latest. If a user rapidly changes provider URL or clicks refresh multiple times, stale responses could overwrite fresh data.

Location: Lines 76-117

Fix: Add cancellation token or sequence tracking:

const loadModels = useCallback(async () => {
  const requestId = ++requestIdRef.current; // Add ref to track latest request
  setLoading(true);
  setModelSource("loading");

  // ... existing logic ...

  // Before setState, check if this is still the latest request
  if (requestId !== requestIdRef.current) {
    return; // Stale request, ignore
  }

  setAvailableModels(upstreamResult.data.models);
  setModelSource("upstream");
  setLoading(false);
}, [providerUrl, apiKey, providerId, providerType, proxyUrl, proxyFallbackToDirect]);

Severity: High - Race conditions can cause incorrect data to be displayed


Review Coverage

  • Logic and correctness - 1 race condition identified
  • Security (OWASP Top 10) - Clean (auth validation present)
  • Error handling - 5 high-priority issues identified
  • Type safety - Clean (proper TypeScript usage)
  • Documentation accuracy - Clean
  • Test coverage - Missing critical tests
  • Code clarity - Good overall

Recommendations

Before merging:

  1. ✅ Fix all High severity error handling issues (#1, #2, #3, #4, #7)
  2. ✅ Add unit tests for fetchUpstreamModels() covering error scenarios
  3. ✅ Add test for race condition in loadModels()
  4. ⚠️ Consider fixing Medium severity issue (#5) for better UX

Post-merge improvements:

  • Add integration tests with mock upstream APIs
  • Consider adding retry logic for transient network failures
  • Add telemetry/metrics for upstream fetch success rate

Automated review by Claude AI

@ding113
ding113 merged commit 706c56f into ding113:dev Jan 1, 2026
6 of 7 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Claude Code Hub Roadmap Jan 1, 2026
@NieiR
NieiR deleted the feat/upstream-model-list branch January 6, 2026 13:21
@NieiR NieiR mentioned this pull request Jan 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:i18n area:provider area:UI enhancement New feature or request size/M Medium PR (< 500 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants