@probelabs/probe 0.6.0-rc154 → 0.6.0-rc161
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 +80 -1
- package/build/agent/FallbackManager.d.ts +176 -0
- package/build/agent/FallbackManager.js +545 -0
- package/build/agent/ProbeAgent.d.ts +9 -1
- package/build/agent/ProbeAgent.js +218 -10
- package/build/agent/RetryManager.d.ts +157 -0
- package/build/agent/RetryManager.js +334 -0
- package/build/agent/acp/server.js +1 -0
- package/build/agent/acp/tools.js +6 -2
- package/build/agent/index.js +1814 -355
- package/build/agent/probeTool.js +20 -2
- package/build/agent/tools.js +16 -0
- package/build/delegate.js +326 -201
- package/build/downloader.js +46 -17
- package/build/extractor.js +12 -12
- package/build/index.js +13 -0
- package/build/tools/common.js +5 -3
- package/build/tools/edit.js +409 -0
- package/build/tools/index.js +11 -0
- package/build/tools/vercel.js +55 -14
- package/build/utils.js +18 -9
- package/cjs/agent/ProbeAgent.cjs +2268 -699
- package/cjs/index.cjs +75902 -74348
- package/package.json +2 -2
- package/src/agent/FallbackManager.d.ts +176 -0
- package/src/agent/FallbackManager.js +545 -0
- package/src/agent/ProbeAgent.d.ts +9 -1
- package/src/agent/ProbeAgent.js +218 -10
- package/src/agent/RetryManager.d.ts +157 -0
- package/src/agent/RetryManager.js +334 -0
- package/src/agent/acp/server.js +1 -0
- package/src/agent/acp/tools.js +6 -2
- package/src/agent/index.js +8 -0
- package/src/agent/probeTool.js +20 -2
- package/src/agent/tools.js +16 -0
- package/src/delegate.js +326 -201
- package/src/downloader.js +46 -17
- package/src/extractor.js +12 -12
- package/src/index.js +13 -0
- package/src/tools/common.js +5 -3
- package/src/tools/edit.js +409 -0
- package/src/tools/index.js +11 -0
- package/src/tools/vercel.js +55 -14
- package/src/utils.js +18 -9
- package/bin/binaries/probe-v0.6.0-rc154-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc154-aarch64-unknown-linux-gnu.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc154-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc154-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc154-x86_64-unknown-linux-gnu.tar.gz +0 -0
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FallbackManager - Handles provider and model fallback configuration
|
|
3
|
+
*
|
|
4
|
+
* Provides flexible fallback strategies for AI API calls:
|
|
5
|
+
* - Same model across different providers (Azure Claude → Bedrock Claude)
|
|
6
|
+
* - Different models on same provider (Claude 3.7 → Claude 3.5)
|
|
7
|
+
* - Cross-provider fallback (Anthropic → OpenAI → Google)
|
|
8
|
+
* - Custom fallback chains with full configuration
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
12
|
+
import { createOpenAI } from '@ai-sdk/openai';
|
|
13
|
+
import { createGoogleGenerativeAI } from '@ai-sdk/google';
|
|
14
|
+
import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Fallback strategies
|
|
18
|
+
*/
|
|
19
|
+
export const FALLBACK_STRATEGIES = {
|
|
20
|
+
SAME_MODEL: 'same-model', // Try same model on different providers
|
|
21
|
+
SAME_PROVIDER: 'same-provider', // Try different models on same provider
|
|
22
|
+
ANY: 'any', // Try any available provider/model
|
|
23
|
+
CUSTOM: 'custom' // Use custom provider list
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Provider configuration schema
|
|
28
|
+
* @typedef {Object} ProviderConfig
|
|
29
|
+
* @property {string} provider - Provider name: 'anthropic', 'openai', 'google', 'bedrock'
|
|
30
|
+
* @property {string} [model] - Model name
|
|
31
|
+
* @property {string} [apiKey] - API key
|
|
32
|
+
* @property {string} [baseURL] - Custom API endpoint
|
|
33
|
+
* @property {number} [maxRetries] - Max retries for this provider (overrides global)
|
|
34
|
+
* @property {string} [region] - AWS region (for Bedrock)
|
|
35
|
+
* @property {string} [accessKeyId] - AWS access key ID (for Bedrock)
|
|
36
|
+
* @property {string} [secretAccessKey] - AWS secret access key (for Bedrock)
|
|
37
|
+
* @property {string} [sessionToken] - AWS session token (for Bedrock)
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Default model mappings for each provider
|
|
42
|
+
*/
|
|
43
|
+
const DEFAULT_MODELS = {
|
|
44
|
+
anthropic: 'claude-sonnet-4-5-20250929',
|
|
45
|
+
openai: 'gpt-4o',
|
|
46
|
+
google: 'gemini-2.0-flash-exp',
|
|
47
|
+
bedrock: 'anthropic.claude-sonnet-4-20250514-v1:0'
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* FallbackManager class for handling provider and model fallback
|
|
52
|
+
*/
|
|
53
|
+
export class FallbackManager {
|
|
54
|
+
/**
|
|
55
|
+
* Create a new FallbackManager
|
|
56
|
+
* @param {Object} options - Configuration options
|
|
57
|
+
* @param {string} [options.strategy='any'] - Fallback strategy
|
|
58
|
+
* @param {Array<string>} [options.models] - List of models for same-provider fallback
|
|
59
|
+
* @param {Array<ProviderConfig>} [options.providers] - List of provider configurations
|
|
60
|
+
* @param {boolean} [options.stopOnSuccess=true] - Stop on first success
|
|
61
|
+
* @param {boolean} [options.continueOnNonRetryableError=false] - Continue to fallback on non-retryable errors
|
|
62
|
+
* @param {number} [options.maxTotalAttempts=10] - Maximum total attempts across all providers
|
|
63
|
+
* @param {boolean} [options.debug=false] - Enable debug logging
|
|
64
|
+
*/
|
|
65
|
+
constructor(options = {}) {
|
|
66
|
+
this.strategy = options.strategy || FALLBACK_STRATEGIES.ANY;
|
|
67
|
+
this.models = Array.isArray(options.models) ? options.models : [];
|
|
68
|
+
this.providers = Array.isArray(options.providers) ? options.providers : [];
|
|
69
|
+
this.stopOnSuccess = options.stopOnSuccess ?? true;
|
|
70
|
+
this.continueOnNonRetryableError = options.continueOnNonRetryableError ?? false;
|
|
71
|
+
this.debug = options.debug ?? false;
|
|
72
|
+
|
|
73
|
+
// Validate maxTotalAttempts
|
|
74
|
+
const maxAttempts = options.maxTotalAttempts ?? 10;
|
|
75
|
+
if (typeof maxAttempts !== 'number' || isNaN(maxAttempts) || maxAttempts < 1 || maxAttempts > 100) {
|
|
76
|
+
throw new Error(`FallbackManager: maxTotalAttempts must be a number between 1 and 100, got: ${maxAttempts}`);
|
|
77
|
+
}
|
|
78
|
+
this.maxTotalAttempts = maxAttempts;
|
|
79
|
+
|
|
80
|
+
// Statistics
|
|
81
|
+
this.stats = {
|
|
82
|
+
totalAttempts: 0,
|
|
83
|
+
providerAttempts: {},
|
|
84
|
+
successfulProvider: null,
|
|
85
|
+
failedProviders: []
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Validate configuration
|
|
89
|
+
this._validateConfiguration();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Validate the fallback configuration
|
|
94
|
+
* @private
|
|
95
|
+
*/
|
|
96
|
+
_validateConfiguration() {
|
|
97
|
+
if (this.strategy === FALLBACK_STRATEGIES.SAME_PROVIDER && this.models.length === 0) {
|
|
98
|
+
throw new Error('FallbackManager: strategy "same-provider" requires models list');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (this.strategy === FALLBACK_STRATEGIES.CUSTOM && this.providers.length === 0) {
|
|
102
|
+
throw new Error('FallbackManager: strategy "custom" requires providers list');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Validate provider configurations
|
|
106
|
+
for (const config of this.providers) {
|
|
107
|
+
if (!config.provider) {
|
|
108
|
+
throw new Error('FallbackManager: Each provider config must have a "provider" field');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!['anthropic', 'openai', 'google', 'bedrock'].includes(config.provider)) {
|
|
112
|
+
throw new Error(`FallbackManager: Invalid provider "${config.provider}". Must be: anthropic, openai, google, or bedrock`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Validate Bedrock configuration
|
|
116
|
+
if (config.provider === 'bedrock') {
|
|
117
|
+
const hasCredentials = config.accessKeyId && config.secretAccessKey && config.region;
|
|
118
|
+
const hasApiKey = config.apiKey;
|
|
119
|
+
|
|
120
|
+
if (!hasCredentials && !hasApiKey) {
|
|
121
|
+
throw new Error('FallbackManager: Bedrock provider requires either (accessKeyId, secretAccessKey, region) or apiKey');
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
// Other providers require apiKey
|
|
125
|
+
if (!config.apiKey) {
|
|
126
|
+
throw new Error(`FallbackManager: Provider "${config.provider}" requires apiKey`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Create a provider instance from configuration
|
|
134
|
+
* @param {ProviderConfig} config - Provider configuration
|
|
135
|
+
* @returns {Object} - Provider instance
|
|
136
|
+
* @throws {Error} - If provider creation fails
|
|
137
|
+
* @private
|
|
138
|
+
*/
|
|
139
|
+
_createProviderInstance(config) {
|
|
140
|
+
try {
|
|
141
|
+
switch (config.provider) {
|
|
142
|
+
case 'anthropic':
|
|
143
|
+
return createAnthropic({
|
|
144
|
+
apiKey: config.apiKey,
|
|
145
|
+
...(config.baseURL && { baseURL: config.baseURL })
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
case 'openai':
|
|
149
|
+
return createOpenAI({
|
|
150
|
+
compatibility: 'strict',
|
|
151
|
+
apiKey: config.apiKey,
|
|
152
|
+
...(config.baseURL && { baseURL: config.baseURL })
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
case 'google':
|
|
156
|
+
return createGoogleGenerativeAI({
|
|
157
|
+
apiKey: config.apiKey,
|
|
158
|
+
...(config.baseURL && { baseURL: config.baseURL })
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
case 'bedrock': {
|
|
162
|
+
const bedrockConfig = {};
|
|
163
|
+
|
|
164
|
+
if (config.apiKey) {
|
|
165
|
+
bedrockConfig.apiKey = config.apiKey;
|
|
166
|
+
} else if (config.accessKeyId && config.secretAccessKey) {
|
|
167
|
+
bedrockConfig.accessKeyId = config.accessKeyId;
|
|
168
|
+
bedrockConfig.secretAccessKey = config.secretAccessKey;
|
|
169
|
+
if (config.sessionToken) {
|
|
170
|
+
bedrockConfig.sessionToken = config.sessionToken;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (config.region) {
|
|
175
|
+
bedrockConfig.region = config.region;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (config.baseURL) {
|
|
179
|
+
bedrockConfig.baseURL = config.baseURL;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return createAmazonBedrock(bedrockConfig);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
default:
|
|
186
|
+
throw new Error(`FallbackManager: Unknown provider "${config.provider}"`);
|
|
187
|
+
}
|
|
188
|
+
} catch (error) {
|
|
189
|
+
// Re-throw with more context
|
|
190
|
+
const providerName = this._getProviderDisplayName(config);
|
|
191
|
+
throw new Error(`Failed to create provider instance for ${providerName}: ${error.message}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Get the model name for a provider configuration
|
|
197
|
+
* @param {ProviderConfig} config - Provider configuration
|
|
198
|
+
* @returns {string} - Model name
|
|
199
|
+
* @private
|
|
200
|
+
*/
|
|
201
|
+
_getModelName(config) {
|
|
202
|
+
return config.model || DEFAULT_MODELS[config.provider];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Get provider display name for logging
|
|
207
|
+
* @param {ProviderConfig} config - Provider configuration
|
|
208
|
+
* @returns {string} - Display name
|
|
209
|
+
* @private
|
|
210
|
+
*/
|
|
211
|
+
_getProviderDisplayName(config) {
|
|
212
|
+
const model = this._getModelName(config);
|
|
213
|
+
const provider = config.provider;
|
|
214
|
+
const url = config.baseURL ? ` (${config.baseURL})` : '';
|
|
215
|
+
return `${provider}/${model}${url}`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Execute a function with fallback support
|
|
220
|
+
* @param {Function} fn - Function that takes (provider, model, config) and returns a Promise
|
|
221
|
+
* @returns {Promise<*>} - Result from the function
|
|
222
|
+
* @throws {Error} - If all fallbacks are exhausted
|
|
223
|
+
*/
|
|
224
|
+
async executeWithFallback(fn) {
|
|
225
|
+
if (this.providers.length === 0) {
|
|
226
|
+
throw new Error('FallbackManager: No providers configured for fallback');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let lastError = null;
|
|
230
|
+
let totalAttempts = 0;
|
|
231
|
+
|
|
232
|
+
for (const config of this.providers) {
|
|
233
|
+
if (totalAttempts >= this.maxTotalAttempts) {
|
|
234
|
+
if (this.debug) {
|
|
235
|
+
console.log(`[FallbackManager] ⚠️ Max total attempts (${this.maxTotalAttempts}) reached`);
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
totalAttempts++;
|
|
241
|
+
this.stats.totalAttempts++;
|
|
242
|
+
|
|
243
|
+
const providerName = this._getProviderDisplayName(config);
|
|
244
|
+
this.stats.providerAttempts[providerName] = (this.stats.providerAttempts[providerName] || 0) + 1;
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
if (this.debug) {
|
|
248
|
+
console.log(`[FallbackManager] Attempting provider: ${providerName} (attempt ${totalAttempts}/${this.maxTotalAttempts})`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const provider = this._createProviderInstance(config);
|
|
252
|
+
const model = this._getModelName(config);
|
|
253
|
+
|
|
254
|
+
const result = await fn(provider, model, config);
|
|
255
|
+
|
|
256
|
+
// Success!
|
|
257
|
+
this.stats.successfulProvider = providerName;
|
|
258
|
+
|
|
259
|
+
if (this.debug) {
|
|
260
|
+
console.log(`[FallbackManager] ✅ Success with provider: ${providerName}`);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return result;
|
|
264
|
+
|
|
265
|
+
} catch (error) {
|
|
266
|
+
lastError = error;
|
|
267
|
+
const errorInfo = {
|
|
268
|
+
message: error.message || error.toString(),
|
|
269
|
+
type: error.type || error.constructor.name,
|
|
270
|
+
statusCode: error.statusCode || error.status
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
this.stats.failedProviders.push({
|
|
274
|
+
provider: providerName,
|
|
275
|
+
error: errorInfo
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
if (this.debug) {
|
|
279
|
+
console.log(`[FallbackManager] ❌ Failed with provider: ${providerName}`, errorInfo);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Check if we should continue to next provider
|
|
283
|
+
// If error is not retryable and continueOnNonRetryableError is false, stop
|
|
284
|
+
if (!this.continueOnNonRetryableError && error.nonRetryable) {
|
|
285
|
+
if (this.debug) {
|
|
286
|
+
console.log(`[FallbackManager] Non-retryable error, stopping fallback chain`);
|
|
287
|
+
}
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Continue to next provider
|
|
292
|
+
if (this.debug) {
|
|
293
|
+
const remaining = this.providers.length - (this.providers.indexOf(config) + 1);
|
|
294
|
+
console.log(`[FallbackManager] Trying next provider (${remaining} remaining)...`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// All providers failed
|
|
300
|
+
if (this.debug) {
|
|
301
|
+
console.log(`[FallbackManager] ❌ All providers exhausted. Total attempts: ${totalAttempts}`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Enhance error with fallback context
|
|
305
|
+
const fallbackError = new Error(
|
|
306
|
+
`All provider fallbacks exhausted after ${totalAttempts} attempts. Last error: ${lastError?.message || 'Unknown error'}`
|
|
307
|
+
);
|
|
308
|
+
fallbackError.cause = lastError;
|
|
309
|
+
fallbackError.stats = this.getStats();
|
|
310
|
+
fallbackError.allProvidersFailed = true;
|
|
311
|
+
|
|
312
|
+
throw fallbackError;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Get fallback statistics
|
|
317
|
+
* @returns {Object} - Statistics object
|
|
318
|
+
*/
|
|
319
|
+
getStats() {
|
|
320
|
+
return {
|
|
321
|
+
...this.stats,
|
|
322
|
+
providerAttempts: { ...this.stats.providerAttempts },
|
|
323
|
+
failedProviders: [...this.stats.failedProviders]
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Reset statistics
|
|
329
|
+
*/
|
|
330
|
+
resetStats() {
|
|
331
|
+
this.stats = {
|
|
332
|
+
totalAttempts: 0,
|
|
333
|
+
providerAttempts: {},
|
|
334
|
+
successfulProvider: null,
|
|
335
|
+
failedProviders: []
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Create a FallbackManager from environment variables
|
|
342
|
+
* @param {boolean} [debug=false] - Enable debug logging
|
|
343
|
+
* @returns {FallbackManager|null} - Configured FallbackManager instance or null if no fallback config
|
|
344
|
+
*/
|
|
345
|
+
export function createFallbackManagerFromEnv(debug = false) {
|
|
346
|
+
const fallbackProvidersEnv = process.env.FALLBACK_PROVIDERS;
|
|
347
|
+
const fallbackModelsEnv = process.env.FALLBACK_MODELS;
|
|
348
|
+
|
|
349
|
+
// If no fallback configuration, return null
|
|
350
|
+
if (!fallbackProvidersEnv && !fallbackModelsEnv) {
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
let providers = [];
|
|
355
|
+
let models = [];
|
|
356
|
+
let strategy = FALLBACK_STRATEGIES.ANY;
|
|
357
|
+
|
|
358
|
+
// Parse providers configuration
|
|
359
|
+
if (fallbackProvidersEnv) {
|
|
360
|
+
try {
|
|
361
|
+
// Validate input is a string and not suspiciously long
|
|
362
|
+
if (typeof fallbackProvidersEnv !== 'string' || fallbackProvidersEnv.length > 10000) {
|
|
363
|
+
console.error('[FallbackManager] FALLBACK_PROVIDERS must be a valid JSON string under 10KB');
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const parsed = JSON.parse(fallbackProvidersEnv);
|
|
368
|
+
|
|
369
|
+
// Validate parsed result is an array
|
|
370
|
+
if (!Array.isArray(parsed)) {
|
|
371
|
+
console.error('[FallbackManager] FALLBACK_PROVIDERS must be a JSON array');
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
providers = parsed;
|
|
376
|
+
strategy = FALLBACK_STRATEGIES.CUSTOM;
|
|
377
|
+
} catch (error) {
|
|
378
|
+
console.error('[FallbackManager] Failed to parse FALLBACK_PROVIDERS:', error.message);
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Parse models configuration
|
|
384
|
+
if (fallbackModelsEnv) {
|
|
385
|
+
try {
|
|
386
|
+
// Validate input is a string and not suspiciously long
|
|
387
|
+
if (typeof fallbackModelsEnv !== 'string' || fallbackModelsEnv.length > 10000) {
|
|
388
|
+
console.error('[FallbackManager] FALLBACK_MODELS must be a valid JSON string under 10KB');
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const parsed = JSON.parse(fallbackModelsEnv);
|
|
393
|
+
|
|
394
|
+
// Validate parsed result is an array
|
|
395
|
+
if (!Array.isArray(parsed)) {
|
|
396
|
+
console.error('[FallbackManager] FALLBACK_MODELS must be a JSON array');
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
models = parsed;
|
|
401
|
+
strategy = FALLBACK_STRATEGIES.SAME_PROVIDER;
|
|
402
|
+
} catch (error) {
|
|
403
|
+
console.error('[FallbackManager] Failed to parse FALLBACK_MODELS:', error.message);
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const maxTotalAttempts = process.env.FALLBACK_MAX_TOTAL_ATTEMPTS
|
|
409
|
+
? (() => {
|
|
410
|
+
const val = parseInt(process.env.FALLBACK_MAX_TOTAL_ATTEMPTS, 10);
|
|
411
|
+
if (isNaN(val) || val < 1 || val > 100) {
|
|
412
|
+
console.warn('[FallbackManager] FALLBACK_MAX_TOTAL_ATTEMPTS must be between 1 and 100, using default: 10');
|
|
413
|
+
return 10;
|
|
414
|
+
}
|
|
415
|
+
return val;
|
|
416
|
+
})()
|
|
417
|
+
: 10;
|
|
418
|
+
|
|
419
|
+
return new FallbackManager({
|
|
420
|
+
strategy,
|
|
421
|
+
providers,
|
|
422
|
+
models,
|
|
423
|
+
maxTotalAttempts,
|
|
424
|
+
debug
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Build a fallback provider list from current environment
|
|
430
|
+
* @param {Object} options - Options for building the list
|
|
431
|
+
* @param {string} [options.primaryProvider] - Primary provider to try first
|
|
432
|
+
* @param {string} [options.primaryModel] - Primary model to use
|
|
433
|
+
* @returns {Array<ProviderConfig>} - List of provider configurations
|
|
434
|
+
*/
|
|
435
|
+
export function buildFallbackProvidersFromEnv(options = {}) {
|
|
436
|
+
const providers = [];
|
|
437
|
+
|
|
438
|
+
// Get all available API keys from environment
|
|
439
|
+
const anthropicApiKey = process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN;
|
|
440
|
+
const openaiApiKey = process.env.OPENAI_API_KEY;
|
|
441
|
+
const googleApiKey = process.env.GOOGLE_GENERATIVE_AI_API_KEY || process.env.GOOGLE_API_KEY;
|
|
442
|
+
const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID;
|
|
443
|
+
const awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
|
|
444
|
+
const awsRegion = process.env.AWS_REGION;
|
|
445
|
+
const awsApiKey = process.env.AWS_BEDROCK_API_KEY;
|
|
446
|
+
|
|
447
|
+
// Get custom URLs
|
|
448
|
+
const llmBaseUrl = process.env.LLM_BASE_URL;
|
|
449
|
+
const anthropicApiUrl = process.env.ANTHROPIC_API_URL || process.env.ANTHROPIC_BASE_URL || llmBaseUrl;
|
|
450
|
+
const openaiApiUrl = process.env.OPENAI_API_URL || llmBaseUrl;
|
|
451
|
+
const googleApiUrl = process.env.GOOGLE_API_URL || llmBaseUrl;
|
|
452
|
+
const awsBedrockBaseUrl = process.env.AWS_BEDROCK_BASE_URL || llmBaseUrl;
|
|
453
|
+
|
|
454
|
+
// Build primary provider config
|
|
455
|
+
const primaryProvider = options.primaryProvider?.toLowerCase();
|
|
456
|
+
const primaryModel = options.primaryModel;
|
|
457
|
+
|
|
458
|
+
// Add primary provider first if specified
|
|
459
|
+
if (primaryProvider === 'anthropic' && anthropicApiKey) {
|
|
460
|
+
providers.push({
|
|
461
|
+
provider: 'anthropic',
|
|
462
|
+
apiKey: anthropicApiKey,
|
|
463
|
+
...(anthropicApiUrl && { baseURL: anthropicApiUrl }),
|
|
464
|
+
...(primaryModel && { model: primaryModel })
|
|
465
|
+
});
|
|
466
|
+
} else if (primaryProvider === 'openai' && openaiApiKey) {
|
|
467
|
+
providers.push({
|
|
468
|
+
provider: 'openai',
|
|
469
|
+
apiKey: openaiApiKey,
|
|
470
|
+
...(openaiApiUrl && { baseURL: openaiApiUrl }),
|
|
471
|
+
...(primaryModel && { model: primaryModel })
|
|
472
|
+
});
|
|
473
|
+
} else if (primaryProvider === 'google' && googleApiKey) {
|
|
474
|
+
providers.push({
|
|
475
|
+
provider: 'google',
|
|
476
|
+
apiKey: googleApiKey,
|
|
477
|
+
...(googleApiUrl && { baseURL: googleApiUrl }),
|
|
478
|
+
...(primaryModel && { model: primaryModel })
|
|
479
|
+
});
|
|
480
|
+
} else if (primaryProvider === 'bedrock' && ((awsAccessKeyId && awsSecretAccessKey && awsRegion) || awsApiKey)) {
|
|
481
|
+
const config = { provider: 'bedrock' };
|
|
482
|
+
|
|
483
|
+
if (awsApiKey) {
|
|
484
|
+
config.apiKey = awsApiKey;
|
|
485
|
+
} else {
|
|
486
|
+
config.accessKeyId = awsAccessKeyId;
|
|
487
|
+
config.secretAccessKey = awsSecretAccessKey;
|
|
488
|
+
config.region = awsRegion;
|
|
489
|
+
if (process.env.AWS_SESSION_TOKEN) {
|
|
490
|
+
config.sessionToken = process.env.AWS_SESSION_TOKEN;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (awsBedrockBaseUrl) config.baseURL = awsBedrockBaseUrl;
|
|
495
|
+
if (primaryModel) config.model = primaryModel;
|
|
496
|
+
|
|
497
|
+
providers.push(config);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Add remaining available providers as fallbacks
|
|
501
|
+
if (anthropicApiKey && primaryProvider !== 'anthropic') {
|
|
502
|
+
providers.push({
|
|
503
|
+
provider: 'anthropic',
|
|
504
|
+
apiKey: anthropicApiKey,
|
|
505
|
+
...(anthropicApiUrl && { baseURL: anthropicApiUrl })
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (openaiApiKey && primaryProvider !== 'openai') {
|
|
510
|
+
providers.push({
|
|
511
|
+
provider: 'openai',
|
|
512
|
+
apiKey: openaiApiKey,
|
|
513
|
+
...(openaiApiUrl && { baseURL: openaiApiUrl })
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (googleApiKey && primaryProvider !== 'google') {
|
|
518
|
+
providers.push({
|
|
519
|
+
provider: 'google',
|
|
520
|
+
apiKey: googleApiKey,
|
|
521
|
+
...(googleApiUrl && { baseURL: googleApiUrl })
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (((awsAccessKeyId && awsSecretAccessKey && awsRegion) || awsApiKey) && primaryProvider !== 'bedrock') {
|
|
526
|
+
const config = { provider: 'bedrock' };
|
|
527
|
+
|
|
528
|
+
if (awsApiKey) {
|
|
529
|
+
config.apiKey = awsApiKey;
|
|
530
|
+
} else {
|
|
531
|
+
config.accessKeyId = awsAccessKeyId;
|
|
532
|
+
config.secretAccessKey = awsSecretAccessKey;
|
|
533
|
+
config.region = awsRegion;
|
|
534
|
+
if (process.env.AWS_SESSION_TOKEN) {
|
|
535
|
+
config.sessionToken = process.env.AWS_SESSION_TOKEN;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (awsBedrockBaseUrl) config.baseURL = awsBedrockBaseUrl;
|
|
540
|
+
|
|
541
|
+
providers.push(config);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return providers;
|
|
545
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// TypeScript definitions for ProbeAgent class
|
|
2
2
|
import { EventEmitter } from 'events';
|
|
3
|
+
import type { RetryOptions } from './RetryManager';
|
|
4
|
+
import type { FallbackOptions } from './FallbackManager';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Configuration options for creating a ProbeAgent instance
|
|
@@ -13,10 +15,12 @@ export interface ProbeAgentOptions {
|
|
|
13
15
|
promptType?: 'code-explorer' | 'engineer' | 'code-review' | 'support' | 'architect';
|
|
14
16
|
/** Allow the use of the 'implement' tool for code editing */
|
|
15
17
|
allowEdit?: boolean;
|
|
18
|
+
/** Enable the delegate tool for task distribution to subagents */
|
|
19
|
+
enableDelegate?: boolean;
|
|
16
20
|
/** Search directory path */
|
|
17
21
|
path?: string;
|
|
18
22
|
/** Force specific AI provider */
|
|
19
|
-
provider?: 'anthropic' | 'openai' | 'google';
|
|
23
|
+
provider?: 'anthropic' | 'openai' | 'google' | 'bedrock';
|
|
20
24
|
/** Override model name */
|
|
21
25
|
model?: string;
|
|
22
26
|
/** Enable debug mode */
|
|
@@ -31,6 +35,10 @@ export interface ProbeAgentOptions {
|
|
|
31
35
|
mcpConfig?: any;
|
|
32
36
|
/** @deprecated Use mcpConfig instead */
|
|
33
37
|
mcpServers?: any[];
|
|
38
|
+
/** Retry configuration for handling transient API failures */
|
|
39
|
+
retry?: RetryOptions;
|
|
40
|
+
/** Fallback configuration for multi-provider support */
|
|
41
|
+
fallback?: FallbackOptions | { auto: boolean };
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
/**
|