|
|
@ -199,88 +199,69 @@ class MCPSubClient( |
|
|
*/ |
|
|
*/ |
|
|
private fun updateToolMaps() { |
|
|
private fun updateToolMaps() { |
|
|
toolMaps.clear() |
|
|
toolMaps.clear() |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
// 转换MCP工具为OpenAI函数格式 |
|
|
|
|
|
tools.forEach { toolUnion -> |
|
|
tools.forEach { toolUnion -> |
|
|
val tool = toolUnion.tool().getOrNull() ?: return@forEach |
|
|
val tool = toolUnion.tool().getOrNull() ?: return@forEach |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
// 获取工具的输入模式 |
|
|
|
|
|
val inputSchema = tool.inputSchema() ?: return@forEach |
|
|
val inputSchema = tool.inputSchema() ?: return@forEach |
|
|
|
|
|
|
|
|
// 使用toString()获取完整JSON字符串 |
|
|
|
|
|
val schemaStr = inputSchema.toString() |
|
|
val schemaStr = inputSchema.toString() |
|
|
Log.d(TAG, "工具 ${tool.name()} 的Schema: $schemaStr") |
|
|
Log.d(TAG, "工具 ${tool.name()} 的Schema: $schemaStr") |
|
|
|
|
|
|
|
|
// 解析properties和required |
|
|
|
|
|
val propertiesMap = HashMap<String, Any>() |
|
|
val propertiesMap = HashMap<String, Any>() |
|
|
val requiredList = ArrayList<String>() |
|
|
val requiredList = ArrayList<String>() |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
// 匹配properties内部的内容 |
|
|
// ✅ 替换原来的正则方式,使用括号匹配方式提取 properties 内容 |
|
|
val propertiesPattern = "properties=\\{(.*?)\\}(?:,|\\s|$)".toRegex(RegexOption.DOT_MATCHES_ALL) |
|
|
val propertiesContent = extractPropertiesContent(schemaStr) |
|
|
val propertiesMatch = propertiesPattern.find(schemaStr) |
|
|
if (propertiesContent != null) { |
|
|
|
|
|
|
|
|
if (propertiesMatch != null) { |
|
|
|
|
|
val propertiesContent = propertiesMatch.groupValues[1] |
|
|
|
|
|
|
|
|
|
|
|
// 匹配每个属性字段,例如 query={description=搜索信息, type=string} |
|
|
|
|
|
val fieldPattern = "([a-zA-Z0-9_]+)=\\{([^{}]+?)\\}".toRegex() |
|
|
val fieldPattern = "([a-zA-Z0-9_]+)=\\{([^{}]+?)\\}".toRegex() |
|
|
val fieldMatches = fieldPattern.findAll(propertiesContent) |
|
|
val fieldMatches = fieldPattern.findAll(propertiesContent) |
|
|
|
|
|
|
|
|
fieldMatches.forEach { fieldMatch -> |
|
|
fieldMatches.forEach { fieldMatch -> |
|
|
val fieldName = fieldMatch.groupValues[1] |
|
|
val fieldName = fieldMatch.groupValues[1] |
|
|
val fieldContent = fieldMatch.groupValues[2] |
|
|
val fieldContent = fieldMatch.groupValues[2] |
|
|
|
|
|
|
|
|
// 解析字段属性 |
|
|
|
|
|
val fieldMap = HashMap<String, Any>() |
|
|
val fieldMap = HashMap<String, Any>() |
|
|
|
|
|
|
|
|
// 解析如 description=搜索信息, type=string 格式的键值对 |
|
|
|
|
|
// 同时处理带引号和不带引号的情况 |
|
|
|
|
|
val keyValuePattern = "([a-zA-Z0-9_]+)=(\"[^\"]*\"|'[^']*'|[^,}\\s]+)".toRegex() |
|
|
val keyValuePattern = "([a-zA-Z0-9_]+)=(\"[^\"]*\"|'[^']*'|[^,}\\s]+)".toRegex() |
|
|
val keyValueMatches = keyValuePattern.findAll(fieldContent) |
|
|
val keyValueMatches = keyValuePattern.findAll(fieldContent) |
|
|
|
|
|
|
|
|
keyValueMatches.forEach { kvMatch -> |
|
|
keyValueMatches.forEach { kvMatch -> |
|
|
val key = kvMatch.groupValues[1] |
|
|
val key = kvMatch.groupValues[1] |
|
|
var value = kvMatch.groupValues[2].trim() |
|
|
var value = kvMatch.groupValues[2].trim() |
|
|
|
|
|
|
|
|
// 去除引号(如果有) |
|
|
if ((value.startsWith("\"") && value.endsWith("\"")) || |
|
|
if ((value.startsWith("\"") && value.endsWith("\"")) || |
|
|
|
|
|
(value.startsWith("'") && value.endsWith("'"))) { |
|
|
(value.startsWith("'") && value.endsWith("'"))) { |
|
|
value = value.substring(1, value.length - 1) |
|
|
value = value.substring(1, value.length - 1) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 处理不同类型的值 |
|
|
|
|
|
val processedValue: Any = when { |
|
|
val processedValue: Any = when { |
|
|
value.equals("true", ignoreCase = true) -> true |
|
|
value.equals("true", ignoreCase = true) -> true |
|
|
value.equals("false", ignoreCase = true) -> false |
|
|
value.equals("false", ignoreCase = true) -> false |
|
|
value.matches("\\d+".toRegex()) -> value.toIntOrNull() ?: value |
|
|
value.matches("\\d+".toRegex()) -> value.toIntOrNull() ?: value |
|
|
value.matches("\\d+\\.\\d+".toRegex()) -> value.toDoubleOrNull() ?: value |
|
|
value.matches("\\d+\\.\\d+".toRegex()) -> value.toDoubleOrNull() ?: value |
|
|
else -> value // 保持为字符串 |
|
|
else -> value |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fieldMap[key] = processedValue |
|
|
fieldMap[key] = processedValue |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 添加到属性映射 |
|
|
|
|
|
propertiesMap[fieldName] = fieldMap |
|
|
propertiesMap[fieldName] = fieldMap |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Log.d(TAG, "成功解析属性: $propertiesMap") |
|
|
Log.d(TAG, "成功解析属性: $propertiesMap") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 解析required |
|
|
// ✅ required 部分保持不变 |
|
|
val requiredPattern = "required=\\[(.*?)\\]".toRegex() |
|
|
val requiredPattern = "required=\\[(.*?)\\]".toRegex() |
|
|
val requiredMatch = requiredPattern.find(schemaStr) |
|
|
val requiredMatch = requiredPattern.find(schemaStr) |
|
|
|
|
|
|
|
|
if (requiredMatch != null) { |
|
|
if (requiredMatch != null) { |
|
|
val requiredStr = requiredMatch.groupValues[1] |
|
|
val requiredStr = requiredMatch.groupValues[1] |
|
|
if (requiredStr.isNotEmpty()) { |
|
|
if (requiredStr.isNotEmpty()) { |
|
|
// 处理可能带引号的字段名 |
|
|
val fieldNames = requiredStr.split(",").map { |
|
|
val fieldNames = requiredStr.split(",").map { |
|
|
|
|
|
val trimmed = it.trim() |
|
|
val trimmed = it.trim() |
|
|
if ((trimmed.startsWith("\"") && trimmed.endsWith("\"")) || |
|
|
if ((trimmed.startsWith("\"") && trimmed.endsWith("\"")) || |
|
|
(trimmed.startsWith("'") && trimmed.endsWith("'"))) { |
|
|
(trimmed.startsWith("'") && trimmed.endsWith("'"))) { |
|
|
trimmed.substring(1, trimmed.length - 1) |
|
|
trimmed.substring(1, trimmed.length - 1) |
|
|
} else { |
|
|
} else { |
|
|
@ -295,8 +276,7 @@ class MCPSubClient( |
|
|
Log.e(TAG, "解析Schema字符串失败: ${e.message}", e) |
|
|
Log.e(TAG, "解析Schema字符串失败: ${e.message}", e) |
|
|
Log.e(TAG, "原始Schema: $schemaStr") |
|
|
Log.e(TAG, "原始Schema: $schemaStr") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 创建OpenAI函数格式 |
|
|
|
|
|
val functionMap = mapOf( |
|
|
val functionMap = mapOf( |
|
|
"name" to tool.name(), |
|
|
"name" to tool.name(), |
|
|
"description" to (tool.description() ?: ""), |
|
|
"description" to (tool.description() ?: ""), |
|
|
@ -306,13 +286,12 @@ class MCPSubClient( |
|
|
"required" to requiredList |
|
|
"required" to requiredList |
|
|
) |
|
|
) |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
// 添加到工具列表 |
|
|
|
|
|
toolMaps.add(mapOf( |
|
|
toolMaps.add(mapOf( |
|
|
"type" to "function", |
|
|
"type" to "function", |
|
|
"function" to functionMap |
|
|
"function" to functionMap |
|
|
)) |
|
|
)) |
|
|
|
|
|
|
|
|
Log.d(TAG, "成功转换工具 ${tool.name()} 到OpenAI函数格式 $functionMap") |
|
|
Log.d(TAG, "成功转换工具 ${tool.name()} 到OpenAI函数格式 $functionMap") |
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
Log.e(TAG, "转换工具 ${tool.name()} 失败: ${e.message}") |
|
|
Log.e(TAG, "转换工具 ${tool.name()} 失败: ${e.message}") |
|
|
@ -321,10 +300,36 @@ class MCPSubClient( |
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
Log.e(TAG, "更新工具映射失败: ${e.message}") |
|
|
Log.e(TAG, "更新工具映射失败: ${e.message}") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Log.d(TAG, "更新工具映射完成,共 ${toolMaps.size} 个工具") |
|
|
Log.d(TAG, "更新工具映射完成,共 ${toolMaps.size} 个工具") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private fun extractPropertiesContent(schemaStr: String): String? { |
|
|
|
|
|
val startKey = "properties={" |
|
|
|
|
|
val startIndex = schemaStr.indexOf(startKey) |
|
|
|
|
|
if (startIndex == -1) return null |
|
|
|
|
|
|
|
|
|
|
|
var braceCount = 0 |
|
|
|
|
|
var endIndex = -1 |
|
|
|
|
|
val contentStart = startIndex + startKey.length |
|
|
|
|
|
|
|
|
|
|
|
for (i in contentStart until schemaStr.length) { |
|
|
|
|
|
when (schemaStr[i]) { |
|
|
|
|
|
'{' -> braceCount++ |
|
|
|
|
|
'}' -> { |
|
|
|
|
|
if (braceCount == 0) { |
|
|
|
|
|
endIndex = i |
|
|
|
|
|
break |
|
|
|
|
|
} else { |
|
|
|
|
|
braceCount-- |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return if (endIndex != -1) schemaStr.substring(contentStart, endIndex) else null |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 将JsonNode转换为Map |
|
|
* 将JsonNode转换为Map |
|
|
*/ |
|
|
*/ |
|
|
|