foliko 1.1.83 → 1.1.85
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/cli/src/ui/chat-ui.js
CHANGED
|
@@ -549,7 +549,7 @@ class ChatUI {
|
|
|
549
549
|
if (!this._lineBuffer) return;
|
|
550
550
|
|
|
551
551
|
// 渲染剩余的 partial line(没有 \n 结尾的尾部文字)
|
|
552
|
-
const rendered = renderLine(this._lineBuffer, this._renderState, true);
|
|
552
|
+
const rendered = this._lineBuffer//renderLine(this._lineBuffer, this._renderState, true);
|
|
553
553
|
if (!this._currentBotMessage) {
|
|
554
554
|
this._currentBotMessage = this.create_message(rendered, colored('● ', GREEN), true);
|
|
555
555
|
} else {
|
package/package.json
CHANGED
package/src/core/agent-chat.js
CHANGED
|
@@ -95,11 +95,14 @@ class AgentChatHandler extends EventEmitter {
|
|
|
95
95
|
model: this.model,
|
|
96
96
|
maxContextTokens: config.maxContextTokens,
|
|
97
97
|
keepRecentMessages: config.keepRecentMessages || 20,
|
|
98
|
+
compressionMessageThreshold: config.compressionMessageThreshold,
|
|
98
99
|
enableSmartCompress: config.enableSmartCompress !== false,
|
|
99
100
|
});
|
|
100
101
|
|
|
101
102
|
// 上下文限制
|
|
102
103
|
this._maxContextTokens = this._contextCompressor._maxContextTokens;
|
|
104
|
+
// 消息数量触发压缩的阈值(可配置,默认 100)
|
|
105
|
+
this._compressionMessageThreshold = this._contextCompressor._compressionMessageThreshold;
|
|
103
106
|
|
|
104
107
|
// Token 计算缓存(避免每次请求重复计算)
|
|
105
108
|
this._toolsTokensCache = null;
|
|
@@ -755,7 +758,7 @@ class AgentChatHandler extends EventEmitter {
|
|
|
755
758
|
|
|
756
759
|
// logger.info(`BEFORE: messages=${messages.length}, tokens=${totalTokens}/${limit}`);
|
|
757
760
|
|
|
758
|
-
if (totalTokens > limit || messages.length >
|
|
761
|
+
if (totalTokens > limit || messages.length > this._compressionMessageThreshold) {
|
|
759
762
|
// logger.info(
|
|
760
763
|
// `Context large (${messages.length} msgs, ${totalTokens}/${this._maxContextTokens} tokens), compressing...`
|
|
761
764
|
// );
|
package/src/core/agent.js
CHANGED
|
@@ -403,6 +403,7 @@ class Agent extends EventEmitter {
|
|
|
403
403
|
// 上下文压缩配置
|
|
404
404
|
maxContextTokens: this.config.maxContextTokens,
|
|
405
405
|
compressionThreshold: this.config.compressionThreshold,
|
|
406
|
+
compressionMessageThreshold: this.config.compressionMessageThreshold,
|
|
406
407
|
keepRecentMessages: this.config.keepRecentMessages,
|
|
407
408
|
enableSmartCompress: this.config.enableSmartCompress,
|
|
408
409
|
});
|
|
@@ -659,6 +659,8 @@ class ContextCompressor {
|
|
|
659
659
|
this.model = config.model || 'deepseek-chat';
|
|
660
660
|
this._maxContextTokens = config.maxContextTokens || this._getDefaultContextLimit();
|
|
661
661
|
this._keepRecentMessages = config.keepRecentMessages || 20;
|
|
662
|
+
// 消息数量阈值:超过该数量即触发压缩(与 token 阈值并联触发)
|
|
663
|
+
this._compressionMessageThreshold = config.compressionMessageThreshold || 200;
|
|
662
664
|
this._enableSmartCompress = config.enableSmartCompress !== false;
|
|
663
665
|
this._compactionSettings = config.compactionSettings || DEFAULT_COMPACTION_SETTINGS;
|
|
664
666
|
|
|
@@ -764,8 +766,11 @@ class ContextCompressor {
|
|
|
764
766
|
content: summaryContent
|
|
765
767
|
};
|
|
766
768
|
|
|
769
|
+
const combined = [...systemMessages, summary, ...recentMessages];
|
|
770
|
+
this._cleanupOrphanedToolResults(combined);
|
|
771
|
+
|
|
767
772
|
messages.length = 0;
|
|
768
|
-
messages.push(...
|
|
773
|
+
messages.push(...combined);
|
|
769
774
|
|
|
770
775
|
this._compressionCount++;
|
|
771
776
|
const tokenCount = this._tokenCounter.countMessages(messages);
|
|
@@ -794,19 +799,81 @@ class ContextCompressor {
|
|
|
794
799
|
content: summaryContent
|
|
795
800
|
};
|
|
796
801
|
|
|
802
|
+
const combined = [...systemMessages, summary, ...recentMessages];
|
|
803
|
+
this._cleanupOrphanedToolResults(combined);
|
|
804
|
+
|
|
797
805
|
messages.length = 0;
|
|
798
|
-
messages.push(...
|
|
806
|
+
messages.push(...combined);
|
|
799
807
|
|
|
800
808
|
this._compressionCount++;
|
|
801
809
|
if (messageStore.compressionState) {
|
|
802
810
|
messageStore.compressionState.count++;
|
|
803
811
|
messageStore.compressionState.lastCompressedAt = Date.now();
|
|
804
|
-
messageStore.compressionState.lastTokenCount = this._tokenCounter.countMessages(
|
|
812
|
+
messageStore.compressionState.lastTokenCount = this._tokenCounter.countMessages(combined);
|
|
805
813
|
}
|
|
806
814
|
|
|
807
815
|
logger.info(`Context simple compressed. Messages: ${messages.length}`);
|
|
808
816
|
}
|
|
809
817
|
|
|
818
|
+
/**
|
|
819
|
+
* 清理孤立的 tool-result 消息(没有对应 tool-call 的)
|
|
820
|
+
* 压缩/裁剪后 recentMessages 可能切碎 tool-call/tool-result 配对,
|
|
821
|
+
* 必须就地清理,否则下游 API 会报 "tool result's tool id not found"
|
|
822
|
+
*/
|
|
823
|
+
_cleanupOrphanedToolResults(messages) {
|
|
824
|
+
const validToolCallIds = new Set();
|
|
825
|
+
for (const msg of messages) {
|
|
826
|
+
if (msg.role !== 'assistant') continue;
|
|
827
|
+
if (Array.isArray(msg.content)) {
|
|
828
|
+
for (const item of msg.content) {
|
|
829
|
+
if ((item.type === 'tool-call' || item.type === 'tool-use') && item.toolCallId) {
|
|
830
|
+
validToolCallIds.add(item.toolCallId);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
if (Array.isArray(msg.tool_calls)) {
|
|
835
|
+
for (const tc of msg.tool_calls) {
|
|
836
|
+
if (tc.id) validToolCallIds.add(tc.id);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
let removedItems = 0;
|
|
842
|
+
let removedMsgs = 0;
|
|
843
|
+
for (const msg of messages) {
|
|
844
|
+
if (msg.role !== 'tool' || !Array.isArray(msg.content)) continue;
|
|
845
|
+
const originalLength = msg.content.length;
|
|
846
|
+
msg.content = msg.content.filter((item) => {
|
|
847
|
+
if (
|
|
848
|
+
item &&
|
|
849
|
+
(item.type === 'tool-result' || item.type === 'tool_result') &&
|
|
850
|
+
item.toolCallId &&
|
|
851
|
+
!validToolCallIds.has(item.toolCallId)
|
|
852
|
+
) {
|
|
853
|
+
removedItems++;
|
|
854
|
+
return false;
|
|
855
|
+
}
|
|
856
|
+
return true;
|
|
857
|
+
});
|
|
858
|
+
if (msg.content.length === 0 && originalLength > 0) {
|
|
859
|
+
msg._orphaned = true;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
864
|
+
if (messages[i]._orphaned) {
|
|
865
|
+
messages.splice(i, 1);
|
|
866
|
+
removedMsgs++;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
if (removedItems > 0 || removedMsgs > 0) {
|
|
871
|
+
logger.debug(
|
|
872
|
+
`[ContextCompressor] cleanup: removed ${removedItems} orphaned tool-result items, ${removedMsgs} orphaned tool messages`
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
810
877
|
async _summarizeMessages(messages) {
|
|
811
878
|
if (!this.framework) {
|
|
812
879
|
throw new Error('Framework not available');
|