claude-flow 2.0.0-alpha.65 → 2.0.0-alpha.67
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/.claude/cache/agent-pool.json +33 -0
- package/.claude/cache/memory-optimization.json +19 -0
- package/.claude/cache/neural-optimization.json +25 -0
- package/.claude/cache/optimized-hooks.json +19 -0
- package/.claude/cache/parallel-processing.json +26 -0
- package/.claude/optimized-settings.json +270 -0
- package/.claude/settings-enhanced.json +278 -0
- package/.claude/settings.json +105 -8
- package/CHANGELOG.md +40 -0
- package/bin/claude-flow +1 -1
- package/dist/cli/simple-commands/hive-mind.js +1 -1
- package/dist/cli/simple-commands/hive-mind.js.map +1 -1
- package/dist/cli/simple-commands/hooks.js +6 -4
- package/dist/cli/simple-commands/hooks.js.map +1 -1
- package/dist/providers/anthropic-provider.d.ts +27 -0
- package/dist/providers/anthropic-provider.d.ts.map +1 -0
- package/dist/providers/anthropic-provider.js +247 -0
- package/dist/providers/anthropic-provider.js.map +1 -0
- package/dist/providers/base-provider.d.ts +134 -0
- package/dist/providers/base-provider.d.ts.map +1 -0
- package/dist/providers/base-provider.js +407 -0
- package/dist/providers/base-provider.js.map +1 -0
- package/dist/providers/cohere-provider.d.ts +28 -0
- package/dist/providers/cohere-provider.d.ts.map +1 -0
- package/dist/providers/cohere-provider.js +407 -0
- package/dist/providers/cohere-provider.js.map +1 -0
- package/dist/providers/google-provider.d.ts +23 -0
- package/dist/providers/google-provider.d.ts.map +1 -0
- package/dist/providers/google-provider.js +362 -0
- package/dist/providers/google-provider.js.map +1 -0
- package/dist/providers/index.d.ts +14 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +18 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/ollama-provider.d.ts +23 -0
- package/dist/providers/ollama-provider.d.ts.map +1 -0
- package/dist/providers/ollama-provider.js +374 -0
- package/dist/providers/ollama-provider.js.map +1 -0
- package/dist/providers/openai-provider.d.ts +23 -0
- package/dist/providers/openai-provider.d.ts.map +1 -0
- package/dist/providers/openai-provider.js +349 -0
- package/dist/providers/openai-provider.js.map +1 -0
- package/dist/providers/provider-manager.d.ts +139 -0
- package/dist/providers/provider-manager.d.ts.map +1 -0
- package/dist/providers/provider-manager.js +513 -0
- package/dist/providers/provider-manager.js.map +1 -0
- package/dist/providers/types.d.ts +356 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +61 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/providers/utils.d.ts +37 -0
- package/dist/providers/utils.d.ts.map +1 -0
- package/dist/providers/utils.js +322 -0
- package/dist/providers/utils.js.map +1 -0
- package/dist/services/agentic-flow-hooks/hook-manager.d.ts +70 -0
- package/dist/services/agentic-flow-hooks/hook-manager.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/hook-manager.js +512 -0
- package/dist/services/agentic-flow-hooks/hook-manager.js.map +1 -0
- package/dist/services/agentic-flow-hooks/index.d.ts +36 -0
- package/dist/services/agentic-flow-hooks/index.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/index.js +325 -0
- package/dist/services/agentic-flow-hooks/index.js.map +1 -0
- package/dist/services/agentic-flow-hooks/llm-hooks.d.ts +33 -0
- package/dist/services/agentic-flow-hooks/llm-hooks.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/llm-hooks.js +415 -0
- package/dist/services/agentic-flow-hooks/llm-hooks.js.map +1 -0
- package/dist/services/agentic-flow-hooks/memory-hooks.d.ts +45 -0
- package/dist/services/agentic-flow-hooks/memory-hooks.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/memory-hooks.js +532 -0
- package/dist/services/agentic-flow-hooks/memory-hooks.js.map +1 -0
- package/dist/services/agentic-flow-hooks/neural-hooks.d.ts +39 -0
- package/dist/services/agentic-flow-hooks/neural-hooks.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/neural-hooks.js +561 -0
- package/dist/services/agentic-flow-hooks/neural-hooks.js.map +1 -0
- package/dist/services/agentic-flow-hooks/performance-hooks.d.ts +33 -0
- package/dist/services/agentic-flow-hooks/performance-hooks.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/performance-hooks.js +621 -0
- package/dist/services/agentic-flow-hooks/performance-hooks.js.map +1 -0
- package/dist/services/agentic-flow-hooks/types.d.ts +379 -0
- package/dist/services/agentic-flow-hooks/types.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/types.js +8 -0
- package/dist/services/agentic-flow-hooks/types.js.map +1 -0
- package/dist/services/agentic-flow-hooks/workflow-hooks.d.ts +39 -0
- package/dist/services/agentic-flow-hooks/workflow-hooks.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/workflow-hooks.js +742 -0
- package/dist/services/agentic-flow-hooks/workflow-hooks.js.map +1 -0
- package/package.json +2 -2
- package/scripts/install-arm64.js +78 -0
- package/scripts/optimize-performance.js +400 -0
- package/scripts/performance-monitor.js +263 -0
- package/src/cli/help-text.js +1 -1
- package/src/cli/simple-cli.js +1 -1
- package/src/cli/simple-commands/hive-mind.js +1 -1
- package/src/cli/simple-commands/hooks.js +8 -6
- package/src/providers/anthropic-provider.ts +282 -0
- package/src/providers/base-provider.ts +560 -0
- package/src/providers/cohere-provider.ts +521 -0
- package/src/providers/google-provider.ts +477 -0
- package/src/providers/index.ts +21 -0
- package/src/providers/ollama-provider.ts +489 -0
- package/src/providers/openai-provider.ts +476 -0
- package/src/providers/provider-manager.ts +654 -0
- package/src/providers/types.ts +531 -0
- package/src/providers/utils.ts +376 -0
- package/src/services/agentic-flow-hooks/hook-manager.ts +701 -0
- package/src/services/agentic-flow-hooks/index.ts +386 -0
- package/src/services/agentic-flow-hooks/llm-hooks.ts +557 -0
- package/src/services/agentic-flow-hooks/memory-hooks.ts +710 -0
- package/src/services/agentic-flow-hooks/neural-hooks.ts +758 -0
- package/src/services/agentic-flow-hooks/performance-hooks.ts +827 -0
- package/src/services/agentic-flow-hooks/types.ts +503 -0
- package/src/services/agentic-flow-hooks/workflow-hooks.ts +1026 -0
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract Base Provider for LLM integrations
|
|
3
|
+
* Provides common functionality for all LLM providers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { EventEmitter } from 'events';
|
|
7
|
+
import { ILogger } from '../core/logger.js';
|
|
8
|
+
import { circuitBreaker, CircuitBreaker } from '../utils/helpers.js';
|
|
9
|
+
import {
|
|
10
|
+
ILLMProvider,
|
|
11
|
+
LLMProvider,
|
|
12
|
+
LLMProviderConfig,
|
|
13
|
+
LLMRequest,
|
|
14
|
+
LLMResponse,
|
|
15
|
+
LLMStreamEvent,
|
|
16
|
+
LLMModel,
|
|
17
|
+
ModelInfo,
|
|
18
|
+
ProviderCapabilities,
|
|
19
|
+
HealthCheckResult,
|
|
20
|
+
ProviderStatus,
|
|
21
|
+
CostEstimate,
|
|
22
|
+
UsageStats,
|
|
23
|
+
UsagePeriod,
|
|
24
|
+
LLMProviderError,
|
|
25
|
+
RateLimitError,
|
|
26
|
+
ProviderUnavailableError,
|
|
27
|
+
} from './types.js';
|
|
28
|
+
|
|
29
|
+
export interface BaseProviderOptions {
|
|
30
|
+
logger: ILogger;
|
|
31
|
+
config: LLMProviderConfig;
|
|
32
|
+
cacheTTL?: number;
|
|
33
|
+
circuitBreakerOptions?: {
|
|
34
|
+
threshold?: number;
|
|
35
|
+
timeout?: number;
|
|
36
|
+
resetTimeout?: number;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export abstract class BaseProvider extends EventEmitter implements ILLMProvider {
|
|
41
|
+
abstract readonly name: LLMProvider;
|
|
42
|
+
abstract readonly capabilities: ProviderCapabilities;
|
|
43
|
+
|
|
44
|
+
protected logger: ILogger;
|
|
45
|
+
protected circuitBreaker: CircuitBreaker;
|
|
46
|
+
protected healthCheckInterval?: NodeJS.Timeout;
|
|
47
|
+
protected lastHealthCheck?: HealthCheckResult;
|
|
48
|
+
protected requestCount = 0;
|
|
49
|
+
protected errorCount = 0;
|
|
50
|
+
protected totalTokens = 0;
|
|
51
|
+
protected totalCost = 0;
|
|
52
|
+
protected requestMetrics: Map<string, any> = new Map();
|
|
53
|
+
|
|
54
|
+
public config: LLMProviderConfig;
|
|
55
|
+
|
|
56
|
+
constructor(options: BaseProviderOptions) {
|
|
57
|
+
super();
|
|
58
|
+
this.logger = options.logger;
|
|
59
|
+
this.config = options.config;
|
|
60
|
+
|
|
61
|
+
// Initialize circuit breaker
|
|
62
|
+
this.circuitBreaker = circuitBreaker(`llm-${this.name}`, {
|
|
63
|
+
threshold: options.circuitBreakerOptions?.threshold || 5,
|
|
64
|
+
timeout: options.circuitBreakerOptions?.timeout || 60000,
|
|
65
|
+
resetTimeout: options.circuitBreakerOptions?.resetTimeout || 300000,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Start health checks if enabled
|
|
69
|
+
if (this.config.enableCaching) {
|
|
70
|
+
this.startHealthChecks();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Initialize the provider
|
|
76
|
+
*/
|
|
77
|
+
async initialize(): Promise<void> {
|
|
78
|
+
this.logger.info(`Initializing ${this.name} provider`, {
|
|
79
|
+
model: this.config.model,
|
|
80
|
+
temperature: this.config.temperature,
|
|
81
|
+
maxTokens: this.config.maxTokens,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Validate configuration
|
|
85
|
+
this.validateConfig();
|
|
86
|
+
|
|
87
|
+
// Provider-specific initialization
|
|
88
|
+
await this.doInitialize();
|
|
89
|
+
|
|
90
|
+
// Perform initial health check
|
|
91
|
+
await this.healthCheck();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Provider-specific initialization
|
|
96
|
+
*/
|
|
97
|
+
protected abstract doInitialize(): Promise<void>;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Validate provider configuration
|
|
101
|
+
*/
|
|
102
|
+
protected validateConfig(): void {
|
|
103
|
+
if (!this.config.model) {
|
|
104
|
+
throw new Error(`Model is required for ${this.name} provider`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!this.validateModel(this.config.model)) {
|
|
108
|
+
throw new Error(`Model ${this.config.model} is not supported by ${this.name} provider`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (this.config.temperature !== undefined) {
|
|
112
|
+
if (this.config.temperature < 0 || this.config.temperature > 2) {
|
|
113
|
+
throw new Error('Temperature must be between 0 and 2');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (this.config.maxTokens !== undefined) {
|
|
118
|
+
const maxAllowed = this.capabilities.maxOutputTokens[this.config.model] || 4096;
|
|
119
|
+
if (this.config.maxTokens > maxAllowed) {
|
|
120
|
+
throw new Error(`Max tokens exceeds limit of ${maxAllowed} for model ${this.config.model}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Complete a request
|
|
127
|
+
*/
|
|
128
|
+
async complete(request: LLMRequest): Promise<LLMResponse> {
|
|
129
|
+
const startTime = Date.now();
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
// Use circuit breaker
|
|
133
|
+
const response = await this.circuitBreaker.execute(async () => {
|
|
134
|
+
return await this.doComplete(request);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Track metrics
|
|
138
|
+
const latency = Date.now() - startTime;
|
|
139
|
+
this.trackRequest(request, response, latency);
|
|
140
|
+
|
|
141
|
+
// Emit events
|
|
142
|
+
this.emit('response', {
|
|
143
|
+
provider: this.name,
|
|
144
|
+
model: response.model,
|
|
145
|
+
latency,
|
|
146
|
+
tokens: response.usage.totalTokens,
|
|
147
|
+
cost: response.cost?.totalCost,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return response;
|
|
151
|
+
} catch (error) {
|
|
152
|
+
this.errorCount++;
|
|
153
|
+
|
|
154
|
+
// Transform to provider error
|
|
155
|
+
const providerError = this.transformError(error);
|
|
156
|
+
|
|
157
|
+
// Track error
|
|
158
|
+
this.emit('error', {
|
|
159
|
+
provider: this.name,
|
|
160
|
+
error: providerError,
|
|
161
|
+
request,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
throw providerError;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Provider-specific completion implementation
|
|
170
|
+
*/
|
|
171
|
+
protected abstract doComplete(request: LLMRequest): Promise<LLMResponse>;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Stream complete a request
|
|
175
|
+
*/
|
|
176
|
+
async *streamComplete(request: LLMRequest): AsyncIterable<LLMStreamEvent> {
|
|
177
|
+
const startTime = Date.now();
|
|
178
|
+
let totalTokens = 0;
|
|
179
|
+
let totalCost = 0;
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
// Check if streaming is supported
|
|
183
|
+
if (!this.capabilities.supportsStreaming) {
|
|
184
|
+
throw new LLMProviderError(
|
|
185
|
+
'Streaming not supported',
|
|
186
|
+
'STREAMING_NOT_SUPPORTED',
|
|
187
|
+
this.name,
|
|
188
|
+
undefined,
|
|
189
|
+
false
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Use circuit breaker
|
|
194
|
+
const stream = await this.circuitBreaker.execute(async () => {
|
|
195
|
+
return this.doStreamComplete(request);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Process stream
|
|
199
|
+
for await (const event of stream) {
|
|
200
|
+
if (event.usage) {
|
|
201
|
+
totalTokens = event.usage.totalTokens;
|
|
202
|
+
}
|
|
203
|
+
if (event.cost) {
|
|
204
|
+
totalCost = event.cost.totalCost;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
yield event;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Track metrics
|
|
211
|
+
const latency = Date.now() - startTime;
|
|
212
|
+
this.trackStreamRequest(request, totalTokens, totalCost, latency);
|
|
213
|
+
|
|
214
|
+
} catch (error) {
|
|
215
|
+
this.errorCount++;
|
|
216
|
+
|
|
217
|
+
// Transform to provider error
|
|
218
|
+
const providerError = this.transformError(error);
|
|
219
|
+
|
|
220
|
+
// Yield error event
|
|
221
|
+
yield {
|
|
222
|
+
type: 'error',
|
|
223
|
+
error: providerError,
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
throw providerError;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Provider-specific stream completion implementation
|
|
232
|
+
*/
|
|
233
|
+
protected abstract doStreamComplete(request: LLMRequest): AsyncIterable<LLMStreamEvent>;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* List available models
|
|
237
|
+
*/
|
|
238
|
+
abstract listModels(): Promise<LLMModel[]>;
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Get model information
|
|
242
|
+
*/
|
|
243
|
+
abstract getModelInfo(model: LLMModel): Promise<ModelInfo>;
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Validate if a model is supported
|
|
247
|
+
*/
|
|
248
|
+
validateModel(model: LLMModel): boolean {
|
|
249
|
+
return this.capabilities.supportedModels.includes(model);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Perform health check
|
|
254
|
+
*/
|
|
255
|
+
async healthCheck(): Promise<HealthCheckResult> {
|
|
256
|
+
const startTime = Date.now();
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
// Provider-specific health check
|
|
260
|
+
const result = await this.doHealthCheck();
|
|
261
|
+
|
|
262
|
+
this.lastHealthCheck = {
|
|
263
|
+
...result,
|
|
264
|
+
latency: Date.now() - startTime,
|
|
265
|
+
timestamp: new Date(),
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
this.emit('health_check', this.lastHealthCheck);
|
|
269
|
+
return this.lastHealthCheck;
|
|
270
|
+
|
|
271
|
+
} catch (error) {
|
|
272
|
+
this.lastHealthCheck = {
|
|
273
|
+
healthy: false,
|
|
274
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
275
|
+
latency: Date.now() - startTime,
|
|
276
|
+
timestamp: new Date(),
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
this.emit('health_check', this.lastHealthCheck);
|
|
280
|
+
return this.lastHealthCheck;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Provider-specific health check implementation
|
|
286
|
+
*/
|
|
287
|
+
protected abstract doHealthCheck(): Promise<HealthCheckResult>;
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Get provider status
|
|
291
|
+
*/
|
|
292
|
+
getStatus(): ProviderStatus {
|
|
293
|
+
const queueLength = this.requestMetrics.size;
|
|
294
|
+
const errorRate = this.requestCount > 0 ? this.errorCount / this.requestCount : 0;
|
|
295
|
+
|
|
296
|
+
return {
|
|
297
|
+
available: this.lastHealthCheck?.healthy ?? false,
|
|
298
|
+
currentLoad: queueLength / 100, // Normalize to 0-1
|
|
299
|
+
queueLength,
|
|
300
|
+
activeRequests: queueLength,
|
|
301
|
+
rateLimitRemaining: this.getRateLimitRemaining(),
|
|
302
|
+
rateLimitReset: this.getRateLimitReset(),
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Get remaining rate limit (override in provider)
|
|
308
|
+
*/
|
|
309
|
+
protected getRateLimitRemaining(): number | undefined {
|
|
310
|
+
return undefined;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Get rate limit reset time (override in provider)
|
|
315
|
+
*/
|
|
316
|
+
protected getRateLimitReset(): Date | undefined {
|
|
317
|
+
return undefined;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Estimate cost for a request
|
|
322
|
+
*/
|
|
323
|
+
async estimateCost(request: LLMRequest): Promise<CostEstimate> {
|
|
324
|
+
const model = request.model || this.config.model;
|
|
325
|
+
const pricing = this.capabilities.pricing?.[model];
|
|
326
|
+
|
|
327
|
+
if (!pricing) {
|
|
328
|
+
return {
|
|
329
|
+
estimatedPromptTokens: 0,
|
|
330
|
+
estimatedCompletionTokens: 0,
|
|
331
|
+
estimatedTotalTokens: 0,
|
|
332
|
+
estimatedCost: {
|
|
333
|
+
prompt: 0,
|
|
334
|
+
completion: 0,
|
|
335
|
+
total: 0,
|
|
336
|
+
currency: 'USD',
|
|
337
|
+
},
|
|
338
|
+
confidence: 0,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Estimate tokens (simple approximation, providers should override)
|
|
343
|
+
const promptTokens = this.estimateTokens(JSON.stringify(request.messages));
|
|
344
|
+
const completionTokens = request.maxTokens || this.config.maxTokens || 1000;
|
|
345
|
+
|
|
346
|
+
const promptCost = (promptTokens / 1000) * pricing.promptCostPer1k;
|
|
347
|
+
const completionCost = (completionTokens / 1000) * pricing.completionCostPer1k;
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
estimatedPromptTokens: promptTokens,
|
|
351
|
+
estimatedCompletionTokens: completionTokens,
|
|
352
|
+
estimatedTotalTokens: promptTokens + completionTokens,
|
|
353
|
+
estimatedCost: {
|
|
354
|
+
prompt: promptCost,
|
|
355
|
+
completion: completionCost,
|
|
356
|
+
total: promptCost + completionCost,
|
|
357
|
+
currency: pricing.currency,
|
|
358
|
+
},
|
|
359
|
+
confidence: 0.7, // 70% confidence in estimation
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Simple token estimation (4 chars = 1 token approximation)
|
|
365
|
+
*/
|
|
366
|
+
protected estimateTokens(text: string): number {
|
|
367
|
+
return Math.ceil(text.length / 4);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Get usage statistics
|
|
372
|
+
*/
|
|
373
|
+
async getUsage(period: UsagePeriod = 'day'): Promise<UsageStats> {
|
|
374
|
+
const now = new Date();
|
|
375
|
+
const start = this.getStartDate(now, period);
|
|
376
|
+
|
|
377
|
+
// In a real implementation, this would query a database
|
|
378
|
+
// For now, return current session stats
|
|
379
|
+
return {
|
|
380
|
+
period: { start, end: now },
|
|
381
|
+
requests: this.requestCount,
|
|
382
|
+
tokens: {
|
|
383
|
+
prompt: Math.floor(this.totalTokens * 0.7), // Estimate
|
|
384
|
+
completion: Math.floor(this.totalTokens * 0.3),
|
|
385
|
+
total: this.totalTokens,
|
|
386
|
+
},
|
|
387
|
+
cost: {
|
|
388
|
+
prompt: this.totalCost * 0.7,
|
|
389
|
+
completion: this.totalCost * 0.3,
|
|
390
|
+
total: this.totalCost,
|
|
391
|
+
currency: 'USD',
|
|
392
|
+
},
|
|
393
|
+
errors: this.errorCount,
|
|
394
|
+
averageLatency: this.calculateAverageLatency(),
|
|
395
|
+
modelBreakdown: {}, // Would need to track per model
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Get start date for period
|
|
401
|
+
*/
|
|
402
|
+
private getStartDate(end: Date, period: UsagePeriod): Date {
|
|
403
|
+
const start = new Date(end);
|
|
404
|
+
switch (period) {
|
|
405
|
+
case 'hour':
|
|
406
|
+
start.setHours(start.getHours() - 1);
|
|
407
|
+
break;
|
|
408
|
+
case 'day':
|
|
409
|
+
start.setDate(start.getDate() - 1);
|
|
410
|
+
break;
|
|
411
|
+
case 'week':
|
|
412
|
+
start.setDate(start.getDate() - 7);
|
|
413
|
+
break;
|
|
414
|
+
case 'month':
|
|
415
|
+
start.setMonth(start.getMonth() - 1);
|
|
416
|
+
break;
|
|
417
|
+
case 'all':
|
|
418
|
+
start.setFullYear(2020); // Arbitrary old date
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
return start;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Calculate average latency
|
|
426
|
+
*/
|
|
427
|
+
private calculateAverageLatency(): number {
|
|
428
|
+
if (this.requestMetrics.size === 0) return 0;
|
|
429
|
+
|
|
430
|
+
let totalLatency = 0;
|
|
431
|
+
let count = 0;
|
|
432
|
+
|
|
433
|
+
this.requestMetrics.forEach((metrics) => {
|
|
434
|
+
if (metrics.latency) {
|
|
435
|
+
totalLatency += metrics.latency;
|
|
436
|
+
count++;
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
return count > 0 ? totalLatency / count : 0;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Track successful request
|
|
445
|
+
*/
|
|
446
|
+
protected trackRequest(request: LLMRequest, response: LLMResponse, latency: number): void {
|
|
447
|
+
this.requestCount++;
|
|
448
|
+
this.totalTokens += response.usage.totalTokens;
|
|
449
|
+
|
|
450
|
+
if (response.cost) {
|
|
451
|
+
this.totalCost += response.cost.totalCost;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Store metrics (in memory for now)
|
|
455
|
+
const requestId = response.id;
|
|
456
|
+
this.requestMetrics.set(requestId, {
|
|
457
|
+
timestamp: new Date(),
|
|
458
|
+
model: response.model,
|
|
459
|
+
tokens: response.usage.totalTokens,
|
|
460
|
+
cost: response.cost?.totalCost,
|
|
461
|
+
latency,
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
// Clean up old metrics (keep last 1000)
|
|
465
|
+
if (this.requestMetrics.size > 1000) {
|
|
466
|
+
const oldestKey = this.requestMetrics.keys().next().value;
|
|
467
|
+
this.requestMetrics.delete(oldestKey);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Track streaming request
|
|
473
|
+
*/
|
|
474
|
+
protected trackStreamRequest(
|
|
475
|
+
request: LLMRequest,
|
|
476
|
+
totalTokens: number,
|
|
477
|
+
totalCost: number,
|
|
478
|
+
latency: number
|
|
479
|
+
): void {
|
|
480
|
+
this.requestCount++;
|
|
481
|
+
this.totalTokens += totalTokens;
|
|
482
|
+
this.totalCost += totalCost;
|
|
483
|
+
|
|
484
|
+
// Store metrics
|
|
485
|
+
const requestId = `stream-${Date.now()}`;
|
|
486
|
+
this.requestMetrics.set(requestId, {
|
|
487
|
+
timestamp: new Date(),
|
|
488
|
+
model: request.model || this.config.model,
|
|
489
|
+
tokens: totalTokens,
|
|
490
|
+
cost: totalCost,
|
|
491
|
+
latency,
|
|
492
|
+
stream: true,
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Transform errors to provider errors
|
|
498
|
+
*/
|
|
499
|
+
protected transformError(error: unknown): LLMProviderError {
|
|
500
|
+
if (error instanceof LLMProviderError) {
|
|
501
|
+
return error;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (error instanceof Error) {
|
|
505
|
+
// Check for common error patterns
|
|
506
|
+
if (error.message.includes('rate limit')) {
|
|
507
|
+
return new RateLimitError(error.message, this.name);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (error.message.includes('timeout') || error.message.includes('ETIMEDOUT')) {
|
|
511
|
+
return new LLMProviderError(
|
|
512
|
+
'Request timed out',
|
|
513
|
+
'TIMEOUT',
|
|
514
|
+
this.name,
|
|
515
|
+
undefined,
|
|
516
|
+
true
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (error.message.includes('ECONNREFUSED') || error.message.includes('fetch failed')) {
|
|
521
|
+
return new ProviderUnavailableError(this.name, { originalError: error.message });
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return new LLMProviderError(
|
|
526
|
+
error instanceof Error ? error.message : String(error),
|
|
527
|
+
'UNKNOWN',
|
|
528
|
+
this.name,
|
|
529
|
+
undefined,
|
|
530
|
+
true
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Start periodic health checks
|
|
536
|
+
*/
|
|
537
|
+
protected startHealthChecks(): void {
|
|
538
|
+
const interval = this.config.cacheTimeout || 300000; // 5 minutes default
|
|
539
|
+
|
|
540
|
+
this.healthCheckInterval = setInterval(() => {
|
|
541
|
+
this.healthCheck().catch((error) => {
|
|
542
|
+
this.logger.error(`Health check failed for ${this.name}`, error);
|
|
543
|
+
});
|
|
544
|
+
}, interval);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Clean up resources
|
|
549
|
+
*/
|
|
550
|
+
destroy(): void {
|
|
551
|
+
if (this.healthCheckInterval) {
|
|
552
|
+
clearInterval(this.healthCheckInterval);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
this.requestMetrics.clear();
|
|
556
|
+
this.removeAllListeners();
|
|
557
|
+
|
|
558
|
+
this.logger.info(`${this.name} provider destroyed`);
|
|
559
|
+
}
|
|
560
|
+
}
|