@codebolt/agent 1.2.1 → 2.2.2
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/README.md +157 -66
- package/dist/builderpattern/agent.d.ts +86 -0
- package/dist/builderpattern/agent.js +324 -0
- package/dist/builderpattern/followupquestionbuilder.d.ts +75 -0
- package/dist/builderpattern/followupquestionbuilder.js +197 -0
- package/dist/builderpattern/index.d.ts +14 -0
- package/dist/builderpattern/index.js +23 -0
- package/dist/builderpattern/llmoutputhandler.d.ts +102 -0
- package/dist/builderpattern/llmoutputhandler.js +452 -0
- package/dist/builderpattern/promptbuilder.d.ts +382 -0
- package/dist/builderpattern/promptbuilder.js +805 -0
- package/dist/builderpattern/systemprompt.d.ts +20 -0
- package/dist/builderpattern/systemprompt.js +48 -0
- package/dist/builderpattern/taskInstruction.d.ts +37 -0
- package/dist/builderpattern/taskInstruction.js +57 -0
- package/dist/builderpattern/usermessage.d.ts +70 -0
- package/dist/builderpattern/usermessage.js +123 -0
- package/dist/composablepattern/agent.d.ts +169 -0
- package/dist/composablepattern/agent.js +598 -0
- package/dist/composablepattern/codebolt-storage.d.ts +67 -0
- package/dist/composablepattern/codebolt-storage.js +320 -0
- package/dist/composablepattern/document.d.ts +149 -0
- package/dist/composablepattern/document.js +405 -0
- package/dist/composablepattern/examples/codebolt-integration-example.d.ts +12 -0
- package/dist/composablepattern/examples/codebolt-integration-example.js +218 -0
- package/dist/composablepattern/examples/codebolt-storage-example.d.ts +40 -0
- package/dist/composablepattern/examples/codebolt-storage-example.js +223 -0
- package/dist/composablepattern/examples/custom-steps-example.d.ts +20 -0
- package/dist/composablepattern/examples/custom-steps-example.js +455 -0
- package/dist/composablepattern/examples/document-agent.d.ts +17 -0
- package/dist/composablepattern/examples/document-agent.js +107 -0
- package/dist/composablepattern/examples/simple-codebolt-integration.d.ts +8 -0
- package/dist/composablepattern/examples/simple-codebolt-integration.js +164 -0
- package/dist/composablepattern/examples/weather-agent.d.ts +18 -0
- package/dist/composablepattern/examples/weather-agent.js +86 -0
- package/dist/composablepattern/examples/workflow-example.d.ts +12 -0
- package/dist/composablepattern/examples/workflow-example.js +463 -0
- package/dist/composablepattern/index.d.ts +63 -0
- package/dist/composablepattern/index.js +115 -0
- package/dist/composablepattern/memory.d.ts +89 -0
- package/dist/composablepattern/memory.js +141 -0
- package/dist/composablepattern/tool.d.ts +84 -0
- package/dist/composablepattern/tool.js +260 -0
- package/dist/composablepattern/types.d.ts +194 -0
- package/dist/composablepattern/types.js +6 -0
- package/dist/composablepattern/user-context.d.ts +214 -0
- package/dist/composablepattern/user-context.js +275 -0
- package/dist/composablepattern/workflow.d.ts +388 -0
- package/dist/composablepattern/workflow.js +562 -0
- package/dist/processor/agent/agentStep.d.ts +49 -0
- package/dist/processor/agent/agentStep.js +241 -0
- package/dist/processor/agent/toolExecutor.d.ts +19 -0
- package/dist/processor/agent/toolExecutor.js +90 -0
- package/dist/processor/index.d.ts +7 -0
- package/dist/processor/index.js +36 -0
- package/dist/processor/messageModifiers/baseMessageModifier.d.ts +20 -0
- package/dist/processor/messageModifiers/baseMessageModifier.js +55 -0
- package/dist/processor/processors/baseProcessor.d.ts +14 -0
- package/dist/processor/processors/baseProcessor.js +29 -0
- package/dist/processor/tools/baseTool.d.ts +10 -0
- package/dist/processor/tools/baseTool.js +20 -0
- package/dist/processor/tools/toolList.d.ts +9 -0
- package/dist/processor/tools/toolList.js +22 -0
- package/dist/processor/types/interfaces.d.ts +94 -0
- package/dist/processor/types/interfaces.js +2 -0
- package/dist/processor-pieces/base/baseMessageModifier.d.ts +12 -0
- package/dist/processor-pieces/base/baseMessageModifier.js +15 -0
- package/dist/processor-pieces/base/basePostInferenceProcessor.d.ts +13 -0
- package/dist/processor-pieces/base/basePostInferenceProcessor.js +18 -0
- package/dist/processor-pieces/base/basePostToolCallProcessor.d.ts +15 -0
- package/dist/processor-pieces/base/basePostToolCallProcessor.js +25 -0
- package/dist/processor-pieces/base/basePreInferenceProcessor.d.ts +12 -0
- package/dist/processor-pieces/base/basePreInferenceProcessor.js +17 -0
- package/dist/processor-pieces/base/basePreToolCallProcessor.d.ts +18 -0
- package/dist/processor-pieces/base/basePreToolCallProcessor.js +66 -0
- package/dist/processor-pieces/base/index.d.ts +9 -0
- package/dist/processor-pieces/base/index.js +18 -0
- package/dist/processor-pieces/index.d.ts +1 -0
- package/dist/processor-pieces/index.js +59 -0
- package/dist/processor-pieces/messageModifiers/addToolsListMessageModifier.d.ts +22 -0
- package/dist/processor-pieces/messageModifiers/addToolsListMessageModifier.js +144 -0
- package/dist/processor-pieces/messageModifiers/argumentProcessorModifier.d.ts +13 -0
- package/dist/processor-pieces/messageModifiers/argumentProcessorModifier.js +68 -0
- package/dist/processor-pieces/messageModifiers/atFileProcessorModifier.d.ts +25 -0
- package/dist/processor-pieces/messageModifiers/atFileProcessorModifier.js +453 -0
- package/dist/processor-pieces/messageModifiers/baseContextMessageModifier.d.ts +15 -0
- package/dist/processor-pieces/messageModifiers/baseContextMessageModifier.js +75 -0
- package/dist/processor-pieces/messageModifiers/baseSystemInstructionMessageModifier.d.ts +11 -0
- package/dist/processor-pieces/messageModifiers/baseSystemInstructionMessageModifier.js +46 -0
- package/dist/processor-pieces/messageModifiers/chatHistoryMessageModifier.d.ts +18 -0
- package/dist/processor-pieces/messageModifiers/chatHistoryMessageModifier.js +104 -0
- package/dist/processor-pieces/messageModifiers/chatRecordingModifier.d.ts +23 -0
- package/dist/processor-pieces/messageModifiers/chatRecordingModifier.js +173 -0
- package/dist/processor-pieces/messageModifiers/coreSystemPromptModifier.d.ts +14 -0
- package/dist/processor-pieces/messageModifiers/coreSystemPromptModifier.js +130 -0
- package/dist/processor-pieces/messageModifiers/directoryContextModifier.d.ts +36 -0
- package/dist/processor-pieces/messageModifiers/directoryContextModifier.js +418 -0
- package/dist/processor-pieces/messageModifiers/environmentContextModifier.d.ts +26 -0
- package/dist/processor-pieces/messageModifiers/environmentContextModifier.js +260 -0
- package/dist/processor-pieces/messageModifiers/handleUrlMessageModifier.d.ts +12 -0
- package/dist/processor-pieces/messageModifiers/handleUrlMessageModifier.js +60 -0
- package/dist/processor-pieces/messageModifiers/ideContextModifier.d.ts +34 -0
- package/dist/processor-pieces/messageModifiers/ideContextModifier.js +157 -0
- package/dist/processor-pieces/messageModifiers/imageAttachmentMessageModifier.d.ts +18 -0
- package/dist/processor-pieces/messageModifiers/imageAttachmentMessageModifier.js +222 -0
- package/dist/processor-pieces/messageModifiers/index.d.ts +11 -0
- package/dist/processor-pieces/messageModifiers/index.js +26 -0
- package/dist/processor-pieces/messageModifiers/memoryImportModifier.d.ts +15 -0
- package/dist/processor-pieces/messageModifiers/memoryImportModifier.js +129 -0
- package/dist/processor-pieces/messageModifiers/toolInjectionModifier.d.ts +20 -0
- package/dist/processor-pieces/messageModifiers/toolInjectionModifier.js +152 -0
- package/dist/processor-pieces/messageModifiers/workingDirectoryMessageModifier.d.ts +22 -0
- package/dist/processor-pieces/messageModifiers/workingDirectoryMessageModifier.js +194 -0
- package/dist/processor-pieces/postInferenceProcessors/loopDetectionModifier.d.ts +29 -0
- package/dist/processor-pieces/postInferenceProcessors/loopDetectionModifier.js +174 -0
- package/dist/processor-pieces/postToolCallProcessors/index.d.ts +1 -0
- package/dist/processor-pieces/postToolCallProcessors/index.js +2 -0
- package/dist/processor-pieces/postToolCallProcessors/shellProcessorModifier.d.ts +25 -0
- package/dist/processor-pieces/postToolCallProcessors/shellProcessorModifier.js +225 -0
- package/dist/processor-pieces/preInferenceProcessors/chatCompressionModifier.d.ts +35 -0
- package/dist/processor-pieces/preInferenceProcessors/chatCompressionModifier.js +255 -0
- package/dist/processor-pieces/preInferenceProcessors/index.d.ts +1 -0
- package/dist/processor-pieces/preInferenceProcessors/index.js +2 -0
- package/dist/processor-pieces/pretoolCallProcessors/index.d.ts +1 -0
- package/dist/processor-pieces/pretoolCallProcessors/index.js +2 -0
- package/dist/processor-pieces/processors/advancedLoopDetectionProcessor.d.ts +41 -0
- package/dist/processor-pieces/processors/advancedLoopDetectionProcessor.js +197 -0
- package/dist/processor-pieces/processors/chatCompressionProcessor.d.ts +26 -0
- package/dist/processor-pieces/processors/chatCompressionProcessor.js +90 -0
- package/dist/processor-pieces/processors/chatRecordingProcessor.d.ts +81 -0
- package/dist/processor-pieces/processors/chatRecordingProcessor.js +329 -0
- package/dist/processor-pieces/processors/contextManagementProcessor.d.ts +40 -0
- package/dist/processor-pieces/processors/contextManagementProcessor.js +305 -0
- package/dist/processor-pieces/processors/loopDetectionProcessor.d.ts +30 -0
- package/dist/processor-pieces/processors/loopDetectionProcessor.js +137 -0
- package/dist/processor-pieces/processors/responseValidationProcessor.d.ts +30 -0
- package/dist/processor-pieces/processors/responseValidationProcessor.js +228 -0
- package/dist/processor-pieces/processors/telemetryProcessor.d.ts +99 -0
- package/dist/processor-pieces/processors/telemetryProcessor.js +311 -0
- package/dist/processor-pieces/processors/tokenManagementProcessor.d.ts +37 -0
- package/dist/processor-pieces/processors/tokenManagementProcessor.js +178 -0
- package/dist/processor-pieces/processors/toolExecutionProcessor.d.ts +43 -0
- package/dist/processor-pieces/processors/toolExecutionProcessor.js +207 -0
- package/dist/processor-pieces/tools/fileTools.d.ts +26 -0
- package/dist/processor-pieces/tools/fileTools.js +162 -0
- package/dist/processor-pieces/utils/messageModifierHelper.d.ts +4 -0
- package/dist/processor-pieces/utils/messageModifierHelper.js +58 -0
- package/dist/types/commonTypes.d.ts +4 -0
- package/dist/types/processorTypes.d.ts +67 -0
- package/dist/types/processorTypes.js +58 -0
- package/dist/unified/agent/agent.d.ts +17 -0
- package/dist/unified/agent/agent.js +79 -0
- package/dist/unified/agent/team.d.ts +2 -0
- package/dist/unified/agent/team.js +6 -0
- package/dist/unified/agent/tools.d.ts +44 -0
- package/dist/unified/agent/tools.js +487 -0
- package/dist/unified/agent/workflow.d.ts +24 -0
- package/dist/unified/agent/workflow.js +275 -0
- package/dist/unified/agent/workflowControls.d.ts +11 -0
- package/dist/unified/agent/workflowControls.js +20 -0
- package/dist/unified/agent/workflowSteps.d.ts +63 -0
- package/dist/unified/agent/workflowSteps.js +284 -0
- package/dist/unified/base/agentStep.d.ts +32 -0
- package/dist/unified/base/agentStep.js +96 -0
- package/dist/unified/base/create/createInitialPromptGenerators.d.ts +5 -0
- package/dist/unified/base/create/createInitialPromptGenerators.js +17 -0
- package/dist/unified/base/index.d.ts +5 -0
- package/dist/unified/base/index.js +13 -0
- package/dist/unified/base/initialPromptGenerator.d.ts +48 -0
- package/dist/unified/base/initialPromptGenerator.js +118 -0
- package/dist/unified/base/responseExecutor.d.ts +36 -0
- package/dist/unified/base/responseExecutor.js +283 -0
- package/dist/unified/index.d.ts +18 -0
- package/dist/unified/index.js +42 -0
- package/dist/unified/team/team.d.ts +1 -0
- package/dist/unified/team/team.js +2 -0
- package/dist/unified/types/libTypes.d.ts +378 -0
- package/dist/unified/types/libTypes.js +6 -0
- package/dist/unified/types/types.d.ts +212 -0
- package/dist/unified/types/types.js +43 -0
- package/dist/unified/utils/utils.d.ts +40 -0
- package/dist/unified/utils/utils.js +219 -0
- package/package.json +29 -7
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ProcessedMessage } from "@codebolt/types/agent";
|
|
2
|
+
import { BasePreInferenceProcessor } from "../base";
|
|
3
|
+
import { FlatUserMessage, MessageObject } from "@codebolt/types/sdk";
|
|
4
|
+
export declare enum CompressionStatus {
|
|
5
|
+
/** The compression was successful */
|
|
6
|
+
COMPRESSED = 1,
|
|
7
|
+
/** The compression failed due to the compression inflating the token count */
|
|
8
|
+
COMPRESSION_FAILED_INFLATED_TOKEN_COUNT = 2,
|
|
9
|
+
/** The compression failed due to an error counting tokens */
|
|
10
|
+
COMPRESSION_FAILED_TOKEN_COUNT_ERROR = 3,
|
|
11
|
+
/** The compression was not necessary and no action was taken */
|
|
12
|
+
NOOP = 4
|
|
13
|
+
}
|
|
14
|
+
export interface ChatCompressionInfo {
|
|
15
|
+
originalTokenCount: number;
|
|
16
|
+
newTokenCount: number;
|
|
17
|
+
compressionStatus: CompressionStatus;
|
|
18
|
+
}
|
|
19
|
+
export interface ChatCompressionOptions {
|
|
20
|
+
contextPercentageThreshold?: number;
|
|
21
|
+
enableCompression?: boolean;
|
|
22
|
+
force?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare class ChatCompressionModifier extends BasePreInferenceProcessor {
|
|
25
|
+
private readonly options;
|
|
26
|
+
private hasFailedCompressionAttempt;
|
|
27
|
+
constructor(options?: ChatCompressionOptions);
|
|
28
|
+
modify(originalRequest: FlatUserMessage, createdMessage: ProcessedMessage): Promise<ProcessedMessage>;
|
|
29
|
+
tryCompressChat(messages: MessageObject[], force?: boolean): Promise<ChatCompressionInfo & {
|
|
30
|
+
compressedMessages?: MessageObject[];
|
|
31
|
+
}>;
|
|
32
|
+
private countTokens;
|
|
33
|
+
private generateCompressionSummary;
|
|
34
|
+
resetCompressionState(): void;
|
|
35
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ChatCompressionModifier = exports.CompressionStatus = void 0;
|
|
4
|
+
const base_1 = require("../base");
|
|
5
|
+
/**
|
|
6
|
+
* Threshold for compression token count as a fraction of the model's token limit.
|
|
7
|
+
* If the chat history exceeds this threshold, it will be compressed.
|
|
8
|
+
*/
|
|
9
|
+
const COMPRESSION_TOKEN_THRESHOLD = 0.7;
|
|
10
|
+
/**
|
|
11
|
+
* The fraction of the latest chat history to keep. A value of 0.3
|
|
12
|
+
* means that only the last 30% of the chat history will be kept after compression.
|
|
13
|
+
*/
|
|
14
|
+
const COMPRESSION_PRESERVE_THRESHOLD = 0.3;
|
|
15
|
+
var CompressionStatus;
|
|
16
|
+
(function (CompressionStatus) {
|
|
17
|
+
/** The compression was successful */
|
|
18
|
+
CompressionStatus[CompressionStatus["COMPRESSED"] = 1] = "COMPRESSED";
|
|
19
|
+
/** The compression failed due to the compression inflating the token count */
|
|
20
|
+
CompressionStatus[CompressionStatus["COMPRESSION_FAILED_INFLATED_TOKEN_COUNT"] = 2] = "COMPRESSION_FAILED_INFLATED_TOKEN_COUNT";
|
|
21
|
+
/** The compression failed due to an error counting tokens */
|
|
22
|
+
CompressionStatus[CompressionStatus["COMPRESSION_FAILED_TOKEN_COUNT_ERROR"] = 3] = "COMPRESSION_FAILED_TOKEN_COUNT_ERROR";
|
|
23
|
+
/** The compression was not necessary and no action was taken */
|
|
24
|
+
CompressionStatus[CompressionStatus["NOOP"] = 4] = "NOOP";
|
|
25
|
+
})(CompressionStatus || (exports.CompressionStatus = CompressionStatus = {}));
|
|
26
|
+
/**
|
|
27
|
+
* Returns the index of the content after the fraction of the total characters in the history.
|
|
28
|
+
*/
|
|
29
|
+
function findIndexAfterFraction(history, fraction) {
|
|
30
|
+
if (fraction <= 0 || fraction >= 1) {
|
|
31
|
+
throw new Error('Fraction must be between 0 and 1');
|
|
32
|
+
}
|
|
33
|
+
const contentLengths = history.map((content) => JSON.stringify(content).length);
|
|
34
|
+
const totalCharacters = contentLengths.reduce((sum, length) => sum + length, 0);
|
|
35
|
+
const targetCharacters = totalCharacters * fraction;
|
|
36
|
+
let charactersSoFar = 0;
|
|
37
|
+
for (let i = 0; i < contentLengths.length; i++) {
|
|
38
|
+
charactersSoFar += contentLengths[i];
|
|
39
|
+
if (charactersSoFar >= targetCharacters) {
|
|
40
|
+
return i;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return contentLengths.length;
|
|
44
|
+
}
|
|
45
|
+
function isFunctionResponse(message) {
|
|
46
|
+
// Check if message has function response content
|
|
47
|
+
if (typeof message.content === 'object' && message.content !== null) {
|
|
48
|
+
return 'functionResponse' in message.content;
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
// Mock token limit for different models (should be replaced with actual implementation)
|
|
53
|
+
function tokenLimit(model) {
|
|
54
|
+
// Default token limits for common models
|
|
55
|
+
if (model.includes('gemini-pro'))
|
|
56
|
+
return 30720;
|
|
57
|
+
if (model.includes('gemini-flash'))
|
|
58
|
+
return 1048576;
|
|
59
|
+
if (model.includes('gpt-4'))
|
|
60
|
+
return 8192;
|
|
61
|
+
if (model.includes('gpt-3.5'))
|
|
62
|
+
return 4096;
|
|
63
|
+
return 8192; // Default fallback
|
|
64
|
+
}
|
|
65
|
+
class ChatCompressionModifier extends base_1.BasePreInferenceProcessor {
|
|
66
|
+
constructor(options = {}) {
|
|
67
|
+
super();
|
|
68
|
+
this.hasFailedCompressionAttempt = false;
|
|
69
|
+
this.options = {
|
|
70
|
+
contextPercentageThreshold: options.contextPercentageThreshold || COMPRESSION_TOKEN_THRESHOLD,
|
|
71
|
+
enableCompression: options.enableCompression !== false,
|
|
72
|
+
force: options.force || false
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async modify(originalRequest, createdMessage) {
|
|
76
|
+
try {
|
|
77
|
+
const compressionResult = await this.tryCompressChat(createdMessage.message.messages, this.options.force || false);
|
|
78
|
+
if (compressionResult.compressionStatus === CompressionStatus.COMPRESSED) {
|
|
79
|
+
return {
|
|
80
|
+
message: {
|
|
81
|
+
...createdMessage.message,
|
|
82
|
+
messages: compressionResult.compressedMessages || createdMessage.message.messages
|
|
83
|
+
},
|
|
84
|
+
metadata: {
|
|
85
|
+
...createdMessage.metadata,
|
|
86
|
+
chatCompressed: true,
|
|
87
|
+
originalTokenCount: compressionResult.originalTokenCount,
|
|
88
|
+
newTokenCount: compressionResult.newTokenCount,
|
|
89
|
+
compressionStatus: compressionResult.compressionStatus
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
...createdMessage,
|
|
95
|
+
metadata: {
|
|
96
|
+
...createdMessage.metadata,
|
|
97
|
+
compressionStatus: compressionResult.compressionStatus,
|
|
98
|
+
originalTokenCount: compressionResult.originalTokenCount,
|
|
99
|
+
newTokenCount: compressionResult.newTokenCount
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
console.error('Error in ChatCompressionModifier:', error);
|
|
105
|
+
this.hasFailedCompressionAttempt = true;
|
|
106
|
+
return createdMessage;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async tryCompressChat(messages, force = false) {
|
|
110
|
+
var _a;
|
|
111
|
+
const curatedHistory = messages;
|
|
112
|
+
// Regardless of `force`, don't do anything if the history is empty.
|
|
113
|
+
if (curatedHistory.length === 0 ||
|
|
114
|
+
(this.hasFailedCompressionAttempt && !force)) {
|
|
115
|
+
return {
|
|
116
|
+
originalTokenCount: 0,
|
|
117
|
+
newTokenCount: 0,
|
|
118
|
+
compressionStatus: CompressionStatus.NOOP,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
// Mock model - should be replaced with actual model detection
|
|
122
|
+
const model = "gemini-pro";
|
|
123
|
+
const originalTokenCount = await this.countTokens(model, curatedHistory);
|
|
124
|
+
if (originalTokenCount === undefined) {
|
|
125
|
+
console.warn(`Could not determine token count for model ${model}.`);
|
|
126
|
+
this.hasFailedCompressionAttempt = !force && true;
|
|
127
|
+
return {
|
|
128
|
+
originalTokenCount: 0,
|
|
129
|
+
newTokenCount: 0,
|
|
130
|
+
compressionStatus: CompressionStatus.COMPRESSION_FAILED_TOKEN_COUNT_ERROR,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const contextPercentageThreshold = this.options.contextPercentageThreshold;
|
|
134
|
+
// Don't compress if not forced and we are under the limit.
|
|
135
|
+
if (!force) {
|
|
136
|
+
const threshold = contextPercentageThreshold !== null && contextPercentageThreshold !== void 0 ? contextPercentageThreshold : COMPRESSION_TOKEN_THRESHOLD;
|
|
137
|
+
if (originalTokenCount < threshold * tokenLimit(model)) {
|
|
138
|
+
return {
|
|
139
|
+
originalTokenCount,
|
|
140
|
+
newTokenCount: originalTokenCount,
|
|
141
|
+
compressionStatus: CompressionStatus.NOOP,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
let compressBeforeIndex = findIndexAfterFraction(curatedHistory, 1 - COMPRESSION_PRESERVE_THRESHOLD);
|
|
146
|
+
// Find the first user message after the index. This is the start of the next turn.
|
|
147
|
+
while (compressBeforeIndex < curatedHistory.length &&
|
|
148
|
+
(((_a = curatedHistory[compressBeforeIndex]) === null || _a === void 0 ? void 0 : _a.role) === 'assistant' ||
|
|
149
|
+
isFunctionResponse(curatedHistory[compressBeforeIndex]))) {
|
|
150
|
+
compressBeforeIndex++;
|
|
151
|
+
}
|
|
152
|
+
const historyToCompress = curatedHistory.slice(0, compressBeforeIndex);
|
|
153
|
+
const historyToKeep = curatedHistory.slice(compressBeforeIndex);
|
|
154
|
+
// Generate AI summary using the compression prompt
|
|
155
|
+
const summary = await this.generateCompressionSummary(historyToCompress);
|
|
156
|
+
const compressedMessages = [
|
|
157
|
+
{
|
|
158
|
+
role: 'user',
|
|
159
|
+
content: summary
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
role: 'assistant',
|
|
163
|
+
content: 'Got it. Thanks for the additional context!'
|
|
164
|
+
},
|
|
165
|
+
...historyToKeep,
|
|
166
|
+
];
|
|
167
|
+
const newTokenCount = await this.countTokens(model, compressedMessages);
|
|
168
|
+
if (newTokenCount === undefined) {
|
|
169
|
+
console.warn('Could not determine compressed history token count.');
|
|
170
|
+
this.hasFailedCompressionAttempt = !force && true;
|
|
171
|
+
return {
|
|
172
|
+
originalTokenCount,
|
|
173
|
+
newTokenCount: originalTokenCount,
|
|
174
|
+
compressionStatus: CompressionStatus.COMPRESSION_FAILED_TOKEN_COUNT_ERROR,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (newTokenCount > originalTokenCount) {
|
|
178
|
+
this.hasFailedCompressionAttempt = !force && true;
|
|
179
|
+
return {
|
|
180
|
+
originalTokenCount,
|
|
181
|
+
newTokenCount,
|
|
182
|
+
compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
originalTokenCount,
|
|
187
|
+
newTokenCount,
|
|
188
|
+
compressionStatus: CompressionStatus.COMPRESSED,
|
|
189
|
+
compressedMessages,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
async countTokens(model, messages) {
|
|
193
|
+
// Mock token counting - should be replaced with actual API call
|
|
194
|
+
// Rough approximation: 4 characters per token
|
|
195
|
+
const totalCharacters = messages.reduce((sum, msg) => {
|
|
196
|
+
return sum + (typeof msg.content === 'string' ? msg.content.length : JSON.stringify(msg.content).length);
|
|
197
|
+
}, 0);
|
|
198
|
+
return Math.ceil(totalCharacters / 4);
|
|
199
|
+
}
|
|
200
|
+
async generateCompressionSummary(messages) {
|
|
201
|
+
// This should use the actual compression prompt and LLM call
|
|
202
|
+
// For now, using a simplified version similar to the original
|
|
203
|
+
const compressionPrompt = `You are tasked with creating a concise summary of a conversation history to preserve context while reducing token usage.
|
|
204
|
+
|
|
205
|
+
Please analyze the conversation and create a state snapshot that captures:
|
|
206
|
+
1. Key topics discussed
|
|
207
|
+
2. Important decisions made
|
|
208
|
+
3. Current context and progress
|
|
209
|
+
4. Any ongoing tasks or issues
|
|
210
|
+
|
|
211
|
+
Format your response as a clear, structured summary that maintains the essential information needed to continue the conversation effectively.`;
|
|
212
|
+
// Mock LLM call - should be replaced with actual LLM integration
|
|
213
|
+
const summaryParts = [];
|
|
214
|
+
let userQuestions = [];
|
|
215
|
+
let assistantResponses = [];
|
|
216
|
+
for (const message of messages) {
|
|
217
|
+
const content = typeof message.content === 'string' ? message.content : JSON.stringify(message.content);
|
|
218
|
+
if (message.role === 'user') {
|
|
219
|
+
if (content.length > 20) {
|
|
220
|
+
userQuestions.push(content.substring(0, 150) + (content.length > 150 ? '...' : ''));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else if (message.role === 'assistant') {
|
|
224
|
+
if (content.length > 20) {
|
|
225
|
+
assistantResponses.push(content.substring(0, 150) + (content.length > 150 ? '...' : ''));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
summaryParts.push('<state_snapshot>');
|
|
230
|
+
summaryParts.push('This is a compressed summary of the previous conversation:');
|
|
231
|
+
summaryParts.push('');
|
|
232
|
+
if (userQuestions.length > 0) {
|
|
233
|
+
summaryParts.push('Key user requests and questions:');
|
|
234
|
+
userQuestions.slice(0, 5).forEach((q, i) => {
|
|
235
|
+
summaryParts.push(`${i + 1}. ${q}`);
|
|
236
|
+
});
|
|
237
|
+
summaryParts.push('');
|
|
238
|
+
}
|
|
239
|
+
if (assistantResponses.length > 0) {
|
|
240
|
+
summaryParts.push('Key assistant responses and actions:');
|
|
241
|
+
assistantResponses.slice(0, 5).forEach((r, i) => {
|
|
242
|
+
summaryParts.push(`${i + 1}. ${r}`);
|
|
243
|
+
});
|
|
244
|
+
summaryParts.push('');
|
|
245
|
+
}
|
|
246
|
+
summaryParts.push(`Total messages compressed: ${messages.length}`);
|
|
247
|
+
summaryParts.push('</state_snapshot>');
|
|
248
|
+
return summaryParts.join('\n');
|
|
249
|
+
}
|
|
250
|
+
resetCompressionState() {
|
|
251
|
+
this.hasFailedCompressionAttempt = false;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
exports.ChatCompressionModifier = ChatCompressionModifier;
|
|
255
|
+
////
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { BaseProcessor, ProcessorInput, ProcessorOutput } from '../../processor';
|
|
2
|
+
export interface AdvancedLoopDetectionProcessorOptions {
|
|
3
|
+
toolCallThreshold?: number;
|
|
4
|
+
contentLoopThreshold?: number;
|
|
5
|
+
contentChunkSize?: number;
|
|
6
|
+
maxHistoryLength?: number;
|
|
7
|
+
enableLLMDetection?: boolean;
|
|
8
|
+
llmCheckAfterTurns?: number;
|
|
9
|
+
defaultLLMCheckInterval?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare class AdvancedLoopDetectionProcessor extends BaseProcessor {
|
|
12
|
+
private readonly toolCallThreshold;
|
|
13
|
+
private readonly contentLoopThreshold;
|
|
14
|
+
private readonly contentChunkSize;
|
|
15
|
+
private readonly maxHistoryLength;
|
|
16
|
+
private readonly enableLLMDetection;
|
|
17
|
+
private readonly llmCheckAfterTurns;
|
|
18
|
+
private readonly defaultLLMCheckInterval;
|
|
19
|
+
private lastToolCallKey;
|
|
20
|
+
private toolCallRepetitionCount;
|
|
21
|
+
private streamContentHistory;
|
|
22
|
+
private contentStats;
|
|
23
|
+
private lastContentIndex;
|
|
24
|
+
private loopDetected;
|
|
25
|
+
private inCodeBlock;
|
|
26
|
+
private turnsInCurrentPrompt;
|
|
27
|
+
private llmCheckInterval;
|
|
28
|
+
private lastCheckTurn;
|
|
29
|
+
constructor(options?: AdvancedLoopDetectionProcessorOptions);
|
|
30
|
+
processInput(input: ProcessorInput): Promise<ProcessorOutput[]>;
|
|
31
|
+
private getToolCallKey;
|
|
32
|
+
private checkToolCallLoop;
|
|
33
|
+
private checkContentLoop;
|
|
34
|
+
private truncateAndUpdate;
|
|
35
|
+
private analyzeContentChunksForLoop;
|
|
36
|
+
private hasMoreChunksToProcess;
|
|
37
|
+
private isLoopDetectedForChunk;
|
|
38
|
+
private isActualContentMatch;
|
|
39
|
+
private resetContentTracking;
|
|
40
|
+
reset(): void;
|
|
41
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AdvancedLoopDetectionProcessor = void 0;
|
|
4
|
+
const processor_1 = require("../../processor");
|
|
5
|
+
const crypto_1 = require("crypto");
|
|
6
|
+
class AdvancedLoopDetectionProcessor extends processor_1.BaseProcessor {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
super();
|
|
9
|
+
// Tool call tracking
|
|
10
|
+
this.lastToolCallKey = null;
|
|
11
|
+
this.toolCallRepetitionCount = 0;
|
|
12
|
+
// Content streaming tracking
|
|
13
|
+
this.streamContentHistory = '';
|
|
14
|
+
this.contentStats = new Map();
|
|
15
|
+
this.lastContentIndex = 0;
|
|
16
|
+
this.loopDetected = false;
|
|
17
|
+
this.inCodeBlock = false;
|
|
18
|
+
// Turn tracking
|
|
19
|
+
this.turnsInCurrentPrompt = 0;
|
|
20
|
+
this.lastCheckTurn = 0;
|
|
21
|
+
this.toolCallThreshold = options.toolCallThreshold || 5;
|
|
22
|
+
this.contentLoopThreshold = options.contentLoopThreshold || 10;
|
|
23
|
+
this.contentChunkSize = options.contentChunkSize || 50;
|
|
24
|
+
this.maxHistoryLength = options.maxHistoryLength || 1000;
|
|
25
|
+
this.enableLLMDetection = options.enableLLMDetection || false;
|
|
26
|
+
this.llmCheckAfterTurns = options.llmCheckAfterTurns || 30;
|
|
27
|
+
this.defaultLLMCheckInterval = options.defaultLLMCheckInterval || 3;
|
|
28
|
+
this.llmCheckInterval = this.defaultLLMCheckInterval;
|
|
29
|
+
}
|
|
30
|
+
async processInput(input) {
|
|
31
|
+
const { message, context } = input;
|
|
32
|
+
// Increment turn counter
|
|
33
|
+
this.turnsInCurrentPrompt++;
|
|
34
|
+
// Check for tool call loops in the last message
|
|
35
|
+
const lastMessage = message.messages[message.messages.length - 1];
|
|
36
|
+
if (lastMessage.tool_calls && lastMessage.tool_calls.length > 0) {
|
|
37
|
+
for (const toolCall of lastMessage.tool_calls) {
|
|
38
|
+
const isLoop = this.checkToolCallLoop({
|
|
39
|
+
name: toolCall.function.name,
|
|
40
|
+
args: typeof toolCall.function.arguments === 'string'
|
|
41
|
+
? JSON.parse(toolCall.function.arguments)
|
|
42
|
+
: toolCall.function.arguments
|
|
43
|
+
});
|
|
44
|
+
if (isLoop) {
|
|
45
|
+
this.loopDetected = true;
|
|
46
|
+
return [this.createEvent('LoopDetected', {
|
|
47
|
+
type: 'tool_call_loop',
|
|
48
|
+
toolCall: toolCall.function.name,
|
|
49
|
+
repetitionCount: this.toolCallRepetitionCount,
|
|
50
|
+
threshold: this.toolCallThreshold
|
|
51
|
+
})];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Check for content loops
|
|
56
|
+
if (lastMessage.content && typeof lastMessage.content === 'string') {
|
|
57
|
+
const isContentLoop = this.checkContentLoop(lastMessage.content);
|
|
58
|
+
if (isContentLoop) {
|
|
59
|
+
this.loopDetected = true;
|
|
60
|
+
return [this.createEvent('LoopDetected', {
|
|
61
|
+
type: 'content_loop',
|
|
62
|
+
contentLength: this.streamContentHistory.length,
|
|
63
|
+
threshold: this.contentLoopThreshold
|
|
64
|
+
})];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// LLM-based loop detection (if enabled)
|
|
68
|
+
if (this.enableLLMDetection &&
|
|
69
|
+
this.turnsInCurrentPrompt >= this.llmCheckAfterTurns &&
|
|
70
|
+
this.turnsInCurrentPrompt - this.lastCheckTurn >= this.llmCheckInterval) {
|
|
71
|
+
this.lastCheckTurn = this.turnsInCurrentPrompt;
|
|
72
|
+
// For now, just log that LLM check would happen
|
|
73
|
+
// In a real implementation, this would call an LLM to analyze the conversation
|
|
74
|
+
console.log('[AdvancedLoopDetection] LLM-based loop check would be performed here');
|
|
75
|
+
}
|
|
76
|
+
return [this.createEvent('LoopCheckCompleted', {
|
|
77
|
+
loopDetected: this.loopDetected,
|
|
78
|
+
turnsInPrompt: this.turnsInCurrentPrompt,
|
|
79
|
+
toolCallCount: this.toolCallRepetitionCount
|
|
80
|
+
})];
|
|
81
|
+
}
|
|
82
|
+
getToolCallKey(toolCall) {
|
|
83
|
+
const argsString = JSON.stringify(toolCall.args);
|
|
84
|
+
const keyString = `${toolCall.name}:${argsString}`;
|
|
85
|
+
return (0, crypto_1.createHash)('sha256').update(keyString).digest('hex');
|
|
86
|
+
}
|
|
87
|
+
checkToolCallLoop(toolCall) {
|
|
88
|
+
const key = this.getToolCallKey(toolCall);
|
|
89
|
+
if (this.lastToolCallKey === key) {
|
|
90
|
+
this.toolCallRepetitionCount++;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
this.lastToolCallKey = key;
|
|
94
|
+
this.toolCallRepetitionCount = 1;
|
|
95
|
+
}
|
|
96
|
+
return this.toolCallRepetitionCount >= this.toolCallThreshold;
|
|
97
|
+
}
|
|
98
|
+
checkContentLoop(content) {
|
|
99
|
+
var _a;
|
|
100
|
+
// Different content elements can often contain repetitive syntax that is not indicative of a loop.
|
|
101
|
+
// To avoid false positives, we detect when we encounter different content types and
|
|
102
|
+
// reset tracking to avoid analyzing content that spans across different element boundaries.
|
|
103
|
+
const numFences = ((_a = content.match(/```/g)) !== null && _a !== void 0 ? _a : []).length;
|
|
104
|
+
const hasTable = /(^|\n)\s*(\|.*\||[|+-]{3,})/.test(content);
|
|
105
|
+
const hasListItem = /(^|\n)\s*[*-+]\s/.test(content) || /(^|\n)\s*\d+\.\s/.test(content);
|
|
106
|
+
const hasHeading = /(^|\n)#+\s/.test(content);
|
|
107
|
+
const hasBlockquote = /(^|\n)>\s/.test(content);
|
|
108
|
+
if (numFences || hasTable || hasListItem || hasHeading || hasBlockquote) {
|
|
109
|
+
// Reset tracking when different content elements are detected
|
|
110
|
+
this.resetContentTracking();
|
|
111
|
+
}
|
|
112
|
+
const wasInCodeBlock = this.inCodeBlock;
|
|
113
|
+
this.inCodeBlock = numFences % 2 === 0 ? this.inCodeBlock : !this.inCodeBlock;
|
|
114
|
+
if (wasInCodeBlock || this.inCodeBlock) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
this.streamContentHistory += content;
|
|
118
|
+
this.truncateAndUpdate();
|
|
119
|
+
return this.analyzeContentChunksForLoop();
|
|
120
|
+
}
|
|
121
|
+
truncateAndUpdate() {
|
|
122
|
+
if (this.streamContentHistory.length <= this.maxHistoryLength) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const truncationAmount = this.streamContentHistory.length - this.maxHistoryLength;
|
|
126
|
+
this.streamContentHistory = this.streamContentHistory.slice(truncationAmount);
|
|
127
|
+
this.lastContentIndex = Math.max(0, this.lastContentIndex - truncationAmount);
|
|
128
|
+
// Update all stored chunk indices to account for the truncation
|
|
129
|
+
for (const [hash, oldIndices] of this.contentStats.entries()) {
|
|
130
|
+
const adjustedIndices = oldIndices
|
|
131
|
+
.map((index) => index - truncationAmount)
|
|
132
|
+
.filter((index) => index >= 0);
|
|
133
|
+
if (adjustedIndices.length > 0) {
|
|
134
|
+
this.contentStats.set(hash, adjustedIndices);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
this.contentStats.delete(hash);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
analyzeContentChunksForLoop() {
|
|
142
|
+
while (this.hasMoreChunksToProcess()) {
|
|
143
|
+
const currentChunk = this.streamContentHistory.substring(this.lastContentIndex, this.lastContentIndex + this.contentChunkSize);
|
|
144
|
+
const chunkHash = (0, crypto_1.createHash)('sha256').update(currentChunk).digest('hex');
|
|
145
|
+
if (this.isLoopDetectedForChunk(currentChunk, chunkHash)) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
this.lastContentIndex++;
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
hasMoreChunksToProcess() {
|
|
153
|
+
return this.lastContentIndex + this.contentChunkSize <= this.streamContentHistory.length;
|
|
154
|
+
}
|
|
155
|
+
isLoopDetectedForChunk(chunk, hash) {
|
|
156
|
+
const existingIndices = this.contentStats.get(hash);
|
|
157
|
+
if (!existingIndices) {
|
|
158
|
+
this.contentStats.set(hash, [this.lastContentIndex]);
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
if (!this.isActualContentMatch(chunk, existingIndices[0])) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
existingIndices.push(this.lastContentIndex);
|
|
165
|
+
if (existingIndices.length < this.contentLoopThreshold) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
// Analyze the most recent occurrences to see if they're clustered closely together
|
|
169
|
+
const recentIndices = existingIndices.slice(-this.contentLoopThreshold);
|
|
170
|
+
const totalDistance = recentIndices[recentIndices.length - 1] - recentIndices[0];
|
|
171
|
+
const averageDistance = totalDistance / (this.contentLoopThreshold - 1);
|
|
172
|
+
const maxAllowedDistance = this.contentChunkSize * 1.5;
|
|
173
|
+
return averageDistance <= maxAllowedDistance;
|
|
174
|
+
}
|
|
175
|
+
isActualContentMatch(currentChunk, originalIndex) {
|
|
176
|
+
const originalChunk = this.streamContentHistory.substring(originalIndex, originalIndex + this.contentChunkSize);
|
|
177
|
+
return originalChunk === currentChunk;
|
|
178
|
+
}
|
|
179
|
+
resetContentTracking(resetHistory = true) {
|
|
180
|
+
if (resetHistory) {
|
|
181
|
+
this.streamContentHistory = '';
|
|
182
|
+
}
|
|
183
|
+
this.contentStats.clear();
|
|
184
|
+
this.lastContentIndex = 0;
|
|
185
|
+
}
|
|
186
|
+
reset() {
|
|
187
|
+
this.lastToolCallKey = null;
|
|
188
|
+
this.toolCallRepetitionCount = 0;
|
|
189
|
+
this.resetContentTracking();
|
|
190
|
+
this.turnsInCurrentPrompt = 0;
|
|
191
|
+
this.llmCheckInterval = this.defaultLLMCheckInterval;
|
|
192
|
+
this.lastCheckTurn = 0;
|
|
193
|
+
this.loopDetected = false;
|
|
194
|
+
this.inCodeBlock = false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
exports.AdvancedLoopDetectionProcessor = AdvancedLoopDetectionProcessor;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { BaseProcessor, ProcessorInput, ProcessorOutput } from '../../processor';
|
|
2
|
+
export interface ChatCompressionInfo {
|
|
3
|
+
originalTokenCount: number;
|
|
4
|
+
newTokenCount: number;
|
|
5
|
+
compressionRatio: number;
|
|
6
|
+
threshold: number;
|
|
7
|
+
}
|
|
8
|
+
export interface ChatCompressionProcessorOptions {
|
|
9
|
+
compressionThreshold?: number;
|
|
10
|
+
compressionPreserveThreshold?: number;
|
|
11
|
+
tokenLimit?: number;
|
|
12
|
+
enableCompression?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare class ChatCompressionProcessor extends BaseProcessor {
|
|
15
|
+
private readonly compressionThreshold;
|
|
16
|
+
private readonly compressionPreserveThreshold;
|
|
17
|
+
private readonly tokenLimit;
|
|
18
|
+
private readonly enableCompression;
|
|
19
|
+
constructor(options?: ChatCompressionProcessorOptions);
|
|
20
|
+
processInput(input: ProcessorInput): Promise<ProcessorOutput[]>;
|
|
21
|
+
private estimateTokenCount;
|
|
22
|
+
setCompressionThreshold(threshold: number): void;
|
|
23
|
+
setTokenLimit(limit: number): void;
|
|
24
|
+
enableCompressionForSession(): void;
|
|
25
|
+
disableCompressionForSession(): void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ChatCompressionProcessor = void 0;
|
|
7
|
+
const processor_1 = require("../../processor");
|
|
8
|
+
const codeboltjs_1 = __importDefault(require("@codebolt/codeboltjs"));
|
|
9
|
+
class ChatCompressionProcessor extends processor_1.BaseProcessor {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
super(options);
|
|
12
|
+
this.compressionThreshold = options.compressionThreshold || 0.7;
|
|
13
|
+
this.compressionPreserveThreshold = options.compressionPreserveThreshold || 0.3;
|
|
14
|
+
this.tokenLimit = options.tokenLimit || 128000;
|
|
15
|
+
this.enableCompression = options.enableCompression !== false;
|
|
16
|
+
}
|
|
17
|
+
async processInput(input) {
|
|
18
|
+
try {
|
|
19
|
+
const { message, context } = input;
|
|
20
|
+
if (!this.enableCompression) {
|
|
21
|
+
return [this.createEvent('ChatCompressionDisabled', {
|
|
22
|
+
reason: 'Compression is disabled for this session'
|
|
23
|
+
})];
|
|
24
|
+
}
|
|
25
|
+
// Get chat history from CodeBolt
|
|
26
|
+
const chatHistory = await codeboltjs_1.default.chat.getChatHistory();
|
|
27
|
+
if (chatHistory.length === 0) {
|
|
28
|
+
return [this.createEvent('NoChatHistory', {
|
|
29
|
+
reason: 'No chat history available for compression'
|
|
30
|
+
})];
|
|
31
|
+
}
|
|
32
|
+
// Estimate token count (simple heuristic: 4 chars per token)
|
|
33
|
+
const estimatedTokens = this.estimateTokenCount(chatHistory);
|
|
34
|
+
const threshold = this.compressionThreshold * this.tokenLimit;
|
|
35
|
+
console.log(`[Compression] Estimated tokens: ${estimatedTokens}, threshold: ${threshold}`);
|
|
36
|
+
if (estimatedTokens < threshold) {
|
|
37
|
+
return [this.createEvent('ChatCompressionSkipped', {
|
|
38
|
+
estimatedTokens,
|
|
39
|
+
threshold,
|
|
40
|
+
reason: 'Below compression threshold'
|
|
41
|
+
})];
|
|
42
|
+
}
|
|
43
|
+
// Perform compression using CodeBolt's chatSummary
|
|
44
|
+
const compressionResult = await codeboltjs_1.default.chatSummary.summarizeAll();
|
|
45
|
+
if (compressionResult.success && compressionResult.data) {
|
|
46
|
+
const newTokenCount = this.estimateTokenCount(compressionResult.data);
|
|
47
|
+
const compressionInfo = {
|
|
48
|
+
originalTokenCount: estimatedTokens,
|
|
49
|
+
newTokenCount,
|
|
50
|
+
compressionRatio: newTokenCount / estimatedTokens,
|
|
51
|
+
threshold: this.compressionThreshold
|
|
52
|
+
};
|
|
53
|
+
// Send notification about compression
|
|
54
|
+
await codeboltjs_1.default.chat.sendNotificationEvent(`Chat compressed: ${estimatedTokens} → ${newTokenCount} tokens`, 'debug');
|
|
55
|
+
return [this.createEvent('ChatCompressed', compressionInfo)];
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
return [this.createEvent('ChatCompressionFailed', {
|
|
59
|
+
error: compressionResult.message || 'Unknown compression error',
|
|
60
|
+
originalTokenCount: estimatedTokens
|
|
61
|
+
})];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
console.error('Error in ChatCompressionProcessor:', error);
|
|
66
|
+
return [this.createEvent('ChatCompressionError', {
|
|
67
|
+
error: error instanceof Error ? error.message : String(error)
|
|
68
|
+
})];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
estimateTokenCount(data) {
|
|
72
|
+
// Simple heuristic: 4 characters per token
|
|
73
|
+
const totalContent = JSON.stringify(data);
|
|
74
|
+
return Math.ceil(totalContent.length / 4);
|
|
75
|
+
}
|
|
76
|
+
// Public methods for external control
|
|
77
|
+
setCompressionThreshold(threshold) {
|
|
78
|
+
this.compressionThreshold = Math.max(0.1, Math.min(0.9, threshold));
|
|
79
|
+
}
|
|
80
|
+
setTokenLimit(limit) {
|
|
81
|
+
this.tokenLimit = Math.max(1000, limit);
|
|
82
|
+
}
|
|
83
|
+
enableCompressionForSession() {
|
|
84
|
+
this.enableCompression = true;
|
|
85
|
+
}
|
|
86
|
+
disableCompressionForSession() {
|
|
87
|
+
this.enableCompression = false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.ChatCompressionProcessor = ChatCompressionProcessor;
|