diff --git a/ToolsForm/.idea/.idea.ToolsForm/.idea/workspace.xml b/ToolsForm/.idea/.idea.ToolsForm/.idea/workspace.xml index cfdca45..e8de0fe 100644 --- a/ToolsForm/.idea/.idea.ToolsForm/.idea/workspace.xml +++ b/ToolsForm/.idea/.idea.ToolsForm/.idea/workspace.xml @@ -8,20 +8,17 @@ + - - - - - + @@ -41,6 +38,7 @@ + @@ -51,10 +49,14 @@ + + + { "customColor": "", "associatedIndex": 1 @@ -67,32 +69,32 @@ - { - "keyToString": { - ".NET 项目.ToolsForm.executor": "Run", - "ASKED_ADD_EXTERNAL_FILES": "true", - "RunOnceActivity.ShowReadmeOnStart": "true", - "XThreadsFramesViewSplitterKey": "0.5078125", - "git-widget-placeholder": "main", - "node.js.detected.package.eslint": "true", - "node.js.detected.package.tslint": "true", - "node.js.selected.package.eslint": "(autodetect)", - "node.js.selected.package.tslint": "(autodetect)", - "nodejs_package_manager_path": "npm", - "settings.editor.selected.configurable": "preferences.pluginManager", - "vue.rearranger.settings.migration": "true", - "发布到文件夹.Publish ToolsForm to folder (1).executor": "Run", - "发布到文件夹.Publish ToolsForm to folder (2).executor": "Run", - "发布到文件夹.Publish ToolsForm to folder.executor": "Run" + +}]]> @@ -147,7 +149,7 @@ - + @@ -225,32 +227,6 @@ diff --git a/ToolsForm/ToolsForm/Manager/SparkWebSDK.cs b/ToolsForm/ToolsForm/Manager/SparkWebSDK.cs new file mode 100644 index 0000000..7e61f95 --- /dev/null +++ b/ToolsForm/ToolsForm/Manager/SparkWebSDK.cs @@ -0,0 +1,323 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net.WebSockets; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.Messaging.Messages; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace ToolsForm.Models; + +public class Header +{ + /// + /// + /// + public int code { get; set; } + + /// + /// + /// + public string message { get; set; } + + /// + /// + /// + public string sid { get; set; } + + /// + /// + /// + public int status { get; set; } +} + +public class TextItem +{ + /// + /// 我可以帮助你的吗? + /// + public string content { get; set; } + + /// + /// + /// + public string role { get; set; } + + /// + /// + /// + public int index { get; set; } +} + +public class Choices +{ + /// + /// + /// + public int status { get; set; } + + /// + /// + /// + public int seq { get; set; } + + /// + /// + /// + public List text { get; set; } +} + +public class Text +{ + /// + /// + /// + public int question_tokens { get; set; } + + /// + /// + /// + public int prompt_tokens { get; set; } + + /// + /// + /// + public int completion_tokens { get; set; } + + /// + /// + /// + public int total_tokens { get; set; } +} + +public class Usage +{ + /// + /// + /// + public Text text { get; set; } +} + +public class Payload +{ + /// + /// + /// + public Choices choices { get; set; } + + /// + /// + /// + public Usage usage { get; set; } +} + +public class ResponseDatas +{ + /// + /// + /// + public Header header { get; set; } + + /// + /// + /// + public Payload payload { get; set; } +} + +public class SparkWebSDK +{ + string wsChatUrl = "ws://spark-api.xf-yun.com/v1.1/chat"; + string chat_appid = "a83ddb30"; + string chat_key = "df2f1b36593607691d184c2282977959"; + string chat_secret = "ZDUwMjczZjM2YTI2YTVhZDU2MjljZDI4"; + string clientId = System.Guid.NewGuid().ToString("N"); + CancellationToken cancellation; + + + private ClientWebSocket _webSocketClient; + + private string GetAuthUrl() + { + //当前时间戳,RFC1123格式 + string date = DateTime.UtcNow.ToString("r"); + //string date = "Thu, 15 Jun 2023 02:32:41 GMT"; + + string host = wsChatUrl.Split("://")[1].Split('/')[0]; + string localPath = wsChatUrl.Split(host)[1]; + + string str = "host: " + host + "\n" + "date: " + date + "\n" + "GET " + localPath + " HTTP/1.1"; + + //hmac-sha256计算签名 + string sha = HMACsha256(chat_secret, str); + //授权(api_key,授权算法,头部,签名) + //authorization格式:api_key="$api_key",algorithm="hmac-sha256",headers="host date request-line",signature="$signature" + string authorization = string.Format("api_key=\"{0}\", algorithm=\"{1}\", headers=\"{2}\", signature=\"{3}\"", chat_key, "hmac-sha256", "host date request-line", sha); + //System.Web.HttpUtility.UrlEncode + + //鉴权参数(host,data,authorization): + //authorization使用base64编码的签名相关信息(签名基于hmac-sha256计算) + string path1 = "authorization" + "=" + Convert.ToBase64String(Encoding.UTF8.GetBytes(authorization)); + date = date.Replace(" ", "%20").Replace(":", "%3A").Replace(",", "%2C"); + string path2 = "date" + "=" + date; + string path3 = "host" + "=" + host; + + string newurl = wsChatUrl + "?" + path1 + "&" + path2 + "&" + path3; + return newurl; + } + + private string HMACsha256(string apiSecretIsKey, string buider) + { + byte[] bytes = Encoding.UTF8.GetBytes(apiSecretIsKey); + System.Security.Cryptography.HMACSHA256 hMACSHA256 = new System.Security.Cryptography.HMACSHA256(bytes); + byte[] date = Encoding.UTF8.GetBytes(buider); + date = hMACSHA256.ComputeHash(date); + hMACSHA256.Clear(); + + return Convert.ToBase64String(date); + } + + // 接收消息方法,改为异步 +async Task ReciceChatAsync(ClientWebSocket ws, CancellationToken cancellationToken) +{ + string msgRecive = ""; + // 全部消息容器 + List bs = new List(); + // 缓冲区 + var buffer = new byte[1024 * 4]; + + while (ws.State == WebSocketState.Open) + { + // 等待接收消息 + var result = await ws.ReceiveAsync(new ArraySegment(buffer), cancellationToken); + + // 如果关闭了连接 + if (result.CloseStatus.HasValue) + { + Console.WriteLine("接收关闭"); + break; + } + + if (result.MessageType == WebSocketMessageType.Text) + { + bs.AddRange(buffer.Take(result.Count)); + + // 判断消息是否已接收完全 + if (result.EndOfMessage) + { + // 发送过来的消息 + string userMsg = Encoding.UTF8.GetString(bs.ToArray(), 0, bs.Count); + bs = new List(); + + JObject jb = JObject.Parse(userMsg); + int code = jb["header"]["code"].ToObject(); + string sid = jb["header"]["sid"].ToString(); + int status = jb["header"]["status"].ToObject(); + string message = jb["header"]["message"].ToString(); + + if (code == 0) + { + // 正常消息处理 + JToken[] ja = jb["payload"]["choices"]["text"].ToArray(); + foreach (var item in ja) + { + msgRecive += item["content"].ToString(); + } + + if (status == 2) + { + // 完成 + Console.WriteLine("最后一条"); + break; + } + } + else + { + // 出错 + Console.WriteLine(message); + break; + } + } + } + } + + Console.WriteLine("退出接收"); + return msgRecive; +} + +// 异步调用 API 方法 + public async Task CallapiForChatAsync(JArray text, CancellationToken cancellationToken) + { + string response = null; + + string url = GetAuthUrl(); + using (var ws = new ClientWebSocket()) + { + await ws.ConnectAsync(new Uri(url), cancellationToken); + + if (ws.State == WebSocketState.Open) + { + // 发送消息 + JObject data = new JObject(); + JObject header = new JObject(); + JObject parameter = new JObject(); + JObject payload = new JObject(); + data.Add("header", header); + data.Add("parameter", parameter); + data.Add("payload", payload); + header.Add("app_id", chat_appid); + header.Add("uid", clientId); + JObject chat = new JObject(); + parameter.Add("chat", chat); + chat.Add("domain", "general"); + chat.Add("max_tokens", 2048); + chat.Add("auditing", "default"); + + JObject message = new JObject(); + payload.Add("message", message); + message.Add("text", text); + + string da = data.ToString(); + var frameData = Encoding.UTF8.GetBytes(da); + + // 发送数据 + await ws.SendAsync(new ArraySegment(frameData), WebSocketMessageType.Text, true, cancellationToken); + + // 接收响应 + string msgRecive = await ReciceChatAsync(ws, cancellationToken); + response = msgRecive; + } + else + { + response = "连接失败"; + } + } + + return response; + } + + + + async Task ReciceChatAsync(ClientWebSocket ws) + { + var buffer = new byte[1024]; + var timeoutTask = Task.Delay(2000); // 超过2秒就超时 + var receiveTask = ws.ReceiveAsync(new ArraySegment(buffer), cancellation); + + // 等待接收数据或超时,先完成的任务会继续 + var completedTask = await Task.WhenAny(receiveTask, timeoutTask); + + if (completedTask == timeoutTask) + { + Console.WriteLine("接收超时"); + return null; // 或者处理超时逻辑 + } + + var result = await receiveTask; // 确保完成后再处理接收到的数据 + return Encoding.UTF8.GetString(buffer, 0, result.Count); + } +} \ No newline at end of file diff --git a/ToolsForm/ToolsForm/Views/AIWindow.axaml.cs b/ToolsForm/ToolsForm/Views/AIWindow.axaml.cs index 42c3441..ba78a27 100644 --- a/ToolsForm/ToolsForm/Views/AIWindow.axaml.cs +++ b/ToolsForm/ToolsForm/Views/AIWindow.axaml.cs @@ -1,67 +1,87 @@ - using System.Linq; - using Avalonia.Controls; - using Avalonia.Input; - using Avalonia.Interactivity; - using Avalonia.Markup.Xaml; - using ToolsForm.Models; // 引入模型命名空间 +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Markup.Xaml; +using Newtonsoft.Json.Linq; +using ToolsForm.Models; // 引入模型命名空间 - namespace ToolsForm.Views +namespace ToolsForm.Views +{ + public partial class AIWindow : Window { - public partial class AIWindow : Window + private DialogueModel dialogueModel; + private TextBox inputTextBox; + private ItemsControl dialogueItems; + private ScrollViewer dialogueScrollViewer; + + private SparkWebSDK _sparkWebSdk; + + public AIWindow() { - private DialogueModel dialogueModel; - private TextBox inputTextBox; - private ItemsControl dialogueItems; - private ScrollViewer dialogueScrollViewer; - public AIWindow() - { - InitializeComponent(); - // 初始化控件 - inputTextBox = this.FindControl("InputTextBox"); - dialogueItems = this.FindControl("DialogueItems"); - dialogueScrollViewer= this.FindControl("DialogueScrollViewer"); - dialogueModel = new DialogueModel(); // 创建模型实例 - DataContext = dialogueModel; // 将数据上下文设置为模型 - } - private void OnInputTextBoxKeyDown(object sender, KeyEventArgs e) - { - if (e.Key == Key.Enter) // 检查是否按下 Enter 键 - { - OnSendButtonClick(sender, e); // 调用发送按钮的点击事件处理程序 - e.Handled = true; // 标记事件已处理,防止进一步处理 - } - } - private void OnSendButtonClick(object sender, RoutedEventArgs e) - { - var userMessage = inputTextBox.Text; - if (!string.IsNullOrWhiteSpace(userMessage)) - { - dialogueModel.AddUserMessage(userMessage); - inputTextBox.Clear(); + InitializeComponent(); + // 初始化控件 + inputTextBox = this.FindControl("InputTextBox"); + dialogueItems = this.FindControl("DialogueItems"); + dialogueScrollViewer = this.FindControl("DialogueScrollViewer"); + dialogueModel = new DialogueModel(); // 创建模型实例 + DataContext = dialogueModel; // 将数据上下文设置为模型 - // 发送 AI 回复 - var aiResponse = GenerateAIResponse(userMessage); - dialogueModel.AddAIResponse(aiResponse); - // // 自动滚动到最新消息 - // dialogueItems.ScrollIntoView(dialogueModel.Dialogues.Last()); - } - + _sparkWebSdk = new SparkWebSDK(); + } + + private void OnInputTextBoxKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Enter) // 检查是否按下 Enter 键 + { + OnSendButtonClick(sender, e); // 调用发送按钮的点击事件处理程序 + e.Handled = true; // 标记事件已处理,防止进一步处理 + } + } + + private async void OnSendButtonClick(object sender, RoutedEventArgs e) + { + var userMessage = inputTextBox.Text; + if (!string.IsNullOrWhiteSpace(userMessage)) + { + dialogueModel.AddUserMessage(userMessage); + inputTextBox.Clear(); + + // 发送 AI 回复并等待结果 + var aiResponse = await GenerateAIResponse(userMessage); + dialogueModel.AddAIResponse(aiResponse); + + // 自动滚动到最新消息 if (dialogueScrollViewer != null) { dialogueScrollViewer.ScrollToEnd(); } } - - private string GenerateAIResponse(string message) - { - // TODO: 这里实现AI生成回复的逻辑 - return "这是AI的回复"; // 示例回复 - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } } - } \ No newline at end of file + + + private async Task GenerateAIResponse(string message) + { + string mess = "没有数据"; + + JArray text = new JArray(); + JObject jb = new JObject(); + jb.Add("role", "user"); + jb.Add("content", message); + text.Add(jb); + + // 直接等待 CallapiForChatAsync 返回结果 + mess = await _sparkWebSdk.CallapiForChatAsync(text, CancellationToken.None); + + return mess; // 返回 AI 回复 + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} \ No newline at end of file diff --git a/ToolsForm/ToolsForm/bin/Debug/net8.0/ToolsForm.dll b/ToolsForm/ToolsForm/bin/Debug/net8.0/ToolsForm.dll index 23548ed..b5240a6 100644 Binary files a/ToolsForm/ToolsForm/bin/Debug/net8.0/ToolsForm.dll and b/ToolsForm/ToolsForm/bin/Debug/net8.0/ToolsForm.dll differ diff --git a/ToolsForm/ToolsForm/bin/Debug/net8.0/ToolsForm.exe b/ToolsForm/ToolsForm/bin/Debug/net8.0/ToolsForm.exe index de613dc..01e8079 100644 Binary files a/ToolsForm/ToolsForm/bin/Debug/net8.0/ToolsForm.exe and b/ToolsForm/ToolsForm/bin/Debug/net8.0/ToolsForm.exe differ diff --git a/ToolsForm/ToolsForm/bin/Debug/net8.0/ToolsForm.pdb b/ToolsForm/ToolsForm/bin/Debug/net8.0/ToolsForm.pdb index 682a60d..41aa757 100644 Binary files a/ToolsForm/ToolsForm/bin/Debug/net8.0/ToolsForm.pdb and b/ToolsForm/ToolsForm/bin/Debug/net8.0/ToolsForm.pdb differ diff --git a/ToolsForm/ToolsForm/obj/Debug/net8.0/Avalonia/ToolsForm.dll b/ToolsForm/ToolsForm/obj/Debug/net8.0/Avalonia/ToolsForm.dll index 23548ed..b5240a6 100644 Binary files a/ToolsForm/ToolsForm/obj/Debug/net8.0/Avalonia/ToolsForm.dll and b/ToolsForm/ToolsForm/obj/Debug/net8.0/Avalonia/ToolsForm.dll differ diff --git a/ToolsForm/ToolsForm/obj/Debug/net8.0/Avalonia/ToolsForm.pdb b/ToolsForm/ToolsForm/obj/Debug/net8.0/Avalonia/ToolsForm.pdb index 682a60d..41aa757 100644 Binary files a/ToolsForm/ToolsForm/obj/Debug/net8.0/Avalonia/ToolsForm.pdb and b/ToolsForm/ToolsForm/obj/Debug/net8.0/Avalonia/ToolsForm.pdb differ diff --git a/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.AssemblyInfo.cs b/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.AssemblyInfo.cs index 85d4eba..880a2ce 100644 --- a/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.AssemblyInfo.cs +++ b/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.AssemblyInfo.cs @@ -13,7 +13,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("ToolsForm")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+b5da1a7f7c82dcd20f0b21f742568b806e389d75")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+67f36bcc4da43ad28c0edee61033c6417823236c")] [assembly: System.Reflection.AssemblyProductAttribute("ToolsForm")] [assembly: System.Reflection.AssemblyTitleAttribute("ToolsForm")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.AssemblyInfoInputs.cache b/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.AssemblyInfoInputs.cache index 5cc89f5..4744bc5 100644 --- a/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.AssemblyInfoInputs.cache +++ b/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.AssemblyInfoInputs.cache @@ -1 +1 @@ -7b96760f001b2471081ba691dfa0e836b351bee82672791a362c10b30535dedd +b6d965a55222d82d427c73ab54d64fca1a5f960cb20160d77994e28bd488268d diff --git a/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.csproj.CoreCompileInputs.cache b/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.csproj.CoreCompileInputs.cache index 0d2f93a..bf4e9a8 100644 --- a/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.csproj.CoreCompileInputs.cache +++ b/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -bf3fd6b20c8cdf001539f535b65b0d319f4b4f7c5c43d09955e03b2516849197 +9fb7436273386345ad7568271cc196a11cf18d5aadf391ba706c7b48ebe9b994 diff --git a/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.dll b/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.dll index 989b914..1d49f68 100644 Binary files a/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.dll and b/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.dll differ diff --git a/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.pdb b/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.pdb index 813a6f2..0e5bc56 100644 Binary files a/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.pdb and b/ToolsForm/ToolsForm/obj/Debug/net8.0/ToolsForm.pdb differ diff --git a/ToolsForm/ToolsForm/obj/Debug/net8.0/apphost.exe b/ToolsForm/ToolsForm/obj/Debug/net8.0/apphost.exe index de613dc..01e8079 100644 Binary files a/ToolsForm/ToolsForm/obj/Debug/net8.0/apphost.exe and b/ToolsForm/ToolsForm/obj/Debug/net8.0/apphost.exe differ diff --git a/ToolsForm/ToolsForm/obj/Debug/net8.0/ref/ToolsForm.dll b/ToolsForm/ToolsForm/obj/Debug/net8.0/ref/ToolsForm.dll index 2f79b94..d950481 100644 Binary files a/ToolsForm/ToolsForm/obj/Debug/net8.0/ref/ToolsForm.dll and b/ToolsForm/ToolsForm/obj/Debug/net8.0/ref/ToolsForm.dll differ diff --git a/ToolsForm/ToolsForm/obj/Debug/net8.0/refint/Avalonia/ToolsForm.dll b/ToolsForm/ToolsForm/obj/Debug/net8.0/refint/Avalonia/ToolsForm.dll index 2f79b94..d950481 100644 Binary files a/ToolsForm/ToolsForm/obj/Debug/net8.0/refint/Avalonia/ToolsForm.dll and b/ToolsForm/ToolsForm/obj/Debug/net8.0/refint/Avalonia/ToolsForm.dll differ diff --git a/ToolsForm/ToolsForm/obj/Debug/net8.0/refint/ToolsForm.dll b/ToolsForm/ToolsForm/obj/Debug/net8.0/refint/ToolsForm.dll index aeb950d..8d781f7 100644 Binary files a/ToolsForm/ToolsForm/obj/Debug/net8.0/refint/ToolsForm.dll and b/ToolsForm/ToolsForm/obj/Debug/net8.0/refint/ToolsForm.dll differ