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; /// /// chat /// 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"; /// /// 用户唯一id /// 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 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 re = Task.Run(() => 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(frameData), WebSocketMessageType.Text, true, cancellation); yield return new WaitUntil(() => re.IsCompleted); Debug.Log("完成"); callback(re.Result); } else { Debug.LogError("连接失败"); callback(null); } } } /// /// 获取语音听写url /// /// 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; } /// /// 星火大模型对话(联系上文) /// /// 依次历史对话,不要多 /// 问题 /// public void Chat(List historyChat, string question, Action 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)); } /// /// 星火大模型对话(不联系上文) /// /// 问题 /// public void Chat(string question, Action 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 bs = new List(); //缓冲区 var buffer = new byte[1024 * 4]; while (ws.State == WebSocketState.Open) { Debug.Log("等待消息接收"); var task = ws.ReceiveAsync(new ArraySegment(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(); 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 bs = new List(); //缓冲区 var buffer = new byte[1024 * 4]; while (ws.State == WebSocketState.Open) { //Debug.Log("等待消息接收"); var task = ws.ReceiveAsync(new ArraySegment(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(); 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 = 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); } /// /// 从此实例检索子数组 /// /// 要检索的数组 /// 起始索引号 /// 检索最大长度 /// 与此实例中在 startIndex 处开头、长度为 length 的子数组等效的一个数组 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; } /// /// 解析文本 /// /// /// private ResponseData GetTextResultData(string ReceviceStr) { ResponseData temp = new ResponseData(); ReaponseDataInfo dataInfo = new ReaponseDataInfo(); TextInfo resultInfo = new TextInfo(); List tempwsS; List tempcwS; Ws tempWs; Cw temocw; var jsonObj = (JObject)JsonConvert.DeserializeObject(ReceviceStr); temp.code = jsonObj["code"].ToObject(); temp.message = jsonObj["message"].ToObject(); temp.sid = jsonObj["sid"].ToObject(); var data = jsonObj["data"]/*.ToObject()*/; dataInfo.status = data["status"].ToObject(); var result = data["result"]/*.ToObject()*/; resultInfo.bg = result["bg"].ToObject(); resultInfo.ed = result["ed"].ToObject(); //resultInfo.pgs = result["pgs"].ToObject(); //resultInfo.rg = result["rg"].ToObject(); resultInfo.sn = result["sn"].ToObject(); ; resultInfo.ls = result["ls"].ToObject(); ; var wss = result["ws"]; tempwsS = new List(); JArray wsArray = wss.ToObject(); for (int i = 0; i < wsArray.Count; i++) { tempWs = new Ws(); tempWs.bg = wsArray[i]["bg"].ToObject(); //tempWs.ed = wsArray[i]["ed"].ToObject(); var cws = wsArray[i]["cw"]; tempcwS = new List(); JArray cwArray = cws.ToObject(); for (int j = 0; j < cwArray.Count; j++) { temocw = new Cw(); temocw.sc = cwArray[j]["sc"].ToObject(); temocw.w = cwArray[j]["w"].ToObject(); tempcwS.Add(temocw); } tempWs.cw = tempcwS.ToArray(); tempwsS.Add(tempWs); } resultInfo.ws = tempwsS.ToArray(); dataInfo.result = resultInfo; temp.data = dataInfo; return temp; } } /// /// 返回数据 /// /// public class ResponseData { public int code; public string message; public string sid; public ReaponseDataInfo data; } public class ReaponseDataInfo { 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; } /// /// 问答对话组 /// public class ChatInfo { public string ask; public string answer; } /// /// 语音听写数据 /// 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(); } }