AIGC 在前端流式获取内容SSE

AIGC 在前端流式获取内容SSE

  • 简介
  • 具体实现

简介

在 OpenAI 的 API 中,SSE 通常用于实现实时数据传输。例如,在聊天模型(如 ChatGPT)中,使用 SSE 可以让客户端实时接收到生成的对话内容,而不需要等待整个响应完成。

EventSource会发送一个get类型的http请求,也可以通过fetch实现post类型的请求。我们这里使用fetch,SSE本质是字节流的传输,fetch中处理对应的字节流信息,同样可以实现EventSource的功能

SSE即Server-Sent 是HTML5提出一个标准。由客户端发起与服务器之间创建TCP连接,然后并维持这个连接,直到客户端或服务器中的任何一方断开。HTTP响应内容有一种特殊的content-type:text/event-stream,该响应头标识了响应内容为事件流,客户端不会关闭连接,而是等待服务端不断得发送响应结果。

服务器通过 SSE 向客户端发送的每条消息通常包含以下几部分:

  1. 事件类型(可选):指定事件的类型。
  2. 数据:事件的主要数据内容。
  3. ID(可选):事件的唯一标识符。
  4. 重试时间(可选):客户端在连接断开后重试连接的时间间隔。

每条消息以两个换行符(\n\n)结束。

// sse 返回数据模型如下

data:{"id":"1805489381010771970","object":"chat.completion.chunk","created":1719297188,"model":"gpt-4o-2024-05-13","choices":[{"index":0,"delta":{"role":"assistant","content":""},"message":null,"finishReason":null}],"usage":null}

data:{"id":"1805489381010771970","object":"chat.completion.chunk","created":1719297188,"model":"gpt-4o-2024-05-13","choices":[{"index":0,"delta":{"content":"你好"},"message":null,"finishReason":null}],"usage":null}

data:{"id":"1805489381010771970","object":"chat.completion.chunk","created":1719297188,"model":"gpt-4o-2024-05-13","choices":[{"index":0,"delta":{"content":"!"},"message":null,"finishReason":null}],"usage":null}

data:{"id":"1805489381010771970","object":"chat.completion.chunk","created":1719297188,"model":"gpt-4o-2024-05-13","choices":[{"index":0,"delta":{},"message":null,"finishReason":"stop"}],"usage":null}

data:[DONE]

具体实现

提示:关于流式获取,我们在业务开发中如果后端服务正常的话响应头Content-Type会是text/event-stream,否则为application/json。

封装一个工具方法来处理流式内容,并且在该方法中,使用者可以通过回调函数的形式来获取内容、状态等信息。

注意:需要通过fetch来创建请求,请求完成之后body为ReadableStream,可以获取它的读取器,一块一块的来读取数据,由于读取的数据为字节流数据,所以需要把字节流转换成字符串。为了更好的解析sse响应数据,这里使用了eventsource-parser第三方库,在读取过程中来拼接文本内容

import { ParseEvent, createParser } from 'eventsource-parser';
import emitter, { EmitterEvents } from './emitter';

interface Options<T> {
  params?: T;
  onSucess?: (text: string, done: boolean, streamData: { id: string } | null) => void;
  onError?: (error: { code: number; msg: string }) => void;
}

const sse = async function <T>(url: string, options?: Options<T>) {
  const userInfo = JSON.parse(localStorage.getItem('userInfo') ?? '{}');
  let responseText = '';
  let streamData: any = null;

  if (!userInfo?.jwt) {
    // 处理无token
    emitter.emit(EmitterEvents.ON_LOGIN, true);
    return;
  }

  function onParse(event: ParseEvent) {
    if (event.type === 'event') {
      const data = event.data;

      if (data === '[DONE]') {
        options?.onSucess?.(responseText, true, streamData);
        return;
      }

      streamData = JSON.parse(data);

      const text = streamData.choices[0].delta?.content || '';

      responseText += text;

      // 尝试解析敏感词检测结果
      try {
        const match = responseText.match(/```json\n(.*?)"code":10013(.*?)```/g);
        if (match) {
          const jsonStr = match
            .pop()!
            .replace(/```json\n/, '')
            .replace(/```$/, '');
          const sensitiveObj = JSON.parse(jsonStr);
          if (sensitiveObj.DONE === 'DONE') {
            responseText = sensitiveObj.filteredText || '**';
            return;
          }
        }
      } catch (e) {
        console.log(e);
      }

      options?.onSucess?.(responseText, false, streamData);
    }
  }

  const res = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      token: userInfo.jwt,
    },
    body: JSON.stringify(options?.params),
  });

  if (res.ok) {
    const contentType = res.headers.get('Content-Type') ?? '';

    if (!contentType.includes('stream')) {
      const data = await res.json();
      options?.onError?.({ code: data.code, msg: data.msg });
      return;
    }

    const parser = createParser(onParse);
    const reader = res.body?.getReader();
    const decoder = new TextDecoder();

    while (true) {
      const content = await reader?.read();
      const decodeContent = decoder.decode(content?.value);
      
      if (content?.done) {
        break;
      }

      parser.feed(decodeContent);
    }
  } else {
    options?.onError?.({ code: res.status, msg: res.statusText });
  }
};

export default sse;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/764734.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python--进程基础

创建进程 os.fork() 该方法只能在linux和mac os中使用&#xff0c;因为其主要基于系统的fork来实现。window中没有这个方法。 通过os.fork()方法会创建一个子进程&#xff0c;子进程的程序集为该语句下方的所有语句。 import os​​print("主进程的PID为:" , os.g…

如何利用GPT-4o生成有趣的梗图

文章目录 如何利用GPT-4o生成有趣的梗图一、引言二、使用GPT-4o生成梗图1. 提供主题2. 调用工具3. 获取图片实际案例输入输出 三、更多功能1. 创意和灵感2. 梗图知识 四、总结 如何利用GPT-4o生成有趣的梗图 梗图&#xff0c;作为互联网文化的一部分&#xff0c;已经成为了我们…

【轻量化】YOLOv8 更换骨干网络之 MobileNetv4 | 《号称最强轻量化网络》

论文地址:https://arxiv.org/pdf/2404.10518 代码地址:https://github.com/tensorflow/models/blob/master/official/vision/modeling/backbones/mobilenet.py 文章速览 文章摘要 MobileNetV4引入了一个名为Universal Inverted Bottleneck (UIB) 的新搜索模块,这个模块融合…

MCU 是什么?一文了解MCU 产业

MCU&#xff08;Microcontroller Unit&#xff09;&#xff0c;中文名为“微控制器单元”、“单片微型计算机”。MCU 将中央处理器&#xff08;CPU&#xff09;、内存&#xff08;RAM&#xff09;、输入 / 输出界面&#xff08;I/O&#xff09;等等一大堆东西&#xff0c;全部整…

kafka的架构

一、架构图 Broker&#xff1a;一台 kafka 服务器就是一个 broker。一个kakfa集群由多个 broker 组成。一个 broker 可以容纳多个 topic。 Producer&#xff1a;消息生产者&#xff0c;就是向 kafka broker 发消息的客户端 Consumer&#xff1a;消息消费者&#xff0c;向 kafk…

Charles抓包工具踩坑记录

请添加图片描述 Charles抓包工具 证书问题 输入网址&#xff1a;chls.pro/ssl 第一个下载证书网址&#xff0c;会出现一直加载不出来&#xff0c;无法下载证书的情况 解决&#xff1a;选择下面save Charles Root。。。 2 证书在mac中禁止修改问题 解决也很简单&#xff0c;按照…

基于ESP32 IDF的WebServer实现以及OTA固件升级实现记录(三)

经过前面两篇的前序铺垫&#xff0c;对webserver以及restful api架构有了大体了解后本篇描述下最终的ota实现的代码以及调试中遇到的诡异bug。 eps32的实际ota实现过程其实esp32官方都已经基本实现好了&#xff0c;我们要做到无非就是把要升级的固件搬运到对应ota flash分区里面…

自定义动态数据源+事务控制

1&#xff1a;首先yml配置两个数据库的链接 spring:application:name: xxxxmain:banner-mode: OFFdatasource: # 默认数据源 datamarkdruid: # 关闭数据库的 web 访问stat-view-servlet:enabled: falseweb-stat-filter:enabled: falsefilt…

巴比达内网穿透:深度剖析其在解决远程连接挑战中的技术优势

在信息技术日新月异的今天&#xff0c;远程协作与管理的需求日益增长&#xff0c;但内网环境的隔离性一直是横亘在高效远程操作面前的一道坎。本文将深入探讨一款专为打破此壁垒而生的工具——巴比达内网穿透&#xff0c;如何以其技术创新和高效性能&#xff0c;成为解决远程连…

electron-builder 打包过慢解决

报错内容如下 > 6-241.0.0 build > electron-builder • electron-builder version24.13.3 os10.0.22631 • loaded configuration filepackage.json ("build" field) • writing effective config filedist\builder-effective-config.yaml • pack…

【Linux详解】进程地址空间

目录 研究背景 验证地址空间 实验一&#xff1a;父子进程变量地址一致性 实验二&#xff1a;变量值修改后父子进程的差异 分析与结论 实验三&#xff1a;进程地址空间验证 理解进程地址空间 区域与页表 写时拷贝机制 进程地址空间的意义 文章手稿&#xff1a; xmind…

7月信用卡新规下:信用卡欠的钱不用还了?

说到信用卡&#xff0c;现在基本上人手一张&#xff0c;大家都有使用过。但你知道吗&#xff0c;使用信用卡不是这么简单容易的事&#xff0c;比如会对你的贷款有影响&#xff0c;透支不还逾期对生活的影响&#xff0c;信用卡新规对持卡人和银行那边的影响。 一、只要不逾期&am…

hamibot 学习

1.参考文档&#xff1a; https://blog.csdn.net/zxl0428/article/details/1285318731.参考官网 快速入手步骤&#xff1a;注册&#xff0c;安装客户端&#xff0c;添加设备&#xff0c;开发脚本&#xff0c;运行脚本 https://hamibot.com/guide1.安装客户端 2.添加设备 …

高通骁龙(Qualcomm Snapdragon)CDSP HVX HTP 芯片简介与开发入门

1. Hexagon DSP/HVX/HTP 硬件演进 说到高通骁龙芯片大家应该不会陌生&#xff0c;其作为最为广泛的移动处理器之一&#xff0c;几乎每一个品牌的智能手机都会使用高通骁龙的处理器。 高通提供了一系列骁龙芯片解决方案。根据性能强弱分为了5个产品系列&#xff1a;从最高端的…

【neo4j图数据库】入门实践篇

探索数据之间的奥秘&#xff1a;Neo4j图数据库引领新纪元 在数字化浪潮汹涌的今天&#xff0c;数据已成为企业最宝贵的资产之一。然而&#xff0c;随着数据量的爆炸性增长和数据关系的日益复杂&#xff0c;传统的关系型数据库在处理诸如社交网络、推荐系统、生物信息学等高度互…

OPCUA相关概念和KepServer OPCUA连接PLC

文章背景 项目中需要使用OPC UA 来读取PLC的点位。本文简单介绍了OPC UA和使用KepServer软件连接PLC并读点。OPC相关概念 OPC之前&#xff0c;软件开发需要写大量驱动程序去连接设备&#xff0c;设备上的一个硬件改变&#xff0c;应用程序都有可能需要重写&#xff0c;不同设备…

水经微图Web版1.9.0发布

水经微图&#xff08;简称“微图”&#xff09;新版已上线&#xff0c;在该版本中主要新增了对WGS84图源加载、火星坐标图源加载和大字体图源加载功能&#xff0c;以及多面要素的加载功能。 现在&#xff0c;为你分享一下本轮迭代的主要新增功能&#xff0c;以及部分功能的效果…

STL空间配置器

空间配置器&#xff08;allocator&#xff09;&#xff08;重点&#xff09; 背景需求&#xff1a;在底层默默的实现空间的分配 问题&#xff1a;空间的申请与对象的创建两者分开&#xff0c;因为不断创建的时候可能会频繁的申请空间扩容。 类似操作&#xff1a;reserve函数…

自动扫描范围在减少剂量多相CT肝脏成像中的应用:基于CNN和高斯模型| 文献速递-深度学习自动化疾病检查

Title 题目 Automatic scan range for dose-reduced multiphase CT imaging of theliver utilizing CNNs and Gaussian models 自动扫描范围在减少剂量多相CT肝脏成像中的应用&#xff1a;基于CNN和高斯模型 01 文献速递介绍 肝癌是全球癌症死亡的第四大原因&#xff0c;每…

告别推广迷茫,Xinstall渠道包助您精准统计应用商店数据!

在App推广的浩瀚征途中&#xff0c;每一位广告主和开发者都面临着同样的挑战&#xff1a;如何在众多应用商店中脱颖而出&#xff0c;实现高效推广与精准获客&#xff1f;今天&#xff0c;就让我们一同探索Xinstall应用商店渠道包的独特魅力&#xff0c;看看它是如何成为解决这一…