From 20c35a3c7ad97ddd6b0f0496c4e5f213ba85f141 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Sat, 24 Jan 2026 16:21:07 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=AF=AD=E9=9F=B3?= =?UTF-8?q?=E8=AF=86=E5=88=AB=E6=A0=87=E7=82=B9=E5=A4=84=E7=90=86=E5=8F=8A?= =?UTF-8?q?MCP=E8=BF=9E=E6=8E=A5=E7=A8=B3=E5=AE=9A=E6=80=A7=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复语音识别在非push_to_talk模式下未移除首标点的问题,提升听写体验 优化MCP客户端连接管理,增加心跳检测和连接状态主动检查,避免假死连接 移除调试日志语句,避免生产环境日志污染 --- .../controllers/translation_controller.dart | 20 ++-- .../Sources/azure_speech/AzureTtsHelper.swift | 2 +- .../chat_api/Sources/chat_api/MCPClient.swift | 91 +++++++++++++++++-- 3 files changed, 98 insertions(+), 15 deletions(-) diff --git a/lib/modules/translation/controllers/translation_controller.dart b/lib/modules/translation/controllers/translation_controller.dart index 808a35bb4..d8766bd9e 100644 --- a/lib/modules/translation/controllers/translation_controller.dart +++ b/lib/modules/translation/controllers/translation_controller.dart @@ -445,7 +445,7 @@ class TranslationController extends GetxController with WidgetsBindingObserver { } await _asrService.startContinuousRecognition( _audioSourceType, - isRemoveFirstPunctuation: false, + isRemoveFirstPunctuation: true, mode: 'push_to_talk', ); //开启识别 _startAsrActiveTracking(); //开启识别活动跟踪 @@ -1066,11 +1066,19 @@ class TranslationController extends GetxController with WidgetsBindingObserver { /// 启动语音识别 Future _startAsrService(String mode) async { - await _asrService.startContinuousRecognition( - _audioSourceType, - isRemoveFirstPunctuation: false, - mode: mode, - ); + if (mode == 'push_to_talk') { + await _asrService.startContinuousRecognition( + _audioSourceType, + isRemoveFirstPunctuation: false, + mode: mode, + ); + } else { + await _asrService.startContinuousRecognition( + _audioSourceType, + isRemoveFirstPunctuation: true, + mode: mode, + ); + } } /// 完成识别启动 diff --git a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift index 4160e2345..6e5d516ab 100644 --- a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift +++ b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift @@ -1455,7 +1455,7 @@ private func ensureAudioSessionQueueSpecificKeySet() { */ private func applyAudioSessionForTTS() -> Bool { var success = true - os_log("liwei-------------TTS applyAudioSessionForTTS currentRecognitionMode:%{public}@", log: self.log, type: .info, currentRecognitionMode) + // os_log("liwei-------------TTS applyAudioSessionForTTS currentRecognitionMode:%{public}@", log: self.log, type: .info, currentRecognitionMode) ensureAudioSessionQueueSpecificKeySet() let applyBody = { if self.isApplyingAudioSessionConfig { return } diff --git a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift index c2da48b8d..5d7146d1a 100644 --- a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift +++ b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift @@ -36,9 +36,11 @@ public class MCPSubClient { // 使用官方MCP Swift SDK private var mcpClient: Client? + private var transport: CustomSseClientTransport? private var tools: [Tool] = [] private var toolMaps: [[String: Any]] = [] private var isConnectedFlag = false + private let toolsLock = NSLock() // 重连配置 private let maxRetryAttempts = 3 @@ -51,6 +53,10 @@ public class MCPSubClient { // 工具调用配置 - 添加这两行 private let maxToolCallRetries = 3 private let toolCallTimeout: TimeInterval = 30.0 + + private let keepAliveInterval: TimeInterval = 25.0 + private let keepAliveTimeout: TimeInterval = 8.0 + private var keepAliveTask: Task? // 连接状态锁 private let connectionLock = NSLock() @@ -89,6 +95,7 @@ public class MCPSubClient { } } ) + self.transport = transport // 3. 连接到服务器(添加超时) try await withTimeout(seconds: 10) { @@ -106,6 +113,7 @@ public class MCPSubClient { isConnectedFlag = true retryCount = 0 // 重置重试计数 currentReconnectDelay = initialReconnectDelay // 重置延迟 + startKeepAlive() return true @@ -116,6 +124,9 @@ public class MCPSubClient { } private func processTools(_ toolList: [Tool]) { + toolsLock.lock() + defer { toolsLock.unlock() } + tools.removeAll() toolMaps.removeAll() @@ -173,6 +184,13 @@ public class MCPSubClient { /// 检查连接状态 /// 检查连接状态 public func checkConnection() async -> Bool { + if isConnectedFlag, let transport { + let isActive = await transport.isConnectionActive() + if !isActive { + isConnectedFlag = false + } + } + if !isConnectedFlag { return await connect() } @@ -339,14 +357,25 @@ public class MCPSubClient { } public func getToolMaps() -> [[String: Any]] { + toolsLock.lock() + defer { toolsLock.unlock() } + return toolMaps } public func containsTool(name: String) -> Bool { + toolsLock.lock() + defer { toolsLock.unlock() } + return tools.contains { $0.name == name } } public func close() async { + keepAliveTask?.cancel() + keepAliveTask = nil + reconnectTask?.cancel() + reconnectTask = nil + if let client = mcpClient { await client.disconnect() } @@ -359,9 +388,9 @@ public class MCPSubClient { if isConnectedFlag { print("[\(serverId)] 连接已断开,更新状态") isConnectedFlag = false - - // 启动重连逻辑 - // startReconnection() + keepAliveTask?.cancel() + keepAliveTask = nil + startReconnection() } } } @@ -369,7 +398,9 @@ public class MCPSubClient { /// 启动重连 private func startReconnection() { // 取消之前的重连任务 - reconnectTask?.cancel() + if reconnectTask != nil { + return + } reconnectTask = Task { [weak self] in await self?.performReconnection() @@ -378,6 +409,8 @@ public class MCPSubClient { /// 执行重连 private func performReconnection() async { + defer { reconnectTask = nil } + while retryCount < maxRetryAttempts && !Task.isCancelled { retryCount += 1 @@ -403,6 +436,8 @@ public class MCPSubClient { private func handleConnectionError(_ error: Error) async { print("[\(serverId)] 连接错误: \(error.localizedDescription)") isConnectedFlag = false + keepAliveTask?.cancel() + keepAliveTask = nil // 如果是MCP特定错误,可以进行特殊处理 if let mcpError = error as? MCPError { @@ -411,9 +446,49 @@ public class MCPSubClient { } // 如果还有重试机会,启动重连 - // if retryCount < maxRetryAttempts { - // startReconnection() - // } + startReconnection() + } + + private func startKeepAlive() { + keepAliveTask?.cancel() + + keepAliveTask = Task { [weak self] in + guard let self else { return } + + while !Task.isCancelled { + try? await Task.sleep(for: .seconds(self.keepAliveInterval)) + + if Task.isCancelled { + return + } + + if !self.isConnectedFlag { + continue + } + + if let transport = self.transport { + let isActive = await transport.isConnectionActive() + if !isActive { + await self.handleConnectionLost() + continue + } + } + + guard let client = self.mcpClient else { + await self.handleConnectionError(MCPError.internalError("MCP client not initialized")) + continue + } + + do { + let (toolList, _) = try await withTimeout(seconds: self.keepAliveTimeout) { + try await client.listTools() + } + self.processTools(toolList) + } catch { + await self.handleConnectionError(error) + } + } + } } /// 获取连接状态 @@ -665,7 +740,7 @@ public class MCPClient { } public func isConnected() -> Bool { - return !subClients.isEmpty && subClients.values.allSatisfy { $0.getConnectionStatus() } + return !subClients.isEmpty && subClients.values.contains { $0.getConnectionStatus() } } public func disconnectAll() async {