{"repo": "blockcodelab/playgrounds-app", "files": {"packages/utils/src/lib/fetch-spark.js": {"5cb0c4574a10b2fc07fcec08d6de9fce04651a71": {"t": 1785768232.5661333, "n": 2649, "h": "e04230a82bd87433", "c": "// 当前讯飞星火 HTTP 调用不支持浏览器跨域,\n// 而在单片机上使用 WebSocket 调用鉴权不方便获取时间戳,\n// 所以在浏览器端用 WebSocket 模拟 fetch 通过 HTTP 调用,\n// 尽可能的与单片机上一致。\n\nimport hmacSHA256 from 'crypto-js/hmac-sha256';\nimport Base64 from 'crypto-js/enc-base64';\n\nconst SPARK_HOST = 'spark-api.xf-yun.com';\n\nconst getPathname = (domain) => {\n switch (domain) {\n case 'lite':\n return '/v1.1/chat';\n case 'generalv3':\n return '/v3.1/chat';\n case 'pro-128k':\n return '/chat/pro-128k';\n case 'generalv3.5':\n return '/v3.5/chat';\n case 'max-32k':\n return '/chat/max-32k';\n case '4.0Ultra':\n return '/v4.0/chat';\n }\n};\n\nconst getWebSocketUrl = (domain, apiSecret, apiKey) => {\n const date = new Date().toGMTString();\n\n const pathname = getPathname(domain);\n\n const signatureRaw = `host: ${SPARK_HOST}\\ndate: ${date}\\nGET ${pathname} HTTP/1.1`;\n const signature = Base64.stringify(hmacSHA256(signatureRaw, apiSecret));\n\n const authorizationRaw = `api_key=\"${apiKey}\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"${signature}\"`;\n const authorization = btoa(authorizationRaw);\n\n return `wss://${SPARK_HOST}${pathname}?authorization=${authorization}&date=${date}&host=${SPARK_HOST}`;\n};\n\nexport function fetchSpark(url, options) {\n const auth = options.auth;\n const body = JSON.parse(options.body);\n\n const data = {\n header: {\n app_id: auth.appId,\n uid: body.user,\n },\n parameter: {\n chat: {\n domain: body.model,\n temperature: body.temperature,\n top_k: body.top_k,\n max_tokens: body.max_tokens,\n },\n },\n payload: {\n message: {\n text: body.messages,\n },\n },\n };\n\n url = getWebSocketUrl(body.model, auth.apiSecret, auth.apiKey);\n\n return new Promise((resolve, reject) => {\n let message = '';\n const ws = new WebSocket(url);\n ws.onopen = () => ws.send(JSON.stringify(data));\n ws.onerror = (e) => reject(e);\n ws.onmessage = async (e) => {\n const data = JSON.parse(e.data);\n if (data.header.code !== 0) {\n return reject(new Error(`Error code: ${data.header.code}`));\n }\n // 合并信息\n message += data.payload.choices.text.map((text) => text.content).join('');\n // 结束会话并返回信息\n if (data.header.status === 2) {\n ws.close();\n resolve({\n json() {\n return {\n choices: [\n {\n message: {\n role: 'assistant',\n content: message.trim(),\n },\n index: 0,\n },\n ],\n };\n },\n });\n }\n };\n });\n}\n"}}}}