@animalabs/membrane 0.5.76 → 0.5.77
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/membrane.d.ts.map +1 -1
- package/dist/membrane.js +242 -17
- package/dist/membrane.js.map +1 -1
- package/dist/providers/bedrock.d.ts +17 -0
- package/dist/providers/bedrock.d.ts.map +1 -1
- package/dist/providers/bedrock.js +83 -13
- package/dist/providers/bedrock.js.map +1 -1
- package/dist/types/config.d.ts +31 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +8 -0
- package/dist/types/config.js.map +1 -1
- package/dist/types/errors.d.ts +12 -0
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/errors.js +19 -0
- package/dist/types/errors.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/response.d.ts +1 -1
- package/dist/types/response.d.ts.map +1 -1
- package/dist/types/response.js.map +1 -1
- package/dist/types/streaming.d.ts +10 -0
- package/dist/types/streaming.d.ts.map +1 -1
- package/dist/types/yielding-stream.d.ts +12 -0
- package/dist/types/yielding-stream.d.ts.map +1 -1
- package/dist/types/yielding-stream.js.map +1 -1
- package/package.json +1 -1
- package/src/membrane.ts +268 -15
- package/src/providers/bedrock.ts +97 -13
- package/src/types/config.ts +48 -4
- package/src/types/errors.ts +18 -0
- package/src/types/index.ts +1 -0
- package/src/types/response.ts +5 -1
- package/src/types/streaming.ts +11 -0
- package/src/types/yielding-stream.ts +13 -0
package/src/types/config.ts
CHANGED
|
@@ -15,17 +15,53 @@ import type { PrefillFormatter } from '../formatters/types.js';
|
|
|
15
15
|
export interface RetryConfig {
|
|
16
16
|
/** Maximum number of retry attempts (default: 3) */
|
|
17
17
|
maxRetries: number;
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
/** Initial retry delay in milliseconds (default: 1000) */
|
|
20
20
|
retryDelayMs: number;
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
/** Backoff multiplier (default: 2) */
|
|
23
23
|
backoffMultiplier: number;
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
/** Maximum retry delay (default: 30000) */
|
|
26
26
|
maxRetryDelayMs: number;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Separate, longer schedule for provider capacity errors (529
|
|
30
|
+
* overloaded_error). Capacity storms last minutes, not seconds — the
|
|
31
|
+
* standard schedule's 30s ceiling turns one into a dead turn. Overloaded
|
|
32
|
+
* retries are always attempted (mirroring the forced 429 retries), with
|
|
33
|
+
* jitter so a fleet backing off doesn't re-create the stampede in sync.
|
|
34
|
+
*
|
|
35
|
+
* maxRetries: 0 here disables this dedicated policy entirely: 529s then
|
|
36
|
+
* follow the base retry config like any other retryable server error
|
|
37
|
+
* (no forced retries, base schedule, no stream-path retry) — the exact
|
|
38
|
+
* pre-policy behavior.
|
|
39
|
+
*/
|
|
40
|
+
overloaded: OverloadedRetryConfig;
|
|
27
41
|
}
|
|
28
42
|
|
|
43
|
+
export interface OverloadedRetryConfig {
|
|
44
|
+
/** Attempt bound for overloaded errors, applied even when the base
|
|
45
|
+
* maxRetries is 0. Like the base maxRetries (and the forced 429 path),
|
|
46
|
+
* this bounds TOTAL attempts, not retries-after-the-first (default: 7) */
|
|
47
|
+
maxRetries: number;
|
|
48
|
+
|
|
49
|
+
/** Initial overloaded retry delay in milliseconds (default: 10000) */
|
|
50
|
+
retryDelayMs: number;
|
|
51
|
+
|
|
52
|
+
/** Backoff multiplier (default: 2) */
|
|
53
|
+
backoffMultiplier: number;
|
|
54
|
+
|
|
55
|
+
/** Maximum overloaded retry delay (default: 300000 — 5 minutes) */
|
|
56
|
+
maxRetryDelayMs: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Shape accepted by MembraneConfig.retry — every field optional, including
|
|
60
|
+
* inside the nested overloaded schedule. */
|
|
61
|
+
export type RetryConfigInput = Partial<Omit<RetryConfig, 'overloaded'>> & {
|
|
62
|
+
overloaded?: Partial<OverloadedRetryConfig>;
|
|
63
|
+
};
|
|
64
|
+
|
|
29
65
|
// ============================================================================
|
|
30
66
|
// Media Processing Config
|
|
31
67
|
// ============================================================================
|
|
@@ -152,7 +188,7 @@ export interface MembraneConfig {
|
|
|
152
188
|
formatter?: PrefillFormatter;
|
|
153
189
|
|
|
154
190
|
/** Retry configuration */
|
|
155
|
-
retry?:
|
|
191
|
+
retry?: RetryConfigInput;
|
|
156
192
|
|
|
157
193
|
/** Media processing configuration */
|
|
158
194
|
media?: Partial<MediaConfig>;
|
|
@@ -176,6 +212,14 @@ export const DEFAULT_RETRY_CONFIG: RetryConfig = {
|
|
|
176
212
|
retryDelayMs: 1000,
|
|
177
213
|
backoffMultiplier: 2,
|
|
178
214
|
maxRetryDelayMs: 30000,
|
|
215
|
+
// 7 attempts = 6 waits: 10s → 20s → 40s → 80s → 160s → 300s, ~10 minutes
|
|
216
|
+
// of patience in total — the scale capacity storms actually resolve on.
|
|
217
|
+
overloaded: {
|
|
218
|
+
maxRetries: 7,
|
|
219
|
+
retryDelayMs: 10_000,
|
|
220
|
+
backoffMultiplier: 2,
|
|
221
|
+
maxRetryDelayMs: 300_000,
|
|
222
|
+
},
|
|
179
223
|
};
|
|
180
224
|
|
|
181
225
|
export const DEFAULT_MEDIA_CONFIG: MediaConfig = {
|
package/src/types/errors.ts
CHANGED
|
@@ -239,6 +239,24 @@ export function unsupportedError(message: string, rawRequest?: unknown): Membran
|
|
|
239
239
|
// Error Classification
|
|
240
240
|
// ============================================================================
|
|
241
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Provider capacity exhaustion — Anthropic 529 overloaded_error, whichever
|
|
244
|
+
* path it arrived by (structured status from the provider handler, or the
|
|
245
|
+
* message-matched fallbacks in classifyError). Used only to CHOOSE the retry
|
|
246
|
+
* schedule among already-retryable errors, never to decide retryability.
|
|
247
|
+
* Matches the same deliberately narrow tokens as classifyError's fallback
|
|
248
|
+
* (status/`529`/exact `overloaded_error`) — a bare 'overloaded' in prose
|
|
249
|
+
* (e.g. "worker pool overloaded") must not put an unrelated error onto the
|
|
250
|
+
* ~10-minute schedule. The provider handlers' own bare-'overloaded' safety
|
|
251
|
+
* nets attach httpStatus 529, so those still land here via the status check.
|
|
252
|
+
*/
|
|
253
|
+
export function isOverloadedError(info: ErrorInfo): boolean {
|
|
254
|
+
if (!info.retryable) return false;
|
|
255
|
+
if (info.httpStatus === 529) return true;
|
|
256
|
+
const m = info.message.toLowerCase();
|
|
257
|
+
return m.includes('529') || m.includes('overloaded_error');
|
|
258
|
+
}
|
|
259
|
+
|
|
242
260
|
export function classifyError(error: unknown): ErrorInfo {
|
|
243
261
|
if (error instanceof MembraneError) {
|
|
244
262
|
return error.toErrorInfo();
|
package/src/types/index.ts
CHANGED
package/src/types/response.ts
CHANGED
|
@@ -15,7 +15,11 @@ export type StopReason =
|
|
|
15
15
|
| 'stop_sequence' // Hit stop sequence
|
|
16
16
|
| 'tool_use' // Stopped for tool use
|
|
17
17
|
| 'refusal' // Content refused by safety
|
|
18
|
-
| 'abort'
|
|
18
|
+
| 'abort' // Request was aborted
|
|
19
|
+
| 'no_progress' // Stall guard ended the turn (issue #39): consecutive
|
|
20
|
+
// automatic resumptions re-sent context without advancing
|
|
21
|
+
| 'round_limit'; // Resumption round cap ended the turn: the turn kept
|
|
22
|
+
// resuming (with progress) past maxResumptionRounds
|
|
19
23
|
|
|
20
24
|
// ============================================================================
|
|
21
25
|
// Usage Information
|
package/src/types/streaming.ts
CHANGED
|
@@ -231,6 +231,17 @@ export interface StreamOptions {
|
|
|
231
231
|
/** Maximum tool execution depth (default: 10) */
|
|
232
232
|
maxToolDepth?: number;
|
|
233
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Cap on AUTOMATIC false-positive stop-sequence resumptions per turn —
|
|
236
|
+
* membrane's own re-streams, not the caller's work. Tool rounds are
|
|
237
|
+
* deliberately not counted: they are governed by maxToolDepth and caller
|
|
238
|
+
* policy. Distinct from maxToolDepth on purpose: raising the tool budget
|
|
239
|
+
* for deep chains must not also raise how often a turn may re-send its
|
|
240
|
+
* full context on membrane's own initiative (issue #39). Exceeding it
|
|
241
|
+
* ends the turn with stopReason 'round_limit'. Default: 24.
|
|
242
|
+
*/
|
|
243
|
+
maxResumptionRounds?: number;
|
|
244
|
+
|
|
234
245
|
/** Timeout for each tool execution */
|
|
235
246
|
toolTimeoutMs?: number;
|
|
236
247
|
|
|
@@ -237,6 +237,19 @@ export interface YieldingStreamOptions {
|
|
|
237
237
|
*/
|
|
238
238
|
maxToolDepth?: number;
|
|
239
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Cap on AUTOMATIC false-positive stop-sequence resumptions per turn —
|
|
242
|
+
* membrane's own re-streams, not the caller's tool work. Tool rounds are
|
|
243
|
+
* deliberately NOT counted: this path's uncapped-by-default tool-loop
|
|
244
|
+
* contract stands (the caller budgets its own work via maxToolDepth).
|
|
245
|
+
* What this bounds is membrane's own failure surface — how many times a
|
|
246
|
+
* turn may re-send its full context on membrane's initiative; an
|
|
247
|
+
* unlimited resumption bound is how the 43-round Ash spin happened
|
|
248
|
+
* (issue #39). Exceeding it ends the turn with stopReason 'round_limit'.
|
|
249
|
+
* Default: 24. `-1` for unlimited, at your own risk.
|
|
250
|
+
*/
|
|
251
|
+
maxResumptionRounds?: number;
|
|
252
|
+
|
|
240
253
|
/**
|
|
241
254
|
* Whether to emit 'tokens' events.
|
|
242
255
|
* Set to false if you only care about tool calls and final response.
|