Compare commits

...

6 Commits

Author SHA1 Message Date
Calvin Wade
4769806b19
Merge eb2aaf1759 into a6ba5d34e0 2025-12-18 22:23:26 +08:00
kangfenmao
a6ba5d34e0 chore(release): v1.7.6 2025-12-18 22:19:11 +08:00
Calvin
eb2aaf1759 refactor: extract emitThinkingCompleteIfNeeded to reduce duplication
Extract the shared logic for emitting THINKING_COMPLETE chunk into a
reusable method. This removes code duplication between text-start and
reasoning-end event handlers as suggested in code review.

Signed-off-by: Calvin <calvinvwei@gmail.com>
2025-12-15 01:09:01 +08:00
Calvin
cc6fcda856 fix: auto-complete thinking when text output starts
This fixes the issue where the thinking timer continues running after
thinking is complete and text output begins. Some AI providers don't
send a reasoning-end event explicitly, so we now auto-complete thinking
when a text-start event is received with accumulated reasoning content.

Fixes #11796

Signed-off-by: Calvin <calvinvwei@gmail.com>
2025-12-14 00:11:10 +08:00
Calvin
1ce2076185 fix: also preserve thinking time when stopping during thinking
This extends the previous fix to also handle the case when the user
stops the reply while thinking is still in progress (not just after
thinking is complete).

Signed-off-by: Calvin <calvinvwei@gmail.com>
2025-12-13 23:07:09 +08:00
Calvin
a4eeb6c4b1 fix: preserve thinking time when stopping reply
Fixes #11886

Signed-off-by: Calvin <calvinvwei@gmail.com>
2025-12-13 22:57:37 +08:00
6 changed files with 94 additions and 39 deletions

View File

@ -134,38 +134,38 @@ artifactBuildCompleted: scripts/artifact-build-completed.js
releaseInfo:
releaseNotes: |
<!--LANG:en-->
Cherry Studio 1.7.5 - Filesystem MCP Overhaul & Topic Management
Cherry Studio 1.7.6 - New Models & MCP Enhancements
This release features a completely rewritten filesystem MCP server, new batch topic management, and improved assistant management.
This release adds support for new AI models and includes a new MCP server for memory management.
✨ New Features
- [MCP] Rewrite filesystem MCP server with improved tool set (glob, ls, grep, read, write, edit, delete)
- [Topics] Add topic manage mode for batch delete and move operations with search functionality
- [Assistants] Merge import/subscribe popups and add export to assistant management
- [Knowledge] Use prompt injection for forced knowledge base search (faster response times)
- [Settings] Add tool use mode setting (prompt/function) to default assistant settings
- [Models] Add support for Xiaomi MiMo model
- [Models] Add support for Gemini 3 Flash and Pro model detection
- [Models] Add support for Volcengine Doubao-Seed-1.8 model
- [MCP] Add Nowledge Mem builtin MCP server for memory management
- [Settings] Add default reasoning effort option to resolve confusion between undefined and none
🐛 Bug Fixes
- [Model] Correct typo in Gemini 3 Pro Image Preview model name
- [Installer] Auto-install VC++ Redistributable without user prompt
- [Notes] Fix notes directory validation and default path reset for cross-platform restore
- [OAuth] Bind OAuth callback server to localhost (127.0.0.1) for security
- [Azure] Restore deployment-based URLs for non-v1 apiVersion
- [Translation] Disable reasoning mode for translation to improve efficiency
- [Image] Update API path for image generation requests in OpenAIBaseClient
- [Windows] Auto-discover and persist Git Bash path on Windows for scoop users
<!--LANG:zh-CN-->
Cherry Studio 1.7.5 - 文件系统 MCP 重构与话题管理
Cherry Studio 1.7.6 - 新模型与 MCP 增强
本次更新完全重写了文件系统 MCP 服务器,新增批量话题管理功能,并改进了助手管理
本次更新添加了多个新 AI 模型支持,并新增记忆管理 MCP 服务器
✨ 新功能
- [MCP] 重写文件系统 MCP 服务器提供改进的工具集glob、ls、grep、read、write、edit、delete
- [话题] 新增话题管理模式,支持批量删除和移动操作,带搜索功能
- [助手] 合并导入/订阅弹窗,并在助手管理中添加导出功能
- [知识库] 使用提示词注入进行强制知识库搜索(响应更快)
- [设置] 在默认助手设置中添加工具使用模式设置prompt/function
- [模型] 添加小米 MiMo 模型支持
- [模型] 添加 Gemini 3 Flash 和 Pro 模型检测支持
- [模型] 添加火山引擎 Doubao-Seed-1.8 模型支持
- [MCP] 新增 Nowledge Mem 内置 MCP 服务器,用于记忆管理
- [设置] 添加默认推理强度选项,解决 undefined 和 none 之间的混淆
🐛 问题修复
- [模型] 修正 Gemini 3 Pro Image Preview 模型名称的拼写错误
- [安装程序] 自动安装 VC++ 运行库,无需用户确认
- [笔记] 修复跨平台恢复场景下的笔记目录验证和默认路径重置逻辑
- [OAuth] 将 OAuth 回调服务器绑定到 localhost (127.0.0.1) 以提高安全性
- [Azure] 修复非 v1 apiVersion 的部署 URL 问题
- [翻译] 禁用翻译时的推理模式以提高效率
- [图像] 更新 OpenAIBaseClient 中图像生成请求的 API 路径
- [Windows] 自动发现并保存 Windows scoop 用户的 Git Bash 路径
<!--LANG:END-->

View File

@ -1,6 +1,6 @@
{
"name": "CherryStudio",
"version": "1.7.5",
"version": "1.7.6",
"private": true,
"description": "A powerful AI assistant for producer.",
"main": "./out/main/index.js",

View File

@ -120,6 +120,23 @@ export class AiSdkToChunkAdapter {
}
}
/**
* THINKING_COMPLETE chunk
* @param final reasoningContent
* @returns THINKING_COMPLETE chunk
*/
private emitThinkingCompleteIfNeeded(final: { reasoningContent: string; [key: string]: any }): boolean {
if (final.reasoningContent) {
this.onChunk({
type: ChunkType.THINKING_COMPLETE,
text: final.reasoningContent
})
final.reasoningContent = ''
return true
}
return false
}
/**
* AI SDK chunk Cherry Studio chunk
* @param chunk AI SDK chunk
@ -145,6 +162,9 @@ export class AiSdkToChunkAdapter {
}
// === 文本相关事件 ===
case 'text-start':
// 如果有未完成的思考内容,先生成 THINKING_COMPLETE
// 这处理了某些提供商不发送 reasoning-end 事件的情况
this.emitThinkingCompleteIfNeeded(final)
this.onChunk({
type: ChunkType.TEXT_START
})
@ -215,11 +235,7 @@ export class AiSdkToChunkAdapter {
})
break
case 'reasoning-end':
this.onChunk({
type: ChunkType.THINKING_COMPLETE,
text: final.reasoningContent || ''
})
final.reasoningContent = ''
this.emitThinkingCompleteIfNeeded(final)
break
// === 工具调用相关事件(原始 AI SDK 事件,如果没有被中间件处理) ===

View File

@ -29,10 +29,20 @@ interface BaseCallbacksDependencies {
assistantMsgId: string
saveUpdatesToDB: any
assistant: Assistant
getCurrentThinkingInfo?: () => { blockId: string | null; millsec: number }
}
export const createBaseCallbacks = (deps: BaseCallbacksDependencies) => {
const { blockManager, dispatch, getState, topicId, assistantMsgId, saveUpdatesToDB, assistant } = deps
const {
blockManager,
dispatch,
getState,
topicId,
assistantMsgId,
saveUpdatesToDB,
assistant,
getCurrentThinkingInfo
} = deps
const startTime = Date.now()
const notificationService = NotificationService.getInstance()
@ -98,10 +108,17 @@ export const createBaseCallbacks = (deps: BaseCallbacksDependencies) => {
const possibleBlockId = findBlockIdForCompletion()
if (possibleBlockId) {
// 更改上一个block的状态为ERROR
const changes = {
// 更改上一个block的状态为ERROR/PAUSED
const changes: Record<string, any> = {
status: isErrorTypeAbort ? MessageBlockStatus.PAUSED : MessageBlockStatus.ERROR
}
// 如果是 thinking block保留实际思考时间
if (blockManager.lastBlockType === MessageBlockType.THINKING) {
const thinkingInfo = getCurrentThinkingInfo?.()
if (thinkingInfo?.blockId === possibleBlockId && thinkingInfo?.millsec && thinkingInfo.millsec > 0) {
changes.thinking_millsec = thinkingInfo.millsec
}
}
blockManager.smartBlockUpdate(possibleBlockId, changes, blockManager.lastBlockType!, true)
}
@ -111,13 +128,28 @@ export const createBaseCallbacks = (deps: BaseCallbacksDependencies) => {
if (currentMessage) {
const allBlockRefs = findAllBlocks(currentMessage)
const blockState = getState().messageBlocks
// 获取当前思考信息(如果有),用于保留实际思考时间
const thinkingInfo = getCurrentThinkingInfo?.()
for (const blockRef of allBlockRefs) {
const block = blockState.entities[blockRef.id]
if (block && block.status === MessageBlockStatus.STREAMING && block.id !== possibleBlockId) {
// 构建更新对象
const changes: Record<string, any> = {
status: isErrorTypeAbort ? MessageBlockStatus.PAUSED : MessageBlockStatus.ERROR
}
// 如果是 thinking block 且有思考时间信息,保留实际思考时间
if (
block.type === MessageBlockType.THINKING &&
thinkingInfo?.blockId === block.id &&
thinkingInfo?.millsec &&
thinkingInfo.millsec > 0
) {
changes.thinking_millsec = thinkingInfo.millsec
}
dispatch(
updateOneBlock({
id: block.id,
changes: { status: isErrorTypeAbort ? MessageBlockStatus.PAUSED : MessageBlockStatus.ERROR }
changes
})
)
}

View File

@ -23,6 +23,12 @@ interface CallbacksDependencies {
export const createCallbacks = (deps: CallbacksDependencies) => {
const { blockManager, dispatch, getState, topicId, assistantMsgId, saveUpdatesToDB, assistant } = deps
// 首先创建 thinkingCallbacks ,以便传递 getCurrentThinkingInfo 给 baseCallbacks
const thinkingCallbacks = createThinkingCallbacks({
blockManager,
assistantMsgId
})
// 创建基础回调
const baseCallbacks = createBaseCallbacks({
blockManager,
@ -31,13 +37,8 @@ export const createCallbacks = (deps: CallbacksDependencies) => {
topicId,
assistantMsgId,
saveUpdatesToDB,
assistant
})
// 创建各类回调
const thinkingCallbacks = createThinkingCallbacks({
blockManager,
assistantMsgId
assistant,
getCurrentThinkingInfo: thinkingCallbacks.getCurrentThinkingInfo
})
const toolCallbacks = createToolCallbacks({

View File

@ -19,6 +19,12 @@ export const createThinkingCallbacks = (deps: ThinkingCallbacksDependencies) =>
let thinking_millsec_now: number = 0
return {
// 获取当前思考时间(用于停止回复时保留思考时间)
getCurrentThinkingInfo: () => ({
blockId: thinkingBlockId,
millsec: thinking_millsec_now > 0 ? performance.now() - thinking_millsec_now : 0
}),
onThinkingStart: async () => {
if (blockManager.hasInitialPlaceholder) {
const changes: Partial<MessageBlock> = {