@yourgpt/llm-sdk 2.1.4-alpha.2 → 2.1.5
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/dist/adapters/index.d.mts +3 -2
- package/dist/adapters/index.d.ts +3 -2
- package/dist/{types-DoZX2k3v.d.mts → base-5n-UuPfS.d.mts} +1 -353
- package/dist/{types-DtPQaoJi.d.ts → base-Di31iy_8.d.ts} +1 -353
- package/dist/fallback/index.d.mts +96 -0
- package/dist/fallback/index.d.ts +96 -0
- package/dist/fallback/index.js +284 -0
- package/dist/fallback/index.mjs +280 -0
- package/dist/index.d.mts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/providers/anthropic/index.d.mts +2 -1
- package/dist/providers/anthropic/index.d.ts +2 -1
- package/dist/providers/azure/index.d.mts +2 -1
- package/dist/providers/azure/index.d.ts +2 -1
- package/dist/providers/google/index.d.mts +2 -1
- package/dist/providers/google/index.d.ts +2 -1
- package/dist/providers/ollama/index.d.mts +3 -2
- package/dist/providers/ollama/index.d.ts +3 -2
- package/dist/providers/openai/index.d.mts +2 -1
- package/dist/providers/openai/index.d.ts +2 -1
- package/dist/providers/openrouter/index.d.mts +2 -1
- package/dist/providers/openrouter/index.d.ts +2 -1
- package/dist/providers/xai/index.d.mts +2 -1
- package/dist/providers/xai/index.d.ts +2 -1
- package/dist/types-BQl1suAv.d.mts +212 -0
- package/dist/types-C0vLXzuw.d.ts +355 -0
- package/dist/types-CNL8ZRne.d.ts +212 -0
- package/dist/types-VDgiUvH2.d.mts +355 -0
- package/package.json +7 -1
- package/dist/adapters/index.js.map +0 -1
- package/dist/adapters/index.mjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/providers/anthropic/index.js.map +0 -1
- package/dist/providers/anthropic/index.mjs.map +0 -1
- package/dist/providers/azure/index.js.map +0 -1
- package/dist/providers/azure/index.mjs.map +0 -1
- package/dist/providers/google/index.js.map +0 -1
- package/dist/providers/google/index.mjs.map +0 -1
- package/dist/providers/ollama/index.js.map +0 -1
- package/dist/providers/ollama/index.mjs.map +0 -1
- package/dist/providers/openai/index.js.map +0 -1
- package/dist/providers/openai/index.mjs.map +0 -1
- package/dist/providers/openrouter/index.js.map +0 -1
- package/dist/providers/openrouter/index.mjs.map +0 -1
- package/dist/providers/xai/index.js.map +0 -1
- package/dist/providers/xai/index.mjs.map +0 -1
- package/dist/yourgpt/index.js.map +0 -1
- package/dist/yourgpt/index.mjs.map +0 -1
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/fallback/errors.ts
|
|
4
|
+
var FallbackExhaustedError = class extends Error {
|
|
5
|
+
constructor(failures) {
|
|
6
|
+
const summary = failures.map((f) => `${f.provider}/${f.model}: ${f.error.message}`).join("; ");
|
|
7
|
+
super(
|
|
8
|
+
`All ${failures.length} model(s) in the fallback chain failed. ${summary}`
|
|
9
|
+
);
|
|
10
|
+
this.name = "FallbackExhaustedError";
|
|
11
|
+
this.failures = failures;
|
|
12
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// src/fallback/routing-store.ts
|
|
17
|
+
var MemoryRoutingStore = class {
|
|
18
|
+
constructor() {
|
|
19
|
+
this._map = /* @__PURE__ */ new Map();
|
|
20
|
+
}
|
|
21
|
+
async get(key) {
|
|
22
|
+
return this._map.get(key);
|
|
23
|
+
}
|
|
24
|
+
async set(key, value) {
|
|
25
|
+
this._map.set(key, value);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// src/fallback/chain.ts
|
|
30
|
+
var ROUND_ROBIN_KEY = "ygpt_fallback_rr_index";
|
|
31
|
+
function defaultIsRetryable(error) {
|
|
32
|
+
if (typeof error === "object" && error !== null) {
|
|
33
|
+
const ctorName = error.constructor?.name ?? "";
|
|
34
|
+
if (ctorName === "APIUserAbortError") return false;
|
|
35
|
+
if (ctorName === "APIConnectionError") return true;
|
|
36
|
+
if (ctorName === "APIConnectionTimeoutError") return true;
|
|
37
|
+
const status = error.status ?? error.statusCode;
|
|
38
|
+
if (typeof status === "number") {
|
|
39
|
+
if (status === 429) return true;
|
|
40
|
+
if (status >= 500) return true;
|
|
41
|
+
if (status >= 400) return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (error instanceof Error) {
|
|
45
|
+
const msg = error.message;
|
|
46
|
+
if (/the operation was aborted/i.test(msg)) return false;
|
|
47
|
+
if (/the user aborted a request/i.test(msg)) return false;
|
|
48
|
+
if (/\b4[0-9]{2}\b/.test(msg) && !/\b429\b/.test(msg)) return false;
|
|
49
|
+
if (/\b429\b/.test(msg)) return true;
|
|
50
|
+
if (/rate[\s_-]?limit/i.test(msg)) return true;
|
|
51
|
+
if (/too many requests/i.test(msg)) return true;
|
|
52
|
+
if (/quota exceeded/i.test(msg)) return true;
|
|
53
|
+
if (/\b5[0-9]{2}\b/.test(msg)) return true;
|
|
54
|
+
if (/internal server error/i.test(msg)) return true;
|
|
55
|
+
if (/service unavailable/i.test(msg)) return true;
|
|
56
|
+
if (/bad gateway/i.test(msg)) return true;
|
|
57
|
+
if (/gateway timeout/i.test(msg)) return true;
|
|
58
|
+
if (/overloaded/i.test(msg)) return true;
|
|
59
|
+
if (/^connection error\.?$/i.test(msg)) return true;
|
|
60
|
+
if (/^request timed out\.?$/i.test(msg)) return true;
|
|
61
|
+
if (/timed?\s*out/i.test(msg)) return true;
|
|
62
|
+
if (/timeout/i.test(msg)) return true;
|
|
63
|
+
if (/fetch failed/i.test(msg)) return true;
|
|
64
|
+
if (/ECONNREFUSED/.test(msg)) return true;
|
|
65
|
+
if (/ECONNRESET/.test(msg)) return true;
|
|
66
|
+
if (/ETIMEDOUT/.test(msg)) return true;
|
|
67
|
+
if (/ENOTFOUND/.test(msg)) return true;
|
|
68
|
+
if (/ENETUNREACH/.test(msg)) return true;
|
|
69
|
+
if (/EHOSTUNREACH/.test(msg)) return true;
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
function calcDelay(base, attempt, backoff) {
|
|
74
|
+
if (backoff === "fixed") return base;
|
|
75
|
+
return base * Math.pow(2, attempt - 1);
|
|
76
|
+
}
|
|
77
|
+
function sleep(ms) {
|
|
78
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
79
|
+
}
|
|
80
|
+
var FallbackChain = class {
|
|
81
|
+
constructor(config) {
|
|
82
|
+
if (config.models.length === 0) {
|
|
83
|
+
throw new Error("FallbackChain requires at least one model.");
|
|
84
|
+
}
|
|
85
|
+
this._config = {
|
|
86
|
+
models: config.models,
|
|
87
|
+
strategy: config.strategy ?? "priority",
|
|
88
|
+
store: config.store ?? new MemoryRoutingStore(),
|
|
89
|
+
retries: config.retries ?? 0,
|
|
90
|
+
retryDelay: config.retryDelay ?? 500,
|
|
91
|
+
retryBackoff: config.retryBackoff ?? "exponential",
|
|
92
|
+
onFallback: config.onFallback,
|
|
93
|
+
onRetry: config.onRetry,
|
|
94
|
+
retryableErrors: config.retryableErrors
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
get provider() {
|
|
98
|
+
return "fallback-chain";
|
|
99
|
+
}
|
|
100
|
+
get model() {
|
|
101
|
+
return this._config.models.map((m) => `${m.provider}/${m.model}`).join(",");
|
|
102
|
+
}
|
|
103
|
+
async _startIndex() {
|
|
104
|
+
if (this._config.strategy !== "round-robin") return 0;
|
|
105
|
+
const stored = await this._config.store.get(ROUND_ROBIN_KEY);
|
|
106
|
+
return typeof stored === "number" ? stored % this._config.models.length : 0;
|
|
107
|
+
}
|
|
108
|
+
async _advanceIndex(successfulIndex) {
|
|
109
|
+
if (this._config.strategy !== "round-robin") return;
|
|
110
|
+
const next = (successfulIndex + 1) % this._config.models.length;
|
|
111
|
+
await this._config.store.set(ROUND_ROBIN_KEY, next);
|
|
112
|
+
}
|
|
113
|
+
_isRetryable(error) {
|
|
114
|
+
return this._config.retryableErrors ? this._config.retryableErrors(error) : defaultIsRetryable(error);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Try streaming from a single adapter, with per-model retries.
|
|
118
|
+
*
|
|
119
|
+
* Returns an async generator that either:
|
|
120
|
+
* - yields all chunks on success, then returns
|
|
121
|
+
* - throws the final error if all retries exhausted or error is non-retryable
|
|
122
|
+
*
|
|
123
|
+
* The `retriesAttempted` out-param is filled via the returned object so callers
|
|
124
|
+
* can record it in FallbackFailure.
|
|
125
|
+
*/
|
|
126
|
+
async *_streamWithRetries(adapter, request, out) {
|
|
127
|
+
const { retries, retryDelay, retryBackoff, onRetry } = this._config;
|
|
128
|
+
const maxAttempts = retries + 1;
|
|
129
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
130
|
+
let contentStarted = false;
|
|
131
|
+
let failureError = null;
|
|
132
|
+
try {
|
|
133
|
+
for await (const chunk of adapter.stream(request)) {
|
|
134
|
+
if (chunk.type === "error") {
|
|
135
|
+
if (!contentStarted) {
|
|
136
|
+
const msg = chunk.message ?? "Unknown error";
|
|
137
|
+
failureError = new Error(msg);
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
yield chunk;
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (chunk.type === "message:start") continue;
|
|
144
|
+
contentStarted = true;
|
|
145
|
+
yield chunk;
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (contentStarted) throw error;
|
|
149
|
+
if (!this._isRetryable(error)) throw error;
|
|
150
|
+
failureError = error instanceof Error ? error : new Error(String(error));
|
|
151
|
+
}
|
|
152
|
+
if (failureError === null) return;
|
|
153
|
+
if (!this._isRetryable(failureError)) throw failureError;
|
|
154
|
+
out.retriesAttempted = attempt - 1;
|
|
155
|
+
if (attempt < maxAttempts) {
|
|
156
|
+
const delayMs = calcDelay(retryDelay, attempt, retryBackoff);
|
|
157
|
+
const retryInfo = {
|
|
158
|
+
model: adapter.model,
|
|
159
|
+
provider: adapter.provider,
|
|
160
|
+
error: failureError,
|
|
161
|
+
retryAttempt: attempt,
|
|
162
|
+
maxRetries: retries,
|
|
163
|
+
delayMs
|
|
164
|
+
};
|
|
165
|
+
onRetry?.(retryInfo);
|
|
166
|
+
await sleep(delayMs);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
out.retriesAttempted = retries;
|
|
170
|
+
throw failureError;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async *stream(request) {
|
|
174
|
+
const { models, onFallback } = this._config;
|
|
175
|
+
const startIndex = await this._startIndex();
|
|
176
|
+
const failures = [];
|
|
177
|
+
for (let i = 0; i < models.length; i++) {
|
|
178
|
+
const index = (startIndex + i) % models.length;
|
|
179
|
+
const adapter = models[index];
|
|
180
|
+
const out = { retriesAttempted: 0 };
|
|
181
|
+
try {
|
|
182
|
+
yield* this._streamWithRetries(adapter, request, out);
|
|
183
|
+
await this._advanceIndex(index);
|
|
184
|
+
return;
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (!this._isRetryable(error)) throw error;
|
|
187
|
+
const failure = {
|
|
188
|
+
model: adapter.model,
|
|
189
|
+
provider: adapter.provider,
|
|
190
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
191
|
+
attempt: i + 1,
|
|
192
|
+
retriesAttempted: out.retriesAttempted
|
|
193
|
+
};
|
|
194
|
+
failures.push(failure);
|
|
195
|
+
const nextOffset = i + 1;
|
|
196
|
+
if (nextOffset < models.length && onFallback) {
|
|
197
|
+
const nextIndex = (startIndex + nextOffset) % models.length;
|
|
198
|
+
onFallback({
|
|
199
|
+
attemptedModel: adapter.model,
|
|
200
|
+
nextModel: models[nextIndex].model,
|
|
201
|
+
error: failure.error,
|
|
202
|
+
attempt: failure.attempt
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
throw new FallbackExhaustedError(failures);
|
|
208
|
+
}
|
|
209
|
+
async complete(request) {
|
|
210
|
+
const { models, onFallback, retries, retryDelay, retryBackoff, onRetry } = this._config;
|
|
211
|
+
const startIndex = await this._startIndex();
|
|
212
|
+
const failures = [];
|
|
213
|
+
for (let i = 0; i < models.length; i++) {
|
|
214
|
+
const index = (startIndex + i) % models.length;
|
|
215
|
+
const adapter = models[index];
|
|
216
|
+
if (!adapter.complete) {
|
|
217
|
+
failures.push({
|
|
218
|
+
model: adapter.model,
|
|
219
|
+
provider: adapter.provider,
|
|
220
|
+
error: new Error(
|
|
221
|
+
`Adapter ${adapter.provider}/${adapter.model} does not implement complete()`
|
|
222
|
+
),
|
|
223
|
+
attempt: i + 1,
|
|
224
|
+
retriesAttempted: 0
|
|
225
|
+
});
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const maxAttempts = retries + 1;
|
|
229
|
+
let lastError = null;
|
|
230
|
+
let retriesAttempted = 0;
|
|
231
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
232
|
+
try {
|
|
233
|
+
const result = await adapter.complete(request);
|
|
234
|
+
await this._advanceIndex(index);
|
|
235
|
+
return result;
|
|
236
|
+
} catch (error) {
|
|
237
|
+
if (!this._isRetryable(error)) throw error;
|
|
238
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
239
|
+
retriesAttempted = attempt - 1;
|
|
240
|
+
if (attempt < maxAttempts) {
|
|
241
|
+
const delayMs = calcDelay(retryDelay, attempt, retryBackoff);
|
|
242
|
+
onRetry?.({
|
|
243
|
+
model: adapter.model,
|
|
244
|
+
provider: adapter.provider,
|
|
245
|
+
error: lastError,
|
|
246
|
+
retryAttempt: attempt,
|
|
247
|
+
maxRetries: retries,
|
|
248
|
+
delayMs
|
|
249
|
+
});
|
|
250
|
+
await sleep(delayMs);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const failure = {
|
|
255
|
+
model: adapter.model,
|
|
256
|
+
provider: adapter.provider,
|
|
257
|
+
error: lastError,
|
|
258
|
+
attempt: i + 1,
|
|
259
|
+
retriesAttempted
|
|
260
|
+
};
|
|
261
|
+
failures.push(failure);
|
|
262
|
+
const nextOffset = i + 1;
|
|
263
|
+
if (nextOffset < models.length && onFallback) {
|
|
264
|
+
const nextIndex = (startIndex + nextOffset) % models.length;
|
|
265
|
+
onFallback({
|
|
266
|
+
attemptedModel: adapter.model,
|
|
267
|
+
nextModel: models[nextIndex].model,
|
|
268
|
+
error: failure.error,
|
|
269
|
+
attempt: failure.attempt
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
throw new FallbackExhaustedError(failures);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
function createFallbackChain(config) {
|
|
277
|
+
return new FallbackChain(config);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
exports.FallbackExhaustedError = FallbackExhaustedError;
|
|
281
|
+
exports.MemoryRoutingStore = MemoryRoutingStore;
|
|
282
|
+
exports.createFallbackChain = createFallbackChain;
|
|
283
|
+
//# sourceMappingURL=index.js.map
|
|
284
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
// src/fallback/errors.ts
|
|
2
|
+
var FallbackExhaustedError = class extends Error {
|
|
3
|
+
constructor(failures) {
|
|
4
|
+
const summary = failures.map((f) => `${f.provider}/${f.model}: ${f.error.message}`).join("; ");
|
|
5
|
+
super(
|
|
6
|
+
`All ${failures.length} model(s) in the fallback chain failed. ${summary}`
|
|
7
|
+
);
|
|
8
|
+
this.name = "FallbackExhaustedError";
|
|
9
|
+
this.failures = failures;
|
|
10
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// src/fallback/routing-store.ts
|
|
15
|
+
var MemoryRoutingStore = class {
|
|
16
|
+
constructor() {
|
|
17
|
+
this._map = /* @__PURE__ */ new Map();
|
|
18
|
+
}
|
|
19
|
+
async get(key) {
|
|
20
|
+
return this._map.get(key);
|
|
21
|
+
}
|
|
22
|
+
async set(key, value) {
|
|
23
|
+
this._map.set(key, value);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// src/fallback/chain.ts
|
|
28
|
+
var ROUND_ROBIN_KEY = "ygpt_fallback_rr_index";
|
|
29
|
+
function defaultIsRetryable(error) {
|
|
30
|
+
if (typeof error === "object" && error !== null) {
|
|
31
|
+
const ctorName = error.constructor?.name ?? "";
|
|
32
|
+
if (ctorName === "APIUserAbortError") return false;
|
|
33
|
+
if (ctorName === "APIConnectionError") return true;
|
|
34
|
+
if (ctorName === "APIConnectionTimeoutError") return true;
|
|
35
|
+
const status = error.status ?? error.statusCode;
|
|
36
|
+
if (typeof status === "number") {
|
|
37
|
+
if (status === 429) return true;
|
|
38
|
+
if (status >= 500) return true;
|
|
39
|
+
if (status >= 400) return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (error instanceof Error) {
|
|
43
|
+
const msg = error.message;
|
|
44
|
+
if (/the operation was aborted/i.test(msg)) return false;
|
|
45
|
+
if (/the user aborted a request/i.test(msg)) return false;
|
|
46
|
+
if (/\b4[0-9]{2}\b/.test(msg) && !/\b429\b/.test(msg)) return false;
|
|
47
|
+
if (/\b429\b/.test(msg)) return true;
|
|
48
|
+
if (/rate[\s_-]?limit/i.test(msg)) return true;
|
|
49
|
+
if (/too many requests/i.test(msg)) return true;
|
|
50
|
+
if (/quota exceeded/i.test(msg)) return true;
|
|
51
|
+
if (/\b5[0-9]{2}\b/.test(msg)) return true;
|
|
52
|
+
if (/internal server error/i.test(msg)) return true;
|
|
53
|
+
if (/service unavailable/i.test(msg)) return true;
|
|
54
|
+
if (/bad gateway/i.test(msg)) return true;
|
|
55
|
+
if (/gateway timeout/i.test(msg)) return true;
|
|
56
|
+
if (/overloaded/i.test(msg)) return true;
|
|
57
|
+
if (/^connection error\.?$/i.test(msg)) return true;
|
|
58
|
+
if (/^request timed out\.?$/i.test(msg)) return true;
|
|
59
|
+
if (/timed?\s*out/i.test(msg)) return true;
|
|
60
|
+
if (/timeout/i.test(msg)) return true;
|
|
61
|
+
if (/fetch failed/i.test(msg)) return true;
|
|
62
|
+
if (/ECONNREFUSED/.test(msg)) return true;
|
|
63
|
+
if (/ECONNRESET/.test(msg)) return true;
|
|
64
|
+
if (/ETIMEDOUT/.test(msg)) return true;
|
|
65
|
+
if (/ENOTFOUND/.test(msg)) return true;
|
|
66
|
+
if (/ENETUNREACH/.test(msg)) return true;
|
|
67
|
+
if (/EHOSTUNREACH/.test(msg)) return true;
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
function calcDelay(base, attempt, backoff) {
|
|
72
|
+
if (backoff === "fixed") return base;
|
|
73
|
+
return base * Math.pow(2, attempt - 1);
|
|
74
|
+
}
|
|
75
|
+
function sleep(ms) {
|
|
76
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
77
|
+
}
|
|
78
|
+
var FallbackChain = class {
|
|
79
|
+
constructor(config) {
|
|
80
|
+
if (config.models.length === 0) {
|
|
81
|
+
throw new Error("FallbackChain requires at least one model.");
|
|
82
|
+
}
|
|
83
|
+
this._config = {
|
|
84
|
+
models: config.models,
|
|
85
|
+
strategy: config.strategy ?? "priority",
|
|
86
|
+
store: config.store ?? new MemoryRoutingStore(),
|
|
87
|
+
retries: config.retries ?? 0,
|
|
88
|
+
retryDelay: config.retryDelay ?? 500,
|
|
89
|
+
retryBackoff: config.retryBackoff ?? "exponential",
|
|
90
|
+
onFallback: config.onFallback,
|
|
91
|
+
onRetry: config.onRetry,
|
|
92
|
+
retryableErrors: config.retryableErrors
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
get provider() {
|
|
96
|
+
return "fallback-chain";
|
|
97
|
+
}
|
|
98
|
+
get model() {
|
|
99
|
+
return this._config.models.map((m) => `${m.provider}/${m.model}`).join(",");
|
|
100
|
+
}
|
|
101
|
+
async _startIndex() {
|
|
102
|
+
if (this._config.strategy !== "round-robin") return 0;
|
|
103
|
+
const stored = await this._config.store.get(ROUND_ROBIN_KEY);
|
|
104
|
+
return typeof stored === "number" ? stored % this._config.models.length : 0;
|
|
105
|
+
}
|
|
106
|
+
async _advanceIndex(successfulIndex) {
|
|
107
|
+
if (this._config.strategy !== "round-robin") return;
|
|
108
|
+
const next = (successfulIndex + 1) % this._config.models.length;
|
|
109
|
+
await this._config.store.set(ROUND_ROBIN_KEY, next);
|
|
110
|
+
}
|
|
111
|
+
_isRetryable(error) {
|
|
112
|
+
return this._config.retryableErrors ? this._config.retryableErrors(error) : defaultIsRetryable(error);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Try streaming from a single adapter, with per-model retries.
|
|
116
|
+
*
|
|
117
|
+
* Returns an async generator that either:
|
|
118
|
+
* - yields all chunks on success, then returns
|
|
119
|
+
* - throws the final error if all retries exhausted or error is non-retryable
|
|
120
|
+
*
|
|
121
|
+
* The `retriesAttempted` out-param is filled via the returned object so callers
|
|
122
|
+
* can record it in FallbackFailure.
|
|
123
|
+
*/
|
|
124
|
+
async *_streamWithRetries(adapter, request, out) {
|
|
125
|
+
const { retries, retryDelay, retryBackoff, onRetry } = this._config;
|
|
126
|
+
const maxAttempts = retries + 1;
|
|
127
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
128
|
+
let contentStarted = false;
|
|
129
|
+
let failureError = null;
|
|
130
|
+
try {
|
|
131
|
+
for await (const chunk of adapter.stream(request)) {
|
|
132
|
+
if (chunk.type === "error") {
|
|
133
|
+
if (!contentStarted) {
|
|
134
|
+
const msg = chunk.message ?? "Unknown error";
|
|
135
|
+
failureError = new Error(msg);
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
yield chunk;
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (chunk.type === "message:start") continue;
|
|
142
|
+
contentStarted = true;
|
|
143
|
+
yield chunk;
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (contentStarted) throw error;
|
|
147
|
+
if (!this._isRetryable(error)) throw error;
|
|
148
|
+
failureError = error instanceof Error ? error : new Error(String(error));
|
|
149
|
+
}
|
|
150
|
+
if (failureError === null) return;
|
|
151
|
+
if (!this._isRetryable(failureError)) throw failureError;
|
|
152
|
+
out.retriesAttempted = attempt - 1;
|
|
153
|
+
if (attempt < maxAttempts) {
|
|
154
|
+
const delayMs = calcDelay(retryDelay, attempt, retryBackoff);
|
|
155
|
+
const retryInfo = {
|
|
156
|
+
model: adapter.model,
|
|
157
|
+
provider: adapter.provider,
|
|
158
|
+
error: failureError,
|
|
159
|
+
retryAttempt: attempt,
|
|
160
|
+
maxRetries: retries,
|
|
161
|
+
delayMs
|
|
162
|
+
};
|
|
163
|
+
onRetry?.(retryInfo);
|
|
164
|
+
await sleep(delayMs);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
out.retriesAttempted = retries;
|
|
168
|
+
throw failureError;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async *stream(request) {
|
|
172
|
+
const { models, onFallback } = this._config;
|
|
173
|
+
const startIndex = await this._startIndex();
|
|
174
|
+
const failures = [];
|
|
175
|
+
for (let i = 0; i < models.length; i++) {
|
|
176
|
+
const index = (startIndex + i) % models.length;
|
|
177
|
+
const adapter = models[index];
|
|
178
|
+
const out = { retriesAttempted: 0 };
|
|
179
|
+
try {
|
|
180
|
+
yield* this._streamWithRetries(adapter, request, out);
|
|
181
|
+
await this._advanceIndex(index);
|
|
182
|
+
return;
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (!this._isRetryable(error)) throw error;
|
|
185
|
+
const failure = {
|
|
186
|
+
model: adapter.model,
|
|
187
|
+
provider: adapter.provider,
|
|
188
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
189
|
+
attempt: i + 1,
|
|
190
|
+
retriesAttempted: out.retriesAttempted
|
|
191
|
+
};
|
|
192
|
+
failures.push(failure);
|
|
193
|
+
const nextOffset = i + 1;
|
|
194
|
+
if (nextOffset < models.length && onFallback) {
|
|
195
|
+
const nextIndex = (startIndex + nextOffset) % models.length;
|
|
196
|
+
onFallback({
|
|
197
|
+
attemptedModel: adapter.model,
|
|
198
|
+
nextModel: models[nextIndex].model,
|
|
199
|
+
error: failure.error,
|
|
200
|
+
attempt: failure.attempt
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
throw new FallbackExhaustedError(failures);
|
|
206
|
+
}
|
|
207
|
+
async complete(request) {
|
|
208
|
+
const { models, onFallback, retries, retryDelay, retryBackoff, onRetry } = this._config;
|
|
209
|
+
const startIndex = await this._startIndex();
|
|
210
|
+
const failures = [];
|
|
211
|
+
for (let i = 0; i < models.length; i++) {
|
|
212
|
+
const index = (startIndex + i) % models.length;
|
|
213
|
+
const adapter = models[index];
|
|
214
|
+
if (!adapter.complete) {
|
|
215
|
+
failures.push({
|
|
216
|
+
model: adapter.model,
|
|
217
|
+
provider: adapter.provider,
|
|
218
|
+
error: new Error(
|
|
219
|
+
`Adapter ${adapter.provider}/${adapter.model} does not implement complete()`
|
|
220
|
+
),
|
|
221
|
+
attempt: i + 1,
|
|
222
|
+
retriesAttempted: 0
|
|
223
|
+
});
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const maxAttempts = retries + 1;
|
|
227
|
+
let lastError = null;
|
|
228
|
+
let retriesAttempted = 0;
|
|
229
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
230
|
+
try {
|
|
231
|
+
const result = await adapter.complete(request);
|
|
232
|
+
await this._advanceIndex(index);
|
|
233
|
+
return result;
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (!this._isRetryable(error)) throw error;
|
|
236
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
237
|
+
retriesAttempted = attempt - 1;
|
|
238
|
+
if (attempt < maxAttempts) {
|
|
239
|
+
const delayMs = calcDelay(retryDelay, attempt, retryBackoff);
|
|
240
|
+
onRetry?.({
|
|
241
|
+
model: adapter.model,
|
|
242
|
+
provider: adapter.provider,
|
|
243
|
+
error: lastError,
|
|
244
|
+
retryAttempt: attempt,
|
|
245
|
+
maxRetries: retries,
|
|
246
|
+
delayMs
|
|
247
|
+
});
|
|
248
|
+
await sleep(delayMs);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const failure = {
|
|
253
|
+
model: adapter.model,
|
|
254
|
+
provider: adapter.provider,
|
|
255
|
+
error: lastError,
|
|
256
|
+
attempt: i + 1,
|
|
257
|
+
retriesAttempted
|
|
258
|
+
};
|
|
259
|
+
failures.push(failure);
|
|
260
|
+
const nextOffset = i + 1;
|
|
261
|
+
if (nextOffset < models.length && onFallback) {
|
|
262
|
+
const nextIndex = (startIndex + nextOffset) % models.length;
|
|
263
|
+
onFallback({
|
|
264
|
+
attemptedModel: adapter.model,
|
|
265
|
+
nextModel: models[nextIndex].model,
|
|
266
|
+
error: failure.error,
|
|
267
|
+
attempt: failure.attempt
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
throw new FallbackExhaustedError(failures);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
function createFallbackChain(config) {
|
|
275
|
+
return new FallbackChain(config);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export { FallbackExhaustedError, MemoryRoutingStore, createFallbackChain };
|
|
279
|
+
//# sourceMappingURL=index.mjs.map
|
|
280
|
+
//# sourceMappingURL=index.mjs.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { G as GenerateTextParams, a as GenerateTextResult, S as StreamTextParams, b as StreamTextResult, T as ToolContext, c as Tool, d as StorageAdapter, e as StorageMessage } from './types-CR8mi9I0.mjs';
|
|
2
2
|
export { A as AssistantMessage, C as CoreMessage, w as DEFAULT_CAPABILITIES, D as DoGenerateParams, f as DoGenerateResult, E as ErrorChunk, F as FilePart, s as FinishChunk, u as FinishReason, m as GenerateStep, I as ImagePart, L as LanguageModel, M as ModelCapabilities, R as ResponseOptions, v as StorageFile, o as StreamChunk, n as StreamPart, g as SystemMessage, p as TextDeltaChunk, j as TextPart, t as TokenUsage, k as ToolCall, q as ToolCallChunk, h as ToolMessage, l as ToolResult, r as ToolResultChunk, i as UserContentPart, U as UserMessage } from './types-CR8mi9I0.mjs';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { A as
|
|
5
|
-
export {
|
|
4
|
+
import { A as ActionDefinition, T as ToolDefinition, a as ToolProfile, K as KnowledgeBaseConfig, W as WebSearchConfig, L as LLMAdapter, D as DoneEventMessage, S as StreamEvent, b as ToolCallInfo, c as TokenUsageRaw, P as ProviderToolRuntimeOptions, M as Message, d as ToolResponse } from './base-5n-UuPfS.mjs';
|
|
5
|
+
export { e as AdapterFactory, m as AnthropicProviderToolOptions, j as AnthropicToolSelectionHints, C as ChatCompletionRequest, n as Citation, f as LLMConfig, l as OpenAIProviderToolOptions, O as OpenAIToolSelectionHints, i as ToolExecution, g as ToolLocation, k as ToolNativeProviderHints, U as UnifiedToolCall, h as UnifiedToolResult } from './base-5n-UuPfS.mjs';
|
|
6
|
+
import { A as AIProvider } from './types-VDgiUvH2.mjs';
|
|
7
|
+
export { a as AnthropicProviderConfig, f as AnthropicTool, h as AnthropicToolResult, g as AnthropicToolUse, b as AzureProviderConfig, B as BaseProviderConfig, m as GeminiFunctionCall, l as GeminiFunctionDeclaration, n as GeminiFunctionResponse, G as GoogleProviderConfig, d as OllamaModelOptions, c as OllamaProviderConfig, O as OpenAIProviderConfig, i as OpenAITool, j as OpenAIToolCall, k as OpenAIToolResult, P as ProviderCapabilities, e as ProviderFormatter, X as XAIProviderConfig } from './types-VDgiUvH2.mjs';
|
|
6
8
|
import * as hono from 'hono';
|
|
7
9
|
import { Hono } from 'hono';
|
|
10
|
+
export { F as FallbackChainConfig, c as FallbackFailure, d as FallbackInfo, b as RetryBackoff, e as RetryInfo, R as RoutingStore, a as RoutingStrategy } from './types-BQl1suAv.mjs';
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
13
|
* generateText - Generate text using a language model
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { G as GenerateTextParams, a as GenerateTextResult, S as StreamTextParams, b as StreamTextResult, T as ToolContext, c as Tool, d as StorageAdapter, e as StorageMessage } from './types-CR8mi9I0.js';
|
|
2
2
|
export { A as AssistantMessage, C as CoreMessage, w as DEFAULT_CAPABILITIES, D as DoGenerateParams, f as DoGenerateResult, E as ErrorChunk, F as FilePart, s as FinishChunk, u as FinishReason, m as GenerateStep, I as ImagePart, L as LanguageModel, M as ModelCapabilities, R as ResponseOptions, v as StorageFile, o as StreamChunk, n as StreamPart, g as SystemMessage, p as TextDeltaChunk, j as TextPart, t as TokenUsage, k as ToolCall, q as ToolCallChunk, h as ToolMessage, l as ToolResult, r as ToolResultChunk, i as UserContentPart, U as UserMessage } from './types-CR8mi9I0.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { A as
|
|
5
|
-
export {
|
|
4
|
+
import { A as ActionDefinition, T as ToolDefinition, a as ToolProfile, K as KnowledgeBaseConfig, W as WebSearchConfig, L as LLMAdapter, D as DoneEventMessage, S as StreamEvent, b as ToolCallInfo, c as TokenUsageRaw, P as ProviderToolRuntimeOptions, M as Message, d as ToolResponse } from './base-Di31iy_8.js';
|
|
5
|
+
export { e as AdapterFactory, m as AnthropicProviderToolOptions, j as AnthropicToolSelectionHints, C as ChatCompletionRequest, n as Citation, f as LLMConfig, l as OpenAIProviderToolOptions, O as OpenAIToolSelectionHints, i as ToolExecution, g as ToolLocation, k as ToolNativeProviderHints, U as UnifiedToolCall, h as UnifiedToolResult } from './base-Di31iy_8.js';
|
|
6
|
+
import { A as AIProvider } from './types-C0vLXzuw.js';
|
|
7
|
+
export { a as AnthropicProviderConfig, f as AnthropicTool, h as AnthropicToolResult, g as AnthropicToolUse, b as AzureProviderConfig, B as BaseProviderConfig, m as GeminiFunctionCall, l as GeminiFunctionDeclaration, n as GeminiFunctionResponse, G as GoogleProviderConfig, d as OllamaModelOptions, c as OllamaProviderConfig, O as OpenAIProviderConfig, i as OpenAITool, j as OpenAIToolCall, k as OpenAIToolResult, P as ProviderCapabilities, e as ProviderFormatter, X as XAIProviderConfig } from './types-C0vLXzuw.js';
|
|
6
8
|
import * as hono from 'hono';
|
|
7
9
|
import { Hono } from 'hono';
|
|
10
|
+
export { F as FallbackChainConfig, c as FallbackFailure, d as FallbackInfo, b as RetryBackoff, e as RetryInfo, R as RoutingStore, a as RoutingStrategy } from './types-CNL8ZRne.js';
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
13
|
* generateText - Generate text using a language model
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { L as LanguageModel } from '../../types-CR8mi9I0.mjs';
|
|
2
|
-
import {
|
|
2
|
+
import { a as AnthropicProviderConfig, A as AIProvider } from '../../types-VDgiUvH2.mjs';
|
|
3
3
|
import 'zod';
|
|
4
|
+
import '../../base-5n-UuPfS.mjs';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Anthropic Provider - Modern Pattern
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { L as LanguageModel } from '../../types-CR8mi9I0.js';
|
|
2
|
-
import {
|
|
2
|
+
import { a as AnthropicProviderConfig, A as AIProvider } from '../../types-C0vLXzuw.js';
|
|
3
3
|
import 'zod';
|
|
4
|
+
import '../../base-Di31iy_8.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Anthropic Provider - Modern Pattern
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { L as LanguageModel } from '../../types-CR8mi9I0.mjs';
|
|
2
|
-
import { G as GoogleProviderConfig, A as AIProvider } from '../../types-
|
|
2
|
+
import { G as GoogleProviderConfig, A as AIProvider } from '../../types-VDgiUvH2.mjs';
|
|
3
3
|
import 'zod';
|
|
4
|
+
import '../../base-5n-UuPfS.mjs';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Google Provider - OpenAI-Compatible
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { L as LanguageModel } from '../../types-CR8mi9I0.js';
|
|
2
|
-
import { G as GoogleProviderConfig, A as AIProvider } from '../../types-
|
|
2
|
+
import { G as GoogleProviderConfig, A as AIProvider } from '../../types-C0vLXzuw.js';
|
|
3
3
|
import 'zod';
|
|
4
|
+
import '../../base-Di31iy_8.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Google Provider - OpenAI-Compatible
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { c as OllamaProviderConfig, A as AIProvider } from '../../types-VDgiUvH2.mjs';
|
|
2
|
+
export { d as OllamaModelOptions } from '../../types-VDgiUvH2.mjs';
|
|
3
|
+
import '../../base-5n-UuPfS.mjs';
|
|
3
4
|
import '../../types-CR8mi9I0.mjs';
|
|
4
5
|
import 'zod';
|
|
5
6
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { c as OllamaProviderConfig, A as AIProvider } from '../../types-C0vLXzuw.js';
|
|
2
|
+
export { d as OllamaModelOptions } from '../../types-C0vLXzuw.js';
|
|
3
|
+
import '../../base-Di31iy_8.js';
|
|
3
4
|
import '../../types-CR8mi9I0.js';
|
|
4
5
|
import 'zod';
|
|
5
6
|
|