v0.1.4版本 #2

Merged
PeterZhong merged 70 commits from host into master 2025-06-15 14:42:58 +00:00
Showing only changes of commit af43d0d774 - Show all commits

View File

@ -229,6 +229,27 @@ public class Gateway
} }
} }
private static (string Matched, string Remaining) ExtractMatchedPart(string input, string toolPattern)
{
if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(toolPattern))
return (string.Empty, input);
Regex regex = new Regex(toolPattern);
Match match = regex.Match(input);
if (!match.Success)
return (string.Empty, input);
string matched = match.Value;
int startIndex = match.Index;
int length = match.Length;
// 构造剩余字符串
string remaining = input.Substring(0, startIndex) + input.Substring(startIndex + length);
return (matched, remaining);
}
public static async void SendMessageStream(string message, string model, string gdbPath, Action<MessageListItem> callback) public static async void SendMessageStream(string message, string model, string gdbPath, Action<MessageListItem> callback)
{ {
Llm bailian = new Bailian Llm bailian = new Bailian
@ -249,8 +270,8 @@ public class Gateway
Content = message Content = message
}); });
goOn = true; goOn = true;
string toolPattern = "^<tool_use>[\\s\\S]*?<\\/tool_use>$"; string toolPattern = "<tool_use>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<arguments>([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/tool_use>";
string promptPattern = "^<prompt>[\\s\\S]*?<\\/prompt>$"; string promptPattern = "<prompt>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<\\/prompt>";
McpServerList mcpServerList = new McpServerList(); McpServerList mcpServerList = new McpServerList();
int loop = 0; int loop = 0;
while (goOn) while (goOn)
@ -270,169 +291,49 @@ public class Gateway
MaxTokens = 1000, MaxTokens = 1000,
}; };
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
string messageContent = ""; string messageContent = ""; //一次请求下完整的response
var (toolMatched, toolRemaining) = ExtractMatchedPart(messageContent, toolPattern);
var (promptMatched, promptRemaining) = ExtractMatchedPart(messageContent, promptPattern);
if (toolMatched == "" && promptMatched == "" && messageContent != "")
{
//如果本次回复不包含任何工具的调用或提示词的调用,则不再请求
break;
}
await foreach(var chunk in bailian.SendChatStreamAsync(jsonContent)) await foreach(var chunk in bailian.SendChatStreamAsync(jsonContent))
{ {
if (chunk == "[DONE]") if (!goOn)
{ {
goOn = false; break;
}else if (chunk.StartsWith("<tool_use>")) }
var (matched, remaining) = ExtractMatchedPart(chunk, toolPattern);
if (matched == "")
{ {
if (Regex.IsMatch(chunk, toolPattern)) var (matchedPrompt, remainingPrompt) = ExtractMatchedPart(chunk, promptPattern);
if (matchedPrompt == "")
{ {
//返回工具卡片 //普通消息文本
messages.Add(new Message MessageListItem chatMessageListItem = new ChatMessageItem()
{ {
Role = "assistant", content = remainingPrompt,
Content = chunk role = "assistant",
}); type = MessageType.CHAT_MESSAGE,
XElement toolUse = XElement.Parse(chunk); id = timestamp.ToString()
string fullToolName = toolUse.Element("name")?.Value; };
string toolArgs = toolUse.Element("arguments")?.Value; messageContent = remainingPrompt;
Dictionary<string, object> toolParams = JsonConvert.DeserializeObject<Dictionary<string, object>>(toolArgs); callback?.Invoke(chatMessageListItem);
string serverName = fullToolName.Contains(":") ? fullToolName.Split(':')[0] : fullToolName;
string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName;
McpServer mcpServer = mcpServerList.GetServer(serverName);
if (mcpServer is SseMcpServer)
{
SseMcpServer sseMcpServer = mcpServer as SseMcpServer;
SseMcpClient client = new SseMcpClient(sseMcpServer.BaseUrl);
CallToolResponse toolResponse = await client.CallToolAsync(toolName,toolParams);
MessageListItem toolMessageItem = new ToolMessageItem
{
toolName = toolName,
toolParams = toolParams,
type = MessageType.TOOL_MESSAGE,
status = toolResponse.IsError ? "fail" : "success",
content = JsonConvert.SerializeObject(toolResponse),
id = timestamp.ToString()
};
messages.Add(new Message
{
Role = "user",
// Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePromptTemplate
Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse))
});
// messages.Add(new Message
// {
// Role = "user",
// Content = JsonConvert.SerializeObject(toolResponse)
// });
callback?.Invoke(toolMessageItem);
}else if (mcpServer is StdioMcpServer)
{
StdioMcpServer stdioMcpServer = mcpServer as StdioMcpServer;
StdioMcpClient client = new StdioMcpClient(stdioMcpServer.Command, stdioMcpServer.Args);
CallToolResponse toolResponse = await client.CallToolAsync(toolName,toolParams);
MessageListItem toolMessageItem = new ToolMessageItem
{
toolName = toolName,
toolParams = toolParams,
type = MessageType.TOOL_MESSAGE,
status = toolResponse.IsError ? "fail" : "success",
content = JsonConvert.SerializeObject(toolResponse),
id = timestamp.ToString()
};
messages.Add(new Message
{
Role = "user",
// Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePromptTemplate
Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse))
});
// messages.Add(new Message
// {
// Role = "user",
// Content = JsonConvert.SerializeObject(toolResponse)
// });
callback?.Invoke(toolMessageItem);
}else if (mcpServer is InnerMcpServer)
{
Type type = Type.GetType("LinkToolAddin.client.tool."+serverName);
MethodInfo method = type.GetMethod(toolName,BindingFlags.Public | BindingFlags.Static);
var methodParams = toolParams.Values.ToArray();
object[] args = new object[methodParams.Length];
for (int i = 0; i < methodParams.Length; i++)
{
if (methodParams[i].GetType() == typeof(JArray))
{
List<string> list = new List<string>();
list = (methodParams[i] as JArray).Select(token => token.ToString()).ToList();
args[i] = list;
}
else
{
args[i] = methodParams[i];
}
}
var task = method.Invoke(null, args) as Task<JsonRpcResultEntity>;
JsonRpcResultEntity innerResult = await task;
if (innerResult is JsonRpcErrorEntity)
{
MessageListItem toolMessageItem = new ToolMessageItem
{
toolName = toolName,
toolParams = toolParams,
type = MessageType.TOOL_MESSAGE,
status = "fail",
content = JsonConvert.SerializeObject(innerResult),
id = timestamp.ToString()
};
messages.Add(new Message
{
Role = "user",
Content = SystemPrompt.ErrorPromptTemplate
});
messages.Add(new Message
{
Role = "user",
Content = JsonConvert.SerializeObject(innerResult)
});
callback?.Invoke(toolMessageItem);
}else if (innerResult is JsonRpcSuccessEntity)
{
MessageListItem toolMessageItem = new ToolMessageItem
{
toolName = toolName,
toolParams = toolParams,
type = MessageType.TOOL_MESSAGE,
status = "success",
content = JsonConvert.SerializeObject(innerResult),
id = timestamp.ToString()
};
messages.Add(new Message
{
Role = "user",
// Content = SystemPrompt.ContinuePromptTemplate
Content = SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(innerResult))
});
// messages.Add(new Message
// {
// Role = "user",
// Content = JsonConvert.SerializeObject(innerResult)
// });
callback?.Invoke(toolMessageItem);
}
}
} }
else else
{ {
MessageListItem toolMessageItem = new ToolMessageItem //包含Prompt调用请求的消息
MessageListItem chatMessageListItem = new ChatMessageItem()
{ {
toolName = "", content = remainingPrompt,
toolParams = new Dictionary<string, object>(), role = "assistant",
type = MessageType.TOOL_MESSAGE, type = MessageType.CHAT_MESSAGE,
status = "loading",
content = "正在生成工具调用参数",
id = timestamp.ToString() id = timestamp.ToString()
}; };
callback?.Invoke(toolMessageItem); callback?.Invoke(chatMessageListItem);
continue; XElement promptUse = XElement.Parse(matchedPrompt);
}
}else if (chunk.StartsWith("<prompt>"))
{
if (Regex.IsMatch(chunk, promptPattern))
{
XElement promptUse = XElement.Parse(chunk);
string promptKey = promptUse.Element("name")?.Value; string promptKey = promptUse.Element("name")?.Value;
string promptContent = DynamicPrompt.GetPrompt(promptKey,null); string promptContent = DynamicPrompt.GetPrompt(promptKey,null);
messages.Add(new Message messages.Add(new Message
@ -446,49 +347,134 @@ public class Gateway
toolParams = null, toolParams = null,
type = MessageType.TOOL_MESSAGE, type = MessageType.TOOL_MESSAGE,
status = "success", status = "success",
content = promptKey, content = "成功调用提示词:"+promptKey,
id = timestamp.ToString() id = (timestamp+1).ToString()
};
callback?.Invoke(toolMessageItem);
}
else
{
MessageListItem toolMessageItem = new ToolMessageItem
{
toolName = "调用提示词",
toolParams = null,
type = MessageType.TOOL_MESSAGE,
status = "loading",
content = "正在调用提示词",
id = timestamp.ToString()
}; };
callback?.Invoke(toolMessageItem); callback?.Invoke(toolMessageItem);
} }
} }
else else
{ {
string content = chunk; //包含工具调用请求的消息
if (content.EndsWith("[DONE]"))
{
content = content.Substring(0, content.Length - 6);
}
//普通流式消息卡片
MessageListItem chatMessageListItem = new ChatMessageItem() MessageListItem chatMessageListItem = new ChatMessageItem()
{ {
content = content, content = remaining,
role = "assistant", role = "assistant",
type = MessageType.CHAT_MESSAGE, type = MessageType.CHAT_MESSAGE,
id = timestamp.ToString() id = timestamp.ToString()
}; };
messageContent = content;
callback?.Invoke(chatMessageListItem); callback?.Invoke(chatMessageListItem);
XElement toolUse = XElement.Parse(matched);
string fullToolName = toolUse.Element("name")?.Value;
string toolArgs = toolUse.Element("arguments")?.Value;
Dictionary<string, object> toolParams = JsonConvert.DeserializeObject<Dictionary<string, object>>(toolArgs);
string serverName = fullToolName.Contains(":") ? fullToolName.Split(':')[0] : fullToolName;
string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName;
McpServer mcpServer = mcpServerList.GetServer(serverName);
if (mcpServer is SseMcpServer)
{
SseMcpServer sseMcpServer = mcpServer as SseMcpServer;
SseMcpClient client = new SseMcpClient(sseMcpServer.BaseUrl);
CallToolResponse toolResponse = await client.CallToolAsync(toolName,toolParams);
MessageListItem toolMessageItem = new ToolMessageItem
{
toolName = toolName,
toolParams = toolParams,
type = MessageType.TOOL_MESSAGE,
status = toolResponse.IsError ? "fail" : "success",
content = JsonConvert.SerializeObject(toolResponse),
id = (timestamp+1).ToString()
};
messages.Add(new Message
{
Role = "user",
Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse))
});
callback?.Invoke(toolMessageItem);
}else if (mcpServer is StdioMcpServer)
{
StdioMcpServer stdioMcpServer = mcpServer as StdioMcpServer;
StdioMcpClient client = new StdioMcpClient(stdioMcpServer.Command, stdioMcpServer.Args);
CallToolResponse toolResponse = await client.CallToolAsync(toolName,toolParams);
MessageListItem toolMessageItem = new ToolMessageItem
{
toolName = toolName,
toolParams = toolParams,
type = MessageType.TOOL_MESSAGE,
status = toolResponse.IsError ? "fail" : "success",
content = JsonConvert.SerializeObject(toolResponse),
id = (timestamp+1).ToString()
};
messages.Add(new Message
{
Role = "user",
Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse))
});
callback?.Invoke(toolMessageItem);
}else if (mcpServer is InnerMcpServer)
{
Type type = Type.GetType("LinkToolAddin.client.tool."+serverName);
MethodInfo method = type.GetMethod(toolName,BindingFlags.Public | BindingFlags.Static);
var methodParams = toolParams.Values.ToArray();
object[] args = new object[methodParams.Length];
for (int i = 0; i < methodParams.Length; i++)
{
if (methodParams[i].GetType() == typeof(JArray))
{
List<string> list = new List<string>();
list = (methodParams[i] as JArray).Select(token => token.ToString()).ToList();
args[i] = list;
}
else
{
args[i] = methodParams[i];
}
}
var task = method.Invoke(null, args) as Task<JsonRpcResultEntity>;
JsonRpcResultEntity innerResult = await task;
if (innerResult is JsonRpcErrorEntity)
{
MessageListItem toolMessageItem = new ToolMessageItem
{
toolName = toolName,
toolParams = toolParams,
type = MessageType.TOOL_MESSAGE,
status = "fail",
content = JsonConvert.SerializeObject(innerResult),
id = (timestamp+1).ToString()
};
messages.Add(new Message
{
Role = "user",
Content = SystemPrompt.ErrorPromptTemplate
});
messages.Add(new Message
{
Role = "user",
Content = JsonConvert.SerializeObject(innerResult)
});
callback?.Invoke(toolMessageItem);
}else if (innerResult is JsonRpcSuccessEntity)
{
MessageListItem toolMessageItem = new ToolMessageItem
{
toolName = toolName,
toolParams = toolParams,
type = MessageType.TOOL_MESSAGE,
status = "success",
content = JsonConvert.SerializeObject(innerResult),
id = (timestamp+1).ToString()
};
messages.Add(new Message
{
Role = "user",
Content = SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(innerResult))
});
callback?.Invoke(toolMessageItem);
}
}
} }
} }
messages.Add(new Message
{
Role = "assistant",
Content = messageContent
});
} }
} }