|
|
@ -68,7 +68,13 @@ private data class ToolCallInfo( |
|
|
var name: String = "", |
|
|
var name: String = "", |
|
|
var arguments: String = "" |
|
|
var arguments: String = "" |
|
|
) { |
|
|
) { |
|
|
fun isValid(): Boolean = id.isNotEmpty() && name.isNotEmpty() |
|
|
fun isValid(): Boolean { |
|
|
|
|
|
return try { |
|
|
|
|
|
id.isNotEmpty() && name.isNotEmpty() |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
false |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -108,6 +114,8 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
private var toolCalls: MutableMap<Int, ToolCallInfo> = mutableMapOf() |
|
|
private var toolCalls: MutableMap<Int, ToolCallInfo> = mutableMapOf() |
|
|
private var isCanceled = false |
|
|
private var isCanceled = false |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// JSON处理 |
|
|
// JSON处理 |
|
|
private val gson = Gson() |
|
|
private val gson = Gson() |
|
|
|
|
|
|
|
|
@ -124,13 +132,6 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
* 初始化ChatAPI服务 |
|
|
* 初始化ChatAPI服务 |
|
|
*/ |
|
|
*/ |
|
|
fun initialize(apiKey: String, baseUrl: String, model: String, mcpServer: String): Boolean { |
|
|
fun initialize(apiKey: String, baseUrl: String, model: String, mcpServer: String): Boolean { |
|
|
Log.d("ChatApiService", "=== ChatApiService 初始化开始 ===") |
|
|
|
|
|
Log.d("ChatApiService", "参数检查:") |
|
|
|
|
|
Log.d("ChatApiService", " - apiKey: ${if (apiKey.isNotEmpty()) "已提供(${apiKey.length}字符)" else "未提供"}") |
|
|
|
|
|
Log.d("ChatApiService", " - baseUrl: '$baseUrl'") |
|
|
|
|
|
Log.d("ChatApiService", " - model: '$model'") |
|
|
|
|
|
Log.d("ChatApiService", " - mcpServer: '$mcpServer'") |
|
|
|
|
|
|
|
|
|
|
|
this.apiKey = apiKey |
|
|
this.apiKey = apiKey |
|
|
if (baseUrl.isNotEmpty()) { |
|
|
if (baseUrl.isNotEmpty()) { |
|
|
this.baseUrl = baseUrl |
|
|
this.baseUrl = baseUrl |
|
|
@ -138,7 +139,6 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
if (model.isNotEmpty()) { |
|
|
if (model.isNotEmpty()) { |
|
|
this.model = model |
|
|
this.model = model |
|
|
} |
|
|
} |
|
|
Log.d("ChatApiService", "原始 baseUrl: $baseUrl") |
|
|
|
|
|
|
|
|
|
|
|
// 处理 baseUrl:移除末尾的 /chat/completions(如果存在) |
|
|
// 处理 baseUrl:移除末尾的 /chat/completions(如果存在) |
|
|
// 因为 openai-kotlin 会自动拼接 /chat/completions |
|
|
// 因为 openai-kotlin 会自动拼接 /chat/completions |
|
|
@ -153,24 +153,16 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
processedBaseUrl += "/" |
|
|
processedBaseUrl += "/" |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Log.d("ChatApiService", "处理后 baseUrl: $processedBaseUrl") |
|
|
|
|
|
|
|
|
|
|
|
return try { |
|
|
return try { |
|
|
Log.d("ChatApiService", "开始创建OpenAI配置...") |
|
|
|
|
|
// 创建OpenAI配置 |
|
|
// 创建OpenAI配置 |
|
|
val config = OpenAIConfig( |
|
|
val config = OpenAIConfig( |
|
|
token = apiKey, |
|
|
token = apiKey, |
|
|
timeout = Timeout(socket = 60.seconds), |
|
|
timeout = Timeout(socket = 60.seconds), |
|
|
host = OpenAIHost(baseUrl = processedBaseUrl) |
|
|
host = OpenAIHost(baseUrl = processedBaseUrl) |
|
|
) |
|
|
) |
|
|
Log.d("ChatApiService", "OpenAI配置创建成功") |
|
|
|
|
|
|
|
|
|
|
|
Log.d("ChatApiService", "正在创建OpenAI客户端...") |
|
|
|
|
|
openAI = OpenAI(config) |
|
|
openAI = OpenAI(config) |
|
|
Log.d("ChatApiService", "OpenAI客户端创建成功") |
|
|
|
|
|
|
|
|
|
|
|
// 初始化MCP客户端 |
|
|
|
|
|
Log.d("ChatApiService", "准备初始化MCP客户端...") |
|
|
|
|
|
// 保存MCP配置以便后续使用 |
|
|
// 保存MCP配置以便后续使用 |
|
|
mcpConfigJson = mcpServer |
|
|
mcpConfigJson = mcpServer |
|
|
// 异步初始化MCP客户端 |
|
|
// 异步初始化MCP客户端 |
|
|
@ -179,7 +171,6 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
isInitialized = apiKey.isNotEmpty() |
|
|
isInitialized = apiKey.isNotEmpty() |
|
|
Log.d("ChatApiService", "ChatApiService初始化完成,isInitialized: $isInitialized") |
|
|
|
|
|
true |
|
|
true |
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
Log.e("ChatApiService", "ChatApiService初始化失败: ${e.message}", e) |
|
|
Log.e("ChatApiService", "ChatApiService初始化失败: ${e.message}", e) |
|
|
@ -231,72 +222,8 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
// 转换消息格式 |
|
|
// 转换消息格式 |
|
|
val chatMessages = convertToChatMessages(messages) |
|
|
val chatMessages = convertToChatMessages(messages) |
|
|
|
|
|
|
|
|
// 获取MCP工具列表 |
|
|
// 直接获取工具列表 |
|
|
val tools = mutableListOf<Tool>() |
|
|
val tools = getOpenAiTools() |
|
|
mcpClient?.getToolMaps()?.forEach { toolMap -> |
|
|
|
|
|
try { |
|
|
|
|
|
Log.d("ChatApiService", "处理工具映射: $toolMap") |
|
|
|
|
|
|
|
|
|
|
|
val type = toolMap["type"] as? String |
|
|
|
|
|
if (type == "function") { |
|
|
|
|
|
@Suppress("UNCHECKED_CAST") |
|
|
|
|
|
val functionMap = toolMap["function"] as? Map<String, Any> |
|
|
|
|
|
if (functionMap == null) { |
|
|
|
|
|
Log.e("ChatApiService", "工具function映射为null") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val name = functionMap["name"] as? String |
|
|
|
|
|
if (name == null) { |
|
|
|
|
|
Log.e("ChatApiService", "工具name为null") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val description = functionMap["description"] as? String ?: "" |
|
|
|
|
|
val parametersMap = functionMap["parameters"] as? Map<String, Any> |
|
|
|
|
|
|
|
|
|
|
|
Log.d("ChatApiService", "工具信息 - 名称: $name, 描述: $description") |
|
|
|
|
|
Log.d("ChatApiService", "参数映射: $parametersMap") |
|
|
|
|
|
|
|
|
|
|
|
if (parametersMap == null) { |
|
|
|
|
|
Log.e("ChatApiService", "工具 $name 的parameters为null") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 验证parametersMap的基本结构 |
|
|
|
|
|
if (!parametersMap.containsKey("type")) { |
|
|
|
|
|
Log.e("ChatApiService", "工具 $name 的parameters缺少type字段") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val parametersJson = gson.toJson(parametersMap) |
|
|
|
|
|
Log.d("ChatApiService", "参数JSON: $parametersJson") |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
val parameters = com.aallam.openai.api.core.Parameters.fromJsonString(parametersJson) |
|
|
|
|
|
Log.d("ChatApiService", "成功创建Parameters对象") |
|
|
|
|
|
|
|
|
|
|
|
tools.add(Tool.function( |
|
|
|
|
|
name = name, |
|
|
|
|
|
description = description, |
|
|
|
|
|
parameters = parameters |
|
|
|
|
|
)) |
|
|
|
|
|
|
|
|
|
|
|
Log.d("ChatApiService", "成功添加工具: $name") |
|
|
|
|
|
|
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e("ChatApiService", "创建Parameters对象失败 for 工具 $name: ${e.message}", e) |
|
|
|
|
|
Log.e("ChatApiService", "失败的参数JSON: $parametersJson") |
|
|
|
|
|
// 跳过这个工具,继续处理其他工具 |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
Log.d("ChatApiService", "跳过非function类型的工具: $type") |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e("ChatApiService", "处理工具映射时出错: ${e.message}", e) |
|
|
|
|
|
Log.e("ChatApiService", "出错的工具映射: $toolMap") |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 构建请求 |
|
|
// 构建请求 |
|
|
val chatCompletionRequest = ChatCompletionRequest( |
|
|
val chatCompletionRequest = ChatCompletionRequest( |
|
|
@ -344,12 +271,6 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
* 发送消息(流式输出) |
|
|
* 发送消息(流式输出) |
|
|
*/ |
|
|
*/ |
|
|
fun sendMessageStream(messages: List<Map<String, Any>>) { |
|
|
fun sendMessageStream(messages: List<Map<String, Any>>) { |
|
|
Log.d("ChatApiService", "=== sendMessageStream 开始 ===") |
|
|
|
|
|
Log.d("ChatApiService", "isInitialized: $isInitialized") |
|
|
|
|
|
Log.d("ChatApiService", "apiKey isEmpty: ${apiKey.isEmpty()}") |
|
|
|
|
|
Log.d("ChatApiService", "openAI is null: ${openAI == null}") |
|
|
|
|
|
Log.d("ChatApiService", "messages size: ${messages.size}") |
|
|
|
|
|
|
|
|
|
|
|
if (!isInitialized || apiKey.isEmpty() || openAI == null) { |
|
|
if (!isInitialized || apiKey.isEmpty() || openAI == null) { |
|
|
Log.e("ChatApiService", "ChatAPI服务未初始化,无法发送消息") |
|
|
Log.e("ChatApiService", "ChatAPI服务未初始化,无法发送消息") |
|
|
streamCallback?.onError(ChatApiException("ChatAPI服务未初始化")) |
|
|
streamCallback?.onError(ChatApiException("ChatAPI服务未初始化")) |
|
|
@ -363,140 +284,28 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
|
|
|
|
|
|
// 检查是否包含图片,决定使用哪个模型 |
|
|
// 检查是否包含图片,决定使用哪个模型 |
|
|
val currentModel = if (containsImage(messages)) visionModel else model |
|
|
val currentModel = if (containsImage(messages)) visionModel else model |
|
|
Log.d("ChatApiService", "选择的模型: $currentModel (默认模型: $model, 视觉模型: $visionModel)") |
|
|
|
|
|
|
|
|
|
|
|
currentStreamJob = launch { |
|
|
currentStreamJob = launch { |
|
|
try { |
|
|
try { |
|
|
Log.e("ChatApiService", "=== 开始流式请求 ===") |
|
|
|
|
|
Log.e("ChatApiService", "消息数量: ${messages.size}") |
|
|
|
|
|
Log.e("ChatApiService", "当前模型: $currentModel") |
|
|
|
|
|
Log.e("ChatApiService", "MCP客户端状态: ${mcpClient != null}") |
|
|
|
|
|
|
|
|
|
|
|
// 转换消息格式 |
|
|
// 转换消息格式 |
|
|
Log.e("ChatApiService", "开始转换消息格式...") |
|
|
|
|
|
val chatMessages = try { |
|
|
val chatMessages = try { |
|
|
convertToChatMessages(messages) |
|
|
convertToChatMessages(messages) |
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
Log.e("ChatApiService", "ERROR: 转换消息格式失败: ${e.message}", e) |
|
|
Log.e("ChatApiService", "转换消息格式失败: ${e.message}", e) |
|
|
throw e |
|
|
throw e |
|
|
} |
|
|
} |
|
|
Log.e("ChatApiService", "转换后的聊天消息数量: ${chatMessages.size}") |
|
|
|
|
|
|
|
|
|
|
|
// 验证转换后的消息 |
|
|
// 直接获取工具列表 |
|
|
chatMessages.forEachIndexed { index, message -> |
|
|
val tools = getOpenAiTools() |
|
|
Log.e("ChatApiService", "消息 $index: role=${message.role}, content类型=${message.content?.javaClass?.simpleName}") |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 获取MCP工具列表 |
|
|
|
|
|
Log.e("ChatApiService", "开始获取MCP工具列表...") |
|
|
|
|
|
val tools = mutableListOf<Tool>() |
|
|
|
|
|
|
|
|
|
|
|
val toolMaps = mcpClient?.getToolMaps() |
|
|
|
|
|
Log.e("ChatApiService", "获取到的工具映射数量: ${toolMaps?.size ?: 0}") |
|
|
|
|
|
|
|
|
|
|
|
toolMaps?.forEach { toolMap -> |
|
|
|
|
|
try { |
|
|
|
|
|
Log.e("ChatApiService", "处理工具映射: $toolMap") |
|
|
|
|
|
|
|
|
|
|
|
val type = toolMap["type"] as? String |
|
|
|
|
|
if (type == "function") { |
|
|
|
|
|
@Suppress("UNCHECKED_CAST") |
|
|
|
|
|
val functionMap = toolMap["function"] as? Map<String, Any> |
|
|
|
|
|
if (functionMap == null) { |
|
|
|
|
|
Log.e("ChatApiService", "ERROR: 工具function映射为null") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val name = functionMap["name"] as? String |
|
|
|
|
|
if (name == null) { |
|
|
|
|
|
Log.e("ChatApiService", "ERROR: 工具name为null") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val description = functionMap["description"] as? String ?: "" |
|
|
|
|
|
val parametersMap = functionMap["parameters"] as? Map<String, Any> |
|
|
|
|
|
|
|
|
|
|
|
Log.e("ChatApiService", "工具信息 - 名称: $name, 描述: $description") |
|
|
|
|
|
Log.e("ChatApiService", "参数映射: $parametersMap") |
|
|
|
|
|
|
|
|
|
|
|
if (parametersMap == null) { |
|
|
|
|
|
Log.e("ChatApiService", "ERROR: 工具 $name 的parameters为null") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 验证parametersMap的基本结构 |
|
|
|
|
|
if (!parametersMap.containsKey("type")) { |
|
|
|
|
|
Log.e("ChatApiService", "ERROR: 工具 $name 的parameters缺少type字段") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val parametersJson = gson.toJson(parametersMap) |
|
|
|
|
|
Log.e("ChatApiService", "参数JSON: $parametersJson") |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
Log.e("ChatApiService", "正在调用 Parameters.fromJsonString...") |
|
|
|
|
|
Log.e("ChatApiService", "参数JSON长度: ${parametersJson.length}") |
|
|
|
|
|
Log.e("ChatApiService", "参数JSON内容预览: ${parametersJson.take(200)}...") |
|
|
|
|
|
|
|
|
|
|
|
if (parametersJson.isBlank()) { |
|
|
|
|
|
Log.e("ChatApiService", "ERROR: parametersJson为空或空白") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val parameters = com.aallam.openai.api.core.Parameters.fromJsonString(parametersJson) |
|
|
|
|
|
Log.e("ChatApiService", "SUCCESS: 成功创建Parameters对象") |
|
|
|
|
|
|
|
|
|
|
|
Log.e("ChatApiService", "正在创建Tool.function...") |
|
|
|
|
|
Log.e("ChatApiService", "工具参数 - name: '$name' (isEmpty: ${name.isEmpty()})") |
|
|
|
|
|
Log.e("ChatApiService", "工具参数 - description: '$description' (isEmpty: ${description.isEmpty()})") |
|
|
|
|
|
Log.e("ChatApiService", "工具参数 - parameters: ${parameters != null}") |
|
|
|
|
|
|
|
|
|
|
|
if (name.isEmpty()) { |
|
|
|
|
|
Log.e("ChatApiService", "ERROR: 工具名称为空,跳过") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val tool = Tool.function( |
|
|
|
|
|
name = name, |
|
|
|
|
|
description = description, |
|
|
|
|
|
parameters = parameters |
|
|
|
|
|
) |
|
|
|
|
|
tools.add(tool) |
|
|
|
|
|
|
|
|
|
|
|
Log.e("ChatApiService", "SUCCESS: 成功添加工具: $name") |
|
|
|
|
|
|
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e("ChatApiService", "ERROR: 创建Parameters对象失败 for 工具 $name: ${e.message}", e) |
|
|
|
|
|
Log.e("ChatApiService", "失败的参数JSON: $parametersJson") |
|
|
|
|
|
Log.e("ChatApiService", "异常堆栈: ${e.stackTraceToString()}") |
|
|
|
|
|
// 跳过这个工具,继续处理其他工具 |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
Log.e("ChatApiService", "跳过非function类型的工具: $type") |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e("ChatApiService", "ERROR: 处理工具映射时出错: ${e.message}", e) |
|
|
|
|
|
Log.e("ChatApiService", "出错的工具映射: $toolMap") |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Log.e("ChatApiService", "最终工具列表大小: ${tools.size}") |
|
|
|
|
|
|
|
|
|
|
|
// 构建请求 |
|
|
// 构建请求 |
|
|
Log.e("ChatApiService", "正在构建ChatCompletionRequest...") |
|
|
|
|
|
Log.e("ChatApiService", "构建参数检查:") |
|
|
|
|
|
Log.e("ChatApiService", " - currentModel: '$currentModel' (isEmpty: ${currentModel.isEmpty()})") |
|
|
|
|
|
Log.e("ChatApiService", " - chatMessages size: ${chatMessages.size}") |
|
|
|
|
|
Log.e("ChatApiService", " - tools size: ${tools.size}") |
|
|
|
|
|
Log.e("ChatApiService", " - openAI对象: ${openAI != null}") |
|
|
|
|
|
|
|
|
|
|
|
if (currentModel.isEmpty()) { |
|
|
if (currentModel.isEmpty()) { |
|
|
Log.e("ChatApiService", "ERROR: 模型名称为空") |
|
|
Log.e("ChatApiService", "模型名称为空") |
|
|
throw IllegalArgumentException("模型名称不能为空") |
|
|
throw IllegalArgumentException("模型名称不能为空") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (chatMessages.isEmpty()) { |
|
|
if (chatMessages.isEmpty()) { |
|
|
Log.e("ChatApiService", "ERROR: 消息列表为空") |
|
|
Log.e("ChatApiService", "消息列表为空") |
|
|
throw IllegalArgumentException("消息列表不能为空") |
|
|
throw IllegalArgumentException("消息列表不能为空") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -508,53 +317,41 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
temperature = 0.7, |
|
|
temperature = 0.7, |
|
|
tools = if (tools.isNotEmpty()) tools else null |
|
|
tools = if (tools.isNotEmpty()) tools else null |
|
|
) |
|
|
) |
|
|
Log.e("ChatApiService", "SUCCESS: ChatCompletionRequest创建成功") |
|
|
|
|
|
|
|
|
|
|
|
if (openAI == null) { |
|
|
if (openAI == null) { |
|
|
Log.e("ChatApiService", "ERROR: openAI对象为null") |
|
|
Log.e("ChatApiService", "openAI对象为null") |
|
|
throw IllegalStateException("OpenAI客户端未初始化") |
|
|
throw IllegalStateException("OpenAI客户端未初始化") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Log.e("ChatApiService", "正在调用 openAI.chatCompletions...") |
|
|
|
|
|
val flow = openAI!!.chatCompletions(chatCompletionRequest) |
|
|
val flow = openAI!!.chatCompletions(chatCompletionRequest) |
|
|
Log.e("ChatApiService", "SUCCESS: 获得chatsFlow,开始收集...") |
|
|
|
|
|
flow |
|
|
flow |
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
Log.e("ChatApiService", "ERROR: 创建ChatCompletionRequest或调用chatCompletions失败: ${e.message}", e) |
|
|
Log.e("ChatApiService", "创建ChatCompletionRequest或调用chatCompletions失败: ${e.message}", e) |
|
|
throw e |
|
|
throw e |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
chatsFlow.collect { result -> |
|
|
chatsFlow.collect { result -> |
|
|
if (isCanceled) return@collect |
|
|
if (isCanceled) return@collect |
|
|
|
|
|
|
|
|
Log.e("ChatApiService", "收到流式响应,结果类型: ${result.javaClass.simpleName}") |
|
|
|
|
|
Log.e("ChatApiService", "result.choices大小: ${result.choices?.size ?: 0}") |
|
|
|
|
|
|
|
|
|
|
|
val choice = result.choices?.firstOrNull() |
|
|
val choice = result.choices?.firstOrNull() |
|
|
if (choice == null) { |
|
|
if (choice == null) { |
|
|
Log.e("ChatApiService", "ERROR: choice为null") |
|
|
Log.e("ChatApiService", "choice为null") |
|
|
return@collect |
|
|
return@collect |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Log.e("ChatApiService", "choice存在,delta类型: ${choice.delta?.javaClass?.simpleName ?: "null"}") |
|
|
|
|
|
|
|
|
|
|
|
val delta = choice.delta |
|
|
val delta = choice.delta |
|
|
if (delta == null) { |
|
|
if (delta == null) { |
|
|
Log.e("ChatApiService", "ERROR: delta为null") |
|
|
Log.e("ChatApiService", "delta为null") |
|
|
return@collect |
|
|
return@collect |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Log.e("ChatApiService", "delta存在,content: ${delta.content}, toolCalls: ${delta.toolCalls?.size ?: 0}") |
|
|
|
|
|
|
|
|
|
|
|
// 处理普通文本内容 |
|
|
// 处理普通文本内容 |
|
|
delta.content?.let { content -> |
|
|
delta.content?.let { content -> |
|
|
Log.e("ChatApiService", "收到文本内容: $content") |
|
|
|
|
|
streamCallback?.onToken(content) |
|
|
streamCallback?.onToken(content) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 收集工具调用信息 |
|
|
// 收集工具调用信息 |
|
|
delta.toolCalls?.forEach { toolCall -> |
|
|
delta.toolCalls?.forEach { toolCall -> |
|
|
Log.e("ChatApiService", "处理工具调用: index=${toolCall.index}, id=${toolCall.id}") |
|
|
|
|
|
val index = toolCall.index |
|
|
val index = toolCall.index |
|
|
|
|
|
|
|
|
// 创建或获取现有的工具调用信息 |
|
|
// 创建或获取现有的工具调用信息 |
|
|
@ -564,7 +361,6 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
try { |
|
|
try { |
|
|
toolCall.id?.let { id -> |
|
|
toolCall.id?.let { id -> |
|
|
toolCallInfo.id = id.toString() |
|
|
toolCallInfo.id = id.toString() |
|
|
Log.e("ChatApiService", "更新工具调用ID: $id") |
|
|
|
|
|
} |
|
|
} |
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
Log.e("ChatApiService", "处理工具调用ID异常: ${e.message}") |
|
|
Log.e("ChatApiService", "处理工具调用ID异常: ${e.message}") |
|
|
@ -573,13 +369,20 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
// 安全处理函数信息 |
|
|
// 安全处理函数信息 |
|
|
try { |
|
|
try { |
|
|
toolCall.function?.let { function -> |
|
|
toolCall.function?.let { function -> |
|
|
function.name?.let { name -> |
|
|
try { |
|
|
toolCallInfo.name = name |
|
|
function.name?.let { name -> |
|
|
Log.e("ChatApiService", "更新工具名称: $name") |
|
|
toolCallInfo.name = name |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e("ChatApiService", "处理工具调用函数名称异常: ${e.message}") |
|
|
} |
|
|
} |
|
|
function.arguments?.let { args -> |
|
|
|
|
|
toolCallInfo.arguments += args |
|
|
try { |
|
|
Log.e("ChatApiService", "添加参数片段: $args") |
|
|
function.arguments?.let { args -> |
|
|
|
|
|
toolCallInfo.arguments += args |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e("ChatApiService", "处理工具调用参数异常: ${e.message}") |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
@ -589,7 +392,7 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (!isCanceled) { |
|
|
if (!isCanceled) { |
|
|
// 处理工具调用或完成 |
|
|
// 检查是否有工具调用需要处理 |
|
|
val hasToolCalls = processToolCalls() |
|
|
val hasToolCalls = processToolCalls() |
|
|
if (!hasToolCalls) { |
|
|
if (!hasToolCalls) { |
|
|
streamCallback?.onComplete() |
|
|
streamCallback?.onComplete() |
|
|
@ -608,25 +411,28 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
* 处理工具调用 |
|
|
* 处理工具调用 |
|
|
*/ |
|
|
*/ |
|
|
private suspend fun processToolCalls(): Boolean { |
|
|
private suspend fun processToolCalls(): Boolean { |
|
|
Log.d("ChatApiService", "开始处理工具调用,工具调用数量: ${toolCalls.size}") |
|
|
val firstToolCall = try { |
|
|
|
|
|
toolCalls.values.firstOrNull { it.isValid() } |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e("ChatApiService", "查找有效工具调用异常: ${e.message}") |
|
|
|
|
|
null |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
val firstToolCall = toolCalls.values.firstOrNull { it.isValid() } |
|
|
|
|
|
if (firstToolCall == null) { |
|
|
if (firstToolCall == null) { |
|
|
Log.d("ChatApiService", "没有有效的工具调用") |
|
|
|
|
|
return false |
|
|
return false |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Log.d("ChatApiService", "第一个有效的工具调用: name=${firstToolCall.name}, id=${firstToolCall.id}") |
|
|
|
|
|
Log.d("ChatApiService", "工具调用参数: ${firstToolCall.arguments}") |
|
|
|
|
|
|
|
|
|
|
|
// 创建函数调用字典 |
|
|
// 创建函数调用字典 |
|
|
val functionCall = mapOf( |
|
|
val functionCall = try { |
|
|
"name" to firstToolCall.name, |
|
|
mapOf( |
|
|
"arguments" to firstToolCall.arguments, |
|
|
"name" to (firstToolCall.name.takeIf { it.isNotEmpty() } ?: ""), |
|
|
"id" to firstToolCall.id |
|
|
"arguments" to (firstToolCall.arguments.takeIf { it.isNotEmpty() } ?: "{}"), |
|
|
) |
|
|
"id" to (firstToolCall.id.takeIf { it.isNotEmpty() } ?: "") |
|
|
|
|
|
) |
|
|
Log.d("ChatApiService", "创建的函数调用字典: $functionCall") |
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e("ChatApiService", "创建函数调用字典异常: ${e.message}") |
|
|
|
|
|
return false |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 通知上层工具调用事件 |
|
|
// 通知上层工具调用事件 |
|
|
streamCallback?.onFunctionCall(convertMapToJsonObject(functionCall)) |
|
|
streamCallback?.onFunctionCall(convertMapToJsonObject(functionCall)) |
|
|
@ -639,36 +445,25 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
val functionName = firstToolCall.name |
|
|
val functionName = firstToolCall.name |
|
|
val argumentsJson = firstToolCall.arguments |
|
|
val argumentsJson = firstToolCall.arguments |
|
|
|
|
|
|
|
|
Log.d("ChatApiService", "准备调用MCP工具: $functionName") |
|
|
|
|
|
Log.d("ChatApiService", "参数JSON: $argumentsJson") |
|
|
|
|
|
|
|
|
|
|
|
val result = if (_mcpClient?.hasToolWithName(functionName) == true) { |
|
|
val result = if (_mcpClient?.hasToolWithName(functionName) == true) { |
|
|
Log.d("ChatApiService", "MCP客户端中找到工具: $functionName") |
|
|
|
|
|
|
|
|
|
|
|
// 解析参数 |
|
|
// 解析参数 |
|
|
val arguments = _mcpClient?.parseJsonArguments(argumentsJson) ?: emptyMap() |
|
|
val arguments = _mcpClient?.parseJsonArguments(argumentsJson) ?: emptyMap() |
|
|
Log.d("ChatApiService", "解析后的参数: $arguments") |
|
|
|
|
|
|
|
|
|
|
|
// 调用MCP工具 |
|
|
// 调用MCP工具 |
|
|
Log.d("ChatApiService", "开始调用MCP工具...") |
|
|
|
|
|
val toolResult = _mcpClient?.callTool(functionName, arguments) |
|
|
val toolResult = _mcpClient?.callTool(functionName, arguments) |
|
|
Log.d("ChatApiService", "MCP工具调用结果: $toolResult") |
|
|
|
|
|
|
|
|
|
|
|
// 处理结果 |
|
|
// 处理结果 |
|
|
if (toolResult != null) { |
|
|
if (toolResult != null) { |
|
|
if (toolResult["isError"] == true) { |
|
|
if (toolResult["isError"] == true) { |
|
|
Log.w("ChatApiService", "MCP工具调用返回错误") |
|
|
|
|
|
// 处理错误情况 |
|
|
// 处理错误情况 |
|
|
val content = toolResult["content"] as? List<*> |
|
|
val content = toolResult["content"] as? List<*> |
|
|
val firstContent = content?.firstOrNull() as? Map<*, *> |
|
|
val firstContent = content?.firstOrNull() as? Map<*, *> |
|
|
val errorText = firstContent?.get("text") as? String ?: "Tool execution failed" |
|
|
val errorText = firstContent?.get("text") as? String ?: "Tool execution failed" |
|
|
mapOf("context" to errorText) |
|
|
mapOf("context" to errorText) |
|
|
} else if (toolResult.containsKey("context")) { |
|
|
} else if (toolResult.containsKey("context")) { |
|
|
Log.d("ChatApiService", "本地函数结果") |
|
|
|
|
|
// 本地函数结果 |
|
|
// 本地函数结果 |
|
|
toolResult |
|
|
toolResult |
|
|
} else { |
|
|
} else { |
|
|
Log.d("ChatApiService", "MCP工具结果") |
|
|
|
|
|
// MCP工具结果 |
|
|
// MCP工具结果 |
|
|
val content = toolResult["content"] as? List<*> |
|
|
val content = toolResult["content"] as? List<*> |
|
|
val firstContent = content?.firstOrNull() as? Map<*, *> |
|
|
val firstContent = content?.firstOrNull() as? Map<*, *> |
|
|
@ -680,13 +475,10 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
mapOf("context" to "Tool call failed") |
|
|
mapOf("context" to "Tool call failed") |
|
|
} |
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
Log.w("ChatApiService", "MCP客户端中未找到工具: $functionName") |
|
|
|
|
|
// 工具不存在 |
|
|
// 工具不存在 |
|
|
mapOf("context" to "Tool not found: $functionName") |
|
|
mapOf("context" to "Tool not found: $functionName") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Log.d("ChatApiService", "最终处理结果: $result") |
|
|
|
|
|
|
|
|
|
|
|
if (!isCanceled) { |
|
|
if (!isCanceled) { |
|
|
// 处理结果 |
|
|
// 处理结果 |
|
|
streamCallback?.onFunctionCallResult( |
|
|
streamCallback?.onFunctionCallResult( |
|
|
@ -808,6 +600,8 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
handler = handler |
|
|
handler = handler |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
true |
|
|
true |
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
Log.e("ChatApiService", "Failed to register function: $name", e) |
|
|
Log.e("ChatApiService", "Failed to register function: $name", e) |
|
|
@ -826,8 +620,8 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
// 直接使用类的CoroutineScope启动协程 |
|
|
// 直接使用类的CoroutineScope启动协程 |
|
|
launch { |
|
|
launch { |
|
|
try { |
|
|
try { |
|
|
val result = _mcpClient?.connectToSSE(serverUrl) ?: false |
|
|
_mcpClient?.connectToSSE(serverUrl) |
|
|
Log.d("ChatApiService", "MCP客户端初始化${if (result) "成功" else "失败"}") |
|
|
|
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
Log.e("ChatApiService", "MCP客户端初始化失败: ${e.message}", e) |
|
|
Log.e("ChatApiService", "MCP客户端初始化失败: ${e.message}", e) |
|
|
} |
|
|
} |
|
|
@ -843,6 +637,88 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
return _mcpClient?.isConnected() ?: false |
|
|
return _mcpClient?.isConnected() ?: false |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 直接从MCPClient获取OpenAI工具格式 |
|
|
|
|
|
* 将MCP工具映射转换为OpenAI工具格式 |
|
|
|
|
|
*/ |
|
|
|
|
|
private fun getOpenAiTools(): List<Tool> { |
|
|
|
|
|
try { |
|
|
|
|
|
val tools = mutableListOf<Tool>() |
|
|
|
|
|
val toolMaps = mcpClient?.getToolMaps() ?: return emptyList() |
|
|
|
|
|
|
|
|
|
|
|
toolMaps.forEach { toolMap -> |
|
|
|
|
|
try { |
|
|
|
|
|
val type = toolMap["type"] as? String |
|
|
|
|
|
if (type == "function") { |
|
|
|
|
|
@Suppress("UNCHECKED_CAST") |
|
|
|
|
|
val functionMap = toolMap["function"] as? Map<String, Any> |
|
|
|
|
|
if (functionMap == null) { |
|
|
|
|
|
Log.e("ChatApiService", "工具function映射为null") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val name = functionMap["name"] as? String |
|
|
|
|
|
if (name == null) { |
|
|
|
|
|
Log.e("ChatApiService", "工具name为null") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val description = functionMap["description"] as? String ?: "" |
|
|
|
|
|
val parametersMap = functionMap["parameters"] as? Map<String, Any> |
|
|
|
|
|
|
|
|
|
|
|
if (parametersMap == null) { |
|
|
|
|
|
Log.e("ChatApiService", "工具 $name 的parameters为null") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 验证parametersMap的基本结构 |
|
|
|
|
|
if (!parametersMap.containsKey("type")) { |
|
|
|
|
|
Log.e("ChatApiService", "工具 $name 的parameters缺少type字段") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val parametersJson = gson.toJson(parametersMap) |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
if (parametersJson.isBlank()) { |
|
|
|
|
|
Log.e("ChatApiService", "parametersJson为空或空白") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val parameters = com.aallam.openai.api.core.Parameters.fromJsonString(parametersJson) |
|
|
|
|
|
|
|
|
|
|
|
if (name.isEmpty()) { |
|
|
|
|
|
Log.e("ChatApiService", "工具名称为空,跳过") |
|
|
|
|
|
return@forEach |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val tool = Tool.function( |
|
|
|
|
|
name = name, |
|
|
|
|
|
description = description, |
|
|
|
|
|
parameters = parameters |
|
|
|
|
|
) |
|
|
|
|
|
tools.add(tool) |
|
|
|
|
|
|
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e("ChatApiService", "创建Parameters对象失败 for 工具 $name: ${e.message}", e) |
|
|
|
|
|
// 跳过这个工具,继续处理其他工具 |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e("ChatApiService", "处理工具映射时出错: ${e.message}", e) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return tools |
|
|
|
|
|
|
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e("ChatApiService", "获取OpenAI工具格式失败: ${e.message}", e) |
|
|
|
|
|
return emptyList() |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 关闭MCP客户端 |
|
|
* 关闭MCP客户端 |
|
|
*/ |
|
|
*/ |
|
|
@ -864,7 +740,7 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
return "MCP客户端未初始化" |
|
|
return "MCP客户端未初始化" |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
try { |
|
|
return try { |
|
|
// 获取函数名称 |
|
|
// 获取函数名称 |
|
|
val name = functionCall.getString("name") |
|
|
val name = functionCall.getString("name") |
|
|
|
|
|
|
|
|
@ -876,10 +752,10 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
val result = _mcpClient?.callTool(name, arguments) |
|
|
val result = _mcpClient?.callTool(name, arguments) |
|
|
|
|
|
|
|
|
// 返回工具调用结果 |
|
|
// 返回工具调用结果 |
|
|
return result?.get("context") as? String ?: "工具调用失败" |
|
|
result?.get("context") as? String ?: "工具调用失败" |
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
Log.e("ChatApiService", "处理MCP工具调用失败: ${e.message}", e) |
|
|
Log.e("ChatApiService", "处理MCP工具调用失败: ${e.message}", e) |
|
|
return "处理MCP工具调用失败: ${e.message}" |
|
|
"处理MCP工具调用失败: ${e.message}" |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -890,6 +766,8 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor |
|
|
return _mcpClient?.getToolMaps() ?: emptyList() |
|
|
return _mcpClient?.getToolMaps() ?: emptyList() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 处理图片 |
|
|
* 处理图片 |
|
|
*/ |
|
|
*/ |
|
|
|