@sparkleideas/integration 3.5.2-patch.1
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 +270 -0
- package/package.json +55 -0
- package/src/__tests__/agent-adapter.test.ts +271 -0
- package/src/__tests__/agentic-flow-agent.test.ts +176 -0
- package/src/__tests__/token-optimizer.test.ts +176 -0
- package/src/agent-adapter.ts +651 -0
- package/src/agentic-flow-agent.ts +802 -0
- package/src/agentic-flow-bridge.ts +803 -0
- package/src/attention-coordinator.ts +679 -0
- package/src/feature-flags.ts +485 -0
- package/src/index.ts +466 -0
- package/src/long-running-worker.ts +871 -0
- package/src/multi-model-router.ts +1079 -0
- package/src/provider-adapter.ts +1168 -0
- package/src/sdk-bridge.ts +435 -0
- package/src/sona-adapter.ts +824 -0
- package/src/specialized-worker.ts +864 -0
- package/src/swarm-adapter.ts +1112 -0
- package/src/token-optimizer.ts +306 -0
- package/src/types.ts +494 -0
- package/src/worker-base.ts +822 -0
- package/src/worker-pool.ts +933 -0
- package/tmp.json +0 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,1168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProviderAdapter - Multi-Provider Support for AI Models
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified interface for working with multiple AI providers
|
|
5
|
+
* (Anthropic, OpenAI, local models, etc.) with automatic selection,
|
|
6
|
+
* failover, and load balancing.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Provider registration and management
|
|
10
|
+
* - Requirement-based provider selection
|
|
11
|
+
* - Automatic failover on provider errors
|
|
12
|
+
* - Rate limiting and quota management
|
|
13
|
+
* - Cost tracking and optimization
|
|
14
|
+
* - Provider health monitoring
|
|
15
|
+
*
|
|
16
|
+
* Compatible with @sparkleideas/agentic-flow's provider manager patterns.
|
|
17
|
+
*
|
|
18
|
+
* @module v3/integration/provider-adapter
|
|
19
|
+
* @version 3.0.0-alpha.1
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { EventEmitter } from 'events';
|
|
23
|
+
import type { Task } from './agentic-flow-agent.js';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Provider interface for AI model providers
|
|
27
|
+
*/
|
|
28
|
+
export interface Provider {
|
|
29
|
+
/** Unique provider identifier */
|
|
30
|
+
id: string;
|
|
31
|
+
/** Provider name */
|
|
32
|
+
name: string;
|
|
33
|
+
/** Provider type */
|
|
34
|
+
type: ProviderType;
|
|
35
|
+
/** Available models */
|
|
36
|
+
models: ModelInfo[];
|
|
37
|
+
/** Provider capabilities */
|
|
38
|
+
capabilities: ProviderCapability[];
|
|
39
|
+
/** Provider status */
|
|
40
|
+
status: ProviderStatus;
|
|
41
|
+
/** Rate limits */
|
|
42
|
+
rateLimits: RateLimits;
|
|
43
|
+
/** Cost per token (input/output) */
|
|
44
|
+
costPerToken: CostInfo;
|
|
45
|
+
/** Provider-specific configuration */
|
|
46
|
+
config?: Record<string, unknown>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Provider types
|
|
51
|
+
*/
|
|
52
|
+
export type ProviderType =
|
|
53
|
+
| 'anthropic'
|
|
54
|
+
| 'openai'
|
|
55
|
+
| 'google'
|
|
56
|
+
| 'azure'
|
|
57
|
+
| 'aws'
|
|
58
|
+
| 'ollama'
|
|
59
|
+
| 'huggingface'
|
|
60
|
+
| 'custom';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Provider capabilities
|
|
64
|
+
*/
|
|
65
|
+
export type ProviderCapability =
|
|
66
|
+
| 'text-completion'
|
|
67
|
+
| 'chat'
|
|
68
|
+
| 'embeddings'
|
|
69
|
+
| 'vision'
|
|
70
|
+
| 'code-generation'
|
|
71
|
+
| 'function-calling'
|
|
72
|
+
| 'streaming'
|
|
73
|
+
| 'fine-tuning'
|
|
74
|
+
| 'batch-processing'
|
|
75
|
+
| 'long-context';
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Provider status
|
|
79
|
+
*/
|
|
80
|
+
export type ProviderStatus =
|
|
81
|
+
| 'available'
|
|
82
|
+
| 'degraded'
|
|
83
|
+
| 'unavailable'
|
|
84
|
+
| 'rate-limited'
|
|
85
|
+
| 'maintenance';
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Model information
|
|
89
|
+
*/
|
|
90
|
+
export interface ModelInfo {
|
|
91
|
+
/** Model identifier */
|
|
92
|
+
id: string;
|
|
93
|
+
/** Model display name */
|
|
94
|
+
name: string;
|
|
95
|
+
/** Maximum context length */
|
|
96
|
+
maxContextLength: number;
|
|
97
|
+
/** Maximum output tokens */
|
|
98
|
+
maxOutputTokens: number;
|
|
99
|
+
/** Supported capabilities */
|
|
100
|
+
capabilities: ProviderCapability[];
|
|
101
|
+
/** Model-specific configuration */
|
|
102
|
+
config?: Record<string, unknown>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Rate limit configuration
|
|
107
|
+
*/
|
|
108
|
+
export interface RateLimits {
|
|
109
|
+
/** Requests per minute */
|
|
110
|
+
requestsPerMinute: number;
|
|
111
|
+
/** Tokens per minute */
|
|
112
|
+
tokensPerMinute: number;
|
|
113
|
+
/** Current request count */
|
|
114
|
+
currentRequests: number;
|
|
115
|
+
/** Current token count */
|
|
116
|
+
currentTokens: number;
|
|
117
|
+
/** Reset timestamp */
|
|
118
|
+
resetAt: number;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Cost information
|
|
123
|
+
*/
|
|
124
|
+
export interface CostInfo {
|
|
125
|
+
/** Cost per 1K input tokens in USD */
|
|
126
|
+
inputPer1K: number;
|
|
127
|
+
/** Cost per 1K output tokens in USD */
|
|
128
|
+
outputPer1K: number;
|
|
129
|
+
/** Currency */
|
|
130
|
+
currency: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Provider requirements for selection
|
|
135
|
+
*/
|
|
136
|
+
export interface ProviderRequirements {
|
|
137
|
+
/** Required capabilities */
|
|
138
|
+
capabilities?: ProviderCapability[];
|
|
139
|
+
/** Minimum context length */
|
|
140
|
+
minContextLength?: number;
|
|
141
|
+
/** Maximum cost per 1K tokens */
|
|
142
|
+
maxCostPer1K?: number;
|
|
143
|
+
/** Preferred provider types */
|
|
144
|
+
preferredTypes?: ProviderType[];
|
|
145
|
+
/** Excluded provider IDs */
|
|
146
|
+
excludeProviders?: string[];
|
|
147
|
+
/** Required model ID */
|
|
148
|
+
modelId?: string;
|
|
149
|
+
/** Require streaming support */
|
|
150
|
+
streaming?: boolean;
|
|
151
|
+
/** Require vision support */
|
|
152
|
+
vision?: boolean;
|
|
153
|
+
/** Custom filters */
|
|
154
|
+
customFilters?: ((provider: Provider) => boolean)[];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Provider selection result
|
|
159
|
+
*/
|
|
160
|
+
export interface ProviderSelectionResult {
|
|
161
|
+
/** Selected provider */
|
|
162
|
+
provider: Provider;
|
|
163
|
+
/** Selected model */
|
|
164
|
+
model: ModelInfo;
|
|
165
|
+
/** Selection score */
|
|
166
|
+
score: number;
|
|
167
|
+
/** Selection reasoning */
|
|
168
|
+
reasons: string[];
|
|
169
|
+
/** Alternative providers */
|
|
170
|
+
alternatives: Array<{
|
|
171
|
+
provider: Provider;
|
|
172
|
+
model: ModelInfo;
|
|
173
|
+
score: number;
|
|
174
|
+
}>;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Execution options
|
|
179
|
+
*/
|
|
180
|
+
export interface ExecutionOptions {
|
|
181
|
+
/** Model to use (overrides automatic selection) */
|
|
182
|
+
modelId?: string;
|
|
183
|
+
/** Temperature for generation */
|
|
184
|
+
temperature?: number;
|
|
185
|
+
/** Maximum tokens to generate */
|
|
186
|
+
maxTokens?: number;
|
|
187
|
+
/** Enable streaming */
|
|
188
|
+
stream?: boolean;
|
|
189
|
+
/** Stop sequences */
|
|
190
|
+
stopSequences?: string[];
|
|
191
|
+
/** Timeout in milliseconds */
|
|
192
|
+
timeout?: number;
|
|
193
|
+
/** Retry configuration */
|
|
194
|
+
retry?: {
|
|
195
|
+
maxAttempts: number;
|
|
196
|
+
backoffMs: number;
|
|
197
|
+
backoffMultiplier: number;
|
|
198
|
+
};
|
|
199
|
+
/** Metadata */
|
|
200
|
+
metadata?: Record<string, unknown>;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Execution result
|
|
205
|
+
*/
|
|
206
|
+
export interface ExecutionResult {
|
|
207
|
+
/** Success indicator */
|
|
208
|
+
success: boolean;
|
|
209
|
+
/** Output content */
|
|
210
|
+
content: string;
|
|
211
|
+
/** Provider used */
|
|
212
|
+
providerId: string;
|
|
213
|
+
/** Model used */
|
|
214
|
+
modelId: string;
|
|
215
|
+
/** Token usage */
|
|
216
|
+
usage: {
|
|
217
|
+
inputTokens: number;
|
|
218
|
+
outputTokens: number;
|
|
219
|
+
totalTokens: number;
|
|
220
|
+
};
|
|
221
|
+
/** Cost in USD */
|
|
222
|
+
cost: number;
|
|
223
|
+
/** Execution latency in milliseconds */
|
|
224
|
+
latencyMs: number;
|
|
225
|
+
/** Error if failed */
|
|
226
|
+
error?: Error;
|
|
227
|
+
/** Metadata */
|
|
228
|
+
metadata?: Record<string, unknown>;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Provider metrics
|
|
233
|
+
*/
|
|
234
|
+
export interface ProviderMetrics {
|
|
235
|
+
/** Total requests */
|
|
236
|
+
totalRequests: number;
|
|
237
|
+
/** Successful requests */
|
|
238
|
+
successfulRequests: number;
|
|
239
|
+
/** Failed requests */
|
|
240
|
+
failedRequests: number;
|
|
241
|
+
/** Average latency in milliseconds */
|
|
242
|
+
avgLatencyMs: number;
|
|
243
|
+
/** Total tokens used */
|
|
244
|
+
totalTokens: number;
|
|
245
|
+
/** Total cost in USD */
|
|
246
|
+
totalCost: number;
|
|
247
|
+
/** Last request timestamp */
|
|
248
|
+
lastRequest: number;
|
|
249
|
+
/** Uptime percentage */
|
|
250
|
+
uptimePercent: number;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Provider adapter configuration
|
|
255
|
+
*/
|
|
256
|
+
export interface ProviderAdapterConfig {
|
|
257
|
+
/** Default provider ID */
|
|
258
|
+
defaultProviderId?: string;
|
|
259
|
+
/** Default model ID */
|
|
260
|
+
defaultModelId?: string;
|
|
261
|
+
/** Enable automatic failover */
|
|
262
|
+
enableFailover?: boolean;
|
|
263
|
+
/** Maximum failover attempts */
|
|
264
|
+
maxFailoverAttempts?: number;
|
|
265
|
+
/** Enable cost tracking */
|
|
266
|
+
enableCostTracking?: boolean;
|
|
267
|
+
/** Cost limit per hour in USD */
|
|
268
|
+
costLimitPerHour?: number;
|
|
269
|
+
/** Enable provider health checks */
|
|
270
|
+
enableHealthChecks?: boolean;
|
|
271
|
+
/** Health check interval in milliseconds */
|
|
272
|
+
healthCheckInterval?: number;
|
|
273
|
+
/** Enable request caching */
|
|
274
|
+
enableCaching?: boolean;
|
|
275
|
+
/** Cache TTL in milliseconds */
|
|
276
|
+
cacheTTL?: number;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* ProviderAdapter - Multi-provider AI model management
|
|
281
|
+
*
|
|
282
|
+
* Usage:
|
|
283
|
+
* ```typescript
|
|
284
|
+
* const adapter = new ProviderAdapter({
|
|
285
|
+
* enableFailover: true,
|
|
286
|
+
* enableCostTracking: true,
|
|
287
|
+
* });
|
|
288
|
+
*
|
|
289
|
+
* // Register providers
|
|
290
|
+
* adapter.registerProvider({
|
|
291
|
+
* id: 'anthropic',
|
|
292
|
+
* name: 'Anthropic',
|
|
293
|
+
* type: 'anthropic',
|
|
294
|
+
* models: [...],
|
|
295
|
+
* capabilities: ['chat', 'code-generation'],
|
|
296
|
+
* status: 'available',
|
|
297
|
+
* rateLimits: { ... },
|
|
298
|
+
* costPerToken: { ... },
|
|
299
|
+
* });
|
|
300
|
+
*
|
|
301
|
+
* // Select provider based on requirements
|
|
302
|
+
* const result = adapter.selectProvider({
|
|
303
|
+
* capabilities: ['code-generation'],
|
|
304
|
+
* maxCostPer1K: 0.01,
|
|
305
|
+
* });
|
|
306
|
+
*
|
|
307
|
+
* // Execute task
|
|
308
|
+
* const output = await adapter.executeWithProvider(task, result.provider);
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
export class ProviderAdapter extends EventEmitter {
|
|
312
|
+
/** Registered providers */
|
|
313
|
+
providers: Map<string, Provider>;
|
|
314
|
+
|
|
315
|
+
/** Provider metrics */
|
|
316
|
+
private metrics: Map<string, ProviderMetrics>;
|
|
317
|
+
|
|
318
|
+
/** Adapter configuration */
|
|
319
|
+
private config: ProviderAdapterConfig;
|
|
320
|
+
|
|
321
|
+
/** Health check timer */
|
|
322
|
+
private healthCheckTimer: NodeJS.Timeout | null = null;
|
|
323
|
+
|
|
324
|
+
/** Request cache */
|
|
325
|
+
private cache: Map<string, { result: ExecutionResult; timestamp: number }>;
|
|
326
|
+
|
|
327
|
+
/** Hourly cost tracking */
|
|
328
|
+
private hourlyCost: { amount: number; resetAt: number };
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Create a new ProviderAdapter instance
|
|
332
|
+
*
|
|
333
|
+
* @param config - Adapter configuration
|
|
334
|
+
*/
|
|
335
|
+
constructor(config: ProviderAdapterConfig = {}) {
|
|
336
|
+
super();
|
|
337
|
+
|
|
338
|
+
this.providers = new Map();
|
|
339
|
+
this.metrics = new Map();
|
|
340
|
+
this.cache = new Map();
|
|
341
|
+
|
|
342
|
+
this.config = {
|
|
343
|
+
enableFailover: config.enableFailover ?? true,
|
|
344
|
+
maxFailoverAttempts: config.maxFailoverAttempts ?? 3,
|
|
345
|
+
enableCostTracking: config.enableCostTracking ?? true,
|
|
346
|
+
costLimitPerHour: config.costLimitPerHour ?? 10,
|
|
347
|
+
enableHealthChecks: config.enableHealthChecks ?? true,
|
|
348
|
+
healthCheckInterval: config.healthCheckInterval ?? 60000,
|
|
349
|
+
enableCaching: config.enableCaching ?? false,
|
|
350
|
+
cacheTTL: config.cacheTTL ?? 300000,
|
|
351
|
+
...config,
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
this.hourlyCost = { amount: 0, resetAt: Date.now() + 3600000 };
|
|
355
|
+
|
|
356
|
+
this.emit('adapter-created', { config: this.config });
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Initialize the adapter
|
|
361
|
+
*/
|
|
362
|
+
async initialize(): Promise<void> {
|
|
363
|
+
// Start health checks if enabled
|
|
364
|
+
if (this.config.enableHealthChecks) {
|
|
365
|
+
this.startHealthChecks();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
this.emit('adapter-initialized');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Shutdown the adapter
|
|
373
|
+
*/
|
|
374
|
+
async shutdown(): Promise<void> {
|
|
375
|
+
this.stopHealthChecks();
|
|
376
|
+
this.providers.clear();
|
|
377
|
+
this.metrics.clear();
|
|
378
|
+
this.cache.clear();
|
|
379
|
+
|
|
380
|
+
this.emit('adapter-shutdown');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Register a provider
|
|
385
|
+
*
|
|
386
|
+
* @param provider - Provider to register
|
|
387
|
+
*/
|
|
388
|
+
registerProvider(provider: Provider): void {
|
|
389
|
+
this.providers.set(provider.id, provider);
|
|
390
|
+
|
|
391
|
+
// Initialize metrics
|
|
392
|
+
this.metrics.set(provider.id, {
|
|
393
|
+
totalRequests: 0,
|
|
394
|
+
successfulRequests: 0,
|
|
395
|
+
failedRequests: 0,
|
|
396
|
+
avgLatencyMs: 0,
|
|
397
|
+
totalTokens: 0,
|
|
398
|
+
totalCost: 0,
|
|
399
|
+
lastRequest: 0,
|
|
400
|
+
uptimePercent: 100,
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
this.emit('provider-registered', { providerId: provider.id });
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Unregister a provider
|
|
408
|
+
*
|
|
409
|
+
* @param providerId - Provider ID to remove
|
|
410
|
+
*/
|
|
411
|
+
unregisterProvider(providerId: string): boolean {
|
|
412
|
+
const removed = this.providers.delete(providerId);
|
|
413
|
+
this.metrics.delete(providerId);
|
|
414
|
+
|
|
415
|
+
if (removed) {
|
|
416
|
+
this.emit('provider-unregistered', { providerId });
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return removed;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Get a provider by ID
|
|
424
|
+
*
|
|
425
|
+
* @param providerId - Provider ID
|
|
426
|
+
* @returns Provider or undefined
|
|
427
|
+
*/
|
|
428
|
+
getProvider(providerId: string): Provider | undefined {
|
|
429
|
+
return this.providers.get(providerId);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Get all registered providers
|
|
434
|
+
*/
|
|
435
|
+
getAllProviders(): Provider[] {
|
|
436
|
+
return Array.from(this.providers.values());
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Get available providers (not unavailable or rate-limited)
|
|
441
|
+
*/
|
|
442
|
+
getAvailableProviders(): Provider[] {
|
|
443
|
+
return Array.from(this.providers.values()).filter(
|
|
444
|
+
(p) => p.status === 'available' || p.status === 'degraded'
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Select the best provider based on requirements
|
|
450
|
+
*
|
|
451
|
+
* @param requirements - Selection requirements
|
|
452
|
+
* @returns Selection result with provider and model
|
|
453
|
+
*/
|
|
454
|
+
selectProvider(requirements: ProviderRequirements = {}): ProviderSelectionResult {
|
|
455
|
+
const candidates: Array<{
|
|
456
|
+
provider: Provider;
|
|
457
|
+
model: ModelInfo;
|
|
458
|
+
score: number;
|
|
459
|
+
reasons: string[];
|
|
460
|
+
}> = [];
|
|
461
|
+
|
|
462
|
+
for (const provider of this.getAvailableProviders()) {
|
|
463
|
+
// Check exclusions
|
|
464
|
+
if (requirements.excludeProviders?.includes(provider.id)) {
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Check capabilities
|
|
469
|
+
if (requirements.capabilities) {
|
|
470
|
+
const hasAllCapabilities = requirements.capabilities.every((cap) =>
|
|
471
|
+
provider.capabilities.includes(cap)
|
|
472
|
+
);
|
|
473
|
+
if (!hasAllCapabilities) {
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Check preferred types
|
|
479
|
+
if (
|
|
480
|
+
requirements.preferredTypes &&
|
|
481
|
+
!requirements.preferredTypes.includes(provider.type)
|
|
482
|
+
) {
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Check custom filters
|
|
487
|
+
if (requirements.customFilters) {
|
|
488
|
+
const passesFilters = requirements.customFilters.every((filter) =>
|
|
489
|
+
filter(provider)
|
|
490
|
+
);
|
|
491
|
+
if (!passesFilters) {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Find suitable model
|
|
497
|
+
for (const model of provider.models) {
|
|
498
|
+
// Check context length
|
|
499
|
+
if (
|
|
500
|
+
requirements.minContextLength &&
|
|
501
|
+
model.maxContextLength < requirements.minContextLength
|
|
502
|
+
) {
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Check model ID
|
|
507
|
+
if (requirements.modelId && model.id !== requirements.modelId) {
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Check streaming
|
|
512
|
+
if (requirements.streaming && !model.capabilities.includes('streaming')) {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Check vision
|
|
517
|
+
if (requirements.vision && !model.capabilities.includes('vision')) {
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Check cost
|
|
522
|
+
const avgCost =
|
|
523
|
+
(provider.costPerToken.inputPer1K + provider.costPerToken.outputPer1K) / 2;
|
|
524
|
+
if (requirements.maxCostPer1K && avgCost > requirements.maxCostPer1K) {
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Calculate score
|
|
529
|
+
const { score, reasons } = this.calculateProviderScore(
|
|
530
|
+
provider,
|
|
531
|
+
model,
|
|
532
|
+
requirements
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
candidates.push({ provider, model, score, reasons });
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (candidates.length === 0) {
|
|
540
|
+
throw new Error('No providers match the requirements');
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Sort by score
|
|
544
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
545
|
+
|
|
546
|
+
const best = candidates[0];
|
|
547
|
+
const alternatives = candidates.slice(1, 4).map(({ provider, model, score }) => ({
|
|
548
|
+
provider,
|
|
549
|
+
model,
|
|
550
|
+
score,
|
|
551
|
+
}));
|
|
552
|
+
|
|
553
|
+
this.emit('provider-selected', {
|
|
554
|
+
providerId: best.provider.id,
|
|
555
|
+
modelId: best.model.id,
|
|
556
|
+
score: best.score,
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
return {
|
|
560
|
+
provider: best.provider,
|
|
561
|
+
model: best.model,
|
|
562
|
+
score: best.score,
|
|
563
|
+
reasons: best.reasons,
|
|
564
|
+
alternatives,
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Execute a task with a specific provider
|
|
570
|
+
*
|
|
571
|
+
* @param task - Task to execute
|
|
572
|
+
* @param provider - Provider to use
|
|
573
|
+
* @param options - Execution options
|
|
574
|
+
* @returns Execution result
|
|
575
|
+
*/
|
|
576
|
+
async executeWithProvider(
|
|
577
|
+
task: Task,
|
|
578
|
+
provider: Provider,
|
|
579
|
+
options: ExecutionOptions = {}
|
|
580
|
+
): Promise<ExecutionResult> {
|
|
581
|
+
// Check cache
|
|
582
|
+
if (this.config.enableCaching) {
|
|
583
|
+
const cached = this.getCachedResult(task, provider.id);
|
|
584
|
+
if (cached) {
|
|
585
|
+
return cached;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Check cost limits
|
|
590
|
+
if (this.config.enableCostTracking) {
|
|
591
|
+
this.checkCostLimits();
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Check rate limits
|
|
595
|
+
this.checkRateLimits(provider);
|
|
596
|
+
|
|
597
|
+
const startTime = Date.now();
|
|
598
|
+
let result: ExecutionResult;
|
|
599
|
+
let attempt = 0;
|
|
600
|
+
const maxAttempts = options.retry?.maxAttempts ?? 1;
|
|
601
|
+
|
|
602
|
+
while (attempt < maxAttempts) {
|
|
603
|
+
try {
|
|
604
|
+
result = await this.executeRequest(task, provider, options);
|
|
605
|
+
|
|
606
|
+
// Update metrics on success
|
|
607
|
+
this.updateMetrics(provider.id, result);
|
|
608
|
+
|
|
609
|
+
// Update rate limits
|
|
610
|
+
this.updateRateLimits(provider, result.usage.totalTokens);
|
|
611
|
+
|
|
612
|
+
// Cache result
|
|
613
|
+
if (this.config.enableCaching) {
|
|
614
|
+
this.cacheResult(task, provider.id, result);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return result;
|
|
618
|
+
} catch (error) {
|
|
619
|
+
attempt++;
|
|
620
|
+
|
|
621
|
+
this.emit('execution-error', {
|
|
622
|
+
providerId: provider.id,
|
|
623
|
+
attempt,
|
|
624
|
+
error: error as Error,
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
if (attempt >= maxAttempts) {
|
|
628
|
+
// Try failover if enabled
|
|
629
|
+
if (this.config.enableFailover) {
|
|
630
|
+
const failoverResult = await this.tryFailover(task, provider, options);
|
|
631
|
+
if (failoverResult) {
|
|
632
|
+
return failoverResult;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Update failure metrics
|
|
637
|
+
const metrics = this.metrics.get(provider.id);
|
|
638
|
+
if (metrics) {
|
|
639
|
+
metrics.failedRequests++;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return {
|
|
643
|
+
success: false,
|
|
644
|
+
content: '',
|
|
645
|
+
providerId: provider.id,
|
|
646
|
+
modelId: options.modelId || provider.models[0]?.id || 'unknown',
|
|
647
|
+
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
648
|
+
cost: 0,
|
|
649
|
+
latencyMs: Date.now() - startTime,
|
|
650
|
+
error: error as Error,
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Wait before retry
|
|
655
|
+
const delay =
|
|
656
|
+
(options.retry?.backoffMs ?? 1000) *
|
|
657
|
+
Math.pow(options.retry?.backoffMultiplier ?? 2, attempt - 1);
|
|
658
|
+
await this.delay(delay);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
throw new Error('Execution failed after all attempts');
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Get provider metrics
|
|
667
|
+
*
|
|
668
|
+
* @param providerId - Provider ID
|
|
669
|
+
* @returns Provider metrics or undefined
|
|
670
|
+
*/
|
|
671
|
+
getProviderMetrics(providerId: string): ProviderMetrics | undefined {
|
|
672
|
+
return this.metrics.get(providerId);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Get all provider metrics
|
|
677
|
+
*/
|
|
678
|
+
getAllMetrics(): Map<string, ProviderMetrics> {
|
|
679
|
+
return new Map(this.metrics);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Update provider status
|
|
684
|
+
*
|
|
685
|
+
* @param providerId - Provider ID
|
|
686
|
+
* @param status - New status
|
|
687
|
+
*/
|
|
688
|
+
updateProviderStatus(providerId: string, status: ProviderStatus): void {
|
|
689
|
+
const provider = this.providers.get(providerId);
|
|
690
|
+
if (provider) {
|
|
691
|
+
provider.status = status;
|
|
692
|
+
|
|
693
|
+
this.emit('provider-status-changed', { providerId, status });
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// ===== Private Methods =====
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Calculate provider score for selection
|
|
701
|
+
*/
|
|
702
|
+
private calculateProviderScore(
|
|
703
|
+
provider: Provider,
|
|
704
|
+
model: ModelInfo,
|
|
705
|
+
requirements: ProviderRequirements
|
|
706
|
+
): { score: number; reasons: string[] } {
|
|
707
|
+
let score = 100;
|
|
708
|
+
const reasons: string[] = [];
|
|
709
|
+
|
|
710
|
+
// Base availability score
|
|
711
|
+
if (provider.status === 'available') {
|
|
712
|
+
score += 20;
|
|
713
|
+
reasons.push('Provider is fully available');
|
|
714
|
+
} else if (provider.status === 'degraded') {
|
|
715
|
+
score -= 10;
|
|
716
|
+
reasons.push('Provider is degraded');
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Cost efficiency
|
|
720
|
+
const avgCost =
|
|
721
|
+
(provider.costPerToken.inputPer1K + provider.costPerToken.outputPer1K) / 2;
|
|
722
|
+
if (avgCost < 0.01) {
|
|
723
|
+
score += 15;
|
|
724
|
+
reasons.push('Low cost provider');
|
|
725
|
+
} else if (avgCost > 0.05) {
|
|
726
|
+
score -= 10;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// Rate limit headroom
|
|
730
|
+
const rateHeadroom = 1 - provider.rateLimits.currentRequests / provider.rateLimits.requestsPerMinute;
|
|
731
|
+
score += rateHeadroom * 10;
|
|
732
|
+
if (rateHeadroom > 0.5) {
|
|
733
|
+
reasons.push('Good rate limit headroom');
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// Historical performance
|
|
737
|
+
const metrics = this.metrics.get(provider.id);
|
|
738
|
+
if (metrics) {
|
|
739
|
+
const successRate =
|
|
740
|
+
metrics.totalRequests > 0
|
|
741
|
+
? metrics.successfulRequests / metrics.totalRequests
|
|
742
|
+
: 1;
|
|
743
|
+
score += successRate * 20;
|
|
744
|
+
|
|
745
|
+
if (successRate > 0.95) {
|
|
746
|
+
reasons.push('High success rate');
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// Latency penalty
|
|
750
|
+
if (metrics.avgLatencyMs > 5000) {
|
|
751
|
+
score -= 15;
|
|
752
|
+
reasons.push('High latency');
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// Preferred provider bonus
|
|
757
|
+
if (requirements.preferredTypes?.includes(provider.type)) {
|
|
758
|
+
score += 10;
|
|
759
|
+
reasons.push('Preferred provider type');
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// Context length bonus
|
|
763
|
+
if (model.maxContextLength > 100000) {
|
|
764
|
+
score += 5;
|
|
765
|
+
reasons.push('Long context support');
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
return { score, reasons };
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Execute a request to the provider
|
|
773
|
+
*/
|
|
774
|
+
private async executeRequest(
|
|
775
|
+
task: Task,
|
|
776
|
+
provider: Provider,
|
|
777
|
+
options: ExecutionOptions
|
|
778
|
+
): Promise<ExecutionResult> {
|
|
779
|
+
const startTime = Date.now();
|
|
780
|
+
|
|
781
|
+
// Execute with provider-like latency (actual API calls via external integrations)
|
|
782
|
+
await this.delay(100);
|
|
783
|
+
|
|
784
|
+
const model = options.modelId
|
|
785
|
+
? provider.models.find((m) => m.id === options.modelId) || provider.models[0]
|
|
786
|
+
: provider.models[0];
|
|
787
|
+
|
|
788
|
+
const inputTokens = Math.ceil(task.description.length / 4);
|
|
789
|
+
const outputTokens = Math.ceil(inputTokens * 1.5);
|
|
790
|
+
|
|
791
|
+
const cost =
|
|
792
|
+
(inputTokens / 1000) * provider.costPerToken.inputPer1K +
|
|
793
|
+
(outputTokens / 1000) * provider.costPerToken.outputPer1K;
|
|
794
|
+
|
|
795
|
+
return {
|
|
796
|
+
success: true,
|
|
797
|
+
content: `Executed task ${task.id} with ${provider.name}`,
|
|
798
|
+
providerId: provider.id,
|
|
799
|
+
modelId: model?.id || 'default',
|
|
800
|
+
usage: {
|
|
801
|
+
inputTokens,
|
|
802
|
+
outputTokens,
|
|
803
|
+
totalTokens: inputTokens + outputTokens,
|
|
804
|
+
},
|
|
805
|
+
cost,
|
|
806
|
+
latencyMs: Date.now() - startTime,
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Try failover to alternative providers
|
|
812
|
+
*/
|
|
813
|
+
private async tryFailover(
|
|
814
|
+
task: Task,
|
|
815
|
+
failedProvider: Provider,
|
|
816
|
+
options: ExecutionOptions
|
|
817
|
+
): Promise<ExecutionResult | null> {
|
|
818
|
+
for (let attempt = 0; attempt < this.config.maxFailoverAttempts!; attempt++) {
|
|
819
|
+
try {
|
|
820
|
+
const selection = this.selectProvider({
|
|
821
|
+
excludeProviders: [failedProvider.id],
|
|
822
|
+
capabilities: failedProvider.capabilities,
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
this.emit('failover-attempt', {
|
|
826
|
+
fromProvider: failedProvider.id,
|
|
827
|
+
toProvider: selection.provider.id,
|
|
828
|
+
attempt: attempt + 1,
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
const result = await this.executeRequest(
|
|
832
|
+
task,
|
|
833
|
+
selection.provider,
|
|
834
|
+
options
|
|
835
|
+
);
|
|
836
|
+
|
|
837
|
+
this.updateMetrics(selection.provider.id, result);
|
|
838
|
+
|
|
839
|
+
this.emit('failover-success', {
|
|
840
|
+
fromProvider: failedProvider.id,
|
|
841
|
+
toProvider: selection.provider.id,
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
return result;
|
|
845
|
+
} catch (error) {
|
|
846
|
+
this.emit('failover-error', {
|
|
847
|
+
attempt: attempt + 1,
|
|
848
|
+
error: error as Error,
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
this.emit('failover-exhausted', { originalProvider: failedProvider.id });
|
|
854
|
+
return null;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/**
|
|
858
|
+
* Update provider metrics
|
|
859
|
+
*/
|
|
860
|
+
private updateMetrics(providerId: string, result: ExecutionResult): void {
|
|
861
|
+
const metrics = this.metrics.get(providerId);
|
|
862
|
+
if (!metrics) return;
|
|
863
|
+
|
|
864
|
+
metrics.totalRequests++;
|
|
865
|
+
metrics.lastRequest = Date.now();
|
|
866
|
+
|
|
867
|
+
if (result.success) {
|
|
868
|
+
metrics.successfulRequests++;
|
|
869
|
+
} else {
|
|
870
|
+
metrics.failedRequests++;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// Update average latency
|
|
874
|
+
const totalLatency = metrics.avgLatencyMs * (metrics.totalRequests - 1) + result.latencyMs;
|
|
875
|
+
metrics.avgLatencyMs = totalLatency / metrics.totalRequests;
|
|
876
|
+
|
|
877
|
+
// Update token and cost totals
|
|
878
|
+
metrics.totalTokens += result.usage.totalTokens;
|
|
879
|
+
metrics.totalCost += result.cost;
|
|
880
|
+
|
|
881
|
+
// Update hourly cost
|
|
882
|
+
if (this.config.enableCostTracking) {
|
|
883
|
+
this.hourlyCost.amount += result.cost;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/**
|
|
888
|
+
* Update rate limits after request
|
|
889
|
+
*/
|
|
890
|
+
private updateRateLimits(provider: Provider, tokensUsed: number): void {
|
|
891
|
+
provider.rateLimits.currentRequests++;
|
|
892
|
+
provider.rateLimits.currentTokens += tokensUsed;
|
|
893
|
+
|
|
894
|
+
// Check if rate limited
|
|
895
|
+
if (
|
|
896
|
+
provider.rateLimits.currentRequests >= provider.rateLimits.requestsPerMinute ||
|
|
897
|
+
provider.rateLimits.currentTokens >= provider.rateLimits.tokensPerMinute
|
|
898
|
+
) {
|
|
899
|
+
provider.status = 'rate-limited';
|
|
900
|
+
|
|
901
|
+
// Schedule reset
|
|
902
|
+
setTimeout(() => {
|
|
903
|
+
provider.rateLimits.currentRequests = 0;
|
|
904
|
+
provider.rateLimits.currentTokens = 0;
|
|
905
|
+
provider.status = 'available';
|
|
906
|
+
}, 60000);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Check rate limits before request
|
|
912
|
+
*/
|
|
913
|
+
private checkRateLimits(provider: Provider): void {
|
|
914
|
+
// Reset rate limits if needed
|
|
915
|
+
const now = Date.now();
|
|
916
|
+
if (now >= provider.rateLimits.resetAt) {
|
|
917
|
+
provider.rateLimits.currentRequests = 0;
|
|
918
|
+
provider.rateLimits.currentTokens = 0;
|
|
919
|
+
provider.rateLimits.resetAt = now + 60000;
|
|
920
|
+
if (provider.status === 'rate-limited') {
|
|
921
|
+
provider.status = 'available';
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
if (provider.status === 'rate-limited') {
|
|
926
|
+
throw new Error(`Provider ${provider.id} is rate limited`);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* Check cost limits
|
|
932
|
+
*/
|
|
933
|
+
private checkCostLimits(): void {
|
|
934
|
+
const now = Date.now();
|
|
935
|
+
|
|
936
|
+
// Reset hourly cost if needed
|
|
937
|
+
if (now >= this.hourlyCost.resetAt) {
|
|
938
|
+
this.hourlyCost.amount = 0;
|
|
939
|
+
this.hourlyCost.resetAt = now + 3600000;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
if (this.hourlyCost.amount >= this.config.costLimitPerHour!) {
|
|
943
|
+
throw new Error(
|
|
944
|
+
`Hourly cost limit exceeded: $${this.hourlyCost.amount.toFixed(2)} / $${this.config.costLimitPerHour}`
|
|
945
|
+
);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/**
|
|
950
|
+
* Get cached result
|
|
951
|
+
*/
|
|
952
|
+
private getCachedResult(
|
|
953
|
+
task: Task,
|
|
954
|
+
providerId: string
|
|
955
|
+
): ExecutionResult | null {
|
|
956
|
+
const cacheKey = `${providerId}:${task.id}:${task.description}`;
|
|
957
|
+
const cached = this.cache.get(cacheKey);
|
|
958
|
+
|
|
959
|
+
if (cached && Date.now() - cached.timestamp < this.config.cacheTTL!) {
|
|
960
|
+
this.emit('cache-hit', { taskId: task.id, providerId });
|
|
961
|
+
return cached.result;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* Cache result
|
|
969
|
+
*/
|
|
970
|
+
private cacheResult(
|
|
971
|
+
task: Task,
|
|
972
|
+
providerId: string,
|
|
973
|
+
result: ExecutionResult
|
|
974
|
+
): void {
|
|
975
|
+
const cacheKey = `${providerId}:${task.id}:${task.description}`;
|
|
976
|
+
this.cache.set(cacheKey, { result, timestamp: Date.now() });
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Start health check timer
|
|
981
|
+
*/
|
|
982
|
+
private startHealthChecks(): void {
|
|
983
|
+
this.healthCheckTimer = setInterval(() => {
|
|
984
|
+
this.performHealthChecks();
|
|
985
|
+
}, this.config.healthCheckInterval!);
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
/**
|
|
989
|
+
* Stop health check timer
|
|
990
|
+
*/
|
|
991
|
+
private stopHealthChecks(): void {
|
|
992
|
+
if (this.healthCheckTimer) {
|
|
993
|
+
clearInterval(this.healthCheckTimer);
|
|
994
|
+
this.healthCheckTimer = null;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
* Perform health checks on all providers
|
|
1000
|
+
*/
|
|
1001
|
+
private performHealthChecks(): void {
|
|
1002
|
+
for (const provider of Array.from(this.providers.values())) {
|
|
1003
|
+
const metrics = this.metrics.get(provider.id);
|
|
1004
|
+
if (!metrics) continue;
|
|
1005
|
+
|
|
1006
|
+
// Calculate uptime based on recent success rate
|
|
1007
|
+
const successRate =
|
|
1008
|
+
metrics.totalRequests > 0
|
|
1009
|
+
? metrics.successfulRequests / metrics.totalRequests
|
|
1010
|
+
: 1;
|
|
1011
|
+
metrics.uptimePercent = successRate * 100;
|
|
1012
|
+
|
|
1013
|
+
// Update provider status based on metrics
|
|
1014
|
+
if (successRate < 0.5 && metrics.totalRequests > 10) {
|
|
1015
|
+
provider.status = 'unavailable';
|
|
1016
|
+
} else if (successRate < 0.8 && metrics.totalRequests > 5) {
|
|
1017
|
+
provider.status = 'degraded';
|
|
1018
|
+
} else if (provider.status !== 'rate-limited') {
|
|
1019
|
+
provider.status = 'available';
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
this.emit('provider-health-check', {
|
|
1023
|
+
providerId: provider.id,
|
|
1024
|
+
status: provider.status,
|
|
1025
|
+
uptimePercent: metrics.uptimePercent,
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
/**
|
|
1031
|
+
* Utility delay function
|
|
1032
|
+
*/
|
|
1033
|
+
private delay(ms: number): Promise<void> {
|
|
1034
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
/**
|
|
1039
|
+
* Create a provider adapter with the given configuration
|
|
1040
|
+
*
|
|
1041
|
+
* @param config - Adapter configuration
|
|
1042
|
+
* @returns Configured ProviderAdapter
|
|
1043
|
+
*/
|
|
1044
|
+
export function createProviderAdapter(
|
|
1045
|
+
config: ProviderAdapterConfig = {}
|
|
1046
|
+
): ProviderAdapter {
|
|
1047
|
+
return new ProviderAdapter(config);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/**
|
|
1051
|
+
* Create default provider configurations for common providers
|
|
1052
|
+
*/
|
|
1053
|
+
export function createDefaultProviders(): Provider[] {
|
|
1054
|
+
return [
|
|
1055
|
+
{
|
|
1056
|
+
id: 'anthropic-claude',
|
|
1057
|
+
name: 'Anthropic Claude',
|
|
1058
|
+
type: 'anthropic',
|
|
1059
|
+
models: [
|
|
1060
|
+
{
|
|
1061
|
+
id: 'claude-3-opus-20240229',
|
|
1062
|
+
name: 'Claude 3 Opus',
|
|
1063
|
+
maxContextLength: 200000,
|
|
1064
|
+
maxOutputTokens: 4096,
|
|
1065
|
+
capabilities: ['chat', 'code-generation', 'vision', 'long-context'],
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
id: 'claude-3-sonnet-20240229',
|
|
1069
|
+
name: 'Claude 3 Sonnet',
|
|
1070
|
+
maxContextLength: 200000,
|
|
1071
|
+
maxOutputTokens: 4096,
|
|
1072
|
+
capabilities: ['chat', 'code-generation', 'vision', 'long-context'],
|
|
1073
|
+
},
|
|
1074
|
+
{
|
|
1075
|
+
id: 'claude-3-5-sonnet-20241022',
|
|
1076
|
+
name: 'Claude 3.5 Sonnet',
|
|
1077
|
+
maxContextLength: 200000,
|
|
1078
|
+
maxOutputTokens: 8192,
|
|
1079
|
+
capabilities: ['chat', 'code-generation', 'vision', 'long-context', 'streaming'],
|
|
1080
|
+
},
|
|
1081
|
+
],
|
|
1082
|
+
capabilities: ['chat', 'code-generation', 'vision', 'streaming', 'long-context'],
|
|
1083
|
+
status: 'available',
|
|
1084
|
+
rateLimits: {
|
|
1085
|
+
requestsPerMinute: 60,
|
|
1086
|
+
tokensPerMinute: 100000,
|
|
1087
|
+
currentRequests: 0,
|
|
1088
|
+
currentTokens: 0,
|
|
1089
|
+
resetAt: Date.now() + 60000,
|
|
1090
|
+
},
|
|
1091
|
+
costPerToken: {
|
|
1092
|
+
inputPer1K: 0.015,
|
|
1093
|
+
outputPer1K: 0.075,
|
|
1094
|
+
currency: 'USD',
|
|
1095
|
+
},
|
|
1096
|
+
},
|
|
1097
|
+
{
|
|
1098
|
+
id: 'openai-gpt4',
|
|
1099
|
+
name: 'OpenAI GPT-4',
|
|
1100
|
+
type: 'openai',
|
|
1101
|
+
models: [
|
|
1102
|
+
{
|
|
1103
|
+
id: 'gpt-4-turbo',
|
|
1104
|
+
name: 'GPT-4 Turbo',
|
|
1105
|
+
maxContextLength: 128000,
|
|
1106
|
+
maxOutputTokens: 4096,
|
|
1107
|
+
capabilities: ['chat', 'code-generation', 'vision', 'function-calling'],
|
|
1108
|
+
},
|
|
1109
|
+
{
|
|
1110
|
+
id: 'gpt-4o',
|
|
1111
|
+
name: 'GPT-4o',
|
|
1112
|
+
maxContextLength: 128000,
|
|
1113
|
+
maxOutputTokens: 4096,
|
|
1114
|
+
capabilities: ['chat', 'code-generation', 'vision', 'function-calling', 'streaming'],
|
|
1115
|
+
},
|
|
1116
|
+
],
|
|
1117
|
+
capabilities: ['chat', 'code-generation', 'vision', 'function-calling', 'streaming'],
|
|
1118
|
+
status: 'available',
|
|
1119
|
+
rateLimits: {
|
|
1120
|
+
requestsPerMinute: 500,
|
|
1121
|
+
tokensPerMinute: 150000,
|
|
1122
|
+
currentRequests: 0,
|
|
1123
|
+
currentTokens: 0,
|
|
1124
|
+
resetAt: Date.now() + 60000,
|
|
1125
|
+
},
|
|
1126
|
+
costPerToken: {
|
|
1127
|
+
inputPer1K: 0.01,
|
|
1128
|
+
outputPer1K: 0.03,
|
|
1129
|
+
currency: 'USD',
|
|
1130
|
+
},
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
id: 'ollama-local',
|
|
1134
|
+
name: 'Ollama Local',
|
|
1135
|
+
type: 'ollama',
|
|
1136
|
+
models: [
|
|
1137
|
+
{
|
|
1138
|
+
id: 'llama3',
|
|
1139
|
+
name: 'Llama 3',
|
|
1140
|
+
maxContextLength: 8192,
|
|
1141
|
+
maxOutputTokens: 2048,
|
|
1142
|
+
capabilities: ['chat', 'code-generation'],
|
|
1143
|
+
},
|
|
1144
|
+
{
|
|
1145
|
+
id: 'codellama',
|
|
1146
|
+
name: 'Code Llama',
|
|
1147
|
+
maxContextLength: 16384,
|
|
1148
|
+
maxOutputTokens: 2048,
|
|
1149
|
+
capabilities: ['chat', 'code-generation'],
|
|
1150
|
+
},
|
|
1151
|
+
],
|
|
1152
|
+
capabilities: ['chat', 'code-generation'],
|
|
1153
|
+
status: 'available',
|
|
1154
|
+
rateLimits: {
|
|
1155
|
+
requestsPerMinute: 1000,
|
|
1156
|
+
tokensPerMinute: 1000000,
|
|
1157
|
+
currentRequests: 0,
|
|
1158
|
+
currentTokens: 0,
|
|
1159
|
+
resetAt: Date.now() + 60000,
|
|
1160
|
+
},
|
|
1161
|
+
costPerToken: {
|
|
1162
|
+
inputPer1K: 0,
|
|
1163
|
+
outputPer1K: 0,
|
|
1164
|
+
currency: 'USD',
|
|
1165
|
+
},
|
|
1166
|
+
},
|
|
1167
|
+
];
|
|
1168
|
+
}
|