合并host分支
This commit is contained in:
commit
51b1cc1b7d
@ -98,6 +98,7 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Folder Include="doc\" />
|
||||||
<Folder Include="resource\" />
|
<Folder Include="resource\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -106,6 +107,7 @@
|
|||||||
<PackageReference Include="ModelContextProtocol" Version="0.1.0-preview.13" />
|
<PackageReference Include="ModelContextProtocol" Version="0.1.0-preview.13" />
|
||||||
<PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.1.0-preview.13" />
|
<PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.1.0-preview.13" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json.Schema" Version="4.0.2-beta2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="C:\Program Files\ArcGIS\Pro\bin\Esri.ProApp.SDK.Desktop.targets" Condition="Exists('C:\Program Files\ArcGIS\Pro\bin\Esri.ProApp.SDK.Desktop.targets') AND !Exists('Esri.ArcGISPro.Extensions.targets')" />
|
<Import Project="C:\Program Files\ArcGIS\Pro\bin\Esri.ProApp.SDK.Desktop.targets" Condition="Exists('C:\Program Files\ArcGIS\Pro\bin\Esri.ProApp.SDK.Desktop.targets') AND !Exists('Esri.ArcGISPro.Extensions.targets')" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -4,10 +4,14 @@ namespace LinkToolAddin.client.prompt;
|
|||||||
|
|
||||||
public class DynamicPrompt
|
public class DynamicPrompt
|
||||||
{
|
{
|
||||||
public static string GetPrompt(string name,Dictionary<string,object> args)
|
public static string GetPrompt(string name,Dictionary<string,object> args = null)
|
||||||
{
|
{
|
||||||
PromptTemplates promptTemplate = new PromptTemplates();
|
PromptTemplates promptTemplate = new PromptTemplates();
|
||||||
string template = promptTemplate.GetPrompt(name);
|
string template = promptTemplate.GetPrompt(name);
|
||||||
|
if (args == null)
|
||||||
|
{
|
||||||
|
return template;
|
||||||
|
}
|
||||||
foreach (KeyValuePair<string,object> pair in args)
|
foreach (KeyValuePair<string,object> pair in args)
|
||||||
{
|
{
|
||||||
string replaceKey = "{{"+pair.Key+"}}";
|
string replaceKey = "{{"+pair.Key+"}}";
|
||||||
@ -15,4 +19,11 @@ public class DynamicPrompt
|
|||||||
}
|
}
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Dictionary<string, string> GetAllPrompts()
|
||||||
|
{
|
||||||
|
PromptTemplates promptTemplate = new PromptTemplates();
|
||||||
|
Dictionary<string, string> template = promptTemplate.GetPromptsDict();
|
||||||
|
return template;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -17,4 +17,9 @@ public class PromptTemplates
|
|||||||
{
|
{
|
||||||
return prompts[name];
|
return prompts[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, string> GetPromptsDict()
|
||||||
|
{
|
||||||
|
return prompts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
107
client/tool/ArcGisPro.cs
Normal file
107
client/tool/ArcGisPro.cs
Normal file
@ -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<JsonRpcResultEntity> ArcGisProTool(string toolName, List<string> 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<JsonRpcResultEntity> 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<FeatureClass>(dataName);
|
||||||
|
FeatureClassDefinition featureClassDefinition = featureClass.GetDefinition();
|
||||||
|
SpatialReference spatialReference = featureClassDefinition.GetSpatialReference();
|
||||||
|
GeometryType geometryType = featureClassDefinition.GetShapeType();
|
||||||
|
result = new JsonRpcSuccessEntity()
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Result = JsonConvert.SerializeObject(new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
{"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<JsonRpcResultEntity> ListData(string gdbPath)
|
||||||
|
{
|
||||||
|
var datasets = new List<string>();
|
||||||
|
await QueuedTask.Run(() =>
|
||||||
|
{
|
||||||
|
using (Geodatabase gdb = new Geodatabase(new FileGeodatabaseConnectionPath(new Uri(gdbPath))))
|
||||||
|
{
|
||||||
|
// 获取所有要素类(Feature Classes)
|
||||||
|
var featureClasses = gdb.GetDefinitions<FeatureClassDefinition>();
|
||||||
|
foreach (var fc in featureClasses)
|
||||||
|
datasets.Add($"要素类: {fc.GetName()}");
|
||||||
|
|
||||||
|
// 获取所有表格(Tables)
|
||||||
|
var tables = gdb.GetDefinitions<TableDefinition>();
|
||||||
|
foreach (var table in tables)
|
||||||
|
datasets.Add($"表格: {table.GetName()}");
|
||||||
|
|
||||||
|
// 获取所有要素数据集(Feature Datasets)
|
||||||
|
var featureDatasets = gdb.GetDefinitions<FeatureDatasetDefinition>();
|
||||||
|
foreach (var fd in featureDatasets)
|
||||||
|
datasets.Add($"要素数据集: {fd.GetName()}");
|
||||||
|
|
||||||
|
// 获取所有栅格数据集(Raster Datasets)
|
||||||
|
var rasterDatasets = gdb.GetDefinitions<RasterDatasetDefinition>();
|
||||||
|
foreach (var raster in rasterDatasets)
|
||||||
|
datasets.Add($"栅格数据: {raster.GetName()}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
JsonRpcResultEntity result = new JsonRpcSuccessEntity()
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Result = JsonConvert.SerializeObject(datasets)
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
client/tool/KnowledgeBase.cs
Normal file
25
client/tool/KnowledgeBase.cs
Normal file
@ -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<JsonRpcResultEntity> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,8 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using LinkToolAddin.host.llm.entity;
|
using LinkToolAddin.host.llm.entity;
|
||||||
|
using LinkToolAddin.host.llm.entity.stream;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace LinkToolAddin.common;
|
namespace LinkToolAddin.common;
|
||||||
@ -18,4 +23,104 @@ public class HttpRequest
|
|||||||
var responseBody = await response.Content.ReadAsStringAsync();
|
var responseBody = await response.Content.ReadAsStringAsync();
|
||||||
return responseBody;
|
return responseBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async IAsyncEnumerable<string> 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<LlmStreamChat>(trimmedLine);
|
||||||
|
return result.Choices[0].Delta.Content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { /* 处理解析异常 */ }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async IAsyncEnumerable<string> PostWithStreamingResponseAsync(
|
||||||
|
string url,
|
||||||
|
string body,
|
||||||
|
string apiKey,
|
||||||
|
string contentType = "application/json",
|
||||||
|
Action<HttpRequestMessage> 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<LlmStreamChat>(chunk);
|
||||||
|
}catch{/*process exception*/}
|
||||||
|
|
||||||
|
if (dataObj is not null)
|
||||||
|
{
|
||||||
|
yield return dataObj.Choices[0].Delta.Content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
150
common/JsonSchemaGenerator.cs
Normal file
150
common/JsonSchemaGenerator.cs
Normal file
@ -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<string, object>();
|
||||||
|
var required = new List<string>();
|
||||||
|
|
||||||
|
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<string, object>
|
||||||
|
{
|
||||||
|
{ "$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<T>)
|
||||||
|
if (IsCollectionType(type, out Type elementType))
|
||||||
|
{
|
||||||
|
return new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "type", "array" },
|
||||||
|
{ "items", GenerateSchemaForType(elementType) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理基本类型(int, string, bool, etc.)
|
||||||
|
if (IsPrimitiveType(type))
|
||||||
|
{
|
||||||
|
string jsonType = MapClrTypeToJsonType(type);
|
||||||
|
var schema = new Dictionary<string, object> { { "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<string, object>();
|
||||||
|
foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||||
|
{
|
||||||
|
props[prop.Name] = GenerateSchemaForType(prop.PropertyType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "type", "object" },
|
||||||
|
{ "properties", props }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认情况
|
||||||
|
return new Dictionary<string, object> { { "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";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,8 +18,8 @@ namespace LinkToolAddin.host
|
|||||||
log.Info("通过反射调用内部MCP工具");
|
log.Info("通过反射调用内部MCP工具");
|
||||||
var jsonRpcEntity = JsonConvert.DeserializeObject<JsonRpcEntity>(jsonRpcString);
|
var jsonRpcEntity = JsonConvert.DeserializeObject<JsonRpcEntity>(jsonRpcString);
|
||||||
|
|
||||||
Type type = Type.GetType("LinkToolAddin.client."+jsonRpcEntity.Method.Split('.')[0]);
|
Type type = Type.GetType("LinkToolAddin.client.tool"+jsonRpcEntity.Method.Split(':')[0]);
|
||||||
MethodInfo method = type.GetMethod(jsonRpcEntity.Method.Split('.')[1],BindingFlags.Public | BindingFlags.Static);
|
MethodInfo method = type.GetMethod(jsonRpcEntity.Method.Split(':')[1],BindingFlags.Public | BindingFlags.Static);
|
||||||
var task = method.Invoke(null, new object[] { jsonRpcEntity.Params }) as Task<JsonRpcResultEntity>;
|
var task = method.Invoke(null, new object[] { jsonRpcEntity.Params }) as Task<JsonRpcResultEntity>;
|
||||||
JsonRpcResultEntity result = await task;
|
JsonRpcResultEntity result = await task;
|
||||||
return JsonConvert.SerializeObject(result);
|
return JsonConvert.SerializeObject(result);
|
||||||
|
|||||||
461
host/Gateway.cs
461
host/Gateway.cs
@ -1,8 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
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.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
using ArcGIS.Desktop.Framework.Dialogs;
|
||||||
|
using ArcGIS.Desktop.Internal.Mapping.Locate;
|
||||||
using LinkToolAddin.client;
|
using LinkToolAddin.client;
|
||||||
using LinkToolAddin.client.prompt;
|
using LinkToolAddin.client.prompt;
|
||||||
using LinkToolAddin.host.llm;
|
using LinkToolAddin.host.llm;
|
||||||
@ -10,13 +19,24 @@ using LinkToolAddin.host.llm.entity;
|
|||||||
using LinkToolAddin.host.mcp;
|
using LinkToolAddin.host.mcp;
|
||||||
using LinkToolAddin.host.prompt;
|
using LinkToolAddin.host.prompt;
|
||||||
using LinkToolAddin.message;
|
using LinkToolAddin.message;
|
||||||
|
using LinkToolAddin.server;
|
||||||
|
using LinkToolAddin.ui.dockpane;
|
||||||
|
using log4net;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using ModelContextProtocol.Client;
|
||||||
using ModelContextProtocol.Protocol.Types;
|
using ModelContextProtocol.Protocol.Types;
|
||||||
using Newtonsoft.Json;
|
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;
|
namespace LinkToolAddin.host;
|
||||||
|
|
||||||
public class Gateway
|
public class Gateway
|
||||||
{
|
{
|
||||||
|
private static ILog log = LogManager.GetLogger(typeof(Gateway));
|
||||||
public static async void SendMessage(string message, string model, string gdbPath, Action<MessageListItem> callback)
|
public static async void SendMessage(string message, string model, string gdbPath, Action<MessageListItem> callback)
|
||||||
{
|
{
|
||||||
Llm bailian = new Bailian
|
Llm bailian = new Bailian
|
||||||
@ -24,10 +44,12 @@ public class Gateway
|
|||||||
api_key = "sk-db177155677e438f832860e7f4da6afc"
|
api_key = "sk-db177155677e438f832860e7f4da6afc"
|
||||||
};
|
};
|
||||||
List<Message> messages = new List<Message>();
|
List<Message> messages = new List<Message>();
|
||||||
|
string toolInfos = await GetToolInfos(new McpServerList());
|
||||||
|
log.Info(SystemPrompt.SysPrompt(gdbPath, toolInfos));
|
||||||
messages.Add(new Message
|
messages.Add(new Message
|
||||||
{
|
{
|
||||||
Role = "system",
|
Role = "system",
|
||||||
Content = SystemPrompt.SysPromptTemplate
|
Content = SystemPrompt.SysPrompt(gdbPath, toolInfos)
|
||||||
});
|
});
|
||||||
messages.Add(new Message
|
messages.Add(new Message
|
||||||
{
|
{
|
||||||
@ -35,9 +57,9 @@ public class Gateway
|
|||||||
Content = message
|
Content = message
|
||||||
});
|
});
|
||||||
bool goOn = true;
|
bool goOn = true;
|
||||||
string pattern = "<tool_use>[\\s\\S]*?<\\/tool_use>";
|
string pattern = "^<tool_use>[\\s\\S]*?<\\/tool_use>$";
|
||||||
string promptPattern = "<prompt>[\\s\\S]*?<\\/prompt>";
|
string promptPattern = "^<prompt>[\\s\\S]*?<\\/prompt>$";
|
||||||
Dictionary<string,McpServer> servers = new Dictionary<string, McpServer>();
|
McpServerList mcpServerList = new McpServerList();
|
||||||
while (goOn)
|
while (goOn)
|
||||||
{
|
{
|
||||||
string reponse = await bailian.SendChatAsync(new LlmJsonContent()
|
string reponse = await bailian.SendChatAsync(new LlmJsonContent()
|
||||||
@ -48,6 +70,8 @@ public class Gateway
|
|||||||
TopP = 1,
|
TopP = 1,
|
||||||
MaxTokens = 1000,
|
MaxTokens = 1000,
|
||||||
});
|
});
|
||||||
|
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
|
log.Info(reponse);
|
||||||
messages.Add(new Message
|
messages.Add(new Message
|
||||||
{
|
{
|
||||||
Role = "assistant",
|
Role = "assistant",
|
||||||
@ -62,7 +86,7 @@ public class Gateway
|
|||||||
Dictionary<string, object> toolParams = JsonConvert.DeserializeObject<Dictionary<string, object>>(toolArgs);
|
Dictionary<string, object> toolParams = JsonConvert.DeserializeObject<Dictionary<string, object>>(toolArgs);
|
||||||
string serverName = fullToolName.Contains(":") ? fullToolName.Split(':')[0] : fullToolName;
|
string serverName = fullToolName.Contains(":") ? fullToolName.Split(':')[0] : fullToolName;
|
||||||
string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName;
|
string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName;
|
||||||
McpServer mcpServer = servers[serverName];
|
McpServer mcpServer = mcpServerList.GetServer(serverName);
|
||||||
if (mcpServer is SseMcpServer)
|
if (mcpServer is SseMcpServer)
|
||||||
{
|
{
|
||||||
SseMcpServer sseMcpServer = mcpServer as SseMcpServer;
|
SseMcpServer sseMcpServer = mcpServer as SseMcpServer;
|
||||||
@ -74,7 +98,8 @@ public class Gateway
|
|||||||
toolParams = toolParams,
|
toolParams = toolParams,
|
||||||
type = MessageType.TOOL_MESSAGE,
|
type = MessageType.TOOL_MESSAGE,
|
||||||
status = toolResponse.IsError ? "fail" : "success",
|
status = toolResponse.IsError ? "fail" : "success",
|
||||||
content = toolResponse.Content.ToString()
|
content = JsonConvert.SerializeObject(toolResponse),
|
||||||
|
id = timestamp.ToString()
|
||||||
};
|
};
|
||||||
messages.Add(new Message
|
messages.Add(new Message
|
||||||
{
|
{
|
||||||
@ -98,7 +123,8 @@ public class Gateway
|
|||||||
toolParams = toolParams,
|
toolParams = toolParams,
|
||||||
type = MessageType.TOOL_MESSAGE,
|
type = MessageType.TOOL_MESSAGE,
|
||||||
status = toolResponse.IsError ? "fail" : "success",
|
status = toolResponse.IsError ? "fail" : "success",
|
||||||
content = toolResponse.Content.ToString()
|
content = JsonConvert.SerializeObject(toolResponse),
|
||||||
|
id = timestamp.ToString()
|
||||||
};
|
};
|
||||||
messages.Add(new Message
|
messages.Add(new Message
|
||||||
{
|
{
|
||||||
@ -111,6 +137,55 @@ public class Gateway
|
|||||||
Content = JsonConvert.SerializeObject(toolResponse)
|
Content = JsonConvert.SerializeObject(toolResponse)
|
||||||
});
|
});
|
||||||
callback?.Invoke(toolMessageItem);
|
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>;
|
||||||
|
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))
|
else if (Regex.IsMatch(reponse, promptPattern))
|
||||||
@ -134,7 +209,8 @@ public class Gateway
|
|||||||
{
|
{
|
||||||
content = reponse,
|
content = reponse,
|
||||||
role = "assistant",
|
role = "assistant",
|
||||||
type = MessageType.CHAT_MESSAGE
|
type = MessageType.CHAT_MESSAGE,
|
||||||
|
id = timestamp.ToString()
|
||||||
};
|
};
|
||||||
callback?.Invoke(chatMessageListItem);
|
callback?.Invoke(chatMessageListItem);
|
||||||
}
|
}
|
||||||
@ -144,6 +220,375 @@ public class Gateway
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async void SendMessageStream(string message, string model, string gdbPath, Action<MessageListItem> callback)
|
||||||
|
{
|
||||||
|
Llm bailian = new Bailian
|
||||||
|
{
|
||||||
|
api_key = "sk-db177155677e438f832860e7f4da6afc"
|
||||||
|
};
|
||||||
|
List<Message> messages = new List<Message>();
|
||||||
|
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 = "^<tool_use>[\\s\\S]*?<\\/tool_use>$";
|
||||||
|
string promptPattern = "^<prompt>[\\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("<tool_use>"))
|
||||||
|
{
|
||||||
|
if (Regex.IsMatch(chunk, toolPattern))
|
||||||
|
{
|
||||||
|
//返回工具卡片
|
||||||
|
XElement toolUse = XElement.Parse(chunk);
|
||||||
|
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.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>;
|
||||||
|
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<string, object>(),
|
||||||
|
type = MessageType.TOOL_MESSAGE,
|
||||||
|
status = "loading",
|
||||||
|
content = "正在生成工具调用参数",
|
||||||
|
id = timestamp.ToString()
|
||||||
|
};
|
||||||
|
callback?.Invoke(toolMessageItem);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}else if (chunk.StartsWith("<prompt>"))
|
||||||
|
{
|
||||||
|
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<string> 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<DescriptionAttribute>()?.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<McpClientTool> 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<McpClientTool> 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<string, string> prompts = DynamicPrompt.GetAllPrompts();
|
||||||
|
foreach (KeyValuePair<string, string> 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<DescriptionAttribute>();
|
||||||
|
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
|
public static async void TestChatMessage(string message, string model, string gdbPath,//message,qwen-max,传个空字符串,传一个方法(回调,用来调用方法)1
|
||||||
Action<MessageListItem> callback)
|
Action<MessageListItem> callback)
|
||||||
|
|||||||
54
host/McpServerList.cs
Normal file
54
host/McpServerList.cs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using LinkToolAddin.host.mcp;
|
||||||
|
|
||||||
|
namespace LinkToolAddin.host;
|
||||||
|
|
||||||
|
public class McpServerList
|
||||||
|
{
|
||||||
|
private Dictionary<string,McpServer> servers = new Dictionary<string, McpServer>();
|
||||||
|
|
||||||
|
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<string, string>()
|
||||||
|
{
|
||||||
|
{"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<McpServer> GetAllServers()
|
||||||
|
{
|
||||||
|
List<McpServer> serverList = new List<McpServer>();
|
||||||
|
foreach (var server in servers)
|
||||||
|
{
|
||||||
|
serverList.Add(server.Value);
|
||||||
|
}
|
||||||
|
return serverList;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
@ -17,9 +18,18 @@ public class Bailian : Llm
|
|||||||
public string max_tokens { get; set; }
|
public string max_tokens { get; set; }
|
||||||
public string app_id { get; set; }
|
public string app_id { get; set; }
|
||||||
public string api_key { get; set; }
|
public string api_key { get; set; }
|
||||||
public IAsyncEnumerable<string> SendChatStreamAsync(string message)
|
public async IAsyncEnumerable<string> 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<string> SendApplicationStreamAsync(string message)
|
public IAsyncEnumerable<string> SendApplicationStreamAsync(string message)
|
||||||
|
|||||||
@ -11,7 +11,7 @@ public interface Llm
|
|||||||
public string top_p { get; set; }
|
public string top_p { get; set; }
|
||||||
public string max_tokens { get; set; }
|
public string max_tokens { get; set; }
|
||||||
|
|
||||||
public IAsyncEnumerable<string> SendChatStreamAsync(string message);
|
public IAsyncEnumerable<string> SendChatStreamAsync(LlmJsonContent jsonContent);
|
||||||
public IAsyncEnumerable<string> SendApplicationStreamAsync(string message);
|
public IAsyncEnumerable<string> SendApplicationStreamAsync(string message);
|
||||||
public Task<string> SendChatAsync(LlmJsonContent jsonContent);
|
public Task<string> SendChatAsync(LlmJsonContent jsonContent);
|
||||||
public Task<string> SendApplicationAsync(CommonInput commonInput);
|
public Task<string> SendApplicationAsync(CommonInput commonInput);
|
||||||
|
|||||||
54
host/llm/entity/stream/LlmStreamChat.cs
Normal file
54
host/llm/entity/stream/LlmStreamChat.cs
Normal file
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
24
host/mcp/InnerMcpServer.cs
Normal file
24
host/mcp/InnerMcpServer.cs
Normal file
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
27
host/mcp/McpPromptDefinition.cs
Normal file
27
host/mcp/McpPromptDefinition.cs
Normal file
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
27
host/mcp/McpToolDefinition.cs
Normal file
27
host/mcp/McpToolDefinition.cs
Normal file
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,10 +2,18 @@
|
|||||||
|
|
||||||
public class SystemPrompt
|
public class SystemPrompt
|
||||||
{
|
{
|
||||||
public static string SysPromptTemplate = "现在你是一个精通ArcGIS Pro的专家,请以此身份回答用户的问题。";
|
public static string SysPromptTemplate = "现在你是一个精通ArcGIS Pro的专家,请以此身份回答用户的问题。你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。MCP工具调用的格式要求示例,必须用<tool_use>标签表示工具调用:<tool_use>\n <name>search</name>\n <arguments>{\\\"query\\\": \\\"上海 人口\\\"}</arguments>\n</tool_use>。你每次调用请求都必须放在单独的一条消息中,不附带任何的文字说明,不带markdown格式。如需文字说明,请另外放在一次单独的消息中。\n当你认为已解决用户最初提出的问题时,请输出单独的一条消息,内容为[DONE],不附带任何其它文字说明,程序识别到后会退出循环。\n此外,你还可以通过<prompt></prompt>调用用户提示词,从而使你更好地理解和完成用户的任务。";
|
||||||
|
|
||||||
public static string ContinuePromptTemplate = "上一个工具执行的结果如下,请据此继续执行";
|
public static string ContinuePromptTemplate = "工具执行的结果如下,根据以上结果决定继续执行工具或是根据结果回答问题。如果不再需要额外说明和额外的操作,请回答单独的一条内容为[DONE]的消息。工具已经成功调用,请勿重复执行上一个工具";
|
||||||
|
|
||||||
public static string ErrorPromptTemplate = "执行上一个工具的时候出现以下错误,请根据报错信息重试";
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -10,14 +10,29 @@ namespace LinkToolAddin.server;
|
|||||||
public class CallArcGISPro
|
public class CallArcGISPro
|
||||||
{
|
{
|
||||||
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(CallArcGISPro));
|
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(CallArcGISPro));
|
||||||
|
|
||||||
public async static Task<JsonRpcResultEntity> CallArcGISProTool(string toolName, List<string> toolParams)
|
public async static Task<JsonRpcResultEntity> CallArcGISProTool(string toolName, List<string> toolParams)
|
||||||
{
|
{
|
||||||
var results = await Geoprocessing.ExecuteToolAsync(toolName, toolParams);
|
var results = await Geoprocessing.ExecuteToolAsync(toolName, toolParams);
|
||||||
log.Info($"CallArcGISProTool: {toolName} | {toolParams}");
|
JsonRpcResultEntity jsonRpcResultEntity;
|
||||||
return new JsonRpcSuccessEntity()
|
if (results.ErrorCode == 0)
|
||||||
{
|
{
|
||||||
Id = 1,
|
jsonRpcResultEntity = new JsonRpcErrorEntity()
|
||||||
Result = results.ToString()
|
{
|
||||||
};
|
Error = new Error()
|
||||||
|
{
|
||||||
|
Code = results.ErrorCode,
|
||||||
|
Message = results.ErrorMessages.ToString()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jsonRpcResultEntity = new JsonRpcSuccessEntity
|
||||||
|
{
|
||||||
|
Result = results.Messages.ToString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return jsonRpcResultEntity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,10 +2,10 @@ namespace LinkToolAddin.server
|
|||||||
{
|
{
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
public partial class JsonRpcErrorEntity
|
public partial class JsonRpcErrorEntity : JsonRpcResultEntity
|
||||||
{
|
{
|
||||||
[JsonProperty("jsonrpc")]
|
[JsonProperty("jsonrpc")]
|
||||||
public string Jsonrpc { get; set; }
|
public string Jsonrpc { get; set; } = "2.0";
|
||||||
|
|
||||||
[JsonProperty("error")]
|
[JsonProperty("error")]
|
||||||
public Error Error { get; set; }
|
public Error Error { get; set; }
|
||||||
|
|||||||
@ -18,11 +18,22 @@
|
|||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="24"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="24"/>
|
||||||
|
<RowDefinition Height="240"/>
|
||||||
|
<RowDefinition Height="24"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<DockPanel Grid.Row="0" LastChildFill="true" KeyboardNavigation.TabNavigation="Local" Height="30">
|
<Button Grid.Row="0" Content="Test Workflow" Name="TestServer" Click="TestWorkflow_OnClick"></Button>
|
||||||
<Button Content="Test Workflow" Name="TestWorkflow" Click="TestWorkflow_OnClick"></Button>
|
<Grid Grid.Row="1">
|
||||||
</DockPanel>
|
<Grid.RowDefinitions><RowDefinition Height="24"/></Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBox Grid.Row="0" Grid.Column="0" ToolTip="输入测试提示词" Name="PromptTestTextBox"></TextBox>
|
||||||
|
<Button Grid.Row="0" Grid.Column="1" Content="测试" Name="PromptTestButton" Click="PromptTestButton_OnClick"></Button>
|
||||||
|
</Grid>
|
||||||
|
<TextBox Grid.Row="2" ToolTip="大模型回复" Name="ReplyTextBox" TextWrapping="Wrap"></TextBox>
|
||||||
|
<Button Grid.Row="3" Content="Test Stream" Name="TestStream" Click="TestStream_OnClick"></Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -1,4 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using LinkToolAddin.client;
|
using LinkToolAddin.client;
|
||||||
@ -26,7 +28,10 @@ namespace LinkToolAddin.ui.dockpane
|
|||||||
{
|
{
|
||||||
private static ILog log = LogManager.GetLogger(typeof(TestDockpaneView));
|
private static ILog log = LogManager.GetLogger(typeof(TestDockpaneView));
|
||||||
|
|
||||||
public TestDockpaneView()//构造方法,没有返回参数1
|
private List<string> idList = new List<string>();
|
||||||
|
private Dictionary<string,MessageListItem> messageDict = new Dictionary<string,MessageListItem>();
|
||||||
|
|
||||||
|
public TestDockpaneView()
|
||||||
{
|
{
|
||||||
InitLogger();//初始化日志输出器1
|
InitLogger();//初始化日志输出器1
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -138,6 +143,35 @@ namespace LinkToolAddin.ui.dockpane
|
|||||||
});
|
});
|
||||||
log.Info(reponse);
|
log.Info(reponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void Request_Bailian_Stream_Test()
|
||||||
|
{
|
||||||
|
LlmJsonContent jsonContent = new LlmJsonContent()
|
||||||
|
{
|
||||||
|
Model = "qwen-max",
|
||||||
|
Messages = new List<Message>()
|
||||||
|
{
|
||||||
|
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)
|
private void TestButton_OnClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
@ -146,12 +180,56 @@ namespace LinkToolAddin.ui.dockpane
|
|||||||
|
|
||||||
private void TestWorkflow_OnClick(object sender, RoutedEventArgs e)
|
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)
|
public void ShowMessage(MessageListItem msg)
|
||||||
{
|
{
|
||||||
log.Info(msg.content);
|
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<string,MessageListItem> 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,7 +41,7 @@ namespace LinkToolAddin.ui.dockpane
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Text shown near the top of the DockPane.
|
/// Text shown near the top of the DockPane.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string _heading = "My DockPane";
|
private string _heading = "Test Dockpane";
|
||||||
public string Heading
|
public string Heading
|
||||||
{
|
{
|
||||||
get => _heading;
|
get => _heading;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user