deepline 0.3.63 → 0.3.65

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.
@@ -16,10 +16,17 @@ export const TOOL_EXECUTE_IDEMPOTENCY_IN_PROGRESS_MAX_ATTEMPTS =
16
16
  TOOL_EXECUTE_IDEMPOTENCY_IN_PROGRESS_RETRY_DELAYS_MS.length + 1;
17
17
  export const TOOL_EXECUTE_RATE_LIMIT_MAX_ATTEMPTS = 8;
18
18
  export const TOOL_EXECUTE_BARE_RATE_LIMIT_MAX_ATTEMPTS = 2;
19
+ // A Deepline async-capacity denial means the provider has not been called.
20
+ // Treat it as queueing, not a failed physical provider attempt. The enclosing
21
+ // tool deadline remains the hard bound; this prevents a normal one-minute
22
+ // queue wait from being compressed into eight five-second retries.
23
+ export const TOOL_EXECUTE_CONCURRENCY_BACKPRESSURE_MAX_ATTEMPTS = 65;
19
24
  export const TOOL_EXECUTE_TRANSPORT_MAX_ATTEMPTS = 3;
20
25
  export const TOOL_EXECUTE_TRANSPORT_RETRY_DELAY_MS = 1_000;
21
26
  export const TOOL_EXECUTE_RETRY_DELAY_FALLBACK_MS = 1_000;
22
27
  export const TOOL_EXECUTE_RETRY_DELAY_MAX_MS = 5_000;
28
+ export const TOOL_EXECUTE_RATE_LIMIT_RETRY_DELAY_MAX_MS = 30_000;
29
+ export const TOOL_EXECUTE_CONCURRENCY_BACKPRESSURE_DELAY_MAX_MS = 5 * 60_000;
23
30
  export const TOOL_EXECUTE_BARE_RATE_LIMIT_BACKPRESSURE_MS = 60_000;
24
31
  export const TOOL_EXECUTE_AUTH_SCOPE_CHANGED_CODE = 'AUTH_SCOPE_CHANGED';
25
32
  export const TOOL_EXECUTE_CUSTOMER_DB_STORAGE_UNAVAILABLE_CODE =
@@ -50,6 +57,7 @@ export type ToolExecuteHttpRetryDecision = {
50
57
  attemptCap: number;
51
58
  reason:
52
59
  | 'rate_limit'
60
+ | 'concurrency_backpressure'
53
61
  | 'idempotency_in_progress'
54
62
  | 'gateway_invocation_in_progress'
55
63
  | 'customer_db_storage_unavailable'
@@ -131,6 +139,19 @@ function isCustomerDbStorageUnavailableResponse(input: {
131
139
  );
132
140
  }
133
141
 
142
+ function isConcurrencyBackpressureResponse(input: {
143
+ status: number;
144
+ bodyText: string;
145
+ }): boolean {
146
+ if (input.status !== 429) return false;
147
+ const body = parseJsonObject(input.bodyText);
148
+ return (
149
+ body?.code === 'RATE_LIMIT' &&
150
+ body.rate_limit_kind === 'concurrency' &&
151
+ body.failure_origin === 'deepline_rate_limit'
152
+ );
153
+ }
154
+
134
155
  function idempotencyInProgressRetryDelayMs(attempt: number): number {
135
156
  return (
136
157
  TOOL_EXECUTE_IDEMPOTENCY_IN_PROGRESS_RETRY_DELAYS_MS[
@@ -164,6 +185,7 @@ function decideToolExecuteHttpRetry(input: {
164
185
  idempotencyInProgress?: boolean;
165
186
  gatewayInvocationInProgress?: boolean;
166
187
  customerDbStorageUnavailable?: boolean;
188
+ concurrencyBackpressure?: boolean;
167
189
  hardBillingFailure?: boolean;
168
190
  hasRetryAfterHeader?: boolean;
169
191
  transientHttpRetrySafe?: boolean;
@@ -177,6 +199,13 @@ function decideToolExecuteHttpRetry(input: {
177
199
  };
178
200
  }
179
201
  if (input.status === 429) {
202
+ if (input.concurrencyBackpressure) {
203
+ return {
204
+ retryable: true,
205
+ attemptCap: TOOL_EXECUTE_CONCURRENCY_BACKPRESSURE_MAX_ATTEMPTS,
206
+ reason: 'concurrency_backpressure',
207
+ };
208
+ }
180
209
  if (!input.hasRetryAfterHeader) {
181
210
  return {
182
211
  retryable: true,
@@ -241,6 +270,7 @@ export function createToolExecuteHttpFailureAttemptTracker(): ToolExecuteHttpFai
241
270
  number
242
271
  > = {
243
272
  rate_limit: 0,
273
+ concurrency_backpressure: 0,
244
274
  idempotency_in_progress: 0,
245
275
  gateway_invocation_in_progress: 0,
246
276
  customer_db_storage_unavailable: 0,
@@ -267,6 +297,10 @@ export function createToolExecuteHttpFailureAttemptTracker(): ToolExecuteHttpFai
267
297
  status: input.status,
268
298
  bodyText: input.bodyText ?? '',
269
299
  }),
300
+ concurrencyBackpressure: isConcurrencyBackpressureResponse({
301
+ status: input.status,
302
+ bodyText: input.bodyText ?? '',
303
+ }),
270
304
  hasRetryAfterHeader: true,
271
305
  transientHttpRetrySafe: input.transientHttpRetrySafe === true,
272
306
  });
@@ -312,11 +346,13 @@ export function classifyToolExecuteHttpFailure(input: {
312
346
  isGatewayInvocationInProgressResponse(input);
313
347
  const customerDbStorageUnavailable =
314
348
  isCustomerDbStorageUnavailableResponse(input);
349
+ const concurrencyBackpressure = isConcurrencyBackpressureResponse(input);
315
350
  const initialRetryDecision = decideToolExecuteHttpRetry({
316
351
  status: input.status,
317
352
  idempotencyInProgress,
318
353
  gatewayInvocationInProgress,
319
354
  customerDbStorageUnavailable,
355
+ concurrencyBackpressure,
320
356
  hasRetryAfterHeader,
321
357
  transientHttpRetrySafe,
322
358
  });
@@ -348,6 +384,7 @@ export function classifyToolExecuteHttpFailure(input: {
348
384
  idempotencyInProgress,
349
385
  gatewayInvocationInProgress,
350
386
  customerDbStorageUnavailable,
387
+ concurrencyBackpressure,
351
388
  hardBillingFailure,
352
389
  hasRetryAfterHeader,
353
390
  transientHttpRetrySafe,
@@ -366,23 +403,31 @@ export function classifyToolExecuteHttpFailure(input: {
366
403
  error.receiptFailureKind = 'repairable';
367
404
  }
368
405
  const retryDelayMs =
369
- input.status === 429
406
+ retryDecision.reason === 'concurrency_backpressure'
370
407
  ? Math.min(
371
- TOOL_EXECUTE_RETRY_DELAY_MAX_MS,
408
+ TOOL_EXECUTE_CONCURRENCY_BACKPRESSURE_DELAY_MAX_MS,
372
409
  Math.max(
373
410
  retryAfterMs,
374
411
  TOOL_EXECUTE_RETRY_DELAY_FALLBACK_MS * input.attempt,
375
412
  ),
376
413
  )
377
- : retryDecision.reason === 'idempotency_in_progress' &&
378
- !hasRetryAfterHeader
379
- ? idempotencyInProgressRetryDelayMs(input.attempt)
380
- : retryDecision.reason === 'gateway_invocation_in_progress' &&
414
+ : input.status === 429
415
+ ? Math.min(
416
+ TOOL_EXECUTE_RATE_LIMIT_RETRY_DELAY_MAX_MS,
417
+ Math.max(
418
+ retryAfterMs,
419
+ TOOL_EXECUTE_RETRY_DELAY_FALLBACK_MS * input.attempt,
420
+ ),
421
+ )
422
+ : retryDecision.reason === 'idempotency_in_progress' &&
381
423
  !hasRetryAfterHeader
382
424
  ? idempotencyInProgressRetryDelayMs(input.attempt)
383
- : retryAfterMs > 0
384
- ? Math.min(TOOL_EXECUTE_RETRY_DELAY_MAX_MS, retryAfterMs)
385
- : TOOL_EXECUTE_RETRY_DELAY_FALLBACK_MS;
425
+ : retryDecision.reason === 'gateway_invocation_in_progress' &&
426
+ !hasRetryAfterHeader
427
+ ? idempotencyInProgressRetryDelayMs(input.attempt)
428
+ : retryAfterMs > 0
429
+ ? Math.min(TOOL_EXECUTE_RETRY_DELAY_MAX_MS, retryAfterMs)
430
+ : TOOL_EXECUTE_RETRY_DELAY_FALLBACK_MS;
386
431
  return {
387
432
  ...retryDecision,
388
433
  error,
@@ -401,6 +446,8 @@ export function classifyToolExecuteHttpFailure(input: {
401
446
  : TOOL_EXECUTE_BARE_RATE_LIMIT_BACKPRESSURE_MS
402
447
  : null,
403
448
  chargeRetryBudget:
404
- shouldRetry && retryDecision.reason !== 'gateway_invocation_in_progress',
449
+ shouldRetry &&
450
+ retryDecision.reason !== 'gateway_invocation_in_progress' &&
451
+ retryDecision.reason !== 'concurrency_backpressure',
405
452
  };
406
453
  }