Skip to content

Commit 7c306a2

Browse files
committed
feat(tarko-agent): implement context compression strategies
Add comprehensive context compression system to prevent context window overflow: - Four built-in strategies: sliding_window, structured_summary, tool_response_compression, smart_truncation - Inspired by Manus (external storage), Claude Code (8-section summary), Gemini CLI (sliding window) - Configurable compression thresholds and strategies - Automatic compression triggers and manual compression support - Integration with Agent message history and event stream - Comprehensive test coverage and documentation - Backward compatible with existing Agent configurations Strategies: - sliding_window: Keeps recent messages (Gemini CLI approach, 70% threshold) - structured_summary: Creates structured summaries (Claude Code approach, 92% threshold) - tool_response_compression: Compresses large tool responses (Manus approach) - smart_truncation: Intelligent message selection based on importance scoring Features: - Token counting and context window management - Compression statistics and monitoring - Strategy registry for custom implementations - Event stream integration for compression notifications - Graceful fallbacks and error handling
1 parent 0f9a59d commit 7c306a2

File tree

18 files changed

+3209
-4
lines changed

18 files changed

+3209
-4
lines changed

multimodal/tarko/agent-interface/src/agent-options.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,64 @@ export interface AgentOptions
241241
AgentMiscOptions,
242242
AgentWorkspaceOptions {}
243243

244+
/**
245+
* Context compression configuration options
246+
*/
247+
export interface AgentContextCompressionOptions {
248+
/**
249+
* Whether context compression is enabled
250+
*
251+
* @defaultValue `true`
252+
*/
253+
enabled?: boolean;
254+
255+
/**
256+
* Compression strategy to use
257+
*
258+
* Built-in strategies:
259+
* - 'sliding_window': Keeps recent messages (Gemini CLI approach)
260+
* - 'structured_summary': Creates structured summaries (Claude Code approach)
261+
* - 'tool_response_compression': Compresses large tool responses (Manus approach)
262+
* - 'smart_truncation': Intelligent message selection
263+
*
264+
* @defaultValue `'sliding_window'`
265+
*/
266+
strategy?: string;
267+
268+
/**
269+
* Threshold for triggering compression (0-1, percentage of context window)
270+
*
271+
* @defaultValue `0.7` (70%)
272+
*/
273+
compressionThreshold?: number;
274+
275+
/**
276+
* Target size after compression (0-1, percentage of original size)
277+
*
278+
* @defaultValue `0.3` (30%)
279+
*/
280+
targetCompressionRatio?: number;
281+
282+
/**
283+
* Minimum number of messages to preserve
284+
*
285+
* @defaultValue `5`
286+
*/
287+
minMessagesToKeep?: number;
288+
289+
/**
290+
* Maximum number of compression attempts per session
291+
*
292+
* @defaultValue `10`
293+
*/
294+
maxCompressionAttempts?: number;
295+
296+
/**
297+
* Custom configuration for specific strategies
298+
*/
299+
strategyConfig?: Record<string, any>;
300+
}
301+
244302
/**
245303
* Options for configuring agent context behavior (e.g. message history)
246304
*/
@@ -255,4 +313,13 @@ export interface AgentContextAwarenessOptions {
255313
* This helps optimize token usage while preserving important conversation context.
256314
*/
257315
maxImagesCount?: number;
316+
317+
/**
318+
* Context compression configuration
319+
*
320+
* Controls how the agent manages context window limits through compression strategies.
321+
* When enabled, the agent will automatically compress older conversation history
322+
* when approaching context window limits.
323+
*/
324+
compression?: AgentContextCompressionOptions;
258325
}

multimodal/tarko/agent/src/agent/message-history.ts

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ import {
1010
ChatCompletionMessageParam,
1111
ChatCompletionContentPart,
1212
ToolCallResult,
13+
AgentContextAwarenessOptions,
1314
} from '@tarko/agent-interface';
1415
import { convertToMultimodalToolCallResult } from '../utils/multimodal';
1516
import { getLogger, isTest } from '@tarko/shared-utils';
17+
import { ContextManager } from '../context-compression/context-manager';
18+
import { CompressionTrigger } from '../context-compression/types';
1619

1720
/**
1821
* Interface for image information in messages
@@ -35,6 +38,9 @@ interface ImageReference {
3538
*/
3639
export class MessageHistory {
3740
private logger = getLogger('MessageHistory');
41+
private contextManager?: ContextManager;
42+
private sessionId?: string;
43+
private iteration = 0;
3844

3945
/**
4046
* Creates a new MessageHistory instance
@@ -44,11 +50,35 @@ export class MessageHistory {
4450
* When specified, limits the total number of images in the conversation history
4551
* to prevent context window overflow. Images beyond this limit will be
4652
* replaced with text placeholders to preserve context while reducing token usage.
53+
* @param contextAwarenessOptions - Optional context awareness options including compression
4754
*/
4855
constructor(
4956
private eventStream: AgentEventStream.Processor,
5057
private maxImagesCount?: number,
51-
) {}
58+
private contextAwarenessOptions?: AgentContextAwarenessOptions,
59+
) {
60+
// Initialize context manager if compression is enabled
61+
if (this.contextAwarenessOptions?.compression?.enabled !== false) {
62+
this.contextManager = new ContextManager(this.contextAwarenessOptions?.compression);
63+
}
64+
}
65+
66+
/**
67+
* Update session information for compression tracking
68+
* @param sessionId Current session identifier
69+
* @param iteration Current iteration number
70+
* @param modelId Model identifier for token counting
71+
* @param provider Model provider
72+
*/
73+
updateSession(sessionId: string, iteration: number, modelId?: string, provider?: string): void {
74+
this.sessionId = sessionId;
75+
this.iteration = iteration;
76+
77+
// Update context manager with model information
78+
if (this.contextManager && modelId && provider) {
79+
this.contextManager.updateModel(modelId, provider);
80+
}
81+
}
5282

5383
/**
5484
* Convert events to message history format for LLM context
@@ -59,11 +89,11 @@ export class MessageHistory {
5989
* @param systemPrompt The base system prompt to include
6090
* @param tools Available tools to enhance the system prompt
6191
*/
62-
toMessageHistory(
92+
async toMessageHistory(
6393
toolCallEngine: ToolCallEngine,
6494
customSystemPrompt: string,
6595
tools: Tool[] = [],
66-
): ChatCompletionMessageParam[] {
96+
): Promise<ChatCompletionMessageParam[]> {
6797
const baseSystemPrompt = this.getSystemPromptWithTime(customSystemPrompt);
6898
// Start with the enhanced system message
6999
const enhancedSystemPrompt = toolCallEngine.preparePrompt(baseSystemPrompt, tools);
@@ -83,6 +113,50 @@ export class MessageHistory {
83113
// Create a unified processing path with optional image limiting
84114
this.processEvents(events, messages, toolCallEngine);
85115

116+
// Apply context compression if enabled
117+
if (this.contextManager && this.sessionId) {
118+
const compressionCheck = await this.contextManager.shouldCompress(
119+
messages,
120+
events,
121+
this.sessionId,
122+
this.iteration
123+
);
124+
125+
if (compressionCheck.shouldCompress) {
126+
this.logger.info(
127+
`Context compression triggered: ${compressionCheck.currentTokens} tokens, reason: ${compressionCheck.reason}`
128+
);
129+
130+
try {
131+
const compressionResult = await this.contextManager.compress(
132+
messages,
133+
events,
134+
this.sessionId,
135+
this.iteration,
136+
compressionCheck.reason
137+
);
138+
139+
// Send compression event to event stream
140+
const compressionEvent = this.eventStream.createEvent('system', {
141+
level: 'info',
142+
message: `Context compressed: ${compressionResult.stats.originalMessageCount}${compressionResult.stats.compressedMessageCount} messages (${(compressionResult.stats.compressionRatio * 100).toFixed(1)}% reduction)`,
143+
details: {
144+
strategy: compressionResult.stats.strategy,
145+
originalTokens: compressionResult.stats.originalTokens,
146+
compressedTokens: compressionResult.stats.compressedTokens,
147+
compressionRatio: compressionResult.stats.compressionRatio,
148+
},
149+
});
150+
this.eventStream.sendEvent(compressionEvent);
151+
152+
return compressionResult.messages;
153+
} catch (error) {
154+
this.logger.error(`Context compression failed: ${error}`);
155+
// Fall back to original messages if compression fails
156+
}
157+
}
158+
}
159+
86160
return messages;
87161
}
88162

multimodal/tarko/agent/src/agent/runner/llm-processor.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export class LLMProcessor {
5757
this.messageHistory = new MessageHistory(
5858
this.eventStream,
5959
this.contextAwarenessOptions?.maxImagesCount,
60+
this.contextAwarenessOptions,
6061
);
6162
this.enableStreamingToolCallEvents = enableStreamingToolCallEvents;
6263
this.enableMetrics = enableMetrics;
@@ -188,8 +189,11 @@ export class LLMProcessor {
188189
this.toolProcessor.setExecutionTools(finalTools);
189190
}
190191

192+
// Update session information for compression tracking
193+
this.messageHistory.updateSession(sessionId, iteration, currentModel.id, currentModel.provider);
194+
191195
// Build messages for current iteration including enhanced system message
192-
const messages = this.messageHistory.toMessageHistory(
196+
const messages = await this.messageHistory.toMessageHistory(
193197
toolCallEngine,
194198
finalSystemPrompt,
195199
finalTools,

0 commit comments

Comments
 (0)