using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using UnityEngine;
public class ChatSpark : LLM
{
#region 参数
///
/// 讯飞的应用设置
///
[SerializeField] private XunfeiSettings m_XunfeiSettings;
///
/// 选择星火大模型版本
///
[Header("选择星火大模型版本")]
[SerializeField] private ModelType m_SparkModel = ModelType.星火大模型V15;
#endregion
private void Awake()
{
OnInit();
}
///
/// 初始化
///
private void OnInit()
{
m_XunfeiSettings = this.GetComponent();
if (m_SparkModel == ModelType.星火大模型V15)
{
url= "https://spark-api.xf-yun.com/v1.1/chat";
return;
}
url = "https://spark-api.xf-yun.com/v2.1/chat";
}
///
/// 发送消息
///
///
public override void PostMsg(string _msg, Action _callback)
{
base.PostMsg(_msg, _callback);
}
///
/// 发送数据
///
///
///
///
public override IEnumerator Request(string _postWord, System.Action _callback)
{
yield return null;
//处理发送数据
RequestData requestData = new RequestData();
requestData.header.app_id = m_XunfeiSettings.m_AppID;
//判断v1.5还是v2
requestData.parameter.chat.domain = GetDomain();
//添加对话列表
List _tempList = new List();
for(int i=0;i
/// 指定访问的领域
/// general指向V1.5版本
/// generalv2指向V2版本
///
///
private string GetDomain()
{
if (m_SparkModel == ModelType.星火大模型V15)
return "general";
return "generalv2";
}
#region 获取鉴权Url
///
/// 获取鉴权url
///
///
private string GetAuthUrl()
{
string date = DateTime.UtcNow.ToString("r");
Uri uri = new Uri(url);
StringBuilder builder = new StringBuilder("host: ").Append(uri.Host).Append("\n").//
Append("date: ").Append(date).Append("\n").//
Append("GET ").Append(uri.LocalPath).Append(" HTTP/1.1");
string sha = HMACsha256(m_XunfeiSettings.m_APISecret, builder.ToString());
string authorization = string.Format("api_key=\"{0}\", algorithm=\"{1}\", headers=\"{2}\", signature=\"{3}\"", m_XunfeiSettings.m_APIKey, "hmac-sha256", "host date request-line", sha);
string NewUrl = "https://" + uri.Host + uri.LocalPath;
string path1 = "authorization" + "=" + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(authorization));
date = date.Replace(" ", "%20").Replace(":", "%3A").Replace(",", "%2C");
string path2 = "date" + "=" + date;
string path3 = "host" + "=" + uri.Host;
NewUrl = NewUrl + "?" + path1 + "&" + path2 + "&" + path3;
return NewUrl;
}
public string HMACsha256(string apiSecretIsKey, string buider)
{
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(apiSecretIsKey);
System.Security.Cryptography.HMACSHA256 hMACSHA256 = new System.Security.Cryptography.HMACSHA256(bytes);
byte[] date = System.Text.Encoding.UTF8.GetBytes(buider);
date = hMACSHA256.ComputeHash(date);
hMACSHA256.Clear();
return Convert.ToBase64String(date);
}
#endregion
#region websocket连接
///
/// websocket
///
private ClientWebSocket m_WebSocket;
private CancellationToken m_CancellationToken;
///
/// 连接服务器,获取回复
///
private async void ConnectHost(string text,Action _callback)
{
try
{
stopwatch.Restart();
m_WebSocket = new ClientWebSocket();
m_CancellationToken = new CancellationToken();
string authUrl = GetAuthUrl();
string url = authUrl.Replace("http://", "ws://").Replace("https://", "wss://");
//Uri uri = new Uri(GetUrl());
Uri uri = new Uri(url);
await m_WebSocket.ConnectAsync(uri, m_CancellationToken);
//发送json
string _jsonData = text;
await m_WebSocket.SendAsync(new ArraySegment(Encoding.UTF8.GetBytes(_jsonData)), WebSocketMessageType.Binary, true, m_CancellationToken); //发送数据
StringBuilder sb = new StringBuilder();
//用于拼接返回的答复
string _callBackMessage = "";
//播放队列.Clear();
while (m_WebSocket.State == WebSocketState.Open)
{
var result = new byte[4096];
await m_WebSocket.ReceiveAsync(new ArraySegment(result), m_CancellationToken);//接受数据
List list = new List(result); while (list[list.Count - 1] == 0x00) list.RemoveAt(list.Count - 1);//去除空字节
var str = Encoding.UTF8.GetString(list.ToArray());
sb.Append(str);
if (str.EndsWith("}"))
{
//获取返回的数据
ResponseData _responseData = JsonUtility.FromJson(sb.ToString());
sb.Clear();
if (_responseData.header.code != 0)
{
//返回错误
//PrintErrorLog(_responseData.code);
Debug.Log("错误码:" + _responseData.header.code);
m_WebSocket.Abort();
break;
}
//没有回复数据
if (_responseData.payload.choices.text.Count == 0)
{
Debug.LogError("没有获取到回复的信息!");
m_WebSocket.Abort();
break;
}
//拼接回复的数据
_callBackMessage += _responseData.payload.choices.text[0].content;
if (_responseData.payload.choices.status == 2)
{
stopwatch.Stop();
Debug.Log("ChatSpark耗时:" + stopwatch.Elapsed.TotalSeconds);
//添加记录
m_DataList.Add(new SendData("assistant", _callBackMessage));
//回调
_callback(_callBackMessage);
m_WebSocket.Abort();
break;
}
}
}
}
catch (Exception ex)
{
Debug.LogError("报错信息: " + ex.Message);
m_WebSocket.Dispose();
}
}
#endregion
#region 数据定义
//发送的数据
[Serializable]
private class RequestData
{
public HeaderData header=new HeaderData();
public ParameterData parameter = new ParameterData();
public MessageData payload = new MessageData();
}
[Serializable]
private class HeaderData
{
public string app_id = string.Empty;//必填
public string uid="admin";//选填,用户的ID
}
[Serializable]
private class ParameterData
{
public ChatParameter chat=new ChatParameter();
}
[Serializable]
private class ChatParameter
{
public string domain = "general";
public float temperature = 0.5f;
public int max_tokens = 1024;
}
[Serializable]
private class MessageData
{
public TextData message=new TextData();
}
[Serializable]
private class TextData
{
public List text = new List();
}
[Serializable]
private class PostMsgData
{
public string role = string.Empty;
public string content = string.Empty;
}
//接收的数据
[Serializable]
private class ResponseData
{
public ReHeaderData header = new ReHeaderData();
public PayloadData payload = new PayloadData();
}
[Serializable]
private class ReHeaderData{
public int code;//错误码,0表示正常,非0表示出错
public string message=string.Empty;//会话是否成功的描述信息
public string sid=string.Empty;
public int status;//会话状态,取值为[0,1,2];0代表首次结果;1代表中间结果;2代表最后一个结果
}
[Serializable]
private class PayloadData
{
public ChoicesData choices = new ChoicesData();
//usage 暂时没用,需要的话自行拓展
}
[Serializable]
private class ChoicesData
{
public int status;
public int seq;
public List text = new List();
}
[Serializable]
private class ReTextData
{
public string content = string.Empty;
public string role = string.Empty;
public int index;
}
private enum ModelType
{
星火大模型V15,
星火大模型V20
}
#endregion
}