From 3c5c63f555b668783e4451e8ff64e18619c4f56f Mon Sep 17 00:00:00 2001 From: one Date: Sat, 14 Jun 2025 21:24:34 +0800 Subject: [PATCH] fix(model): qwen3 model detection (#7201) --- src/renderer/src/config/models.ts | 8 +++-- .../src/utils/__tests__/naming.test.ts | 33 +++++++++++++++++++ src/renderer/src/utils/naming.ts | 14 ++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/config/models.ts b/src/renderer/src/config/models.ts index 78f4ff3d0b..3c849cc854 100644 --- a/src/renderer/src/config/models.ts +++ b/src/renderer/src/config/models.ts @@ -145,6 +145,7 @@ import YoudaoLogo from '@renderer/assets/images/providers/netease-youdao.svg' import NomicLogo from '@renderer/assets/images/providers/nomic.png' import { getProviderByModel } from '@renderer/services/AssistantService' import { Model } from '@renderer/types' +import { getBaseModelName } from '@renderer/utils' import OpenAI from 'openai' import { WEB_SEARCH_PROMPT_FOR_OPENROUTER } from './prompts' @@ -2484,9 +2485,10 @@ export function isSupportedThinkingTokenQwenModel(model?: Model): boolean { return false } + const baseName = getBaseModelName(model.id, '/').toLowerCase() + return ( - model.id.toLowerCase().startsWith('qwen3') || - model.id.toLowerCase().startsWith('qwen/qwen3') || + baseName.startsWith('qwen3') || [ 'qwen-plus-latest', 'qwen-plus-0428', @@ -2494,7 +2496,7 @@ export function isSupportedThinkingTokenQwenModel(model?: Model): boolean { 'qwen-turbo-latest', 'qwen-turbo-0428', 'qwen-turbo-2025-04-28' - ].includes(model.id.toLowerCase()) + ].includes(baseName) ) } diff --git a/src/renderer/src/utils/__tests__/naming.test.ts b/src/renderer/src/utils/__tests__/naming.test.ts index 4a76334bfe..1d4560a3ab 100644 --- a/src/renderer/src/utils/__tests__/naming.test.ts +++ b/src/renderer/src/utils/__tests__/naming.test.ts @@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest' import { firstLetter, generateColorFromChar, + getBaseModelName, getBriefInfo, getDefaultGroupName, getFirstCharacter, @@ -157,6 +158,38 @@ describe('naming', () => { }) }) + describe('getBaseModelName', () => { + it('should extract base model name with single delimiter', () => { + expect(getBaseModelName('DeepSeek/DeepSeek-R1')).toBe('DeepSeek-R1') + expect(getBaseModelName('openai/gpt-4.1')).toBe('gpt-4.1') + expect(getBaseModelName('anthropic/claude-3.5-sonnet')).toBe('claude-3.5-sonnet') + }) + + it('should extract base model name with multiple levels', () => { + expect(getBaseModelName('Pro/deepseek-ai/DeepSeek-R1')).toBe('DeepSeek-R1') + expect(getBaseModelName('org/team/group/model')).toBe('model') + }) + + it('should return original id if no delimiter found', () => { + expect(getBaseModelName('deepseek-r1')).toBe('deepseek-r1') + expect(getBaseModelName('deepseek-r1:free')).toBe('deepseek-r1:free') + }) + + it('should handle edge cases', () => { + // 验证空字符串的情况 + expect(getBaseModelName('')).toBe('') + // 验证以分隔符结尾的字符串 + expect(getBaseModelName('model/')).toBe('') + expect(getBaseModelName('model/name/')).toBe('') + // 验证以分隔符开头的字符串 + expect(getBaseModelName('/model')).toBe('model') + expect(getBaseModelName('/path/to/model')).toBe('model') + // 验证连续分隔符的情况 + expect(getBaseModelName('model//name')).toBe('name') + expect(getBaseModelName('model///name')).toBe('name') + }) + }) + describe('generateColorFromChar', () => { it('should generate a valid hex color code', () => { // 验证生成有效的十六进制颜色代码 diff --git a/src/renderer/src/utils/naming.ts b/src/renderer/src/utils/naming.ts index f475fa7421..df178104de 100644 --- a/src/renderer/src/utils/naming.ts +++ b/src/renderer/src/utils/naming.ts @@ -46,6 +46,20 @@ export const getDefaultGroupName = (id: string, provider?: string): string => { return str } +/** + * 从模型 ID 中提取基础名称。 + * 例如: + * - 'deepseek/deepseek-r1' => 'deepseek-r1' + * - 'deepseek-ai/deepseek/deepseek-r1' => 'deepseek-r1' + * @param {string} id 模型 ID + * @param {string} [delimiter='/'] 分隔符,默认为 '/' + * @returns {string} 基础名称 + */ +export const getBaseModelName = (id: string, delimiter: string = '/'): string => { + const parts = id.split(delimiter) + return parts[parts.length - 1] +} + /** * 用于获取 avatar 名字的辅助函数,会取出字符串的第一个字符,支持表情符号。 * @param {string} str 输入字符串