diff --git a/LinkToolAddin.csproj b/LinkToolAddin.csproj
index e0d0d0e..a4143cc 100644
--- a/LinkToolAddin.csproj
+++ b/LinkToolAddin.csproj
@@ -98,6 +98,7 @@
+
@@ -106,6 +107,7 @@
+
diff --git a/client/prompt/DynamicPrompt.cs b/client/prompt/DynamicPrompt.cs
index 2a342e7..52d695c 100644
--- a/client/prompt/DynamicPrompt.cs
+++ b/client/prompt/DynamicPrompt.cs
@@ -4,10 +4,14 @@ namespace LinkToolAddin.client.prompt;
public class DynamicPrompt
{
- public static string GetPrompt(string name,Dictionary args)
+ public static string GetPrompt(string name,Dictionary args = null)
{
PromptTemplates promptTemplate = new PromptTemplates();
string template = promptTemplate.GetPrompt(name);
+ if (args == null)
+ {
+ return template;
+ }
foreach (KeyValuePair pair in args)
{
string replaceKey = "{{"+pair.Key+"}}";
@@ -15,4 +19,11 @@ public class DynamicPrompt
}
return template;
}
+
+ public static Dictionary GetAllPrompts()
+ {
+ PromptTemplates promptTemplate = new PromptTemplates();
+ Dictionary template = promptTemplate.GetPromptsDict();
+ return template;
+ }
}
\ No newline at end of file
diff --git a/client/prompt/PromptTemplates.cs b/client/prompt/PromptTemplates.cs
index 3992482..9ef4ed3 100644
--- a/client/prompt/PromptTemplates.cs
+++ b/client/prompt/PromptTemplates.cs
@@ -17,4 +17,9 @@ public class PromptTemplates
{
return prompts[name];
}
+
+ public Dictionary GetPromptsDict()
+ {
+ return prompts;
+ }
}
\ No newline at end of file
diff --git a/client/tool/ArcGisPro.cs b/client/tool/ArcGisPro.cs
new file mode 100644
index 0000000..da2e604
--- /dev/null
+++ b/client/tool/ArcGisPro.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Threading.Tasks;
+using ArcGIS.Core.Data;
+using ArcGIS.Core.Data.Raster;
+using ArcGIS.Core.Geometry;
+using ArcGIS.Desktop.Framework.Threading.Tasks;
+using LinkToolAddin.server;
+using ModelContextProtocol.Server;
+using Newtonsoft.Json;
+
+namespace LinkToolAddin.client.tool;
+
+public class ArcGisPro
+{
+ [McpServerTool, Description("可以通过调用ArcGIS Pro的地理处理工具实现一些数据处理功能。")]
+ public static async Task ArcGisProTool(string toolName, List toolParams)
+ {
+ // Call the ArcGIS Pro method and get the result
+ var result = await server.CallArcGISPro.CallArcGISProTool(toolName, toolParams);
+
+ // Serialize the result back to a JSON string
+ return result;
+ }
+
+ [McpServerTool, Description("查看指定数据的坐标系、范围、几何类型、是否有Z坐标和M坐标,获取字段列表等")]
+ public static async Task DataProperty(string datasetPath,string dataName)
+ {
+ JsonRpcResultEntity result = new JsonRpcResultEntity();
+ await QueuedTask.Run(() =>
+ {
+ try
+ {
+ using Geodatabase gdb = new Geodatabase(new FileGeodatabaseConnectionPath(new Uri(datasetPath)));
+ FeatureClass featureClass = gdb.OpenDataset(dataName);
+ FeatureClassDefinition featureClassDefinition = featureClass.GetDefinition();
+ SpatialReference spatialReference = featureClassDefinition.GetSpatialReference();
+ GeometryType geometryType = featureClassDefinition.GetShapeType();
+ result = new JsonRpcSuccessEntity()
+ {
+ Id = 1,
+ Result = JsonConvert.SerializeObject(new Dictionary()
+ {
+ {"spatialReference", spatialReference.Name+"(WKID:"+spatialReference.Wkid+")"},
+ {"dataName", dataName},
+ {"geometryType", geometryType.ToString()},
+ {"hasZValue", featureClassDefinition.HasZ()},
+ {"hasMValue", featureClassDefinition.HasM()},
+ {"fields",featureClassDefinition.GetFields()}
+ })
+ };
+ return result;
+ }
+ catch (Exception ex)
+ {
+ result = new JsonRpcErrorEntity()
+ {
+ Error = new Error()
+ {
+ Message = ex.Message
+ },
+ Id = 1
+ };
+ return result;
+ }
+ });
+ return result;
+ }
+
+ [McpServerTool, Description("列出gdb数据库中的所有数据名称")]
+ public static async Task ListData(string gdbPath)
+ {
+ var datasets = new List();
+ await QueuedTask.Run(() =>
+ {
+ using (Geodatabase gdb = new Geodatabase(new FileGeodatabaseConnectionPath(new Uri(gdbPath))))
+ {
+ // 获取所有要素类(Feature Classes)
+ var featureClasses = gdb.GetDefinitions();
+ foreach (var fc in featureClasses)
+ datasets.Add($"要素类: {fc.GetName()}");
+
+ // 获取所有表格(Tables)
+ var tables = gdb.GetDefinitions();
+ foreach (var table in tables)
+ datasets.Add($"表格: {table.GetName()}");
+
+ // 获取所有要素数据集(Feature Datasets)
+ var featureDatasets = gdb.GetDefinitions();
+ foreach (var fd in featureDatasets)
+ datasets.Add($"要素数据集: {fd.GetName()}");
+
+ // 获取所有栅格数据集(Raster Datasets)
+ var rasterDatasets = gdb.GetDefinitions();
+ foreach (var raster in rasterDatasets)
+ datasets.Add($"栅格数据: {raster.GetName()}");
+ }
+ });
+ JsonRpcResultEntity result = new JsonRpcSuccessEntity()
+ {
+ Id = 1,
+ Result = JsonConvert.SerializeObject(datasets)
+ };
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/client/tool/KnowledgeBase.cs b/client/tool/KnowledgeBase.cs
new file mode 100644
index 0000000..9d2b162
--- /dev/null
+++ b/client/tool/KnowledgeBase.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Threading.Tasks;
+using LinkToolAddin.host.llm.entity;
+using LinkToolAddin.resource;
+using LinkToolAddin.server;
+using ModelContextProtocol.Server;
+using Newtonsoft.Json;
+
+namespace LinkToolAddin.client.tool;
+
+public class KnowledgeBase
+{
+ [McpServerTool, Description("可以查询ArcGIS Pro的帮助文档获取关于地理处理工具使用参数的说明")]
+ public static async Task QueryArcgisHelpDoc(string query)
+ {
+ DocDb docDb = new DocDb("sk-db177155677e438f832860e7f4da6afc", DocDb.KnowledgeBase.ArcGISProHelpDoc);
+ KnowledgeResult knowledgeResult = await docDb.Retrieve(query);
+ JsonRpcResultEntity result = new JsonRpcSuccessEntity()
+ {
+ Result = JsonConvert.SerializeObject(knowledgeResult.ChunkList),
+ };
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/common/HttpRequest.cs b/common/HttpRequest.cs
index a38807c..16c9383 100644
--- a/common/HttpRequest.cs
+++ b/common/HttpRequest.cs
@@ -1,8 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using LinkToolAddin.host.llm.entity;
+using LinkToolAddin.host.llm.entity.stream;
using Newtonsoft.Json;
namespace LinkToolAddin.common;
@@ -18,4 +23,104 @@ public class HttpRequest
var responseBody = await response.Content.ReadAsStringAsync();
return responseBody;
}
+
+ public static async IAsyncEnumerable SendStreamPostRequestAsync(string url, string jsonContent, string apiKey)
+ {
+ using var client = new HttpClient();
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
+
+ // 发送 POST 请求并获取响应流
+ var response = await client.PostAsync(url,new StringContent(jsonContent, Encoding.UTF8, "application/json"));
+
+ // 验证响应状态
+ response.EnsureSuccessStatusCode();
+
+ // 获取响应流
+ using var stream = await response.Content.ReadAsStreamAsync();
+ using var reader = new StreamReader(stream);
+
+ // 流式读取
+ string line;
+ while ((line = await reader.ReadLineAsync()) != null)
+ {
+ string content = ProcessPartialResponse(line);
+ yield return content;
+ }
+ }
+
+ private static string ProcessPartialResponse(string rawData)
+ {
+ try
+ {
+ var lines = rawData.Split(new[] { "data: " }, StringSplitOptions.RemoveEmptyEntries);
+ foreach (var line in lines)
+ {
+ var trimmedLine = line.Trim();
+ if (!string.IsNullOrEmpty(trimmedLine))
+ {
+ var result = JsonConvert.DeserializeObject(trimmedLine);
+ return result.Choices[0].Delta.Content;
+ }
+ }
+ }
+ catch { /* 处理解析异常 */ }
+ return null;
+ }
+
+ public static async IAsyncEnumerable PostWithStreamingResponseAsync(
+ string url,
+ string body,
+ string apiKey,
+ string contentType = "application/json",
+ Action configureHeaders = null)
+ {
+ using (var client = new HttpClient())
+ {
+ // 设置超时时间为30分钟
+ client.Timeout = TimeSpan.FromMinutes(30);
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
+ using (var request = new HttpRequestMessage(HttpMethod.Post, url))
+ {
+ // 设置请求头和Body
+ Console.WriteLine("开始请求...");
+ configureHeaders?.Invoke(request);
+ request.Content = new StringContent(body, Encoding.UTF8, contentType);
+
+ // 发送请求并立即开始读取响应流
+ using (var response = await client.SendAsync(
+ request,
+ HttpCompletionOption.ResponseHeadersRead))
+ {
+ response.EnsureSuccessStatusCode();
+
+ // 获取响应流
+ using (var stream = await response.Content.ReadAsStreamAsync())
+ using (var reader = new StreamReader(stream))
+ {
+ string line;
+
+ StringBuilder incompleteJsonBuffer = new StringBuilder();
+
+ // 流式读取并输出到控制台
+ while ((line = await reader.ReadLineAsync()) != null)
+ {
+ foreach (var chunk in line.Split(new[] { "data: " }, StringSplitOptions.RemoveEmptyEntries))
+ {
+ LlmStreamChat dataObj = null;
+ try
+ {
+ dataObj = JsonConvert.DeserializeObject(chunk);
+ }catch{/*process exception*/}
+
+ if (dataObj is not null)
+ {
+ yield return dataObj.Choices[0].Delta.Content;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/common/JsonSchemaGenerator.cs b/common/JsonSchemaGenerator.cs
new file mode 100644
index 0000000..ff799c4
--- /dev/null
+++ b/common/JsonSchemaGenerator.cs
@@ -0,0 +1,150 @@
+using System.Collections.ObjectModel;
+
+namespace LinkToolAddin.common;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text.Json;
+
+public static class JsonSchemaGenerator
+{
+ public static string GenerateJsonSchema(MethodInfo methodInfo)
+ {
+ var parameters = methodInfo.GetParameters();
+ var properties = new Dictionary();
+ var required = new List();
+
+ foreach (var param in parameters)
+ {
+ var paramName = param.Name ?? throw new InvalidOperationException("参数没有名称。");
+ var paramSchema = GenerateSchemaForType(param.ParameterType);
+ properties[paramName] = paramSchema;
+
+ if (!param.IsOptional)
+ {
+ required.Add(paramName);
+ }
+ }
+
+ var schemaRoot = new Dictionary
+ {
+ { "$schema", "http://json-schema.org/draft-07/schema#" },
+ { "type", "object" },
+ { "properties", properties }
+ };
+
+ if (required.Count > 0)
+ {
+ schemaRoot["required"] = required;
+ }
+
+ var options = new JsonSerializerOptions { WriteIndented = true };
+ return JsonSerializer.Serialize(schemaRoot, options);
+ }
+
+ private static object GenerateSchemaForType(Type type)
+ {
+ // 处理可空类型
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
+ {
+ var underlyingType = Nullable.GetUnderlyingType(type);
+ return new[] { GenerateSchemaForType(underlyingType), "null" };
+ }
+
+ // 处理集合类型(数组或IEnumerable)
+ if (IsCollectionType(type, out Type elementType))
+ {
+ return new Dictionary
+ {
+ { "type", "array" },
+ { "items", GenerateSchemaForType(elementType) }
+ };
+ }
+
+ // 处理基本类型(int, string, bool, etc.)
+ if (IsPrimitiveType(type))
+ {
+ string jsonType = MapClrTypeToJsonType(type);
+ var schema = new Dictionary { { "type", jsonType } };
+
+ if (type == typeof(DateTime))
+ schema["format"] = "date-time";
+ else if (type == typeof(Guid))
+ schema["format"] = "uuid";
+
+ return schema;
+ }
+
+ // 处理复杂类型(类、结构体)
+ if (type.IsClass || type.IsValueType)
+ {
+ var props = new Dictionary();
+ foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ {
+ props[prop.Name] = GenerateSchemaForType(prop.PropertyType);
+ }
+
+ return new Dictionary
+ {
+ { "type", "object" },
+ { "properties", props }
+ };
+ }
+
+ // 默认情况
+ return new Dictionary { { "type", "any" } };
+ }
+
+ private static bool IsCollectionType(Type type, out Type elementType)
+ {
+ if (type == typeof(string))
+ {
+ elementType = null;
+ return false;
+ }
+
+ if (type.IsArray)
+ {
+ elementType = type.GetElementType();
+ return true;
+ }
+
+ if (type.IsGenericType)
+ {
+ var genericTypeDef = type.GetGenericTypeDefinition();
+ if (genericTypeDef == typeof(IEnumerable<>) ||
+ genericTypeDef == typeof(List<>) ||
+ genericTypeDef == typeof(Collection<>))
+ {
+ elementType = type.GetGenericArguments()[0];
+ return true;
+ }
+ }
+
+ elementType = null;
+ return false;
+ }
+
+ private static bool IsPrimitiveType(Type type)
+ {
+ return type.IsPrimitive || type == typeof(string) || type == typeof(decimal) || type == typeof(DateTime) || type == typeof(Guid);
+ }
+
+ private static string MapClrTypeToJsonType(Type type)
+ {
+ if (type == typeof(int) || type == typeof(short) || type == typeof(long) ||
+ type == typeof(uint) || type == typeof(ushort) || type == typeof(ulong))
+ return "integer";
+ if (type == typeof(float) || type == typeof(double) || type == typeof(decimal))
+ return "number";
+ if (type == typeof(bool))
+ return "boolean";
+ if (type == typeof(string))
+ return "string";
+ if (type == typeof(DateTime) || type == typeof(Guid))
+ return "string";
+ return "any";
+ }
+}
\ No newline at end of file
diff --git a/host/CallMcp.cs b/host/CallMcp.cs
index a98488d..0e48789 100644
--- a/host/CallMcp.cs
+++ b/host/CallMcp.cs
@@ -18,8 +18,8 @@ namespace LinkToolAddin.host
log.Info("通过反射调用内部MCP工具");
var jsonRpcEntity = JsonConvert.DeserializeObject(jsonRpcString);
- Type type = Type.GetType("LinkToolAddin.client."+jsonRpcEntity.Method.Split('.')[0]);
- MethodInfo method = type.GetMethod(jsonRpcEntity.Method.Split('.')[1],BindingFlags.Public | BindingFlags.Static);
+ Type type = Type.GetType("LinkToolAddin.client.tool"+jsonRpcEntity.Method.Split(':')[0]);
+ MethodInfo method = type.GetMethod(jsonRpcEntity.Method.Split(':')[1],BindingFlags.Public | BindingFlags.Static);
var task = method.Invoke(null, new object[] { jsonRpcEntity.Params }) as Task;
JsonRpcResultEntity result = await task;
return JsonConvert.SerializeObject(result);
diff --git a/host/Gateway.cs b/host/Gateway.cs
index 4aca530..aef75b3 100644
--- a/host/Gateway.cs
+++ b/host/Gateway.cs
@@ -1,8 +1,17 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Reflection;
+using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
+using System.Threading.Tasks;
+using System.Xml;
using System.Xml.Linq;
+using ArcGIS.Desktop.Framework.Dialogs;
+using ArcGIS.Desktop.Internal.Mapping.Locate;
using LinkToolAddin.client;
using LinkToolAddin.client.prompt;
using LinkToolAddin.host.llm;
@@ -10,13 +19,24 @@ using LinkToolAddin.host.llm.entity;
using LinkToolAddin.host.mcp;
using LinkToolAddin.host.prompt;
using LinkToolAddin.message;
+using LinkToolAddin.server;
+using LinkToolAddin.ui.dockpane;
+using log4net;
+using Microsoft.Extensions.AI;
+using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Types;
using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json.Schema;
+using Newtonsoft.Json.Schema.Generation;
+using Tool = LinkToolAddin.host.mcp.Tool;
+using LinkToolAddin.common;
namespace LinkToolAddin.host;
public class Gateway
{
+ private static ILog log = LogManager.GetLogger(typeof(Gateway));
public static async void SendMessage(string message, string model, string gdbPath, Action callback)
{
Llm bailian = new Bailian
@@ -24,10 +44,12 @@ public class Gateway
api_key = "sk-db177155677e438f832860e7f4da6afc"
};
List messages = new List();
+ string toolInfos = await GetToolInfos(new McpServerList());
+ log.Info(SystemPrompt.SysPrompt(gdbPath, toolInfos));
messages.Add(new Message
{
Role = "system",
- Content = SystemPrompt.SysPromptTemplate
+ Content = SystemPrompt.SysPrompt(gdbPath, toolInfos)
});
messages.Add(new Message
{
@@ -35,9 +57,9 @@ public class Gateway
Content = message
});
bool goOn = true;
- string pattern = "[\\s\\S]*?<\\/tool_use>";
- string promptPattern = "[\\s\\S]*?<\\/prompt>";
- Dictionary servers = new Dictionary();
+ string pattern = "^[\\s\\S]*?<\\/tool_use>$";
+ string promptPattern = "^[\\s\\S]*?<\\/prompt>$";
+ McpServerList mcpServerList = new McpServerList();
while (goOn)
{
string reponse = await bailian.SendChatAsync(new LlmJsonContent()
@@ -48,6 +70,8 @@ public class Gateway
TopP = 1,
MaxTokens = 1000,
});
+ long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+ log.Info(reponse);
messages.Add(new Message
{
Role = "assistant",
@@ -62,7 +86,7 @@ public class Gateway
Dictionary toolParams = JsonConvert.DeserializeObject>(toolArgs);
string serverName = fullToolName.Contains(":") ? fullToolName.Split(':')[0] : fullToolName;
string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName;
- McpServer mcpServer = servers[serverName];
+ McpServer mcpServer = mcpServerList.GetServer(serverName);
if (mcpServer is SseMcpServer)
{
SseMcpServer sseMcpServer = mcpServer as SseMcpServer;
@@ -74,7 +98,8 @@ public class Gateway
toolParams = toolParams,
type = MessageType.TOOL_MESSAGE,
status = toolResponse.IsError ? "fail" : "success",
- content = toolResponse.Content.ToString()
+ content = JsonConvert.SerializeObject(toolResponse),
+ id = timestamp.ToString()
};
messages.Add(new Message
{
@@ -98,7 +123,8 @@ public class Gateway
toolParams = toolParams,
type = MessageType.TOOL_MESSAGE,
status = toolResponse.IsError ? "fail" : "success",
- content = toolResponse.Content.ToString()
+ content = JsonConvert.SerializeObject(toolResponse),
+ id = timestamp.ToString()
};
messages.Add(new Message
{
@@ -111,6 +137,55 @@ public class Gateway
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 task = method.Invoke(null, toolParams.Values.ToArray()) as Task;
+ 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)
+ };
+ 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)
+ };
+ messages.Add(new Message
+ {
+ Role = "user",
+ Content = SystemPrompt.ContinuePromptTemplate
+ });
+ messages.Add(new Message
+ {
+ Role = "user",
+ Content = JsonConvert.SerializeObject(innerResult)
+ });
+ callback?.Invoke(toolMessageItem);
+ }
}
}
else if (Regex.IsMatch(reponse, promptPattern))
@@ -134,7 +209,8 @@ public class Gateway
{
content = reponse,
role = "assistant",
- type = MessageType.CHAT_MESSAGE
+ type = MessageType.CHAT_MESSAGE,
+ id = timestamp.ToString()
};
callback?.Invoke(chatMessageListItem);
}
@@ -144,6 +220,375 @@ public class Gateway
}
}
}
+
+ public static async void SendMessageStream(string message, string model, string gdbPath, Action callback)
+ {
+ Llm bailian = new Bailian
+ {
+ api_key = "sk-db177155677e438f832860e7f4da6afc"
+ };
+ List messages = new List();
+ string toolInfos = await GetToolInfos(new McpServerList());
+ log.Info(SystemPrompt.SysPrompt(gdbPath, toolInfos));
+ messages.Add(new Message
+ {
+ Role = "system",
+ Content = SystemPrompt.SysPrompt(gdbPath, toolInfos)
+ });
+ messages.Add(new Message
+ {
+ Role = "user",
+ Content = message
+ });
+ bool goOn = true;
+ string toolPattern = "^[\\s\\S]*?<\\/tool_use>$";
+ string promptPattern = "^[\\s\\S]*?<\\/prompt>$";
+ McpServerList mcpServerList = new McpServerList();
+ while (goOn)
+ {
+ LlmJsonContent jsonContent = new LlmJsonContent()
+ {
+ Model = model,
+ Messages = messages,
+ Temperature = 0.7,
+ TopP = 1,
+ MaxTokens = 1000,
+ };
+ long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+ string messageContent = "";
+ await foreach(var chunk in bailian.SendChatStreamAsync(jsonContent))
+ {
+ if (chunk == "[DONE]")
+ {
+ goOn = false;
+ }else if (chunk.StartsWith(""))
+ {
+ if (Regex.IsMatch(chunk, toolPattern))
+ {
+ //返回工具卡片
+ XElement toolUse = XElement.Parse(chunk);
+ 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;
+ 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
+ });
+ 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
+ });
+ 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 task = method.Invoke(null, toolParams.Values.ToArray()) as Task;
+ 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
+ });
+ messages.Add(new Message
+ {
+ Role = "user",
+ Content = JsonConvert.SerializeObject(innerResult)
+ });
+ callback?.Invoke(toolMessageItem);
+ }
+ }
+ }
+ else
+ {
+ MessageListItem toolMessageItem = new ToolMessageItem
+ {
+ toolName = "",
+ toolParams = new Dictionary(),
+ type = MessageType.TOOL_MESSAGE,
+ status = "loading",
+ content = "正在生成工具调用参数",
+ id = timestamp.ToString()
+ };
+ callback?.Invoke(toolMessageItem);
+ continue;
+ }
+ }else if (chunk.StartsWith(""))
+ {
+ if (Regex.IsMatch(chunk, promptPattern))
+ {
+ XElement promptUse = XElement.Parse(chunk);
+ string promptKey = promptUse.Element("name")?.Value;
+ string promptContent = DynamicPrompt.GetPrompt(promptKey,null);
+ messages.Add(new Message
+ {
+ Role = "user",
+ Content = JsonConvert.SerializeObject(promptContent)
+ });
+ MessageListItem toolMessageItem = new ToolMessageItem
+ {
+ toolName = "调用提示词",
+ toolParams = null,
+ type = MessageType.TOOL_MESSAGE,
+ status = "success",
+ content = promptKey,
+ id = timestamp.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);
+ }
+ }
+ else
+ {
+ //普通流式消息卡片
+ MessageListItem chatMessageListItem = new ChatMessageItem()
+ {
+ content = chunk,
+ role = "assistant",
+ type = MessageType.CHAT_MESSAGE,
+ id = timestamp.ToString()
+ };
+ messageContent = chunk;
+ callback?.Invoke(chatMessageListItem);
+ }
+ }
+ messages.Add(new Message
+ {
+ Role = "assistant",
+ Content = messageContent
+ });
+ }
+ }
+
+ private static async Task GetToolInfos(McpServerList mcpServerList)
+ {
+ int loop = 0;
+ StringBuilder toolInfos = new StringBuilder();
+ foreach (McpServer mcpServer in mcpServerList.GetAllServers())
+ {
+ loop++;
+ if (loop > 3)
+ {
+ MessageBox.Show("达到最大循环次数", "退出循环");
+ break;
+ }
+ if (mcpServer is InnerMcpServer)
+ {
+ InnerMcpServer innerMcpServer = (InnerMcpServer)mcpServer;
+ Type type = Type.GetType("LinkToolAddin.client.tool." + innerMcpServer.Name);
+ MethodInfo[] methods = type.GetMethods();
+ foreach (MethodInfo method in methods)
+ {
+ if (method.IsPublic && method.IsStatic)
+ {
+ string methodName = method.Name;
+ string methodDescription = method.GetCustomAttribute()?.Description;
+ string methodParamSchema = LinkToolAddin.common.JsonSchemaGenerator.GenerateJsonSchema(method);
+ McpToolDefinition toolDefinition = new McpToolDefinition
+ {
+ Tool = new Tool
+ {
+ Name = innerMcpServer.Name + ":" + methodName,
+ Description = methodDescription,
+ Arguments = methodParamSchema
+ }
+ };
+ XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition));
+ toolInfos.AppendLine(node.ToString());
+ toolInfos.AppendLine();
+ }
+ }
+ }
+ else if(mcpServer is SseMcpServer)
+ {
+ SseMcpClient client = new SseMcpClient((mcpServer as SseMcpServer).BaseUrl);
+ IList tools = await client.GetToolListAsync();
+ foreach (McpClientTool tool in tools)
+ {
+ string toolName = (mcpServer as SseMcpServer).Name + ":" + tool.Name;
+ string toolDescription = tool.Description;
+ string toolParamSchema = tool.JsonSchema.ToString();
+ McpToolDefinition toolDefinition = new McpToolDefinition
+ {
+ Tool = new Tool
+ {
+ Name = toolName,
+ Description = toolDescription,
+ Arguments = toolParamSchema
+ }
+ };
+ toolInfos.AppendLine(JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition)).ToString());
+ toolInfos.AppendLine();
+ }
+ }else if (mcpServer is StdioMcpServer)
+ {
+ StdioMcpClient client = new StdioMcpClient((mcpServer as StdioMcpServer).Command, (mcpServer as StdioMcpServer).Args);
+ IList tools = await client.GetToolListAsync();
+ foreach (McpClientTool tool in tools)
+ {
+ string toolName = (mcpServer as StdioMcpServer).Name + ":" + tool.Name;;
+ string toolDescription = tool.Description;
+ string toolParamSchema = tool.JsonSchema.ToString();
+ McpToolDefinition toolDefinition = new McpToolDefinition
+ {
+ Tool = new Tool
+ {
+ Name = toolName,
+ Description = toolDescription,
+ Arguments = CompressJson(toolParamSchema)
+ }
+ };
+ toolInfos.AppendLine(JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition)).ToString());
+ toolInfos.AppendLine();
+ }
+ }
+ }
+
+ Dictionary prompts = DynamicPrompt.GetAllPrompts();
+ foreach (KeyValuePair prompt in prompts)
+ {
+ McpPromptDefinition promptDefinition = new McpPromptDefinition
+ {
+ Prompt = new LinkToolAddin.host.mcp.Prompt
+ {
+ Name = prompt.Key
+ }
+ };
+ XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(promptDefinition));
+ toolInfos.AppendLine(node.ToString());
+ toolInfos.AppendLine();
+ }
+ return toolInfos.ToString();
+ }
+
+ public static string CompressJson(string json)
+ {
+ // 解析JSON并自动去除无关空白
+ var token = JToken.Parse(json);
+ // 序列化为无格式紧凑字符串
+ return token.ToString(Newtonsoft.Json.Formatting.None);
+ }
+
+ private static string GenerateMethodParamSchema(MethodInfo method)
+ {
+ var generator = new JSchemaGenerator
+ {
+ // 启用属性注解处理
+ DefaultRequired = Required.DisallowNull,
+ SchemaReferenceHandling = SchemaReferenceHandling.None
+ };
+
+ var paramSchema = new JSchema { Type = JSchemaType.Object };
+
+ foreach (ParameterInfo param in method.GetParameters())
+ {
+ // 生成参数类型的基础Schema
+ JSchema typeSchema = generator.Generate(param.ParameterType);
+
+ // 添加Description描述
+ var descriptionAttr = param.GetCustomAttribute();
+ if (descriptionAttr != null)
+ {
+ typeSchema.Description = descriptionAttr.Description; // 网页6的Description特性处理
+ }
+
+ paramSchema.Properties.Add(param.Name, typeSchema);
+ }
+ var settings = new JsonSerializerSettings {
+ Formatting = Newtonsoft.Json.Formatting.None, // 关键设置:禁用缩进和换行
+ NullValueHandling = NullValueHandling.Ignore // 可选:忽略空值
+ };
+ return JsonConvert.SerializeObject(paramSchema, settings);;
+ }
public static async void TestChatMessage(string message, string model, string gdbPath,//message,qwen-max,传个空字符串,传一个方法(回调,用来调用方法)1
Action callback)
diff --git a/host/McpServerList.cs b/host/McpServerList.cs
new file mode 100644
index 0000000..2c03a41
--- /dev/null
+++ b/host/McpServerList.cs
@@ -0,0 +1,54 @@
+using System.Collections.Generic;
+using LinkToolAddin.host.mcp;
+
+namespace LinkToolAddin.host;
+
+public class McpServerList
+{
+ private Dictionary servers = new Dictionary();
+
+ public McpServerList()
+ {
+ servers.Add("gaode",new SseMcpServer
+ {
+ Name = "gaode",
+ Type = "sse",
+ Description = "高德地图API",
+ IsActive = true,
+ BaseUrl = "https://mcp.amap.com/sse?key=ed418512c94ade8f83d42c37b77d2bb2",
+ Headers = new Dictionary()
+ {
+ {"Content-Type","application/json"}
+ }
+ });
+ servers.Add("ArcGisPro", new InnerMcpServer
+ {
+ Name = "ArcGisPro",
+ Type = "inner",
+ Description = "可以调用arcgis的地理处理工具或执行python代码等",
+ IsActive = true
+ });
+ }
+
+ public McpServer GetServer(string name)
+ {
+ if (servers.ContainsKey(name))
+ {
+ return servers[name];
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public List GetAllServers()
+ {
+ List serverList = new List();
+ foreach (var server in servers)
+ {
+ serverList.Add(server.Value);
+ }
+ return serverList;
+ }
+}
\ No newline at end of file
diff --git a/host/llm/Bailian.cs b/host/llm/Bailian.cs
index bd444c7..b43eed0 100644
--- a/host/llm/Bailian.cs
+++ b/host/llm/Bailian.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
@@ -17,9 +18,18 @@ public class Bailian : Llm
public string max_tokens { get; set; }
public string app_id { get; set; }
public string api_key { get; set; }
- public IAsyncEnumerable SendChatStreamAsync(string message)
+ public async IAsyncEnumerable SendChatStreamAsync(LlmJsonContent jsonContent)
{
- throw new System.NotImplementedException();
+ jsonContent.Stream = true;
+ StringBuilder builder = new StringBuilder();
+ await foreach (var chunk in HttpRequest.PostWithStreamingResponseAsync(
+ "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
+ JsonConvert.SerializeObject(jsonContent),
+ api_key))
+ {
+ builder.Append(chunk);
+ yield return builder.ToString();
+ }
}
public IAsyncEnumerable SendApplicationStreamAsync(string message)
diff --git a/host/llm/Llm.cs b/host/llm/Llm.cs
index 62a11f4..1807b9d 100644
--- a/host/llm/Llm.cs
+++ b/host/llm/Llm.cs
@@ -11,7 +11,7 @@ public interface Llm
public string top_p { get; set; }
public string max_tokens { get; set; }
- public IAsyncEnumerable SendChatStreamAsync(string message);
+ public IAsyncEnumerable SendChatStreamAsync(LlmJsonContent jsonContent);
public IAsyncEnumerable SendApplicationStreamAsync(string message);
public Task SendChatAsync(LlmJsonContent jsonContent);
public Task SendApplicationAsync(CommonInput commonInput);
diff --git a/host/llm/entity/stream/LlmStreamChat.cs b/host/llm/entity/stream/LlmStreamChat.cs
new file mode 100644
index 0000000..6991982
--- /dev/null
+++ b/host/llm/entity/stream/LlmStreamChat.cs
@@ -0,0 +1,54 @@
+namespace LinkToolAddin.host.llm.entity.stream
+{
+ using System;
+ using System.Collections.Generic;
+
+ using System.Globalization;
+ using Newtonsoft.Json;
+ using Newtonsoft.Json.Converters;
+
+ public partial class LlmStreamChat
+ {
+ [JsonProperty("choices")]
+ public Choice[] Choices { get; set; }
+
+ [JsonProperty("object")]
+ public string Object { get; set; }
+
+ [JsonProperty("usage")]
+ public object Usage { get; set; }
+
+ [JsonProperty("created")]
+ public long Created { get; set; }
+
+ [JsonProperty("system_fingerprint")]
+ public object SystemFingerprint { get; set; }
+
+ [JsonProperty("model")]
+ public string Model { get; set; }
+
+ [JsonProperty("id")]
+ public string Id { get; set; }
+ }
+
+ public partial class Choice
+ {
+ [JsonProperty("finish_reason")]
+ public string FinishReason { get; set; }
+
+ [JsonProperty("delta")]
+ public Delta Delta { get; set; }
+
+ [JsonProperty("index")]
+ public long Index { get; set; }
+
+ [JsonProperty("logprobs")]
+ public object Logprobs { get; set; }
+ }
+
+ public partial class Delta
+ {
+ [JsonProperty("content")]
+ public string Content { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/host/mcp/InnerMcpServer.cs b/host/mcp/InnerMcpServer.cs
new file mode 100644
index 0000000..2b06319
--- /dev/null
+++ b/host/mcp/InnerMcpServer.cs
@@ -0,0 +1,24 @@
+namespace LinkToolAddin.host.mcp
+{
+ using System;
+ using System.Collections.Generic;
+
+ using System.Globalization;
+ using Newtonsoft.Json;
+ using Newtonsoft.Json.Converters;
+
+ public partial class InnerMcpServer : McpServer
+ {
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("type")]
+ public string Type { get; set; }
+
+ [JsonProperty("description")]
+ public string Description { get; set; }
+
+ [JsonProperty("isActive")]
+ public bool IsActive { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/host/mcp/McpPromptDefinition.cs b/host/mcp/McpPromptDefinition.cs
new file mode 100644
index 0000000..83ee903
--- /dev/null
+++ b/host/mcp/McpPromptDefinition.cs
@@ -0,0 +1,27 @@
+namespace LinkToolAddin.host.mcp
+{
+ using System;
+ using System.Collections.Generic;
+
+ using System.Globalization;
+ using Newtonsoft.Json;
+ using Newtonsoft.Json.Converters;
+
+ public partial class McpPromptDefinition
+ {
+ [JsonProperty("prompt")]
+ public Prompt Prompt { get; set; }
+ }
+
+ public partial class Prompt
+ {
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("description")]
+ public string Description { get; set; }
+
+ [JsonProperty("arguments")]
+ public string Arguments { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/host/mcp/McpToolDefinition.cs b/host/mcp/McpToolDefinition.cs
new file mode 100644
index 0000000..6e79610
--- /dev/null
+++ b/host/mcp/McpToolDefinition.cs
@@ -0,0 +1,27 @@
+namespace LinkToolAddin.host.mcp
+{
+ using System;
+ using System.Collections.Generic;
+
+ using System.Globalization;
+ using Newtonsoft.Json;
+ using Newtonsoft.Json.Converters;
+
+ public partial class McpToolDefinition
+ {
+ [JsonProperty("tool")]
+ public Tool Tool { get; set; }
+ }
+
+ public partial class Tool
+ {
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("description")]
+ public string Description { get; set; }
+
+ [JsonProperty("arguments")]
+ public string Arguments { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/host/prompt/SystemPrompt.cs b/host/prompt/SystemPrompt.cs
index bcd3768..0dde1f7 100644
--- a/host/prompt/SystemPrompt.cs
+++ b/host/prompt/SystemPrompt.cs
@@ -2,10 +2,18 @@
public class SystemPrompt
{
- public static string SysPromptTemplate = "现在你是一个精通ArcGIS Pro的专家,请以此身份回答用户的问题。";
+ public static string SysPromptTemplate = "现在你是一个精通ArcGIS Pro的专家,请以此身份回答用户的问题。你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。MCP工具调用的格式要求示例,必须用标签表示工具调用:\n search\n {\\\"query\\\": \\\"上海 人口\\\"}\n。你每次调用请求都必须放在单独的一条消息中,不附带任何的文字说明,不带markdown格式。如需文字说明,请另外放在一次单独的消息中。\n当你认为已解决用户最初提出的问题时,请输出单独的一条消息,内容为[DONE],不附带任何其它文字说明,程序识别到后会退出循环。\n此外,你还可以通过调用用户提示词,从而使你更好地理解和完成用户的任务。";
- public static string ContinuePromptTemplate = "上一个工具执行的结果如下,请据此继续执行";
+ public static string ContinuePromptTemplate = "工具执行的结果如下,根据以上结果决定继续执行工具或是根据结果回答问题。如果不再需要额外说明和额外的操作,请回答单独的一条内容为[DONE]的消息。工具已经成功调用,请勿重复执行上一个工具";
public static string ErrorPromptTemplate = "执行上一个工具的时候出现以下错误,请根据报错信息重试";
+ public static string SysPrompt(string gdbPath, string toolInfos)
+ {
+ string sysPrompt = SysPromptTemplate;
+ sysPrompt = sysPrompt.Replace("{{gdbPath}}", gdbPath);
+ sysPrompt = sysPrompt.Replace("{{toolInfos}}", toolInfos);
+ return sysPrompt;
+ }
+
}
\ No newline at end of file
diff --git a/server/CallArcGISPro.cs b/server/CallArcGISPro.cs
index 39f5ef0..049fd3f 100644
--- a/server/CallArcGISPro.cs
+++ b/server/CallArcGISPro.cs
@@ -10,14 +10,29 @@ namespace LinkToolAddin.server;
public class CallArcGISPro
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(CallArcGISPro));
+
public async static Task CallArcGISProTool(string toolName, List toolParams)
{
var results = await Geoprocessing.ExecuteToolAsync(toolName, toolParams);
- log.Info($"CallArcGISProTool: {toolName} | {toolParams}");
- return new JsonRpcSuccessEntity()
+ JsonRpcResultEntity jsonRpcResultEntity;
+ if (results.ErrorCode == 0)
{
- Id = 1,
- Result = results.ToString()
- };
+ jsonRpcResultEntity = new JsonRpcErrorEntity()
+ {
+ Error = new Error()
+ {
+ Code = results.ErrorCode,
+ Message = results.ErrorMessages.ToString()
+ }
+ };
+ }
+ else
+ {
+ jsonRpcResultEntity = new JsonRpcSuccessEntity
+ {
+ Result = results.Messages.ToString()
+ };
+ }
+ return jsonRpcResultEntity;
}
}
\ No newline at end of file
diff --git a/server/JsonRpcErrorEntity.cs b/server/JsonRpcErrorEntity.cs
index 46381de..ad0d795 100644
--- a/server/JsonRpcErrorEntity.cs
+++ b/server/JsonRpcErrorEntity.cs
@@ -2,10 +2,10 @@ namespace LinkToolAddin.server
{
using Newtonsoft.Json;
- public partial class JsonRpcErrorEntity
+ public partial class JsonRpcErrorEntity : JsonRpcResultEntity
{
[JsonProperty("jsonrpc")]
- public string Jsonrpc { get; set; }
+ public string Jsonrpc { get; set; } = "2.0";
[JsonProperty("error")]
public Error Error { get; set; }
diff --git a/ui/dockpane/TestDockpane.xaml b/ui/dockpane/TestDockpane.xaml
index 4782989..fd7d8c8 100644
--- a/ui/dockpane/TestDockpane.xaml
+++ b/ui/dockpane/TestDockpane.xaml
@@ -18,11 +18,22 @@
-
-
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ui/dockpane/TestDockpane.xaml.cs b/ui/dockpane/TestDockpane.xaml.cs
index 8eeab55..44bed74 100644
--- a/ui/dockpane/TestDockpane.xaml.cs
+++ b/ui/dockpane/TestDockpane.xaml.cs
@@ -1,4 +1,6 @@
using System.Collections.Generic;
+using System.Linq;
+using System.Text;
using System.Windows;
using System.Windows.Controls;
using LinkToolAddin.client;
@@ -26,7 +28,10 @@ namespace LinkToolAddin.ui.dockpane
{
private static ILog log = LogManager.GetLogger(typeof(TestDockpaneView));
- public TestDockpaneView()//构造方法,没有返回参数1
+ private List idList = new List();
+ private Dictionary messageDict = new Dictionary();
+
+ public TestDockpaneView()
{
InitLogger();//初始化日志输出器1
InitializeComponent();
@@ -138,6 +143,35 @@ namespace LinkToolAddin.ui.dockpane
});
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)
{
@@ -146,12 +180,56 @@ namespace LinkToolAddin.ui.dockpane
private void TestWorkflow_OnClick(object sender, RoutedEventArgs e)
{
- Gateway.SendMessage("你好","qwen-max","test.gdb",ShowMessage);//标题名称,模型名称,虚构的gdb的路径,回调1
+ // Gateway.SendMessage("你有什么工具可以调用的?","qwen-max","test.gdb",ShowMessage);
+ Gateway.TestWorkflow("dafdfdgdagdgui","","",AddReply);
}
public void ShowMessage(MessageListItem msg)
{
log.Info(msg.content);
}
+
+ private void PromptTestButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ string userPrompt = PromptTestTextBox.Text;
+ // Gateway.SendMessage(userPrompt,"qwen-max","C:/Project/test.gdb",AddReply);
+ Gateway.SendMessageStream(userPrompt,"qwen-max","D:\\01_Project\\20250305_LinkTool\\20250420_AiDemoProject\\20250420_AiDemoProject.gdb",AddReplyStream);
+ }
+
+ public void AddReplyStream(MessageListItem msg)
+ {
+ string id = msg.id;
+ if (idList.Contains(id))
+ {
+ messageDict[id] = msg;
+ }
+ else
+ {
+ idList.Add(id);
+ messageDict.Add(msg.id, msg);
+ }
+ ReplyTextBox.Clear();
+ StringBuilder builder = new StringBuilder();
+ foreach (KeyValuePair pair in messageDict)
+ {
+ MessageListItem msgItem = pair.Value;
+ builder.AppendLine(msgItem.content);
+ ReplyTextBox.Text = builder.ToString();
+ ReplyTextBox.ScrollToEnd();
+ }
+ }
+
+ 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();
+ }
}
}
diff --git a/ui/dockpane/TestDockpaneViewModel.cs b/ui/dockpane/TestDockpaneViewModel.cs
index 0661d9c..daf9d4c 100644
--- a/ui/dockpane/TestDockpaneViewModel.cs
+++ b/ui/dockpane/TestDockpaneViewModel.cs
@@ -41,7 +41,7 @@ namespace LinkToolAddin.ui.dockpane
///
/// Text shown near the top of the DockPane.
///
- private string _heading = "My DockPane";
+ private string _heading = "Test Dockpane";
public string Heading
{
get => _heading;