@earendil-works/pi-ai 1.0.4 → 1.2.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.
Files changed (121) hide show
  1. package/README.md +14 -3
  2. package/dist/api/anthropic-messages.d.ts.map +1 -1
  3. package/dist/api/anthropic-messages.js +63 -22
  4. package/dist/api/anthropic-messages.js.map +1 -1
  5. package/dist/api/bedrock-converse-stream.js +33 -2
  6. package/dist/api/bedrock-converse-stream.js.map +1 -1
  7. package/dist/api/classifier-shared.d.ts +21 -0
  8. package/dist/api/classifier-shared.d.ts.map +1 -0
  9. package/dist/api/classifier-shared.js +98 -0
  10. package/dist/api/classifier-shared.js.map +1 -0
  11. package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -1
  12. package/dist/api/cloudflare-workers-ai-system-one.js +2 -1
  13. package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -1
  14. package/dist/api/lazy.d.ts.map +1 -1
  15. package/dist/api/lazy.js +4 -3
  16. package/dist/api/lazy.js.map +1 -1
  17. package/dist/api/llama-cpp-classify.d.ts.map +1 -1
  18. package/dist/api/llama-cpp-classify.js +2 -0
  19. package/dist/api/llama-cpp-classify.js.map +1 -1
  20. package/dist/api/mistral-conversations.d.ts.map +1 -1
  21. package/dist/api/mistral-conversations.js +31 -14
  22. package/dist/api/mistral-conversations.js.map +1 -1
  23. package/dist/api/openai-codex-responses.d.ts.map +1 -1
  24. package/dist/api/openai-codex-responses.js +7 -4
  25. package/dist/api/openai-codex-responses.js.map +1 -1
  26. package/dist/api/openai-decisions.d.ts +4 -0
  27. package/dist/api/openai-decisions.d.ts.map +1 -0
  28. package/dist/api/openai-decisions.js +162 -0
  29. package/dist/api/openai-decisions.js.map +1 -0
  30. package/dist/api/openai-decisions.lazy.d.ts +3 -0
  31. package/dist/api/openai-decisions.lazy.d.ts.map +1 -0
  32. package/dist/api/openai-decisions.lazy.js +4 -0
  33. package/dist/api/openai-decisions.lazy.js.map +1 -0
  34. package/dist/api/system-one-shared.d.ts +0 -1
  35. package/dist/api/system-one-shared.d.ts.map +1 -1
  36. package/dist/api/system-one-shared.js +5 -89
  37. package/dist/api/system-one-shared.js.map +1 -1
  38. package/dist/api/typesafe-system-one.d.ts.map +1 -1
  39. package/dist/api/typesafe-system-one.js +2 -1
  40. package/dist/api/typesafe-system-one.js.map +1 -1
  41. package/dist/auth/oauth/anthropic.d.ts.map +1 -1
  42. package/dist/auth/oauth/anthropic.js +14 -6
  43. package/dist/auth/oauth/anthropic.js.map +1 -1
  44. package/dist/auth/oauth/openai-chatgpt.js +1 -1
  45. package/dist/auth/oauth/openai-chatgpt.js.map +1 -1
  46. package/dist/auth/oauth/openai-codex.d.ts.map +1 -1
  47. package/dist/auth/oauth/openai-codex.js +4 -4
  48. package/dist/auth/oauth/openai-codex.js.map +1 -1
  49. package/dist/auth/types.d.ts +5 -0
  50. package/dist/auth/types.d.ts.map +1 -1
  51. package/dist/auth/types.js.map +1 -1
  52. package/dist/models.d.ts +5 -0
  53. package/dist/models.d.ts.map +1 -1
  54. package/dist/models.js +3 -1
  55. package/dist/models.js.map +1 -1
  56. package/dist/providers/compat-schema.d.ts +338 -0
  57. package/dist/providers/compat-schema.d.ts.map +1 -0
  58. package/dist/providers/compat-schema.js +375 -0
  59. package/dist/providers/compat-schema.js.map +1 -0
  60. package/dist/providers/data/.manifest.json +1 -1
  61. package/dist/providers/data/amazon-bedrock.json +1 -1
  62. package/dist/providers/data/anthropic.json +1 -1
  63. package/dist/providers/data/baseten.json +1 -1
  64. package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  65. package/dist/providers/data/cloudflare-workers-ai.json +1 -1
  66. package/dist/providers/data/github-copilot.json +1 -1
  67. package/dist/providers/data/google-vertex.json +1 -1
  68. package/dist/providers/data/google.json +1 -1
  69. package/dist/providers/data/huggingface.json +1 -1
  70. package/dist/providers/data/meta.json +1 -1
  71. package/dist/providers/data/minimax-cn.json +1 -1
  72. package/dist/providers/data/minimax.json +1 -1
  73. package/dist/providers/data/mistral.json +1 -1
  74. package/dist/providers/data/openai.json +1 -1
  75. package/dist/providers/data/opencode-go.json +1 -1
  76. package/dist/providers/data/opencode.json +1 -1
  77. package/dist/providers/data/openrouter.json +1 -1
  78. package/dist/providers/data/radius.json +1 -1
  79. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  80. package/dist/providers/faux.d.ts.map +1 -1
  81. package/dist/providers/faux.js +25 -8
  82. package/dist/providers/faux.js.map +1 -1
  83. package/dist/providers/model-schema.d.ts +91 -0
  84. package/dist/providers/model-schema.d.ts.map +1 -0
  85. package/dist/providers/model-schema.js +55 -0
  86. package/dist/providers/model-schema.js.map +1 -0
  87. package/dist/providers/openai.d.ts.map +1 -1
  88. package/dist/providers/openai.js +12 -3
  89. package/dist/providers/openai.js.map +1 -1
  90. package/dist/providers/openrouter-api.d.ts +40 -0
  91. package/dist/providers/openrouter-api.d.ts.map +1 -0
  92. package/dist/providers/openrouter-api.js +41 -0
  93. package/dist/providers/openrouter-api.js.map +1 -0
  94. package/dist/providers/openrouter.d.ts +11 -1
  95. package/dist/providers/openrouter.d.ts.map +1 -1
  96. package/dist/providers/openrouter.js +52 -1
  97. package/dist/providers/openrouter.js.map +1 -1
  98. package/dist/providers/radius.d.ts.map +1 -1
  99. package/dist/providers/radius.js +4 -12
  100. package/dist/providers/radius.js.map +1 -1
  101. package/dist/types.d.ts +21 -304
  102. package/dist/types.d.ts.map +1 -1
  103. package/dist/types.js.map +1 -1
  104. package/dist/utils/estimate.js +1 -1
  105. package/dist/utils/estimate.js.map +1 -1
  106. package/dist/utils/event-stream.d.ts +10 -1
  107. package/dist/utils/event-stream.d.ts.map +1 -1
  108. package/dist/utils/event-stream.js +25 -0
  109. package/dist/utils/event-stream.js.map +1 -1
  110. package/dist/utils/model-operations.d.ts +3 -1
  111. package/dist/utils/model-operations.d.ts.map +1 -1
  112. package/dist/utils/model-operations.js +6 -0
  113. package/dist/utils/model-operations.js.map +1 -1
  114. package/dist/utils/provider-retry.d.ts +2 -0
  115. package/dist/utils/provider-retry.d.ts.map +1 -1
  116. package/dist/utils/provider-retry.js +2 -0
  117. package/dist/utils/provider-retry.js.map +1 -1
  118. package/dist/utils/retry.d.ts.map +1 -1
  119. package/dist/utils/retry.js +5 -2
  120. package/dist/utils/retry.js.map +1 -1
  121. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"provider-retry.js","sourceRoot":"","sources":["../../src/utils/provider-retry.ts"],"names":[],"mappings":"AAAA,MAAM,0BAA0B,GAAG,MAAM,CAAC;AAa1C,SAAS,eAAe,CAAC,KAAc;IACtC,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7F,OAAO,CACN,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC;QAChE,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,YAAY,OAAO,CAAC,CACjE,CAAC;AACH,CAAC;AAED,gGAAgG;AAChG,SAAS,wBAAwB,CAAC,KAAoB;IACrD,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACzD,IAAI,WAAW,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACxC,IAAI,WAAW,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IAE1C,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5C,OAAO,CACN,KAAK,CAAC,MAAM,KAAK,GAAG;QACpB,KAAK,CAAC,MAAM,KAAK,GAAG;QACpB,KAAK,CAAC,MAAM,KAAK,GAAG;QACpB,CAAC,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG,CAAC,CACzD,CAAC;AACH,CAAC;AAED,SAAS,0BAA0B,CAClC,OAAe,EACf,eAAmC,EACnC,oBAA4B;IAE5B,MAAM,UAAU,GAAG,eAAe,IAAI,0BAA0B,CAAC;IACjE,IAAI,UAAU,GAAG,CAAC,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CACd,oBAAoB,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,uBAAuB,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,OAAO,oBAAoB,EAAE,CAC7H,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,SAAS,eAAe,CAAC,KAAoB,EAAE,UAAkB,EAAE,eAAmC;IACrG,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC1D,IAAI,YAAY,EAAE,CAAC;QAClB,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;QAC9C,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,0BAA0B,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACtG,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;IACrD,IAAI,UAAU,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC;QAC7F,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,OAAO,0BAA0B,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC1G,CAAC;IAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,UAAU,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACnE,OAAO,gBAAgB,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,gBAAgB;IACxB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC3C,KAAK,CAAC,IAAI,GAAG,YAAY,CAAC;IAC1B,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,cAAc,CAAC,EAAU,EAAE,MAAoB;IACvD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACrB,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;YAC3B,OAAO;QACR,CAAC;QAED,MAAM,OAAO,GAAG,GAAG,EAAE;YACpB,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,UAAU,CACzB,GAAG,EAAE;YACJ,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,OAAO,EAAE,CAAC;QACX,CAAC,EACD,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CACf,CAAC;QACF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACzC,OAAyB,EACzB,OAAO,GAAyB,EAAE;IAElC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC;IAC3C,IAAI,gBAAgB,GAAG,UAAU,CAAC;IAElC,SAAS,CAAC;QACT,IAAI,CAAC;YACJ,8EAA8E;YAC9E,OAAO,MAAM,OAAO,EAAE,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;gBAAE,MAAM,gBAAgB,EAAE,CAAC;YACtD,IAAI,gBAAgB,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;YAEtG,MAAM,UAAU,GAAG,UAAU,GAAG,gBAAgB,CAAC;YACjD,gBAAgB,EAAE,CAAC;YACnB,MAAM,cAAc,CAAC,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACnG,CAAC;IACF,CAAC;AACF,CAAC","sourcesContent":["const DEFAULT_MAX_RETRY_DELAY_MS = 60_000;\n\ninterface ProviderRetryOptions {\n\tmaxRetries?: number;\n\tmaxRetryDelayMs?: number;\n\tsignal?: AbortSignal;\n}\n\ninterface ProviderError extends Error {\n\tstatus: number | undefined;\n\theaders: Headers | undefined;\n}\n\nfunction isProviderError(error: unknown): error is ProviderError {\n\tif (!(error instanceof Error) || !(\"status\" in error) || !(\"headers\" in error)) return false;\n\treturn (\n\t\t(error.status === undefined || typeof error.status === \"number\") &&\n\t\t(error.headers === undefined || error.headers instanceof Headers)\n\t);\n}\n\n/** Mirrors the pinned OpenAI/Anthropic SDK retry policy; review when either SDK is upgraded. */\nfunction isRetryableProviderError(error: ProviderError): boolean {\n\tconst shouldRetry = error.headers?.get(\"x-should-retry\");\n\tif (shouldRetry === \"true\") return true;\n\tif (shouldRetry === \"false\") return false;\n\n\tif (error.status === undefined) return true;\n\treturn (\n\t\terror.status === 408 ||\n\t\terror.status === 409 ||\n\t\terror.status === 429 ||\n\t\t(typeof error.status === \"number\" && error.status >= 500)\n\t);\n}\n\nfunction validateServerRetryDelayMs(\n\tdelayMs: number,\n\tmaxRetryDelayMs: number | undefined,\n\tproviderErrorMessage: string,\n): number {\n\tconst maxDelayMs = maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;\n\tif (maxDelayMs > 0 && delayMs > maxDelayMs) {\n\t\tthrow new Error(\n\t\t\t`Server requested ${Math.ceil(delayMs / 1000)}s retry delay (max: ${Math.ceil(maxDelayMs / 1000)}s). ${providerErrorMessage}`,\n\t\t);\n\t}\n\treturn delayMs;\n}\n\nfunction getRetryDelayMs(error: ProviderError, retryIndex: number, maxRetryDelayMs: number | undefined): number {\n\tconst retryAfterMs = error.headers?.get(\"retry-after-ms\");\n\tif (retryAfterMs) {\n\t\tconst value = Number.parseFloat(retryAfterMs);\n\t\tif (Number.isFinite(value)) return validateServerRetryDelayMs(value, maxRetryDelayMs, error.message);\n\t}\n\n\tconst retryAfter = error.headers?.get(\"retry-after\");\n\tif (retryAfter) {\n\t\tconst seconds = Number.parseFloat(retryAfter);\n\t\tconst delayMs = Number.isNaN(seconds) ? Date.parse(retryAfter) - Date.now() : seconds * 1000;\n\t\tif (Number.isFinite(delayMs)) return validateServerRetryDelayMs(delayMs, maxRetryDelayMs, error.message);\n\t}\n\n\tconst exponentialDelay = Math.min(0.5 * 2 ** retryIndex, 8) * 1000;\n\treturn exponentialDelay * (1 - Math.random() * 0.25);\n}\n\nfunction createAbortError(): Error {\n\tconst error = new Error(\"Request aborted\");\n\terror.name = \"AbortError\";\n\treturn error;\n}\n\nfunction abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(createAbortError());\n\t\t\treturn;\n\t\t}\n\n\t\tconst onAbort = () => {\n\t\t\tclearTimeout(timeout);\n\t\t\treject(createAbortError());\n\t\t};\n\t\tconst timeout = setTimeout(\n\t\t\t() => {\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tresolve();\n\t\t\t},\n\t\t\tMath.max(0, ms),\n\t\t);\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t});\n}\n\n/**\n * Reproduce the retry behavior used by the OpenAI and Anthropic SDKs while making\n * their backoff sleep interruptible. Their built-in retry timers ignore the\n * request AbortSignal, so callers must invoke the SDK with `maxRetries: 0` and\n * wrap the request with this helper. Provider-requested delays above\n * `maxRetryDelayMs` fail immediately (60 seconds by default); set it to zero to\n * disable the limit.\n */\nexport async function retryProviderRequest<T>(\n\trequest: () => Promise<T>,\n\toptions: ProviderRetryOptions = {},\n): Promise<T> {\n\tconst maxRetries = options.maxRetries ?? 0;\n\tlet retriesRemaining = maxRetries;\n\n\tfor (;;) {\n\t\ttry {\n\t\t\t// Each retry is a fresh SDK request, so X-Stainless-Retry-Count remains zero.\n\t\t\treturn await request();\n\t\t} catch (error) {\n\t\t\tif (options.signal?.aborted) throw createAbortError();\n\t\t\tif (retriesRemaining <= 0 || !isProviderError(error) || !isRetryableProviderError(error)) throw error;\n\n\t\t\tconst retryIndex = maxRetries - retriesRemaining;\n\t\t\tretriesRemaining--;\n\t\t\tawait abortableSleep(getRetryDelayMs(error, retryIndex, options.maxRetryDelayMs), options.signal);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"provider-retry.js","sourceRoot":"","sources":["../../src/utils/provider-retry.ts"],"names":[],"mappings":"AAAA,MAAM,0BAA0B,GAAG,MAAM,CAAC;AAe1C,SAAS,eAAe,CAAC,KAAc;IACtC,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7F,OAAO,CACN,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC;QAChE,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,YAAY,OAAO,CAAC,CACjE,CAAC;AACH,CAAC;AAED,gGAAgG;AAChG,SAAS,wBAAwB,CAAC,KAAoB;IACrD,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACzD,IAAI,WAAW,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACxC,IAAI,WAAW,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IAE1C,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5C,OAAO,CACN,KAAK,CAAC,MAAM,KAAK,GAAG;QACpB,KAAK,CAAC,MAAM,KAAK,GAAG;QACpB,KAAK,CAAC,MAAM,KAAK,GAAG;QACpB,CAAC,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG,CAAC,CACzD,CAAC;AACH,CAAC;AAED,SAAS,0BAA0B,CAClC,OAAe,EACf,eAAmC,EACnC,oBAA4B;IAE5B,MAAM,UAAU,GAAG,eAAe,IAAI,0BAA0B,CAAC;IACjE,IAAI,UAAU,GAAG,CAAC,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CACd,oBAAoB,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,uBAAuB,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,OAAO,oBAAoB,EAAE,CAC7H,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,SAAS,eAAe,CAAC,KAAoB,EAAE,UAAkB,EAAE,eAAmC;IACrG,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC1D,IAAI,YAAY,EAAE,CAAC;QAClB,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;QAC9C,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,0BAA0B,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACtG,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;IACrD,IAAI,UAAU,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC;QAC7F,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,OAAO,0BAA0B,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC1G,CAAC;IAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,UAAU,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACnE,OAAO,gBAAgB,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,gBAAgB;IACxB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC3C,KAAK,CAAC,IAAI,GAAG,YAAY,CAAC;IAC1B,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,cAAc,CAAC,EAAU,EAAE,MAAoB;IACvD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACrB,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;YAC3B,OAAO;QACR,CAAC;QAED,MAAM,OAAO,GAAG,GAAG,EAAE;YACpB,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,UAAU,CACzB,GAAG,EAAE;YACJ,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,OAAO,EAAE,CAAC;QACX,CAAC,EACD,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CACf,CAAC;QACF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACzC,OAAyB,EACzB,OAAO,GAAyB,EAAE;IAElC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC;IAC3C,IAAI,gBAAgB,GAAG,UAAU,CAAC;IAElC,SAAS,CAAC;QACT,IAAI,CAAC;YACJ,8EAA8E;YAC9E,OAAO,MAAM,OAAO,EAAE,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;gBAAE,MAAM,gBAAgB,EAAE,CAAC;YACtD,IAAI,gBAAgB,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;YACtG,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,eAAe,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;gBAAE,MAAM,KAAK,CAAC;YAE/F,MAAM,UAAU,GAAG,UAAU,GAAG,gBAAgB,CAAC;YACjD,gBAAgB,EAAE,CAAC;YACnB,MAAM,cAAc,CAAC,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACnG,CAAC;IACF,CAAC;AACF,CAAC","sourcesContent":["const DEFAULT_MAX_RETRY_DELAY_MS = 60_000;\n\ninterface ProviderRetryOptions {\n\tmaxRetries?: number;\n\tmaxRetryDelayMs?: number;\n\tsignal?: AbortSignal;\n\t/** HTTP statuses that fail at once although the default policy would retry them. */\n\tnoRetryStatuses?: readonly number[];\n}\n\ninterface ProviderError extends Error {\n\tstatus: number | undefined;\n\theaders: Headers | undefined;\n}\n\nfunction isProviderError(error: unknown): error is ProviderError {\n\tif (!(error instanceof Error) || !(\"status\" in error) || !(\"headers\" in error)) return false;\n\treturn (\n\t\t(error.status === undefined || typeof error.status === \"number\") &&\n\t\t(error.headers === undefined || error.headers instanceof Headers)\n\t);\n}\n\n/** Mirrors the pinned OpenAI/Anthropic SDK retry policy; review when either SDK is upgraded. */\nfunction isRetryableProviderError(error: ProviderError): boolean {\n\tconst shouldRetry = error.headers?.get(\"x-should-retry\");\n\tif (shouldRetry === \"true\") return true;\n\tif (shouldRetry === \"false\") return false;\n\n\tif (error.status === undefined) return true;\n\treturn (\n\t\terror.status === 408 ||\n\t\terror.status === 409 ||\n\t\terror.status === 429 ||\n\t\t(typeof error.status === \"number\" && error.status >= 500)\n\t);\n}\n\nfunction validateServerRetryDelayMs(\n\tdelayMs: number,\n\tmaxRetryDelayMs: number | undefined,\n\tproviderErrorMessage: string,\n): number {\n\tconst maxDelayMs = maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;\n\tif (maxDelayMs > 0 && delayMs > maxDelayMs) {\n\t\tthrow new Error(\n\t\t\t`Server requested ${Math.ceil(delayMs / 1000)}s retry delay (max: ${Math.ceil(maxDelayMs / 1000)}s). ${providerErrorMessage}`,\n\t\t);\n\t}\n\treturn delayMs;\n}\n\nfunction getRetryDelayMs(error: ProviderError, retryIndex: number, maxRetryDelayMs: number | undefined): number {\n\tconst retryAfterMs = error.headers?.get(\"retry-after-ms\");\n\tif (retryAfterMs) {\n\t\tconst value = Number.parseFloat(retryAfterMs);\n\t\tif (Number.isFinite(value)) return validateServerRetryDelayMs(value, maxRetryDelayMs, error.message);\n\t}\n\n\tconst retryAfter = error.headers?.get(\"retry-after\");\n\tif (retryAfter) {\n\t\tconst seconds = Number.parseFloat(retryAfter);\n\t\tconst delayMs = Number.isNaN(seconds) ? Date.parse(retryAfter) - Date.now() : seconds * 1000;\n\t\tif (Number.isFinite(delayMs)) return validateServerRetryDelayMs(delayMs, maxRetryDelayMs, error.message);\n\t}\n\n\tconst exponentialDelay = Math.min(0.5 * 2 ** retryIndex, 8) * 1000;\n\treturn exponentialDelay * (1 - Math.random() * 0.25);\n}\n\nfunction createAbortError(): Error {\n\tconst error = new Error(\"Request aborted\");\n\terror.name = \"AbortError\";\n\treturn error;\n}\n\nfunction abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(createAbortError());\n\t\t\treturn;\n\t\t}\n\n\t\tconst onAbort = () => {\n\t\t\tclearTimeout(timeout);\n\t\t\treject(createAbortError());\n\t\t};\n\t\tconst timeout = setTimeout(\n\t\t\t() => {\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tresolve();\n\t\t\t},\n\t\t\tMath.max(0, ms),\n\t\t);\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t});\n}\n\n/**\n * Reproduce the retry behavior used by the OpenAI and Anthropic SDKs while making\n * their backoff sleep interruptible. Their built-in retry timers ignore the\n * request AbortSignal, so callers must invoke the SDK with `maxRetries: 0` and\n * wrap the request with this helper. Provider-requested delays above\n * `maxRetryDelayMs` fail immediately (60 seconds by default); set it to zero to\n * disable the limit.\n */\nexport async function retryProviderRequest<T>(\n\trequest: () => Promise<T>,\n\toptions: ProviderRetryOptions = {},\n): Promise<T> {\n\tconst maxRetries = options.maxRetries ?? 0;\n\tlet retriesRemaining = maxRetries;\n\n\tfor (;;) {\n\t\ttry {\n\t\t\t// Each retry is a fresh SDK request, so X-Stainless-Retry-Count remains zero.\n\t\t\treturn await request();\n\t\t} catch (error) {\n\t\t\tif (options.signal?.aborted) throw createAbortError();\n\t\t\tif (retriesRemaining <= 0 || !isProviderError(error) || !isRetryableProviderError(error)) throw error;\n\t\t\tif (error.status !== undefined && options.noRetryStatuses?.includes(error.status)) throw error;\n\n\t\t\tconst retryIndex = maxRetries - retriesRemaining;\n\t\t\tretriesRemaining--;\n\t\t\tawait abortableSleep(getRetryDelayMs(error, retryIndex, options.maxRetryDelayMs), options.signal);\n\t\t}\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AA0GpD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,WAAW,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,gCAAgC,QAAS,CAAC;AAEvD,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,aAAa,GAAG,iBAAiB,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIlH;AAED,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC9B,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,CAClB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,mFAAmF;IACnF,mBAAmB,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,mFAAmF;IACnF,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnG;AA0BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,EACxC,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,SAAS,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,gBAAgB,CAAC,CA6C3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAK5E"}
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AA6GpD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,WAAW,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,gCAAgC,QAAS,CAAC;AAEvD,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,aAAa,GAAG,iBAAiB,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIlH;AAED,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC9B,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,CAClB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,mFAAmF;IACnF,mBAAmB,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,mFAAmF;IACnF,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnG;AA0BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,EACxC,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,SAAS,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,gBAAgB,CAAC,CA6C3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAK5E"}
@@ -23,6 +23,8 @@ const NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN = buildProviderErrorPattern([
23
23
  const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
24
24
  // Generic provider load, HTTP status, and server-side transient failures.
25
25
  "overloaded",
26
+ "server_busy",
27
+ "servers are currently busy",
26
28
  "currently experiencing high demand",
27
29
  "model is at capacity",
28
30
  "rate.?limit",
@@ -38,8 +40,9 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
38
40
  "server.?error",
39
41
  "internal.?error",
40
42
  // Wrapper/provider text for transient upstream failures, including OpenRouter
41
- // "Provider returned error" responses (#2264).
42
- "provider.?returned.?error",
43
+ // "Provider returned error" responses (#2264) and gateway wording such as
44
+ // "The upstream provider returned an error" (#10842).
45
+ "provider.?returned.?(?:an.?)?error",
43
46
  "exceeded request buffer limit while retrying upstream",
44
47
  // Network, proxy, and fetch transport failures. This includes OpenAI Codex
45
48
  // raw-fetch failures such as "upstream connect", "connection refused", and
@@ -1 +1 @@
1
- {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"../types.ts\";\n\nfunction buildProviderErrorPattern(patterns: readonly string[]): RegExp {\n\treturn new RegExp(patterns.join(\"|\"), \"i\");\n}\n\nconst NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN = buildProviderErrorPattern([\n\t// OpenCode Go/free-tier limits returned as 429 JSON error types by OpenCode's\n\t// Zen API. These are subscription/account limits, not transient throttles.\n\t\"GoUsageLimitError\",\n\t\"FreeUsageLimitError\",\n\n\t// OpenCode Go subscription-limit text asks users to enable available-balance\n\t// usage after rolling/weekly/monthly limits are reached.\n\t\"Monthly usage limit reached\",\n\t\"available balance\",\n\n\t// Generic quota/budget/billing exhaustion. `insufficient_quota` is OpenAI's\n\t// quota/billing error code; the other strings cover common gateway wording.\n\t\"insufficient_quota\",\n\t\"out of budget\",\n\t\"quota exceeded\",\n\t\"billing\",\n\n\t// Sign in with ChatGPT: the subscription's shared usage limit, which resets\n\t// after hours rather than seconds.\n\t\"subscription_sharing_usage_limit_exceeded\",\n]);\n\nconst RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([\n\t// Generic provider load, HTTP status, and server-side transient failures.\n\t\"overloaded\",\n\t\"currently experiencing high demand\",\n\t\"model is at capacity\",\n\t\"rate.?limit\",\n\t\"too many requests\",\n\t\"429\",\n\t\"500\",\n\t\"502\",\n\t\"503\",\n\t\"504\",\n\t\"520\",\n\t\"524\",\n\t\"service.?unavailable\",\n\t\"server.?error\",\n\t\"internal.?error\",\n\n\t// Wrapper/provider text for transient upstream failures, including OpenRouter\n\t// \"Provider returned error\" responses (#2264).\n\t\"provider.?returned.?error\",\n\t\"exceeded request buffer limit while retrying upstream\",\n\n\t// Network, proxy, and fetch transport failures. This includes OpenAI Codex\n\t// raw-fetch failures such as \"upstream connect\", \"connection refused\", and\n\t// \"reset before headers\" (#733), plus OpenRouter connection drops (#3317).\n\t\"network.?error\",\n\t\"connection.?error\",\n\t\"connection.?refused\",\n\t\"connection.?lost\",\n\t\"other side closed\",\n\t\"fetch failed\",\n\t\"getaddrinfo\",\n\t\"ENOTFOUND\",\n\t\"EAI_AGAIN\",\n\t\"upstream.?connect\",\n\t\"reset before headers\",\n\t\"socket hang up\",\n\t\"socket connection was closed\",\n\t\"timed? out\",\n\t\"timeout\",\n\t\"terminated\",\n\n\t// WebSocket transports can report close/error text instead of HTTP/fetch text.\n\t\"websocket.?closed\",\n\t\"websocket.?error\",\n\n\t// Premature stream endings from SDKs and transports. Anthropic can throw\n\t// \"stream ended without ...\" and \"Anthropic stream ended before message_stop\"\n\t// (#4433); Bedrock/Smithy can throw an HTTP/2 no-response error (#3594).\n\t\"ended without\",\n\t\"stream ended before message_stop\",\n\t\"stream ended before a terminal response event\",\n\t\"http2 request did not get a response\",\n\t// Node ERR_HTTP2_STREAM_CANCEL: the HTTP/2 session died before the request was\n\t// sent, e.g. after the Bedrock SDK's 5-minute session timeout (#10379).\n\t\"pending stream has been canceled\",\n\n\t// Provider-requested retry delay cap failures should flow through the outer\n\t// retry policy so callers can surface/abort the backoff (#1123).\n\t\"retry delay\",\n\n\t// Explicit retry guidance emitted mid-stream by OpenAI Responses and Bedrock\n\t// stream exceptions (#6019).\n\t\"you can retry your request\",\n\t\"try your request again\",\n\t\"please retry your request\",\n\n\t// gRPC based providers (e.g. NVIDIA NIM)\n\t\"ResourceExhausted\",\n\n\t// Sign in with ChatGPT: usage or user data temporarily unavailable. Usage\n\t// failures can arrive mid-stream without an HTTP 503 in the message.\n\t\"subscription_sharing_usage_unavailable\",\n\t\"subscription_sharing_user_unavailable\",\n]);\n\n/**\n * Retry policy: bounded attempts with exponential backoff (`baseDelayMs * 2^(attempt-1)`).\n * `maxAgentDelayMs` caps each computed delay and defaults to 60 seconds.\n * Matches `settings.retry` (`enabled`, `maxRetries`, `baseDelayMs`, `maxAgentDelayMs`) in coding-agent; kept\n * here so the classifier and the policy-driven retry loop live together and stay reusable\n * by the SDK and other callers.\n */\nexport interface RetryPolicy {\n\tenabled: boolean;\n\t/** Max retry attempts (0 = no retries). The initial call never counts as a retry. */\n\tmaxRetries: number;\n\t/** Base delay in ms. Per-attempt delay is `baseDelayMs * 2^(attempt-1)` before jitter. */\n\tbaseDelayMs: number;\n\t/** Optional cap for agent-level retry delays in ms. Defaults to 60 seconds. */\n\tmaxAgentDelayMs?: number;\n}\n\nexport const DEFAULT_MAX_AGENT_RETRY_DELAY_MS = 60_000;\n\nexport function retryDelayMs(policy: Pick<RetryPolicy, \"baseDelayMs\" | \"maxAgentDelayMs\">, attempt: number): number {\n\tconst delay = policy.baseDelayMs * 2 ** Math.max(0, attempt - 1);\n\tconst safeDelay = Number.isSafeInteger(delay) ? delay : Number.MAX_SAFE_INTEGER;\n\treturn Math.min(safeDelay, policy.maxAgentDelayMs ?? DEFAULT_MAX_AGENT_RETRY_DELAY_MS);\n}\n\n/** Optional callbacks emitted by {@link retryAssistantCall} around each retry. */\nexport interface RetryCallbacks {\n\t/** Emitted before the backoff sleep of each retry attempt (1-indexed). */\n\tonRetryScheduled?: (\n\t\tattempt: number,\n\t\tmaxAttempts: number,\n\t\tdelayMs: number,\n\t\terrorMessage: string,\n\t) => void | Promise<void>;\n\t/** Emitted after the backoff sleep, immediately before the retried call starts. */\n\tonRetryAttemptStart?: () => void | Promise<void>;\n\t/** Emitted once when the loop ends: success if a later call completed normally. */\n\tonRetryFinished?: (success: boolean, attempt: number, finalError?: string) => void | Promise<void>;\n}\n\nclass RetrySleepAbortError extends Error {\n\tconstructor() {\n\t\tsuper(\"Aborted\");\n\t}\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(new RetrySleepAbortError());\n\t\t\treturn;\n\t\t}\n\t\tconst timeout = setTimeout(resolve, ms);\n\t\tsignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\treject(new RetrySleepAbortError());\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n}\n\n/**\n * Run a single assistant-producing call with bounded retry on transient errors.\n *\n * Behavior:\n * - A successful response is returned immediately. Aborts are terminal and never\n * retried, but reported as unsuccessful if they happen after a retry was scheduled.\n * Aborts during the backoff sleep are normalized to an aborted `AssistantMessage`\n * too, so callers do not need to care when cancellation happened.\n * - A non-retryable error (per {@link isRetryableAssistantError}, including quota/\n * billing exhaustion) is returned immediately so deterministic errors fail fast.\n * - Otherwise retries up to `maxRetries` times with exponential backoff, emitting\n * `onRetryScheduled` before each sleep, `onRetryAttemptStart` after each sleep before\n * the retried call starts, and `onRetryFinished` once at the end (whether the loop\n * ends in success, exhausted retries, or an aborted backoff).\n *\n * When `policy` is undefined or disabled, the first response is returned unchanged\n * (equivalent to calling `produce()` directly).\n */\nexport async function retryAssistantCall(\n\tproduce: () => Promise<AssistantMessage>,\n\tpolicy: RetryPolicy | undefined,\n\tsignal: AbortSignal | undefined,\n\tcallbacks?: RetryCallbacks,\n): Promise<AssistantMessage> {\n\tconst maxAttempts = policy?.enabled ? policy.maxRetries : 0;\n\n\tlet attempt = 0;\n\tlet lastRetry: { attempt: number; errorMessage: string } | undefined;\n\tfor (;;) {\n\t\tconst response = await produce();\n\n\t\t// Abort: terminal but not successful. Never retry an aborted message.\n\t\tif (response.stopReason === \"aborted\") {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt);\n\t\t\treturn response;\n\t\t}\n\n\t\t// Success: non-error, non-abort responses return as-is.\n\t\tif (response.stopReason !== \"error\") {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(true, lastRetry.attempt);\n\t\t\treturn response;\n\t\t}\n\n\t\t// Non-retryable, or budget exhausted: return the final error message.\n\t\tif (attempt >= maxAttempts || !isRetryableAssistantError(response)) {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt, response.errorMessage);\n\t\t\treturn response;\n\t\t}\n\n\t\tattempt++;\n\t\tlastRetry = { attempt, errorMessage: response.errorMessage || \"Unknown error\" };\n\t\tconst delayMs = retryDelayMs(policy!, attempt);\n\t\tawait callbacks?.onRetryScheduled?.(attempt, maxAttempts, delayMs, lastRetry.errorMessage);\n\n\t\t// Normalize aborts during retry backoff to the same AssistantMessage shape as\n\t\t// provider stream aborts, so callers do not need to care when cancellation happened.\n\t\ttry {\n\t\t\tawait sleep(delayMs, signal);\n\t\t} catch (error) {\n\t\t\tawait callbacks?.onRetryFinished?.(false, attempt, lastRetry.errorMessage);\n\t\t\tif (error instanceof RetrySleepAbortError) {\n\t\t\t\tconst { errorMessage: _errorMessage, ...rest } = response;\n\t\t\t\treturn { ...rest, stopReason: \"aborted\" };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tawait callbacks?.onRetryAttemptStart?.();\n\t}\n}\n\n/**\n * Classifies whether a failed assistant message looks like a transient provider\n * or transport error, so callers can decide if the last assistant turn should be\n * restarted.\n *\n * This does not implement retry policy. Callers should first handle context\n * overflow separately, then apply their own retry budget, backoff, and reporting\n * before restarting the assistant turn.\n */\nexport function isRetryableAssistantError(message: AssistantMessage): boolean {\n\tif (message.stopReason !== \"error\" || !message.errorMessage) return false;\n\tconst errorMessage = message.errorMessage;\n\tif (NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN.test(errorMessage)) return false;\n\treturn RETRYABLE_PROVIDER_ERROR_PATTERN.test(errorMessage);\n}\n"]}
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"../types.ts\";\n\nfunction buildProviderErrorPattern(patterns: readonly string[]): RegExp {\n\treturn new RegExp(patterns.join(\"|\"), \"i\");\n}\n\nconst NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN = buildProviderErrorPattern([\n\t// OpenCode Go/free-tier limits returned as 429 JSON error types by OpenCode's\n\t// Zen API. These are subscription/account limits, not transient throttles.\n\t\"GoUsageLimitError\",\n\t\"FreeUsageLimitError\",\n\n\t// OpenCode Go subscription-limit text asks users to enable available-balance\n\t// usage after rolling/weekly/monthly limits are reached.\n\t\"Monthly usage limit reached\",\n\t\"available balance\",\n\n\t// Generic quota/budget/billing exhaustion. `insufficient_quota` is OpenAI's\n\t// quota/billing error code; the other strings cover common gateway wording.\n\t\"insufficient_quota\",\n\t\"out of budget\",\n\t\"quota exceeded\",\n\t\"billing\",\n\n\t// Sign in with ChatGPT: the subscription's shared usage limit, which resets\n\t// after hours rather than seconds.\n\t\"subscription_sharing_usage_limit_exceeded\",\n]);\n\nconst RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([\n\t// Generic provider load, HTTP status, and server-side transient failures.\n\t\"overloaded\",\n\t\"server_busy\",\n\t\"servers are currently busy\",\n\t\"currently experiencing high demand\",\n\t\"model is at capacity\",\n\t\"rate.?limit\",\n\t\"too many requests\",\n\t\"429\",\n\t\"500\",\n\t\"502\",\n\t\"503\",\n\t\"504\",\n\t\"520\",\n\t\"524\",\n\t\"service.?unavailable\",\n\t\"server.?error\",\n\t\"internal.?error\",\n\n\t// Wrapper/provider text for transient upstream failures, including OpenRouter\n\t// \"Provider returned error\" responses (#2264) and gateway wording such as\n\t// \"The upstream provider returned an error\" (#10842).\n\t\"provider.?returned.?(?:an.?)?error\",\n\t\"exceeded request buffer limit while retrying upstream\",\n\n\t// Network, proxy, and fetch transport failures. This includes OpenAI Codex\n\t// raw-fetch failures such as \"upstream connect\", \"connection refused\", and\n\t// \"reset before headers\" (#733), plus OpenRouter connection drops (#3317).\n\t\"network.?error\",\n\t\"connection.?error\",\n\t\"connection.?refused\",\n\t\"connection.?lost\",\n\t\"other side closed\",\n\t\"fetch failed\",\n\t\"getaddrinfo\",\n\t\"ENOTFOUND\",\n\t\"EAI_AGAIN\",\n\t\"upstream.?connect\",\n\t\"reset before headers\",\n\t\"socket hang up\",\n\t\"socket connection was closed\",\n\t\"timed? out\",\n\t\"timeout\",\n\t\"terminated\",\n\n\t// WebSocket transports can report close/error text instead of HTTP/fetch text.\n\t\"websocket.?closed\",\n\t\"websocket.?error\",\n\n\t// Premature stream endings from SDKs and transports. Anthropic can throw\n\t// \"stream ended without ...\" and \"Anthropic stream ended before message_stop\"\n\t// (#4433); Bedrock/Smithy can throw an HTTP/2 no-response error (#3594).\n\t\"ended without\",\n\t\"stream ended before message_stop\",\n\t\"stream ended before a terminal response event\",\n\t\"http2 request did not get a response\",\n\t// Node ERR_HTTP2_STREAM_CANCEL: the HTTP/2 session died before the request was\n\t// sent, e.g. after the Bedrock SDK's 5-minute session timeout (#10379).\n\t\"pending stream has been canceled\",\n\n\t// Provider-requested retry delay cap failures should flow through the outer\n\t// retry policy so callers can surface/abort the backoff (#1123).\n\t\"retry delay\",\n\n\t// Explicit retry guidance emitted mid-stream by OpenAI Responses and Bedrock\n\t// stream exceptions (#6019).\n\t\"you can retry your request\",\n\t\"try your request again\",\n\t\"please retry your request\",\n\n\t// gRPC based providers (e.g. NVIDIA NIM)\n\t\"ResourceExhausted\",\n\n\t// Sign in with ChatGPT: usage or user data temporarily unavailable. Usage\n\t// failures can arrive mid-stream without an HTTP 503 in the message.\n\t\"subscription_sharing_usage_unavailable\",\n\t\"subscription_sharing_user_unavailable\",\n]);\n\n/**\n * Retry policy: bounded attempts with exponential backoff (`baseDelayMs * 2^(attempt-1)`).\n * `maxAgentDelayMs` caps each computed delay and defaults to 60 seconds.\n * Matches `settings.retry` (`enabled`, `maxRetries`, `baseDelayMs`, `maxAgentDelayMs`) in coding-agent; kept\n * here so the classifier and the policy-driven retry loop live together and stay reusable\n * by the SDK and other callers.\n */\nexport interface RetryPolicy {\n\tenabled: boolean;\n\t/** Max retry attempts (0 = no retries). The initial call never counts as a retry. */\n\tmaxRetries: number;\n\t/** Base delay in ms. Per-attempt delay is `baseDelayMs * 2^(attempt-1)` before jitter. */\n\tbaseDelayMs: number;\n\t/** Optional cap for agent-level retry delays in ms. Defaults to 60 seconds. */\n\tmaxAgentDelayMs?: number;\n}\n\nexport const DEFAULT_MAX_AGENT_RETRY_DELAY_MS = 60_000;\n\nexport function retryDelayMs(policy: Pick<RetryPolicy, \"baseDelayMs\" | \"maxAgentDelayMs\">, attempt: number): number {\n\tconst delay = policy.baseDelayMs * 2 ** Math.max(0, attempt - 1);\n\tconst safeDelay = Number.isSafeInteger(delay) ? delay : Number.MAX_SAFE_INTEGER;\n\treturn Math.min(safeDelay, policy.maxAgentDelayMs ?? DEFAULT_MAX_AGENT_RETRY_DELAY_MS);\n}\n\n/** Optional callbacks emitted by {@link retryAssistantCall} around each retry. */\nexport interface RetryCallbacks {\n\t/** Emitted before the backoff sleep of each retry attempt (1-indexed). */\n\tonRetryScheduled?: (\n\t\tattempt: number,\n\t\tmaxAttempts: number,\n\t\tdelayMs: number,\n\t\terrorMessage: string,\n\t) => void | Promise<void>;\n\t/** Emitted after the backoff sleep, immediately before the retried call starts. */\n\tonRetryAttemptStart?: () => void | Promise<void>;\n\t/** Emitted once when the loop ends: success if a later call completed normally. */\n\tonRetryFinished?: (success: boolean, attempt: number, finalError?: string) => void | Promise<void>;\n}\n\nclass RetrySleepAbortError extends Error {\n\tconstructor() {\n\t\tsuper(\"Aborted\");\n\t}\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(new RetrySleepAbortError());\n\t\t\treturn;\n\t\t}\n\t\tconst timeout = setTimeout(resolve, ms);\n\t\tsignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\treject(new RetrySleepAbortError());\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n}\n\n/**\n * Run a single assistant-producing call with bounded retry on transient errors.\n *\n * Behavior:\n * - A successful response is returned immediately. Aborts are terminal and never\n * retried, but reported as unsuccessful if they happen after a retry was scheduled.\n * Aborts during the backoff sleep are normalized to an aborted `AssistantMessage`\n * too, so callers do not need to care when cancellation happened.\n * - A non-retryable error (per {@link isRetryableAssistantError}, including quota/\n * billing exhaustion) is returned immediately so deterministic errors fail fast.\n * - Otherwise retries up to `maxRetries` times with exponential backoff, emitting\n * `onRetryScheduled` before each sleep, `onRetryAttemptStart` after each sleep before\n * the retried call starts, and `onRetryFinished` once at the end (whether the loop\n * ends in success, exhausted retries, or an aborted backoff).\n *\n * When `policy` is undefined or disabled, the first response is returned unchanged\n * (equivalent to calling `produce()` directly).\n */\nexport async function retryAssistantCall(\n\tproduce: () => Promise<AssistantMessage>,\n\tpolicy: RetryPolicy | undefined,\n\tsignal: AbortSignal | undefined,\n\tcallbacks?: RetryCallbacks,\n): Promise<AssistantMessage> {\n\tconst maxAttempts = policy?.enabled ? policy.maxRetries : 0;\n\n\tlet attempt = 0;\n\tlet lastRetry: { attempt: number; errorMessage: string } | undefined;\n\tfor (;;) {\n\t\tconst response = await produce();\n\n\t\t// Abort: terminal but not successful. Never retry an aborted message.\n\t\tif (response.stopReason === \"aborted\") {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt);\n\t\t\treturn response;\n\t\t}\n\n\t\t// Success: non-error, non-abort responses return as-is.\n\t\tif (response.stopReason !== \"error\") {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(true, lastRetry.attempt);\n\t\t\treturn response;\n\t\t}\n\n\t\t// Non-retryable, or budget exhausted: return the final error message.\n\t\tif (attempt >= maxAttempts || !isRetryableAssistantError(response)) {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt, response.errorMessage);\n\t\t\treturn response;\n\t\t}\n\n\t\tattempt++;\n\t\tlastRetry = { attempt, errorMessage: response.errorMessage || \"Unknown error\" };\n\t\tconst delayMs = retryDelayMs(policy!, attempt);\n\t\tawait callbacks?.onRetryScheduled?.(attempt, maxAttempts, delayMs, lastRetry.errorMessage);\n\n\t\t// Normalize aborts during retry backoff to the same AssistantMessage shape as\n\t\t// provider stream aborts, so callers do not need to care when cancellation happened.\n\t\ttry {\n\t\t\tawait sleep(delayMs, signal);\n\t\t} catch (error) {\n\t\t\tawait callbacks?.onRetryFinished?.(false, attempt, lastRetry.errorMessage);\n\t\t\tif (error instanceof RetrySleepAbortError) {\n\t\t\t\tconst { errorMessage: _errorMessage, ...rest } = response;\n\t\t\t\treturn { ...rest, stopReason: \"aborted\" };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tawait callbacks?.onRetryAttemptStart?.();\n\t}\n}\n\n/**\n * Classifies whether a failed assistant message looks like a transient provider\n * or transport error, so callers can decide if the last assistant turn should be\n * restarted.\n *\n * This does not implement retry policy. Callers should first handle context\n * overflow separately, then apply their own retry budget, backoff, and reporting\n * before restarting the assistant turn.\n */\nexport function isRetryableAssistantError(message: AssistantMessage): boolean {\n\tif (message.stopReason !== \"error\" || !message.errorMessage) return false;\n\tconst errorMessage = message.errorMessage;\n\tif (NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN.test(errorMessage)) return false;\n\treturn RETRYABLE_PROVIDER_ERROR_PATTERN.test(errorMessage);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-ai",
3
- "version": "1.0.4",
3
+ "version": "1.2.0",
4
4
  "description": "Unified LLM API with automatic model discovery and provider configuration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -69,7 +69,7 @@
69
69
  "dependencies": {
70
70
  "@anthropic-ai/sdk": "0.129.0",
71
71
  "@aws-sdk/client-bedrock-runtime": "3.1127.0",
72
- "@earendil-works/pi-telemetry": "^1.0.4",
72
+ "@earendil-works/pi-telemetry": "^1.2.0",
73
73
  "@google/genai": "2.21.0",
74
74
  "@smithy/node-http-handler": "4.12.1",
75
75
  "http-proxy-agent": "9.1.0",