修复AI流式传输时,界面闪烁的问题
This commit is contained in:
parent
33d13b62f1
commit
78d65a6aff
|
|
@ -812,8 +812,33 @@ public class MarkdownWpfRenderer
|
||||||
link.Inlines.Add(extraInline);
|
link.Inlines.Add(extraInline);
|
||||||
|
|
||||||
link.Click += (s, e) =>
|
link.Click += (s, e) =>
|
||||||
|
{
|
||||||
|
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));
|
LinkNavigate?.Invoke(linkInline, new MarkdownLinkNavigateEventArgs(linkInline.Url));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return link;
|
return link;
|
||||||
|
|
@ -828,14 +853,6 @@ public class MarkdownWpfRenderer
|
||||||
return new WpfDocs.Run(literalInline.Content.ToString());
|
return new WpfDocs.Run(literalInline.Content.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public FontFamily GetNormalTextFontFamily()
|
public FontFamily GetNormalTextFontFamily()
|
||||||
{
|
{
|
||||||
return new FontFamily("-apple-system,BlinkMacSystemFont,Segoe UI Adjusted,Segoe UI,Liberation Sans,sans-serif");
|
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 =
|
public static readonly DependencyProperty RenderedContentProperty =
|
||||||
DependencyProperty.Register(nameof(RenderedContent), typeof(FrameworkElement), typeof(MarkdownViewer), new PropertyMetadata(null));
|
DependencyProperty.Register(nameof(RenderedContent), typeof(FrameworkElement), typeof(MarkdownViewer), new PropertyMetadata(null));
|
||||||
|
private ContentControl? _contentControl;
|
||||||
|
|
||||||
|
|
||||||
private async Task RenderProcessAsync(CancellationToken cancellationToken)
|
private async Task RenderProcessAsync(CancellationToken cancellationToken)
|
||||||
|
|
@ -57,17 +58,22 @@ public class MarkdownViewer : Control
|
||||||
MarkdownDocument? doc =
|
MarkdownDocument? doc =
|
||||||
await Task.Run(() =>
|
await Task.Run(() =>
|
||||||
{
|
{
|
||||||
var doc = Markdig.Markdown.Parse(
|
return Markdig.Markdown.Parse(content, pipeline);
|
||||||
content,
|
|
||||||
pipeline);
|
|
||||||
|
|
||||||
return doc;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 确保在UI线程上执行
|
||||||
|
await Dispatcher.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
// 仅首次创建ContentControl或在需要时重新创建
|
||||||
|
if (_contentControl == null)
|
||||||
|
{
|
||||||
|
_contentControl = new ContentControl();
|
||||||
|
RenderedContent = _contentControl;
|
||||||
|
}
|
||||||
|
|
||||||
var renderer = new MarkdownWpfRenderer();
|
var renderer = new MarkdownWpfRenderer();
|
||||||
ContentControl contentControl =new();
|
renderer.RenderDocumentTo(_contentControl, doc, cancellationToken);
|
||||||
RenderedContent = contentControl;
|
});
|
||||||
renderer.RenderDocumentTo(contentControl, doc, cancellationToken);
|
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ class AiHelper
|
||||||
TopK= 10,
|
TopK= 10,
|
||||||
TopP = 0.85f,
|
TopP = 0.85f,
|
||||||
Temperature = 0.7f,
|
Temperature = 0.7f,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (UseTool)
|
if (UseTool)
|
||||||
|
|
@ -70,5 +71,27 @@ class AiHelper
|
||||||
yield return response.Text;
|
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.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using ExcelHelper.Message;
|
||||||
|
|
||||||
namespace ExcelHelper.Views.Components;
|
namespace ExcelHelper.Views.Components;
|
||||||
|
|
||||||
|
|
@ -67,6 +69,13 @@ public partial class AiMessageControll : UserControl
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
//DataContext = this;
|
//DataContext = this;
|
||||||
|
WeakReferenceMessenger.Default.Register<ScrollToEndMessage>("MessageListScrollToEnd", (o, s) => {
|
||||||
|
|
||||||
|
Dispatcher.BeginInvoke(() =>
|
||||||
|
{
|
||||||
|
ScrollToEnd();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SendButton_Click(object sender, RoutedEventArgs e)
|
private void SendButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
|
||||||
}
|
}
|
||||||
MaxRow = ExcelData.Count();
|
MaxRow = ExcelData.Count();
|
||||||
|
|
||||||
PromptString = Excel2Prompt.ConverterToPrompt(ExcelData, columns,StartCell);
|
ExcelPromptString = Excel2Prompt.ConverterToPrompt(ExcelData, columns,StartCell);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -260,6 +260,13 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(message))
|
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
|
Messages.Add(new ChatMessage
|
||||||
{
|
{
|
||||||
Content = message,
|
Content = message,
|
||||||
|
|
@ -272,18 +279,26 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
|
||||||
IsUser = false,
|
IsUser = false,
|
||||||
Content = ""
|
Content = ""
|
||||||
};
|
};
|
||||||
|
|
||||||
Messages.Add(aiChat);
|
Messages.Add(aiChat);
|
||||||
#if DEBUG
|
#if !DEBUG
|
||||||
var results = TEST_AI_CONTENT.Split('\n');
|
var results = TEST_AI_CONTENT.Split('\n');
|
||||||
foreach(var result in results)
|
foreach(var result in results)
|
||||||
{
|
{
|
||||||
aiChat.Content += result;
|
aiChat.Content += result;
|
||||||
|
WeakReferenceMessenger.Default.Send(new ScrollToEndMessage(""));
|
||||||
await Task.Delay(5);
|
await Task.Delay(5);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
var promptedMsg = PromptUtil.UsePrompt(message,PromptString);
|
var promptedMsg = PromptUtil.UsePrompt(message,ExcelPromptString);
|
||||||
await foreach(var result in AiHelper.GetStreamingResponseAsync(promptedMsg))
|
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;
|
aiChat.Content += result;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -293,196 +308,143 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
|
||||||
|
|
||||||
|
|
||||||
private const string TEST_AI_CONTENT = @"
|
private const string TEST_AI_CONTENT = @"
|
||||||
# Welcome to Leanote! 欢迎来到Leanote!
|
这篇文章包含markdown语法基本的内容, 目的是放在自己的博客园上, 通过开发者控制台快速选中,
|
||||||
|
从而自定义自己博客园markdown样式.当然本文也可以当markdown语法学习之用.
|
||||||
|
|
||||||
## 1. 排版
|
在markdown里强制换行是在末尾添加2个空格+1个回车.
|
||||||
|
在markdown里可以使用 \ 对特殊符号进行转义.
|
||||||
|
|
||||||
**粗体** *斜体*
|
# 1. 标题
|
||||||
|
|
||||||
~~这是一段错误的文本。~~
|
|
||||||
|
|
||||||
引用:
|
|
||||||
|
|
||||||
> 引用Leanote官方的话, 为什么要做Leanote, 原因是...
|
|
||||||
|
|
||||||
有充列表:
|
|
||||||
1. 支持Vim
|
|
||||||
2. 支持Emacs
|
|
||||||
|
|
||||||
无序列表:
|
|
||||||
|
|
||||||
- 项目1
|
|
||||||
- 项目2
|
|
||||||
|
|
||||||
|
|
||||||
## 2. 图片与链接
|
|
||||||
|
|
||||||
图片:
|
|
||||||

|
|
||||||
链接:
|
|
||||||
|
|
||||||
[这是去往Leanote官方博客的链接](http://leanote.leanote.com)
|
|
||||||
|
|
||||||
## 3. 标题
|
|
||||||
|
|
||||||
以下是各级标题, 最多支持5级标题
|
|
||||||
|
|
||||||
|
**语法**
|
||||||
```
|
```
|
||||||
# h1
|
# This is an <h1> tag
|
||||||
## h2
|
## This is an <h2> tag
|
||||||
### h3
|
### This is an <h3> tag
|
||||||
#### h4
|
#### This is an <h4> tag
|
||||||
##### h4
|
|
||||||
###### h5
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 4. 代码
|
**实例**
|
||||||
|
|
||||||
示例:
|
# This is an h1 tag
|
||||||
|
## This is an h2 tag
|
||||||
|
### This is an h3 tag
|
||||||
|
#### This is an h4 tag
|
||||||
|
|
||||||
function get(key) {
|
# 2. 强调和斜体
|
||||||
return m[key];
|
|
||||||
|
**语法**
|
||||||
|
```
|
||||||
|
*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);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
代码高亮示例:
|
**实例**
|
||||||
|
|
||||||
``` javascript
|
```js
|
||||||
/**
|
for (var i=0; i<100; i++) {
|
||||||
* nth element in the fibonacci series.
|
console.log(""hello world"" + i);
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.write(fib(10));
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
也可以通过缩进来显示代码, 下面是示例:
|
||||||
class Employee:
|
|
||||||
empCount = 0
|
|
||||||
|
|
||||||
def __init__(self, name, salary):
|
console.loe(""Hello_World"");
|
||||||
self.name = name
|
|
||||||
self.salary = salary
|
|
||||||
Employee.empCount += 1
|
|
||||||
```
|
|
||||||
|
|
||||||
# 5. Markdown 扩展
|
# 参考链接
|
||||||
|
|
||||||
Markdown 扩展支持:
|
https://guides.github.com/features/mastering-markdown/
|
||||||
|
https://help.github.com/articles/basic-writing-and-formatting-syntax/
|
||||||
* 表格
|
|
||||||
* 定义型列表
|
|
||||||
* 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!
|
|
||||||
```
|
|
||||||
|
|
||||||
流程图:
|
|
||||||
|
|
||||||
```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) 参考更多使用方法。
|
|
||||||
";
|
";
|
||||||
#region Props
|
#region Props
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
|
|
@ -596,6 +558,6 @@ $$
|
||||||
private string _dbName;
|
private string _dbName;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _promptString;
|
private string _excelPromptString;
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue