diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/enhance/ConversationService.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/enhance/ConversationService.kt index 4b177442b7..ed6efbfa83 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/enhance/ConversationService.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/enhance/ConversationService.kt @@ -138,6 +138,7 @@ class ConversationService( // 获取SUMMARY功能类型的AIService实例 val summaryService = multiServiceManager.getServiceForFunction(FunctionType.SUMMARY) + val summaryThinking = multiServiceManager.getThinkingRequestForFunction(FunctionType.SUMMARY) var summaryHistory = sanitizedMessages var summaryPrompt = FunctionalPrompts.summaryUserMessage(useEnglish) val baseSummaryMetadata = @@ -273,6 +274,8 @@ class ConversationService( context = context, chatHistory = preparedHistory, modelParameters = modelParameters, + enableThinking = summaryThinking.enableThinking, + thinkingOptionId = summaryThinking.thinkingOptionId, recordTokenUsage = recordTokenUsage, ) @@ -1174,6 +1177,7 @@ ${FunctionalPrompts.translationUserPrompt(targetLanguage, text)} try { // 获取总结功能的AIService实例 val summaryService = multiServiceManager.getServiceForFunction(FunctionType.SUMMARY) + val summaryThinking = multiServiceManager.getThinkingRequestForFunction(FunctionType.SUMMARY) // 获取模型参数 val modelParameters = multiServiceManager.getModelParametersForFunction(FunctionType.SUMMARY) @@ -1182,6 +1186,8 @@ ${FunctionalPrompts.translationUserPrompt(targetLanguage, text)} context = context, chatHistory = chatHistory + PromptTurn(kind = PromptTurnKind.USER, content = descriptionPrompt), modelParameters = modelParameters, + enableThinking = summaryThinking.enableThinking, + thinkingOptionId = summaryThinking.thinkingOptionId, ) stream.collect { content -> 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..d2dd802e69 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 @@ -7,6 +7,7 @@ import com.ai.assistance.operit.api.chat.llmprovider.AIServiceFactory import com.ai.assistance.operit.api.chat.llmprovider.RateLimitedAIService import com.ai.assistance.operit.api.chat.llmprovider.RateLimiterRegistry import com.ai.assistance.operit.api.chat.llmprovider.RequestConcurrencyRegistry +import com.ai.assistance.operit.api.chat.llmprovider.ThinkingQualityMappingRegistry import com.ai.assistance.operit.data.model.FunctionType import com.ai.assistance.operit.data.model.ModelConfigData import com.ai.assistance.operit.data.model.ModelParameter @@ -77,6 +78,28 @@ class MultiServiceManager(private val context: Context) { } } + internal suspend fun getThinkingRequestForFunction(functionType: FunctionType): FunctionThinkingRequest { + val configMapping = functionalConfigManager.getConfigMappingForFunction(functionType) + val config = modelConfigManager.getModelConfigFlow(configMapping.configId).first() + val modelName = + getModelByIndex( + config.modelName, + getValidModelIndex(config.modelName, configMapping.modelIndex) + ) + val mapping = + ThinkingQualityMappingRegistry.resolve( + providerTypeId = config.apiProviderTypeId, + modelName = modelName, + apiEndpoint = config.apiEndpoint, + thinkingConfigurations = config.thinkingConfigurations + ) + return FunctionThinkingRequest( + enableThinking = configMapping.enableThinking || mapping.reasoningRequired, + thinkingOptionId = + mapping.resolveOptionId(configMapping.thinkingOptionId, config.thinkingOptionId) + ) + } + /** 获取指定功能类型的AIService */ suspend fun getServiceForFunction(functionType: FunctionType): AIService { ensureInitialized() @@ -419,3 +442,8 @@ class MultiServiceManager(private val context: Context) { } } + +internal data class FunctionThinkingRequest( + val enableThinking: Boolean, + val thinkingOptionId: String, +) diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/AIService.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/AIService.kt index b85658b00e..ec8cc1c8ff 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/AIService.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/AIService.kt @@ -62,6 +62,7 @@ interface AIService { chatHistory: List = emptyList(), modelParameters: List> = emptyList(), enableThinking: Boolean = false, + thinkingOptionId: String? = null, stream: Boolean = true, availableTools: List? = null, preserveThinkInHistory: Boolean = false, // 新增参数,控制是否保留历史中的思考过程 diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/ClaudeProvider.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/ClaudeProvider.kt index a2f4f07b08..d8104b5933 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/ClaudeProvider.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/ClaudeProvider.kt @@ -1069,6 +1069,7 @@ open class ClaudeProvider( chatHistory: List, modelParameters: List> = emptyList(), enableThinking: Boolean, + thinkingOptionId: String? = null, stream: Boolean = true, availableTools: List? = null, preserveThinkInHistory: Boolean = false @@ -1119,7 +1120,7 @@ open class ClaudeProvider( apiEndpoint = apiEndpoint, thinkingConfigurations = thinkingConfigurations, enableThinking = enableThinking, - optionId = thinkingOptionId, + optionId = thinkingOptionId ?: this.thinkingOptionId, ) // 日志输出时省略过长的tools字段 @@ -1301,6 +1302,7 @@ open class ClaudeProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean, @@ -1422,6 +1424,7 @@ open class ClaudeProvider( chatHistory, modelParameters, enableThinking, + thinkingOptionId, stream, availableTools, preserveThinkInHistory diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/DeepseekProvider.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/DeepseekProvider.kt index 4c89dc685a..f5b0f71017 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/DeepseekProvider.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/DeepseekProvider.kt @@ -107,6 +107,7 @@ class DeepseekProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean @@ -120,7 +121,7 @@ class DeepseekProvider( apiEndpoint = configuredApiEndpoint, thinkingConfigurations = thinkingConfigurations, enableThinking = enableThinking, - optionId = thinkingOptionId, + optionId = thinkingOptionId ?: this.thinkingOptionId, ) } @@ -540,6 +541,7 @@ class DeepseekProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean, @@ -551,7 +553,7 @@ class DeepseekProvider( onUsageFinalized: (suspend (attempt: Int?) -> Unit)?, ): Stream { // 直接调用父类的sendMessage实现 - return super.sendMessage(context, chatHistory, modelParameters, enableThinking, stream, availableTools, preserveThinkInHistory, onTokensUpdated, onUsageReported, onNonFatalError, enableRetry, recordTokenUsage, onUsageFinalized) + return super.sendMessage(context, chatHistory, modelParameters, enableThinking, thinkingOptionId, stream, availableTools, preserveThinkInHistory, onTokensUpdated, onUsageReported, onNonFatalError, enableRetry, recordTokenUsage, onUsageFinalized) } } @@ -1317,6 +1319,7 @@ private class DeepseekResponsesProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean @@ -1345,7 +1348,7 @@ private class DeepseekResponsesProvider( apiEndpoint = responsesApiEndpoint, thinkingConfigurations = thinkingConfigurations, enableThinking = enableThinking, - optionId = thinkingOptionId, + optionId = thinkingOptionId ?: this.thinkingOptionId, ) return createJsonRequestBody(requestJson.toString()) } diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/DoubaoAIProvider.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/DoubaoAIProvider.kt index 0b63c07c33..9178dd3503 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/DoubaoAIProvider.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/DoubaoAIProvider.kt @@ -49,6 +49,7 @@ class DoubaoAIProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean @@ -65,7 +66,7 @@ class DoubaoAIProvider( apiEndpoint = configuredApiEndpoint, thinkingConfigurations = thinkingConfigurations, enableThinking = enableThinking, - optionId = thinkingOptionId, + optionId = thinkingOptionId ?: this.thinkingOptionId, ) return createJsonRequestBody(jsonObject.toString()) diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/GeminiProvider.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/GeminiProvider.kt index ee306d7846..85131dde8f 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/GeminiProvider.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/GeminiProvider.kt @@ -1166,6 +1166,7 @@ open class GeminiProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean, @@ -1236,7 +1237,7 @@ open class GeminiProvider( ) } - val requestBody = createRequestBody(context, chatHistory, modelParameters, enableThinking, availableTools, preserveThinkInHistory) + val requestBody = createRequestBody(context, chatHistory, modelParameters, enableThinking, thinkingOptionId, availableTools, preserveThinkInHistory) onTokensUpdated( tokenCacheManager.totalInputTokenCount, tokenCacheManager.cachedInputTokenCount, @@ -1351,6 +1352,7 @@ open class GeminiProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String? = null, availableTools: List? = null, preserveThinkInHistory: Boolean = false ): RequestBody { @@ -1456,7 +1458,7 @@ open class GeminiProvider( apiEndpoint = apiEndpoint, thinkingConfigurations = thinkingConfigurations, enableThinking = enableThinking, - optionId = thinkingOptionId, + optionId = thinkingOptionId ?: this.thinkingOptionId, ) val jsonString = json.toString() @@ -2218,12 +2220,11 @@ open class GeminiProvider( // 提供一个通用的系统提示,以防止某些需要它的模型出现错误。 val testHistory = listOf("system" to "You are a helpful assistant.").toPromptTurns() val stream = sendMessage( - context, - testHistory + PromptTurn(kind = PromptTurnKind.USER, content = "Hi"), - emptyList(), - false, - false, - null, + context = context, + chatHistory = testHistory + PromptTurn(kind = PromptTurnKind.USER, content = "Hi"), + modelParameters = emptyList(), + enableThinking = false, + stream = false, onTokensUpdated = { _, _, _ -> }, onUsageReported = null, onNonFatalError = {}, diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/KimiProvider.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/KimiProvider.kt index 9dc1e03dea..b17449fde0 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/KimiProvider.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/KimiProvider.kt @@ -53,6 +53,7 @@ open class KimiProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean @@ -66,7 +67,7 @@ open class KimiProvider( apiEndpoint = configuredApiEndpoint, thinkingConfigurations = thinkingConfigurations, enableThinking = enableThinking, - optionId = thinkingOptionId, + optionId = thinkingOptionId ?: this.thinkingOptionId, ) } @@ -483,6 +484,7 @@ open class KimiProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean, @@ -498,6 +500,7 @@ open class KimiProvider( chatHistory, modelParameters, enableThinking, + thinkingOptionId, stream, availableTools, preserveThinkInHistory, diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/LlamaProvider.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/LlamaProvider.kt index bbffa4e792..a62d21ff54 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/LlamaProvider.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/LlamaProvider.kt @@ -167,6 +167,7 @@ class LlamaProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean, diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/MNNProvider.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/MNNProvider.kt index ac4620d63f..73a2e1c2d7 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/MNNProvider.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/MNNProvider.kt @@ -601,6 +601,7 @@ class MNNProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean, diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/NvidiaAIProvider.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/NvidiaAIProvider.kt index 9e7c57389f..3931c2b0c6 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/NvidiaAIProvider.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/NvidiaAIProvider.kt @@ -51,6 +51,7 @@ class NvidiaAIProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean @@ -73,7 +74,7 @@ class NvidiaAIProvider( apiEndpoint = configuredApiEndpoint, thinkingConfigurations = thinkingConfigurations, enableThinking = enableThinking, - optionId = thinkingOptionId, + optionId = thinkingOptionId ?: this.thinkingOptionId, ) return createJsonRequestBody(jsonObject.toString()) diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenAIProvider.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenAIProvider.kt index b8890bac1f..c72c03e496 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenAIProvider.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenAIProvider.kt @@ -579,6 +579,7 @@ open class OpenAIProvider( chatHistory: List, modelParameters: List> = emptyList(), enableThinking: Boolean = false, + thinkingOptionId: String? = null, stream: Boolean = true, availableTools: List? = null, preserveThinkInHistory: Boolean = false @@ -594,7 +595,7 @@ open class OpenAIProvider( apiEndpoint = apiEndpoint, thinkingConfigurations = thinkingConfigurations, enableThinking = enableThinking, - optionId = thinkingOptionId, + optionId = thinkingOptionId ?: this.thinkingOptionId, ) return createJsonRequestBody(requestJson.toString()) } @@ -3209,6 +3210,7 @@ open class OpenAIProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean, @@ -3269,6 +3271,7 @@ open class OpenAIProvider( currentHistory, modelParameters, enableThinking, + thinkingOptionId, stream, availableTools, preserveThinkInHistory diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenAIResponsesProvider.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenAIResponsesProvider.kt index 0f490ce333..ad21ff263f 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenAIResponsesProvider.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenAIResponsesProvider.kt @@ -51,6 +51,7 @@ open class OpenAIResponsesProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean @@ -119,7 +120,7 @@ open class OpenAIResponsesProvider( apiEndpoint = responsesApiEndpoint, thinkingConfigurations = thinkingConfigurations, enableThinking = enableThinking, - optionId = thinkingOptionId, + optionId = thinkingOptionId ?: this.thinkingOptionId, ) } diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenCodeProvider.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenCodeProvider.kt index e1f952f10f..515af154de 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenCodeProvider.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenCodeProvider.kt @@ -46,6 +46,7 @@ class OpenCodeProvider private constructor( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean, @@ -62,7 +63,7 @@ class OpenCodeProvider private constructor( apiEndpoint = baseEndpoint, thinkingConfigurations = thinkingConfigurations, enableThinking = enableThinking, - optionId = thinkingOptionId, + optionId = thinkingOptionId ?: this.thinkingOptionId, protocol = protocol, ) val thinkingEnabled = enableThinking || thinkingMapping.reasoningRequired @@ -244,6 +245,7 @@ internal class OpenCodeChatProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean @@ -323,6 +325,7 @@ internal class OpenCodeResponsesProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenRouterProvider.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenRouterProvider.kt index 3b0a86bdc6..ce23ba241c 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenRouterProvider.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/OpenRouterProvider.kt @@ -56,6 +56,7 @@ open class OpenRouterProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean @@ -104,7 +105,7 @@ open class OpenRouterProvider( apiEndpoint = openRouterApiEndpoint, thinkingConfigurations = thinkingConfigurations, enableThinking = enableThinking, - optionId = thinkingOptionId, + optionId = thinkingOptionId ?: this.thinkingOptionId, ) } diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/QwenAIProvider.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/QwenAIProvider.kt index cf10716983..3ca29273df 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/QwenAIProvider.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/QwenAIProvider.kt @@ -59,6 +59,7 @@ class QwenAIProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean @@ -100,7 +101,7 @@ class QwenAIProvider( apiEndpoint = configuredApiEndpoint, thinkingConfigurations = thinkingConfigurations, enableThinking = enableThinking, - optionId = thinkingOptionId, + optionId = thinkingOptionId ?: this.thinkingOptionId, ) } @@ -109,6 +110,7 @@ class QwenAIProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean, @@ -120,6 +122,6 @@ class QwenAIProvider( onUsageFinalized: (suspend (attempt: Int?) -> Unit)?, ): Stream { // 直接调用父类的sendMessage实现,它已经包含了续写逻辑和stream参数处理 - return super.sendMessage(context, chatHistory, modelParameters, enableThinking, stream, availableTools, preserveThinkInHistory, onTokensUpdated, onUsageReported, onNonFatalError, enableRetry, recordTokenUsage, onUsageFinalized) + return super.sendMessage(context, chatHistory, modelParameters, enableThinking, thinkingOptionId, stream, availableTools, preserveThinkInHistory, onTokensUpdated, onUsageReported, onNonFatalError, enableRetry, recordTokenUsage, onUsageFinalized) } } diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/RateLimitedAIService.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/RateLimitedAIService.kt index 7258ac52ac..49f48efc69 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/RateLimitedAIService.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/RateLimitedAIService.kt @@ -33,6 +33,7 @@ class RateLimitedAIService( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean, @@ -94,6 +95,7 @@ class RateLimitedAIService( chatHistory = chatHistory, modelParameters = modelParameters, enableThinking = enableThinking, + thinkingOptionId = thinkingOptionId, stream = stream, availableTools = availableTools, preserveThinkInHistory = preserveThinkInHistory, diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/ThinkingQualityMapping.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/ThinkingQualityMapping.kt index 7198bafafd..755452eae6 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/ThinkingQualityMapping.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/ThinkingQualityMapping.kt @@ -49,6 +49,15 @@ internal data class ThinkingQualityMapping( fun optionFor(id: String): ThinkingQualityOption? = options.firstOrNull { it.id == id } fun textValueFor(id: String): String? = (optionFor(id)?.wireValue as? ThinkingQualityWireValue.Text)?.value fun numberValueFor(id: String): Int? = (optionFor(id)?.wireValue as? ThinkingQualityWireValue.Number)?.value + + fun resolveOptionId(preferred: String, fallback: String = ""): String { + if (control != ThinkingQualityControl.LEVELS) { + return preferred.ifBlank { fallback } + } + if (preferred.isNotBlank() && optionFor(preferred) != null) return preferred + if (fallback.isNotBlank() && optionFor(fallback) != null) return fallback + return options.firstOrNull()?.id.orEmpty() + } } internal object ThinkingQualityMappingRegistry { diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/TokenTrackingAIService.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/TokenTrackingAIService.kt index a7fce6eae1..c2968a761a 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/TokenTrackingAIService.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/TokenTrackingAIService.kt @@ -63,6 +63,7 @@ class TokenTrackingAIService( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean, @@ -97,6 +98,7 @@ class TokenTrackingAIService( chatHistory = chatHistory, modelParameters = modelParameters, enableThinking = enableThinking, + thinkingOptionId = thinkingOptionId, stream = stream, availableTools = availableTools, preserveThinkInHistory = preserveThinkInHistory, @@ -113,6 +115,7 @@ class TokenTrackingAIService( chatHistory = chatHistory, modelParameters = modelParameters, enableThinking = enableThinking, + thinkingOptionId = thinkingOptionId, stream = stream, availableTools = availableTools, preserveThinkInHistory = preserveThinkInHistory, diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/ToolPkgJsAiProviderService.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/ToolPkgJsAiProviderService.kt index 92ba59459c..3e2e9736b3 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/ToolPkgJsAiProviderService.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/ToolPkgJsAiProviderService.kt @@ -116,6 +116,7 @@ internal class ToolPkgJsAiProviderService( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean, @@ -142,6 +143,7 @@ internal class ToolPkgJsAiProviderService( availableTools?.let { tools -> JSONArray(tools.map(::serializeToolPrompt)) } ) put("enableThinking", enableThinking) + put("thinkingOptionId", thinkingOptionId ?: JSONObject.NULL) put("stream", stream) put("preserveThinkInHistory", preserveThinkInHistory) put("enableRetry", enableRetry) diff --git a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/XaiProvider.kt b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/XaiProvider.kt index 6564879a5b..c3515ae243 100644 --- a/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/XaiProvider.kt +++ b/app/src/main/java/com/ai/assistance/operit/api/chat/llmprovider/XaiProvider.kt @@ -44,6 +44,7 @@ class XaiProvider( chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean @@ -67,7 +68,7 @@ class XaiProvider( apiEndpoint = configuredApiEndpoint, thinkingConfigurations = thinkingConfigurations, enableThinking = enableThinking, - optionId = thinkingOptionId, + optionId = thinkingOptionId ?: this.thinkingOptionId, ) return createJsonRequestBody(requestJson.toString()) diff --git a/app/src/main/java/com/ai/assistance/operit/data/preferences/FunctionalConfigManager.kt b/app/src/main/java/com/ai/assistance/operit/data/preferences/FunctionalConfigManager.kt index 1c1cf5b9a5..b64fb758db 100644 --- a/app/src/main/java/com/ai/assistance/operit/data/preferences/FunctionalConfigManager.kt +++ b/app/src/main/java/com/ai/assistance/operit/data/preferences/FunctionalConfigManager.kt @@ -31,7 +31,9 @@ private val Context.functionalConfigDataStore: DataStore by @Serializable data class FunctionConfigMapping( val configId: String = FunctionalConfigManager.DEFAULT_CONFIG_ID, - val modelIndex: Int = 0 + val modelIndex: Int = 0, + val enableThinking: Boolean = false, + val thinkingOptionId: String = "" ) internal data class FunctionConfigMappingRepair( @@ -181,7 +183,20 @@ class FunctionalConfigManager(private val context: Context) { // 设置指定功能的配置ID和模型索引 suspend fun setConfigForFunction(functionType: FunctionType, configId: String, modelIndex: Int) { val mapping = functionConfigMappingWithIndexFlow.first().toMutableMap() - mapping[functionType] = FunctionConfigMapping(configId, modelIndex) + val current = mapping[functionType] ?: FunctionConfigMapping(DEFAULT_CONFIG_ID, 0) + mapping[functionType] = current.copy(configId = configId, modelIndex = modelIndex) + saveFunctionConfigMappingWithIndex(mapping) + } + + suspend fun setThinkingForFunction( + functionType: FunctionType, + enableThinking: Boolean, + thinkingOptionId: String + ) { + val mapping = functionConfigMappingWithIndexFlow.first().toMutableMap() + val current = mapping[functionType] ?: FunctionConfigMapping(DEFAULT_CONFIG_ID, 0) + mapping[functionType] = + current.copy(enableThinking = enableThinking, thinkingOptionId = thinkingOptionId) saveFunctionConfigMappingWithIndex(mapping) } 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 4ee8ab2cda..5a8e15caff 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 @@ -27,6 +27,8 @@ import com.ai.assistance.operit.api.chat.llmprovider.AIServiceFactory import com.ai.assistance.operit.api.chat.llmprovider.MediaCapabilityProbe import com.ai.assistance.operit.api.chat.llmprovider.MediaLinkBuilder import com.ai.assistance.operit.api.chat.llmprovider.ModelConnectionTestOutcome +import com.ai.assistance.operit.api.chat.llmprovider.ThinkingQualityControl +import com.ai.assistance.operit.api.chat.llmprovider.ThinkingQualityMappingRegistry import com.ai.assistance.operit.ui.features.settings.components.ExpandableStatusText import com.ai.assistance.operit.api.chat.EnhancedAIService import com.ai.assistance.operit.core.chat.hooks.PromptTurn @@ -172,6 +174,7 @@ fun FunctionalConfigScreen( functionType = functionType, currentConfig = currentConfig, currentModelIndex = currentConfigMapping.modelIndex, + currentMapping = currentConfigMapping, availableConfigs = configSummaries, onConfigSelected = { configId, modelIndex -> scope.launch { @@ -187,6 +190,16 @@ fun FunctionalConfigScreen( ) showSaveSuccess = true } + }, + onThinkingChanged = { enableThinking, optionId -> + scope.launch { + functionalConfigManager.setThinkingForFunction( + functionType, + enableThinking, + optionId + ) + showSaveSuccess = true + } } ) @@ -266,8 +279,10 @@ fun FunctionConfigCard( functionType: FunctionType, currentConfig: ModelConfigSummary?, currentModelIndex: Int, + currentMapping: FunctionConfigMapping, availableConfigs: List, - onConfigSelected: (String, Int) -> Unit + onConfigSelected: (String, Int) -> Unit, + onThinkingChanged: (Boolean, String) -> Unit = { _, _ -> } ) { var expanded by remember { mutableStateOf(false) } var expandedConfigId by remember { mutableStateOf(null) } // 记录当前展开的配置的模型列表 @@ -783,6 +798,15 @@ fun FunctionConfigCard( } } } + + if (functionType == FunctionType.SUMMARY) { + SummaryThinkingControls( + currentConfig = currentConfig, + currentModelIndex = currentModelIndex, + currentMapping = currentMapping, + onThinkingChanged = onThinkingChanged + ) + } } // 配置列表 @@ -1059,3 +1083,96 @@ private data class FunctionTestDisplay( } } } + +@Composable +private fun SummaryThinkingControls( + currentConfig: ModelConfigSummary?, + currentModelIndex: Int, + currentMapping: FunctionConfigMapping, + onThinkingChanged: (Boolean, String) -> Unit +) { + val config = currentConfig ?: return + val modelName = getModelByIndex(config.modelName, currentModelIndex).ifBlank { config.modelName } + val mapping = + ThinkingQualityMappingRegistry.resolve( + providerTypeId = config.apiProviderTypeId, + modelName = modelName, + apiEndpoint = config.apiEndpoint, + thinkingConfigurations = config.thinkingConfigurations + ) + if (mapping.control == ThinkingQualityControl.UNSUPPORTED) return + + val selectedOptionId = mapping.resolveOptionId(currentMapping.thinkingOptionId, config.thinkingOptionId) + val thinkingEnabled = currentMapping.enableThinking || mapping.reasoningRequired + var menuExpanded by remember(config.id, modelName, mapping.control) { mutableStateOf(false) } + val selectedLabel = + mapping.optionFor(selectedOptionId)?.displayLabel + ?: selectedOptionId.ifBlank { stringResource(id = R.string.function_summary_thinking_default_level) } + + Spacer(modifier = Modifier.height(8.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + OutlinedButton( + onClick = { + if (!mapping.reasoningRequired) { + onThinkingChanged(!thinkingEnabled, selectedOptionId) + } + }, + enabled = !mapping.reasoningRequired, + modifier = Modifier.height(32.dp), + contentPadding = PaddingValues(horizontal = 8.dp), + shape = RoundedCornerShape(4.dp) + ) { + Text( + text = + if (thinkingEnabled) stringResource(id = R.string.function_summary_thinking_on) + else stringResource(id = R.string.function_summary_thinking_off), + style = MaterialTheme.typography.bodySmall + ) + } + + if (mapping.control == ThinkingQualityControl.LEVELS && mapping.options.isNotEmpty()) { + Box { + OutlinedButton( + onClick = { menuExpanded = true }, + enabled = thinkingEnabled, + modifier = Modifier.height(32.dp), + contentPadding = PaddingValues(horizontal = 8.dp), + shape = RoundedCornerShape(4.dp) + ) { + Text(text = selectedLabel, style = MaterialTheme.typography.bodySmall) + Spacer(modifier = Modifier.width(2.dp)) + Icon( + imageVector = + if (menuExpanded) Icons.Default.KeyboardArrowUp + else Icons.Default.KeyboardArrowDown, + contentDescription = stringResource(id = R.string.function_summary_thinking_level), + modifier = Modifier.size(14.dp) + ) + } + DropdownMenu( + expanded = menuExpanded, + onDismissRequest = { menuExpanded = false } + ) { + mapping.options.forEach { option -> + DropdownMenuItem( + text = { + Text( + text = option.displayLabel, + style = MaterialTheme.typography.bodySmall + ) + }, + onClick = { + menuExpanded = false + onThinkingChanged(true, option.id) + } + ) + } + } + } + } + } +} diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 03f25df057..9d3b1e1b42 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -2950,6 +2950,10 @@ Main chat function for daily conversation interaction with users Generates a short, accurate conversation title from the first user message. Compress context and generate chat summaries + Think on + Think off + Thinking level + Default Process memories and store them in the memory library Process intelligent binding and mixing of file content, providing more precise code processing capabilities. Recommend using fast, inexpensive models like Gemini Flash, not recommended to use DeepSeek R1 Used for automatic traversal and operation of UI interfaces. The corresponding UI controller model is only activated when the user explicitly requests to use an "agent" for control, for example by choosing dedicated models such as autoglm-phone-9b. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b091493d12..1e9309d077 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3033,6 +3033,10 @@ 主要的对话功能,用于与用户进行日常对话交互 根据用户第一条消息自动生成简短、准确的对话标题 压缩上下文,生成聊天摘要 + 思考开 + 思考关 + 思考档位 + 默认 处理记忆并存放到记忆库 处理文件内容的智能绑定与混合,提供更精确的代码处理能力。建议使用快速的廉价模型如Gemini Flash,不建议使用DeepSeek R1 用于UI界面的自动遍历与操作,对应的 UI 控制器模型仅在用户明确要求使用“智能体”控制时才会启用,例如可选择 autoglm 等专用模型。 diff --git a/app/src/test/java/com/ai/assistance/operit/api/chat/llmprovider/ColdStreamCancellationTest.kt b/app/src/test/java/com/ai/assistance/operit/api/chat/llmprovider/ColdStreamCancellationTest.kt index cee7132bd0..8fc78ad8db 100644 --- a/app/src/test/java/com/ai/assistance/operit/api/chat/llmprovider/ColdStreamCancellationTest.kt +++ b/app/src/test/java/com/ai/assistance/operit/api/chat/llmprovider/ColdStreamCancellationTest.kt @@ -105,6 +105,7 @@ class ColdStreamCancellationTest { chatHistory: List, modelParameters: List>, enableThinking: Boolean, + thinkingOptionId: String?, stream: Boolean, availableTools: List?, preserveThinkInHistory: Boolean, diff --git a/app/src/test/java/com/ai/assistance/operit/api/chat/llmprovider/ThinkingQualityMappingTest.kt b/app/src/test/java/com/ai/assistance/operit/api/chat/llmprovider/ThinkingQualityMappingTest.kt index c7d8da689b..0c12b2ccba 100644 --- a/app/src/test/java/com/ai/assistance/operit/api/chat/llmprovider/ThinkingQualityMappingTest.kt +++ b/app/src/test/java/com/ai/assistance/operit/api/chat/llmprovider/ThinkingQualityMappingTest.kt @@ -102,4 +102,17 @@ class ThinkingQualityMappingTest { assertEquals(ThinkingQualityControl.UNSUPPORTED, mapping.control) } + + @Test + fun resolveOptionIdFallsBackToFirstLevelWhenBlank() { + val mapping = mapping(ApiProviderType.OPENAI, "gpt-5.6-luna") + assertEquals(mapping.options.first().id, mapping.resolveOptionId("", "")) + } + + @Test + fun resolveOptionIdKeepsPreferredWhenSupported() { + val mapping = mapping(ApiProviderType.OPENAI, "gpt-5.6-luna") + val preferred = mapping.options.last().id + assertEquals(preferred, mapping.resolveOptionId(preferred, mapping.options.first().id)) + } } diff --git a/app/src/test/java/com/ai/assistance/operit/data/preferences/FunctionalConfigMappingRepairTest.kt b/app/src/test/java/com/ai/assistance/operit/data/preferences/FunctionalConfigMappingRepairTest.kt index e86d28ffc1..529e4d5cb7 100644 --- a/app/src/test/java/com/ai/assistance/operit/data/preferences/FunctionalConfigMappingRepairTest.kt +++ b/app/src/test/java/com/ai/assistance/operit/data/preferences/FunctionalConfigMappingRepairTest.kt @@ -12,9 +12,7 @@ class FunctionalConfigMappingRepairTest { FunctionType.SUMMARY to FunctionConfigMapping("default", 2), FunctionType.TITLE_GENERATION to FunctionConfigMapping("deleted", 1), ) - val repair = remapDeletedConfigReferences(mapping, "deleted") - assertEquals( listOf(FunctionType.CHAT, FunctionType.TITLE_GENERATION), repair.affectedFunctions, @@ -30,10 +28,21 @@ class FunctionalConfigMappingRepairTest { FunctionType.CHAT to FunctionConfigMapping("default", 0), FunctionType.SUMMARY to FunctionConfigMapping("other", 2), ) - val repair = remapDeletedConfigReferences(mapping, "deleted") - assertEquals(emptyList(), repair.affectedFunctions) assertEquals(mapping, repair.mapping) } + + @Test + fun keepsThinkingFieldsWhenOnlyConfigIdAndIndexAreCopied() { + val mapping = FunctionConfigMapping( + configId = "old", + modelIndex = 2, + enableThinking = true, + thinkingOptionId = "high", + ) + val updated = mapping.copy(configId = "new", modelIndex = 0) + assertEquals(true, updated.enableThinking) + assertEquals("high", updated.thinkingOptionId) + } }