foliko 1.0.86 → 1.0.87

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.
@@ -0,0 +1,63 @@
1
+ /**
2
+ * 测试 chat 和 chatStream 的差异
3
+ */
4
+
5
+ const { Framework } = require('../src');
6
+ const AIPlugin = require('../plugins/ai-plugin');
7
+ const SessionPlugin = require('../plugins/session-plugin');
8
+ require('dotenv').config();
9
+
10
+ async function main() {
11
+ console.log('=== 测试 chat vs chatStream 差异 ===\n');
12
+
13
+ const framework = new Framework({ debug: false });
14
+
15
+ await framework.loadPlugin(
16
+ new AIPlugin({
17
+ provider: 'minimax',
18
+ model: 'MiniMax-M2.7',
19
+ apiKey: process.env.MINIMAX_API_KEY || 'your-api-key',
20
+ })
21
+ );
22
+ await framework.loadPlugin(new SessionPlugin());
23
+
24
+ const systemPrompt = `你是一个有帮助的助手。`;
25
+
26
+ const agent = framework.createAgent({
27
+ name: 'TestAgent',
28
+ systemPrompt: systemPrompt,
29
+ });
30
+
31
+ const sessionId = 'test_session';
32
+ const sessionPlugin = framework.pluginManager.get('session');
33
+ sessionPlugin.getOrCreateSession(sessionId, { metadata: {} });
34
+
35
+ // 测试 chat
36
+ console.log('--- Test 1: chat() ---');
37
+ const result1 = await agent.chat('你好,介绍一下自己', { sessionId });
38
+ console.log('chat result.message length:', result1.message?.length);
39
+ console.log('chat result.message:', result1.message?.substring(0, 100));
40
+
41
+ // 测试 chatStream
42
+ console.log('\n--- Test 2: chatStream() ---');
43
+ let streamText = '';
44
+ for await (const chunk of agent.chatStream('今天天气怎么样?', { sessionId })) {
45
+ if (chunk.type === 'text') {
46
+ streamText += chunk.text;
47
+ }
48
+ }
49
+ console.log('chatStream full text length:', streamText.length);
50
+ console.log('chatStream full text:', streamText.substring(0, 100));
51
+
52
+ // 测试更多轮对话后的 chat
53
+ console.log('\n--- Test 3: 多轮后 chat() ---');
54
+ await agent.chat('再说一次你的名字', { sessionId });
55
+ const result3 = await agent.chat('我叫什么?', { sessionId });
56
+ console.log('chat result.message length:', result3.message?.length);
57
+ console.log('chat result.message:', result3.message?.substring(0, 100));
58
+
59
+ await framework.destroy();
60
+ console.log('\n[Done]');
61
+ }
62
+
63
+ main().catch(console.error);
@@ -0,0 +1,60 @@
1
+ /**
2
+ * 测试并发 chat 调用
3
+ */
4
+
5
+ const { Framework } = require('../src');
6
+ const AIPlugin = require('../plugins/ai-plugin');
7
+ const SessionPlugin = require('../plugins/session-plugin');
8
+ require('dotenv').config();
9
+
10
+ async function main() {
11
+ console.log('=== 测试并发 chat 调用 ===\n');
12
+
13
+ const framework = new Framework({ debug: false });
14
+
15
+ await framework.loadPlugin(
16
+ new AIPlugin({
17
+ provider: 'minimax',
18
+ model: 'MiniMax-M2.7',
19
+ apiKey: process.env.MINIMAX_API_KEY || 'your-api-key',
20
+ })
21
+ );
22
+ await framework.loadPlugin(new SessionPlugin());
23
+
24
+ const systemPrompt = `你是一个微信助手。`;
25
+
26
+ // 创建一个 agent(模拟单个用户)
27
+ const agent = framework.createAgent({
28
+ name: 'WeixinAgent',
29
+ systemPrompt: systemPrompt,
30
+ });
31
+
32
+ const sessionId = 'weixin_test_user';
33
+ const sessionPlugin = framework.pluginManager.get('session');
34
+ sessionPlugin.getOrCreateSession(sessionId, { metadata: { platform: 'weixin' } });
35
+
36
+ // 模拟 3 个并发 chat 调用
37
+ console.log('\n--- 并发发送 3 条消息 ---');
38
+
39
+ const results = await Promise.all([
40
+ agent.chat('你好', { sessionId }),
41
+ agent.chat('今天天气怎么样?', { sessionId }),
42
+ agent.chat('帮我查一下天气', { sessionId }),
43
+ ]);
44
+
45
+ console.log('\n--- 结果 ---');
46
+ for (let i = 0; i < results.length; i++) {
47
+ const r = results[i];
48
+ console.log(`[${i}] message length: ${r.message?.length || 'EMPTY'}`);
49
+ console.log(`[${i}] message: ${r.message?.substring(0, 50) || 'EMPTY'}...`);
50
+ }
51
+
52
+ // 检查 _messages 状态
53
+ const chatHandler = agent._chatHandler;
54
+ console.log(`\n[DEBUG] _messages count: ${chatHandler?._messages.length}`);
55
+
56
+ await framework.destroy();
57
+ console.log('\n[Done]');
58
+ }
59
+
60
+ main().catch(console.error);
@@ -0,0 +1,77 @@
1
+ /**
2
+ * 测试长对话压缩场景
3
+ */
4
+
5
+ const { Framework } = require('../src');
6
+ const AIPlugin = require('../plugins/ai-plugin');
7
+ const SessionPlugin = require('../plugins/session-plugin');
8
+ require('dotenv').config();
9
+
10
+ async function main() {
11
+ console.log('=== 测试长对话压缩场景 ===\n');
12
+
13
+ const framework = new Framework({ debug: false });
14
+
15
+ await framework.loadPlugin(
16
+ new AIPlugin({
17
+ provider: 'minimax',
18
+ model: 'MiniMax-M2.7',
19
+ apiKey: process.env.MINIMAX_API_KEY || 'your-api-key',
20
+ })
21
+ );
22
+ await framework.loadPlugin(new SessionPlugin());
23
+
24
+ const systemPrompt = `你是一个有帮助的助手。`;
25
+
26
+ const agent = framework.createAgent({
27
+ name: 'TestAgent',
28
+ systemPrompt: systemPrompt,
29
+ });
30
+
31
+ const sessionId = 'test_session';
32
+ const sessionPlugin = framework.pluginManager.get('session');
33
+ sessionPlugin.getOrCreateSession(sessionId, { metadata: {} });
34
+
35
+ // 发送多条长消息来触发压缩
36
+ const longContent = `请详细描述以下内容:人工智能的发展历史、现状和未来趋势。包括机器学习、深度学习、自然语言处理等核心技术的发展历程,以及在各个行业中的应用场景。同时分析当前面临的挑战,如数据隐私、算法偏见、能源消耗等问题。`;
37
+
38
+ console.log('--- 发送 15 条长消息 ---');
39
+ for (let i = 1; i <= 15; i++) {
40
+ try {
41
+ const result = await agent.chat(`${longContent}\n\n[消息 ${i}]`, { sessionId });
42
+ const msgLen = result.message?.length || 0;
43
+ console.log(`[${i}] message length: ${msgLen}, success: ${result.success}`);
44
+
45
+ if (!result.message) {
46
+ console.log(`[ERROR] message is empty!`);
47
+ console.log(`[DEBUG] result:`, JSON.stringify(result, null, 2));
48
+ break;
49
+ }
50
+ } catch (err) {
51
+ console.error(`[${i}] Error:`, err.message);
52
+ }
53
+ }
54
+
55
+ // 检查压缩统计
56
+ const chatHandler = agent._chatHandler;
57
+ console.log(`\n[DEBUG] compression count: ${chatHandler?._compressionCount}`);
58
+ console.log(`[DEBUG] _messages count: ${chatHandler?._messages.length}`);
59
+
60
+ // 压缩后再发一条
61
+ console.log('\n--- 压缩后发送测试 ---');
62
+ try {
63
+ const result = await agent.chat('你好,压缩后你还认识我吗?', { sessionId });
64
+ console.log(`message length: ${result.message?.length || 0}`);
65
+ console.log(`message: ${result.message?.substring(0, 100)}`);
66
+ if (!result.message) {
67
+ console.log(`[ERROR] message is empty after compression!`);
68
+ }
69
+ } catch (err) {
70
+ console.error(`Error:`, err.message);
71
+ }
72
+
73
+ await framework.destroy();
74
+ console.log('\n[Done]');
75
+ }
76
+
77
+ main().catch(console.error);
@@ -0,0 +1,93 @@
1
+ /**
2
+ * 模拟微信持续聊天场景
3
+ * 测试多轮对话后 result.message 是否为空
4
+ */
5
+
6
+ const { Framework } = require('../src');
7
+ const AIPlugin = require('../plugins/ai-plugin');
8
+ const SessionPlugin = require('../plugins/session-plugin');
9
+ require('dotenv').config();
10
+
11
+ async function main() {
12
+ console.log('=== 模拟微信持续聊天场景 ===\n');
13
+
14
+ const framework = new Framework({ debug: false });
15
+
16
+ await framework.loadPlugin(
17
+ new AIPlugin({
18
+ provider: 'minimax',
19
+ model: 'MiniMax-M2.7',
20
+ apiKey: process.env.MINIMAX_API_KEY || 'your-api-key',
21
+ })
22
+ );
23
+
24
+ // 加载 session 插件
25
+ await framework.loadPlugin(new SessionPlugin());
26
+
27
+ // 模拟微信的 system prompt
28
+ const systemPrompt = `你是一个微信助手。`;
29
+
30
+ // 创建 Agent(模拟 _getSessionAgent)
31
+ const agent = framework.createAgent({
32
+ name: 'WeixinAgent',
33
+ systemPrompt: systemPrompt,
34
+ });
35
+
36
+ // 模拟 session
37
+ const sessionPlugin = framework.pluginManager.get('session');
38
+ const sessionId = 'weixin_test_user';
39
+
40
+ // 初始化 session
41
+ sessionPlugin.getOrCreateSession(sessionId, {
42
+ metadata: { platform: 'weixin', userId: 'test_user' },
43
+ });
44
+
45
+ // 模拟微信的 _processChat
46
+ const processChat = async (userId, text) => {
47
+ console.log(`\n[User] ${text}`);
48
+
49
+ try {
50
+ const result = await agent.chat(text, { sessionId: `weixin_${userId}` });
51
+ const message = result.message || '';
52
+
53
+ if (message) {
54
+ console.log(`[Agent] ${message.substring(0, 100)}${message.length > 100 ? '...' : ''}`);
55
+ console.log(`[OK] message length: ${message.length}`);
56
+ } else {
57
+ console.log(`[WARNING] result.message is empty!`);
58
+ console.log(`[DEBUG] result:`, JSON.stringify(result, null, 2).substring(0, 500));
59
+ }
60
+
61
+ // 检查 _messages 状态
62
+ const chatHandler = agent._chatHandler;
63
+ if (chatHandler) {
64
+ console.log(`[DEBUG] _messages count: ${chatHandler._messages.length}`);
65
+ }
66
+
67
+ return message;
68
+ } catch (err) {
69
+ console.error(`[Error]`, err.message);
70
+ return '';
71
+ }
72
+ };
73
+
74
+ // 模拟多轮对话
75
+ await processChat('user1', '你好');
76
+ await processChat('user1', '今天天气怎么样?');
77
+ await processChat('user1', '帮我查一下北京的人口');
78
+ await processChat('user1', '再说一次北京的人口');
79
+
80
+ console.log('\n--- 测试压缩场景 ---');
81
+ // 发送更多消息触发压缩
82
+ for (let i = 0; i < 5; i++) {
83
+ await processChat('user1', `这是第 ${i + 1} 条测试消息`);
84
+ }
85
+
86
+ // 压缩后再发送一条
87
+ await processChat('user1', '你好,你还记得我吗?');
88
+
89
+ console.log('\n[Done]');
90
+ await framework.destroy();
91
+ }
92
+
93
+ main().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foliko",
3
- "version": "1.0.86",
3
+ "version": "1.0.87",
4
4
  "description": "简约的插件化 Agent 框架",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -335,6 +335,11 @@ class SessionPlugin extends Plugin {
335
335
  addMessage(sessionId, message) {
336
336
  const session = this.getSession(sessionId)
337
337
  if (session) {
338
+ // 基础消息验证
339
+ if (!message || !message.role || !message.content) {
340
+ log.warn('Invalid message format, skipping:', { sessionId, message });
341
+ return session;
342
+ }
338
343
  session.messages.push(message)
339
344
  session.lastActive = new Date()
340
345
 
@@ -349,6 +354,22 @@ class SessionPlugin extends Plugin {
349
354
  return session
350
355
  }
351
356
 
357
+ /**
358
+ * 整体替换会话消息(用于压缩后同步)
359
+ * @param {string} sessionId - 会话 ID
360
+ * @param {Array} messages - 新的消息数组
361
+ */
362
+ replaceMessages(sessionId, messages) {
363
+ const session = this.getSession(sessionId)
364
+ if (session) {
365
+ session.messages = messages || []
366
+ session.lastActive = new Date()
367
+ // 持久化到 storage
368
+ this._saveToStorage(session)
369
+ }
370
+ return session
371
+ }
372
+
352
373
  /**
353
374
  * 获取会话历史
354
375
  */
@@ -123,6 +123,10 @@ class AgentChatHandler extends EventEmitter {
123
123
 
124
124
  // Session 历史存储配置
125
125
  this._sessionHistoryKey = config.sessionHistoryKey || 'chat_history';
126
+
127
+ // Per-Session 队列管理
128
+ this._sessionQueues = new Map(); // sessionId -> Queue of {message, options, resolve, reject}
129
+ this._processingSessions = new Set(); // sessionId -> processing flag
126
130
  }
127
131
 
128
132
  /**
@@ -139,6 +143,7 @@ class AgentChatHandler extends EventEmitter {
139
143
  if (!sessionPlugin) return [];
140
144
 
141
145
  const messages = sessionPlugin.getHistory(sessionId);
146
+ // 直接返回,保存时已清理,数据应该是干净的
142
147
  return messages || [];
143
148
  } catch (err) {
144
149
  // 忽略加载错误
@@ -153,28 +158,78 @@ class AgentChatHandler extends EventEmitter {
153
158
  * @private
154
159
  */
155
160
  _saveHistoryToSession(sessionId, messages) {
156
- if (!sessionId || !this.agent?.framework || !messages || messages.length === 0) return;
161
+ if (!sessionId || !this.agent?.framework || !messages) return;
157
162
 
158
163
  try {
159
164
  const sessionPlugin = this.agent.framework.pluginManager.get('session');
160
165
  if (!sessionPlugin) return;
161
166
 
162
- // 获取 session 中已有消息数量,避免重复添加
163
- const existingHistory = sessionPlugin.getHistory(sessionId);
164
- const existingCount = existingHistory.length;
165
-
166
- // 只添加新消息(避免重复添加已加载的历史)
167
- const simpleMessages = messages;
168
-
169
- const newMessages = simpleMessages.slice(existingCount);
170
- for (const msg of newMessages) {
171
- sessionPlugin.addMessage(sessionId, msg);
172
- }
167
+ // 清理消息格式后整体替换
168
+ const cleanedMessages = prepareMessagesForAPI(messages);
169
+ sessionPlugin.replaceMessages(sessionId, cleanedMessages);
173
170
  } catch (err) {
174
171
  // 忽略保存错误
175
172
  }
176
173
  }
177
174
 
175
+ /**
176
+ * 将消息加入队列(Per-Session)
177
+ * @param {string} sessionId - 会话 ID
178
+ * @param {string|Object} message - 消息
179
+ * @param {Object} options - 选项
180
+ * @returns {Promise}
181
+ */
182
+ enqueue(sessionId, message, options = {}) {
183
+ if (!this._sessionQueues.has(sessionId)) {
184
+ this._sessionQueues.set(sessionId, []);
185
+ }
186
+ const queue = this._sessionQueues.get(sessionId);
187
+
188
+ if (this._processingSessions.has(sessionId)) {
189
+ // 已在处理中,加入队列
190
+ return new Promise((resolve, reject) => {
191
+ queue.push({ message, options, resolve, reject });
192
+ });
193
+ }
194
+
195
+ // 直接处理
196
+ return this._processWithSession(sessionId, message, options);
197
+ }
198
+
199
+ /**
200
+ * 处理单个 session 的消息
201
+ * @param {string} sessionId - 会话 ID
202
+ * @param {string|Object} message - 消息
203
+ * @param {Object} options - 选项
204
+ * @returns {Promise}
205
+ * @private
206
+ */
207
+ async _processWithSession(sessionId, message, options) {
208
+ this._processingSessions.add(sessionId);
209
+ try {
210
+ return await this._doChat(message, options);
211
+ } finally {
212
+ this._processingSessions.delete(sessionId);
213
+ // 处理队列中的下一条
214
+ setImmediate(() => this._drainQueue(sessionId));
215
+ }
216
+ }
217
+
218
+ /**
219
+ * 消费队列中的下一条消息
220
+ * @param {string} sessionId - 会话 ID
221
+ * @private
222
+ */
223
+ _drainQueue(sessionId) {
224
+ const queue = this._sessionQueues.get(sessionId);
225
+ if (!queue || queue.length === 0) return;
226
+
227
+ const item = queue.shift();
228
+ this._processWithSession(sessionId, item.message, item.options)
229
+ .then(item.resolve)
230
+ .catch(item.reject);
231
+ }
232
+
178
233
  /**
179
234
  * 静态方法:保留接口兼容性(无实际作用)
180
235
  */
@@ -287,9 +342,10 @@ class AgentChatHandler extends EventEmitter {
287
342
  * 1. 如果启用了智能摘要且有 AI 客户端,对早期消息进行 AI 总结
288
343
  * 2. 否则使用简单裁剪 + 标记
289
344
  * 3. 支持超时控制,防止压缩阻塞太久
345
+ * @param {string} sessionId - 会话 ID(用于压缩后同步)
290
346
  * @private
291
347
  */
292
- async _compressContext() {
348
+ async _compressContext(sessionId) {
293
349
  // 如果已经有压缩在进行,返回现有的 Promise
294
350
  if (this._compressionInProgress && this._compressionPromise) {
295
351
  logger.debug('Compression already in progress, waiting for it...');
@@ -303,7 +359,7 @@ class AgentChatHandler extends EventEmitter {
303
359
  this._compressionInProgress = true;
304
360
 
305
361
  // 创建压缩 Promise,带超时控制
306
- this._compressionPromise = this._executeCompressionWithTimeout().finally(() => {
362
+ this._compressionPromise = this._executeCompressionWithTimeout(sessionId).finally(() => {
307
363
  this._compressionInProgress = false;
308
364
  this._compressionPromise = null;
309
365
  });
@@ -313,15 +369,16 @@ class AgentChatHandler extends EventEmitter {
313
369
 
314
370
  /**
315
371
  * 执行压缩(带超时)
372
+ * @param {string} sessionId - 会话 ID
316
373
  * @private
317
374
  */
318
- async _executeCompressionWithTimeout() {
375
+ async _executeCompressionWithTimeout(sessionId) {
319
376
  try {
320
- return await Promise.race([this._doCompress(), this._createTimeoutPromise()]);
377
+ return await Promise.race([this._doCompress(sessionId), this._createTimeoutPromise()]);
321
378
  } catch (err) {
322
379
  logger.warn('Compression failed:', err.message);
323
380
  // 压缩失败时使用简单的截断策略
324
- this._simpleCompress();
381
+ this._simpleCompress(sessionId);
325
382
  }
326
383
  }
327
384
 
@@ -358,9 +415,10 @@ class AgentChatHandler extends EventEmitter {
358
415
 
359
416
  /**
360
417
  * 执行实际压缩逻辑
418
+ * @param {string} sessionId - 会话 ID
361
419
  * @private
362
420
  */
363
- async _doCompress() {
421
+ async _doCompress(sessionId) {
364
422
  const systemMessages = this._messages.filter((m) => m.role === 'system');
365
423
  const otherMessages = this._messages.filter((m) => m.role !== 'system');
366
424
 
@@ -398,13 +456,19 @@ class AgentChatHandler extends EventEmitter {
398
456
  logger.info(
399
457
  `Context compressed (${this._compressionCount} times). Messages: ${this._messages.length}, Est. tokens: ${totalTokens}`
400
458
  );
459
+
460
+ // 压缩后立即同步到 session
461
+ if (sessionId) {
462
+ this._saveHistoryToSession(sessionId, this._messages);
463
+ }
401
464
  }
402
465
 
403
466
  /**
404
467
  * 简单压缩(当 AI 压缩失败时使用)
468
+ * @param {string} sessionId - 会话 ID
405
469
  * @private
406
470
  */
407
- _simpleCompress() {
471
+ _simpleCompress(sessionId) {
408
472
  const systemMessages = this._messages.filter((m) => m.role === 'system');
409
473
  const otherMessages = this._messages.filter((m) => m.role !== 'system');
410
474
  const recentMessages = otherMessages.slice(-this._keepRecentMessages);
@@ -425,6 +489,11 @@ class AgentChatHandler extends EventEmitter {
425
489
  logger.info(
426
490
  `Context simple compressed (${this._compressionCount} times). Messages: ${this._messages.length}`
427
491
  );
492
+
493
+ // 压缩后立即同步到 session
494
+ if (sessionId) {
495
+ this._saveHistoryToSession(sessionId, this._messages);
496
+ }
428
497
  }
429
498
 
430
499
  /**
@@ -653,15 +722,25 @@ ${truncatedContent}${truncatedNote}
653
722
  }
654
723
 
655
724
  /**
656
- * 发送消息(非流式)
725
+ * 发送消息(非流式)- 使用队列机制
657
726
  * @param {string|Object} message - 消息
658
727
  * @param {Object} options - 选项
659
728
  */
660
729
  async chat(message, options = {}) {
730
+ const sessionId = options.sessionId || null;
731
+ return this.enqueue(sessionId, message, options);
732
+ }
733
+
734
+ /**
735
+ * 执行实际聊天逻辑
736
+ * @param {string|Object} message - 消息
737
+ * @param {Object} options - 选项
738
+ * @private
739
+ */
740
+ async _doChat(message, options = {}) {
661
741
  const sessionId = options.sessionId || null;
662
742
  const context = { sessionId, isStream: false };
663
743
  const framework = this.agent.framework;
664
- const self = this; // 保存引用用于回调
665
744
 
666
745
  try {
667
746
  // 从 session 加载聊天历史
@@ -696,7 +775,7 @@ ${truncatedContent}${truncatedNote}
696
775
  `Context large (${totalTokens}/${this._maxContextTokens} tokens = msgs:${messagesTokens} + tools:${toolsTokens} + sys:${systemPromptTokens}), compressing...`
697
776
  );
698
777
  // 使用带超时控制的压缩
699
- await this._compressContext();
778
+ await this._compressContext(sessionId);
700
779
  }
701
780
 
702
781
  const maxSteps = options.maxSteps || this._maxSteps;
@@ -791,7 +870,7 @@ ${truncatedContent}${truncatedNote}
791
870
  `Context large (${totalTokens}/${this._maxContextTokens} tokens), compressing...`
792
871
  );
793
872
  // 流式调用时等待压缩完成(使用带超时控制的压缩)
794
- await this._compressContext();
873
+ await this._compressContext(sessionId);
795
874
  }
796
875
 
797
876
  const maxSteps = options.maxSteps || this._maxSteps;
@@ -1001,38 +1080,6 @@ ${truncatedContent}${truncatedNote}
1001
1080
  return cleaned;
1002
1081
  }
1003
1082
 
1004
- /**
1005
- * 清理消息格式
1006
- * @private
1007
- */
1008
- _cleanMessages(messages) {
1009
- return messages.map((msg) => {
1010
- if (!msg || typeof msg !== 'object') {
1011
- return { role: 'user', content: '' };
1012
- }
1013
-
1014
- const cleaned = {
1015
- role: msg.role || 'user',
1016
- };
1017
-
1018
- if (Array.isArray(msg.content)) {
1019
- cleaned.content = msg.content;
1020
- } else if (typeof msg.content === 'string') {
1021
- cleaned.content = msg.content;
1022
- } else if (typeof msg.content === 'object' && msg.content !== null) {
1023
- // 对象类型的 content(如 tool result),转为字符串
1024
- cleaned.content =
1025
- typeof msg.content === 'object' && msg.content.content !== undefined
1026
- ? String(msg.content.content)
1027
- : JSON.stringify(msg.content);
1028
- } else {
1029
- cleaned.content = msg.text || msg.input || '';
1030
- }
1031
-
1032
- return cleaned;
1033
- });
1034
- }
1035
-
1036
1083
  /**
1037
1084
  * 销毁
1038
1085
  */