Compare commits

..

1 Commits
host ... master

Author SHA1 Message Date
108ee40ffe Merge pull request 'v0.1.4版本' (#2) from host into master
Reviewed-on: #2
2025-06-15 14:42:58 +00:00
17 changed files with 289 additions and 645 deletions

2
.gitignore vendored
View File

@ -7,7 +7,7 @@
./vs/ ./vs/
./bin/ ./bin/
./obj/ ./obj/
./Properties/ ./Properties/launchSettings.json
*.sln.iml *.sln.iml
*.DotSettings *.DotSettings
*.dotCover *.dotCover

View File

@ -26,7 +26,6 @@
<button refID="DialogDockpane_ShowButton" size="large" /> <button refID="DialogDockpane_ShowButton" size="large" />
<button refID="Version_Button" size="large" /> <button refID="Version_Button" size="large" />
<button refID="LinkToolAddin_ui_dockpane_TestDockpane_ShowButton" size="large" /> <button refID="LinkToolAddin_ui_dockpane_TestDockpane_ShowButton" size="large" />
<button refID="LinkToolAddin_ui_mcp_McpConfigWindow" size="large" />
</group> </group>
<group id="PreferenceGroup" caption="设置项" appearsOnAddInTab="false"> <group id="PreferenceGroup" caption="设置项" appearsOnAddInTab="false">
<button refID="Preference_Button" size="large" /> <button refID="Preference_Button" size="large" />
@ -45,9 +44,6 @@
<button id="LinkToolAddin_ui_dockpane_TestDockpane_ShowButton" caption="测试面板" className="LinkToolAddin.ui.dockpane.TestDockpane_ShowButton" loadOnClick="true" smallImage="GenericButtonPurple16" largeImage="GenericButtonPurple32"> <button id="LinkToolAddin_ui_dockpane_TestDockpane_ShowButton" caption="测试面板" className="LinkToolAddin.ui.dockpane.TestDockpane_ShowButton" loadOnClick="true" smallImage="GenericButtonPurple16" largeImage="GenericButtonPurple32">
<tooltip heading="测试面板">打开测试面板<disabledText /></tooltip> <tooltip heading="测试面板">打开测试面板<disabledText /></tooltip>
</button> </button>
<button id="LinkToolAddin_ui_mcp_McpConfigWindow" caption="McpConfigWindow" className="LinkToolAddin.ui.mcp.ShowMcpConfigWindow" loadOnClick="true" smallImage="GenericButtonPurple16" largeImage="GenericButtonPurple32">
<tooltip heading="Tooltip Heading">Tooltip text<disabledText /></tooltip>
</button>
</controls> </controls>
<dockPanes> <dockPanes>
<dockPane id="DialogDockpane" caption="LinkTool" className="LinkToolAddin.ui.dockpane.DialogDockpaneViewModel" dock="group" dockWith="esri_core_projectDockPane"> <dockPane id="DialogDockpane" caption="LinkTool" className="LinkToolAddin.ui.dockpane.DialogDockpaneViewModel" dock="group" dockWith="esri_core_projectDockPane">

View File

@ -2,7 +2,7 @@
"profiles": { "profiles": {
"LinkToolAddin": { "LinkToolAddin": {
"commandName": "Executable", "commandName": "Executable",
"executablePath": "C:\\Program Files\\ArcGIS\\Pro\\bin\\ArcGISPro.exe", "executablePath": "C:\\Users\\86158\\AppData\\Local\\Programs\\ArcGIS\\Pro\\bin\\ArcGISPro.exe",
"applicationUrl": "https://localhost:5001", "applicationUrl": "https://localhost:5001",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"

View File

@ -137,8 +137,8 @@ public class ArcGisPro
return result; return result;
} }
[McpServerTool, Description("获取要素类的属性表内容可以查看属性表中的至多前20条记录的内容传入Where子句筛选特定数据例如“OBJECTID > 10”")] [McpServerTool, Description("获取要素类的属性表内容可以查看属性表中的至多前20条记录的内容")]
public static async Task<JsonRpcResultEntity> GetFeatureDatasetAttributeTable(string datasetPath, string dataName, string whereClause) public static async Task<JsonRpcResultEntity> GetFeatureDatasetAttributeTable(string datasetPath, string dataName, string rowsLimit)
{ {
JsonRpcResultEntity result = new JsonRpcResultEntity(); JsonRpcResultEntity result = new JsonRpcResultEntity();
await QueuedTask.Run(async () => await QueuedTask.Run(async () =>
@ -147,7 +147,7 @@ public class ArcGisPro
{ {
using Geodatabase gdb = new Geodatabase(new FileGeodatabaseConnectionPath(new Uri(datasetPath))); using Geodatabase gdb = new Geodatabase(new FileGeodatabaseConnectionPath(new Uri(datasetPath)));
FeatureClass featureClass = gdb.OpenDataset<FeatureClass>(dataName); FeatureClass featureClass = gdb.OpenDataset<FeatureClass>(dataName);
List<Dictionary<string, string>> attributeTable = await GetAttributeTableAsync(featureClass,whereClause); List<Dictionary<string, string>> attributeTable = await GetAttributeTableAsync(featureClass,Convert.ToInt32(rowsLimit));
result = new JsonRpcSuccessEntity() result = new JsonRpcSuccessEntity()
{ {
Id = 1, Id = 1,
@ -170,15 +170,15 @@ public class ArcGisPro
return result; return result;
} }
private static async Task<List<Dictionary<string, string>>> GetAttributeTableAsync(FeatureClass featureClass,String whereClause) private static async Task<List<Dictionary<string, string>>> GetAttributeTableAsync(FeatureClass featureClass,int limit = 5)
{ {
int limit = 20; if (limit > 20)
limit = 20;
return await QueuedTask.Run(() => return await QueuedTask.Run(() =>
{ {
var result = new List<Dictionary<string, string>>(); var result = new List<Dictionary<string, string>>();
QueryFilter queryFilter = new QueryFilter();
queryFilter.WhereClause = whereClause; using (var cursor = featureClass.Search())
using (var cursor = featureClass.Search(queryFilter))
{ {
int i = 0; int i = 0;
while (cursor.MoveNext()) while (cursor.MoveNext())
@ -198,10 +198,7 @@ public class ArcGisPro
// 处理 DBNull 值 // 处理 DBNull 值
if (value is DBNull) if (value is DBNull)
value = ""; value = null;
if (value == null)
value = "";
record[field.Name] = value.ToString(); record[field.Name] = value.ToString();
} }

View File

@ -43,19 +43,175 @@ public class Gateway
{ {
private static ILog log = LogManager.GetLogger(typeof(Gateway)); private static ILog log = LogManager.GetLogger(typeof(Gateway));
private static bool goOn = true; private static bool goOn = true;
private static DialogDockpaneView dockpaneView = null;
public static void SetDockpaneView(DialogDockpaneView view)
{
dockpaneView = view;
}
public static void StopConversation() public static void StopConversation()
{ {
goOn = false; goOn = false;
if (dockpaneView != null) }
public static async void SendMessage(string message, string model, string gdbPath, Action<MessageListItem> callback)
{
Llm bailian = new Bailian
{ {
dockpaneView.hideProgressBar(); 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 pattern = "^<tool_use>[\\s\\S]*?<\\/tool_use>$";
string promptPattern = "^<prompt>[\\s\\S]*?<\\/prompt>$";
McpServerList mcpServerList = new McpServerList();
while (goOn)
{
string reponse = await bailian.SendChatAsync(new LlmJsonContent()
{
Model = model,
Messages = messages,
Temperature = 0.3,
TopP = 0.6,
TopK = 25,
MaxTokens = 1024,
ThinkingBudget = 1024
});
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
log.Info(reponse);
messages.Add(new Message
{
Role = "assistant",
Content = reponse
});
if (Regex.IsMatch(reponse, pattern))
{
//工具类型的消息
XElement toolUse = XElement.Parse(reponse);
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.ErrorPrompt(JsonConvert.SerializeObject(toolResponse)) : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse))
});
callback?.Invoke(toolMessageItem);
}else if (mcpServer is StdioMcpServer)
{
StdioMcpServer stdioMcpServer = mcpServer as StdioMcpServer;
StdioMcpClient client = new StdioMcpClient(stdioMcpServer.Command, stdioMcpServer.Args);
CallToolResponse toolResponse = await client.CallToolAsync(toolName,toolParams);
MessageListItem toolMessageItem = new ToolMessageItem
{
toolName = toolName,
toolParams = toolParams,
type = MessageType.TOOL_MESSAGE,
status = toolResponse.IsError ? "fail" : "success",
content = JsonConvert.SerializeObject(toolResponse),
id = timestamp.ToString()
};
messages.Add(new Message
{
Role = "user",
Content = toolResponse.IsError ? SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(toolResponse)) : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse))
});
callback?.Invoke(toolMessageItem);
}else if (mcpServer is InnerMcpServer)
{
Type type = Type.GetType("LinkToolAddin.client.tool."+serverName);
MethodInfo method = type.GetMethod(toolName,BindingFlags.Public | BindingFlags.Static);
var 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.ErrorPrompt(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.ContinuePrompt(JsonConvert.SerializeObject(innerResult))
});
callback?.Invoke(toolMessageItem);
}
}
}
else if (Regex.IsMatch(reponse, promptPattern))
{
XElement prompt = XElement.Parse(reponse);
string fullPromptName = prompt.Element("name")?.Value;
string promptArgs = prompt.Element("arguments")?.Value;
Dictionary<string, object> promptParams = JsonConvert.DeserializeObject<Dictionary<string, object>>(promptArgs);
string serverName = fullPromptName.Contains(":") ? fullPromptName.Split(':')[0] : fullPromptName;
string promptName = fullPromptName.Contains(":") ? fullPromptName.Split(':')[1] : fullPromptName;
string promptRes = DynamicPrompt.GetPrompt(promptName, null);
messages.Add(new Message
{
Role = "user",
Content = promptRes
});
}
else
{
MessageListItem chatMessageListItem = new ChatMessageItem()
{
content = reponse,
role = "assistant",
type = MessageType.CHAT_MESSAGE,
id = timestamp.ToString()
};
callback?.Invoke(chatMessageListItem);
}
if (reponse.EndsWith("[DONE]"))
{
goOn = false;
}
} }
} }
@ -80,81 +236,33 @@ public class Gateway
return (matched, remaining); return (matched, remaining);
} }
public static async void SendMessageStream(string message, string model, string gdbPath, List<Message> historyMessages, Action<MessageListItem,List< Message>> callback) public static async void SendMessageStream(string message, string model, string gdbPath, Action<MessageListItem> callback)
{ {
Llm modelObj = new Bailian(); Llm bailian = new Bailian
List<string> bailianModels = ["Moonshot-Kimi-K2-Instruct","deepseek-r1","deepseek-v3","qwen3-235b-a22b","qwen3-32b","qwen-plus","qwen-turbo","abab6.5s-chat"]; {
List<string> dmxModels = ["grok-3-mini","o4-mini","claude-3-5-haiku-20241022","claude-sonnet-4-20250514-thinking","claude-3-7-sonnet-20250219-thinking","grok-4-ssvip","gemini-2.5-pro-ssvip","gemini-2.5-flash-ssvip","o3-pro","gpt-4.1","gpt-4o"]; api_key = "sk-db177155677e438f832860e7f4da6afc"
List<Message> messages = null; };
List<Message> messages = new List<Message>();
string toolInfos = ""; string toolInfos = "";
if (historyMessages.IsNullOrEmpty()) try
{ {
messages = new List<Message>(); toolInfos = await GetToolInfos(new McpServerList());
try messages.Add(new Message
{ {
toolInfos = await GetToolInfos(new McpServerList(),messages,callback); Role = "system",
messages.Add(new Message Content = SystemPrompt.SysPrompt(gdbPath, toolInfos)
{ });
Role = "system", messages.Add(new Message
Content = SystemPrompt.SysPrompt(gdbPath, toolInfos)
});
}catch (Exception ex)
{ {
log.Error(ex); Role = "user",
MessageListItem endMessageListItem2 = new ChatMessageItem Content = message
{ });
type = MessageType.WARNING, }catch (Exception ex)
content = $"MCP列表读取失败{ex.Message}", {
id = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString(), log.Error(ex);
role = "system", MessageBox.Show(ex.Message,"获取MCP列表失败");
};
callback?.Invoke(endMessageListItem2,messages);
}
} }
else
{
messages = historyMessages;
}
if (bailianModels.Contains(model))
{
modelObj = new Bailian
{
api_key = "sk-db177155677e438f832860e7f4da6afc"
};
}else if (dmxModels.Contains(model))
{
modelObj = new DmxApi
{
api_key = "sk-VQeuLUmhO1LL8H97tFj5kuWOqGFD4CFRmAsdqhxkmkYxUUlP"
};
}
else
{
MessageListItem endMessageListItem2 = new ChatMessageItem
{
type = MessageType.ERROR,
content = $"目前暂未支持{model}模型,请选择其他模型。",
id = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString(),
role = "system"
};
callback?.Invoke(endMessageListItem2,messages);
MessageListItem endMessageListItem1 = new ChatMessageItem
{
type = MessageType.END_TAG,
content = "",
id = (DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + 3).ToString(),
role = "assistant"
};
callback?.Invoke(endMessageListItem1,messages);
return;
}
messages.Add(new Message
{
Role = "user",
Content = message
});
goOn = true; goOn = true;
long accTokens = 0;
string toolPattern = "<tool_use>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<arguments>([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/tool_use>"; string toolPattern = "<tool_use>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<arguments>([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/tool_use>";
string promptPattern = "<prompt>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<arguments>([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/prompt>"; string promptPattern = "<prompt>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<arguments>([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/prompt>";
McpServerList mcpServerList = new McpServerList(); McpServerList mcpServerList = new McpServerList();
@ -165,35 +273,10 @@ public class Gateway
bool executedTool = false; bool executedTool = false;
while (goOn) while (goOn)
{ {
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
loop++; loop++;
if (loop > 500) if (loop > 500)
{ {
MessageListItem endMessageListItem2 = new ChatMessageItem MessageBox.Show("达到最大循环次数", "退出循环");
{
type = MessageType.ERROR,
content = "达到最大循环次数,已退出循环。",
id = timestamp.ToString(),
role = "system"
};
callback?.Invoke(endMessageListItem2,messages);
MessageListItem endMessageListItem1 = new ChatMessageItem
{
type = MessageType.END_TAG,
content = "",
id = (timestamp + 3).ToString(),
role = "assistant"
};
callback?.Invoke(endMessageListItem1,messages);
// MessageBox.Show("达到最大循环次数", "退出循环");
MessageListItem endAllMsgItem = new ChatMessageItem
{
type = MessageType.END_ALL,
content = "",
id = (timestamp + 4).ToString(),
role = "assistant"
};
callback?.Invoke(endAllMsgItem,messages);
break; break;
} }
LlmJsonContent jsonContent = new LlmJsonContent() LlmJsonContent jsonContent = new LlmJsonContent()
@ -202,8 +285,10 @@ public class Gateway
Messages = messages, Messages = messages,
Temperature = 0.3, Temperature = 0.3,
TopP = 0.4, TopP = 0.4,
TopK = 7,
MaxTokens = 1000, MaxTokens = 1000,
}; };
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
List<McpToolRequest> mcpToolRequests = new List<McpToolRequest>(); List<McpToolRequest> mcpToolRequests = new List<McpToolRequest>();
List<PromptRequest> promptRequests = new List<PromptRequest>(); List<PromptRequest> promptRequests = new List<PromptRequest>();
var (toolMatched, toolRemaining) = ExtractMatchedPart(messageContent, toolPattern); var (toolMatched, toolRemaining) = ExtractMatchedPart(messageContent, toolPattern);
@ -219,21 +304,13 @@ public class Gateway
id = (timestamp + 3).ToString(), id = (timestamp + 3).ToString(),
role = "assistant" role = "assistant"
}; };
callback?.Invoke(endMessageListItem1,messages); callback?.Invoke(endMessageListItem1);
MessageListItem endAllMsgItem = new ChatMessageItem
{
type = MessageType.END_ALL,
content = "",
id = (timestamp + 4).ToString(),
role = "assistant"
};
callback?.Invoke(endAllMsgItem,messages);
break; break;
} }
try try
{ {
await foreach(LlmStreamChat llmStreamChat in modelObj.SendChatStreamAsync(jsonContent)) await foreach(LlmStreamChat llmStreamChat in bailian.SendChatStreamAsync(jsonContent))
{ {
if (!goOn) if (!goOn)
{ {
@ -244,15 +321,7 @@ public class Gateway
id = (timestamp+3).ToString(), id = (timestamp+3).ToString(),
role = "assistant" role = "assistant"
}; };
callback?.Invoke(endMessageListItem2,messages); callback?.Invoke(endMessageListItem2);
MessageListItem endAllMsgItem = new ChatMessageItem
{
type = MessageType.END_ALL,
content = "",
id = (timestamp + 4).ToString(),
role = "assistant"
};
callback?.Invoke(endAllMsgItem,messages);
break; break;
} }
try try
@ -267,7 +336,7 @@ public class Gateway
}; };
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
callback?.Invoke(reasonMessageListItem,messages); callback?.Invoke(reasonMessageListItem);
}); });
messageContent = chunk; messageContent = chunk;
var (matched, remaining) = ExtractMatchedPart(chunk, toolPattern); var (matched, remaining) = ExtractMatchedPart(chunk, toolPattern);
@ -286,7 +355,7 @@ public class Gateway
}; };
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
callback?.Invoke(chatMessageListItem,messages); callback?.Invoke(chatMessageListItem);
}); });
} }
else else
@ -299,7 +368,7 @@ public class Gateway
type = MessageType.CHAT_MESSAGE, type = MessageType.CHAT_MESSAGE,
id = timestamp.ToString() id = timestamp.ToString()
}; };
callback?.Invoke(chatMessageListItem,messages); callback?.Invoke(chatMessageListItem);
XElement promptUse = XElement.Parse(matchedPrompt); XElement promptUse = XElement.Parse(matchedPrompt);
string promptKey = promptUse.Element("name")?.Value; string promptKey = promptUse.Element("name")?.Value;
string promptArgs = promptUse.Element("arguments")?.Value; string promptArgs = promptUse.Element("arguments")?.Value;
@ -333,7 +402,7 @@ public class Gateway
}; };
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
callback?.Invoke(chatMessageListItem,messages); callback?.Invoke(chatMessageListItem);
}); });
if (!executedTool) if (!executedTool)
{ {
@ -350,7 +419,7 @@ public class Gateway
}; };
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
callback?.Invoke(toolMessageListItem,messages); callback?.Invoke(toolMessageListItem);
}); });
} }
mcpToolRequests = new List<McpToolRequest>(); mcpToolRequests = new List<McpToolRequest>();
@ -372,32 +441,7 @@ public class Gateway
}catch (Exception e) }catch (Exception e)
{ {
log.Error(e.Message); log.Error(e.Message);
MessageListItem endMessageListItem2 = new ChatMessageItem MessageBox.Show(e.Message, "请求大模型出错");
{
type = MessageType.ERROR,
content = $"请求大模型出错:{e.Message}",
id = timestamp.ToString(),
role = "system"
};
callback?.Invoke(endMessageListItem2,messages);
MessageListItem endMessageListItem1 = new ChatMessageItem
{
type = MessageType.END_TAG,
content = "",
id = (timestamp + 3).ToString(),
role = "assistant"
};
callback?.Invoke(endMessageListItem1,messages);
// MessageBox.Show(e.Message, "请求大模型出错");
MessageListItem endAllMsgItem = new ChatMessageItem
{
type = MessageType.END_ALL,
content = "",
id = (timestamp + 4).ToString(),
role = "assistant"
};
callback?.Invoke(endAllMsgItem,messages);
break;
} }
if (messageContent != "") if (messageContent != "")
{ {
@ -441,7 +485,7 @@ public class Gateway
}); });
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
callback?.Invoke(toolMessageItem1,messages); callback?.Invoke(toolMessageItem1);
}); });
} }
else if (mcpServer is StdioMcpServer) else if (mcpServer is StdioMcpServer)
@ -469,7 +513,7 @@ public class Gateway
}); });
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
callback?.Invoke(toolMessageItem1,messages); callback?.Invoke(toolMessageItem1);
}); });
} }
else if (mcpServer is InnerMcpServer) else if (mcpServer is InnerMcpServer)
@ -500,7 +544,7 @@ public class Gateway
}); });
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
callback?.Invoke(toolMessageItem,messages); callback?.Invoke(toolMessageItem);
}); });
queriedKnowledge = true; queriedKnowledge = true;
} }
@ -525,7 +569,7 @@ public class Gateway
}); });
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
callback?.Invoke(toolMessageItem,messages); callback?.Invoke(toolMessageItem);
}); });
} }
continue; continue;
@ -576,7 +620,7 @@ public class Gateway
}); });
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
callback?.Invoke(toolMessageItem1,messages); callback?.Invoke(toolMessageItem1);
}); });
} }
else if (innerResult is JsonRpcSuccessEntity) else if (innerResult is JsonRpcSuccessEntity)
@ -599,7 +643,7 @@ public class Gateway
}); });
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
callback?.Invoke(toolMessageItem1,messages); callback?.Invoke(toolMessageItem1);
}); });
} }
} }
@ -635,7 +679,7 @@ public class Gateway
}; };
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
callback?.Invoke(toolMessageItem,messages); callback?.Invoke(toolMessageItem);
}); });
} }
catch (Exception e) catch (Exception e)
@ -651,13 +695,12 @@ public class Gateway
id = (timestamp+3).ToString(), id = (timestamp+3).ToString(),
role = "assistant" role = "assistant"
}; };
callback?.Invoke(endMessageListItem,messages); callback?.Invoke(endMessageListItem);
} }
} }
private static async Task<string> GetToolInfos(McpServerList mcpServerList,List<Message> historyMessages,Action<MessageListItem,List<Message>> callback) private static async Task<string> GetToolInfos(McpServerList mcpServerList)
{ {
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
StringBuilder toolInfos = new StringBuilder(); StringBuilder toolInfos = new StringBuilder();
int i = 0; int i = 0;
List<int> failedMcp = new List<int>(); List<int> failedMcp = new List<int>();
@ -741,7 +784,7 @@ public class Gateway
{ {
log.Error(e.Message); log.Error(e.Message);
failedMcp.Add(i); failedMcp.Add(i);
failedMcpString.Add(mcpServerList.GetAllServerNames()[i-1]); failedMcpString.Add(mcpServerList.GetAllServerNames()[i]);
} }
} }
@ -762,15 +805,7 @@ public class Gateway
if (!failedMcp.IsNullOrEmpty()) if (!failedMcp.IsNullOrEmpty())
{ {
MessageListItem endMessageListItem2 = new ChatMessageItem MessageBox.Show($"读取失败的MCP服务有{JsonConvert.SerializeObject(failedMcpString)}","MCP读取错误");
{
type = MessageType.WARNING,
content = $"读取失败的MCP服务有{JsonConvert.SerializeObject(failedMcpString)}",
id = (timestamp-2).ToString(),
role = "system"
};
callback?.Invoke(endMessageListItem2,historyMessages);
// MessageBox.Show($"读取失败的MCP服务有{JsonConvert.SerializeObject(failedMcpString)}","MCP读取错误");
} }
return toolInfos.ToString(); return toolInfos.ToString();
} }

View File

@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using LinkToolAddin.host.mcp; using LinkToolAddin.host.mcp;
using Newtonsoft.Json;
namespace LinkToolAddin.host; namespace LinkToolAddin.host;
@ -37,58 +36,53 @@ public class McpServerList
// "有地理信息的相关案例步骤参考以及Arcgis Pro的工具详细信息", // "有地理信息的相关案例步骤参考以及Arcgis Pro的工具详细信息",
IsActive = true IsActive = true
}); });
servers.Add("filesystem", new StdioMcpServer() //servers.Add("filesystem", new StdioMcpServer()
{ //{
Name = "filesystem", // Name = "filesystem",
Type = "stdio", // Type = "stdio",
Command = "npx", // Command = "npx",
Args = new List<string>() // Args = new List<string>()
{ // {
"-y", // "-y",
"@modelcontextprotocol/server-filesystem", // "@modelcontextprotocol/server-filesystem",
"F:\\secondsemester\\linktool\\test\\LinkTool0607\\LinkTool0607.gdb" // "F:\\secondsemester\\linktool\\test\\LinkTool0607\\LinkTool0607.gdb"
} // }
}); //});
// servers.Add("fetch", new StdioMcpServer() //servers.Add("fetch", new StdioMcpServer()
// { //{
// Name = "fetch", // Name = "fetch",
// Type = "stdio", // Type = "stdio",
// Command = "uvx", // Command = "uvx",
// Args = new List<string>() // Args = new List<string>()
// { // {
// "mcp-server-fetch" // "mcp-server-fetch"
// } // }
// }); //});
// servers.Add("bing-search", new StdioMcpServer() //servers.Add("bing-search", new StdioMcpServer()
// { //{
// Name = "bing-search", // Name = "bing-search",
// Type = "stdio", // Type = "stdio",
// Command = "npx", // Command = "npx",
// Args = new List<string>() // Args = new List<string>()
// { // {
// "bing-cn-mcp" // "bing-cn-mcp"
// } // }
// }); //});
// servers.Add("mcp-python-interpreter", new StdioMcpServer() //servers.Add("mcp-python-interpreter", new StdioMcpServer()
// { //{
// Name = "mcp-python-interpreter", // Name = "mcp-python-interpreter",
// Type = "stdio", // Type = "stdio",
// Command = "uvx", // Command = "uvx",
// Args = new List<string>() // Args = new List<string>()
// { // {
// "--native-tls", // "--native-tls",
// "mcp-python-interpreter", // "mcp-python-interpreter",
// "--dir", // "--dir",
// "F:\\secondsemester\\linktool\\test\\LinkTool0607\\LinkTool0607.gdb", // "F:\\secondsemester\\linktool\\test\\LinkTool0607\\LinkTool0607.gdb",
// "--python-path", // "--python-path",
// "C:\\Program Files\\ArcGIS\\Pro\\bin\\Python\\envs\\custom\\python.exe" // "C:\\Program Files\\ArcGIS\\Pro\\bin\\Python\\envs\\custom\\python.exe"
// } // }
// }); //});
}
public McpServerList(string json)
{
servers = JsonConvert.DeserializeObject<Dictionary<string, McpServer>>(json);
} }
public McpServer GetServer(string name) public McpServer GetServer(string name)

View File

@ -1,52 +0,0 @@
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using LinkToolAddin.common;
using LinkToolAddin.host.llm.entity;
using LinkToolAddin.host.llm.entity.stream;
using Newtonsoft.Json;
namespace LinkToolAddin.host.llm;
public class DmxApi : Llm
{
public string model { get; set; } = "gpt-4o";
public string temperature { get; set; }
public string top_p { get; set; }
public string max_tokens { get; set; }
public string app_id { get; set; }
public string api_key { get; set; }
public async IAsyncEnumerable<LlmStreamChat> SendChatStreamAsync(LlmJsonContent jsonContent)
{
jsonContent.Stream = true;
StringBuilder contentBuilder = new StringBuilder();
StringBuilder reasonBuilder = new StringBuilder();
await foreach (LlmStreamChat chunk in HttpRequest.PostWithStreamingResponseAsync(
"https://www.dmxapi.cn/v1/chat/completions",
JsonConvert.SerializeObject(jsonContent),
api_key))
{
contentBuilder.Append(chunk.Choices[0].Delta.Content);
reasonBuilder.Append(chunk.Choices[0].Delta.ResoningContent);
LlmStreamChat fullChunk = chunk;
fullChunk.Choices[0].Delta.Content = contentBuilder.ToString();
fullChunk.Choices[0].Delta.ResoningContent = reasonBuilder.ToString();
yield return fullChunk;
}
}
public IAsyncEnumerable<string> SendApplicationStreamAsync(string message)
{
throw new System.NotImplementedException();
}
public Task<string> SendChatAsync(LlmJsonContent jsonContent)
{
throw new System.NotImplementedException();
}
public Task<string> SendApplicationAsync(CommonInput commonInput)
{
throw new System.NotImplementedException();
}
}

View File

@ -24,7 +24,7 @@ namespace LinkToolAddin.host.llm.entity
public int MaxTokens { get; set; } = 2048; public int MaxTokens { get; set; } = 2048;
[JsonProperty("top_k")] [JsonProperty("top_k")]
public int TopK { get; set; } public int TopK { get; set; } = 40;
[JsonProperty("enable_thinking")] [JsonProperty("enable_thinking")]
public bool EnableThinking { get; set; } = true; public bool EnableThinking { get; set; } = true;

View File

@ -23,7 +23,7 @@
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="24"/> <RowDefinition Height="24"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="16"/> <RowDefinition Height="24"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Grid.Row="0" HorizontalAlignment="Center"> <Grid Grid.Row="0" HorizontalAlignment="Center">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@ -40,15 +40,13 @@
<ComboBox Grid.Column="4" Name="ModelComboBox" SelectionChanged="ModelComboBox_OnSelectionChanged"> <ComboBox Grid.Column="4" Name="ModelComboBox" SelectionChanged="ModelComboBox_OnSelectionChanged">
<ComboBoxItem Content="qwen3-235b-a22b" IsSelected="True"/> <ComboBoxItem Content="qwen3-235b-a22b" IsSelected="True"/>
<ComboBoxItem Content="qwen3-32b" /> <ComboBoxItem Content="qwen3-32b" />
<ComboBoxItem Content="qwq-32b" />
<ComboBoxItem Content="qwen-max-latest" />
<ComboBoxItem Content="deepseek-r1" /> <ComboBoxItem Content="deepseek-r1" />
<ComboBoxItem Content="deepseek-r1-0528" />
<ComboBoxItem Content="deepseek-r1-distill-qwen-32b" />
<ComboBoxItem Content="deepseek-r1-distill-llama-70b" />
<ComboBoxItem Content="deepseek-v3" /> <ComboBoxItem Content="deepseek-v3" />
<ComboBoxItem Content="Moonshot-Kimi-K2-Instruct" />
<Separator></Separator>
<ComboBoxItem Content="grok-3-mini" />
<ComboBoxItem Content="o4-mini" />
<ComboBoxItem Content="claude-3-5-haiku-20241022" />
<ComboBoxItem Content="gemini-2.5-flash-ssvip" />
<Separator></Separator>
<ComboBoxItem Content="自定义" /> <ComboBoxItem Content="自定义" />
</ComboBox> </ComboBox>
</Grid> </Grid>
@ -63,14 +61,8 @@
<ColumnDefinition Width="*"></ColumnDefinition> <ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="48"></ColumnDefinition> <ColumnDefinition Width="48"></ColumnDefinition>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Name="QuestionTextbox" Text="" TextWrapping="Wrap" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" PreviewKeyDown="QuestionTextbox_OnPreviewKeyDown"></TextBox> <TextBox Grid.Column="0" Name="QuestionTextbox" Text="这是用户的问题" TextWrapping="Wrap"></TextBox>
<Button Grid.Column="1" Name="SendButton" Content="发送" Click="SendButton_OnClick"></Button> <Button Grid.Column="1" Name="SendButton" Content="发送" Click="SendButton_OnClick"></Button>
</Grid> </Grid>
<ProgressBar x:Name="IndeterminateProgressBar"
Grid.Row="4"
Margin="3"
Height="16"
IsIndeterminate="True"
Visibility="Hidden"/>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -2,10 +2,8 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Windows; using System.Windows;
@ -16,7 +14,6 @@ using Microsoft.Extensions.Logging;
using System.Text.Json; using System.Text.Json;
using System.Threading; using System.Threading;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using ArcGIS.Desktop.Core; using ArcGIS.Desktop.Core;
using ArcGIS.Desktop.Core.Geoprocessing; using ArcGIS.Desktop.Core.Geoprocessing;
@ -36,7 +33,6 @@ using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Types; using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Server; using ModelContextProtocol.Server;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace LinkToolAddin.ui.dockpane namespace LinkToolAddin.ui.dockpane
{ {
@ -54,14 +50,12 @@ namespace LinkToolAddin.ui.dockpane
private List<string> idList = new List<string>(); private List<string> idList = new List<string>();
private ConcurrentDictionary<string,MessageListItem> messageDict = new ConcurrentDictionary<string,MessageListItem>(); private ConcurrentDictionary<string,MessageListItem> messageDict = new ConcurrentDictionary<string,MessageListItem>();
private ConcurrentDictionary<string,Border> borderItemsDict = new ConcurrentDictionary<string,Border>(); private ConcurrentDictionary<string,Border> borderItemsDict = new ConcurrentDictionary<string,Border>();
private List<Message> historyMessages = new List<Message>();
public DialogDockpaneView() public DialogDockpaneView()
{ {
InitLogger(); InitLogger();
InitializeComponent(); InitializeComponent();
DataContext = this; DataContext = this;
Gateway.SetDockpaneView(this);
} }
private async void TestServer_OnClick(object sender, RoutedEventArgs e) private async void TestServer_OnClick(object sender, RoutedEventArgs e)
@ -106,8 +100,6 @@ namespace LinkToolAddin.ui.dockpane
private void SendButton_OnClick(object sender, RoutedEventArgs e) private void SendButton_OnClick(object sender, RoutedEventArgs e)
{ {
StatusTextBlock.Text = "正在读取用户输入和工具列表";
IndeterminateProgressBar.Visibility = Visibility.Visible;
string question = QuestionTextbox.Text; string question = QuestionTextbox.Text;
string defaultGdbPath = Project.Current.DefaultGeodatabasePath; string defaultGdbPath = Project.Current.DefaultGeodatabasePath;
string gdbPath = @""; string gdbPath = @"";
@ -131,12 +123,7 @@ namespace LinkToolAddin.ui.dockpane
model = ShowInputBox("自定义模型", "请输入模型名称:", ""); model = ShowInputBox("自定义模型", "请输入模型名称:", "");
} }
ScrollViewer.ScrollToBottom(); ScrollViewer.ScrollToBottom();
Gateway.SendMessageStream(question,model,defaultGdbPath,historyMessages,NewMessage_Recall); Gateway.SendMessageStream(question,model,defaultGdbPath,NewMessage_Recall);
}
public void hideProgressBar()
{
IndeterminateProgressBar.Visibility = Visibility.Hidden;
} }
private string ShowInputBox(string title, string message, string defaultValue = "") private string ShowInputBox(string title, string message, string defaultValue = "")
@ -179,13 +166,7 @@ namespace LinkToolAddin.ui.dockpane
return textBox.Text; return textBox.Text;
} }
public void NewMessage_Recall(MessageListItem msg, List<Message> historyMessages) public void NewMessage_Recall(MessageListItem msg)
{
this.historyMessages = historyMessages;
NewMessage_Recall(msg);
}
private void NewMessage_Recall(MessageListItem msg)
{ {
string msgId = msg.id; string msgId = msg.id;
log.Info(msg.content); log.Info(msg.content);
@ -201,10 +182,6 @@ namespace LinkToolAddin.ui.dockpane
if (msg.type == MessageType.END_TAG) if (msg.type == MessageType.END_TAG)
{ {
StatusTextBlock.Text = ""; StatusTextBlock.Text = "";
}else if (msg.type == MessageType.END_ALL)
{
StatusTextBlock.Text = "";
IndeterminateProgressBar.Visibility = Visibility.Hidden;
} }
return; return;
} }
@ -246,16 +223,7 @@ namespace LinkToolAddin.ui.dockpane
{ {
if (msg.type == MessageType.WARNING) if (msg.type == MessageType.WARNING)
{ {
Border border = GetWarningChatBorder(msg);
borderItemsDict[msgId] = border;
ChatHistoryStackPanel.Children.Add(border);
StatusTextBlock.Text = "出现警告信息";
}else if (msg.type == MessageType.ERROR)
{
Border border = GetErrorChatBorder(msg);
borderItemsDict[msgId] = border;
ChatHistoryStackPanel.Children.Add(border);
StatusTextBlock.Text = "出现错误信息";
} }
} }
} }
@ -268,10 +236,6 @@ namespace LinkToolAddin.ui.dockpane
if (msg.type == MessageType.END_TAG) if (msg.type == MessageType.END_TAG)
{ {
StatusTextBlock.Text = ""; StatusTextBlock.Text = "";
}else if (msg.type == MessageType.END_ALL)
{
StatusTextBlock.Text = "";
IndeterminateProgressBar.Visibility = Visibility.Hidden;
} }
ChatHistoryStackPanel.Children.Remove(borderItemsDict[msgId]); ChatHistoryStackPanel.Children.Remove(borderItemsDict[msgId]);
borderItemsDict.TryRemove(msgId, out Border border); borderItemsDict.TryRemove(msgId, out Border border);
@ -299,7 +263,7 @@ namespace LinkToolAddin.ui.dockpane
StatusTextBlock.Text = "正在读取用户输入"; StatusTextBlock.Text = "正在读取用户输入";
} }
} }
else if(msg.role == "assistant") else
{ {
if (msg.type == MessageType.REASON_MESSAGE) if (msg.type == MessageType.REASON_MESSAGE)
{ {
@ -316,23 +280,6 @@ namespace LinkToolAddin.ui.dockpane
textBox.Text = msg.content; textBox.Text = msg.content;
StatusTextBlock.Text = "回答生成中"; StatusTextBlock.Text = "回答生成中";
} }
}else if (msg.role == "system")
{
if (msg.type == MessageType.WARNING)
{
Border borderItem = borderItemsDict[msgId];
Grid grid = borderItem.Child as Grid;
TextBox textBox = grid.Children[1] as TextBox;
textBox.Text = msg.content;
StatusTextBlock.Text = "出现警告信息";
}else if (msg.type == MessageType.ERROR)
{
Border borderItem = borderItemsDict[msgId];
Grid grid = borderItem.Child as Grid;
TextBox textBox = grid.Children[1] as TextBox;
textBox.Text = msg.content;
StatusTextBlock.Text = "出现错误信息";
}
} }
} }
if (Math.Abs(verticalOffset + viewportHeight - contentHeight) < tolerance) if (Math.Abs(verticalOffset + viewportHeight - contentHeight) < tolerance)
@ -499,66 +446,6 @@ namespace LinkToolAddin.ui.dockpane
return border; return border;
} }
private Border GetWarningChatBorder(MessageListItem msg)
{
Border border = new Border();
border.Margin = new Thickness(24);
border.Padding = new Thickness(8);
border.BorderThickness = new Thickness(1);
border.BorderBrush = Brushes.DarkGoldenrod;
border.CornerRadius = new CornerRadius(3);
border.Background = Brushes.Moccasin;
Grid grid = new Grid();
// Image icon = new Image()
// {
// Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.tool.png"),
// Stretch = Stretch.Fill,
// HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center
// };
// icon.Margin = new Thickness(0, 0, 8, 0);
// grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(24, GridUnitType.Pixel)});
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(1, GridUnitType.Star)});
TextBlock textBlock = new TextBlock();
textBlock.Text = (msg as ChatMessageItem).content;
textBlock.TextWrapping = TextWrapping.Wrap;
// grid.Children.Add(icon);
grid.Children.Add(textBlock);
// Grid.SetColumn(icon, 0);
Grid.SetColumn(textBlock, 1);
border.Child = grid;
return border;
}
private Border GetErrorChatBorder(MessageListItem msg)
{
Border border = new Border();
border.Margin = new Thickness(24);
border.Padding = new Thickness(8);
border.BorderThickness = new Thickness(1);
border.BorderBrush = Brushes.Crimson;
border.CornerRadius = new CornerRadius(3);
border.Background = Brushes.LightPink;
Grid grid = new Grid();
// Image icon = new Image()
// {
// Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.tool.png"),
// Stretch = Stretch.Fill,
// HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center
// };
// icon.Margin = new Thickness(0, 0, 8, 0);
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(24, GridUnitType.Pixel)});
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(1, GridUnitType.Star)});
TextBlock textBlock = new TextBlock();
textBlock.Text = (msg as ChatMessageItem).content;
textBlock.TextWrapping = TextWrapping.Wrap;
// grid.Children.Add(icon);
grid.Children.Add(textBlock);
// Grid.SetColumn(icon, 0);
Grid.SetColumn(textBlock, 1);
border.Child = grid;
return border;
}
private Border GetToolChatBorder(MessageListItem msg) private Border GetToolChatBorder(MessageListItem msg)
{ {
Border border = new Border(); Border border = new Border();
@ -592,46 +479,17 @@ namespace LinkToolAddin.ui.dockpane
argsButton.Content = "参数"; argsButton.Content = "参数";
argsButton.Tag = msg as ToolMessageItem; argsButton.Tag = msg as ToolMessageItem;
argsButton.Click += ToolArgsButton_OnClick; argsButton.Click += ToolArgsButton_OnClick;
border.Child = grid;
Button resButton = new Button(); Button resButton = new Button();
resButton.Content = "结果"; resButton.Content = "结果";
resButton.Tag = msg as ToolMessageItem; resButton.Tag = msg as ToolMessageItem;
resButton.Click += ToolResButton_OnClick; resButton.Click += ToolResButton_OnClick;
grid.Children.Add(argsButton); grid.Children.Add(argsButton);
Grid.SetColumn(argsButton, 2); Grid.SetColumn(argsButton, 2);
grid.Children.Add(resButton); grid.Children.Add(resButton);
Grid.SetColumn(resButton, 3); Grid.SetColumn(resButton, 3);
if (toolMsg.toolParams.ContainsKey("toolName"))
{
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(36, GridUnitType.Pixel)});
Button checkButton = new Button();
checkButton.Content = "检查";
checkButton.Tag = toolMsg;
checkButton.Click += ToolCheckButton_OnClick;
grid.Children.Add(checkButton);
Grid.SetColumn(checkButton, 4);
}
border.Child = grid;
return border; return border;
} }
private void ToolCheckButton_OnClick(object sender, RoutedEventArgs e)
{
try
{
Button button = sender as Button;
ToolMessageItem toolItem = button.Tag as ToolMessageItem;
string gisToolName = toolItem.toolParams["toolName"] as string;
JArray gisToolParams = toolItem.toolParams["toolParams"] as JArray;
List<string> gitToolParamsList = (gisToolParams as JArray).Select(token => token.ToString()).ToList();
Geoprocessing.OpenToolDialog(gisToolName,gitToolParamsList);
}catch (Exception ex)
{
log.Error(ex);
ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show(ex.Message,"打开工具失败");
}
}
private void ToolArgsButton_OnClick(object sender, RoutedEventArgs e) private void ToolArgsButton_OnClick(object sender, RoutedEventArgs e)
{ {
@ -670,7 +528,6 @@ namespace LinkToolAddin.ui.dockpane
borderItemsDict.Clear(); borderItemsDict.Clear();
ChatHistoryStackPanel.Children.Clear(); ChatHistoryStackPanel.Children.Clear();
QuestionTextbox.Clear(); QuestionTextbox.Clear();
historyMessages.Clear();
StatusTextBlock.Text = ""; StatusTextBlock.Text = "";
} }
@ -696,43 +553,5 @@ namespace LinkToolAddin.ui.dockpane
string model = ModelComboBox.SelectedValue.ToString(); string model = ModelComboBox.SelectedValue.ToString();
log.Info(model); log.Info(model);
} }
private void QuestionTextbox_OnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
{
// Shift + Enter: 插入换行
QuestionTextbox.SelectedText = Environment.NewLine;
e.Handled = true;
}
else
{
// Enter: 触发发送
e.Handled = true;
SendButton_OnClick(sender, null);
}
}
}
private void QuestionTextbox_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
{
// Shift + Enter: 插入换行
QuestionTextbox.SelectedText = Environment.NewLine;
e.Handled = true;
}
else
{
// Enter: 触发发送
e.Handled = true;
SendButton_OnClick(sender, null);
}
}
}
} }
} }

View File

@ -201,7 +201,16 @@ namespace LinkToolAddin.ui.dockpane
private async void PromptTestButton_OnClick(object sender, RoutedEventArgs e) private async void PromptTestButton_OnClick(object sender, RoutedEventArgs e)
{ {
throw new NotImplementedException(); string userPrompt = PromptTestTextBox.Text;
// model可选值qwen3-235b-a22b,qwen-max,deepseek-r1
try
{
await Task.Run(() => Gateway.SendMessageStream(userPrompt,"qwen3-235b-a22b", "F:\\secondsemester\\linktool\\test\\LinkTool0604\\LinkTool0604.gdb", AddReplyStream));
}
catch (Exception exception)
{
log.Error(exception.Message);
}
} }
public async void AddReplyStream(MessageListItem msg) public async void AddReplyStream(MessageListItem msg)

View File

@ -1,27 +0,0 @@
<controls:ProWindow x:Class="LinkToolAddin.ui.mcp.McpConfigWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:controls="clr-namespace:ArcGIS.Desktop.Framework.Controls;assembly=ArcGIS.Desktop.Framework"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:extensions="clr-namespace:ArcGIS.Desktop.Extensions;assembly=ArcGIS.Desktop.Extensions"
mc:Ignorable="d"
Title="MCP服务器配置" Height="450" Width="500"
WindowStartupLocation="CenterOwner"
>
<controls:ProWindow.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<extensions:DesignOnlyResourceDictionary Source="pack://application:,,,/ArcGIS.Desktop.Framework;component\Themes\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</controls:ProWindow.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Name="McpJsonTextBox" AcceptsReturn="True"></TextBox>
<Button Grid.Row="1" Name="McpJsonButton">保存</Button>
</Grid>
</controls:ProWindow>

View File

@ -1,71 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using LinkToolAddin.host;
using LinkToolAddin.host.mcp;
using Newtonsoft.Json;
using Path = System.IO.Path;
namespace LinkToolAddin.ui.mcp
{
/// <summary>
/// Interaction logic for McpConfigWindow.xaml
/// </summary>
public partial class McpConfigWindow : ArcGIS.Desktop.Framework.Controls.ProWindow
{
private static Dictionary<string,McpServer> servers = new Dictionary<string, McpServer>();
public McpConfigWindow()
{
InitializeComponent();
string mcpConfigStr = ReadMcpConfig();
McpJsonTextBox.Text = mcpConfigStr;
}
private string ReadMcpConfig()
{
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string configDirPath = Path.Combine(appDataPath, "LinkTool");
string mcpConfigPath = Path.Combine(appDataPath, "LinkTool", "McpConfig.json");
Directory.CreateDirectory(configDirPath);
if (!File.Exists(mcpConfigPath))
{
File.Create(mcpConfigPath);
McpServerList mcpServerList = new McpServerList();
string json = JsonConvert.SerializeObject(mcpServerList.GetAllServers());
File.WriteAllText(mcpConfigPath, json);
return json;
}
else
{
string mcpConfigStr = File.ReadAllText(mcpConfigPath);
McpServerList mcpServerList = new McpServerList(mcpConfigStr);
return JsonConvert.SerializeObject(mcpServerList.GetAllServers());
}
}
private void WriteMcpConfig(string json)
{
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string configDirPath = Path.Combine(appDataPath, "LinkTool");
string mcpConfigPath = Path.Combine(appDataPath, "LinkTool", "McpConfig.json");
Directory.CreateDirectory(configDirPath);
if (!File.Exists(mcpConfigPath))
{
File.Create(mcpConfigPath);
}
File.WriteAllText(mcpConfigPath, json);
}
}
}

View File

@ -1,42 +0,0 @@
using ArcGIS.Core.CIM;
using ArcGIS.Core.Data;
using ArcGIS.Core.Geometry;
using ArcGIS.Desktop.Catalog;
using ArcGIS.Desktop.Core;
using ArcGIS.Desktop.Editing;
using ArcGIS.Desktop.Extensions;
using ArcGIS.Desktop.Framework;
using ArcGIS.Desktop.Framework.Contracts;
using ArcGIS.Desktop.Framework.Dialogs;
using ArcGIS.Desktop.Framework.Threading.Tasks;
using ArcGIS.Desktop.KnowledgeGraph;
using ArcGIS.Desktop.Layouts;
using ArcGIS.Desktop.Mapping;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LinkToolAddin.ui.mcp
{
internal class ShowMcpConfigWindow : Button
{
private McpConfigWindow _mcpconfigwindow = null;
protected override void OnClick()
{
//already open?
if (_mcpconfigwindow != null)
return;
_mcpconfigwindow = new McpConfigWindow();
_mcpconfigwindow.Owner = FrameworkApplication.Current.MainWindow;
_mcpconfigwindow.Closed += (o, e) => { _mcpconfigwindow = null; };
_mcpconfigwindow.Show();
//uncomment for modal
//_mcpconfigwindow.ShowDialog();
}
}
}

View File

@ -6,6 +6,4 @@ public class ChatMessageItem : MessageListItem
public string role { get; set; } public string role { get; set; }
public string content { get; set; } public string content { get; set; }
public MessageType type { get; set; } public MessageType type { get; set; }
public long accumulateTokens { get; set; }
} }

View File

@ -7,8 +7,7 @@ public enum MessageType
REASON_MESSAGE, REASON_MESSAGE,
END_TAG, END_TAG,
WARNING, WARNING,
ERROR, ERROR
END_ALL
} }
public interface MessageListItem public interface MessageListItem
@ -17,5 +16,4 @@ public interface MessageListItem
string role { get; set; } string role { get; set; }
string content { get; set; } string content { get; set; }
MessageType type { get; set; } MessageType type { get; set; }
long accumulateTokens { get; set; }
} }

View File

@ -12,6 +12,4 @@ public class ToolMessageItem : MessageListItem
public MessageType type { get; set; } public MessageType type { get; set; }
public string status { get; set; } public string status { get; set; }
public string result { get; set; } public string result { get; set; }
public long accumulateTokens { get; set; }
} }