|
- import API from '@/api/config.js'
- import config from '@/utils/config.js'
- import { getClient, getNonce, getVersion, guid } from '@/utils/util.js'
-
- let BASE_URL = config.BASE_URL
- // #ifdef H5
- BASE_URL = '/api'
- // #endif
-
- function buildRequestData(params = {}) {
- const token = uni.getStorageSync('token')
- const uid = uni.getStorageSync('open_id')
- let mac = uni.getStorageSync('mac')
- if (!mac) {
- mac = uni.getSystemInfoSync().deviceId || guid()
- uni.setStorageSync('mac', mac)
- }
- const { version, version_code } = getVersion()
- const data = {
- token,
- uid,
- mac,
- base_timestamp: parseInt(Date.now() / 1000),
- client: getClient(),
- source: uni.getStorageSync('source') || '',
- client_ios: uni.getSystemInfoSync().platform === 'ios' ? 1 : 0,
- version,
- version_code,
- ...params,
- stream: 1
- }
- Object.keys(data).forEach((key) => {
- if (data[key] === '') delete data[key]
- })
- return data
- }
-
- function buildHeader() {
- const token = uni.getStorageSync('token')
- const { timeStr, nonce } = getNonce()
- const header = {
- 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
- Accept: 'text/event-stream',
- timestr: timeStr,
- nonce
- }
- if (token) header.token = token
- return header
- }
-
- function mergeBytes(pending, bytes) {
- if (!pending || !pending.length) return bytes
- const merged = new Uint8Array(pending.length + bytes.length)
- merged.set(pending, 0)
- merged.set(bytes, pending.length)
- return merged
- }
-
- function appendCodePoint(text, codePoint) {
- if (codePoint <= 0xffff) return text + String.fromCharCode(codePoint)
- const value = codePoint - 0x10000
- return text + String.fromCharCode(0xd800 + (value >> 10), 0xdc00 + (value & 0x3ff))
- }
-
- function isContinuationByte(byte) {
- return (byte & 0xc0) === 0x80
- }
-
- function decodeUtf8Fallback(bytes, state, flush = false) {
- const input = mergeBytes(state.utf8Pending, bytes)
- let output = ''
- let i = 0
-
- while (i < input.length) {
- const first = input[i]
- if (first < 0x80) {
- output += String.fromCharCode(first)
- i += 1
- continue
- }
-
- let needed = 0
- let codePoint = 0
- if (first >= 0xc2 && first <= 0xdf) {
- needed = 2
- codePoint = first & 0x1f
- } else if (first >= 0xe0 && first <= 0xef) {
- needed = 3
- codePoint = first & 0x0f
- } else if (first >= 0xf0 && first <= 0xf4) {
- needed = 4
- codePoint = first & 0x07
- } else {
- output += '\uFFFD'
- i += 1
- continue
- }
-
- if (i + needed > input.length) break
-
- let valid = true
- for (let j = 1; j < needed; j += 1) {
- const next = input[i + j]
- if (!isContinuationByte(next)) {
- valid = false
- break
- }
- codePoint = (codePoint << 6) | (next & 0x3f)
- }
-
- const second = input[i + 1]
- if (
- !valid ||
- (needed === 3 && ((first === 0xe0 && second < 0xa0) || (first === 0xed && second >= 0xa0))) ||
- (needed === 4 && ((first === 0xf0 && second < 0x90) || (first === 0xf4 && second >= 0x90)))
- ) {
- output += '\uFFFD'
- i += 1
- continue
- }
-
- output = appendCodePoint(output, codePoint)
- i += needed
- }
-
- const pending = Array.prototype.slice.call(input, i)
- state.utf8Pending = flush ? [] : pending
- if (flush && pending.length) output += '\uFFFD'
- return output
- }
-
- function arrayBufferToString(buffer, state, flush = false) {
- const bytes = buffer ? new Uint8Array(buffer) : new Uint8Array(0)
- if (!state.disableTextDecoder && typeof TextDecoder !== 'undefined') {
- try {
- if (!state.decoder) state.decoder = new TextDecoder('utf-8')
- return state.decoder.decode(bytes, { stream: !flush })
- } catch (e) {
- state.disableTextDecoder = true
- }
- }
- return decodeUtf8Fallback(bytes, state, flush)
- }
-
- function consumeSse(bufferState, chunk, callbacks) {
- bufferState.text += String(chunk || '').replace(/\r\n/g, '\n')
- const parts = bufferState.text.split(/\n\n+/)
- bufferState.text = parts.pop() || ''
- parts.forEach((part) => {
- const lines = part.split('\n')
- let event = 'message'
- let dataText = ''
- lines.forEach((line) => {
- if (line.indexOf('event:') === 0) event = line.replace(/^event:\s*/, '').trim()
- if (line.indexOf('data:') === 0) dataText += line.replace(/^data:\s*/, '')
- })
- if (!dataText) return
- let data = {}
- try {
- data = JSON.parse(dataText)
- } catch (e) {
- data = { content: dataText }
- }
- if (event === 'start') callbacks.onStart && callbacks.onStart(data)
- else if (event === 'message') callbacks.onMessage && callbacks.onMessage(data)
- else if (event === 'done') callbacks.onDone && callbacks.onDone(data)
- else if (event === 'error') callbacks.onError && callbacks.onError(data)
- })
- }
-
- function flushSse(bufferState, callbacks) {
- const tail = arrayBufferToString(null, bufferState, true)
- if (tail) consumeSse(bufferState, tail, callbacks)
- if (bufferState.text && bufferState.text.trim()) {
- consumeSse(bufferState, '\n\n', callbacks)
- }
- }
-
- export function requestNameStream(params, callbacks = {}) {
- // #ifdef MP-WEIXIN
- const bufferState = { text: '', utf8Pending: [] }
- const task = wx.request({
- url: `${BASE_URL}${API.INTERFACE_AINAME_POST_SEND}`,
- method: 'POST',
- header: buildHeader(),
- data: buildRequestData(params),
- enableChunked: true,
- success: (res) => {
- flushSse(bufferState, callbacks)
- if (res.statusCode >= 400) {
- callbacks.onError && callbacks.onError({ msg: `网络异常 (${res.statusCode})` })
- }
- },
- fail: (e) => {
- callbacks.onError && callbacks.onError({ msg: e.errMsg || '生成失败' })
- }
- })
- task.onChunkReceived((res) => {
- consumeSse(bufferState, arrayBufferToString(res.data, bufferState), callbacks)
- })
- return task
- // #endif
-
- // #ifndef MP-WEIXIN
- callbacks.onError && callbacks.onError({ msg: '当前端暂不支持流式生成' })
- return null
- // #endif
- }
|