using System.Net.WebSockets; using System.Text; using XFEExtension.NetCore.Exceptions; using XFEExtension.NetCore.XFEChatGPT.ChatGPTInnerClass.DefaultClass; using XFEExtension.NetCore.XFEChatGPT.ChatGPTInnerClass.HelperClass; using XFEExtension.NetCore.XFEChatGPT.OtherInnerClass; using XFEExtension.NetCore.XFETransform; using XFEExtension.NetCore.XFETransform.Json; namespace XFEExtension.NetCore.XFEChatGPT; /// /// 基于gpt.api.xfegzs.com开放接口的ChatGPT问答类(记忆模式) /// public class MemorableXFEChatGPT : XFEChatGPTBase { private readonly XFEGPTMemoryDialog _xFEGPTMemoryDialog; /// /// 获取或设置对话记录 /// public XFEGPTMemoryDialog MemoryDialog { get { return _xFEGPTMemoryDialog; } } #region 公共方法 #region 创建对话 /// /// 创建一个新的对话,使用默认通信协议(快速响应通讯协议) /// /// 对话的ID /// 系统消息 public void CreateDialog(string dialogId, string system) { _xFEGPTMemoryDialog.Add(dialogId, new XFEGPTMessageCollection([new XFEGPTMessage(dialogId, new GPTMessage("system", system))], false, ChatGPTModel.gpt3point5turbo, XFEComProtocol.XFEFAST, false, 0.7)); } /// /// 创建一个新的对话,使用默认通信协议(快速响应通讯协议) /// /// 对话的ID /// 系统消息 /// 是否记录对话信息 public void CreateDialog(string dialogId, string system, bool hasMemory) { _xFEGPTMemoryDialog.Add(dialogId, new XFEGPTMessageCollection([new XFEGPTMessage(dialogId, new GPTMessage("system", system))], false, ChatGPTModel.gpt3point5turbo, XFEComProtocol.XFEFAST, hasMemory, 0.7)); } /// /// 创建一个新的对话,使用默认通信协议(快速响应通讯协议) /// /// 对话的ID /// 系统消息 /// 是否记录对话信息 /// 是否为流式输出 public void CreateDialog(string dialogId, string system, bool hasMemory, bool streamMode) { _xFEGPTMemoryDialog.Add(dialogId, new XFEGPTMessageCollection([new XFEGPTMessage(dialogId, new GPTMessage("system", system))], hasMemory, ChatGPTModel.gpt3point5turbo, XFEComProtocol.XFEFAST, streamMode, 0.7)); } /// /// 创建一个新的对话,使用默认通信协议(快速响应通讯协议) /// /// 对话的ID /// 系统消息 /// 是否记录对话信息 /// 是否为流式输出 /// 所用的ChatGPT模型 public void CreateDialog(string dialogId, string system, bool hasMemory, bool streamMode, ChatGPTModel chatGPTModel) { _xFEGPTMemoryDialog.Add(dialogId, new XFEGPTMessageCollection([new XFEGPTMessage(dialogId, new GPTMessage("system", system))], hasMemory, chatGPTModel, XFEComProtocol.XFEFAST, streamMode, 0.7)); } /// /// 创建一个新的对话,使用默认通信协议(快速响应通讯协议) /// /// 对话的ID /// 系统消息 /// 是否记录对话信息 /// 是否为流式输出 /// 所用的ChatGPT模型 /// 系统的Temperature public void CreateDialog(string dialogId, string system, bool hasMemory, bool streamMode, ChatGPTModel chatGPTModel, double temperature) { _xFEGPTMemoryDialog.Add(dialogId, new XFEGPTMessageCollection([new XFEGPTMessage(dialogId, new GPTMessage("system", system))], hasMemory, chatGPTModel, XFEComProtocol.XFEFAST, streamMode, temperature)); } /// /// 创建一个新的对话,自定义通讯协议 /// /// 对话的ID /// 系统消息 /// 是否记录对话信息 /// 是否为流式输出 /// 所用的ChatGPT模型 /// 通信协议 public void CreateDialog(string dialogId, string system, bool hasMemory, bool streamMode, ChatGPTModel chatGPTModel, XFEComProtocol comProtocol) { _xFEGPTMemoryDialog.Add(dialogId, new XFEGPTMessageCollection([new XFEGPTMessage(dialogId, new GPTMessage("system", system))], hasMemory, chatGPTModel, comProtocol, streamMode, 0.7)); } /// /// 创建一个新的对话,自定义通讯协议 /// /// 对话的ID /// 系统消息 /// 是否记录对话信息 /// 是否为流式输出 /// 所用的ChatGPT模型 /// 系统的Temperature /// 通信协议 public void CreateDialog(string dialogId, string system, bool hasMemory, bool streamMode, ChatGPTModel chatGPTModel, double temperature, XFEComProtocol comProtocol) { _xFEGPTMemoryDialog.Add(dialogId, new XFEGPTMessageCollection([new XFEGPTMessage(dialogId, new GPTMessage("system", system))], hasMemory, chatGPTModel, comProtocol, streamMode, temperature)); } #endregion #region 插入对话 /// /// 插入对话 /// /// 要插入的对话ID /// 用户对话内容 /// AI对话的内容 /// 插入的消息ID public string InsertDialog(string dialogId, string userMessage, string assistantMessage) { var messageId = Guid.NewGuid().ToString(); _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId, new GPTMessage("user", userMessage))); _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId + "_Assistant", new GPTMessage("assistant", assistantMessage))); return messageId; } /// /// 插入对话 /// /// 要插入的对话ID /// 插入的消息ID /// 用户对话内容 /// AI对话的内容 public void InsertDialog(string dialogId, string messageId, string userMessage, string assistantMessage) { _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId, new GPTMessage("user", userMessage))); _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId + "_Assistant", new GPTMessage("assistant", assistantMessage))); } /// /// 插入对话 /// /// 要插入的对话ID /// 对话,以二维数组的形式 /// public string[] InsertDialog(string dialogId, string[,] dialogMessage) { try { if (dialogMessage.GetLength(1) != 2) { throw new XFEChatGPTException("dialogMessage的格式不正确!应为string[任意数量,2]"); } if (dialogMessage.Length % 2 == 1) { throw new XFEChatGPTException("dialogMessage不合法!只能成对添加user和assistant"); } var messageId = new string[dialogMessage.Length]; for (var i = 0; i < dialogMessage.Length; i += 2) { messageId[i] = Guid.NewGuid().ToString(); messageId[i + 1] = messageId[i] + "_Assistant"; _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId[i], new GPTMessage("user", dialogMessage[i == 0 ? 0 : i / 2, 0]))); _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId[i + 1], new GPTMessage("assistant", dialogMessage[i == 0 ? 0 : i / 2, 1]))); } return messageId; } catch (IndexOutOfRangeException ex) { throw new XFEChatGPTException("dialogMessage的格式不正确!应为string[任意数量,2]", ex); } } /// /// 插入对话 /// /// 要插入的对话ID /// 对话 /// /// public string[] InsertDialog(string dialogId, params string[] dialogMessage) { try { if (dialogMessage.Length % 2 == 1) { throw new XFEChatGPTException("dialogMessage不合法!只能成对添加user和assistant"); } var messageId = new string[dialogMessage.Length]; for (var i = 0; i < dialogMessage.Length; i += 2) { messageId[i] = Guid.NewGuid().ToString(); messageId[i + 1] = messageId[i] + "_Assistant"; _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId[i], new GPTMessage("user", dialogMessage[i]))); _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId[i + 1], new GPTMessage("assistant", dialogMessage[i + 1]))); } return messageId; } catch (IndexOutOfRangeException ex) { throw new XFEChatGPTException("dialogMessage的格式不正确!应为string[任意数量,2]", ex); } } /// /// 插入对话 /// /// 要插入的对话ID /// 对话的消息ID,以数组的形式 /// 对话,以二维数组的形式 /// public void InsertDialog(string dialogId, string[] messageId, string[,] dialogMessage) { try { if (messageId.Length != dialogMessage.Length) { throw new XFEChatGPTException("messageId与dialogMessage数量不对应"); } if (dialogMessage.GetLength(1) != 2) { throw new XFEChatGPTException("dialogMessage的格式不正确!应为string[任意数量,2]"); } if (dialogMessage.Length % 2 == 1) { throw new XFEChatGPTException("dialogMessage不合法!只能成对添加user和assistant"); } for (var i = 0; i < dialogMessage.Length; i += 2) { _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId[i], new GPTMessage("user", dialogMessage[i == 0 ? 0 : i / 2, 0]))); _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId[i + 1], new GPTMessage("assistant", dialogMessage[i == 0 ? 0 : i / 2, 1]))); } } catch (IndexOutOfRangeException ex) { throw new Exception("dialogMessage的格式不正确!应为string[任意数量,2]", ex); } } /// /// 插入对话 /// /// 要插入的对话ID /// 对话的消息ID,以数组的形式 /// 对话,以一维数组的形式 /// public void InsertDialog(string dialogId, string[] messageId, string[] dialogMessage) { try { if (messageId.Length != dialogMessage.Length) { throw new XFEChatGPTException("messageId与dialogMessage数量不对应"); } if (dialogMessage.Length % 2 == 1) { throw new XFEChatGPTException("dialogMessage不合法!只能成对添加user和assistant"); } for (var i = 0; i < dialogMessage.Length; i += 2) { _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId[i], new GPTMessage("user", dialogMessage[i]))); _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId[i + 1], new GPTMessage("assistant", dialogMessage[i + 1]))); } } catch (IndexOutOfRangeException ex) { throw new XFEChatGPTException("dialogMessage的格式不正确!应为string[任意数量,2]", ex); } } /// /// 插入对话,自动补全对话 /// /// 要插入的对话ID /// 对话 /// /// public string[] InsertDialogAutoComplete(string dialogId, params string[] dialogMessage) { try { var messageId = dialogMessage.Length % 2 == 0 ? new string[dialogMessage.Length] : new string[dialogMessage.Length + 1]; for (var i = 0; i < dialogMessage.Length; i += 2) { messageId[i] = Guid.NewGuid().ToString(); messageId[i + 1] = messageId[i] + "_Assistant"; _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId[i], new GPTMessage("user", dialogMessage[i]))); _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId[i + 1], new GPTMessage("assistant", dialogMessage.Length > i + 1 ? dialogMessage[i + 1] : "该条问题未回复"))); } return messageId; } catch (IndexOutOfRangeException ex) { throw new XFEChatGPTException("dialogMessage的格式不正确!", ex); } } #endregion #region 其余操作 /// /// 获取对话 /// /// 对话ID /// public GPTMessage[] GetDialog(string dialogId) { return _xFEGPTMemoryDialog[dialogId].GetGPTMessages(); } /// /// 以字符串数组的形式获取对话 /// /// /// public string[] GetDialogStrings(string dialogId) { return _xFEGPTMemoryDialog[dialogId].GetGPTMessageStrings(); } #endregion #region 开始对话 /// /// 开始对话 /// /// 对话ID /// 本消息ID /// 询问的内容 public void AskChatGPT(string dialogId, string messageId, string askMessage) { if (_xFEGPTMemoryDialog[dialogId].GetGPTMessages().CheckLegal()) { //判断如果最后一条消息是用户消息,则在添加新的用户消息前添加一条助手消息然后再添加用户消息,否则直接添加用户消息 if (_xFEGPTMemoryDialog[dialogId].GetLastRole() == "user") { _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId + "_Assistant", new GPTMessage("assistant", "正在思考中..."))); _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId, new GPTMessage("user", askMessage))); } else { _xFEGPTMemoryDialog[dialogId].Add(new XFEGPTMessage(messageId, new GPTMessage("user", askMessage))); } var nowThread = new Thread(StartGetGPTMessage); nowThread.Start(new MessageIdDialogIdAndThread(messageId, dialogId, nowThread)); } else { throw new XFEChatGPTException("对话信息不合法"); } } #endregion #region 停止对话 //TODO: /// /// 停止对话(仅在流式输出模式下有效,非流式输出模式下调用该方法将不会有任何效果) /// public static void StopChat() { } #endregion #endregion #region 线程方法 private async void StartGetGPTMessage(object? sender) { var messageIdAndThread = (MessageIdDialogIdAndThread)sender!; var dialogId = messageIdAndThread.DialogId; var messageId = messageIdAndThread.MessageId; var thread = messageIdAndThread.Thread; var nowDialog = _xFEGPTMemoryDialog[dialogId]; if (nowDialog.StreamMode) { var isStarted = false; try { #region 进行HTTP请求 ClientWebSocket webSocket = new(); await webSocket.ConnectAsync(new Uri("ws://gpt.api.xfegzs.com/"), CancellationToken.None); if (nowDialog.MemorableMode) { var json = XFEJson.Serialize(nowDialog.CreateAskMessage()); if (json is not null) await webSocket.SendAsync(new ArraySegment(Encoding.UTF8.GetBytes(json)), WebSocketMessageType.Text, true, CancellationToken.None); } else { var json = XFEJson.Serialize(new XFEAskGPTMessage(false, nowDialog.StreamMode, nowDialog.ChatGPTModel.GetModelString(), null, nowDialog.XFEComProtocol, nowDialog.System, nowDialog[^1].GPTMessage.Content)); if (json is not null) await webSocket.SendAsync(new ArraySegment(Encoding.UTF8.GetBytes(json)), WebSocketMessageType.Text, true, CancellationToken.None); } #endregion while (true) { #region 读取消息 var receiveBuffer = new byte[1024]; var receiveResult = await webSocket.ReceiveAsync(new ArraySegment(receiveBuffer), CancellationToken.None); var bufferList = new List(); bufferList.AddRange(receiveBuffer.Take(receiveResult.Count)); while (!receiveResult.EndOfMessage) { receiveResult = await webSocket.ReceiveAsync(new ArraySegment(receiveBuffer), CancellationToken.None); bufferList.AddRange(receiveBuffer.Take(receiveResult.Count)); } var receivedBinaryBuffer = bufferList.ToArray(); var nowReceivedMessage = Encoding.UTF8.GetString(receivedBinaryBuffer); #endregion if (nowDialog.XFEComProtocol == XFEComProtocol.XFEFAST) { if (nowReceivedMessage == "[DONE]") { XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs("[XFEDONE]", messageId, GenerateState.End, dialogId)); break; } if (nowReceivedMessage.Contains("[XFERemoteAPIError]")) { if (!isStarted) { XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs("[XFESTART]", messageId, GenerateState.Start, dialogId)); _xFEGPTMemoryDialog.InstanceUpdate(dialogId, messageId + "_Assistant", "该回复发生了一个错误,可能需要重新回复", true); } else { _xFEGPTMemoryDialog.InstanceUpdate(dialogId, messageId + "_Assistant", "该回复发生了一个错误,可能需要重新回复", false); } XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs(nowReceivedMessage.Replace("[XFERemoteAPIError]", string.Empty), messageId, GenerateState.Error, dialogId)); } else if (nowReceivedMessage == "[XFE]") { XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs("[XFESTART]", messageId, GenerateState.Start, dialogId)); isStarted = true; _xFEGPTMemoryDialog.InstanceUpdate(dialogId, messageId + "_Assistant", string.Empty, true); } else { _xFEGPTMemoryDialog.InstanceUpdate(dialogId, messageId + "_Assistant", nowReceivedMessage, false); XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs(nowReceivedMessage, messageId, GenerateState.Continue, dialogId)); } } else { if (nowReceivedMessage == "[DONE]") { XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs("[XFEDONE]", messageId, GenerateState.End, dialogId)); break; } if (nowReceivedMessage.Contains("[XFERemoteAPIError]")) { if (!isStarted) { XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs("[XFESTART]", messageId, GenerateState.Start, dialogId)); _xFEGPTMemoryDialog.InstanceUpdate(dialogId, messageId + "_Assistant", "该回复发生了一个错误,可能需要重新回复", true); } else { _xFEGPTMemoryDialog.InstanceUpdate(dialogId, messageId + "_Assistant", "该回复发生了一个错误,可能需要重新回复", false); } XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs(nowReceivedMessage.Replace("[XFERemoteAPIError]", string.Empty), messageId, GenerateState.Error, dialogId)); } else if (nowReceivedMessage == "[XFE]") { XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs("[XFESTART]", messageId, GenerateState.Start, dialogId)); isStarted = true; _xFEGPTMemoryDialog.InstanceUpdate(dialogId, messageId + "_Assistant", string.Empty, true); } else { if (nowReceivedMessage.Contains("[XFE]")) { nowReceivedMessage = nowReceivedMessage.Replace("[XFE]", string.Empty); _xFEGPTMemoryDialog.InstanceUpdate(dialogId, messageId + "_Assistant", nowReceivedMessage, false); XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs(nowReceivedMessage, messageId, GenerateState.Continue, dialogId)); } else { if (!isStarted) { XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs("[XFESTART]", messageId, GenerateState.Start, dialogId)); _xFEGPTMemoryDialog.InstanceUpdate(dialogId, messageId + "_Assistant", "该回复发生了一个错误,可能需要重新回复", true); } else { _xFEGPTMemoryDialog.InstanceUpdate(dialogId, messageId + "_Assistant", "该回复发生了一个错误,可能需要重新回复", false); } } } } } await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Connection closed.", CancellationToken.None); } catch (Exception ex) when (ex is not XFEChatGPTException) { if (!isStarted) { XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs("[XFESTART]", messageId, GenerateState.Start, dialogId)); _xFEGPTMemoryDialog.InstanceUpdate(dialogId, messageId + "_Assistant", "该回复发生了一个错误,可能需要重新回复", true); } else { _xFEGPTMemoryDialog.InstanceUpdate(dialogId, messageId + "_Assistant", "该回复发生了一个错误,可能需要重新回复", false); } XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs(ex.ToString(), messageId, GenerateState.Error, dialogId)); Console.WriteLine(ex.ToString()); } } else { try { #region 进行HTTP请求 ClientWebSocket webSocket = new(); await webSocket.ConnectAsync(new Uri("ws://gpt.api.xfegzs.com/"), CancellationToken.None); if (nowDialog.MemorableMode) { var json = XFEJson.Serialize(nowDialog.CreateAskMessage()); if (json is not null) await webSocket.SendAsync(new ArraySegment(Encoding.UTF8.GetBytes(json)), WebSocketMessageType.Text, true, CancellationToken.None); } else { var json = XFEJson.Serialize(new XFEAskGPTMessage(false, nowDialog.StreamMode, nowDialog.ChatGPTModel.GetModelString(), null, nowDialog.XFEComProtocol, nowDialog.System, nowDialog[^1].GPTMessage.Content)); if (json is not null) await webSocket.SendAsync(new ArraySegment(Encoding.UTF8.GetBytes(json)), WebSocketMessageType.Text, true, CancellationToken.None); } #endregion #region 读取消息 var receiveBuffer = new byte[1024]; var receiveResult = await webSocket.ReceiveAsync(new ArraySegment(receiveBuffer), CancellationToken.None); var bufferList = new List(); bufferList.AddRange(receiveBuffer.Take(receiveResult.Count)); while (!receiveResult.EndOfMessage) { receiveResult = await webSocket.ReceiveAsync(new ArraySegment(receiveBuffer), CancellationToken.None); bufferList.AddRange(receiveBuffer.Take(receiveResult.Count)); } var receivedBinaryBuffer = bufferList.ToArray(); var nowReceivedMessage = Encoding.UTF8.GetString(receivedBinaryBuffer); #endregion if (nowReceivedMessage.Contains("[XFE]")) { nowReceivedMessage = nowReceivedMessage.Replace("[XFE]", string.Empty); _xFEGPTMemoryDialog.InstanceUpdate(dialogId, messageId + "_Assistant", nowReceivedMessage, true); XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs(nowReceivedMessage, messageId, GenerateState.Start, dialogId)); } else { _xFEGPTMemoryDialog.InstanceUpdate(dialogId, messageId + "_Assistant", nowReceivedMessage, true); XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs(nowReceivedMessage.Replace("[XFERemoteAPIError]", string.Empty), messageId, GenerateState.Error, dialogId)); } await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Connection closed.", CancellationToken.None); } catch (Exception ex) { _xFEGPTMemoryDialog.InstanceUpdate(dialogId, messageId + "_Assistant", ex.ToString(), true); XFEChatGPTMessageReceived?.Invoke(this, new PrivateMemorableGPTMessageReceivedEventArgs(ex.ToString(), messageId, GenerateState.Error, dialogId)); Console.WriteLine(ex.ToString()); } } ThreadList.Remove(thread); } #endregion #region 公有事件 /// /// 当收到ChatGPT的消息时触发 /// public event EventHandler? XFEChatGPTMessageReceived; #endregion #region 构造方法 /// /// 创建一个有记忆功能的XFEChatGPT对象 /// public MemorableXFEChatGPT() => _xFEGPTMemoryDialog = new PrivateXFEGPTMemoryDialog(); #endregion }