修复AI流式传输时,界面闪烁的问题
This commit is contained in:
parent
33d13b62f1
commit
78d65a6aff
|
@ -813,7 +813,32 @@ public class MarkdownWpfRenderer
|
|||
|
||||
link.Click += (s, e) =>
|
||||
{
|
||||
LinkNavigate?.Invoke(linkInline, new MarkdownLinkNavigateEventArgs(linkInline.Url));
|
||||
if (linkInline.Url != null
|
||||
&& (linkInline.Url.StartsWith("http://")
|
||||
|| linkInline.Url.StartsWith("https://")
|
||||
)
|
||||
)
|
||||
{
|
||||
|
||||
// 使用系统默认浏览器打开链接
|
||||
try
|
||||
{
|
||||
var psi = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = linkInline.Url,
|
||||
UseShellExecute = true
|
||||
};
|
||||
System.Diagnostics.Process.Start(psi);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"打开链接时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LinkNavigate?.Invoke(linkInline, new MarkdownLinkNavigateEventArgs(linkInline.Url));
|
||||
}
|
||||
};
|
||||
|
||||
return link;
|
||||
|
@ -828,14 +853,6 @@ public class MarkdownWpfRenderer
|
|||
return new WpfDocs.Run(literalInline.Content.ToString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public FontFamily GetNormalTextFontFamily()
|
||||
{
|
||||
return new FontFamily("-apple-system,BlinkMacSystemFont,Segoe UI Adjusted,Segoe UI,Liberation Sans,sans-serif");
|
||||
|
|
|
@ -43,6 +43,7 @@ public class MarkdownViewer : Control
|
|||
|
||||
public static readonly DependencyProperty RenderedContentProperty =
|
||||
DependencyProperty.Register(nameof(RenderedContent), typeof(FrameworkElement), typeof(MarkdownViewer), new PropertyMetadata(null));
|
||||
private ContentControl? _contentControl;
|
||||
|
||||
|
||||
private async Task RenderProcessAsync(CancellationToken cancellationToken)
|
||||
|
@ -57,17 +58,22 @@ public class MarkdownViewer : Control
|
|||
MarkdownDocument? doc =
|
||||
await Task.Run(() =>
|
||||
{
|
||||
var doc = Markdig.Markdown.Parse(
|
||||
content,
|
||||
pipeline);
|
||||
|
||||
return doc;
|
||||
return Markdig.Markdown.Parse(content, pipeline);
|
||||
});
|
||||
|
||||
var renderer = new MarkdownWpfRenderer();
|
||||
ContentControl contentControl =new();
|
||||
RenderedContent = contentControl;
|
||||
renderer.RenderDocumentTo(contentControl, doc, cancellationToken);
|
||||
// 确保在UI线程上执行
|
||||
await Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
// 仅首次创建ContentControl或在需要时重新创建
|
||||
if (_contentControl == null)
|
||||
{
|
||||
_contentControl = new ContentControl();
|
||||
RenderedContent = _contentControl;
|
||||
}
|
||||
|
||||
var renderer = new MarkdownWpfRenderer();
|
||||
renderer.RenderDocumentTo(_contentControl, doc, cancellationToken);
|
||||
});
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
|
|
@ -58,17 +58,40 @@ class AiHelper
|
|||
TopK= 10,
|
||||
TopP = 0.85f,
|
||||
Temperature = 0.7f,
|
||||
|
||||
};
|
||||
|
||||
if (UseTool)
|
||||
{
|
||||
//TODO: add function call
|
||||
}
|
||||
|
||||
|
||||
await foreach (var response in _client.GetStreamingResponseAsync(question, chatOptions))
|
||||
{
|
||||
yield return response.Text;
|
||||
}
|
||||
}
|
||||
public async IAsyncEnumerable<string> GetStreamingResponseAsync(List<ChatMessage> messages)
|
||||
{
|
||||
// 获取所有的工具函数
|
||||
//var toolsList = AgentExecutor.GetDefaultTools();
|
||||
var chatOptions = new ChatOptions
|
||||
{
|
||||
// TODO: Models config
|
||||
TopK = 10,
|
||||
TopP = 0.85f,
|
||||
Temperature = 0.7f,
|
||||
|
||||
};
|
||||
|
||||
if (UseTool)
|
||||
{
|
||||
//TODO: add function call
|
||||
}
|
||||
|
||||
await foreach (var response in _client.GetStreamingResponseAsync(messages, chatOptions))
|
||||
{
|
||||
yield return response.Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
|
||||
namespace ExcelHelper.Message
|
||||
{
|
||||
class ScrollToEndMessage : ValueChangedMessage<string>
|
||||
{
|
||||
public ScrollToEndMessage(string value) : base(value)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,8 @@ using System.Windows.Input;
|
|||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Media;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using ExcelHelper.Message;
|
||||
|
||||
namespace ExcelHelper.Views.Components;
|
||||
|
||||
|
@ -67,6 +69,13 @@ public partial class AiMessageControll : UserControl
|
|||
{
|
||||
InitializeComponent();
|
||||
//DataContext = this;
|
||||
WeakReferenceMessenger.Default.Register<ScrollToEndMessage>("MessageListScrollToEnd", (o, s) => {
|
||||
|
||||
Dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
ScrollToEnd();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void SendButton_Click(object sender, RoutedEventArgs e)
|
||||
|
|
|
@ -88,7 +88,7 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
|
|||
}
|
||||
MaxRow = ExcelData.Count();
|
||||
|
||||
PromptString = Excel2Prompt.ConverterToPrompt(ExcelData, columns,StartCell);
|
||||
ExcelPromptString = Excel2Prompt.ConverterToPrompt(ExcelData, columns,StartCell);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -260,6 +260,13 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
|
|||
{
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
var history = Messages.Select(it => new Microsoft.Extensions.AI.ChatMessage()
|
||||
{
|
||||
Role = it.IsUser
|
||||
? Microsoft.Extensions.AI.ChatRole.User
|
||||
: Microsoft.Extensions.AI.ChatRole.Assistant,
|
||||
Text = it.Content
|
||||
}).ToList();
|
||||
Messages.Add(new ChatMessage
|
||||
{
|
||||
Content = message,
|
||||
|
@ -272,18 +279,26 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
|
|||
IsUser = false,
|
||||
Content = ""
|
||||
};
|
||||
|
||||
Messages.Add(aiChat);
|
||||
#if DEBUG
|
||||
#if !DEBUG
|
||||
var results = TEST_AI_CONTENT.Split('\n');
|
||||
foreach(var result in results)
|
||||
{
|
||||
aiChat.Content += result;
|
||||
WeakReferenceMessenger.Default.Send(new ScrollToEndMessage(""));
|
||||
await Task.Delay(5);
|
||||
}
|
||||
#else
|
||||
var promptedMsg = PromptUtil.UsePrompt(message,PromptString);
|
||||
await foreach(var result in AiHelper.GetStreamingResponseAsync(promptedMsg))
|
||||
var promptedMsg = PromptUtil.UsePrompt(message,ExcelPromptString);
|
||||
history.Add(new Microsoft.Extensions.AI.ChatMessage
|
||||
{
|
||||
Role = Microsoft.Extensions.AI.ChatRole.User,
|
||||
Text = promptedMsg
|
||||
});
|
||||
await foreach(var result in AiHelper.GetStreamingResponseAsync(history))
|
||||
{
|
||||
|
||||
aiChat.Content += result;
|
||||
}
|
||||
#endif
|
||||
|
@ -293,196 +308,143 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
|
|||
|
||||
|
||||
private const string TEST_AI_CONTENT = @"
|
||||
# Welcome to Leanote! 欢迎来到Leanote!
|
||||
|
||||
## 1. 排版
|
||||
|
||||
**粗体** *斜体*
|
||||
|
||||
~~这是一段错误的文本。~~
|
||||
|
||||
引用:
|
||||
|
||||
> 引用Leanote官方的话, 为什么要做Leanote, 原因是...
|
||||
|
||||
有充列表:
|
||||
1. 支持Vim
|
||||
2. 支持Emacs
|
||||
|
||||
无序列表:
|
||||
|
||||
- 项目1
|
||||
- 项目2
|
||||
|
||||
|
||||
## 2. 图片与链接
|
||||
|
||||
图片:
|
||||

|
||||
链接:
|
||||
|
||||
[这是去往Leanote官方博客的链接](http://leanote.leanote.com)
|
||||
|
||||
## 3. 标题
|
||||
|
||||
以下是各级标题, 最多支持5级标题
|
||||
|
||||
这篇文章包含markdown语法基本的内容, 目的是放在自己的博客园上, 通过开发者控制台快速选中,
|
||||
从而自定义自己博客园markdown样式.当然本文也可以当markdown语法学习之用.
|
||||
|
||||
在markdown里强制换行是在末尾添加2个空格+1个回车.
|
||||
在markdown里可以使用 \ 对特殊符号进行转义.
|
||||
|
||||
# 1. 标题
|
||||
|
||||
**语法**
|
||||
```
|
||||
# h1
|
||||
## h2
|
||||
### h3
|
||||
#### h4
|
||||
##### h4
|
||||
###### h5
|
||||
# This is an <h1> tag
|
||||
## This is an <h2> tag
|
||||
### This is an <h3> tag
|
||||
#### This is an <h4> tag
|
||||
```
|
||||
|
||||
## 4. 代码
|
||||
|
||||
示例:
|
||||
|
||||
function get(key) {
|
||||
return m[key];
|
||||
}
|
||||
|
||||
代码高亮示例:
|
||||
|
||||
``` javascript
|
||||
/**
|
||||
* nth element in the fibonacci series.
|
||||
* @param n >= 0
|
||||
* @return the nth element, >= 0.
|
||||
*/
|
||||
function fib(n) {
|
||||
var a = 1, b = 1;
|
||||
var tmp;
|
||||
while (--n >= 0) {
|
||||
tmp = a;
|
||||
a += b;
|
||||
b = tmp;
|
||||
}
|
||||
return a;
|
||||
|
||||
**实例**
|
||||
|
||||
# This is an h1 tag
|
||||
## This is an h2 tag
|
||||
### This is an h3 tag
|
||||
#### This is an h4 tag
|
||||
|
||||
# 2. 强调和斜体
|
||||
|
||||
**语法**
|
||||
```
|
||||
*This text will be italic*
|
||||
_This will also be italic_
|
||||
|
||||
**This text will be bold**
|
||||
__This will also be bold__
|
||||
```
|
||||
(个人不喜欢2个下划线中间包含的内容被斜体, 会和网址冲突, 我会在自定义博客园样式中去除这个样式.)
|
||||
|
||||
**实例**
|
||||
|
||||
*This text will be italic*
|
||||
_This will also be italic_
|
||||
|
||||
**This text will be bold**
|
||||
__This will also be bold__
|
||||
|
||||
# 3. 有序列表和无序列表
|
||||
|
||||
**语法**
|
||||
```
|
||||
* Item 1
|
||||
* Item 2
|
||||
* Item 3
|
||||
|
||||
1. Item 1
|
||||
2. Item 2
|
||||
3. Item 3
|
||||
```
|
||||
|
||||
**实例**
|
||||
* Item 1
|
||||
* Item 2
|
||||
* Item 3
|
||||
|
||||
1. Item 1
|
||||
2. Item 2
|
||||
3. Item 3
|
||||
|
||||
# 4. 图片
|
||||
|
||||
**语法**
|
||||
```
|
||||

|
||||
```
|
||||
|
||||
**实例**
|
||||

|
||||
|
||||
# 5. 超链接
|
||||
|
||||
**语法**
|
||||
```
|
||||
[link-name](link-url)
|
||||
```
|
||||
|
||||
**实例**
|
||||
|
||||
[阿胜4K](http://www.cnblogs.com/asheng2016/)
|
||||
|
||||
# 6. 引用
|
||||
|
||||
**语法**
|
||||
```
|
||||
> 引用本意是引用别人的话之类
|
||||
> 但我个人喜欢把引用当成""注意""使用
|
||||
```
|
||||
|
||||
**实例**
|
||||
|
||||
> If you please draw me a sheep!
|
||||
> 不想当将军的士兵, 不是好士兵.
|
||||
|
||||
# 7. 单行代码
|
||||
|
||||
**语法**
|
||||
```
|
||||
`This is an inline code.`
|
||||
```
|
||||
|
||||
**实例**
|
||||
|
||||
`同样的单行代码, 我经常用来显示特殊名词`
|
||||
|
||||
# 8. 多行代码
|
||||
|
||||
**语法**
|
||||
````
|
||||
```javascript
|
||||
for (var i=0; i<100; i++) {
|
||||
console.log(""hello world"" + i);
|
||||
}
|
||||
|
||||
document.write(fib(10));
|
||||
```
|
||||
|
||||
```python
|
||||
class Employee:
|
||||
empCount = 0
|
||||
|
||||
def __init__(self, name, salary):
|
||||
self.name = name
|
||||
self.salary = salary
|
||||
Employee.empCount += 1
|
||||
```
|
||||
|
||||
# 5. Markdown 扩展
|
||||
|
||||
Markdown 扩展支持:
|
||||
|
||||
* 表格
|
||||
* 定义型列表
|
||||
* Html 标签
|
||||
* 脚注
|
||||
* 目录
|
||||
* 时序图与流程图
|
||||
* MathJax 公式
|
||||
|
||||
## 5.1 表格
|
||||
|
||||
Item | Value
|
||||
-------- | ---
|
||||
Computer | \$1600
|
||||
Phone | \$12
|
||||
Pipe | \$1
|
||||
|
||||
可以指定对齐方式, 如Item列左对齐, Value列右对齐, Qty列居中对齐
|
||||
|
||||
| Item | Value | Qty |
|
||||
| :------- | ----: | :---: |
|
||||
| Computer | \$1600 | 5 |
|
||||
| Phone | \$12 | 12 |
|
||||
| Pipe | \$1 | 234 |
|
||||
|
||||
|
||||
## 5.2 定义型列表
|
||||
|
||||
名词 1
|
||||
: 定义 1(左侧有一个可见的冒号和四个不可见的空格)
|
||||
|
||||
代码块 2
|
||||
: 这是代码块的定义(左侧有一个可见的冒号和四个不可见的空格)
|
||||
|
||||
代码块(左侧有八个不可见的空格)
|
||||
|
||||
## 5.3 Html 标签
|
||||
```
|
||||
````
|
||||
|
||||
**实例**
|
||||
|
||||
**提示**, 如果想对图片的宽度和高度进行控制, 你也可以通过img标签, 如:
|
||||
|
||||
|
||||
## 5.4 脚注
|
||||
|
||||
Leanote[^footnote]来创建一个脚注
|
||||
[^footnote]: Leanote是一款强大的开源云笔记产品.
|
||||
|
||||
## 5.5 目录
|
||||
|
||||
通过 `[TOC]` 在文档中插入目录, 如:
|
||||
|
||||
[TOC]
|
||||
|
||||
## 5.6 时序图与流程图
|
||||
|
||||
```sequence
|
||||
Alice->Bob: Hello Bob, how are you?
|
||||
Note right of Bob: Bob thinks
|
||||
Bob-->Alice: I am good thanks!
|
||||
```js
|
||||
for (var i=0; i<100; i++) {
|
||||
console.log(""hello world"" + i);
|
||||
}
|
||||
```
|
||||
|
||||
流程图:
|
||||
|
||||
```flow
|
||||
st=>start: Start
|
||||
e=>end
|
||||
op=>operation: My Operation
|
||||
cond=>condition: Yes or No?
|
||||
|
||||
st->op->cond
|
||||
cond(yes)->e
|
||||
cond(no)->op
|
||||
```
|
||||
|
||||
> **提示:** 更多关于时序图与流程图的语法请参考:
|
||||
|
||||
> - [时序图语法](http://bramp.github.io/js-sequence-diagrams/)
|
||||
> - [流程图语法](http://adrai.github.io/flowchart.js)
|
||||
|
||||
## 5.7 MathJax 公式
|
||||
|
||||
$ 表示行内公式:
|
||||
|
||||
质能守恒方程可以用一个很简洁的方程式 $E=mc^2$ 来表达。
|
||||
|
||||
$$ 表示整行公式:
|
||||
|
||||
$$\sum_{i=1}^n a_i=0$$
|
||||
|
||||
$$f(x_1,x_x,\ldots,x_n) = x_1^2 + x_2^2 + \cdots + x_n^2 $$
|
||||
|
||||
$$\sum^{j-1}_{k=0}{\widehat{\gamma}_{kj} z_k}$$
|
||||
|
||||
更复杂的公式:
|
||||
$$
|
||||
\begin{eqnarray}
|
||||
\vec\nabla \times (\vec\nabla f) & = & 0 \cdots\cdots梯度场必是无旋场\\
|
||||
\vec\nabla \cdot(\vec\nabla \times \vec F) & = & 0\cdots\cdots旋度场必是无散场\\
|
||||
\vec\nabla \cdot (\vec\nabla f) & = & {\vec\nabla}^2f\\
|
||||
\vec\nabla \times(\vec\nabla \times \vec F) & = & \vec\nabla(\vec\nabla \cdot \vec F) - {\vec\nabla}^2 \vec F\\
|
||||
\end{eqnarray}
|
||||
$$
|
||||
|
||||
访问 [MathJax](http://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference) 参考更多使用方法。
|
||||
|
||||
也可以通过缩进来显示代码, 下面是示例:
|
||||
|
||||
console.loe(""Hello_World"");
|
||||
|
||||
# 参考链接
|
||||
|
||||
https://guides.github.com/features/mastering-markdown/
|
||||
https://help.github.com/articles/basic-writing-and-formatting-syntax/
|
||||
";
|
||||
#region Props
|
||||
[ObservableProperty]
|
||||
|
@ -596,6 +558,6 @@ $$
|
|||
private string _dbName;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _promptString;
|
||||
private string _excelPromptString;
|
||||
#endregion
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue