mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-08 22:39:36 +08:00
fix(models): qwen-mt-flash supports text delta (#11448)
refactor(models): improve text delta support check for qwen-mt models Replace direct qwen-mt model check with regex pattern matching Add comprehensive test cases for isNotSupportTextDeltaModel Update all references to use new function name
This commit is contained in:
parent
fd3b7f717d
commit
69d31a1e2b
@ -24,9 +24,9 @@ import {
|
|||||||
isGemmaModel,
|
isGemmaModel,
|
||||||
isGenerateImageModels,
|
isGenerateImageModels,
|
||||||
isMaxTemperatureOneModel,
|
isMaxTemperatureOneModel,
|
||||||
isNotSupportedTextDelta,
|
|
||||||
isNotSupportSystemMessageModel,
|
isNotSupportSystemMessageModel,
|
||||||
isNotSupportTemperatureAndTopP,
|
isNotSupportTemperatureAndTopP,
|
||||||
|
isNotSupportTextDeltaModel,
|
||||||
isSupportedFlexServiceTier,
|
isSupportedFlexServiceTier,
|
||||||
isSupportedModel,
|
isSupportedModel,
|
||||||
isSupportFlexServiceTierModel,
|
isSupportFlexServiceTierModel,
|
||||||
@ -215,12 +215,51 @@ describe('model utils', () => {
|
|||||||
|
|
||||||
it('aggregates boolean helpers based on regex rules', () => {
|
it('aggregates boolean helpers based on regex rules', () => {
|
||||||
expect(isAnthropicModel(createModel({ id: 'claude-3.5' }))).toBe(true)
|
expect(isAnthropicModel(createModel({ id: 'claude-3.5' }))).toBe(true)
|
||||||
expect(isQwenMTModel(createModel({ id: 'qwen-mt-large' }))).toBe(true)
|
expect(isQwenMTModel(createModel({ id: 'qwen-mt-plus' }))).toBe(true)
|
||||||
expect(isNotSupportedTextDelta(createModel({ id: 'qwen-mt-large' }))).toBe(true)
|
|
||||||
expect(isNotSupportSystemMessageModel(createModel({ id: 'gemma-moe' }))).toBe(true)
|
expect(isNotSupportSystemMessageModel(createModel({ id: 'gemma-moe' }))).toBe(true)
|
||||||
expect(isOpenAIOpenWeightModel(createModel({ id: 'gpt-oss-free' }))).toBe(true)
|
expect(isOpenAIOpenWeightModel(createModel({ id: 'gpt-oss-free' }))).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('isNotSupportedTextDelta', () => {
|
||||||
|
it('returns true for qwen-mt-turbo and qwen-mt-plus models', () => {
|
||||||
|
// qwen-mt series that don't support text delta
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-mt-turbo' }))).toBe(true)
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-mt-plus' }))).toBe(true)
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'Qwen-MT-Turbo' }))).toBe(true)
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'QWEN-MT-PLUS' }))).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns false for qwen-mt-flash and other models', () => {
|
||||||
|
// qwen-mt-flash supports text delta
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-mt-flash' }))).toBe(false)
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'Qwen-MT-Flash' }))).toBe(false)
|
||||||
|
|
||||||
|
// Legacy qwen models without mt prefix (support text delta)
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-turbo' }))).toBe(false)
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-plus' }))).toBe(false)
|
||||||
|
|
||||||
|
// Other qwen models
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-max' }))).toBe(false)
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen2.5-72b' }))).toBe(false)
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-vl-plus' }))).toBe(false)
|
||||||
|
|
||||||
|
// Non-qwen models
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'gpt-4o' }))).toBe(false)
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'claude-3.5' }))).toBe(false)
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'glm-4-plus' }))).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles models with version suffixes', () => {
|
||||||
|
// qwen-mt models with version suffixes
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-mt-turbo-1201' }))).toBe(true)
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-mt-plus-0828' }))).toBe(true)
|
||||||
|
|
||||||
|
// Legacy qwen models with version suffixes (support text delta)
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-turbo-0828' }))).toBe(false)
|
||||||
|
expect(isNotSupportTextDeltaModel(createModel({ id: 'qwen-plus-latest' }))).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('evaluates GPT-5 family helpers', () => {
|
it('evaluates GPT-5 family helpers', () => {
|
||||||
expect(isGPT5SeriesModel(createModel({ id: 'gpt-5-preview' }))).toBe(true)
|
expect(isGPT5SeriesModel(createModel({ id: 'gpt-5-preview' }))).toBe(true)
|
||||||
expect(isGPT5SeriesModel(createModel({ id: 'gpt-5.1-preview' }))).toBe(false)
|
expect(isGPT5SeriesModel(createModel({ id: 'gpt-5.1-preview' }))).toBe(false)
|
||||||
|
|||||||
@ -111,8 +111,11 @@ export const isAnthropicModel = (model?: Model): boolean => {
|
|||||||
return modelId.startsWith('claude')
|
return modelId.startsWith('claude')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isNotSupportedTextDelta = (model: Model): boolean => {
|
const NOT_SUPPORT_TEXT_DELTA_MODEL_REGEX = new RegExp('qwen-mt-(?:turbo|plus)')
|
||||||
return isQwenMTModel(model)
|
|
||||||
|
export const isNotSupportTextDeltaModel = (model: Model): boolean => {
|
||||||
|
const modelId = getLowerBaseModelName(model.id)
|
||||||
|
return NOT_SUPPORT_TEXT_DELTA_MODEL_REGEX.test(modelId)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isNotSupportSystemMessageModel = (model: Model): boolean => {
|
export const isNotSupportSystemMessageModel = (model: Model): boolean => {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { TopView } from '@renderer/components/TopView'
|
import { TopView } from '@renderer/components/TopView'
|
||||||
import { isNotSupportedTextDelta } from '@renderer/config/models'
|
import { isNotSupportTextDeltaModel } from '@renderer/config/models'
|
||||||
import { useProvider } from '@renderer/hooks/useProvider'
|
import { useProvider } from '@renderer/hooks/useProvider'
|
||||||
import type { Model, Provider } from '@renderer/types'
|
import type { Model, Provider } from '@renderer/types'
|
||||||
import { getDefaultGroupName } from '@renderer/utils'
|
import { getDefaultGroupName } from '@renderer/utils'
|
||||||
@ -58,7 +58,7 @@ const PopupContainer: React.FC<Props> = ({ title, provider, resolve }) => {
|
|||||||
group: values.group ?? getDefaultGroupName(id)
|
group: values.group ?? getDefaultGroupName(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
addModel({ ...model, supported_text_delta: !isNotSupportedTextDelta(model) })
|
addModel({ ...model, supported_text_delta: !isNotSupportTextDeltaModel(model) })
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {
|
|||||||
groupQwenModels,
|
groupQwenModels,
|
||||||
isEmbeddingModel,
|
isEmbeddingModel,
|
||||||
isFunctionCallingModel,
|
isFunctionCallingModel,
|
||||||
isNotSupportedTextDelta,
|
isNotSupportTextDeltaModel,
|
||||||
isReasoningModel,
|
isReasoningModel,
|
||||||
isRerankModel,
|
isRerankModel,
|
||||||
isVisionModel,
|
isVisionModel,
|
||||||
@ -136,13 +136,13 @@ const PopupContainer: React.FC<Props> = ({ providerId, resolve }) => {
|
|||||||
addModel({
|
addModel({
|
||||||
...model,
|
...model,
|
||||||
endpoint_type: endpointTypes.includes('image-generation') ? 'image-generation' : endpointTypes[0],
|
endpoint_type: endpointTypes.includes('image-generation') ? 'image-generation' : endpointTypes[0],
|
||||||
supported_text_delta: !isNotSupportedTextDelta(model)
|
supported_text_delta: !isNotSupportTextDeltaModel(model)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
NewApiAddModelPopup.show({ title: t('settings.models.add.add_model'), provider, model })
|
NewApiAddModelPopup.show({ title: t('settings.models.add.add_model'), provider, model })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addModel({ ...model, supported_text_delta: !isNotSupportedTextDelta(model) })
|
addModel({ ...model, supported_text_delta: !isNotSupportTextDeltaModel(model) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { TopView } from '@renderer/components/TopView'
|
import { TopView } from '@renderer/components/TopView'
|
||||||
import { endpointTypeOptions } from '@renderer/config/endpointTypes'
|
import { endpointTypeOptions } from '@renderer/config/endpointTypes'
|
||||||
import { isNotSupportedTextDelta } from '@renderer/config/models'
|
import { isNotSupportTextDeltaModel } from '@renderer/config/models'
|
||||||
import { useDynamicLabelWidth } from '@renderer/hooks/useDynamicLabelWidth'
|
import { useDynamicLabelWidth } from '@renderer/hooks/useDynamicLabelWidth'
|
||||||
import { useProvider } from '@renderer/hooks/useProvider'
|
import { useProvider } from '@renderer/hooks/useProvider'
|
||||||
import type { EndpointType, Model, Provider } from '@renderer/types'
|
import type { EndpointType, Model, Provider } from '@renderer/types'
|
||||||
@ -65,7 +65,7 @@ const PopupContainer: React.FC<Props> = ({ title, provider, resolve, model, endp
|
|||||||
endpoint_type: isNewApiProvider(provider) ? values.endpointType : undefined
|
endpoint_type: isNewApiProvider(provider) ? values.endpointType : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
addModel({ ...model, supported_text_delta: !isNotSupportedTextDelta(model) })
|
addModel({ ...model, supported_text_delta: !isNotSupportTextDeltaModel(model) })
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { TopView } from '@renderer/components/TopView'
|
import { TopView } from '@renderer/components/TopView'
|
||||||
import { endpointTypeOptions } from '@renderer/config/endpointTypes'
|
import { endpointTypeOptions } from '@renderer/config/endpointTypes'
|
||||||
import { isNotSupportedTextDelta } from '@renderer/config/models'
|
import { isNotSupportTextDeltaModel } from '@renderer/config/models'
|
||||||
import { useDynamicLabelWidth } from '@renderer/hooks/useDynamicLabelWidth'
|
import { useDynamicLabelWidth } from '@renderer/hooks/useDynamicLabelWidth'
|
||||||
import { useProvider } from '@renderer/hooks/useProvider'
|
import { useProvider } from '@renderer/hooks/useProvider'
|
||||||
import type { EndpointType, Model, Provider } from '@renderer/types'
|
import type { EndpointType, Model, Provider } from '@renderer/types'
|
||||||
@ -48,7 +48,7 @@ const PopupContainer: React.FC<Props> = ({ title, provider, resolve, batchModels
|
|||||||
addModel({
|
addModel({
|
||||||
...model,
|
...model,
|
||||||
endpoint_type: values.endpointType,
|
endpoint_type: values.endpointType,
|
||||||
supported_text_delta: !isNotSupportedTextDelta(model)
|
supported_text_delta: !isNotSupportTextDeltaModel(model)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { DEFAULT_MIN_APPS } from '@renderer/config/minapps'
|
|||||||
import {
|
import {
|
||||||
glm45FlashModel,
|
glm45FlashModel,
|
||||||
isFunctionCallingModel,
|
isFunctionCallingModel,
|
||||||
isNotSupportedTextDelta,
|
isNotSupportTextDeltaModel,
|
||||||
SYSTEM_MODELS
|
SYSTEM_MODELS
|
||||||
} from '@renderer/config/models'
|
} from '@renderer/config/models'
|
||||||
import { BUILTIN_OCR_PROVIDERS, BUILTIN_OCR_PROVIDERS_MAP, DEFAULT_OCR_PROVIDER } from '@renderer/config/ocr'
|
import { BUILTIN_OCR_PROVIDERS, BUILTIN_OCR_PROVIDERS_MAP, DEFAULT_OCR_PROVIDER } from '@renderer/config/ocr'
|
||||||
@ -1988,7 +1988,7 @@ const migrateConfig = {
|
|||||||
const updateModelTextDelta = (model?: Model) => {
|
const updateModelTextDelta = (model?: Model) => {
|
||||||
if (model) {
|
if (model) {
|
||||||
model.supported_text_delta = true
|
model.supported_text_delta = true
|
||||||
if (isNotSupportedTextDelta(model)) {
|
if (isNotSupportTextDeltaModel(model)) {
|
||||||
model.supported_text_delta = false
|
model.supported_text_delta = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user