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
}