修复AI流式传输时,界面闪烁的问题

This commit is contained in:
lihanbo 2025-02-28 15:34:29 +08:00
parent 33d13b62f1
commit 78d65a6aff
6 changed files with 241 additions and 208 deletions

View File

@ -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");

View File

@ -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 { }
}

View File

@ -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;
}
}
}

View File

@ -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)
{
}
}
}

View File

@ -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)

View File

@ -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.com/images/logo/leanote_icon_blue.png)
:
[这是去往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.
****
```
![img-name](img-url)
```
****
![logo](https://news.cnblogs.com/images/logo.gif)
# 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
}