using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Xml.Linq; using LinkToolAddin.client; using LinkToolAddin.common; using LinkToolAddin.host; using LinkToolAddin.host.llm; using LinkToolAddin.host.llm.entity; using LinkToolAddin.host.mcp; using LinkToolAddin.host.prompt; using LinkToolAddin.message; using LinkToolAddin.resource; using LinkToolAddin.server; using log4net; using log4net.Appender; using log4net.Config; using log4net.Layout; using ModelContextProtocol.Client; using ModelContextProtocol.Protocol.Types; using Newtonsoft.Json; using MessageBox = ArcGIS.Desktop.Framework.Dialogs.MessageBox; namespace LinkToolAddin.ui.dockpane { /// /// Interaction logic for TestDockpaneView.xaml /// public partial class TestDockpaneView : UserControl { private static ILog log = LogManager.GetLogger(typeof(TestDockpaneView)); private List idList = new List(); private ConcurrentDictionary messageDict = new ConcurrentDictionary(); public TestDockpaneView() { InitLogger(); InitializeComponent(); } protected void InitLogger() { // 1. 创建控制台输出器(Appender) var consoleAppender = new ConsoleAppender { Layout = new PatternLayout("%date [%thread] %-5level %logger - %message%newline"), Threshold = log4net.Core.Level.Info // 仅输出 Info 及以上级别 }; consoleAppender.ActivateOptions(); // 激活配置 // 2. 创建文件滚动输出器(按大小滚动) var fileAppender = new RollingFileAppender { File = System.IO.Path.Combine("Logs", "linktool_app.log"), // 日志文件路径 AppendToFile = true, // 追加模式 RollingStyle = RollingFileAppender.RollingMode.Size, // 按文件大小滚动 MaxSizeRollBackups = 10, // 保留 10 个历史文件 MaximumFileSize = "1MB", // 单个文件最大 1MB StaticLogFileName = true, // 固定文件名(否则自动追加序号) Layout = new PatternLayout("%date [%thread] %-5level %logger - %message%newline"), Threshold = log4net.Core.Level.Info // 仅输出 Info 及以上级别 }; fileAppender.ActivateOptions(); // 激活配置 // 3. 直接通过 BasicConfigurator 注册 Appender BasicConfigurator.Configure(consoleAppender, fileAppender); log = LogManager.GetLogger(typeof(DialogDockpaneView)); // 测试日志输出 log.Debug("Debug 日志(控制台可见)"); log.Info("Info 日志(控制台和文件可见)"); log.Error("Error 日志(严重问题)"); } public void CallBack(string str,object obj) { log.Info($"CallBack {str}"); } private async void TestServer_OnClick(object sender, RoutedEventArgs e) { log.Info("TestServer Clicked"); ArcGISProMcpServer.TestMcpServer(); } private async void StdioMcp_test() { List args = new List(); args.Add("mcp-server-time"); args.Add("--local-timezone=America/New_York"); McpClient stdioMcpClient = new StdioMcpClient("uvx",args); IList tools = await stdioMcpClient.GetToolListAsync(); foreach (McpClientTool tool in tools) { log.Info(tool.JsonSchema.ToString()); } CallToolResponse response = await stdioMcpClient.CallToolAsync("get_current_time", new Dictionary { { "timezone", "America/New_York" } }); log.Info(JsonConvert.SerializeObject(response)); } private async void SseMcp_test() { SseMcpClient client = new SseMcpClient("https://mcp.amap.com/sse?key=ed418512c94ade8f83d42c37b77d2bb2"); IList tools = await client.GetToolListAsync(); foreach (McpClientTool tool in tools) { log.Info(tool.JsonSchema.ToString()); } } private async void Retrieve_Test() { log.Info("TestServer Clicked"); // string jsonRpcString = @"{""jsonrpc"":""2.0"",""method"":""CallArcGISPro.CallArcGISProTool"",""params"":{""toolName"":""analysis.Buffer"",""toolParams"":""[\""D:/01_Development/02_ArcGIS_Pro_Project/20250319_GisAi/Test.gdb/河流\"",\""D:/01_Development/02_ArcGIS_Pro_Project/20250319_GisAi/Test.gdb/河流buffer\"",\""100\""]""},""id"":1}"; DocDb docDb = new DocDb("sk-db177155677e438f832860e7f4da6afc", DocDb.KnowledgeBase.ArcGISProHelpDoc); string query = "缓冲区"; KnowledgeResult knowledgeResult = await docDb.Retrieve(query); log.Info(JsonConvert.SerializeObject(knowledgeResult.ChunkList)); } private async void Request_Bailian_Test() { Llm bailian = new Bailian { api_key = "sk-db177155677e438f832860e7f4da6afc", app_id = "6a77c5a68de64f469b79fcdcde9d5001", }; string reponse = await bailian.SendChatAsync(new LlmJsonContent() { Model = "qwen-max", Messages = new List() { new Message() { Role = "user", Content = "你是谁" } }, Temperature = 0.7, TopP = 1, MaxTokens = 1000, }); log.Info(reponse); } private async void Request_Bailian_Stream_Test() { LlmJsonContent jsonContent = new LlmJsonContent() { Model = "qwen-max", Messages = new List() { new Message() { Role = "user", Content = "给我写一篇1000字的高考议论文" } }, Temperature = 0.7, TopP = 1, MaxTokens = 1000, Stream = true }; Llm bailian = new Bailian { api_key = "sk-db177155677e438f832860e7f4da6afc", app_id = "6a77c5a68de64f469b79fcdcde9d5001", }; await foreach (var chunk in bailian.SendChatStreamAsync(jsonContent)) { log.Info(chunk); } } private void TestButton_OnClick(object sender, RoutedEventArgs e) { throw new System.NotImplementedException(); } private void TestWorkflow_OnClick(object sender, RoutedEventArgs e) { // Gateway.SendMessage("你有什么工具可以调用的?","qwen-max","test.gdb",ShowMessage); Gateway.TestWorkflow("dafdfdgdagdgui","","",AddReply); } public void ShowMessage(MessageListItem msg) { log.Info(msg.content); } private async void PromptTestButton_OnClick(object sender, RoutedEventArgs e) { string userPrompt = PromptTestTextBox.Text; // model可选值:qwen3-235b-a22b,qwen-max,deepseek-r1 try { await Task.Run(() => Gateway.SendMessageStream(userPrompt,"qwen3-235b-a22b", "D:\\01_Project\\20250305_LinkTool\\20250420_AiDemoProject\\20250420_AiDemoProject.gdb", AddReplyStream)); } catch (Exception exception) { log.Error(exception.Message); } } public async void AddReplyStream(MessageListItem msg) { await Task.Run(() => ProcessReplyStream(msg)); } private void ProcessReplyStream(MessageListItem msg) { string id = msg.id; if (idList.Contains(id)) { messageDict[id] = msg; } else { idList.Add(id); messageDict.TryAdd(msg.id, msg); } try { StringBuilder builder = new StringBuilder(); foreach (string idStr in idList) { MessageListItem msgItem = messageDict[idStr]; string content = msgItem.content; if (msgItem.type == MessageType.REASON_MESSAGE) { content = "" + content + ""; } builder.AppendLine(content); builder.AppendLine(); } Application.Current.Dispatcher.Invoke(() => { ReplyTextBox.Clear(); ReplyTextBox.Text = builder.ToString(); ReplyTextBox.ScrollToEnd(); }); }catch (Exception exception) { log.Error(exception.Message); } } public void AddReply(MessageListItem msg) { string content = msg.content; log.Info(content); string originContent = ReplyTextBox.Text; ReplyTextBox.Text = originContent + content; } private void TestStream_OnClick(object sender, RoutedEventArgs e) { Request_Bailian_Stream_Test(); } private void StopConversation_OnClick(object sender, RoutedEventArgs e) { Gateway.StopConversation(); } private async void TestArcGisTool_OnClick(object sender, RoutedEventArgs e) { Type type1 = Type.GetType("LinkToolAddin.client.tool.ArcGisPro"); string xmlStr = "\nArcGisPro:ArcGisProTool\n{\"toolName\": \"Buffer\", \"toolParams\": [\"D:\\\\01_Project\\\\20250305_LinkTool\\\\20250420_AiDemoProject\\\\20250420_AiDemoProject.gdb\\\\LandUse_2005_Copy\", \"D:\\\\01_Project\\\\20250305_LinkTool\\\\20250420_AiDemoProject\\\\20250420_AiDemoProject.gdb\\\\LandUse_2005_Buffer30m\", \"30 Meters\", \"NONE\", \"ROUND\", \"ALL\"]}\n"; XElement toolUse = XElement.Parse(xmlStr); string fullToolName = toolUse.Element("name")?.Value; string toolArgs = toolUse.Element("arguments")?.Value; Dictionary toolParams = JsonConvert.DeserializeObject>(toolArgs); string serverName = fullToolName.Contains(":") ? fullToolName.Split(':')[0] : fullToolName; string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName; McpServerList mcpServerList = new McpServerList(); McpServer mcpServer = mcpServerList.GetServer(serverName); if (mcpServer is InnerMcpServer) { Type type = Type.GetType("LinkToolAddin.client.tool." + serverName); var toolParamsValues = toolParams.Values.ToArray(); MethodInfo method = type.GetMethod(toolName, BindingFlags.Public | BindingFlags.Static); var task = method.Invoke(null, toolParams.Values.ToArray()) as Task; JsonRpcResultEntity innerResult = await task; MessageListItem toolMessageItem = new ToolMessageItem { toolName = toolName, toolParams = toolParams, type = MessageType.TOOL_MESSAGE, status = "fail", content = JsonConvert.SerializeObject(innerResult), id = "1test" }; AddReply(toolMessageItem); } } private void TestResource_OnClick(object sender, RoutedEventArgs e) { string content = LocalResource.ReadFileByResource("LinkToolAddin.resource.SystemPrompt.txt"); MessageBox.Show(content); } } }