@jeffreycao/copilot-api 1.9.4 → 1.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -6
- package/README.zh-CN.md +24 -6
- package/dist/{config-BQvWqYh_.js → config-BJt9unC0.js} +12 -8
- package/dist/{config-BQvWqYh_.js.map → config-BJt9unC0.js.map} +1 -1
- package/dist/main.js +1 -1
- package/dist/{server-D1nq9oGf.js → server-BiAUjFEP.js} +309 -53
- package/dist/server-BiAUjFEP.js.map +1 -0
- package/dist/{start-1ogeufrV.js → start-CEjvjjF9.js} +3 -3
- package/dist/{start-1ogeufrV.js.map → start-CEjvjjF9.js.map} +1 -1
- package/package.json +1 -1
- package/dist/server-D1nq9oGf.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PATHS } from "./paths-Cla6y5eD.js";
|
|
2
2
|
import { COMPACT_AUTO_CONTINUE, COMPACT_REQUEST, HTTPError, cacheModels, compactAutoContinuePromptStarts, compactMessageSections, compactSummaryPromptStart, compactSystemPromptStarts, compactTextOnlyGuard, copilotBaseUrl, copilotHeaders, forwardError, generateRequestIdFromPayload, generateTraceId, getCopilotUsage, getRootSessionId, getUUID, isNullish, parseUserIdMetadata, prepareForCompact, prepareInteractionHeaders, prepareMessageProxyHeaders, requestContext, resolveTraceId as resolveTraceId$1, sleep, state } from "./utils-DEJvF68W.js";
|
|
3
|
-
import { getAnthropicApiKey, getClaudeTokenMultiplier, getConfig, getExtraPromptForModel, getProviderConfig, getReasoningEffortForModel, getSmallModel, isMessagesApiEnabled, isResponsesApiContextManagementModel, isResponsesApiWebSearchEnabled } from "./config-
|
|
3
|
+
import { getAnthropicApiKey, getClaudeTokenMultiplier, getConfig, getExtraPromptForModel, getProviderConfig, getReasoningEffortForModel, getSmallModel, isMessagesApiEnabled, isResponsesApiContextManagementModel, isResponsesApiWebSearchEnabled } from "./config-BJt9unC0.js";
|
|
4
4
|
import consola from "consola";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
6
|
import path from "node:path";
|
|
@@ -1052,7 +1052,10 @@ const calculateToolCallsTokens = (toolCalls, encoder, constants) => {
|
|
|
1052
1052
|
const calculateContentPartsTokens = (contentParts, encoder) => {
|
|
1053
1053
|
let tokens = 0;
|
|
1054
1054
|
for (const part of contentParts) if (part.type === "image_url") tokens += encoder.encode(part.image_url.url).length + 85;
|
|
1055
|
-
else if (part.
|
|
1055
|
+
else if (part.type === "file") {
|
|
1056
|
+
tokens += encoder.encode(part.file.file_data).length;
|
|
1057
|
+
if (part.file.filename) tokens += encoder.encode(part.file.filename).length;
|
|
1058
|
+
} else if (part.text) tokens += encoder.encode(part.text).length;
|
|
1056
1059
|
return tokens;
|
|
1057
1060
|
};
|
|
1058
1061
|
/**
|
|
@@ -1304,13 +1307,19 @@ function mapOpenAIStopReasonToAnthropic(finishReason) {
|
|
|
1304
1307
|
//#endregion
|
|
1305
1308
|
//#region src/routes/messages/non-stream-translation.ts
|
|
1306
1309
|
const THINKING_TEXT = "Thinking...";
|
|
1307
|
-
|
|
1310
|
+
const RICH_TOOL_RESULT_MOVED_TEXT = "Rich tool result content was moved to a user message because this upstream does not support it in tool messages.";
|
|
1311
|
+
const COPILOT_TOOL_CONTENT_SUPPORT_TYPE = ["array", "image"];
|
|
1312
|
+
function translateToOpenAI(payload, options = {}) {
|
|
1308
1313
|
const modelId = payload.model;
|
|
1309
1314
|
const model = state.models?.data.find((m) => m.id === modelId);
|
|
1310
1315
|
const thinkingBudget = getThinkingBudget(payload, model);
|
|
1316
|
+
const capabilities = {
|
|
1317
|
+
supportPdf: options.supportPdf ?? false,
|
|
1318
|
+
toolContentSupportType: options.toolContentSupportType ?? COPILOT_TOOL_CONTENT_SUPPORT_TYPE
|
|
1319
|
+
};
|
|
1311
1320
|
return {
|
|
1312
1321
|
model: modelId,
|
|
1313
|
-
messages: translateAnthropicMessagesToOpenAI(payload, modelId,
|
|
1322
|
+
messages: translateAnthropicMessagesToOpenAI(payload, modelId, capabilities),
|
|
1314
1323
|
max_tokens: payload.max_tokens,
|
|
1315
1324
|
stop: payload.stop_sequences,
|
|
1316
1325
|
stream: payload.stream,
|
|
@@ -1333,9 +1342,9 @@ function getThinkingBudget(payload, model) {
|
|
|
1333
1342
|
}
|
|
1334
1343
|
}
|
|
1335
1344
|
}
|
|
1336
|
-
function translateAnthropicMessagesToOpenAI(payload, modelId,
|
|
1345
|
+
function translateAnthropicMessagesToOpenAI(payload, modelId, capabilities) {
|
|
1337
1346
|
const systemMessages = handleSystemPrompt(payload.system);
|
|
1338
|
-
const otherMessages = payload.messages.flatMap((message) => message.role === "user" ? handleUserMessage(message) : handleAssistantMessage(message, modelId));
|
|
1347
|
+
const otherMessages = payload.messages.flatMap((message) => message.role === "user" ? handleUserMessage(message, capabilities) : handleAssistantMessage(message, modelId, capabilities));
|
|
1339
1348
|
return [...systemMessages, ...otherMessages];
|
|
1340
1349
|
}
|
|
1341
1350
|
function handleSystemPrompt(system) {
|
|
@@ -1351,19 +1360,21 @@ function handleSystemPrompt(system) {
|
|
|
1351
1360
|
}).join("\n\n")
|
|
1352
1361
|
}];
|
|
1353
1362
|
}
|
|
1354
|
-
function handleUserMessage(message) {
|
|
1363
|
+
function handleUserMessage(message, capabilities) {
|
|
1355
1364
|
const newMessages = [];
|
|
1356
1365
|
if (Array.isArray(message.content)) {
|
|
1357
1366
|
const toolResultBlocks = message.content.filter((block) => block.type === "tool_result");
|
|
1358
1367
|
const otherBlocks = message.content.filter((block) => block.type !== "tool_result");
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1368
|
+
const movedToolResultUserMessages = [];
|
|
1369
|
+
for (const block of toolResultBlocks) {
|
|
1370
|
+
const result = handleToolResultBlock(block, capabilities);
|
|
1371
|
+
newMessages.push(result.toolMessage);
|
|
1372
|
+
if (result.movedUserMessage) movedToolResultUserMessages.push(result.movedUserMessage);
|
|
1373
|
+
}
|
|
1374
|
+
newMessages.push(...movedToolResultUserMessages);
|
|
1364
1375
|
if (otherBlocks.length > 0) newMessages.push({
|
|
1365
1376
|
role: "user",
|
|
1366
|
-
content: mapContent(otherBlocks)
|
|
1377
|
+
content: mapContent(otherBlocks, { supportPdf: capabilities.supportPdf })
|
|
1367
1378
|
});
|
|
1368
1379
|
} else newMessages.push({
|
|
1369
1380
|
role: "user",
|
|
@@ -1371,7 +1382,61 @@ function handleUserMessage(message) {
|
|
|
1371
1382
|
});
|
|
1372
1383
|
return newMessages;
|
|
1373
1384
|
}
|
|
1374
|
-
function
|
|
1385
|
+
function handleToolResultBlock(block, capabilities) {
|
|
1386
|
+
if (typeof block.content === "string") return { toolMessage: createToolMessage(block.tool_use_id, block.content) };
|
|
1387
|
+
if (!Array.isArray(block.content)) return { toolMessage: createToolMessage(block.tool_use_id, "") };
|
|
1388
|
+
const support = getToolContentSupport(capabilities);
|
|
1389
|
+
const hasImage = block.content.some((block$1) => block$1.type === "image");
|
|
1390
|
+
const hasDocument = block.content.some((block$1) => block$1.type === "document");
|
|
1391
|
+
const content = mapContent(block.content, { supportPdf: capabilities.supportPdf });
|
|
1392
|
+
const hasPdfFile = hasDocument && capabilities.supportPdf;
|
|
1393
|
+
const shouldMoveImageToUserMessage = hasImage && !support.image;
|
|
1394
|
+
const shouldMovePdfToUserMessage = hasPdfFile && !support.pdf;
|
|
1395
|
+
if (shouldMoveImageToUserMessage || shouldMovePdfToUserMessage) return {
|
|
1396
|
+
movedUserMessage: createToolResultUserMessage(block, capabilities.supportPdf),
|
|
1397
|
+
toolMessage: createToolMessage(block.tool_use_id, getTextToolContent(content) || RICH_TOOL_RESULT_MOVED_TEXT)
|
|
1398
|
+
};
|
|
1399
|
+
const hasRichContent = hasImage || hasPdfFile;
|
|
1400
|
+
if (support.array || hasRichContent) return { toolMessage: createToolMessage(block.tool_use_id, content) };
|
|
1401
|
+
return { toolMessage: createToolMessage(block.tool_use_id, getTextToolContent(content)) };
|
|
1402
|
+
}
|
|
1403
|
+
function getTextToolContent(content) {
|
|
1404
|
+
if (!Array.isArray(content)) return content ?? "";
|
|
1405
|
+
return content.flatMap((part) => part.type === "text" && part.text.length > 0 ? [part.text] : []).join("\n");
|
|
1406
|
+
}
|
|
1407
|
+
function getToolContentSupport(capabilities) {
|
|
1408
|
+
return {
|
|
1409
|
+
array: capabilities.toolContentSupportType.includes("array"),
|
|
1410
|
+
image: capabilities.toolContentSupportType.includes("image"),
|
|
1411
|
+
pdf: capabilities.supportPdf && capabilities.toolContentSupportType.includes("pdf")
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
function createToolMessage(toolCallId, content) {
|
|
1415
|
+
return {
|
|
1416
|
+
role: "tool",
|
|
1417
|
+
tool_call_id: toolCallId,
|
|
1418
|
+
content
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
function createToolResultUserMessage(block, supportPdf) {
|
|
1422
|
+
const prefix = {
|
|
1423
|
+
type: "text",
|
|
1424
|
+
text: `Tool result for ${block.tool_use_id}:`
|
|
1425
|
+
};
|
|
1426
|
+
const content = mapContent(block.content, { supportPdf });
|
|
1427
|
+
if (Array.isArray(content)) return {
|
|
1428
|
+
role: "user",
|
|
1429
|
+
content: [prefix, ...content]
|
|
1430
|
+
};
|
|
1431
|
+
return {
|
|
1432
|
+
role: "user",
|
|
1433
|
+
content: [prefix, {
|
|
1434
|
+
type: "text",
|
|
1435
|
+
text: content ?? ""
|
|
1436
|
+
}]
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
function handleAssistantMessage(message, modelId, capabilities) {
|
|
1375
1440
|
if (!Array.isArray(message.content)) return [{
|
|
1376
1441
|
role: "assistant",
|
|
1377
1442
|
content: mapContent(message.content)
|
|
@@ -1384,7 +1449,7 @@ function handleAssistantMessage(message, modelId) {
|
|
|
1384
1449
|
const signature = thinkingBlocks.find((b) => b.signature)?.signature;
|
|
1385
1450
|
return toolUseBlocks.length > 0 ? [{
|
|
1386
1451
|
role: "assistant",
|
|
1387
|
-
content: mapContent(message.content),
|
|
1452
|
+
content: mapContent(message.content, { supportPdf: capabilities.supportPdf }),
|
|
1388
1453
|
reasoning_text: allThinkingContent,
|
|
1389
1454
|
reasoning_opaque: signature,
|
|
1390
1455
|
tool_calls: toolUseBlocks.map((toolUse) => ({
|
|
@@ -1397,12 +1462,12 @@ function handleAssistantMessage(message, modelId) {
|
|
|
1397
1462
|
}))
|
|
1398
1463
|
}] : [{
|
|
1399
1464
|
role: "assistant",
|
|
1400
|
-
content: mapContent(message.content),
|
|
1465
|
+
content: mapContent(message.content, { supportPdf: capabilities.supportPdf }),
|
|
1401
1466
|
reasoning_text: allThinkingContent,
|
|
1402
1467
|
reasoning_opaque: signature
|
|
1403
1468
|
}];
|
|
1404
1469
|
}
|
|
1405
|
-
function mapContent(content) {
|
|
1470
|
+
function mapContent(content, options = {}) {
|
|
1406
1471
|
if (typeof content === "string") return content;
|
|
1407
1472
|
if (!Array.isArray(content)) return null;
|
|
1408
1473
|
const contentParts = [];
|
|
@@ -1420,7 +1485,7 @@ function mapContent(content) {
|
|
|
1420
1485
|
});
|
|
1421
1486
|
break;
|
|
1422
1487
|
case "document":
|
|
1423
|
-
contentParts.push(createDocumentTextPart());
|
|
1488
|
+
contentParts.push(options.supportPdf ? createDocumentFilePart(block) : createDocumentTextPart());
|
|
1424
1489
|
break;
|
|
1425
1490
|
case "tool_reference":
|
|
1426
1491
|
contentParts.push({
|
|
@@ -1434,7 +1499,16 @@ function mapContent(content) {
|
|
|
1434
1499
|
function createDocumentTextPart() {
|
|
1435
1500
|
return {
|
|
1436
1501
|
type: "text",
|
|
1437
|
-
text: "
|
|
1502
|
+
text: "PDF/document content is not supported by this Chat Completions upstream. Use the available text extracted from the document."
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
function createDocumentFilePart(block) {
|
|
1506
|
+
return {
|
|
1507
|
+
type: "file",
|
|
1508
|
+
file: {
|
|
1509
|
+
file_data: `data:${block.source.media_type};base64,${block.source.data}`,
|
|
1510
|
+
filename: block.title ?? "document.pdf"
|
|
1511
|
+
}
|
|
1438
1512
|
};
|
|
1439
1513
|
}
|
|
1440
1514
|
function translateAnthropicToolsToOpenAI(anthropicTools) {
|
|
@@ -1479,7 +1553,7 @@ function translateToAnthropic(response) {
|
|
|
1479
1553
|
let stopReason = response.choices[0]?.finish_reason ?? null;
|
|
1480
1554
|
for (const choice of response.choices) {
|
|
1481
1555
|
const textBlocks = getAnthropicTextBlocks(choice.message.content);
|
|
1482
|
-
const thinkBlocks = getAnthropicThinkBlocks(choice.message
|
|
1556
|
+
const thinkBlocks = getAnthropicThinkBlocks(getOpenAIReasoningText(choice.message), choice.message.reasoning_opaque);
|
|
1483
1557
|
const toolUseBlocks = getAnthropicToolUseBlocks(choice.message.tool_calls);
|
|
1484
1558
|
assistantContentBlocks.push(...thinkBlocks, ...textBlocks, ...toolUseBlocks);
|
|
1485
1559
|
if (choice.finish_reason === "tool_calls" || stopReason === "stop") stopReason = choice.finish_reason;
|
|
@@ -1499,6 +1573,9 @@ function translateToAnthropic(response) {
|
|
|
1499
1573
|
}
|
|
1500
1574
|
};
|
|
1501
1575
|
}
|
|
1576
|
+
function getOpenAIReasoningText(message) {
|
|
1577
|
+
return message.reasoning_text ?? message.reasoning_content;
|
|
1578
|
+
}
|
|
1502
1579
|
function getAnthropicTextBlocks(messageContent) {
|
|
1503
1580
|
if (typeof messageContent === "string" && messageContent.length > 0) return [{
|
|
1504
1581
|
type: "text",
|
|
@@ -2860,7 +2937,10 @@ function isToolBlockOpen(state$1) {
|
|
|
2860
2937
|
}
|
|
2861
2938
|
function translateChunkToAnthropicEvents(chunk, state$1) {
|
|
2862
2939
|
const events$1 = [];
|
|
2863
|
-
if (chunk.choices.length === 0)
|
|
2940
|
+
if (chunk.choices.length === 0) {
|
|
2941
|
+
completePendingMessage(state$1, events$1, chunk);
|
|
2942
|
+
return events$1;
|
|
2943
|
+
}
|
|
2864
2944
|
const choice = chunk.choices[0];
|
|
2865
2945
|
const { delta } = choice;
|
|
2866
2946
|
handleMessageStart(state$1, events$1, chunk);
|
|
@@ -2873,6 +2953,17 @@ function translateChunkToAnthropicEvents(chunk, state$1) {
|
|
|
2873
2953
|
});
|
|
2874
2954
|
return events$1;
|
|
2875
2955
|
}
|
|
2956
|
+
function flushPendingAnthropicStreamEvents(state$1) {
|
|
2957
|
+
const events$1 = [];
|
|
2958
|
+
completePendingMessage(state$1, events$1);
|
|
2959
|
+
return events$1;
|
|
2960
|
+
}
|
|
2961
|
+
function completePendingMessage(state$1, events$1, chunk) {
|
|
2962
|
+
if (!state$1.pendingMessageDelta) return;
|
|
2963
|
+
if (chunk?.usage) state$1.pendingMessageDelta.usage = getAnthropicUsageFromOpenAIChunk(chunk);
|
|
2964
|
+
events$1.push(state$1.pendingMessageDelta, { type: "message_stop" });
|
|
2965
|
+
state$1.pendingMessageDelta = void 0;
|
|
2966
|
+
}
|
|
2876
2967
|
function handleFinish(choice, state$1, context) {
|
|
2877
2968
|
const { events: events$1, chunk } = context;
|
|
2878
2969
|
if (choice.finish_reason && choice.finish_reason.length > 0) {
|
|
@@ -2886,20 +2977,24 @@ function handleFinish(choice, state$1, context) {
|
|
|
2886
2977
|
state$1.contentBlockIndex++;
|
|
2887
2978
|
if (!toolBlockOpen) handleReasoningOpaque(choice.delta, events$1, state$1);
|
|
2888
2979
|
}
|
|
2889
|
-
|
|
2980
|
+
state$1.pendingMessageDelta = {
|
|
2890
2981
|
type: "message_delta",
|
|
2891
2982
|
delta: {
|
|
2892
2983
|
stop_reason: mapOpenAIStopReasonToAnthropic(choice.finish_reason),
|
|
2893
2984
|
stop_sequence: null
|
|
2894
2985
|
},
|
|
2895
|
-
usage:
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
...chunk.usage?.prompt_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: chunk.usage.prompt_tokens_details.cached_tokens }
|
|
2899
|
-
}
|
|
2900
|
-
}, { type: "message_stop" });
|
|
2986
|
+
usage: getAnthropicUsageFromOpenAIChunk(chunk)
|
|
2987
|
+
};
|
|
2988
|
+
if (chunk.usage) completePendingMessage(state$1, events$1, chunk);
|
|
2901
2989
|
}
|
|
2902
2990
|
}
|
|
2991
|
+
function getAnthropicUsageFromOpenAIChunk(chunk) {
|
|
2992
|
+
return {
|
|
2993
|
+
input_tokens: (chunk.usage?.prompt_tokens ?? 0) - (chunk.usage?.prompt_tokens_details?.cached_tokens ?? 0),
|
|
2994
|
+
output_tokens: chunk.usage?.completion_tokens ?? 0,
|
|
2995
|
+
...chunk.usage?.prompt_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: chunk.usage.prompt_tokens_details.cached_tokens }
|
|
2996
|
+
};
|
|
2997
|
+
}
|
|
2903
2998
|
function handleToolCalls(delta, state$1, events$1) {
|
|
2904
2999
|
if (delta.tool_calls && delta.tool_calls.length > 0) {
|
|
2905
3000
|
closeThinkingBlockIfOpen(state$1, events$1);
|
|
@@ -3057,10 +3152,12 @@ function handleReasoningOpaque(delta, events$1, state$1) {
|
|
|
3057
3152
|
}
|
|
3058
3153
|
}
|
|
3059
3154
|
function handleThinkingText(delta, state$1, events$1) {
|
|
3060
|
-
|
|
3155
|
+
const reasoningText = delta.reasoning_text ?? delta.reasoning_content;
|
|
3156
|
+
if (reasoningText && reasoningText.length > 0) {
|
|
3061
3157
|
if (state$1.contentBlockOpen) {
|
|
3062
|
-
delta.content =
|
|
3158
|
+
delta.content = reasoningText;
|
|
3063
3159
|
delta.reasoning_text = void 0;
|
|
3160
|
+
delta.reasoning_content = void 0;
|
|
3064
3161
|
return;
|
|
3065
3162
|
}
|
|
3066
3163
|
if (!state$1.thinkingBlockOpen) {
|
|
@@ -3079,7 +3176,7 @@ function handleThinkingText(delta, state$1, events$1) {
|
|
|
3079
3176
|
index: state$1.contentBlockIndex,
|
|
3080
3177
|
delta: {
|
|
3081
3178
|
type: "thinking_delta",
|
|
3082
|
-
thinking:
|
|
3179
|
+
thinking: reasoningText
|
|
3083
3180
|
}
|
|
3084
3181
|
});
|
|
3085
3182
|
}
|
|
@@ -3153,6 +3250,14 @@ const handleWithChatCompletions = async (c, anthropicPayload, options) => {
|
|
|
3153
3250
|
});
|
|
3154
3251
|
}
|
|
3155
3252
|
}
|
|
3253
|
+
for (const event of flushPendingAnthropicStreamEvents(streamState)) {
|
|
3254
|
+
const eventData = JSON.stringify(event);
|
|
3255
|
+
debugLazy(logger$7, () => ["Translated Anthropic event:", eventData]);
|
|
3256
|
+
await stream.writeSSE({
|
|
3257
|
+
event: event.type,
|
|
3258
|
+
data: eventData
|
|
3259
|
+
});
|
|
3260
|
+
}
|
|
3156
3261
|
recordUsage(usage);
|
|
3157
3262
|
});
|
|
3158
3263
|
};
|
|
@@ -3419,16 +3524,19 @@ const modelRoutes = new Hono();
|
|
|
3419
3524
|
modelRoutes.get("/", async (c) => {
|
|
3420
3525
|
try {
|
|
3421
3526
|
if (!state.models) await cacheModels();
|
|
3422
|
-
const models = state.models?.data.map((model) =>
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3527
|
+
const models = state.models?.data.map((model) => {
|
|
3528
|
+
const is1m = model.capabilities.limits?.max_context_window_tokens === 1e6;
|
|
3529
|
+
return {
|
|
3530
|
+
...model,
|
|
3531
|
+
id: is1m ? `${model.id}[1m]` : model.id,
|
|
3532
|
+
object: "model",
|
|
3533
|
+
type: "model",
|
|
3534
|
+
created: 0,
|
|
3535
|
+
created_at: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
3536
|
+
owned_by: model.vendor,
|
|
3537
|
+
display_name: model.name
|
|
3538
|
+
};
|
|
3539
|
+
});
|
|
3432
3540
|
return c.json({
|
|
3433
3541
|
object: "list",
|
|
3434
3542
|
data: models,
|
|
@@ -3463,8 +3571,14 @@ async function handleProviderCountTokens(c) {
|
|
|
3463
3571
|
const provider = c.req.param("provider");
|
|
3464
3572
|
try {
|
|
3465
3573
|
const anthropicPayload = await c.req.json();
|
|
3466
|
-
const openAIPayload = translateToOpenAI(anthropicPayload);
|
|
3467
3574
|
const modelId = anthropicPayload.model.trim();
|
|
3575
|
+
const providerConfig = getProviderConfig(provider);
|
|
3576
|
+
const modelConfig = providerConfig?.models?.[modelId];
|
|
3577
|
+
const translationOptions = providerConfig?.type === "openai-compatible" ? {
|
|
3578
|
+
supportPdf: modelConfig?.supportPdf,
|
|
3579
|
+
toolContentSupportType: modelConfig?.toolContentSupportType ?? []
|
|
3580
|
+
} : void 0;
|
|
3581
|
+
const openAIPayload = translateToOpenAI(anthropicPayload, translationOptions);
|
|
3468
3582
|
let selectedModel = state.models?.data.find((model) => model.id === modelId);
|
|
3469
3583
|
if (!selectedModel && modelId) selectedModel = createFallbackModel(modelId);
|
|
3470
3584
|
if (!selectedModel) {
|
|
@@ -3493,12 +3607,8 @@ async function handleProviderCountTokens(c) {
|
|
|
3493
3607
|
|
|
3494
3608
|
//#endregion
|
|
3495
3609
|
//#region src/services/providers/anthropic-proxy.ts
|
|
3496
|
-
const
|
|
3497
|
-
|
|
3498
|
-
"anthropic-beta",
|
|
3499
|
-
"accept",
|
|
3500
|
-
"user-agent"
|
|
3501
|
-
];
|
|
3610
|
+
const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
|
|
3611
|
+
const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
|
|
3502
3612
|
const STRIPPED_RESPONSE_HEADERS = [
|
|
3503
3613
|
"connection",
|
|
3504
3614
|
"content-encoding",
|
|
@@ -3520,7 +3630,12 @@ function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
|
|
|
3520
3630
|
accept: "application/json",
|
|
3521
3631
|
...authHeaders
|
|
3522
3632
|
};
|
|
3523
|
-
for (const headerName of
|
|
3633
|
+
for (const headerName of SHARED_FORWARDABLE_HEADERS) {
|
|
3634
|
+
const headerValue = requestHeaders.get(headerName);
|
|
3635
|
+
if (headerValue) headers[headerName] = headerValue;
|
|
3636
|
+
}
|
|
3637
|
+
if (providerConfig.type !== "anthropic") return headers;
|
|
3638
|
+
for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
|
|
3524
3639
|
const headerValue = requestHeaders.get(headerName);
|
|
3525
3640
|
if (headerValue) headers[headerName] = headerValue;
|
|
3526
3641
|
}
|
|
@@ -3542,6 +3657,13 @@ async function forwardProviderMessages(providerConfig, payload, requestHeaders)
|
|
|
3542
3657
|
body: JSON.stringify(payload)
|
|
3543
3658
|
});
|
|
3544
3659
|
}
|
|
3660
|
+
async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
|
|
3661
|
+
return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
|
|
3662
|
+
method: "POST",
|
|
3663
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
3664
|
+
body: JSON.stringify(payload)
|
|
3665
|
+
});
|
|
3666
|
+
}
|
|
3545
3667
|
async function forwardProviderModels(providerConfig, requestHeaders) {
|
|
3546
3668
|
return await fetch(`${providerConfig.baseUrl}/v1/models`, {
|
|
3547
3669
|
method: "GET",
|
|
@@ -3562,13 +3684,18 @@ async function handleProviderMessages(c) {
|
|
|
3562
3684
|
try {
|
|
3563
3685
|
const payload = await c.req.json();
|
|
3564
3686
|
const modelConfig = providerConfig.models?.[payload.model];
|
|
3565
|
-
payload
|
|
3566
|
-
payload.top_p ??= modelConfig?.topP;
|
|
3567
|
-
payload.top_k ??= modelConfig?.topK;
|
|
3687
|
+
applyModelDefaults(payload, modelConfig);
|
|
3568
3688
|
debugJson(logger$3, "provider.messages.request", {
|
|
3569
3689
|
payload,
|
|
3570
3690
|
provider
|
|
3571
3691
|
});
|
|
3692
|
+
if (providerConfig.type === "openai-compatible") return await handleOpenAICompatibleProviderMessages(c, {
|
|
3693
|
+
modelConfig,
|
|
3694
|
+
payload,
|
|
3695
|
+
provider,
|
|
3696
|
+
providerConfig
|
|
3697
|
+
});
|
|
3698
|
+
applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
|
|
3572
3699
|
const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers);
|
|
3573
3700
|
if (!upstreamResponse.ok) {
|
|
3574
3701
|
logger$3.error("Failed to create responses", upstreamResponse);
|
|
@@ -3597,6 +3724,68 @@ async function handleProviderMessages(c) {
|
|
|
3597
3724
|
throw error;
|
|
3598
3725
|
}
|
|
3599
3726
|
}
|
|
3727
|
+
const applyModelDefaults = (payload, modelConfig) => {
|
|
3728
|
+
payload.temperature ??= modelConfig?.temperature;
|
|
3729
|
+
payload.top_p ??= modelConfig?.topP;
|
|
3730
|
+
payload.top_k ??= modelConfig?.topK;
|
|
3731
|
+
};
|
|
3732
|
+
const applyMissingExtraBody = (payload, options) => {
|
|
3733
|
+
for (const [key, value] of Object.entries(options.extraBody ?? {})) if (!Object.hasOwn(payload, key)) payload[key] = value;
|
|
3734
|
+
};
|
|
3735
|
+
const handleOpenAICompatibleProviderMessages = async (c, options) => {
|
|
3736
|
+
const { modelConfig, payload, provider, providerConfig } = options;
|
|
3737
|
+
const openAIPayload = createOpenAICompatiblePayload(payload, modelConfig);
|
|
3738
|
+
debugJson(logger$3, "provider.messages.openai_compatible.request", {
|
|
3739
|
+
payload: openAIPayload,
|
|
3740
|
+
provider
|
|
3741
|
+
});
|
|
3742
|
+
const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers);
|
|
3743
|
+
if (!upstreamResponse.ok) {
|
|
3744
|
+
logger$3.error("Failed to create openai-compatible responses", upstreamResponse);
|
|
3745
|
+
throw new HTTPError("Failed to create openai-compatible responses", upstreamResponse);
|
|
3746
|
+
}
|
|
3747
|
+
const contentType = upstreamResponse.headers.get("content-type") ?? "";
|
|
3748
|
+
if (Boolean(openAIPayload.stream) && contentType.includes("text/event-stream")) return streamOpenAICompatibleProviderMessages({
|
|
3749
|
+
c,
|
|
3750
|
+
payload,
|
|
3751
|
+
provider,
|
|
3752
|
+
upstreamResponse
|
|
3753
|
+
});
|
|
3754
|
+
const jsonBody = await upstreamResponse.json();
|
|
3755
|
+
return respondOpenAICompatibleProviderMessagesJson(c, {
|
|
3756
|
+
body: jsonBody,
|
|
3757
|
+
payload,
|
|
3758
|
+
provider
|
|
3759
|
+
});
|
|
3760
|
+
};
|
|
3761
|
+
const createOpenAICompatiblePayload = (payload, modelConfig) => {
|
|
3762
|
+
const openAIPayload = translateToOpenAI(payload, {
|
|
3763
|
+
supportPdf: modelConfig?.supportPdf,
|
|
3764
|
+
toolContentSupportType: modelConfig?.toolContentSupportType ?? []
|
|
3765
|
+
});
|
|
3766
|
+
if (payload.top_k !== void 0) openAIPayload.top_k = payload.top_k;
|
|
3767
|
+
if (openAIPayload.stream) openAIPayload.stream_options = { include_usage: true };
|
|
3768
|
+
normalizeOpenAICompatibleReasoningContent(openAIPayload);
|
|
3769
|
+
applyOpenAICompatibleRequestOverrides(openAIPayload, {
|
|
3770
|
+
extraBody: modelConfig?.extraBody,
|
|
3771
|
+
source: payload
|
|
3772
|
+
});
|
|
3773
|
+
applyMissingExtraBody(openAIPayload, { extraBody: modelConfig?.extraBody });
|
|
3774
|
+
if (!Object.hasOwn(openAIPayload, "parallel_tool_calls")) openAIPayload.parallel_tool_calls = true;
|
|
3775
|
+
return openAIPayload;
|
|
3776
|
+
};
|
|
3777
|
+
const normalizeOpenAICompatibleReasoningContent = (payload) => {
|
|
3778
|
+
for (const message of payload.messages) {
|
|
3779
|
+
if (message.role !== "assistant") continue;
|
|
3780
|
+
if (message.reasoning_content === void 0 && message.reasoning_text !== void 0) message.reasoning_content = message.reasoning_text;
|
|
3781
|
+
delete message.reasoning_text;
|
|
3782
|
+
delete message.reasoning_opaque;
|
|
3783
|
+
}
|
|
3784
|
+
};
|
|
3785
|
+
const applyOpenAICompatibleRequestOverrides = (payload, options) => {
|
|
3786
|
+
const allowedKeys = new Set(Object.keys(options.extraBody ?? {}));
|
|
3787
|
+
for (const key of allowedKeys) if (Object.hasOwn(options.source, key)) payload[key] = options.source[key];
|
|
3788
|
+
};
|
|
3600
3789
|
const streamProviderMessages = ({ c, payload, provider, providerConfig, upstreamResponse }) => {
|
|
3601
3790
|
logger$3.debug("provider.messages.streaming");
|
|
3602
3791
|
const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
|
|
@@ -3628,6 +3817,66 @@ const streamProviderMessages = ({ c, payload, provider, providerConfig, upstream
|
|
|
3628
3817
|
recordUsage(usage);
|
|
3629
3818
|
});
|
|
3630
3819
|
};
|
|
3820
|
+
const streamOpenAICompatibleProviderMessages = ({ c, payload, provider, upstreamResponse }) => {
|
|
3821
|
+
logger$3.debug("provider.messages.openai_compatible.streaming");
|
|
3822
|
+
const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
|
|
3823
|
+
return streamSSE(c, async (stream) => {
|
|
3824
|
+
let usage = {};
|
|
3825
|
+
const streamState = {
|
|
3826
|
+
messageStartSent: false,
|
|
3827
|
+
contentBlockIndex: 0,
|
|
3828
|
+
contentBlockOpen: false,
|
|
3829
|
+
toolCalls: {},
|
|
3830
|
+
thinkingBlockOpen: false
|
|
3831
|
+
};
|
|
3832
|
+
for await (const chunk of events(upstreamResponse)) {
|
|
3833
|
+
logger$3.debug("provider.messages.openai_compatible.raw_stream_event:", chunk.data);
|
|
3834
|
+
if (chunk.event === "ping") {
|
|
3835
|
+
await stream.writeSSE({
|
|
3836
|
+
event: "ping",
|
|
3837
|
+
data: "{\"type\":\"ping\"}"
|
|
3838
|
+
});
|
|
3839
|
+
continue;
|
|
3840
|
+
}
|
|
3841
|
+
if (!chunk.data || chunk.data === "[DONE]") {
|
|
3842
|
+
if (chunk.data === "[DONE]") break;
|
|
3843
|
+
continue;
|
|
3844
|
+
}
|
|
3845
|
+
const parsed = parseOpenAICompatibleStreamChunk(chunk.data);
|
|
3846
|
+
if (!parsed) continue;
|
|
3847
|
+
if (parsed.usage) usage = normalizeOpenAIUsage(parsed.usage);
|
|
3848
|
+
const events$1 = translateChunkToAnthropicEvents(parsed, streamState);
|
|
3849
|
+
for (const event of events$1) {
|
|
3850
|
+
const eventData = JSON.stringify(event);
|
|
3851
|
+
debugLazy(logger$3, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
|
|
3852
|
+
await stream.writeSSE({
|
|
3853
|
+
event: event.type,
|
|
3854
|
+
data: eventData
|
|
3855
|
+
});
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3858
|
+
for (const event of flushPendingAnthropicStreamEvents(streamState)) {
|
|
3859
|
+
const eventData = JSON.stringify(event);
|
|
3860
|
+
debugLazy(logger$3, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
|
|
3861
|
+
await stream.writeSSE({
|
|
3862
|
+
event: event.type,
|
|
3863
|
+
data: eventData
|
|
3864
|
+
});
|
|
3865
|
+
}
|
|
3866
|
+
recordUsage(usage);
|
|
3867
|
+
});
|
|
3868
|
+
};
|
|
3869
|
+
const parseOpenAICompatibleStreamChunk = (data) => {
|
|
3870
|
+
try {
|
|
3871
|
+
return JSON.parse(data);
|
|
3872
|
+
} catch (error) {
|
|
3873
|
+
logger$3.error("provider.messages.openai_compatible.parse_chunk_error", {
|
|
3874
|
+
data,
|
|
3875
|
+
error
|
|
3876
|
+
});
|
|
3877
|
+
return null;
|
|
3878
|
+
}
|
|
3879
|
+
};
|
|
3631
3880
|
const parseProviderStreamEvent = (data, providerConfig) => {
|
|
3632
3881
|
try {
|
|
3633
3882
|
const parsed = JSON.parse(data);
|
|
@@ -3666,6 +3915,13 @@ const respondProviderMessagesJson = (c, options) => {
|
|
|
3666
3915
|
debugJson(logger$3, "provider.messages.no_stream result:", body);
|
|
3667
3916
|
return c.json(body);
|
|
3668
3917
|
};
|
|
3918
|
+
const respondOpenAICompatibleProviderMessagesJson = (c, options) => {
|
|
3919
|
+
const { body, payload, provider } = options;
|
|
3920
|
+
createProviderMessagesUsageRecorder(payload, provider)(normalizeOpenAIUsage(body.usage));
|
|
3921
|
+
const anthropicResponse = translateToAnthropic(body);
|
|
3922
|
+
debugJson(logger$3, "provider.messages.openai_compatible.no_stream result:", anthropicResponse);
|
|
3923
|
+
return c.json(anthropicResponse);
|
|
3924
|
+
};
|
|
3669
3925
|
const createProviderMessagesUsageRecorder = (payload, provider) => createProviderTokenUsageRecorder({
|
|
3670
3926
|
endpoint: "provider_messages",
|
|
3671
3927
|
model: payload.model,
|
|
@@ -3985,4 +4241,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
|
|
|
3985
4241
|
|
|
3986
4242
|
//#endregion
|
|
3987
4243
|
export { server };
|
|
3988
|
-
//# sourceMappingURL=server-
|
|
4244
|
+
//# sourceMappingURL=server-BiAUjFEP.js.map
|