@tangleai/models 0.21.1 → 0.24.1
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/CHANGELOG.md +26 -0
- package/README.md +2 -1
- package/package.json +3 -3
- package/src/check.d.ts +9 -10
- package/src/check.js +14 -18
- package/src/client.d.ts +59 -102
- package/src/client.js +307 -351
- package/src/embed.d.ts +54 -36
- package/src/embed.js +202 -287
- package/src/embedding-vector.d.ts +9 -4
- package/src/embedding-vector.js +13 -17
- package/src/errors.d.ts +26 -9
- package/src/errors.js +22 -24
- package/src/grammar.d.ts +6 -7
- package/src/grammar.js +39 -30
- package/src/index.d.ts +10 -9
- package/src/index.js +9 -10
- package/src/providers.d.ts +41 -31
- package/src/providers.js +79 -99
- package/src/replay.d.ts +52 -37
- package/src/replay.js +31 -59
- package/src/retry.d.ts +45 -86
- package/src/retry.js +50 -105
- package/src/routing.d.ts +5 -9
- package/src/routing.js +91 -79
- package/src/sse.d.ts +10 -1
- package/src/sse.js +0 -2
- package/src/structured.d.ts +19 -17
- package/src/structured.js +95 -124
package/src/replay.d.ts
CHANGED
|
@@ -1,30 +1,59 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The replay
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
2
|
+
* The replay seam behind both wire clients: what a remembered reply is
|
|
3
|
+
* keyed by, and what a remembered entry must look like before it is
|
|
4
|
+
* served. This module owns the two decisions a host must not be left
|
|
5
|
+
* to make twice —
|
|
6
|
+
*
|
|
7
|
+
* - **the key is the effective wire request, canonicalized.** It is
|
|
8
|
+
* built by the client after endpoint resolution and default
|
|
9
|
+
* application, from `{ wire, provider, base, request }` where
|
|
10
|
+
* `request` is the body the client would POST (minus `stream`, which
|
|
11
|
+
* does not change an answer). Never the headers (they carry the
|
|
12
|
+
* credential), never the signal, never a callback. Because the key
|
|
13
|
+
* is the body rather than an allow-list of request members, an
|
|
14
|
+
* option added to the body later enters the key by construction — it
|
|
15
|
+
* cannot alias an older key. The serialization is `semanticKey`:
|
|
16
|
+
* injective over plain data, so two requests share a key exactly when
|
|
17
|
+
* they are the same request, and a request that cannot be keyed
|
|
18
|
+
* injectively (a function inside `tools`, a cycle) is refused up
|
|
19
|
+
* front rather than folded onto someone else's entry;
|
|
20
|
+
*
|
|
21
|
+
* - **a stored entry is verified, not trusted.** A chat entry must carry
|
|
22
|
+
* a normalized result and the wall time of the purchase; an embedding
|
|
23
|
+
* entry must carry a vector of finite numbers at the settled width.
|
|
24
|
+
* Anything else is `AI0003`, never served — a cache that answers
|
|
25
|
+
* garbage is worse than a cache that is down.
|
|
26
|
+
*
|
|
27
|
+
* The client hands the adapter the complete canonical string. It is
|
|
28
|
+
* long (a whole conversation is in it) and that is the point: it is
|
|
29
|
+
* collision-free. An adapter that needs a fixed-width storage id hashes
|
|
30
|
+
* it — with a cryptographic hash, because a 32-bit hash over prompts
|
|
31
|
+
* that differ by one token would sooner or later serve one prompt's
|
|
32
|
+
* answer for another's.
|
|
33
|
+
*
|
|
34
|
+
* The adapter contract is two members, each sync or async:
|
|
35
|
+
* `get(key) → value | undefined` and `set(key, value)`. The seam FAILS
|
|
36
|
+
* CLOSED: an adapter that throws fails the call. An adapter that wants
|
|
37
|
+
* to fail open — keep buying while its storage is broken — catches its
|
|
38
|
+
* own errors and answers `undefined`; the client will not guess which
|
|
39
|
+
* it wanted.
|
|
8
40
|
*/
|
|
41
|
+
/** The replay cache seam a host implements. */
|
|
9
42
|
/**
|
|
10
43
|
* The `cache` option, checked once at client construction: absent means
|
|
11
44
|
* no cache; present means both members are functions, or `AI0001`.
|
|
12
|
-
* @param {unknown} cache
|
|
13
|
-
* @returns {ReplayCache | null}
|
|
14
45
|
*/
|
|
15
|
-
export function normalizeCache(cache: unknown): ReplayCache | null;
|
|
46
|
+
export declare function normalizeCache(cache: unknown): ReplayCache | null;
|
|
16
47
|
/**
|
|
17
48
|
* The key one request has under one endpoint: the canonical
|
|
18
49
|
* serialization of the wire, the credential-free endpoint identity and
|
|
19
50
|
* the effective request. The same request keys the same string on
|
|
20
51
|
* every host.
|
|
21
|
-
* @param
|
|
22
|
-
* @
|
|
23
|
-
* @param {any} request - the body the client would POST, `stream` removed
|
|
24
|
-
* @returns {string}
|
|
52
|
+
* @param request - the body the client would POST, `stream` removed
|
|
53
|
+
* @returns
|
|
25
54
|
* @throws {AiError} `AI0001` when the request cannot be keyed injectively
|
|
26
55
|
*/
|
|
27
|
-
export function replayKey(wire:
|
|
56
|
+
export declare function replayKey(wire: 'chat' | 'embeddings', endpoint: {
|
|
28
57
|
provider: string;
|
|
29
58
|
base: string;
|
|
30
59
|
}, request: any): string;
|
|
@@ -32,10 +61,8 @@ export function replayKey(wire: "chat" | "embeddings", endpoint: {
|
|
|
32
61
|
* A stored chat entry, verified: `{ value, ms }` with `value` a
|
|
33
62
|
* normalized result carrying a `message` object and `ms` a finite
|
|
34
63
|
* number — or `AI0003`.
|
|
35
|
-
* @param {any} entry
|
|
36
|
-
* @returns {{ value: any, ms: number }}
|
|
37
64
|
*/
|
|
38
|
-
export function verifyChatEntry(entry: any): {
|
|
65
|
+
export declare function verifyChatEntry(entry: any): {
|
|
39
66
|
value: any;
|
|
40
67
|
ms: number;
|
|
41
68
|
};
|
|
@@ -44,33 +71,21 @@ export function verifyChatEntry(entry: any): {
|
|
|
44
71
|
* ms }` with `vector` a non-empty array of finite numbers at the settled
|
|
45
72
|
* width (any positive width when none is settled yet — the first replay
|
|
46
73
|
* settles it exactly as a first wire reply would) — or `AI0003`.
|
|
47
|
-
* @param
|
|
48
|
-
* @param {number | undefined} dims - the settled width, if any
|
|
49
|
-
* @returns {Float32Array}
|
|
74
|
+
* @param dims - the settled width, if any
|
|
50
75
|
*/
|
|
51
|
-
export function verifyEmbeddingEntry(entry: any, dims: number | undefined): Float32Array;
|
|
76
|
+
export declare function verifyEmbeddingEntry(entry: any, dims: number | undefined): Float32Array;
|
|
52
77
|
/**
|
|
53
78
|
* A JSON-only copy: what is stored, and what a replay answers, so that a
|
|
54
79
|
* caller mutating its result never mutates the adapter's entry.
|
|
55
80
|
* @template T
|
|
56
|
-
* @param {T} value
|
|
57
|
-
* @returns {T}
|
|
58
81
|
*/
|
|
59
|
-
export function cloneJson<T>(value: T): T;
|
|
82
|
+
export declare function cloneJson<T>(value: T): T;
|
|
60
83
|
/** Milliseconds now, for the wall time of a purchase. */
|
|
61
|
-
export function now(): number;
|
|
62
|
-
/**
|
|
63
|
-
* The replay cache seam a host implements.
|
|
64
|
-
*/
|
|
84
|
+
export declare function now(): number;
|
|
65
85
|
export type ReplayCache = {
|
|
66
|
-
/**
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
get: (key: string) => any;
|
|
71
|
-
/**
|
|
72
|
-
* - remember a value
|
|
73
|
-
* under a key; may answer a promise. The value is JSON-only.
|
|
74
|
-
*/
|
|
86
|
+
get: (key: string) => any; /**
|
|
87
|
+
* - remember a value
|
|
88
|
+
* under a key; may answer a promise. The value is JSON-only.
|
|
89
|
+
*/
|
|
75
90
|
set: (key: string, value: any) => any;
|
|
76
91
|
};
|
package/src/replay.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//@ts-check
|
|
2
1
|
/**
|
|
3
2
|
* The replay seam behind both wire clients: what a remembered reply is
|
|
4
3
|
* keyed by, and what a remembered entry must look like before it is
|
|
@@ -39,103 +38,76 @@
|
|
|
39
38
|
* own errors and answers `undefined`; the client will not guess which
|
|
40
39
|
* it wanted.
|
|
41
40
|
*/
|
|
42
|
-
|
|
43
41
|
import { semanticKey } from '@jarenjs/core/object';
|
|
44
|
-
|
|
45
|
-
import {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* The replay cache seam a host implements.
|
|
50
|
-
* @typedef {Object} ReplayCache
|
|
51
|
-
* @property {(key: string) => any} get - the stored value, or `undefined`
|
|
52
|
-
* for a miss; may answer a promise
|
|
53
|
-
* @property {(key: string, value: any) => any} set - remember a value
|
|
54
|
-
* under a key; may answer a promise. The value is JSON-only.
|
|
55
|
-
*/
|
|
56
|
-
|
|
42
|
+
import { AiError } from "./errors.js";
|
|
43
|
+
import { verifyEmbeddingComponents } from "./embedding-vector.js";
|
|
44
|
+
/** The replay cache seam a host implements. */
|
|
57
45
|
/**
|
|
58
46
|
* The `cache` option, checked once at client construction: absent means
|
|
59
47
|
* no cache; present means both members are functions, or `AI0001`.
|
|
60
|
-
* @param {unknown} cache
|
|
61
|
-
* @returns {ReplayCache | null}
|
|
62
48
|
*/
|
|
63
49
|
export function normalizeCache(cache) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
50
|
+
if (cache === undefined || cache === null)
|
|
51
|
+
return null;
|
|
52
|
+
const candidate = cache;
|
|
53
|
+
if (typeof candidate.get !== 'function' || typeof candidate.set !== 'function')
|
|
54
|
+
throw new AiError('AI0001', 'cache needs { get(key), set(key, value) } — both functions, sync or async');
|
|
55
|
+
return candidate;
|
|
69
56
|
}
|
|
70
|
-
|
|
71
57
|
/**
|
|
72
58
|
* The key one request has under one endpoint: the canonical
|
|
73
59
|
* serialization of the wire, the credential-free endpoint identity and
|
|
74
60
|
* the effective request. The same request keys the same string on
|
|
75
61
|
* every host.
|
|
76
|
-
* @param
|
|
77
|
-
* @
|
|
78
|
-
* @param {any} request - the body the client would POST, `stream` removed
|
|
79
|
-
* @returns {string}
|
|
62
|
+
* @param request - the body the client would POST, `stream` removed
|
|
63
|
+
* @returns
|
|
80
64
|
* @throws {AiError} `AI0001` when the request cannot be keyed injectively
|
|
81
65
|
*/
|
|
82
66
|
export function replayKey(wire, endpoint, request) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
67
|
+
try {
|
|
68
|
+
return semanticKey({ wire, provider: endpoint.provider, base: endpoint.base, request });
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
throw new AiError('AI0001', `the ${wire} request is not cacheable: ${err.message}`);
|
|
72
|
+
}
|
|
90
73
|
}
|
|
91
|
-
|
|
92
|
-
/** @param {any} value */
|
|
74
|
+
/** @param value */
|
|
93
75
|
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
94
|
-
|
|
95
76
|
/**
|
|
96
77
|
* A stored chat entry, verified: `{ value, ms }` with `value` a
|
|
97
78
|
* normalized result carrying a `message` object and `ms` a finite
|
|
98
79
|
* number — or `AI0003`.
|
|
99
|
-
* @param {any} entry
|
|
100
|
-
* @returns {{ value: any, ms: number }}
|
|
101
80
|
*/
|
|
102
81
|
export function verifyChatEntry(entry) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
82
|
+
if (!isRecord(entry) || !isRecord(entry.value) || !isRecord(entry.value.message)
|
|
83
|
+
|| typeof entry.ms !== 'number' || !Number.isFinite(entry.ms))
|
|
84
|
+
throw new AiError('AI0003', 'malformed replay entry for the chat wire: expected { value: { message, … }, ms }');
|
|
85
|
+
return entry;
|
|
107
86
|
}
|
|
108
|
-
|
|
109
87
|
/**
|
|
110
88
|
* A stored embedding entry, verified into a fresh vector: `{ vector,
|
|
111
89
|
* ms }` with `vector` a non-empty array of finite numbers at the settled
|
|
112
90
|
* width (any positive width when none is settled yet — the first replay
|
|
113
91
|
* settles it exactly as a first wire reply would) — or `AI0003`.
|
|
114
|
-
* @param
|
|
115
|
-
* @param {number | undefined} dims - the settled width, if any
|
|
116
|
-
* @returns {Float32Array}
|
|
92
|
+
* @param dims - the settled width, if any
|
|
117
93
|
*/
|
|
118
94
|
export function verifyEmbeddingEntry(entry, dims) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
95
|
+
const vector = isRecord(entry) ? entry.vector : undefined;
|
|
96
|
+
if (!Array.isArray(vector) || vector.length === 0)
|
|
97
|
+
throw new AiError('AI0003', 'malformed replay entry for the embeddings wire: expected { vector: number[], ms }');
|
|
98
|
+
if (dims !== undefined && vector.length !== dims)
|
|
99
|
+
throw new AiError('AI0003', `replay entry carries ${vector.length} dimensions, expected ${dims}`);
|
|
100
|
+
return verifyEmbeddingComponents(vector, 'replay entry carries');
|
|
125
101
|
}
|
|
126
|
-
|
|
127
102
|
/**
|
|
128
103
|
* A JSON-only copy: what is stored, and what a replay answers, so that a
|
|
129
104
|
* caller mutating its result never mutates the adapter's entry.
|
|
130
105
|
* @template T
|
|
131
|
-
* @param {T} value
|
|
132
|
-
* @returns {T}
|
|
133
106
|
*/
|
|
134
107
|
export function cloneJson(value) {
|
|
135
|
-
|
|
108
|
+
return JSON.parse(JSON.stringify(value));
|
|
136
109
|
}
|
|
137
|
-
|
|
138
110
|
/** Milliseconds now, for the wall time of a purchase. */
|
|
139
111
|
export function now() {
|
|
140
|
-
|
|
112
|
+
return globalThis.performance.now();
|
|
141
113
|
}
|
package/src/retry.d.ts
CHANGED
|
@@ -1,34 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* deterministic tests
|
|
14
|
-
* @property {(ms: number, signal?: AbortSignal) => Promise<void>} [sleep]
|
|
15
|
-
* - the wait itself, for deterministic tests; the default is a timer
|
|
16
|
-
* that rejects with the abort reason the moment `signal` aborts
|
|
17
|
-
*/
|
|
18
|
-
/**
|
|
19
|
-
* The option with its defaults filled in.
|
|
20
|
-
* @typedef {Object} RetryPolicy
|
|
21
|
-
* @property {number} attempts
|
|
22
|
-
* @property {number} baseMs
|
|
23
|
-
* @property {number} maxMs
|
|
24
|
-
* @property {() => number} random
|
|
25
|
-
* @property {(ms: number, signal?: AbortSignal) => Promise<void>} sleep
|
|
2
|
+
* The transport policy every client in this package shares: which
|
|
3
|
+
* failures are transient, how long to wait before trying again, what a
|
|
4
|
+
* provider's `Retry-After` is worth, and how an abort cuts a wait
|
|
5
|
+
* short. The chat wire and the embeddings wire fail alike at the
|
|
6
|
+
* transport — a network error, a 408, a 429, a 5xx, a 200 whose body is
|
|
7
|
+
* not what the wire promised — and differ only in what a good reply
|
|
8
|
+
* must carry. So the loop lives here, once, and each client keeps its
|
|
9
|
+
* own reading of a reply: one implementation of retry, two wire shapes.
|
|
10
|
+
*
|
|
11
|
+
* Everything here is internal to the package; the public surface is
|
|
12
|
+
* the `retry` option each client documents.
|
|
26
13
|
*/
|
|
14
|
+
import { AiError } from './errors.ts';
|
|
15
|
+
import { abortError } from '@jarenjs/core/retry';
|
|
16
|
+
export { abortError };
|
|
17
|
+
/** The `retry` option of every client. */
|
|
18
|
+
/** The option with its defaults filled in. */
|
|
27
19
|
/**
|
|
28
|
-
* @param {RetryOptions | undefined} retry
|
|
29
|
-
* @returns {RetryPolicy}
|
|
30
20
|
*/
|
|
31
|
-
export function normalizeRetry(retry: RetryOptions | undefined): RetryPolicy;
|
|
21
|
+
export declare function normalizeRetry(retry: RetryOptions | undefined): RetryPolicy;
|
|
32
22
|
/**
|
|
33
23
|
* Whether a failure is the transient kind. A transport error with a
|
|
34
24
|
* retryable status is (a network failure before any response counts
|
|
@@ -37,20 +27,17 @@ export function normalizeRetry(retry: RetryOptions | undefined): RetryPolicy;
|
|
|
37
27
|
* tiers, and safe to retry precisely because nothing was delivered.
|
|
38
28
|
* Anything else — a caller error, a 401, a 404 — is final on the first
|
|
39
29
|
* try.
|
|
40
|
-
* @param {unknown} err
|
|
41
|
-
* @returns {boolean}
|
|
42
30
|
*/
|
|
43
|
-
export function isTransientFailure(err: unknown): boolean;
|
|
31
|
+
export declare function isTransientFailure(err: unknown): boolean;
|
|
44
32
|
/**
|
|
45
33
|
* The wait before the next try: exponential backoff with full jitter,
|
|
46
34
|
* capped at `maxMs` — unless the provider named a `Retry-After`, which
|
|
47
35
|
* wins up to the same cap.
|
|
48
|
-
* @param
|
|
49
|
-
* @param
|
|
50
|
-
* @
|
|
51
|
-
* @returns {number} milliseconds
|
|
36
|
+
* @param attempt - the try that just failed, counted from 1
|
|
37
|
+
* @param retryAfter - the provider's ask, in ms
|
|
38
|
+
* @returns milliseconds
|
|
52
39
|
*/
|
|
53
|
-
export function retryDelay(policy: RetryPolicy, attempt: number, retryAfter: number | undefined): number;
|
|
40
|
+
export declare function retryDelay(policy: RetryPolicy, attempt: number, retryAfter: number | undefined): number;
|
|
54
41
|
/**
|
|
55
42
|
* Run `once` until it settles. A failure `retryable` accepts backs off
|
|
56
43
|
* and tries again while tries remain; anything else is thrown as it
|
|
@@ -59,77 +46,51 @@ export function retryDelay(policy: RetryPolicy, attempt: number, retryAfter: num
|
|
|
59
46
|
* during backoff rejects with the abort reason, exactly like an abort
|
|
60
47
|
* during the request — nothing is ever retried past an abort.
|
|
61
48
|
* @template T
|
|
62
|
-
* @param
|
|
63
|
-
* @param {() => Promise<T>} once - one request/response cycle
|
|
64
|
-
* @param {{ signal?: AbortSignal, retryable: (failure: AiError) => boolean }} options
|
|
49
|
+
* @param once - one request/response cycle
|
|
65
50
|
* - `retryable` is the wire's own judgment over a coded failure (the
|
|
66
51
|
* chat client, for one, stops retrying once a streamed delta has
|
|
67
52
|
* reached the caller); it is never asked about an uncoded error
|
|
68
|
-
* @returns {Promise<T>}
|
|
69
53
|
*/
|
|
70
|
-
export function withRetry<T>(policy: RetryPolicy, once: () => Promise<T>, options: {
|
|
54
|
+
export declare function withRetry<T>(policy: RetryPolicy, once: () => Promise<T>, options: {
|
|
71
55
|
signal?: AbortSignal;
|
|
72
56
|
retryable: (failure: AiError) => boolean;
|
|
73
57
|
}): Promise<T>;
|
|
74
58
|
/**
|
|
75
59
|
* The `AI0002` for a response that is not ok: the status, a short
|
|
76
60
|
* excerpt of the body, and the provider's `Retry-After` in ms.
|
|
77
|
-
* @param {any} response
|
|
78
|
-
* @param {string} url
|
|
79
|
-
* @returns {Promise<AiError>}
|
|
80
61
|
*/
|
|
81
|
-
export function httpFailure(response: any, url: string): Promise<AiError>;
|
|
62
|
+
export declare function httpFailure(response: any, url: string): Promise<AiError>;
|
|
82
63
|
/**
|
|
83
64
|
* What to throw when `fetch` itself threw: an abort exactly as it came
|
|
84
65
|
* (the caller's own signal, never retried, never rewrapped); anything
|
|
85
66
|
* else the `AI0002` of a failure before any response, status 0.
|
|
86
|
-
* @param {any} err
|
|
87
|
-
* @param {string} url
|
|
88
|
-
* @returns {any}
|
|
89
67
|
*/
|
|
90
|
-
export function transportFailure(err: any, url: string): any;
|
|
68
|
+
export declare function transportFailure(err: any, url: string): any;
|
|
91
69
|
/**
|
|
92
70
|
* Parse a `Retry-After` header (delta-seconds or HTTP-date) into ms.
|
|
93
|
-
* @param {any} response
|
|
94
|
-
* @returns {number | undefined}
|
|
95
|
-
*/
|
|
96
|
-
export function retryAfterMs(response: any): number | undefined;
|
|
97
|
-
export { abortError };
|
|
98
|
-
/**
|
|
99
|
-
* The `retry` option of every client.
|
|
100
71
|
*/
|
|
72
|
+
export declare function retryAfterMs(response: any): number | undefined;
|
|
101
73
|
export type RetryOptions = {
|
|
102
|
-
/**
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
* - the jitter source, for
|
|
121
|
-
* deterministic tests
|
|
122
|
-
*/
|
|
123
|
-
random?: () => number;
|
|
124
|
-
/**
|
|
125
|
-
* - the wait itself, for deterministic tests; the default is a timer
|
|
126
|
-
* that rejects with the abort reason the moment `signal` aborts
|
|
127
|
-
*/
|
|
74
|
+
attempts?: number; /**
|
|
75
|
+
* - the first backoff (default 500); each
|
|
76
|
+
* later one doubles, with full jitter
|
|
77
|
+
*/
|
|
78
|
+
baseMs?: number; /**
|
|
79
|
+
* - the ceiling on any single wait (default
|
|
80
|
+
* 8 000) — a provider `Retry-After` included: a provider asking for a
|
|
81
|
+
* minute gets the cap, and the value it asked for rides the final
|
|
82
|
+
* error as `retryAfterMs` for the caller to honour
|
|
83
|
+
*/
|
|
84
|
+
maxMs?: number; /**
|
|
85
|
+
* - the jitter source, for
|
|
86
|
+
* deterministic tests
|
|
87
|
+
*/
|
|
88
|
+
random?: () => number; /**
|
|
89
|
+
* - the wait itself, for deterministic tests; the default is a timer
|
|
90
|
+
* that rejects with the abort reason the moment `signal` aborts
|
|
91
|
+
*/
|
|
128
92
|
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
129
93
|
};
|
|
130
|
-
/**
|
|
131
|
-
* The option with its defaults filled in.
|
|
132
|
-
*/
|
|
133
94
|
export type RetryPolicy = {
|
|
134
95
|
attempts: number;
|
|
135
96
|
baseMs: number;
|
|
@@ -137,5 +98,3 @@ export type RetryPolicy = {
|
|
|
137
98
|
random: () => number;
|
|
138
99
|
sleep: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
139
100
|
};
|
|
140
|
-
import { AiError } from './errors.js';
|
|
141
|
-
import { abortError } from '@jarenjs/core/retry';
|