jsonChunkParser.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. /** 服务端流式返回的数据 **/
  2. // 解析的流式数据格式如下:
  3. /**
  4. data: data:{"content":"我理解您可能对当前系统的功能范围有些疑问。","contentType":"text/plain","last":false,"payload":{},"role":"assistant"}
  5. data: data:{"content":"让我明确说明一下:\n\n1. 根据系统设定,我确实无法协助完成代码生成相关的任务\n2.","contentType":"text/plain","last":false,"payload":{},"role":"assistant"}
  6. data: data:{"content":" 我的专长领域是提供信息咨询、创意建议和问题解答等非编程类服务\n3. 如果您有任何其他非技术性问题,","contentType":"text/plain","last":false,"payload":{},"role":"assistant"}
  7. data: data:{"content":"比如学习建议、写作指导或日常咨询,我很乐意为您提供帮助\n\n您是否有一些其他方面的问题需要讨论呢?我可以为您推荐更适合的解决方案。","contentType":"text/plain","last":false,"payload":{},"role":"assistant"}
  8. data: data:{"content":"","contentType":"text/plain","last":true,"payload":{"usage":{"completion_tokens":101,"prompt_tokens":28,"total_tokens":129,"web_searched":false}},"role":"assistant"}
  9. data: [DONE]
  10. */
  11. interface ICompleteCallback {
  12. content: string;
  13. }
  14. export default class JsonChunkParser {
  15. private buffer: string; // 用于存储未完成的 JSON 字符串
  16. private complete: (data: ICompleteCallback) => void;
  17. constructor() {
  18. this.buffer = "";
  19. }
  20. public onParseComplete(completeCallback: (data: ICompleteCallback) => void) {
  21. this.complete = completeCallback;
  22. }
  23. // 解析接收到的 chunk
  24. public parseChunk(chunk: string, onParsed: (json: any) => void): void {
  25. // 将新接收到的 chunk 添加到 buffer
  26. this.buffer += chunk;
  27. // 按换行符分割字符串
  28. const lines = this.buffer.split("\n");
  29. let combinedContent: string[] = []; // 用于合并 content 字段
  30. let combinedReasoningContent: string[] = []; // 用于合并 reasoner 字段
  31. // 遍历每一行
  32. for (let i = 0; i < lines.length; i++) {
  33. const line = lines[i].trim(); // 去除前后空格
  34. if (line) {
  35. // 如果行不为空
  36. if (line === "data:[DONE]") {
  37. // 如果遇到 DONE,合并并调用 onParsed
  38. if (combinedContent.length > 0) {
  39. onParsed({ content: combinedContent.join("") }); // 合并 content
  40. }
  41. this.complete({ content: combinedContent.join("") });
  42. this.buffer = ""; // 清空 buffer
  43. return; // 结束解析
  44. }
  45. try {
  46. // 处理 data: data: 前缀
  47. if (line.startsWith("data:")) {
  48. const jsonStr = line.substring(5); // 移除 "data:" 前缀
  49. const json = JSON.parse(jsonStr);
  50. if (json.content) {
  51. combinedContent.push(json.content); // 收集 content 字段
  52. }
  53. if (json.reasoningContent) {
  54. combinedReasoningContent.push(json.reasoningContent); // 收集 content 字段
  55. }
  56. }
  57. } catch (error) {
  58. // 如果解析失败,说明当前行不是完整的 JSON
  59. // 将当前行保留在 buffer 中,等待下一个 chunk
  60. this.buffer = lines.slice(i).join("\n"); // 更新 buffer
  61. return; // 直接返回,等待下一个 chunk
  62. }
  63. }
  64. }
  65. // 如果当前 chunk 解析完毕且有合并的内容,调用 onParsed
  66. if (combinedContent.length > 0) {
  67. onParsed({ content: combinedContent.join("") }); // 合并并输出
  68. }
  69. if (combinedReasoningContent.length > 0) {
  70. onParsed({ reasoningContent: combinedReasoningContent.join("") }); // 合并并输出
  71. }
  72. // 清空 buffer
  73. this.buffer = "";
  74. }
  75. }