@prestyj/ai 4.3.209 → 4.3.237
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 +1 -1
- package/dist/index.cjs +323 -191
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -2
- package/dist/index.d.ts +30 -2
- package/dist/index.js +319 -190
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -36,6 +36,7 @@ __export(index_exports, {
|
|
|
36
36
|
StreamResult: () => StreamResult,
|
|
37
37
|
formatError: () => formatError,
|
|
38
38
|
formatErrorForDisplay: () => formatErrorForDisplay,
|
|
39
|
+
isUsageLimitError: () => isUsageLimitError,
|
|
39
40
|
palsuAssistantMessage: () => palsuAssistantMessage,
|
|
40
41
|
palsuText: () => palsuText,
|
|
41
42
|
palsuThinking: () => palsuThinking,
|
|
@@ -43,11 +44,27 @@ __export(index_exports, {
|
|
|
43
44
|
providerRegistry: () => providerRegistry,
|
|
44
45
|
registerPalsuProvider: () => registerPalsuProvider,
|
|
45
46
|
setProviderDiagnostic: () => setProviderDiagnostic,
|
|
46
|
-
stream: () => stream
|
|
47
|
+
stream: () => stream,
|
|
48
|
+
toAnthropicMessages: () => toAnthropicMessages,
|
|
49
|
+
toOpenAIMessages: () => toOpenAIMessages
|
|
47
50
|
});
|
|
48
51
|
module.exports = __toCommonJS(index_exports);
|
|
49
52
|
|
|
50
53
|
// src/errors.ts
|
|
54
|
+
function readHeader(headers, ...names) {
|
|
55
|
+
if (!headers) return void 0;
|
|
56
|
+
const getter = typeof headers.get === "function" ? (name) => headers.get(name) ?? void 0 : typeof headers === "object" ? (name) => {
|
|
57
|
+
const rec = headers;
|
|
58
|
+
const value = rec[name] ?? rec[name.toLowerCase()];
|
|
59
|
+
return typeof value === "string" ? value : void 0;
|
|
60
|
+
} : void 0;
|
|
61
|
+
if (!getter) return void 0;
|
|
62
|
+
for (const name of names) {
|
|
63
|
+
const value = getter(name);
|
|
64
|
+
if (value != null) return value;
|
|
65
|
+
}
|
|
66
|
+
return void 0;
|
|
67
|
+
}
|
|
51
68
|
var EZCoderAIError = class extends Error {
|
|
52
69
|
source;
|
|
53
70
|
requestId;
|
|
@@ -63,6 +80,8 @@ var EZCoderAIError = class extends Error {
|
|
|
63
80
|
var ProviderError = class extends EZCoderAIError {
|
|
64
81
|
provider;
|
|
65
82
|
statusCode;
|
|
83
|
+
/** Unix seconds when a usage/rate limit resets, when the provider reports it. */
|
|
84
|
+
resetsAt;
|
|
66
85
|
constructor(provider, message, options) {
|
|
67
86
|
super(message, {
|
|
68
87
|
source: "provider",
|
|
@@ -73,6 +92,7 @@ var ProviderError = class extends EZCoderAIError {
|
|
|
73
92
|
this.name = "ProviderError";
|
|
74
93
|
this.provider = provider;
|
|
75
94
|
this.statusCode = options?.statusCode;
|
|
95
|
+
this.resetsAt = options?.resetsAt;
|
|
76
96
|
}
|
|
77
97
|
};
|
|
78
98
|
var PROVIDER_DISPLAY = {
|
|
@@ -93,10 +113,36 @@ var PROVIDER_STATUS_URL = {
|
|
|
93
113
|
function providerDisplayName(provider) {
|
|
94
114
|
return PROVIDER_DISPLAY[provider] ?? provider;
|
|
95
115
|
}
|
|
116
|
+
function isUsageLimitError(err) {
|
|
117
|
+
if (!(err instanceof Error)) return false;
|
|
118
|
+
return /usage limit reached/i.test(err.message);
|
|
119
|
+
}
|
|
120
|
+
function formatResetTime(resetsAt) {
|
|
121
|
+
const when = new Date(resetsAt * 1e3);
|
|
122
|
+
const sameDay = when.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
|
|
123
|
+
return sameDay ? when.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" }) : when.toLocaleString(void 0, {
|
|
124
|
+
weekday: "short",
|
|
125
|
+
hour: "numeric",
|
|
126
|
+
minute: "2-digit"
|
|
127
|
+
});
|
|
128
|
+
}
|
|
96
129
|
function formatError(err) {
|
|
97
130
|
if (err instanceof ProviderError) {
|
|
98
131
|
const name = providerDisplayName(err.provider);
|
|
99
132
|
const cleanMessage = cleanProviderMessage(err.message);
|
|
133
|
+
if (isUsageLimitError(err)) {
|
|
134
|
+
const resetClause = err.resetsAt ? ` It resets at ${formatResetTime(err.resetsAt)}.` : "";
|
|
135
|
+
return {
|
|
136
|
+
headline: `${name} usage limit reached.`,
|
|
137
|
+
source: "provider",
|
|
138
|
+
message: `Your ${name} usage is finished.${resetClause}`,
|
|
139
|
+
provider: err.provider,
|
|
140
|
+
statusCode: err.statusCode,
|
|
141
|
+
...err.requestId ? { requestId: err.requestId } : {},
|
|
142
|
+
...err.resetsAt ? { resetsAt: err.resetsAt } : {},
|
|
143
|
+
guidance: "Try again once it's back. Your conversation is preserved."
|
|
144
|
+
};
|
|
145
|
+
}
|
|
100
146
|
return {
|
|
101
147
|
headline: `${name} returned an error.`,
|
|
102
148
|
source: "provider",
|
|
@@ -320,6 +366,9 @@ function zodToJsonSchema(schema) {
|
|
|
320
366
|
const { $schema: _schema, ...rest } = jsonSchema;
|
|
321
367
|
return normalizeRootForAnthropic(rest);
|
|
322
368
|
}
|
|
369
|
+
function resolveToolSchema(tool) {
|
|
370
|
+
return tool.rawInputSchema ?? zodToJsonSchema(tool.parameters);
|
|
371
|
+
}
|
|
323
372
|
function normalizeRootForAnthropic(schema) {
|
|
324
373
|
const branches = schema.oneOf ?? schema.anyOf;
|
|
325
374
|
if (!branches || branches.length === 0) {
|
|
@@ -376,6 +425,61 @@ function normalizeRootForAnthropic(schema) {
|
|
|
376
425
|
}
|
|
377
426
|
|
|
378
427
|
// src/providers/transform.ts
|
|
428
|
+
function hasValidThinkingSignature(part) {
|
|
429
|
+
return typeof part.signature === "string" && part.signature.trim().length > 0;
|
|
430
|
+
}
|
|
431
|
+
function isRawThinking(part) {
|
|
432
|
+
if (part.type !== "raw") return false;
|
|
433
|
+
const t = part.data.type;
|
|
434
|
+
return t === "thinking" || t === "redacted_thinking";
|
|
435
|
+
}
|
|
436
|
+
function isPositionSensitiveThinking(part) {
|
|
437
|
+
if (part.type === "thinking") return hasValidThinkingSignature(part);
|
|
438
|
+
return isRawThinking(part);
|
|
439
|
+
}
|
|
440
|
+
function toAnthropicAssistantPart(part, idMap) {
|
|
441
|
+
if (part.type === "text") return { type: "text", text: part.text };
|
|
442
|
+
if (part.type === "thinking") {
|
|
443
|
+
const sig = part.signature;
|
|
444
|
+
return sig && sig.trim().length > 0 ? { type: "thinking", thinking: part.text, signature: sig } : { type: "text", text: part.text };
|
|
445
|
+
}
|
|
446
|
+
if (part.type === "tool_call")
|
|
447
|
+
return {
|
|
448
|
+
type: "tool_use",
|
|
449
|
+
id: remapAnthropicToolCallId(part.id, idMap),
|
|
450
|
+
name: part.name,
|
|
451
|
+
input: part.args
|
|
452
|
+
};
|
|
453
|
+
if (part.type === "server_tool_call")
|
|
454
|
+
return {
|
|
455
|
+
type: "server_tool_use",
|
|
456
|
+
id: part.id,
|
|
457
|
+
name: part.name,
|
|
458
|
+
input: part.input
|
|
459
|
+
};
|
|
460
|
+
if (part.type === "server_tool_result")
|
|
461
|
+
return part.data;
|
|
462
|
+
if (part.type === "raw") return part.data;
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
function toAnthropicAssistantContent(content, isLatest, idMap) {
|
|
466
|
+
if (!isLatest) {
|
|
467
|
+
return content.filter((part) => {
|
|
468
|
+
if (part.type === "thinking" || isRawThinking(part)) return false;
|
|
469
|
+
if (part.type === "text" && !part.text) return false;
|
|
470
|
+
return true;
|
|
471
|
+
}).map((part) => toAnthropicAssistantPart(part, idMap)).filter((b) => b !== null);
|
|
472
|
+
}
|
|
473
|
+
const lastThinkingIdx = content.reduce(
|
|
474
|
+
(last, part, idx) => isPositionSensitiveThinking(part) ? idx : last,
|
|
475
|
+
-1
|
|
476
|
+
);
|
|
477
|
+
return content.filter((part, idx) => {
|
|
478
|
+
if (part.type === "thinking" && !hasValidThinkingSignature(part) && !part.text) return false;
|
|
479
|
+
if (part.type === "text" && !part.text && idx > lastThinkingIdx) return false;
|
|
480
|
+
return true;
|
|
481
|
+
}).map((part) => toAnthropicAssistantPart(part, idMap)).filter((b) => b !== null);
|
|
482
|
+
}
|
|
379
483
|
var NON_VISION_USER_IMAGE_PLACEHOLDER = "(image omitted: model does not support images)";
|
|
380
484
|
var NON_VISION_TOOL_IMAGE_PLACEHOLDER = "(tool image omitted: model does not support images)";
|
|
381
485
|
function stripImages(content, placeholder) {
|
|
@@ -452,7 +556,13 @@ function toAnthropicMessages(messages, cacheControl) {
|
|
|
452
556
|
let systemText;
|
|
453
557
|
const out = [];
|
|
454
558
|
const idMap = /* @__PURE__ */ new Map();
|
|
559
|
+
const lastAssistantIdx = messages.reduce(
|
|
560
|
+
(last, m, i) => m.role === "assistant" ? i : last,
|
|
561
|
+
-1
|
|
562
|
+
);
|
|
563
|
+
let msgIdx = -1;
|
|
455
564
|
for (const msg of messages) {
|
|
565
|
+
msgIdx++;
|
|
456
566
|
if (msg.role === "system") {
|
|
457
567
|
systemText = msg.content;
|
|
458
568
|
continue;
|
|
@@ -475,33 +585,7 @@ function toAnthropicMessages(messages, cacheControl) {
|
|
|
475
585
|
continue;
|
|
476
586
|
}
|
|
477
587
|
if (msg.role === "assistant") {
|
|
478
|
-
const content = typeof msg.content === "string" ? msg.content : msg.content
|
|
479
|
-
if (part.type === "thinking" && !part.signature) return false;
|
|
480
|
-
if (part.type === "text" && !part.text) return false;
|
|
481
|
-
return true;
|
|
482
|
-
}).map((part) => {
|
|
483
|
-
if (part.type === "text") return { type: "text", text: part.text };
|
|
484
|
-
if (part.type === "thinking")
|
|
485
|
-
return { type: "thinking", thinking: part.text, signature: part.signature };
|
|
486
|
-
if (part.type === "tool_call")
|
|
487
|
-
return {
|
|
488
|
-
type: "tool_use",
|
|
489
|
-
id: remapAnthropicToolCallId(part.id, idMap),
|
|
490
|
-
name: part.name,
|
|
491
|
-
input: part.args
|
|
492
|
-
};
|
|
493
|
-
if (part.type === "server_tool_call")
|
|
494
|
-
return {
|
|
495
|
-
type: "server_tool_use",
|
|
496
|
-
id: part.id,
|
|
497
|
-
name: part.name,
|
|
498
|
-
input: part.input
|
|
499
|
-
};
|
|
500
|
-
if (part.type === "server_tool_result")
|
|
501
|
-
return part.data;
|
|
502
|
-
if (part.type === "raw") return part.data;
|
|
503
|
-
return null;
|
|
504
|
-
}).filter(Boolean);
|
|
588
|
+
const content = typeof msg.content === "string" ? msg.content : toAnthropicAssistantContent(msg.content, msgIdx === lastAssistantIdx, idMap);
|
|
505
589
|
if (Array.isArray(content) && content.length === 0) continue;
|
|
506
590
|
out.push({ role: "assistant", content });
|
|
507
591
|
continue;
|
|
@@ -587,13 +671,13 @@ function toAnthropicToolChoice(choice) {
|
|
|
587
671
|
if (choice === "required") return { type: "any" };
|
|
588
672
|
return { type: "tool", name: choice.name };
|
|
589
673
|
}
|
|
590
|
-
function
|
|
591
|
-
return /opus-4-7|opus-4
|
|
674
|
+
function isAdaptiveThinkingModel(model) {
|
|
675
|
+
return /opus-4[-.]8|opus-4[-.]7|opus-4[-.]6|sonnet-4[-.]6/.test(model);
|
|
592
676
|
}
|
|
593
677
|
function toAnthropicThinking(level, maxTokens, model) {
|
|
594
|
-
if (
|
|
595
|
-
let effort = level
|
|
596
|
-
if (effort === "
|
|
678
|
+
if (isAdaptiveThinkingModel(model)) {
|
|
679
|
+
let effort = level;
|
|
680
|
+
if (effort === "xhigh" && !/opus-4-8|opus-4-7/.test(model)) {
|
|
597
681
|
effort = "high";
|
|
598
682
|
}
|
|
599
683
|
return {
|
|
@@ -602,7 +686,7 @@ function toAnthropicThinking(level, maxTokens, model) {
|
|
|
602
686
|
outputConfig: { effort }
|
|
603
687
|
};
|
|
604
688
|
}
|
|
605
|
-
const effectiveLevel = level === "xhigh" ? "high" : level;
|
|
689
|
+
const effectiveLevel = level === "xhigh" || level === "max" ? "high" : level;
|
|
606
690
|
const budgetMap = {
|
|
607
691
|
low: Math.max(1024, Math.floor(maxTokens * 0.25)),
|
|
608
692
|
medium: Math.max(2048, Math.floor(maxTokens * 0.5)),
|
|
@@ -725,7 +809,7 @@ function toOpenAITools(tools) {
|
|
|
725
809
|
function: {
|
|
726
810
|
name: tool.name,
|
|
727
811
|
description: tool.description,
|
|
728
|
-
parameters:
|
|
812
|
+
parameters: resolveToolSchema(tool)
|
|
729
813
|
}
|
|
730
814
|
}));
|
|
731
815
|
}
|
|
@@ -736,7 +820,7 @@ function toOpenAIToolChoice(choice) {
|
|
|
736
820
|
return { type: "function", function: { name: choice.name } };
|
|
737
821
|
}
|
|
738
822
|
function toOpenAIReasoningEffort(level, _model) {
|
|
739
|
-
return level;
|
|
823
|
+
return level === "max" ? "xhigh" : level;
|
|
740
824
|
}
|
|
741
825
|
function normalizeAnthropicStopReason(reason) {
|
|
742
826
|
switch (reason) {
|
|
@@ -767,10 +851,22 @@ function normalizeOpenAIStopReason(reason) {
|
|
|
767
851
|
}
|
|
768
852
|
}
|
|
769
853
|
|
|
770
|
-
// src/
|
|
854
|
+
// src/utils/json.ts
|
|
771
855
|
function isJsonObject(value) {
|
|
772
856
|
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
773
857
|
}
|
|
858
|
+
function parseToolArguments(argsJson) {
|
|
859
|
+
if (!argsJson) return {};
|
|
860
|
+
try {
|
|
861
|
+
const parsed = JSON.parse(argsJson);
|
|
862
|
+
const unwrapped = typeof parsed === "string" ? JSON.parse(parsed) : parsed;
|
|
863
|
+
return isJsonObject(unwrapped) ? unwrapped : {};
|
|
864
|
+
} catch {
|
|
865
|
+
return {};
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// src/providers/anthropic.ts
|
|
774
870
|
function createClient(options) {
|
|
775
871
|
const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
|
|
776
872
|
return new import_sdk.default({
|
|
@@ -863,7 +959,7 @@ async function* runStream(options) {
|
|
|
863
959
|
})(),
|
|
864
960
|
stream: useStreaming
|
|
865
961
|
};
|
|
866
|
-
const hasAdaptiveThinking =
|
|
962
|
+
const hasAdaptiveThinking = isAdaptiveThinkingModel(options.model);
|
|
867
963
|
const betaHeaders = [
|
|
868
964
|
...isOAuth ? ["claude-code-20250219", "oauth-2025-04-20"] : [],
|
|
869
965
|
...options.compaction ? ["compact-2026-01-12"] : [],
|
|
@@ -1187,6 +1283,18 @@ function messageToResponse(message) {
|
|
|
1187
1283
|
}
|
|
1188
1284
|
};
|
|
1189
1285
|
}
|
|
1286
|
+
function readUnifiedRateLimit(headers) {
|
|
1287
|
+
const status = readHeader(headers, "anthropic-ratelimit-unified-status");
|
|
1288
|
+
const resetRaw = readHeader(
|
|
1289
|
+
headers,
|
|
1290
|
+
"anthropic-ratelimit-unified-reset",
|
|
1291
|
+
"anthropic-ratelimit-unified-5h-reset",
|
|
1292
|
+
"anthropic-ratelimit-unified-7d-reset"
|
|
1293
|
+
);
|
|
1294
|
+
const resetNum = resetRaw != null ? Number(resetRaw) : Number.NaN;
|
|
1295
|
+
const resetsAt = Number.isFinite(resetNum) && resetNum > 0 ? resetNum : void 0;
|
|
1296
|
+
return { rejected: status === "rejected", ...resetsAt ? { resetsAt } : {} };
|
|
1297
|
+
}
|
|
1190
1298
|
function toError(err) {
|
|
1191
1299
|
if (err instanceof import_sdk.default.APIError) {
|
|
1192
1300
|
const errorBody = err.error;
|
|
@@ -1195,6 +1303,18 @@ function toError(err) {
|
|
|
1195
1303
|
const bodyMessage = typeof nestedError?.message === "string" ? nestedError.message : typeof errorBody?.message === "string" ? errorBody.message : void 0;
|
|
1196
1304
|
const bodyType = typeof nestedError?.type === "string" ? nestedError.type : typeof errorBody?.type === "string" ? errorBody.type : typeof err.type === "string" ? err.type : void 0;
|
|
1197
1305
|
const message = bodyType && bodyMessage ? `${bodyType}: ${bodyMessage}` : bodyMessage ?? err.message;
|
|
1306
|
+
if (err.status === 429) {
|
|
1307
|
+
const limit = readUnifiedRateLimit(err.headers);
|
|
1308
|
+
const farOff = limit.resetsAt != null && limit.resetsAt * 1e3 - Date.now() > 6e4;
|
|
1309
|
+
if (limit.rejected || farOff) {
|
|
1310
|
+
return new ProviderError("anthropic", "Claude usage limit reached", {
|
|
1311
|
+
statusCode: 429,
|
|
1312
|
+
...requestId ? { requestId } : {},
|
|
1313
|
+
...limit.resetsAt ? { resetsAt: limit.resetsAt } : {},
|
|
1314
|
+
cause: err
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1198
1318
|
return new ProviderError("anthropic", message, {
|
|
1199
1319
|
statusCode: err.status,
|
|
1200
1320
|
...requestId ? { requestId } : {},
|
|
@@ -1227,19 +1347,30 @@ function fnv1aHash(value) {
|
|
|
1227
1347
|
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
1228
1348
|
}
|
|
1229
1349
|
|
|
1230
|
-
// src/
|
|
1231
|
-
function
|
|
1232
|
-
return
|
|
1350
|
+
// src/utils/env.ts
|
|
1351
|
+
function getEnvironment() {
|
|
1352
|
+
return globalThis.process?.env;
|
|
1233
1353
|
}
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
return {};
|
|
1354
|
+
|
|
1355
|
+
// src/providers/openai.ts
|
|
1356
|
+
function extractOpenAIUsage(usage) {
|
|
1357
|
+
let cacheRead = 0;
|
|
1358
|
+
const details = usage.prompt_tokens_details;
|
|
1359
|
+
if (details?.cached_tokens) {
|
|
1360
|
+
cacheRead = details.cached_tokens;
|
|
1242
1361
|
}
|
|
1362
|
+
const usageAny = usage;
|
|
1363
|
+
if (!cacheRead && typeof usageAny.cached_tokens === "number" && usageAny.cached_tokens > 0) {
|
|
1364
|
+
cacheRead = usageAny.cached_tokens;
|
|
1365
|
+
}
|
|
1366
|
+
if (!cacheRead && typeof usageAny.prompt_cache_hit_tokens === "number" && usageAny.prompt_cache_hit_tokens > 0) {
|
|
1367
|
+
cacheRead = usageAny.prompt_cache_hit_tokens;
|
|
1368
|
+
}
|
|
1369
|
+
return {
|
|
1370
|
+
inputTokens: usage.prompt_tokens - cacheRead,
|
|
1371
|
+
outputTokens: usage.completion_tokens,
|
|
1372
|
+
cacheRead
|
|
1373
|
+
};
|
|
1243
1374
|
}
|
|
1244
1375
|
function createClient2(options) {
|
|
1245
1376
|
return new import_openai.default({
|
|
@@ -1295,7 +1426,7 @@ async function* runStream2(options) {
|
|
|
1295
1426
|
params.thinking = { type: "disabled" };
|
|
1296
1427
|
}
|
|
1297
1428
|
}
|
|
1298
|
-
if (
|
|
1429
|
+
if (getEnvironment()?.GGAI_DUMP_REQUEST) {
|
|
1299
1430
|
const fs = await import("fs");
|
|
1300
1431
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1301
1432
|
const dumpPath = `/tmp/ggai-request-${ts}.json`;
|
|
@@ -1336,19 +1467,7 @@ async function* runStream2(options) {
|
|
|
1336
1467
|
for await (const chunk of stream2) {
|
|
1337
1468
|
const choice = chunk.choices?.[0];
|
|
1338
1469
|
if (chunk.usage) {
|
|
1339
|
-
outputTokens = chunk.usage
|
|
1340
|
-
const details = chunk.usage.prompt_tokens_details;
|
|
1341
|
-
if (details?.cached_tokens) {
|
|
1342
|
-
cacheRead = details.cached_tokens;
|
|
1343
|
-
}
|
|
1344
|
-
const usageAny = chunk.usage;
|
|
1345
|
-
if (!cacheRead && typeof usageAny.cached_tokens === "number" && usageAny.cached_tokens > 0) {
|
|
1346
|
-
cacheRead = usageAny.cached_tokens;
|
|
1347
|
-
}
|
|
1348
|
-
if (!cacheRead && typeof usageAny.prompt_cache_hit_tokens === "number" && usageAny.prompt_cache_hit_tokens > 0) {
|
|
1349
|
-
cacheRead = usageAny.prompt_cache_hit_tokens;
|
|
1350
|
-
}
|
|
1351
|
-
inputTokens = chunk.usage.prompt_tokens - cacheRead;
|
|
1470
|
+
({ inputTokens, outputTokens, cacheRead } = extractOpenAIUsage(chunk.usage));
|
|
1352
1471
|
}
|
|
1353
1472
|
if (!choice) continue;
|
|
1354
1473
|
if (choice.finish_reason) {
|
|
@@ -1494,17 +1613,7 @@ function completionToResponse(completion) {
|
|
|
1494
1613
|
let outputTokens = 0;
|
|
1495
1614
|
let cacheRead = 0;
|
|
1496
1615
|
if (completion.usage) {
|
|
1497
|
-
outputTokens = completion.usage
|
|
1498
|
-
const details = completion.usage.prompt_tokens_details;
|
|
1499
|
-
if (details?.cached_tokens) cacheRead = details.cached_tokens;
|
|
1500
|
-
const usageAny = completion.usage;
|
|
1501
|
-
if (!cacheRead && typeof usageAny.cached_tokens === "number" && usageAny.cached_tokens > 0) {
|
|
1502
|
-
cacheRead = usageAny.cached_tokens;
|
|
1503
|
-
}
|
|
1504
|
-
if (!cacheRead && typeof usageAny.prompt_cache_hit_tokens === "number" && usageAny.prompt_cache_hit_tokens > 0) {
|
|
1505
|
-
cacheRead = usageAny.prompt_cache_hit_tokens;
|
|
1506
|
-
}
|
|
1507
|
-
inputTokens = completion.usage.prompt_tokens - cacheRead;
|
|
1616
|
+
({ inputTokens, outputTokens, cacheRead } = extractOpenAIUsage(completion.usage));
|
|
1508
1617
|
}
|
|
1509
1618
|
const stopReason = normalizeOpenAIStopReason(choice?.finish_reason ?? null);
|
|
1510
1619
|
return {
|
|
@@ -1552,24 +1661,65 @@ function providerDiag(phase, data) {
|
|
|
1552
1661
|
_diagFn?.(phase, data);
|
|
1553
1662
|
}
|
|
1554
1663
|
|
|
1555
|
-
// src/
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1664
|
+
// src/utils/sse.ts
|
|
1665
|
+
function parseSseBuffer(buffer) {
|
|
1666
|
+
const events = [];
|
|
1667
|
+
let cursor = 0;
|
|
1668
|
+
while (true) {
|
|
1669
|
+
const next = buffer.indexOf("\n\n", cursor);
|
|
1670
|
+
if (next === -1) break;
|
|
1671
|
+
const raw = buffer.slice(cursor, next);
|
|
1672
|
+
cursor = next + 2;
|
|
1673
|
+
let eventName;
|
|
1674
|
+
const dataLines = [];
|
|
1675
|
+
for (const line of raw.split("\n")) {
|
|
1676
|
+
if (line.startsWith("event:")) {
|
|
1677
|
+
eventName = line.slice("event:".length).trim();
|
|
1678
|
+
} else if (line.startsWith("data:")) {
|
|
1679
|
+
dataLines.push(line.slice("data:".length).trimStart());
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
if (dataLines.length > 0) {
|
|
1683
|
+
events.push({ event: eventName, data: dataLines.join("\n") });
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
return { events, remaining: buffer.slice(cursor) };
|
|
1559
1687
|
}
|
|
1560
|
-
function
|
|
1561
|
-
|
|
1688
|
+
async function* readSseStream(body) {
|
|
1689
|
+
const reader = body.getReader();
|
|
1690
|
+
const decoder = new TextDecoder();
|
|
1691
|
+
let buffer = "";
|
|
1562
1692
|
try {
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1693
|
+
while (true) {
|
|
1694
|
+
const { done, value } = await reader.read();
|
|
1695
|
+
if (done) break;
|
|
1696
|
+
buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
|
|
1697
|
+
const parsed2 = parseSseBuffer(buffer);
|
|
1698
|
+
buffer = parsed2.remaining;
|
|
1699
|
+
yield* parsed2.events;
|
|
1700
|
+
}
|
|
1701
|
+
buffer += decoder.decode().replace(/\r\n/g, "\n");
|
|
1702
|
+
const parsed = parseSseBuffer(buffer + "\n\n");
|
|
1703
|
+
yield* parsed.events;
|
|
1704
|
+
} finally {
|
|
1705
|
+
reader.releaseLock();
|
|
1568
1706
|
}
|
|
1569
1707
|
}
|
|
1708
|
+
|
|
1709
|
+
// src/utils/request-id.ts
|
|
1710
|
+
function extractRequestIdFromMessage(message) {
|
|
1711
|
+
const match = message.match(/request ID ([a-z0-9-]{8,})/i);
|
|
1712
|
+
return match?.[1];
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
// src/providers/openai-codex.ts
|
|
1716
|
+
var DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
|
|
1570
1717
|
function outputTextKey(itemId, contentIndex) {
|
|
1571
1718
|
return `${itemId ?? ""}:${contentIndex ?? 0}`;
|
|
1572
1719
|
}
|
|
1720
|
+
function isVisibleOutputItem(itemType) {
|
|
1721
|
+
return itemType === "message";
|
|
1722
|
+
}
|
|
1573
1723
|
function streamOpenAICodex(options) {
|
|
1574
1724
|
return new StreamResult(runStream3(options));
|
|
1575
1725
|
}
|
|
@@ -1628,7 +1778,9 @@ async function* runStream3(options) {
|
|
|
1628
1778
|
const text = await response.text().catch(() => "");
|
|
1629
1779
|
const parsed = parseCodexErrorBody(text);
|
|
1630
1780
|
const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
|
|
1631
|
-
const requestId = parsed.requestId ?? response.headers
|
|
1781
|
+
const requestId = parsed.requestId ?? readHeader(response.headers, "x-request-id", "openai-request-id", "x-oai-request-id");
|
|
1782
|
+
const usageLimit = codexUsageLimitError(parsed.errorObj, response.status, requestId);
|
|
1783
|
+
if (usageLimit) throw usageLimit;
|
|
1632
1784
|
let hint;
|
|
1633
1785
|
if (response.status === 400 && text.includes("not supported")) {
|
|
1634
1786
|
if (options.model === "gpt-5.5-pro") {
|
|
@@ -1653,6 +1805,7 @@ async function* runStream3(options) {
|
|
|
1653
1805
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
1654
1806
|
const outputItemTypes = /* @__PURE__ */ new Map();
|
|
1655
1807
|
const outputTextByPart = /* @__PURE__ */ new Map();
|
|
1808
|
+
const pendingOutputTextByPart = /* @__PURE__ */ new Map();
|
|
1656
1809
|
let inputTokens = 0;
|
|
1657
1810
|
let outputTokens = 0;
|
|
1658
1811
|
let cacheRead = 0;
|
|
@@ -1669,7 +1822,13 @@ async function* runStream3(options) {
|
|
|
1669
1822
|
const nested = event.error ?? void 0;
|
|
1670
1823
|
const message = nested?.message ?? event.message ?? "Codex stream emitted an error chunk without a message.";
|
|
1671
1824
|
const code = nested?.code ?? nested?.type ?? event.code ?? "server_error";
|
|
1672
|
-
const requestId =
|
|
1825
|
+
const requestId = extractRequestIdFromMessage(message) ?? event.request_id;
|
|
1826
|
+
const usageLimit = codexUsageLimitError(
|
|
1827
|
+
nested ?? event,
|
|
1828
|
+
void 0,
|
|
1829
|
+
requestId
|
|
1830
|
+
);
|
|
1831
|
+
if (usageLimit) throw usageLimit;
|
|
1673
1832
|
throw new ProviderError("openai", message, {
|
|
1674
1833
|
...requestId != null ? { requestId } : {},
|
|
1675
1834
|
...code === "server_error" ? { statusCode: 500 } : {}
|
|
@@ -1678,7 +1837,7 @@ async function* runStream3(options) {
|
|
|
1678
1837
|
if (type === "response.failed") {
|
|
1679
1838
|
const nested = event.error;
|
|
1680
1839
|
const message = nested?.message ?? "Codex response failed.";
|
|
1681
|
-
const requestId =
|
|
1840
|
+
const requestId = extractRequestIdFromMessage(message) ?? event.request_id;
|
|
1682
1841
|
throw new ProviderError("openai", message, {
|
|
1683
1842
|
...requestId != null ? { requestId } : {}
|
|
1684
1843
|
});
|
|
@@ -1689,11 +1848,17 @@ async function* runStream3(options) {
|
|
|
1689
1848
|
const contentIndex = event.content_index;
|
|
1690
1849
|
const key = outputTextKey(itemId, contentIndex);
|
|
1691
1850
|
outputTextByPart.set(key, `${outputTextByPart.get(key) ?? ""}${delta}`);
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
} else {
|
|
1851
|
+
const itemType = itemId ? outputItemTypes.get(itemId) : void 0;
|
|
1852
|
+
if (itemId && isVisibleOutputItem(itemType)) {
|
|
1695
1853
|
textAccum += delta;
|
|
1696
1854
|
yield { type: "text_delta", text: delta };
|
|
1855
|
+
} else if (itemId && itemType == null) {
|
|
1856
|
+
const pending = pendingOutputTextByPart.get(key);
|
|
1857
|
+
pendingOutputTextByPart.set(key, {
|
|
1858
|
+
itemId,
|
|
1859
|
+
contentIndex: contentIndex ?? 0,
|
|
1860
|
+
text: `${pending?.text ?? ""}${delta}`
|
|
1861
|
+
});
|
|
1697
1862
|
}
|
|
1698
1863
|
}
|
|
1699
1864
|
if (type === "response.output_text.done") {
|
|
@@ -1706,11 +1871,17 @@ async function* runStream3(options) {
|
|
|
1706
1871
|
const missingText = streamedText ? fullText.slice(streamedText.length) : fullText;
|
|
1707
1872
|
outputTextByPart.set(key, fullText);
|
|
1708
1873
|
if (missingText && fullText.startsWith(streamedText)) {
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
} else {
|
|
1874
|
+
const itemType = itemId ? outputItemTypes.get(itemId) : void 0;
|
|
1875
|
+
if (itemId && isVisibleOutputItem(itemType)) {
|
|
1712
1876
|
textAccum += missingText;
|
|
1713
1877
|
yield { type: "text_delta", text: missingText };
|
|
1878
|
+
} else if (itemId && itemType == null) {
|
|
1879
|
+
const pending = pendingOutputTextByPart.get(key);
|
|
1880
|
+
pendingOutputTextByPart.set(key, {
|
|
1881
|
+
itemId,
|
|
1882
|
+
contentIndex: contentIndex ?? 0,
|
|
1883
|
+
text: `${pending?.text ?? ""}${missingText}`
|
|
1884
|
+
});
|
|
1714
1885
|
}
|
|
1715
1886
|
}
|
|
1716
1887
|
}
|
|
@@ -1729,6 +1900,17 @@ async function* runStream3(options) {
|
|
|
1729
1900
|
if (itemType === "reasoning" && options.thinking) {
|
|
1730
1901
|
yield { type: "thinking_delta", text: "" };
|
|
1731
1902
|
}
|
|
1903
|
+
if (itemId && itemType) {
|
|
1904
|
+
const pending = [...pendingOutputTextByPart.entries()].filter(([, pendingPart]) => pendingPart.itemId === itemId).sort(([, a], [, b]) => a.contentIndex - b.contentIndex);
|
|
1905
|
+
for (const [key, pendingPart] of pending) {
|
|
1906
|
+
pendingOutputTextByPart.delete(key);
|
|
1907
|
+
if (!pendingPart.text) continue;
|
|
1908
|
+
if (isVisibleOutputItem(itemType)) {
|
|
1909
|
+
textAccum += pendingPart.text;
|
|
1910
|
+
yield { type: "text_delta", text: pendingPart.text };
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1732
1914
|
}
|
|
1733
1915
|
if (type === "response.output_item.added") {
|
|
1734
1916
|
const item = event.item;
|
|
@@ -1774,7 +1956,7 @@ async function* runStream3(options) {
|
|
|
1774
1956
|
const id = `${callId}|${itemId}`;
|
|
1775
1957
|
const tc = toolCalls.get(id);
|
|
1776
1958
|
if (tc) {
|
|
1777
|
-
const args =
|
|
1959
|
+
const args = parseToolArguments(tc.argsJson);
|
|
1778
1960
|
yield {
|
|
1779
1961
|
type: "toolcall_done",
|
|
1780
1962
|
id: tc.id,
|
|
@@ -1798,7 +1980,7 @@ async function* runStream3(options) {
|
|
|
1798
1980
|
contentParts.push({ type: "text", text: textAccum });
|
|
1799
1981
|
}
|
|
1800
1982
|
for (const [, tc] of toolCalls) {
|
|
1801
|
-
const args =
|
|
1983
|
+
const args = parseToolArguments(tc.argsJson);
|
|
1802
1984
|
const toolCall = {
|
|
1803
1985
|
type: "tool_call",
|
|
1804
1986
|
id: tc.id,
|
|
@@ -1821,33 +2003,13 @@ async function* runStream3(options) {
|
|
|
1821
2003
|
return streamResponse;
|
|
1822
2004
|
}
|
|
1823
2005
|
async function* parseSSE(body) {
|
|
1824
|
-
const
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
if (done) break;
|
|
1831
|
-
buffer += decoder.decode(value, { stream: true });
|
|
1832
|
-
let idx = buffer.indexOf("\n\n");
|
|
1833
|
-
while (idx !== -1) {
|
|
1834
|
-
const chunk = buffer.slice(0, idx);
|
|
1835
|
-
buffer = buffer.slice(idx + 2);
|
|
1836
|
-
const dataLines = chunk.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim());
|
|
1837
|
-
if (dataLines.length > 0) {
|
|
1838
|
-
const data = dataLines.join("\n").trim();
|
|
1839
|
-
if (data && data !== "[DONE]") {
|
|
1840
|
-
try {
|
|
1841
|
-
yield JSON.parse(data);
|
|
1842
|
-
} catch {
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1846
|
-
idx = buffer.indexOf("\n\n");
|
|
1847
|
-
}
|
|
2006
|
+
for await (const event of readSseStream(body)) {
|
|
2007
|
+
const data = event.data.trim();
|
|
2008
|
+
if (!data || data === "[DONE]") continue;
|
|
2009
|
+
try {
|
|
2010
|
+
yield JSON.parse(data);
|
|
2011
|
+
} catch {
|
|
1848
2012
|
}
|
|
1849
|
-
} finally {
|
|
1850
|
-
reader.releaseLock();
|
|
1851
2013
|
}
|
|
1852
2014
|
}
|
|
1853
2015
|
function remapCodexId(id, idMap) {
|
|
@@ -1858,10 +2020,6 @@ function remapCodexId(id, idMap) {
|
|
|
1858
2020
|
idMap.set(id, mapped);
|
|
1859
2021
|
return mapped;
|
|
1860
2022
|
}
|
|
1861
|
-
function codexToolResultText(content) {
|
|
1862
|
-
if (typeof content === "string") return content;
|
|
1863
|
-
return content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
1864
|
-
}
|
|
1865
2023
|
function toCodexInput(messages, options) {
|
|
1866
2024
|
let system;
|
|
1867
2025
|
const input = [];
|
|
@@ -1918,7 +2076,7 @@ function toCodexInput(messages, options) {
|
|
|
1918
2076
|
const toolImages = [];
|
|
1919
2077
|
for (const result of msg.content) {
|
|
1920
2078
|
const [callId] = result.toolCallId.includes("|") ? result.toolCallId.split("|", 2) : [result.toolCallId];
|
|
1921
|
-
const text =
|
|
2079
|
+
const text = toolResultText(result.content);
|
|
1922
2080
|
input.push({
|
|
1923
2081
|
type: "function_call_output",
|
|
1924
2082
|
call_id: remapCodexId(callId, idMap),
|
|
@@ -1953,14 +2111,10 @@ function toCodexTools(tools) {
|
|
|
1953
2111
|
type: "function",
|
|
1954
2112
|
name: tool.name,
|
|
1955
2113
|
description: tool.description,
|
|
1956
|
-
parameters:
|
|
2114
|
+
parameters: resolveToolSchema(tool),
|
|
1957
2115
|
strict: null
|
|
1958
2116
|
}));
|
|
1959
2117
|
}
|
|
1960
|
-
function extractCodexRequestId(message) {
|
|
1961
|
-
const match = message.match(/request ID ([a-z0-9-]{8,})/i);
|
|
1962
|
-
return match?.[1];
|
|
1963
|
-
}
|
|
1964
2118
|
function parseCodexErrorBody(text) {
|
|
1965
2119
|
if (!text) return {};
|
|
1966
2120
|
try {
|
|
@@ -1968,13 +2122,35 @@ function parseCodexErrorBody(text) {
|
|
|
1968
2122
|
const error = parsed.error;
|
|
1969
2123
|
const detail = parsed.detail;
|
|
1970
2124
|
const message = error?.message ?? parsed.message ?? (typeof detail === "string" ? detail : void 0);
|
|
1971
|
-
const requestId = parsed.request_id ?? error?.request_id ?? (message ?
|
|
1972
|
-
|
|
2125
|
+
const requestId = parsed.request_id ?? error?.request_id ?? (message ? extractRequestIdFromMessage(message) : void 0);
|
|
2126
|
+
const errorObj = error ?? parsed;
|
|
2127
|
+
return {
|
|
2128
|
+
...message ? { message } : {},
|
|
2129
|
+
...requestId ? { requestId } : {},
|
|
2130
|
+
...errorObj ? { errorObj } : {}
|
|
2131
|
+
};
|
|
1973
2132
|
} catch {
|
|
1974
2133
|
const trimmed = text.trim().slice(0, 240);
|
|
1975
2134
|
return trimmed ? { message: trimmed } : {};
|
|
1976
2135
|
}
|
|
1977
2136
|
}
|
|
2137
|
+
var CODEX_USAGE_LIMIT_CODE = /usage_limit_reached|usage_not_included/i;
|
|
2138
|
+
var CODEX_RATE_LIMIT_CODE = /rate_limit_exceeded/i;
|
|
2139
|
+
function codexUsageLimitError(errorObj, statusCode, requestId) {
|
|
2140
|
+
const code = String(errorObj?.code ?? errorObj?.type ?? "");
|
|
2141
|
+
const rateLimits = errorObj?.rate_limits;
|
|
2142
|
+
const resetsAtRaw = (typeof errorObj?.resets_at === "number" ? errorObj.resets_at : void 0) ?? rateLimits?.primary?.resets_at ?? rateLimits?.secondary?.resets_at;
|
|
2143
|
+
const resetsInSeconds = typeof errorObj?.resets_in_seconds === "number" ? errorObj.resets_in_seconds : void 0;
|
|
2144
|
+
const resetsAt = typeof resetsAtRaw === "number" && resetsAtRaw > 0 ? resetsAtRaw : resetsInSeconds != null && resetsInSeconds > 0 ? Math.floor(Date.now() / 1e3) + resetsInSeconds : void 0;
|
|
2145
|
+
const isHardUsage = CODEX_USAGE_LIMIT_CODE.test(code);
|
|
2146
|
+
const isRateOr429 = CODEX_RATE_LIMIT_CODE.test(code) || statusCode === 429;
|
|
2147
|
+
if (!isHardUsage && !(isRateOr429 && resetsAt != null)) return null;
|
|
2148
|
+
return new ProviderError("openai", "ChatGPT usage limit reached", {
|
|
2149
|
+
statusCode: statusCode ?? 429,
|
|
2150
|
+
...requestId ? { requestId } : {},
|
|
2151
|
+
...resetsAt ? { resetsAt } : {}
|
|
2152
|
+
});
|
|
2153
|
+
}
|
|
1978
2154
|
|
|
1979
2155
|
// src/providers/gemini.ts
|
|
1980
2156
|
var DEFAULT_CODE_ASSIST_BASE_URL = "https://cloudcode-pa.googleapis.com";
|
|
@@ -1996,12 +2172,6 @@ var CODE_ASSIST_SUPPORTED_MODELS = /* @__PURE__ */ new Set([
|
|
|
1996
2172
|
"gemma-4-31b-it",
|
|
1997
2173
|
"gemma-4-26b-a4b-it"
|
|
1998
2174
|
]);
|
|
1999
|
-
function isJsonObject4(value) {
|
|
2000
|
-
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
2001
|
-
}
|
|
2002
|
-
function getEnvironment() {
|
|
2003
|
-
return globalThis.process?.env;
|
|
2004
|
-
}
|
|
2005
2175
|
function getGoogleProject(options) {
|
|
2006
2176
|
const env = getEnvironment();
|
|
2007
2177
|
return options.projectId ?? env?.GOOGLE_CLOUD_PROJECT ?? env?.GOOGLE_CLOUD_PROJECT_ID;
|
|
@@ -2101,7 +2271,7 @@ function toGeminiTools(tools) {
|
|
|
2101
2271
|
functionDeclarations: tools.map((tool) => ({
|
|
2102
2272
|
name: tool.name,
|
|
2103
2273
|
description: tool.description,
|
|
2104
|
-
parameters: sanitizeSchema(
|
|
2274
|
+
parameters: sanitizeSchema(resolveToolSchema(tool))
|
|
2105
2275
|
}))
|
|
2106
2276
|
}
|
|
2107
2277
|
];
|
|
@@ -2112,7 +2282,7 @@ function sanitizeSchema(schema) {
|
|
|
2112
2282
|
return clone;
|
|
2113
2283
|
}
|
|
2114
2284
|
function stripUnsupportedSchemaFields(value) {
|
|
2115
|
-
if (!
|
|
2285
|
+
if (!isJsonObject(value)) {
|
|
2116
2286
|
if (Array.isArray(value)) {
|
|
2117
2287
|
for (const item of value) stripUnsupportedSchemaFields(item);
|
|
2118
2288
|
}
|
|
@@ -2121,7 +2291,7 @@ function stripUnsupportedSchemaFields(value) {
|
|
|
2121
2291
|
delete value.$schema;
|
|
2122
2292
|
delete value.additionalProperties;
|
|
2123
2293
|
for (const item of Object.values(value)) {
|
|
2124
|
-
if (
|
|
2294
|
+
if (isJsonObject(item) || Array.isArray(item)) {
|
|
2125
2295
|
stripUnsupportedSchemaFields(item);
|
|
2126
2296
|
}
|
|
2127
2297
|
}
|
|
@@ -2144,6 +2314,7 @@ function toGemini3ThinkingLevel(level) {
|
|
|
2144
2314
|
return "MEDIUM";
|
|
2145
2315
|
case "high":
|
|
2146
2316
|
case "xhigh":
|
|
2317
|
+
case "max":
|
|
2147
2318
|
return "HIGH";
|
|
2148
2319
|
}
|
|
2149
2320
|
}
|
|
@@ -2155,6 +2326,7 @@ function toThinkingBudget(level) {
|
|
|
2155
2326
|
return 8192;
|
|
2156
2327
|
case "high":
|
|
2157
2328
|
case "xhigh":
|
|
2329
|
+
case "max":
|
|
2158
2330
|
return 8192;
|
|
2159
2331
|
}
|
|
2160
2332
|
}
|
|
@@ -2234,54 +2406,11 @@ function normalizeGeminiStopReason(reason) {
|
|
|
2234
2406
|
return "end_turn";
|
|
2235
2407
|
}
|
|
2236
2408
|
}
|
|
2237
|
-
function parseSseEvents(buffer) {
|
|
2238
|
-
const events = [];
|
|
2239
|
-
let cursor = 0;
|
|
2240
|
-
while (true) {
|
|
2241
|
-
const next = buffer.indexOf("\n\n", cursor);
|
|
2242
|
-
if (next === -1) break;
|
|
2243
|
-
const raw = buffer.slice(cursor, next);
|
|
2244
|
-
cursor = next + 2;
|
|
2245
|
-
let eventName;
|
|
2246
|
-
const dataLines = [];
|
|
2247
|
-
for (const line of raw.split("\n")) {
|
|
2248
|
-
if (line.startsWith("event:")) {
|
|
2249
|
-
eventName = line.slice("event:".length).trim();
|
|
2250
|
-
} else if (line.startsWith("data:")) {
|
|
2251
|
-
dataLines.push(line.slice("data:".length).trimStart());
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
if (dataLines.length > 0) {
|
|
2255
|
-
events.push({ event: eventName, data: dataLines.join("\n") });
|
|
2256
|
-
}
|
|
2257
|
-
}
|
|
2258
|
-
return { events, remaining: buffer.slice(cursor) };
|
|
2259
|
-
}
|
|
2260
2409
|
async function* streamSse(response) {
|
|
2261
2410
|
if (!response.body) return;
|
|
2262
|
-
const
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
try {
|
|
2266
|
-
while (true) {
|
|
2267
|
-
const { done, value } = await reader.read();
|
|
2268
|
-
if (done) break;
|
|
2269
|
-
buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
|
|
2270
|
-
const parsed2 = parseSseEvents(buffer);
|
|
2271
|
-
buffer = parsed2.remaining;
|
|
2272
|
-
for (const event of parsed2.events) {
|
|
2273
|
-
if (event.data === "[DONE]") continue;
|
|
2274
|
-
yield JSON.parse(event.data);
|
|
2275
|
-
}
|
|
2276
|
-
}
|
|
2277
|
-
buffer += decoder.decode().replace(/\r\n/g, "\n");
|
|
2278
|
-
const parsed = parseSseEvents(buffer + "\n\n");
|
|
2279
|
-
for (const event of parsed.events) {
|
|
2280
|
-
if (event.data === "[DONE]") continue;
|
|
2281
|
-
yield JSON.parse(event.data);
|
|
2282
|
-
}
|
|
2283
|
-
} finally {
|
|
2284
|
-
reader.releaseLock();
|
|
2411
|
+
for await (const event of readSseStream(response.body)) {
|
|
2412
|
+
if (event.data === "[DONE]") continue;
|
|
2413
|
+
yield JSON.parse(event.data);
|
|
2285
2414
|
}
|
|
2286
2415
|
}
|
|
2287
2416
|
function candidatesFromResponse(response) {
|
|
@@ -2304,7 +2433,7 @@ function readFunctionCallPart(part) {
|
|
|
2304
2433
|
return {
|
|
2305
2434
|
...part.functionCall.id ? { id: part.functionCall.id } : {},
|
|
2306
2435
|
name: part.functionCall.name,
|
|
2307
|
-
args:
|
|
2436
|
+
args: isJsonObject(part.functionCall.args) ? part.functionCall.args : {}
|
|
2308
2437
|
};
|
|
2309
2438
|
}
|
|
2310
2439
|
function makeToolCallId(index, providerId) {
|
|
@@ -2736,6 +2865,7 @@ function registerPalsuProvider(config) {
|
|
|
2736
2865
|
StreamResult,
|
|
2737
2866
|
formatError,
|
|
2738
2867
|
formatErrorForDisplay,
|
|
2868
|
+
isUsageLimitError,
|
|
2739
2869
|
palsuAssistantMessage,
|
|
2740
2870
|
palsuText,
|
|
2741
2871
|
palsuThinking,
|
|
@@ -2743,6 +2873,8 @@ function registerPalsuProvider(config) {
|
|
|
2743
2873
|
providerRegistry,
|
|
2744
2874
|
registerPalsuProvider,
|
|
2745
2875
|
setProviderDiagnostic,
|
|
2746
|
-
stream
|
|
2876
|
+
stream,
|
|
2877
|
+
toAnthropicMessages,
|
|
2878
|
+
toOpenAIMessages
|
|
2747
2879
|
});
|
|
2748
2880
|
//# sourceMappingURL=index.cjs.map
|