@code-yeongyu/senpi-ai 2026.9.26 → 2026.9.27-3
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/api/anthropic-messages.d.ts.map +1 -1
- package/dist/api/anthropic-messages.js +10 -7
- package/dist/api/anthropic-messages.js.map +1 -1
- package/dist/api/openai-completions.d.ts.map +1 -1
- package/dist/api/openai-completions.js +9 -1
- package/dist/api/openai-completions.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/provider-diagnostic.d.ts +33 -0
- package/dist/provider-diagnostic.d.ts.map +1 -0
- package/dist/provider-diagnostic.js +56 -0
- package/dist/provider-diagnostic.js.map +1 -0
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/fireworks.json +1 -1
- package/dist/providers/data/opencode-go.json +1 -1
- package/dist/providers/data/opencode.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/provider-diagnostic-carrier.d.ts +5 -0
- package/dist/utils/provider-diagnostic-carrier.d.ts.map +1 -0
- package/dist/utils/provider-diagnostic-carrier.js +18 -0
- package/dist/utils/provider-diagnostic-carrier.js.map +1 -0
- package/dist/utils/provider-diagnostic-sources.d.ts +26 -0
- package/dist/utils/provider-diagnostic-sources.d.ts.map +1 -0
- package/dist/utils/provider-diagnostic-sources.js +103 -0
- package/dist/utils/provider-diagnostic-sources.js.map +1 -0
- package/dist/utils/provider-diagnostic-vocabulary.d.ts +36 -0
- package/dist/utils/provider-diagnostic-vocabulary.d.ts.map +1 -0
- package/dist/utils/provider-diagnostic-vocabulary.js +129 -0
- package/dist/utils/provider-diagnostic-vocabulary.js.map +1 -0
- package/dist/utils/provider-retry.d.ts.map +1 -1
- package/dist/utils/provider-retry.js +5 -4
- package/dist/utils/provider-retry.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/** Serialized UTF-8 budget for one emitted diagnostic. */
|
|
2
|
+
export const PROVIDER_DIAGNOSTIC_MAX_BYTES = 512;
|
|
3
|
+
/**
|
|
4
|
+
* Closed allowlist of provider error tokens and the family each one proves.
|
|
5
|
+
* Exact tokens only: Anthropic `error.type` values and OpenAI-compatible
|
|
6
|
+
* `error.code` values. `billing_error` proves a billing failure, not quota
|
|
7
|
+
* exhaustion, so it stays `unknown`.
|
|
8
|
+
*/
|
|
9
|
+
export function categoryForProviderCode(code) {
|
|
10
|
+
switch (code) {
|
|
11
|
+
case "authentication_error":
|
|
12
|
+
case "invalid_api_key":
|
|
13
|
+
return "auth";
|
|
14
|
+
case "rate_limit_error":
|
|
15
|
+
case "rate_limit_exceeded":
|
|
16
|
+
return "rate_limit";
|
|
17
|
+
case "insufficient_quota":
|
|
18
|
+
return "quota";
|
|
19
|
+
case "context_length_exceeded":
|
|
20
|
+
return "context_limit";
|
|
21
|
+
case "invalid_request_error":
|
|
22
|
+
return "invalid_request";
|
|
23
|
+
case "overloaded_error":
|
|
24
|
+
case "api_error":
|
|
25
|
+
case "timeout_error":
|
|
26
|
+
return "provider_unavailable";
|
|
27
|
+
case "billing_error":
|
|
28
|
+
case "permission_error":
|
|
29
|
+
case "not_found_error":
|
|
30
|
+
return "unknown";
|
|
31
|
+
default:
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** The HTTP status a recognized token may arrive with; anything else is contradictory evidence. */
|
|
36
|
+
export function isProviderCodeStatusCompatible(code, status) {
|
|
37
|
+
switch (code) {
|
|
38
|
+
case "authentication_error":
|
|
39
|
+
case "invalid_api_key":
|
|
40
|
+
return status === 401;
|
|
41
|
+
case "rate_limit_error":
|
|
42
|
+
case "rate_limit_exceeded":
|
|
43
|
+
case "insufficient_quota":
|
|
44
|
+
return status === 429;
|
|
45
|
+
case "context_length_exceeded":
|
|
46
|
+
case "invalid_request_error":
|
|
47
|
+
return status === 400;
|
|
48
|
+
case "overloaded_error":
|
|
49
|
+
case "api_error":
|
|
50
|
+
case "timeout_error":
|
|
51
|
+
return status >= 500 && status <= 599;
|
|
52
|
+
case "billing_error":
|
|
53
|
+
return status === 402;
|
|
54
|
+
case "permission_error":
|
|
55
|
+
return status === 403;
|
|
56
|
+
case "not_found_error":
|
|
57
|
+
return status === 404;
|
|
58
|
+
default:
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Status-only family. 402/403/429 alone cannot tell quota, auth and rate limit apart. */
|
|
63
|
+
export function categoryForProviderStatus(status) {
|
|
64
|
+
if (status === 401)
|
|
65
|
+
return "auth";
|
|
66
|
+
if (status === 400)
|
|
67
|
+
return "invalid_request";
|
|
68
|
+
if (status >= 500 && status <= 599)
|
|
69
|
+
return "provider_unavailable";
|
|
70
|
+
return "unknown";
|
|
71
|
+
}
|
|
72
|
+
export function isProviderDiagnosticStatus(value) {
|
|
73
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 400 && value <= 599;
|
|
74
|
+
}
|
|
75
|
+
export function isProviderDiagnosticCategory(value) {
|
|
76
|
+
switch (value) {
|
|
77
|
+
case "auth":
|
|
78
|
+
case "rate_limit":
|
|
79
|
+
case "quota":
|
|
80
|
+
case "context_limit":
|
|
81
|
+
case "invalid_request":
|
|
82
|
+
case "provider_unavailable":
|
|
83
|
+
case "unknown":
|
|
84
|
+
return true;
|
|
85
|
+
default:
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Fresh flat object, one field at a time, dropped when it exceeds the byte budget. */
|
|
90
|
+
export function buildProviderDiagnostic(input) {
|
|
91
|
+
const diagnostic = { category: input.category, evidence: input.evidence };
|
|
92
|
+
if (input.httpStatus !== undefined)
|
|
93
|
+
diagnostic.httpStatus = input.httpStatus;
|
|
94
|
+
if (input.code !== undefined)
|
|
95
|
+
diagnostic.code = input.code;
|
|
96
|
+
const size = new TextEncoder().encode(JSON.stringify(diagnostic)).length;
|
|
97
|
+
return size <= PROVIDER_DIAGNOSTIC_MAX_BYTES ? diagnostic : undefined;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Classify already-extracted structured facts. A supplied status that is not a
|
|
101
|
+
* valid 4xx/5xx integer, or a recognized code that contradicts the status,
|
|
102
|
+
* yields nothing. An unrecognized code proves nothing and is discarded; a valid
|
|
103
|
+
* status may still classify on its own.
|
|
104
|
+
*/
|
|
105
|
+
export function classifyProviderFailure(facts) {
|
|
106
|
+
const statusSupplied = facts.status !== undefined && facts.status !== null;
|
|
107
|
+
if (statusSupplied && !isProviderDiagnosticStatus(facts.status))
|
|
108
|
+
return undefined;
|
|
109
|
+
const status = statusSupplied && isProviderDiagnosticStatus(facts.status) ? facts.status : undefined;
|
|
110
|
+
const category = facts.code === undefined ? undefined : categoryForProviderCode(facts.code);
|
|
111
|
+
if (facts.code !== undefined && category !== undefined) {
|
|
112
|
+
if (status !== undefined && !isProviderCodeStatusCompatible(facts.code, status))
|
|
113
|
+
return undefined;
|
|
114
|
+
return buildProviderDiagnostic({
|
|
115
|
+
category,
|
|
116
|
+
...(status === undefined ? {} : { httpStatus: status }),
|
|
117
|
+
code: facts.code,
|
|
118
|
+
evidence: "structured_code",
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (status === undefined)
|
|
122
|
+
return undefined;
|
|
123
|
+
return buildProviderDiagnostic({
|
|
124
|
+
category: categoryForProviderStatus(status),
|
|
125
|
+
httpStatus: status,
|
|
126
|
+
evidence: "structured_status",
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=provider-diagnostic-vocabulary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-diagnostic-vocabulary.js","sourceRoot":"","sources":["../../src/utils/provider-diagnostic-vocabulary.ts"],"names":[],"mappings":"AAMA,0DAA0D;AAC1D,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IACnD,QAAQ,IAAI,EAAE,CAAC;QACd,KAAK,sBAAsB,CAAC;QAC5B,KAAK,iBAAiB;YACrB,OAAO,MAAM,CAAC;QACf,KAAK,kBAAkB,CAAC;QACxB,KAAK,qBAAqB;YACzB,OAAO,YAAY,CAAC;QACrB,KAAK,oBAAoB;YACxB,OAAO,OAAO,CAAC;QAChB,KAAK,yBAAyB;YAC7B,OAAO,eAAe,CAAC;QACxB,KAAK,uBAAuB;YAC3B,OAAO,iBAAiB,CAAC;QAC1B,KAAK,kBAAkB,CAAC;QACxB,KAAK,WAAW,CAAC;QACjB,KAAK,eAAe;YACnB,OAAO,sBAAsB,CAAC;QAC/B,KAAK,eAAe,CAAC;QACrB,KAAK,kBAAkB,CAAC;QACxB,KAAK,iBAAiB;YACrB,OAAO,SAAS,CAAC;QAClB;YACC,OAAO,SAAS,CAAC;IACnB,CAAC;AACF,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,8BAA8B,CAAC,IAAY,EAAE,MAAc;IAC1E,QAAQ,IAAI,EAAE,CAAC;QACd,KAAK,sBAAsB,CAAC;QAC5B,KAAK,iBAAiB;YACrB,OAAO,MAAM,KAAK,GAAG,CAAC;QACvB,KAAK,kBAAkB,CAAC;QACxB,KAAK,qBAAqB,CAAC;QAC3B,KAAK,oBAAoB;YACxB,OAAO,MAAM,KAAK,GAAG,CAAC;QACvB,KAAK,yBAAyB,CAAC;QAC/B,KAAK,uBAAuB;YAC3B,OAAO,MAAM,KAAK,GAAG,CAAC;QACvB,KAAK,kBAAkB,CAAC;QACxB,KAAK,WAAW,CAAC;QACjB,KAAK,eAAe;YACnB,OAAO,MAAM,IAAI,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC;QACvC,KAAK,eAAe;YACnB,OAAO,MAAM,KAAK,GAAG,CAAC;QACvB,KAAK,kBAAkB;YACtB,OAAO,MAAM,KAAK,GAAG,CAAC;QACvB,KAAK,iBAAiB;YACrB,OAAO,MAAM,KAAK,GAAG,CAAC;QACvB;YACC,OAAO,KAAK,CAAC;IACf,CAAC;AACF,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,yBAAyB,CAAC,MAAc;IACvD,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,iBAAiB,CAAC;IAC7C,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,IAAI,GAAG;QAAE,OAAO,sBAAsB,CAAC;IAClE,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,KAAc;IACxD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG,CAAC;AAC7F,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,KAAc;IAC1D,QAAQ,KAAK,EAAE,CAAC;QACf,KAAK,MAAM,CAAC;QACZ,KAAK,YAAY,CAAC;QAClB,KAAK,OAAO,CAAC;QACb,KAAK,eAAe,CAAC;QACrB,KAAK,iBAAiB,CAAC;QACvB,KAAK,sBAAsB,CAAC;QAC5B,KAAK,SAAS;YACb,OAAO,IAAI,CAAC;QACb;YACC,OAAO,KAAK,CAAC;IACf,CAAC;AACF,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,uBAAuB,CAAC,KAKvC;IACA,MAAM,UAAU,GAAuB,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC9F,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS;QAAE,UAAU,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IAC7E,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,UAAU,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IAC3D,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC;IACzE,OAAO,IAAI,IAAI,6BAA6B,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;AACvE,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAA2B;IAClE,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC;IAC3E,IAAI,cAAc,IAAI,CAAC,0BAA0B,CAAC,KAAK,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IAClF,MAAM,MAAM,GAAG,cAAc,IAAI,0BAA0B,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IACrG,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,uBAAuB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5F,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QACxD,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,8BAA8B,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC;YAAE,OAAO,SAAS,CAAC;QAClG,OAAO,uBAAuB,CAAC;YAC9B,QAAQ;YACR,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;YACvD,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,QAAQ,EAAE,iBAAiB;SAC3B,CAAC,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3C,OAAO,uBAAuB,CAAC;QAC9B,QAAQ,EAAE,yBAAyB,CAAC,MAAM,CAAC;QAC3C,UAAU,EAAE,MAAM;QAClB,QAAQ,EAAE,mBAAmB;KAC7B,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type {\n\tProviderDiagnostic,\n\tProviderDiagnosticCategory,\n\tProviderDiagnosticEvidence,\n} from \"../provider-diagnostic.ts\";\n\n/** Serialized UTF-8 budget for one emitted diagnostic. */\nexport const PROVIDER_DIAGNOSTIC_MAX_BYTES = 512;\n\n/**\n * Closed allowlist of provider error tokens and the family each one proves.\n * Exact tokens only: Anthropic `error.type` values and OpenAI-compatible\n * `error.code` values. `billing_error` proves a billing failure, not quota\n * exhaustion, so it stays `unknown`.\n */\nexport function categoryForProviderCode(code: string): ProviderDiagnosticCategory | undefined {\n\tswitch (code) {\n\t\tcase \"authentication_error\":\n\t\tcase \"invalid_api_key\":\n\t\t\treturn \"auth\";\n\t\tcase \"rate_limit_error\":\n\t\tcase \"rate_limit_exceeded\":\n\t\t\treturn \"rate_limit\";\n\t\tcase \"insufficient_quota\":\n\t\t\treturn \"quota\";\n\t\tcase \"context_length_exceeded\":\n\t\t\treturn \"context_limit\";\n\t\tcase \"invalid_request_error\":\n\t\t\treturn \"invalid_request\";\n\t\tcase \"overloaded_error\":\n\t\tcase \"api_error\":\n\t\tcase \"timeout_error\":\n\t\t\treturn \"provider_unavailable\";\n\t\tcase \"billing_error\":\n\t\tcase \"permission_error\":\n\t\tcase \"not_found_error\":\n\t\t\treturn \"unknown\";\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\n/** The HTTP status a recognized token may arrive with; anything else is contradictory evidence. */\nexport function isProviderCodeStatusCompatible(code: string, status: number): boolean {\n\tswitch (code) {\n\t\tcase \"authentication_error\":\n\t\tcase \"invalid_api_key\":\n\t\t\treturn status === 401;\n\t\tcase \"rate_limit_error\":\n\t\tcase \"rate_limit_exceeded\":\n\t\tcase \"insufficient_quota\":\n\t\t\treturn status === 429;\n\t\tcase \"context_length_exceeded\":\n\t\tcase \"invalid_request_error\":\n\t\t\treturn status === 400;\n\t\tcase \"overloaded_error\":\n\t\tcase \"api_error\":\n\t\tcase \"timeout_error\":\n\t\t\treturn status >= 500 && status <= 599;\n\t\tcase \"billing_error\":\n\t\t\treturn status === 402;\n\t\tcase \"permission_error\":\n\t\t\treturn status === 403;\n\t\tcase \"not_found_error\":\n\t\t\treturn status === 404;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\n/** Status-only family. 402/403/429 alone cannot tell quota, auth and rate limit apart. */\nexport function categoryForProviderStatus(status: number): ProviderDiagnosticCategory {\n\tif (status === 401) return \"auth\";\n\tif (status === 400) return \"invalid_request\";\n\tif (status >= 500 && status <= 599) return \"provider_unavailable\";\n\treturn \"unknown\";\n}\n\nexport function isProviderDiagnosticStatus(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isInteger(value) && value >= 400 && value <= 599;\n}\n\nexport function isProviderDiagnosticCategory(value: unknown): value is ProviderDiagnosticCategory {\n\tswitch (value) {\n\t\tcase \"auth\":\n\t\tcase \"rate_limit\":\n\t\tcase \"quota\":\n\t\tcase \"context_limit\":\n\t\tcase \"invalid_request\":\n\t\tcase \"provider_unavailable\":\n\t\tcase \"unknown\":\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\n/** Fresh flat object, one field at a time, dropped when it exceeds the byte budget. */\nexport function buildProviderDiagnostic(input: {\n\tcategory: ProviderDiagnosticCategory;\n\thttpStatus?: number;\n\tcode?: string;\n\tevidence: ProviderDiagnosticEvidence;\n}): ProviderDiagnostic | undefined {\n\tconst diagnostic: ProviderDiagnostic = { category: input.category, evidence: input.evidence };\n\tif (input.httpStatus !== undefined) diagnostic.httpStatus = input.httpStatus;\n\tif (input.code !== undefined) diagnostic.code = input.code;\n\tconst size = new TextEncoder().encode(JSON.stringify(diagnostic)).length;\n\treturn size <= PROVIDER_DIAGNOSTIC_MAX_BYTES ? diagnostic : undefined;\n}\n\nexport interface ProviderFailureFacts {\n\t/** Status read from the transport error; `undefined` for an in-stream error. */\n\treadonly status: unknown;\n\treadonly code: string | undefined;\n}\n\n/**\n * Classify already-extracted structured facts. A supplied status that is not a\n * valid 4xx/5xx integer, or a recognized code that contradicts the status,\n * yields nothing. An unrecognized code proves nothing and is discarded; a valid\n * status may still classify on its own.\n */\nexport function classifyProviderFailure(facts: ProviderFailureFacts): ProviderDiagnostic | undefined {\n\tconst statusSupplied = facts.status !== undefined && facts.status !== null;\n\tif (statusSupplied && !isProviderDiagnosticStatus(facts.status)) return undefined;\n\tconst status = statusSupplied && isProviderDiagnosticStatus(facts.status) ? facts.status : undefined;\n\tconst category = facts.code === undefined ? undefined : categoryForProviderCode(facts.code);\n\tif (facts.code !== undefined && category !== undefined) {\n\t\tif (status !== undefined && !isProviderCodeStatusCompatible(facts.code, status)) return undefined;\n\t\treturn buildProviderDiagnostic({\n\t\t\tcategory,\n\t\t\t...(status === undefined ? {} : { httpStatus: status }),\n\t\t\tcode: facts.code,\n\t\t\tevidence: \"structured_code\",\n\t\t});\n\t}\n\tif (status === undefined) return undefined;\n\treturn buildProviderDiagnostic({\n\t\tcategory: categoryForProviderStatus(status),\n\t\thttpStatus: status,\n\t\tevidence: \"structured_status\",\n\t});\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-retry.d.ts","sourceRoot":"","sources":["../../src/utils/provider-retry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"provider-retry.d.ts","sourceRoot":"","sources":["../../src/utils/provider-retry.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,uBAAwB,SAAQ,KAAK;IACrD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC9B;AAED,UAAU,oBAAoB;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AA0FD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,EAC3C,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACzB,OAAO,GAAE,oBAAyB,GAChC,OAAO,CAAC,CAAC,CAAC,CAiBZ;AAED,UAAU,qBAAqB,CAAC,MAAM,EAAE,SAAS;IAChD,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9B,QAAQ,EAAE,SAAS,CAAC;CACpB;AAgCD,wBAAsB,0BAA0B,CAAC,MAAM,EAAE,SAAS,EACjE,OAAO,EAAE,MAAM,OAAO,CAAC,qBAAqB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,EAChE,OAAO,GAAE,oBAAyB,GAChC,OAAO,CAAC,qBAAqB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAUnD"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { attachProviderDiagnostic, peekProviderDiagnostic } from "./provider-diagnostic-carrier.js";
|
|
1
2
|
import { appendRetryAfterMsMarker, extract429RetryAfterMs } from "./retry-hint.js";
|
|
2
3
|
const DEFAULT_MAX_RETRY_DELAY_MS = 60_000;
|
|
3
4
|
const DIGITALOCEAN_STREAM_FAILURE_MESSAGE = "Upstream error from DigitalOcean: stream failed";
|
|
@@ -25,14 +26,14 @@ function isRetryableProviderError(error) {
|
|
|
25
26
|
error.status === 429 ||
|
|
26
27
|
(typeof error.status === "number" && error.status >= 500));
|
|
27
28
|
}
|
|
28
|
-
function validateServerRetryDelayMs(delayMs, maxRetryDelayMs,
|
|
29
|
+
function validateServerRetryDelayMs(delayMs, maxRetryDelayMs, providerError) {
|
|
29
30
|
const maxDelayMs = maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;
|
|
30
31
|
if (maxDelayMs > 0 && delayMs > maxDelayMs) {
|
|
31
|
-
const message = `Server requested ${Math.ceil(delayMs / 1000)}s retry delay (max: ${Math.ceil(maxDelayMs / 1000)}s). ${
|
|
32
|
+
const message = `Server requested ${Math.ceil(delayMs / 1000)}s retry delay (max: ${Math.ceil(maxDelayMs / 1000)}s). ${providerError.message}`;
|
|
32
33
|
const error = Object.assign(new Error(appendRetryAfterMsMarker(message, delayMs)), {
|
|
33
34
|
retryAfterMs: delayMs,
|
|
34
35
|
});
|
|
35
|
-
throw error;
|
|
36
|
+
throw attachProviderDiagnostic(error, peekProviderDiagnostic(providerError));
|
|
36
37
|
}
|
|
37
38
|
return delayMs;
|
|
38
39
|
}
|
|
@@ -43,7 +44,7 @@ function getRetryDelayMs(error, retryIndex, maxRetryDelayMs) {
|
|
|
43
44
|
bodyText: "",
|
|
44
45
|
});
|
|
45
46
|
if (hintMs !== undefined) {
|
|
46
|
-
return validateServerRetryDelayMs(hintMs, maxRetryDelayMs, error
|
|
47
|
+
return validateServerRetryDelayMs(hintMs, maxRetryDelayMs, error);
|
|
47
48
|
}
|
|
48
49
|
const exponentialDelay = Math.min(0.5 * 2 ** retryIndex, 8) * 1000;
|
|
49
50
|
return exponentialDelay * (1 - Math.random() * 0.25);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-retry.js","sourceRoot":"","sources":["../../src/utils/provider-retry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEnF,MAAM,0BAA0B,GAAG,MAAM,CAAC;AAC1C,MAAM,mCAAmC,GAAG,iDAAiD,CAAC;AAiB9F,SAAS,eAAe,CAAC,KAAc;IACtC,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,KAAK,CAAC,OAAO,KAAK,mCAAmC;QAAE,OAAO,IAAI,CAAC;IACvE,IAAI,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAChE,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,OAAO,GAAG,oBAAoB,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,uBAAuB,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,OAAO,oBAAoB,EAAE,CAAC;QAC9I,MAAM,KAAK,GAA4B,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,wBAAwB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE;YAC3G,YAAY,EAAE,OAAO;SACrB,CAAC,CAAC;QACH,MAAM,KAAK,CAAC;IACb,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,SAAS,eAAe,CAAC,KAAoB,EAAE,UAAkB,EAAE,eAAmC;IACrG,MAAM,MAAM,GAAG,sBAAsB,CAAC;QACrC,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,QAAQ,EAAE,EAAE;KACZ,CAAC,CAAC;IACH,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,0BAA0B,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3E,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;AAOD,KAAK,SAAS,CAAC,CAAC,sBAAsB,CACrC,KAA6B,EAC7B,QAA+B;IAE/B,IAAI,KAAK,CAAC,IAAI;QAAE,OAAO;IACvB,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,cAAc,GAAG,KAAK,CAAC;IAC3B,IAAI,CAAC;QACJ,MAAM,KAAK,CAAC,KAAK,CAAC;QAClB,SAAS,CAAC;YACT,IAAI,IAA4B,CAAC;YACjC,IAAI,CAAC;gBACJ,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YAC9B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,cAAc,GAAG,IAAI,CAAC;gBACtB,MAAM,KAAK,CAAC;YACb,CAAC;YACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACf,SAAS,GAAG,IAAI,CAAC;gBACjB,OAAO;YACR,CAAC;YACD,MAAM,IAAI,CAAC,KAAK,CAAC;QAClB,CAAC;IACF,CAAC;YAAS,CAAC;QACV,IAAI,CAAC,SAAS,IAAI,CAAC,cAAc,EAAE,CAAC;YACnC,MAAM,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;QAC3B,CAAC;IACF,CAAC;AACF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC/C,OAAgE,EAChE,OAAO,GAAyB,EAAE;IAElC,OAAO,oBAAoB,CAAC,KAAK,IAAI,EAAE;QACtC,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;QACxD,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACpC,OAAO;YACN,MAAM,EAAE,sBAAsB,CAAC,KAAK,EAAE,QAAQ,CAAC;YAC/C,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC1B,CAAC;IACH,CAAC,EAAE,OAAO,CAAC,CAAC;AACb,CAAC","sourcesContent":["import { appendRetryAfterMsMarker, extract429RetryAfterMs } from \"./retry-hint.ts\";\n\nconst DEFAULT_MAX_RETRY_DELAY_MS = 60_000;\nconst DIGITALOCEAN_STREAM_FAILURE_MESSAGE = \"Upstream error from DigitalOcean: stream failed\";\n\nexport interface ProviderRetryDelayError extends Error {\n\treadonly retryAfterMs: number;\n}\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)) return false;\n\tif (error.message === DIGITALOCEAN_STREAM_FAILURE_MESSAGE) return true;\n\tif (!(\"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\tconst message = `Server requested ${Math.ceil(delayMs / 1000)}s retry delay (max: ${Math.ceil(maxDelayMs / 1000)}s). ${providerErrorMessage}`;\n\t\tconst error: ProviderRetryDelayError = Object.assign(new Error(appendRetryAfterMsMarker(message, delayMs)), {\n\t\t\tretryAfterMs: delayMs,\n\t\t});\n\t\tthrow error;\n\t}\n\treturn delayMs;\n}\n\nfunction getRetryDelayMs(error: ProviderError, retryIndex: number, maxRetryDelayMs: number | undefined): number {\n\tconst hintMs = extract429RetryAfterMs({\n\t\tstatus: 429,\n\t\theaders: error.headers,\n\t\tbodyText: \"\",\n\t});\n\tif (hintMs !== undefined) {\n\t\treturn validateServerRetryDelayMs(hintMs, 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\ninterface ProviderStreamAttempt<TChunk, TMetadata> {\n\tstream: AsyncIterable<TChunk>;\n\tmetadata: TMetadata;\n}\n\nasync function* replayPrefetchedStream<TChunk>(\n\tfirst: IteratorResult<TChunk>,\n\titerator: AsyncIterator<TChunk>,\n): AsyncGenerator<TChunk> {\n\tif (first.done) return;\n\tlet completed = false;\n\tlet providerFailed = false;\n\ttry {\n\t\tyield first.value;\n\t\tfor (;;) {\n\t\t\tlet next: IteratorResult<TChunk>;\n\t\t\ttry {\n\t\t\t\tnext = await iterator.next();\n\t\t\t} catch (error) {\n\t\t\t\tproviderFailed = true;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tif (next.done) {\n\t\t\t\tcompleted = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tyield next.value;\n\t\t}\n\t} finally {\n\t\tif (!completed && !providerFailed) {\n\t\t\tawait iterator.return?.();\n\t\t}\n\t}\n}\n\nexport async function retryProviderStreamRequest<TChunk, TMetadata>(\n\trequest: () => Promise<ProviderStreamAttempt<TChunk, TMetadata>>,\n\toptions: ProviderRetryOptions = {},\n): Promise<ProviderStreamAttempt<TChunk, TMetadata>> {\n\treturn retryProviderRequest(async () => {\n\t\tconst attempt = await request();\n\t\tconst iterator = attempt.stream[Symbol.asyncIterator]();\n\t\tconst first = await iterator.next();\n\t\treturn {\n\t\t\tstream: replayPrefetchedStream(first, iterator),\n\t\t\tmetadata: attempt.metadata,\n\t\t};\n\t}, options);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"provider-retry.js","sourceRoot":"","sources":["../../src/utils/provider-retry.ts"],"names":[],"mappings":"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","sourcesContent":["import { attachProviderDiagnostic, peekProviderDiagnostic } from \"./provider-diagnostic-carrier.ts\";\nimport { appendRetryAfterMsMarker, extract429RetryAfterMs } from \"./retry-hint.ts\";\n\nconst DEFAULT_MAX_RETRY_DELAY_MS = 60_000;\nconst DIGITALOCEAN_STREAM_FAILURE_MESSAGE = \"Upstream error from DigitalOcean: stream failed\";\n\nexport interface ProviderRetryDelayError extends Error {\n\treadonly retryAfterMs: number;\n}\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)) return false;\n\tif (error.message === DIGITALOCEAN_STREAM_FAILURE_MESSAGE) return true;\n\tif (!(\"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\tproviderError: ProviderError,\n): number {\n\tconst maxDelayMs = maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;\n\tif (maxDelayMs > 0 && delayMs > maxDelayMs) {\n\t\tconst message = `Server requested ${Math.ceil(delayMs / 1000)}s retry delay (max: ${Math.ceil(maxDelayMs / 1000)}s). ${providerError.message}`;\n\t\tconst error: ProviderRetryDelayError = Object.assign(new Error(appendRetryAfterMsMarker(message, delayMs)), {\n\t\t\tretryAfterMs: delayMs,\n\t\t});\n\t\tthrow attachProviderDiagnostic(error, peekProviderDiagnostic(providerError));\n\t}\n\treturn delayMs;\n}\n\nfunction getRetryDelayMs(error: ProviderError, retryIndex: number, maxRetryDelayMs: number | undefined): number {\n\tconst hintMs = extract429RetryAfterMs({\n\t\tstatus: 429,\n\t\theaders: error.headers,\n\t\tbodyText: \"\",\n\t});\n\tif (hintMs !== undefined) {\n\t\treturn validateServerRetryDelayMs(hintMs, maxRetryDelayMs, error);\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\ninterface ProviderStreamAttempt<TChunk, TMetadata> {\n\tstream: AsyncIterable<TChunk>;\n\tmetadata: TMetadata;\n}\n\nasync function* replayPrefetchedStream<TChunk>(\n\tfirst: IteratorResult<TChunk>,\n\titerator: AsyncIterator<TChunk>,\n): AsyncGenerator<TChunk> {\n\tif (first.done) return;\n\tlet completed = false;\n\tlet providerFailed = false;\n\ttry {\n\t\tyield first.value;\n\t\tfor (;;) {\n\t\t\tlet next: IteratorResult<TChunk>;\n\t\t\ttry {\n\t\t\t\tnext = await iterator.next();\n\t\t\t} catch (error) {\n\t\t\t\tproviderFailed = true;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tif (next.done) {\n\t\t\t\tcompleted = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tyield next.value;\n\t\t}\n\t} finally {\n\t\tif (!completed && !providerFailed) {\n\t\t\tawait iterator.return?.();\n\t\t}\n\t}\n}\n\nexport async function retryProviderStreamRequest<TChunk, TMetadata>(\n\trequest: () => Promise<ProviderStreamAttempt<TChunk, TMetadata>>,\n\toptions: ProviderRetryOptions = {},\n): Promise<ProviderStreamAttempt<TChunk, TMetadata>> {\n\treturn retryProviderRequest(async () => {\n\t\tconst attempt = await request();\n\t\tconst iterator = attempt.stream[Symbol.asyncIterator]();\n\t\tconst first = await iterator.next();\n\t\treturn {\n\t\t\tstream: replayPrefetchedStream(first, iterator),\n\t\t\tmetadata: attempt.metadata,\n\t\t};\n\t}, options);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi-ai",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.27-3",
|
|
4
4
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"dependencies": {
|
|
85
85
|
"@anthropic-ai/sdk": "0.127.0",
|
|
86
86
|
"@aws-sdk/client-bedrock-runtime": "3.1136.0",
|
|
87
|
-
"@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.9.
|
|
87
|
+
"@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.9.27-3",
|
|
88
88
|
"@google/genai": "2.23.0",
|
|
89
89
|
"@smithy/node-http-handler": "4.12.1",
|
|
90
90
|
"http-proxy-agent": "9.1.0",
|