mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-29 14:31:35 +08:00
fix: qwen-mt translate (#9627)
* feat(translate): 优化翻译助手类型定义和服务逻辑 重构翻译助手类型定义,将内容字段和模型校验逻辑分离 修改翻译服务以使用助手中的模型而非全局配置 为QwenMT模型添加特殊处理逻辑 * docs(i18n): 添加Qwen MT模型在对话中不可用的错误提示 * refactor(openai): 优化翻译选项处理逻辑 移除不必要的的TranslateAssistant类型断言并重构翻译选项的生成方式 * fix(types): 修复isTranslateAssistant类型检查逻辑 确保assistant.content为字符串类型时才返回true
This commit is contained in:
parent
8240493685
commit
626a5ed4f1
@ -46,6 +46,7 @@ import {
|
||||
EFFORT_RATIO,
|
||||
FileTypes,
|
||||
isSystemProvider,
|
||||
isTranslateAssistant,
|
||||
MCPCallToolResponse,
|
||||
MCPTool,
|
||||
MCPToolResponse,
|
||||
@ -54,7 +55,6 @@ import {
|
||||
Provider,
|
||||
SystemProviderIds,
|
||||
ToolCallResponse,
|
||||
TranslateAssistant,
|
||||
WebSearchSource
|
||||
} from '@renderer/types'
|
||||
import { ChunkType, TextStartChunk, ThinkingStartChunk } from '@renderer/types/chunk'
|
||||
@ -569,13 +569,18 @@ export class OpenAIAPIClient extends OpenAIBaseClient<
|
||||
const extra_body: Record<string, any> = {}
|
||||
|
||||
if (isQwenMTModel(model)) {
|
||||
const targetLanguage = (assistant as TranslateAssistant).targetLanguage
|
||||
extra_body.translation_options = {
|
||||
source_lang: 'auto',
|
||||
target_lang: mapLanguageToQwenMTModel(targetLanguage!)
|
||||
}
|
||||
if (!extra_body.translation_options.target_lang) {
|
||||
throw new Error(t('translate.error.not_supported', { language: targetLanguage?.value }))
|
||||
if (isTranslateAssistant(assistant)) {
|
||||
const targetLanguage = assistant.targetLanguage
|
||||
const translationOptions = {
|
||||
source_lang: 'auto',
|
||||
target_lang: mapLanguageToQwenMTModel(targetLanguage)
|
||||
} as const
|
||||
if (!translationOptions.target_lang) {
|
||||
throw new Error(t('translate.error.not_supported', { language: targetLanguage.value }))
|
||||
}
|
||||
extra_body.translation_options = translationOptions
|
||||
} else {
|
||||
throw new Error(t('translate.error.chat_qwen_mt'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3785,6 +3785,7 @@
|
||||
},
|
||||
"empty": "Translation content is empty",
|
||||
"error": {
|
||||
"chat_qwen_mt": "Qwen MT model cannot be used in chat. Please go to the translation page.",
|
||||
"detect": {
|
||||
"qwen_mt": "QwenMT model cannot be used for language detection",
|
||||
"unknown": "Unknown language detected",
|
||||
|
||||
@ -3785,6 +3785,7 @@
|
||||
},
|
||||
"empty": "翻訳内容が空です",
|
||||
"error": {
|
||||
"chat_qwen_mt": "Qwen MT モデルは対話で使用できません。翻訳ページに移動してください",
|
||||
"detect": {
|
||||
"qwen_mt": "QwenMTモデルは言語検出に使用できません",
|
||||
"unknown": "検出された言語は不明です",
|
||||
|
||||
@ -3785,6 +3785,7 @@
|
||||
},
|
||||
"empty": "Содержимое перевода пусто",
|
||||
"error": {
|
||||
"chat_qwen_mt": "Модель Qwen MT недоступна для использования в диалоге, перейдите на страницу перевода",
|
||||
"detect": {
|
||||
"qwen_mt": "Модель QwenMT не может использоваться для определения языка",
|
||||
"unknown": "Обнаружен неизвестный язык",
|
||||
|
||||
@ -3785,6 +3785,7 @@
|
||||
},
|
||||
"empty": "翻译内容为空",
|
||||
"error": {
|
||||
"chat_qwen_mt": "Qwen MT 模型不可在对话中使用,请转至翻译页面",
|
||||
"detect": {
|
||||
"qwen_mt": "QwenMT模型不能用于语言检测",
|
||||
"unknown": "检测到未知语言",
|
||||
|
||||
@ -3785,6 +3785,7 @@
|
||||
},
|
||||
"empty": "翻譯內容為空",
|
||||
"error": {
|
||||
"chat_qwen_mt": "Qwen MT 模型不可在对话中使用,請轉至翻譯頁面",
|
||||
"detect": {
|
||||
"qwen_mt": "QwenMT模型不能用於語言檢測",
|
||||
"unknown": "檢測到未知語言",
|
||||
|
||||
@ -3785,6 +3785,7 @@
|
||||
},
|
||||
"empty": "Το μεταφρασμένο κείμενο είναι κενό",
|
||||
"error": {
|
||||
"chat_qwen_mt": "Τα μοντέλα Qwen MT δεν είναι διαθέσιμα για χρήση σε διαλόγους, παρακαλώ μεταβείτε στη σελίδα μετάφρασης",
|
||||
"detect": {
|
||||
"qwen_mt": "Το μοντέλο QwenMT δεν μπορεί να χρησιμοποιηθεί για εντοπισμό γλώσσας",
|
||||
"unknown": "Ανιχνεύθηκε άγνωστη γλώσσα",
|
||||
|
||||
@ -3785,6 +3785,7 @@
|
||||
},
|
||||
"empty": "El contenido de traducción está vacío",
|
||||
"error": {
|
||||
"chat_qwen_mt": "El modelo Qwen MT no está disponible para uso en conversaciones, por favor vaya a la página de traducción.",
|
||||
"detect": {
|
||||
"qwen_mt": "El modelo QwenMT no se puede utilizar para la detección de idiomas",
|
||||
"unknown": "Se detectó un idioma desconocido",
|
||||
|
||||
@ -3785,6 +3785,7 @@
|
||||
},
|
||||
"empty": "Le contenu à traduire est vide",
|
||||
"error": {
|
||||
"chat_qwen_mt": "Les modèles Qwen MT ne peuvent pas être utilisés dans les conversations, veuillez vous rendre sur la page de traduction.",
|
||||
"detect": {
|
||||
"qwen_mt": "Le modèle QwenMT ne peut pas être utilisé pour la détection de langues",
|
||||
"unknown": "Langue inconnue détectée",
|
||||
|
||||
@ -3785,6 +3785,7 @@
|
||||
},
|
||||
"empty": "O conteúdo de tradução está vazio",
|
||||
"error": {
|
||||
"chat_qwen_mt": "Modelos Qwen MT não estão disponíveis para uso em conversas. Por favor, vá para a página de tradução.",
|
||||
"detect": {
|
||||
"qwen_mt": "O modelo QwenMT não pode ser usado para detecção de idioma",
|
||||
"unknown": "Idioma desconhecido detectado",
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
MAX_CONTEXT_COUNT,
|
||||
UNLIMITED_CONTEXT_COUNT
|
||||
} from '@renderer/config/constant'
|
||||
import { isQwenMTModel } from '@renderer/config/models'
|
||||
import { UNKNOWN } from '@renderer/config/translate'
|
||||
import i18n from '@renderer/i18n'
|
||||
import store from '@renderer/store'
|
||||
@ -52,11 +53,10 @@ export function getDefaultAssistant(): Assistant {
|
||||
}
|
||||
|
||||
export function getDefaultTranslateAssistant(targetLanguage: TranslateLanguage, text: string): TranslateAssistant {
|
||||
const translateModel = getTranslateModel()
|
||||
const model = getTranslateModel()
|
||||
const assistant: Assistant = getDefaultAssistant()
|
||||
assistant.model = translateModel
|
||||
|
||||
if (!assistant.model) {
|
||||
if (!model) {
|
||||
logger.error('No translate model')
|
||||
throw new Error(i18n.t('translate.error.not_configured'))
|
||||
}
|
||||
@ -66,15 +66,32 @@ export function getDefaultTranslateAssistant(targetLanguage: TranslateLanguage,
|
||||
throw new Error('Unknown target language')
|
||||
}
|
||||
|
||||
assistant.settings = {
|
||||
const settings = {
|
||||
temperature: 0.7
|
||||
}
|
||||
|
||||
assistant.prompt = store
|
||||
.getState()
|
||||
.settings.translateModelPrompt.replaceAll('{{target_language}}', targetLanguage.value)
|
||||
.replaceAll('{{text}}', text)
|
||||
return { ...assistant, targetLanguage }
|
||||
let prompt: string
|
||||
let content: string
|
||||
if (isQwenMTModel(model)) {
|
||||
content = text
|
||||
prompt = ''
|
||||
} else {
|
||||
content = 'follow system instruction'
|
||||
prompt = store
|
||||
.getState()
|
||||
.settings.translateModelPrompt.replaceAll('{{target_language}}', targetLanguage.value)
|
||||
.replaceAll('{{text}}', text)
|
||||
}
|
||||
|
||||
const translateAssistant = {
|
||||
...assistant,
|
||||
model,
|
||||
settings,
|
||||
prompt,
|
||||
targetLanguage,
|
||||
content
|
||||
} satisfies TranslateAssistant
|
||||
return translateAssistant
|
||||
}
|
||||
|
||||
export function getDefaultAssistantSettings() {
|
||||
|
||||
@ -15,12 +15,7 @@ import { formatErrorMessage, isAbortError } from '@renderer/utils/error'
|
||||
import { t } from 'i18next'
|
||||
|
||||
import { hasApiKey } from './ApiService'
|
||||
import {
|
||||
getDefaultModel,
|
||||
getDefaultTranslateAssistant,
|
||||
getProviderByModel,
|
||||
getTranslateModel
|
||||
} from './AssistantService'
|
||||
import { getDefaultTranslateAssistant, getProviderByModel } from './AssistantService'
|
||||
|
||||
const logger = loggerService.withContext('TranslateService')
|
||||
interface FetchTranslateProps {
|
||||
@ -30,11 +25,7 @@ interface FetchTranslateProps {
|
||||
}
|
||||
|
||||
async function fetchTranslate({ assistant, onResponse, abortKey }: FetchTranslateProps) {
|
||||
const model = getTranslateModel() || assistant.model || getDefaultModel()
|
||||
|
||||
if (!model) {
|
||||
throw new Error(t('translate.error.not_configured'))
|
||||
}
|
||||
const model = assistant.model
|
||||
|
||||
const provider = getProviderByModel(model)
|
||||
|
||||
@ -58,8 +49,8 @@ async function fetchTranslate({ assistant, onResponse, abortKey }: FetchTranslat
|
||||
|
||||
const params: CompletionsParams = {
|
||||
callType: 'translate',
|
||||
messages: 'do',
|
||||
assistant: { ...assistant, model },
|
||||
messages: assistant.content,
|
||||
assistant,
|
||||
streamOutput: stream,
|
||||
enableReasoning,
|
||||
onResponse,
|
||||
|
||||
@ -35,10 +35,19 @@ export type Assistant = {
|
||||
regularPhrases?: QuickPhrase[] // Added for regular phrase
|
||||
tags?: string[] // 助手标签
|
||||
enableMemory?: boolean
|
||||
// for translate. 更好的做法是定义base assistant,把 Assistant 作为多种不同定义 assistant 的联合类型,但重构代价太大
|
||||
content?: string
|
||||
targetLanguage?: TranslateLanguage
|
||||
}
|
||||
|
||||
export type TranslateAssistant = Assistant & {
|
||||
targetLanguage?: TranslateLanguage
|
||||
model: Model
|
||||
content: string
|
||||
targetLanguage: TranslateLanguage
|
||||
}
|
||||
|
||||
export const isTranslateAssistant = (assistant: Assistant): assistant is TranslateAssistant => {
|
||||
return (assistant.model && assistant.targetLanguage && typeof assistant.content === 'string') !== undefined
|
||||
}
|
||||
|
||||
export type AssistantsSortType = 'tags' | 'list'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user