@myassis/gateway 1.0.109 → 1.0.110

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.
@@ -37,6 +37,10 @@ const DEFAULT_CONFIG = {
37
37
  summaryTriggerChars: index_js_2.appConfig.summaryTriggerChars,
38
38
  enabled: true,
39
39
  };
40
+ /** 单次摘要模型请求超时(毫秒) */
41
+ const SUMMARY_REQUEST_TIMEOUT = 45000;
42
+ /** 整个压缩流程的硬超时(毫秒),超过后放弃压缩、沿用原始历史 */
43
+ const SUMMARY_OVERALL_TIMEOUT = 90000;
40
44
  /**
41
45
  * 会话级压缩任务表:sessionId -> 进行中的压缩任务。
42
46
  *
@@ -298,6 +302,7 @@ class MemoryManager {
298
302
  compressionTasks.delete(this.session.id);
299
303
  }
300
304
  if (!summary) {
305
+ this.emitSSE({ type: 'context_compress_done' });
301
306
  // 摘要失败/被中断:沿用旧摘要或退回原始历史,不写入残缺摘要
302
307
  if (plan.action === 'incremental') {
303
308
  return [
@@ -307,6 +312,7 @@ class MemoryManager {
307
312
  }
308
313
  return plan.prevRetained;
309
314
  }
315
+ this.emitSSE({ type: 'context_compress_done' });
310
316
  return [
311
317
  this.toSummaryMessage(summary, plan.newBoundaryAt, '', plan.ledger),
312
318
  ...plan.retained,
@@ -351,7 +357,7 @@ class MemoryManager {
351
357
  const prompt = TURN_COMPACT_PROMPT_HEAD + transcript;
352
358
  try {
353
359
  const models = await this.getSummaryModels();
354
- const llmClient = new LLMClient_js_1.LLMClient(models, [{ role: 'user', content: prompt }], this.signal, [], 600000);
360
+ const llmClient = new LLMClient_js_1.LLMClient(models, [{ role: 'user', content: prompt }], this.signal, [], SUMMARY_REQUEST_TIMEOUT);
355
361
  if (this.session.useSystemMode) {
356
362
  const { getRequestToken, getServerBaseUrl } = await Promise.resolve().then(() => __importStar(require('../../api/index.js')));
357
363
  llmClient.setSystemMode(true, this.session.selectModelId, getServerBaseUrl(), getRequestToken());
@@ -554,6 +560,26 @@ ${conversation}
554
560
  if (!this.childAgent) {
555
561
  this.emitSSE({ type: 'context_compressing' });
556
562
  }
563
+ try {
564
+ return await Promise.race([
565
+ this.doGenerateSummary(messages, lastSummary),
566
+ this.summaryTimeout(),
567
+ ]);
568
+ }
569
+ catch (error) {
570
+ logger.warn(`压缩流程异常,保留原始历史: ${error?.message}`);
571
+ return lastSummary || null;
572
+ }
573
+ }
574
+ summaryTimeout() {
575
+ return new Promise((resolve) => {
576
+ setTimeout(() => {
577
+ logger.warn(`压缩流程超时(${SUMMARY_OVERALL_TIMEOUT}ms),放弃压缩`);
578
+ resolve(null);
579
+ }, SUMMARY_OVERALL_TIMEOUT);
580
+ });
581
+ }
582
+ async doGenerateSummary(messages, lastSummary) {
557
583
  const MAX_INPUT_CHARS = 30000; // 单次摘要最大输入字符数
558
584
  // 格式化所有消息用于估算长度
559
585
  const formattedMessages = messages
@@ -565,7 +591,7 @@ ${conversation}
565
591
  return await this.hierarchicalSummary(formattedMessages, lastSummary);
566
592
  }
567
593
  const summaryPrompt = this.buildSummaryPrompt(messages, lastSummary);
568
- const llmClient = new LLMClient_js_1.LLMClient(await this.getSummaryModels(), [{ role: 'user', content: summaryPrompt }], this.signal, [], 600000);
594
+ const llmClient = new LLMClient_js_1.LLMClient(await this.getSummaryModels(), [{ role: 'user', content: summaryPrompt }], this.signal, [], SUMMARY_REQUEST_TIMEOUT);
569
595
  if (this.session.useSystemMode) {
570
596
  const { getRequestToken, getServerBaseUrl } = await Promise.resolve().then(() => __importStar(require('../../api/index.js')));
571
597
  llmClient.setSystemMode(true, this.session.selectModelId, getServerBaseUrl(), getRequestToken());
@@ -598,7 +624,7 @@ ${conversation}
598
624
  const models = await this.getSummaryModels();
599
625
  const summarizeBatch = async (batch) => {
600
626
  const batchPrompt = `请简洁总结以下对话片段的关键信息,特别关注:项目结构、关键决策、错误及修复。\n\n${batch.join('\n\n')}`;
601
- const llmClient = new LLMClient_js_1.LLMClient(models, [{ role: 'user', content: batchPrompt }], this.signal, [], 600000);
627
+ const llmClient = new LLMClient_js_1.LLMClient(models, [{ role: 'user', content: batchPrompt }], this.signal, [], SUMMARY_REQUEST_TIMEOUT);
602
628
  if (this.session.useSystemMode) {
603
629
  const { getRequestToken, getServerBaseUrl } = await Promise.resolve().then(() => __importStar(require('../../api/index.js')));
604
630
  llmClient.setSystemMode(true, this.session.selectModelId, getServerBaseUrl(), getRequestToken());
@@ -609,6 +635,7 @@ ${conversation}
609
635
  // 保持批次顺序,同时限制并发
610
636
  const subSummaries = new Array(batches.length).fill('');
611
637
  let aborted = false;
638
+ let consecutiveFailures = 0;
612
639
  for (let i = 0; i < batches.length; i += CONCURRENCY) {
613
640
  if (this.signal.aborted) {
614
641
  aborted = true;
@@ -616,14 +643,26 @@ ${conversation}
616
643
  }
617
644
  const slice = batches.slice(i, i + CONCURRENCY);
618
645
  const results = await Promise.allSettled(slice.map((batch) => summarizeBatch(batch)));
646
+ let groupSuccess = 0;
619
647
  results.forEach((result, offset) => {
620
648
  if (result.status === 'fulfilled' && result.value) {
621
649
  subSummaries[i + offset] = result.value;
650
+ groupSuccess++;
622
651
  }
623
652
  else if (result.status === 'rejected') {
624
653
  logger.warn(`子摘要生成失败: ${result.reason?.message}`);
625
654
  }
626
655
  });
656
+ if (groupSuccess === 0) {
657
+ consecutiveFailures++;
658
+ if (consecutiveFailures >= 2) {
659
+ logger.warn(`分层摘要连续失败,提前终止`);
660
+ break;
661
+ }
662
+ }
663
+ else {
664
+ consecutiveFailures = 0;
665
+ }
627
666
  }
628
667
  // 被中断时不写入残缺摘要,交由调用方回退到原始历史
629
668
  if (aborted || this.signal.aborted) {
@@ -638,7 +677,7 @@ ${conversation}
638
677
  }
639
678
  // 合并子摘要为最终摘要
640
679
  const mergePrompt = this.buildMergePrompt(valid, lastSummary);
641
- const mergeLlmClient = new LLMClient_js_1.LLMClient(models, [{ role: 'user', content: mergePrompt }], this.signal, [], 600000);
680
+ const mergeLlmClient = new LLMClient_js_1.LLMClient(models, [{ role: 'user', content: mergePrompt }], this.signal, [], SUMMARY_REQUEST_TIMEOUT);
642
681
  if (this.session.useSystemMode) {
643
682
  const { getRequestToken, getServerBaseUrl } = await Promise.resolve().then(() => __importStar(require('../../api/index.js')));
644
683
  mergeLlmClient.setSystemMode(true, this.session.selectModelId, getServerBaseUrl(), getRequestToken());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myassis/gateway",
3
- "version": "1.0.109",
3
+ "version": "1.0.110",
4
4
  "description": "我的助手 Gateway Service - 本地 AI 网关服务,支持认证、WebSocket 实时通信和任务调度",
5
5
  "main": "dist/index.js",
6
6
  "bin": {