ict.shenzhi/Assets/Scripts/UI/MainScenesUI/Chat/IChatManager.cs

459 lines
14 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;
using Newtonsoft.Json.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using Newtonsoft.Json;
public class IChatManager : MonoBehaviour
{
public static IChatManager instance;
ClientWebSocket ws;
CancellationToken cancellation;
/// <summary>
/// chat
/// </summary>
const string wsChatUrl = "ws://spark-api.xf-yun.com/v1.1/chat";
const string chat_appid = "a83ddb30";//
const string chat_key = "df2f1b36593607691d184c2282977959";
const string chat_secret = "ZDUwMjczZjM2YTI2YTVhZDU2MjljZDI4";
/// <summary>
/// 用户唯一id
/// </summary>
string clientId = System.Guid.NewGuid().ToString("N");
private void Awake()
{
instance = this;
}
private void OnDestroy()
{
StopAllCoroutines();
if (ws != null && ws.State == WebSocketState.Open)
{
ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "", cancellation);
ws = null;
}
}
// Start is called before the first frame update
void Start()
{
}
IEnumerator CallapiForChat(JArray text, Action<string> callback)
{
Debug.Log("开始调用API");
MyDebuggers.Log("开始聊天");
string url = GetAuthUrl(wsChatUrl, chat_key, chat_secret);
using (ws = new ClientWebSocket())
{
//连接
ws.ConnectAsync(new Uri(url), cancellation).Wait(2000);
if (ws.State == WebSocketState.Open)
{
Task<string> re = Task.Run<string>(() => ReciceChat(ws));
//连接成功,开始发送数据
//请求数据均为json字符串
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);//最大tokebs长度
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);
//发送数据
ws.SendAsync(new ArraySegment<byte>(frameData), WebSocketMessageType.Text, true, cancellation);
yield return new WaitUntil(() => re.IsCompleted);
Debug.Log("完成");
callback(re.Result);
}
else
{
Debug.LogError("连接失败");
callback(null);
}
}
}
/// <summary>
/// 获取语音听写url
/// </summary>
/// <returns></returns>
private string GetAuthUrl(string whichUrl, string key, string secret)
{
//当前时间戳RFC1123格式
string date = DateTime.UtcNow.ToString("r");
//string date = "Thu, 15 Jun 2023 02:32:41 GMT";
string host = whichUrl.Split("://")[1].Split('/')[0];
string localPath = whichUrl.Split(host)[1];
string str = "host: " + host + "\n" + "date: " + date + "\n" + "GET " + localPath + " HTTP/1.1";
//hmac-sha256计算签名
string sha = HMACsha256(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}\"", 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 = whichUrl + "?" + path1 + "&" + path2 + "&" + path3;
return newurl;
}
/// <summary>
/// 星火大模型对话(联系上文)
/// </summary>
/// <param name="historyChat">依次历史对话,不要多</param>
/// <param name="question">问题</param>
/// <param name="callback"></param>
public void Chat(List<ChatInfo> historyChat, string question, Action<string> callback)
{
JArray text = new JArray();
for (int i = 0; i < historyChat.Count; i++)
{
JObject jb = new JObject();
jb.Add("role", "user");
jb.Add("content", historyChat[i].ask);
text.Add(jb);
JObject jb2 = new JObject();
jb2.Add("role", "assistant");
jb2.Add("content", historyChat[i].answer);
text.Add(jb2);
}
//问题
JObject jb111 = new JObject();
jb111.Add("role", "user");
jb111.Add("content", question);
text.Add(jb111);
StartCoroutine(CallapiForChat(text, callback));
}
/// <summary>
/// 星火大模型对话(不联系上文)
/// </summary>
/// <param name="question">问题</param>
/// <param name="callback"></param>
public void Chat(string question, Action<string> callback)
{
JArray text = new JArray();
JObject jb = new JObject();
jb.Add("role", "user");
jb.Add("content", question);
text.Add(jb);
StartCoroutine(CallapiForChat(text, callback));
}
string ReciveText(ClientWebSocket ws)
{
string msgRecive = "";
//全部消息容器
List<byte> bs = new List<byte>();
//缓冲区
var buffer = new byte[1024 * 4];
while (ws.State == WebSocketState.Open)
{
Debug.Log("等待消息接收");
var task = ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
task.Wait();
WebSocketReceiveResult result = task.Result;
//文本消息
if (result.CloseStatus.HasValue)
{
Debug.Log("接收关闭");
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);
var resultObj = GetTextResultData(userMsg);
string msg = resultObj.data.result.GetResultText();
//清空消息容器
bs = new List<byte>();
Debug.Log(msg);
msgRecive = msgRecive + msg;
if (resultObj.data.result.ls)
{
//关闭
Debug.Log("最后一块数据");
break;
}
}
}
}
ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "", cancellation);
Debug.Log("退出接收");
return msgRecive;
}
string ReciceChat(ClientWebSocket ws)
{
string msgRecive = "";
//全部消息容器
List<byte> bs = new List<byte>();
//缓冲区
var buffer = new byte[1024 * 4];
while (ws.State == WebSocketState.Open)
{
//Debug.Log("等待消息接收");
var task = ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
task.Wait();
WebSocketReceiveResult result = task.Result;
//文本消息
if (result.CloseStatus.HasValue)
{
Debug.Log("接收关闭");
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<byte>();
JObject jb = JObject.Parse(userMsg);
int code = jb["header"]["code"].ToObject<int>();
string sid = jb["header"]["sid"].ToString();
int status = jb["header"]["status"].ToObject<int>();
string message = jb["header"]["message"].ToString();
if (code == 0)
{
//正常
JToken[] ja = jb["payload"]["choices"]["text"].ToArray();
foreach (var item in ja)
{
msgRecive = msgRecive + item["content"].ToString();
//Debug.Log(item["content"].ToString());
}
if (status == 2)
{
//完成
Debug.Log("最后一条");
break;
}
}
else
{
//出错
Debug.LogError(message);
break;
}
}
}
}
Debug.Log("退出接收");
return msgRecive;
}
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);
}
/// <summary>
/// 从此实例检索子数组
/// </summary>
/// <param name="source">要检索的数组</param>
/// <param name="startIndex">起始索引号</param>
/// <param name="length">检索最大长度</param>
/// <returns>与此实例中在 startIndex 处开头、长度为 length 的子数组等效的一个数组</returns>
public static byte[] SubArray(byte[] source, int startIndex, int length)
{
if (startIndex < 0 || startIndex > source.Length || length < 0)
{
return null;
}
byte[] Destination;
if (startIndex + length <= source.Length)
{
Destination = new byte[length];
Array.Copy(source, startIndex, Destination, 0, length);
}
else
{
Destination = new byte[(source.Length - startIndex)];
Array.Copy(source, startIndex, Destination, 0, source.Length - startIndex);
}
return Destination;
}
/// <summary>
/// 解析文本
/// </summary>
/// <param name="ReceviceStr"></param>
/// <returns></returns>
private ResponseData<TextInfo> GetTextResultData(string ReceviceStr)
{
ResponseData<TextInfo> temp = new ResponseData<TextInfo>();
ReaponseDataInfo<TextInfo> dataInfo = new ReaponseDataInfo<TextInfo>();
TextInfo resultInfo = new TextInfo();
List<Ws> tempwsS;
List<Cw> tempcwS;
Ws tempWs;
Cw temocw;
var jsonObj = (JObject)JsonConvert.DeserializeObject(ReceviceStr);
temp.code = jsonObj["code"].ToObject<int>();
temp.message = jsonObj["message"].ToObject<string>();
temp.sid = jsonObj["sid"].ToObject<string>();
var data = jsonObj["data"]/*.ToObject<JObject>()*/;
dataInfo.status = data["status"].ToObject<int>();
var result = data["result"]/*.ToObject<JObject>()*/;
resultInfo.bg = result["bg"].ToObject<int>();
resultInfo.ed = result["ed"].ToObject<int>();
//resultInfo.pgs = result["pgs"].ToObject<string>();
//resultInfo.rg = result["rg"].ToObject<int[]>();
resultInfo.sn = result["sn"].ToObject<int>(); ;
resultInfo.ls = result["ls"].ToObject<bool>(); ;
var wss = result["ws"];
tempwsS = new List<Ws>();
JArray wsArray = wss.ToObject<JArray>();
for (int i = 0; i < wsArray.Count; i++)
{
tempWs = new Ws();
tempWs.bg = wsArray[i]["bg"].ToObject<int>();
//tempWs.ed = wsArray[i]["ed"].ToObject<int>();
var cws = wsArray[i]["cw"];
tempcwS = new List<Cw>();
JArray cwArray = cws.ToObject<JArray>();
for (int j = 0; j < cwArray.Count; j++)
{
temocw = new Cw();
temocw.sc = cwArray[j]["sc"].ToObject<int>();
temocw.w = cwArray[j]["w"].ToObject<string>();
tempcwS.Add(temocw);
}
tempWs.cw = tempcwS.ToArray();
tempwsS.Add(tempWs);
}
resultInfo.ws = tempwsS.ToArray();
dataInfo.result = resultInfo;
temp.data = dataInfo;
return temp;
}
}
/// <summary>
/// 返回数据
/// </summary>
/// <typeparam name="T"></typeparam>
public class ResponseData<T>
{
public int code;
public string message;
public string sid;
public ReaponseDataInfo<T> data;
}
public class ReaponseDataInfo<T>
{
public int status;
public T result;
}
public class Ws
{
public Cw[] cw;
public int bg;
public int ed;
}
public class Cw
{
public int sc;
public string w;
}
/// <summary>
/// 问答对话组
/// </summary>
public class ChatInfo
{
public string ask;
public string answer;
}
/// <summary>
/// 语音听写数据
/// </summary>
public class TextInfo
{
public int bg;
public int ed;
public string pgs;
public int[] rg;
public int sn;
public bool ls;
public Ws[] ws;
public string GetResultText()
{
StringBuilder strB = new StringBuilder();
for (int i = 0; i < ws.Length; i++)
{
strB.Append(ws[i].cw[0].w);
}
return strB.ToString();
}
}