@code-yeongyu/senpi-ai 2026.7.31 → 2026.8.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/README.md +2 -1
- package/dist/api/anthropic-messages.d.ts.map +1 -1
- package/dist/api/anthropic-messages.js +56 -25
- package/dist/api/anthropic-messages.js.map +1 -1
- package/dist/api/anthropic-tool-pairs.d.ts +3 -0
- package/dist/api/anthropic-tool-pairs.d.ts.map +1 -0
- package/dist/api/anthropic-tool-pairs.js +134 -0
- package/dist/api/anthropic-tool-pairs.js.map +1 -0
- package/dist/api/azure-openai-responses.d.ts.map +1 -1
- package/dist/api/azure-openai-responses.js +7 -3
- package/dist/api/azure-openai-responses.js.map +1 -1
- package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
- package/dist/api/bedrock-converse-stream.js +64 -4
- package/dist/api/bedrock-converse-stream.js.map +1 -1
- package/dist/api/google-generative-ai.d.ts.map +1 -1
- package/dist/api/google-generative-ai.js +12 -2
- package/dist/api/google-generative-ai.js.map +1 -1
- package/dist/api/google-shared.d.ts.map +1 -1
- package/dist/api/google-shared.js +13 -6
- package/dist/api/google-shared.js.map +1 -1
- package/dist/api/google-vertex.d.ts.map +1 -1
- package/dist/api/google-vertex.js +12 -2
- package/dist/api/google-vertex.js.map +1 -1
- package/dist/api/mistral-conversations.d.ts.map +1 -1
- package/dist/api/mistral-conversations.js +19 -10
- package/dist/api/mistral-conversations.js.map +1 -1
- package/dist/api/openai-codex-responses/fallback-state.d.ts +24 -0
- package/dist/api/openai-codex-responses/fallback-state.d.ts.map +1 -0
- package/dist/api/openai-codex-responses/fallback-state.js +70 -0
- package/dist/api/openai-codex-responses/fallback-state.js.map +1 -0
- package/dist/api/openai-codex-responses.d.ts +2 -16
- package/dist/api/openai-codex-responses.d.ts.map +1 -1
- package/dist/api/openai-codex-responses.js +94 -105
- package/dist/api/openai-codex-responses.js.map +1 -1
- package/dist/api/openai-completions.d.ts.map +1 -1
- package/dist/api/openai-completions.js +19 -10
- package/dist/api/openai-completions.js.map +1 -1
- package/dist/api/openai-prompt-cache.d.ts +1 -0
- package/dist/api/openai-prompt-cache.d.ts.map +1 -1
- package/dist/api/openai-prompt-cache.js +7 -0
- package/dist/api/openai-prompt-cache.js.map +1 -1
- package/dist/api/openai-responses-shared.d.ts.map +1 -1
- package/dist/api/openai-responses-shared.js +11 -1
- package/dist/api/openai-responses-shared.js.map +1 -1
- package/dist/api/openai-responses.d.ts.map +1 -1
- package/dist/api/openai-responses.js +9 -5
- package/dist/api/openai-responses.js.map +1 -1
- package/dist/api/openrouter-images.js +3 -2
- package/dist/api/openrouter-images.js.map +1 -1
- package/dist/api/pi-messages.js +2 -2
- package/dist/api/pi-messages.js.map +1 -1
- package/dist/api/simple-options.d.ts.map +1 -1
- package/dist/api/simple-options.js +1 -0
- package/dist/api/simple-options.js.map +1 -1
- package/dist/auth/oauth/openrouter.d.ts +3 -1
- package/dist/auth/oauth/openrouter.d.ts.map +1 -1
- package/dist/auth/oauth/openrouter.js +81 -26
- package/dist/auth/oauth/openrouter.js.map +1 -1
- package/dist/auth/resolve.d.ts +2 -0
- package/dist/auth/resolve.d.ts.map +1 -1
- package/dist/auth/resolve.js +16 -8
- package/dist/auth/resolve.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/model.d.ts +2 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js.map +1 -1
- package/dist/models.d.ts +14 -0
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +95 -19
- package/dist/models.js.map +1 -1
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/amazon-bedrock.json +1 -1
- package/dist/providers/data/azure-openai-responses.json +1 -1
- package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/dist/providers/data/fireworks.json +1 -1
- package/dist/providers/data/github-copilot.json +1 -1
- package/dist/providers/data/huggingface.json +1 -1
- package/dist/providers/data/mistral.json +1 -1
- package/dist/providers/data/openai-codex.json +1 -1
- package/dist/providers/data/openai.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/qwen-token-plan-cn.json +1 -1
- package/dist/providers/data/qwen-token-plan.json +1 -1
- package/dist/providers/data/together.json +1 -1
- package/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/dist/providers/data/zai-coding-cn.json +1 -1
- package/dist/providers/data/zai.json +1 -1
- package/dist/providers/faux.d.ts.map +1 -1
- package/dist/providers/faux.js +4 -1
- package/dist/providers/faux.js.map +1 -1
- package/dist/providers/opencode-go.js +1 -1
- package/dist/providers/opencode-go.js.map +1 -1
- package/dist/types.d.ts +18 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/error-body.d.ts.map +1 -1
- package/dist/utils/error-body.js +23 -4
- package/dist/utils/error-body.js.map +1 -1
- package/dist/utils/prompt-cache-ttl.d.ts.map +1 -1
- package/dist/utils/prompt-cache-ttl.js +10 -1
- package/dist/utils/prompt-cache-ttl.js.map +1 -1
- package/dist/utils/provider-retry.d.ts +3 -0
- package/dist/utils/provider-retry.d.ts.map +1 -1
- package/dist/utils/provider-retry.js +13 -12
- package/dist/utils/provider-retry.js.map +1 -1
- package/dist/utils/retry-hint.d.ts +24 -0
- package/dist/utils/retry-hint.d.ts.map +1 -0
- package/dist/utils/retry-hint.js +233 -0
- package/dist/utils/retry-hint.js.map +1 -0
- package/dist/utils/unavailable-tool-text.d.ts +3 -0
- package/dist/utils/unavailable-tool-text.d.ts.map +1 -0
- package/dist/utils/unavailable-tool-text.js +36 -0
- package/dist/utils/unavailable-tool-text.js.map +1 -0
- package/package.json +10 -6
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strict 429 retry-hint extractor with canonical marker helpers.
|
|
3
|
+
*
|
|
4
|
+
* Precedence (first match wins, malformed source falls through):
|
|
5
|
+
* 1. retry-after-ms header (strict `^\d+(?:\.\d+)?$`, ceil)
|
|
6
|
+
* 2. retry-after header (strict integer delta-seconds `^\d+$`, else HTTP-date)
|
|
7
|
+
* 3. x-ratelimit-reset{,-requests,-tokens} (epoch seconds, max-wins)
|
|
8
|
+
* 4. JSON retryDelay strings (ms/s/m units, recursive, max-wins; numeric rejected)
|
|
9
|
+
* 5. body prose (ms-marker, seconds-marker, relative, resets-at ISO8601)
|
|
10
|
+
*
|
|
11
|
+
* Eligibility gate: status 429 OR body marks rate-limit error.
|
|
12
|
+
* Explicit zero = 0 (beats lower-precedence positives).
|
|
13
|
+
* Past absolute times clamp to 0.
|
|
14
|
+
* Absolute times resolve against response Date header (clock-skew safe), else nowMs.
|
|
15
|
+
*/
|
|
16
|
+
const MARKER_RE = /\(retry-after-ms: (\d+)\)$/;
|
|
17
|
+
const RATE_LIMIT_BODY_RE = /\b(rate_limit_error|rate_limit_exceeded|too_many_requests|resource_exhausted|rate limit (?:exceeded|hit)|too many requests)\b/i;
|
|
18
|
+
const RATE_LIMIT_JSON_VALUES = new Set([
|
|
19
|
+
"rate_limit_error",
|
|
20
|
+
"rate_limit_exceeded",
|
|
21
|
+
"too_many_requests",
|
|
22
|
+
"resource_exhausted",
|
|
23
|
+
]);
|
|
24
|
+
const RETRY_AFTER_MS_RE = /^\d+(?:\.\d+)?$/;
|
|
25
|
+
const DELTA_SECONDS_RE = /^\d+$/;
|
|
26
|
+
/* ---------- eligibility ---------- */
|
|
27
|
+
function isRateLimited(status, bodyText) {
|
|
28
|
+
if (status === 429)
|
|
29
|
+
return true;
|
|
30
|
+
if (RATE_LIMIT_BODY_RE.test(bodyText))
|
|
31
|
+
return true;
|
|
32
|
+
// JSON type/code fields or retryDelay presence (rate-limit signal)
|
|
33
|
+
const json = tryParseJSON(bodyText);
|
|
34
|
+
if (json !== undefined) {
|
|
35
|
+
const found = scanValue(json, (v, k) => {
|
|
36
|
+
if (k !== undefined && k.toLowerCase() === "retrydelay" && typeof v === "string")
|
|
37
|
+
return true;
|
|
38
|
+
if (typeof v === "string" && RATE_LIMIT_JSON_VALUES.has(v))
|
|
39
|
+
return true;
|
|
40
|
+
if (v === 429)
|
|
41
|
+
return true;
|
|
42
|
+
return false;
|
|
43
|
+
});
|
|
44
|
+
if (found)
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
/* ---------- 1. retry-after-ms header ---------- */
|
|
50
|
+
function fromRetryAfterMsHeader(h) {
|
|
51
|
+
const raw = h?.get("retry-after-ms");
|
|
52
|
+
if (raw === null || raw === undefined)
|
|
53
|
+
return undefined;
|
|
54
|
+
if (!RETRY_AFTER_MS_RE.test(raw))
|
|
55
|
+
return undefined;
|
|
56
|
+
const val = Number.parseFloat(raw);
|
|
57
|
+
if (!Number.isFinite(val) || val < 0)
|
|
58
|
+
return undefined;
|
|
59
|
+
return Math.ceil(val);
|
|
60
|
+
}
|
|
61
|
+
/* ---------- 2. retry-after header ---------- */
|
|
62
|
+
function fromRetryAfterHeader(h, nowMs) {
|
|
63
|
+
const raw = h?.get("retry-after");
|
|
64
|
+
if (raw === null || raw === undefined)
|
|
65
|
+
return undefined;
|
|
66
|
+
// integer delta-seconds
|
|
67
|
+
if (DELTA_SECONDS_RE.test(raw)) {
|
|
68
|
+
return Number.parseInt(raw, 10) * 1000;
|
|
69
|
+
}
|
|
70
|
+
// HTTP-date — must contain alphabetic chars to be a real date string
|
|
71
|
+
if (/[a-zA-Z]/.test(raw)) {
|
|
72
|
+
const parsed = Date.parse(raw);
|
|
73
|
+
if (!Number.isNaN(parsed)) {
|
|
74
|
+
const base = dateHeaderMs(h) ?? nowMs;
|
|
75
|
+
return clampAbsolute(parsed, base);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
/* ---------- 3. x-ratelimit-reset* headers ---------- */
|
|
81
|
+
function fromXRateLimitReset(h, nowMs) {
|
|
82
|
+
let best;
|
|
83
|
+
for (const key of ["x-ratelimit-reset", "x-ratelimit-reset-requests", "x-ratelimit-reset-tokens"]) {
|
|
84
|
+
const raw = h?.get(key);
|
|
85
|
+
if (raw === null || raw === undefined)
|
|
86
|
+
continue;
|
|
87
|
+
const epoch = Number.parseFloat(raw);
|
|
88
|
+
if (!Number.isFinite(epoch) || epoch < 0)
|
|
89
|
+
continue;
|
|
90
|
+
// reject "12abc" — strict number check
|
|
91
|
+
if (!/^\d+(?:\.\d+)?$/.test(raw))
|
|
92
|
+
continue;
|
|
93
|
+
const delta = Math.ceil(epoch * 1000 - nowMs);
|
|
94
|
+
const clamped = Math.max(0, delta);
|
|
95
|
+
if (best === undefined || clamped > best)
|
|
96
|
+
best = clamped;
|
|
97
|
+
}
|
|
98
|
+
return best;
|
|
99
|
+
}
|
|
100
|
+
/* ---------- 4. JSON retryDelay (recursive, max-wins) ---------- */
|
|
101
|
+
const RETRY_DELAY_RE = /^(\d+(?:\.\d+)?)\s*(ms|s|m)$/;
|
|
102
|
+
function fromJsonRetryDelay(bodyText) {
|
|
103
|
+
const json = tryParseJSON(bodyText);
|
|
104
|
+
if (json === undefined)
|
|
105
|
+
return undefined;
|
|
106
|
+
let best;
|
|
107
|
+
scanValue(json, (v) => {
|
|
108
|
+
if (typeof v !== "string")
|
|
109
|
+
return false;
|
|
110
|
+
const m = v.match(RETRY_DELAY_RE);
|
|
111
|
+
if (!m)
|
|
112
|
+
return false;
|
|
113
|
+
const num = Number.parseFloat(m[1]);
|
|
114
|
+
const unit = m[2];
|
|
115
|
+
const ms = unit === "ms" ? num : unit === "s" ? num * 1000 : num * 60_000;
|
|
116
|
+
const intMs = Math.ceil(ms);
|
|
117
|
+
if (best === undefined || intMs > best)
|
|
118
|
+
best = intMs;
|
|
119
|
+
return false; // keep scanning
|
|
120
|
+
});
|
|
121
|
+
return best;
|
|
122
|
+
}
|
|
123
|
+
/* ---------- 5. body prose ---------- */
|
|
124
|
+
function fromBodyProse(bodyText, nowMs) {
|
|
125
|
+
// ms-marker: retry-after-ms: <N>
|
|
126
|
+
let m = bodyText.match(/retry-after-ms:\s*(\d+)/i);
|
|
127
|
+
if (m)
|
|
128
|
+
return Number.parseInt(m[1], 10);
|
|
129
|
+
// seconds-marker: retry-after: <N>
|
|
130
|
+
m = bodyText.match(/retry-after:\s*(\d+)/i);
|
|
131
|
+
if (m)
|
|
132
|
+
return Number.parseInt(m[1], 10) * 1000;
|
|
133
|
+
// relative prose: try again | retry | wait after ... [in] N <unit>
|
|
134
|
+
m = bodyText.match(/(?:try again|retry|wait after).*?(?:in\s+)?(\d+(?:\.\d+)?)\s*(ms|s|sec|seconds?|m|min|minutes?)\b/i);
|
|
135
|
+
if (m)
|
|
136
|
+
return convertUnit(Number.parseFloat(m[1]), m[2]);
|
|
137
|
+
// resets at <ISO8601-with-tz>
|
|
138
|
+
m = bodyText.match(/resets?\s+at\s+(\S+)/i);
|
|
139
|
+
if (m) {
|
|
140
|
+
const parsed = Date.parse(m[1]);
|
|
141
|
+
if (!Number.isNaN(parsed))
|
|
142
|
+
return clampAbsolute(parsed, nowMs);
|
|
143
|
+
}
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
/* ---------- helpers ---------- */
|
|
147
|
+
function dateHeaderMs(h) {
|
|
148
|
+
const raw = h?.get("date");
|
|
149
|
+
if (raw === null || raw === undefined)
|
|
150
|
+
return undefined;
|
|
151
|
+
const parsed = Date.parse(raw);
|
|
152
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
153
|
+
}
|
|
154
|
+
function clampAbsolute(targetMs, baseMs) {
|
|
155
|
+
return Math.max(0, targetMs - baseMs);
|
|
156
|
+
}
|
|
157
|
+
function convertUnit(num, unit) {
|
|
158
|
+
const u = unit.toLowerCase();
|
|
159
|
+
if (u === "ms")
|
|
160
|
+
return Math.ceil(num);
|
|
161
|
+
if (u === "s" || u === "sec" || u === "second" || u === "seconds")
|
|
162
|
+
return Math.ceil(num * 1000);
|
|
163
|
+
return Math.ceil(num * 60_000); // m / min / minute / minutes
|
|
164
|
+
}
|
|
165
|
+
/** Recursively walk JSON, calling fn on every leaf value with its key. */
|
|
166
|
+
function scanValue(value, fn, key) {
|
|
167
|
+
if (value === null)
|
|
168
|
+
return fn(null, key);
|
|
169
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
|
|
170
|
+
return fn(value, key);
|
|
171
|
+
if (Array.isArray(value)) {
|
|
172
|
+
for (const item of value) {
|
|
173
|
+
if (scanValue(item, fn, key))
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
if (typeof value === "object") {
|
|
179
|
+
for (const [k, v] of Object.entries(value)) {
|
|
180
|
+
if (scanValue(v, fn, k))
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
function tryParseJSON(text) {
|
|
188
|
+
// strip SSE data: prefix lines
|
|
189
|
+
const cleaned = text.replace(/^data:\s*/gm, "").trim();
|
|
190
|
+
if (!cleaned.startsWith("{") && !cleaned.startsWith("["))
|
|
191
|
+
return undefined;
|
|
192
|
+
try {
|
|
193
|
+
return JSON.parse(cleaned);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/* ---------- main extractor ---------- */
|
|
200
|
+
export function extract429RetryAfterMs(input, nowMs) {
|
|
201
|
+
const now = nowMs ?? Date.now();
|
|
202
|
+
if (!isRateLimited(input.status, input.bodyText))
|
|
203
|
+
return undefined;
|
|
204
|
+
// 1. retry-after-ms header
|
|
205
|
+
const fromMs = fromRetryAfterMsHeader(input.headers);
|
|
206
|
+
if (fromMs !== undefined)
|
|
207
|
+
return fromMs;
|
|
208
|
+
// 2. retry-after header
|
|
209
|
+
const fromRa = fromRetryAfterHeader(input.headers, now);
|
|
210
|
+
if (fromRa !== undefined)
|
|
211
|
+
return fromRa;
|
|
212
|
+
// 3. x-ratelimit-reset* headers
|
|
213
|
+
const fromXrl = fromXRateLimitReset(input.headers, now);
|
|
214
|
+
if (fromXrl !== undefined)
|
|
215
|
+
return fromXrl;
|
|
216
|
+
// 4. JSON retryDelay
|
|
217
|
+
const fromJson = fromJsonRetryDelay(input.bodyText);
|
|
218
|
+
if (fromJson !== undefined)
|
|
219
|
+
return fromJson;
|
|
220
|
+
// 5. body prose
|
|
221
|
+
return fromBodyProse(input.bodyText, now);
|
|
222
|
+
}
|
|
223
|
+
/* ---------- marker helpers ---------- */
|
|
224
|
+
export function appendRetryAfterMsMarker(message, hintMs) {
|
|
225
|
+
return `${message} (retry-after-ms: ${hintMs})`;
|
|
226
|
+
}
|
|
227
|
+
export function parseRetryAfterMsMarker(message) {
|
|
228
|
+
const m = message.match(MARKER_RE);
|
|
229
|
+
if (!m)
|
|
230
|
+
return undefined;
|
|
231
|
+
return Number.parseInt(m[1], 10);
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=retry-hint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-hint.js","sourceRoot":"","sources":["../../src/utils/retry-hint.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Strict 429 retry-hint extractor with canonical marker helpers.\n *\n * Precedence (first match wins, malformed source falls through):\n * 1. retry-after-ms header (strict `^\\d+(?:\\.\\d+)?$`, ceil)\n * 2. retry-after header (strict integer delta-seconds `^\\d+$`, else HTTP-date)\n * 3. x-ratelimit-reset{,-requests,-tokens} (epoch seconds, max-wins)\n * 4. JSON retryDelay strings (ms/s/m units, recursive, max-wins; numeric rejected)\n * 5. body prose (ms-marker, seconds-marker, relative, resets-at ISO8601)\n *\n * Eligibility gate: status 429 OR body marks rate-limit error.\n * Explicit zero = 0 (beats lower-precedence positives).\n * Past absolute times clamp to 0.\n * Absolute times resolve against response Date header (clock-skew safe), else nowMs.\n */\n\nconst MARKER_RE = /\\(retry-after-ms: (\\d+)\\)$/;\nconst RATE_LIMIT_BODY_RE =\n\t/\\b(rate_limit_error|rate_limit_exceeded|too_many_requests|resource_exhausted|rate limit (?:exceeded|hit)|too many requests)\\b/i;\nconst RATE_LIMIT_JSON_VALUES = new Set([\n\t\"rate_limit_error\",\n\t\"rate_limit_exceeded\",\n\t\"too_many_requests\",\n\t\"resource_exhausted\",\n]);\nconst RETRY_AFTER_MS_RE = /^\\d+(?:\\.\\d+)?$/;\nconst DELTA_SECONDS_RE = /^\\d+$/;\n\n/* ---------- public types ---------- */\n\nexport interface RetryHintInput {\n\tstatus?: number;\n\theaders?: Headers;\n\tbodyText: string;\n}\n\n/* ---------- eligibility ---------- */\n\nfunction isRateLimited(status: number | undefined, bodyText: string): boolean {\n\tif (status === 429) return true;\n\tif (RATE_LIMIT_BODY_RE.test(bodyText)) return true;\n\t// JSON type/code fields or retryDelay presence (rate-limit signal)\n\tconst json = tryParseJSON(bodyText);\n\tif (json !== undefined) {\n\t\tconst found = scanValue(json, (v, k) => {\n\t\t\tif (k !== undefined && k.toLowerCase() === \"retrydelay\" && typeof v === \"string\") return true;\n\t\t\tif (typeof v === \"string\" && RATE_LIMIT_JSON_VALUES.has(v)) return true;\n\t\t\tif (v === 429) return true;\n\t\t\treturn false;\n\t\t});\n\t\tif (found) return true;\n\t}\n\treturn false;\n}\n\n/* ---------- 1. retry-after-ms header ---------- */\n\nfunction fromRetryAfterMsHeader(h: Headers | undefined): number | undefined {\n\tconst raw = h?.get(\"retry-after-ms\");\n\tif (raw === null || raw === undefined) return undefined;\n\tif (!RETRY_AFTER_MS_RE.test(raw)) return undefined;\n\tconst val = Number.parseFloat(raw);\n\tif (!Number.isFinite(val) || val < 0) return undefined;\n\treturn Math.ceil(val);\n}\n\n/* ---------- 2. retry-after header ---------- */\n\nfunction fromRetryAfterHeader(h: Headers | undefined, nowMs: number): number | undefined {\n\tconst raw = h?.get(\"retry-after\");\n\tif (raw === null || raw === undefined) return undefined;\n\t// integer delta-seconds\n\tif (DELTA_SECONDS_RE.test(raw)) {\n\t\treturn Number.parseInt(raw, 10) * 1000;\n\t}\n\t// HTTP-date — must contain alphabetic chars to be a real date string\n\tif (/[a-zA-Z]/.test(raw)) {\n\t\tconst parsed = Date.parse(raw);\n\t\tif (!Number.isNaN(parsed)) {\n\t\t\tconst base = dateHeaderMs(h) ?? nowMs;\n\t\t\treturn clampAbsolute(parsed, base);\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/* ---------- 3. x-ratelimit-reset* headers ---------- */\n\nfunction fromXRateLimitReset(h: Headers | undefined, nowMs: number): number | undefined {\n\tlet best: number | undefined;\n\tfor (const key of [\"x-ratelimit-reset\", \"x-ratelimit-reset-requests\", \"x-ratelimit-reset-tokens\"]) {\n\t\tconst raw = h?.get(key);\n\t\tif (raw === null || raw === undefined) continue;\n\t\tconst epoch = Number.parseFloat(raw);\n\t\tif (!Number.isFinite(epoch) || epoch < 0) continue;\n\t\t// reject \"12abc\" — strict number check\n\t\tif (!/^\\d+(?:\\.\\d+)?$/.test(raw)) continue;\n\t\tconst delta = Math.ceil(epoch * 1000 - nowMs);\n\t\tconst clamped = Math.max(0, delta);\n\t\tif (best === undefined || clamped > best) best = clamped;\n\t}\n\treturn best;\n}\n\n/* ---------- 4. JSON retryDelay (recursive, max-wins) ---------- */\n\nconst RETRY_DELAY_RE = /^(\\d+(?:\\.\\d+)?)\\s*(ms|s|m)$/;\n\nfunction fromJsonRetryDelay(bodyText: string): number | undefined {\n\tconst json = tryParseJSON(bodyText);\n\tif (json === undefined) return undefined;\n\tlet best: number | undefined;\n\tscanValue(json, (v) => {\n\t\tif (typeof v !== \"string\") return false;\n\t\tconst m = v.match(RETRY_DELAY_RE);\n\t\tif (!m) return false;\n\t\tconst num = Number.parseFloat(m[1]);\n\t\tconst unit = m[2];\n\t\tconst ms = unit === \"ms\" ? num : unit === \"s\" ? num * 1000 : num * 60_000;\n\t\tconst intMs = Math.ceil(ms);\n\t\tif (best === undefined || intMs > best) best = intMs;\n\t\treturn false; // keep scanning\n\t});\n\treturn best;\n}\n\n/* ---------- 5. body prose ---------- */\n\nfunction fromBodyProse(bodyText: string, nowMs: number): number | undefined {\n\t// ms-marker: retry-after-ms: <N>\n\tlet m = bodyText.match(/retry-after-ms:\\s*(\\d+)/i);\n\tif (m) return Number.parseInt(m[1], 10);\n\n\t// seconds-marker: retry-after: <N>\n\tm = bodyText.match(/retry-after:\\s*(\\d+)/i);\n\tif (m) return Number.parseInt(m[1], 10) * 1000;\n\n\t// relative prose: try again | retry | wait after ... [in] N <unit>\n\tm = bodyText.match(\n\t\t/(?:try again|retry|wait after).*?(?:in\\s+)?(\\d+(?:\\.\\d+)?)\\s*(ms|s|sec|seconds?|m|min|minutes?)\\b/i,\n\t);\n\tif (m) return convertUnit(Number.parseFloat(m[1]), m[2]);\n\n\t// resets at <ISO8601-with-tz>\n\tm = bodyText.match(/resets?\\s+at\\s+(\\S+)/i);\n\tif (m) {\n\t\tconst parsed = Date.parse(m[1]);\n\t\tif (!Number.isNaN(parsed)) return clampAbsolute(parsed, nowMs);\n\t}\n\n\treturn undefined;\n}\n\n/* ---------- helpers ---------- */\n\nfunction dateHeaderMs(h: Headers | undefined): number | undefined {\n\tconst raw = h?.get(\"date\");\n\tif (raw === null || raw === undefined) return undefined;\n\tconst parsed = Date.parse(raw);\n\treturn Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction clampAbsolute(targetMs: number, baseMs: number): number {\n\treturn Math.max(0, targetMs - baseMs);\n}\n\nfunction convertUnit(num: number, unit: string): number {\n\tconst u = unit.toLowerCase();\n\tif (u === \"ms\") return Math.ceil(num);\n\tif (u === \"s\" || u === \"sec\" || u === \"second\" || u === \"seconds\") return Math.ceil(num * 1000);\n\treturn Math.ceil(num * 60_000); // m / min / minute / minutes\n}\n\n/** Recursively walk JSON, calling fn on every leaf value with its key. */\nfunction scanValue(\n\tvalue: unknown,\n\tfn: (v: string | number | boolean | null, key?: string) => boolean,\n\tkey?: string,\n): boolean {\n\tif (value === null) return fn(null, key);\n\tif (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") return fn(value, key);\n\tif (Array.isArray(value)) {\n\t\tfor (const item of value) {\n\t\t\tif (scanValue(item, fn, key)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\tif (typeof value === \"object\") {\n\t\tfor (const [k, v] of Object.entries(value)) {\n\t\t\tif (scanValue(v, fn, k)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\treturn false;\n}\n\nfunction tryParseJSON(text: string): unknown {\n\t// strip SSE data: prefix lines\n\tconst cleaned = text.replace(/^data:\\s*/gm, \"\").trim();\n\tif (!cleaned.startsWith(\"{\") && !cleaned.startsWith(\"[\")) return undefined;\n\ttry {\n\t\treturn JSON.parse(cleaned);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/* ---------- main extractor ---------- */\n\nexport function extract429RetryAfterMs(input: RetryHintInput, nowMs?: number): number | undefined {\n\tconst now = nowMs ?? Date.now();\n\n\tif (!isRateLimited(input.status, input.bodyText)) return undefined;\n\n\t// 1. retry-after-ms header\n\tconst fromMs = fromRetryAfterMsHeader(input.headers);\n\tif (fromMs !== undefined) return fromMs;\n\n\t// 2. retry-after header\n\tconst fromRa = fromRetryAfterHeader(input.headers, now);\n\tif (fromRa !== undefined) return fromRa;\n\n\t// 3. x-ratelimit-reset* headers\n\tconst fromXrl = fromXRateLimitReset(input.headers, now);\n\tif (fromXrl !== undefined) return fromXrl;\n\n\t// 4. JSON retryDelay\n\tconst fromJson = fromJsonRetryDelay(input.bodyText);\n\tif (fromJson !== undefined) return fromJson;\n\n\t// 5. body prose\n\treturn fromBodyProse(input.bodyText, now);\n}\n\n/* ---------- marker helpers ---------- */\n\nexport function appendRetryAfterMsMarker(message: string, hintMs: number): string {\n\treturn `${message} (retry-after-ms: ${hintMs})`;\n}\n\nexport function parseRetryAfterMsMarker(message: string): number | undefined {\n\tconst m = message.match(MARKER_RE);\n\tif (!m) return undefined;\n\treturn Number.parseInt(m[1], 10);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unavailable-tool-text.d.ts","sourceRoot":"","sources":["../../src/utils/unavailable-tool-text.ts"],"names":[],"mappings":"AAqBA,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,MAAM,EACZ,kBAAkB,EAAE,SAAS,MAAM,EAAE,EACrC,eAAe,EAAE,OAAO,GACtB,MAAM,CAUR;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAI3E"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const MAX_LISTED_TOOLS = 8;
|
|
2
|
+
function escapeXmlAttribute(value) {
|
|
3
|
+
return value
|
|
4
|
+
.replaceAll("&", "&")
|
|
5
|
+
.replaceAll('"', """)
|
|
6
|
+
.replaceAll("'", "'")
|
|
7
|
+
.replaceAll("<", "<")
|
|
8
|
+
.replaceAll(">", ">");
|
|
9
|
+
}
|
|
10
|
+
function availableToolGuidance(availableToolNames) {
|
|
11
|
+
if (availableToolNames.length === 0) {
|
|
12
|
+
return "To edit files, call only tools available in this request.";
|
|
13
|
+
}
|
|
14
|
+
const listed = availableToolNames.slice(0, MAX_LISTED_TOOLS).join(", ");
|
|
15
|
+
const omitted = availableToolNames.length - MAX_LISTED_TOOLS;
|
|
16
|
+
const suffix = omitted > 0 ? ` (and ${omitted} more)` : "";
|
|
17
|
+
return `To edit files, call your own tools: ${listed}${suffix}.`;
|
|
18
|
+
}
|
|
19
|
+
export function demotedToolCallText(name, availableToolNames, firstOccurrence) {
|
|
20
|
+
const escapedName = escapeXmlAttribute(name);
|
|
21
|
+
if (!firstOccurrence)
|
|
22
|
+
return `<unavailable-tool-call name="${escapedName}"/>`;
|
|
23
|
+
return [
|
|
24
|
+
`<unavailable-tool-call name="${escapedName}">`,
|
|
25
|
+
"Transcript record, not an action available to you. An earlier model in this session",
|
|
26
|
+
`called "${escapedName}"; that tool does not exist for you and its input is omitted.`,
|
|
27
|
+
availableToolGuidance(availableToolNames),
|
|
28
|
+
"</unavailable-tool-call>",
|
|
29
|
+
].join("\n");
|
|
30
|
+
}
|
|
31
|
+
export function demotedToolResultText(name, content) {
|
|
32
|
+
const escapedName = escapeXmlAttribute(name);
|
|
33
|
+
const safeContent = content.replace(/<\/unavailable-tool-result/gi, (match) => `<${match.slice(1)}`);
|
|
34
|
+
return `<unavailable-tool-result name="${escapedName}">${safeContent}</unavailable-tool-result>`;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=unavailable-tool-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unavailable-tool-text.js","sourceRoot":"","sources":["../../src/utils/unavailable-tool-text.ts"],"names":[],"mappings":"AAAA,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAE3B,SAAS,kBAAkB,CAAC,KAAa;IACxC,OAAO,KAAK;SACV,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;SACxB,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC;SACzB,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC;SACzB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,qBAAqB,CAAC,kBAAqC;IACnE,IAAI,kBAAkB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrC,OAAO,2DAA2D,CAAC;IACpE,CAAC;IACD,MAAM,MAAM,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,GAAG,gBAAgB,CAAC;IAC7D,MAAM,MAAM,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,OAAO,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,OAAO,uCAAuC,MAAM,GAAG,MAAM,GAAG,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAClC,IAAY,EACZ,kBAAqC,EACrC,eAAwB;IAExB,MAAM,WAAW,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,CAAC,eAAe;QAAE,OAAO,gCAAgC,WAAW,KAAK,CAAC;IAC9E,OAAO;QACN,gCAAgC,WAAW,IAAI;QAC/C,qFAAqF;QACrF,WAAW,WAAW,+DAA+D;QACrF,qBAAqB,CAAC,kBAAkB,CAAC;QACzC,0BAA0B;KAC1B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACd,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,OAAe;IAClE,MAAM,WAAW,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,8BAA8B,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACxG,OAAO,kCAAkC,WAAW,KAAK,WAAW,4BAA4B,CAAC;AAClG,CAAC","sourcesContent":["const MAX_LISTED_TOOLS = 8;\n\nfunction escapeXmlAttribute(value: string): string {\n\treturn value\n\t\t.replaceAll(\"&\", \"&\")\n\t\t.replaceAll('\"', \""\")\n\t\t.replaceAll(\"'\", \"'\")\n\t\t.replaceAll(\"<\", \"<\")\n\t\t.replaceAll(\">\", \">\");\n}\n\nfunction availableToolGuidance(availableToolNames: readonly string[]): string {\n\tif (availableToolNames.length === 0) {\n\t\treturn \"To edit files, call only tools available in this request.\";\n\t}\n\tconst listed = availableToolNames.slice(0, MAX_LISTED_TOOLS).join(\", \");\n\tconst omitted = availableToolNames.length - MAX_LISTED_TOOLS;\n\tconst suffix = omitted > 0 ? ` (and ${omitted} more)` : \"\";\n\treturn `To edit files, call your own tools: ${listed}${suffix}.`;\n}\n\nexport function demotedToolCallText(\n\tname: string,\n\tavailableToolNames: readonly string[],\n\tfirstOccurrence: boolean,\n): string {\n\tconst escapedName = escapeXmlAttribute(name);\n\tif (!firstOccurrence) return `<unavailable-tool-call name=\"${escapedName}\"/>`;\n\treturn [\n\t\t`<unavailable-tool-call name=\"${escapedName}\">`,\n\t\t\"Transcript record, not an action available to you. An earlier model in this session\",\n\t\t`called \"${escapedName}\"; that tool does not exist for you and its input is omitted.`,\n\t\tavailableToolGuidance(availableToolNames),\n\t\t\"</unavailable-tool-call>\",\n\t].join(\"\\n\");\n}\n\nexport function demotedToolResultText(name: string, content: string): string {\n\tconst escapedName = escapeXmlAttribute(name);\n\tconst safeContent = content.replace(/<\\/unavailable-tool-result/gi, (match) => `<${match.slice(1)}`);\n\treturn `<unavailable-tool-result name=\"${escapedName}\">${safeContent}</unavailable-tool-result>`;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi-ai",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.8.3",
|
|
4
4
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
"types": "./dist/api/*.d.ts",
|
|
28
28
|
"import": "./dist/api/*.js"
|
|
29
29
|
},
|
|
30
|
+
"./utils/*": {
|
|
31
|
+
"types": "./dist/utils/*.d.ts",
|
|
32
|
+
"import": "./dist/utils/*.js"
|
|
33
|
+
},
|
|
30
34
|
"./oauth": {
|
|
31
35
|
"types": "./dist/oauth.d.ts",
|
|
32
36
|
"import": "./dist/oauth.js"
|
|
@@ -58,12 +62,12 @@
|
|
|
58
62
|
"generate-model-catalog": "tsx scripts/generate-models.ts --strict --json-only --json-output ../../.artifacts/model-catalog",
|
|
59
63
|
"generate-image-models": "tsx scripts/generate-image-models.ts --strict",
|
|
60
64
|
"check:model-data": "tsx scripts/check-model-data.ts",
|
|
61
|
-
"build": "
|
|
62
|
-
"build:offline": "npm run check:model-data &&
|
|
63
|
-
"dev": "
|
|
64
|
-
"dev:tsc": "
|
|
65
|
+
"build": "tsc -p tsconfig.build.json && shx chmod +x dist/cli.js && shx rm -rf dist/providers/data && shx cp -r src/providers/data dist/providers/data",
|
|
66
|
+
"build:offline": "npm run check:model-data && tsc -p tsconfig.build.json && shx chmod +x dist/cli.js && shx rm -rf dist/providers/data && shx cp -r src/providers/data dist/providers/data",
|
|
67
|
+
"dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput",
|
|
68
|
+
"dev:tsc": "tsc -p tsconfig.build.json --watch --preserveWatchOutput",
|
|
65
69
|
"test": "vitest --run",
|
|
66
|
-
"prepublishOnly": "shx rm -rf dist && tsx scripts/generate-models.ts && tsx scripts/generate-image-models.ts &&
|
|
70
|
+
"prepublishOnly": "shx rm -rf dist && tsx scripts/generate-models.ts && tsx scripts/generate-image-models.ts && tsc -p tsconfig.build.json && shx chmod +x dist/cli.js"
|
|
67
71
|
},
|
|
68
72
|
"dependencies": {
|
|
69
73
|
"@anthropic-ai/sdk": "0.91.1",
|