OpenAI Node.js SDK前后端对接

Fetch方案

node.js

服务
const OpenAI = require('openai')

const gptClient = new OpenAI({
  apiKey: 'sk-key',
  baseURL: 'url'
})

type CompletionOptions = {
  model?: string,
  tools?: any[],
  temperature?: number,
  isJson?: boolean,
  stream?: boolean,
  messages: Message[], response_format?: { type: 'json_object' }
}

/**
 * 处理完成的消息并返回聊天完成的创建请求
 * @param {Array} messages - 消息数组
 * @param {Object} options - 选项对象
 * @returns {Promise} - 返回客户端的聊天完成创建请求的结果
 */
exports.onCompletion = function (messages: Message[], options = {} as CompletionOptions) {
  console.log('onCompletion')
  const body: CompletionOptions = {
    model: options.model || 'gpt-4o',
    messages
  }
  if (options.tools) {
    body.tools = options.tools
  }
  if (options.isJson) {
    body.response_format = {
      type: 'json_object'
    }
  }
  if (options.stream) {
    body.stream = true
  }
  return gptClient.chat.completions.create(body)
}
接口
router.post('/onCompletion', async (req: ExpressRequest, res: ExpressResponse) => {
  const { prompt, stream = true, needEncode } = req.body
  if (!prompt) {
    res.status(403).json({ success: false, message: 'no prompt' })
  }
  // console.log(req.body)
  try {
    const headers = {
      'Content-Type': 'text/event-stream',
      'Connection': 'keep-alive',
      'Cache-Control': 'no-cache'
    }

    // console.log(JSON.stringify(input))
    console.log('Application对话开始')
    if (stream) {
      res.writeHead(200, headers)
    }
    onCompletion([{ role: 'system', content: '你是一名优秀的人工智能顾问,可以友善且专业的回答我提出的问题。', }, { role: 'user', content: prompt }], { stream })
      .then(async (ret: any) => { // 返回了一个stream或对象
        // console.log({ ret })
        if (!stream) {
          console.log(JSON.stringify(ret))
          return res.send(ret.choices[0].message.content)
        }
        for await (const chunk of ret) {
          // console.log(JSON.stringify(chunk))
          // console.log(chunk.choices[0])
          if (chunk.choices[0].finish_reason) {
            console.log('Application对话结束')
            res.end()
          } else {
            res.write(chunk.choices[0].delta.content)
          }
        }
      })
      .catch(async (error: any) => {
        let message = error.message || error
        console.error({ error: message })
        res.status(500).json({ success: false, message })
      })
  } catch (error) {
    res.status(500).send('获取页面签名失败')
  }
})

前端

服务
/**
  *
  * @param {string} path 路径
  * @param {object} body 参数
  * @param {function} streamCallback 参数为text的回调函数
  * @returns
  */
async functionsse(path, body, streamCallback) {
  let hostname = window.baseUrl
  if (path.startsWith('http')) {
    hostname = ''
  }
  const response = await fetch(window.baseUrl + path, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json;charset=UTF-8',
      Accept: 'application/json;charset=UTF-8',
      sessiontoken: AV.User.current().getSessionToken()
    },
    body: JSON.stringify(body)
  })
  // console.log(response)
  if (response.status < 200 || response.status >= 300) {
    const json = await response.json()
    throw json.error || json.message || json || '请求失败'
  }
  const tDecoder = new TextDecoder('utf-8')
  const reader = response.body?.getReader()
  let text = ''
  let isAdd = false
  try {
    while (true) {
      const { done, value } = await reader.read()
      if (isAdd) {
        text += tDecoder.decode(value, { stream: true })
      } else {
        text = value ? tDecoder.decode(value, { stream: true }) : text
      }
      if (done) {
        console.log('done!!!')
        streamCallback && streamCallback('', done)
        break
      }
      streamCallback && streamCallback(text, done)
    }
  } catch (e) {
    console.error('请求post错误', e)
    return []
  }
}
调用
this.result = ''
try {
  http.sse('/api/onCompletion', {
    prompt: this.prompt,
  }, (text, done) => {
    // console.log(text)
    this.result += text
    if(done) {
      this.$store.config.loading = false
    }
  })
} catch(error) {
  showError(error)
  this.$store.config.loading = false
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值