byt-lingxiao-ai 0.2.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/AiMessage.vue +358 -3
- package/components/ChatAvatar.vue +1 -1
- package/components/ChatMessageList.vue +5 -0
- package/components/ChatWindow.vue +4 -2
- package/components/ChatWindowDialog.vue +5 -0
- package/components/config/index.js +4 -0
- package/components/mixins/messageMixin.js +20 -9
- package/components/mixins/webSocketMixin.js +3 -1
- package/components/utils/StreamParser.js +67 -39
- package/dist/index.common.js +29626 -2953
- package/dist/index.common.js.map +1 -1
- package/dist/index.css +11 -1
- package/dist/index.umd.js +29616 -2943
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/index.umd.min.js.map +1 -1
- package/package.json +5 -3
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SSE (Server-Sent Events) 流解析器
|
|
3
|
-
* 优化点:
|
|
4
|
-
* 1. 使用状态机模式处理标签解析
|
|
5
|
-
* 2. 批量更新减少DOM操作
|
|
6
|
-
* 3. 内存优化:避免频繁字符串拼接
|
|
7
|
-
* 4. 可配置的更新频率
|
|
8
|
-
*/
|
|
9
1
|
export class StreamParser {
|
|
10
2
|
constructor(options = {}) {
|
|
11
3
|
this.options = {
|
|
@@ -46,6 +38,23 @@ export class StreamParser {
|
|
|
46
38
|
this.metrics.chunks++;
|
|
47
39
|
this.buffer += chunk;
|
|
48
40
|
|
|
41
|
+
if (this.options.debug) {
|
|
42
|
+
console.log('[StreamParser] 收到chunk:', chunk.substring(0, 100));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 尝试解析为 SSE 格式
|
|
46
|
+
if (this.buffer.includes('data:')) {
|
|
47
|
+
this.processSSEFormat(callback);
|
|
48
|
+
} else {
|
|
49
|
+
// 直接处理纯文本流
|
|
50
|
+
this.processPlainText(callback);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 处理标准 SSE 格式
|
|
56
|
+
*/
|
|
57
|
+
processSSEFormat(callback) {
|
|
49
58
|
// SSE 格式:事件由双换行符分隔
|
|
50
59
|
const events = this.buffer.split('\n\n');
|
|
51
60
|
|
|
@@ -56,15 +65,33 @@ export class StreamParser {
|
|
|
56
65
|
for (const event of events) {
|
|
57
66
|
if (event.trim()) {
|
|
58
67
|
this.metrics.events++;
|
|
59
|
-
this.
|
|
68
|
+
this.processSSEEvent(event, callback);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 处理纯文本流格式
|
|
75
|
+
*/
|
|
76
|
+
processPlainText(callback) {
|
|
77
|
+
const content = this.buffer;
|
|
78
|
+
this.buffer = ''; // 清空缓冲区
|
|
79
|
+
|
|
80
|
+
if (content) {
|
|
81
|
+
this.metrics.chars += content.length;
|
|
82
|
+
|
|
83
|
+
if (this.options.debug) {
|
|
84
|
+
console.log('[StreamParser] 处理纯文本:', content);
|
|
60
85
|
}
|
|
86
|
+
|
|
87
|
+
this.parseContent(content, callback);
|
|
61
88
|
}
|
|
62
89
|
}
|
|
63
90
|
|
|
64
91
|
/**
|
|
65
92
|
* 处理单个SSE事件
|
|
66
93
|
*/
|
|
67
|
-
|
|
94
|
+
processSSEEvent(eventStr, callback) {
|
|
68
95
|
const lines = eventStr.split('\n');
|
|
69
96
|
|
|
70
97
|
for (const line of lines) {
|
|
@@ -83,11 +110,16 @@ export class StreamParser {
|
|
|
83
110
|
|
|
84
111
|
if (content) {
|
|
85
112
|
this.metrics.chars += content.length;
|
|
113
|
+
|
|
114
|
+
if (this.options.debug) {
|
|
115
|
+
console.log('[StreamParser] 解析SSE内容:', content);
|
|
116
|
+
}
|
|
117
|
+
|
|
86
118
|
this.parseContent(content, callback);
|
|
87
119
|
}
|
|
88
120
|
} catch (error) {
|
|
89
121
|
if (this.options.debug) {
|
|
90
|
-
console.warn('[StreamParser] JSON解析失败:', data);
|
|
122
|
+
console.warn('[StreamParser] JSON解析失败:', data, error);
|
|
91
123
|
}
|
|
92
124
|
}
|
|
93
125
|
}
|
|
@@ -158,15 +190,16 @@ export class StreamParser {
|
|
|
158
190
|
handleTag(tag) {
|
|
159
191
|
const tagName = tag.toLowerCase();
|
|
160
192
|
|
|
193
|
+
if (this.options.debug) {
|
|
194
|
+
console.log('[StreamParser] 处理标签:', tag);
|
|
195
|
+
}
|
|
196
|
+
|
|
161
197
|
if (tagName === '<think>') {
|
|
162
198
|
this.status = 'thinking';
|
|
163
199
|
} else if (tagName === '</think>') {
|
|
164
200
|
this.status = 'output';
|
|
165
201
|
}
|
|
166
202
|
// 可扩展:支持更多标签类型
|
|
167
|
-
// else if (tagName.startsWith('<code')) {
|
|
168
|
-
// this.status = 'code';
|
|
169
|
-
// }
|
|
170
203
|
}
|
|
171
204
|
|
|
172
205
|
/**
|
|
@@ -198,7 +231,10 @@ export class StreamParser {
|
|
|
198
231
|
* 立即刷新缓冲区
|
|
199
232
|
*/
|
|
200
233
|
flush(callback) {
|
|
201
|
-
if (!callback)
|
|
234
|
+
if (!callback) {
|
|
235
|
+
console.warn('[StreamParser] flush: callback 为空');
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
202
238
|
|
|
203
239
|
const hasThinking = this.thinkingBuffer.length > 0;
|
|
204
240
|
const hasContent = this.contentBuffer.length > 0;
|
|
@@ -212,12 +248,26 @@ export class StreamParser {
|
|
|
212
248
|
status: this.status
|
|
213
249
|
};
|
|
214
250
|
|
|
251
|
+
if (this.options.debug) {
|
|
252
|
+
console.log('[StreamParser] 刷新缓冲区:', {
|
|
253
|
+
thinking: result.thinking?.length || 0,
|
|
254
|
+
content: result.content?.length || 0,
|
|
255
|
+
status: result.status,
|
|
256
|
+
thinkingPreview: result.thinking?.substring(0, 50),
|
|
257
|
+
contentPreview: result.content?.substring(0, 50)
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
215
261
|
// 清空缓冲区
|
|
216
262
|
this.thinkingBuffer = [];
|
|
217
263
|
this.contentBuffer = [];
|
|
218
264
|
|
|
219
265
|
// 回调更新
|
|
220
|
-
|
|
266
|
+
try {
|
|
267
|
+
callback(result);
|
|
268
|
+
} catch (error) {
|
|
269
|
+
console.error('[StreamParser] 回调执行错误:', error);
|
|
270
|
+
}
|
|
221
271
|
}
|
|
222
272
|
|
|
223
273
|
/**
|
|
@@ -252,26 +302,4 @@ export class StreamParser {
|
|
|
252
302
|
}
|
|
253
303
|
this.reset();
|
|
254
304
|
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* 使用示例:
|
|
259
|
-
*
|
|
260
|
-
* const parser = new StreamParser({ debug: true });
|
|
261
|
-
*
|
|
262
|
-
* // 处理流
|
|
263
|
-
* for await (const chunk of stream) {
|
|
264
|
-
* parser.processChunk(chunk, (result) => {
|
|
265
|
-
* if (result.thinking) {
|
|
266
|
-
* message.thinking += result.thinking;
|
|
267
|
-
* }
|
|
268
|
-
* if (result.content) {
|
|
269
|
-
* message.content += result.content;
|
|
270
|
-
* }
|
|
271
|
-
* this.$forceUpdate();
|
|
272
|
-
* });
|
|
273
|
-
* }
|
|
274
|
-
*
|
|
275
|
-
* // 完成
|
|
276
|
-
* parser.finish();
|
|
277
|
-
*/
|
|
305
|
+
}
|