@jacobbd/relay-ai 0.9.2 → 0.9.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/{chunk-Q2FTCICO.js → chunk-NYKVDBQC.js} +2 -2
- package/dist/{chunk-Q2FTCICO.js.map → chunk-NYKVDBQC.js.map} +1 -1
- package/dist/{chunk-GQCFLSEM.js → chunk-PVGAE7HA.js} +506 -185
- package/dist/chunk-PVGAE7HA.js.map +1 -0
- package/dist/cli.js +148 -9
- package/dist/cli.js.map +1 -1
- package/dist/core/index.d.ts +32 -4
- package/dist/core/index.js +617 -109
- package/dist/core/index.js.map +1 -1
- package/dist/{provider-templates-XKNRKAQU.js → provider-templates-CGWE66TD.js} +2 -2
- package/dist/ui/public/app.js +190 -1
- package/dist/{ui-command-SDMYDYT6.js → ui-command-JQPEZAMN.js} +59 -7
- package/dist/ui-command-JQPEZAMN.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-GQCFLSEM.js.map +0 -1
- package/dist/ui-command-SDMYDYT6.js.map +0 -1
- /package/dist/{provider-templates-XKNRKAQU.js.map → provider-templates-CGWE66TD.js.map} +0 -0
package/dist/core/index.js
CHANGED
|
@@ -50,7 +50,7 @@ import { join as join2 } from "path";
|
|
|
50
50
|
// package.json
|
|
51
51
|
var package_default = {
|
|
52
52
|
name: "@jacobbd/relay-ai",
|
|
53
|
-
version: "0.9.
|
|
53
|
+
version: "0.9.3",
|
|
54
54
|
publishConfig: {
|
|
55
55
|
access: "public"
|
|
56
56
|
},
|
|
@@ -328,9 +328,7 @@ function createResponsesLiteNormalizeState() {
|
|
|
328
328
|
textDeltaForwarded: false,
|
|
329
329
|
messageAddedIds: /* @__PURE__ */ new Set(),
|
|
330
330
|
messageDoneIds: /* @__PURE__ */ new Set(),
|
|
331
|
-
|
|
332
|
-
functionDeltaIndexes: /* @__PURE__ */ new Set(),
|
|
333
|
-
functionDoneCallIds: /* @__PURE__ */ new Set()
|
|
331
|
+
functionCalls: []
|
|
334
332
|
};
|
|
335
333
|
}
|
|
336
334
|
function nextId(state, prefix) {
|
|
@@ -354,20 +352,92 @@ function normalizeErrorEvent(event) {
|
|
|
354
352
|
}
|
|
355
353
|
};
|
|
356
354
|
}
|
|
357
|
-
function
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
355
|
+
function resolveFunctionCall(state, hint) {
|
|
356
|
+
if (hint.callId) {
|
|
357
|
+
const byCallId = state.functionCalls.find((entry2) => entry2.callId === hint.callId);
|
|
358
|
+
if (byCallId) return byCallId;
|
|
359
|
+
}
|
|
360
|
+
if (hint.itemId) {
|
|
361
|
+
const byItemId = state.functionCalls.find((entry2) => entry2.itemId === hint.itemId);
|
|
362
|
+
if (byItemId) return byItemId;
|
|
363
|
+
}
|
|
364
|
+
if (hint.outputIndex !== void 0) {
|
|
365
|
+
const open3 = [...state.functionCalls].reverse().find((entry2) => entry2.outputIndex === hint.outputIndex && !entry2.done && !(hint.callId && entry2.callId !== hint.callId));
|
|
366
|
+
if (open3) return open3;
|
|
367
|
+
}
|
|
368
|
+
if (hint.callId === void 0 && hint.itemId === void 0 && hint.outputIndex === void 0 && state.lastFunctionCall) {
|
|
369
|
+
return state.lastFunctionCall;
|
|
370
|
+
}
|
|
371
|
+
const entry = {
|
|
372
|
+
itemId: hint.itemId ?? nextId(state, "fc"),
|
|
373
|
+
callId: hint.callId ?? hint.itemId ?? nextId(state, "call"),
|
|
374
|
+
name: "",
|
|
375
|
+
args: "",
|
|
376
|
+
upstream: {},
|
|
377
|
+
outputIndex: hint.outputIndex ?? state.lastOutputIndex,
|
|
378
|
+
added: false,
|
|
379
|
+
deltaForwarded: false,
|
|
380
|
+
doneSeen: false,
|
|
381
|
+
done: false
|
|
382
|
+
};
|
|
383
|
+
state.functionCalls.push(entry);
|
|
384
|
+
return entry;
|
|
385
|
+
}
|
|
386
|
+
function absorbFunctionItem(entry, item, authoritative) {
|
|
387
|
+
entry.upstream = { ...entry.upstream, ...item };
|
|
388
|
+
const name = asString(item.name);
|
|
389
|
+
if (name) entry.name = name;
|
|
390
|
+
const callId = asString(item.call_id);
|
|
391
|
+
if (callId) entry.callId = callId;
|
|
392
|
+
if (authoritative && typeof item.arguments === "string") entry.upstreamArgs = item.arguments;
|
|
393
|
+
}
|
|
394
|
+
function resolveFunctionArgs(entry) {
|
|
395
|
+
if (entry.upstreamArgs) return entry.upstreamArgs;
|
|
396
|
+
if (entry.args) return entry.args;
|
|
397
|
+
return entry.upstreamArgs;
|
|
398
|
+
}
|
|
399
|
+
function functionItemPayload(entry, extra) {
|
|
361
400
|
return {
|
|
362
|
-
...
|
|
401
|
+
...entry.upstream,
|
|
363
402
|
type: "function_call",
|
|
364
|
-
id,
|
|
365
|
-
call_id: callId,
|
|
366
|
-
name:
|
|
367
|
-
|
|
368
|
-
|
|
403
|
+
id: entry.itemId,
|
|
404
|
+
call_id: entry.callId,
|
|
405
|
+
name: entry.name,
|
|
406
|
+
...extra
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
function functionAddedEvent(entry) {
|
|
410
|
+
entry.added = true;
|
|
411
|
+
return {
|
|
412
|
+
type: "response.output_item.added",
|
|
413
|
+
output_index: entry.outputIndex,
|
|
414
|
+
item: functionItemPayload(entry, { arguments: "" })
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
function functionDoneEvent(entry, args) {
|
|
418
|
+
entry.done = true;
|
|
419
|
+
return {
|
|
420
|
+
type: "response.output_item.done",
|
|
421
|
+
output_index: entry.outputIndex,
|
|
422
|
+
item: functionItemPayload(entry, { arguments: args, status: "completed" })
|
|
369
423
|
};
|
|
370
424
|
}
|
|
425
|
+
function completeFunctionCall(entry, args) {
|
|
426
|
+
if (entry.done) return [];
|
|
427
|
+
const events = [];
|
|
428
|
+
if (!entry.added) events.push(functionAddedEvent(entry));
|
|
429
|
+
if (!entry.deltaForwarded && args.length > 0) {
|
|
430
|
+
entry.deltaForwarded = true;
|
|
431
|
+
events.push({
|
|
432
|
+
type: "response.function_call_arguments.delta",
|
|
433
|
+
item_id: entry.itemId,
|
|
434
|
+
output_index: entry.outputIndex,
|
|
435
|
+
delta: args
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
events.push(functionDoneEvent(entry, args));
|
|
439
|
+
return events;
|
|
440
|
+
}
|
|
371
441
|
function messageText(item) {
|
|
372
442
|
if (typeof item.text === "string") return item.text;
|
|
373
443
|
if (!Array.isArray(item.content)) return "";
|
|
@@ -394,47 +464,43 @@ function synthesizeMessage(item, outputIndex, state) {
|
|
|
394
464
|
];
|
|
395
465
|
}
|
|
396
466
|
function synthesizeFunctionCall(item, outputIndex, state) {
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if (!state.functionAddedIndexes.has(outputIndex)) {
|
|
402
|
-
events.push({
|
|
403
|
-
type: "response.output_item.added",
|
|
404
|
-
output_index: outputIndex,
|
|
405
|
-
item: { ...normalized, arguments: "" }
|
|
406
|
-
});
|
|
407
|
-
state.functionAddedIndexes.add(outputIndex);
|
|
408
|
-
}
|
|
409
|
-
if (!state.functionDeltaIndexes.has(outputIndex) && typeof normalized.arguments === "string" && normalized.arguments.length > 0) {
|
|
410
|
-
events.push({
|
|
411
|
-
type: "response.function_call_arguments.delta",
|
|
412
|
-
item_id: normalized.id,
|
|
413
|
-
output_index: outputIndex,
|
|
414
|
-
delta: normalized.arguments
|
|
415
|
-
});
|
|
416
|
-
state.functionDeltaIndexes.add(outputIndex);
|
|
417
|
-
}
|
|
418
|
-
events.push({
|
|
419
|
-
type: "response.output_item.done",
|
|
420
|
-
output_index: outputIndex,
|
|
421
|
-
item: { ...normalized, status: "completed" }
|
|
467
|
+
const entry = resolveFunctionCall(state, {
|
|
468
|
+
itemId: asString(item.id),
|
|
469
|
+
callId: asString(item.call_id),
|
|
470
|
+
outputIndex
|
|
422
471
|
});
|
|
423
|
-
|
|
424
|
-
state.
|
|
425
|
-
|
|
472
|
+
absorbFunctionItem(entry, item, true);
|
|
473
|
+
state.lastFunctionCall = entry;
|
|
474
|
+
state.lastOutputIndex = entry.outputIndex;
|
|
475
|
+
const args = resolveFunctionArgs(entry);
|
|
476
|
+
if (args === void 0) {
|
|
477
|
+
entry.doneSeen = true;
|
|
478
|
+
return [];
|
|
479
|
+
}
|
|
480
|
+
return completeFunctionCall(entry, args);
|
|
426
481
|
}
|
|
427
482
|
function recoverFromCompletedOutput(response, state) {
|
|
428
|
-
if (!Array.isArray(response.output)) return [];
|
|
429
483
|
const recovered = [];
|
|
430
|
-
response.output
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
484
|
+
if (Array.isArray(response.output)) {
|
|
485
|
+
response.output.forEach((item, index) => {
|
|
486
|
+
if (!isRecord(item) || typeof item.type !== "string") return;
|
|
487
|
+
if (item.type === "message" && !state.textDeltaForwarded) {
|
|
488
|
+
recovered.push(...synthesizeMessage(item, index, state));
|
|
489
|
+
} else if (item.type === "function_call") {
|
|
490
|
+
recovered.push(...synthesizeFunctionCall(item, index, state));
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
for (const entry of state.functionCalls) {
|
|
495
|
+
if (entry.done || !entry.doneSeen) continue;
|
|
496
|
+
recovered.push(normalizeErrorEvent({
|
|
497
|
+
error: {
|
|
498
|
+
type: "invalid_response",
|
|
499
|
+
code: "incomplete_function_call",
|
|
500
|
+
message: `Provider ended the response without arguments for function call "${entry.callId}"${entry.name ? ` (${entry.name})` : ""}.`
|
|
501
|
+
}
|
|
502
|
+
}));
|
|
503
|
+
}
|
|
438
504
|
return recovered;
|
|
439
505
|
}
|
|
440
506
|
function normalizeResponsesLiteEvent(event, state) {
|
|
@@ -450,19 +516,40 @@ function normalizeResponsesLiteEvent(event, state) {
|
|
|
450
516
|
return [{ ...event, output_index: outputIndex, item: { ...event.item, id } }];
|
|
451
517
|
}
|
|
452
518
|
if (event.item.type === "function_call") {
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
519
|
+
const entry = resolveFunctionCall(state, {
|
|
520
|
+
itemId: asString(event.item.id),
|
|
521
|
+
callId: asString(event.item.call_id),
|
|
522
|
+
outputIndex
|
|
523
|
+
});
|
|
524
|
+
absorbFunctionItem(entry, event.item, false);
|
|
525
|
+
entry.outputIndex = outputIndex;
|
|
526
|
+
entry.added = true;
|
|
527
|
+
state.lastFunctionCall = entry;
|
|
528
|
+
return [{ ...event, output_index: outputIndex, item: functionItemPayload(entry, { arguments: "" }) }];
|
|
456
529
|
}
|
|
457
530
|
return [{ ...event, output_index: outputIndex }];
|
|
458
531
|
}
|
|
459
532
|
if (event.type === "response.output_item.done" && isRecord(event.item)) {
|
|
460
533
|
const outputIndex = typeof event.output_index === "number" ? event.output_index : state.lastOutputIndex;
|
|
461
534
|
if (event.item.type === "function_call") {
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
535
|
+
const entry = resolveFunctionCall(state, {
|
|
536
|
+
itemId: asString(event.item.id),
|
|
537
|
+
callId: asString(event.item.call_id),
|
|
538
|
+
outputIndex
|
|
539
|
+
});
|
|
540
|
+
absorbFunctionItem(entry, event.item, true);
|
|
541
|
+
entry.outputIndex = outputIndex;
|
|
542
|
+
entry.doneSeen = true;
|
|
543
|
+
state.lastFunctionCall = entry;
|
|
544
|
+
state.lastOutputIndex = outputIndex;
|
|
545
|
+
const args = resolveFunctionArgs(entry);
|
|
546
|
+
if (args === void 0 || !entry.name) return [];
|
|
547
|
+
if (entry.done) return [];
|
|
548
|
+
const events = [];
|
|
549
|
+
if (!entry.added) events.push(functionAddedEvent(entry));
|
|
550
|
+
events.push({ ...event, output_index: outputIndex, item: functionItemPayload(entry, { arguments: args, status: "completed" }) });
|
|
551
|
+
entry.done = true;
|
|
552
|
+
return events;
|
|
466
553
|
}
|
|
467
554
|
if (event.item.type === "message") {
|
|
468
555
|
const id = asString(event.item.id) ?? state.lastMessageItemId ?? nextId(state, "msg");
|
|
@@ -489,12 +576,16 @@ function normalizeResponsesLiteEvent(event, state) {
|
|
|
489
576
|
return events;
|
|
490
577
|
}
|
|
491
578
|
if (event.type === "response.function_call_arguments.delta") {
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
579
|
+
const entry = resolveFunctionCall(state, {
|
|
580
|
+
itemId: asString(event.item_id),
|
|
581
|
+
outputIndex: typeof event.output_index === "number" ? event.output_index : void 0
|
|
582
|
+
});
|
|
583
|
+
const delta = typeof event.delta === "string" ? event.delta : "";
|
|
584
|
+
entry.args += delta;
|
|
585
|
+
entry.deltaForwarded = true;
|
|
586
|
+
state.lastFunctionCall = entry;
|
|
587
|
+
state.lastOutputIndex = entry.outputIndex;
|
|
588
|
+
return [{ ...event, item_id: entry.itemId, output_index: entry.outputIndex, delta }];
|
|
498
589
|
}
|
|
499
590
|
if (event.type === "response.completed" || event.type === "response.incomplete") {
|
|
500
591
|
const response = isRecord(event.response) ? event.response : {};
|
|
@@ -922,10 +1013,11 @@ async function createLanguageModel(spec) {
|
|
|
922
1013
|
return model;
|
|
923
1014
|
}
|
|
924
1015
|
var ANTHROPIC_EFFORT_LEVELS = ["low", "medium", "high"];
|
|
925
|
-
var OPENAI_EFFORT_LEVELS = ["low", "medium", "high"
|
|
1016
|
+
var OPENAI_EFFORT_LEVELS = ["low", "medium", "high"];
|
|
926
1017
|
var GEMINI_EFFORT_LEVELS = ["low", "medium", "high"];
|
|
927
1018
|
var MISTRAL_EFFORT_LEVELS = ["high", "off"];
|
|
928
|
-
var
|
|
1019
|
+
var XAI_CHAT_EFFORT_LEVELS = ["low", "high"];
|
|
1020
|
+
var XAI_RESPONSES_EFFORT_LEVELS = ["low", "medium", "high"];
|
|
929
1021
|
var OPENROUTER_EFFORT_LEVELS = ["none", "minimal", "low", "medium", "high", "xhigh"];
|
|
930
1022
|
var DEEPSEEK_EFFORT_LEVELS = ["high", "max", "off"];
|
|
931
1023
|
var GLM_52_EFFORT_LEVELS = ["high", "xhigh"];
|
|
@@ -937,6 +1029,15 @@ var EMPTY_REASONING = {
|
|
|
937
1029
|
source: "none",
|
|
938
1030
|
confidence: "inferred"
|
|
939
1031
|
};
|
|
1032
|
+
var GEMINI_25_BUDGETS = {
|
|
1033
|
+
low: 1024,
|
|
1034
|
+
medium: 4096,
|
|
1035
|
+
high: 8192,
|
|
1036
|
+
xhigh: 16384,
|
|
1037
|
+
max: 16384,
|
|
1038
|
+
minimal: 512,
|
|
1039
|
+
none: 0
|
|
1040
|
+
};
|
|
940
1041
|
function isClaudeReasoningModel(modelId) {
|
|
941
1042
|
const lower = modelId.toLowerCase();
|
|
942
1043
|
if (!lower.startsWith("claude-")) return false;
|
|
@@ -951,6 +1052,10 @@ function isGeminiReasoningModel(modelId) {
|
|
|
951
1052
|
const lower = modelId.toLowerCase();
|
|
952
1053
|
return lower.startsWith("gemini-2.5-") || lower.startsWith("gemini-3") || lower.startsWith("gemini-3.");
|
|
953
1054
|
}
|
|
1055
|
+
function isGemini3Model(modelId) {
|
|
1056
|
+
const lower = modelId.toLowerCase();
|
|
1057
|
+
return lower.startsWith("gemini-3") || lower.startsWith("gemini-3.");
|
|
1058
|
+
}
|
|
954
1059
|
function isMistralReasoningModel(modelId) {
|
|
955
1060
|
const lower = modelId.toLowerCase();
|
|
956
1061
|
return lower.startsWith("mistral-") || lower.startsWith("magistral-") || lower.startsWith("ministral-") || lower.includes("reasoning");
|
|
@@ -983,6 +1088,9 @@ function isGlm52ReasoningModel(modelId) {
|
|
|
983
1088
|
const lower = modelId.toLowerCase();
|
|
984
1089
|
return lower === "glm-5.2" || lower === "z-ai/glm-5.2" || lower === "zai/glm-5.2" || lower === "zai-org/glm-5.2" || lower === "zai-org/glm5.2" || lower === "glm5.2";
|
|
985
1090
|
}
|
|
1091
|
+
function toCamelCase(str) {
|
|
1092
|
+
return str.replace(/[-_]([a-z])/g, (_, g) => g.toUpperCase());
|
|
1093
|
+
}
|
|
986
1094
|
function hasSupportedParameter(metadata, param) {
|
|
987
1095
|
return (metadata?.supportedParameters ?? []).some((p) => p === param);
|
|
988
1096
|
}
|
|
@@ -1018,7 +1126,179 @@ function openRouterReasoningCapabilities(metadata) {
|
|
|
1018
1126
|
}
|
|
1019
1127
|
return EMPTY_REASONING;
|
|
1020
1128
|
}
|
|
1129
|
+
function mapCodexEffortToDeepSeek(effort) {
|
|
1130
|
+
switch (effort) {
|
|
1131
|
+
case "off":
|
|
1132
|
+
case "none":
|
|
1133
|
+
return "off";
|
|
1134
|
+
case "low":
|
|
1135
|
+
case "medium":
|
|
1136
|
+
case "high":
|
|
1137
|
+
return "high";
|
|
1138
|
+
case "xhigh":
|
|
1139
|
+
case "max":
|
|
1140
|
+
return "max";
|
|
1141
|
+
default:
|
|
1142
|
+
if (effort === "high" || effort === "max") return effort;
|
|
1143
|
+
return void 0;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
function deepSeekEffortProviderOptions(effort) {
|
|
1147
|
+
const mapped = mapCodexEffortToDeepSeek(effort);
|
|
1148
|
+
if (!mapped) return void 0;
|
|
1149
|
+
const thinking = { type: mapped === "off" ? "disabled" : "enabled" };
|
|
1150
|
+
const spread = { thinking };
|
|
1151
|
+
if (mapped === "off") {
|
|
1152
|
+
return {
|
|
1153
|
+
deepseek: spread,
|
|
1154
|
+
openaiCompatible: spread
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
return {
|
|
1158
|
+
openaiCompatible: { reasoningEffort: mapped, ...spread },
|
|
1159
|
+
deepseek: spread
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
function mapCodexEffortToAnthropic(effort) {
|
|
1163
|
+
switch (effort) {
|
|
1164
|
+
case "none":
|
|
1165
|
+
case "minimal":
|
|
1166
|
+
case "low":
|
|
1167
|
+
return "low";
|
|
1168
|
+
case "medium":
|
|
1169
|
+
return "medium";
|
|
1170
|
+
case "high":
|
|
1171
|
+
case "xhigh":
|
|
1172
|
+
case "max":
|
|
1173
|
+
return effort === "xhigh" ? "high" : effort === "max" ? "max" : "high";
|
|
1174
|
+
default:
|
|
1175
|
+
if (ANTHROPIC_EFFORT_LEVELS.includes(effort)) {
|
|
1176
|
+
return effort;
|
|
1177
|
+
}
|
|
1178
|
+
return void 0;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
var OPENAI_MODEL_REASONING = {
|
|
1182
|
+
"gpt-5-pro": { levels: ["high"], defaultLevel: "high" },
|
|
1183
|
+
"gpt-5.1": { levels: ["none", "low", "medium", "high"], defaultLevel: "none" },
|
|
1184
|
+
"gpt-5.1-codex-max": { levels: ["low", "medium", "high", "xhigh"], defaultLevel: "medium" },
|
|
1185
|
+
"gpt-5.2": { levels: ["none", "low", "medium", "high", "xhigh"], defaultLevel: "none" },
|
|
1186
|
+
"gpt-5.2-codex": { levels: ["low", "medium", "high", "xhigh"], defaultLevel: "medium" },
|
|
1187
|
+
"gpt-5.2-pro": { levels: ["medium", "high", "xhigh"], defaultLevel: "medium" },
|
|
1188
|
+
"gpt-5.3-codex": { levels: ["low", "medium", "high", "xhigh"], defaultLevel: "medium" },
|
|
1189
|
+
"gpt-5.4": { levels: ["none", "low", "medium", "high", "xhigh"], defaultLevel: "none" },
|
|
1190
|
+
"gpt-5.4-mini": { levels: ["none", "low", "medium", "high", "xhigh"], defaultLevel: "none" },
|
|
1191
|
+
"gpt-5.4-nano": { levels: ["none", "low", "medium", "high", "xhigh"], defaultLevel: "none" },
|
|
1192
|
+
"gpt-5.4-pro": { levels: ["medium", "high", "xhigh"], defaultLevel: "medium" },
|
|
1193
|
+
"gpt-5.5": { levels: ["none", "low", "medium", "high", "xhigh"], defaultLevel: "medium" },
|
|
1194
|
+
"gpt-5.5-pro": { levels: ["medium", "high", "xhigh"], defaultLevel: "high" },
|
|
1195
|
+
"gpt-5.6": { levels: ["none", "low", "medium", "high", "xhigh", "max"], defaultLevel: "medium" },
|
|
1196
|
+
"gpt-5.6-luna": { levels: ["none", "low", "medium", "high", "xhigh", "max"], defaultLevel: "medium" },
|
|
1197
|
+
"gpt-5.6-sol": { levels: ["none", "low", "medium", "high", "xhigh", "max"], defaultLevel: "medium" },
|
|
1198
|
+
"gpt-5.6-terra": { levels: ["none", "low", "medium", "high", "xhigh", "max"], defaultLevel: "medium" }
|
|
1199
|
+
};
|
|
1200
|
+
var OPENAI_NON_REASONING_MODELS = /* @__PURE__ */ new Set([
|
|
1201
|
+
"chat-latest",
|
|
1202
|
+
"gpt-5-chat-latest",
|
|
1203
|
+
"gpt-5.1-chat-latest",
|
|
1204
|
+
"gpt-5.2-chat-latest",
|
|
1205
|
+
"gpt-5.3-chat-latest"
|
|
1206
|
+
]);
|
|
1207
|
+
var OPENAI_DATED_SNAPSHOT_SUFFIX = /-\d{4}-\d{2}-\d{2}$/;
|
|
1208
|
+
function canonicalOpenAiModelId(modelId, metadata) {
|
|
1209
|
+
return (metadata?.upstreamModelId ?? modelId ?? "").toLowerCase();
|
|
1210
|
+
}
|
|
1211
|
+
function openAiReasoningProfile(modelId, metadata) {
|
|
1212
|
+
const id = canonicalOpenAiModelId(modelId, metadata);
|
|
1213
|
+
if (!id) return void 0;
|
|
1214
|
+
return OPENAI_MODEL_REASONING[id] ?? OPENAI_MODEL_REASONING[id.replace(OPENAI_DATED_SNAPSHOT_SUFFIX, "")];
|
|
1215
|
+
}
|
|
1216
|
+
function openAiModelReasons(modelId, metadata) {
|
|
1217
|
+
const id = canonicalOpenAiModelId(modelId, metadata);
|
|
1218
|
+
if (OPENAI_NON_REASONING_MODELS.has(id.replace(OPENAI_DATED_SNAPSHOT_SUFFIX, ""))) return false;
|
|
1219
|
+
return !!openAiReasoningProfile(modelId, metadata) || modelPrefersResponsesApi(id) || !!metadata?.reasoning;
|
|
1220
|
+
}
|
|
1221
|
+
function mapCodexEffortToOpenAI(effort, allowed) {
|
|
1222
|
+
return allowed.includes(effort) ? effort : void 0;
|
|
1223
|
+
}
|
|
1224
|
+
function mapCodexEffortToOpenAICompatible(effort) {
|
|
1225
|
+
if (effort === "xhigh") return "high";
|
|
1226
|
+
const allowed = ["low", "medium", "high"];
|
|
1227
|
+
return allowed.includes(effort) ? effort : void 0;
|
|
1228
|
+
}
|
|
1229
|
+
function mapCodexEffortToGlm52(effort) {
|
|
1230
|
+
switch (effort) {
|
|
1231
|
+
case "high":
|
|
1232
|
+
return "high";
|
|
1233
|
+
case "xhigh":
|
|
1234
|
+
case "max":
|
|
1235
|
+
return "max";
|
|
1236
|
+
default:
|
|
1237
|
+
return void 0;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
function mapCodexEffortToXai(effort, supportsMedium) {
|
|
1241
|
+
switch (effort) {
|
|
1242
|
+
case "low":
|
|
1243
|
+
return "low";
|
|
1244
|
+
case "medium":
|
|
1245
|
+
return supportsMedium ? "medium" : void 0;
|
|
1246
|
+
case "high":
|
|
1247
|
+
case "xhigh":
|
|
1248
|
+
case "max":
|
|
1249
|
+
return "high";
|
|
1250
|
+
default:
|
|
1251
|
+
return void 0;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
function mapCodexEffortToGeminiLevel(effort) {
|
|
1255
|
+
switch (effort) {
|
|
1256
|
+
case "none":
|
|
1257
|
+
case "minimal":
|
|
1258
|
+
case "low":
|
|
1259
|
+
return "low";
|
|
1260
|
+
case "medium":
|
|
1261
|
+
return "medium";
|
|
1262
|
+
case "high":
|
|
1263
|
+
case "xhigh":
|
|
1264
|
+
case "max":
|
|
1265
|
+
return "high";
|
|
1266
|
+
default:
|
|
1267
|
+
return GEMINI_EFFORT_LEVELS.includes(effort) ? effort : void 0;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
function mapCodexEffortToGeminiBudget(effort) {
|
|
1271
|
+
const direct = GEMINI_25_BUDGETS[effort];
|
|
1272
|
+
if (direct !== void 0) return direct > 0 ? direct : void 0;
|
|
1273
|
+
const level = mapCodexEffortToGeminiLevel(effort);
|
|
1274
|
+
if (!level) return void 0;
|
|
1275
|
+
return GEMINI_25_BUDGETS[level];
|
|
1276
|
+
}
|
|
1277
|
+
function withMappableLevels(caps, npm, modelId, metadata) {
|
|
1278
|
+
if (caps.mode !== "controllable") return caps;
|
|
1279
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1280
|
+
const levels = caps.levels.filter((level) => {
|
|
1281
|
+
const mapped = effortProviderOptions(npm, level, modelId, metadata);
|
|
1282
|
+
if (mapped === void 0) return false;
|
|
1283
|
+
const wire = JSON.stringify(mapped);
|
|
1284
|
+
if (seen.has(wire)) return false;
|
|
1285
|
+
seen.add(wire);
|
|
1286
|
+
return true;
|
|
1287
|
+
});
|
|
1288
|
+
if (levels.length === caps.levels.length) return caps;
|
|
1289
|
+
if (levels.length === 0) {
|
|
1290
|
+
return { ...caps, levels: [], defaultLevel: "", mode: "internal-only" };
|
|
1291
|
+
}
|
|
1292
|
+
return {
|
|
1293
|
+
...caps,
|
|
1294
|
+
levels,
|
|
1295
|
+
defaultLevel: levels.includes(caps.defaultLevel) ? caps.defaultLevel : levels[levels.length - 1]
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1021
1298
|
function getReasoningCapabilities(npm, modelId, metadata) {
|
|
1299
|
+
return withMappableLevels(resolveRawReasoningCapabilities(npm, modelId, metadata), npm, modelId, metadata);
|
|
1300
|
+
}
|
|
1301
|
+
function resolveRawReasoningCapabilities(npm, modelId, metadata) {
|
|
1022
1302
|
const id = modelId.toLowerCase();
|
|
1023
1303
|
if (isOpenRouterRoute(npm, metadata)) {
|
|
1024
1304
|
return openRouterReasoningCapabilities(metadata);
|
|
@@ -1039,15 +1319,18 @@ function getReasoningCapabilities(npm, modelId, metadata) {
|
|
|
1039
1319
|
return EMPTY_REASONING;
|
|
1040
1320
|
}
|
|
1041
1321
|
if (npm === "@ai-sdk/openai" || npm === "@ai-sdk/azure") {
|
|
1042
|
-
const
|
|
1043
|
-
|
|
1322
|
+
const canonicalId = canonicalOpenAiModelId(modelId, metadata);
|
|
1323
|
+
const profile = openAiReasoningProfile(modelId, metadata);
|
|
1324
|
+
const prefersResponses = modelPrefersResponsesApi(canonicalId);
|
|
1325
|
+
if (openAiModelReasons(modelId, metadata) && shouldUseOpenAiResponsesEndpoint(canonicalId)) {
|
|
1326
|
+
const levels = profile?.levels ?? [...OPENAI_EFFORT_LEVELS];
|
|
1044
1327
|
return {
|
|
1045
|
-
levels: [...
|
|
1046
|
-
defaultLevel: "medium",
|
|
1328
|
+
levels: [...levels],
|
|
1329
|
+
defaultLevel: profile?.defaultLevel ?? (levels.includes("medium") ? "medium" : levels[levels.length - 1]),
|
|
1047
1330
|
supportsSummaries: true,
|
|
1331
|
+
source: profile || prefersResponses ? "provider-rule" : "model-metadata",
|
|
1332
|
+
confidence: profile || prefersResponses ? "documented" : "inferred",
|
|
1048
1333
|
mode: "controllable",
|
|
1049
|
-
source: prefersResponses ? "provider-rule" : "model-metadata",
|
|
1050
|
-
confidence: prefersResponses ? "documented" : "inferred",
|
|
1051
1334
|
wireFormat: { kind: "openai-reasoning-effort" }
|
|
1052
1335
|
};
|
|
1053
1336
|
}
|
|
@@ -1083,7 +1366,7 @@ function getReasoningCapabilities(npm, modelId, metadata) {
|
|
|
1083
1366
|
}
|
|
1084
1367
|
if (npm === "@ai-sdk/xai") {
|
|
1085
1368
|
if (isXaiReasoningEffortModel(modelId)) {
|
|
1086
|
-
const levels = modelPrefersResponsesApi(modelId) ? [
|
|
1369
|
+
const levels = modelPrefersResponsesApi(modelId) ? [...XAI_RESPONSES_EFFORT_LEVELS] : [...XAI_CHAT_EFFORT_LEVELS];
|
|
1087
1370
|
return {
|
|
1088
1371
|
levels,
|
|
1089
1372
|
defaultLevel: xaiDefaultReasoningEffort(modelId),
|
|
@@ -1164,6 +1447,91 @@ function getReasoningCapabilities(npm, modelId, metadata) {
|
|
|
1164
1447
|
}
|
|
1165
1448
|
return EMPTY_REASONING;
|
|
1166
1449
|
}
|
|
1450
|
+
function effortProviderOptions(npm, effort, modelId, metadata) {
|
|
1451
|
+
if (!effort) return void 0;
|
|
1452
|
+
if (isOpenRouterRoute(npm, metadata)) {
|
|
1453
|
+
const caps = openRouterReasoningCapabilities(metadata);
|
|
1454
|
+
if (caps.mode !== "controllable") return void 0;
|
|
1455
|
+
const allowed = new Set(OPENROUTER_EFFORT_LEVELS);
|
|
1456
|
+
const mapped = allowed.has(effort) ? effort : effort === "max" ? "xhigh" : void 0;
|
|
1457
|
+
return mapped ? { openrouter: { reasoning: { effort: mapped, exclude: false } } } : void 0;
|
|
1458
|
+
}
|
|
1459
|
+
if (npm === "@ai-sdk/openai" || npm === "@ai-sdk/azure") {
|
|
1460
|
+
if (!modelId || !shouldUseOpenAiResponsesEndpoint(canonicalOpenAiModelId(modelId, metadata))) return void 0;
|
|
1461
|
+
if (!openAiModelReasons(modelId, metadata)) return void 0;
|
|
1462
|
+
const allowed = openAiReasoningProfile(modelId, metadata)?.levels ?? OPENAI_EFFORT_LEVELS;
|
|
1463
|
+
const reasoningEffort = mapCodexEffortToOpenAI(effort, allowed);
|
|
1464
|
+
return reasoningEffort ? { openai: { reasoningEffort } } : void 0;
|
|
1465
|
+
}
|
|
1466
|
+
if (npm === "@ai-sdk/xai") {
|
|
1467
|
+
if (!modelId || !isXaiReasoningEffortModel(modelId)) return void 0;
|
|
1468
|
+
const reasoningEffort = mapCodexEffortToXai(effort, modelPrefersResponsesApi(modelId));
|
|
1469
|
+
return reasoningEffort ? { xai: { reasoningEffort } } : void 0;
|
|
1470
|
+
}
|
|
1471
|
+
if (npm === "@ai-sdk/anthropic" || npm === VERTEX_ANTHROPIC_NPM) {
|
|
1472
|
+
if (!modelId || !isClaudeReasoningModel(modelId)) return void 0;
|
|
1473
|
+
const mapped = mapCodexEffortToAnthropic(effort);
|
|
1474
|
+
return mapped ? { anthropic: { thinking: { type: "adaptive", effort: mapped } } } : void 0;
|
|
1475
|
+
}
|
|
1476
|
+
if (npm === "@ai-sdk/google") {
|
|
1477
|
+
const id = modelId ?? "";
|
|
1478
|
+
if (isGemini3Model(id)) {
|
|
1479
|
+
const thinkingLevel = mapCodexEffortToGeminiLevel(effort);
|
|
1480
|
+
return thinkingLevel ? { google: { thinkingConfig: { thinkingLevel, includeThoughts: true } } } : void 0;
|
|
1481
|
+
}
|
|
1482
|
+
const thinkingBudget = mapCodexEffortToGeminiBudget(effort);
|
|
1483
|
+
return thinkingBudget ? { google: { thinkingConfig: { thinkingBudget, includeThoughts: true } } } : void 0;
|
|
1484
|
+
}
|
|
1485
|
+
if (npm === "@ai-sdk/mistral") {
|
|
1486
|
+
if (!modelId || !isMistralReasoningModel(modelId)) return void 0;
|
|
1487
|
+
const reasoningEffort = effort === "off" || effort === "none" ? "none" : "high";
|
|
1488
|
+
return { mistral: { reasoningEffort } };
|
|
1489
|
+
}
|
|
1490
|
+
if (npm === "@ai-sdk/openai-compatible" || npm === "@ai-sdk/openai") {
|
|
1491
|
+
if (!modelId) return void 0;
|
|
1492
|
+
if (isDeepSeekReasoningModel(modelId)) {
|
|
1493
|
+
return deepSeekEffortProviderOptions(effort);
|
|
1494
|
+
}
|
|
1495
|
+
if (isKimiReasoningModel(modelId)) {
|
|
1496
|
+
const reasoningEffort = mapCodexEffortToOpenAICompatible(effort);
|
|
1497
|
+
if (reasoningEffort) {
|
|
1498
|
+
const key = metadata?.providerId ? toCamelCase(metadata.providerId) : "openaiCompatible";
|
|
1499
|
+
return { [key]: { reasoningEffort } };
|
|
1500
|
+
}
|
|
1501
|
+
return void 0;
|
|
1502
|
+
}
|
|
1503
|
+
if (isGlm52ReasoningModel(modelId)) {
|
|
1504
|
+
const reasoningEffort = mapCodexEffortToGlm52(effort);
|
|
1505
|
+
if (reasoningEffort) {
|
|
1506
|
+
const key = metadata?.providerId ? toCamelCase(metadata.providerId) : "openaiCompatible";
|
|
1507
|
+
return { [key]: { reasoningEffort } };
|
|
1508
|
+
}
|
|
1509
|
+
return void 0;
|
|
1510
|
+
}
|
|
1511
|
+
if (hasSupportedParameter(metadata, "reasoning_effort")) {
|
|
1512
|
+
const reasoningEffort = mapCodexEffortToOpenAICompatible(effort);
|
|
1513
|
+
return reasoningEffort ? { openai: { reasoningEffort }, openaiCompatible: { reasoningEffort } } : void 0;
|
|
1514
|
+
}
|
|
1515
|
+
if (hasSupportedParameter(metadata, "reasoning")) {
|
|
1516
|
+
const allowed = new Set(OPENROUTER_EFFORT_LEVELS);
|
|
1517
|
+
const mapped = allowed.has(effort) ? effort : effort === "max" ? "xhigh" : void 0;
|
|
1518
|
+
return mapped ? { openrouter: { reasoning: { effort: mapped, exclude: false } } } : void 0;
|
|
1519
|
+
}
|
|
1520
|
+
return void 0;
|
|
1521
|
+
}
|
|
1522
|
+
return void 0;
|
|
1523
|
+
}
|
|
1524
|
+
function deepMergeProviderOptions(a, b) {
|
|
1525
|
+
if (!a && !b) return void 0;
|
|
1526
|
+
if (!a) return b;
|
|
1527
|
+
if (!b) return a;
|
|
1528
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
1529
|
+
const out = {};
|
|
1530
|
+
for (const key of keys) {
|
|
1531
|
+
out[key] = { ...a[key] ?? {}, ...b[key] ?? {} };
|
|
1532
|
+
}
|
|
1533
|
+
return out;
|
|
1534
|
+
}
|
|
1167
1535
|
|
|
1168
1536
|
// src/registry/io.ts
|
|
1169
1537
|
import {
|
|
@@ -1379,6 +1747,7 @@ var DEFAULT_RETRYABLE = {
|
|
|
1379
1747
|
CREDENTIAL_UNAVAILABLE: false,
|
|
1380
1748
|
OAUTH_REFRESH_FAILED: true,
|
|
1381
1749
|
UNSUPPORTED_MODEL: false,
|
|
1750
|
+
UNSUPPORTED_REASONING_LEVEL: false,
|
|
1382
1751
|
UNSUPPORTED_REGISTRY_VERSION: false,
|
|
1383
1752
|
PROVIDER_LOAD_FAILED: true
|
|
1384
1753
|
};
|
|
@@ -1410,6 +1779,80 @@ function isRelayCoreError(err) {
|
|
|
1410
1779
|
return err instanceof RelayCoreError;
|
|
1411
1780
|
}
|
|
1412
1781
|
|
|
1782
|
+
// src/core/reasoning.ts
|
|
1783
|
+
var RELAY_REASONING_LEVELS = [
|
|
1784
|
+
"off",
|
|
1785
|
+
"none",
|
|
1786
|
+
"minimal",
|
|
1787
|
+
"low",
|
|
1788
|
+
"medium",
|
|
1789
|
+
"high",
|
|
1790
|
+
"xhigh",
|
|
1791
|
+
"max"
|
|
1792
|
+
];
|
|
1793
|
+
function isRelayReasoningLevel(value) {
|
|
1794
|
+
return typeof value === "string" && RELAY_REASONING_LEVELS.includes(value);
|
|
1795
|
+
}
|
|
1796
|
+
function reasoningNpmForRoute(provider, model) {
|
|
1797
|
+
if (model.modelFormat === "cloud-code") return "@ai-sdk/google";
|
|
1798
|
+
return model.npm ?? provider.api.npm ?? "";
|
|
1799
|
+
}
|
|
1800
|
+
function resolveReasoningProviderOptions(level, provider, model, routeId) {
|
|
1801
|
+
if (!isRelayReasoningLevel(level)) {
|
|
1802
|
+
throw new RelayCoreError(
|
|
1803
|
+
"UNSUPPORTED_REASONING_LEVEL",
|
|
1804
|
+
`Unknown reasoning level "${String(level)}" \u2014 expected one of: ${RELAY_REASONING_LEVELS.join(", ")}.`,
|
|
1805
|
+
{ providerId: provider.id, routeId }
|
|
1806
|
+
);
|
|
1807
|
+
}
|
|
1808
|
+
const npm = reasoningNpmForRoute(provider, model);
|
|
1809
|
+
const upstreamModelId = model.upstreamModelId ?? model.id;
|
|
1810
|
+
const metadata = {
|
|
1811
|
+
providerId: provider.id,
|
|
1812
|
+
upstreamModelId,
|
|
1813
|
+
...model.apiUrl ?? provider.api.url ? { apiBaseUrl: model.apiUrl ?? provider.api.url } : {},
|
|
1814
|
+
...model.supportedParameters ? { supportedParameters: model.supportedParameters } : {},
|
|
1815
|
+
...model.reasoning !== void 0 ? { reasoning: model.reasoning } : {},
|
|
1816
|
+
...model.interleavedReasoningField ? { interleavedReasoningField: model.interleavedReasoningField } : {}
|
|
1817
|
+
};
|
|
1818
|
+
const caps = getReasoningCapabilities(npm, upstreamModelId, metadata);
|
|
1819
|
+
if (caps.mode !== "controllable" || !caps.levels.includes(level)) {
|
|
1820
|
+
const available = caps.levels.length > 0 ? caps.levels.join(", ") : "none";
|
|
1821
|
+
throw new RelayCoreError(
|
|
1822
|
+
"UNSUPPORTED_REASONING_LEVEL",
|
|
1823
|
+
`Model "${model.id}" on provider "${provider.name}" does not support reasoning level "${level}" \u2014 available levels: ${available}. See capabilities.reasoningLevels from listRelayModels().`,
|
|
1824
|
+
{ providerId: provider.id, routeId }
|
|
1825
|
+
);
|
|
1826
|
+
}
|
|
1827
|
+
const resolved = effortProviderOptions(npm, level, upstreamModelId, metadata);
|
|
1828
|
+
if (!resolved) {
|
|
1829
|
+
throw new RelayCoreError(
|
|
1830
|
+
"UNSUPPORTED_REASONING_LEVEL",
|
|
1831
|
+
`Model "${model.id}" on provider "${provider.name}" advertises reasoning level "${level}" but Relay has no request mapping for it \u2014 this is a relay-ai bug, please report it.`,
|
|
1832
|
+
{ providerId: provider.id, routeId }
|
|
1833
|
+
);
|
|
1834
|
+
}
|
|
1835
|
+
return resolved;
|
|
1836
|
+
}
|
|
1837
|
+
async function withReasoningProviderOptions(model, providerOptions) {
|
|
1838
|
+
const { wrapLanguageModel: wrapLanguageModel2 } = await import("ai");
|
|
1839
|
+
return wrapLanguageModel2({
|
|
1840
|
+
// `LanguageModel` also admits a bare model-id string and the legacy v2
|
|
1841
|
+
// interface; everything Core builds is a concrete current-spec model.
|
|
1842
|
+
model,
|
|
1843
|
+
middleware: {
|
|
1844
|
+
specificationVersion: "v3",
|
|
1845
|
+
transformParams: async ({ params }) => ({
|
|
1846
|
+
...params,
|
|
1847
|
+
providerOptions: deepMergeProviderOptions(
|
|
1848
|
+
providerOptions,
|
|
1849
|
+
params.providerOptions
|
|
1850
|
+
)
|
|
1851
|
+
})
|
|
1852
|
+
}
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1413
1856
|
// src/core/route-id.ts
|
|
1414
1857
|
var SEPARATOR = "::";
|
|
1415
1858
|
function toRelayRouteId(providerId, modelId) {
|
|
@@ -1447,7 +1890,7 @@ function loadCoreRegistry(path) {
|
|
|
1447
1890
|
}
|
|
1448
1891
|
function mapReasoning(provider, model) {
|
|
1449
1892
|
const base = { tools: "unknown", vision: "unknown" };
|
|
1450
|
-
const npm =
|
|
1893
|
+
const npm = reasoningNpmForRoute(provider, model);
|
|
1451
1894
|
const upstreamModelId = model.upstreamModelId ?? model.id;
|
|
1452
1895
|
try {
|
|
1453
1896
|
const caps = getReasoningCapabilities(npm, upstreamModelId, {
|
|
@@ -1463,13 +1906,16 @@ function mapReasoning(provider, model) {
|
|
|
1463
1906
|
return { ...base, reasoning: "none" };
|
|
1464
1907
|
case "internal-only":
|
|
1465
1908
|
return { ...base, reasoning: "fixed" };
|
|
1466
|
-
case "controllable":
|
|
1909
|
+
case "controllable": {
|
|
1910
|
+
const levels = caps.levels.filter(isRelayReasoningLevel);
|
|
1911
|
+
if (levels.length === 0) return { ...base, reasoning: "fixed" };
|
|
1467
1912
|
return {
|
|
1468
1913
|
...base,
|
|
1469
1914
|
reasoning: "adjustable",
|
|
1470
|
-
reasoningLevels:
|
|
1471
|
-
defaultReasoningLevel: caps.defaultLevel
|
|
1915
|
+
reasoningLevels: levels,
|
|
1916
|
+
...isRelayReasoningLevel(caps.defaultLevel) ? { defaultReasoningLevel: caps.defaultLevel } : {}
|
|
1472
1917
|
};
|
|
1918
|
+
}
|
|
1473
1919
|
default:
|
|
1474
1920
|
return { ...base, reasoning: "unknown" };
|
|
1475
1921
|
}
|
|
@@ -2637,10 +3083,21 @@ function providerRefreshToken(providerId, authType, authRef) {
|
|
|
2637
3083
|
|
|
2638
3084
|
// src/core/antigravity-model.ts
|
|
2639
3085
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2640
|
-
var
|
|
2641
|
-
var
|
|
2642
|
-
var
|
|
3086
|
+
var CLOUD_CODE_BASES = ANTIGRAVITY_BASE_URLS.map((base) => base.replace(/\/+$/, ""));
|
|
3087
|
+
var CLOUD_CODE_BASE = CLOUD_CODE_BASES[0];
|
|
3088
|
+
var STREAM_URLS = CLOUD_CODE_BASES.map((base) => `${base}/${ANTIGRAVITY_API_VERSION}:streamGenerateContent?alt=sse`);
|
|
3089
|
+
var UNARY_URLS = CLOUD_CODE_BASES.map((base) => `${base}/${ANTIGRAVITY_API_VERSION}:generateContent`);
|
|
2643
3090
|
var SDK_BASE_URL = `${CLOUD_CODE_BASE}/v1beta`;
|
|
3091
|
+
var ENDPOINT_FAILOVER_STATUSES = /* @__PURE__ */ new Set([404, 408, 429]);
|
|
3092
|
+
function shouldTryNextEndpoint(status) {
|
|
3093
|
+
return ENDPOINT_FAILOVER_STATUSES.has(status) || status >= 500;
|
|
3094
|
+
}
|
|
3095
|
+
function discardResponse(response) {
|
|
3096
|
+
try {
|
|
3097
|
+
void response.body?.cancel();
|
|
3098
|
+
} catch {
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
2644
3101
|
function unwrapCloudCodeSsePayload(payload) {
|
|
2645
3102
|
const trimmed = payload.trim();
|
|
2646
3103
|
if (trimmed === "" || trimmed === "[DONE]") return payload;
|
|
@@ -2699,6 +3156,12 @@ function createCloudCodeSseUnwrapper() {
|
|
|
2699
3156
|
}
|
|
2700
3157
|
function createCloudCodeFetch(options, fetchImpl) {
|
|
2701
3158
|
let accessToken = options.accessToken;
|
|
3159
|
+
const debug = (msg) => {
|
|
3160
|
+
try {
|
|
3161
|
+
options.onDebug?.(`cloud-code: ${msg}`);
|
|
3162
|
+
} catch {
|
|
3163
|
+
}
|
|
3164
|
+
};
|
|
2702
3165
|
return async (input, init) => {
|
|
2703
3166
|
const url = requestUrl(input);
|
|
2704
3167
|
const streaming = url.includes("streamGenerateContent");
|
|
@@ -2714,35 +3177,66 @@ function createCloudCodeFetch(options, fetchImpl) {
|
|
|
2714
3177
|
request: geminiBody
|
|
2715
3178
|
};
|
|
2716
3179
|
const body = JSON.stringify(envelope);
|
|
2717
|
-
const
|
|
3180
|
+
const bodyByteLength = Buffer.byteLength(body, "utf8");
|
|
3181
|
+
const upstreamUrls = streaming ? STREAM_URLS : UNARY_URLS;
|
|
2718
3182
|
const doFetch = fetchImpl ?? ((input2, init2) => globalThis.fetch(input2, init2));
|
|
2719
|
-
const send = (token) =>
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
}
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
3183
|
+
const send = async (url2, token) => {
|
|
3184
|
+
try {
|
|
3185
|
+
return await doFetch(url2, {
|
|
3186
|
+
method: "POST",
|
|
3187
|
+
headers: {
|
|
3188
|
+
"Content-Type": "application/json",
|
|
3189
|
+
Authorization: `Bearer ${token}`,
|
|
3190
|
+
"User-Agent": ANTIGRAVITY_USER_AGENT
|
|
3191
|
+
},
|
|
3192
|
+
body,
|
|
3193
|
+
signal
|
|
3194
|
+
});
|
|
3195
|
+
} catch (err) {
|
|
3196
|
+
if (isAbortError(err, signal)) throw abortError(signal, err);
|
|
3197
|
+
throw err;
|
|
3198
|
+
}
|
|
3199
|
+
};
|
|
3200
|
+
const sendWithFailover = async (token, startIndex = 0) => {
|
|
3201
|
+
let lastError;
|
|
3202
|
+
for (let i = startIndex; i < upstreamUrls.length; i += 1) {
|
|
3203
|
+
const url2 = upstreamUrls[i];
|
|
3204
|
+
const isLast = i === upstreamUrls.length - 1;
|
|
3205
|
+
const where = `endpoint=${i + 1}/${upstreamUrls.length} host=${endpointHost(url2)}`;
|
|
3206
|
+
let response2;
|
|
2740
3207
|
try {
|
|
2741
|
-
|
|
3208
|
+
debug(`request ${where} kind=${streaming ? "stream" : "unary"} payloadBytes=${bodyByteLength}`);
|
|
3209
|
+
response2 = await send(url2, token);
|
|
2742
3210
|
} catch (err) {
|
|
2743
|
-
if (isAbortError(err, signal)) throw
|
|
2744
|
-
|
|
3211
|
+
if (isAbortError(err, signal)) throw err;
|
|
3212
|
+
lastError = err;
|
|
3213
|
+
debug(`network failure ${where} errorName=${errorName(err)}`);
|
|
2745
3214
|
}
|
|
3215
|
+
if (response2) {
|
|
3216
|
+
if (isLast || !shouldTryNextEndpoint(response2.status)) {
|
|
3217
|
+
debug(`response ${where} status=${response2.status}`);
|
|
3218
|
+
return { response: response2, url: url2, index: i };
|
|
3219
|
+
}
|
|
3220
|
+
discardResponse(response2);
|
|
3221
|
+
lastError = new Error(`Cloud Code Assist endpoint returned ${response2.status}`);
|
|
3222
|
+
debug(`retryable status=${response2.status} ${where} \u2014 trying next endpoint`);
|
|
3223
|
+
}
|
|
3224
|
+
if (signal?.aborted) throw abortError(signal);
|
|
3225
|
+
}
|
|
3226
|
+
throw lastError ?? new Error("All Cloud Code Assist endpoints failed");
|
|
3227
|
+
};
|
|
3228
|
+
const tokenUsed = accessToken;
|
|
3229
|
+
let { response, index: servedByIndex } = await sendWithFailover(tokenUsed);
|
|
3230
|
+
if (response.status === 401 && options.refreshToken && !signal?.aborted) {
|
|
3231
|
+
debug("status=401 \u2014 refreshing credential");
|
|
3232
|
+
const refreshed = await options.refreshToken().catch(() => null);
|
|
3233
|
+
if (refreshed && refreshed !== tokenUsed && !signal?.aborted) {
|
|
3234
|
+
accessToken = refreshed;
|
|
3235
|
+
discardResponse(response);
|
|
3236
|
+
({ response } = await sendWithFailover(refreshed, servedByIndex));
|
|
3237
|
+
debug(`retry after refresh status=${response.status}`);
|
|
3238
|
+
} else {
|
|
3239
|
+
debug(`refresh did not yield a new credential (refreshed=${refreshed ? "same" : "none"})`);
|
|
2746
3240
|
}
|
|
2747
3241
|
}
|
|
2748
3242
|
return adaptUpstreamResponse(response, streaming);
|
|
@@ -2757,6 +3251,17 @@ async function createAntigravityCloudCodeModel(options) {
|
|
|
2757
3251
|
});
|
|
2758
3252
|
return google(options.modelId);
|
|
2759
3253
|
}
|
|
3254
|
+
function endpointHost(url) {
|
|
3255
|
+
try {
|
|
3256
|
+
return new URL(url).host;
|
|
3257
|
+
} catch {
|
|
3258
|
+
return "unknown";
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
function errorName(err) {
|
|
3262
|
+
if (err instanceof Error) return err.name || "Error";
|
|
3263
|
+
return typeof err;
|
|
3264
|
+
}
|
|
2760
3265
|
function isWrappedCloudCodeBody(parsed) {
|
|
2761
3266
|
return !!parsed && typeof parsed === "object" && !Array.isArray(parsed) && "response" in parsed && parsed.response !== null && typeof parsed.response === "object";
|
|
2762
3267
|
}
|
|
@@ -2858,6 +3363,8 @@ async function createRelayModel(routeId, options) {
|
|
|
2858
3363
|
const { providerId, modelId } = parseRelayRouteId(routeId);
|
|
2859
3364
|
const registry = loadCoreRegistry();
|
|
2860
3365
|
const { provider, model } = findRoute(registry, providerId, modelId, routeId);
|
|
3366
|
+
const reasoningOptions = options?.reasoning === void 0 ? void 0 : resolveReasoningProviderOptions(options.reasoning, provider, model, routeId);
|
|
3367
|
+
const finish = (built) => reasoningOptions ? withReasoningProviderOptions(built, reasoningOptions) : Promise.resolve(built);
|
|
2861
3368
|
if (isAntigravityCloudCodeRoute(provider, model)) {
|
|
2862
3369
|
const apiKey2 = await resolveCredential(provider, routeId);
|
|
2863
3370
|
const providerData2 = await resolveProviderOAuthProviderData(provider.authRef);
|
|
@@ -2870,12 +3377,13 @@ async function createRelayModel(routeId, options) {
|
|
|
2870
3377
|
);
|
|
2871
3378
|
}
|
|
2872
3379
|
try {
|
|
2873
|
-
return await createAntigravityCloudCodeModel({
|
|
3380
|
+
return await finish(await createAntigravityCloudCodeModel({
|
|
2874
3381
|
modelId: model.upstreamModelId ?? model.id,
|
|
2875
3382
|
accessToken: apiKey2,
|
|
2876
3383
|
projectId,
|
|
2877
|
-
refreshToken: providerRefreshToken(provider.id, provider.authType, provider.authRef)
|
|
2878
|
-
|
|
3384
|
+
refreshToken: providerRefreshToken(provider.id, provider.authType, provider.authRef),
|
|
3385
|
+
...options?.onDebug ? { onDebug: options.onDebug } : {}
|
|
3386
|
+
}));
|
|
2879
3387
|
} catch (err) {
|
|
2880
3388
|
if (isRelayCoreError(err)) throw err;
|
|
2881
3389
|
throw new RelayCoreError(
|
|
@@ -2912,7 +3420,7 @@ async function createRelayModel(routeId, options) {
|
|
|
2912
3420
|
...options?.onDebug ? { onDebug: options.onDebug } : {}
|
|
2913
3421
|
};
|
|
2914
3422
|
try {
|
|
2915
|
-
return await createLanguageModel(spec);
|
|
3423
|
+
return await finish(await createLanguageModel(spec));
|
|
2916
3424
|
} catch (err) {
|
|
2917
3425
|
if (isRelayCoreError(err)) throw err;
|
|
2918
3426
|
throw new RelayCoreError(
|