UNPKG

llm-service-provider

Version:

LLM service provider with API key management and streaming capabilities

142 lines (141 loc) 5.05 kB
class XunfeiWebSocketParams { constructor(APIKey, APISecret, gpt_url, prompt) { this.APIKey = APIKey; this.APISecret = APISecret; const parsedUrl = new URL(gpt_url); this.host = parsedUrl.host; this.path = parsedUrl.pathname + parsedUrl.search; this.gpt_url = gpt_url; this.prompt = prompt; } async create_url() { const now = new Date(); const date = this.format_date_time(now); const signature_origin = `host: ${this.host}\ndate: ${date}\nGET ${this.path} HTTP/1.1`; const signature_sha = await this.generateHmac(this.APISecret, signature_origin); if (!signature_sha) { return null; } const signature_sha_base64 = btoa(String.fromCharCode(...new Uint8Array(signature_sha))); const authorization_origin = `api_key="${this.APIKey}", algorithm="hmac-sha256", headers="host date request-line", signature="${signature_sha_base64}"`; const authorization = btoa(authorization_origin); const v = { authorization: authorization, date: date, host: this.host }; const queryString = new URLSearchParams(v).toString(); const wsUrl = `${this.gpt_url}?${queryString}`; return wsUrl; } async generateHmac(key, data) { try { const encoder = new TextEncoder(); const keyData = encoder.encode(key); const dataData = encoder.encode(data); const cryptoKey = await window.crypto.subtle.importKey('raw', keyData, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']); const signature = await window.crypto.subtle.sign('HMAC', cryptoKey, dataData); return signature; } catch (error) { console.error('Error generating HMAC:', error); return null; } } format_date_time(date) { return date.toUTCString(); } } function on_error(error) { console.log("### error:", error); } function on_close(close_status_code, close_msg) { console.log("### closed ###"); } function on_open(ws, prompt) { const data = JSON.stringify({ "payload": { "message": { "text": [ { "role": "system", "content": "" }, { "role": "user", "content": prompt || "请在此处输入你的问题!!!" } ] } }, "parameter": { "chat": { "max_tokens": 32768, "domain": "x1", "top_k": 6, "temperature": 1.2, "tools": [ { "web_search": { "search_mode": "normal", "enable": false }, "type": "web_search" } ] } }, "header": { "app_id": "7802f8ba" } }); ws.send(data); } export default async function request_xunfei(api_secret, api_key, gpt_url, prompt) { try { const wsParam = new XunfeiWebSocketParams(api_key, api_secret, gpt_url, prompt); const wsUrl = await wsParam.create_url(); if (!wsUrl) { return null; } const ws = new WebSocket(wsUrl); ws.onopen = () => on_open(ws, wsParam.prompt); ws.onerror = on_error; ws.onclose = (event) => on_close(event.code, event.reason); const { readable, writable } = new TransformStream(); const writer = writable.getWriter(); const encoder = new TextEncoder(); ws.onmessage = (event) => { try { const data = event.data; const message = JSON.parse(data); if (message.payload?.choices?.text?.[0]?.content) { const content = message.payload.choices.text[0].content; writer.write(encoder.encode(`data: ${JSON.stringify({ choices: [{ delta: { content } }] })}\n\n`)); } if (message.header?.code !== 0) { writer.close(); ws.close(); } if (message.payload?.choices?.status === 2) { writer.write(encoder.encode('data: [DONE]\n\n')); writer.close(); ws.close(); } } catch (e) { console.error('Error processing message:', e); } }; ws.onerror = (error) => { console.error('WebSocket error:', error); writer.close(); }; return readable.getReader(); } catch (error) { console.error('Error in request_xunfei:', error); return null; } } export { request_xunfei, XunfeiWebSocketParams };