123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- /** 服务端流式返回的数据 **/
- // 解析的流式数据格式如下:
- /**
- data: data:{"content":"我理解您可能对当前系统的功能范围有些疑问。","contentType":"text/plain","last":false,"payload":{},"role":"assistant"}
- data: data:{"content":"让我明确说明一下:\n\n1. 根据系统设定,我确实无法协助完成代码生成相关的任务\n2.","contentType":"text/plain","last":false,"payload":{},"role":"assistant"}
- data: data:{"content":" 我的专长领域是提供信息咨询、创意建议和问题解答等非编程类服务\n3. 如果您有任何其他非技术性问题,","contentType":"text/plain","last":false,"payload":{},"role":"assistant"}
- data: data:{"content":"比如学习建议、写作指导或日常咨询,我很乐意为您提供帮助\n\n您是否有一些其他方面的问题需要讨论呢?我可以为您推荐更适合的解决方案。","contentType":"text/plain","last":false,"payload":{},"role":"assistant"}
- data: data:{"content":"","contentType":"text/plain","last":true,"payload":{"usage":{"completion_tokens":101,"prompt_tokens":28,"total_tokens":129,"web_searched":false}},"role":"assistant"}
- data:{"content":{"answer":{"payload":{},"text":"你好,我是饭饭,很高兴你能使用QA"},"qaId":"qa_9605"},"contentType":"aiseek/qa","last":true,"payload":{},"role":"assistant"}
- data: [DONE]
- */
- export interface ICompleteCallback {
- content: string;
- reasoningContent?: string;
- body: Record<string,any>
- }
- type TContentType = "text/plain" | "aiseek/qa" | 'application/json' | 'aiseek/audio_chunk' | 'aiseek/thinking' | 'aiseek/function_call' | 'aiseek/multimodal'
- type TJsonMessage = {contentType: TContentType, content: string | any, reasoningContent: string}
- export default class JsonChunkParser {
- private buffer: string; // 用于存储未完成的 JSON 字符串
- private complete: (data: ICompleteCallback) => void;
- constructor() {
- this.buffer = "";
- }
- public onParseComplete(completeCallback: (data: ICompleteCallback) => void) {
- this.complete = completeCallback;
- }
- // 解析接收到的 chunk
- public parseChunk(chunk: string, onParsed: (json: ICompleteCallback) => void): void {
- // 将新接收到的 chunk 添加到 buffer
- this.buffer += chunk;
- // 按换行符分割字符串
- const lines = this.buffer.split("\n");
- let receivedJsonBody = {}
- let combinedContent: string[] = []; // 用于合并 content 字段
- let combinedReasoningContent: string[] = []; // 用于合并 reasoner 字段
-
- // 遍历每一行
- for (let i = 0; i < lines.length; i++) {
- const line = lines[i].trim(); // 去除前后空格
- if (line) {
- // 如果行不为空
- if (line === "data:[DONE]") {
- // 如果遇到 DONE,合并并调用 onParsed
- if (combinedContent.length > 0) {
- onParsed({ content: combinedContent.join("") , body: receivedJsonBody}); // 合并 content
- }
- this.complete({ content: combinedContent.join(""), body: receivedJsonBody });
- this.buffer = ""; // 清空 buffer
- return; // 结束解析
- }
- try {
- // 处理 data: data: 前缀
- if (line.startsWith("data:")) {
- const jsonStr = line.substring(5); // 移除 "data:" 前缀
- const json: TJsonMessage = JSON.parse(jsonStr);
- receivedJsonBody = json
- // 文本回复
- if(json.contentType === "text/plain") {
- if (json.content) {
- combinedContent.push(json.content as string); // 收集 content 字段
- }
- if (json.reasoningContent) {
- combinedReasoningContent.push(json.reasoningContent); // 收集 content 字段
- }
- }
- // QA 回复
- else if(json.contentType === "aiseek/qa") {
- if (json.content.answer?.text) {
- combinedContent.push(json.content.answer.text); // 收集 QA 的 answer 文本
- }
- }
-
- }
- } catch (error) {
- // 如果解析失败,说明当前行不是完整的 JSON
- // 将当前行保留在 buffer 中,等待下一个 chunk
- this.buffer = lines.slice(i).join("\n"); // 更新 buffer
- return; // 直接返回,等待下一个 chunk
- }
- }
- }
- // 如果当前 chunk 解析完毕且有合并的内容,调用 onParsed
- if (combinedContent.length > 0) {
- onParsed({ content: combinedContent.join(""), body: receivedJsonBody }); // 合并并输出
- }
- // if (combinedReasoningContent.length > 0) {
- // onParsed({ reasoningContent: combinedReasoningContent.join(""), body: receivedJsonBody , body: receivedJsonBody }); // 合并并输出
- // }
- // 清空 buffer
- this.buffer = "";
- }
- }
|