@loxia-labs/loxia-autopilot-one 1.0.1 → 1.0.3
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 +14 -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
|
+
const a0_0x5d1028=a0_0x1ea6;(function(_0x244a67,_0x5603bf){const _0x4aac03=a0_0x1ea6,_0x61b607=_0x244a67();while(!![]){try{const _0x389a84=-parseInt(_0x4aac03(0x128))/0x1*(parseInt(_0x4aac03(0x126))/0x2)+-parseInt(_0x4aac03(0x116))/0x3+parseInt(_0x4aac03(0x122))/0x4+parseInt(_0x4aac03(0x11c))/0x5*(-parseInt(_0x4aac03(0x100))/0x6)+parseInt(_0x4aac03(0x130))/0x7+-parseInt(_0x4aac03(0x111))/0x8+parseInt(_0x4aac03(0x118))/0x9;if(_0x389a84===_0x5603bf)break;else _0x61b607['push'](_0x61b607['shift']());}catch(_0x8b51e6){_0x61b607['push'](_0x61b607['shift']());}}}(a0_0xab84,0x8fda8));import{encoding_for_model}from'tiktoken';import{MODELS,COMPACTION_CONFIG,TOKEN_COUNTING_MODES}from'../utilities/constants.js';function a0_0xab84(){const _0x438603=['y2XLyw51Ca','q3jLyxrLzcb0AwT0B2TLBIbLBMnVzgvY','BM93','DMfSAwrHDgvdB21Wywn0Aw9U','odKWmJy3mMXeBezzvG','z3b0ltq','C3rYAw5N','z2v0tw9KzwXnyxHpDxrWDxruB2TLBNm','vg9Rzw4Gy2fJAguGy2XLyxjLzcbJB21WBgv0zwX5','mJGWmJKXofrnBw5lAa','r1buxZrFtuLosq','mtu0nJaWnJvOCujvEwi','BwvZC2fNzq','q29UDMvYC2f0Aw9UihrVA2vUignVDw50','C29YDa','otKWnun3BwfHDW','x3nPBxbSzuHHC2G','A2v5CW','qundvvjbveu','ve9lru5Fq09vtLrFq0fdsevFvfrmx01t','x2DLDevUy29Kzxi','mJe1nJqYmfHKqwrAzG','vg9Rzw5dB3vUDgLUz1nLCNzPy2uGAw5PDgLHBgL6zwq','ueHjxZq','D2fYBG','mNbht2rpva','x2DLDe1LC3nHz2vgB3jTyxr0Aw5Nt3zLCMHLywq','mZmXnZK1Axj2rK1N','DgLTzxn0yw1W','BgvUz3rO','qu5usfjpueLdx09qvvm','x2DLDenVBNzLCNnHDgLVBK92zxjOzwfK','Aw5MBW','y291BNruB2TLBNm','zw5JB2rLCNm','ntqYmJyYn2nZBxHWDq','y29UDgvUDa','vg9Rzw4Gy2fJAguGy2XLyxjLzcbMB3iGBw9KzwW','C2L6zq','zgvSzxrL','Aw5JBhvKzxm','Bg9Nz2vY','x2DLDenHy2HLzfrVA2vUq291BNq','tufyx1riuKvtse9mra','revgqvvmvf9usfjfu0Hpteq','zgvIDwC','DgLRDg9Rzw5nB2rLBe1HCa','zNjVBq','Dg9Rzw5dywnOzq','rNjLzwqGDgLRDg9Rzw4Gzw5JB2rLCG','qu5usfjpueLdx0HbsuTv','zw50CMLLCW','x2DLDenHy2HLs2v5','mtKYwwTitvHl','C2v0','z2v0','y2XLyxi','revfufnfruTFuJe','y2fSy3vSyxrLvgfYz2v0vg9Rzw5dB3vUDa','qu5usfjpueLdx1nptK5fva','Dg9tDhjPBMC','q29TCgfJDgLVBIb2ywXPzgf0Aw9U','yw50AhjVCgLJ','x2nHy2HLvg9Rzw5dB3vUDa','zMXVB3i','x2vZDgLTyxrLvg9Rzw5Z'];a0_0xab84=function(){return _0x438603;};return a0_0xab84();}function a0_0x1ea6(_0x21bb1d,_0x55e987){_0x21bb1d=_0x21bb1d-0x100;const _0xab842=a0_0xab84();let _0x1ea641=_0xab842[_0x21bb1d];if(a0_0x1ea6['uWvcYs']===undefined){var _0x4c061c=function(_0x498bc4){const _0x5cc5c0='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x22d55a='',_0x1365dc='';for(let _0x5d798a=0x0,_0x4130e7,_0x3de2c0,_0x1691a=0x0;_0x3de2c0=_0x498bc4['charAt'](_0x1691a++);~_0x3de2c0&&(_0x4130e7=_0x5d798a%0x4?_0x4130e7*0x40+_0x3de2c0:_0x3de2c0,_0x5d798a++%0x4)?_0x22d55a+=String['fromCharCode'](0xff&_0x4130e7>>(-0x2*_0x5d798a&0x6)):0x0){_0x3de2c0=_0x5cc5c0['indexOf'](_0x3de2c0);}for(let _0x36cc2f=0x0,_0x572791=_0x22d55a['length'];_0x36cc2f<_0x572791;_0x36cc2f++){_0x1365dc+='%'+('00'+_0x22d55a['charCodeAt'](_0x36cc2f)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x1365dc);};a0_0x1ea6['CEGzJM']=_0x4c061c,a0_0x1ea6['vHUMAv']={},a0_0x1ea6['uWvcYs']=!![];}const _0x5a6016=_0xab842[0x0],_0x535464=_0x21bb1d+_0x5a6016,_0x6fe1b6=a0_0x1ea6['vHUMAv'][_0x535464];return!_0x6fe1b6?(_0x1ea641=a0_0x1ea6['CEGzJM'](_0x1ea641),a0_0x1ea6['vHUMAv'][_0x535464]=_0x1ea641):_0x1ea641=_0x6fe1b6,_0x1ea641;}class TokenCountingService{constructor(_0x22d55a){const _0x448902=a0_0x1ea6;this[_0x448902(0x136)]=_0x22d55a,this['tokenCache']=new Map(),this['encoders']=new Map(),this['tiktokenModelMap']={[MODELS[_0x448902(0x106)]]:_0x448902(0x112),[MODELS['ANTHROPIC_OPUS']]:'gpt-4',[MODELS[_0x448902(0x13f)]]:'gpt-4',[MODELS['GPT_4']]:_0x448902(0x112),[MODELS[_0x448902(0x117)]]:'gpt-4','gpt-4o':'gpt-4','gpt-4o-mini':'gpt-4','gpt-4-turbo':_0x448902(0x112),'gpt-3.5-turbo':'gpt-4',[MODELS[_0x448902(0x104)]]:'gpt-4',[MODELS['PHI_4']]:_0x448902(0x112),'azure-ai-grok3':_0x448902(0x112),'azure-ai-deepseek-r1':_0x448902(0x112),'azure-openai-gpt-5':_0x448902(0x112),'azure-openai-gpt-4':_0x448902(0x112),'azure-openai-gpt-4o':_0x448902(0x112),[COMPACTION_CONFIG['COMPACTION_MODEL']]:_0x448902(0x112),[COMPACTION_CONFIG['COMPACTION_MODEL_FALLBACK']]:_0x448902(0x112)},this[_0x448902(0x136)]?.['info'](_0x448902(0x123),{'supportedModels':Object[_0x448902(0x11e)](this[_0x448902(0x13b)])['length'],'cacheEnabled':!![]});}async[a0_0x5d1028(0x12e)](_0x1365dc,_0x5d798a,_0x4130e7=TOKEN_COUNTING_MODES[a0_0x5d1028(0x11f)]){const _0x1f7b4a=a0_0x5d1028;if(!_0x1365dc||typeof _0x1365dc!==_0x1f7b4a(0x113))return 0x0;if(_0x4130e7===TOKEN_COUNTING_MODES['CACHED']){const _0x3de2c0=this['_getCachedTokenCount'](_0x1365dc,_0x5d798a);if(_0x3de2c0!==null)return _0x3de2c0;}if(_0x4130e7===TOKEN_COUNTING_MODES['ESTIMATED'])return this['_estimateTokens'](_0x1365dc);try{const _0x1691a=await this[_0x1f7b4a(0x121)](_0x5d798a),_0x36cc2f=_0x1691a['encode'](_0x1365dc),_0x572791=_0x36cc2f['length'];return this[_0x1f7b4a(0x10a)](_0x1365dc,_0x5d798a,_0x572791),_0x572791;}catch(_0x27d33d){return this['logger']?.['warn']('Tiktoken\x20encoding\x20failed,\x20falling\x20back\x20to\x20estimation',{'model':_0x5d798a,'error':_0x27d33d[_0x1f7b4a(0x119)]}),this[_0x1f7b4a(0x10c)](_0x1365dc);}}async['estimateConversationTokens'](_0x3abd2f,_0x86e080,_0x32e18f=TOKEN_COUNTING_MODES[a0_0x5d1028(0x11f)]){const _0x286bdb=a0_0x5d1028;if(!Array['isArray'](_0x3abd2f)||_0x3abd2f[_0x286bdb(0x12a)]===0x0)return 0x0;let _0x4e5630=0x0;for(const _0x574837 of _0x3abd2f){if(!_0x574837['content'])continue;const _0xd9f691=this[_0x286bdb(0x127)](_0x86e080),_0x1c134e=await this['countTokens'](_0x574837[_0x286bdb(0x131)],_0x86e080,_0x32e18f);_0x4e5630+=_0x1c134e+_0xd9f691;}const _0xb50ed2=this[_0x286bdb(0x12c)](_0x3abd2f['length']);return _0x4e5630+=_0xb50ed2,this['logger']?.[_0x286bdb(0x13a)](_0x286bdb(0x11a),{'model':_0x86e080,'messageCount':_0x3abd2f[_0x286bdb(0x12a)],'totalTokens':_0x4e5630,'mode':_0x32e18f}),_0x4e5630;}['getModelContextWindow'](_0x5f3b2a){const _0x4c6369=a0_0x5d1028,_0x11124b={[MODELS['ANTHROPIC_SONNET']]:0x30d40,[MODELS[_0x4c6369(0x12b)]]:0x30d40,[MODELS['ANTHROPIC_HAIKU']]:0x30d40,[MODELS['GPT_4']]:0x1f400,[MODELS[_0x4c6369(0x117)]]:0x1f400,'gpt-4o':0x1f400,'gpt-4o-mini':0x1f400,'gpt-4-turbo':0x1f400,'gpt-3.5-turbo':0x4000,[MODELS[_0x4c6369(0x104)]]:0x1f400,[MODELS[_0x4c6369(0x124)]]: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['COMPACTION_MODEL']]:0x1f400,[COMPACTION_CONFIG['COMPACTION_MODEL_FALLBACK']]:0x1f400,'autopilot-model-router':0x4000},_0x554161=_0x11124b[_0x5f3b2a];if(!_0x554161)return this['logger']?.[_0x4c6369(0x125)]('Unknown model context window, using default',{'model':_0x5f3b2a,'defaultWindow':0x1f400}),0x1f400;return _0x554161;}[a0_0x5d1028(0x114)](_0x37e30b){const _0x523617=a0_0x5d1028,_0x5150a7={[MODELS['ANTHROPIC_SONNET']]:0x2000,[MODELS['ANTHROPIC_OPUS']]:0x2000,[MODELS['ANTHROPIC_HAIKU']]:0x2000,[MODELS['GPT_4']]:0x2000,[MODELS[_0x523617(0x117)]]:0x4000,'gpt-4o':0x2000,'gpt-4o-mini':0x4000,'gpt-4-turbo':0x2000,'gpt-3.5-turbo':0x1000,[MODELS['DEEPSEEK_R1']]:0x2000,[MODELS[_0x523617(0x124)]]: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['COMPACTION_MODEL']]:0x2000,[COMPACTION_CONFIG['COMPACTION_MODEL_FALLBACK']]:0x2000,'autopilot-model-router':0x800};return _0x5150a7[_0x37e30b]||0x2000;}['shouldTriggerCompaction'](_0x36b00f,_0x2bec88,_0x342b5e,_0x30d36c=COMPACTION_CONFIG[a0_0x5d1028(0x139)]){const _0x41d89a=a0_0x5d1028;(_0x30d36c<COMPACTION_CONFIG['MIN_THRESHOLD']||_0x30d36c>COMPACTION_CONFIG[_0x41d89a(0x138)])&&(this[_0x41d89a(0x136)]?.['warn']('Invalid compaction threshold, using default',{'provided':_0x30d36c,'default':COMPACTION_CONFIG['DEFAULT_THRESHOLD']}),_0x30d36c=COMPACTION_CONFIG['DEFAULT_THRESHOLD']);const requiredTokens=_0x36b00f+_0x2bec88,_0x414512=_0x30d36c*_0x342b5e,_0xd6df14=requiredTokens>=_0x414512;return this[_0x41d89a(0x136)]?.['debug']('Compaction\x20trigger\x20check',{'currentTokens':_0x36b00f,'maxOutputTokens':_0x2bec88,'contextWindow':_0x342b5e,'threshold':_0x30d36c,'requiredTokens':requiredTokens,'thresholdTokens':_0x414512,'shouldTrigger':_0xd6df14,'utilizationPercent':(requiredTokens/_0x342b5e*0x64)['toFixed'](0x2)}),_0xd6df14;}['calculateTargetTokenCount'](_0x2c017a,_0x334cc1=COMPACTION_CONFIG['MAX_ACCEPTABLE_TOKEN_COUNT_AFTER']){const _0x484f4c=a0_0x5d1028;return Math[_0x484f4c(0x10b)](_0x2c017a*_0x334cc1);}[a0_0x5d1028(0x110)](_0x492dcd,_0x1112fa,_0x2eb739){const _0x198508=a0_0x5d1028,_0x2318f4=(_0x492dcd-_0x1112fa)/_0x492dcd*0x64,_0x23b295=this[_0x198508(0x105)](_0x2eb739),_0x3ec764=_0x1112fa>_0x23b295,_0x5a5f66=_0x2318f4>=COMPACTION_CONFIG['MIN_REDUCTION_PERCENTAGE'],_0x1f11d1=!_0x3ec764&&_0x5a5f66;return this['logger']?.[_0x198508(0x12d)](_0x198508(0x108),{'originalTokens':_0x492dcd,'compactedTokens':_0x1112fa,'reductionPercent':_0x2318f4['toFixed'](0x2),'targetTokens':_0x23b295,'exceedsTarget':_0x3ec764,'sufficientReduction':_0x5a5f66,'valid':_0x1f11d1}),{'valid':_0x1f11d1,'reductionPercent':_0x2318f4,'exceedsTarget':_0x3ec764,'sufficientReduction':_0x5a5f66,'targetTokens':_0x23b295,'compactedTokens':_0x1112fa,'originalTokens':_0x492dcd};}['clearCache'](_0x506dcb=null){const _0x22906d=a0_0x5d1028;if(_0x506dcb){for(const [_0x4d2286,_0x2d7f1f]of this[_0x22906d(0x13d)][_0x22906d(0x140)]()){_0x2d7f1f['model']===_0x506dcb&&this['tokenCache'][_0x22906d(0x134)](_0x4d2286);}this[_0x22906d(0x136)]?.['debug'](_0x22906d(0x132),{'model':_0x506dcb});}else this['tokenCache']['clear'](),this['logger']?.['debug'](_0x22906d(0x115));}async[a0_0x5d1028(0x121)](_0x5dab9c){const _0x23b4b6=a0_0x5d1028,_0x322e62=this['tiktokenModelMap'][_0x5dab9c]||_0x23b4b6(0x112);if(this['encoders']['has'](_0x322e62))return this[_0x23b4b6(0x12f)][_0x23b4b6(0x102)](_0x322e62);const _0x2b08cd=encoding_for_model(_0x322e62);return this['encoders'][_0x23b4b6(0x101)](_0x322e62,_0x2b08cd),this[_0x23b4b6(0x136)]?.[_0x23b4b6(0x13a)](_0x23b4b6(0x10e),{'model':_0x5dab9c,'tiktokenModel':_0x322e62}),_0x2b08cd;}['_estimateTokens'](_0x127465){if(!_0x127465||typeof _0x127465!=='string')return 0x0;return Math['ceil'](_0x127465['length']/COMPACTION_CONFIG['CHARS_PER_TOKEN_ESTIMATE']);}[a0_0x5d1028(0x137)](_0x14a093,_0x2f3a51){const _0x3b1a58=a0_0x5d1028,_0x3a1d28=this[_0x3b1a58(0x141)](_0x14a093,_0x2f3a51),_0x174eb1=this[_0x3b1a58(0x13d)][_0x3b1a58(0x102)](_0x3a1d28);if(!_0x174eb1)return null;const _0x5dacd2=Date['now']();if(_0x5dacd2-_0x174eb1['timestamp']>COMPACTION_CONFIG[_0x3b1a58(0x120)])return this[_0x3b1a58(0x13d)][_0x3b1a58(0x134)](_0x3a1d28),null;return _0x174eb1['count'];}['_cacheTokenCount'](_0x5427bc,_0x1fff2b,_0x32d27b){const _0x101a11=a0_0x5d1028,_0xb91598=this[_0x101a11(0x141)](_0x5427bc,_0x1fff2b);this['tokenCache']['set'](_0xb91598,{'count':_0x32d27b,'model':_0x1fff2b,'timestamp':Date[_0x101a11(0x10f)]()});if(this['tokenCache'][_0x101a11(0x133)]>0x3e8){const _0x581227=Array[_0x101a11(0x13c)](this['tokenCache'][_0x101a11(0x140)]());_0x581227[_0x101a11(0x11b)]((_0x484700,_0x5a2eda)=>_0x484700[0x1]['timestamp']-_0x5a2eda[0x1][_0x101a11(0x129)]);const _0x4a8fde=Math[_0x101a11(0x10b)](_0x581227[_0x101a11(0x12a)]*0.2);for(let _0x29e3bf=0x0;_0x29e3bf<_0x4a8fde;_0x29e3bf++){this['tokenCache']['delete'](_0x581227[_0x29e3bf][0x0]);}}}[a0_0x5d1028(0x141)](_0x1cfdd3,_0x140b44){const _0x108ac=a0_0x5d1028,_0x194f19=this[_0x108ac(0x11d)](_0x1cfdd3);return _0x140b44+':'+_0x194f19;}['_simpleHash'](_0x2c9749){const _0x1a8ba1=a0_0x5d1028;let _0x441d9e=0x0;for(let _0x1e8509=0x0;_0x1e8509<_0x2c9749['length'];_0x1e8509++){const _0x2ca5de=_0x2c9749['charCodeAt'](_0x1e8509);_0x441d9e=(_0x441d9e<<0x5)-_0x441d9e+_0x2ca5de,_0x441d9e=_0x441d9e&_0x441d9e;}return _0x441d9e[_0x1a8ba1(0x107)](0x24);}[a0_0x5d1028(0x127)](_0xe6aba5){const _0x171cc1=a0_0x5d1028;if(_0xe6aba5[_0x171cc1(0x135)](_0x171cc1(0x109))||_0xe6aba5['includes']('claude'))return 0xa;return 0x5;}[a0_0x5d1028(0x12c)](_0x283b04){const _0x10e61f=0x32,_0x59492b=_0x283b04*0x2;return _0x10e61f+_0x59492b;}async[a0_0x5d1028(0x10d)](){const _0x349433=a0_0x5d1028;for(const [_0x23aaa9,_0x57d288]of this[_0x349433(0x12f)][_0x349433(0x140)]()){try{_0x57d288['free'](),this[_0x349433(0x136)]?.[_0x349433(0x13a)](_0x349433(0x13e),{'model':_0x23aaa9});}catch(_0x51bfe3){this[_0x349433(0x136)]?.[_0x349433(0x125)]('Failed\x20to\x20free\x20encoder',{'model':_0x23aaa9,'error':_0x51bfe3['message']});}}this['encoders']['clear'](),this[_0x349433(0x13d)][_0x349433(0x103)](),this['logger']?.[_0x349433(0x12d)]('TokenCountingService\x20cleaned\x20up');}}export default TokenCountingService;
|