claw-subagent-service 0.0.114 → 0.0.116

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claw-subagent-service",
3
- "version": "0.0.114",
3
+ "version": "0.0.116",
4
4
  "description": "虾说智能助手",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -29,6 +29,10 @@ class MessageHandler {
29
29
  this._groupConfigCache = new Map();
30
30
  this._defaultMaxRounds = 10;
31
31
  this._groupConfigCacheTTL = config.groupConfigCacheTTL || 60000; // 默认缓存 60 秒
32
+
33
+ // 消息合并相关
34
+ this._pendingMessages = new Map(); // 待合并的消息
35
+ this._messageMergeTimeout = 500; // 合并等待时间(毫秒)
32
36
  }
33
37
 
34
38
  /**
@@ -215,42 +219,102 @@ class MessageHandler {
215
219
  });
216
220
  }
217
221
 
218
- // 如果配置了代理地址,使用流式处理
219
- if (this.isStreamingEnabled) {
220
- try {
221
- await this.handleNormalMessageStream(msg);
222
- // 流式处理成功,群聊轮数 +1
223
- if (msg.conversationType === 3) {
224
- this._incrementGroupRoundCount(msg.targetId, maxRounds);
225
- }
226
- } catch (err) {
227
- this.log?.error(`[MessageHandler] 流式处理失败,回退到非流式: ${err.message}`);
228
- const reply = await this.handleNormalMessage(msg);
229
- if (reply) {
230
- const targetId = this.getReplyTarget(msg);
231
- await this.sendFn(targetId, reply, msg.conversationType);
232
- // 非流式回退成功,群聊轮数 +1
233
- if (msg.conversationType === 3) {
234
- this._incrementGroupRoundCount(msg.targetId, maxRounds);
235
- }
236
- }
222
+ // 消息合并逻辑:如果是图片消息,等待一段时间看是否有文字消息跟随
223
+ if (msg.messageType === 'RC:ImgMsg') {
224
+ await this._handleImageMessageWithMerge(msg, maxRounds);
225
+ return;
226
+ }
227
+
228
+ // 普通消息直接处理
229
+ await this._processMessage(msg, maxRounds);
230
+ } catch (err) {
231
+ this.log?.error(`[MessageHandler] 处理消息异常: ${err.message}`);
232
+ const targetId = msg.conversationType === 3 ? msg.targetId : msg.senderUserId;
233
+ await this.sendFn(targetId, `处理失败: ${err.message}`, msg.conversationType);
234
+ }
235
+ }
236
+
237
+ /**
238
+ * 处理图片消息,支持合并后续文字消息
239
+ */
240
+ async _handleImageMessageWithMerge(msg, maxRounds) {
241
+ const userId = msg.senderUserId;
242
+ const conversationKey = `${msg.conversationType}-${msg.targetId}-${userId}`;
243
+
244
+ // 设置待处理图片消息
245
+ this._pendingMessages.set(conversationKey, {
246
+ imageMsg: msg,
247
+ timestamp: Date.now(),
248
+ });
249
+
250
+ // 等待一段时间,看是否有文字消息跟随
251
+ await new Promise(resolve => setTimeout(resolve, this._messageMergeTimeout));
252
+
253
+ // 获取待处理消息
254
+ const pending = this._pendingMessages.get(conversationKey);
255
+ this._pendingMessages.delete(conversationKey);
256
+
257
+ if (!pending) {
258
+ return; // 消息已被处理
259
+ }
260
+
261
+ // 构建合并后的消息内容
262
+ const imageContent = this._extractMessageContent(pending.imageMsg);
263
+ let mergedContent = imageContent;
264
+
265
+ if (pending.textMsg) {
266
+ const textContent = typeof pending.textMsg.content === 'string'
267
+ ? pending.textMsg.content
268
+ : (pending.textMsg.content?.content || '');
269
+ mergedContent = `${textContent}\n${imageContent}`;
270
+ this.log?.info(`[MessageHandler] 合并消息: 图片+文字`);
271
+ }
272
+
273
+ // 创建合并后的消息对象
274
+ const mergedMsg = {
275
+ ...pending.imageMsg,
276
+ content: mergedContent,
277
+ messageType: 'RC:TxtMsg', // 转为文本消息处理
278
+ };
279
+
280
+ await this._processMessage(mergedMsg, maxRounds);
281
+ }
282
+
283
+ /**
284
+ * 处理普通消息(包括合并后的消息)
285
+ */
286
+ async _processMessage(msg, maxRounds) {
287
+ // 如果配置了代理地址,使用流式处理
288
+ if (this.isStreamingEnabled) {
289
+ try {
290
+ await this.handleNormalMessageStream(msg);
291
+ // 流式处理成功,群聊轮数 +1
292
+ if (msg.conversationType === 3) {
293
+ this._incrementGroupRoundCount(msg.targetId, maxRounds);
237
294
  }
238
- } else {
239
- // 降级到非流式处理
295
+ } catch (err) {
296
+ this.log?.error(`[MessageHandler] 流式处理失败,回退到非流式: ${err.message}`);
240
297
  const reply = await this.handleNormalMessage(msg);
241
298
  if (reply) {
242
299
  const targetId = this.getReplyTarget(msg);
243
300
  await this.sendFn(targetId, reply, msg.conversationType);
244
- // 非流式处理成功,群聊轮数 +1
301
+ // 非流式回退成功,群聊轮数 +1
245
302
  if (msg.conversationType === 3) {
246
303
  this._incrementGroupRoundCount(msg.targetId, maxRounds);
247
304
  }
248
305
  }
249
306
  }
250
- } catch (err) {
251
- this.log?.error(`[MessageHandler] 处理消息异常: ${err.message}`);
252
- const targetId = msg.conversationType === 3 ? msg.targetId : msg.senderUserId;
253
- await this.sendFn(targetId, `处理失败: ${err.message}`, msg.conversationType);
307
+ } else {
308
+ // 降级到非流式处理
309
+ const reply = await this.handleNormalMessage(msg);
310
+ if (reply) {
311
+ const targetId = this.getReplyTarget(msg);
312
+ await this.sendFn(targetId, reply, msg.conversationType);
313
+ // 非流式处理成功,群聊轮数 +1
314
+ if (msg.conversationType === 3) {
315
+ this._incrementGroupRoundCount(msg.targetId, maxRounds);
316
+ }
317
+ }
254
318
  }
255
319
  }
256
320
 
@@ -545,44 +609,6 @@ class MessageHandler {
545
609
  return '[图片](无法获取图片地址)';
546
610
  }
547
611
 
548
- // 检查是否有文字内容(从 content 或 extra 中提取)
549
- let textContent = '';
550
-
551
- // 1. 从 content 对象中提取文字
552
- if (typeof content === 'object' && content !== null && content.content) {
553
- textContent = content.content;
554
- } else if (typeof content === 'string') {
555
- // 尝试解析 content 是否为 JSON
556
- try {
557
- const contentObj = JSON.parse(content);
558
- if (contentObj.content) {
559
- textContent = contentObj.content;
560
- }
561
- } catch (e) {
562
- // content 不是 JSON,可能是纯文本
563
- if (content && !content.startsWith('data:image')) {
564
- textContent = content;
565
- }
566
- }
567
- }
568
-
569
- // 2. 从 extra 字段中提取文字
570
- if (!textContent && msg.extra) {
571
- try {
572
- const extraData = JSON.parse(msg.extra);
573
- if (extraData.textContent) {
574
- textContent = extraData.textContent;
575
- }
576
- } catch (e) {
577
- // extra 不是 JSON,忽略
578
- }
579
- }
580
-
581
- // 构建返回内容:如果有文字,返回文字+图片;否则只返回图片
582
- if (textContent) {
583
- return `${textContent}\n[图片] ${imageUri}`;
584
- }
585
-
586
612
  return `[图片] ${imageUri}`;
587
613
  }
588
614
 
@@ -254,7 +254,7 @@ class OpenClawClient {
254
254
  // 会话历史管理:为每个用户维护对话上下文
255
255
  static conversationHistory = new Map();
256
256
  // 最大历史轮数(用户+AI 各算一轮)
257
- static maxHistoryRounds = 10;
257
+ static maxHistoryRounds = 50;
258
258
  // 单条消息最大长度(超过则截断)
259
259
  static maxMessageLength = 2000;
260
260