@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,539 +1 @@
|
|
|
1
|
-
import { BUDGET_LIMITS, COST_PER_TOKEN, USAGE_ALERTS } from '../utilities/constants.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Budget and usage tracking service for monitoring AI model costs and token usage
|
|
5
|
-
*/
|
|
6
|
-
export class BudgetService {
|
|
7
|
-
constructor(config, logger) {
|
|
8
|
-
this.config = config || {};
|
|
9
|
-
this.logger = logger;
|
|
10
|
-
this.usage = {
|
|
11
|
-
daily: new Map(),
|
|
12
|
-
weekly: new Map(),
|
|
13
|
-
monthly: new Map(),
|
|
14
|
-
total: {
|
|
15
|
-
tokens: 0,
|
|
16
|
-
cost: 0,
|
|
17
|
-
requests: 0
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
this.budgets = {
|
|
22
|
-
daily: BUDGET_LIMITS.DAILY,
|
|
23
|
-
weekly: BUDGET_LIMITS.WEEKLY,
|
|
24
|
-
monthly: BUDGET_LIMITS.MONTHLY
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
this.alerts = {
|
|
28
|
-
enabled: true,
|
|
29
|
-
thresholds: USAGE_ALERTS.THRESHOLDS,
|
|
30
|
-
lastAlertTimes: new Map()
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
this.loadUsageData();
|
|
34
|
-
this.setupPeriodicSave();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Track token usage for a specific model and agent
|
|
39
|
-
* @param {string} agentId - Agent identifier
|
|
40
|
-
* @param {string} modelId - Model identifier
|
|
41
|
-
* @param {Object} tokenUsage - Token usage data
|
|
42
|
-
* @param {number} tokenUsage.prompt_tokens - Input tokens used
|
|
43
|
-
* @param {number} tokenUsage.completion_tokens - Output tokens used
|
|
44
|
-
* @param {number} tokenUsage.total_tokens - Total tokens used
|
|
45
|
-
* @returns {Object} Updated usage statistics and cost
|
|
46
|
-
*/
|
|
47
|
-
trackUsage(agentId, modelId, tokenUsage) {
|
|
48
|
-
try {
|
|
49
|
-
const cost = this.calculateCost(modelId, tokenUsage);
|
|
50
|
-
const now = new Date();
|
|
51
|
-
const dayKey = this.getDayKey(now);
|
|
52
|
-
const weekKey = this.getWeekKey(now);
|
|
53
|
-
const monthKey = this.getMonthKey(now);
|
|
54
|
-
|
|
55
|
-
// Initialize usage entries if they don't exist
|
|
56
|
-
this.initializeUsageEntry(this.usage.daily, dayKey);
|
|
57
|
-
this.initializeUsageEntry(this.usage.weekly, weekKey);
|
|
58
|
-
this.initializeUsageEntry(this.usage.monthly, monthKey);
|
|
59
|
-
|
|
60
|
-
// Update usage statistics
|
|
61
|
-
const usageData = {
|
|
62
|
-
agentId,
|
|
63
|
-
modelId,
|
|
64
|
-
tokens: tokenUsage.total_tokens,
|
|
65
|
-
cost,
|
|
66
|
-
timestamp: now.toISOString()
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
this.updateUsageEntry(this.usage.daily.get(dayKey), usageData);
|
|
70
|
-
this.updateUsageEntry(this.usage.weekly.get(weekKey), usageData);
|
|
71
|
-
this.updateUsageEntry(this.usage.monthly.get(monthKey), usageData);
|
|
72
|
-
|
|
73
|
-
// Update total usage
|
|
74
|
-
this.usage.total.tokens += tokenUsage.total_tokens;
|
|
75
|
-
this.usage.total.cost += cost;
|
|
76
|
-
this.usage.total.requests += 1;
|
|
77
|
-
|
|
78
|
-
// Check budget limits and send alerts if necessary
|
|
79
|
-
this.checkBudgetLimits(dayKey, weekKey, monthKey);
|
|
80
|
-
|
|
81
|
-
this.logger.info('Usage tracked', {
|
|
82
|
-
agentId,
|
|
83
|
-
modelId,
|
|
84
|
-
tokens: tokenUsage.total_tokens,
|
|
85
|
-
cost,
|
|
86
|
-
totalCost: this.usage.total.cost
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
cost,
|
|
91
|
-
totalCost: this.usage.total.cost,
|
|
92
|
-
totalTokens: this.usage.total.tokens,
|
|
93
|
-
dailyUsage: this.usage.daily.get(dayKey),
|
|
94
|
-
budgetRemaining: this.getRemainingBudget()
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
} catch (error) {
|
|
98
|
-
this.logger.error('Failed to track usage', { error: error.message, agentId, modelId });
|
|
99
|
-
throw error;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Calculate cost based on model and token usage
|
|
105
|
-
* @param {string} modelId - Model identifier
|
|
106
|
-
* @param {Object} tokenUsage - Token usage data
|
|
107
|
-
* @returns {number} Cost in USD
|
|
108
|
-
*/
|
|
109
|
-
calculateCost(modelId, tokenUsage) {
|
|
110
|
-
const modelCosts = COST_PER_TOKEN[modelId];
|
|
111
|
-
|
|
112
|
-
if (!modelCosts) {
|
|
113
|
-
this.logger.warn('Unknown model for cost calculation', { modelId });
|
|
114
|
-
return 0;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const inputCost = (tokenUsage.prompt_tokens || 0) * modelCosts.input;
|
|
118
|
-
const outputCost = (tokenUsage.completion_tokens || 0) * modelCosts.output;
|
|
119
|
-
|
|
120
|
-
return inputCost + outputCost;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Get current usage statistics
|
|
125
|
-
* @param {string} period - 'daily', 'weekly', 'monthly', or 'total'
|
|
126
|
-
* @param {Date} date - Date for the period (optional, defaults to now)
|
|
127
|
-
* @returns {Object} Usage statistics
|
|
128
|
-
*/
|
|
129
|
-
getUsage(period = 'daily', date = new Date()) {
|
|
130
|
-
switch (period) {
|
|
131
|
-
case 'daily':
|
|
132
|
-
return this.usage.daily.get(this.getDayKey(date)) || this.createEmptyUsage();
|
|
133
|
-
case 'weekly':
|
|
134
|
-
return this.usage.weekly.get(this.getWeekKey(date)) || this.createEmptyUsage();
|
|
135
|
-
case 'monthly':
|
|
136
|
-
return this.usage.monthly.get(this.getMonthKey(date)) || this.createEmptyUsage();
|
|
137
|
-
case 'total':
|
|
138
|
-
return { ...this.usage.total };
|
|
139
|
-
default:
|
|
140
|
-
throw new Error(`Invalid period: ${period}`);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Get usage by agent
|
|
146
|
-
* @param {string} agentId - Agent identifier
|
|
147
|
-
* @param {string} period - 'daily', 'weekly', 'monthly', or 'total'
|
|
148
|
-
* @returns {Object} Agent-specific usage statistics
|
|
149
|
-
*/
|
|
150
|
-
getAgentUsage(agentId, period = 'daily') {
|
|
151
|
-
const usage = this.getUsage(period);
|
|
152
|
-
return usage.byAgent?.[agentId] || this.createEmptyUsage();
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Get usage by model
|
|
157
|
-
* @param {string} modelId - Model identifier
|
|
158
|
-
* @param {string} period - 'daily', 'weekly', 'monthly', or 'total'
|
|
159
|
-
* @returns {Object} Model-specific usage statistics
|
|
160
|
-
*/
|
|
161
|
-
getModelUsage(modelId, period = 'daily') {
|
|
162
|
-
const usage = this.getUsage(period);
|
|
163
|
-
return usage.byModel?.[modelId] || this.createEmptyUsage();
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Set budget limits
|
|
168
|
-
* @param {Object} budgets - Budget configuration
|
|
169
|
-
* @param {number} budgets.daily - Daily budget limit in USD
|
|
170
|
-
* @param {number} budgets.weekly - Weekly budget limit in USD
|
|
171
|
-
* @param {number} budgets.monthly - Monthly budget limit in USD
|
|
172
|
-
*/
|
|
173
|
-
setBudgets(budgets) {
|
|
174
|
-
if (budgets.daily !== undefined) this.budgets.daily = budgets.daily;
|
|
175
|
-
if (budgets.weekly !== undefined) this.budgets.weekly = budgets.weekly;
|
|
176
|
-
if (budgets.monthly !== undefined) this.budgets.monthly = budgets.monthly;
|
|
177
|
-
|
|
178
|
-
this.logger.info('Budget limits updated', this.budgets);
|
|
179
|
-
this.saveUsageData();
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Get remaining budget for each period
|
|
184
|
-
* @returns {Object} Remaining budget amounts
|
|
185
|
-
*/
|
|
186
|
-
getRemainingBudget() {
|
|
187
|
-
const now = new Date();
|
|
188
|
-
const dailyUsage = this.getUsage('daily', now);
|
|
189
|
-
const weeklyUsage = this.getUsage('weekly', now);
|
|
190
|
-
const monthlyUsage = this.getUsage('monthly', now);
|
|
191
|
-
|
|
192
|
-
return {
|
|
193
|
-
daily: Math.max(0, this.budgets.daily - dailyUsage.cost),
|
|
194
|
-
weekly: Math.max(0, this.budgets.weekly - weeklyUsage.cost),
|
|
195
|
-
monthly: Math.max(0, this.budgets.monthly - monthlyUsage.cost)
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Check if usage is within budget limits
|
|
201
|
-
* @param {string} period - 'daily', 'weekly', or 'monthly'
|
|
202
|
-
* @returns {boolean} True if within budget
|
|
203
|
-
*/
|
|
204
|
-
isWithinBudget(period = 'daily') {
|
|
205
|
-
const usage = this.getUsage(period);
|
|
206
|
-
const budget = this.budgets[period];
|
|
207
|
-
return usage.cost <= budget;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Get usage trends and analytics
|
|
212
|
-
* @param {number} days - Number of days to analyze
|
|
213
|
-
* @returns {Object} Usage trends and analytics
|
|
214
|
-
*/
|
|
215
|
-
getUsageTrends(days = 30) {
|
|
216
|
-
const trends = {
|
|
217
|
-
dailyAverages: {
|
|
218
|
-
cost: 0,
|
|
219
|
-
tokens: 0,
|
|
220
|
-
requests: 0
|
|
221
|
-
},
|
|
222
|
-
topAgents: [],
|
|
223
|
-
topModels: [],
|
|
224
|
-
costByDay: [],
|
|
225
|
-
tokensByDay: []
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
const now = new Date();
|
|
229
|
-
let totalCost = 0;
|
|
230
|
-
let totalTokens = 0;
|
|
231
|
-
let totalRequests = 0;
|
|
232
|
-
const agentUsage = new Map();
|
|
233
|
-
const modelUsage = new Map();
|
|
234
|
-
|
|
235
|
-
// Analyze daily usage for the specified period
|
|
236
|
-
for (let i = 0; i < days; i++) {
|
|
237
|
-
const date = new Date(now);
|
|
238
|
-
date.setDate(date.getDate() - i);
|
|
239
|
-
const dayKey = this.getDayKey(date);
|
|
240
|
-
const dayUsage = this.usage.daily.get(dayKey);
|
|
241
|
-
|
|
242
|
-
if (dayUsage) {
|
|
243
|
-
totalCost += dayUsage.cost;
|
|
244
|
-
totalTokens += dayUsage.tokens;
|
|
245
|
-
totalRequests += dayUsage.requests;
|
|
246
|
-
|
|
247
|
-
trends.costByDay.unshift({ date: dayKey, cost: dayUsage.cost });
|
|
248
|
-
trends.tokensByDay.unshift({ date: dayKey, tokens: dayUsage.tokens });
|
|
249
|
-
|
|
250
|
-
// Aggregate agent usage
|
|
251
|
-
Object.entries(dayUsage.byAgent || {}).forEach(([agentId, usage]) => {
|
|
252
|
-
if (!agentUsage.has(agentId)) {
|
|
253
|
-
agentUsage.set(agentId, { cost: 0, tokens: 0, requests: 0 });
|
|
254
|
-
}
|
|
255
|
-
const current = agentUsage.get(agentId);
|
|
256
|
-
current.cost += usage.cost;
|
|
257
|
-
current.tokens += usage.tokens;
|
|
258
|
-
current.requests += usage.requests;
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
// Aggregate model usage
|
|
262
|
-
Object.entries(dayUsage.byModel || {}).forEach(([modelId, usage]) => {
|
|
263
|
-
if (!modelUsage.has(modelId)) {
|
|
264
|
-
modelUsage.set(modelId, { cost: 0, tokens: 0, requests: 0 });
|
|
265
|
-
}
|
|
266
|
-
const current = modelUsage.get(modelId);
|
|
267
|
-
current.cost += usage.cost;
|
|
268
|
-
current.tokens += usage.tokens;
|
|
269
|
-
current.requests += usage.requests;
|
|
270
|
-
});
|
|
271
|
-
} else {
|
|
272
|
-
trends.costByDay.unshift({ date: dayKey, cost: 0 });
|
|
273
|
-
trends.tokensByDay.unshift({ date: dayKey, tokens: 0 });
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Calculate averages
|
|
278
|
-
trends.dailyAverages.cost = totalCost / days;
|
|
279
|
-
trends.dailyAverages.tokens = totalTokens / days;
|
|
280
|
-
trends.dailyAverages.requests = totalRequests / days;
|
|
281
|
-
|
|
282
|
-
// Sort and get top agents and models
|
|
283
|
-
trends.topAgents = Array.from(agentUsage.entries())
|
|
284
|
-
.map(([id, usage]) => ({ id, ...usage }))
|
|
285
|
-
.sort((a, b) => b.cost - a.cost)
|
|
286
|
-
.slice(0, 10);
|
|
287
|
-
|
|
288
|
-
trends.topModels = Array.from(modelUsage.entries())
|
|
289
|
-
.map(([id, usage]) => ({ id, ...usage }))
|
|
290
|
-
.sort((a, b) => b.cost - a.cost)
|
|
291
|
-
.slice(0, 10);
|
|
292
|
-
|
|
293
|
-
return trends;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Export usage data for reporting
|
|
298
|
-
* @param {string} format - 'json' or 'csv'
|
|
299
|
-
* @param {Date} startDate - Start date for export
|
|
300
|
-
* @param {Date} endDate - End date for export
|
|
301
|
-
* @returns {string} Exported data
|
|
302
|
-
*/
|
|
303
|
-
exportUsageData(format = 'json', startDate = null, endDate = null) {
|
|
304
|
-
const data = {
|
|
305
|
-
budgets: this.budgets,
|
|
306
|
-
total: this.usage.total,
|
|
307
|
-
daily: {},
|
|
308
|
-
weekly: {},
|
|
309
|
-
monthly: {}
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
// Filter data by date range if specified
|
|
313
|
-
const filterByDateRange = (entries, keyFormatter) => {
|
|
314
|
-
const filtered = {};
|
|
315
|
-
entries.forEach((usage, key) => {
|
|
316
|
-
const date = keyFormatter(key);
|
|
317
|
-
if ((!startDate || date >= startDate) && (!endDate || date <= endDate)) {
|
|
318
|
-
filtered[key] = usage;
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
return filtered;
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
if (startDate || endDate) {
|
|
325
|
-
data.daily = filterByDateRange(this.usage.daily, key => new Date(key));
|
|
326
|
-
data.weekly = filterByDateRange(this.usage.weekly, key => new Date(key));
|
|
327
|
-
data.monthly = filterByDateRange(this.usage.monthly, key => new Date(key + '-01'));
|
|
328
|
-
} else {
|
|
329
|
-
this.usage.daily.forEach((usage, key) => data.daily[key] = usage);
|
|
330
|
-
this.usage.weekly.forEach((usage, key) => data.weekly[key] = usage);
|
|
331
|
-
this.usage.monthly.forEach((usage, key) => data.monthly[key] = usage);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (format === 'csv') {
|
|
335
|
-
return this.convertToCSV(data);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return JSON.stringify(data, null, 2);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Private helper methods
|
|
342
|
-
|
|
343
|
-
initializeUsageEntry(usageMap, key) {
|
|
344
|
-
if (!usageMap.has(key)) {
|
|
345
|
-
usageMap.set(key, this.createEmptyUsage());
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
createEmptyUsage() {
|
|
350
|
-
return {
|
|
351
|
-
cost: 0,
|
|
352
|
-
tokens: 0,
|
|
353
|
-
requests: 0,
|
|
354
|
-
byAgent: {},
|
|
355
|
-
byModel: {}
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
updateUsageEntry(entry, usageData) {
|
|
360
|
-
entry.cost += usageData.cost;
|
|
361
|
-
entry.tokens += usageData.tokens;
|
|
362
|
-
entry.requests += 1;
|
|
363
|
-
|
|
364
|
-
// Update by agent
|
|
365
|
-
if (!entry.byAgent[usageData.agentId]) {
|
|
366
|
-
entry.byAgent[usageData.agentId] = this.createEmptyUsage();
|
|
367
|
-
}
|
|
368
|
-
entry.byAgent[usageData.agentId].cost += usageData.cost;
|
|
369
|
-
entry.byAgent[usageData.agentId].tokens += usageData.tokens;
|
|
370
|
-
entry.byAgent[usageData.agentId].requests += 1;
|
|
371
|
-
|
|
372
|
-
// Update by model
|
|
373
|
-
if (!entry.byModel[usageData.modelId]) {
|
|
374
|
-
entry.byModel[usageData.modelId] = this.createEmptyUsage();
|
|
375
|
-
}
|
|
376
|
-
entry.byModel[usageData.modelId].cost += usageData.cost;
|
|
377
|
-
entry.byModel[usageData.modelId].tokens += usageData.tokens;
|
|
378
|
-
entry.byModel[usageData.modelId].requests += 1;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
checkBudgetLimits(dayKey, weekKey, monthKey) {
|
|
382
|
-
const dailyUsage = this.usage.daily.get(dayKey);
|
|
383
|
-
const weeklyUsage = this.usage.weekly.get(weekKey);
|
|
384
|
-
const monthlyUsage = this.usage.monthly.get(monthKey);
|
|
385
|
-
|
|
386
|
-
// Check daily budget
|
|
387
|
-
if (dailyUsage.cost > this.budgets.daily) {
|
|
388
|
-
this.sendBudgetAlert('daily', dailyUsage.cost, this.budgets.daily);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Check weekly budget
|
|
392
|
-
if (weeklyUsage.cost > this.budgets.weekly) {
|
|
393
|
-
this.sendBudgetAlert('weekly', weeklyUsage.cost, this.budgets.weekly);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// Check monthly budget
|
|
397
|
-
if (monthlyUsage.cost > this.budgets.monthly) {
|
|
398
|
-
this.sendBudgetAlert('monthly', monthlyUsage.cost, this.budgets.monthly);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// Check threshold alerts
|
|
402
|
-
this.checkThresholdAlerts(dailyUsage, weeklyUsage, monthlyUsage);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
checkThresholdAlerts(dailyUsage, weeklyUsage, monthlyUsage) {
|
|
406
|
-
const thresholds = this.alerts.thresholds;
|
|
407
|
-
|
|
408
|
-
this.checkThreshold('daily', dailyUsage.cost, this.budgets.daily, thresholds);
|
|
409
|
-
this.checkThreshold('weekly', weeklyUsage.cost, this.budgets.weekly, thresholds);
|
|
410
|
-
this.checkThreshold('monthly', monthlyUsage.cost, this.budgets.monthly, thresholds);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
checkThreshold(period, usage, budget, thresholds) {
|
|
414
|
-
const percentage = (usage / budget) * 100;
|
|
415
|
-
|
|
416
|
-
for (const threshold of thresholds) {
|
|
417
|
-
if (percentage >= threshold) {
|
|
418
|
-
const alertKey = `${period}_${threshold}`;
|
|
419
|
-
const lastAlert = this.alerts.lastAlertTimes.get(alertKey);
|
|
420
|
-
const now = Date.now();
|
|
421
|
-
|
|
422
|
-
// Only send alert if we haven't sent one in the last hour
|
|
423
|
-
if (!lastAlert || (now - lastAlert) > 3600000) {
|
|
424
|
-
this.sendThresholdAlert(period, percentage, threshold, usage, budget);
|
|
425
|
-
this.alerts.lastAlertTimes.set(alertKey, now);
|
|
426
|
-
}
|
|
427
|
-
break; // Only send the highest threshold alert
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
sendBudgetAlert(period, currentUsage, budgetLimit) {
|
|
433
|
-
this.logger.warn('Budget limit exceeded', {
|
|
434
|
-
period,
|
|
435
|
-
currentUsage,
|
|
436
|
-
budgetLimit,
|
|
437
|
-
excess: currentUsage - budgetLimit
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
// Emit event for external handling
|
|
441
|
-
if (typeof process !== 'undefined' && process.emit) {
|
|
442
|
-
process.emit('budgetAlert', {
|
|
443
|
-
type: 'budget_exceeded',
|
|
444
|
-
period,
|
|
445
|
-
currentUsage,
|
|
446
|
-
budgetLimit,
|
|
447
|
-
excess: currentUsage - budgetLimit
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
sendThresholdAlert(period, percentage, threshold, usage, budget) {
|
|
453
|
-
this.logger.warn('Budget threshold reached', {
|
|
454
|
-
period,
|
|
455
|
-
percentage: Math.round(percentage),
|
|
456
|
-
threshold,
|
|
457
|
-
usage,
|
|
458
|
-
budget
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
// Emit event for external handling
|
|
462
|
-
if (typeof process !== 'undefined' && process.emit) {
|
|
463
|
-
process.emit('budgetAlert', {
|
|
464
|
-
type: 'threshold_reached',
|
|
465
|
-
period,
|
|
466
|
-
percentage: Math.round(percentage),
|
|
467
|
-
threshold,
|
|
468
|
-
usage,
|
|
469
|
-
budget
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
getDayKey(date) {
|
|
475
|
-
return date.toISOString().split('T')[0];
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
getWeekKey(date) {
|
|
479
|
-
const year = date.getFullYear();
|
|
480
|
-
const weekNum = this.getWeekNumber(date);
|
|
481
|
-
return `${year}-W${weekNum.toString().padStart(2, '0')}`;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
getMonthKey(date) {
|
|
485
|
-
const year = date.getFullYear();
|
|
486
|
-
const month = date.getMonth() + 1;
|
|
487
|
-
return `${year}-${month.toString().padStart(2, '0')}`;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
getWeekNumber(date) {
|
|
491
|
-
const firstDayOfYear = new Date(date.getFullYear(), 0, 1);
|
|
492
|
-
const pastDaysOfYear = (date - firstDayOfYear) / 86400000;
|
|
493
|
-
return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
convertToCSV(data) {
|
|
497
|
-
const rows = [];
|
|
498
|
-
rows.push(['Period', 'Date', 'Cost', 'Tokens', 'Requests', 'Agent', 'Model']);
|
|
499
|
-
|
|
500
|
-
// Convert daily data
|
|
501
|
-
Object.entries(data.daily).forEach(([date, usage]) => {
|
|
502
|
-
rows.push(['Daily', date, usage.cost, usage.tokens, usage.requests, '', '']);
|
|
503
|
-
|
|
504
|
-
Object.entries(usage.byAgent || {}).forEach(([agentId, agentUsage]) => {
|
|
505
|
-
rows.push(['Daily', date, agentUsage.cost, agentUsage.tokens, agentUsage.requests, agentId, '']);
|
|
506
|
-
});
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
return rows.map(row => row.join(',')).join('\n');
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
loadUsageData() {
|
|
513
|
-
try {
|
|
514
|
-
// In a real implementation, this would load from persistent storage
|
|
515
|
-
// For now, we'll just initialize with empty data
|
|
516
|
-
this.logger.info('Budget service initialized');
|
|
517
|
-
} catch (error) {
|
|
518
|
-
this.logger.error('Failed to load usage data', { error: error.message });
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
saveUsageData() {
|
|
523
|
-
try {
|
|
524
|
-
// In a real implementation, this would save to persistent storage
|
|
525
|
-
this.logger.debug('Usage data saved');
|
|
526
|
-
} catch (error) {
|
|
527
|
-
this.logger.error('Failed to save usage data', { error: error.message });
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
setupPeriodicSave() {
|
|
532
|
-
// Save usage data every 5 minutes
|
|
533
|
-
setInterval(() => {
|
|
534
|
-
this.saveUsageData();
|
|
535
|
-
}, 300000);
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
export default BudgetService;
|
|
1
|
+
function a0_0x15a2(_0x1b4fb0,_0x21d86f){_0x1b4fb0=_0x1b4fb0-0xfe;const _0x43c493=a0_0x43c4();let _0x15a2b6=_0x43c493[_0x1b4fb0];if(a0_0x15a2['dlXCqV']===undefined){var _0x52189e=function(_0xdfb201){const _0x218bc0='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x3f46d5='',_0x1b33f8='';for(let _0x18e8ed=0x0,_0x2b47d4,_0x5f3230,_0x7c71e0=0x0;_0x5f3230=_0xdfb201['charAt'](_0x7c71e0++);~_0x5f3230&&(_0x2b47d4=_0x18e8ed%0x4?_0x2b47d4*0x40+_0x5f3230:_0x5f3230,_0x18e8ed++%0x4)?_0x3f46d5+=String['fromCharCode'](0xff&_0x2b47d4>>(-0x2*_0x18e8ed&0x6)):0x0){_0x5f3230=_0x218bc0['indexOf'](_0x5f3230);}for(let _0x5d657b=0x0,_0x3d617a=_0x3f46d5['length'];_0x5d657b<_0x3d617a;_0x5d657b++){_0x1b33f8+='%'+('00'+_0x3f46d5['charCodeAt'](_0x5d657b)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x1b33f8);};a0_0x15a2['wKrQKW']=_0x52189e,a0_0x15a2['zfJWOC']={},a0_0x15a2['dlXCqV']=!![];}const _0x15ae97=_0x43c493[0x0],_0x25978f=_0x1b4fb0+_0x15ae97,_0x159967=a0_0x15a2['zfJWOC'][_0x25978f];return!_0x159967?(_0x15a2b6=a0_0x15a2['wKrQKW'](_0x15a2b6),a0_0x15a2['zfJWOC'][_0x25978f]=_0x15a2b6):_0x15a2b6=_0x159967,_0x15a2b6;}const a0_0x3e6de1=a0_0x15a2;(function(_0x40ad9b,_0x5691a9){const _0x3c7cb4=a0_0x15a2,_0x42d128=_0x40ad9b();while(!![]){try{const _0x35ed40=-parseInt(_0x3c7cb4(0x12b))/0x1+-parseInt(_0x3c7cb4(0x154))/0x2+parseInt(_0x3c7cb4(0x141))/0x3*(-parseInt(_0x3c7cb4(0x119))/0x4)+-parseInt(_0x3c7cb4(0x105))/0x5+parseInt(_0x3c7cb4(0x11f))/0x6+-parseInt(_0x3c7cb4(0x115))/0x7+parseInt(_0x3c7cb4(0x13a))/0x8;if(_0x35ed40===_0x5691a9)break;else _0x42d128['push'](_0x42d128['shift']());}catch(_0x3092af){_0x42d128['push'](_0x42d128['shift']());}}}(a0_0x43c4,0xa7ea5));function a0_0x43c4(){const _0xabd31c=['C2vUzej1zgDLDefSzxj0','z2v0uMvTywLUAw5NqNvKz2v0','Aw5WDxq','z2v0rNvSBfLLyxi','BM93','v0vfs0Xz','DgHYzxnOB2XKx3jLywnOzwq','Aw5MBW','z2v0tw9UDgHlzxK','Dg9Rzw5Z','vg9Rzw5Z','CM91BMq','C3bSAxq','yNvKz2v0qwXLCNq','mZa4mZmZovfQD2fbua','zw50CMLLCW','z2v0','yNvKz2v0x2v4y2vLzgvK','mJa0nJa0y0jMDfPg','z2v0rgf5s2v5','AxnxAxrOAw5cDwrNzxq','DxbKyxrLvxnHz2vfBNrYEq','z2v0v2vLA051BwjLCG','D2vLA2X5','nJG4ntq4mhn1tfDvAq','y3jLyxrLrw1WDhLvC2fNzq','sw52ywXPzcbWzxjPB2q6ia','rMfPBgvKihrVigXVywqGDxnHz2uGzgf0yq','Aw5PDgLHBgL6zvvZywDLrw50CNK','zxHWB3j0vxnHz2veyxrH','DxnHz2u','zM9YrwfJAa','y2HLy2TcDwrNzxrmAw1PDhm','y29TCgXLDgLVBL90B2TLBNm','y2HLy2TuAhjLC2HVBgrbBgvYDhm','BwvZC2fNzq','mta0mtuZnvbXD2rVua','rgf0zq','vxnHz2uGDhjHy2TLza','z2v0vxnHz2u','z2v0rgf0zq','Dg9Rzw5ZqNLeyxK','C2vUzfrOCMvZAg9SzefSzxj0','Dg90ywW','CgfKu3rHCNq','Dg9ju09tDhjPBMC','C2f2zvvZywDLrgf0yq','q29ZDa','Dw5ZAgLMDa','Bg9Nz2vY','y29ZDa','mJi0nZC2mdHnANLRAfm','zgfPBhK','DhjHy2TvC2fNzq','qNvKz2v0igXPBwL0igv4y2vLzgvK','C2v0','y2HLy2TuAhjLC2HVBgq','ugvYAw9K','oxztuvH1yG','yNLnB2rLBa','vxnHz2uGzgf0ysbZyxzLza','Dg90ywXFDg9Rzw5Z','tu9oveHmwq','z2v0v2vLA0TLEq','C3rYAw5NAwz5','veHsrvnit0XeuW','Bw9UDgHSEq','C2v0qNvKz2v0CW','C2v0DxbqzxjPB2rPy1nHDMu','zw1PDa','y29UDMvYDfrVq1nw','zgfPBhLbDMvYywDLCW','vw5RBM93BIbTB2rLBcbMB3iGy29ZDcbJywXJDwXHDgLVBG','ywXLCNrZ','yNvKz2v0CW','y29ZDej5rgf5','BgfZDefSzxj0vgLTzxm','mJm2nJa3mKzICvrkrG','C2v0rgf0zq','Bw9KzwXjza','Dw5KzwzPBMvK','CMvXDwvZDhm','ywDLBNrjza','zxjYB3i','qNvKz2v0ihrOCMvZAg9SzcbYzwfJAgvK','C29YDa','ANnVBG','D2fYBG','mJi1ndK5nvfNA09HDa','yNLbz2vUDa'];a0_0x43c4=function(){return _0xabd31c;};return a0_0x43c4();}import{BUDGET_LIMITS,COST_PER_TOKEN,USAGE_ALERTS}from'../utilities/constants.js';export class BudgetService{constructor(_0x3f46d5,_0x1b33f8){const _0x2b9554=a0_0x15a2;this['config']=_0x3f46d5||{},this[_0x2b9554(0x138)]=_0x1b33f8,this['usage']={'daily':new Map(),'weekly':new Map(),'monthly':new Map(),'total':{'tokens':0x0,'cost':0x0,'requests':0x0}},this[_0x2b9554(0x151)]={'daily':BUDGET_LIMITS['DAILY'],'weekly':BUDGET_LIMITS[_0x2b9554(0x10c)],'monthly':BUDGET_LIMITS[_0x2b9554(0x145)]},this[_0x2b9554(0x150)]={'enabled':!![],'thresholds':USAGE_ALERTS[_0x2b9554(0x148)],'lastAlertTimes':new Map()},this['loadUsageData'](),this[_0x2b9554(0x14b)]();}[a0_0x3e6de1(0x13c)](_0x18e8ed,_0x2b47d4,_0x5f3230){const _0x150699=a0_0x3e6de1;try{const _0x7c71e0=this['calculateCost'](_0x2b47d4,_0x5f3230),_0x5d657b=new Date(),_0x3d617a=this['getDayKey'](_0x5d657b),_0x37f865=this[_0x150699(0x146)](_0x5d657b),_0x15730e=this['getMonthKey'](_0x5d657b);this[_0x150699(0x123)](this[_0x150699(0x125)]['daily'],_0x3d617a),this[_0x150699(0x123)](this[_0x150699(0x125)]['weekly'],_0x37f865),this[_0x150699(0x123)](this['usage']['monthly'],_0x15730e);const _0x40c2fc={'agentId':_0x18e8ed,'modelId':_0x2b47d4,'tokens':_0x5f3230['total_tokens'],'cost':_0x7c71e0,'timestamp':_0x5d657b[_0x150699(0x134)]()};return this['updateUsageEntry'](this[_0x150699(0x125)]['daily'][_0x150699(0x117)](_0x3d617a),_0x40c2fc),this[_0x150699(0x11c)](this['usage']['weekly']['get'](_0x37f865),_0x40c2fc),this['updateUsageEntry'](this['usage'][_0x150699(0x149)]['get'](_0x15730e),_0x40c2fc),this['usage'][_0x150699(0x132)][_0x150699(0x110)]+=_0x5f3230[_0x150699(0x144)],this[_0x150699(0x125)][_0x150699(0x132)]['cost']+=_0x7c71e0,this[_0x150699(0x125)]['total']['requests']+=0x1,this[_0x150699(0x127)](_0x3d617a,_0x37f865,_0x15730e),this[_0x150699(0x138)]['info'](_0x150699(0x12d),{'agentId':_0x18e8ed,'modelId':_0x2b47d4,'tokens':_0x5f3230[_0x150699(0x144)],'cost':_0x7c71e0,'totalCost':this[_0x150699(0x125)]['total']['cost']}),{'cost':_0x7c71e0,'totalCost':this['usage']['total']['cost'],'totalTokens':this[_0x150699(0x125)][_0x150699(0x132)]['tokens'],'dailyUsage':this['usage'][_0x150699(0x13b)]['get'](_0x3d617a),'budgetRemaining':this['getRemainingBudget']()};}catch(_0x1185fc){this[_0x150699(0x138)][_0x150699(0x100)]('Failed\x20to\x20track\x20usage',{'error':_0x1185fc[_0x150699(0x12a)],'agentId':_0x18e8ed,'modelId':_0x2b47d4});throw _0x1185fc;}}['calculateCost'](_0x173e67,_0x554feb){const _0x46b8a9=a0_0x3e6de1,_0x274a5e=COST_PER_TOKEN[_0x173e67];if(!_0x274a5e)return this[_0x46b8a9(0x138)]['warn'](_0x46b8a9(0x14f),{'modelId':_0x173e67}),0x0;const _0x4e45c5=(_0x554feb['prompt_tokens']||0x0)*_0x274a5e[_0x46b8a9(0x109)],_0x21c94a=(_0x554feb[_0x46b8a9(0x128)]||0x0)*_0x274a5e['output'];return _0x4e45c5+_0x21c94a;}['getUsage'](_0x3e1214='daily',_0x435de1=new Date()){const _0x4beabc=a0_0x3e6de1;switch(_0x3e1214){case _0x4beabc(0x13b):return this['usage']['daily'][_0x4beabc(0x117)](this['getDayKey'](_0x435de1))||this['createEmptyUsage']();case'weekly':return this['usage'][_0x4beabc(0x11e)]['get'](this['getWeekKey'](_0x435de1))||this['createEmptyUsage']();case _0x4beabc(0x149):return this['usage']['monthly']['get'](this['getMonthKey'](_0x435de1))||this['createEmptyUsage']();case'total':return{...this[_0x4beabc(0x125)][_0x4beabc(0x132)]};default:throw new Error(_0x4beabc(0x121)+_0x3e1214);}}['getAgentUsage'](_0x3a4a6c,_0x46cc76='daily'){const _0x38a65a=a0_0x3e6de1,_0x1a0da8=this[_0x38a65a(0x12e)](_0x46cc76);return _0x1a0da8['byAgent']?.[_0x3a4a6c]||this['createEmptyUsage']();}['getModelUsage'](_0x210860,_0x5b42ca='daily'){const _0x417d30=this['getUsage'](_0x5b42ca);return _0x417d30['byModel']?.[_0x210860]||this['createEmptyUsage']();}[a0_0x3e6de1(0x14a)](_0x5b4472){const _0x20a5dc=a0_0x3e6de1;if(_0x5b4472[_0x20a5dc(0x13b)]!==undefined)this[_0x20a5dc(0x151)][_0x20a5dc(0x13b)]=_0x5b4472['daily'];if(_0x5b4472['weekly']!==undefined)this[_0x20a5dc(0x151)]['weekly']=_0x5b4472[_0x20a5dc(0x11e)];if(_0x5b4472['monthly']!==undefined)this[_0x20a5dc(0x151)][_0x20a5dc(0x149)]=_0x5b4472[_0x20a5dc(0x149)];this[_0x20a5dc(0x138)][_0x20a5dc(0x10e)]('Budget\x20limits\x20updated',this['budgets']),this['saveUsageData']();}[a0_0x3e6de1(0x108)](){const _0x62daa9=a0_0x3e6de1,_0x71c8f4=new Date(),_0x57311c=this['getUsage']('daily',_0x71c8f4),_0x93200c=this['getUsage'](_0x62daa9(0x11e),_0x71c8f4),_0x1e8297=this['getUsage'](_0x62daa9(0x149),_0x71c8f4);return{'daily':Math['max'](0x0,this['budgets'][_0x62daa9(0x13b)]-_0x57311c[_0x62daa9(0x139)]),'weekly':Math['max'](0x0,this['budgets']['weekly']-_0x93200c['cost']),'monthly':Math['max'](0x0,this['budgets'][_0x62daa9(0x149)]-_0x1e8297['cost'])};}[a0_0x3e6de1(0x11b)](_0x538150=a0_0x3e6de1(0x13b)){const _0x36f717=a0_0x3e6de1,_0xb6ef2=this[_0x36f717(0x12e)](_0x538150),_0x497d90=this['budgets'][_0x538150];return _0xb6ef2['cost']<=_0x497d90;}['getUsageTrends'](_0x240a38=0x1e){const _0x57bc51=a0_0x3e6de1,_0xf2a934={'dailyAverages':{'cost':0x0,'tokens':0x0,'requests':0x0},'topAgents':[],'topModels':[],'costByDay':[],'tokensByDay':[]},_0x382600=new Date();let _0x68dbb6=0x0,_0x2a3faa=0x0,_0x2a7450=0x0;const _0x524ac7=new Map(),_0x187a15=new Map();for(let _0x361c59=0x0;_0x361c59<_0x240a38;_0x361c59++){const _0x11a4ca=new Date(_0x382600);_0x11a4ca[_0x57bc51(0x155)](_0x11a4ca[_0x57bc51(0x12f)]()-_0x361c59);const _0x329f85=this[_0x57bc51(0x11a)](_0x11a4ca),_0x51ec72=this['usage']['daily'][_0x57bc51(0x117)](_0x329f85);_0x51ec72?(_0x68dbb6+=_0x51ec72['cost'],_0x2a3faa+=_0x51ec72[_0x57bc51(0x110)],_0x2a7450+=_0x51ec72['requests'],_0xf2a934[_0x57bc51(0x152)]['unshift']({'date':_0x329f85,'cost':_0x51ec72['cost']}),_0xf2a934['tokensByDay']['unshift']({'date':_0x329f85,'tokens':_0x51ec72[_0x57bc51(0x110)]}),Object[_0x57bc51(0x116)](_0x51ec72[_0x57bc51(0x106)]||{})[_0x57bc51(0x126)](([_0x38e562,_0x27d5e2])=>{const _0x413034=_0x57bc51;!_0x524ac7['has'](_0x38e562)&&_0x524ac7[_0x413034(0x13e)](_0x38e562,{'cost':0x0,'tokens':0x0,'requests':0x0});const _0xf8e8f1=_0x524ac7['get'](_0x38e562);_0xf8e8f1[_0x413034(0x139)]+=_0x27d5e2['cost'],_0xf8e8f1['tokens']+=_0x27d5e2['tokens'],_0xf8e8f1['requests']+=_0x27d5e2['requests'];}),Object[_0x57bc51(0x116)](_0x51ec72[_0x57bc51(0x142)]||{})['forEach'](([_0x10e729,_0x4902ad])=>{const _0x4d81e5=_0x57bc51;!_0x187a15['has'](_0x10e729)&&_0x187a15['set'](_0x10e729,{'cost':0x0,'tokens':0x0,'requests':0x0});const _0x636a07=_0x187a15[_0x4d81e5(0x117)](_0x10e729);_0x636a07['cost']+=_0x4902ad[_0x4d81e5(0x139)],_0x636a07['tokens']+=_0x4902ad[_0x4d81e5(0x110)],_0x636a07['requests']+=_0x4902ad[_0x4d81e5(0xfe)];})):(_0xf2a934['costByDay']['unshift']({'date':_0x329f85,'cost':0x0}),_0xf2a934[_0x57bc51(0x130)][_0x57bc51(0x137)]({'date':_0x329f85,'tokens':0x0}));}return _0xf2a934[_0x57bc51(0x14e)][_0x57bc51(0x139)]=_0x68dbb6/_0x240a38,_0xf2a934[_0x57bc51(0x14e)][_0x57bc51(0x110)]=_0x2a3faa/_0x240a38,_0xf2a934[_0x57bc51(0x14e)]['requests']=_0x2a7450/_0x240a38,_0xf2a934['topAgents']=Array['from'](_0x524ac7[_0x57bc51(0x116)]())['map'](([_0x16aaf9,_0x45fa51])=>({'id':_0x16aaf9,..._0x45fa51}))['sort']((_0x27c196,_0x20b356)=>_0x20b356['cost']-_0x27c196[_0x57bc51(0x139)])['slice'](0x0,0xa),_0xf2a934['topModels']=Array['from'](_0x187a15['entries']())['map'](([_0x4ece05,_0x2a60b0])=>({'id':_0x4ece05,..._0x2a60b0}))[_0x57bc51(0x102)]((_0x6388d0,_0x2c64d1)=>_0x2c64d1[_0x57bc51(0x139)]-_0x6388d0['cost'])['slice'](0x0,0xa),_0xf2a934;}[a0_0x3e6de1(0x124)](_0x3c3faa=a0_0x3e6de1(0x103),_0x41d1f2=null,_0x14da64=null){const _0x8dcc0c=a0_0x3e6de1,_0x161857={'budgets':this[_0x8dcc0c(0x151)],'total':this['usage']['total'],'daily':{},'weekly':{},'monthly':{}},_0x178974=(_0x482462,_0x18dd98)=>{const _0xf0e2ee=_0x8dcc0c,_0x4760c6={};return _0x482462[_0xf0e2ee(0x126)]((_0x38a08c,_0x4a09f6)=>{const _0x3af333=_0x18dd98(_0x4a09f6);(!_0x41d1f2||_0x3af333>=_0x41d1f2)&&(!_0x14da64||_0x3af333<=_0x14da64)&&(_0x4760c6[_0x4a09f6]=_0x38a08c);}),_0x4760c6;};_0x41d1f2||_0x14da64?(_0x161857[_0x8dcc0c(0x13b)]=_0x178974(this[_0x8dcc0c(0x125)]['daily'],_0x54300e=>new Date(_0x54300e)),_0x161857[_0x8dcc0c(0x11e)]=_0x178974(this['usage'][_0x8dcc0c(0x11e)],_0x57983b=>new Date(_0x57983b)),_0x161857[_0x8dcc0c(0x149)]=_0x178974(this[_0x8dcc0c(0x125)]['monthly'],_0x3d199d=>new Date(_0x3d199d+'-01'))):(this['usage'][_0x8dcc0c(0x13b)][_0x8dcc0c(0x126)]((_0x52fea4,_0x4d3acd)=>_0x161857[_0x8dcc0c(0x13b)][_0x4d3acd]=_0x52fea4),this[_0x8dcc0c(0x125)][_0x8dcc0c(0x11e)][_0x8dcc0c(0x126)]((_0x2dff70,_0x118b87)=>_0x161857[_0x8dcc0c(0x11e)][_0x118b87]=_0x2dff70),this['usage'][_0x8dcc0c(0x149)]['forEach']((_0x20685a,_0x4ded4b)=>_0x161857['monthly'][_0x4ded4b]=_0x20685a));if(_0x3c3faa==='csv')return this[_0x8dcc0c(0x14d)](_0x161857);return JSON[_0x8dcc0c(0x147)](_0x161857,null,0x2);}[a0_0x3e6de1(0x123)](_0x5bd921,_0x15f4db){const _0x22a12b=a0_0x3e6de1;!_0x5bd921['has'](_0x15f4db)&&_0x5bd921[_0x22a12b(0x13e)](_0x15f4db,this['createEmptyUsage']());}[a0_0x3e6de1(0x120)](){return{'cost':0x0,'tokens':0x0,'requests':0x0,'byAgent':{},'byModel':{}};}['updateUsageEntry'](_0x2e0dc6,_0x32aad5){const _0x49354d=a0_0x3e6de1;_0x2e0dc6[_0x49354d(0x139)]+=_0x32aad5[_0x49354d(0x139)],_0x2e0dc6[_0x49354d(0x110)]+=_0x32aad5['tokens'],_0x2e0dc6['requests']+=0x1,!_0x2e0dc6['byAgent'][_0x32aad5['agentId']]&&(_0x2e0dc6['byAgent'][_0x32aad5[_0x49354d(0xff)]]=this[_0x49354d(0x120)]()),_0x2e0dc6['byAgent'][_0x32aad5[_0x49354d(0xff)]]['cost']+=_0x32aad5['cost'],_0x2e0dc6[_0x49354d(0x106)][_0x32aad5[_0x49354d(0xff)]][_0x49354d(0x110)]+=_0x32aad5['tokens'],_0x2e0dc6[_0x49354d(0x106)][_0x32aad5['agentId']]['requests']+=0x1,!_0x2e0dc6['byModel'][_0x32aad5[_0x49354d(0x156)]]&&(_0x2e0dc6['byModel'][_0x32aad5['modelId']]=this['createEmptyUsage']()),_0x2e0dc6[_0x49354d(0x142)][_0x32aad5['modelId']]['cost']+=_0x32aad5['cost'],_0x2e0dc6['byModel'][_0x32aad5[_0x49354d(0x156)]]['tokens']+=_0x32aad5['tokens'],_0x2e0dc6['byModel'][_0x32aad5[_0x49354d(0x156)]][_0x49354d(0xfe)]+=0x1;}[a0_0x3e6de1(0x127)](_0x23e5e7,_0x38f0f2,_0x42e20f){const _0x3dcc03=a0_0x3e6de1,_0x356b6e=this[_0x3dcc03(0x125)][_0x3dcc03(0x13b)][_0x3dcc03(0x117)](_0x23e5e7),_0x49375e=this[_0x3dcc03(0x125)][_0x3dcc03(0x11e)]['get'](_0x38f0f2),_0x400a4a=this['usage'][_0x3dcc03(0x149)][_0x3dcc03(0x117)](_0x42e20f);_0x356b6e[_0x3dcc03(0x139)]>this['budgets']['daily']&&this['sendBudgetAlert']('daily',_0x356b6e['cost'],this[_0x3dcc03(0x151)]['daily']),_0x49375e[_0x3dcc03(0x139)]>this[_0x3dcc03(0x151)][_0x3dcc03(0x11e)]&&this[_0x3dcc03(0x107)]('weekly',_0x49375e[_0x3dcc03(0x139)],this['budgets']['weekly']),_0x400a4a[_0x3dcc03(0x139)]>this['budgets'][_0x3dcc03(0x149)]&&this['sendBudgetAlert'](_0x3dcc03(0x149),_0x400a4a['cost'],this['budgets'][_0x3dcc03(0x149)]),this[_0x3dcc03(0x129)](_0x356b6e,_0x49375e,_0x400a4a);}[a0_0x3e6de1(0x129)](_0x186c15,_0x47863c,_0x45c778){const _0x5f082b=a0_0x3e6de1,_0x3b5c0a=this[_0x5f082b(0x150)]['thresholds'];this[_0x5f082b(0x13f)](_0x5f082b(0x13b),_0x186c15[_0x5f082b(0x139)],this[_0x5f082b(0x151)]['daily'],_0x3b5c0a),this[_0x5f082b(0x13f)]('weekly',_0x47863c[_0x5f082b(0x139)],this[_0x5f082b(0x151)]['weekly'],_0x3b5c0a),this[_0x5f082b(0x13f)](_0x5f082b(0x149),_0x45c778[_0x5f082b(0x139)],this[_0x5f082b(0x151)][_0x5f082b(0x149)],_0x3b5c0a);}[a0_0x3e6de1(0x13f)](_0x56b964,_0x572d7d,_0x2842ce,_0x223282){const _0x52baf1=a0_0x3e6de1,_0x12de53=_0x572d7d/_0x2842ce*0x64;for(const _0x26892a of _0x223282){if(_0x12de53>=_0x26892a){const _0x1bb8c9=_0x56b964+'_'+_0x26892a,_0x59c42e=this['alerts'][_0x52baf1(0x153)][_0x52baf1(0x117)](_0x1bb8c9),_0x1db4db=Date[_0x52baf1(0x10b)]();(!_0x59c42e||_0x1db4db-_0x59c42e>0x36ee80)&&(this['sendThresholdAlert'](_0x56b964,_0x12de53,_0x26892a,_0x572d7d,_0x2842ce),this[_0x52baf1(0x150)]['lastAlertTimes'][_0x52baf1(0x13e)](_0x1bb8c9,_0x1db4db));break;}}}[a0_0x3e6de1(0x107)](_0x1c6377,_0x4191d7,_0x200690){const _0x3d39ef=a0_0x3e6de1;this[_0x3d39ef(0x138)][_0x3d39ef(0x104)](_0x3d39ef(0x13d),{'period':_0x1c6377,'currentUsage':_0x4191d7,'budgetLimit':_0x200690,'excess':_0x4191d7-_0x200690}),typeof process!=='undefined'&&process['emit']&&process['emit']('budgetAlert',{'type':_0x3d39ef(0x118),'period':_0x1c6377,'currentUsage':_0x4191d7,'budgetLimit':_0x200690,'excess':_0x4191d7-_0x200690});}[a0_0x3e6de1(0x131)](_0x418bdb,_0x4d272c,_0x44e6ca,_0x1cd759,_0x21ffbd){const _0x902da2=a0_0x3e6de1;this[_0x902da2(0x138)]['warn'](_0x902da2(0x101),{'period':_0x418bdb,'percentage':Math[_0x902da2(0x112)](_0x4d272c),'threshold':_0x44e6ca,'usage':_0x1cd759,'budget':_0x21ffbd}),typeof process!==_0x902da2(0x157)&&process[_0x902da2(0x14c)]&&process['emit'](_0x902da2(0x114),{'type':_0x902da2(0x10d),'period':_0x418bdb,'percentage':Math['round'](_0x4d272c),'threshold':_0x44e6ca,'usage':_0x1cd759,'budget':_0x21ffbd});}[a0_0x3e6de1(0x11a)](_0xe8ef5b){const _0x45bdf5=a0_0x3e6de1;return _0xe8ef5b[_0x45bdf5(0x134)]()[_0x45bdf5(0x113)]('T')[0x0];}['getWeekKey'](_0x244b2b){const _0xd2d858=a0_0x3e6de1,_0x53626b=_0x244b2b['getFullYear'](),_0x356cec=this[_0xd2d858(0x11d)](_0x244b2b);return _0x53626b+'-W'+_0x356cec['toString']()[_0xd2d858(0x133)](0x2,'0');}[a0_0x3e6de1(0x10f)](_0x5612aa){const _0x533f4c=a0_0x3e6de1,_0x5686c4=_0x5612aa[_0x533f4c(0x10a)](),_0x4f59c5=_0x5612aa['getMonth']()+0x1;return _0x5686c4+'-'+_0x4f59c5['toString']()['padStart'](0x2,'0');}[a0_0x3e6de1(0x11d)](_0x1d321a){const _0x70820f=new Date(_0x1d321a['getFullYear'](),0x0,0x1),_0x27436e=(_0x1d321a-_0x70820f)/0x5265c00;return Math['ceil']((_0x27436e+_0x70820f['getDay']()+0x1)/0x7);}[a0_0x3e6de1(0x14d)](_0x86226){const _0x4ad074=a0_0x3e6de1,_0x5531b5=[];return _0x5531b5['push']([_0x4ad074(0x140),_0x4ad074(0x12c),_0x4ad074(0x136),_0x4ad074(0x111),'Requests','Agent','Model']),Object['entries'](_0x86226[_0x4ad074(0x13b)])['forEach'](([_0x56bd30,_0xcb0e25])=>{const _0x3453d6=_0x4ad074;_0x5531b5['push'](['Daily',_0x56bd30,_0xcb0e25['cost'],_0xcb0e25[_0x3453d6(0x110)],_0xcb0e25['requests'],'','']),Object[_0x3453d6(0x116)](_0xcb0e25[_0x3453d6(0x106)]||{})[_0x3453d6(0x126)](([_0x5f315c,_0x125039])=>{_0x5531b5['push'](['Daily',_0x56bd30,_0x125039['cost'],_0x125039['tokens'],_0x125039['requests'],_0x5f315c,'']);});}),_0x5531b5['map'](_0x43eace=>_0x43eace['join'](','))['join']('\x0a');}['loadUsageData'](){const _0x4c01b3=a0_0x3e6de1;try{this['logger'][_0x4c01b3(0x10e)]('Budget\x20service\x20initialized');}catch(_0x526485){this['logger']['error'](_0x4c01b3(0x122),{'error':_0x526485[_0x4c01b3(0x12a)]});}}[a0_0x3e6de1(0x135)](){const _0x2a3e38=a0_0x3e6de1;try{this['logger']['debug'](_0x2a3e38(0x143));}catch(_0x447f7b){this[_0x2a3e38(0x138)]['error']('Failed\x20to\x20save\x20usage\x20data',{'error':_0x447f7b[_0x2a3e38(0x12a)]});}}['setupPeriodicSave'](){setInterval(()=>{const _0x398d61=a0_0x15a2;this[_0x398d61(0x135)]();},0x493e0);}}export default BudgetService;
|