
之前调用写过调用百度的文心一言写网站,讯飞的星火认知模型开放了,这次尝试一下使用流式来进行用户的交互。
本文章主要开发的是一个web应用。
值得一提的是官网很贴心的给了代码!!!

我这里展示的js的小demo

解析了一下代码结构:
主要的核心代码是在index.html为简单的页面。
index.js中封装了方法,惊喜的发现前台使用的流式相应是使用的websocket协议。
解读分析一下代码:
- 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>
其实这个主页没有太多解释的地方。
我们将后台代码分模块进行研究
- 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"。