阿丹:
之前调用写过调用百度的文心一言写网站,讯飞的星火认知模型开放了,这次尝试一下使用流式来进行用户的交互。
官网:
平台简介 | 讯飞开放平台文档中心
星火认知大模型Web文档 | 讯飞开放平台文档中心
简介:
本文章主要开发的是一个web应用。
值得一提的是官网很贴心的给了代码!!!
我这里展示的js的小demo
阅读解析一下demo代码--js
解析了一下代码结构:
主要的核心代码是在index.html为简单的页面。
index.js中封装了方法,惊喜的发现前台使用的流式相应是使用的websocket协议。
解读分析一下代码:
index.html
<!--
* @Autor: lycheng
* @Date: 2020-01-13 16:12:22
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>产品体验-大模型</title>
</head>
<body>
<div>
<div>
<h2>产品体验-大模型</h2>
<div class="page-main">
<div>
<textarea id="input_text" placeholder="请输入您要问大模型的问题" style="width: 600px">秦始皇的儿子是谁?</textarea>
</div>
<div>
<button class="audio-ctrl-btn">立即提问</button>
</div>
<br>
<div>
<textarea id="output_text" style="width: 800px;height: 500px"></textarea>
</div>
</div>
</div>
</div>
</body>
</html>
其实这个主页没有太多解释的地方。
我们来看一下封装的方法--index.js
我们将后台代码分模块进行研究
构造与后台建立websocket连接的url
function getWebsocketUrl() {
return new Promise((resolve, reject) => {
var apiKey = API_KEY
var apiSecret = API_SECRET
var url = 'wss://spark-api.xf-yun.com/v1.1/chat'
var host = location.host
var date = new Date().toGMTString()
var algorithm = 'hmac-sha256'
var headers = 'host date request-line'
var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v1.1/chat HTTP/1.1`
var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret)
var signature = CryptoJS.enc.Base64.stringify(signatureSha)
var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`
var authorization = btoa(authorizationOrigin)
url = `${url}?authorization=${authorization}&date=${date}&host=${host}`
resolve(url)
})
}
代码解析:
function getWebsocketUrl() {
定义了一个名为getWebsocketUrl
的函数。return new Promise((resolve, reject) => {
使用Promise
构造函数创建了一个异步操作,并传入resolve
和reject
回调函数。var apiKey = API_KEY
声明了一个变量apiKey
,并将其初始化为API_KEY
的值。var apiSecret = API_SECRET
声明了一个变量apiSecret
,并将其初始化为API_SECRET
的值。var url = 'wss://spark-api.xf-yun.com/v1.1/chat'
声明了一个变量url
,并将其初始化为一个 WebSocket 连接的 URL。var host = location.host
声明了一个变量host
,并将其初始化为当前页面的主机名。var date = new Date().toGMTString()
声明了一个变量date
,并将其初始化为当前时间的 GMT 表示形式。var algorithm = 'hmac-sha256'
声明了一个变量algorithm
,并将其初始化为字符串'hmac-sha256'
。var headers = 'host date request-line'
声明了一个变量headers
,并将其初始化为字符串'host date request-line'
。var signatureOrigin =
host: ${host}\ndate: ${date}\nGET /v1.1/chat HTTP/1.1`` 声明了一个变量signatureOrigin
,并将其初始化为一个字符串,包含了请求的头部信息。var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret)
使用 CryptoJS 的 HMAC-SHA256 算法对signatureOrigin
进行哈希运算,并将结果存储在变量signatureSha
中。var signature = CryptoJS.enc.Base64.stringify(signatureSha)
将signatureSha
转换为 Base64 编码的字符串,并将结果存储在变量signature
中。var authorizationOrigin =
api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"声明了一个变量
authorizationOrigin`,并将其初始化为一个包含授权信息的字符串。var authorization = btoa(authorizationOrigin)
使用btoa
函数将authorizationOrigin
转换为 Base64 编码的字符串,并将结果存储在变量authorization
中。url =
${url}?authorization=${authorization}&date=${date}&host=${host}将带有授权信息的 URL 拼接在原始 URL 的后面,并将结果存储回变量
url` 中。resolve(url)
调用resolve
回调函数,并将带有授权信息的 URL 作为参数传递给它。}) }
结束异步操作块和函数定义。return new Promise((resolve, reject) => { ... } ) }
返回一个 Promise 对象,该对象将在异步操作完成后通过调用resolve
回调函数来解析 URL。
这段代码的目的是通过使用 HMAC-SHA256 算法对请求进行签名,并添加授权信息到 WebSocket 连接 URL 中,以获取一个合法的 WebSocket 连接 URL。
封装的调用的工具类
class TTSRecorder {
constructor({
appId = APPID
} = {}) {
this.appId = appId
this.status = 'init'
}
// 修改状态
setStatus(status) {
this.onWillStatusChange && this.onWillStatusChange(this.status, status)
this.status = status
}
// 连接websocket
connectWebSocket() {
this.setStatus('ttsing')
return getWebsocketUrl().then(url => {
let ttsWS
if ('WebSocket' in window) {
ttsWS = new WebSocket(url)
} else if ('MozWebSocket' in window) {
ttsWS = new MozWebSocket(url)
} else {
alert('浏览器不支持WebSocket')
return
}
this.ttsWS = ttsWS
ttsWS.onopen = e => {
this.webSocketSend()
}
ttsWS.onmessage = e => {
this.result(e.data)
}
ttsWS.onerror = e => {
clearTimeout(this.playTimeout)
this.setStatus('error')
alert('WebSocket报错,请f12查看详情')
console.error(`详情查看:${encodeURI(url.replace('wss:', 'https:'))}`)
}
ttsWS.onclose = e => {
console.log(e)
}
})
}
// websocket发送数据
webSocketSend() {
var params = {
"header": {
"app_id": this.appId,
"uid": "fd3f47e4-d"
},
"parameter": {
"chat": {
"domain": "general",
"temperature": 0.5,
"max_tokens": 1024
}
},
"payload": {
"message": {
"text": [
{
"role": "user",
"content": "中国第一个皇帝是谁?"
},
{
"role": "assistant",
"content": "秦始皇"
},
{
"role": "user",
"content": "秦始皇修的长城吗"
},
{
"role": "assistant",
"content": "是的"
},
{
"role": "user",
"content": $('#input_text').text()
}
]
}
}
}
console.log(JSON.stringify(params))
this.ttsWS.send(JSON.stringify(params))
}
start() {
total_res = ""; // 请空回答历史
this.connectWebSocket()
}
// websocket接收数据的处理
result(resultData) {
let jsonData = JSON.parse(resultData)
total_res = total_res + resultData
$('#output_text').val(total_res)
// console.log(resultData)
// 提问失败
if (jsonData.header.code !== 0) {
alert(`提问失败: ${jsonData.header.code}:${jsonData.header.message}`)
console.error(`${jsonData.header.code}:${jsonData.header.message}`)
return
}
if (jsonData.header.code === 0 && jsonData.header.status === 2) {
this.ttsWS.close()
bigModel.setStatus("init")
}
}
}
代码解释:
在构造函数中,有一个可选的参数appId
,默认值为"APPID"。该函数会将appId
赋值给隶属于类的属性this.appId
,并将状态初始化为"init"。
setStatus(status)
是一个方法,用于改变状态。在调用该方法之前,可以通过设置onWillStatusChange
回调函数来执行一些额外操作。该方法会将状态设置为传入的status
值。
connectWebSocket()
是一个方法,用于建立WebSocket连接。在开始建立连接之前,会将状态设置为"ttsing"。然后,通过调用getWebsocketUrl()
方法获取WebSocket的URL,并创建一个WebSocket实例,保存在ttsWS
属性中。如果浏览器不支持WebSocket,则会弹出一个警告,而后续的操作将中止。
创建WebSocket实例后,定义了几个回调函数:onopen
、onmessage
、onerror
和onclose
。在连接成功时,会调用webSocketSend()
方法发送数据;接收到消息时,会调用result()
方法处理数据;发生错误时,会更改状态为"error";关闭连接时,在控制台打印相关信息。
webSocketSend()
方法用于向WebSocket发送数据。首先,创建了一个包含参数的params
对象,其中包括应用ID、用户ID、聊天参数和要转换为语音的文本。通过调用JSON.stringify()
方法将参数对象转换为字符串,并使用WebSocket实例的send()
方法发送。
start()
方法是启动TTS录制的入口。在该方法中,将总的回答历史total_res
初始化为空字符串,然后调用connectWebSocket()
方法建立WebSocket连接。
result(resultData)
方法用于处理接收到的WebSocket数据。首先,将接收到的JSON字符串转换为对象,将结果累加到total_res
变量中,并将其值显示在output_text
元素中。如果提问失败(header.code
不为0),会弹出一个警告并输出错误信息。如果提问成功但会话结束(header.status
为2),则关闭WebSocket连接,并将状态设置为"init"。