@llumiverse/core 1.3.0 → 1.4.0-dev.20260629.090753Z
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/lib/CompletionStream.d.ts +48 -0
- package/lib/CompletionStream.d.ts.map +1 -0
- package/lib/{esm/CompletionStream.js → CompletionStream.js} +142 -67
- package/lib/CompletionStream.js.map +1 -0
- package/lib/{types/Driver.d.ts → Driver.d.ts} +38 -5
- package/lib/Driver.d.ts.map +1 -0
- package/lib/{esm/Driver.js → Driver.js} +151 -106
- package/lib/Driver.js.map +1 -0
- package/lib/{types/async.d.ts → async.d.ts} +3 -3
- package/lib/async.d.ts.map +1 -0
- package/lib/{esm/async.js → async.js} +4 -4
- package/lib/async.js.map +1 -0
- package/lib/{types/conversation-utils.d.ts → conversation-utils.d.ts} +9 -0
- package/lib/conversation-utils.d.ts.map +1 -0
- package/lib/{esm/conversation-utils.js → conversation-utils.js} +78 -15
- package/lib/conversation-utils.js.map +1 -0
- package/lib/embeddings.d.ts +58 -0
- package/lib/embeddings.d.ts.map +1 -0
- package/lib/embeddings.js +131 -0
- package/lib/embeddings.js.map +1 -0
- package/lib/formatters/commons.d.ts +3 -0
- package/lib/formatters/commons.d.ts.map +1 -0
- package/lib/formatters/commons.js +4 -0
- package/lib/formatters/commons.js.map +1 -0
- package/lib/{types/formatters → formatters}/generic.d.ts +2 -2
- package/lib/formatters/generic.d.ts.map +1 -0
- package/lib/{esm/formatters → formatters}/generic.js +8 -8
- package/lib/formatters/generic.js.map +1 -0
- package/lib/formatters/index.d.ts +4 -0
- package/lib/formatters/index.d.ts.map +1 -0
- package/lib/formatters/index.js +4 -0
- package/lib/formatters/index.js.map +1 -0
- package/lib/{types/formatters → formatters}/nova.d.ts +4 -4
- package/lib/formatters/nova.d.ts.map +1 -0
- package/lib/{esm/formatters → formatters}/nova.js +19 -17
- package/lib/formatters/nova.js.map +1 -0
- package/lib/http-agent.d.ts +53 -0
- package/lib/http-agent.d.ts.map +1 -0
- package/lib/http-agent.js +141 -0
- package/lib/http-agent.js.map +1 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +7 -0
- package/lib/index.js.map +1 -0
- package/lib/{types/json.d.ts → json.d.ts} +1 -1
- package/lib/json.d.ts.map +1 -0
- package/lib/{esm/json.js → json.js} +4 -3
- package/lib/json.js.map +1 -0
- package/lib/logger.d.ts +3 -0
- package/lib/logger.d.ts.map +1 -0
- package/lib/logger.js +42 -0
- package/lib/logger.js.map +1 -0
- package/lib/resolver.d.ts +2 -0
- package/lib/resolver.d.ts.map +1 -0
- package/lib/{esm/resolver.js → resolver.js} +7 -4
- package/lib/resolver.js.map +1 -0
- package/lib/stream.d.ts.map +1 -0
- package/lib/stream.js.map +1 -0
- package/lib/{types/validation.d.ts → validation.d.ts} +1 -1
- package/lib/validation.d.ts.map +1 -0
- package/lib/validation.js +100 -0
- package/lib/validation.js.map +1 -0
- package/package.json +28 -38
- package/src/CompletionStream.ts +208 -115
- package/src/Driver.error.test.ts +101 -72
- package/src/Driver.ts +208 -152
- package/src/async.ts +30 -30
- package/src/conversation-utils.ts +92 -16
- package/src/embeddings.ts +157 -0
- package/src/formatters/commons.ts +2 -2
- package/src/formatters/generic.ts +20 -17
- package/src/formatters/index.ts +3 -3
- package/src/formatters/nova.ts +53 -51
- package/src/http-agent.test.ts +281 -0
- package/src/http-agent.ts +177 -0
- package/src/index.ts +7 -5
- package/src/json.ts +6 -5
- package/src/logger.ts +42 -0
- package/src/resolver.ts +12 -9
- package/src/stream.ts +4 -5
- package/src/validation.ts +42 -29
- package/lib/cjs/CompletionStream.js +0 -293
- package/lib/cjs/CompletionStream.js.map +0 -1
- package/lib/cjs/Driver.js +0 -300
- package/lib/cjs/Driver.js.map +0 -1
- package/lib/cjs/async.js +0 -131
- package/lib/cjs/async.js.map +0 -1
- package/lib/cjs/conversation-utils.js +0 -579
- package/lib/cjs/conversation-utils.js.map +0 -1
- package/lib/cjs/formatters/commons.js +0 -7
- package/lib/cjs/formatters/commons.js.map +0 -1
- package/lib/cjs/formatters/generic.js +0 -59
- package/lib/cjs/formatters/generic.js.map +0 -1
- package/lib/cjs/formatters/index.js +0 -20
- package/lib/cjs/formatters/index.js.map +0 -1
- package/lib/cjs/formatters/nova.js +0 -99
- package/lib/cjs/formatters/nova.js.map +0 -1
- package/lib/cjs/index.js +0 -22
- package/lib/cjs/index.js.map +0 -1
- package/lib/cjs/json.js +0 -29
- package/lib/cjs/json.js.map +0 -1
- package/lib/cjs/package.json +0 -3
- package/lib/cjs/resolver.js +0 -45
- package/lib/cjs/resolver.js.map +0 -1
- package/lib/cjs/stream.js +0 -30
- package/lib/cjs/stream.js.map +0 -1
- package/lib/cjs/validation.js +0 -96
- package/lib/cjs/validation.js.map +0 -1
- package/lib/esm/CompletionStream.js.map +0 -1
- package/lib/esm/Driver.js.map +0 -1
- package/lib/esm/async.js.map +0 -1
- package/lib/esm/conversation-utils.js.map +0 -1
- package/lib/esm/formatters/commons.js +0 -4
- package/lib/esm/formatters/commons.js.map +0 -1
- package/lib/esm/formatters/generic.js.map +0 -1
- package/lib/esm/formatters/index.js +0 -4
- package/lib/esm/formatters/index.js.map +0 -1
- package/lib/esm/formatters/nova.js.map +0 -1
- package/lib/esm/index.js +0 -6
- package/lib/esm/index.js.map +0 -1
- package/lib/esm/json.js.map +0 -1
- package/lib/esm/resolver.js.map +0 -1
- package/lib/esm/stream.js.map +0 -1
- package/lib/esm/validation.js +0 -88
- package/lib/esm/validation.js.map +0 -1
- package/lib/types/CompletionStream.d.ts +0 -20
- package/lib/types/CompletionStream.d.ts.map +0 -1
- package/lib/types/Driver.d.ts.map +0 -1
- package/lib/types/async.d.ts.map +0 -1
- package/lib/types/conversation-utils.d.ts.map +0 -1
- package/lib/types/formatters/commons.d.ts +0 -3
- package/lib/types/formatters/commons.d.ts.map +0 -1
- package/lib/types/formatters/generic.d.ts.map +0 -1
- package/lib/types/formatters/index.d.ts +0 -4
- package/lib/types/formatters/index.d.ts.map +0 -1
- package/lib/types/formatters/nova.d.ts.map +0 -1
- package/lib/types/index.d.ts +0 -6
- package/lib/types/index.d.ts.map +0 -1
- package/lib/types/json.d.ts.map +0 -1
- package/lib/types/resolver.d.ts +0 -2
- package/lib/types/resolver.d.ts.map +0 -1
- package/lib/types/stream.d.ts.map +0 -1
- package/lib/types/validation.d.ts.map +0 -1
- /package/lib/{types/stream.d.ts → stream.d.ts} +0 -0
- /package/lib/{esm/stream.js → stream.js} +0 -0
package/src/Driver.error.test.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
-
AIModel,
|
|
3
|
-
Completion,
|
|
4
|
-
CompletionChunkObject,
|
|
5
|
-
DriverOptions,
|
|
6
|
-
EmbeddingsOptions,
|
|
7
|
-
EmbeddingsResult,
|
|
8
|
-
ExecutionOptions,
|
|
9
|
-
LlumiverseErrorContext,
|
|
10
|
-
ModelSearchPayload,
|
|
2
|
+
type AIModel,
|
|
3
|
+
type Completion,
|
|
4
|
+
type CompletionChunkObject,
|
|
5
|
+
type DriverOptions,
|
|
6
|
+
type EmbeddingsOptions,
|
|
7
|
+
type EmbeddingsResult,
|
|
8
|
+
type ExecutionOptions,
|
|
11
9
|
LlumiverseError,
|
|
10
|
+
type LlumiverseErrorContext,
|
|
11
|
+
type ModelSearchPayload,
|
|
12
12
|
} from '@llumiverse/common';
|
|
13
13
|
import { beforeEach, describe, expect, it } from 'vitest';
|
|
14
|
+
import { errorWith, getProp } from '../test/__helpers__/test-utils.js';
|
|
14
15
|
import { AbstractDriver } from './Driver.js';
|
|
15
16
|
|
|
16
17
|
// Simple test driver implementation
|
|
@@ -21,7 +22,10 @@ class TestDriver extends AbstractDriver<DriverOptions, string> {
|
|
|
21
22
|
throw new Error('Not implemented');
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
async requestTextCompletionStream(
|
|
25
|
+
async requestTextCompletionStream(
|
|
26
|
+
_prompt: string,
|
|
27
|
+
_options: ExecutionOptions,
|
|
28
|
+
): Promise<AsyncIterable<CompletionChunkObject>> {
|
|
25
29
|
throw new Error('Not implemented');
|
|
26
30
|
}
|
|
27
31
|
|
|
@@ -36,6 +40,11 @@ class TestDriver extends AbstractDriver<DriverOptions, string> {
|
|
|
36
40
|
async generateEmbeddings(_options: EmbeddingsOptions): Promise<EmbeddingsResult> {
|
|
37
41
|
throw new Error('Not implemented');
|
|
38
42
|
}
|
|
43
|
+
|
|
44
|
+
// Re-exposed for tests — base class declares this as protected
|
|
45
|
+
isRetryableError(statusCode: number | undefined, message: string): boolean | undefined {
|
|
46
|
+
return super.isRetryableError(statusCode, message);
|
|
47
|
+
}
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
describe('AbstractDriver Error Formatting', () => {
|
|
@@ -53,90 +62,118 @@ describe('AbstractDriver Error Formatting', () => {
|
|
|
53
62
|
describe('isRetryableError', () => {
|
|
54
63
|
describe('HTTP status codes', () => {
|
|
55
64
|
it('should mark 429 as retryable (rate limit)', () => {
|
|
56
|
-
expect(driver
|
|
65
|
+
expect(driver.isRetryableError(429, 'Rate limit exceeded')).toBe(true);
|
|
57
66
|
});
|
|
58
67
|
|
|
59
68
|
it('should mark 408 as retryable (timeout)', () => {
|
|
60
|
-
expect(driver
|
|
69
|
+
expect(driver.isRetryableError(408, 'Request timeout')).toBe(true);
|
|
61
70
|
});
|
|
62
71
|
|
|
63
72
|
it('should mark 529 as retryable (overloaded)', () => {
|
|
64
|
-
expect(driver
|
|
73
|
+
expect(driver.isRetryableError(529, 'Service overloaded')).toBe(true);
|
|
65
74
|
});
|
|
66
75
|
|
|
67
76
|
it('should mark 5xx as retryable (server errors)', () => {
|
|
68
|
-
expect(driver
|
|
69
|
-
expect(driver
|
|
70
|
-
expect(driver
|
|
71
|
-
expect(driver
|
|
77
|
+
expect(driver.isRetryableError(500, 'Internal server error')).toBe(true);
|
|
78
|
+
expect(driver.isRetryableError(502, 'Bad gateway')).toBe(true);
|
|
79
|
+
expect(driver.isRetryableError(503, 'Service unavailable')).toBe(true);
|
|
80
|
+
expect(driver.isRetryableError(504, 'Gateway timeout')).toBe(true);
|
|
72
81
|
});
|
|
73
82
|
|
|
74
83
|
it('should mark 4xx as not retryable (except 429, 408)', () => {
|
|
75
|
-
expect(driver
|
|
76
|
-
expect(driver
|
|
77
|
-
expect(driver
|
|
78
|
-
expect(driver
|
|
84
|
+
expect(driver.isRetryableError(400, 'Bad request')).toBe(false);
|
|
85
|
+
expect(driver.isRetryableError(401, 'Unauthorized')).toBe(false);
|
|
86
|
+
expect(driver.isRetryableError(403, 'Forbidden')).toBe(false);
|
|
87
|
+
expect(driver.isRetryableError(404, 'Not found')).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should honor transient provider messages before non-retryable 4xx status codes', () => {
|
|
91
|
+
expect(
|
|
92
|
+
driver.isRetryableError(400, 'Unable to fetch URL. Status: URL_REJECTED-REJECTED_CLIENT_THROTTLED'),
|
|
93
|
+
).toBe(true);
|
|
94
|
+
expect(
|
|
95
|
+
driver.isRetryableError(400, 'Unable to fetch URL. Status: URL_REJECTED-REJECTED_RATE_LIMITED'),
|
|
96
|
+
).toBe(true);
|
|
97
|
+
expect(driver.isRetryableError(400, 'Request throttled by upstream host')).toBe(true);
|
|
98
|
+
expect(driver.isRetryableError(400, 'Please retry with a valid model id')).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should mark transport aborts and deadline-exceeded as retryable even under a 4xx status', () => {
|
|
102
|
+
expect(driver.isRetryableError(undefined, 'This operation was aborted')).toBe(true);
|
|
103
|
+
expect(driver.isRetryableError(499, 'This operation was aborted')).toBe(true);
|
|
104
|
+
expect(driver.isRetryableError(400, 'Deadline expired before operation could complete')).toBe(true);
|
|
105
|
+
expect(driver.isRetryableError(504, '[504] DEADLINE_EXCEEDED')).toBe(true);
|
|
106
|
+
// a genuine non-transient 4xx without an abort/deadline signal stays non-retryable
|
|
107
|
+
expect(driver.isRetryableError(400, 'Invalid argument')).toBe(false);
|
|
79
108
|
});
|
|
80
109
|
|
|
81
110
|
it('should mark 2xx and 3xx as not retryable', () => {
|
|
82
|
-
expect(driver
|
|
83
|
-
expect(driver
|
|
111
|
+
expect(driver.isRetryableError(200, 'OK')).toBe(false);
|
|
112
|
+
expect(driver.isRetryableError(301, 'Moved permanently')).toBe(false);
|
|
84
113
|
});
|
|
85
114
|
});
|
|
86
115
|
|
|
87
116
|
describe('message-based detection', () => {
|
|
88
117
|
it('should detect rate limit in message', () => {
|
|
89
|
-
expect(driver
|
|
90
|
-
expect(driver
|
|
91
|
-
expect(driver
|
|
118
|
+
expect(driver.isRetryableError(undefined, 'Rate limit exceeded')).toBe(true);
|
|
119
|
+
expect(driver.isRetryableError(undefined, 'You have hit the rate limit')).toBe(true);
|
|
120
|
+
expect(driver.isRetryableError(undefined, 'RATE_LIMIT_EXCEEDED')).toBe(true);
|
|
92
121
|
});
|
|
93
122
|
|
|
94
123
|
it('should detect timeout in message', () => {
|
|
95
|
-
expect(driver
|
|
96
|
-
expect(driver
|
|
97
|
-
expect(driver
|
|
124
|
+
expect(driver.isRetryableError(undefined, 'Request timeout')).toBe(true);
|
|
125
|
+
expect(driver.isRetryableError(undefined, 'Connection timed out')).toBe(true);
|
|
126
|
+
expect(driver.isRetryableError(undefined, 'TIMEOUT_ERROR')).toBe(true);
|
|
98
127
|
});
|
|
99
128
|
|
|
100
129
|
it('should detect retry in message', () => {
|
|
101
|
-
expect(driver
|
|
102
|
-
expect(driver
|
|
130
|
+
expect(driver.isRetryableError(undefined, 'Please retry later')).toBe(true);
|
|
131
|
+
expect(driver.isRetryableError(undefined, 'Retry the request')).toBe(true);
|
|
103
132
|
});
|
|
104
133
|
|
|
105
134
|
it('should detect overload in message', () => {
|
|
106
|
-
expect(driver
|
|
107
|
-
expect(driver
|
|
108
|
-
expect(driver
|
|
135
|
+
expect(driver.isRetryableError(undefined, 'Service overloaded')).toBe(true);
|
|
136
|
+
expect(driver.isRetryableError(undefined, 'Server is overload')).toBe(true);
|
|
137
|
+
expect(driver.isRetryableError(undefined, 'System overloaded')).toBe(true);
|
|
109
138
|
});
|
|
110
139
|
|
|
111
140
|
it('should detect resource exhausted in message', () => {
|
|
112
|
-
expect(driver
|
|
113
|
-
expect(driver
|
|
141
|
+
expect(driver.isRetryableError(undefined, 'Resource exhausted')).toBe(true);
|
|
142
|
+
expect(driver.isRetryableError(undefined, 'Resources exhausted')).toBe(true);
|
|
114
143
|
});
|
|
115
144
|
|
|
116
145
|
it('should detect throttle in message', () => {
|
|
117
|
-
expect(driver
|
|
118
|
-
expect(driver
|
|
146
|
+
expect(driver.isRetryableError(undefined, 'Request throttled')).toBe(true);
|
|
147
|
+
expect(driver.isRetryableError(undefined, 'Throttling exception')).toBe(true);
|
|
119
148
|
});
|
|
120
149
|
|
|
121
150
|
it('should detect status codes in message', () => {
|
|
122
|
-
expect(driver
|
|
123
|
-
expect(driver
|
|
151
|
+
expect(driver.isRetryableError(undefined, 'Error 429: Too many requests')).toBe(true);
|
|
152
|
+
expect(driver.isRetryableError(undefined, 'HTTP 529 error')).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should classify invalid_grant credential issuer failures as non-retryable', () => {
|
|
156
|
+
expect(
|
|
157
|
+
driver.isRetryableError(
|
|
158
|
+
undefined,
|
|
159
|
+
"Error code invalid_grant: Error connecting to the given credential's issuer.",
|
|
160
|
+
),
|
|
161
|
+
).toBe(false);
|
|
124
162
|
});
|
|
125
163
|
|
|
126
164
|
it('should mark unknown messages as undefined (let consumer decide)', () => {
|
|
127
|
-
expect(driver
|
|
128
|
-
expect(driver
|
|
129
|
-
expect(driver
|
|
165
|
+
expect(driver.isRetryableError(undefined, 'Invalid API key')).toBeUndefined();
|
|
166
|
+
expect(driver.isRetryableError(undefined, 'Bad request')).toBeUndefined();
|
|
167
|
+
expect(driver.isRetryableError(undefined, 'Model not found')).toBeUndefined();
|
|
130
168
|
});
|
|
131
169
|
});
|
|
132
170
|
});
|
|
133
171
|
|
|
134
172
|
describe('formatLlumiverseError', () => {
|
|
135
173
|
it('should format error with status code', () => {
|
|
136
|
-
const originalError =
|
|
137
|
-
(originalError as any).status = 429;
|
|
174
|
+
const originalError = errorWith('Rate limit exceeded', { status: 429 });
|
|
138
175
|
|
|
139
|
-
const formatted = driver
|
|
176
|
+
const formatted = driver.formatLlumiverseError(originalError, mockContext);
|
|
140
177
|
|
|
141
178
|
expect(formatted).toBeInstanceOf(LlumiverseError);
|
|
142
179
|
expect(formatted.code).toBe(429);
|
|
@@ -148,20 +185,18 @@ describe('AbstractDriver Error Formatting', () => {
|
|
|
148
185
|
});
|
|
149
186
|
|
|
150
187
|
it('should extract status from statusCode property', () => {
|
|
151
|
-
const originalError =
|
|
152
|
-
(originalError as any).statusCode = 500;
|
|
188
|
+
const originalError = errorWith('Server error', { statusCode: 500 });
|
|
153
189
|
|
|
154
|
-
const formatted = driver
|
|
190
|
+
const formatted = driver.formatLlumiverseError(originalError, mockContext);
|
|
155
191
|
|
|
156
192
|
expect(formatted.code).toBe(500);
|
|
157
193
|
expect(formatted.retryable).toBe(true);
|
|
158
194
|
});
|
|
159
195
|
|
|
160
196
|
it('should extract status from code property', () => {
|
|
161
|
-
const originalError =
|
|
162
|
-
(originalError as any).code = 408;
|
|
197
|
+
const originalError = errorWith('Timeout', { code: 408 });
|
|
163
198
|
|
|
164
|
-
const formatted = driver
|
|
199
|
+
const formatted = driver.formatLlumiverseError(originalError, mockContext);
|
|
165
200
|
|
|
166
201
|
expect(formatted.code).toBe(408);
|
|
167
202
|
expect(formatted.retryable).toBe(true);
|
|
@@ -170,7 +205,7 @@ describe('AbstractDriver Error Formatting', () => {
|
|
|
170
205
|
it('should use undefined when no status code found', () => {
|
|
171
206
|
const originalError = new Error('Generic error');
|
|
172
207
|
|
|
173
|
-
const formatted = driver
|
|
208
|
+
const formatted = driver.formatLlumiverseError(originalError, mockContext);
|
|
174
209
|
|
|
175
210
|
expect(formatted.code).toBeUndefined();
|
|
176
211
|
expect(formatted.retryable).toBeUndefined(); // Unknown retryability
|
|
@@ -179,7 +214,7 @@ describe('AbstractDriver Error Formatting', () => {
|
|
|
179
214
|
it('should handle non-Error objects', () => {
|
|
180
215
|
const originalError = 'String error message';
|
|
181
216
|
|
|
182
|
-
const formatted = driver
|
|
217
|
+
const formatted = driver.formatLlumiverseError(originalError, mockContext);
|
|
183
218
|
|
|
184
219
|
expect(formatted.message).toContain('String error message');
|
|
185
220
|
expect(formatted.originalError).toBe(originalError);
|
|
@@ -188,46 +223,41 @@ describe('AbstractDriver Error Formatting', () => {
|
|
|
188
223
|
it('should preserve provider in message', () => {
|
|
189
224
|
const error = new Error('Test error');
|
|
190
225
|
|
|
191
|
-
const formatted = driver
|
|
226
|
+
const formatted = driver.formatLlumiverseError(error, mockContext);
|
|
192
227
|
|
|
193
228
|
expect(formatted.message).toMatch(/^\[test-provider\]/);
|
|
194
229
|
});
|
|
195
230
|
|
|
196
231
|
it('should determine retryability based on status and message', () => {
|
|
197
232
|
// Retryable by status
|
|
198
|
-
const retryableError =
|
|
199
|
-
(retryableError
|
|
200
|
-
const formatted1 = driver['formatLlumiverseError'](retryableError, mockContext);
|
|
233
|
+
const retryableError = errorWith('Error', { status: 429 });
|
|
234
|
+
const formatted1 = driver.formatLlumiverseError(retryableError, mockContext);
|
|
201
235
|
expect(formatted1.retryable).toBe(true);
|
|
202
236
|
|
|
203
237
|
// Not retryable by status
|
|
204
|
-
const nonRetryableError =
|
|
205
|
-
(nonRetryableError
|
|
206
|
-
const formatted2 = driver['formatLlumiverseError'](nonRetryableError, mockContext);
|
|
238
|
+
const nonRetryableError = errorWith('Error', { status: 400 });
|
|
239
|
+
const formatted2 = driver.formatLlumiverseError(nonRetryableError, mockContext);
|
|
207
240
|
expect(formatted2.retryable).toBe(false);
|
|
208
241
|
|
|
209
242
|
// Retryable by message
|
|
210
243
|
const messageRetryable = new Error('Rate limit exceeded');
|
|
211
|
-
const formatted3 = driver
|
|
244
|
+
const formatted3 = driver.formatLlumiverseError(messageRetryable, mockContext);
|
|
212
245
|
expect(formatted3.retryable).toBe(true);
|
|
213
246
|
});
|
|
214
247
|
});
|
|
215
248
|
|
|
216
249
|
describe('driver override capability', () => {
|
|
217
250
|
class CustomDriver extends TestDriver {
|
|
218
|
-
public formatLlumiverseError(
|
|
219
|
-
error: unknown,
|
|
220
|
-
context: LlumiverseErrorContext
|
|
221
|
-
): LlumiverseError {
|
|
251
|
+
public formatLlumiverseError(error: unknown, context: LlumiverseErrorContext): LlumiverseError {
|
|
222
252
|
// Custom logic: check for specific error type
|
|
223
|
-
if ((error
|
|
253
|
+
if (getProp(error, 'type') === 'custom_retryable') {
|
|
224
254
|
return new LlumiverseError(
|
|
225
255
|
`[${this.provider}] Custom retryable error`,
|
|
226
256
|
true,
|
|
227
257
|
context,
|
|
228
258
|
error,
|
|
229
259
|
undefined,
|
|
230
|
-
'CUSTOM_ERROR'
|
|
260
|
+
'CUSTOM_ERROR',
|
|
231
261
|
);
|
|
232
262
|
}
|
|
233
263
|
// Fall back to default
|
|
@@ -239,7 +269,7 @@ describe('AbstractDriver Error Formatting', () => {
|
|
|
239
269
|
const customDriver = new CustomDriver({});
|
|
240
270
|
const customError = { type: 'custom_retryable', message: 'Custom error' };
|
|
241
271
|
|
|
242
|
-
const formatted = customDriver
|
|
272
|
+
const formatted = customDriver.formatLlumiverseError(customError, mockContext);
|
|
243
273
|
|
|
244
274
|
expect(formatted.name).toBe('CUSTOM_ERROR');
|
|
245
275
|
expect(formatted.code).toBeUndefined();
|
|
@@ -249,10 +279,9 @@ describe('AbstractDriver Error Formatting', () => {
|
|
|
249
279
|
|
|
250
280
|
it('should fall back to default for non-custom errors', () => {
|
|
251
281
|
const customDriver = new CustomDriver({});
|
|
252
|
-
const regularError =
|
|
253
|
-
(regularError as any).status = 500;
|
|
282
|
+
const regularError = errorWith('Regular error', { status: 500 });
|
|
254
283
|
|
|
255
|
-
const formatted = customDriver
|
|
284
|
+
const formatted = customDriver.formatLlumiverseError(regularError, mockContext);
|
|
256
285
|
|
|
257
286
|
expect(formatted.code).toBe(500);
|
|
258
287
|
expect(formatted.retryable).toBe(true);
|