@nick3/copilot-api 1.3.8 → 1.4.2
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/{accounts-manager-BGBtDChT.js → accounts-manager-CtZD9BhK.js} +3 -3
- package/dist/{accounts-manager-BGBtDChT.js.map → accounts-manager-CtZD9BhK.js.map} +1 -1
- package/dist/{auth-BvIHvhP9.js → auth-QjAXKuWY.js} +3 -3
- package/dist/{auth-BvIHvhP9.js.map → auth-QjAXKuWY.js.map} +1 -1
- package/dist/{check-usage-CiTcYDij.js → check-usage-DXujADko.js} +4 -4
- package/dist/{check-usage-CiTcYDij.js.map → check-usage-DXujADko.js.map} +1 -1
- package/dist/{get-copilot-token-CUT8hpgX.js → get-copilot-token-BMypymQn.js} +2 -2
- package/dist/{get-copilot-token-CUT8hpgX.js.map → get-copilot-token-BMypymQn.js.map} +1 -1
- package/dist/main.js +3 -3
- package/dist/{poll-access-token-Dnd_xK36.js → poll-access-token-BqarRUZn.js} +2 -2
- package/dist/{poll-access-token-Dnd_xK36.js.map → poll-access-token-BqarRUZn.js.map} +1 -1
- package/dist/{server-COWbIpDR.js → server-CKtDlwHW.js} +1058 -1061
- package/dist/server-CKtDlwHW.js.map +1 -0
- package/dist/{start-BbULwJ9B.js → start-CQKD78BE.js} +6 -6
- package/dist/{start-BbULwJ9B.js.map → start-CQKD78BE.js.map} +1 -1
- package/dist/{utils-BaoXuYkx.js → utils-Ce30L8HS.js} +34 -10
- package/dist/utils-Ce30L8HS.js.map +1 -0
- package/package.json +1 -1
- package/dist/server-COWbIpDR.js.map +0 -1
- package/dist/utils-BaoXuYkx.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { PATHS } from "./paths-DoT4SZ8f.js";
|
|
2
2
|
import { listAccountsFromRegistry } from "./accounts-registry-c7rs5Ed9.js";
|
|
3
|
-
import { HTTPError, accountFromState, cacheModels, copilotBaseUrl, copilotHeaders, forwardError, generateRequestIdFromPayload, getCopilotUsage, getRootSessionId, getUUID, isNullish, prepareForCompact, prepareInteractionHeaders, sleep, state } from "./utils-
|
|
4
|
-
import "./get-copilot-token-
|
|
5
|
-
import { PROVIDER_TYPE_ANTHROPIC, accountsManager, getAliasTargetSet, getConfig, getExtraPromptForModel, getModelAliases, getModelAliasesInfo, getModelRefreshIntervalMs, getProviderConfig, getReasoningEffortForModel, getSmallModel, isForceAgentEnabled, isFreeModelLoadBalancingEnabled, isMessageStartInputTokensFallbackEnabled, isMessagesApiEnabled, isResponsesApiContextManagementModel, mergeConfigWithDefaults, shouldCompactUseSmallModel } from "./accounts-manager-
|
|
3
|
+
import { HTTPError, accountFromState, cacheModels, copilotBaseUrl, copilotHeaders, forwardError, generateRequestIdFromPayload, getCopilotUsage, getRootSessionId, getUUID, isNullish, parseUserIdMetadata, prepareForCompact, prepareInteractionHeaders, sleep, state } from "./utils-Ce30L8HS.js";
|
|
4
|
+
import "./get-copilot-token-BMypymQn.js";
|
|
5
|
+
import { PROVIDER_TYPE_ANTHROPIC, accountsManager, getAliasTargetSet, getConfig, getExtraPromptForModel, getModelAliases, getModelAliasesInfo, getModelRefreshIntervalMs, getProviderConfig, getReasoningEffortForModel, getSmallModel, isForceAgentEnabled, isFreeModelLoadBalancingEnabled, isMessageStartInputTokensFallbackEnabled, isMessagesApiEnabled, isResponsesApiContextManagementModel, mergeConfigWithDefaults, shouldCompactUseSmallModel } from "./accounts-manager-CtZD9BhK.js";
|
|
6
6
|
import consola from "consola";
|
|
7
7
|
import fs, { readFile } from "node:fs/promises";
|
|
8
8
|
import * as path$1 from "node:path";
|
|
@@ -2439,570 +2439,108 @@ const getTokenCount = async (payload, model) => {
|
|
|
2439
2439
|
};
|
|
2440
2440
|
|
|
2441
2441
|
//#endregion
|
|
2442
|
-
//#region src/services/copilot/create-
|
|
2443
|
-
|
|
2442
|
+
//#region src/services/copilot/create-chat-completions.ts
|
|
2443
|
+
function isGpt5MiniFamily(modelId) {
|
|
2444
|
+
return modelId === "gpt-5-mini" || modelId.startsWith("gpt-5-mini-");
|
|
2445
|
+
}
|
|
2446
|
+
function applyDefaultReasoningEffort(payload) {
|
|
2447
|
+
if (!isGpt5MiniFamily(payload.model)) return payload;
|
|
2448
|
+
if (payload.reasoning_effort !== null && payload.reasoning_effort !== void 0) return payload;
|
|
2449
|
+
return {
|
|
2450
|
+
...payload,
|
|
2451
|
+
reasoning_effort: getReasoningEffortForModel("gpt-5-mini")
|
|
2452
|
+
};
|
|
2453
|
+
}
|
|
2454
|
+
const getChatInitiator = (messages) => {
|
|
2455
|
+
if (isForceAgentEnabled()) return messages.some((msg) => ["assistant", "tool"].includes(msg.role)) ? "agent" : "user";
|
|
2456
|
+
const lastMessage = messages.at(-1);
|
|
2457
|
+
if (!lastMessage) return "user";
|
|
2458
|
+
return ["assistant", "tool"].includes(lastMessage.role) ? "agent" : "user";
|
|
2459
|
+
};
|
|
2460
|
+
const createChatCompletions = async (payload, account, options) => {
|
|
2444
2461
|
const ctx = account ?? accountFromState();
|
|
2445
2462
|
if (!ctx.copilotToken) throw new Error("Copilot token not found");
|
|
2463
|
+
const enableVision = payload.messages.some((x) => typeof x.content !== "string" && x.content?.some((x$1) => x$1.type === "image_url"));
|
|
2464
|
+
const initiator = options?.initiator ?? getChatInitiator(payload.messages);
|
|
2446
2465
|
const headers = {
|
|
2447
|
-
...copilotHeaders(ctx,
|
|
2448
|
-
"x-initiator": initiator
|
|
2466
|
+
...copilotHeaders(ctx, enableVision, options?.upstreamRequestId),
|
|
2467
|
+
"x-initiator": options?.subagentMarker ? "agent" : initiator
|
|
2449
2468
|
};
|
|
2450
|
-
prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
const response = await fetch(`${copilotBaseUrl(ctx)}/
|
|
2469
|
+
prepareInteractionHeaders(options?.sessionId, Boolean(options?.subagentMarker), headers);
|
|
2470
|
+
const upstreamPayload = applyDefaultReasoningEffort(payload);
|
|
2471
|
+
prepareForCompact(headers, options?.isCompact);
|
|
2472
|
+
const response = await fetch(`${copilotBaseUrl(ctx)}/chat/completions`, {
|
|
2454
2473
|
method: "POST",
|
|
2455
2474
|
headers,
|
|
2456
|
-
body: JSON.stringify(
|
|
2475
|
+
body: JSON.stringify(upstreamPayload)
|
|
2457
2476
|
});
|
|
2458
2477
|
if (!response.ok) {
|
|
2459
|
-
consola.error("Failed to create
|
|
2460
|
-
throw new HTTPError("Failed to create
|
|
2478
|
+
consola.error("Failed to create chat completions", response);
|
|
2479
|
+
throw new HTTPError("Failed to create chat completions", response);
|
|
2461
2480
|
}
|
|
2462
2481
|
if (payload.stream) return events(response);
|
|
2463
2482
|
return await response.json();
|
|
2464
2483
|
};
|
|
2465
2484
|
|
|
2466
2485
|
//#endregion
|
|
2467
|
-
//#region src/routes/
|
|
2468
|
-
const
|
|
2469
|
-
const
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
const
|
|
2473
|
-
const
|
|
2474
|
-
const
|
|
2475
|
-
const
|
|
2476
|
-
|
|
2477
|
-
const
|
|
2478
|
-
const
|
|
2479
|
-
const { safetyIdentifier, promptCacheKey } =
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
summary: "auto"
|
|
2498
|
-
},
|
|
2499
|
-
include: ["reasoning.encrypted_content"]
|
|
2500
|
-
};
|
|
2501
|
-
};
|
|
2502
|
-
const encodeCompactionCarrierSignature = (compaction) => {
|
|
2503
|
-
return `${COMPACTION_SIGNATURE_PREFIX}${compaction.encrypted_content}${COMPACTION_SIGNATURE_SEPARATOR}${compaction.id}`;
|
|
2504
|
-
};
|
|
2505
|
-
const decodeCompactionCarrierSignature = (signature) => {
|
|
2506
|
-
if (signature.startsWith(COMPACTION_SIGNATURE_PREFIX)) {
|
|
2507
|
-
const raw = signature.slice(4);
|
|
2508
|
-
const separatorIndex = raw.indexOf(COMPACTION_SIGNATURE_SEPARATOR);
|
|
2509
|
-
if (separatorIndex <= 0 || separatorIndex === raw.length - 1) return;
|
|
2510
|
-
const encrypted_content = raw.slice(0, separatorIndex);
|
|
2511
|
-
const id = raw.slice(separatorIndex + 1);
|
|
2512
|
-
if (!encrypted_content) return;
|
|
2513
|
-
return {
|
|
2514
|
-
id,
|
|
2515
|
-
encrypted_content
|
|
2516
|
-
};
|
|
2517
|
-
}
|
|
2518
|
-
};
|
|
2519
|
-
const translateMessage = (message, model, applyPhase) => {
|
|
2520
|
-
if (message.role === "user") return translateUserMessage(message);
|
|
2521
|
-
return translateAssistantMessage(message, model, applyPhase);
|
|
2522
|
-
};
|
|
2523
|
-
const translateUserMessage = (message) => {
|
|
2524
|
-
if (typeof message.content === "string") return [createMessage("user", message.content)];
|
|
2525
|
-
if (!Array.isArray(message.content)) return [];
|
|
2526
|
-
const items = [];
|
|
2527
|
-
const pendingContent = [];
|
|
2528
|
-
for (const block of message.content) {
|
|
2529
|
-
if (block.type === "tool_result") {
|
|
2530
|
-
flushPendingContent(pendingContent, items, { role: "user" });
|
|
2531
|
-
items.push(createFunctionCallOutput(block));
|
|
2532
|
-
continue;
|
|
2533
|
-
}
|
|
2534
|
-
const converted = translateUserContentBlock(block);
|
|
2535
|
-
if (converted) pendingContent.push(converted);
|
|
2486
|
+
//#region src/routes/chat-completions/handler.ts
|
|
2487
|
+
const logger$6 = createHandlerLogger("chat-completions-handler");
|
|
2488
|
+
const CHAT_COMPLETIONS_ENDPOINT$1 = "/chat/completions";
|
|
2489
|
+
async function handleCompletion$1(c) {
|
|
2490
|
+
await checkRateLimit(state);
|
|
2491
|
+
const store = getRequestHistoryStore();
|
|
2492
|
+
const request = buildRequestContext$1(c);
|
|
2493
|
+
const payload = await c.req.json();
|
|
2494
|
+
const clientModel = payload.model;
|
|
2495
|
+
const streamRequested = Boolean(payload.stream);
|
|
2496
|
+
const initiator = getChatInitiator(payload.messages);
|
|
2497
|
+
const userId = payload.user ?? void 0;
|
|
2498
|
+
const { safetyIdentifier, sessionId: promptCacheKey } = parseUserIdMetadata(userId);
|
|
2499
|
+
const normalizedSafetyIdentifier = safetyIdentifier ?? void 0;
|
|
2500
|
+
const normalizedPromptCacheKey = promptCacheKey ?? void 0;
|
|
2501
|
+
request.userId = userId;
|
|
2502
|
+
request.safetyIdentifier = normalizedSafetyIdentifier;
|
|
2503
|
+
request.promptCacheKey = normalizedPromptCacheKey;
|
|
2504
|
+
request.initiator = initiator;
|
|
2505
|
+
if (getAliasTargetSet().has(clientModel.toLowerCase())) {
|
|
2506
|
+
recordSelectionFailure$2(store, {
|
|
2507
|
+
request,
|
|
2508
|
+
clientModel,
|
|
2509
|
+
stream: streamRequested,
|
|
2510
|
+
reason: "MODEL_NOT_SUPPORTED"
|
|
2511
|
+
});
|
|
2512
|
+
return selectionFailureResponse$2(c, {
|
|
2513
|
+
clientModel,
|
|
2514
|
+
reason: "MODEL_NOT_SUPPORTED"
|
|
2515
|
+
});
|
|
2536
2516
|
}
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
if (
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
continue;
|
|
2554
|
-
}
|
|
2555
|
-
if (block.type === "thinking" && block.signature) {
|
|
2556
|
-
const compactionContent = createCompactionContent(block);
|
|
2557
|
-
if (compactionContent) {
|
|
2558
|
-
flushPendingContent(pendingContent, items, {
|
|
2559
|
-
role: "assistant",
|
|
2560
|
-
phase: assistantPhase
|
|
2561
|
-
});
|
|
2562
|
-
items.push(compactionContent);
|
|
2563
|
-
continue;
|
|
2564
|
-
}
|
|
2565
|
-
if (block.signature.includes("@")) {
|
|
2566
|
-
flushPendingContent(pendingContent, items, {
|
|
2567
|
-
role: "assistant",
|
|
2568
|
-
phase: assistantPhase
|
|
2569
|
-
});
|
|
2570
|
-
items.push(createReasoningContent(block));
|
|
2571
|
-
continue;
|
|
2572
|
-
}
|
|
2573
|
-
}
|
|
2574
|
-
const converted = translateAssistantContentBlock(block);
|
|
2575
|
-
if (converted) pendingContent.push(converted);
|
|
2517
|
+
logger$6.debug("Request payload:", JSON.stringify(payload).slice(-400));
|
|
2518
|
+
const selection = await accountsManager.selectAccountForRequest([{
|
|
2519
|
+
modelId: clientModel,
|
|
2520
|
+
endpoint: CHAT_COMPLETIONS_ENDPOINT$1
|
|
2521
|
+
}]);
|
|
2522
|
+
if (!selection.ok) {
|
|
2523
|
+
recordSelectionFailure$2(store, {
|
|
2524
|
+
request,
|
|
2525
|
+
clientModel,
|
|
2526
|
+
stream: streamRequested,
|
|
2527
|
+
reason: selection.reason
|
|
2528
|
+
});
|
|
2529
|
+
return selectionFailureResponse$2(c, {
|
|
2530
|
+
clientModel,
|
|
2531
|
+
reason: selection.reason
|
|
2532
|
+
});
|
|
2576
2533
|
}
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
const
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
default: return;
|
|
2588
|
-
}
|
|
2589
|
-
};
|
|
2590
|
-
const translateAssistantContentBlock = (block) => {
|
|
2591
|
-
switch (block.type) {
|
|
2592
|
-
case "text": return createOutPutTextContent(block.text);
|
|
2593
|
-
default: return;
|
|
2594
|
-
}
|
|
2595
|
-
};
|
|
2596
|
-
const flushPendingContent = (pendingContent, target, message) => {
|
|
2597
|
-
if (pendingContent.length === 0) return;
|
|
2598
|
-
const messageContent = [...pendingContent];
|
|
2599
|
-
target.push(createMessage(message.role, messageContent, message.phase));
|
|
2600
|
-
pendingContent.length = 0;
|
|
2601
|
-
};
|
|
2602
|
-
const createMessage = (role, content, phase) => ({
|
|
2603
|
-
type: MESSAGE_TYPE,
|
|
2604
|
-
role,
|
|
2605
|
-
content,
|
|
2606
|
-
...role === "assistant" && phase ? { phase } : {}
|
|
2607
|
-
});
|
|
2608
|
-
const resolveAssistantPhase = (_model, content, applyPhase) => {
|
|
2609
|
-
if (!applyPhase) return;
|
|
2610
|
-
if (typeof content === "string") return "final_answer";
|
|
2611
|
-
if (!Array.isArray(content)) return;
|
|
2612
|
-
if (!content.some((block) => block.type === "text")) return;
|
|
2613
|
-
return content.some((block) => block.type === "tool_use") ? "commentary" : "final_answer";
|
|
2614
|
-
};
|
|
2615
|
-
const shouldApplyPhase = (model) => {
|
|
2616
|
-
return getExtraPromptForModel(model).includes("## Intermediary updates");
|
|
2617
|
-
};
|
|
2618
|
-
const createTextContent = (text) => ({
|
|
2619
|
-
type: "input_text",
|
|
2620
|
-
text
|
|
2621
|
-
});
|
|
2622
|
-
const createOutPutTextContent = (text) => ({
|
|
2623
|
-
type: "output_text",
|
|
2624
|
-
text
|
|
2625
|
-
});
|
|
2626
|
-
const createImageContent = (block) => ({
|
|
2627
|
-
type: "input_image",
|
|
2628
|
-
image_url: `data:${block.source.media_type};base64,${block.source.data}`,
|
|
2629
|
-
detail: "auto"
|
|
2630
|
-
});
|
|
2631
|
-
const createReasoningContent = (block) => {
|
|
2632
|
-
const { encryptedContent, id } = parseReasoningSignature(block.signature);
|
|
2633
|
-
const thinking = block.thinking === THINKING_TEXT$1 ? "" : block.thinking;
|
|
2634
|
-
return {
|
|
2635
|
-
id,
|
|
2636
|
-
type: "reasoning",
|
|
2637
|
-
summary: thinking ? [{
|
|
2638
|
-
type: "summary_text",
|
|
2639
|
-
text: thinking
|
|
2640
|
-
}] : [],
|
|
2641
|
-
encrypted_content: encryptedContent
|
|
2642
|
-
};
|
|
2643
|
-
};
|
|
2644
|
-
const createCompactionContent = (block) => {
|
|
2645
|
-
const compaction = decodeCompactionCarrierSignature(block.signature);
|
|
2646
|
-
if (!compaction) return;
|
|
2647
|
-
return {
|
|
2648
|
-
id: compaction.id,
|
|
2649
|
-
type: "compaction",
|
|
2650
|
-
encrypted_content: compaction.encrypted_content
|
|
2651
|
-
};
|
|
2652
|
-
};
|
|
2653
|
-
const parseReasoningSignature = (signature) => {
|
|
2654
|
-
const splitIndex = signature.lastIndexOf("@");
|
|
2655
|
-
if (splitIndex <= 0 || splitIndex === signature.length - 1) return {
|
|
2656
|
-
encryptedContent: signature,
|
|
2657
|
-
id: ""
|
|
2658
|
-
};
|
|
2659
|
-
return {
|
|
2660
|
-
encryptedContent: signature.slice(0, splitIndex),
|
|
2661
|
-
id: signature.slice(splitIndex + 1)
|
|
2662
|
-
};
|
|
2663
|
-
};
|
|
2664
|
-
const createFunctionToolCall = (block) => ({
|
|
2665
|
-
type: "function_call",
|
|
2666
|
-
call_id: block.id,
|
|
2667
|
-
name: block.name,
|
|
2668
|
-
arguments: JSON.stringify(block.input),
|
|
2669
|
-
status: "completed"
|
|
2670
|
-
});
|
|
2671
|
-
const createFunctionCallOutput = (block) => ({
|
|
2672
|
-
type: "function_call_output",
|
|
2673
|
-
call_id: block.tool_use_id,
|
|
2674
|
-
output: convertToolResultContent(block.content),
|
|
2675
|
-
status: block.is_error ? "incomplete" : "completed"
|
|
2676
|
-
});
|
|
2677
|
-
const translateSystemPrompt = (system, model) => {
|
|
2678
|
-
if (!system) return null;
|
|
2679
|
-
const extraPrompt = getExtraPromptForModel(model);
|
|
2680
|
-
if (typeof system === "string") return system + extraPrompt;
|
|
2681
|
-
const text = system.map((block, index) => {
|
|
2682
|
-
if (index === 0) return block.text + extraPrompt;
|
|
2683
|
-
return block.text;
|
|
2684
|
-
}).join(" ");
|
|
2685
|
-
return text.length > 0 ? text : null;
|
|
2686
|
-
};
|
|
2687
|
-
const convertAnthropicTools = (tools) => {
|
|
2688
|
-
if (!tools || tools.length === 0) return null;
|
|
2689
|
-
return tools.map((tool) => ({
|
|
2690
|
-
type: "function",
|
|
2691
|
-
name: tool.name,
|
|
2692
|
-
parameters: tool.input_schema,
|
|
2693
|
-
strict: false,
|
|
2694
|
-
...tool.description ? { description: tool.description } : {}
|
|
2695
|
-
}));
|
|
2696
|
-
};
|
|
2697
|
-
const convertAnthropicToolChoice = (choice) => {
|
|
2698
|
-
if (!choice) return "auto";
|
|
2699
|
-
switch (choice.type) {
|
|
2700
|
-
case "auto": return "auto";
|
|
2701
|
-
case "any": return "required";
|
|
2702
|
-
case "tool": return choice.name ? {
|
|
2703
|
-
type: "function",
|
|
2704
|
-
name: choice.name
|
|
2705
|
-
} : "auto";
|
|
2706
|
-
case "none": return "none";
|
|
2707
|
-
default: return "auto";
|
|
2708
|
-
}
|
|
2709
|
-
};
|
|
2710
|
-
const translateResponsesResultToAnthropic = (response) => {
|
|
2711
|
-
const contentBlocks = mapOutputToAnthropicContent(response.output);
|
|
2712
|
-
const usage = mapResponsesUsage(response);
|
|
2713
|
-
let anthropicContent = fallbackContentBlocks(response.output_text);
|
|
2714
|
-
if (contentBlocks.length > 0) anthropicContent = contentBlocks;
|
|
2715
|
-
const stopReason = mapResponsesStopReason(response);
|
|
2716
|
-
return {
|
|
2717
|
-
id: response.id,
|
|
2718
|
-
type: "message",
|
|
2719
|
-
role: "assistant",
|
|
2720
|
-
content: anthropicContent,
|
|
2721
|
-
model: response.model,
|
|
2722
|
-
stop_reason: stopReason,
|
|
2723
|
-
stop_sequence: null,
|
|
2724
|
-
usage
|
|
2725
|
-
};
|
|
2726
|
-
};
|
|
2727
|
-
const mapOutputToAnthropicContent = (output) => {
|
|
2728
|
-
const contentBlocks = [];
|
|
2729
|
-
for (const item of output) switch (item.type) {
|
|
2730
|
-
case "reasoning": {
|
|
2731
|
-
const thinkingText = extractReasoningText(item);
|
|
2732
|
-
if (thinkingText.length > 0) contentBlocks.push({
|
|
2733
|
-
type: "thinking",
|
|
2734
|
-
thinking: thinkingText,
|
|
2735
|
-
signature: (item.encrypted_content ?? "") + "@" + item.id
|
|
2736
|
-
});
|
|
2737
|
-
break;
|
|
2738
|
-
}
|
|
2739
|
-
case "function_call": {
|
|
2740
|
-
const toolUseBlock = createToolUseContentBlock(item);
|
|
2741
|
-
if (toolUseBlock) contentBlocks.push(toolUseBlock);
|
|
2742
|
-
break;
|
|
2743
|
-
}
|
|
2744
|
-
case "message": {
|
|
2745
|
-
const combinedText = combineMessageTextContent(item.content);
|
|
2746
|
-
if (combinedText.length > 0) contentBlocks.push({
|
|
2747
|
-
type: "text",
|
|
2748
|
-
text: combinedText
|
|
2749
|
-
});
|
|
2750
|
-
break;
|
|
2751
|
-
}
|
|
2752
|
-
case "compaction": {
|
|
2753
|
-
const compactionBlock = createCompactionThinkingBlock(item);
|
|
2754
|
-
if (compactionBlock) contentBlocks.push(compactionBlock);
|
|
2755
|
-
break;
|
|
2756
|
-
}
|
|
2757
|
-
default: {
|
|
2758
|
-
const combinedText = combineMessageTextContent(item.content);
|
|
2759
|
-
if (combinedText.length > 0) contentBlocks.push({
|
|
2760
|
-
type: "text",
|
|
2761
|
-
text: combinedText
|
|
2762
|
-
});
|
|
2763
|
-
}
|
|
2764
|
-
}
|
|
2765
|
-
return contentBlocks;
|
|
2766
|
-
};
|
|
2767
|
-
const combineMessageTextContent = (content) => {
|
|
2768
|
-
if (!Array.isArray(content)) return "";
|
|
2769
|
-
let aggregated = "";
|
|
2770
|
-
for (const block of content) {
|
|
2771
|
-
if (isResponseOutputText(block)) {
|
|
2772
|
-
aggregated += block.text;
|
|
2773
|
-
continue;
|
|
2774
|
-
}
|
|
2775
|
-
if (isResponseOutputRefusal(block)) {
|
|
2776
|
-
aggregated += block.refusal;
|
|
2777
|
-
continue;
|
|
2778
|
-
}
|
|
2779
|
-
if (typeof block.text === "string") {
|
|
2780
|
-
aggregated += block.text;
|
|
2781
|
-
continue;
|
|
2782
|
-
}
|
|
2783
|
-
if (typeof block.reasoning === "string") {
|
|
2784
|
-
aggregated += block.reasoning;
|
|
2785
|
-
continue;
|
|
2786
|
-
}
|
|
2787
|
-
}
|
|
2788
|
-
return aggregated;
|
|
2789
|
-
};
|
|
2790
|
-
const extractReasoningText = (item) => {
|
|
2791
|
-
const segments = [];
|
|
2792
|
-
const collectFromBlocks = (blocks) => {
|
|
2793
|
-
if (!Array.isArray(blocks)) return;
|
|
2794
|
-
for (const block of blocks) if (typeof block.text === "string") {
|
|
2795
|
-
segments.push(block.text);
|
|
2796
|
-
continue;
|
|
2797
|
-
}
|
|
2798
|
-
};
|
|
2799
|
-
if (!item.summary || item.summary.length === 0) return THINKING_TEXT$1;
|
|
2800
|
-
collectFromBlocks(item.summary);
|
|
2801
|
-
return segments.join("").trim();
|
|
2802
|
-
};
|
|
2803
|
-
const createToolUseContentBlock = (call) => {
|
|
2804
|
-
const toolId = call.call_id;
|
|
2805
|
-
if (!call.name || !toolId) return null;
|
|
2806
|
-
const input = parseFunctionCallArguments(call.arguments);
|
|
2807
|
-
return {
|
|
2808
|
-
type: "tool_use",
|
|
2809
|
-
id: toolId,
|
|
2810
|
-
name: call.name,
|
|
2811
|
-
input
|
|
2812
|
-
};
|
|
2813
|
-
};
|
|
2814
|
-
const createCompactionThinkingBlock = (item) => {
|
|
2815
|
-
if (!item.id || !item.encrypted_content) return null;
|
|
2816
|
-
return {
|
|
2817
|
-
type: "thinking",
|
|
2818
|
-
thinking: THINKING_TEXT$1,
|
|
2819
|
-
signature: encodeCompactionCarrierSignature({
|
|
2820
|
-
id: item.id,
|
|
2821
|
-
encrypted_content: item.encrypted_content
|
|
2822
|
-
})
|
|
2823
|
-
};
|
|
2824
|
-
};
|
|
2825
|
-
const parseFunctionCallArguments = (rawArguments) => {
|
|
2826
|
-
if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return {};
|
|
2827
|
-
try {
|
|
2828
|
-
const parsed = JSON.parse(rawArguments);
|
|
2829
|
-
if (Array.isArray(parsed)) return { arguments: parsed };
|
|
2830
|
-
if (parsed && typeof parsed === "object") return parsed;
|
|
2831
|
-
} catch (error) {
|
|
2832
|
-
consola.warn("Failed to parse function call arguments", {
|
|
2833
|
-
error,
|
|
2834
|
-
rawArguments
|
|
2835
|
-
});
|
|
2836
|
-
}
|
|
2837
|
-
return { raw_arguments: rawArguments };
|
|
2838
|
-
};
|
|
2839
|
-
const fallbackContentBlocks = (outputText) => {
|
|
2840
|
-
if (!outputText) return [];
|
|
2841
|
-
return [{
|
|
2842
|
-
type: "text",
|
|
2843
|
-
text: outputText
|
|
2844
|
-
}];
|
|
2845
|
-
};
|
|
2846
|
-
const mapResponsesStopReason = (response) => {
|
|
2847
|
-
const { status, incomplete_details: incompleteDetails } = response;
|
|
2848
|
-
if (status === "completed") {
|
|
2849
|
-
if (response.output.some((item) => item.type === "function_call")) return "tool_use";
|
|
2850
|
-
return "end_turn";
|
|
2851
|
-
}
|
|
2852
|
-
if (status === "incomplete") {
|
|
2853
|
-
if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
|
|
2854
|
-
if (incompleteDetails?.reason === "content_filter") return "end_turn";
|
|
2855
|
-
}
|
|
2856
|
-
return null;
|
|
2857
|
-
};
|
|
2858
|
-
const mapResponsesUsage = (response) => {
|
|
2859
|
-
const inputTokens = response.usage?.input_tokens ?? 0;
|
|
2860
|
-
const outputTokens = response.usage?.output_tokens ?? 0;
|
|
2861
|
-
const inputCachedTokens = response.usage?.input_tokens_details?.cached_tokens;
|
|
2862
|
-
return {
|
|
2863
|
-
input_tokens: inputTokens - (inputCachedTokens ?? 0),
|
|
2864
|
-
output_tokens: outputTokens,
|
|
2865
|
-
...response.usage?.input_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: response.usage.input_tokens_details.cached_tokens }
|
|
2866
|
-
};
|
|
2867
|
-
};
|
|
2868
|
-
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
2869
|
-
const isResponseOutputText = (block) => isRecord(block) && "type" in block && block.type === "output_text";
|
|
2870
|
-
const isResponseOutputRefusal = (block) => isRecord(block) && "type" in block && block.type === "refusal";
|
|
2871
|
-
const parseUserId = (userId) => {
|
|
2872
|
-
if (!userId || typeof userId !== "string") return {
|
|
2873
|
-
safetyIdentifier: null,
|
|
2874
|
-
promptCacheKey: null
|
|
2875
|
-
};
|
|
2876
|
-
const userMatch = userId.match(/user_([^_]+)_account/);
|
|
2877
|
-
const safetyIdentifier = userMatch ? userMatch[1] : null;
|
|
2878
|
-
const sessionMatch = userId.match(/_session_(.+)$/);
|
|
2879
|
-
const promptCacheKey = sessionMatch ? sessionMatch[1] : null;
|
|
2880
|
-
return {
|
|
2881
|
-
safetyIdentifier,
|
|
2882
|
-
promptCacheKey
|
|
2883
|
-
};
|
|
2884
|
-
};
|
|
2885
|
-
const convertToolResultContent = (content) => {
|
|
2886
|
-
if (typeof content === "string") return content;
|
|
2887
|
-
if (Array.isArray(content)) {
|
|
2888
|
-
const result = [];
|
|
2889
|
-
for (const block of content) switch (block.type) {
|
|
2890
|
-
case "text":
|
|
2891
|
-
result.push(createTextContent(block.text));
|
|
2892
|
-
break;
|
|
2893
|
-
case "image":
|
|
2894
|
-
result.push(createImageContent(block));
|
|
2895
|
-
break;
|
|
2896
|
-
default: break;
|
|
2897
|
-
}
|
|
2898
|
-
return result;
|
|
2899
|
-
}
|
|
2900
|
-
return "";
|
|
2901
|
-
};
|
|
2902
|
-
|
|
2903
|
-
//#endregion
|
|
2904
|
-
//#region src/services/copilot/create-chat-completions.ts
|
|
2905
|
-
function isGpt5MiniFamily(modelId) {
|
|
2906
|
-
return modelId === "gpt-5-mini" || modelId.startsWith("gpt-5-mini-");
|
|
2907
|
-
}
|
|
2908
|
-
function applyDefaultReasoningEffort(payload) {
|
|
2909
|
-
if (!isGpt5MiniFamily(payload.model)) return payload;
|
|
2910
|
-
if (payload.reasoning_effort !== null && payload.reasoning_effort !== void 0) return payload;
|
|
2911
|
-
return {
|
|
2912
|
-
...payload,
|
|
2913
|
-
reasoning_effort: getReasoningEffortForModel("gpt-5-mini")
|
|
2914
|
-
};
|
|
2915
|
-
}
|
|
2916
|
-
const getChatInitiator = (messages) => {
|
|
2917
|
-
if (isForceAgentEnabled()) return messages.some((msg) => ["assistant", "tool"].includes(msg.role)) ? "agent" : "user";
|
|
2918
|
-
const lastMessage = messages.at(-1);
|
|
2919
|
-
if (!lastMessage) return "user";
|
|
2920
|
-
return ["assistant", "tool"].includes(lastMessage.role) ? "agent" : "user";
|
|
2921
|
-
};
|
|
2922
|
-
const createChatCompletions = async (payload, account, options) => {
|
|
2923
|
-
const ctx = account ?? accountFromState();
|
|
2924
|
-
if (!ctx.copilotToken) throw new Error("Copilot token not found");
|
|
2925
|
-
const enableVision = payload.messages.some((x) => typeof x.content !== "string" && x.content?.some((x$1) => x$1.type === "image_url"));
|
|
2926
|
-
const initiator = options?.initiator ?? getChatInitiator(payload.messages);
|
|
2927
|
-
const headers = {
|
|
2928
|
-
...copilotHeaders(ctx, enableVision, options?.upstreamRequestId),
|
|
2929
|
-
"x-initiator": options?.subagentMarker ? "agent" : initiator
|
|
2930
|
-
};
|
|
2931
|
-
prepareInteractionHeaders(options?.sessionId, Boolean(options?.subagentMarker), headers);
|
|
2932
|
-
const upstreamPayload = applyDefaultReasoningEffort(payload);
|
|
2933
|
-
prepareForCompact(headers, options?.isCompact);
|
|
2934
|
-
const response = await fetch(`${copilotBaseUrl(ctx)}/chat/completions`, {
|
|
2935
|
-
method: "POST",
|
|
2936
|
-
headers,
|
|
2937
|
-
body: JSON.stringify(upstreamPayload)
|
|
2938
|
-
});
|
|
2939
|
-
if (!response.ok) {
|
|
2940
|
-
consola.error("Failed to create chat completions", response);
|
|
2941
|
-
throw new HTTPError("Failed to create chat completions", response);
|
|
2942
|
-
}
|
|
2943
|
-
if (payload.stream) return events(response);
|
|
2944
|
-
return await response.json();
|
|
2945
|
-
};
|
|
2946
|
-
|
|
2947
|
-
//#endregion
|
|
2948
|
-
//#region src/routes/chat-completions/handler.ts
|
|
2949
|
-
const logger$6 = createHandlerLogger("chat-completions-handler");
|
|
2950
|
-
const CHAT_COMPLETIONS_ENDPOINT$1 = "/chat/completions";
|
|
2951
|
-
async function handleCompletion$1(c) {
|
|
2952
|
-
await checkRateLimit(state);
|
|
2953
|
-
const store = getRequestHistoryStore();
|
|
2954
|
-
const request = buildRequestContext$1(c);
|
|
2955
|
-
const payload = await c.req.json();
|
|
2956
|
-
const clientModel = payload.model;
|
|
2957
|
-
const streamRequested = Boolean(payload.stream);
|
|
2958
|
-
const initiator = getChatInitiator(payload.messages);
|
|
2959
|
-
const userId = payload.user ?? void 0;
|
|
2960
|
-
const { safetyIdentifier, promptCacheKey } = parseUserId(userId);
|
|
2961
|
-
const normalizedSafetyIdentifier = safetyIdentifier ?? void 0;
|
|
2962
|
-
const normalizedPromptCacheKey = promptCacheKey ?? void 0;
|
|
2963
|
-
request.userId = userId;
|
|
2964
|
-
request.safetyIdentifier = normalizedSafetyIdentifier;
|
|
2965
|
-
request.promptCacheKey = normalizedPromptCacheKey;
|
|
2966
|
-
request.initiator = initiator;
|
|
2967
|
-
if (getAliasTargetSet().has(clientModel.toLowerCase())) {
|
|
2968
|
-
recordSelectionFailure$2(store, {
|
|
2969
|
-
request,
|
|
2970
|
-
clientModel,
|
|
2971
|
-
stream: streamRequested,
|
|
2972
|
-
reason: "MODEL_NOT_SUPPORTED"
|
|
2973
|
-
});
|
|
2974
|
-
return selectionFailureResponse$2(c, {
|
|
2975
|
-
clientModel,
|
|
2976
|
-
reason: "MODEL_NOT_SUPPORTED"
|
|
2977
|
-
});
|
|
2978
|
-
}
|
|
2979
|
-
logger$6.debug("Request payload:", JSON.stringify(payload).slice(-400));
|
|
2980
|
-
const selection = await accountsManager.selectAccountForRequest([{
|
|
2981
|
-
modelId: clientModel,
|
|
2982
|
-
endpoint: CHAT_COMPLETIONS_ENDPOINT$1
|
|
2983
|
-
}]);
|
|
2984
|
-
if (!selection.ok) {
|
|
2985
|
-
recordSelectionFailure$2(store, {
|
|
2986
|
-
request,
|
|
2987
|
-
clientModel,
|
|
2988
|
-
stream: streamRequested,
|
|
2989
|
-
reason: selection.reason
|
|
2990
|
-
});
|
|
2991
|
-
return selectionFailureResponse$2(c, {
|
|
2992
|
-
clientModel,
|
|
2993
|
-
reason: selection.reason
|
|
2994
|
-
});
|
|
2995
|
-
}
|
|
2996
|
-
const { account, selectedModel } = selection;
|
|
2997
|
-
const upstreamPayload = {
|
|
2998
|
-
...payload,
|
|
2999
|
-
model: selectedModel.id
|
|
3000
|
-
};
|
|
3001
|
-
const premiumRemainingBefore = account.premiumRemaining;
|
|
3002
|
-
const premiumUnlimitedBefore = account.unlimited;
|
|
3003
|
-
await logTokenCountForRequest({
|
|
3004
|
-
payload: upstreamPayload,
|
|
3005
|
-
selectedModel
|
|
2534
|
+
const { account, selectedModel } = selection;
|
|
2535
|
+
const upstreamPayload = {
|
|
2536
|
+
...payload,
|
|
2537
|
+
model: selectedModel.id
|
|
2538
|
+
};
|
|
2539
|
+
const premiumRemainingBefore = account.premiumRemaining;
|
|
2540
|
+
const premiumUnlimitedBefore = account.unlimited;
|
|
2541
|
+
await logTokenCountForRequest({
|
|
2542
|
+
payload: upstreamPayload,
|
|
2543
|
+
selectedModel
|
|
3006
2544
|
});
|
|
3007
2545
|
if (state.manualApprove) await awaitApproval();
|
|
3008
2546
|
const payloadWithMaxTokens = applyDefaultMaxTokens(upstreamPayload, selectedModel);
|
|
@@ -3379,571 +2917,1030 @@ const createEmbeddings = async (payload, account) => {
|
|
|
3379
2917
|
};
|
|
3380
2918
|
|
|
3381
2919
|
//#endregion
|
|
3382
|
-
//#region src/routes/embeddings/route.ts
|
|
3383
|
-
const embeddingRoutes = new Hono();
|
|
3384
|
-
const EMBEDDINGS_ENDPOINT = "/embeddings";
|
|
3385
|
-
embeddingRoutes.post("/", async (c) => {
|
|
3386
|
-
try {
|
|
3387
|
-
const store = getRequestHistoryStore();
|
|
3388
|
-
const requestId = randomUUID();
|
|
3389
|
-
const startedAtMs = Date.now();
|
|
3390
|
-
const method = c.req.raw.method;
|
|
3391
|
-
const path$2 = new URL(c.req.url, "http://local").pathname;
|
|
3392
|
-
const { ip: clientIp, source: clientIpSource } = getClientIpInfo(c);
|
|
3393
|
-
const userAgent = c.req.header("user-agent") ?? void 0;
|
|
3394
|
-
const ctx = {
|
|
3395
|
-
requestId,
|
|
3396
|
-
startedAtMs,
|
|
3397
|
-
method,
|
|
3398
|
-
path: path$2,
|
|
3399
|
-
clientIp,
|
|
3400
|
-
clientIpSource,
|
|
3401
|
-
userAgent
|
|
2920
|
+
//#region src/routes/embeddings/route.ts
|
|
2921
|
+
const embeddingRoutes = new Hono();
|
|
2922
|
+
const EMBEDDINGS_ENDPOINT = "/embeddings";
|
|
2923
|
+
embeddingRoutes.post("/", async (c) => {
|
|
2924
|
+
try {
|
|
2925
|
+
const store = getRequestHistoryStore();
|
|
2926
|
+
const requestId = randomUUID();
|
|
2927
|
+
const startedAtMs = Date.now();
|
|
2928
|
+
const method = c.req.raw.method;
|
|
2929
|
+
const path$2 = new URL(c.req.url, "http://local").pathname;
|
|
2930
|
+
const { ip: clientIp, source: clientIpSource } = getClientIpInfo(c);
|
|
2931
|
+
const userAgent = c.req.header("user-agent") ?? void 0;
|
|
2932
|
+
const ctx = {
|
|
2933
|
+
requestId,
|
|
2934
|
+
startedAtMs,
|
|
2935
|
+
method,
|
|
2936
|
+
path: path$2,
|
|
2937
|
+
clientIp,
|
|
2938
|
+
clientIpSource,
|
|
2939
|
+
userAgent
|
|
2940
|
+
};
|
|
2941
|
+
const payload = await c.req.json();
|
|
2942
|
+
const clientModel = payload.model;
|
|
2943
|
+
if (getAliasTargetSet().has(clientModel.toLowerCase())) {
|
|
2944
|
+
recordSelectionFailure$1(store, {
|
|
2945
|
+
ctx,
|
|
2946
|
+
clientModel,
|
|
2947
|
+
reason: "MODEL_NOT_SUPPORTED"
|
|
2948
|
+
});
|
|
2949
|
+
return selectionFailureResponse$1(c, clientModel, "MODEL_NOT_SUPPORTED");
|
|
2950
|
+
}
|
|
2951
|
+
const selection = await accountsManager.selectAccountForRequest([{
|
|
2952
|
+
modelId: clientModel,
|
|
2953
|
+
endpoint: EMBEDDINGS_ENDPOINT
|
|
2954
|
+
}]);
|
|
2955
|
+
if (!selection.ok) {
|
|
2956
|
+
recordSelectionFailure$1(store, {
|
|
2957
|
+
ctx,
|
|
2958
|
+
clientModel,
|
|
2959
|
+
reason: selection.reason
|
|
2960
|
+
});
|
|
2961
|
+
return selectionFailureResponse$1(c, clientModel, selection.reason);
|
|
2962
|
+
}
|
|
2963
|
+
const upstreamPayload = {
|
|
2964
|
+
...payload,
|
|
2965
|
+
model: selection.selectedModel.id
|
|
2966
|
+
};
|
|
2967
|
+
return await runEmbeddingsWithAccount({
|
|
2968
|
+
c,
|
|
2969
|
+
store,
|
|
2970
|
+
ctx,
|
|
2971
|
+
payload: upstreamPayload,
|
|
2972
|
+
clientModel,
|
|
2973
|
+
selection
|
|
2974
|
+
});
|
|
2975
|
+
} catch (error) {
|
|
2976
|
+
return await forwardError(c, error);
|
|
2977
|
+
}
|
|
2978
|
+
});
|
|
2979
|
+
function recordSelectionFailure$1(store, params) {
|
|
2980
|
+
const { ctx, clientModel, reason } = params;
|
|
2981
|
+
const finishedAtMs = Date.now();
|
|
2982
|
+
store.insert({
|
|
2983
|
+
requestId: ctx.requestId,
|
|
2984
|
+
startedAtMs: ctx.startedAtMs,
|
|
2985
|
+
finishedAtMs,
|
|
2986
|
+
durationMs: finishedAtMs - ctx.startedAtMs,
|
|
2987
|
+
method: ctx.method,
|
|
2988
|
+
path: ctx.path,
|
|
2989
|
+
upstreamEndpoint: EMBEDDINGS_ENDPOINT,
|
|
2990
|
+
stream: false,
|
|
2991
|
+
clientModel,
|
|
2992
|
+
clientIp: ctx.clientIp,
|
|
2993
|
+
clientIpSource: ctx.clientIpSource,
|
|
2994
|
+
userAgent: ctx.userAgent,
|
|
2995
|
+
httpStatus: reason === "MODEL_NOT_SUPPORTED" ? 400 : 429,
|
|
2996
|
+
selectionFailureReason: reason
|
|
2997
|
+
});
|
|
2998
|
+
}
|
|
2999
|
+
function selectionFailureResponse$1(c, clientModel, reason) {
|
|
3000
|
+
if (reason === "MODEL_NOT_SUPPORTED") return c.json({ error: {
|
|
3001
|
+
message: `Model "${clientModel}" is not available for any configured account.`,
|
|
3002
|
+
type: "invalid_request_error"
|
|
3003
|
+
} }, 400);
|
|
3004
|
+
return c.json({ error: {
|
|
3005
|
+
message: "All accounts have exhausted their quota. Please wait for quota refresh or add additional accounts.",
|
|
3006
|
+
type: "rate_limit_error"
|
|
3007
|
+
} }, 429);
|
|
3008
|
+
}
|
|
3009
|
+
async function runEmbeddingsWithAccount({ c, store, ctx, payload, clientModel, selection }) {
|
|
3010
|
+
const { account, reservation, selectedModel, endpoint, costUnits } = selection;
|
|
3011
|
+
const premiumRemainingBefore = account.premiumRemaining;
|
|
3012
|
+
const premiumUnlimitedBefore = account.unlimited;
|
|
3013
|
+
let httpStatus = 200;
|
|
3014
|
+
let usage = {};
|
|
3015
|
+
let errorName;
|
|
3016
|
+
let errorStatus;
|
|
3017
|
+
let errorMessage;
|
|
3018
|
+
let finishedAtMs;
|
|
3019
|
+
try {
|
|
3020
|
+
const accountCtx = toAccountContext(account);
|
|
3021
|
+
const response = await createEmbeddings(payload, accountCtx);
|
|
3022
|
+
usage = normalizeEmbeddingsUsage(response.usage);
|
|
3023
|
+
finishedAtMs = Date.now();
|
|
3024
|
+
return c.json(response);
|
|
3025
|
+
} catch (error) {
|
|
3026
|
+
finishedAtMs = Date.now();
|
|
3027
|
+
const details = extractErrorDetails(error);
|
|
3028
|
+
httpStatus = details.httpStatus;
|
|
3029
|
+
errorName = details.errorName;
|
|
3030
|
+
errorStatus = details.errorStatus;
|
|
3031
|
+
errorMessage = details.errorMessage;
|
|
3032
|
+
if (details.unauthorized) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
|
|
3033
|
+
throw error;
|
|
3034
|
+
} finally {
|
|
3035
|
+
const finishedAtMsFinal = finishedAtMs ?? Date.now();
|
|
3036
|
+
await accountsManager.finalizeQuota(account, reservation);
|
|
3037
|
+
const premiumRemainingAfter = account.premiumRemaining;
|
|
3038
|
+
const premiumUnlimitedAfter = account.unlimited;
|
|
3039
|
+
store.insert({
|
|
3040
|
+
requestId: ctx.requestId,
|
|
3041
|
+
startedAtMs: ctx.startedAtMs,
|
|
3042
|
+
finishedAtMs: finishedAtMsFinal,
|
|
3043
|
+
durationMs: finishedAtMsFinal - ctx.startedAtMs,
|
|
3044
|
+
method: ctx.method,
|
|
3045
|
+
path: ctx.path,
|
|
3046
|
+
upstreamEndpoint: endpoint,
|
|
3047
|
+
stream: false,
|
|
3048
|
+
accountId: account.id,
|
|
3049
|
+
accountType: account.accountType,
|
|
3050
|
+
costUnits,
|
|
3051
|
+
clientModel,
|
|
3052
|
+
upstreamModel: selectedModel.id,
|
|
3053
|
+
clientIp: ctx.clientIp,
|
|
3054
|
+
clientIpSource: ctx.clientIpSource,
|
|
3055
|
+
userAgent: ctx.userAgent,
|
|
3056
|
+
...usage,
|
|
3057
|
+
premiumRemainingBefore,
|
|
3058
|
+
premiumRemainingAfter,
|
|
3059
|
+
premiumRemainingDiff: computeDiff(premiumRemainingBefore, premiumRemainingAfter),
|
|
3060
|
+
premiumUnlimitedBefore,
|
|
3061
|
+
premiumUnlimitedAfter,
|
|
3062
|
+
httpStatus,
|
|
3063
|
+
errorName,
|
|
3064
|
+
errorStatus,
|
|
3065
|
+
errorMessage
|
|
3066
|
+
});
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
|
|
3070
|
+
//#endregion
|
|
3071
|
+
//#region src/lib/models.ts
|
|
3072
|
+
const findEndpointModel = (sdkModelId) => {
|
|
3073
|
+
const models = state.models?.data ?? [];
|
|
3074
|
+
const exactMatch = models.find((m) => m.id === sdkModelId);
|
|
3075
|
+
if (exactMatch) return exactMatch;
|
|
3076
|
+
const normalized = _normalizeSdkModelId(sdkModelId);
|
|
3077
|
+
if (!normalized) return;
|
|
3078
|
+
const modelName = `claude-${normalized.family}-${normalized.version}`;
|
|
3079
|
+
const model = models.find((m) => m.id === modelName);
|
|
3080
|
+
if (model) return model;
|
|
3081
|
+
};
|
|
3082
|
+
/**
|
|
3083
|
+
* Normalizes an SDK model ID to extract the model family and version.
|
|
3084
|
+
* this method from github copilot extension
|
|
3085
|
+
* Examples:
|
|
3086
|
+
* - "claude-opus-4-5-20251101" -> { family: "opus", version: "4.5" }
|
|
3087
|
+
* - "claude-3-5-sonnet-20241022" -> { family: "sonnet", version: "3.5" }
|
|
3088
|
+
* - "claude-sonnet-4-20250514" -> { family: "sonnet", version: "4" }
|
|
3089
|
+
* - "claude-haiku-3-5-20250514" -> { family: "haiku", version: "3.5" }
|
|
3090
|
+
* - "claude-haiku-4.5" -> { family: "haiku", version: "4.5" }
|
|
3091
|
+
*/
|
|
3092
|
+
const _normalizeSdkModelId = (sdkModelId) => {
|
|
3093
|
+
const withoutDate = sdkModelId.toLowerCase().replace(/-\d{8}$/, "");
|
|
3094
|
+
const pattern1 = withoutDate.match(/^claude-(\w+)-(\d+)-(\d+)$/);
|
|
3095
|
+
if (pattern1) return {
|
|
3096
|
+
family: pattern1[1],
|
|
3097
|
+
version: `${pattern1[2]}.${pattern1[3]}`
|
|
3098
|
+
};
|
|
3099
|
+
const pattern2 = withoutDate.match(/^claude-(\d+)-(\d+)-(\w+)$/);
|
|
3100
|
+
if (pattern2) return {
|
|
3101
|
+
family: pattern2[3],
|
|
3102
|
+
version: `${pattern2[1]}.${pattern2[2]}`
|
|
3103
|
+
};
|
|
3104
|
+
const pattern3 = withoutDate.match(/^claude-(\w+)-(\d+)\.(\d+)$/);
|
|
3105
|
+
if (pattern3) return {
|
|
3106
|
+
family: pattern3[1],
|
|
3107
|
+
version: `${pattern3[2]}.${pattern3[3]}`
|
|
3108
|
+
};
|
|
3109
|
+
const pattern4 = withoutDate.match(/^claude-(\w+)-(\d+)$/);
|
|
3110
|
+
if (pattern4) return {
|
|
3111
|
+
family: pattern4[1],
|
|
3112
|
+
version: pattern4[2]
|
|
3113
|
+
};
|
|
3114
|
+
const pattern5 = withoutDate.match(/^claude-(\d+)-(\w+)$/);
|
|
3115
|
+
if (pattern5) return {
|
|
3116
|
+
family: pattern5[2],
|
|
3117
|
+
version: pattern5[1]
|
|
3118
|
+
};
|
|
3119
|
+
};
|
|
3120
|
+
|
|
3121
|
+
//#endregion
|
|
3122
|
+
//#region src/routes/messages/utils.ts
|
|
3123
|
+
function mapOpenAIStopReasonToAnthropic(finishReason) {
|
|
3124
|
+
if (finishReason === null) return null;
|
|
3125
|
+
return {
|
|
3126
|
+
stop: "end_turn",
|
|
3127
|
+
length: "max_tokens",
|
|
3128
|
+
tool_calls: "tool_use",
|
|
3129
|
+
content_filter: "end_turn"
|
|
3130
|
+
}[finishReason];
|
|
3131
|
+
}
|
|
3132
|
+
const mergeContentWithText = (toolResult, textBlock) => {
|
|
3133
|
+
if (typeof toolResult.content === "string") return {
|
|
3134
|
+
...toolResult,
|
|
3135
|
+
content: `${toolResult.content}\n\n${textBlock.text}`
|
|
3136
|
+
};
|
|
3137
|
+
return {
|
|
3138
|
+
...toolResult,
|
|
3139
|
+
content: [...toolResult.content, textBlock]
|
|
3140
|
+
};
|
|
3141
|
+
};
|
|
3142
|
+
const mergeContentWithTexts = (toolResult, textBlocks) => {
|
|
3143
|
+
if (typeof toolResult.content === "string") {
|
|
3144
|
+
const appendedTexts = textBlocks.map((tb) => tb.text).join("\n\n");
|
|
3145
|
+
return {
|
|
3146
|
+
...toolResult,
|
|
3147
|
+
content: `${toolResult.content}\n\n${appendedTexts}`
|
|
3402
3148
|
};
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3149
|
+
}
|
|
3150
|
+
return {
|
|
3151
|
+
...toolResult,
|
|
3152
|
+
content: [...toolResult.content, ...textBlocks]
|
|
3153
|
+
};
|
|
3154
|
+
};
|
|
3155
|
+
const mergeToolResult = (toolResults, textBlocks) => {
|
|
3156
|
+
if (toolResults.length === textBlocks.length) return toolResults.map((toolResult, index) => mergeContentWithText(toolResult, textBlocks[index]));
|
|
3157
|
+
const lastIndex = toolResults.length - 1;
|
|
3158
|
+
return toolResults.map((toolResult, index) => index === lastIndex ? mergeContentWithTexts(toolResult, textBlocks) : toolResult);
|
|
3159
|
+
};
|
|
3160
|
+
const mergeToolResultForClaude = (anthropicPayload) => {
|
|
3161
|
+
for (const msg of anthropicPayload.messages) {
|
|
3162
|
+
if (msg.role !== "user" || !Array.isArray(msg.content)) continue;
|
|
3163
|
+
const toolResults = [];
|
|
3164
|
+
const textBlocks = [];
|
|
3165
|
+
let valid = true;
|
|
3166
|
+
for (const block of msg.content) if (block.type === "tool_result") toolResults.push(block);
|
|
3167
|
+
else if (block.type === "text") textBlocks.push(block);
|
|
3168
|
+
else {
|
|
3169
|
+
valid = false;
|
|
3170
|
+
break;
|
|
3424
3171
|
}
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
ctx,
|
|
3433
|
-
payload: upstreamPayload,
|
|
3434
|
-
clientModel,
|
|
3435
|
-
selection
|
|
3436
|
-
});
|
|
3172
|
+
if (!valid || toolResults.length === 0 || textBlocks.length === 0) continue;
|
|
3173
|
+
msg.content = mergeToolResult(toolResults, textBlocks);
|
|
3174
|
+
}
|
|
3175
|
+
};
|
|
3176
|
+
const estimateInputTokens = async (payload, selectedModel, logger$7) => {
|
|
3177
|
+
try {
|
|
3178
|
+
return (await getTokenCount(payload, selectedModel)).input;
|
|
3437
3179
|
} catch (error) {
|
|
3438
|
-
|
|
3180
|
+
logger$7.warn("Failed to estimate input tokens for message_start", error);
|
|
3181
|
+
return;
|
|
3439
3182
|
}
|
|
3440
|
-
}
|
|
3441
|
-
|
|
3442
|
-
const
|
|
3183
|
+
};
|
|
3184
|
+
const isWarmupProbeRequest = (payload) => {
|
|
3185
|
+
const lastMsg = payload.messages.at(-1);
|
|
3186
|
+
if (!lastMsg || lastMsg.role !== "user" || !Array.isArray(lastMsg.content)) return false;
|
|
3187
|
+
const lastBlock = lastMsg.content.at(-1);
|
|
3188
|
+
if (!lastBlock || lastBlock.type !== "text") return false;
|
|
3189
|
+
const text = lastBlock.text.trim().toLowerCase();
|
|
3190
|
+
if (!(lastBlock.cache_control?.type === "ephemeral")) return false;
|
|
3191
|
+
if (text === "warmup") return true;
|
|
3192
|
+
if (text === "hello") {
|
|
3193
|
+
const preludeBlocks = lastMsg.content.slice(0, -1);
|
|
3194
|
+
if (preludeBlocks.length === 0) return false;
|
|
3195
|
+
return preludeBlocks.every((block) => block.type === "text" && block.text.trimStart().toLowerCase().startsWith("<system-reminder"));
|
|
3196
|
+
}
|
|
3197
|
+
return false;
|
|
3198
|
+
};
|
|
3199
|
+
const handleSelectionFailure = (context) => {
|
|
3200
|
+
const { c, store, requestId, startedAtMs, method, path: path$2, streamRequested, clientModel, clientIp, clientIpSource, userAgent, userId, safetyIdentifier, promptCacheKey, initiator, selection } = context;
|
|
3443
3201
|
const finishedAtMs = Date.now();
|
|
3444
3202
|
store.insert({
|
|
3445
|
-
requestId
|
|
3446
|
-
startedAtMs
|
|
3203
|
+
requestId,
|
|
3204
|
+
startedAtMs,
|
|
3447
3205
|
finishedAtMs,
|
|
3448
|
-
durationMs: finishedAtMs -
|
|
3449
|
-
method
|
|
3450
|
-
path:
|
|
3451
|
-
|
|
3452
|
-
stream: false,
|
|
3206
|
+
durationMs: finishedAtMs - startedAtMs,
|
|
3207
|
+
method,
|
|
3208
|
+
path: path$2,
|
|
3209
|
+
stream: streamRequested,
|
|
3453
3210
|
clientModel,
|
|
3454
|
-
clientIp
|
|
3455
|
-
clientIpSource
|
|
3456
|
-
userAgent
|
|
3457
|
-
|
|
3458
|
-
|
|
3211
|
+
clientIp,
|
|
3212
|
+
clientIpSource,
|
|
3213
|
+
userAgent,
|
|
3214
|
+
userId,
|
|
3215
|
+
safetyIdentifier,
|
|
3216
|
+
promptCacheKey,
|
|
3217
|
+
initiator,
|
|
3218
|
+
httpStatus: selection.reason === "MODEL_NOT_SUPPORTED" ? 400 : 429,
|
|
3219
|
+
selectionFailureReason: selection.reason
|
|
3220
|
+
});
|
|
3221
|
+
if (selection.reason === "MODEL_NOT_SUPPORTED") return c.json({ error: {
|
|
3222
|
+
message: `Model "${clientModel}" is not available for any configured account.`,
|
|
3223
|
+
type: "invalid_request_error"
|
|
3224
|
+
} }, 400);
|
|
3225
|
+
return c.json({ error: {
|
|
3226
|
+
message: "All accounts have exhausted their quota. Please wait for quota refresh or add additional accounts.",
|
|
3227
|
+
type: "rate_limit_error"
|
|
3228
|
+
} }, 429);
|
|
3229
|
+
};
|
|
3230
|
+
const maybeBlockOriginalModelName = (context) => {
|
|
3231
|
+
if (!getAliasTargetSet().has(context.clientModel.toLowerCase())) return null;
|
|
3232
|
+
return handleSelectionFailure({
|
|
3233
|
+
...context,
|
|
3234
|
+
selection: {
|
|
3235
|
+
ok: false,
|
|
3236
|
+
reason: "MODEL_NOT_SUPPORTED"
|
|
3237
|
+
}
|
|
3238
|
+
});
|
|
3239
|
+
};
|
|
3240
|
+
|
|
3241
|
+
//#endregion
|
|
3242
|
+
//#region src/routes/messages/non-stream-translation.ts
|
|
3243
|
+
const THINKING_TEXT = "Thinking...";
|
|
3244
|
+
function translateToOpenAI(payload) {
|
|
3245
|
+
const modelId = payload.model;
|
|
3246
|
+
const model = state.models?.data.find((m) => m.id === modelId);
|
|
3247
|
+
const thinkingBudget = getThinkingBudget(payload, model);
|
|
3248
|
+
return {
|
|
3249
|
+
model: modelId,
|
|
3250
|
+
messages: translateAnthropicMessagesToOpenAI(payload, modelId, thinkingBudget),
|
|
3251
|
+
max_tokens: payload.max_tokens,
|
|
3252
|
+
stop: payload.stop_sequences,
|
|
3253
|
+
stream: payload.stream,
|
|
3254
|
+
temperature: payload.temperature,
|
|
3255
|
+
top_p: payload.top_p,
|
|
3256
|
+
user: payload.metadata?.user_id,
|
|
3257
|
+
tools: translateAnthropicToolsToOpenAI(payload.tools),
|
|
3258
|
+
tool_choice: translateAnthropicToolChoiceToOpenAI(payload.tool_choice),
|
|
3259
|
+
thinking_budget: thinkingBudget
|
|
3260
|
+
};
|
|
3261
|
+
}
|
|
3262
|
+
function getThinkingBudget(payload, model) {
|
|
3263
|
+
const thinking = payload.thinking;
|
|
3264
|
+
if (model && thinking) {
|
|
3265
|
+
const maxThinkingBudget = Math.min(model.capabilities.supports.max_thinking_budget ?? 0, (model.capabilities.limits.max_output_tokens ?? 0) - 1);
|
|
3266
|
+
thinking.budget_tokens ??= maxThinkingBudget;
|
|
3267
|
+
if (maxThinkingBudget > 0) {
|
|
3268
|
+
const budgetTokens = Math.min(thinking.budget_tokens, maxThinkingBudget);
|
|
3269
|
+
return Math.max(budgetTokens, model.capabilities.supports.min_thinking_budget ?? 1024);
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
function translateAnthropicMessagesToOpenAI(payload, modelId, _thinkingBudget) {
|
|
3274
|
+
const systemMessages = handleSystemPrompt(payload.system);
|
|
3275
|
+
const otherMessages = payload.messages.flatMap((message) => message.role === "user" ? handleUserMessage(message) : handleAssistantMessage(message, modelId));
|
|
3276
|
+
return [...systemMessages, ...otherMessages];
|
|
3277
|
+
}
|
|
3278
|
+
function handleSystemPrompt(system) {
|
|
3279
|
+
if (!system) return [];
|
|
3280
|
+
if (typeof system === "string") return [{
|
|
3281
|
+
role: "system",
|
|
3282
|
+
content: system
|
|
3283
|
+
}];
|
|
3284
|
+
else return [{
|
|
3285
|
+
role: "system",
|
|
3286
|
+
content: system.map((block) => {
|
|
3287
|
+
return block.text;
|
|
3288
|
+
}).join("\n\n")
|
|
3289
|
+
}];
|
|
3290
|
+
}
|
|
3291
|
+
function handleUserMessage(message) {
|
|
3292
|
+
const newMessages = [];
|
|
3293
|
+
if (Array.isArray(message.content)) {
|
|
3294
|
+
const toolResultBlocks = message.content.filter((block) => block.type === "tool_result");
|
|
3295
|
+
const otherBlocks = message.content.filter((block) => block.type !== "tool_result");
|
|
3296
|
+
for (const block of toolResultBlocks) newMessages.push({
|
|
3297
|
+
role: "tool",
|
|
3298
|
+
tool_call_id: block.tool_use_id,
|
|
3299
|
+
content: mapContent(block.content)
|
|
3300
|
+
});
|
|
3301
|
+
if (otherBlocks.length > 0) newMessages.push({
|
|
3302
|
+
role: "user",
|
|
3303
|
+
content: mapContent(otherBlocks)
|
|
3304
|
+
});
|
|
3305
|
+
} else newMessages.push({
|
|
3306
|
+
role: "user",
|
|
3307
|
+
content: mapContent(message.content)
|
|
3459
3308
|
});
|
|
3309
|
+
return newMessages;
|
|
3460
3310
|
}
|
|
3461
|
-
function
|
|
3462
|
-
if (
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
}
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3311
|
+
function handleAssistantMessage(message, modelId) {
|
|
3312
|
+
if (!Array.isArray(message.content)) return [{
|
|
3313
|
+
role: "assistant",
|
|
3314
|
+
content: mapContent(message.content)
|
|
3315
|
+
}];
|
|
3316
|
+
const toolUseBlocks = message.content.filter((block) => block.type === "tool_use");
|
|
3317
|
+
let thinkingBlocks = message.content.filter((block) => block.type === "thinking");
|
|
3318
|
+
if (modelId.startsWith("claude")) thinkingBlocks = thinkingBlocks.filter((b) => b.thinking && b.thinking !== THINKING_TEXT && b.signature && !b.signature.includes("@"));
|
|
3319
|
+
const thinkingContents = thinkingBlocks.filter((b) => b.thinking && b.thinking !== THINKING_TEXT).map((b) => b.thinking);
|
|
3320
|
+
const allThinkingContent = thinkingContents.length > 0 ? thinkingContents.join("\n\n") : void 0;
|
|
3321
|
+
const signature = thinkingBlocks.find((b) => b.signature)?.signature;
|
|
3322
|
+
return toolUseBlocks.length > 0 ? [{
|
|
3323
|
+
role: "assistant",
|
|
3324
|
+
content: mapContent(message.content),
|
|
3325
|
+
reasoning_text: allThinkingContent,
|
|
3326
|
+
reasoning_opaque: signature,
|
|
3327
|
+
tool_calls: toolUseBlocks.map((toolUse) => ({
|
|
3328
|
+
id: toolUse.id,
|
|
3329
|
+
type: "function",
|
|
3330
|
+
function: {
|
|
3331
|
+
name: toolUse.name,
|
|
3332
|
+
arguments: JSON.stringify(toolUse.input)
|
|
3333
|
+
}
|
|
3334
|
+
}))
|
|
3335
|
+
}] : [{
|
|
3336
|
+
role: "assistant",
|
|
3337
|
+
content: mapContent(message.content),
|
|
3338
|
+
reasoning_text: allThinkingContent,
|
|
3339
|
+
reasoning_opaque: signature
|
|
3340
|
+
}];
|
|
3470
3341
|
}
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
const details = extractErrorDetails(error);
|
|
3490
|
-
httpStatus = details.httpStatus;
|
|
3491
|
-
errorName = details.errorName;
|
|
3492
|
-
errorStatus = details.errorStatus;
|
|
3493
|
-
errorMessage = details.errorMessage;
|
|
3494
|
-
if (details.unauthorized) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
|
|
3495
|
-
throw error;
|
|
3496
|
-
} finally {
|
|
3497
|
-
const finishedAtMsFinal = finishedAtMs ?? Date.now();
|
|
3498
|
-
await accountsManager.finalizeQuota(account, reservation);
|
|
3499
|
-
const premiumRemainingAfter = account.premiumRemaining;
|
|
3500
|
-
const premiumUnlimitedAfter = account.unlimited;
|
|
3501
|
-
store.insert({
|
|
3502
|
-
requestId: ctx.requestId,
|
|
3503
|
-
startedAtMs: ctx.startedAtMs,
|
|
3504
|
-
finishedAtMs: finishedAtMsFinal,
|
|
3505
|
-
durationMs: finishedAtMsFinal - ctx.startedAtMs,
|
|
3506
|
-
method: ctx.method,
|
|
3507
|
-
path: ctx.path,
|
|
3508
|
-
upstreamEndpoint: endpoint,
|
|
3509
|
-
stream: false,
|
|
3510
|
-
accountId: account.id,
|
|
3511
|
-
accountType: account.accountType,
|
|
3512
|
-
costUnits,
|
|
3513
|
-
clientModel,
|
|
3514
|
-
upstreamModel: selectedModel.id,
|
|
3515
|
-
clientIp: ctx.clientIp,
|
|
3516
|
-
clientIpSource: ctx.clientIpSource,
|
|
3517
|
-
userAgent: ctx.userAgent,
|
|
3518
|
-
...usage,
|
|
3519
|
-
premiumRemainingBefore,
|
|
3520
|
-
premiumRemainingAfter,
|
|
3521
|
-
premiumRemainingDiff: computeDiff(premiumRemainingBefore, premiumRemainingAfter),
|
|
3522
|
-
premiumUnlimitedBefore,
|
|
3523
|
-
premiumUnlimitedAfter,
|
|
3524
|
-
httpStatus,
|
|
3525
|
-
errorName,
|
|
3526
|
-
errorStatus,
|
|
3527
|
-
errorMessage
|
|
3528
|
-
});
|
|
3342
|
+
function mapContent(content) {
|
|
3343
|
+
if (typeof content === "string") return content;
|
|
3344
|
+
if (!Array.isArray(content)) return null;
|
|
3345
|
+
if (!content.some((block) => block.type === "image")) return content.filter((block) => block.type === "text").map((block) => block.text).join("\n\n");
|
|
3346
|
+
const contentParts = [];
|
|
3347
|
+
for (const block of content) switch (block.type) {
|
|
3348
|
+
case "text":
|
|
3349
|
+
contentParts.push({
|
|
3350
|
+
type: "text",
|
|
3351
|
+
text: block.text
|
|
3352
|
+
});
|
|
3353
|
+
break;
|
|
3354
|
+
case "image":
|
|
3355
|
+
contentParts.push({
|
|
3356
|
+
type: "image_url",
|
|
3357
|
+
image_url: { url: `data:${block.source.media_type};base64,${block.source.data}` }
|
|
3358
|
+
});
|
|
3359
|
+
break;
|
|
3529
3360
|
}
|
|
3361
|
+
return contentParts;
|
|
3362
|
+
}
|
|
3363
|
+
function translateAnthropicToolsToOpenAI(anthropicTools) {
|
|
3364
|
+
if (!anthropicTools) return;
|
|
3365
|
+
return anthropicTools.map((tool) => ({
|
|
3366
|
+
type: "function",
|
|
3367
|
+
function: {
|
|
3368
|
+
name: tool.name,
|
|
3369
|
+
description: tool.description,
|
|
3370
|
+
parameters: normalizeToolSchema(tool.input_schema)
|
|
3371
|
+
}
|
|
3372
|
+
}));
|
|
3530
3373
|
}
|
|
3531
|
-
|
|
3532
|
-
//#endregion
|
|
3533
|
-
//#region src/lib/models.ts
|
|
3534
|
-
const findEndpointModel = (sdkModelId) => {
|
|
3535
|
-
const models = state.models?.data ?? [];
|
|
3536
|
-
const exactMatch = models.find((m) => m.id === sdkModelId);
|
|
3537
|
-
if (exactMatch) return exactMatch;
|
|
3538
|
-
const normalized = _normalizeSdkModelId(sdkModelId);
|
|
3539
|
-
if (!normalized) return;
|
|
3540
|
-
const modelName = `claude-${normalized.family}-${normalized.version}`;
|
|
3541
|
-
const model = models.find((m) => m.id === modelName);
|
|
3542
|
-
if (model) return model;
|
|
3543
|
-
};
|
|
3544
3374
|
/**
|
|
3545
|
-
*
|
|
3546
|
-
*
|
|
3547
|
-
* Examples:
|
|
3548
|
-
* - "claude-opus-4-5-20251101" -> { family: "opus", version: "4.5" }
|
|
3549
|
-
* - "claude-3-5-sonnet-20241022" -> { family: "sonnet", version: "3.5" }
|
|
3550
|
-
* - "claude-sonnet-4-20250514" -> { family: "sonnet", version: "4" }
|
|
3551
|
-
* - "claude-haiku-3-5-20250514" -> { family: "haiku", version: "3.5" }
|
|
3552
|
-
* - "claude-haiku-4.5" -> { family: "haiku", version: "4.5" }
|
|
3375
|
+
* Ensures `type: "object"` schema has a `properties` field.
|
|
3376
|
+
* OpenAI's API rejects object schemas without it.
|
|
3553
3377
|
*/
|
|
3554
|
-
const
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
family: pattern1[1],
|
|
3559
|
-
version: `${pattern1[2]}.${pattern1[3]}`
|
|
3560
|
-
};
|
|
3561
|
-
const pattern2 = withoutDate.match(/^claude-(\d+)-(\d+)-(\w+)$/);
|
|
3562
|
-
if (pattern2) return {
|
|
3563
|
-
family: pattern2[3],
|
|
3564
|
-
version: `${pattern2[1]}.${pattern2[2]}`
|
|
3565
|
-
};
|
|
3566
|
-
const pattern3 = withoutDate.match(/^claude-(\w+)-(\d+)\.(\d+)$/);
|
|
3567
|
-
if (pattern3) return {
|
|
3568
|
-
family: pattern3[1],
|
|
3569
|
-
version: `${pattern3[2]}.${pattern3[3]}`
|
|
3570
|
-
};
|
|
3571
|
-
const pattern4 = withoutDate.match(/^claude-(\w+)-(\d+)$/);
|
|
3572
|
-
if (pattern4) return {
|
|
3573
|
-
family: pattern4[1],
|
|
3574
|
-
version: pattern4[2]
|
|
3575
|
-
};
|
|
3576
|
-
const pattern5 = withoutDate.match(/^claude-(\d+)-(\w+)$/);
|
|
3577
|
-
if (pattern5) return {
|
|
3578
|
-
family: pattern5[2],
|
|
3579
|
-
version: pattern5[1]
|
|
3378
|
+
const normalizeToolSchema = (schema) => {
|
|
3379
|
+
if (schema.type === "object" && !schema.properties) return {
|
|
3380
|
+
...schema,
|
|
3381
|
+
properties: {}
|
|
3580
3382
|
};
|
|
3383
|
+
return schema;
|
|
3581
3384
|
};
|
|
3385
|
+
function translateAnthropicToolChoiceToOpenAI(anthropicToolChoice) {
|
|
3386
|
+
if (!anthropicToolChoice) return;
|
|
3387
|
+
switch (anthropicToolChoice.type) {
|
|
3388
|
+
case "auto": return "auto";
|
|
3389
|
+
case "any": return "required";
|
|
3390
|
+
case "tool":
|
|
3391
|
+
if (anthropicToolChoice.name) return {
|
|
3392
|
+
type: "function",
|
|
3393
|
+
function: { name: anthropicToolChoice.name }
|
|
3394
|
+
};
|
|
3395
|
+
return;
|
|
3396
|
+
case "none": return "none";
|
|
3397
|
+
default: return;
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
function translateToAnthropic(response) {
|
|
3401
|
+
const assistantContentBlocks = [];
|
|
3402
|
+
let stopReason = response.choices[0]?.finish_reason ?? null;
|
|
3403
|
+
for (const choice of response.choices) {
|
|
3404
|
+
const textBlocks = getAnthropicTextBlocks(choice.message.content);
|
|
3405
|
+
const thinkBlocks = getAnthropicThinkBlocks(choice.message.reasoning_text, choice.message.reasoning_opaque);
|
|
3406
|
+
const toolUseBlocks = getAnthropicToolUseBlocks(choice.message.tool_calls);
|
|
3407
|
+
assistantContentBlocks.push(...thinkBlocks, ...textBlocks, ...toolUseBlocks);
|
|
3408
|
+
if (choice.finish_reason === "tool_calls" || stopReason === "stop") stopReason = choice.finish_reason;
|
|
3409
|
+
}
|
|
3410
|
+
return {
|
|
3411
|
+
id: response.id,
|
|
3412
|
+
type: "message",
|
|
3413
|
+
role: "assistant",
|
|
3414
|
+
model: response.model,
|
|
3415
|
+
content: assistantContentBlocks,
|
|
3416
|
+
stop_reason: mapOpenAIStopReasonToAnthropic(stopReason),
|
|
3417
|
+
stop_sequence: null,
|
|
3418
|
+
usage: {
|
|
3419
|
+
input_tokens: (response.usage?.prompt_tokens ?? 0) - (response.usage?.prompt_tokens_details?.cached_tokens ?? 0),
|
|
3420
|
+
output_tokens: response.usage?.completion_tokens ?? 0,
|
|
3421
|
+
...response.usage?.prompt_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: response.usage.prompt_tokens_details.cached_tokens }
|
|
3422
|
+
}
|
|
3423
|
+
};
|
|
3424
|
+
}
|
|
3425
|
+
function getAnthropicTextBlocks(messageContent) {
|
|
3426
|
+
if (typeof messageContent === "string" && messageContent.length > 0) return [{
|
|
3427
|
+
type: "text",
|
|
3428
|
+
text: messageContent
|
|
3429
|
+
}];
|
|
3430
|
+
if (Array.isArray(messageContent)) return messageContent.filter((part) => part.type === "text").map((part) => ({
|
|
3431
|
+
type: "text",
|
|
3432
|
+
text: part.text
|
|
3433
|
+
}));
|
|
3434
|
+
return [];
|
|
3435
|
+
}
|
|
3436
|
+
function getAnthropicThinkBlocks(reasoningText, reasoningOpaque) {
|
|
3437
|
+
if (reasoningText && reasoningText.length > 0) return [{
|
|
3438
|
+
type: "thinking",
|
|
3439
|
+
thinking: reasoningText,
|
|
3440
|
+
signature: reasoningOpaque || ""
|
|
3441
|
+
}];
|
|
3442
|
+
if (reasoningOpaque && reasoningOpaque.length > 0) return [{
|
|
3443
|
+
type: "thinking",
|
|
3444
|
+
thinking: THINKING_TEXT,
|
|
3445
|
+
signature: reasoningOpaque
|
|
3446
|
+
}];
|
|
3447
|
+
return [];
|
|
3448
|
+
}
|
|
3449
|
+
function getAnthropicToolUseBlocks(toolCalls) {
|
|
3450
|
+
if (!toolCalls) return [];
|
|
3451
|
+
return toolCalls.map((toolCall) => ({
|
|
3452
|
+
type: "tool_use",
|
|
3453
|
+
id: toolCall.id,
|
|
3454
|
+
name: toolCall.function.name,
|
|
3455
|
+
input: JSON.parse(toolCall.function.arguments)
|
|
3456
|
+
}));
|
|
3457
|
+
}
|
|
3582
3458
|
|
|
3583
3459
|
//#endregion
|
|
3584
|
-
//#region src/routes/messages/
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3460
|
+
//#region src/routes/messages/count-tokens-handler.ts
|
|
3461
|
+
/**
|
|
3462
|
+
* Handles token counting for Anthropic messages
|
|
3463
|
+
*/
|
|
3464
|
+
async function handleCountTokens(c) {
|
|
3465
|
+
try {
|
|
3466
|
+
const anthropicBeta = c.req.header("anthropic-beta");
|
|
3467
|
+
const anthropicPayload = await c.req.json();
|
|
3468
|
+
const openAIPayload = translateToOpenAI(anthropicPayload);
|
|
3469
|
+
const selectedModel = findEndpointModel(anthropicPayload.model);
|
|
3470
|
+
anthropicPayload.model = selectedModel?.id ?? anthropicPayload.model;
|
|
3471
|
+
if (!selectedModel) {
|
|
3472
|
+
consola.warn("Model not found, returning default token count");
|
|
3473
|
+
return c.json({ input_tokens: 1 });
|
|
3474
|
+
}
|
|
3475
|
+
const tokenCount = await getTokenCount(openAIPayload, selectedModel);
|
|
3476
|
+
if (anthropicPayload.tools && anthropicPayload.tools.length > 0) {
|
|
3477
|
+
let addToolSystemPromptCount = false;
|
|
3478
|
+
if (anthropicBeta) {
|
|
3479
|
+
const toolsLength = anthropicPayload.tools.length;
|
|
3480
|
+
addToolSystemPromptCount = !anthropicPayload.tools.some((tool) => tool.name.startsWith("mcp__") || tool.name === "Skill" && toolsLength === 1);
|
|
3481
|
+
}
|
|
3482
|
+
if (addToolSystemPromptCount) {
|
|
3483
|
+
if (anthropicPayload.model.startsWith("claude")) tokenCount.input = tokenCount.input + 346;
|
|
3484
|
+
else if (anthropicPayload.model.startsWith("grok")) tokenCount.input = tokenCount.input + 120;
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
let finalTokenCount = tokenCount.input + tokenCount.output;
|
|
3488
|
+
if (anthropicPayload.model.startsWith("claude")) finalTokenCount = Math.round(finalTokenCount * 1.15);
|
|
3489
|
+
consola.info("Token count:", finalTokenCount);
|
|
3490
|
+
return c.json({ input_tokens: finalTokenCount });
|
|
3491
|
+
} catch (error) {
|
|
3492
|
+
consola.error("Error counting tokens:", error);
|
|
3493
|
+
return c.json({ input_tokens: 1 });
|
|
3494
|
+
}
|
|
3593
3495
|
}
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3496
|
+
|
|
3497
|
+
//#endregion
|
|
3498
|
+
//#region src/services/copilot/create-responses.ts
|
|
3499
|
+
const createResponses = async (payload, { vision, initiator, upstreamRequestId, subagentMarker, sessionId, isCompact }, account) => {
|
|
3500
|
+
const ctx = account ?? accountFromState();
|
|
3501
|
+
if (!ctx.copilotToken) throw new Error("Copilot token not found");
|
|
3502
|
+
const headers = {
|
|
3503
|
+
...copilotHeaders(ctx, vision, upstreamRequestId),
|
|
3504
|
+
"x-initiator": initiator
|
|
3598
3505
|
};
|
|
3506
|
+
prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
|
|
3507
|
+
prepareForCompact(headers, isCompact);
|
|
3508
|
+
payload.service_tier = null;
|
|
3509
|
+
const response = await fetch(`${copilotBaseUrl(ctx)}/responses`, {
|
|
3510
|
+
method: "POST",
|
|
3511
|
+
headers,
|
|
3512
|
+
body: JSON.stringify(payload)
|
|
3513
|
+
});
|
|
3514
|
+
if (!response.ok) {
|
|
3515
|
+
consola.error("Failed to create responses", response);
|
|
3516
|
+
throw new HTTPError("Failed to create responses", response);
|
|
3517
|
+
}
|
|
3518
|
+
if (payload.stream) return events(response);
|
|
3519
|
+
return await response.json();
|
|
3520
|
+
};
|
|
3521
|
+
|
|
3522
|
+
//#endregion
|
|
3523
|
+
//#region src/routes/messages/responses-translation.ts
|
|
3524
|
+
const MESSAGE_TYPE = "message";
|
|
3525
|
+
const COMPACTION_SIGNATURE_PREFIX = "cm1#";
|
|
3526
|
+
const COMPACTION_SIGNATURE_SEPARATOR = "@";
|
|
3527
|
+
const THINKING_TEXT$1 = "Thinking...";
|
|
3528
|
+
const translateAnthropicMessagesToResponsesPayload = (payload, modelOverride) => {
|
|
3529
|
+
const model = modelOverride ?? payload.model;
|
|
3530
|
+
const input = [];
|
|
3531
|
+
const applyPhase = shouldApplyPhase(payload.model);
|
|
3532
|
+
for (const message of payload.messages) input.push(...translateMessage(message, payload.model, applyPhase));
|
|
3533
|
+
const translatedTools = convertAnthropicTools(payload.tools);
|
|
3534
|
+
const toolChoice = convertAnthropicToolChoice(payload.tool_choice);
|
|
3535
|
+
const { safetyIdentifier, sessionId: promptCacheKey } = parseUserIdMetadata(payload.metadata?.user_id);
|
|
3599
3536
|
return {
|
|
3600
|
-
|
|
3601
|
-
|
|
3537
|
+
model,
|
|
3538
|
+
input,
|
|
3539
|
+
instructions: translateSystemPrompt(payload.system, model),
|
|
3540
|
+
temperature: 1,
|
|
3541
|
+
top_p: payload.top_p ?? null,
|
|
3542
|
+
max_output_tokens: Math.max(payload.max_tokens, 12800),
|
|
3543
|
+
tools: translatedTools,
|
|
3544
|
+
tool_choice: toolChoice,
|
|
3545
|
+
metadata: payload.metadata ? { ...payload.metadata } : null,
|
|
3546
|
+
safety_identifier: safetyIdentifier,
|
|
3547
|
+
prompt_cache_key: promptCacheKey,
|
|
3548
|
+
stream: payload.stream ?? null,
|
|
3549
|
+
store: false,
|
|
3550
|
+
parallel_tool_calls: true,
|
|
3551
|
+
reasoning: {
|
|
3552
|
+
effort: getReasoningEffortForModel(model),
|
|
3553
|
+
summary: "auto"
|
|
3554
|
+
},
|
|
3555
|
+
include: ["reasoning.encrypted_content"]
|
|
3602
3556
|
};
|
|
3603
3557
|
};
|
|
3604
|
-
const
|
|
3605
|
-
|
|
3606
|
-
|
|
3558
|
+
const encodeCompactionCarrierSignature = (compaction) => {
|
|
3559
|
+
return `${COMPACTION_SIGNATURE_PREFIX}${compaction.encrypted_content}${COMPACTION_SIGNATURE_SEPARATOR}${compaction.id}`;
|
|
3560
|
+
};
|
|
3561
|
+
const decodeCompactionCarrierSignature = (signature) => {
|
|
3562
|
+
if (signature.startsWith(COMPACTION_SIGNATURE_PREFIX)) {
|
|
3563
|
+
const raw = signature.slice(4);
|
|
3564
|
+
const separatorIndex = raw.indexOf(COMPACTION_SIGNATURE_SEPARATOR);
|
|
3565
|
+
if (separatorIndex <= 0 || separatorIndex === raw.length - 1) return;
|
|
3566
|
+
const encrypted_content = raw.slice(0, separatorIndex);
|
|
3567
|
+
const id = raw.slice(separatorIndex + 1);
|
|
3568
|
+
if (!encrypted_content) return;
|
|
3607
3569
|
return {
|
|
3608
|
-
|
|
3609
|
-
|
|
3570
|
+
id,
|
|
3571
|
+
encrypted_content
|
|
3610
3572
|
};
|
|
3611
3573
|
}
|
|
3612
|
-
return {
|
|
3613
|
-
...toolResult,
|
|
3614
|
-
content: [...toolResult.content, ...textBlocks]
|
|
3615
|
-
};
|
|
3616
3574
|
};
|
|
3617
|
-
const
|
|
3618
|
-
if (
|
|
3619
|
-
|
|
3620
|
-
return toolResults.map((toolResult, index) => index === lastIndex ? mergeContentWithTexts(toolResult, textBlocks) : toolResult);
|
|
3575
|
+
const translateMessage = (message, model, applyPhase) => {
|
|
3576
|
+
if (message.role === "user") return translateUserMessage(message);
|
|
3577
|
+
return translateAssistantMessage(message, model, applyPhase);
|
|
3621
3578
|
};
|
|
3622
|
-
const
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
break;
|
|
3579
|
+
const translateUserMessage = (message) => {
|
|
3580
|
+
if (typeof message.content === "string") return [createMessage("user", message.content)];
|
|
3581
|
+
if (!Array.isArray(message.content)) return [];
|
|
3582
|
+
const items = [];
|
|
3583
|
+
const pendingContent = [];
|
|
3584
|
+
for (const block of message.content) {
|
|
3585
|
+
if (block.type === "tool_result") {
|
|
3586
|
+
flushPendingContent(pendingContent, items, { role: "user" });
|
|
3587
|
+
items.push(createFunctionCallOutput(block));
|
|
3588
|
+
continue;
|
|
3633
3589
|
}
|
|
3634
|
-
|
|
3635
|
-
|
|
3590
|
+
const converted = translateUserContentBlock(block);
|
|
3591
|
+
if (converted) pendingContent.push(converted);
|
|
3636
3592
|
}
|
|
3593
|
+
flushPendingContent(pendingContent, items, { role: "user" });
|
|
3594
|
+
return items;
|
|
3637
3595
|
};
|
|
3638
|
-
const
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3596
|
+
const translateAssistantMessage = (message, model, applyPhase) => {
|
|
3597
|
+
const assistantPhase = resolveAssistantPhase(model, message.content, applyPhase);
|
|
3598
|
+
if (typeof message.content === "string") return [createMessage("assistant", message.content, assistantPhase)];
|
|
3599
|
+
if (!Array.isArray(message.content)) return [];
|
|
3600
|
+
const items = [];
|
|
3601
|
+
const pendingContent = [];
|
|
3602
|
+
for (const block of message.content) {
|
|
3603
|
+
if (block.type === "tool_use") {
|
|
3604
|
+
flushPendingContent(pendingContent, items, {
|
|
3605
|
+
role: "assistant",
|
|
3606
|
+
phase: assistantPhase
|
|
3607
|
+
});
|
|
3608
|
+
items.push(createFunctionToolCall(block));
|
|
3609
|
+
continue;
|
|
3610
|
+
}
|
|
3611
|
+
if (block.type === "thinking" && block.signature) {
|
|
3612
|
+
const compactionContent = createCompactionContent(block);
|
|
3613
|
+
if (compactionContent) {
|
|
3614
|
+
flushPendingContent(pendingContent, items, {
|
|
3615
|
+
role: "assistant",
|
|
3616
|
+
phase: assistantPhase
|
|
3617
|
+
});
|
|
3618
|
+
items.push(compactionContent);
|
|
3619
|
+
continue;
|
|
3620
|
+
}
|
|
3621
|
+
if (block.signature.includes("@")) {
|
|
3622
|
+
flushPendingContent(pendingContent, items, {
|
|
3623
|
+
role: "assistant",
|
|
3624
|
+
phase: assistantPhase
|
|
3625
|
+
});
|
|
3626
|
+
items.push(createReasoningContent(block));
|
|
3627
|
+
continue;
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3630
|
+
const converted = translateAssistantContentBlock(block);
|
|
3631
|
+
if (converted) pendingContent.push(converted);
|
|
3644
3632
|
}
|
|
3633
|
+
flushPendingContent(pendingContent, items, {
|
|
3634
|
+
role: "assistant",
|
|
3635
|
+
phase: assistantPhase
|
|
3636
|
+
});
|
|
3637
|
+
return items;
|
|
3645
3638
|
};
|
|
3646
|
-
const
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
const text = lastBlock.text.trim().toLowerCase();
|
|
3652
|
-
if (!(lastBlock.cache_control?.type === "ephemeral")) return false;
|
|
3653
|
-
if (text === "warmup") return true;
|
|
3654
|
-
if (text === "hello") {
|
|
3655
|
-
const preludeBlocks = lastMsg.content.slice(0, -1);
|
|
3656
|
-
if (preludeBlocks.length === 0) return false;
|
|
3657
|
-
return preludeBlocks.every((block) => block.type === "text" && block.text.trimStart().toLowerCase().startsWith("<system-reminder"));
|
|
3639
|
+
const translateUserContentBlock = (block) => {
|
|
3640
|
+
switch (block.type) {
|
|
3641
|
+
case "text": return createTextContent(block.text);
|
|
3642
|
+
case "image": return createImageContent(block);
|
|
3643
|
+
default: return;
|
|
3658
3644
|
}
|
|
3659
|
-
return false;
|
|
3660
3645
|
};
|
|
3661
|
-
const
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
if (
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
return
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3646
|
+
const translateAssistantContentBlock = (block) => {
|
|
3647
|
+
switch (block.type) {
|
|
3648
|
+
case "text": return createOutPutTextContent(block.text);
|
|
3649
|
+
default: return;
|
|
3650
|
+
}
|
|
3651
|
+
};
|
|
3652
|
+
const flushPendingContent = (pendingContent, target, message) => {
|
|
3653
|
+
if (pendingContent.length === 0) return;
|
|
3654
|
+
const messageContent = [...pendingContent];
|
|
3655
|
+
target.push(createMessage(message.role, messageContent, message.phase));
|
|
3656
|
+
pendingContent.length = 0;
|
|
3657
|
+
};
|
|
3658
|
+
const createMessage = (role, content, phase) => ({
|
|
3659
|
+
type: MESSAGE_TYPE,
|
|
3660
|
+
role,
|
|
3661
|
+
content,
|
|
3662
|
+
...role === "assistant" && phase ? { phase } : {}
|
|
3663
|
+
});
|
|
3664
|
+
const resolveAssistantPhase = (_model, content, applyPhase) => {
|
|
3665
|
+
if (!applyPhase) return;
|
|
3666
|
+
if (typeof content === "string") return "final_answer";
|
|
3667
|
+
if (!Array.isArray(content)) return;
|
|
3668
|
+
if (!content.some((block) => block.type === "text")) return;
|
|
3669
|
+
return content.some((block) => block.type === "tool_use") ? "commentary" : "final_answer";
|
|
3670
|
+
};
|
|
3671
|
+
const shouldApplyPhase = (model) => {
|
|
3672
|
+
return getExtraPromptForModel(model).includes("## Intermediary updates");
|
|
3673
|
+
};
|
|
3674
|
+
const createTextContent = (text) => ({
|
|
3675
|
+
type: "input_text",
|
|
3676
|
+
text
|
|
3677
|
+
});
|
|
3678
|
+
const createOutPutTextContent = (text) => ({
|
|
3679
|
+
type: "output_text",
|
|
3680
|
+
text
|
|
3681
|
+
});
|
|
3682
|
+
const createImageContent = (block) => ({
|
|
3683
|
+
type: "input_image",
|
|
3684
|
+
image_url: `data:${block.source.media_type};base64,${block.source.data}`,
|
|
3685
|
+
detail: "auto"
|
|
3686
|
+
});
|
|
3687
|
+
const createReasoningContent = (block) => {
|
|
3688
|
+
const { encryptedContent, id } = parseReasoningSignature(block.signature);
|
|
3689
|
+
const thinking = block.thinking === THINKING_TEXT$1 ? "" : block.thinking;
|
|
3690
|
+
return {
|
|
3691
|
+
id,
|
|
3692
|
+
type: "reasoning",
|
|
3693
|
+
summary: thinking ? [{
|
|
3694
|
+
type: "summary_text",
|
|
3695
|
+
text: thinking
|
|
3696
|
+
}] : [],
|
|
3697
|
+
encrypted_content: encryptedContent
|
|
3698
|
+
};
|
|
3691
3699
|
};
|
|
3692
|
-
const
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
});
|
|
3700
|
+
const createCompactionContent = (block) => {
|
|
3701
|
+
const compaction = decodeCompactionCarrierSignature(block.signature);
|
|
3702
|
+
if (!compaction) return;
|
|
3703
|
+
return {
|
|
3704
|
+
id: compaction.id,
|
|
3705
|
+
type: "compaction",
|
|
3706
|
+
encrypted_content: compaction.encrypted_content
|
|
3707
|
+
};
|
|
3701
3708
|
};
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
const model = state.models?.data.find((m) => m.id === modelId);
|
|
3709
|
-
const thinkingBudget = getThinkingBudget(payload, model);
|
|
3709
|
+
const parseReasoningSignature = (signature) => {
|
|
3710
|
+
const splitIndex = signature.lastIndexOf("@");
|
|
3711
|
+
if (splitIndex <= 0 || splitIndex === signature.length - 1) return {
|
|
3712
|
+
encryptedContent: signature,
|
|
3713
|
+
id: ""
|
|
3714
|
+
};
|
|
3710
3715
|
return {
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
max_tokens: payload.max_tokens,
|
|
3714
|
-
stop: payload.stop_sequences,
|
|
3715
|
-
stream: payload.stream,
|
|
3716
|
-
temperature: payload.temperature,
|
|
3717
|
-
top_p: payload.top_p,
|
|
3718
|
-
user: payload.metadata?.user_id,
|
|
3719
|
-
tools: translateAnthropicToolsToOpenAI(payload.tools),
|
|
3720
|
-
tool_choice: translateAnthropicToolChoiceToOpenAI(payload.tool_choice),
|
|
3721
|
-
thinking_budget: thinkingBudget
|
|
3716
|
+
encryptedContent: signature.slice(0, splitIndex),
|
|
3717
|
+
id: signature.slice(splitIndex + 1)
|
|
3722
3718
|
};
|
|
3723
|
-
}
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
return
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
}).join("\n\n")
|
|
3751
|
-
}];
|
|
3752
|
-
}
|
|
3753
|
-
function handleUserMessage(message) {
|
|
3754
|
-
const newMessages = [];
|
|
3755
|
-
if (Array.isArray(message.content)) {
|
|
3756
|
-
const toolResultBlocks = message.content.filter((block) => block.type === "tool_result");
|
|
3757
|
-
const otherBlocks = message.content.filter((block) => block.type !== "tool_result");
|
|
3758
|
-
for (const block of toolResultBlocks) newMessages.push({
|
|
3759
|
-
role: "tool",
|
|
3760
|
-
tool_call_id: block.tool_use_id,
|
|
3761
|
-
content: mapContent(block.content)
|
|
3762
|
-
});
|
|
3763
|
-
if (otherBlocks.length > 0) newMessages.push({
|
|
3764
|
-
role: "user",
|
|
3765
|
-
content: mapContent(otherBlocks)
|
|
3766
|
-
});
|
|
3767
|
-
} else newMessages.push({
|
|
3768
|
-
role: "user",
|
|
3769
|
-
content: mapContent(message.content)
|
|
3770
|
-
});
|
|
3771
|
-
return newMessages;
|
|
3772
|
-
}
|
|
3773
|
-
function handleAssistantMessage(message, modelId) {
|
|
3774
|
-
if (!Array.isArray(message.content)) return [{
|
|
3775
|
-
role: "assistant",
|
|
3776
|
-
content: mapContent(message.content)
|
|
3777
|
-
}];
|
|
3778
|
-
const toolUseBlocks = message.content.filter((block) => block.type === "tool_use");
|
|
3779
|
-
let thinkingBlocks = message.content.filter((block) => block.type === "thinking");
|
|
3780
|
-
if (modelId.startsWith("claude")) thinkingBlocks = thinkingBlocks.filter((b) => b.thinking && b.thinking !== THINKING_TEXT && b.signature && !b.signature.includes("@"));
|
|
3781
|
-
const thinkingContents = thinkingBlocks.filter((b) => b.thinking && b.thinking !== THINKING_TEXT).map((b) => b.thinking);
|
|
3782
|
-
const allThinkingContent = thinkingContents.length > 0 ? thinkingContents.join("\n\n") : void 0;
|
|
3783
|
-
const signature = thinkingBlocks.find((b) => b.signature)?.signature;
|
|
3784
|
-
return toolUseBlocks.length > 0 ? [{
|
|
3785
|
-
role: "assistant",
|
|
3786
|
-
content: mapContent(message.content),
|
|
3787
|
-
reasoning_text: allThinkingContent,
|
|
3788
|
-
reasoning_opaque: signature,
|
|
3789
|
-
tool_calls: toolUseBlocks.map((toolUse) => ({
|
|
3790
|
-
id: toolUse.id,
|
|
3791
|
-
type: "function",
|
|
3792
|
-
function: {
|
|
3793
|
-
name: toolUse.name,
|
|
3794
|
-
arguments: JSON.stringify(toolUse.input)
|
|
3795
|
-
}
|
|
3796
|
-
}))
|
|
3797
|
-
}] : [{
|
|
3798
|
-
role: "assistant",
|
|
3799
|
-
content: mapContent(message.content),
|
|
3800
|
-
reasoning_text: allThinkingContent,
|
|
3801
|
-
reasoning_opaque: signature
|
|
3802
|
-
}];
|
|
3803
|
-
}
|
|
3804
|
-
function mapContent(content) {
|
|
3805
|
-
if (typeof content === "string") return content;
|
|
3806
|
-
if (!Array.isArray(content)) return null;
|
|
3807
|
-
if (!content.some((block) => block.type === "image")) return content.filter((block) => block.type === "text").map((block) => block.text).join("\n\n");
|
|
3808
|
-
const contentParts = [];
|
|
3809
|
-
for (const block of content) switch (block.type) {
|
|
3810
|
-
case "text":
|
|
3811
|
-
contentParts.push({
|
|
3812
|
-
type: "text",
|
|
3813
|
-
text: block.text
|
|
3814
|
-
});
|
|
3815
|
-
break;
|
|
3816
|
-
case "image":
|
|
3817
|
-
contentParts.push({
|
|
3818
|
-
type: "image_url",
|
|
3819
|
-
image_url: { url: `data:${block.source.media_type};base64,${block.source.data}` }
|
|
3820
|
-
});
|
|
3821
|
-
break;
|
|
3822
|
-
}
|
|
3823
|
-
return contentParts;
|
|
3824
|
-
}
|
|
3825
|
-
function translateAnthropicToolsToOpenAI(anthropicTools) {
|
|
3826
|
-
if (!anthropicTools) return;
|
|
3827
|
-
return anthropicTools.map((tool) => ({
|
|
3719
|
+
};
|
|
3720
|
+
const createFunctionToolCall = (block) => ({
|
|
3721
|
+
type: "function_call",
|
|
3722
|
+
call_id: block.id,
|
|
3723
|
+
name: block.name,
|
|
3724
|
+
arguments: JSON.stringify(block.input),
|
|
3725
|
+
status: "completed"
|
|
3726
|
+
});
|
|
3727
|
+
const createFunctionCallOutput = (block) => ({
|
|
3728
|
+
type: "function_call_output",
|
|
3729
|
+
call_id: block.tool_use_id,
|
|
3730
|
+
output: convertToolResultContent(block.content),
|
|
3731
|
+
status: block.is_error ? "incomplete" : "completed"
|
|
3732
|
+
});
|
|
3733
|
+
const translateSystemPrompt = (system, model) => {
|
|
3734
|
+
if (!system) return null;
|
|
3735
|
+
const extraPrompt = getExtraPromptForModel(model);
|
|
3736
|
+
if (typeof system === "string") return system + extraPrompt;
|
|
3737
|
+
const text = system.map((block, index) => {
|
|
3738
|
+
if (index === 0) return block.text + extraPrompt;
|
|
3739
|
+
return block.text;
|
|
3740
|
+
}).join(" ");
|
|
3741
|
+
return text.length > 0 ? text : null;
|
|
3742
|
+
};
|
|
3743
|
+
const convertAnthropicTools = (tools) => {
|
|
3744
|
+
if (!tools || tools.length === 0) return null;
|
|
3745
|
+
return tools.map((tool) => ({
|
|
3828
3746
|
type: "function",
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
}
|
|
3747
|
+
name: tool.name,
|
|
3748
|
+
parameters: normalizeToolSchema(tool.input_schema),
|
|
3749
|
+
strict: false,
|
|
3750
|
+
...tool.description ? { description: tool.description } : {}
|
|
3834
3751
|
}));
|
|
3835
|
-
}
|
|
3836
|
-
|
|
3837
|
-
if (!
|
|
3838
|
-
switch (
|
|
3752
|
+
};
|
|
3753
|
+
const convertAnthropicToolChoice = (choice) => {
|
|
3754
|
+
if (!choice) return "auto";
|
|
3755
|
+
switch (choice.type) {
|
|
3839
3756
|
case "auto": return "auto";
|
|
3840
3757
|
case "any": return "required";
|
|
3841
|
-
case "tool":
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
};
|
|
3846
|
-
return;
|
|
3758
|
+
case "tool": return choice.name ? {
|
|
3759
|
+
type: "function",
|
|
3760
|
+
name: choice.name
|
|
3761
|
+
} : "auto";
|
|
3847
3762
|
case "none": return "none";
|
|
3848
|
-
default: return;
|
|
3849
|
-
}
|
|
3850
|
-
}
|
|
3851
|
-
function translateToAnthropic(response) {
|
|
3852
|
-
const assistantContentBlocks = [];
|
|
3853
|
-
let stopReason = response.choices[0]?.finish_reason ?? null;
|
|
3854
|
-
for (const choice of response.choices) {
|
|
3855
|
-
const textBlocks = getAnthropicTextBlocks(choice.message.content);
|
|
3856
|
-
const thinkBlocks = getAnthropicThinkBlocks(choice.message.reasoning_text, choice.message.reasoning_opaque);
|
|
3857
|
-
const toolUseBlocks = getAnthropicToolUseBlocks(choice.message.tool_calls);
|
|
3858
|
-
assistantContentBlocks.push(...thinkBlocks, ...textBlocks, ...toolUseBlocks);
|
|
3859
|
-
if (choice.finish_reason === "tool_calls" || stopReason === "stop") stopReason = choice.finish_reason;
|
|
3763
|
+
default: return "auto";
|
|
3860
3764
|
}
|
|
3765
|
+
};
|
|
3766
|
+
const translateResponsesResultToAnthropic = (response) => {
|
|
3767
|
+
const contentBlocks = mapOutputToAnthropicContent(response.output);
|
|
3768
|
+
const usage = mapResponsesUsage(response);
|
|
3769
|
+
let anthropicContent = fallbackContentBlocks(response.output_text);
|
|
3770
|
+
if (contentBlocks.length > 0) anthropicContent = contentBlocks;
|
|
3771
|
+
const stopReason = mapResponsesStopReason(response);
|
|
3861
3772
|
return {
|
|
3862
3773
|
id: response.id,
|
|
3863
3774
|
type: "message",
|
|
3864
3775
|
role: "assistant",
|
|
3776
|
+
content: anthropicContent,
|
|
3865
3777
|
model: response.model,
|
|
3866
|
-
|
|
3867
|
-
stop_reason: mapOpenAIStopReasonToAnthropic(stopReason),
|
|
3778
|
+
stop_reason: stopReason,
|
|
3868
3779
|
stop_sequence: null,
|
|
3869
|
-
usage
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3780
|
+
usage
|
|
3781
|
+
};
|
|
3782
|
+
};
|
|
3783
|
+
const mapOutputToAnthropicContent = (output) => {
|
|
3784
|
+
const contentBlocks = [];
|
|
3785
|
+
for (const item of output) switch (item.type) {
|
|
3786
|
+
case "reasoning": {
|
|
3787
|
+
const thinkingText = extractReasoningText(item);
|
|
3788
|
+
if (thinkingText.length > 0) contentBlocks.push({
|
|
3789
|
+
type: "thinking",
|
|
3790
|
+
thinking: thinkingText,
|
|
3791
|
+
signature: (item.encrypted_content ?? "") + "@" + item.id
|
|
3792
|
+
});
|
|
3793
|
+
break;
|
|
3794
|
+
}
|
|
3795
|
+
case "function_call": {
|
|
3796
|
+
const toolUseBlock = createToolUseContentBlock(item);
|
|
3797
|
+
if (toolUseBlock) contentBlocks.push(toolUseBlock);
|
|
3798
|
+
break;
|
|
3799
|
+
}
|
|
3800
|
+
case "message": {
|
|
3801
|
+
const combinedText = combineMessageTextContent(item.content);
|
|
3802
|
+
if (combinedText.length > 0) contentBlocks.push({
|
|
3803
|
+
type: "text",
|
|
3804
|
+
text: combinedText
|
|
3805
|
+
});
|
|
3806
|
+
break;
|
|
3807
|
+
}
|
|
3808
|
+
case "compaction": {
|
|
3809
|
+
const compactionBlock = createCompactionThinkingBlock(item);
|
|
3810
|
+
if (compactionBlock) contentBlocks.push(compactionBlock);
|
|
3811
|
+
break;
|
|
3812
|
+
}
|
|
3813
|
+
default: {
|
|
3814
|
+
const combinedText = combineMessageTextContent(item.content);
|
|
3815
|
+
if (combinedText.length > 0) contentBlocks.push({
|
|
3816
|
+
type: "text",
|
|
3817
|
+
text: combinedText
|
|
3818
|
+
});
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3821
|
+
return contentBlocks;
|
|
3822
|
+
};
|
|
3823
|
+
const combineMessageTextContent = (content) => {
|
|
3824
|
+
if (!Array.isArray(content)) return "";
|
|
3825
|
+
let aggregated = "";
|
|
3826
|
+
for (const block of content) {
|
|
3827
|
+
if (isResponseOutputText(block)) {
|
|
3828
|
+
aggregated += block.text;
|
|
3829
|
+
continue;
|
|
3830
|
+
}
|
|
3831
|
+
if (isResponseOutputRefusal(block)) {
|
|
3832
|
+
aggregated += block.refusal;
|
|
3833
|
+
continue;
|
|
3834
|
+
}
|
|
3835
|
+
if (typeof block.text === "string") {
|
|
3836
|
+
aggregated += block.text;
|
|
3837
|
+
continue;
|
|
3838
|
+
}
|
|
3839
|
+
if (typeof block.reasoning === "string") {
|
|
3840
|
+
aggregated += block.reasoning;
|
|
3841
|
+
continue;
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
3844
|
+
return aggregated;
|
|
3845
|
+
};
|
|
3846
|
+
const extractReasoningText = (item) => {
|
|
3847
|
+
const segments = [];
|
|
3848
|
+
const collectFromBlocks = (blocks) => {
|
|
3849
|
+
if (!Array.isArray(blocks)) return;
|
|
3850
|
+
for (const block of blocks) if (typeof block.text === "string") {
|
|
3851
|
+
segments.push(block.text);
|
|
3852
|
+
continue;
|
|
3873
3853
|
}
|
|
3874
3854
|
};
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
if (
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
}));
|
|
3885
|
-
return [];
|
|
3886
|
-
}
|
|
3887
|
-
function getAnthropicThinkBlocks(reasoningText, reasoningOpaque) {
|
|
3888
|
-
if (reasoningText && reasoningText.length > 0) return [{
|
|
3889
|
-
type: "thinking",
|
|
3890
|
-
thinking: reasoningText,
|
|
3891
|
-
signature: reasoningOpaque || ""
|
|
3892
|
-
}];
|
|
3893
|
-
if (reasoningOpaque && reasoningOpaque.length > 0) return [{
|
|
3894
|
-
type: "thinking",
|
|
3895
|
-
thinking: THINKING_TEXT,
|
|
3896
|
-
signature: reasoningOpaque
|
|
3897
|
-
}];
|
|
3898
|
-
return [];
|
|
3899
|
-
}
|
|
3900
|
-
function getAnthropicToolUseBlocks(toolCalls) {
|
|
3901
|
-
if (!toolCalls) return [];
|
|
3902
|
-
return toolCalls.map((toolCall) => ({
|
|
3855
|
+
if (!item.summary || item.summary.length === 0) return THINKING_TEXT$1;
|
|
3856
|
+
collectFromBlocks(item.summary);
|
|
3857
|
+
return segments.join("").trim();
|
|
3858
|
+
};
|
|
3859
|
+
const createToolUseContentBlock = (call) => {
|
|
3860
|
+
const toolId = call.call_id;
|
|
3861
|
+
if (!call.name || !toolId) return null;
|
|
3862
|
+
const input = parseFunctionCallArguments(call.arguments);
|
|
3863
|
+
return {
|
|
3903
3864
|
type: "tool_use",
|
|
3904
|
-
id:
|
|
3905
|
-
name:
|
|
3906
|
-
input
|
|
3907
|
-
}
|
|
3908
|
-
}
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3865
|
+
id: toolId,
|
|
3866
|
+
name: call.name,
|
|
3867
|
+
input
|
|
3868
|
+
};
|
|
3869
|
+
};
|
|
3870
|
+
const createCompactionThinkingBlock = (item) => {
|
|
3871
|
+
if (!item.id || !item.encrypted_content) return null;
|
|
3872
|
+
return {
|
|
3873
|
+
type: "thinking",
|
|
3874
|
+
thinking: THINKING_TEXT$1,
|
|
3875
|
+
signature: encodeCompactionCarrierSignature({
|
|
3876
|
+
id: item.id,
|
|
3877
|
+
encrypted_content: item.encrypted_content
|
|
3878
|
+
})
|
|
3879
|
+
};
|
|
3880
|
+
};
|
|
3881
|
+
const parseFunctionCallArguments = (rawArguments) => {
|
|
3882
|
+
if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return {};
|
|
3916
3883
|
try {
|
|
3917
|
-
const
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
const selectedModel = findEndpointModel(anthropicPayload.model);
|
|
3921
|
-
anthropicPayload.model = selectedModel?.id ?? anthropicPayload.model;
|
|
3922
|
-
if (!selectedModel) {
|
|
3923
|
-
consola.warn("Model not found, returning default token count");
|
|
3924
|
-
return c.json({ input_tokens: 1 });
|
|
3925
|
-
}
|
|
3926
|
-
const tokenCount = await getTokenCount(openAIPayload, selectedModel);
|
|
3927
|
-
if (anthropicPayload.tools && anthropicPayload.tools.length > 0) {
|
|
3928
|
-
let addToolSystemPromptCount = false;
|
|
3929
|
-
if (anthropicBeta) {
|
|
3930
|
-
const toolsLength = anthropicPayload.tools.length;
|
|
3931
|
-
addToolSystemPromptCount = !anthropicPayload.tools.some((tool) => tool.name.startsWith("mcp__") || tool.name === "Skill" && toolsLength === 1);
|
|
3932
|
-
}
|
|
3933
|
-
if (addToolSystemPromptCount) {
|
|
3934
|
-
if (anthropicPayload.model.startsWith("claude")) tokenCount.input = tokenCount.input + 346;
|
|
3935
|
-
else if (anthropicPayload.model.startsWith("grok")) tokenCount.input = tokenCount.input + 120;
|
|
3936
|
-
}
|
|
3937
|
-
}
|
|
3938
|
-
let finalTokenCount = tokenCount.input + tokenCount.output;
|
|
3939
|
-
if (anthropicPayload.model.startsWith("claude")) finalTokenCount = Math.round(finalTokenCount * 1.15);
|
|
3940
|
-
consola.info("Token count:", finalTokenCount);
|
|
3941
|
-
return c.json({ input_tokens: finalTokenCount });
|
|
3884
|
+
const parsed = JSON.parse(rawArguments);
|
|
3885
|
+
if (Array.isArray(parsed)) return { arguments: parsed };
|
|
3886
|
+
if (parsed && typeof parsed === "object") return parsed;
|
|
3942
3887
|
} catch (error) {
|
|
3943
|
-
consola.
|
|
3944
|
-
|
|
3888
|
+
consola.warn("Failed to parse function call arguments", {
|
|
3889
|
+
error,
|
|
3890
|
+
rawArguments
|
|
3891
|
+
});
|
|
3945
3892
|
}
|
|
3946
|
-
}
|
|
3893
|
+
return { raw_arguments: rawArguments };
|
|
3894
|
+
};
|
|
3895
|
+
const fallbackContentBlocks = (outputText) => {
|
|
3896
|
+
if (!outputText) return [];
|
|
3897
|
+
return [{
|
|
3898
|
+
type: "text",
|
|
3899
|
+
text: outputText
|
|
3900
|
+
}];
|
|
3901
|
+
};
|
|
3902
|
+
const mapResponsesStopReason = (response) => {
|
|
3903
|
+
const { status, incomplete_details: incompleteDetails } = response;
|
|
3904
|
+
if (status === "completed") {
|
|
3905
|
+
if (response.output.some((item) => item.type === "function_call")) return "tool_use";
|
|
3906
|
+
return "end_turn";
|
|
3907
|
+
}
|
|
3908
|
+
if (status === "incomplete") {
|
|
3909
|
+
if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
|
|
3910
|
+
if (incompleteDetails?.reason === "content_filter") return "end_turn";
|
|
3911
|
+
}
|
|
3912
|
+
return null;
|
|
3913
|
+
};
|
|
3914
|
+
const mapResponsesUsage = (response) => {
|
|
3915
|
+
const inputTokens = response.usage?.input_tokens ?? 0;
|
|
3916
|
+
const outputTokens = response.usage?.output_tokens ?? 0;
|
|
3917
|
+
const inputCachedTokens = response.usage?.input_tokens_details?.cached_tokens;
|
|
3918
|
+
return {
|
|
3919
|
+
input_tokens: inputTokens - (inputCachedTokens ?? 0),
|
|
3920
|
+
output_tokens: outputTokens,
|
|
3921
|
+
...response.usage?.input_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: response.usage.input_tokens_details.cached_tokens }
|
|
3922
|
+
};
|
|
3923
|
+
};
|
|
3924
|
+
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
3925
|
+
const isResponseOutputText = (block) => isRecord(block) && "type" in block && block.type === "output_text";
|
|
3926
|
+
const isResponseOutputRefusal = (block) => isRecord(block) && "type" in block && block.type === "refusal";
|
|
3927
|
+
const convertToolResultContent = (content) => {
|
|
3928
|
+
if (typeof content === "string") return content;
|
|
3929
|
+
if (Array.isArray(content)) {
|
|
3930
|
+
const result = [];
|
|
3931
|
+
for (const block of content) switch (block.type) {
|
|
3932
|
+
case "text":
|
|
3933
|
+
result.push(createTextContent(block.text));
|
|
3934
|
+
break;
|
|
3935
|
+
case "image":
|
|
3936
|
+
result.push(createImageContent(block));
|
|
3937
|
+
break;
|
|
3938
|
+
default: break;
|
|
3939
|
+
}
|
|
3940
|
+
return result;
|
|
3941
|
+
}
|
|
3942
|
+
return "";
|
|
3943
|
+
};
|
|
3947
3944
|
|
|
3948
3945
|
//#endregion
|
|
3949
3946
|
//#region src/routes/messages/responses-stream-translation.ts
|
|
@@ -4834,7 +4831,7 @@ async function handleCompletion(c) {
|
|
|
4834
4831
|
const streamRequested = Boolean(anthropicPayload.stream);
|
|
4835
4832
|
const rawUserId = anthropicPayload.metadata?.user_id;
|
|
4836
4833
|
const userId = typeof rawUserId === "string" ? rawUserId : void 0;
|
|
4837
|
-
const { safetyIdentifier, promptCacheKey } =
|
|
4834
|
+
const { safetyIdentifier, sessionId: promptCacheKey } = parseUserIdMetadata(userId);
|
|
4838
4835
|
const normalizedSafetyIdentifier = safetyIdentifier ?? void 0;
|
|
4839
4836
|
const normalizedPromptCacheKey = promptCacheKey ?? void 0;
|
|
4840
4837
|
const blockedResponse = maybeBlockOriginalModelName({
|
|
@@ -5865,7 +5862,7 @@ const handleResponses = async (c) => {
|
|
|
5865
5862
|
const streamRequested = Boolean(payload.stream);
|
|
5866
5863
|
const { initiator: initialInitiator } = getResponsesRequestOptions(payload);
|
|
5867
5864
|
const userId = payload.metadata?.user_id;
|
|
5868
|
-
const { safetyIdentifier, promptCacheKey } =
|
|
5865
|
+
const { safetyIdentifier, sessionId: promptCacheKey } = parseUserIdMetadata(userId);
|
|
5869
5866
|
const normalizedSafetyIdentifier = safetyIdentifier ?? void 0;
|
|
5870
5867
|
const normalizedPromptCacheKey = promptCacheKey ?? void 0;
|
|
5871
5868
|
request.userId = userId;
|
|
@@ -6398,4 +6395,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
|
|
|
6398
6395
|
|
|
6399
6396
|
//#endregion
|
|
6400
6397
|
export { server };
|
|
6401
|
-
//# sourceMappingURL=server-
|
|
6398
|
+
//# sourceMappingURL=server-CKtDlwHW.js.map
|