diff --git a/LinkToolAddin.csproj b/LinkToolAddin.csproj
index d764cc6..bece90c 100644
--- a/LinkToolAddin.csproj
+++ b/LinkToolAddin.csproj
@@ -132,6 +132,14 @@
Always
+
+
+ Always
+
+
+
+ Always
+
diff --git a/host/Gateway.cs b/host/Gateway.cs
index 63ef2b2..a2af774 100644
--- a/host/Gateway.cs
+++ b/host/Gateway.cs
@@ -282,12 +282,28 @@ public class Gateway
{
//如果本次回复不包含任何工具的调用或提示词的调用,则不再请求
goOn = false;
+ MessageListItem endMessageListItem1 = new ChatMessageItem
+ {
+ type = MessageType.END_TAG,
+ content = "",
+ id = (timestamp+3).ToString(),
+ role = "assistant"
+ };
+ callback?.Invoke(endMessageListItem1);
break;
}
await foreach(LlmStreamChat llmStreamChat in bailian.SendChatStreamAsync(jsonContent))
{
if (!goOn)
{
+ MessageListItem endMessageListItem2 = new ChatMessageItem
+ {
+ type = MessageType.END_TAG,
+ content = "",
+ id = (timestamp+3).ToString(),
+ role = "assistant"
+ };
+ callback?.Invoke(endMessageListItem2);
break;
}
@@ -299,7 +315,7 @@ public class Gateway
content = llmStreamChat.Choices[0].Delta.ResoningContent,
role = "assistant",
type = MessageType.REASON_MESSAGE,
- id = (timestamp-1).ToString()
+ id = (timestamp+2).ToString()
};
Application.Current.Dispatcher.Invoke(() =>
{
@@ -328,17 +344,14 @@ public class Gateway
else
{
//包含Prompt调用请求的消息
- if (remainingPrompt != "")
+ MessageListItem chatMessageListItem = new ChatMessageItem()
{
- MessageListItem chatMessageListItem = new ChatMessageItem()
- {
- content = remainingPrompt,
- role = "assistant",
- type = MessageType.CHAT_MESSAGE,
- id = timestamp.ToString()
- };
- callback?.Invoke(chatMessageListItem);
- }
+ content = remainingPrompt,
+ role = "assistant",
+ type = MessageType.CHAT_MESSAGE,
+ id = timestamp.ToString()
+ };
+ callback?.Invoke(chatMessageListItem);
XElement promptUse = XElement.Parse(matchedPrompt);
string promptKey = promptUse.Element("name")?.Value;
string promptArgs = promptUse.Element("arguments")?.Value;
@@ -355,20 +368,6 @@ public class Gateway
else
{
//包含工具调用请求的消息
- if (remaining != "")
- {
- MessageListItem chatMessageListItem = new ChatMessageItem()
- {
- content = remaining,
- role = "assistant",
- type = MessageType.CHAT_MESSAGE,
- id = timestamp.ToString()
- };
- Application.Current.Dispatcher.Invoke(() =>
- {
- callback?.Invoke(chatMessageListItem);
- });
- }
XElement toolUse = XElement.Parse(matched);
string fullToolName = toolUse.Element("name")?.Value;
string toolArgs = toolUse.Element("arguments")?.Value;
@@ -377,6 +376,32 @@ public class Gateway
string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName;
McpServer mcpServer = mcpServerList.GetServer(serverName);
//将工具调用请求添加至列表中待一次回答完整后再统一进行调用
+ MessageListItem chatMessageListItem = new ChatMessageItem()
+ {
+ content = remaining,
+ role = "assistant",
+ type = MessageType.CHAT_MESSAGE,
+ id = timestamp.ToString()
+ };
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ callback?.Invoke(chatMessageListItem);
+ });
+ MessageListItem toolMessageListItem = new ToolMessageItem()
+ {
+ content = "",
+ result = "",
+ toolName = toolName,
+ toolParams = toolParams,
+ role = "user",
+ type = MessageType.TOOL_MESSAGE,
+ id = (timestamp+1).ToString(),
+ status = "loading"
+ };
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ callback?.Invoke(toolMessageListItem);
+ });
mcpToolRequests = new List();
McpToolRequest mcpToolRequest = new McpToolRequest()
{
@@ -421,6 +446,7 @@ public class Gateway
type = MessageType.TOOL_MESSAGE,
status = toolResponse.IsError ? "fail" : "success",
content = JsonConvert.SerializeObject(toolResponse),
+ result = JsonConvert.SerializeObject(toolResponse),
id = (timestamp + 1).ToString(),
role = "user"
};
@@ -448,6 +474,7 @@ public class Gateway
type = MessageType.TOOL_MESSAGE,
status = toolResponse.IsError ? "fail" : "success",
content = JsonConvert.SerializeObject(toolResponse),
+ result = JsonConvert.SerializeObject(toolResponse),
id = (timestamp + 1).ToString(),
role = "user"
};
@@ -494,6 +521,7 @@ public class Gateway
type = MessageType.TOOL_MESSAGE,
status = "fail",
content = JsonConvert.SerializeObject(innerResult),
+ result = JsonConvert.SerializeObject(innerResult),
id = (timestamp + 1).ToString(),
role = "user"
};
@@ -516,6 +544,7 @@ public class Gateway
type = MessageType.TOOL_MESSAGE,
status = "success",
content = JsonConvert.SerializeObject(innerResult),
+ result = JsonConvert.SerializeObject(innerResult),
id = (timestamp + 1).ToString(),
role = "user"
};
@@ -569,6 +598,14 @@ public class Gateway
log.Error(e.Message);
}
}
+ MessageListItem endMessageListItem = new ChatMessageItem
+ {
+ type = MessageType.END_TAG,
+ content = "",
+ id = (timestamp+3).ToString(),
+ role = "assistant"
+ };
+ callback?.Invoke(endMessageListItem);
}
}
diff --git a/resource/img/fold.png b/resource/img/fold.png
new file mode 100644
index 0000000..2690b19
Binary files /dev/null and b/resource/img/fold.png differ
diff --git a/resource/img/unfold.png b/resource/img/unfold.png
new file mode 100644
index 0000000..749e87e
Binary files /dev/null and b/resource/img/unfold.png differ
diff --git a/ui/VersionButton.cs b/ui/VersionButton.cs
index 62e157d..bc4df57 100644
--- a/ui/VersionButton.cs
+++ b/ui/VersionButton.cs
@@ -22,7 +22,7 @@ namespace LinkToolAddin
{
internal class VersionButton : Button
{
- private string version = "0.1.0";
+ private string version = "0.1.1";
protected override void OnClick()
{
MessageBox.Show($"当前LinkTool版本为{version}", "版本信息");
diff --git a/ui/dockpane/DialogDockpane.xaml b/ui/dockpane/DialogDockpane.xaml
index fafe132..d232eaa 100644
--- a/ui/dockpane/DialogDockpane.xaml
+++ b/ui/dockpane/DialogDockpane.xaml
@@ -25,18 +25,30 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
diff --git a/ui/dockpane/DialogDockpane.xaml.cs b/ui/dockpane/DialogDockpane.xaml.cs
index 1148b88..d11083d 100644
--- a/ui/dockpane/DialogDockpane.xaml.cs
+++ b/ui/dockpane/DialogDockpane.xaml.cs
@@ -117,13 +117,17 @@ namespace LinkToolAddin.ui.dockpane
QuestionTextbox.Text = "";
borderItemsDict[timestamp.ToString()] = userMsgBoder;
ChatHistoryStackPanel.Children.Add(userMsgBoder);
- Gateway.SendMessageStream(question,"qwen-max",defaultGdbPath,NewMessage_Recall);
+ Gateway.SendMessageStream(question,"qwen3-235b-a22b",defaultGdbPath,NewMessage_Recall);
}
public void NewMessage_Recall(MessageListItem msg)
{
string msgId = msg.id;
log.Info(msg.content);
+ double verticalOffset = ScrollViewer.VerticalOffset;
+ double viewportHeight = ScrollViewer.ViewportHeight;
+ double contentHeight = ChatHistoryStackPanel.ActualHeight;
+ double tolerance = 24;
if (!idList.Contains(msgId))
{
//不存在该消息,需添加到ListView中
@@ -147,17 +151,32 @@ namespace LinkToolAddin.ui.dockpane
ChatHistoryStackPanel.Children.Add(border);
}
}
- else
+ else if(msg.role == "assistant")
{
- Border border = GetAiChatBorder(msg);
- borderItemsDict[msgId] = border;
- ChatHistoryStackPanel.Children.Add(border);
+ if (msg.type == MessageType.REASON_MESSAGE)
+ {
+ Border border = GetAiReasonBorder(msg);
+ borderItemsDict[msgId] = border;
+ ChatHistoryStackPanel.Children.Add(border);
+ }else if (msg.type == MessageType.CHAT_MESSAGE)
+ {
+ Border border = GetAiChatBorder(msg);
+ borderItemsDict[msgId] = border;
+ ChatHistoryStackPanel.Children.Add(border);
+ }
}
}
else
{
//已有该消息,只需要修改内容
messageDict[msgId] = msg;
+ if (msg.content == "")
+ {
+ ChatHistoryStackPanel.Children.Remove(borderItemsDict[msgId]);
+ borderItemsDict.TryRemove(msgId, out Border border);
+ messageDict.TryRemove(msgId, out MessageListItem tempMsg);
+ idList.Remove(msgId);
+ }
if (msg.role == "user")
{
if (msg.type == MessageType.TOOL_MESSAGE)
@@ -166,22 +185,42 @@ namespace LinkToolAddin.ui.dockpane
Grid grid = borderItem.Child as Grid;
TextBlock textBlock = grid.Children[1] as TextBlock;
textBlock.Text = (msg as ToolMessageItem).toolName;
+ StatusTextBlock.Text = "正在执行工具";
}else if (msg.type == MessageType.CHAT_MESSAGE)
{
Border borderItem = borderItemsDict[msgId];
Grid grid = borderItem.Child as Grid;
TextBox textBox = grid.Children[1] as TextBox;
textBox.Text = msg.content;
+ StatusTextBlock.Text = "正在读取用户输入";
}
}
else
{
- Border borderItem = borderItemsDict[msgId];
- Grid grid = borderItem.Child as Grid;
- TextBox textBox = grid.Children[1] as TextBox;
- textBox.Text = msg.content;
+ if (msg.type == MessageType.REASON_MESSAGE)
+ {
+ Border borderItem = borderItemsDict[msgId];
+ Grid grid = borderItem.Child as Grid;
+ TextBox textBox = grid.Children[0] as TextBox;
+ textBox.Text = msg.content;
+ StatusTextBlock.Text = "深度思考中";
+ }else if (msg.type == MessageType.CHAT_MESSAGE)
+ {
+ 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.END_TAG)
+ {
+ StatusTextBlock.Text = "";
+ }
}
}
+ if (Math.Abs(verticalOffset + viewportHeight - contentHeight) < tolerance)
+ {
+ ScrollViewer.ScrollToBottom();
+ }
}
private Border GetAiChatBorder(MessageListItem msg)
@@ -204,6 +243,8 @@ namespace LinkToolAddin.ui.dockpane
grid.Children.Add(textBox);
Grid.SetColumn(icon, 0);
Grid.SetColumn(textBox, 1);
+ textBox.IsReadOnly = true;
+ textBox.BorderThickness = new Thickness(0);
textBox.Background = Brushes.Transparent;
textBox.Text = msg.content;
textBox.TextWrapping = TextWrapping.Wrap;
@@ -211,6 +252,105 @@ namespace LinkToolAddin.ui.dockpane
return border;
}
+ private Border GetAiReasonBorder(MessageListItem msg)
+ {
+ Border border = new Border();
+ border.Margin = new Thickness(8, 12, 8, 12);
+ border.BorderThickness = new Thickness(0);
+ // border.Background = Brushes.DarkSeaGreen;
+ Grid grid = new Grid();
+ Image icon = new Image()
+ {
+ Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.linktool.png"),
+ Stretch = Stretch.Fill,
+ HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top
+ };
+ Image fold = new Image()
+ {
+ Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.fold.png"),
+ Stretch = Stretch.Fill,
+ HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top
+ };
+ Image unfold = new Image()
+ {
+ Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.unfold.png"),
+ Stretch = Stretch.Fill,
+ HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top
+ };
+ grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(24, GridUnitType.Pixel)});
+ grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(1, GridUnitType.Star)});
+ grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(16, GridUnitType.Pixel)});
+ TextBox textBox = new TextBox();
+ // grid.Children.Add(icon);
+ grid.Children.Add(textBox);
+ // Grid.SetColumn(icon, 0);
+ Grid.SetColumn(textBox, 1);
+ textBox.IsReadOnly = true;
+ textBox.Foreground = Brushes.Gray;
+ textBox.BorderThickness = new Thickness(2,0,0,0);
+ textBox.BorderBrush = Brushes.Gray;
+ textBox.Background = Brushes.Transparent;
+ textBox.Text = msg.content;
+ textBox.TextWrapping = TextWrapping.Wrap;
+ Button button = new Button();
+ StackPanel panel = new StackPanel();
+ panel.Orientation = Orientation.Horizontal;
+ panel.Children.Add(fold);
+ button.Content = panel;
+ button.BorderThickness = new Thickness(0);
+ button.Background = Brushes.Transparent;
+ button.Width = 16;
+ button.Height = 16;
+ button.Padding = new Thickness(0);
+ button.HorizontalAlignment = HorizontalAlignment.Center;
+ button.VerticalAlignment = VerticalAlignment.Top;
+ button.Tag = "fold";
+ button.Click += FoldButton_OnClick;
+ Grid.SetColumn(button, 2);
+ grid.Children.Add(button);
+ border.Child = grid;
+ return border;
+ }
+
+ private void FoldButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ Button button = sender as Button;
+ string tag = button.Tag.ToString();
+ if (tag == "fold")
+ {
+ button.Tag = "unfold";
+ Grid grid = button.Parent as Grid;
+ TextBox textBox = grid.Children[0] as TextBox;
+ textBox.Visibility = Visibility.Collapsed;
+ StackPanel stackPanel = button.Content as StackPanel;
+ Image unfold = new Image()
+ {
+ Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.unfold.png"),
+ Stretch = Stretch.Fill,
+ HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top
+ };
+ stackPanel.Children.Clear();
+ stackPanel.Children.Add(unfold);
+ }
+ else
+ {
+ button.Tag = "fold";
+ Image fold = new Image()
+ {
+ Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.fold.png"),
+ Stretch = Stretch.Fill,
+ HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top
+ };
+ Grid grid = button.Parent as Grid;
+ TextBox textBox = grid.Children[0] as TextBox;
+ textBox.Visibility = Visibility.Visible;
+ StackPanel stackPanel = button.Content as StackPanel;
+ stackPanel.Children.Clear();
+ stackPanel.Children.Add(fold);
+ }
+
+ }
+
private Border GetUserChatBorder(MessageListItem msg)
{
Border border = new Border();
@@ -227,11 +367,14 @@ namespace LinkToolAddin.ui.dockpane
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(1, GridUnitType.Star)});
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(24, GridUnitType.Pixel)});
TextBox textBox = new TextBox();
+ textBox.HorizontalAlignment = HorizontalAlignment.Right;
grid.Children.Add(icon);
grid.Children.Add(textBox);
Grid.SetColumn(icon, 1);
Grid.SetColumn(textBox, 0);
textBox.Background = Brushes.Transparent;
+ textBox.IsReadOnly = true;
+ textBox.BorderThickness = new Thickness(0);
textBox.Text = msg.content;
textBox.TextWrapping = TextWrapping.Wrap;
border.Child = grid;
@@ -245,6 +388,7 @@ namespace LinkToolAddin.ui.dockpane
border.Padding = new Thickness(8);
border.BorderThickness = new Thickness(1);
border.BorderBrush = Brushes.Gray;
+ border.CornerRadius = new CornerRadius(3);
// border.Background = Brushes.DarkSeaGreen;
Grid grid = new Grid();
Image icon = new Image()
@@ -256,17 +400,46 @@ namespace LinkToolAddin.ui.dockpane
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)});
+ grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(36, GridUnitType.Pixel)});
+ grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(36, GridUnitType.Pixel)});
TextBlock textBlock = new TextBlock();
- textBlock.Text = (msg as ToolMessageItem).toolName;
+ ToolMessageItem toolMsg = msg as ToolMessageItem;
+ textBlock.Text = toolMsg.toolName + " | " + toolMsg.status;
textBlock.TextWrapping = TextWrapping.Wrap;
grid.Children.Add(icon);
grid.Children.Add(textBlock);
Grid.SetColumn(icon, 0);
Grid.SetColumn(textBlock, 1);
+ Button argsButton = new Button();
+ argsButton.Content = "参数";
+ argsButton.Tag = msg as ToolMessageItem;
+ argsButton.Click += ToolArgsButton_OnClick;
border.Child = grid;
+ Button resButton = new Button();
+ resButton.Content = "结果";
+ resButton.Tag = msg as ToolMessageItem;
+ resButton.Click += ToolResButton_OnClick;
+ grid.Children.Add(argsButton);
+ Grid.SetColumn(argsButton, 2);
+ grid.Children.Add(resButton);
+ Grid.SetColumn(resButton, 3);
return border;
}
+ private void ToolArgsButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ Button button = sender as Button;
+ ToolMessageItem toolItem = button.Tag as ToolMessageItem;
+ ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show(JsonConvert.SerializeObject(toolItem.toolParams),toolItem.toolName+"工具参数");
+ }
+
+ private void ToolResButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ Button button = sender as Button;
+ ToolMessageItem toolItem = button.Tag as ToolMessageItem;
+ ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show(toolItem.result,toolItem.toolName+"运行结果");
+ }
+
private void TestButton_OnClick(object sender, RoutedEventArgs e)
{
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
@@ -282,5 +455,29 @@ namespace LinkToolAddin.ui.dockpane
};
NewMessage_Recall(toolMessageItem);
}
+
+ private void ClearButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ idList.Clear();
+ messageDict.Clear();
+ borderItemsDict.Clear();
+ ChatHistoryStackPanel.Children.Clear();
+ QuestionTextbox.Clear();
+ }
+
+ private void TopButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ ScrollViewer.ScrollToTop();
+ }
+
+ private void BottomButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ ScrollViewer.ScrollToBottom();
+ }
+
+ private void StopButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ Gateway.StopConversation();
+ }
}
}
diff --git a/ui/message/MessageListItem.cs b/ui/message/MessageListItem.cs
index 9b97709..3b0ce37 100644
--- a/ui/message/MessageListItem.cs
+++ b/ui/message/MessageListItem.cs
@@ -5,6 +5,7 @@ public enum MessageType
TOOL_MESSAGE,
CHAT_MESSAGE,
REASON_MESSAGE,
+ END_TAG
}
public interface MessageListItem