@memberjunction/ai-prompts 5.0.0 → 5.1.0
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ai-prompts",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.1.0",
|
|
5
5
|
"description": "MemberJunction: AI Prompt Execution and Management",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -18,16 +18,16 @@
|
|
|
18
18
|
"author": "MemberJunction.com",
|
|
19
19
|
"license": "ISC",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@memberjunction/ai": "5.
|
|
22
|
-
"@memberjunction/ai-core-plus": "5.
|
|
23
|
-
"@memberjunction/ai-engine-base": "5.
|
|
24
|
-
"@memberjunction/aiengine": "5.
|
|
25
|
-
"@memberjunction/core": "5.
|
|
26
|
-
"@memberjunction/core-entities": "5.
|
|
27
|
-
"@memberjunction/credentials": "5.
|
|
28
|
-
"@memberjunction/global": "5.
|
|
29
|
-
"@memberjunction/templates": "5.
|
|
30
|
-
"@memberjunction/templates-base-types": "5.
|
|
21
|
+
"@memberjunction/ai": "5.1.0",
|
|
22
|
+
"@memberjunction/ai-core-plus": "5.1.0",
|
|
23
|
+
"@memberjunction/ai-engine-base": "5.1.0",
|
|
24
|
+
"@memberjunction/aiengine": "5.1.0",
|
|
25
|
+
"@memberjunction/core": "5.1.0",
|
|
26
|
+
"@memberjunction/core-entities": "5.1.0",
|
|
27
|
+
"@memberjunction/credentials": "5.1.0",
|
|
28
|
+
"@memberjunction/global": "5.1.0",
|
|
29
|
+
"@memberjunction/templates": "5.1.0",
|
|
30
|
+
"@memberjunction/templates-base-types": "5.1.0",
|
|
31
31
|
"date-fns": "^2.30.0",
|
|
32
32
|
"date-fns-tz": "^2.0.0",
|
|
33
33
|
"dotenv": "^17.2.4",
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for AIPromptRunner Failover Bug Fix (Phase 1).
|
|
3
|
-
*
|
|
4
|
-
* These tests verify that the critical failover bug is fixed:
|
|
5
|
-
* - Network errors trigger failover to alternate models
|
|
6
|
-
* - Rate limit errors trigger failover after retries exhausted
|
|
7
|
-
* - Successful results don't trigger unnecessary failover
|
|
8
|
-
* - All candidates exhausted returns last error correctly
|
|
9
|
-
*
|
|
10
|
-
* Background: The bug was that executeModelWithFailover() returned failed results
|
|
11
|
-
* without checking result.success, only catching thrown exceptions. Provider drivers
|
|
12
|
-
* (GeminiLLM, OpenAILLM, etc.) return ChatResult{success: false} instead of throwing,
|
|
13
|
-
* so failover never triggered for network failures, rate limits, etc.
|
|
14
|
-
*
|
|
15
|
-
* To run these tests:
|
|
16
|
-
* npm test # Run all tests
|
|
17
|
-
* npm run test:watch # Watch mode
|
|
18
|
-
* npm run test:coverage # With coverage
|
|
19
|
-
*
|
|
20
|
-
* @since 3.3.0 (Phase 1 failover bug fix)
|
|
21
|
-
*/
|
|
22
|
-
export {};
|
|
23
|
-
//# sourceMappingURL=AIPromptRunner.failover.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AIPromptRunner.failover.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/AIPromptRunner.failover.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG"}
|
|
@@ -1,471 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for AIPromptRunner Failover Bug Fix (Phase 1).
|
|
3
|
-
*
|
|
4
|
-
* These tests verify that the critical failover bug is fixed:
|
|
5
|
-
* - Network errors trigger failover to alternate models
|
|
6
|
-
* - Rate limit errors trigger failover after retries exhausted
|
|
7
|
-
* - Successful results don't trigger unnecessary failover
|
|
8
|
-
* - All candidates exhausted returns last error correctly
|
|
9
|
-
*
|
|
10
|
-
* Background: The bug was that executeModelWithFailover() returned failed results
|
|
11
|
-
* without checking result.success, only catching thrown exceptions. Provider drivers
|
|
12
|
-
* (GeminiLLM, OpenAILLM, etc.) return ChatResult{success: false} instead of throwing,
|
|
13
|
-
* so failover never triggered for network failures, rate limits, etc.
|
|
14
|
-
*
|
|
15
|
-
* To run these tests:
|
|
16
|
-
* npm test # Run all tests
|
|
17
|
-
* npm run test:watch # Watch mode
|
|
18
|
-
* npm run test:coverage # With coverage
|
|
19
|
-
*
|
|
20
|
-
* @since 3.3.0 (Phase 1 failover bug fix)
|
|
21
|
-
*/
|
|
22
|
-
// ============================================================================
|
|
23
|
-
// Test Helper Functions
|
|
24
|
-
// ============================================================================
|
|
25
|
-
/**
|
|
26
|
-
* Simulates the processFailoverError logic for testing purposes.
|
|
27
|
-
* Returns decision object indicating what the failover loop should do.
|
|
28
|
-
*/
|
|
29
|
-
function simulateProcessFailoverError(error, errorInfo, candidate, attemptIndex, allCandidates, failoverAttempts, failoverConfig) {
|
|
30
|
-
let updatedCandidates = [...allCandidates];
|
|
31
|
-
// Record the failover attempt
|
|
32
|
-
const failoverAttempt = {
|
|
33
|
-
attemptNumber: attemptIndex + 1,
|
|
34
|
-
modelId: candidate.modelId,
|
|
35
|
-
vendorId: candidate.vendorId,
|
|
36
|
-
error,
|
|
37
|
-
errorType: errorInfo.errorType,
|
|
38
|
-
duration: 0,
|
|
39
|
-
timestamp: new Date()
|
|
40
|
-
};
|
|
41
|
-
failoverAttempts.push(failoverAttempt);
|
|
42
|
-
// Vendor-level errors: filter out all candidates from this vendor
|
|
43
|
-
if (errorInfo.errorType === 'Authentication' || errorInfo.errorType === 'VendorValidationError') {
|
|
44
|
-
const beforeFilter = updatedCandidates.length;
|
|
45
|
-
updatedCandidates = updatedCandidates.filter(c => c.vendorId !== candidate.vendorId);
|
|
46
|
-
const afterFilter = updatedCandidates.length;
|
|
47
|
-
// Adjust attemptIndex if candidates were removed before current position
|
|
48
|
-
if (afterFilter < beforeFilter && attemptIndex < allCandidates.length) {
|
|
49
|
-
// Don't adjust - the main loop will handle the updated candidates list
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
// Check if there are NO MORE candidates left to try AFTER filtering
|
|
53
|
-
// After filtering vendor candidates, we may have removed the current one,
|
|
54
|
-
// so check if there are any candidates remaining
|
|
55
|
-
const hasMoreCandidates = updatedCandidates.length > 0 &&
|
|
56
|
-
(attemptIndex < updatedCandidates.length - 1 ||
|
|
57
|
-
updatedCandidates[attemptIndex]?.vendorId !== candidate.vendorId);
|
|
58
|
-
const isLastCandidate = !hasMoreCandidates;
|
|
59
|
-
// Fatal errors: stop immediately ONLY if canFailover is false
|
|
60
|
-
// (canFailover=true means we can try other vendors even for Fatal severity errors like Auth)
|
|
61
|
-
if (errorInfo.severity === 'Fatal' && !errorInfo.canFailover) {
|
|
62
|
-
return { shouldRetry: false, shouldContinue: false, updatedCandidates };
|
|
63
|
-
}
|
|
64
|
-
// Check errorScope filter if configured
|
|
65
|
-
if (failoverConfig.errorScope && failoverConfig.errorScope !== 'All') {
|
|
66
|
-
const matchesScope = errorMatchesScope(errorInfo.errorType, failoverConfig.errorScope);
|
|
67
|
-
if (!matchesScope) {
|
|
68
|
-
return { shouldRetry: false, shouldContinue: false, updatedCandidates };
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
// Rate limit errors: for testing, assume handleRateLimitRetry returns false (retries exhausted)
|
|
72
|
-
// In reality, this would check MaxRetries configuration and retry attempt count
|
|
73
|
-
if (errorInfo.errorType === 'RateLimit') {
|
|
74
|
-
const shouldRetry = false; // Simplified: assume retries exhausted
|
|
75
|
-
if (shouldRetry) {
|
|
76
|
-
return { shouldRetry: true, shouldContinue: false, updatedCandidates };
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
// If this is the last candidate, we're done
|
|
80
|
-
if (isLastCandidate) {
|
|
81
|
-
return { shouldRetry: false, shouldContinue: false, updatedCandidates };
|
|
82
|
-
}
|
|
83
|
-
// Continue to next candidate
|
|
84
|
-
return { shouldRetry: false, shouldContinue: true, updatedCandidates };
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Checks if error type matches the configured error scope
|
|
88
|
-
*/
|
|
89
|
-
function errorMatchesScope(errorType, errorScope) {
|
|
90
|
-
if (errorScope === 'All')
|
|
91
|
-
return true;
|
|
92
|
-
switch (errorScope) {
|
|
93
|
-
case 'NetworkOnly':
|
|
94
|
-
return errorType === 'NetworkError';
|
|
95
|
-
case 'RateLimitOnly':
|
|
96
|
-
return errorType === 'RateLimit';
|
|
97
|
-
case 'ServiceErrorOnly':
|
|
98
|
-
return errorType === 'ServiceUnavailable' || errorType === 'InternalServerError';
|
|
99
|
-
default:
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Simulates executeModelWithFailover with the Phase 1 fix applied.
|
|
105
|
-
* This is a simplified version that tests the core failover logic.
|
|
106
|
-
*/
|
|
107
|
-
function simulateExecuteModelWithFailover(candidates, executeModelFn, failoverConfig) {
|
|
108
|
-
return new Promise(async (resolve, reject) => {
|
|
109
|
-
let allCandidates = [...candidates];
|
|
110
|
-
const failoverAttempts = [];
|
|
111
|
-
let lastError = null;
|
|
112
|
-
let candidateIndex = 0;
|
|
113
|
-
while (candidateIndex < allCandidates.length) {
|
|
114
|
-
const candidate = allCandidates[candidateIndex];
|
|
115
|
-
try {
|
|
116
|
-
// Execute the model with this candidate
|
|
117
|
-
const result = await executeModelFn(candidate);
|
|
118
|
-
// PHASE 1 FIX: Check if result failed but is retriable
|
|
119
|
-
if (!result.success && result.errorInfo?.canFailover) {
|
|
120
|
-
lastError = result.exception || new Error(result.errorMessage || 'Model execution failed');
|
|
121
|
-
// Use shared failover error handling logic
|
|
122
|
-
const decision = simulateProcessFailoverError(lastError, result.errorInfo, candidate, candidateIndex, allCandidates, failoverAttempts, failoverConfig);
|
|
123
|
-
// Update candidates list (may have been filtered)
|
|
124
|
-
const candidatesRemoved = allCandidates.length - decision.updatedCandidates.length;
|
|
125
|
-
allCandidates = decision.updatedCandidates;
|
|
126
|
-
if (decision.shouldRetry) {
|
|
127
|
-
// Retry same model/vendor - don't increment index
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
if (decision.shouldContinue) {
|
|
131
|
-
// Try next candidate - but if candidates were removed, the "next" candidate
|
|
132
|
-
// may already be at the current index
|
|
133
|
-
if (candidatesRemoved > 0) {
|
|
134
|
-
// Check if current candidate is still in the list
|
|
135
|
-
const stillExists = allCandidates[candidateIndex]?.modelId === candidate.modelId;
|
|
136
|
-
if (!stillExists) {
|
|
137
|
-
// Current candidate was removed, stay at same index
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
// Move to next candidate
|
|
142
|
-
candidateIndex++;
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
// Otherwise break (fatal error or last candidate)
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
// If we reach here, the result was successful
|
|
149
|
-
resolve(result);
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
catch (error) {
|
|
153
|
-
lastError = error;
|
|
154
|
-
// Create error info for thrown exception
|
|
155
|
-
const errorInfo = {
|
|
156
|
-
error: lastError,
|
|
157
|
-
errorType: 'NetworkError',
|
|
158
|
-
severity: 'Retriable',
|
|
159
|
-
canFailover: true
|
|
160
|
-
};
|
|
161
|
-
// Use shared failover error handling logic
|
|
162
|
-
const decision = simulateProcessFailoverError(lastError, errorInfo, candidate, candidateIndex, allCandidates, failoverAttempts, failoverConfig);
|
|
163
|
-
// Update candidates list (may have been filtered)
|
|
164
|
-
const candidatesRemoved = allCandidates.length - decision.updatedCandidates.length;
|
|
165
|
-
allCandidates = decision.updatedCandidates;
|
|
166
|
-
if (decision.shouldRetry) {
|
|
167
|
-
// Retry same model/vendor - don't increment index
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
if (decision.shouldContinue) {
|
|
171
|
-
// Try next candidate - but if candidates were removed, the "next" candidate
|
|
172
|
-
// may already be at the current index
|
|
173
|
-
if (candidatesRemoved > 0) {
|
|
174
|
-
// Check if current candidate is still in the list
|
|
175
|
-
const stillExists = allCandidates[candidateIndex]?.modelId === candidate.modelId;
|
|
176
|
-
if (!stillExists) {
|
|
177
|
-
// Current candidate was removed, stay at same index
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
// Move to next candidate
|
|
182
|
-
candidateIndex++;
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
// Otherwise break (fatal error or last candidate)
|
|
186
|
-
break;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
// All candidates exhausted, return final error
|
|
190
|
-
const finalError = lastError || new Error('All failover attempts exhausted');
|
|
191
|
-
resolve({
|
|
192
|
-
success: false,
|
|
193
|
-
errorMessage: `All ${candidates.length} failover attempts failed. Last error: ${finalError.message}`,
|
|
194
|
-
exception: finalError,
|
|
195
|
-
errorInfo: {
|
|
196
|
-
error: finalError,
|
|
197
|
-
errorType: 'Unknown',
|
|
198
|
-
severity: 'Fatal',
|
|
199
|
-
canFailover: false
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
// ============================================================================
|
|
205
|
-
// Tests for Phase 1 Failover Bug Fix
|
|
206
|
-
// ============================================================================
|
|
207
|
-
describe('AIPromptRunner Failover Bug Fix (Phase 1)', () => {
|
|
208
|
-
it('should trigger failover when network error occurs', async () => {
|
|
209
|
-
const candidates = [
|
|
210
|
-
{ modelId: 'model-1', modelName: 'Gemini 3 Flash', vendorId: 'vendor-google', vendorName: 'Google', priority: 1 },
|
|
211
|
-
{ modelId: 'model-2', modelName: 'GPT-4o', vendorId: 'vendor-openai', vendorName: 'OpenAI', priority: 2 }
|
|
212
|
-
];
|
|
213
|
-
let attemptCount = 0;
|
|
214
|
-
const executeModelFn = async (candidate) => {
|
|
215
|
-
attemptCount++;
|
|
216
|
-
if (candidate.modelId === 'model-1') {
|
|
217
|
-
// First model fails with network error
|
|
218
|
-
return {
|
|
219
|
-
success: false,
|
|
220
|
-
errorMessage: 'TypeError: fetch failed',
|
|
221
|
-
exception: new Error('TypeError: fetch failed'),
|
|
222
|
-
errorInfo: {
|
|
223
|
-
error: new Error('TypeError: fetch failed'),
|
|
224
|
-
errorType: 'NetworkError',
|
|
225
|
-
severity: 'Retriable',
|
|
226
|
-
canFailover: true
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
else {
|
|
231
|
-
// Second model succeeds
|
|
232
|
-
return {
|
|
233
|
-
success: true,
|
|
234
|
-
data: 'Success with fallback model'
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
const failoverConfig = {
|
|
239
|
-
strategy: 'priority',
|
|
240
|
-
maxAttempts: 2
|
|
241
|
-
};
|
|
242
|
-
const result = await simulateExecuteModelWithFailover(candidates, executeModelFn, failoverConfig);
|
|
243
|
-
expect(attemptCount).toBe(2);
|
|
244
|
-
expect(result.success).toBe(true);
|
|
245
|
-
expect(result.data).toBe('Success with fallback model');
|
|
246
|
-
});
|
|
247
|
-
it('should trigger failover when rate limit error occurs', async () => {
|
|
248
|
-
const candidates = [
|
|
249
|
-
{ modelId: 'model-1', modelName: 'GPT-4', vendorId: 'vendor-openai', vendorName: 'OpenAI', priority: 1 },
|
|
250
|
-
{ modelId: 'model-2', modelName: 'Claude Opus', vendorId: 'vendor-anthropic', vendorName: 'Anthropic', priority: 2 }
|
|
251
|
-
];
|
|
252
|
-
let attemptCount = 0;
|
|
253
|
-
const executeModelFn = async (candidate) => {
|
|
254
|
-
attemptCount++;
|
|
255
|
-
if (candidate.modelId === 'model-1') {
|
|
256
|
-
// First model hits rate limit
|
|
257
|
-
return {
|
|
258
|
-
success: false,
|
|
259
|
-
errorMessage: 'Rate limit exceeded',
|
|
260
|
-
exception: new Error('Rate limit exceeded'),
|
|
261
|
-
errorInfo: {
|
|
262
|
-
error: new Error('Rate limit exceeded'),
|
|
263
|
-
httpStatusCode: 429,
|
|
264
|
-
errorType: 'RateLimit',
|
|
265
|
-
severity: 'Retriable',
|
|
266
|
-
canFailover: true,
|
|
267
|
-
suggestedRetryDelaySeconds: 30
|
|
268
|
-
}
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
272
|
-
// Second model succeeds
|
|
273
|
-
return {
|
|
274
|
-
success: true,
|
|
275
|
-
data: 'Success with alternate provider'
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
const failoverConfig = {
|
|
280
|
-
strategy: 'priority',
|
|
281
|
-
maxAttempts: 2
|
|
282
|
-
};
|
|
283
|
-
const result = await simulateExecuteModelWithFailover(candidates, executeModelFn, failoverConfig);
|
|
284
|
-
expect(attemptCount).toBe(2);
|
|
285
|
-
expect(result.success).toBe(true);
|
|
286
|
-
expect(result.data).toBe('Success with alternate provider');
|
|
287
|
-
});
|
|
288
|
-
it('should not trigger failover when result is successful', async () => {
|
|
289
|
-
const candidates = [
|
|
290
|
-
{ modelId: 'model-1', modelName: 'Gemini 3 Flash', vendorId: 'vendor-google', vendorName: 'Google', priority: 1 },
|
|
291
|
-
{ modelId: 'model-2', modelName: 'GPT-4o', vendorId: 'vendor-openai', vendorName: 'OpenAI', priority: 2 }
|
|
292
|
-
];
|
|
293
|
-
let attemptCount = 0;
|
|
294
|
-
const executeModelFn = async (candidate) => {
|
|
295
|
-
attemptCount++;
|
|
296
|
-
// First model succeeds
|
|
297
|
-
return {
|
|
298
|
-
success: true,
|
|
299
|
-
data: `Success with ${candidate.modelName}`
|
|
300
|
-
};
|
|
301
|
-
};
|
|
302
|
-
const failoverConfig = {
|
|
303
|
-
strategy: 'priority',
|
|
304
|
-
maxAttempts: 2
|
|
305
|
-
};
|
|
306
|
-
const result = await simulateExecuteModelWithFailover(candidates, executeModelFn, failoverConfig);
|
|
307
|
-
expect(attemptCount).toBe(1);
|
|
308
|
-
expect(result.success).toBe(true);
|
|
309
|
-
expect(result.data).toBe('Success with Gemini 3 Flash');
|
|
310
|
-
});
|
|
311
|
-
it('should return last error when all candidates are exhausted', async () => {
|
|
312
|
-
const candidates = [
|
|
313
|
-
{ modelId: 'model-1', modelName: 'Gemini 3 Flash', vendorId: 'vendor-google', vendorName: 'Google', priority: 1 },
|
|
314
|
-
{ modelId: 'model-2', modelName: 'GPT-4o', vendorId: 'vendor-openai', vendorName: 'OpenAI', priority: 2 },
|
|
315
|
-
{ modelId: 'model-3', modelName: 'Claude Opus', vendorId: 'vendor-anthropic', vendorName: 'Anthropic', priority: 3 }
|
|
316
|
-
];
|
|
317
|
-
let attemptCount = 0;
|
|
318
|
-
const executeModelFn = async (candidate) => {
|
|
319
|
-
attemptCount++;
|
|
320
|
-
// All models fail with network errors
|
|
321
|
-
return {
|
|
322
|
-
success: false,
|
|
323
|
-
errorMessage: `Network failure for ${candidate.modelName}`,
|
|
324
|
-
exception: new Error(`Network failure for ${candidate.modelName}`),
|
|
325
|
-
errorInfo: {
|
|
326
|
-
error: new Error(`Network failure for ${candidate.modelName}`),
|
|
327
|
-
errorType: 'NetworkError',
|
|
328
|
-
severity: 'Retriable',
|
|
329
|
-
canFailover: true
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
};
|
|
333
|
-
const failoverConfig = {
|
|
334
|
-
strategy: 'priority',
|
|
335
|
-
maxAttempts: 3
|
|
336
|
-
};
|
|
337
|
-
const result = await simulateExecuteModelWithFailover(candidates, executeModelFn, failoverConfig);
|
|
338
|
-
expect(attemptCount).toBe(3);
|
|
339
|
-
expect(result.success).toBe(false);
|
|
340
|
-
expect(result.errorMessage).toContain('All 3 failover attempts failed');
|
|
341
|
-
expect(result.errorMessage).toContain('Claude Opus');
|
|
342
|
-
});
|
|
343
|
-
it('should trigger failover when service unavailable error occurs', async () => {
|
|
344
|
-
const candidates = [
|
|
345
|
-
{ modelId: 'model-1', modelName: 'Gemini Pro', vendorId: 'vendor-google', vendorName: 'Google', priority: 1 },
|
|
346
|
-
{ modelId: 'model-2', modelName: 'GPT-4', vendorId: 'vendor-openai', vendorName: 'OpenAI', priority: 2 }
|
|
347
|
-
];
|
|
348
|
-
let attemptCount = 0;
|
|
349
|
-
const executeModelFn = async (candidate) => {
|
|
350
|
-
attemptCount++;
|
|
351
|
-
if (candidate.modelId === 'model-1') {
|
|
352
|
-
// First model returns 503 Service Unavailable
|
|
353
|
-
return {
|
|
354
|
-
success: false,
|
|
355
|
-
statusText: 'Service Unavailable',
|
|
356
|
-
errorMessage: 'Service temporarily unavailable',
|
|
357
|
-
exception: new Error('Service temporarily unavailable'),
|
|
358
|
-
errorInfo: {
|
|
359
|
-
error: new Error('Service temporarily unavailable'),
|
|
360
|
-
httpStatusCode: 503,
|
|
361
|
-
errorType: 'ServiceUnavailable',
|
|
362
|
-
severity: 'Retriable',
|
|
363
|
-
canFailover: true
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
else {
|
|
368
|
-
// Second model succeeds
|
|
369
|
-
return {
|
|
370
|
-
success: true,
|
|
371
|
-
data: 'Success after service unavailable'
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
};
|
|
375
|
-
const failoverConfig = {
|
|
376
|
-
strategy: 'priority',
|
|
377
|
-
maxAttempts: 2
|
|
378
|
-
};
|
|
379
|
-
const result = await simulateExecuteModelWithFailover(candidates, executeModelFn, failoverConfig);
|
|
380
|
-
expect(attemptCount).toBe(2);
|
|
381
|
-
expect(result.success).toBe(true);
|
|
382
|
-
expect(result.data).toBe('Success after service unavailable');
|
|
383
|
-
});
|
|
384
|
-
it('should filter all vendor candidates when vendor-level error occurs', async () => {
|
|
385
|
-
const candidates = [
|
|
386
|
-
{ modelId: 'model-1', modelName: 'GPT-4', vendorId: 'vendor-openai', vendorName: 'OpenAI', priority: 1 },
|
|
387
|
-
{ modelId: 'model-2', modelName: 'GPT-4o', vendorId: 'vendor-openai', vendorName: 'OpenAI', priority: 2 },
|
|
388
|
-
{ modelId: 'model-3', modelName: 'Claude Opus', vendorId: 'vendor-anthropic', vendorName: 'Anthropic', priority: 3 }
|
|
389
|
-
];
|
|
390
|
-
let attemptCount = 0;
|
|
391
|
-
const attemptedModels = [];
|
|
392
|
-
const executeModelFn = async (candidate) => {
|
|
393
|
-
attemptCount++;
|
|
394
|
-
attemptedModels.push(candidate.modelName);
|
|
395
|
-
if (candidate.vendorId === 'vendor-openai') {
|
|
396
|
-
// OpenAI models fail with authentication error (vendor-level)
|
|
397
|
-
return {
|
|
398
|
-
success: false,
|
|
399
|
-
errorMessage: 'Invalid API key',
|
|
400
|
-
exception: new Error('Invalid API key'),
|
|
401
|
-
errorInfo: {
|
|
402
|
-
error: new Error('Invalid API key'),
|
|
403
|
-
httpStatusCode: 401,
|
|
404
|
-
errorType: 'Authentication',
|
|
405
|
-
severity: 'Fatal',
|
|
406
|
-
canFailover: true // canFailover=true for vendor-level errors to try other vendors
|
|
407
|
-
}
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
// Anthropic model succeeds
|
|
412
|
-
return {
|
|
413
|
-
success: true,
|
|
414
|
-
data: 'Success with different vendor'
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
const failoverConfig = {
|
|
419
|
-
strategy: 'priority',
|
|
420
|
-
maxAttempts: 3
|
|
421
|
-
};
|
|
422
|
-
const result = await simulateExecuteModelWithFailover(candidates, executeModelFn, failoverConfig);
|
|
423
|
-
expect(attemptCount).toBe(2); // GPT-4 fails, GPT-4o skipped, Claude Opus succeeds
|
|
424
|
-
expect(attemptedModels).toEqual(['GPT-4', 'Claude Opus']);
|
|
425
|
-
expect(result.success).toBe(true);
|
|
426
|
-
expect(result.data).toBe('Success with different vendor');
|
|
427
|
-
});
|
|
428
|
-
it('should respect errorScope filter and not failover for non-matching errors', async () => {
|
|
429
|
-
const candidates = [
|
|
430
|
-
{ modelId: 'model-1', modelName: 'GPT-4', vendorId: 'vendor-openai', vendorName: 'OpenAI', priority: 1 },
|
|
431
|
-
{ modelId: 'model-2', modelName: 'Claude Opus', vendorId: 'vendor-anthropic', vendorName: 'Anthropic', priority: 2 }
|
|
432
|
-
];
|
|
433
|
-
let attemptCount = 0;
|
|
434
|
-
const executeModelFn = async (candidate) => {
|
|
435
|
-
attemptCount++;
|
|
436
|
-
if (candidate.modelId === 'model-1') {
|
|
437
|
-
// First model fails with rate limit (not network error)
|
|
438
|
-
return {
|
|
439
|
-
success: false,
|
|
440
|
-
errorMessage: 'Rate limit exceeded',
|
|
441
|
-
exception: new Error('Rate limit exceeded'),
|
|
442
|
-
errorInfo: {
|
|
443
|
-
error: new Error('Rate limit exceeded'),
|
|
444
|
-
httpStatusCode: 429,
|
|
445
|
-
errorType: 'RateLimit',
|
|
446
|
-
severity: 'Retriable',
|
|
447
|
-
canFailover: true
|
|
448
|
-
}
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
else {
|
|
452
|
-
// Should not reach here due to errorScope filter
|
|
453
|
-
return {
|
|
454
|
-
success: true,
|
|
455
|
-
data: 'Should not be reached'
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
const failoverConfig = {
|
|
460
|
-
strategy: 'priority',
|
|
461
|
-
errorScope: 'NetworkOnly', // Only failover for network errors
|
|
462
|
-
maxAttempts: 2
|
|
463
|
-
};
|
|
464
|
-
const result = await simulateExecuteModelWithFailover(candidates, executeModelFn, failoverConfig);
|
|
465
|
-
expect(attemptCount).toBe(1);
|
|
466
|
-
expect(result.success).toBe(false);
|
|
467
|
-
expect(result.errorMessage).toContain('Rate limit exceeded');
|
|
468
|
-
});
|
|
469
|
-
});
|
|
470
|
-
export {};
|
|
471
|
-
//# sourceMappingURL=AIPromptRunner.failover.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AIPromptRunner.failover.test.js","sourceRoot":"","sources":["../../src/__tests__/AIPromptRunner.failover.test.ts"],"names":[],"mappings":"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"}
|