修复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

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

View File

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

View File

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

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

View File

@ -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.com/images/logo/leanote_icon_blue.png)
:
[这是去往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_
``` javascript **This text will be bold**
/** __This will also be bold__
* nth element in the fibonacci series. ```
* @param n >= 0 (2线, , .)
* @return the nth element, >= 0.
*/ ****
function fib(n) {
var a = 1, b = 1; *This text will be italic*
var tmp; _This will also be italic_
while (--n >= 0) {
tmp = a; **This text will be bold**
a += b; __This will also be bold__
b = tmp;
} # 3.
return a;
****
```
* 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)); ****
```js
for (var i=0; i<100; i++) {
console.log(""hello world"" + i);
}
``` ```
```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
} }