diff --git a/lib/data/services/chat_history_service.dart b/lib/data/services/chat_history_service.dart index 9a1688314..e1c017902 100644 --- a/lib/data/services/chat_history_service.dart +++ b/lib/data/services/chat_history_service.dart @@ -8,50 +8,50 @@ import 'package:chat_storage/chat_storage.dart'; class ChatHistoryService extends GetxService { // 最大历史记录数量 static const int _maxHistoryCount = 200; // 每个对话最多保存的消息数 - + @override void onInit() { super.onInit(); Logger.info('ChatHistoryService 已初始化'); } - + /// 保存聊天记录 - /// + /// /// [agentId] 代理ID /// [messages] 消息列表 Future saveHistory(String agentId, List messages) async { try { if (messages.isEmpty) return; - + // 获取最新的消息 final latestMessage = messages.last; - + // 如果是加载中的消息则跳过 if (latestMessage.isIntermediate) return; - + // 直接插入最新的消息到数据库 await ChatStorage.saveMessage( sessionId: agentId, message: latestMessage.content, sender: latestMessage.role, ); - + // 检查并清理过多的历史记录 await _cleanupOldMessages(agentId); } catch (e) { Logger.error('保存聊天记录失败', e.toString()); } } - + /// 清理旧消息,保持每个会话的消息数量不超过最大限制 Future _cleanupOldMessages(String agentId) async { try { final pagedResult = await ChatStorage.getMessages( sessionId: agentId, page: 1, - pageSize: 1, // 只获取总数信息 + pageSize: 1, // 只获取总数信息 ); - + // 如果消息总数超过最大限制,删除最早的消息 if (pagedResult.totalCount > _maxHistoryCount) { final messagesToDelete = pagedResult.totalCount - _maxHistoryCount; @@ -61,13 +61,13 @@ class ChatHistoryService extends GetxService { page: 1, pageSize: messagesToDelete, ); - + // 构建要删除的消息ID列表 final List messageIds = oldestMessages.messages .where((m) => m.id != null) .map((m) => m.id!) .toList(); - + // 删除这些消息 if (messageIds.isNotEmpty) { await ChatStorage.deleteMessages(messageIds: messageIds); @@ -77,9 +77,9 @@ class ChatHistoryService extends GetxService { Logger.error('清理旧消息失败', e); } } - + /// 加载聊天记录 - /// + /// /// [agentId] 代理ID /// 返回消息列表,如果没有找到或解析错误则返回空列表 Future> loadHistory(String agentId) async { @@ -89,29 +89,30 @@ class ChatHistoryService extends GetxService { page: 1, pageSize: _maxHistoryCount, ); - + if (pagedResult.messages.isEmpty) { return []; } - + // 将ChatStorage.ChatMessage转换为Message final messages = pagedResult.messages.map((chatMessage) { return Message( role: chatMessage.sender, content: chatMessage.message, - timestamp: DateTime.parse(chatMessage.timestamp), // 直接使用chatMessage的timestamp + timestamp: DateTime.parse( + chatMessage.timestamp), // 直接使用chatMessage的timestamp // isLoading 默认false // isLoading: metadata['isLoading'] ?? false, ); }).toList(); - + return messages; } catch (e) { Logger.error('加载聊天记录失败', e); return []; } } - + /// 清除特定代理的聊天记录 Future clearHistory(String agentId) async { try { @@ -121,7 +122,7 @@ class ChatHistoryService extends GetxService { Logger.error('清除聊天记录失败', e); } } - + /// 清除所有聊天记录 Future clearAllHistory() async { try { @@ -131,4 +132,4 @@ class ChatHistoryService extends GetxService { Logger.error('清除所有聊天记录失败', e); } } -} \ No newline at end of file +} diff --git a/lib/modules/agent/controllers/agent_controller.dart b/lib/modules/agent/controllers/agent_controller.dart index c665ff063..698a44d99 100644 --- a/lib/modules/agent/controllers/agent_controller.dart +++ b/lib/modules/agent/controllers/agent_controller.dart @@ -2,6 +2,8 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:amap_service/amap_service.dart'; +import 'package:deep_voice/core/utils/upload_oss.dart'; +import 'package:deep_voice/data/models/user_Info.dart'; import 'package:deep_voice/data/services/location_manager.dart'; import 'package:deep_voice/data/services/music_manager.dart'; import 'package:deep_voice/data/services/navigation_manager.dart'; @@ -548,7 +550,9 @@ class AgentController extends GetxController { // 复制图片到应用永久存储目录 final permanentImagePath = await _copyImageToPermanentStorage(imagePath); - + var ImageUrl = await UploadOss.upload( + filepath: imagePath, rootDir: 'User/${User.instance.uid}/ChatImages'); + Logger.i(TAG, '图片上传: $imagePath URL: $ImageUrl'); // 显示的文本,如果没有提供则使用默认值 final displayText = text?.isNotEmpty == true ? text! : '[图片]'; @@ -578,6 +582,7 @@ class AgentController extends GetxController { // 调用Agent Service处理图片,考虑TTS状态 await AgentService.processImageInput( permanentImagePath, + ImageUrl, text: text ?? '', speakResponse: isTtsEnabled.value, ); diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt index ac3f5d9eb..24a3d1e74 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt @@ -576,6 +576,7 @@ object AgentService : CoroutineScope { ) { // 创建带图片的用户消息并处理 val userMessage = createUserMessageWithImage(text, imageBase64) + Log.d(TAG, "liwei--------- 图像Mesage: ${userMessage}") // 图片描述用于存储 val displayText = text.ifEmpty { "[图片]" } processWithChatApiInternal(userMessage, displayText, speakResponse, true) @@ -599,7 +600,7 @@ object AgentService : CoroutineScope { ) { // 如果有正在进行的AI流式输出,先停止它 stopAiStream() - + stopTts() // 记录新的AI任务 currentAiJob = launch { try { @@ -621,16 +622,16 @@ object AgentService : CoroutineScope { val messagesWithSystemPrompt = JSONArray() // 先添加系统提示词 - if (systemPrompt.isNotEmpty()) { - val location = LocationService.getCachedLocation()?: mapOf( - "user_location" to "定位未开启", - "user_lat" to "定位未开启", - "user_lng" to "定位未开启", - ) - val _systemPrompt = Utils.fillTemplate(systemPrompt,location) - messagesWithSystemPrompt.put(createSystemMessage(_systemPrompt)) - Log.d(TAG, "liwei--------- 系统提示词: ${_systemPrompt}") - } + if (systemPrompt.isNotEmpty()) { + val location = LocationService.getCachedLocation()?: mapOf( + "user_location" to "定位未开启", + "user_lat" to "定位未开启", + "user_lng" to "定位未开启", + ) + val _systemPrompt = Utils.fillTemplate(systemPrompt,location) + messagesWithSystemPrompt.put(createSystemMessage(_systemPrompt)) + Log.d(TAG, "liwei--------- 系统提示词: ${_systemPrompt}") + } // 再添加历史消息 for (i in 0 until historyMessages.length()) { @@ -665,7 +666,7 @@ object AgentService : CoroutineScope { // 兜底处理,直接添加原消息 addToHistoryMessages(userMessage) } - +// Log.e("ChatApiService", "liwei---------sendMessageStream messages:${messagesWithSystemPrompt}") chatApiService.sendMessageStream( messagesWithSystemPrompt, object : StreamCallback { @@ -684,25 +685,21 @@ object AgentService : CoroutineScope { ttsService?.flushStream() } val response = responseBuilder.toString() - + // 发送完整回复,包含是否有图片的标记 + val responseData = mutableMapOf( + "text" to response, + "userInput" to displayText + ) + if (hasImage) { + responseData["hasImage"] = true + } + sendEvent("assistant_response", responseData) if (response.isNotEmpty()) { - // 发送完整回复,包含是否有图片的标记 - val responseData = mutableMapOf( - "text" to response, - "userInput" to displayText - ) - if (hasImage) { - responseData["hasImage"] = true - } - sendEvent("assistant_response", responseData) - // 添加AI回复到历史记录 addToHistoryMessages(createAssistantMessage(response)) - - // 保存聊天记录 - saveChatMessage(displayText, response,aiMetadata,userMetadata.toString()) } - + // 保存聊天记录 + saveChatMessage(displayText, response,aiMetadata,userMetadata.toString()) // 标记AI流式输出已完成 isAiStreaming = false currentAiJob = null @@ -861,14 +858,15 @@ object AgentService : CoroutineScope { ) if (userMessageId != -1L) { - // 保存AI回复 - val assistantMessageId = chatStorageHelper.saveMessage( - sessionId = sessionId, - message = assistantMessage, - sender = "assistant", - metadata = aiMetadata - ) - + if (assistantMessage.isNotEmpty() ) { + // 保存AI回复 + val assistantMessageId = chatStorageHelper.saveMessage( + sessionId = sessionId, + message = assistantMessage, + sender = "assistant", + metadata = aiMetadata + ) + } // if (assistantMessageId != -1L) { // Log.d(TAG, "聊天记录已保存:用户消息ID=$userMessageId, 助手消息ID=$assistantMessageId") // } else { @@ -970,7 +968,7 @@ object AgentService : CoroutineScope { /** * 创建带图片的用户消息 */ - private fun createUserMessageWithImage(text: String, imageBase64: String): JSONObject { + private fun createUserMessageWithImage(text: String, imageUrl: String): JSONObject { val content = JSONArray().apply { if (text.isNotEmpty()) { put(JSONObject().apply { @@ -981,8 +979,8 @@ object AgentService : CoroutineScope { put(JSONObject().apply { put("type", "image_url") put("image_url", JSONObject().apply { - put("url", "data:image/jpeg;base64,$imageBase64") - put("detail", "auto") +// put("url", "data:image/jpeg;base64,$imageBase64") + put("url", imageUrl) }) }) } @@ -996,7 +994,7 @@ object AgentService : CoroutineScope { /** * 处理图片输入 */ - fun processImageInput(imagePath: String, text: String = "", speakResponse: Boolean = false): Boolean { + fun processImageInput(imagePath: String, imageUrl: String,text: String = "", speakResponse: Boolean = false): Boolean { if (!isInitialized) { Log.e(TAG, "服务未初始化") sendEvent("error", mapOf("code" to "NOT_INITIALIZED", "message" to "服务未初始化")) @@ -1019,16 +1017,16 @@ object AgentService : CoroutineScope { launch { try { // 将图片转换为Base64格式 - val imageBase64 = chatApiService.fileToBase64(imagePath) ?: run { - sendEvent("error", mapOf( - "code" to "IMAGE_CONVERSION_FAILED", - "message" to "图片转换失败" - )) - return@launch - } + // val imageBase64 = chatApiService.fileToBase64(imagePath) ?: run { + // sendEvent("error", mapOf( + // "code" to "IMAGE_CONVERSION_FAILED", + // "message" to "图片转换失败" + // )) + // return@launch + // } // 创建带图片的用户消息并处理 - val userMessage = createUserMessageWithImage(text, imageBase64) + val userMessage = createUserMessageWithImage(text, imageUrl) // 图片描述用于存储 val displayText = text.ifEmpty { "[图片]" } @@ -1037,7 +1035,7 @@ object AgentService : CoroutineScope { "status" to "ready", "imagePath" to imagePath )) - + Log.d(TAG, "liwei--------- 图像Mesage: ${userMessage}") // 处理消息并传入存储路径 processWithChatApiInternal(userMessage, displayText, speakResponse, true, imagePath) } catch (e: Exception) { diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt index cf00c7f3c..06b8a29b5 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt @@ -130,13 +130,18 @@ class AgentServicePlugin : FlutterPlugin, MethodCallHandler, EventChannel.Stream result.error("INVALID_ARGUMENTS", "图片路径不能为空", null) return } - + val imageUrl = call.argument("imageUrl") + if (imageUrl.isNullOrEmpty()) { + result.error("INVALID_ARGUMENTS", "图片Url不能为空", null) + return + } val text = call.argument("text") ?: "" val speakResponse = call.argument("speakResponse") ?: false try { val isProcessing = AgentService.processImageInput( imagePath = imagePath, + imageUrl = imageUrl, text = text, speakResponse = speakResponse ) diff --git a/local_plugins/agent_service/lib/agent_service.dart b/local_plugins/agent_service/lib/agent_service.dart index 2c53859c1..4ee6907cb 100644 --- a/local_plugins/agent_service/lib/agent_service.dart +++ b/local_plugins/agent_service/lib/agent_service.dart @@ -326,11 +326,12 @@ class AgentService { /// [text] 可选的文本描述或问题 /// [speakResponse] 是否朗读响应 /// 返回是否成功处理图片 - static Future processImageInput(String imagePath, + static Future processImageInput(String imagePath, String imageUrl, {String text = "", bool speakResponse = false}) async { try { final bool result = await _channel.invokeMethod('processImageInput', { 'imagePath': imagePath, + 'imageUrl': imageUrl, 'text': text, 'speakResponse': speakResponse, }); diff --git a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiPlugin.kt b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiPlugin.kt index c369deac9..080390e55 100644 --- a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiPlugin.kt +++ b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiPlugin.kt @@ -37,30 +37,30 @@ class ChatApiPlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamHandl chatApiService = ChatApiService(flutterPluginBinding.applicationContext) // 设置流式回调 - chatApiService?.setStreamCallback(object : StreamCallback { - override fun onToken(token: String) { - channel.invokeMethod("onToken", token) - } - - override fun onComplete() { - channel.invokeMethod("onComplete", null) - } - - override fun onError(error: Exception) { - channel.invokeMethod("onError", error.message) - } - - override fun onFunctionCall(functionCall: org.json.JSONObject) { - channel.invokeMethod("onFunctionCall", functionCall.toString()) - } - - override fun onFunctionCallResult(functionCall: org.json.JSONObject, functionCallResult: org.json.JSONObject) { - channel.invokeMethod("onFunctionCallResult", mapOf( - "functionCall" to functionCall.toString(), - "functionCallResult" to functionCallResult.toString() - )) - } - }) +// chatApiService?.setStreamCallback(object : StreamCallback { +// override fun onToken(token: String) { +// channel.invokeMethod("onToken", token) +// } +// +// override fun onComplete() { +// channel.invokeMethod("onComplete", null) +// } +// +// override fun onError(error: Exception) { +// channel.invokeMethod("onError", error.message) +// } +// +// override fun onFunctionCall(functionCall: org.json.JSONObject) { +// channel.invokeMethod("onFunctionCall", functionCall.toString()) +// } +// +// override fun onFunctionCallResult(functionCall: org.json.JSONObject, functionCallResult: org.json.JSONObject) { +// channel.invokeMethod("onFunctionCallResult", mapOf( +// "functionCall" to functionCall.toString(), +// "functionCallResult" to functionCallResult.toString() +// )) +// } +// }) } override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { @@ -84,14 +84,14 @@ class ChatApiPlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamHandl val success = chatApiService?.initialize(apiKey, baseUrl, model, mcpServer) ?: false result.success(success) } - "chatCompletionStream" -> { - val messages = call.argument>>("messages") ?: emptyList() - val tool = call.argument("tool") ?: false - pluginScope.launch { - chatApiService?.chatCompletionStream(messages, tool) - } - result.success(true) - } + // "chatCompletionStream" -> { + // val messages = call.argument>>("messages") ?: emptyList() + // val tool = call.argument("tool") ?: false + // pluginScope.launch { + // chatApiService?.chatCompletionStream(messages, tool) + // } + // result.success(true) + // } "cancelChatStream" -> { chatApiService?.cancelChatStream() result.success(true) diff --git a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt index bad3aa1a2..9e505f8ec 100644 --- a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt +++ b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt @@ -23,6 +23,7 @@ import android.util.Log import org.json.JSONObject import android.os.Handler import android.os.Looper +import java.util.UUID /** * ChatAPI服务异常 @@ -109,10 +110,12 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor // 流式请求相关 private var currentStreamJob: Job? = null - private var streamCallback: StreamCallback? = null +// private var streamCallback: StreamCallback? = null private var currentMessages: List> = emptyList() private var toolCalls: MutableMap = mutableMapOf() - private var isCanceled = false + private var sessions: MutableMap = mutableMapOf() + private var currSessionId = "" // +// private var isCanceled = false @@ -124,9 +127,9 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor /** * 设置流式回调 */ - fun setStreamCallback(callback: StreamCallback) { - streamCallback = callback - } +// fun setStreamCallback(callback: StreamCallback) { +// streamCallback = callback +// } /** * 初始化ChatAPI服务 @@ -258,32 +261,33 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor */ fun sendMessageStream(messages: org.json.JSONArray, callback: StreamCallback) { // 设置回调 - setStreamCallback(callback) +// setStreamCallback(callback) // 转换JSONArray为List> val messageList = convertJsonArrayToList(messages) - + currSessionId = UUID.randomUUID().toString() + sessions[currSessionId] = callback // 调用原始方法 - sendMessageStream(messageList) + sendMessageStream(currSessionId,messageList) } /** * 发送消息(流式输出) */ - fun sendMessageStream(messages: List>) { + fun sendMessageStream(sessionid:String,messages: List>) { if (!isInitialized || apiKey.isEmpty() || openAI == null) { Log.e("ChatApiService", "ChatAPI服务未初始化,无法发送消息") - streamCallback?.onError(ChatApiException("ChatAPI服务未初始化")) + sessions[sessionid]?.onError(ChatApiException("ChatAPI服务未初始化")) return } // 重置状态 - isCanceled = false +// isCanceled = false currentMessages = messages toolCalls.clear() - + val currentModel = model // 检查是否包含图片,决定使用哪个模型 - val currentModel = if (containsImage(messages)) visionModel else model +// val currentModel = if (containsImage(messages)) visionModel else model currentStreamJob = launch { try { @@ -323,7 +327,7 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor Log.e("ChatApiService", "openAI对象为null") throw IllegalStateException("OpenAI客户端未初始化") } - +// Log.e("ChatApiService", "liwei---------,发送AI请求 model:${currentModel} messages:${chatMessages}") val flow = openAI!!.chatCompletions(chatCompletionRequest) flow } catch (e: Exception) { @@ -332,7 +336,9 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor } chatsFlow.collect { result -> - if (isCanceled) return@collect + if (currSessionId != sessionid) { + return@collect + } val choice = result.choices.firstOrNull() if (choice == null) { @@ -348,7 +354,7 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor // 处理普通文本内容 delta.content?.let { content -> - streamCallback?.onToken(content) + sessions[sessionid]?.onToken(content) } // 收集工具调用信息 @@ -389,18 +395,20 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor } } - if (!isCanceled) { + if (currSessionId == sessionid) { // 检查是否有工具调用需要处理 - val hasToolCalls = processToolCalls() + val hasToolCalls = processToolCalls(sessionid) if (!hasToolCalls) { - streamCallback?.onComplete() + sessions[sessionid]?.onComplete() } + }else{ + sessions[sessionid]?.onComplete() } } catch (e: Exception) { - if (!isCanceled) { - streamCallback?.onError(ChatApiException("流式请求失败: ${e.message}")) - } +// if (currSessionId == sessionid) { + sessions[sessionid]?.onError(ChatApiException("流式请求失败: ${e.message}")) +// } } } } @@ -408,7 +416,7 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor /** * 处理工具调用 */ - private suspend fun processToolCalls(): Boolean { + private suspend fun processToolCalls(sessionid: String): Boolean { val firstToolCall = try { toolCalls.values.firstOrNull { it.isValid() } } catch (e: Exception) { @@ -430,11 +438,11 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor return false } // 通知上层工具调用事件 - streamCallback?.onFunctionCall(convertMapToJsonObject(functionCall)) + sessions[sessionid]?.onFunctionCall(convertMapToJsonObject(functionCall)) // 在后台队列处理工具调用 launch { try { - if (!isCanceled) { + if (currSessionId == sessionid) { // 通过MCP客户端处理工具调用 val functionName = firstToolCall.name val argumentsJson = firstToolCall.arguments @@ -471,29 +479,35 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor // 工具不存在 mapOf("context" to "Tool not found: $functionName") } - if (!isCanceled) { + if (currSessionId == sessionid) { // 处理结果 - streamCallback?.onFunctionCallResult( + sessions[sessionid]?.onFunctionCallResult( convertMapToJsonObject(functionCall), convertMapToJsonObject(result) ) // 将结果发送回OpenAI继续对话 sendFunctionCallResultInternal( + sessionid= sessionid, messages = currentMessages, functionCall = functionCall, functionResult = gson.toJson(result) ) + }else{ + sessions[sessionid]?.onComplete() } } } catch (e: Exception) { Log.e("ChatApiService", "工具调用处理过程中出错: ${e.message}", e) - if (!isCanceled) { + if (currSessionId == sessionid) { val errorMessage = "工具调用处理失败: ${e.message}" sendFunctionCallResultInternal( + sessionid= sessionid, messages = currentMessages, functionCall = functionCall, functionResult = errorMessage ) + }else{ + sessions[sessionid]?.onComplete() } } } @@ -505,11 +519,12 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor * 发送函数调用结果(内部方法) */ private suspend fun sendFunctionCallResultInternal( + sessionid: String, messages: List>, functionCall: Map, functionResult: String ) { - if (isCanceled) return + if (currSessionId != sessionid) return val fullMessages = messages.toMutableList() @@ -542,14 +557,16 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor ) // 重新发起对话,让AI基于工具调用结果生成回复 - sendMessageStream(fullMessages) + sendMessageStream(sessionid,fullMessages) } /** * 取消当前流式请求 */ fun cancelCurrentStream(): Boolean { - isCanceled = true + sessions[currSessionId]?.onComplete() + sessions.remove(currSessionId) + currSessionId = "" currentStreamJob?.cancel() currentStreamJob = null return true @@ -778,9 +795,9 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor * 聊天完成流式接口 * 与iOS版本保持一致的接口 */ - fun chatCompletionStream(messages: List>, tool: Boolean = false) { + fun chatCompletionStream(sessionid: String,messages: List>, tool: Boolean = false) { // 直接调用sendMessageStream,因为该方法已经处理了工具调用 - sendMessageStream(messages) + sendMessageStream(sessionid,messages) } // MARK: - 工具方法 @@ -893,27 +910,70 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor } is List<*> -> { // 对于多模态内容,提取文本部分(简化处理) - var textContent = "" + val contentParts = mutableListOf() content.forEach { item -> if (item is Map<*, *>) { val type = item["type"] as? String - if (type == "text") { - val text = item["text"] as? String ?: "" - textContent += text + when (type) { + "text" -> { + val text = item["text"] as? String ?: "" + contentParts.add(TextPart(text)) + } + "image_url" -> { + val imageUrlMap = item["image_url"] as? Map<*, *> + val url = imageUrlMap?.get("url") as? String ?: "" + val detail = imageUrlMap?.get("detail") as? String ?: "auto" + if (url.isNotEmpty()) { + contentParts.add(ImagePart(url, detail)) + } + } // 处理其他可能的内容类型 + else -> { + // 记录未知类型,便于调试 + Log.d("MessageParser", "Unknown content type: $type") + } } + } else { + // 非Map类型的内容,记录日志 + Log.d("MessageParser", "Item is not a Map: $item") } } - - val finalContent = if (textContent.isEmpty()) "多媒体内容" else textContent - - val chatMessage = when (chatRole) { - Role.System -> ChatMessage.System(finalContent) - Role.User -> ChatMessage.User(finalContent) - Role.Assistant -> ChatMessage.Assistant(finalContent) - else -> ChatMessage.User(finalContent) + + // 创建消息对象 + if (contentParts.isNotEmpty()) { + val chatMessage = when (chatRole) { + Role.System -> { + // 系统消息简化显示,将图片替换为[图片] + val displayText = contentParts.joinToString { part -> + when (part) { + is TextPart -> part.text // 使用TextPart + is ImagePart -> "[图片]" // 使用ImagePart + } + } + ChatMessage.System(displayText) + } + Role.User -> ChatMessage.User(contentParts) + Role.Assistant -> { + // 将多模态内容转换为ListContent + val listContent = ListContent(contentParts) + // 提取文本内容作为Assistant消息的content + val textContent = contentParts + .filterIsInstance() + .joinToString(separator = "") { it.text } + + // 调用Assistant函数,传递文本内容 + ChatMessage.Assistant( + content = textContent.ifEmpty { null }, + toolCalls = null // 如果有工具调用,需要在这里添加 + ) + } + else -> ChatMessage.User(contentParts) + } + chatMessages.add(chatMessage) + } else { + // 内容为空时的处理 + Log.d("MessageParser", "No valid content parts found") } - chatMessages.add(chatMessage) } } } @@ -1035,13 +1095,13 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor */ private fun convertJsonArrayToList(jsonArray: org.json.JSONArray): List> { val result = mutableListOf>() - +// Log.e("ChatApiService", "convertJsonArrayToList 1:${jsonArray}") for (i in 0 until jsonArray.length()) { val jsonObject = jsonArray.optJSONObject(i) ?: continue val map = convertJsonObjectToMap(jsonObject) result.add(map) } - +// Log.e("ChatApiService", "convertJsonArrayToList 2:${result}") return result } @@ -1123,9 +1183,9 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor } // 通知Flutter端处理函数调用 - mainHandler.post { - streamCallback?.onFunctionCall(functionCall) - } +// mainHandler.post { +// streamCallback?.onFunctionCall(functionCall) +// } // 返回一个标记,表示函数已被调用 return "Function '$functionName' called with arguments: $arguments" diff --git a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift index c5638966d..cf1c6a3a0 100644 --- a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift +++ b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift @@ -184,9 +184,9 @@ public class ChatApiService: NSObject { // 检查是否包含图片,决定使用哪个模型 var currentModel = model - if containsImage(in: messages) { - currentModel = visionModel - } + // if containsImage(in: messages) { + // currentModel = visionModel + // } // 转换消息格式 let chatMessages = try convertToChatMessages(messages) diff --git a/local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationService.kt b/local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationService.kt index e36eaa569..937828580 100644 --- a/local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationService.kt +++ b/local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationService.kt @@ -33,7 +33,7 @@ object LocationService { locationClient?.setLocationListener { location -> if (location != null && location.errorCode == 0) { currentLocation = location - Log.d(TAG, "liwei---------定位更新: ${location.latitude}, ${location.longitude}") + // Log.d(TAG, "liwei---------定位更新: ${location.latitude}, ${location.longitude}") } else { Log.w(TAG, "liwei---------定位失败: ${location?.errorCode} - ${location?.errorInfo}") }