mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-23 18:10:26 +08:00
Merge branch 'main' into fix/next-release-bugs
This commit is contained in:
commit
42733d0fc8
@ -9,12 +9,29 @@ class ContextMenu {
|
|||||||
const template: MenuItemConstructorOptions[] = this.createEditMenuItems(properties)
|
const template: MenuItemConstructorOptions[] = this.createEditMenuItems(properties)
|
||||||
const filtered = template.filter((item) => item.visible !== false)
|
const filtered = template.filter((item) => item.visible !== false)
|
||||||
if (filtered.length > 0) {
|
if (filtered.length > 0) {
|
||||||
const menu = Menu.buildFromTemplate(filtered)
|
const menu = Menu.buildFromTemplate([...filtered, ...this.createInspectMenuItems(w)])
|
||||||
menu.popup()
|
menu.popup()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createInspectMenuItems(w: Electron.BrowserWindow): MenuItemConstructorOptions[] {
|
||||||
|
const locale = locales[configManager.getLanguage()]
|
||||||
|
const { common } = locale.translation
|
||||||
|
const template: MenuItemConstructorOptions[] = [
|
||||||
|
{
|
||||||
|
id: 'inspect',
|
||||||
|
label: common.inspect,
|
||||||
|
click: () => {
|
||||||
|
w.webContents.toggleDevTools()
|
||||||
|
},
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return template
|
||||||
|
}
|
||||||
|
|
||||||
private createEditMenuItems(properties: Electron.ContextMenuParams): MenuItemConstructorOptions[] {
|
private createEditMenuItems(properties: Electron.ContextMenuParams): MenuItemConstructorOptions[] {
|
||||||
const locale = locales[configManager.getLanguage()]
|
const locale = locales[configManager.getLanguage()]
|
||||||
const { common } = locale.translation
|
const { common } = locale.translation
|
||||||
|
|||||||
@ -304,6 +304,7 @@
|
|||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
"copied": "Copied",
|
"copied": "Copied",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
|
"inspect": "Inspect",
|
||||||
"cut": "Cut",
|
"cut": "Cut",
|
||||||
"default": "Default",
|
"default": "Default",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
|
|||||||
@ -304,6 +304,7 @@
|
|||||||
"confirm": "確認",
|
"confirm": "確認",
|
||||||
"copied": "コピーされました",
|
"copied": "コピーされました",
|
||||||
"copy": "コピー",
|
"copy": "コピー",
|
||||||
|
"inspect": "検査",
|
||||||
"cut": "切り取り",
|
"cut": "切り取り",
|
||||||
"default": "デフォルト",
|
"default": "デフォルト",
|
||||||
"delete": "削除",
|
"delete": "削除",
|
||||||
|
|||||||
@ -304,6 +304,7 @@
|
|||||||
"confirm": "Подтверждение",
|
"confirm": "Подтверждение",
|
||||||
"copied": "Скопировано",
|
"copied": "Скопировано",
|
||||||
"copy": "Копировать",
|
"copy": "Копировать",
|
||||||
|
"inspect": "Осмотреть",
|
||||||
"cut": "Вырезать",
|
"cut": "Вырезать",
|
||||||
"default": "По умолчанию",
|
"default": "По умолчанию",
|
||||||
"delete": "Удалить",
|
"delete": "Удалить",
|
||||||
|
|||||||
@ -304,6 +304,7 @@
|
|||||||
"confirm": "确认",
|
"confirm": "确认",
|
||||||
"copied": "已复制",
|
"copied": "已复制",
|
||||||
"copy": "复制",
|
"copy": "复制",
|
||||||
|
"inspect": "检查",
|
||||||
"cut": "剪切",
|
"cut": "剪切",
|
||||||
"default": "默认",
|
"default": "默认",
|
||||||
"delete": "删除",
|
"delete": "删除",
|
||||||
|
|||||||
@ -304,6 +304,7 @@
|
|||||||
"confirm": "確認",
|
"confirm": "確認",
|
||||||
"copied": "已複製",
|
"copied": "已複製",
|
||||||
"copy": "複製",
|
"copy": "複製",
|
||||||
|
"inspect": "檢查",
|
||||||
"cut": "剪下",
|
"cut": "剪下",
|
||||||
"default": "預設",
|
"default": "預設",
|
||||||
"delete": "刪除",
|
"delete": "刪除",
|
||||||
|
|||||||
@ -6,7 +6,11 @@ import {
|
|||||||
MdiLightbulbOn90
|
MdiLightbulbOn90
|
||||||
} from '@renderer/components/Icons/SVGIcon'
|
} from '@renderer/components/Icons/SVGIcon'
|
||||||
import { useQuickPanel } from '@renderer/components/QuickPanel'
|
import { useQuickPanel } from '@renderer/components/QuickPanel'
|
||||||
import { isSupportedReasoningEffortGrokModel, isSupportedThinkingTokenGeminiModel } from '@renderer/config/models'
|
import {
|
||||||
|
isSupportedReasoningEffortGrokModel,
|
||||||
|
isSupportedThinkingTokenGeminiModel,
|
||||||
|
isSupportedThinkingTokenQwenModel
|
||||||
|
} from '@renderer/config/models'
|
||||||
import { useAssistant } from '@renderer/hooks/useAssistant'
|
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||||
import { Assistant, Model, ReasoningEffortOptions } from '@renderer/types'
|
import { Assistant, Model, ReasoningEffortOptions } from '@renderer/types'
|
||||||
import { Tooltip } from 'antd'
|
import { Tooltip } from 'antd'
|
||||||
@ -30,7 +34,8 @@ interface Props {
|
|||||||
const MODEL_SUPPORTED_OPTIONS: Record<string, ThinkingOption[]> = {
|
const MODEL_SUPPORTED_OPTIONS: Record<string, ThinkingOption[]> = {
|
||||||
default: ['off', 'low', 'medium', 'high'],
|
default: ['off', 'low', 'medium', 'high'],
|
||||||
grok: ['off', 'low', 'high'],
|
grok: ['off', 'low', 'high'],
|
||||||
gemini: ['off', 'low', 'medium', 'high', 'auto']
|
gemini: ['off', 'low', 'medium', 'high', 'auto'],
|
||||||
|
qwen: ['off', 'low', 'medium', 'high', 'auto']
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选项转换映射表:当选项不支持时使用的替代选项
|
// 选项转换映射表:当选项不支持时使用的替代选项
|
||||||
@ -49,6 +54,7 @@ const ThinkingButton: FC<Props> = ({ ref, model, assistant, ToolbarButton }): Re
|
|||||||
|
|
||||||
const isGrokModel = isSupportedReasoningEffortGrokModel(model)
|
const isGrokModel = isSupportedReasoningEffortGrokModel(model)
|
||||||
const isGeminiModel = isSupportedThinkingTokenGeminiModel(model)
|
const isGeminiModel = isSupportedThinkingTokenGeminiModel(model)
|
||||||
|
const isQwenModel = isSupportedThinkingTokenQwenModel(model)
|
||||||
|
|
||||||
const currentReasoningEffort = useMemo(() => {
|
const currentReasoningEffort = useMemo(() => {
|
||||||
return assistant.settings?.reasoning_effort || 'off'
|
return assistant.settings?.reasoning_effort || 'off'
|
||||||
@ -58,8 +64,9 @@ const ThinkingButton: FC<Props> = ({ ref, model, assistant, ToolbarButton }): Re
|
|||||||
const modelType = useMemo(() => {
|
const modelType = useMemo(() => {
|
||||||
if (isGeminiModel) return 'gemini'
|
if (isGeminiModel) return 'gemini'
|
||||||
if (isGrokModel) return 'grok'
|
if (isGrokModel) return 'grok'
|
||||||
|
if (isQwenModel) return 'qwen'
|
||||||
return 'default'
|
return 'default'
|
||||||
}, [isGeminiModel, isGrokModel])
|
}, [isGeminiModel, isGrokModel, isQwenModel])
|
||||||
|
|
||||||
// 获取当前模型支持的选项
|
// 获取当前模型支持的选项
|
||||||
const supportedOptions = useMemo(() => {
|
const supportedOptions = useMemo(() => {
|
||||||
@ -73,7 +80,8 @@ const ThinkingButton: FC<Props> = ({ ref, model, assistant, ToolbarButton }): Re
|
|||||||
const fallbackOption = OPTION_FALLBACK[currentReasoningEffort as ThinkingOption]
|
const fallbackOption = OPTION_FALLBACK[currentReasoningEffort as ThinkingOption]
|
||||||
|
|
||||||
updateAssistantSettings({
|
updateAssistantSettings({
|
||||||
reasoning_effort: fallbackOption === 'off' ? undefined : fallbackOption
|
reasoning_effort: fallbackOption === 'off' ? undefined : fallbackOption,
|
||||||
|
qwenThinkMode: fallbackOption === 'off'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [currentReasoningEffort, supportedOptions, updateAssistantSettings, model.id])
|
}, [currentReasoningEffort, supportedOptions, updateAssistantSettings, model.id])
|
||||||
@ -103,12 +111,14 @@ const ThinkingButton: FC<Props> = ({ ref, model, assistant, ToolbarButton }): Re
|
|||||||
// 然后更新设置
|
// 然后更新设置
|
||||||
if (!isEnabled) {
|
if (!isEnabled) {
|
||||||
updateAssistantSettings({
|
updateAssistantSettings({
|
||||||
reasoning_effort: undefined
|
reasoning_effort: undefined,
|
||||||
|
qwenThinkMode: false
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
updateAssistantSettings({
|
updateAssistantSettings({
|
||||||
reasoning_effort: option
|
reasoning_effort: option,
|
||||||
|
qwenThinkMode: true
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import {
|
|||||||
filterEmptyMessages,
|
filterEmptyMessages,
|
||||||
filterUserRoleStartMessages
|
filterUserRoleStartMessages
|
||||||
} from '@renderer/services/MessagesService'
|
} from '@renderer/services/MessagesService'
|
||||||
import { processReqMessages } from '@renderer/services/ModelMessageService'
|
import { processPostsuffixQwen3Model, processReqMessages } from '@renderer/services/ModelMessageService'
|
||||||
import store from '@renderer/store'
|
import store from '@renderer/store'
|
||||||
import {
|
import {
|
||||||
Assistant,
|
Assistant,
|
||||||
@ -401,6 +401,20 @@ export default class OpenAICompatibleProvider extends BaseOpenAiProvider {
|
|||||||
const { signal } = abortController
|
const { signal } = abortController
|
||||||
await this.checkIsCopilot()
|
await this.checkIsCopilot()
|
||||||
|
|
||||||
|
const lastUserMsg = userMessages.findLast((m) => m.role === 'user')
|
||||||
|
if (lastUserMsg) {
|
||||||
|
const postsuffix = '/no_think'
|
||||||
|
// qwenThinkMode === true 表示思考模式啓用,此時不應添加 /no_think,如果存在則移除
|
||||||
|
const qwenThinkModeEnabled = assistant.settings?.qwenThinkMode === true
|
||||||
|
const currentContent = lastUserMsg.content // content 類型:string | ChatCompletionContentPart[] | null
|
||||||
|
|
||||||
|
lastUserMsg.content = processPostsuffixQwen3Model(
|
||||||
|
currentContent,
|
||||||
|
postsuffix,
|
||||||
|
qwenThinkModeEnabled
|
||||||
|
) as ChatCompletionContentPart[]
|
||||||
|
}
|
||||||
|
|
||||||
//当 systemMessage 内容为空时不发送 systemMessage
|
//当 systemMessage 内容为空时不发送 systemMessage
|
||||||
let reqMessages: ChatCompletionMessageParam[]
|
let reqMessages: ChatCompletionMessageParam[]
|
||||||
if (!systemMessage.content) {
|
if (!systemMessage.content) {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Model } from '@renderer/types'
|
import { Model } from '@renderer/types'
|
||||||
import { ChatCompletionMessageParam } from 'openai/resources'
|
import { ChatCompletionContentPart, ChatCompletionContentPartText, ChatCompletionMessageParam } from 'openai/resources'
|
||||||
|
|
||||||
export function processReqMessages(
|
export function processReqMessages(
|
||||||
model: Model,
|
model: Model,
|
||||||
@ -40,3 +40,63 @@ function interleaveUserAndAssistantMessages(messages: ChatCompletionMessageParam
|
|||||||
|
|
||||||
return processedMessages
|
return processedMessages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process postsuffix for Qwen3 model
|
||||||
|
export function processPostsuffixQwen3Model(
|
||||||
|
// content 類型:string | ChatCompletionContentPart[] | null
|
||||||
|
content: string | ChatCompletionContentPart[] | null,
|
||||||
|
postsuffix: string,
|
||||||
|
qwenThinkModeEnabled: boolean
|
||||||
|
): string | ChatCompletionContentPart[] | null {
|
||||||
|
if (typeof content === 'string') {
|
||||||
|
if (qwenThinkModeEnabled) {
|
||||||
|
// 思考模式启用,移除 postsuffix
|
||||||
|
if (content.endsWith(postsuffix)) {
|
||||||
|
return content.substring(0, content.length - postsuffix.length).trimEnd()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 思考模式未启用,添加 postsuffix
|
||||||
|
if (!content.endsWith(postsuffix)) {
|
||||||
|
return content + postsuffix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (Array.isArray(content)) {
|
||||||
|
let lastTextPartIndex = -1
|
||||||
|
for (let i = content.length - 1; i >= 0; i--) {
|
||||||
|
if (content[i].type === 'text') {
|
||||||
|
lastTextPartIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastTextPartIndex !== -1) {
|
||||||
|
const textPart = content[lastTextPartIndex] as ChatCompletionContentPartText
|
||||||
|
if (qwenThinkModeEnabled) {
|
||||||
|
// 思考模式启用,移除 postsuffix
|
||||||
|
if (textPart.text.endsWith(postsuffix)) {
|
||||||
|
textPart.text = textPart.text.substring(0, textPart.text.length - postsuffix.length).trimEnd()
|
||||||
|
// 可選:如果 textPart.text 變為空,可以考慮是否移除該 part
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 思考模式未启用,添加 postsuffix
|
||||||
|
if (!textPart.text.endsWith(postsuffix)) {
|
||||||
|
textPart.text += postsuffix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 數組中沒有文本部分
|
||||||
|
if (!qwenThinkModeEnabled) {
|
||||||
|
// 思考模式未啓用,需要添加 postsuffix
|
||||||
|
// 如果沒有文本部分,則添加一個新的文本部分
|
||||||
|
content.push({ type: 'text', text: postsuffix })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// currentContent 是 null
|
||||||
|
if (!qwenThinkModeEnabled) {
|
||||||
|
// 思考模式未启用,需要添加 postsuffix
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|||||||
@ -60,6 +60,7 @@ export type AssistantSettings = {
|
|||||||
defaultModel?: Model
|
defaultModel?: Model
|
||||||
customParameters?: AssistantSettingCustomParameters[]
|
customParameters?: AssistantSettingCustomParameters[]
|
||||||
reasoning_effort?: ReasoningEffortOptions
|
reasoning_effort?: ReasoningEffortOptions
|
||||||
|
qwenThinkMode?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Agent = Omit<Assistant, 'model'> & {
|
export type Agent = Omit<Assistant, 'model'> & {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user