tabby-ai-assistant 1.0.5 → 1.0.6
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/dist/components/chat/ai-sidebar.component.d.ts +147 -0
- package/dist/components/chat/chat-interface.component.d.ts +38 -6
- package/dist/components/settings/general-settings.component.d.ts +6 -3
- package/dist/components/settings/provider-config.component.d.ts +25 -12
- package/dist/components/terminal/command-preview.component.d.ts +38 -0
- package/dist/index-full.d.ts +8 -0
- package/dist/index-minimal.d.ts +3 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +1 -2
- package/dist/providers/tabby/ai-config.provider.d.ts +57 -5
- package/dist/providers/tabby/ai-hotkey.provider.d.ts +8 -14
- package/dist/providers/tabby/ai-toolbar-button.provider.d.ts +8 -9
- package/dist/services/chat/ai-sidebar.service.d.ts +89 -0
- package/dist/services/chat/chat-history.service.d.ts +78 -0
- package/dist/services/chat/chat-session.service.d.ts +57 -2
- package/dist/services/context/compaction.d.ts +90 -0
- package/dist/services/context/manager.d.ts +69 -0
- package/dist/services/context/memory.d.ts +116 -0
- package/dist/services/context/token-budget.d.ts +105 -0
- package/dist/services/core/ai-assistant.service.d.ts +40 -1
- package/dist/services/core/checkpoint.service.d.ts +130 -0
- package/dist/services/platform/escape-sequence.service.d.ts +132 -0
- package/dist/services/platform/platform-detection.service.d.ts +146 -0
- package/dist/services/providers/anthropic-provider.service.d.ts +5 -0
- package/dist/services/providers/base-provider.service.d.ts +6 -1
- package/dist/services/providers/glm-provider.service.d.ts +5 -0
- package/dist/services/providers/minimax-provider.service.d.ts +10 -1
- package/dist/services/providers/ollama-provider.service.d.ts +76 -0
- package/dist/services/providers/openai-compatible.service.d.ts +5 -0
- package/dist/services/providers/openai-provider.service.d.ts +5 -0
- package/dist/services/providers/vllm-provider.service.d.ts +82 -0
- package/dist/services/terminal/buffer-analyzer.service.d.ts +128 -0
- package/dist/services/terminal/terminal-manager.service.d.ts +185 -0
- package/dist/services/terminal/terminal-tools.service.d.ts +79 -0
- package/dist/types/ai.types.d.ts +92 -0
- package/dist/types/provider.types.d.ts +1 -1
- package/package.json +7 -10
- package/src/components/chat/ai-sidebar.component.ts +945 -0
- package/src/components/chat/chat-input.component.html +9 -24
- package/src/components/chat/chat-input.component.scss +3 -2
- package/src/components/chat/chat-interface.component.html +77 -69
- package/src/components/chat/chat-interface.component.scss +54 -4
- package/src/components/chat/chat-interface.component.ts +250 -34
- package/src/components/chat/chat-settings.component.scss +4 -4
- package/src/components/chat/chat-settings.component.ts +22 -11
- package/src/components/common/error-message.component.html +15 -0
- package/src/components/common/error-message.component.scss +77 -0
- package/src/components/common/error-message.component.ts +2 -96
- package/src/components/common/loading-spinner.component.html +4 -0
- package/src/components/common/loading-spinner.component.scss +57 -0
- package/src/components/common/loading-spinner.component.ts +2 -63
- package/src/components/security/consent-dialog.component.html +22 -0
- package/src/components/security/consent-dialog.component.scss +34 -0
- package/src/components/security/consent-dialog.component.ts +2 -55
- package/src/components/security/password-prompt.component.html +19 -0
- package/src/components/security/password-prompt.component.scss +30 -0
- package/src/components/security/password-prompt.component.ts +2 -54
- package/src/components/security/risk-confirm-dialog.component.html +8 -12
- package/src/components/security/risk-confirm-dialog.component.scss +8 -5
- package/src/components/security/risk-confirm-dialog.component.ts +6 -6
- package/src/components/settings/ai-settings-tab.component.html +16 -20
- package/src/components/settings/ai-settings-tab.component.scss +8 -5
- package/src/components/settings/ai-settings-tab.component.ts +12 -12
- package/src/components/settings/general-settings.component.html +8 -17
- package/src/components/settings/general-settings.component.scss +6 -3
- package/src/components/settings/general-settings.component.ts +62 -22
- package/src/components/settings/provider-config.component.html +19 -39
- package/src/components/settings/provider-config.component.scss +182 -39
- package/src/components/settings/provider-config.component.ts +119 -7
- package/src/components/settings/security-settings.component.scss +1 -1
- package/src/components/terminal/ai-toolbar-button.component.html +8 -0
- package/src/components/terminal/ai-toolbar-button.component.scss +20 -0
- package/src/components/terminal/ai-toolbar-button.component.ts +2 -30
- package/src/components/terminal/command-preview.component.html +61 -0
- package/src/components/terminal/command-preview.component.scss +72 -0
- package/src/components/terminal/command-preview.component.ts +127 -140
- package/src/components/terminal/command-suggestion.component.html +23 -0
- package/src/components/terminal/command-suggestion.component.scss +55 -0
- package/src/components/terminal/command-suggestion.component.ts +2 -77
- package/src/index-minimal.ts +32 -0
- package/src/index.ts +94 -11
- package/src/index.ts.backup +165 -0
- package/src/providers/tabby/ai-config.provider.ts +60 -51
- package/src/providers/tabby/ai-hotkey.provider.ts +23 -39
- package/src/providers/tabby/ai-settings-tab.provider.ts +2 -2
- package/src/providers/tabby/ai-toolbar-button.provider.ts +29 -24
- package/src/services/chat/ai-sidebar.service.ts +258 -0
- package/src/services/chat/chat-history.service.ts +308 -0
- package/src/services/chat/chat-history.service.ts.backup +239 -0
- package/src/services/chat/chat-session.service.ts +276 -3
- package/src/services/context/compaction.ts +483 -0
- package/src/services/context/manager.ts +442 -0
- package/src/services/context/memory.ts +519 -0
- package/src/services/context/token-budget.ts +422 -0
- package/src/services/core/ai-assistant.service.ts +280 -5
- package/src/services/core/ai-provider-manager.service.ts +2 -2
- package/src/services/core/checkpoint.service.ts +619 -0
- package/src/services/platform/escape-sequence.service.ts +499 -0
- package/src/services/platform/platform-detection.service.ts +494 -0
- package/src/services/providers/anthropic-provider.service.ts +28 -1
- package/src/services/providers/base-provider.service.ts +7 -1
- package/src/services/providers/glm-provider.service.ts +28 -1
- package/src/services/providers/minimax-provider.service.ts +209 -11
- package/src/services/providers/ollama-provider.service.ts +445 -0
- package/src/services/providers/openai-compatible.service.ts +9 -0
- package/src/services/providers/openai-provider.service.ts +9 -0
- package/src/services/providers/vllm-provider.service.ts +463 -0
- package/src/services/security/risk-assessment.service.ts +6 -2
- package/src/services/terminal/buffer-analyzer.service.ts +594 -0
- package/src/services/terminal/terminal-manager.service.ts +748 -0
- package/src/services/terminal/terminal-tools.service.ts +441 -0
- package/src/styles/ai-assistant.scss +78 -6
- package/src/types/ai.types.ts +144 -0
- package/src/types/provider.types.ts +1 -1
- package/tsconfig.json +9 -9
- package/webpack.config.js +28 -6
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { LoggerService } from '../core/logger.service';
|
|
3
|
+
import { ContextConfig, TokenUsage } from '../../types/ai.types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 预算分配策略
|
|
7
|
+
*/
|
|
8
|
+
export interface BudgetAllocation {
|
|
9
|
+
context: number; // 上下文预算
|
|
10
|
+
reserved: number; // 预留预算
|
|
11
|
+
buffer: number; // 安全缓冲
|
|
12
|
+
available: number; // 可用预算
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 预算使用情况
|
|
17
|
+
*/
|
|
18
|
+
export interface BudgetUsage {
|
|
19
|
+
current: TokenUsage;
|
|
20
|
+
allocated: BudgetAllocation;
|
|
21
|
+
utilizationRate: number;
|
|
22
|
+
projectedUsage: number;
|
|
23
|
+
remaining: number;
|
|
24
|
+
warnings: string[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 预算策略配置
|
|
29
|
+
*/
|
|
30
|
+
export interface BudgetStrategy {
|
|
31
|
+
conservative: boolean; // 保守策略:更多缓冲
|
|
32
|
+
aggressive: boolean; // 激进策略:最大化使用
|
|
33
|
+
adaptive: boolean; // 自适应:根据历史调整
|
|
34
|
+
dynamic: boolean; // 动态:根据实时使用调整
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Token预算管理器
|
|
39
|
+
* 负责动态分配和管理Token预算
|
|
40
|
+
*/
|
|
41
|
+
@Injectable({
|
|
42
|
+
providedIn: 'root'
|
|
43
|
+
})
|
|
44
|
+
export class TokenBudget {
|
|
45
|
+
private config: ContextConfig;
|
|
46
|
+
private currentUsage: TokenUsage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
47
|
+
private usageHistory: TokenUsage[] = [];
|
|
48
|
+
private strategy: BudgetStrategy = {
|
|
49
|
+
conservative: false,
|
|
50
|
+
aggressive: false,
|
|
51
|
+
adaptive: true,
|
|
52
|
+
dynamic: true
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
constructor(
|
|
56
|
+
private logger: LoggerService,
|
|
57
|
+
config?: ContextConfig
|
|
58
|
+
) {
|
|
59
|
+
this.config = config || {
|
|
60
|
+
maxContextTokens: 200000,
|
|
61
|
+
reservedOutputTokens: 16000,
|
|
62
|
+
compactThreshold: 0.85,
|
|
63
|
+
pruneThreshold: 0.70,
|
|
64
|
+
messagesToKeep: 3,
|
|
65
|
+
bufferPercentage: 0.10
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
this.logger.info('TokenBudget initialized', { config: this.config });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 计算预算分配
|
|
73
|
+
*/
|
|
74
|
+
calculateBudget(): BudgetAllocation {
|
|
75
|
+
const { maxContextTokens, reservedOutputTokens, bufferPercentage } = this.config;
|
|
76
|
+
|
|
77
|
+
// 基础预算计算
|
|
78
|
+
let context = maxContextTokens - reservedOutputTokens;
|
|
79
|
+
let reserved = reservedOutputTokens;
|
|
80
|
+
let buffer = Math.floor(context * bufferPercentage);
|
|
81
|
+
|
|
82
|
+
// 根据策略调整
|
|
83
|
+
if (this.strategy.conservative) {
|
|
84
|
+
// 保守策略:增加缓冲,减少可用预算
|
|
85
|
+
buffer = Math.floor(context * (bufferPercentage * 1.5));
|
|
86
|
+
} else if (this.strategy.aggressive) {
|
|
87
|
+
// 激进策略:减少缓冲,增加可用预算
|
|
88
|
+
buffer = Math.floor(context * (bufferPercentage * 0.5));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (this.strategy.adaptive && this.usageHistory.length > 0) {
|
|
92
|
+
// 自适应策略:根据历史使用情况调整
|
|
93
|
+
const avgUsage = this.getAverageUsage();
|
|
94
|
+
const usageVariation = this.calculateUsageVariation();
|
|
95
|
+
const adjustmentFactor = 1 - (usageVariation * 0.1);
|
|
96
|
+
|
|
97
|
+
buffer = Math.floor(buffer * adjustmentFactor);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const available = context - buffer;
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
context,
|
|
104
|
+
reserved,
|
|
105
|
+
buffer,
|
|
106
|
+
available: Math.max(0, available)
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 检查预算阈值
|
|
112
|
+
*/
|
|
113
|
+
checkThresholds(usage?: TokenUsage): {
|
|
114
|
+
shouldPrune: boolean;
|
|
115
|
+
shouldCompact: boolean;
|
|
116
|
+
shouldTruncate: boolean;
|
|
117
|
+
urgency: 'low' | 'medium' | 'high' | 'critical';
|
|
118
|
+
} {
|
|
119
|
+
const currentUsage = usage || this.currentUsage;
|
|
120
|
+
const allocation = this.calculateBudget();
|
|
121
|
+
const totalUsed = currentUsage.input + currentUsage.output;
|
|
122
|
+
const usageRate = totalUsed / allocation.available;
|
|
123
|
+
|
|
124
|
+
const result = {
|
|
125
|
+
shouldPrune: usageRate >= this.config.pruneThreshold,
|
|
126
|
+
shouldCompact: usageRate >= this.config.compactThreshold,
|
|
127
|
+
shouldTruncate: usageRate >= 0.95,
|
|
128
|
+
urgency: 'low' as 'low' | 'medium' | 'high' | 'critical'
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
if (usageRate >= 0.95) {
|
|
132
|
+
result.urgency = 'critical';
|
|
133
|
+
} else if (usageRate >= 0.85) {
|
|
134
|
+
result.urgency = 'high';
|
|
135
|
+
} else if (usageRate >= 0.70) {
|
|
136
|
+
result.urgency = 'medium';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* 分配Token
|
|
144
|
+
*/
|
|
145
|
+
allocateTokens(
|
|
146
|
+
type: 'input' | 'output' | 'cache_read' | 'cache_write',
|
|
147
|
+
amount: number,
|
|
148
|
+
sessionId?: string
|
|
149
|
+
): boolean {
|
|
150
|
+
const allocation = this.calculateBudget();
|
|
151
|
+
const currentTotal = this.currentUsage.input + this.currentUsage.output;
|
|
152
|
+
const projectedTotal = currentTotal + amount;
|
|
153
|
+
|
|
154
|
+
// 检查是否超出预算
|
|
155
|
+
if (projectedTotal > allocation.available) {
|
|
156
|
+
const overage = projectedTotal - allocation.available;
|
|
157
|
+
this.logger.warn('Token budget exceeded', {
|
|
158
|
+
type,
|
|
159
|
+
amount,
|
|
160
|
+
overage,
|
|
161
|
+
sessionId
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// 根据策略决定是否允许
|
|
165
|
+
if (this.strategy.aggressive) {
|
|
166
|
+
// 激进策略:允许超出
|
|
167
|
+
this.updateUsage(type, amount);
|
|
168
|
+
return true;
|
|
169
|
+
} else {
|
|
170
|
+
// 保守策略:拒绝超出
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
this.updateUsage(type, amount);
|
|
176
|
+
this.logger.debug('Tokens allocated', { type, amount, sessionId });
|
|
177
|
+
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* 追踪使用量
|
|
183
|
+
*/
|
|
184
|
+
trackUsage(usage: TokenUsage): void {
|
|
185
|
+
// 保存到历史
|
|
186
|
+
this.usageHistory.push({ ...usage });
|
|
187
|
+
|
|
188
|
+
// 限制历史记录数量
|
|
189
|
+
if (this.usageHistory.length > 100) {
|
|
190
|
+
this.usageHistory.shift();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 更新当前使用量
|
|
194
|
+
this.currentUsage = { ...usage };
|
|
195
|
+
|
|
196
|
+
// 动态调整策略
|
|
197
|
+
if (this.strategy.dynamic) {
|
|
198
|
+
this.adjustStrategy();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
this.logger.debug('Usage tracked', { usage });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* 获取预算使用情况
|
|
206
|
+
*/
|
|
207
|
+
getBudgetUsage(): BudgetUsage {
|
|
208
|
+
const allocation = this.calculateBudget();
|
|
209
|
+
const currentTotal = this.currentUsage.input + this.currentUsage.output;
|
|
210
|
+
const utilizationRate = currentTotal / allocation.available;
|
|
211
|
+
const projectedUsage = this.projectUsage();
|
|
212
|
+
const remaining = allocation.available - currentTotal;
|
|
213
|
+
|
|
214
|
+
const warnings: string[] = [];
|
|
215
|
+
const thresholds = this.checkThresholds();
|
|
216
|
+
|
|
217
|
+
if (thresholds.shouldPrune) {
|
|
218
|
+
warnings.push('建议进行Prune操作以节省Token');
|
|
219
|
+
}
|
|
220
|
+
if (thresholds.shouldCompact) {
|
|
221
|
+
warnings.push('建议进行Compact操作以节省Token');
|
|
222
|
+
}
|
|
223
|
+
if (thresholds.shouldTruncate) {
|
|
224
|
+
warnings.push('严重警告:必须进行Truncate操作');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (utilizationRate > 0.9) {
|
|
228
|
+
warnings.push('Token使用率过高,请注意预算管理');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
current: this.currentUsage,
|
|
233
|
+
allocated: allocation,
|
|
234
|
+
utilizationRate,
|
|
235
|
+
projectedUsage,
|
|
236
|
+
remaining,
|
|
237
|
+
warnings
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 预估未来使用量
|
|
243
|
+
*/
|
|
244
|
+
projectUsage(horizon: number = 10): number {
|
|
245
|
+
if (this.usageHistory.length < 2) {
|
|
246
|
+
return this.currentUsage.input + this.currentUsage.output;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 简化的线性预测
|
|
250
|
+
const recentHistory = this.usageHistory.slice(-horizon);
|
|
251
|
+
const avgGrowth = this.calculateAverageGrowth(recentHistory);
|
|
252
|
+
const currentTotal = this.currentUsage.input + this.currentUsage.output;
|
|
253
|
+
|
|
254
|
+
return currentTotal + (avgGrowth * horizon);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* 重置预算
|
|
259
|
+
*/
|
|
260
|
+
reset(sessionId?: string): void {
|
|
261
|
+
this.currentUsage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
262
|
+
|
|
263
|
+
this.logger.info('Token budget reset', { sessionId });
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* 更新策略
|
|
268
|
+
*/
|
|
269
|
+
updateStrategy(strategy: Partial<BudgetStrategy>): void {
|
|
270
|
+
this.strategy = { ...this.strategy, ...strategy };
|
|
271
|
+
this.logger.info('Budget strategy updated', { strategy: this.strategy });
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* 获取策略
|
|
276
|
+
*/
|
|
277
|
+
getStrategy(): BudgetStrategy {
|
|
278
|
+
return { ...this.strategy };
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 更新配置
|
|
283
|
+
*/
|
|
284
|
+
updateConfig(config: Partial<ContextConfig>): void {
|
|
285
|
+
this.config = { ...this.config, ...config };
|
|
286
|
+
this.logger.info('TokenBudget config updated', { config: this.config });
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* 获取配置
|
|
291
|
+
*/
|
|
292
|
+
getConfig(): ContextConfig {
|
|
293
|
+
return { ...this.config };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* 估算消息Token成本
|
|
298
|
+
*/
|
|
299
|
+
estimateMessageCost(message: string, role: 'user' | 'assistant' | 'system'): number {
|
|
300
|
+
// 粗略估算
|
|
301
|
+
const baseCost = Math.ceil(message.length / 4);
|
|
302
|
+
|
|
303
|
+
// 角色调整
|
|
304
|
+
const roleMultiplier = {
|
|
305
|
+
user: 1.0,
|
|
306
|
+
assistant: 1.2, // 输出通常更复杂
|
|
307
|
+
system: 0.8 // 系统提示通常简洁
|
|
308
|
+
}[role];
|
|
309
|
+
|
|
310
|
+
return Math.ceil(baseCost * roleMultiplier);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* 优化预算分配
|
|
315
|
+
*/
|
|
316
|
+
optimizeAllocation(): BudgetAllocation {
|
|
317
|
+
const current = this.calculateBudget();
|
|
318
|
+
const usage = this.getBudgetUsage();
|
|
319
|
+
|
|
320
|
+
// 如果使用率过低,减少缓冲
|
|
321
|
+
if (usage.utilizationRate < 0.5) {
|
|
322
|
+
const newBuffer = Math.floor(current.buffer * 0.8);
|
|
323
|
+
return {
|
|
324
|
+
...current,
|
|
325
|
+
buffer: newBuffer,
|
|
326
|
+
available: current.context - newBuffer
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// 如果使用率过高,增加缓冲
|
|
331
|
+
if (usage.utilizationRate > 0.85) {
|
|
332
|
+
const newBuffer = Math.floor(current.buffer * 1.2);
|
|
333
|
+
return {
|
|
334
|
+
...current,
|
|
335
|
+
buffer: newBuffer,
|
|
336
|
+
available: Math.max(0, current.context - newBuffer)
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return current;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ==================== 私有方法 ====================
|
|
344
|
+
|
|
345
|
+
private updateUsage(type: string, amount: number): void {
|
|
346
|
+
switch (type) {
|
|
347
|
+
case 'input':
|
|
348
|
+
this.currentUsage.input += amount;
|
|
349
|
+
break;
|
|
350
|
+
case 'output':
|
|
351
|
+
this.currentUsage.output += amount;
|
|
352
|
+
break;
|
|
353
|
+
case 'cache_read':
|
|
354
|
+
this.currentUsage.cacheRead += amount;
|
|
355
|
+
break;
|
|
356
|
+
case 'cache_write':
|
|
357
|
+
this.currentUsage.cacheWrite += amount;
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private getAverageUsage(): number {
|
|
363
|
+
if (this.usageHistory.length === 0) {
|
|
364
|
+
return 0;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const total = this.usageHistory.reduce((sum, usage) => {
|
|
368
|
+
return sum + usage.input + usage.output;
|
|
369
|
+
}, 0);
|
|
370
|
+
|
|
371
|
+
return total / this.usageHistory.length;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
private calculateUsageVariation(): number {
|
|
375
|
+
if (this.usageHistory.length < 2) {
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const usage = this.usageHistory.map(u => u.input + u.output);
|
|
380
|
+
const mean = usage.reduce((a, b) => a + b, 0) / usage.length;
|
|
381
|
+
const variance = usage.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / usage.length;
|
|
382
|
+
|
|
383
|
+
return Math.sqrt(variance) / mean;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
private calculateAverageGrowth(history: TokenUsage[]): number {
|
|
387
|
+
if (history.length < 2) {
|
|
388
|
+
return 0;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
let totalGrowth = 0;
|
|
392
|
+
for (let i = 1; i < history.length; i++) {
|
|
393
|
+
const prev = history[i - 1];
|
|
394
|
+
const curr = history[i];
|
|
395
|
+
const prevTotal = prev.input + prev.output;
|
|
396
|
+
const currTotal = curr.input + curr.output;
|
|
397
|
+
totalGrowth += currTotal - prevTotal;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return totalGrowth / (history.length - 1);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
private adjustStrategy(): void {
|
|
404
|
+
const usage = this.getBudgetUsage();
|
|
405
|
+
|
|
406
|
+
// 如果使用率持续过高,转向保守策略
|
|
407
|
+
if (usage.utilizationRate > 0.9) {
|
|
408
|
+
this.strategy.conservative = true;
|
|
409
|
+
this.strategy.aggressive = false;
|
|
410
|
+
}
|
|
411
|
+
// 如果使用率持续过低,转向激进策略
|
|
412
|
+
else if (usage.utilizationRate < 0.4) {
|
|
413
|
+
this.strategy.conservative = false;
|
|
414
|
+
this.strategy.aggressive = true;
|
|
415
|
+
}
|
|
416
|
+
// 否则保持平衡
|
|
417
|
+
else {
|
|
418
|
+
this.strategy.conservative = false;
|
|
419
|
+
this.strategy.aggressive = false;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|