tool-call-retry 0.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/README.md +141 -0
- package/dist/__tests__/backoff.test.d.ts +2 -0
- package/dist/__tests__/backoff.test.d.ts.map +1 -0
- package/dist/__tests__/backoff.test.js +117 -0
- package/dist/__tests__/backoff.test.js.map +1 -0
- package/dist/__tests__/circuit-breaker.test.d.ts +2 -0
- package/dist/__tests__/circuit-breaker.test.d.ts.map +1 -0
- package/dist/__tests__/circuit-breaker.test.js +129 -0
- package/dist/__tests__/circuit-breaker.test.js.map +1 -0
- package/dist/__tests__/classify.test.d.ts +2 -0
- package/dist/__tests__/classify.test.d.ts.map +1 -0
- package/dist/__tests__/classify.test.js +139 -0
- package/dist/__tests__/classify.test.js.map +1 -0
- package/dist/__tests__/retry.test.d.ts +2 -0
- package/dist/__tests__/retry.test.d.ts.map +1 -0
- package/dist/__tests__/retry.test.js +277 -0
- package/dist/__tests__/retry.test.js.map +1 -0
- package/dist/backoff.d.ts +3 -0
- package/dist/backoff.d.ts.map +1 -0
- package/dist/backoff.js +37 -0
- package/dist/backoff.js.map +1 -0
- package/dist/circuit-breaker.d.ts +10 -0
- package/dist/circuit-breaker.d.ts.map +1 -0
- package/dist/circuit-breaker.js +100 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/classify.d.ts +3 -0
- package/dist/classify.d.ts.map +1 -0
- package/dist/classify.js +112 -0
- package/dist/classify.js.map +1 -0
- package/dist/format-error.d.ts +6 -0
- package/dist/format-error.d.ts.map +1 -0
- package/dist/format-error.js +73 -0
- package/dist/format-error.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/retry.d.ts +7 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +127 -0
- package/dist/retry.js.map +1 -0
- package/dist/types.d.ts +65 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatErrorForLLM = formatErrorForLLM;
|
|
4
|
+
const classify_js_1 = require("./classify.js");
|
|
5
|
+
const SANITIZE_PATTERNS = [
|
|
6
|
+
// Stack traces
|
|
7
|
+
[/\n\s+at\s+.+/g, ''],
|
|
8
|
+
// Localhost URLs
|
|
9
|
+
[/https?:\/\/localhost[^\s]*/gi, '[localhost]'],
|
|
10
|
+
[/https?:\/\/127\.0\.0\.1[^\s]*/gi, '[localhost]'],
|
|
11
|
+
// Passwords in URLs
|
|
12
|
+
[/(:\/\/[^:@\s]+:)[^@\s]+(@)/g, '$1[redacted]$2'],
|
|
13
|
+
// Authorization headers / bearer tokens
|
|
14
|
+
[/bearer\s+[A-Za-z0-9\-._~+/]+=*/gi, 'bearer [redacted]'],
|
|
15
|
+
// API key-like patterns (long alphanumeric strings)
|
|
16
|
+
[/\b(sk|pk|api|key|token|secret)[-_][A-Za-z0-9\-_]{8,}/gi, '[redacted]'],
|
|
17
|
+
];
|
|
18
|
+
function sanitizeMessage(message) {
|
|
19
|
+
let s = message;
|
|
20
|
+
for (const [pattern, replacement] of SANITIZE_PATTERNS) {
|
|
21
|
+
s = s.replace(pattern, replacement);
|
|
22
|
+
}
|
|
23
|
+
return s.trim();
|
|
24
|
+
}
|
|
25
|
+
function formatErrorForLLM(error, options) {
|
|
26
|
+
const classification = (0, classify_js_1.classifyError)(error);
|
|
27
|
+
let code;
|
|
28
|
+
let message;
|
|
29
|
+
let retriable;
|
|
30
|
+
let suggestion;
|
|
31
|
+
switch (classification.category) {
|
|
32
|
+
case 'rate-limited':
|
|
33
|
+
code = 'RATE_LIMITED';
|
|
34
|
+
message = 'Rate limit exceeded';
|
|
35
|
+
retriable = true;
|
|
36
|
+
suggestion = 'Wait before retrying or reduce request frequency';
|
|
37
|
+
break;
|
|
38
|
+
case 'retriable':
|
|
39
|
+
code = 'SERVICE_UNAVAILABLE';
|
|
40
|
+
message = 'Service temporarily unavailable';
|
|
41
|
+
retriable = true;
|
|
42
|
+
suggestion = 'Retry the operation';
|
|
43
|
+
break;
|
|
44
|
+
case 'timeout':
|
|
45
|
+
code = 'TIMEOUT';
|
|
46
|
+
message = 'Request timed out';
|
|
47
|
+
retriable = true;
|
|
48
|
+
suggestion = 'Try again or reduce payload size';
|
|
49
|
+
break;
|
|
50
|
+
case 'non-retriable': {
|
|
51
|
+
code = 'INVALID_REQUEST';
|
|
52
|
+
const rawMessage = classification.message || (error instanceof Error ? error.message : String(error));
|
|
53
|
+
message = sanitizeMessage(rawMessage);
|
|
54
|
+
retriable = false;
|
|
55
|
+
suggestion = 'Check the tool arguments';
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case 'unknown':
|
|
59
|
+
default:
|
|
60
|
+
code = 'UNKNOWN_ERROR';
|
|
61
|
+
message = 'An unexpected error occurred';
|
|
62
|
+
retriable = true;
|
|
63
|
+
suggestion = 'Retry once; if it persists, report the issue';
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
const result = { error: true, code, message, retriable, suggestion };
|
|
67
|
+
if (options?.toolName)
|
|
68
|
+
result.tool = options.toolName;
|
|
69
|
+
if (options?.attemptsMade !== undefined)
|
|
70
|
+
result.attemptsMade = options.attemptsMade;
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=format-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-error.js","sourceRoot":"","sources":["../src/format-error.ts"],"names":[],"mappings":";;AAyBA,8CAmDC;AA3ED,+CAA8C;AAE9C,MAAM,iBAAiB,GAA4B;IACjD,eAAe;IACf,CAAC,eAAe,EAAE,EAAE,CAAC;IACrB,iBAAiB;IACjB,CAAC,8BAA8B,EAAE,aAAa,CAAC;IAC/C,CAAC,iCAAiC,EAAE,aAAa,CAAC;IAClD,oBAAoB;IACpB,CAAC,6BAA6B,EAAE,gBAAgB,CAAC;IACjD,wCAAwC;IACxC,CAAC,kCAAkC,EAAE,mBAAmB,CAAC;IACzD,oDAAoD;IACpD,CAAC,wDAAwD,EAAE,YAAY,CAAC;CACzE,CAAC;AAEF,SAAS,eAAe,CAAC,OAAe;IACtC,IAAI,CAAC,GAAG,OAAO,CAAC;IAChB,KAAK,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,IAAI,iBAAiB,EAAE,CAAC;QACvD,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;AAClB,CAAC;AAED,SAAgB,iBAAiB,CAC/B,KAAc,EACd,OAAsD;IAEtD,MAAM,cAAc,GAAG,IAAA,2BAAa,EAAC,KAAK,CAAC,CAAC;IAE5C,IAAI,IAAY,CAAC;IACjB,IAAI,OAAe,CAAC;IACpB,IAAI,SAAkB,CAAC;IACvB,IAAI,UAAkB,CAAC;IAEvB,QAAQ,cAAc,CAAC,QAAQ,EAAE,CAAC;QAChC,KAAK,cAAc;YACjB,IAAI,GAAG,cAAc,CAAC;YACtB,OAAO,GAAG,qBAAqB,CAAC;YAChC,SAAS,GAAG,IAAI,CAAC;YACjB,UAAU,GAAG,kDAAkD,CAAC;YAChE,MAAM;QACR,KAAK,WAAW;YACd,IAAI,GAAG,qBAAqB,CAAC;YAC7B,OAAO,GAAG,iCAAiC,CAAC;YAC5C,SAAS,GAAG,IAAI,CAAC;YACjB,UAAU,GAAG,qBAAqB,CAAC;YACnC,MAAM;QACR,KAAK,SAAS;YACZ,IAAI,GAAG,SAAS,CAAC;YACjB,OAAO,GAAG,mBAAmB,CAAC;YAC9B,SAAS,GAAG,IAAI,CAAC;YACjB,UAAU,GAAG,kCAAkC,CAAC;YAChD,MAAM;QACR,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,IAAI,GAAG,iBAAiB,CAAC;YACzB,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,IAAI,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACtG,OAAO,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;YACtC,SAAS,GAAG,KAAK,CAAC;YAClB,UAAU,GAAG,0BAA0B,CAAC;YACxC,MAAM;QACR,CAAC;QACD,KAAK,SAAS,CAAC;QACf;YACE,IAAI,GAAG,eAAe,CAAC;YACvB,OAAO,GAAG,8BAA8B,CAAC;YACzC,SAAS,GAAG,IAAI,CAAC;YACjB,UAAU,GAAG,8CAA8C,CAAC;YAC5D,MAAM;IACV,CAAC;IAED,MAAM,MAAM,GAAsB,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;IACxF,IAAI,OAAO,EAAE,QAAQ;QAAE,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC;IACtD,IAAI,OAAO,EAAE,YAAY,KAAK,SAAS;QAAE,MAAM,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IACpF,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { withRetry, wrapTools, createRetryPolicy } from './retry.js';
|
|
2
|
+
export { createCircuitBreaker } from './circuit-breaker.js';
|
|
3
|
+
export { classifyError } from './classify.js';
|
|
4
|
+
export { formatErrorForLLM } from './format-error.js';
|
|
5
|
+
export type { ErrorCategory, ErrorClassification, ErrorClassifier, BackoffStrategy, JitterStrategy, CircuitState, RetryPolicy, CircuitBreakerConfig, LLMFormattedError, ToolRetryOptions, ToolRetryHooks, } from './types.js';
|
|
6
|
+
export type { CircuitBreakerInstance } from './circuit-breaker.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,YAAY,EACV,aAAa,EACb,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,cAAc,EACd,YAAY,EACZ,WAAW,EACX,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatErrorForLLM = exports.classifyError = exports.createCircuitBreaker = exports.createRetryPolicy = exports.wrapTools = exports.withRetry = void 0;
|
|
4
|
+
// tool-call-retry - AI-specific retry wrapper with circuit breaker for tool calls
|
|
5
|
+
var retry_js_1 = require("./retry.js");
|
|
6
|
+
Object.defineProperty(exports, "withRetry", { enumerable: true, get: function () { return retry_js_1.withRetry; } });
|
|
7
|
+
Object.defineProperty(exports, "wrapTools", { enumerable: true, get: function () { return retry_js_1.wrapTools; } });
|
|
8
|
+
Object.defineProperty(exports, "createRetryPolicy", { enumerable: true, get: function () { return retry_js_1.createRetryPolicy; } });
|
|
9
|
+
var circuit_breaker_js_1 = require("./circuit-breaker.js");
|
|
10
|
+
Object.defineProperty(exports, "createCircuitBreaker", { enumerable: true, get: function () { return circuit_breaker_js_1.createCircuitBreaker; } });
|
|
11
|
+
var classify_js_1 = require("./classify.js");
|
|
12
|
+
Object.defineProperty(exports, "classifyError", { enumerable: true, get: function () { return classify_js_1.classifyError; } });
|
|
13
|
+
var format_error_js_1 = require("./format-error.js");
|
|
14
|
+
Object.defineProperty(exports, "formatErrorForLLM", { enumerable: true, get: function () { return format_error_js_1.formatErrorForLLM; } });
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,kFAAkF;AAClF,uCAAqE;AAA5D,qGAAA,SAAS,OAAA;AAAE,qGAAA,SAAS,OAAA;AAAE,6GAAA,iBAAiB,OAAA;AAChD,2DAA4D;AAAnD,0HAAA,oBAAoB,OAAA;AAC7B,6CAA8C;AAArC,4GAAA,aAAa,OAAA;AACtB,qDAAsD;AAA7C,oHAAA,iBAAiB,OAAA"}
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { LLMFormattedError, RetryPolicy, ToolRetryOptions } from './types.js';
|
|
2
|
+
export declare function createRetryPolicy(options?: Partial<RetryPolicy>): Required<RetryPolicy>;
|
|
3
|
+
export declare function withRetry<T>(fn: () => Promise<T>, options?: ToolRetryOptions & {
|
|
4
|
+
toolName?: string;
|
|
5
|
+
}): Promise<T | LLMFormattedError>;
|
|
6
|
+
export declare function wrapTools<T extends Record<string, (args: unknown) => Promise<unknown>>>(tools: T, options?: ToolRetryOptions): T;
|
|
7
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAMnF,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC,CAUvF;AAmBD,wBAAsB,SAAS,CAAC,CAAC,EAC/B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,CAAC,EAAE,gBAAgB,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC,CAAC,GAAG,iBAAiB,CAAC,CA+FhC;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,EACrF,KAAK,EAAE,CAAC,EACR,OAAO,CAAC,EAAE,gBAAgB,GACzB,CAAC,CAQH"}
|
package/dist/retry.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRetryPolicy = createRetryPolicy;
|
|
4
|
+
exports.withRetry = withRetry;
|
|
5
|
+
exports.wrapTools = wrapTools;
|
|
6
|
+
const classify_js_1 = require("./classify.js");
|
|
7
|
+
const backoff_js_1 = require("./backoff.js");
|
|
8
|
+
const circuit_breaker_js_1 = require("./circuit-breaker.js");
|
|
9
|
+
const format_error_js_1 = require("./format-error.js");
|
|
10
|
+
function createRetryPolicy(options) {
|
|
11
|
+
return {
|
|
12
|
+
maxRetries: options?.maxRetries ?? 3,
|
|
13
|
+
strategy: options?.strategy ?? 'exponential',
|
|
14
|
+
initialDelayMs: options?.initialDelayMs ?? 1000,
|
|
15
|
+
maxDelayMs: options?.maxDelayMs ?? 30000,
|
|
16
|
+
multiplier: options?.multiplier ?? 2,
|
|
17
|
+
jitter: options?.jitter ?? 'full',
|
|
18
|
+
maxTotalTimeMs: options?.maxTotalTimeMs ?? 60000,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function sleep(ms, signal) {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
if (signal?.aborted) {
|
|
24
|
+
reject(new DOMException('Aborted', 'AbortError'));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const timer = setTimeout(resolve, ms);
|
|
28
|
+
if (signal) {
|
|
29
|
+
const onAbort = () => {
|
|
30
|
+
clearTimeout(timer);
|
|
31
|
+
reject(new DOMException('Aborted', 'AbortError'));
|
|
32
|
+
};
|
|
33
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async function withRetry(fn, options) {
|
|
38
|
+
const policy = createRetryPolicy({
|
|
39
|
+
...options?.policy,
|
|
40
|
+
...(options?.maxRetries !== undefined ? { maxRetries: options.maxRetries } : {}),
|
|
41
|
+
});
|
|
42
|
+
const cb = options?.circuitBreaker !== false
|
|
43
|
+
? (0, circuit_breaker_js_1.createCircuitBreaker)(options?.circuitBreaker === undefined ? undefined : options.circuitBreaker)
|
|
44
|
+
: null;
|
|
45
|
+
const startTime = Date.now();
|
|
46
|
+
let prevDelay = policy.initialDelayMs;
|
|
47
|
+
let attempt = 0;
|
|
48
|
+
while (true) {
|
|
49
|
+
// Check circuit breaker before each attempt
|
|
50
|
+
if (cb && !cb.isCallPermitted) {
|
|
51
|
+
const formatted = (0, format_error_js_1.formatErrorForLLM)(new Error('Circuit breaker is open'), { toolName: options?.toolName, attemptsMade: attempt });
|
|
52
|
+
if (options?.onPermanentFailure === 'return-error')
|
|
53
|
+
return formatted;
|
|
54
|
+
const err = new Error('Circuit breaker is open');
|
|
55
|
+
options?.hooks?.onPermanentFailure?.({
|
|
56
|
+
error: err,
|
|
57
|
+
attempts: attempt,
|
|
58
|
+
totalMs: Date.now() - startTime,
|
|
59
|
+
formattedError: formatted,
|
|
60
|
+
});
|
|
61
|
+
throw err;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const result = await fn();
|
|
65
|
+
cb?.recordSuccess();
|
|
66
|
+
options?.hooks?.onSuccess?.({ attempts: attempt + 1, totalMs: Date.now() - startTime });
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
// Check if aborted by signal
|
|
71
|
+
if (options?.signal?.aborted) {
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
cb?.recordFailure();
|
|
75
|
+
const classification = (0, classify_js_1.classifyError)(error, options?.classifyError);
|
|
76
|
+
attempt++;
|
|
77
|
+
const isPermanent = classification.category === 'non-retriable' || attempt > policy.maxRetries;
|
|
78
|
+
if (isPermanent) {
|
|
79
|
+
const formatted = (0, format_error_js_1.formatErrorForLLM)(error, {
|
|
80
|
+
toolName: options?.toolName,
|
|
81
|
+
attemptsMade: attempt,
|
|
82
|
+
});
|
|
83
|
+
options?.hooks?.onPermanentFailure?.({
|
|
84
|
+
error,
|
|
85
|
+
attempts: attempt,
|
|
86
|
+
totalMs: Date.now() - startTime,
|
|
87
|
+
formattedError: formatted,
|
|
88
|
+
});
|
|
89
|
+
if (options?.onPermanentFailure === 'return-error')
|
|
90
|
+
return formatted;
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
// Check total time budget
|
|
94
|
+
if (Date.now() - startTime >= policy.maxTotalTimeMs) {
|
|
95
|
+
const formatted = (0, format_error_js_1.formatErrorForLLM)(error, {
|
|
96
|
+
toolName: options?.toolName,
|
|
97
|
+
attemptsMade: attempt,
|
|
98
|
+
});
|
|
99
|
+
options?.hooks?.onPermanentFailure?.({
|
|
100
|
+
error,
|
|
101
|
+
attempts: attempt,
|
|
102
|
+
totalMs: Date.now() - startTime,
|
|
103
|
+
formattedError: formatted,
|
|
104
|
+
});
|
|
105
|
+
if (options?.onPermanentFailure === 'return-error')
|
|
106
|
+
return formatted;
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
let delay = (0, backoff_js_1.computeDelay)(attempt, policy, prevDelay);
|
|
110
|
+
if (classification.retryAfterMs !== undefined) {
|
|
111
|
+
delay = Math.max(delay, classification.retryAfterMs);
|
|
112
|
+
}
|
|
113
|
+
prevDelay = delay;
|
|
114
|
+
options?.hooks?.onRetry?.({ attempt, error, delayMs: delay, classification });
|
|
115
|
+
await sleep(delay, options?.signal);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function wrapTools(tools, options) {
|
|
120
|
+
const wrapped = {};
|
|
121
|
+
for (const key of Object.keys(tools)) {
|
|
122
|
+
const original = tools[key];
|
|
123
|
+
wrapped[key] = (args) => withRetry(() => original(args), { ...options, toolName: key });
|
|
124
|
+
}
|
|
125
|
+
return wrapped;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export type ErrorCategory = 'retriable' | 'non-retriable' | 'rate-limited' | 'timeout' | 'unknown';
|
|
2
|
+
export interface ErrorClassification {
|
|
3
|
+
category: ErrorCategory;
|
|
4
|
+
code: string;
|
|
5
|
+
message: string;
|
|
6
|
+
statusCode?: number;
|
|
7
|
+
retryAfterMs?: number;
|
|
8
|
+
}
|
|
9
|
+
export type ErrorClassifier = (error: unknown) => ErrorClassification | null;
|
|
10
|
+
export type BackoffStrategy = 'exponential' | 'linear' | 'fixed' | 'custom';
|
|
11
|
+
export type JitterStrategy = 'full' | 'equal' | 'decorrelated' | 'none';
|
|
12
|
+
export type CircuitState = 'closed' | 'open' | 'half-open';
|
|
13
|
+
export interface RetryPolicy {
|
|
14
|
+
maxRetries?: number;
|
|
15
|
+
strategy?: BackoffStrategy;
|
|
16
|
+
initialDelayMs?: number;
|
|
17
|
+
maxDelayMs?: number;
|
|
18
|
+
multiplier?: number;
|
|
19
|
+
jitter?: JitterStrategy;
|
|
20
|
+
maxTotalTimeMs?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface CircuitBreakerConfig {
|
|
23
|
+
enabled?: boolean;
|
|
24
|
+
failureThreshold?: number;
|
|
25
|
+
rollingWindowMs?: number;
|
|
26
|
+
resetTimeoutMs?: number;
|
|
27
|
+
successThreshold?: number;
|
|
28
|
+
}
|
|
29
|
+
export interface LLMFormattedError {
|
|
30
|
+
error: true;
|
|
31
|
+
code: string;
|
|
32
|
+
message: string;
|
|
33
|
+
retriable: boolean;
|
|
34
|
+
suggestion: string;
|
|
35
|
+
tool?: string;
|
|
36
|
+
attemptsMade?: number;
|
|
37
|
+
}
|
|
38
|
+
export interface ToolRetryOptions {
|
|
39
|
+
policy?: RetryPolicy;
|
|
40
|
+
maxRetries?: number;
|
|
41
|
+
circuitBreaker?: CircuitBreakerConfig | false;
|
|
42
|
+
classifyError?: ErrorClassifier;
|
|
43
|
+
onPermanentFailure?: 'throw' | 'return-error';
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
hooks?: ToolRetryHooks;
|
|
46
|
+
}
|
|
47
|
+
export interface ToolRetryHooks {
|
|
48
|
+
onRetry?: (info: {
|
|
49
|
+
attempt: number;
|
|
50
|
+
error: unknown;
|
|
51
|
+
delayMs: number;
|
|
52
|
+
classification: ErrorClassification;
|
|
53
|
+
}) => void;
|
|
54
|
+
onSuccess?: (info: {
|
|
55
|
+
attempts: number;
|
|
56
|
+
totalMs: number;
|
|
57
|
+
}) => void;
|
|
58
|
+
onPermanentFailure?: (info: {
|
|
59
|
+
error: unknown;
|
|
60
|
+
attempts: number;
|
|
61
|
+
totalMs: number;
|
|
62
|
+
formattedError: LLMFormattedError;
|
|
63
|
+
}) => void;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,eAAe,GAAG,cAAc,GAAG,SAAS,GAAG,SAAS,CAAC;AAEnG,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,aAAa,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,mBAAmB,GAAG,IAAI,CAAC;AAE7E,MAAM,MAAM,eAAe,GAAG,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAC5E,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,cAAc,GAAG,MAAM,CAAC;AACxE,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,IAAI,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,oBAAoB,GAAG,KAAK,CAAC;IAC9C,aAAa,CAAC,EAAE,eAAe,CAAC;IAChC,kBAAkB,CAAC,EAAE,OAAO,GAAG,cAAc,CAAC;IAC9C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,mBAAmB,CAAA;KAAE,KAAK,IAAI,CAAC;IACpH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAClE,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,iBAAiB,CAAA;KAAE,KAAK,IAAI,CAAC;CAC/H"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tool-call-retry",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AI-specific retry wrapper with circuit breaker for tool calls",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"lint": "eslint src/",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [],
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^25.5.0",
|
|
27
|
+
"@typescript-eslint/eslint-plugin": "^8.57.1",
|
|
28
|
+
"@typescript-eslint/parser": "^8.57.1",
|
|
29
|
+
"eslint": "^10.1.0",
|
|
30
|
+
"typescript": "^5.9.3",
|
|
31
|
+
"vitest": "^4.1.0"
|
|
32
|
+
}
|
|
33
|
+
}
|