Browse Source

上传图片理解修复

weicu
liwei1dao 1 year ago
parent
commit
7e28d32a78
  1. 43
      lib/data/services/chat_history_service.dart
  2. 7
      lib/modules/agent/controllers/agent_controller.dart
  3. 94
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  4. 7
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt
  5. 3
      local_plugins/agent_service/lib/agent_service.dart
  6. 64
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiPlugin.kt
  7. 162
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt
  8. 6
      local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift
  9. 2
      local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/LocationService.kt

43
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<void> saveHistory(String agentId, List<Message> 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<void> _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<int> 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<List<Message>> 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<void> clearHistory(String agentId) async {
try {
@ -121,7 +122,7 @@ class ChatHistoryService extends GetxService {
Logger.error('清除聊天记录失败', e);
}
}
/// 清除所有聊天记录
Future<void> clearAllHistory() async {
try {
@ -131,4 +132,4 @@ class ChatHistoryService extends GetxService {
Logger.error('清除所有聊天记录失败', e);
}
}
}
}

7
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,
);

94
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<String, Any>(
"text" to response,
"userInput" to displayText
)
if (hasImage) {
responseData["hasImage"] = true
}
sendEvent("assistant_response", responseData)
if (response.isNotEmpty()) {
// 发送完整回复,包含是否有图片的标记
val responseData = mutableMapOf<String, Any>(
"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) {

7
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<String>("imageUrl")
if (imageUrl.isNullOrEmpty()) {
result.error("INVALID_ARGUMENTS", "图片Url不能为空", null)
return
}
val text = call.argument<String>("text") ?: ""
val speakResponse = call.argument<Boolean>("speakResponse") ?: false
try {
val isProcessing = AgentService.processImageInput(
imagePath = imagePath,
imageUrl = imageUrl,
text = text,
speakResponse = speakResponse
)

3
local_plugins/agent_service/lib/agent_service.dart

@ -326,11 +326,12 @@ class AgentService {
/// [text] 可选的文本描述或问题
/// [speakResponse] 是否朗读响应
/// 返回是否成功处理图片
static Future<bool> processImageInput(String imagePath,
static Future<bool> 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,
});

64
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<List<Map<String, Any>>>("messages") ?: emptyList()
val tool = call.argument<Boolean>("tool") ?: false
pluginScope.launch {
chatApiService?.chatCompletionStream(messages, tool)
}
result.success(true)
}
// "chatCompletionStream" -> {
// val messages = call.argument<List<Map<String, Any>>>("messages") ?: emptyList()
// val tool = call.argument<Boolean>("tool") ?: false
// pluginScope.launch {
// chatApiService?.chatCompletionStream(messages, tool)
// }
// result.success(true)
// }
"cancelChatStream" -> {
chatApiService?.cancelChatStream()
result.success(true)

162
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<Map<String, Any>> = emptyList()
private var toolCalls: MutableMap<Int, ToolCallInfo> = mutableMapOf()
private var isCanceled = false
private var sessions: MutableMap<String, StreamCallback?> = 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<Map<String, Any>>
val messageList = convertJsonArrayToList(messages)
currSessionId = UUID.randomUUID().toString()
sessions[currSessionId] = callback
// 调用原始方法
sendMessageStream(messageList)
sendMessageStream(currSessionId,messageList)
}
/**
* 发送消息(流式输出)
*/
fun sendMessageStream(messages: List<Map<String, Any>>) {
fun sendMessageStream(sessionid:String,messages: List<Map<String, Any>>) {
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<Map<String, Any>>,
functionCall: Map<String, Any>,
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<Map<String, Any>>, tool: Boolean = false) {
fun chatCompletionStream(sessionid: String,messages: List<Map<String, Any>>, 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<ContentPart>()
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<TextPart>()
.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<Map<String, Any>> {
val result = mutableListOf<Map<String, Any>>()
// 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"

6
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)

2
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}")
}

Loading…
Cancel
Save