chatGPT 流式效果之前/后端实现、EventSource/sse

记录流式打字效果的原理
更新于: 2024-06-25 15:45:08

零零碎碎

名称代码
k8s 的 proxy_buffer 
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/configuration-snippet: |
      proxy_set_header X-Script-Name /myservice;
    nginx.ingress.kubernetes.io/proxy-request-buffering: "off"
    nginx.ingress.kubernetes.io/rewrite-target: /api
web 前端“标准” sse 接收实现 
web 前端“兼容” sse 接收实现
const el = document.getElementById("content") as HTMLElement;
const token = `YOUR_TOKEN`;
const body = JSON.stringify({ question: "来一个100字作文" });

fetch("https://ai-chat-api.beta.saybot.net/api/v1/chat/sse", {
  method: "post",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${token}`,
  },
  body,
}).then(async (response) => {
  // Get the readable stream from the response body
  const stream = response.body;
  // Get the reader from the stream
  const reader = stream!.getReader();
  const decoder = new TextDecoder("utf-8");
  while (true) {
    const { done, value } = await reader.read();
    // console.log("done/value: ", done, value);
    // onChunk(result.value);
    if (done) break;
    const str = decoder.decode(value);
    const json_re = /data:(\{[^}]+\})/;
    const matched = str.match(json_re);
    if (matched) {
      const item = JSON.parse(matched[1]);
      console.log("item:", item);
      el.innerHTML += item.content;
    }
  }
});
使用 SseParser 实现
const response = await fetch('http://API_URL/api/pcb/pmall/ppt/compress', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: `filepath=${res1.data}`
});

const reader = response.body.getReader();

let decoder = new TextDecoder();
let buffer = '';

while (true) {
  const { done, value } = await reader.read();

  if (done) {
    //全部获取流式数据结束后的操作
    console.log('end...');
    break;
  }

  // 将读取到的数据添加到缓冲区(可能是因为客户端处理数据的速度比服务器发送数据的速度快,
  //导致一次性接收到多条数据。这时候就要用buffer缓冲区来处理一下)
  //后端给我的数据格式:{"chunk_message": "我是数据"}
  const cur = decoder.decode(value, { stream: true });
  buffer += cur;
  console.log('buffer every time: ', SseParser.parse(buffer));
}
fetch 与 onMessage 
var body = {};
var onMessage = (item) => { };

try {
  const response = await fetchWrapper(apiHost + endpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
    credentials: 'include',
  });

  if (!response.body) {
    const error = new Error('No response body');
    return Promise.reject(error);
  }
  const reader = response.body.getReader();
  const decoder = new TextDecoder('utf-8');
  // eslint-disable-next-line no-constant-condition
  while (true) {
    const { value, done } = await reader.read();
    // onEnd
    if (done) break;
    const chunk = decoder.decode(value);
    SseParser.parse(chunk, { type: 'prefixedJson', callback: ({ item }) => onMessage(item) });
  }
} catch (error) {
  return Promise.reject(error);
}
express 的 sse 实现// TODO
express 的 伪 sse 实现// TODO