From f5ff737e696777062228441e2ebce920979bb079 Mon Sep 17 00:00:00 2001 From: MIOYULIN Date: Tue, 8 Sep 2026 01:00:52 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=B8=BA=E6=9C=8D=E5=8A=A1=E5=88=B7?= =?UTF-8?q?=E6=96=B0=E8=B0=83=E7=94=A8=E6=98=BE=E5=BC=8F=E4=BC=A0=E9=80=92?= =?UTF-8?q?=20cancelStreaming=3Dfalse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题背景: EnhancedAIService 的 refreshServiceForFunction() 和 refreshAllServices() 默认参数 cancelStreaming=true,调用时会取消当前正在进行的流式传输响应。 但项目中有大量调用点(设置页面切换模型、更改API配置等场景)在配置变更时 调用这些方法刷新服务,本意是让新配置对后续请求生效,却意外中断了用户当前 正在进行的对话流式输出,影响用户体验。 修复内容: 1. 为所有 UI 设置页面和服务配置变更处的 refreshServiceForFunction() / refreshAllServices() 调用显式传入 cancelStreaming=false,确保配置刷新 不会打断当前流式响应 2. 修复 MultiServiceManager.releaseLease() 中 closeRetiredServiceLocked() 缺少 cancelStreaming 参数的问题,租约释放时不应默认取消流式传输 涉及文件(12个): - EnhancedAIService.kt - MultiServiceManager.kt - StandardSoftwareSettingsModifyTools.kt - WebChatHttpBridge.kt - ApiConfigDelegate.kt - AgentChatInputSection.kt - ClassicChatSettingsBar.kt - FunctionalConfigScreen.kt - ModelConfigScreen.kt - AdvancedSettingsSection.kt - ModelApiSettingsSection.kt - AutoGlmOneClickToolScreen.kt --- .../operit/api/chat/EnhancedAIService.kt | 24 +++++++++---------- .../api/chat/enhance/MultiServiceManager.kt | 14 +++++------ .../StandardSoftwareSettingsModifyTools.kt | 6 ++--- .../integrations/http/WebChatHttpBridge.kt | 2 +- .../operit/services/core/ApiConfigDelegate.kt | 4 ++-- .../input/agent/AgentChatInputSection.kt | 6 ++--- .../input/classic/ClassicChatSettingsBar.kt | 6 ++--- .../screens/FunctionalConfigScreen.kt | 7 ++---- .../settings/screens/ModelConfigScreen.kt | 14 ++++------- .../sections/AdvancedSettingsSection.kt | 4 ++-- .../sections/ModelApiSettingsSection.kt | 8 +++---- .../autoglm/AutoGlmOneClickToolScreen.kt | 5 +--- 12 files changed, 42 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/EnhancedAIService.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/EnhancedAIService.kt index 8b16ab9e68..8810850ce8 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/EnhancedAIService.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/EnhancedAIService.kt @@ -38,7 +38,6 @@ import com.ai.assistance.operit.data.model.ToolResult import com.ai.assistance.operit.data.model.ModelConfigData import com.ai.assistance.operit.data.model.ModelParameter import com.ai.assistance.operit.data.model.AITool -import com.ai.assistance.operit.data.model.ConversationSummaryConfig import com.ai.assistance.operit.data.preferences.ApiPreferences import com.ai.assistance.operit.data.preferences.ExternalHttpApiPreferences import com.ai.assistance.operit.data.preferences.WakeWordPreferences @@ -169,24 +168,24 @@ class EnhancedAIService private constructor(private val context: Context) { * @param context 应用上下文 * @param functionType 功能类型 */ - suspend fun refreshServiceForFunction(context: Context, functionType: FunctionType) { + suspend fun refreshServiceForFunction(context: Context, functionType: FunctionType, cancelStreaming: Boolean = true) { val allInstances = buildList { add(getInstance(context)) addAll(CHAT_INSTANCES.values) }.distinct() - allInstances.forEach { it.multiServiceManager.refreshServiceForFunction(functionType) } + allInstances.forEach { it.multiServiceManager.refreshServiceForFunction(functionType, cancelStreaming) } } /** * 刷新所有 AIService 实例(非实例化方式) * @param context 应用上下文 */ - suspend fun refreshAllServices(context: Context) { + suspend fun refreshAllServices(context: Context, cancelStreaming: Boolean = true) { val allInstances = buildList { add(getInstance(context)) addAll(CHAT_INSTANCES.values) }.distinct() - allInstances.forEach { it.multiServiceManager.refreshAllServices() } + allInstances.forEach { it.multiServiceManager.refreshAllServices(cancelStreaming) } } /** @@ -1792,9 +1791,8 @@ class EnhancedAIService private constructor(private val context: Context) { R.string.enhanced_pure_thinking_only_warning ) ) - val pureThinkingWarningDisplayContent = "\n$pureThinkingWarning" - context.roundManager.appendContent(pureThinkingWarningDisplayContent) - collector.emit(pureThinkingWarningDisplayContent) + context.roundManager.appendContent("\n$pureThinkingWarning") + collector.emit(pureThinkingWarning) try { context.conversationHistory.add( PromptTurn(kind = PromptTurnKind.TOOL_RESULT, content = pureThinkingWarning) @@ -2595,13 +2593,13 @@ class EnhancedAIService private constructor(private val context: Context) { suspend fun generateSummary( messages: List>, previousSummary: String?, - summaryConfig: ConversationSummaryConfig = ConversationSummaryConfig(), + customRules: String? = null, recordTokenUsage: Boolean = true, ): String { return generateSummaryFromPromptTurns( messages.toPromptTurns(), previousSummary, - summaryConfig, + customRules, recordTokenUsage, ) } @@ -2609,7 +2607,7 @@ class EnhancedAIService private constructor(private val context: Context) { suspend fun generateSummaryFromPromptTurns( messages: List, previousSummary: String?, - summaryConfig: ConversationSummaryConfig = ConversationSummaryConfig(), + customRules: String? = null, recordTokenUsage: Boolean = true, ): String { // 调用ConversationService中的方法 @@ -2617,7 +2615,7 @@ class EnhancedAIService private constructor(private val context: Context) { messages, previousSummary, multiServiceManager, - summaryConfig, + customRules, recordTokenUsage, ) } @@ -2929,7 +2927,7 @@ class EnhancedAIService private constructor(private val context: Context) { val toolExposureMode = ToolExposureMode.resolve(config.apiProviderType) // 获取所有工具分类 - val isEnglish = !LocaleUtils.usesChineseContent(context) + val isEnglish = LocaleUtils.getCurrentLanguage(context) == "en" // 后端识图服务是否可用(IMAGE_RECOGNITION 功能),用于 intent-based 视觉模型 val hasBackendImageRecognition = multiServiceManager.hasImageRecognitionConfigured() diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/enhance/MultiServiceManager.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/enhance/MultiServiceManager.kt index 88d6b44d2d..428c222f3d 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/enhance/MultiServiceManager.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/enhance/MultiServiceManager.kt @@ -219,10 +219,10 @@ class MultiServiceManager(private val context: Context) { } /** 刷新指定功能类型的服务实例 当配置更改时调用此方法 */ - suspend fun refreshServiceForFunction(functionType: FunctionType) { + suspend fun refreshServiceForFunction(functionType: FunctionType, cancelStreaming: Boolean = true) { ensureInitialized() serviceMutex.withLock { - serviceInstances.remove(functionType)?.let { retireManagedServiceLocked(it) } + serviceInstances.remove(functionType)?.let { retireManagedServiceLocked(it, cancelStreaming) } if (functionType == FunctionType.CHAT) { defaultService = null @@ -238,7 +238,7 @@ class MultiServiceManager(private val context: Context) { } /** 刷新所有服务实例 当全局设置更改时调用此方法 */ - suspend fun refreshAllServices() { + suspend fun refreshAllServices(cancelStreaming: Boolean = true) { ensureInitialized() serviceMutex.withLock { val services = mutableSetOf() @@ -252,7 +252,7 @@ class MultiServiceManager(private val context: Context) { retiredServices.clear() defaultService = null services.forEach { service -> - closeManagedServiceLocked(service, cancelStreaming = true) + closeManagedServiceLocked(service, cancelStreaming = cancelStreaming) } AppLogger.d(TAG, "已清除所有服务实例缓存并释放资源") } @@ -265,15 +265,15 @@ class MultiServiceManager(private val context: Context) { } } - private fun retireManagedServiceLocked(managedService: ManagedService) { + private fun retireManagedServiceLocked(managedService: ManagedService, cancelStreaming: Boolean = true) { managedService.retired = true retiredServices.add(managedService) closeRetiredServiceLocked(managedService) } - private fun closeRetiredServiceLocked(managedService: ManagedService) { + private fun closeRetiredServiceLocked(managedService: ManagedService, cancelStreaming: Boolean = true) { if (managedService.retired && managedService.activeLeases == 0) { - closeManagedServiceLocked(managedService, cancelStreaming = false) + closeManagedServiceLocked(managedService, cancelStreaming) } } diff --git a/app/src/main/java/com/ai/assistance/operit/core/tools/defaultTool/standard/StandardSoftwareSettingsModifyTools.kt b/app/src/main/java/com/ai/assistance/operit/core/tools/defaultTool/standard/StandardSoftwareSettingsModifyTools.kt index ba9d58eabc..b46e064997 100644 --- a/app/src/main/java/com/ai/assistance/operit/core/tools/defaultTool/standard/StandardSoftwareSettingsModifyTools.kt +++ b/app/src/main/java/com/ai/assistance/operit/core/tools/defaultTool/standard/StandardSoftwareSettingsModifyTools.kt @@ -1089,7 +1089,7 @@ class StandardSoftwareSettingsModifyTools(private val context: Context) { .map { it.key } .sortedBy { it.name } affectedFunctions.forEach { functionType -> - runCatching { EnhancedAIService.refreshServiceForFunction(context, functionType) } + runCatching { EnhancedAIService.refreshServiceForFunction(context, functionType, cancelStreaming = false) } } ToolResult( @@ -1157,7 +1157,7 @@ class StandardSoftwareSettingsModifyTools(private val context: Context) { affectedFunctions .sortedBy { it.name } .forEach { functionType -> - runCatching { EnhancedAIService.refreshServiceForFunction(context, functionType) } + runCatching { EnhancedAIService.refreshServiceForFunction(context, functionType, cancelStreaming = false) } } ToolResult( @@ -1333,7 +1333,7 @@ class StandardSoftwareSettingsModifyTools(private val context: Context) { val selectedModel = getModelByIndex(config.modelName, actualModelIndex) functionalConfigManager.setConfigForFunction(functionType, configId, actualModelIndex) - runCatching { EnhancedAIService.refreshServiceForFunction(context, functionType) } + runCatching { EnhancedAIService.refreshServiceForFunction(context, functionType, cancelStreaming = false) } ToolResult( toolName = tool.name, diff --git a/app/src/main/java/com/ai/assistance/operit/integrations/http/WebChatHttpBridge.kt b/app/src/main/java/com/ai/assistance/operit/integrations/http/WebChatHttpBridge.kt index aa26505850..fb5057ef81 100644 --- a/app/src/main/java/com/ai/assistance/operit/integrations/http/WebChatHttpBridge.kt +++ b/app/src/main/java/com/ai/assistance/operit/integrations/http/WebChatHttpBridge.kt @@ -441,7 +441,7 @@ class WebChatHttpBridge( normalizedModelIndex ) } - EnhancedAIService.refreshServiceForFunction(appContext, FunctionType.CHAT) + EnhancedAIService.refreshServiceForFunction(appContext, FunctionType.CHAT, cancelStreaming = false) } WebSelectModelResponse( diff --git a/app/src/main/java/com/ai/assistance/operit/services/core/ApiConfigDelegate.kt b/app/src/main/java/com/ai/assistance/operit/services/core/ApiConfigDelegate.kt index 4404cce630..d302f17997 100644 --- a/app/src/main/java/com/ai/assistance/operit/services/core/ApiConfigDelegate.kt +++ b/app/src/main/java/com/ai/assistance/operit/services/core/ApiConfigDelegate.kt @@ -569,7 +569,7 @@ class ApiConfigDelegate( } val enhancedAiService = withContext(Dispatchers.IO) { - EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT) + EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT, cancelStreaming = false) EnhancedAIService.getInstance(context) } withContext(Dispatchers.Main) { onConfigChanged(enhancedAiService) } @@ -631,7 +631,7 @@ class ApiConfigDelegate( modelConfigManager.updateThinkingOptionId(effectiveChatConfigId.value, optionId) val enhancedAiService = withContext(Dispatchers.IO) { - EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT) + EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT, cancelStreaming = false) EnhancedAIService.getInstance(context) } withContext(Dispatchers.Main) { onConfigChanged(enhancedAiService) } diff --git a/app/src/main/java/com/ai/assistance/operit/ui/features/chat/components/style/input/agent/AgentChatInputSection.kt b/app/src/main/java/com/ai/assistance/operit/ui/features/chat/components/style/input/agent/AgentChatInputSection.kt index b9484c9410..97f95b0194 100644 --- a/app/src/main/java/com/ai/assistance/operit/ui/features/chat/components/style/input/agent/AgentChatInputSection.kt +++ b/app/src/main/java/com/ai/assistance/operit/ui/features/chat/components/style/input/agent/AgentChatInputSection.kt @@ -339,7 +339,7 @@ fun AgentChatInputSection( memoryProfileId = profileId, ), ) - EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT) + EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT, cancelStreaming = false) } showCharacterCardMemoryBindingSwitchConfirm = false pendingCharacterCardMemorySelection = null @@ -559,7 +559,7 @@ fun AgentChatInputSection( selectedId, modelIndex, ) - EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT) + EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT, cancelStreaming = false) showModelSelectorPopup.value = false } } @@ -580,7 +580,7 @@ fun AgentChatInputSection( } else { scope.launch { userPreferencesManager.setActiveMemorySpace(selectedId) - EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT) + EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT, cancelStreaming = false) showExtraSettingsPopup.value = false } } diff --git a/app/src/main/java/com/ai/assistance/operit/ui/features/chat/components/style/input/classic/ClassicChatSettingsBar.kt b/app/src/main/java/com/ai/assistance/operit/ui/features/chat/components/style/input/classic/ClassicChatSettingsBar.kt index 1bc0267f46..31a14f535e 100644 --- a/app/src/main/java/com/ai/assistance/operit/ui/features/chat/components/style/input/classic/ClassicChatSettingsBar.kt +++ b/app/src/main/java/com/ai/assistance/operit/ui/features/chat/components/style/input/classic/ClassicChatSettingsBar.kt @@ -293,7 +293,7 @@ fun ClassicChatSettingsBar( } else { scope.launch { functionalConfigManager.setConfigForFunction(FunctionType.CHAT, selectedId, modelIndex) - EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT) + EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT, cancelStreaming = false) } } } @@ -312,7 +312,7 @@ fun ClassicChatSettingsBar( scope.launch { userPreferencesManager.setActiveMemorySpace(selectedId) // 用户偏好和记忆库绑定,可能影响AI行为,所以刷新服务 - EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT) + EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT, cancelStreaming = false) } } } @@ -372,7 +372,7 @@ fun ClassicChatSettingsBar( memoryProfileId = profileId, ) ) - EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT) + EnhancedAIService.refreshServiceForFunction(context, FunctionType.CHAT, cancelStreaming = false) } showMemoryDropdown = false showCharacterCardMemoryBindingSwitchConfirm = false diff --git a/app/src/main/java/com/ai/assistance/operit/ui/features/settings/screens/FunctionalConfigScreen.kt b/app/src/main/java/com/ai/assistance/operit/ui/features/settings/screens/FunctionalConfigScreen.kt index cea1523db2..b9674724ae 100644 --- a/app/src/main/java/com/ai/assistance/operit/ui/features/settings/screens/FunctionalConfigScreen.kt +++ b/app/src/main/java/com/ai/assistance/operit/ui/features/settings/screens/FunctionalConfigScreen.kt @@ -178,10 +178,7 @@ fun FunctionalConfigScreen( modelIndex ) // 刷新服务实例 - EnhancedAIService.refreshServiceForFunction( - context, - functionType - ) + EnhancedAIService.refreshServiceForFunction(context, functionType, cancelStreaming = false) showSaveSuccess = true } } @@ -197,7 +194,7 @@ fun FunctionalConfigScreen( scope.launch { functionalConfigManager.resetAllFunctionConfigs() // 刷新所有服务实例 - EnhancedAIService.refreshAllServices(context) + EnhancedAIService.refreshAllServices(context, cancelStreaming = false) showSaveSuccess = true } }, diff --git a/app/src/main/java/com/ai/assistance/operit/ui/features/settings/screens/ModelConfigScreen.kt b/app/src/main/java/com/ai/assistance/operit/ui/features/settings/screens/ModelConfigScreen.kt index 98a2ded110..e826699500 100644 --- a/app/src/main/java/com/ai/assistance/operit/ui/features/settings/screens/ModelConfigScreen.kt +++ b/app/src/main/java/com/ai/assistance/operit/ui/features/settings/screens/ModelConfigScreen.kt @@ -331,10 +331,7 @@ fun ModelConfigScreen( savedMapping.configId == targetConfigId && savedMapping.modelIndex == targetModelIndex ) - EnhancedAIService.refreshServiceForFunction( - context.applicationContext, - FunctionType.CHAT - ) + EnhancedAIService.refreshServiceForFunction(context.applicationContext, FunctionType.CHAT, cancelStreaming = false) true } catch (e: CancellationException) { throw e @@ -510,10 +507,7 @@ fun ModelConfigScreen( affectedFunctions.forEach { functionType -> try { - EnhancedAIService.refreshServiceForFunction( - context.applicationContext, - functionType, - ) + EnhancedAIService.refreshServiceForFunction(context.applicationContext, functionType, cancelStreaming = false) } catch (e: Exception) { AppLogger.e( "ModelConfigScreen", @@ -1138,7 +1132,7 @@ private fun ThinkingConfigurationsSection( saveMutex.withLock { withContext(Dispatchers.IO) { configManager.updateThinkingConfigurations(latestConfig.id, value) - EnhancedAIService.refreshAllServices(configManager.appContext) + EnhancedAIService.refreshAllServices(configManager.appContext, cancelStreaming = false) } } } @@ -1942,7 +1936,7 @@ private fun CustomHeadersSettingsSection( saveMutex.withLock { withContext(Dispatchers.IO) { configManager.updateCustomHeaders(latestConfig.id, serializedHeaders) - EnhancedAIService.refreshAllServices(configManager.appContext) + EnhancedAIService.refreshAllServices(configManager.appContext, cancelStreaming = false) } } } diff --git a/app/src/main/java/com/ai/assistance/operit/ui/features/settings/sections/AdvancedSettingsSection.kt b/app/src/main/java/com/ai/assistance/operit/ui/features/settings/sections/AdvancedSettingsSection.kt index e86ae6e0b4..cd1a12c128 100644 --- a/app/src/main/java/com/ai/assistance/operit/ui/features/settings/sections/AdvancedSettingsSection.kt +++ b/app/src/main/java/com/ai/assistance/operit/ui/features/settings/sections/AdvancedSettingsSection.kt @@ -106,7 +106,7 @@ fun AdvancedSettingsSection( useMultipleApiKeys = state.useMultipleApiKeys, apiKeyPool = state.apiKeyPool ) - EnhancedAIService.refreshAllServices(configManager.appContext) + EnhancedAIService.refreshAllServices(configManager.appContext, cancelStreaming = false) } } @@ -148,7 +148,7 @@ fun AdvancedSettingsSection( requestLimitPerMinute = state.requestLimitPerMinute, maxConcurrentRequests = state.maxConcurrentRequests ) - EnhancedAIService.refreshAllServices(configManager.appContext) + EnhancedAIService.refreshAllServices(configManager.appContext, cancelStreaming = false) } LaunchedEffect(config.id) { diff --git a/app/src/main/java/com/ai/assistance/operit/ui/features/settings/sections/ModelApiSettingsSection.kt b/app/src/main/java/com/ai/assistance/operit/ui/features/settings/sections/ModelApiSettingsSection.kt index 4590975e27..59dfbff7e2 100644 --- a/app/src/main/java/com/ai/assistance/operit/ui/features/settings/sections/ModelApiSettingsSection.kt +++ b/app/src/main/java/com/ai/assistance/operit/ui/features/settings/sections/ModelApiSettingsSection.kt @@ -281,9 +281,7 @@ fun ModelApiSettingsSection( enableToolCall = state.enableToolCall, ) - EnhancedAIService.refreshAllServices( - configManager.appContext - ) + EnhancedAIService.refreshAllServices(configManager.appContext, cancelStreaming = false) } } } @@ -608,7 +606,7 @@ fun ModelApiSettingsSection( scope.launch { codexAuthManager.logout() codexUsageError = false - EnhancedAIService.refreshAllServices(configManager.appContext) + EnhancedAIService.refreshAllServices(configManager.appContext, cancelStreaming = false) showNotification(context.getString(R.string.codex_logout_success)) } }, @@ -929,7 +927,7 @@ fun ModelApiSettingsSection( onLoginSuccess = { showCodexLoginDialog = false scope.launch { - EnhancedAIService.refreshAllServices(configManager.appContext) + EnhancedAIService.refreshAllServices(configManager.appContext, cancelStreaming = false) showNotification(context.getString(R.string.codex_login_success)) } }, diff --git a/app/src/main/java/com/ai/assistance/operit/ui/features/toolbox/screens/autoglm/AutoGlmOneClickToolScreen.kt b/app/src/main/java/com/ai/assistance/operit/ui/features/toolbox/screens/autoglm/AutoGlmOneClickToolScreen.kt index 9413523639..d248595a15 100644 --- a/app/src/main/java/com/ai/assistance/operit/ui/features/toolbox/screens/autoglm/AutoGlmOneClickToolScreen.kt +++ b/app/src/main/java/com/ai/assistance/operit/ui/features/toolbox/screens/autoglm/AutoGlmOneClickToolScreen.kt @@ -135,10 +135,7 @@ private fun AutoGlmOneClickScreen( configId, 0 ) - EnhancedAIService.refreshServiceForFunction( - context, - FunctionType.UI_CONTROLLER - ) + EnhancedAIService.refreshServiceForFunction(context, FunctionType.UI_CONTROLLER, cancelStreaming = false) // 自动应用 AutoGLM 推荐参数 try {