@loxia-labs/loxia-autopilot-one 1.0.1 → 1.0.4
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 +44 -54
- package/bin/cli.js +1 -115
- package/bin/loxia-terminal-v2.js +3 -0
- package/bin/loxia-terminal.js +3 -0
- package/bin/start-with-terminal.js +3 -0
- package/package.json +15 -15
- package/scripts/install-scanners.js +1 -235
- package/src/analyzers/CSSAnalyzer.js +1 -297
- package/src/analyzers/ConfigValidator.js +1 -690
- package/src/analyzers/ESLintAnalyzer.js +1 -320
- package/src/analyzers/JavaScriptAnalyzer.js +1 -261
- package/src/analyzers/PrettierFormatter.js +1 -247
- package/src/analyzers/PythonAnalyzer.js +1 -266
- package/src/analyzers/SecurityAnalyzer.js +1 -729
- package/src/analyzers/TypeScriptAnalyzer.js +1 -247
- package/src/analyzers/codeCloneDetector/analyzer.js +1 -344
- package/src/analyzers/codeCloneDetector/detector.js +1 -203
- package/src/analyzers/codeCloneDetector/index.js +1 -160
- package/src/analyzers/codeCloneDetector/parser.js +1 -199
- package/src/analyzers/codeCloneDetector/reporter.js +1 -148
- package/src/analyzers/codeCloneDetector/scanner.js +1 -59
- package/src/core/agentPool.js +1 -1474
- package/src/core/agentScheduler.js +1 -2147
- package/src/core/contextManager.js +1 -709
- package/src/core/messageProcessor.js +1 -732
- package/src/core/orchestrator.js +1 -548
- package/src/core/stateManager.js +1 -877
- package/src/index.js +1 -631
- package/src/interfaces/cli.js +1 -549
- package/src/interfaces/terminal/__tests__/smoke/advancedFeatures.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/agentControl.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/agents.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/components.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/connection.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/enhancements.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/imports.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/messages.test.js +1 -0
- package/src/interfaces/terminal/__tests__/smoke/tools.test.js +1 -0
- package/src/interfaces/terminal/api/apiClient.js +1 -0
- package/src/interfaces/terminal/api/messageRouter.js +1 -0
- package/src/interfaces/terminal/api/session.js +1 -0
- package/src/interfaces/terminal/api/websocket.js +1 -0
- package/src/interfaces/terminal/components/AgentCreator.js +1 -0
- package/src/interfaces/terminal/components/AgentEditor.js +1 -0
- package/src/interfaces/terminal/components/AgentSwitcher.js +1 -0
- package/src/interfaces/terminal/components/ErrorBoundary.js +1 -0
- package/src/interfaces/terminal/components/ErrorPanel.js +1 -0
- package/src/interfaces/terminal/components/Header.js +1 -0
- package/src/interfaces/terminal/components/HelpPanel.js +1 -0
- package/src/interfaces/terminal/components/InputBox.js +1 -0
- package/src/interfaces/terminal/components/Layout.js +1 -0
- package/src/interfaces/terminal/components/LoadingSpinner.js +1 -0
- package/src/interfaces/terminal/components/MessageList.js +1 -0
- package/src/interfaces/terminal/components/MultilineTextInput.js +1 -0
- package/src/interfaces/terminal/components/SearchPanel.js +1 -0
- package/src/interfaces/terminal/components/SettingsPanel.js +1 -0
- package/src/interfaces/terminal/components/StatusBar.js +1 -0
- package/src/interfaces/terminal/components/TextInput.js +1 -0
- package/src/interfaces/terminal/config/agentEditorConstants.js +1 -0
- package/src/interfaces/terminal/config/constants.js +1 -0
- package/src/interfaces/terminal/index.js +1 -0
- package/src/interfaces/terminal/state/useAgentControl.js +1 -0
- package/src/interfaces/terminal/state/useAgents.js +1 -0
- package/src/interfaces/terminal/state/useConnection.js +1 -0
- package/src/interfaces/terminal/state/useMessages.js +1 -0
- package/src/interfaces/terminal/state/useTools.js +1 -0
- package/src/interfaces/terminal/utils/debugLogger.js +1 -0
- package/src/interfaces/terminal/utils/settingsStorage.js +1 -0
- package/src/interfaces/terminal/utils/theme.js +1 -0
- package/src/interfaces/webServer.js +1 -2162
- package/src/modules/fileExplorer/controller.js +1 -280
- package/src/modules/fileExplorer/index.js +1 -37
- package/src/modules/fileExplorer/middleware.js +1 -92
- package/src/modules/fileExplorer/routes.js +1 -125
- package/src/modules/fileExplorer/types.js +1 -44
- package/src/services/aiService.js +1 -1232
- package/src/services/apiKeyManager.js +1 -164
- package/src/services/benchmarkService.js +1 -366
- package/src/services/budgetService.js +1 -539
- package/src/services/contextInjectionService.js +1 -247
- package/src/services/conversationCompactionService.js +1 -637
- package/src/services/errorHandler.js +1 -810
- package/src/services/fileAttachmentService.js +1 -544
- package/src/services/modelRouterService.js +1 -366
- package/src/services/modelsService.js +1 -322
- package/src/services/qualityInspector.js +1 -796
- package/src/services/tokenCountingService.js +1 -536
- package/src/tools/agentCommunicationTool.js +1 -1344
- package/src/tools/agentDelayTool.js +1 -485
- package/src/tools/asyncToolManager.js +1 -604
- package/src/tools/baseTool.js +1 -800
- package/src/tools/browserTool.js +1 -920
- package/src/tools/cloneDetectionTool.js +1 -621
- package/src/tools/dependencyResolverTool.js +1 -1215
- package/src/tools/fileContentReplaceTool.js +1 -875
- package/src/tools/fileSystemTool.js +1 -1107
- package/src/tools/fileTreeTool.js +1 -853
- package/src/tools/imageTool.js +1 -901
- package/src/tools/importAnalyzerTool.js +1 -1060
- package/src/tools/jobDoneTool.js +1 -248
- package/src/tools/seekTool.js +1 -956
- package/src/tools/staticAnalysisTool.js +1 -1778
- package/src/tools/taskManagerTool.js +1 -2873
- package/src/tools/terminalTool.js +1 -2304
- package/src/tools/webTool.js +1 -1430
- package/src/types/agent.js +1 -519
- package/src/types/contextReference.js +1 -972
- package/src/types/conversation.js +1 -730
- package/src/types/toolCommand.js +1 -747
- package/src/utilities/attachmentValidator.js +1 -292
- package/src/utilities/configManager.js +1 -582
- package/src/utilities/constants.js +1 -722
- package/src/utilities/directoryAccessManager.js +1 -535
- package/src/utilities/fileProcessor.js +1 -307
- package/src/utilities/logger.js +1 -436
- package/src/utilities/tagParser.js +1 -1246
- package/src/utilities/toolConstants.js +1 -317
- package/web-ui/build/index.html +2 -2
- package/web-ui/build/static/{index-Dy2bYbOa.css → index-CClD1090.css} +1 -1
- package/web-ui/build/static/{index-CjkkcnFA.js → index-lCBai6dX.js} +66 -67
|
@@ -1,536 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TokenCountingService - Accurate token counting for conversation compactization
|
|
3
|
-
*
|
|
4
|
-
* Purpose:
|
|
5
|
-
* - Provide accurate token counting using tiktoken (OpenAI's tokenizer)
|
|
6
|
-
* - Support multiple models with different tokenization schemes
|
|
7
|
-
* - Determine when conversation compactization should be triggered
|
|
8
|
-
* - Cache token counts for performance optimization
|
|
9
|
-
* - Provide fallback estimation when accurate counting unavailable
|
|
10
|
-
*
|
|
11
|
-
* Key Features:
|
|
12
|
-
* - Accurate token counting via tiktoken library
|
|
13
|
-
* - Model-specific tokenization
|
|
14
|
-
* - Intelligent caching with TTL
|
|
15
|
-
* - Fast estimation fallback
|
|
16
|
-
* - Context window management
|
|
17
|
-
* - Compaction trigger detection
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { encoding_for_model } from 'tiktoken';
|
|
21
|
-
import {
|
|
22
|
-
MODELS,
|
|
23
|
-
COMPACTION_CONFIG,
|
|
24
|
-
TOKEN_COUNTING_MODES,
|
|
25
|
-
} from '../utilities/constants.js';
|
|
26
|
-
|
|
27
|
-
class TokenCountingService {
|
|
28
|
-
constructor(logger) {
|
|
29
|
-
this.logger = logger;
|
|
30
|
-
|
|
31
|
-
// Token count cache for performance
|
|
32
|
-
this.tokenCache = new Map();
|
|
33
|
-
|
|
34
|
-
// Tiktoken encoders cache (reuse encoders for efficiency)
|
|
35
|
-
this.encoders = new Map();
|
|
36
|
-
|
|
37
|
-
// Supported tiktoken models mapping
|
|
38
|
-
this.tiktokenModelMap = {
|
|
39
|
-
// Anthropic models use OpenAI's cl100k_base encoding
|
|
40
|
-
[MODELS.ANTHROPIC_SONNET]: 'gpt-4',
|
|
41
|
-
[MODELS.ANTHROPIC_OPUS]: 'gpt-4',
|
|
42
|
-
[MODELS.ANTHROPIC_HAIKU]: 'gpt-4',
|
|
43
|
-
|
|
44
|
-
// OpenAI models
|
|
45
|
-
[MODELS.GPT_4]: 'gpt-4',
|
|
46
|
-
[MODELS.GPT_4_MINI]: 'gpt-4',
|
|
47
|
-
'gpt-4o': 'gpt-4',
|
|
48
|
-
'gpt-4o-mini': 'gpt-4',
|
|
49
|
-
'gpt-4-turbo': 'gpt-4',
|
|
50
|
-
'gpt-3.5-turbo': 'gpt-4', // Uses cl100k_base encoding
|
|
51
|
-
|
|
52
|
-
// DeepSeek uses similar tokenization to GPT-4
|
|
53
|
-
[MODELS.DEEPSEEK_R1]: 'gpt-4',
|
|
54
|
-
|
|
55
|
-
// Phi models - fallback to GPT-4 encoding
|
|
56
|
-
[MODELS.PHI_4]: 'gpt-4',
|
|
57
|
-
|
|
58
|
-
// Azure AI Foundry models
|
|
59
|
-
'azure-ai-grok3': 'gpt-4',
|
|
60
|
-
'azure-ai-deepseek-r1': 'gpt-4',
|
|
61
|
-
'azure-openai-gpt-5': 'gpt-4',
|
|
62
|
-
'azure-openai-gpt-4': 'gpt-4',
|
|
63
|
-
'azure-openai-gpt-4o': 'gpt-4',
|
|
64
|
-
|
|
65
|
-
// Compaction models
|
|
66
|
-
[COMPACTION_CONFIG.COMPACTION_MODEL]: 'gpt-4',
|
|
67
|
-
[COMPACTION_CONFIG.COMPACTION_MODEL_FALLBACK]: 'gpt-4',
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
this.logger?.info('TokenCountingService initialized', {
|
|
71
|
-
supportedModels: Object.keys(this.tiktokenModelMap).length,
|
|
72
|
-
cacheEnabled: true
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Count tokens in text using accurate tiktoken encoder
|
|
78
|
-
* @param {string} text - Text to count tokens in
|
|
79
|
-
* @param {string} model - Model name for appropriate tokenization
|
|
80
|
-
* @param {string} mode - Counting mode (accurate, estimated, cached)
|
|
81
|
-
* @returns {Promise<number>} Token count
|
|
82
|
-
*/
|
|
83
|
-
async countTokens(text, model, mode = TOKEN_COUNTING_MODES.ACCURATE) {
|
|
84
|
-
// Validate input
|
|
85
|
-
if (!text || typeof text !== 'string') {
|
|
86
|
-
return 0;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Check cache first if enabled
|
|
90
|
-
if (mode === TOKEN_COUNTING_MODES.CACHED) {
|
|
91
|
-
const cached = this._getCachedTokenCount(text, model);
|
|
92
|
-
if (cached !== null) {
|
|
93
|
-
return cached;
|
|
94
|
-
}
|
|
95
|
-
// Fall through to accurate counting if not cached
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Use fast estimation if requested
|
|
99
|
-
if (mode === TOKEN_COUNTING_MODES.ESTIMATED) {
|
|
100
|
-
return this._estimateTokens(text);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Accurate counting with tiktoken
|
|
104
|
-
try {
|
|
105
|
-
const encoder = await this._getEncoder(model);
|
|
106
|
-
const tokens = encoder.encode(text);
|
|
107
|
-
const count = tokens.length;
|
|
108
|
-
|
|
109
|
-
// Cache the result
|
|
110
|
-
this._cacheTokenCount(text, model, count);
|
|
111
|
-
|
|
112
|
-
return count;
|
|
113
|
-
|
|
114
|
-
} catch (error) {
|
|
115
|
-
this.logger?.warn('Tiktoken encoding failed, falling back to estimation', {
|
|
116
|
-
model,
|
|
117
|
-
error: error.message
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// Fallback to estimation
|
|
121
|
-
return this._estimateTokens(text);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Estimate conversation token count including all messages
|
|
127
|
-
* @param {Array} messages - Array of message objects with { role, content }
|
|
128
|
-
* @param {string} model - Model name for tokenization
|
|
129
|
-
* @param {string} mode - Counting mode
|
|
130
|
-
* @returns {Promise<number>} Total token count for conversation
|
|
131
|
-
*/
|
|
132
|
-
async estimateConversationTokens(messages, model, mode = TOKEN_COUNTING_MODES.ACCURATE) {
|
|
133
|
-
if (!Array.isArray(messages) || messages.length === 0) {
|
|
134
|
-
return 0;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
let totalTokens = 0;
|
|
138
|
-
|
|
139
|
-
// Count tokens for each message
|
|
140
|
-
for (const message of messages) {
|
|
141
|
-
if (!message.content) {
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Message formatting overhead (role + formatting)
|
|
146
|
-
const formattingOverhead = this._getMessageFormattingOverhead(model);
|
|
147
|
-
|
|
148
|
-
// Content tokens
|
|
149
|
-
const contentTokens = await this.countTokens(message.content, model, mode);
|
|
150
|
-
|
|
151
|
-
totalTokens += contentTokens + formattingOverhead;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Add conversation-level overhead (system instructions, etc.)
|
|
155
|
-
const conversationOverhead = this._getConversationOverhead(messages.length);
|
|
156
|
-
totalTokens += conversationOverhead;
|
|
157
|
-
|
|
158
|
-
this.logger?.debug('Conversation token count', {
|
|
159
|
-
model,
|
|
160
|
-
messageCount: messages.length,
|
|
161
|
-
totalTokens,
|
|
162
|
-
mode
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
return totalTokens;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Get context window size for a model
|
|
170
|
-
* @param {string} model - Model name
|
|
171
|
-
* @returns {number} Context window size in tokens
|
|
172
|
-
*/
|
|
173
|
-
getModelContextWindow(model) {
|
|
174
|
-
// Model context windows (from aiService model specs and vendor documentation)
|
|
175
|
-
const contextWindows = {
|
|
176
|
-
// Anthropic Claude models
|
|
177
|
-
[MODELS.ANTHROPIC_SONNET]: 200000,
|
|
178
|
-
[MODELS.ANTHROPIC_OPUS]: 200000,
|
|
179
|
-
[MODELS.ANTHROPIC_HAIKU]: 200000,
|
|
180
|
-
|
|
181
|
-
// OpenAI models
|
|
182
|
-
[MODELS.GPT_4]: 128000,
|
|
183
|
-
[MODELS.GPT_4_MINI]: 128000,
|
|
184
|
-
'gpt-4o': 128000,
|
|
185
|
-
'gpt-4o-mini': 128000,
|
|
186
|
-
'gpt-4-turbo': 128000,
|
|
187
|
-
'gpt-3.5-turbo': 16384,
|
|
188
|
-
|
|
189
|
-
// DeepSeek models
|
|
190
|
-
[MODELS.DEEPSEEK_R1]: 128000,
|
|
191
|
-
|
|
192
|
-
// Phi models
|
|
193
|
-
[MODELS.PHI_4]: 16384,
|
|
194
|
-
|
|
195
|
-
// Azure AI Foundry models
|
|
196
|
-
'azure-ai-grok3': 128000,
|
|
197
|
-
'azure-ai-deepseek-r1': 128000,
|
|
198
|
-
'azure-openai-gpt-5': 128000,
|
|
199
|
-
'azure-openai-gpt-4': 128000,
|
|
200
|
-
'azure-openai-gpt-4o': 128000,
|
|
201
|
-
|
|
202
|
-
// Compaction models
|
|
203
|
-
[COMPACTION_CONFIG.COMPACTION_MODEL]: 128000,
|
|
204
|
-
[COMPACTION_CONFIG.COMPACTION_MODEL_FALLBACK]: 128000,
|
|
205
|
-
|
|
206
|
-
// Router model
|
|
207
|
-
'autopilot-model-router': 16384,
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
const contextWindow = contextWindows[model];
|
|
211
|
-
|
|
212
|
-
if (!contextWindow) {
|
|
213
|
-
this.logger?.warn('Unknown model context window, using default', {
|
|
214
|
-
model,
|
|
215
|
-
defaultWindow: 128000
|
|
216
|
-
});
|
|
217
|
-
return 128000; // Default to 128k
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return contextWindow;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Get maximum output tokens for a model
|
|
225
|
-
* @param {string} model - Model name
|
|
226
|
-
* @returns {number} Maximum output tokens
|
|
227
|
-
*/
|
|
228
|
-
getModelMaxOutputTokens(model) {
|
|
229
|
-
// Max output tokens - increased to 8K-20K where supported for better responses
|
|
230
|
-
// Note: These are conservative estimates to ensure compaction triggers appropriately
|
|
231
|
-
const maxOutputTokens = {
|
|
232
|
-
// Anthropic Claude models - support up to 8K output
|
|
233
|
-
[MODELS.ANTHROPIC_SONNET]: 8192,
|
|
234
|
-
[MODELS.ANTHROPIC_OPUS]: 8192,
|
|
235
|
-
[MODELS.ANTHROPIC_HAIKU]: 8192,
|
|
236
|
-
|
|
237
|
-
// OpenAI models
|
|
238
|
-
[MODELS.GPT_4]: 8192, // Supports up to 16K, using 8K for safety
|
|
239
|
-
[MODELS.GPT_4_MINI]: 16384, // Already at max
|
|
240
|
-
'gpt-4o': 8192, // Supports up to 16K, using 8K
|
|
241
|
-
'gpt-4o-mini': 16384, // Already at max
|
|
242
|
-
'gpt-4-turbo': 8192, // Increased from default
|
|
243
|
-
'gpt-3.5-turbo': 4096, // Smaller model, keep at 4K
|
|
244
|
-
|
|
245
|
-
// DeepSeek models
|
|
246
|
-
[MODELS.DEEPSEEK_R1]: 8192, // Already at max
|
|
247
|
-
|
|
248
|
-
// Phi models - smaller architecture
|
|
249
|
-
[MODELS.PHI_4]: 4096, // Increased from 2048
|
|
250
|
-
|
|
251
|
-
// Azure AI Foundry models
|
|
252
|
-
'azure-ai-grok3': 8192, // Increased from 4K
|
|
253
|
-
'azure-ai-deepseek-r1': 8192, // Already at max
|
|
254
|
-
'azure-openai-gpt-5': 8192, // Increased from 4K
|
|
255
|
-
'azure-openai-gpt-4': 8192, // Increased from default
|
|
256
|
-
'azure-openai-gpt-4o': 8192, // Increased from default
|
|
257
|
-
|
|
258
|
-
// Compaction models - keep moderate for efficiency
|
|
259
|
-
[COMPACTION_CONFIG.COMPACTION_MODEL]: 8192,
|
|
260
|
-
[COMPACTION_CONFIG.COMPACTION_MODEL_FALLBACK]: 8192,
|
|
261
|
-
|
|
262
|
-
// Router model - keep small for fast routing
|
|
263
|
-
'autopilot-model-router': 2048,
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
return maxOutputTokens[model] || 8192; // Default increased to 8K
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Determine if compaction should be triggered
|
|
271
|
-
* @param {number} currentTokens - Current conversation token count (K)
|
|
272
|
-
* @param {number} maxOutputTokens - Max tokens model can output (X)
|
|
273
|
-
* @param {number} contextWindow - Model's context window size (C)
|
|
274
|
-
* @param {number} threshold - Trigger threshold (default 0.8 = 80%)
|
|
275
|
-
* @returns {boolean} True if compaction should be triggered
|
|
276
|
-
*/
|
|
277
|
-
shouldTriggerCompaction(currentTokens, maxOutputTokens, contextWindow, threshold = COMPACTION_CONFIG.DEFAULT_THRESHOLD) {
|
|
278
|
-
// Validate threshold
|
|
279
|
-
if (threshold < COMPACTION_CONFIG.MIN_THRESHOLD || threshold > COMPACTION_CONFIG.MAX_THRESHOLD) {
|
|
280
|
-
this.logger?.warn('Invalid compaction threshold, using default', {
|
|
281
|
-
provided: threshold,
|
|
282
|
-
default: COMPACTION_CONFIG.DEFAULT_THRESHOLD
|
|
283
|
-
});
|
|
284
|
-
threshold = COMPACTION_CONFIG.DEFAULT_THRESHOLD;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Calculate: K + X >= threshold * C
|
|
288
|
-
const requiredTokens = currentTokens + maxOutputTokens;
|
|
289
|
-
const thresholdTokens = threshold * contextWindow;
|
|
290
|
-
const shouldTrigger = requiredTokens >= thresholdTokens;
|
|
291
|
-
|
|
292
|
-
this.logger?.debug('Compaction trigger check', {
|
|
293
|
-
currentTokens,
|
|
294
|
-
maxOutputTokens,
|
|
295
|
-
contextWindow,
|
|
296
|
-
threshold,
|
|
297
|
-
requiredTokens,
|
|
298
|
-
thresholdTokens,
|
|
299
|
-
shouldTrigger,
|
|
300
|
-
utilizationPercent: ((requiredTokens / contextWindow) * 100).toFixed(2)
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
return shouldTrigger;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Calculate how many tokens to target after compaction
|
|
308
|
-
* @param {number} contextWindow - Model's context window size
|
|
309
|
-
* @param {number} targetThreshold - Target threshold after compaction (default 85%)
|
|
310
|
-
* @returns {number} Target token count after compaction
|
|
311
|
-
*/
|
|
312
|
-
calculateTargetTokenCount(contextWindow, targetThreshold = COMPACTION_CONFIG.MAX_ACCEPTABLE_TOKEN_COUNT_AFTER) {
|
|
313
|
-
return Math.floor(contextWindow * targetThreshold);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Validate that compaction achieved sufficient reduction
|
|
318
|
-
* @param {number} originalTokens - Token count before compaction
|
|
319
|
-
* @param {number} compactedTokens - Token count after compaction
|
|
320
|
-
* @param {number} contextWindow - Model's context window
|
|
321
|
-
* @returns {Object} Validation result { valid, reductionPercent, exceedsTarget }
|
|
322
|
-
*/
|
|
323
|
-
validateCompaction(originalTokens, compactedTokens, contextWindow) {
|
|
324
|
-
const reductionPercent = ((originalTokens - compactedTokens) / originalTokens) * 100;
|
|
325
|
-
const targetTokens = this.calculateTargetTokenCount(contextWindow);
|
|
326
|
-
const exceedsTarget = compactedTokens > targetTokens;
|
|
327
|
-
const sufficientReduction = reductionPercent >= COMPACTION_CONFIG.MIN_REDUCTION_PERCENTAGE;
|
|
328
|
-
|
|
329
|
-
const valid = !exceedsTarget && sufficientReduction;
|
|
330
|
-
|
|
331
|
-
this.logger?.info('Compaction validation', {
|
|
332
|
-
originalTokens,
|
|
333
|
-
compactedTokens,
|
|
334
|
-
reductionPercent: reductionPercent.toFixed(2),
|
|
335
|
-
targetTokens,
|
|
336
|
-
exceedsTarget,
|
|
337
|
-
sufficientReduction,
|
|
338
|
-
valid
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
return {
|
|
342
|
-
valid,
|
|
343
|
-
reductionPercent,
|
|
344
|
-
exceedsTarget,
|
|
345
|
-
sufficientReduction,
|
|
346
|
-
targetTokens,
|
|
347
|
-
compactedTokens,
|
|
348
|
-
originalTokens
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Clear token count cache
|
|
354
|
-
* @param {string} model - Optional: clear cache for specific model only
|
|
355
|
-
*/
|
|
356
|
-
clearCache(model = null) {
|
|
357
|
-
if (model) {
|
|
358
|
-
// Clear cache entries for specific model
|
|
359
|
-
for (const [key, value] of this.tokenCache.entries()) {
|
|
360
|
-
if (value.model === model) {
|
|
361
|
-
this.tokenCache.delete(key);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
this.logger?.debug('Token cache cleared for model', { model });
|
|
365
|
-
} else {
|
|
366
|
-
// Clear entire cache
|
|
367
|
-
this.tokenCache.clear();
|
|
368
|
-
this.logger?.debug('Token cache cleared completely');
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Get encoder for model (with caching)
|
|
374
|
-
* @private
|
|
375
|
-
*/
|
|
376
|
-
async _getEncoder(model) {
|
|
377
|
-
// Get tiktoken model name
|
|
378
|
-
const tiktokenModel = this.tiktokenModelMap[model] || 'gpt-4';
|
|
379
|
-
|
|
380
|
-
// Check if encoder is already cached
|
|
381
|
-
if (this.encoders.has(tiktokenModel)) {
|
|
382
|
-
return this.encoders.get(tiktokenModel);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// Create new encoder
|
|
386
|
-
const encoder = encoding_for_model(tiktokenModel);
|
|
387
|
-
this.encoders.set(tiktokenModel, encoder);
|
|
388
|
-
|
|
389
|
-
this.logger?.debug('Created tiktoken encoder', {
|
|
390
|
-
model,
|
|
391
|
-
tiktokenModel
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
return encoder;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Fast token estimation (character-based)
|
|
399
|
-
* @private
|
|
400
|
-
*/
|
|
401
|
-
_estimateTokens(text) {
|
|
402
|
-
if (!text || typeof text !== 'string') {
|
|
403
|
-
return 0;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// Use configured estimation ratio
|
|
407
|
-
return Math.ceil(text.length / COMPACTION_CONFIG.CHARS_PER_TOKEN_ESTIMATE);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Get cached token count if available and not expired
|
|
412
|
-
* @private
|
|
413
|
-
*/
|
|
414
|
-
_getCachedTokenCount(text, model) {
|
|
415
|
-
const cacheKey = this._getCacheKey(text, model);
|
|
416
|
-
const cached = this.tokenCache.get(cacheKey);
|
|
417
|
-
|
|
418
|
-
if (!cached) {
|
|
419
|
-
return null;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Check if cache entry is expired
|
|
423
|
-
const now = Date.now();
|
|
424
|
-
if (now - cached.timestamp > COMPACTION_CONFIG.TOKEN_COUNT_CACHE_TTL_MS) {
|
|
425
|
-
this.tokenCache.delete(cacheKey);
|
|
426
|
-
return null;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
return cached.count;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* Cache token count with timestamp
|
|
434
|
-
* @private
|
|
435
|
-
*/
|
|
436
|
-
_cacheTokenCount(text, model, count) {
|
|
437
|
-
const cacheKey = this._getCacheKey(text, model);
|
|
438
|
-
|
|
439
|
-
this.tokenCache.set(cacheKey, {
|
|
440
|
-
count,
|
|
441
|
-
model,
|
|
442
|
-
timestamp: Date.now()
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
// Prevent cache from growing indefinitely
|
|
446
|
-
if (this.tokenCache.size > 1000) {
|
|
447
|
-
// Remove oldest entries
|
|
448
|
-
const entries = Array.from(this.tokenCache.entries());
|
|
449
|
-
entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
|
|
450
|
-
|
|
451
|
-
// Remove oldest 20%
|
|
452
|
-
const toRemove = Math.floor(entries.length * 0.2);
|
|
453
|
-
for (let i = 0; i < toRemove; i++) {
|
|
454
|
-
this.tokenCache.delete(entries[i][0]);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Generate cache key for text + model
|
|
461
|
-
* @private
|
|
462
|
-
*/
|
|
463
|
-
_getCacheKey(text, model) {
|
|
464
|
-
// Use a simple hash for the text to avoid storing full text as key
|
|
465
|
-
const hash = this._simpleHash(text);
|
|
466
|
-
return `${model}:${hash}`;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/**
|
|
470
|
-
* Simple hash function for cache keys
|
|
471
|
-
* @private
|
|
472
|
-
*/
|
|
473
|
-
_simpleHash(str) {
|
|
474
|
-
let hash = 0;
|
|
475
|
-
for (let i = 0; i < str.length; i++) {
|
|
476
|
-
const char = str.charCodeAt(i);
|
|
477
|
-
hash = ((hash << 5) - hash) + char;
|
|
478
|
-
hash = hash & hash; // Convert to 32-bit integer
|
|
479
|
-
}
|
|
480
|
-
return hash.toString(36);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* Get message formatting overhead for model
|
|
485
|
-
* Accounts for role labels, XML tags, etc.
|
|
486
|
-
* @private
|
|
487
|
-
*/
|
|
488
|
-
_getMessageFormattingOverhead(model) {
|
|
489
|
-
// Different models have different formatting
|
|
490
|
-
// Anthropic: ~10 tokens per message (role tags)
|
|
491
|
-
// OpenAI: ~5 tokens per message (JSON formatting)
|
|
492
|
-
|
|
493
|
-
if (model.includes('anthropic') || model.includes('claude')) {
|
|
494
|
-
return 10;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
return 5; // Default for OpenAI-style models
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
/**
|
|
501
|
-
* Get conversation-level overhead
|
|
502
|
-
* Accounts for system prompts, special tokens, etc.
|
|
503
|
-
* @private
|
|
504
|
-
*/
|
|
505
|
-
_getConversationOverhead(messageCount) {
|
|
506
|
-
// Base overhead for conversation structure
|
|
507
|
-
const baseOverhead = 50;
|
|
508
|
-
|
|
509
|
-
// Additional overhead scales with message count
|
|
510
|
-
const scalingOverhead = messageCount * 2;
|
|
511
|
-
|
|
512
|
-
return baseOverhead + scalingOverhead;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* Clean up resources (close encoders)
|
|
517
|
-
*/
|
|
518
|
-
async cleanup() {
|
|
519
|
-
// Free tiktoken encoders
|
|
520
|
-
for (const [model, encoder] of this.encoders.entries()) {
|
|
521
|
-
try {
|
|
522
|
-
encoder.free();
|
|
523
|
-
this.logger?.debug('Freed tiktoken encoder', { model });
|
|
524
|
-
} catch (error) {
|
|
525
|
-
this.logger?.warn('Failed to free encoder', { model, error: error.message });
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
this.encoders.clear();
|
|
530
|
-
this.tokenCache.clear();
|
|
531
|
-
|
|
532
|
-
this.logger?.info('TokenCountingService cleaned up');
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
export default TokenCountingService;
|
|
1
|
+
function a0_0x404f(_0x51d496,_0x263c5d){_0x51d496=_0x51d496-0xc0;const _0x5eec30=a0_0x5eec();let _0x404fc7=_0x5eec30[_0x51d496];if(a0_0x404f['HhxLgS']===undefined){var _0x3a7935=function(_0x5d9c06){const _0x30f995='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x39deaf='',_0x9dc902='';for(let _0x222e5b=0x0,_0x270674,_0x49e451,_0x3f6edb=0x0;_0x49e451=_0x5d9c06['charAt'](_0x3f6edb++);~_0x49e451&&(_0x270674=_0x222e5b%0x4?_0x270674*0x40+_0x49e451:_0x49e451,_0x222e5b++%0x4)?_0x39deaf+=String['fromCharCode'](0xff&_0x270674>>(-0x2*_0x222e5b&0x6)):0x0){_0x49e451=_0x30f995['indexOf'](_0x49e451);}for(let _0x4c3474=0x0,_0x3d2053=_0x39deaf['length'];_0x4c3474<_0x3d2053;_0x4c3474++){_0x9dc902+='%'+('00'+_0x39deaf['charCodeAt'](_0x4c3474)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x9dc902);};a0_0x404f['XSmPsr']=_0x3a7935,a0_0x404f['pyVWJb']={},a0_0x404f['HhxLgS']=!![];}const _0x37a080=_0x5eec30[0x0],_0x5412f7=_0x51d496+_0x37a080,_0x1ad6bd=a0_0x404f['pyVWJb'][_0x5412f7];return!_0x1ad6bd?(_0x404fc7=a0_0x404f['XSmPsr'](_0x404fc7),a0_0x404f['pyVWJb'][_0x5412f7]=_0x404fc7):_0x404fc7=_0x1ad6bd,_0x404fc7;}const a0_0x419ff6=a0_0x404f;function a0_0x5eec(){const _0x1c650d=['BwvZC2fNzq','odyZndmYrgLzDNnV','zNjVBq','q09nuefdveLptL9nt0rfta','revgqvvmvf9usfjfu0Hpteq','y29UDgvUDa','DgLRDg9Rzw5nB2rLBe1HCa','mMj0C3LxzG','z2v0tw9KzwXdB250zxH0v2LUzg93','mJbntvL3s1a','mti5nJy3ovbYCxfHvG','odeWndeZnKPlv3ztCG','qu5usfjpueLdx0HbsuTv','tuLox1jfrfvdveLptL9qrvjdru5uquDf','ve9lru5Fq09vtLrFq0fdsevFvfrmx01t','Dg9tDhjPBMC','zgvIDwC','y2XLyxjdywnOzq','mJaXmZu4ntn2yK1kwNm','C3rYAw5N','BgvUz3rO','C2v0','y2XLyw51Ca','mJmWotKXmfHUuMPHCq','q09nuefdveLptL9nt0rftf9gquXmqKfdsW','vg9Rzw5dB3vUDgLUz1nLCNzPy2uGy2XLyw5Lzcb1Ca','z3b0ltq','revfufnfruTFuJe','zgvSzxrL','mtyWnZG0mwnnqM1krG','AxnbCNjHEq','q0fdseve','x2DLDevUy29Kzxi','y2XLyxi','z2v0tw9KzwXnyxHpDxrWDxruB2TLBNm','vgLRDg9Rzw4Gzw5JB2rPBMCGzMfPBgvKlcbMywXSAw5NigjHy2SGDg8Gzxn0Aw1HDgLVBG','x2DLDenHy2HLzfrVA2vUq291BNq','Dg9Rzw5dywnOzq','AgfZ','q29TCgfJDgLVBIb0CMLNz2vYignOzwnR','Bw9KzwW','x2vZDgLTyxrLvg9Rzw5Z','y2XHDwrL','rMfPBgvKihrVigzYzwuGzw5JB2rLCG','A2v5CW','DMfSAwrHDgvdB21Wywn0Aw9U','x3nPBxbSzuHHC2G','y291BNruB2TLBNm','vg9Rzw4Gy2fJAguGy2XLyxjLzcbJB21WBgv0zwX5','r1buxZrFtuLosq','zMXVB3i','q0HbuLnFuevsx1rps0vox0vtveLnqvrf','zw5JB2rLCNm','x2DLDenHy2HLs2v5','q29UDMvYC2f0Aw9UihrVA2vUignVDw50','tufyx0fdq0vqvefctevFve9lru5Fq09vtLrFquzurvi','otu2otyZwgvuCe9k','mtbjEuv6uge','zw50CMLLCW','x2DLDenVBNzLCNnHDgLVBK92zxjOzwfK','ueHjxZq','zw5JB2rL','x2DLDe1LC3nHz2vgB3jTyxr0Aw5Nt3zLCMHLywq','Bg9Nz2vY','rvnusu1bveve','z2v0','qundvvjbveu','DgLTzxn0yw1W'];a0_0x5eec=function(){return _0x1c650d;};return a0_0x5eec();}(function(_0x411312,_0x3a51c8){const _0x415921=a0_0x404f,_0x3ca8aa=_0x411312();while(!![]){try{const _0x25653c=-parseInt(_0x415921(0xe8))/0x1+parseInt(_0x415921(0xe5))/0x2*(-parseInt(_0x415921(0xfb))/0x3)+parseInt(_0x415921(0xdf))/0x4*(-parseInt(_0x415921(0xd3))/0x5)+-parseInt(_0x415921(0xf5))/0x6+-parseInt(_0x415921(0xd2))/0x7+-parseInt(_0x415921(0xe9))/0x8+-parseInt(_0x415921(0xf0))/0x9*(-parseInt(_0x415921(0xe7))/0xa);if(_0x25653c===_0x3a51c8)break;else _0x3ca8aa['push'](_0x3ca8aa['shift']());}catch(_0x36f724){_0x3ca8aa['push'](_0x3ca8aa['shift']());}}}(a0_0x5eec,0xa4efd));import{encoding_for_model}from'tiktoken';import{MODELS,COMPACTION_CONFIG,TOKEN_COUNTING_MODES}from'../utilities/constants.js';class TokenCountingService{constructor(_0x39deaf){const _0x2cc6b8=a0_0x404f;this['logger']=_0x39deaf,this[_0x2cc6b8(0x103)]=new Map(),this['encoders']=new Map(),this['tiktokenModelMap']={[MODELS['ANTHROPIC_SONNET']]:_0x2cc6b8(0xf8),[MODELS['ANTHROPIC_OPUS']]:'gpt-4',[MODELS[_0x2cc6b8(0xea)]]:_0x2cc6b8(0xf8),[MODELS['GPT_4']]:'gpt-4',[MODELS[_0x2cc6b8(0xcb)]]:_0x2cc6b8(0xf8),'gpt-4o':'gpt-4','gpt-4o-mini':'gpt-4','gpt-4-turbo':_0x2cc6b8(0xf8),'gpt-3.5-turbo':_0x2cc6b8(0xf8),[MODELS[_0x2cc6b8(0xf9)]]:'gpt-4',[MODELS[_0x2cc6b8(0xd6)]]:_0x2cc6b8(0xf8),'azure-ai-grok3':'gpt-4','azure-ai-deepseek-r1':'gpt-4','azure-openai-gpt-5':_0x2cc6b8(0xf8),'azure-openai-gpt-4':'gpt-4','azure-openai-gpt-4o':'gpt-4',[COMPACTION_CONFIG['COMPACTION_MODEL']]:_0x2cc6b8(0xf8),[COMPACTION_CONFIG['COMPACTION_MODEL_FALLBACK']]:'gpt-4'},this[_0x2cc6b8(0xd9)]?.['info']('TokenCountingService\x20initialized',{'supportedModels':Object[_0x2cc6b8(0xc6)](this['tiktokenModelMap'])['length'],'cacheEnabled':!![]});}async[a0_0x419ff6(0xc9)](_0x9dc902,_0x222e5b,_0x270674=TOKEN_COUNTING_MODES[a0_0x419ff6(0xdc)]){const _0x1cb57c=a0_0x419ff6;if(!_0x9dc902||typeof _0x9dc902!==_0x1cb57c(0xf1))return 0x0;if(_0x270674===TOKEN_COUNTING_MODES[_0x1cb57c(0xfd)]){const _0x49e451=this[_0x1cb57c(0x102)](_0x9dc902,_0x222e5b);if(_0x49e451!==null)return _0x49e451;}if(_0x270674===TOKEN_COUNTING_MODES[_0x1cb57c(0xda)])return this[_0x1cb57c(0xc3)](_0x9dc902);try{const _0x3f6edb=await this[_0x1cb57c(0xfe)](_0x222e5b),_0x4c3474=_0x3f6edb[_0x1cb57c(0xd7)](_0x9dc902),_0x3d2053=_0x4c3474[_0x1cb57c(0xf2)];return this['_cacheTokenCount'](_0x9dc902,_0x222e5b,_0x3d2053),_0x3d2053;}catch(_0x1c382f){return this[_0x1cb57c(0xd9)]?.['warn'](_0x1cb57c(0x101),{'model':_0x222e5b,'error':_0x1c382f[_0x1cb57c(0xde)]}),this[_0x1cb57c(0xc3)](_0x9dc902);}}async['estimateConversationTokens'](_0x41d8fd,_0x58dc07,_0x27198e=TOKEN_COUNTING_MODES[a0_0x419ff6(0xdc)]){const _0x4a9adb=a0_0x419ff6;if(!Array[_0x4a9adb(0xfc)](_0x41d8fd)||_0x41d8fd['length']===0x0)return 0x0;let _0x2150ce=0x0;for(const _0x4ec58c of _0x41d8fd){if(!_0x4ec58c[_0x4a9adb(0xe3)])continue;const _0x23ccbc=this['_getMessageFormattingOverhead'](_0x58dc07),_0x22c251=await this['countTokens'](_0x4ec58c[_0x4a9adb(0xe3)],_0x58dc07,_0x27198e);_0x2150ce+=_0x22c251+_0x23ccbc;}const _0x4eba1e=this[_0x4a9adb(0xd5)](_0x41d8fd[_0x4a9adb(0xf2)]);return _0x2150ce+=_0x4eba1e,this[_0x4a9adb(0xd9)]?.[_0x4a9adb(0xee)](_0x4a9adb(0xd0),{'model':_0x58dc07,'messageCount':_0x41d8fd['length'],'totalTokens':_0x2150ce,'mode':_0x27198e}),_0x2150ce;}[a0_0x419ff6(0xe6)](_0xd35df7){const _0x2c9133=a0_0x419ff6,_0xe85724={[MODELS['ANTHROPIC_SONNET']]:0x30d40,[MODELS['ANTHROPIC_OPUS']]:0x30d40,[MODELS['ANTHROPIC_HAIKU']]:0x30d40,[MODELS['GPT_4']]:0x1f400,[MODELS[_0x2c9133(0xcb)]]:0x1f400,'gpt-4o':0x1f400,'gpt-4o-mini':0x1f400,'gpt-4-turbo':0x1f400,'gpt-3.5-turbo':0x4000,[MODELS['DEEPSEEK_R1']]:0x1f400,[MODELS[_0x2c9133(0xd6)]]:0x4000,'azure-ai-grok3':0x1f400,'azure-ai-deepseek-r1':0x1f400,'azure-openai-gpt-5':0x1f400,'azure-openai-gpt-4':0x1f400,'azure-openai-gpt-4o':0x1f400,[COMPACTION_CONFIG[_0x2c9133(0xe1)]]:0x1f400,[COMPACTION_CONFIG['COMPACTION_MODEL_FALLBACK']]:0x1f400,'autopilot-model-router':0x4000},_0x298507=_0xe85724[_0xd35df7];if(!_0x298507)return this['logger']?.['warn']('Unknown model context window, using default',{'model':_0xd35df7,'defaultWindow':0x1f400}),0x1f400;return _0x298507;}[a0_0x419ff6(0x100)](_0x5c029a){const _0x16bbae=a0_0x419ff6,_0x5d790b={[MODELS['ANTHROPIC_SONNET']]:0x2000,[MODELS['ANTHROPIC_OPUS']]:0x2000,[MODELS['ANTHROPIC_HAIKU']]:0x2000,[MODELS['GPT_4']]:0x2000,[MODELS[_0x16bbae(0xcb)]]:0x4000,'gpt-4o':0x2000,'gpt-4o-mini':0x4000,'gpt-4-turbo':0x2000,'gpt-3.5-turbo':0x1000,[MODELS[_0x16bbae(0xf9)]]:0x2000,[MODELS[_0x16bbae(0xd6)]]:0x1000,'azure-ai-grok3':0x2000,'azure-ai-deepseek-r1':0x2000,'azure-openai-gpt-5':0x2000,'azure-openai-gpt-4':0x2000,'azure-openai-gpt-4o':0x2000,[COMPACTION_CONFIG[_0x16bbae(0xe1)]]:0x2000,[COMPACTION_CONFIG[_0x16bbae(0xf6)]]:0x2000,'autopilot-model-router':0x800};return _0x5d790b[_0x5c029a]||0x2000;}['shouldTriggerCompaction'](_0x530ea9,_0x41b5e1,_0xa3e45e,_0x364ad0=COMPACTION_CONFIG[a0_0x419ff6(0xe2)]){const _0x25d1fb=a0_0x419ff6;(_0x364ad0<COMPACTION_CONFIG['MIN_THRESHOLD']||_0x364ad0>COMPACTION_CONFIG['MAX_THRESHOLD'])&&(this['logger']?.['warn']('Invalid compaction threshold, using default',{'provided':_0x364ad0,'default':COMPACTION_CONFIG[_0x25d1fb(0xe2)]}),_0x364ad0=COMPACTION_CONFIG['DEFAULT_THRESHOLD']);const requiredTokens=_0x530ea9+_0x41b5e1,_0x44eb76=_0x364ad0*_0xa3e45e,_0x2c3133=requiredTokens>=_0x44eb76;return this['logger']?.[_0x25d1fb(0xee)](_0x25d1fb(0xc1),{'currentTokens':_0x530ea9,'maxOutputTokens':_0x41b5e1,'contextWindow':_0xa3e45e,'threshold':_0x364ad0,'requiredTokens':requiredTokens,'thresholdTokens':_0x44eb76,'shouldTrigger':_0x2c3133,'utilizationPercent':(requiredTokens/_0xa3e45e*0x64)['toFixed'](0x2)}),_0x2c3133;}['calculateTargetTokenCount'](_0x536319,_0x38ef74=COMPACTION_CONFIG[a0_0x419ff6(0xd1)]){return Math['floor'](_0x536319*_0x38ef74);}[a0_0x419ff6(0xc7)](_0x16930f,_0x138bd6,_0xb49a31){const _0x57eaf6=a0_0x419ff6,_0x3cf57a=(_0x16930f-_0x138bd6)/_0x16930f*0x64,_0x38c339=this['calculateTargetTokenCount'](_0xb49a31),_0x4bfe0a=_0x138bd6>_0x38c339,_0x2c6499=_0x3cf57a>=COMPACTION_CONFIG[_0x57eaf6(0xeb)],_0x296906=!_0x4bfe0a&&_0x2c6499;return this[_0x57eaf6(0xd9)]?.['info']('Compaction\x20validation',{'originalTokens':_0x16930f,'compactedTokens':_0x138bd6,'reductionPercent':_0x3cf57a['toFixed'](0x2),'targetTokens':_0x38c339,'exceedsTarget':_0x4bfe0a,'sufficientReduction':_0x2c6499,'valid':_0x296906}),{'valid':_0x296906,'reductionPercent':_0x3cf57a,'exceedsTarget':_0x4bfe0a,'sufficientReduction':_0x2c6499,'targetTokens':_0x38c339,'compactedTokens':_0x138bd6,'originalTokens':_0x16930f};}[a0_0x419ff6(0xef)](_0x50784f=null){const _0x1cb217=a0_0x419ff6;if(_0x50784f){for(const [_0x10271a,_0x413250]of this['tokenCache'][_0x1cb217(0xd4)]()){_0x413250[_0x1cb217(0xc2)]===_0x50784f&&this[_0x1cb217(0x103)][_0x1cb217(0xfa)](_0x10271a);}this[_0x1cb217(0xd9)]?.['debug']('Token\x20cache\x20cleared\x20for\x20model',{'model':_0x50784f});}else this['tokenCache'][_0x1cb217(0xff)](),this[_0x1cb217(0xd9)]?.['debug'](_0x1cb217(0xca));}async['_getEncoder'](_0x12aff2){const _0x4bdac8=a0_0x419ff6,_0x1d349d=this[_0x4bdac8(0xe4)][_0x12aff2]||_0x4bdac8(0xf8);if(this['encoders'][_0x4bdac8(0xc0)](_0x1d349d))return this[_0x4bdac8(0xce)]['get'](_0x1d349d);const _0x25ddea=encoding_for_model(_0x1d349d);return this[_0x4bdac8(0xce)][_0x4bdac8(0xf3)](_0x1d349d,_0x25ddea),this['logger']?.[_0x4bdac8(0xee)]('Created\x20tiktoken\x20encoder',{'model':_0x12aff2,'tiktokenModel':_0x1d349d}),_0x25ddea;}['_estimateTokens'](_0xdda81){const _0x2d68d6=a0_0x419ff6;if(!_0xdda81||typeof _0xdda81!==_0x2d68d6(0xf1))return 0x0;return Math['ceil'](_0xdda81['length']/COMPACTION_CONFIG[_0x2d68d6(0xcd)]);}[a0_0x419ff6(0x102)](_0x23772f,_0x5be4dc){const _0x4896c9=a0_0x419ff6,_0x42546b=this[_0x4896c9(0xcf)](_0x23772f,_0x5be4dc),_0x2cb0d8=this[_0x4896c9(0x103)][_0x4896c9(0xdb)](_0x42546b);if(!_0x2cb0d8)return null;const _0x4393c3=Date['now']();if(_0x4393c3-_0x2cb0d8[_0x4896c9(0xdd)]>COMPACTION_CONFIG[_0x4896c9(0xec)])return this[_0x4896c9(0x103)]['delete'](_0x42546b),null;return _0x2cb0d8['count'];}['_cacheTokenCount'](_0x58da30,_0x53d01f,_0x70e739){const _0x15f332=a0_0x419ff6,_0x5f573c=this[_0x15f332(0xcf)](_0x58da30,_0x53d01f);this['tokenCache']['set'](_0x5f573c,{'count':_0x70e739,'model':_0x53d01f,'timestamp':Date['now']()});if(this[_0x15f332(0x103)]['size']>0x3e8){const _0x32d242=Array[_0x15f332(0xe0)](this[_0x15f332(0x103)]['entries']());_0x32d242['sort']((_0x47349c,_0x223dda)=>_0x47349c[0x1][_0x15f332(0xdd)]-_0x223dda[0x1][_0x15f332(0xdd)]);const _0x4b0396=Math[_0x15f332(0xcc)](_0x32d242[_0x15f332(0xf2)]*0.2);for(let _0x99e9ca=0x0;_0x99e9ca<_0x4b0396;_0x99e9ca++){this[_0x15f332(0x103)]['delete'](_0x32d242[_0x99e9ca][0x0]);}}}[a0_0x419ff6(0xcf)](_0x46b3cd,_0x23dfed){const _0x48466b=a0_0x419ff6,_0x532736=this[_0x48466b(0xc8)](_0x46b3cd);return _0x23dfed+':'+_0x532736;}[a0_0x419ff6(0xc8)](_0x2d0af0){const _0xafd7d8=a0_0x419ff6;let _0x58e0a3=0x0;for(let _0x3344a7=0x0;_0x3344a7<_0x2d0af0['length'];_0x3344a7++){const _0x27c70e=_0x2d0af0['charCodeAt'](_0x3344a7);_0x58e0a3=(_0x58e0a3<<0x5)-_0x58e0a3+_0x27c70e,_0x58e0a3=_0x58e0a3&_0x58e0a3;}return _0x58e0a3[_0xafd7d8(0xed)](0x24);}[a0_0x419ff6(0xd8)](_0x1aba9c){const _0x1cc5ab=a0_0x419ff6;if(_0x1aba9c['includes']('anthropic')||_0x1aba9c['includes'](_0x1cc5ab(0xc4)))return 0xa;return 0x5;}['_getConversationOverhead'](_0x381b21){const _0x1c664a=0x32,_0x5aec32=_0x381b21*0x2;return _0x1c664a+_0x5aec32;}async[a0_0x419ff6(0xf4)](){const _0x287860=a0_0x419ff6;for(const [_0x12788a,_0x1866bd]of this[_0x287860(0xce)]['entries']()){try{_0x1866bd['free'](),this['logger']?.[_0x287860(0xee)]('Freed\x20tiktoken\x20encoder',{'model':_0x12788a});}catch(_0x3b1dd5){this['logger']?.['warn'](_0x287860(0xc5),{'model':_0x12788a,'error':_0x3b1dd5[_0x287860(0xde)]});}}this['encoders']['clear'](),this['tokenCache'][_0x287860(0xff)](),this['logger']?.['info'](_0x287860(0xf7));}}export default TokenCountingService;
|