@agentskit/adapters 0.17.4 → 0.18.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/README.md +2 -0
- package/dist/catalog/index.cjs +78 -93
- package/dist/catalog/index.cjs.map +1 -1
- package/dist/catalog/index.d.cts +1 -1
- package/dist/catalog/index.d.ts +1 -1
- package/dist/catalog/index.js +1 -1
- package/dist/{chunk-ICIKG7XA.js → chunk-MJDQXBVD.js} +89 -100
- package/dist/chunk-MJDQXBVD.js.map +1 -0
- package/dist/index.cjs +97 -112
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -4
- package/dist/index.d.ts +29 -4
- package/dist/index.js +4 -5
- package/dist/index.js.map +1 -1
- package/dist/retry-Bc6xtQCm.d.cts +49 -0
- package/dist/retry-Bc6xtQCm.d.ts +49 -0
- package/package.json +6 -5
- package/dist/chunk-ICIKG7XA.js.map +0 -1
- package/dist/utils-ddwu4MN4.d.cts +0 -66
- package/dist/utils-ddwu4MN4.d.ts +0 -66
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var core = require('@agentskit/core');
|
|
4
|
+
var net = require('@agentskit/net');
|
|
4
5
|
|
|
5
6
|
// src/stream-errors.ts
|
|
6
7
|
function isAbortError(err) {
|
|
@@ -238,27 +239,75 @@ function generic(config) {
|
|
|
238
239
|
}
|
|
239
240
|
};
|
|
240
241
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
242
|
+
var DEFAULT_RETRY = {
|
|
243
|
+
maxAttempts: 3,
|
|
244
|
+
baseDelayMs: 500,
|
|
245
|
+
maxDelayMs: 8e3,
|
|
246
|
+
jitter: true,
|
|
247
|
+
retryOn: ({ error, response }) => {
|
|
248
|
+
if (response) return [408, 429, 500, 502, 503, 504].includes(response.status);
|
|
249
|
+
if (isAbortError(error)) return false;
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
var RetryableResponse = class extends Error {
|
|
254
|
+
constructor(response, retryAfterMs) {
|
|
255
|
+
super(`HTTP ${response.status}`);
|
|
256
|
+
this.response = response;
|
|
257
|
+
this.retryAfterMs = retryAfterMs;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
async function fetchWithRetry(doFetch, signal, retryOpt = {}) {
|
|
261
|
+
const opts = { ...DEFAULT_RETRY, ...retryOpt };
|
|
262
|
+
const maxAttempts = Math.floor(opts.maxAttempts);
|
|
263
|
+
if (!Number.isFinite(maxAttempts) || maxAttempts < 1) {
|
|
264
|
+
throw new core.AdapterError({
|
|
265
|
+
code: core.ErrorCodes.AK_CONFIG_INVALID,
|
|
266
|
+
message: "Retry maxAttempts must be finite and at least 1 after flooring"
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
247
270
|
try {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
if (!line.startsWith("data: ")) continue;
|
|
256
|
-
const data = line.slice(6).trim();
|
|
257
|
-
if (data) yield data;
|
|
271
|
+
return await net.retry(async ({ attempt }) => {
|
|
272
|
+
let response;
|
|
273
|
+
try {
|
|
274
|
+
response = await doFetch(signal);
|
|
275
|
+
} catch (error) {
|
|
276
|
+
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
277
|
+
throw error;
|
|
258
278
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
279
|
+
if (response.ok || attempt >= maxAttempts || !opts.retryOn({ response, attempt })) return response;
|
|
280
|
+
const retryAfter = net.parseRetryAfter(response.headers.get("retry-after"));
|
|
281
|
+
await cancelBody(response.body);
|
|
282
|
+
throw new RetryableResponse(
|
|
283
|
+
response,
|
|
284
|
+
retryAfter === void 0 ? void 0 : Math.min(retryAfter, opts.maxDelayMs)
|
|
285
|
+
);
|
|
286
|
+
}, {
|
|
287
|
+
retries: maxAttempts - 1,
|
|
288
|
+
minDelayMs: opts.baseDelayMs,
|
|
289
|
+
maxDelayMs: opts.maxDelayMs,
|
|
290
|
+
jitter: opts.jitter ? "full" : "none",
|
|
291
|
+
shouldRetry: (error, attempt) => {
|
|
292
|
+
if (error instanceof RetryableResponse) return true;
|
|
293
|
+
if (isAbortError(error)) return false;
|
|
294
|
+
return opts.retryOn({ error, attempt });
|
|
295
|
+
},
|
|
296
|
+
delayFor: (error) => error instanceof RetryableResponse ? error.retryAfterMs : void 0,
|
|
297
|
+
onRetry: ({ error, attempt, delayMs }) => {
|
|
298
|
+
retryOpt.onRetry?.({
|
|
299
|
+
attempt,
|
|
300
|
+
delayMs,
|
|
301
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
302
|
+
});
|
|
303
|
+
},
|
|
304
|
+
sleep: retryOpt.sleep ? (ms, retrySignal) => abortableSleep(ms, retrySignal ?? signal, retryOpt.sleep) : net.sleep,
|
|
305
|
+
signal
|
|
306
|
+
});
|
|
307
|
+
} catch (error) {
|
|
308
|
+
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
309
|
+
if (error instanceof RetryableResponse) return error.response;
|
|
310
|
+
throw error;
|
|
262
311
|
}
|
|
263
312
|
}
|
|
264
313
|
async function* readNDJSONLines(stream) {
|
|
@@ -268,8 +317,12 @@ async function* readNDJSONLines(stream) {
|
|
|
268
317
|
try {
|
|
269
318
|
while (true) {
|
|
270
319
|
const { done, value } = await reader.read();
|
|
271
|
-
|
|
272
|
-
|
|
320
|
+
buffer += done ? decoder.decode() : decoder.decode(value, { stream: true });
|
|
321
|
+
if (done) {
|
|
322
|
+
const trimmed = buffer.trim();
|
|
323
|
+
if (trimmed) yield trimmed;
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
273
326
|
const lines = buffer.split("\n");
|
|
274
327
|
buffer = lines.pop() ?? "";
|
|
275
328
|
for (const line of lines) {
|
|
@@ -281,8 +334,6 @@ async function* readNDJSONLines(stream) {
|
|
|
281
334
|
reader.releaseLock();
|
|
282
335
|
}
|
|
283
336
|
}
|
|
284
|
-
|
|
285
|
-
// src/openai-stream.ts
|
|
286
337
|
var SUCCESS_FINISH_REASONS = /* @__PURE__ */ new Set(["stop", "tool_calls", "function_call"]);
|
|
287
338
|
async function* parseOpenAIStream(stream) {
|
|
288
339
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
@@ -314,7 +365,7 @@ async function* parseOpenAIStream(stream) {
|
|
|
314
365
|
pendingToolCalls.clear();
|
|
315
366
|
return true;
|
|
316
367
|
};
|
|
317
|
-
for await (const data of
|
|
368
|
+
for await (const { data } of net.parseSSE(stream)) {
|
|
318
369
|
if (data === "[DONE]") {
|
|
319
370
|
if (finishReason !== void 0 && !SUCCESS_FINISH_REASONS.has(finishReason)) {
|
|
320
371
|
pendingToolCalls.clear();
|
|
@@ -436,7 +487,7 @@ async function* parseAnthropicStream(stream) {
|
|
|
436
487
|
};
|
|
437
488
|
return true;
|
|
438
489
|
};
|
|
439
|
-
for await (const data of
|
|
490
|
+
for await (const { data } of net.parseSSE(stream)) {
|
|
440
491
|
if (data === "[DONE]") continue;
|
|
441
492
|
try {
|
|
442
493
|
const event = JSON.parse(data);
|
|
@@ -517,7 +568,7 @@ var GEMINI_SUCCESS_FINISH = /* @__PURE__ */ new Set([
|
|
|
517
568
|
]);
|
|
518
569
|
async function* parseGeminiStream(stream) {
|
|
519
570
|
let finishReason;
|
|
520
|
-
for await (const data of
|
|
571
|
+
for await (const { data } of net.parseSSE(stream)) {
|
|
521
572
|
try {
|
|
522
573
|
const event = JSON.parse(data);
|
|
523
574
|
if (event.usageMetadata) {
|
|
@@ -634,70 +685,6 @@ async function* parseOllamaStream(stream) {
|
|
|
634
685
|
}
|
|
635
686
|
yield { type: "done" };
|
|
636
687
|
}
|
|
637
|
-
var DEFAULT_RETRY = {
|
|
638
|
-
maxAttempts: 3,
|
|
639
|
-
baseDelayMs: 500,
|
|
640
|
-
maxDelayMs: 8e3,
|
|
641
|
-
jitter: true,
|
|
642
|
-
retryOn: ({ error, response }) => {
|
|
643
|
-
if (response) {
|
|
644
|
-
return [408, 429, 500, 502, 503, 504].includes(response.status);
|
|
645
|
-
}
|
|
646
|
-
if (isAbortError(error)) return false;
|
|
647
|
-
return true;
|
|
648
|
-
}
|
|
649
|
-
};
|
|
650
|
-
function defaultSleep(ms) {
|
|
651
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
652
|
-
}
|
|
653
|
-
function computeDelay(attempt, opts) {
|
|
654
|
-
const exp = Math.min(opts.maxDelayMs, opts.baseDelayMs * Math.pow(2, attempt - 1));
|
|
655
|
-
if (!opts.jitter) return exp;
|
|
656
|
-
return Math.floor(Math.random() * exp);
|
|
657
|
-
}
|
|
658
|
-
function parseRetryAfter(value, maxDelayMs) {
|
|
659
|
-
if (!value) return void 0;
|
|
660
|
-
const n = Number(value);
|
|
661
|
-
if (Number.isFinite(n) && n >= 0) return Math.min(n * 1e3, maxDelayMs);
|
|
662
|
-
const date = Date.parse(value);
|
|
663
|
-
if (!Number.isNaN(date)) return Math.min(maxDelayMs, Math.max(0, date - Date.now()));
|
|
664
|
-
return void 0;
|
|
665
|
-
}
|
|
666
|
-
async function fetchWithRetry(doFetch, signal, retryOpt = {}) {
|
|
667
|
-
const opts = {
|
|
668
|
-
...DEFAULT_RETRY,
|
|
669
|
-
...retryOpt
|
|
670
|
-
};
|
|
671
|
-
const sleep = retryOpt.sleep ?? defaultSleep;
|
|
672
|
-
let lastError;
|
|
673
|
-
for (let attempt = 1; attempt <= opts.maxAttempts; attempt++) {
|
|
674
|
-
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
675
|
-
try {
|
|
676
|
-
const response = await doFetch(signal);
|
|
677
|
-
if (response.ok) return response;
|
|
678
|
-
if (attempt >= opts.maxAttempts || !opts.retryOn({ response, attempt })) {
|
|
679
|
-
return response;
|
|
680
|
-
}
|
|
681
|
-
const retryAfterMs = parseRetryAfter(response.headers.get("retry-after"), opts.maxDelayMs);
|
|
682
|
-
const delay = retryAfterMs ?? computeDelay(attempt, opts);
|
|
683
|
-
retryOpt.onRetry?.({ attempt, delayMs: delay, reason: `HTTP ${response.status}` });
|
|
684
|
-
await cancelBody(response.body);
|
|
685
|
-
await abortableSleep(delay, signal, sleep);
|
|
686
|
-
} catch (err) {
|
|
687
|
-
lastError = err;
|
|
688
|
-
if (isAbortError(err)) throw err;
|
|
689
|
-
if (attempt >= opts.maxAttempts || !opts.retryOn({ error: err, attempt })) throw err;
|
|
690
|
-
const delay = computeDelay(attempt, opts);
|
|
691
|
-
retryOpt.onRetry?.({
|
|
692
|
-
attempt,
|
|
693
|
-
delayMs: delay,
|
|
694
|
-
reason: err instanceof Error ? err.message : String(err)
|
|
695
|
-
});
|
|
696
|
-
await abortableSleep(delay, signal, sleep);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
throw lastError ?? new Error("fetchWithRetry: exhausted attempts");
|
|
700
|
-
}
|
|
701
688
|
function chunkText(text, targetSize = 32) {
|
|
702
689
|
if (text.length <= targetSize) return [text];
|
|
703
690
|
const out = [];
|
|
@@ -714,14 +701,14 @@ function chunkText(text, targetSize = 32) {
|
|
|
714
701
|
return out;
|
|
715
702
|
}
|
|
716
703
|
function simulateStream(doFetch, extractText, errorLabel, options = {}) {
|
|
717
|
-
const { chunkSize = 32, delayMs = 8, retry } = options;
|
|
704
|
+
const { chunkSize = 32, delayMs = 8, retry: retry2 } = options;
|
|
718
705
|
let abortController = new AbortController();
|
|
719
706
|
return {
|
|
720
707
|
stream: async function* () {
|
|
721
708
|
const controller = abortController;
|
|
722
709
|
if (!controller) return;
|
|
723
710
|
try {
|
|
724
|
-
const response = await fetchWithRetry(doFetch, controller.signal,
|
|
711
|
+
const response = await fetchWithRetry(doFetch, controller.signal, retry2 ?? {});
|
|
725
712
|
if (!response.ok) {
|
|
726
713
|
await cancelBody(response.body);
|
|
727
714
|
yield adapterErrorChunk(`${errorLabel} error: ${response.status}`);
|
|
@@ -741,7 +728,7 @@ function simulateStream(doFetch, extractText, errorLabel, options = {}) {
|
|
|
741
728
|
for (const chunk of chunks) {
|
|
742
729
|
if (abortController === null || controller.signal.aborted) return;
|
|
743
730
|
if (delayMs > 0) {
|
|
744
|
-
await abortableSleep(delayMs, controller.signal,
|
|
731
|
+
await abortableSleep(delayMs, controller.signal, net.sleep);
|
|
745
732
|
}
|
|
746
733
|
yield { type: "text", content: chunk };
|
|
747
734
|
}
|
|
@@ -760,14 +747,14 @@ function simulateStream(doFetch, extractText, errorLabel, options = {}) {
|
|
|
760
747
|
}
|
|
761
748
|
|
|
762
749
|
// src/stream-source.ts
|
|
763
|
-
function createStreamSource(doFetch, parse, errorLabel,
|
|
750
|
+
function createStreamSource(doFetch, parse, errorLabel, retry2) {
|
|
764
751
|
let abortController = new AbortController();
|
|
765
752
|
return {
|
|
766
753
|
stream: async function* () {
|
|
767
754
|
const controller = abortController;
|
|
768
755
|
if (!controller) return;
|
|
769
756
|
try {
|
|
770
|
-
const response = await fetchWithRetry(doFetch, controller.signal,
|
|
757
|
+
const response = await fetchWithRetry(doFetch, controller.signal, retry2 ?? {});
|
|
771
758
|
if (!response.ok) {
|
|
772
759
|
await cancelBody(response.body);
|
|
773
760
|
yield adapterErrorChunk(`${errorLabel} error: ${response.status}`);
|
|
@@ -929,7 +916,7 @@ function toGeminiContents(messages) {
|
|
|
929
916
|
|
|
930
917
|
// src/anthropic.ts
|
|
931
918
|
function anthropic(config) {
|
|
932
|
-
const { apiKey, model, baseUrl = "https://api.anthropic.com", maxTokens = 4096, retry } = config;
|
|
919
|
+
const { apiKey, model, baseUrl = "https://api.anthropic.com", maxTokens = 4096, retry: retry2 } = config;
|
|
933
920
|
return {
|
|
934
921
|
capabilities: {
|
|
935
922
|
streaming: true,
|
|
@@ -964,7 +951,7 @@ function anthropic(config) {
|
|
|
964
951
|
}),
|
|
965
952
|
parseAnthropicStream,
|
|
966
953
|
"Anthropic API",
|
|
967
|
-
|
|
954
|
+
retry2
|
|
968
955
|
);
|
|
969
956
|
}
|
|
970
957
|
};
|
|
@@ -972,7 +959,7 @@ function anthropic(config) {
|
|
|
972
959
|
|
|
973
960
|
// src/openai.ts
|
|
974
961
|
function openai(config) {
|
|
975
|
-
const { apiKey, model, baseUrl = "https://api.openai.com", retry } = config;
|
|
962
|
+
const { apiKey, model, baseUrl = "https://api.openai.com", retry: retry2 } = config;
|
|
976
963
|
const apiRoot = baseUrl.replace(/\/v1\/?$/, "");
|
|
977
964
|
const isCanonicalOpenAI = (() => {
|
|
978
965
|
try {
|
|
@@ -1022,7 +1009,7 @@ function openai(config) {
|
|
|
1022
1009
|
}),
|
|
1023
1010
|
parseOpenAIStream,
|
|
1024
1011
|
"OpenAI API",
|
|
1025
|
-
|
|
1012
|
+
retry2
|
|
1026
1013
|
);
|
|
1027
1014
|
}
|
|
1028
1015
|
};
|
|
@@ -1030,7 +1017,7 @@ function openai(config) {
|
|
|
1030
1017
|
|
|
1031
1018
|
// src/gemini.ts
|
|
1032
1019
|
function gemini(config) {
|
|
1033
|
-
const { apiKey, model, baseUrl = "https://generativelanguage.googleapis.com", retry } = config;
|
|
1020
|
+
const { apiKey, model, baseUrl = "https://generativelanguage.googleapis.com", retry: retry2 } = config;
|
|
1034
1021
|
return {
|
|
1035
1022
|
capabilities: {
|
|
1036
1023
|
streaming: true,
|
|
@@ -1066,7 +1053,7 @@ function gemini(config) {
|
|
|
1066
1053
|
),
|
|
1067
1054
|
parseGeminiStream,
|
|
1068
1055
|
"Gemini API",
|
|
1069
|
-
|
|
1056
|
+
retry2
|
|
1070
1057
|
);
|
|
1071
1058
|
}
|
|
1072
1059
|
};
|
|
@@ -1087,7 +1074,7 @@ var grok = createOpenAICompatibleAdapter("https://api.x.ai");
|
|
|
1087
1074
|
|
|
1088
1075
|
// src/ollama.ts
|
|
1089
1076
|
function ollama(config) {
|
|
1090
|
-
const { model, baseUrl = "http://localhost:11434", retry } = config;
|
|
1077
|
+
const { model, baseUrl = "http://localhost:11434", retry: retry2 } = config;
|
|
1091
1078
|
return {
|
|
1092
1079
|
capabilities: {
|
|
1093
1080
|
streaming: true,
|
|
@@ -1113,7 +1100,7 @@ function ollama(config) {
|
|
|
1113
1100
|
}),
|
|
1114
1101
|
parseOllamaStream,
|
|
1115
1102
|
"Ollama API",
|
|
1116
|
-
|
|
1103
|
+
retry2
|
|
1117
1104
|
);
|
|
1118
1105
|
}
|
|
1119
1106
|
};
|
|
@@ -1207,8 +1194,6 @@ function langgraph(config) {
|
|
|
1207
1194
|
mode: "events"
|
|
1208
1195
|
});
|
|
1209
1196
|
}
|
|
1210
|
-
|
|
1211
|
-
// src/vercel-ai.ts
|
|
1212
1197
|
var UI_STREAM_HEADER = "x-vercel-ai-ui-message-stream";
|
|
1213
1198
|
async function* parseVercelTextStream(stream) {
|
|
1214
1199
|
const reader = stream.getReader();
|
|
@@ -1226,7 +1211,7 @@ async function* parseVercelTextStream(stream) {
|
|
|
1226
1211
|
yield { type: "done" };
|
|
1227
1212
|
}
|
|
1228
1213
|
async function* parseVercelUiMessageStreamV1(stream) {
|
|
1229
|
-
for await (const data of
|
|
1214
|
+
for await (const { data } of net.parseSSE(stream)) {
|
|
1230
1215
|
if (data === "[DONE]") {
|
|
1231
1216
|
yield { type: "done" };
|
|
1232
1217
|
return;
|
|
@@ -1268,7 +1253,7 @@ async function* parseVercelStream(stream, response) {
|
|
|
1268
1253
|
yield* parseVercelTextStream(stream);
|
|
1269
1254
|
}
|
|
1270
1255
|
function vercelAI(config) {
|
|
1271
|
-
const { api, headers = {}, retry } = config;
|
|
1256
|
+
const { api, headers = {}, retry: retry2 } = config;
|
|
1272
1257
|
return {
|
|
1273
1258
|
capabilities: {
|
|
1274
1259
|
// Vercel AI routes can do any of these — whether they do depends on
|
|
@@ -1289,7 +1274,7 @@ function vercelAI(config) {
|
|
|
1289
1274
|
}),
|
|
1290
1275
|
parseVercelStream,
|
|
1291
1276
|
"API",
|
|
1292
|
-
|
|
1277
|
+
retry2
|
|
1293
1278
|
);
|
|
1294
1279
|
}
|
|
1295
1280
|
};
|
|
@@ -1382,7 +1367,7 @@ var cerebrasAdapter = cerebras;
|
|
|
1382
1367
|
// src/azure-openai.ts
|
|
1383
1368
|
var DEFAULT_API_VERSION = "2024-10-21";
|
|
1384
1369
|
function azureOpenAI(config) {
|
|
1385
|
-
const { apiKey, endpoint, deployment, apiVersion = DEFAULT_API_VERSION, retry } = config;
|
|
1370
|
+
const { apiKey, endpoint, deployment, apiVersion = DEFAULT_API_VERSION, retry: retry2 } = config;
|
|
1386
1371
|
const includeUsage = config.includeUsage ?? true;
|
|
1387
1372
|
const url = `${endpoint.replace(/\/$/, "")}/openai/deployments/${encodeURIComponent(deployment)}/chat/completions?api-version=${encodeURIComponent(apiVersion)}`;
|
|
1388
1373
|
return {
|
|
@@ -1419,7 +1404,7 @@ function azureOpenAI(config) {
|
|
|
1419
1404
|
}),
|
|
1420
1405
|
parseOpenAIStream,
|
|
1421
1406
|
"Azure OpenAI API",
|
|
1422
|
-
|
|
1407
|
+
retry2
|
|
1423
1408
|
);
|
|
1424
1409
|
}
|
|
1425
1410
|
};
|
|
@@ -1572,7 +1557,7 @@ var qwen = bail;
|
|
|
1572
1557
|
|
|
1573
1558
|
// src/vertex.ts
|
|
1574
1559
|
function vertex(config) {
|
|
1575
|
-
const { project, region, model, accessToken, publisher = "google", retry } = config;
|
|
1560
|
+
const { project, region, model, accessToken, publisher = "google", retry: retry2 } = config;
|
|
1576
1561
|
const url = `https://${region}-aiplatform.googleapis.com/v1/projects/${project}/locations/${region}/publishers/${publisher}/models/${model}:streamGenerateContent?alt=sse`;
|
|
1577
1562
|
return {
|
|
1578
1563
|
capabilities: {
|
|
@@ -1610,7 +1595,7 @@ function vertex(config) {
|
|
|
1610
1595
|
},
|
|
1611
1596
|
parseGeminiStream,
|
|
1612
1597
|
"Vertex AI",
|
|
1613
|
-
|
|
1598
|
+
retry2
|
|
1614
1599
|
);
|
|
1615
1600
|
}
|
|
1616
1601
|
};
|