|
@@ -33,7 +33,10 @@ data: [DONE]
|
|
export interface ICompleteCallback {
|
|
export interface ICompleteCallback {
|
|
content: string;
|
|
content: string;
|
|
reasoningContent?: string;
|
|
reasoningContent?: string;
|
|
- body: Record<string,any>
|
|
|
|
|
|
+ body: null| (TJsonMessage & Record<string,any>)
|
|
|
|
+}
|
|
|
|
+export interface IAudioPared {
|
|
|
|
+ body: null| (TJsonMessage & Record<string,any>)
|
|
}
|
|
}
|
|
|
|
|
|
type TContentType = "text/plain" | "aiseek/qa" | 'application/json' | 'aiseek/audio_chunk' | 'aiseek/thinking' | 'aiseek/function_call' | 'aiseek/multimodal'
|
|
type TContentType = "text/plain" | "aiseek/qa" | 'application/json' | 'aiseek/audio_chunk' | 'aiseek/thinking' | 'aiseek/function_call' | 'aiseek/multimodal'
|
|
@@ -49,83 +52,94 @@ export default class JsonChunkParser {
|
|
public onParseComplete(completeCallback: (data: ICompleteCallback) => void) {
|
|
public onParseComplete(completeCallback: (data: ICompleteCallback) => void) {
|
|
this.complete = completeCallback;
|
|
this.complete = completeCallback;
|
|
}
|
|
}
|
|
|
|
+
|
|
// 解析接收到的 chunk
|
|
// 解析接收到的 chunk
|
|
- public parseChunk(chunk: string, onParsed: (json: ICompleteCallback) => void): void {
|
|
|
|
|
|
+ public parseChunk(
|
|
|
|
+ chunk: string,
|
|
|
|
+ onParsed: (json: ICompleteCallback) => void,
|
|
|
|
+ onAudioParsed: (json: IAudioPared) => void
|
|
|
|
+ ): void {
|
|
// 将新接收到的 chunk 添加到 buffer
|
|
// 将新接收到的 chunk 添加到 buffer
|
|
this.buffer += chunk;
|
|
this.buffer += chunk;
|
|
|
|
|
|
// 按换行符分割字符串
|
|
// 按换行符分割字符串
|
|
const lines = this.buffer.split("\n");
|
|
const lines = this.buffer.split("\n");
|
|
- let receivedJsonBody = {}
|
|
|
|
- let audio = ''
|
|
|
|
- let combinedContent: string[] = []; // 用于合并 content 字段
|
|
|
|
- let combinedReasoningContent: string[] = []; // 用于合并 reasoner 字段
|
|
|
|
|
|
+ let receivedJsonBody:any = {}
|
|
|
|
+ let audio = '' // todo: audio 是否需要拼接保留??
|
|
|
|
+ let combinedContentText: string[] = []; // 用于合并 content 字段
|
|
|
|
|
|
// 遍历每一行
|
|
// 遍历每一行
|
|
for (let i = 0; i < lines.length; i++) {
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const line = lines[i].trim(); // 去除前后空格
|
|
const line = lines[i].trim(); // 去除前后空格
|
|
-
|
|
|
|
|
|
+ // 如果行不为空
|
|
if (line) {
|
|
if (line) {
|
|
- // 如果行不为空
|
|
|
|
- if (line === "data:[DONE]") {
|
|
|
|
- // 如果遇到 DONE,合并并调用 onParsed
|
|
|
|
- if (combinedContent.length > 0) {
|
|
|
|
- onParsed({ content: combinedContent.join("") , body: receivedJsonBody}); // 合并 content
|
|
|
|
|
|
+ // 如果遇到 DONE,合并并调用 onParsed
|
|
|
|
+ // 同时兼容 "data:[DONE]" 和 "data: [DONE]" 两种格式
|
|
|
|
+ if (line.startsWith("data:")) {
|
|
|
|
+ const dataPayload = line.substring(5).trim();
|
|
|
|
+ if (dataPayload === "[DONE]") {
|
|
|
|
+ if (combinedContentText.length > 0 || audio.length > 0) {
|
|
|
|
+ onParsed({ content: combinedContentText.join("") , body: receivedJsonBody});
|
|
|
|
+ }
|
|
|
|
+ this.complete({ content: combinedContentText.join(""), body: receivedJsonBody });
|
|
|
|
+ this.buffer = "";
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
- this.complete({ content: combinedContent.join(""), body: receivedJsonBody });
|
|
|
|
- this.buffer = ""; // 清空 buffer
|
|
|
|
- return; // 结束解析
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+ // 尝试解析为 json 对象
|
|
try {
|
|
try {
|
|
// 处理 data: data: 前缀
|
|
// 处理 data: data: 前缀
|
|
if (line.startsWith("data:")) {
|
|
if (line.startsWith("data:")) {
|
|
- const jsonStr = line.substring(5); // 移除 "data:" 前缀
|
|
|
|
|
|
+ const jsonStr = line.substring(5).trim(); // 移除 "data:" 前缀并去除空格
|
|
const json: TJsonMessage = JSON.parse(jsonStr);
|
|
const json: TJsonMessage = JSON.parse(jsonStr);
|
|
receivedJsonBody = json
|
|
receivedJsonBody = json
|
|
- console.log(555555, receivedJsonBody)
|
|
|
|
|
|
+ // console.log(11111, receivedJsonBody)
|
|
// 文本回复
|
|
// 文本回复
|
|
if(json.contentType === "text/plain") {
|
|
if(json.contentType === "text/plain") {
|
|
if (json.content) {
|
|
if (json.content) {
|
|
- combinedContent.push(json.content as string); // 收集 content 字段
|
|
|
|
|
|
+ combinedContentText.push(json.content as string); // 收集 content 字段
|
|
}
|
|
}
|
|
- if (json.reasoningContent) {
|
|
|
|
- combinedReasoningContent.push(json.reasoningContent); // 收集 content 字段
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
// QA 回复
|
|
// QA 回复
|
|
else if(json.contentType === "aiseek/qa") {
|
|
else if(json.contentType === "aiseek/qa") {
|
|
if (json.content.answer?.text) {
|
|
if (json.content.answer?.text) {
|
|
- combinedContent.push(json.content.answer.text); // 收集 QA 的 answer 文本
|
|
|
|
|
|
+ combinedContentText.push(json.content.answer.text); // 收集 QA 的 answer 文本
|
|
}
|
|
}
|
|
- }else if(json.contentType === "aiseek/audio_chunk"){
|
|
|
|
- if(json.content.sentenceBegin){
|
|
|
|
- combinedContent.push(json.content.sentenceBegin); // 收集 sentenceBegin 文本
|
|
|
|
|
|
+ }
|
|
|
|
+ // 语音回复消息
|
|
|
|
+ else if(json.contentType === "aiseek/audio_chunk"){
|
|
|
|
+ if(json.content?.sentenceBegin?.length){
|
|
|
|
+ combinedContentText.push(json.content.sentenceBegin); // 收集 sentenceBegin 文本
|
|
}
|
|
}
|
|
|
|
+ // 收集 audio base64 格式文本
|
|
if(json.content.audio){
|
|
if(json.content.audio){
|
|
- audio+=audio;
|
|
|
|
|
|
+ audio += json.content.audio;
|
|
|
|
+ }
|
|
|
|
+ // 音频流单独处理回调
|
|
|
|
+ // 由于 语音和文本长度是不同步的,只要有语音就要调用一次 onAudioParsed
|
|
|
|
+ if (audio.length >= 0) {
|
|
|
|
+ onAudioParsed({body: json }); // 合并并输出
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
}
|
|
}
|
|
} catch (error) {
|
|
} catch (error) {
|
|
- // 如果解析失败,说明当前行不是完整的 JSON
|
|
|
|
|
|
+ // 如果解析失败,说明当前行不是完整的 JSON(通常是最后一行)
|
|
|
|
+ // 在返回前,先把已成功解析并累计的文本吐出去,避免丢失
|
|
|
|
+ if (combinedContentText.length > 0) {
|
|
|
|
+ onParsed({ content: combinedContentText.join(''), body: receivedJsonBody });
|
|
|
|
+ }
|
|
// 将当前行保留在 buffer 中,等待下一个 chunk
|
|
// 将当前行保留在 buffer 中,等待下一个 chunk
|
|
- this.buffer = lines.slice(i).join("\n"); // 更新 buffer
|
|
|
|
|
|
+ this.buffer = lines.slice(i).join("\n");
|
|
return; // 直接返回,等待下一个 chunk
|
|
return; // 直接返回,等待下一个 chunk
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- // 如果当前 chunk 解析完毕且有合并的内容,调用 onParsed
|
|
|
|
- if (combinedContent.length > 0 || audio.length >= 0) {
|
|
|
|
- onParsed({ content: combinedContent.join(""), body: receivedJsonBody }); // 合并并输出
|
|
|
|
|
|
+ // 如果当前 combinedContent有内容合并后 调用 onParsed
|
|
|
|
+ if (combinedContentText.length > 0) {
|
|
|
|
+ onParsed({ content: combinedContentText.join(''), body: receivedJsonBody }); // 合并并输出
|
|
}
|
|
}
|
|
|
|
|
|
- // if (combinedReasoningContent.length > 0) {
|
|
|
|
- // onParsed({ reasoningContent: combinedReasoningContent.join(""), body: receivedJsonBody , body: receivedJsonBody }); // 合并并输出
|
|
|
|
- // }
|
|
|
|
-
|
|
|
|
// 清空 buffer
|
|
// 清空 buffer
|
|
this.buffer = "";
|
|
this.buffer = "";
|
|
}
|
|
}
|