ChatGpt 使用fetch-event-source实现sse流式处理

本文介绍了微软的fetch-event-source库,用于简化前端JavaScript与服务器之间的EventSource连接,包括如何使用fetchEventSource创建连接,处理服务器推送事件,以及解决跨域问题的方法。还展示了在Vue3和PythonFlask中的使用示例。
摘要由CSDN通过智能技术生成

@microsoft/fetch-event-source 是一个由微软提供的库,用于在客户端和服务器之间建立基于 EventSource 的连接。EventSource 是一种 HTTP 协议,允许服务器向客户端推送实时事件流。该库提供了对 EventSource 协议的封装,使得在前端 JavaScript 中使用 EventSource 变得更加方便。

在 @microsoft/fetch-event-source 中,主要使用 fetchEventSource 函数来创建一个新的 EventSource 连接。这个函数接受一个 URL 参数,以及一个配置对象,其中可以包含一些选项,如请求方法、请求头、请求体等。当服务器向客户端推送事件时,可以通过 onmessage 回调函数来处理这些事件。此外,还可以提供 onerror 和 onclose 回调函数来处理连接错误和关闭事件

一、安装
pnpm install @microsoft/fetch-event-source
二、使用

 前端vue3:

import { fetchEventSource } from '@microsoft/fetch-event-source'

class RetriableError extends Error {}
class FatalError extends Error {}

const EventStreamContentType = 'text/event-stream; charset=utf-8'

export const fetchEventGpt = (data: any, callData: (arg0: any) => void) => {
  const ctrl = new AbortController()
  fetchEventSource('/api/stream', {
    method: 'POST',
    mode: 'no-cors',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(data),
    signal: ctrl.signal,
    openWhenHidden: true,
    async onopen(response: any) {
      // 成功连接时回调
      if (response.ok && response.headers.get('content-type') === EventStreamContentType) {
        return // everything's good
      } else if (response.status >= 400 && response.status < 500 && response.status !== 429) {
        // client-side errors are usually non-retriable:
        throw new FatalError()
      } else {
        throw new RetriableError()
      }
    },
    onmessage(msg: { data: any; event: string }) {
      // 服务器返回消息回调 返回{ data,event,id,retry } ,data即服务器返回数据
      // if the server emits an error message, throw an exception
      // so it gets handled by the onerror callback below:
      if (msg.event === 'FatalError') {
        throw new FatalError(msg.data)
      }

      if (msg.event == '') {
        // 进行连接正常的操作

        try {
          const data = JSON.parse(msg.data)?? {}
          const {finish, code } = data??{}
          callData(data)
          if(code*1 === 200 && finish) {
              ctrl.abort()
          }

        } catch (err) {
          console.log(err)
          throw err
        }
      }

      if (msg.event === 'close') {
        ctrl.abort()
      }
    },
    onclose() {
      // 正常结束的回调
      ctrl?.abort()
      throw new RetriableError()
    },
    onerror(err: any) {
      // 连接出现异常回调
      // 必须抛出错误才会停止
      ctrl?.abort()
      if (err instanceof FatalError) {
        throw err // rethrow to stop the operation
      } else {
        // do nothing to automatically retry. You can also
        // return a specific retry interval here.
      }
    },
  })
}

 服务端python:

from gevent import monkey

monkey.patch_all()

import time


from flask import Response, stream_with_context
from flask import Flask
from gevent.pywsgi import WSGIServer
from flask import request

app = Flask(__name__)


def predict():
    chatbot = [""]
    mid = """
    一、引言\n1. 背景介绍\n2. 研究意义\n\n二、多旋翼无人机概述\n
                                      1. 多旋翼无人机的定义\n2. 多旋翼无人机的特点\n3. 多旋翼无人机的基本结构\n\n
                                      三、多旋翼无人机控制方法\n1. 手动控制\n2. 遥控控制\n3. 自主控制\n\n
                                      四、多旋翼无人机调度方法\n1. 手动调度\n2. 遥控调度\n3. 自主调度\n\n
                                      五、多旋翼无人机应用实例\n1. 农业领域\n2. 航拍领域\n3. 搜索救援\n
                                      4. 其他应用领域\n\n六、多旋翼无人机的安全问题\n1. 飞行安全隐患\n
                                      2. 数据隐私问题\n3. 人机交互问题\n\n七、结论\n1. 研究总结\n2. 研究局限\n3. 研究展望
    """
    s = ""
    for response in mid:
        s += response
        yield [s], []


@app.route("/api/stream", methods=["GET", "POST"])
def progress():
    @stream_with_context
    def generate():
        ratio = 1
        data_stream = predict()
        for data in data_stream:
            yield str("data:") + str(data) + "\n\n"
            print("ratio:", ratio)
            time.sleep(0.1)

    headers = {
        "Content-Type": "text/event-stream",
        "Cache-Control": "no-cache",
        "X-Accel-Buffering": "no",
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "GET,POST",
        "Access-Control-Allow-Headers": "x-requested-with,content-type",
    }

    return Response(generate(), mimetype="text/event-stream", headers=headers)


if __name__ == "__main__":
    http_server = WSGIServer(("0.0.0.0", int(8081)), app)
    http_server.serve_forever()

        我们创建了一个指向 http://192.168.4.164:8081/gptchat/gpt 的 EventSource 连接,使用 POST 方法发送 JSON 格式的请求体数据。当接收到服务器推送的事件时,会打印事件数据。如果发生错误或连接关闭,也会打印相应的信息。

        需要注意的是,虽然 EventSource 本身不支持 POST 方法,但通过使用 fetchEventSource 函数和适当的配置,我们可以模拟 POST 请求的效果。在服务器端,需要正确处理这种 POST 请求,并返回正确格式的事件流数据。

        此外,由于 EventSource 是基于 HTTP 的协议,因此它只能在支持 HTTP 的环境中使用,如浏览器端或 Node.js 服务器端。同时,由于它是基于长连接的协议,因此在使用过程中需要注意连接的管理和错误处理。

三、遇到问题

   1、跨域问题

 设置mode: "no-cors" 解决跨域问题,但是返回的body信息为null

在vite.config.ts配置代理

  devServer: {
    port: 8089,
    proxy: {
      "/api": {
        target: "http://192.168.4.164:8081",
        changeOrigin: true,
      }
    },

  • 21
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值