diff --git a/DrfxFontFixer/DrfxFontFixer.csproj b/DrfxFontFixer/DrfxFontFixer.csproj index 943c7a2..b37301f 100644 --- a/DrfxFontFixer/DrfxFontFixer.csproj +++ b/DrfxFontFixer/DrfxFontFixer.csproj @@ -5,6 +5,7 @@ 13.0 true true + AnyCPU;x64 diff --git a/NeoUI/NeoUI/Controls/DatePicker.xaml b/NeoUI/NeoUI/Controls/DatePicker.xaml index 7d9fc02..5838189 100644 --- a/NeoUI/NeoUI/Controls/DatePicker.xaml +++ b/NeoUI/NeoUI/Controls/DatePicker.xaml @@ -111,7 +111,6 @@ - @@ -363,7 +362,6 @@ @@ -371,6 +369,7 @@ x:Name="PART_TextBox" Grid.Row="0" Grid.Column="0" + VerticalAlignment="Stretch" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" Background="{TemplateBinding Background}" @@ -392,7 +391,6 @@ - - - + diff --git a/NeoUI/NeoUI/NeoUI.csproj b/NeoUI/NeoUI/NeoUI.csproj index c204ed6..e61dbec 100644 --- a/NeoUI/NeoUI/NeoUI.csproj +++ b/NeoUI/NeoUI/NeoUI.csproj @@ -18,6 +18,7 @@ False True WPF 拟态控件 + AnyCPU;x64 diff --git a/NeoUI/NeoUITest/ControlTestWindow.xaml b/NeoUI/NeoUITest/ControlTestWindow.xaml index 7a3d0a1..590689a 100644 --- a/NeoUI/NeoUITest/ControlTestWindow.xaml +++ b/NeoUI/NeoUITest/ControlTestWindow.xaml @@ -74,36 +74,33 @@ DataContext="{StaticResource Dgd}" ItemsSource="{Binding DataCollection}" Style="{StaticResource CheckBoxHeadDataGridStyle}" /> - - - + + + + + + + \ No newline at end of file diff --git a/WPFUI.Test/ChatFunction/ChatDialogue.xaml.cs b/WPFUI.Test/ChatFunction/ChatDialogue.xaml.cs new file mode 100644 index 0000000..a014d0d --- /dev/null +++ b/WPFUI.Test/ChatFunction/ChatDialogue.xaml.cs @@ -0,0 +1,218 @@ +using System.Diagnostics; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +namespace WPFUI.Test +{ + /// + /// ChatDialogue.xaml 的交互逻辑 + /// + public partial class ChatDialogue + { + private ChatDialogueViewModel viewModel = new ChatDialogueViewModel(); + public ChatDialogue() + { + InitializeComponent(); + this.DataContext = viewModel; + } + + private void OpenHyperlink(object sender, ExecutedRoutedEventArgs e) + { + Process.Start(e.Parameter.ToString()); + } + + private void ClickOnImage(object sender, System.Windows.Input.ExecutedRoutedEventArgs e) + { + MessageBox.Show($"URL: {e.Parameter}"); + } + + + //_deepSeekClient = new DeepSeekClient("sk-3a3126167f1343228b1a5745bcd0bf01"); + ///// + ///// DeepSeek对象 + ///// + //private DeepSeekClient _deepSeekClient; + + ///// + ///// 系统角色设定 + ///// + //private const string SysContent = "你是 DeepSeek AI 提供的人工智能助手,你更擅长中文和英文的对话。你会为用户提供安全,有帮助,准确的回答。同时,你会拒绝一切涉及恐怖主义,种族歧视,黄色暴力等问题的回答。Moonshot AI 为专有名词,不可翻译成其他语言"; + + + ///// + ///// 单轮流式聊天 + ///// + ///// + ///// + //private async void button1_Click(object sender, RoutedEventArgs e) + //{ + // try + // { + // richTextBox1.Document.Blocks.Clear(); + // //------ + // var chatReq = new ChatRequest + // { + // model = ChatModels.DeepseekChat, + // messages = new List { + // new MessagesItem { role = ChatRoles.System, content = SysContent }, + // new MessagesItem { role = ChatRoles.User, content = textBox1.Text } + // }, + // stream = true + // }; + // // 订阅事件 + // _deepSeekClient.MessageReceivedEventHandler -= OnMessageReceived; + // _deepSeekClient.ErrorEventHandler -= OnErrorMsg; + // _deepSeekClient.MessageReceivedEventHandler += OnMessageReceived; + // _deepSeekClient.ErrorEventHandler += OnErrorMsg; + // // 开始任务 + // await _deepSeekClient.ChatStreamAsync(chatReq).ConfigureAwait(true); + // // 订阅SSE 消息事件 + // void OnMessageReceived(object s, ChatResponse m) + // { + // //Console.WriteLine("聊天内容:" + ee); + // this.Dispatcher.Invoke(() => + // { + // var msg = m.choices?.FirstOrDefault()?.delta.content; + // if (msg != null) richTextBox1.AppendText(msg); + // }); + // } + // // 订阅SSE 错误事件 + // void OnErrorMsg(object s, Exception ex) + // { + // MessageBox.Show(ex.Message); + // } + // } + // catch (Exception ex) + // { + // Console.WriteLine("流式聊天失败:" + ex.Message); + // } + //} + + ///// + ///// 单轮普通聊天 + ///// + ///// + ///// + //private async void button2_Click(object sender, RoutedEventArgs e) + //{ + // try + // { + // var chatReq = new ChatRequest + // { + // model = ChatModels.DeepseekChat, + // messages = new List { + // new MessagesItem { role = ChatRoles.System, content = SysContent }, + // new MessagesItem { role = ChatRoles.User, content = textBox1.Text } + // } + // }; + // var chatRes = await _deepSeekClient.ChatAsync(chatReq).ConfigureAwait(true); + // Console.WriteLine("聊天内容:" + chatRes.choices?.FirstOrDefault()?.message.content); + // Paragraph paragraph = new Paragraph(); + // paragraph.Inlines.Add(new Run(chatRes.choices?.FirstOrDefault()?.message.content)); + // richTextBox1.Document.Blocks.Add(paragraph); + // } + // catch (Exception ex) + // { + // Console.WriteLine("聊天失败:" + ex.Message); + // } + //} + + ///// + ///// 多轮对话列表 + ///// + //private List HistoryList = new List { new MessagesItem { role = ChatRoles.System, content = SysContent } }; + + ///// + ///// 多轮流式对话 + ///// + ///// + ///// + //private async void button3_Click(object sender, RoutedEventArgs e) + //{ + + // try + // { + // richTextBox1.Document.Blocks.Clear(); + // //------当前要说的内容 + // HistoryList.Add(new MessagesItem { role = ChatRoles.User, content = textBox1.Text }); + // //------ + // var chatReq = new ChatRequest + // { + // model = ChatModels.DeepseekChat, + // messages = HistoryList, + // stream = true + // }; + // // 订阅事件 + // _deepSeekClient.MessageReceivedEventHandler -= OnMessageReceived; + // _deepSeekClient.ErrorEventHandler -= OnErrorMsg; + // _deepSeekClient.CloseedEventHandler -= OnCloseed; + // _deepSeekClient.MessageReceivedEventHandler += OnMessageReceived; + // _deepSeekClient.ErrorEventHandler += OnErrorMsg; + // _deepSeekClient.CloseedEventHandler += OnCloseed; + // // 开始任务 + // await _deepSeekClient.ChatStreamAsync(chatReq).ConfigureAwait(true); + // // 局部函数,订阅SSE 消息事件 + // void OnMessageReceived(object s, ChatResponse m) + // { + // //Console.WriteLine("聊天内容:" + ee); + // this.Dispatcher + // .Invoke( + // () => + // { + // var msg = m.choices?.FirstOrDefault()?.delta.content; + // if (msg != null) + // { + // richTextBox1.AppendText(msg); + // } + // }); + // } + // // 局部函数,订阅SSE 错误事件 + // void OnErrorMsg(object s, Exception ex) + // { + // MessageBox.Show(ex.Message); + // } + // // 局部函数,订阅SSE 关闭事件 + // void OnCloseed(object s, string ex) + // { + // HistoryList.Add(new MessagesItem { role = ChatRoles.Assistant, content = textBox1.Text }); + // } + // } + // catch (Exception ex) + // { + // Console.WriteLine("流式聊天失败:" + ex.Message); + // } + //} + + ///// + ///// 多轮普通聊天 + ///// + ///// + ///// + //private async void button4_Click(object sender, RoutedEventArgs e) + //{ + // try + // { + // //------当前要说的内容 + // HistoryList.Add(new MessagesItem { role = ChatRoles.User, content = textBox1.Text }); + // //------------------- + // var chatReq = new ChatRequest + // { + // model = ChatModels.DeepseekChat, + // messages = HistoryList + // }; + // var chatRes = await _deepSeekClient.ChatAsync(chatReq).ConfigureAwait(true); + // HistoryList.Add(new MessagesItem { role = ChatRoles.Assistant, content = textBox1.Text }); + // Console.WriteLine("聊天内容:" + chatRes.choices?.FirstOrDefault()?.message.content); + // Paragraph paragraph = new Paragraph(); + // paragraph.Inlines.Add(new Run(chatRes.choices?.FirstOrDefault()?.message.content)); + // richTextBox1.Document.Blocks.Add(paragraph); + // } + // catch (Exception ex) + // { + // Console.WriteLine("聊天失败:" + ex.Message); + // } + //} + } +} diff --git a/WPFUI.Test/ChatFunction/ChatDialogueViewModel.cs b/WPFUI.Test/ChatFunction/ChatDialogueViewModel.cs new file mode 100644 index 0000000..a270dc0 --- /dev/null +++ b/WPFUI.Test/ChatFunction/ChatDialogueViewModel.cs @@ -0,0 +1,238 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; + +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; + +using LangChain.Chains.LLM; +using LangChain.Memory; +using LangChain.Prompts; +using LangChain.Providers; +using LangChain.Providers.DeepSeek; +using LangChain.Providers.DeepSeek.Predefined; +using LangChain.Providers.OpenAI; +using LangChain.Providers.OpenAI.Predefined; +using LangChain.Schema; + +using Markdig; +using Markdig.Wpf.ColorCode; + +using tryAGI.OpenAI; + +//using static LangChain.Chains.Chain; +namespace WPFUI.Test +{ + public partial class ChatDialogueViewModel : ObservableObject + { + static readonly DeepSeekConfiguration config = new DeepSeekConfiguration() + { + ApiKey = "sk-3a3126167f1343228b1a5745bcd0bf01", + Endpoint = "https://api.deepseek.com", + ChatSettings = new() { UseStreaming = true } + }; + private ScrollViewer scrollViewer; + /// + /// 当前AI的回复 + /// + [ObservableProperty] + public partial Message? CurrentRespone { get; set; } = Message.Ai(string.Empty); + /// + /// 发送给AI的消息,包括上下文记录 + /// + private Message? CurrentRequest { get; set; } = Message.Empty; + public MarkdownPipeline Pipeline { get; set; } = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseColorCodeWpf().Build(); + + public ChatDialogueViewModel() + { + ChatHistory ??= new ObservableCollection(); + } + /// + /// 用户输入 + /// + [ObservableProperty] + [NotifyCanExecuteChangedFor(nameof(SendCommand))] + public partial string UserInput { get; set; } + private bool CanSend() + { + return !string.IsNullOrEmpty(UserInput); + } + [RelayCommand] + private async void PromptChat() + { + try + { + var deepseekLLM = new DeepSeekChatModel(new DeepSeekProvider(config)); + var prompt = new PromptTemplate(new PromptTemplateInput( + template: "Revit二次开发中,使用变量doc和uidoc两个变量,构造一个保证可以执行的C#代码块,添加相应注释,不需要方法签名和using命名空间,但使用时需要完整的命名空间。实现{需求}的功能", inputVariables: ["需求"])); + deepseekLLM.RequestSent += DeepseekLLM_RequestSent; + var chain = new LlmChain(new LlmChainInput(deepseekLLM, prompt)); + var result = await chain.CallAsync(new ChainValues(new Dictionary + { + { "需求", UserInput} + })).ConfigureAwait(true); + // The result is an object with a `text` property. + var respones = result.Value["text"].ToString(); + } + catch (Exception ex) + { + + } + } + /// + /// 历史聊天记录 + /// + public ObservableCollection ChatHistory { get; set; } + + [RelayCommand(CanExecute = nameof(CanSend), IncludeCancelCommand = true)] + private async Task SendAsync(object obj, CancellationToken cancellationToken) + { + try + { + if (obj is ScrollViewer scroll) + { + scrollViewer = scroll; + } + + ChatHistory.Add(Message.Human(UserInput)); + //UserInput.Content=string.Empty; + + + #region DeepSeek + var deepseekLLM = new DeepSeekChatModel(new DeepSeekProvider(config)); + deepseekLLM.ResponseReceived += DeepseekLLM_ResponseReceived; + + deepseekLLM.DeltaReceived += DeepseekLLM_DeltaReceived; + + //deepseekLLM.RequestSent += DeepseekLLM_RequestSent; + + CurrentRequest += Message.Human(UserInput); + UserInput = string.Empty; + /*var result = */ + await deepseekLLM.GenerateAsync(CurrentRequest, cancellationToken: cancellationToken); + #endregion + + // Since the LLMChain is a single-input, single-output chain, we can also call it with `run`. + // This takes in a string and returns the `text` property. + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + } + + private async static Task RunChainAsync() + { + try + { + var client = new OpenAiClient("sk-3a3126167f1343228b1a5745bcd0bf01"); + OpenAiProvider provider = new OpenAiProvider(client); + var llm = new OpenAiLatestFastChatModel(provider); + var embeddingModel = new TextEmbeddingV3SmallModel(provider); + var prompt = new PromptTemplate(new PromptTemplateInput( +template: "Revit二次开发中,使用变量doc和uidoc两个变量,构造一个保证可以执行的C#代码块,添加相应注释,不需要方法签名和using命名空间,但使用时需要完整的命名空间。实现{需求}的功能", inputVariables: ["需求"])); + var chain = new LlmChain(new LlmChainInput(llm, prompt)); + + var result2 = await chain.RunAsync("彩色长筒靴"); + Console.WriteLine(result2); + var chatPrompt = ChatPromptTemplate.FromPromptMessages([ + SystemMessagePromptTemplate.FromTemplate( + "You are a helpful assistant that translates {input_language} to {output_language}."), + HumanMessagePromptTemplate.FromTemplate("{text}") + ]); + + var chainB = new LlmChain(new LlmChainInput(llm, chatPrompt) + { + Verbose = true + }); + + var resultB = await chainB.CallAsync(new ChainValues(new Dictionary(3) + { + {"input_language", "English"}, + {"output_language", "French"}, + {"text", "I love programming"}, + })); + Console.WriteLine(resultB.Value["text"]); + } + catch (Exception ex) + { + + } + } + + private void DeepseekLLM_RequestSent(object sender, ChatRequest e) + { + Debug.WriteLine("-------------RequestSent-------------"); + foreach (var mes in e.Messages) + { + Debug.WriteLine($"{mes}"); + + } + //Debug.WriteLine("-------------RequestSent-------------"); + //Debug.WriteLine($"发送者{sender}"); + //scrollViewer.Dispatcher.Invoke(() => + //{ + // ChatHistory.Add(CurrentRespone); + //}); + } + //接收完毕 + private void DeepseekLLM_ResponseReceived(object sender, ChatResponse e) + { + //Debug.WriteLine("-------------ResponseReceived-------------"); + Application.Current.Dispatcher.Invoke(() => + { + //Debug.WriteLine($"发送者:{sender};使用量:{e.Usage}"); + CurrentRequest += e.LastMessage; + CurrentRespone = Message.Ai(string.Empty); + //最后一条完整的消息 + //Debug.WriteLine($"{ChatHistory}"); + //ChatHistory.Add(e.LastMessage); + //Respones.Content += e.Content; + }); + } + //partial void OnCurrentResponeChanged(Message? value) + //{ + //} + //接收Delta + private void DeepseekLLM_DeltaReceived(object sender, ChatResponseDelta e) + { + if (string.IsNullOrEmpty(e.Content)) + { + return; + } + scrollViewer.Dispatcher.Invoke(() => + { + ChatHistory.Remove(CurrentRespone); + //Debug.WriteLine("-------------DeltaReceived-------------"); + + Debug.WriteLine($"{e.Content}"); + + CurrentRespone += Message.Ai(e.Content); + ChatHistory.Add(CurrentRespone); + Task.Delay(1); + //ChatHistory. + //判断滚动条是否在底部 + if (scrollViewer.VerticalOffset == scrollViewer.ExtentHeight - scrollViewer.ViewportHeight) + { + scrollViewer?.ScrollToEnd(); + } + //Respones.Content += e.Content; + }); + } + + [RelayCommand] + private void NewSession() + { + ChatHistory?.Clear(); + CurrentRequest = Message.Empty; + } + + } +} \ No newline at end of file diff --git a/WPFUI.Test/ChatFunction/ColorCodeBlockRenderer.cs b/WPFUI.Test/ChatFunction/ColorCodeBlockRenderer.cs new file mode 100644 index 0000000..f6ee8b6 --- /dev/null +++ b/WPFUI.Test/ChatFunction/ColorCodeBlockRenderer.cs @@ -0,0 +1,109 @@ +using System.Text; +using System.Windows; +using System.Windows.Documents; + +using ColorCode; +using ColorCode.Styling; +using ColorCode.Wpf; + +using Markdig.Parsers; +using Markdig.Renderers; +using Markdig.Renderers.Wpf; +using Markdig.Syntax; + +namespace Markdig.Wpf.ColorCode; + +public class ColorCodeBlockRenderer : WpfObjectRenderer +{ + private readonly CodeBlockRenderer _underlyingCodeBlockRenderer; + private readonly StyleDictionary _styleDictionary; + + /// + /// Create a new with the specified and . + /// + /// The underlying CodeBlockRenderer to handle unsupported languages. + /// A StyleDictionary for custom styling. + public ColorCodeBlockRenderer(CodeBlockRenderer underlyingCodeBlockRenderer, StyleDictionary styleDictionary) + { + _underlyingCodeBlockRenderer = underlyingCodeBlockRenderer; + _styleDictionary = styleDictionary; + } + + /// + /// Writes the specified to the . + /// + /// The renderer. + /// The code block to render. + protected override void Write(WpfRenderer renderer, CodeBlock codeBlock) + { + if (codeBlock is not FencedCodeBlock fencedCodeBlock || + codeBlock.Parser is not FencedCodeBlockParser fencedCodeBlockParser) + { + _underlyingCodeBlockRenderer.Write(renderer, codeBlock); + + return; + } + + var language = ExtractLanguage(fencedCodeBlock, fencedCodeBlockParser); + + if (language is null) + { + _underlyingCodeBlockRenderer.Write(renderer, codeBlock); + + return; + } + + var code = ExtractCode(codeBlock); + //if (string.IsNullOrEmpty(code)) + //{ + // return; + //} + var formatter = new RichTextBoxFormatter(_styleDictionary); + var paragraph = new Paragraph(); + paragraph.SetResourceReference(FrameworkContentElement.StyleProperty, Styles.CodeBlockStyleKey); + formatter.FormatInlines(code, language, paragraph.Inlines); + + renderer.WriteBlock(paragraph); + } + + private static ILanguage? ExtractLanguage(IFencedBlock fencedCodeBlock, FencedCodeBlockParser parser) + { + var languageId = fencedCodeBlock.Info!.Replace(parser.InfoPrefix!, string.Empty); + + return string.IsNullOrWhiteSpace(languageId) ? null : Languages.FindById(languageId); + } + + private static string ExtractCode(LeafBlock leafBlock) + { + var code = new StringBuilder(); + var lines = leafBlock.Lines.Lines; + //if (leafBlock.Line == 0) + //{ + // return string.Empty; + //} + + var totalLines = lines == null ? 0 : lines.Length; + + for (var index = 0; index < totalLines; index++) + { + var line = lines[index]; + var slice = line.Slice; + + if (slice.Text == null) + { + continue; + } + + var lineText = slice.Text.Substring(slice.Start, slice.Length); + + if (index > 0) + { + code.AppendLine(); + } + + code.Append(lineText); + } + + return code.ToString(); + } +} \ No newline at end of file diff --git a/WPFUI.Test/ChatFunction/ColorCodeWpfExtension.cs b/WPFUI.Test/ChatFunction/ColorCodeWpfExtension.cs new file mode 100644 index 0000000..25a6a5f --- /dev/null +++ b/WPFUI.Test/ChatFunction/ColorCodeWpfExtension.cs @@ -0,0 +1,55 @@ +using ColorCode.Styling; +using Markdig.Renderers; +using Markdig.Renderers.Wpf; + +namespace Markdig.Wpf.ColorCode; + +public class ColorCodeWpfExtension : IMarkdownExtension +{ + private readonly StyleDictionary _styleDictionary; + + /// + /// Creates a new with the specified . + /// + /// A dictionary indicating how to style the code. + public ColorCodeWpfExtension(StyleDictionary styleDictionary) => _styleDictionary = styleDictionary; + + /// + /// Sets up this extension for the specified pipeline. + /// + /// The pipeline. + public void Setup(MarkdownPipelineBuilder pipeline) + { + } + + /// + /// Sets up this extension for the specified renderer. + /// + /// The pipeline used to parse the document. + /// The renderer. + public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) + { + if (renderer is not WpfRenderer wpfRenderer) + { + return; + } + + var codeBlockRenderer = wpfRenderer.ObjectRenderers.FindExact(); + + if (codeBlockRenderer != null) + { + wpfRenderer.ObjectRenderers.Remove(codeBlockRenderer); + } + else + { + codeBlockRenderer = new CodeBlockRenderer(); + } + + wpfRenderer.ObjectRenderers.AddIfNotAlready( + new ColorCodeBlockRenderer( + codeBlockRenderer, + _styleDictionary + ) + ); + } +} diff --git a/WPFUI.Test/ChatFunction/CustomChatWindow.xaml b/WPFUI.Test/ChatFunction/CustomChatWindow.xaml new file mode 100644 index 0000000..f84268f --- /dev/null +++ b/WPFUI.Test/ChatFunction/CustomChatWindow.xaml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/WPFUI.Test/ChatFunction/CustomChatWindow.xaml.cs b/WPFUI.Test/ChatFunction/CustomChatWindow.xaml.cs new file mode 100644 index 0000000..6741e9d --- /dev/null +++ b/WPFUI.Test/ChatFunction/CustomChatWindow.xaml.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +//using LangChain.Chains.LLM; +//using LangChain.DocumentLoaders; +//using LangChain.Prompts; +using LangChain.Providers; +using LangChain.Providers.DeepSeek; +using LangChain.Providers.DeepSeek.Predefined; +using LangChain.Providers.OpenAI; +using LangChain.Providers.OpenAI.Predefined; +//using LangChain.Schema; + +namespace WPFUI.Test +{ + /// + /// CustomChatWindow.xaml 的交互逻辑 + /// + public partial class CustomChatWindow : Window + { + public CustomChatWindow() + { + InitializeComponent(); + //ChatWithAI(); + //ChatWithDeepSeek(); + } + private async void ChatWithDeepSeek() + { + try + { + var Response = await DeepSeekHttpClient.Run(); + } + catch (Exception) + { + throw; + } + } + + } +} diff --git a/WPFUI.Test/ChatFunction/DeepSeekClient.cs b/WPFUI.Test/ChatFunction/DeepSeekClient.cs new file mode 100644 index 0000000..8d1af4b --- /dev/null +++ b/WPFUI.Test/ChatFunction/DeepSeekClient.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +//using Azure; + +namespace WPFUI.Test +{ + class DeepSeekHttpClient + { + private readonly HttpClient _client; + private const string BaseUrl = "https://api.deepseek.com"; + private const string ApiUrl = "https://api.deepseek.com/chat/completions"; + private const string ApiKey = "sk-3a3126167f1343228b1a5745bcd0bf01"; // 替换为你的实际API Key + + public DeepSeekHttpClient() + { + _client = new HttpClient(); + _client.DefaultRequestHeaders.Add("Authorization", $"Bearer {ApiKey}"); + } + + public async Task GetResponseAsync(string prompt) + { + using var request = CreateRequest(prompt); + using var response = await _client.SendAsync(request); + + var responseJson = await response.Content.ReadAsStringAsync(); + if (!response.IsSuccessStatusCode) + { + throw new HttpRequestException($"API Error: {response.StatusCode}\n{responseJson}"); + } + + return ExtractResponseContent(responseJson); + } + + private HttpRequestMessage CreateRequest(string prompt) + { + var body = new + { + model = "deepseek-chat", + messages = new[] { new { role = "user", content = prompt } }, + temperature = 0.7 + }; + + return new HttpRequestMessage(HttpMethod.Post, ApiUrl) + { + Content = new StringContent( + JsonSerializer.Serialize(body), + Encoding.UTF8, + "application/json") + }; + } + + private static string ExtractResponseContent(string json) + { + using var doc = JsonDocument.Parse(json); + return doc.RootElement + .GetProperty("choices")[0] + .GetProperty("message") + .GetProperty("content") + .GetString() ?? string.Empty; + } + + public static async Task Run() + { + var client = new DeepSeekHttpClient(); + return await client.GetResponseAsync("如何学习人工智能?"); + } + } +} diff --git a/WPFUI.Test/ChatFunction/ExtensionMethods.cs b/WPFUI.Test/ChatFunction/ExtensionMethods.cs new file mode 100644 index 0000000..b3ba493 --- /dev/null +++ b/WPFUI.Test/ChatFunction/ExtensionMethods.cs @@ -0,0 +1,30 @@ +using System; +using System.Windows.Media; + +namespace ColorCode.Wpf.Common +{ + public static class ExtensionMethods + { + public static SolidColorBrush GetSolidColorBrush(this string hex) + { + hex = hex.Replace("#", string.Empty); + + byte a = 255; + int index = 0; + + if (hex.Length == 8) + { + a = (byte)(Convert.ToUInt32(hex.Substring(index, 2), 16)); + index += 2; + } + + byte r = (byte)(Convert.ToUInt32(hex.Substring(index, 2), 16)); + index += 2; + byte g = (byte)(Convert.ToUInt32(hex.Substring(index, 2), 16)); + index += 2; + byte b = (byte)(Convert.ToUInt32(hex.Substring(index, 2), 16)); + SolidColorBrush myBrush = new(Color.FromArgb(a, r, g, b)); + return myBrush; + } + } +} diff --git a/WPFUI.Test/ChatFunction/MarkdownPipelineBuilderExtension.cs b/WPFUI.Test/ChatFunction/MarkdownPipelineBuilderExtension.cs new file mode 100644 index 0000000..593e7bc --- /dev/null +++ b/WPFUI.Test/ChatFunction/MarkdownPipelineBuilderExtension.cs @@ -0,0 +1,24 @@ +using ColorCode.Styling; + +namespace Markdig.Wpf.ColorCode; + +/// +/// Extensions for adding ColorCode to the Markdig pipeline. +/// +public static class MarkdownPipelineBuilderExtension +{ + /// + /// Use ColorCode to apply code colorization to markdown within the Markdig pipeline. + /// + /// The pipeline the ColorCode extension is being added to. + /// An optional StyleDictionary for custom styling. + /// The MarkdownPipelineBuilder with the added ColorCode extension. + public static MarkdownPipelineBuilder UseColorCodeWpf( + this MarkdownPipelineBuilder pipeline, + StyleDictionary? styleDictionary = null) + { + pipeline.Extensions.Add(new ColorCodeWpfExtension(styleDictionary ?? StyleDictionary.DefaultLight)); + + return pipeline; + } +} diff --git a/WPFUI.Test/ChatFunction/MarkdownStyleRes.xaml b/WPFUI.Test/ChatFunction/MarkdownStyleRes.xaml new file mode 100644 index 0000000..45d34c7 --- /dev/null +++ b/WPFUI.Test/ChatFunction/MarkdownStyleRes.xaml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/WPFUI.Test/ChatFunction/RichTextBoxFormatter.cs b/WPFUI.Test/ChatFunction/RichTextBoxFormatter.cs new file mode 100644 index 0000000..04abe0f --- /dev/null +++ b/WPFUI.Test/ChatFunction/RichTextBoxFormatter.cs @@ -0,0 +1,149 @@ +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; + +using ColorCode.Common; +using ColorCode.Parsing; +using ColorCode.Styling; +using ColorCode.Wpf.Common; + +namespace ColorCode.Wpf +{ + /// + /// Creates a , for rendering Syntax Highlighted code to a RichTextBlock. + /// + public class RichTextBoxFormatter : CodeColorizerBase + { + /// + /// Creates a , for rendering Syntax Highlighted code to a RichTextBlock. + /// + /// The Custom styles to Apply to the formatted Code. + /// The language parser that the instance will use for its lifetime. + public RichTextBoxFormatter(StyleDictionary? style = null, ILanguageParser? languageParser = null) : base(style, languageParser) + { + } + + /// + /// Adds Syntax Highlighted Source Code to the provided RichTextBlock. + /// + /// The source code to colorize. + /// The language to use to colorize the source code. + /// The Control to add the Text to. + public void FormatRichTextBox(string sourceCode, ILanguage language, RichTextBox richText) + { + var paragraph = new Paragraph(); + richText.Document.Blocks.Add(paragraph); + FormatInlines(sourceCode, language, paragraph.Inlines); + } + + /// + /// Adds Syntax Highlighted Source Code to the provided InlineCollection. + /// + /// The source code to colorize. + /// The language to use to colorize the source code. + /// InlineCollection to add the Text to. + public void FormatInlines(string sourceCode, ILanguage language, InlineCollection inlineCollection) + { + InlineCollection = inlineCollection; + languageParser.Parse(sourceCode, language, (parsedSourceCode, captures) => Write(parsedSourceCode, captures)); + } + + private InlineCollection? InlineCollection { get; set; } + + protected override void Write(string parsedSourceCode, IList scopes) + { + var styleInsertions = new List(); + + foreach (Scope scope in scopes) + GetStyleInsertionsForCapturedStyle(scope, styleInsertions); + + styleInsertions.SortStable((x, y) => x.Index.CompareTo(y.Index)); + + int offset = 0; + + Scope? previousScope = null; + + foreach (var styleinsertion in styleInsertions) + { + var text = parsedSourceCode.Substring(offset, styleinsertion.Index - offset); + CreateSpan(text, previousScope); + if (!string.IsNullOrWhiteSpace(styleinsertion.Text)) + { + CreateSpan(text, previousScope); + } + offset = styleinsertion.Index; + + previousScope = styleinsertion.Scope; + } + + var remaining = parsedSourceCode.Substring(offset); + // Ensures that those loose carriages don't run away! + if (remaining != "\r") + { + CreateSpan(remaining, null); + } + } + + private void CreateSpan(string text, Scope? scope) + { + var span = new Span(); + var run = new Run + { + Text = text + }; + + // Styles and writes the text to the span. + if (scope != null) StyleRun(run, scope); + span.Inlines.Add(run); + + InlineCollection?.Add(span); + } + + private void StyleRun(Run run, Scope scope) + { + string? foreground = null; + string? background = null; + bool italic = false; + bool bold = false; + + if (Styles.Contains(scope.Name)) + { + Styling.Style style = Styles[scope.Name]; + + foreground = style.Foreground; + background = style.Background; + italic = style.Italic; + bold = style.Bold; + } + + if (!string.IsNullOrWhiteSpace(foreground)) + run.Foreground = foreground.GetSolidColorBrush(); + + //Background isn't supported, but a workaround could be created. + + if (italic) + run.FontStyle = FontStyles.Italic; + + if (bold) + run.FontWeight = FontWeights.Bold; + } + + private void GetStyleInsertionsForCapturedStyle(Scope scope, ICollection styleInsertions) + { + styleInsertions.Add(new TextInsertion + { + Index = scope.Index, + Scope = scope + }); + + foreach (Scope childScope in scope.Children) + GetStyleInsertionsForCapturedStyle(childScope, styleInsertions); + + styleInsertions.Add(new TextInsertion + { + Index = scope.Index + scope.Length + }); + } + } +} \ No newline at end of file diff --git a/WPFUI.Test/ChatFunction/RoleToDisplayConverter.cs b/WPFUI.Test/ChatFunction/RoleToDisplayConverter.cs new file mode 100644 index 0000000..e41b488 --- /dev/null +++ b/WPFUI.Test/ChatFunction/RoleToDisplayConverter.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Data; +using System.Windows.Media; + +using LangChain.Providers; + +namespace WPFUI.Test +{ + internal class RoleToDisplayConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + + if (value is MessageRole role) + { + switch (role) + { + case MessageRole.System: + break; + case MessageRole.Human: + if (targetType == typeof(HorizontalAlignment)) + { + return HorizontalAlignment.Right; + + } + else if (targetType == typeof(Brush)) + { + var UserMessgeBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#785dc8")); + return UserMessgeBrush; + } + else if (targetType == typeof(Visibility)) + { + return Visibility.Collapsed; + } + break; + case MessageRole.Ai: + if (targetType == typeof(HorizontalAlignment)) + { + return HorizontalAlignment.Left; + } + else if (targetType == typeof(Brush)) + { + var ModelMessageBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#292E33")); + return ModelMessageBrush; + } + else if (targetType == typeof(Visibility)) + { + return Visibility.Visible; + } + break; + case MessageRole.Chat: + break; + case MessageRole.ToolCall: + break; + case MessageRole.ToolResult: + break; + default: + break; + } + + + } + return Binding.DoNothing; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/WPFUI.Test/GlobalUsings.cs b/WPFUI.Test/GlobalUsings.cs new file mode 100644 index 0000000..46b2b6b --- /dev/null +++ b/WPFUI.Test/GlobalUsings.cs @@ -0,0 +1,5 @@ +// global using 指令 + +global using System; +global using System.Collections.Generic; +global using System.IO; diff --git a/WPFUI.Test/GlobalVariables.cs b/WPFUI.Test/GlobalVariables.cs new file mode 100644 index 0000000..01b0c94 --- /dev/null +++ b/WPFUI.Test/GlobalVariables.cs @@ -0,0 +1,16 @@ + +using System.Reflection; + +using WPFUI.Test.Web; + + +namespace WPFUI.Test; + +public class GlobalVariables +{ + public static string AddInPath = Assembly.GetExecutingAssembly().Location; + public static string DirAssembly = Path.GetDirectoryName(AddInPath); + + public static AuthenticationService AuthenticationService { get; } = new AuthenticationService(); + public static bool IsLogin { get; set; } = false; +} diff --git a/WPFUI.Test/MainWindow.xaml b/WPFUI.Test/MainWindow.xaml new file mode 100644 index 0000000..fa6b41b --- /dev/null +++ b/WPFUI.Test/MainWindow.xaml @@ -0,0 +1,19 @@ + + + + + +