heibaiapi 0.6.2 → 0.7.0
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/LICENSE +21 -21
- package/README.md +351 -348
- package/dist/main.js +25 -943
- package/dist/main.js.map +1 -1
- package/package.json +68 -68
package/dist/main.js
CHANGED
@@ -5,11 +5,11 @@ import fs from "node:fs/promises";
|
|
5
5
|
import os from "node:os";
|
6
6
|
import path from "node:path";
|
7
7
|
import { randomUUID } from "node:crypto";
|
8
|
-
import { getProxyForUrl } from "proxy-from-env";
|
9
|
-
import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
|
10
8
|
import clipboard from "clipboardy";
|
11
9
|
import { serve } from "srvx";
|
12
10
|
import invariant from "tiny-invariant";
|
11
|
+
import { getProxyForUrl } from "proxy-from-env";
|
12
|
+
import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
|
13
13
|
import { execSync } from "node:child_process";
|
14
14
|
import process$1 from "node:process";
|
15
15
|
import { Hono } from "hono";
|
@@ -17,21 +17,17 @@ import { cors } from "hono/cors";
|
|
17
17
|
import { logger } from "hono/logger";
|
18
18
|
import { streamSSE } from "hono/streaming";
|
19
19
|
import { events } from "fetch-event-stream";
|
20
|
-
import fs$1 from "node:fs";
|
21
20
|
|
22
21
|
//#region src/lib/paths.ts
|
23
22
|
const APP_DIR = path.join(os.homedir(), ".local", "share", "copilot-api");
|
24
23
|
const GITHUB_TOKEN_PATH = path.join(APP_DIR, "github_token");
|
25
|
-
const CONFIG_PATH = path.join(APP_DIR, "config.json");
|
26
24
|
const PATHS = {
|
27
25
|
APP_DIR,
|
28
|
-
GITHUB_TOKEN_PATH
|
29
|
-
CONFIG_PATH
|
26
|
+
GITHUB_TOKEN_PATH
|
30
27
|
};
|
31
28
|
async function ensurePaths() {
|
32
29
|
await fs.mkdir(PATHS.APP_DIR, { recursive: true });
|
33
30
|
await ensureFile(PATHS.GITHUB_TOKEN_PATH);
|
34
|
-
await ensureFile(PATHS.CONFIG_PATH);
|
35
31
|
}
|
36
32
|
async function ensureFile(filePath) {
|
37
33
|
try {
|
@@ -796,10 +792,8 @@ async function handleCompletion$1(c) {
|
|
796
792
|
consola.debug("Request payload:", JSON.stringify(payload).slice(-400));
|
797
793
|
const selectedModel = state.models?.data.find((model) => model.id === payload.model);
|
798
794
|
try {
|
799
|
-
if (selectedModel) {
|
800
|
-
|
801
|
-
consola.info("Current token count:", tokenCount);
|
802
|
-
} else consola.warn("No model selected, skipping token count calculation");
|
795
|
+
if (selectedModel) consola.info(`[ModelTranslation_OpenAI] 客户端请求: ${payload.model} -> 服务端使用: ${selectedModel.id}`);
|
796
|
+
else consola.warn("No model selected, skipping token count calculation");
|
803
797
|
} catch (error) {
|
804
798
|
consola.warn("Failed to calculate token count:", error);
|
805
799
|
}
|
@@ -891,9 +885,14 @@ function translateToOpenAI(payload) {
|
|
891
885
|
};
|
892
886
|
}
|
893
887
|
function translateModelName(model) {
|
894
|
-
|
895
|
-
|
896
|
-
|
888
|
+
let serverModel;
|
889
|
+
serverModel = model;
|
890
|
+
if (model.includes("claude-3-5")) serverModel = "claude-3.5-sonnet";
|
891
|
+
else if (model.includes("claude-opus")) serverModel = "claude-sonnet-4.5";
|
892
|
+
else if (model.includes("claude-sonnet-4.5")) serverModel = "claude-sonnet-4.5";
|
893
|
+
else if (model.includes("claude-sonnet-4")) serverModel = "claude-sonnet-4";
|
894
|
+
consola.info(`[ModelTranslation] 客户端请求: ${model} -> 服务端使用: ${serverModel}`);
|
895
|
+
return serverModel;
|
897
896
|
}
|
898
897
|
function translateAnthropicMessagesToOpenAI(anthropicMessages, system) {
|
899
898
|
const systemMessages = handleSystemPrompt(system);
|
@@ -1084,7 +1083,6 @@ async function handleCountTokens(c) {
|
|
1084
1083
|
let finalTokenCount = tokenCount.input + tokenCount.output;
|
1085
1084
|
if (anthropicPayload.model.startsWith("claude")) finalTokenCount = Math.round(finalTokenCount * 1.15);
|
1086
1085
|
else if (anthropicPayload.model.startsWith("grok")) finalTokenCount = Math.round(finalTokenCount * 1.03);
|
1087
|
-
consola.info("Token count:", finalTokenCount);
|
1088
1086
|
return c.json({ input_tokens: finalTokenCount });
|
1089
1087
|
} catch (error) {
|
1090
1088
|
consola.error("Error counting tokens:", error);
|
@@ -1092,805 +1090,6 @@ async function handleCountTokens(c) {
|
|
1092
1090
|
}
|
1093
1091
|
}
|
1094
1092
|
|
1095
|
-
//#endregion
|
1096
|
-
//#region src/services/copilot/create-responses.ts
|
1097
|
-
const createResponses = async (payload, { vision, initiator }) => {
|
1098
|
-
if (!state.copilotToken) throw new Error("Copilot token not found");
|
1099
|
-
const headers = {
|
1100
|
-
...copilotHeaders(state, vision),
|
1101
|
-
"X-Initiator": initiator
|
1102
|
-
};
|
1103
|
-
const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
|
1104
|
-
method: "POST",
|
1105
|
-
headers,
|
1106
|
-
body: JSON.stringify(payload)
|
1107
|
-
});
|
1108
|
-
if (!response.ok) {
|
1109
|
-
consola.error("Failed to create responses", response);
|
1110
|
-
throw new HTTPError("Failed to create responses", response);
|
1111
|
-
}
|
1112
|
-
if (payload.stream) return events(response);
|
1113
|
-
return await response.json();
|
1114
|
-
};
|
1115
|
-
|
1116
|
-
//#endregion
|
1117
|
-
//#region src/lib/config.ts
|
1118
|
-
const defaultConfig = { extraPrompts: { "gpt-5-codex": `
|
1119
|
-
## Tool use
|
1120
|
-
- You have access to many tools. If a tool exists to perform a specific task, you MUST use that tool instead of running a terminal command to perform that task.
|
1121
|
-
### Bash tool
|
1122
|
-
When using the Bash tool, follow these rules:
|
1123
|
-
- always run_in_background set to false, unless you are running a long-running command (e.g., a server or a watch command).
|
1124
|
-
### BashOutput tool
|
1125
|
-
When using the BashOutput tool, follow these rules:
|
1126
|
-
- Only Bash Tool run_in_background set to true, Use BashOutput to read the output later
|
1127
|
-
### TodoWrite tool
|
1128
|
-
When using the TodoWrite tool, follow these rules:
|
1129
|
-
- Skip using the TodoWrite tool for tasks with three or fewer steps.
|
1130
|
-
- Do not make single-step todo lists.
|
1131
|
-
- When you made a todo, update it after having performed one of the sub-tasks that you shared on the todo list.
|
1132
|
-
## Special user requests
|
1133
|
-
- If the user makes a simple request (such as asking for the time) which you can fulfill by running a terminal command (such as 'date'), you should do so.
|
1134
|
-
` } };
|
1135
|
-
let cachedConfig = null;
|
1136
|
-
function ensureConfigFile() {
|
1137
|
-
try {
|
1138
|
-
fs$1.accessSync(PATHS.CONFIG_PATH, fs$1.constants.R_OK | fs$1.constants.W_OK);
|
1139
|
-
} catch {
|
1140
|
-
fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(defaultConfig, null, 2)}\n`, "utf8");
|
1141
|
-
try {
|
1142
|
-
fs$1.chmodSync(PATHS.CONFIG_PATH, 384);
|
1143
|
-
} catch {
|
1144
|
-
return;
|
1145
|
-
}
|
1146
|
-
}
|
1147
|
-
}
|
1148
|
-
function readConfigFromDisk() {
|
1149
|
-
ensureConfigFile();
|
1150
|
-
try {
|
1151
|
-
const raw = fs$1.readFileSync(PATHS.CONFIG_PATH, "utf8");
|
1152
|
-
if (!raw.trim()) {
|
1153
|
-
fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(defaultConfig, null, 2)}\n`, "utf8");
|
1154
|
-
return defaultConfig;
|
1155
|
-
}
|
1156
|
-
return JSON.parse(raw);
|
1157
|
-
} catch (error) {
|
1158
|
-
consola.error("Failed to read config file, using default config", error);
|
1159
|
-
return defaultConfig;
|
1160
|
-
}
|
1161
|
-
}
|
1162
|
-
function getConfig() {
|
1163
|
-
if (!cachedConfig) cachedConfig = readConfigFromDisk();
|
1164
|
-
return cachedConfig;
|
1165
|
-
}
|
1166
|
-
function getExtraPromptForModel(model) {
|
1167
|
-
return getConfig().extraPrompts?.[model] ?? "";
|
1168
|
-
}
|
1169
|
-
|
1170
|
-
//#endregion
|
1171
|
-
//#region src/routes/messages/responses-translation.ts
|
1172
|
-
const MESSAGE_TYPE = "message";
|
1173
|
-
const translateAnthropicMessagesToResponsesPayload = (payload) => {
|
1174
|
-
const input = [];
|
1175
|
-
for (const message of payload.messages) input.push(...translateMessage(message));
|
1176
|
-
const translatedTools = convertAnthropicTools(payload.tools);
|
1177
|
-
const toolChoice = convertAnthropicToolChoice(payload.tool_choice);
|
1178
|
-
const { safetyIdentifier, promptCacheKey } = parseUserId(payload.metadata?.user_id);
|
1179
|
-
return {
|
1180
|
-
model: payload.model,
|
1181
|
-
input,
|
1182
|
-
instructions: translateSystemPrompt(payload.system, payload.model),
|
1183
|
-
temperature: payload.temperature ?? null,
|
1184
|
-
top_p: payload.top_p ?? null,
|
1185
|
-
max_output_tokens: payload.max_tokens,
|
1186
|
-
tools: translatedTools,
|
1187
|
-
tool_choice: toolChoice,
|
1188
|
-
metadata: payload.metadata ? { ...payload.metadata } : null,
|
1189
|
-
safety_identifier: safetyIdentifier,
|
1190
|
-
prompt_cache_key: promptCacheKey,
|
1191
|
-
stream: payload.stream ?? null,
|
1192
|
-
store: false,
|
1193
|
-
parallel_tool_calls: true,
|
1194
|
-
reasoning: {
|
1195
|
-
effort: "high",
|
1196
|
-
summary: "detailed"
|
1197
|
-
},
|
1198
|
-
include: ["reasoning.encrypted_content"]
|
1199
|
-
};
|
1200
|
-
};
|
1201
|
-
const translateMessage = (message) => {
|
1202
|
-
if (message.role === "user") return translateUserMessage(message);
|
1203
|
-
return translateAssistantMessage(message);
|
1204
|
-
};
|
1205
|
-
const translateUserMessage = (message) => {
|
1206
|
-
if (typeof message.content === "string") return [createMessage("user", message.content)];
|
1207
|
-
if (!Array.isArray(message.content)) return [];
|
1208
|
-
const items = [];
|
1209
|
-
const pendingContent = [];
|
1210
|
-
for (const block of message.content) {
|
1211
|
-
if (block.type === "tool_result") {
|
1212
|
-
flushPendingContent("user", pendingContent, items);
|
1213
|
-
items.push(createFunctionCallOutput(block));
|
1214
|
-
continue;
|
1215
|
-
}
|
1216
|
-
const converted = translateUserContentBlock(block);
|
1217
|
-
if (converted) pendingContent.push(converted);
|
1218
|
-
}
|
1219
|
-
flushPendingContent("user", pendingContent, items);
|
1220
|
-
return items;
|
1221
|
-
};
|
1222
|
-
const translateAssistantMessage = (message) => {
|
1223
|
-
if (typeof message.content === "string") return [createMessage("assistant", message.content)];
|
1224
|
-
if (!Array.isArray(message.content)) return [];
|
1225
|
-
const items = [];
|
1226
|
-
const pendingContent = [];
|
1227
|
-
for (const block of message.content) {
|
1228
|
-
if (block.type === "tool_use") {
|
1229
|
-
flushPendingContent("assistant", pendingContent, items);
|
1230
|
-
items.push(createFunctionToolCall(block));
|
1231
|
-
continue;
|
1232
|
-
}
|
1233
|
-
if (block.type === "thinking" && block.signature && block.signature.includes("@")) {
|
1234
|
-
flushPendingContent("assistant", pendingContent, items);
|
1235
|
-
items.push(createReasoningContent(block));
|
1236
|
-
continue;
|
1237
|
-
}
|
1238
|
-
const converted = translateAssistantContentBlock(block);
|
1239
|
-
if (converted) pendingContent.push(converted);
|
1240
|
-
}
|
1241
|
-
flushPendingContent("assistant", pendingContent, items);
|
1242
|
-
return items;
|
1243
|
-
};
|
1244
|
-
const translateUserContentBlock = (block) => {
|
1245
|
-
switch (block.type) {
|
1246
|
-
case "text": return createTextContent(block.text);
|
1247
|
-
case "image": return createImageContent(block);
|
1248
|
-
default: return;
|
1249
|
-
}
|
1250
|
-
};
|
1251
|
-
const translateAssistantContentBlock = (block) => {
|
1252
|
-
switch (block.type) {
|
1253
|
-
case "text": return createOutPutTextContent(block.text);
|
1254
|
-
default: return;
|
1255
|
-
}
|
1256
|
-
};
|
1257
|
-
const flushPendingContent = (role, pendingContent, target) => {
|
1258
|
-
if (pendingContent.length === 0) return;
|
1259
|
-
const messageContent = [...pendingContent];
|
1260
|
-
target.push(createMessage(role, messageContent));
|
1261
|
-
pendingContent.length = 0;
|
1262
|
-
};
|
1263
|
-
const createMessage = (role, content) => ({
|
1264
|
-
type: MESSAGE_TYPE,
|
1265
|
-
role,
|
1266
|
-
content
|
1267
|
-
});
|
1268
|
-
const createTextContent = (text) => ({
|
1269
|
-
type: "input_text",
|
1270
|
-
text
|
1271
|
-
});
|
1272
|
-
const createOutPutTextContent = (text) => ({
|
1273
|
-
type: "output_text",
|
1274
|
-
text
|
1275
|
-
});
|
1276
|
-
const createImageContent = (block) => ({
|
1277
|
-
type: "input_image",
|
1278
|
-
image_url: `data:${block.source.media_type};base64,${block.source.data}`,
|
1279
|
-
detail: "auto"
|
1280
|
-
});
|
1281
|
-
const createReasoningContent = (block) => {
|
1282
|
-
const array = block.signature.split("@");
|
1283
|
-
const signature = array[0];
|
1284
|
-
return {
|
1285
|
-
id: array[1],
|
1286
|
-
type: "reasoning",
|
1287
|
-
summary: [{
|
1288
|
-
type: "summary_text",
|
1289
|
-
text: block.thinking
|
1290
|
-
}],
|
1291
|
-
encrypted_content: signature
|
1292
|
-
};
|
1293
|
-
};
|
1294
|
-
const createFunctionToolCall = (block) => ({
|
1295
|
-
type: "function_call",
|
1296
|
-
call_id: block.id,
|
1297
|
-
name: block.name,
|
1298
|
-
arguments: JSON.stringify(block.input),
|
1299
|
-
status: "completed"
|
1300
|
-
});
|
1301
|
-
const createFunctionCallOutput = (block) => ({
|
1302
|
-
type: "function_call_output",
|
1303
|
-
call_id: block.tool_use_id,
|
1304
|
-
output: convertToolResultContent(block.content),
|
1305
|
-
status: block.is_error ? "incomplete" : "completed"
|
1306
|
-
});
|
1307
|
-
const translateSystemPrompt = (system, model) => {
|
1308
|
-
if (!system) return null;
|
1309
|
-
const extraPrompt = getExtraPromptForModel(model);
|
1310
|
-
if (typeof system === "string") return system + extraPrompt;
|
1311
|
-
const text = system.map((block, index) => {
|
1312
|
-
if (index === 0) return block.text + extraPrompt;
|
1313
|
-
return block.text;
|
1314
|
-
}).join(" ");
|
1315
|
-
return text.length > 0 ? text : null;
|
1316
|
-
};
|
1317
|
-
const convertAnthropicTools = (tools) => {
|
1318
|
-
if (!tools || tools.length === 0) return null;
|
1319
|
-
return tools.map((tool) => ({
|
1320
|
-
type: "function",
|
1321
|
-
name: tool.name,
|
1322
|
-
parameters: tool.input_schema,
|
1323
|
-
strict: false,
|
1324
|
-
...tool.description ? { description: tool.description } : {}
|
1325
|
-
}));
|
1326
|
-
};
|
1327
|
-
const convertAnthropicToolChoice = (choice) => {
|
1328
|
-
if (!choice) return "auto";
|
1329
|
-
switch (choice.type) {
|
1330
|
-
case "auto": return "auto";
|
1331
|
-
case "any": return "required";
|
1332
|
-
case "tool": return choice.name ? {
|
1333
|
-
type: "function",
|
1334
|
-
name: choice.name
|
1335
|
-
} : "auto";
|
1336
|
-
case "none": return "none";
|
1337
|
-
default: return "auto";
|
1338
|
-
}
|
1339
|
-
};
|
1340
|
-
const translateResponsesResultToAnthropic = (response) => {
|
1341
|
-
const contentBlocks = mapOutputToAnthropicContent(response.output);
|
1342
|
-
const usage = mapResponsesUsage(response);
|
1343
|
-
let anthropicContent = fallbackContentBlocks(response.output_text);
|
1344
|
-
if (contentBlocks.length > 0) anthropicContent = contentBlocks;
|
1345
|
-
const stopReason = mapResponsesStopReason(response);
|
1346
|
-
return {
|
1347
|
-
id: response.id,
|
1348
|
-
type: "message",
|
1349
|
-
role: "assistant",
|
1350
|
-
content: anthropicContent,
|
1351
|
-
model: response.model,
|
1352
|
-
stop_reason: stopReason,
|
1353
|
-
stop_sequence: null,
|
1354
|
-
usage
|
1355
|
-
};
|
1356
|
-
};
|
1357
|
-
const mapOutputToAnthropicContent = (output) => {
|
1358
|
-
const contentBlocks = [];
|
1359
|
-
for (const item of output) switch (item.type) {
|
1360
|
-
case "reasoning": {
|
1361
|
-
const thinkingText = extractReasoningText(item);
|
1362
|
-
if (thinkingText.length > 0) contentBlocks.push({
|
1363
|
-
type: "thinking",
|
1364
|
-
thinking: thinkingText,
|
1365
|
-
signature: (item.encrypted_content ?? "") + "@" + item.id
|
1366
|
-
});
|
1367
|
-
break;
|
1368
|
-
}
|
1369
|
-
case "function_call": {
|
1370
|
-
const toolUseBlock = createToolUseContentBlock(item);
|
1371
|
-
if (toolUseBlock) contentBlocks.push(toolUseBlock);
|
1372
|
-
break;
|
1373
|
-
}
|
1374
|
-
case "message": {
|
1375
|
-
const combinedText = combineMessageTextContent(item.content);
|
1376
|
-
if (combinedText.length > 0) contentBlocks.push({
|
1377
|
-
type: "text",
|
1378
|
-
text: combinedText
|
1379
|
-
});
|
1380
|
-
break;
|
1381
|
-
}
|
1382
|
-
default: {
|
1383
|
-
const combinedText = combineMessageTextContent(item.content);
|
1384
|
-
if (combinedText.length > 0) contentBlocks.push({
|
1385
|
-
type: "text",
|
1386
|
-
text: combinedText
|
1387
|
-
});
|
1388
|
-
}
|
1389
|
-
}
|
1390
|
-
return contentBlocks;
|
1391
|
-
};
|
1392
|
-
const combineMessageTextContent = (content) => {
|
1393
|
-
if (!Array.isArray(content)) return "";
|
1394
|
-
let aggregated = "";
|
1395
|
-
for (const block of content) {
|
1396
|
-
if (isResponseOutputText(block)) {
|
1397
|
-
aggregated += block.text;
|
1398
|
-
continue;
|
1399
|
-
}
|
1400
|
-
if (isResponseOutputRefusal(block)) {
|
1401
|
-
aggregated += block.refusal;
|
1402
|
-
continue;
|
1403
|
-
}
|
1404
|
-
if (typeof block.text === "string") {
|
1405
|
-
aggregated += block.text;
|
1406
|
-
continue;
|
1407
|
-
}
|
1408
|
-
if (typeof block.reasoning === "string") {
|
1409
|
-
aggregated += block.reasoning;
|
1410
|
-
continue;
|
1411
|
-
}
|
1412
|
-
}
|
1413
|
-
return aggregated;
|
1414
|
-
};
|
1415
|
-
const extractReasoningText = (item) => {
|
1416
|
-
const segments = [];
|
1417
|
-
const collectFromBlocks = (blocks) => {
|
1418
|
-
if (!Array.isArray(blocks)) return;
|
1419
|
-
for (const block of blocks) if (typeof block.text === "string") {
|
1420
|
-
segments.push(block.text);
|
1421
|
-
continue;
|
1422
|
-
}
|
1423
|
-
};
|
1424
|
-
collectFromBlocks(item.summary);
|
1425
|
-
return segments.join("").trim();
|
1426
|
-
};
|
1427
|
-
const createToolUseContentBlock = (call) => {
|
1428
|
-
const toolId = call.call_id;
|
1429
|
-
if (!call.name || !toolId) return null;
|
1430
|
-
const input = parseFunctionCallArguments(call.arguments);
|
1431
|
-
return {
|
1432
|
-
type: "tool_use",
|
1433
|
-
id: toolId,
|
1434
|
-
name: call.name,
|
1435
|
-
input
|
1436
|
-
};
|
1437
|
-
};
|
1438
|
-
const parseFunctionCallArguments = (rawArguments) => {
|
1439
|
-
if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return {};
|
1440
|
-
try {
|
1441
|
-
const parsed = JSON.parse(rawArguments);
|
1442
|
-
if (Array.isArray(parsed)) return { arguments: parsed };
|
1443
|
-
if (parsed && typeof parsed === "object") return parsed;
|
1444
|
-
} catch (error) {
|
1445
|
-
consola.warn("Failed to parse function call arguments", {
|
1446
|
-
error,
|
1447
|
-
rawArguments
|
1448
|
-
});
|
1449
|
-
}
|
1450
|
-
return { raw_arguments: rawArguments };
|
1451
|
-
};
|
1452
|
-
const fallbackContentBlocks = (outputText) => {
|
1453
|
-
if (!outputText) return [];
|
1454
|
-
return [{
|
1455
|
-
type: "text",
|
1456
|
-
text: outputText
|
1457
|
-
}];
|
1458
|
-
};
|
1459
|
-
const mapResponsesStopReason = (response) => {
|
1460
|
-
const { status, incomplete_details: incompleteDetails } = response;
|
1461
|
-
if (status === "completed") return "end_turn";
|
1462
|
-
if (status === "incomplete") {
|
1463
|
-
if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
|
1464
|
-
if (incompleteDetails?.reason === "content_filter") return "end_turn";
|
1465
|
-
}
|
1466
|
-
return null;
|
1467
|
-
};
|
1468
|
-
const mapResponsesUsage = (response) => {
|
1469
|
-
const inputTokens = response.usage?.input_tokens ?? 0;
|
1470
|
-
const outputTokens = response.usage?.output_tokens ?? 0;
|
1471
|
-
return {
|
1472
|
-
input_tokens: inputTokens - (response.usage?.input_tokens_details?.cached_tokens ?? 0),
|
1473
|
-
output_tokens: outputTokens,
|
1474
|
-
...response.usage?.input_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: response.usage.input_tokens_details.cached_tokens }
|
1475
|
-
};
|
1476
|
-
};
|
1477
|
-
const isRecord = (value) => typeof value === "object" && value !== null;
|
1478
|
-
const isResponseOutputText = (block) => isRecord(block) && "type" in block && block.type === "output_text";
|
1479
|
-
const isResponseOutputRefusal = (block) => isRecord(block) && "type" in block && block.type === "refusal";
|
1480
|
-
const parseUserId = (userId) => {
|
1481
|
-
if (!userId || typeof userId !== "string") return {
|
1482
|
-
safetyIdentifier: null,
|
1483
|
-
promptCacheKey: null
|
1484
|
-
};
|
1485
|
-
const userMatch = userId.match(/user_([^_]+)_account/);
|
1486
|
-
const safetyIdentifier = userMatch ? userMatch[1] : null;
|
1487
|
-
const sessionMatch = userId.match(/_session_(.+)$/);
|
1488
|
-
return {
|
1489
|
-
safetyIdentifier,
|
1490
|
-
promptCacheKey: sessionMatch ? sessionMatch[1] : null
|
1491
|
-
};
|
1492
|
-
};
|
1493
|
-
const convertToolResultContent = (content) => {
|
1494
|
-
if (typeof content === "string") return content;
|
1495
|
-
if (Array.isArray(content)) {
|
1496
|
-
const result = [];
|
1497
|
-
for (const block of content) switch (block.type) {
|
1498
|
-
case "text":
|
1499
|
-
result.push(createTextContent(block.text));
|
1500
|
-
break;
|
1501
|
-
case "image":
|
1502
|
-
result.push(createImageContent(block));
|
1503
|
-
break;
|
1504
|
-
default: break;
|
1505
|
-
}
|
1506
|
-
return result;
|
1507
|
-
}
|
1508
|
-
return "";
|
1509
|
-
};
|
1510
|
-
|
1511
|
-
//#endregion
|
1512
|
-
//#region src/routes/messages/responses-stream-translation.ts
|
1513
|
-
const createResponsesStreamState = () => ({
|
1514
|
-
messageStartSent: false,
|
1515
|
-
messageCompleted: false,
|
1516
|
-
nextContentBlockIndex: 0,
|
1517
|
-
blockIndexByKey: /* @__PURE__ */ new Map(),
|
1518
|
-
openBlocks: /* @__PURE__ */ new Set(),
|
1519
|
-
blockHasDelta: /* @__PURE__ */ new Set(),
|
1520
|
-
functionCallStateByOutputIndex: /* @__PURE__ */ new Map()
|
1521
|
-
});
|
1522
|
-
const translateResponsesStreamEvent = (rawEvent, state$1) => {
|
1523
|
-
const eventType = rawEvent.type;
|
1524
|
-
switch (eventType) {
|
1525
|
-
case "response.created": return handleResponseCreated(rawEvent, state$1);
|
1526
|
-
case "response.output_item.added": return handleOutputItemAdded(rawEvent, state$1);
|
1527
|
-
case "response.reasoning_summary_text.delta": return handleReasoningSummaryTextDelta(rawEvent, state$1);
|
1528
|
-
case "response.output_text.delta": return handleOutputTextDelta(rawEvent, state$1);
|
1529
|
-
case "response.reasoning_summary_text.done": return handleReasoningSummaryTextDone(rawEvent, state$1);
|
1530
|
-
case "response.output_text.done": return handleOutputTextDone(rawEvent, state$1);
|
1531
|
-
case "response.output_item.done": return handleOutputItemDone(rawEvent, state$1);
|
1532
|
-
case "response.function_call_arguments.delta": return handleFunctionCallArgumentsDelta(rawEvent, state$1);
|
1533
|
-
case "response.function_call_arguments.done": return handleFunctionCallArgumentsDone(rawEvent, state$1);
|
1534
|
-
case "response.completed":
|
1535
|
-
case "response.incomplete": return handleResponseCompleted(rawEvent, state$1);
|
1536
|
-
case "response.failed": return handleResponseFailed(rawEvent, state$1);
|
1537
|
-
case "error": return handleErrorEvent(rawEvent, state$1);
|
1538
|
-
default:
|
1539
|
-
consola.debug("Unknown Responses stream event type:", eventType);
|
1540
|
-
return [];
|
1541
|
-
}
|
1542
|
-
};
|
1543
|
-
const handleResponseCreated = (rawEvent, state$1) => {
|
1544
|
-
return messageStart(state$1, rawEvent.response);
|
1545
|
-
};
|
1546
|
-
const handleOutputItemAdded = (rawEvent, state$1) => {
|
1547
|
-
const events$1 = new Array();
|
1548
|
-
const functionCallDetails = extractFunctionCallDetails(rawEvent);
|
1549
|
-
if (!functionCallDetails) return events$1;
|
1550
|
-
const { outputIndex, toolCallId, name, initialArguments } = functionCallDetails;
|
1551
|
-
const blockIndex = openFunctionCallBlock(state$1, {
|
1552
|
-
outputIndex,
|
1553
|
-
toolCallId,
|
1554
|
-
name,
|
1555
|
-
events: events$1
|
1556
|
-
});
|
1557
|
-
if (initialArguments !== void 0 && initialArguments.length > 0) {
|
1558
|
-
events$1.push({
|
1559
|
-
type: "content_block_delta",
|
1560
|
-
index: blockIndex,
|
1561
|
-
delta: {
|
1562
|
-
type: "input_json_delta",
|
1563
|
-
partial_json: initialArguments
|
1564
|
-
}
|
1565
|
-
});
|
1566
|
-
state$1.blockHasDelta.add(blockIndex);
|
1567
|
-
}
|
1568
|
-
return events$1;
|
1569
|
-
};
|
1570
|
-
const handleOutputItemDone = (rawEvent, state$1) => {
|
1571
|
-
const events$1 = new Array();
|
1572
|
-
const item = rawEvent.item;
|
1573
|
-
if (item.type !== "reasoning") return events$1;
|
1574
|
-
const outputIndex = rawEvent.output_index;
|
1575
|
-
const blockIndex = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
|
1576
|
-
const signature = (item.encrypted_content ?? "") + "@" + item.id;
|
1577
|
-
if (signature) {
|
1578
|
-
events$1.push({
|
1579
|
-
type: "content_block_delta",
|
1580
|
-
index: blockIndex,
|
1581
|
-
delta: {
|
1582
|
-
type: "signature_delta",
|
1583
|
-
signature
|
1584
|
-
}
|
1585
|
-
});
|
1586
|
-
state$1.blockHasDelta.add(blockIndex);
|
1587
|
-
}
|
1588
|
-
closeBlockIfOpen(state$1, blockIndex, events$1);
|
1589
|
-
return events$1;
|
1590
|
-
};
|
1591
|
-
const handleFunctionCallArgumentsDelta = (rawEvent, state$1) => {
|
1592
|
-
const events$1 = new Array();
|
1593
|
-
const outputIndex = rawEvent.output_index;
|
1594
|
-
const deltaText = rawEvent.delta;
|
1595
|
-
const blockIndex = openFunctionCallBlock(state$1, {
|
1596
|
-
outputIndex,
|
1597
|
-
events: events$1
|
1598
|
-
});
|
1599
|
-
events$1.push({
|
1600
|
-
type: "content_block_delta",
|
1601
|
-
index: blockIndex,
|
1602
|
-
delta: {
|
1603
|
-
type: "input_json_delta",
|
1604
|
-
partial_json: deltaText
|
1605
|
-
}
|
1606
|
-
});
|
1607
|
-
state$1.blockHasDelta.add(blockIndex);
|
1608
|
-
return events$1;
|
1609
|
-
};
|
1610
|
-
const handleFunctionCallArgumentsDone = (rawEvent, state$1) => {
|
1611
|
-
const events$1 = new Array();
|
1612
|
-
const outputIndex = rawEvent.output_index;
|
1613
|
-
const blockIndex = openFunctionCallBlock(state$1, {
|
1614
|
-
outputIndex,
|
1615
|
-
events: events$1
|
1616
|
-
});
|
1617
|
-
const finalArguments = typeof rawEvent.arguments === "string" ? rawEvent.arguments : void 0;
|
1618
|
-
if (!state$1.blockHasDelta.has(blockIndex) && finalArguments) {
|
1619
|
-
events$1.push({
|
1620
|
-
type: "content_block_delta",
|
1621
|
-
index: blockIndex,
|
1622
|
-
delta: {
|
1623
|
-
type: "input_json_delta",
|
1624
|
-
partial_json: finalArguments
|
1625
|
-
}
|
1626
|
-
});
|
1627
|
-
state$1.blockHasDelta.add(blockIndex);
|
1628
|
-
}
|
1629
|
-
closeBlockIfOpen(state$1, blockIndex, events$1);
|
1630
|
-
state$1.functionCallStateByOutputIndex.delete(outputIndex);
|
1631
|
-
return events$1;
|
1632
|
-
};
|
1633
|
-
const handleOutputTextDelta = (rawEvent, state$1) => {
|
1634
|
-
const events$1 = new Array();
|
1635
|
-
const outputIndex = rawEvent.output_index;
|
1636
|
-
const contentIndex = rawEvent.content_index;
|
1637
|
-
const deltaText = rawEvent.delta;
|
1638
|
-
if (!deltaText) return events$1;
|
1639
|
-
const blockIndex = openTextBlockIfNeeded(state$1, {
|
1640
|
-
outputIndex,
|
1641
|
-
contentIndex,
|
1642
|
-
events: events$1
|
1643
|
-
});
|
1644
|
-
events$1.push({
|
1645
|
-
type: "content_block_delta",
|
1646
|
-
index: blockIndex,
|
1647
|
-
delta: {
|
1648
|
-
type: "text_delta",
|
1649
|
-
text: deltaText
|
1650
|
-
}
|
1651
|
-
});
|
1652
|
-
state$1.blockHasDelta.add(blockIndex);
|
1653
|
-
return events$1;
|
1654
|
-
};
|
1655
|
-
const handleReasoningSummaryTextDelta = (rawEvent, state$1) => {
|
1656
|
-
const outputIndex = rawEvent.output_index;
|
1657
|
-
const deltaText = rawEvent.delta;
|
1658
|
-
const events$1 = new Array();
|
1659
|
-
const blockIndex = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
|
1660
|
-
events$1.push({
|
1661
|
-
type: "content_block_delta",
|
1662
|
-
index: blockIndex,
|
1663
|
-
delta: {
|
1664
|
-
type: "thinking_delta",
|
1665
|
-
thinking: deltaText
|
1666
|
-
}
|
1667
|
-
});
|
1668
|
-
state$1.blockHasDelta.add(blockIndex);
|
1669
|
-
return events$1;
|
1670
|
-
};
|
1671
|
-
const handleReasoningSummaryTextDone = (rawEvent, state$1) => {
|
1672
|
-
const outputIndex = rawEvent.output_index;
|
1673
|
-
const text = rawEvent.text;
|
1674
|
-
const events$1 = new Array();
|
1675
|
-
const blockIndex = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
|
1676
|
-
if (text && !state$1.blockHasDelta.has(blockIndex)) events$1.push({
|
1677
|
-
type: "content_block_delta",
|
1678
|
-
index: blockIndex,
|
1679
|
-
delta: {
|
1680
|
-
type: "thinking_delta",
|
1681
|
-
thinking: text
|
1682
|
-
}
|
1683
|
-
});
|
1684
|
-
return events$1;
|
1685
|
-
};
|
1686
|
-
const handleOutputTextDone = (rawEvent, state$1) => {
|
1687
|
-
const events$1 = new Array();
|
1688
|
-
const outputIndex = rawEvent.output_index;
|
1689
|
-
const contentIndex = rawEvent.content_index;
|
1690
|
-
const text = rawEvent.text;
|
1691
|
-
const blockIndex = openTextBlockIfNeeded(state$1, {
|
1692
|
-
outputIndex,
|
1693
|
-
contentIndex,
|
1694
|
-
events: events$1
|
1695
|
-
});
|
1696
|
-
if (text && !state$1.blockHasDelta.has(blockIndex)) events$1.push({
|
1697
|
-
type: "content_block_delta",
|
1698
|
-
index: blockIndex,
|
1699
|
-
delta: {
|
1700
|
-
type: "text_delta",
|
1701
|
-
text
|
1702
|
-
}
|
1703
|
-
});
|
1704
|
-
closeBlockIfOpen(state$1, blockIndex, events$1);
|
1705
|
-
return events$1;
|
1706
|
-
};
|
1707
|
-
const handleResponseCompleted = (rawEvent, state$1) => {
|
1708
|
-
const response = rawEvent.response;
|
1709
|
-
const events$1 = new Array();
|
1710
|
-
closeAllOpenBlocks(state$1, events$1);
|
1711
|
-
const anthropic = translateResponsesResultToAnthropic(response);
|
1712
|
-
events$1.push({
|
1713
|
-
type: "message_delta",
|
1714
|
-
delta: {
|
1715
|
-
stop_reason: anthropic.stop_reason,
|
1716
|
-
stop_sequence: anthropic.stop_sequence
|
1717
|
-
},
|
1718
|
-
usage: anthropic.usage
|
1719
|
-
}, { type: "message_stop" });
|
1720
|
-
state$1.messageCompleted = true;
|
1721
|
-
return events$1;
|
1722
|
-
};
|
1723
|
-
const handleResponseFailed = (rawEvent, state$1) => {
|
1724
|
-
const response = rawEvent.response;
|
1725
|
-
const events$1 = new Array();
|
1726
|
-
closeAllOpenBlocks(state$1, events$1);
|
1727
|
-
const message = response.error?.message ?? "The response failed due to an unknown error.";
|
1728
|
-
events$1.push(buildErrorEvent(message));
|
1729
|
-
state$1.messageCompleted = true;
|
1730
|
-
return events$1;
|
1731
|
-
};
|
1732
|
-
const handleErrorEvent = (rawEvent, state$1) => {
|
1733
|
-
const message = typeof rawEvent.message === "string" ? rawEvent.message : "An unexpected error occurred during streaming.";
|
1734
|
-
state$1.messageCompleted = true;
|
1735
|
-
return [buildErrorEvent(message)];
|
1736
|
-
};
|
1737
|
-
const messageStart = (state$1, response) => {
|
1738
|
-
state$1.messageStartSent = true;
|
1739
|
-
const inputCachedTokens = response.usage?.input_tokens_details?.cached_tokens;
|
1740
|
-
const inputTokens = (response.usage?.input_tokens ?? 0) - (inputCachedTokens ?? 0);
|
1741
|
-
return [{
|
1742
|
-
type: "message_start",
|
1743
|
-
message: {
|
1744
|
-
id: response.id,
|
1745
|
-
type: "message",
|
1746
|
-
role: "assistant",
|
1747
|
-
content: [],
|
1748
|
-
model: response.model,
|
1749
|
-
stop_reason: null,
|
1750
|
-
stop_sequence: null,
|
1751
|
-
usage: {
|
1752
|
-
input_tokens: inputTokens,
|
1753
|
-
output_tokens: 0,
|
1754
|
-
...inputCachedTokens !== void 0 && { cache_creation_input_tokens: inputCachedTokens }
|
1755
|
-
}
|
1756
|
-
}
|
1757
|
-
}];
|
1758
|
-
};
|
1759
|
-
const openTextBlockIfNeeded = (state$1, params) => {
|
1760
|
-
const { outputIndex, contentIndex, events: events$1 } = params;
|
1761
|
-
const key = getBlockKey(outputIndex, contentIndex);
|
1762
|
-
let blockIndex = state$1.blockIndexByKey.get(key);
|
1763
|
-
if (blockIndex === void 0) {
|
1764
|
-
blockIndex = state$1.nextContentBlockIndex;
|
1765
|
-
state$1.nextContentBlockIndex += 1;
|
1766
|
-
state$1.blockIndexByKey.set(key, blockIndex);
|
1767
|
-
}
|
1768
|
-
if (!state$1.openBlocks.has(blockIndex)) {
|
1769
|
-
events$1.push({
|
1770
|
-
type: "content_block_start",
|
1771
|
-
index: blockIndex,
|
1772
|
-
content_block: {
|
1773
|
-
type: "text",
|
1774
|
-
text: ""
|
1775
|
-
}
|
1776
|
-
});
|
1777
|
-
state$1.openBlocks.add(blockIndex);
|
1778
|
-
}
|
1779
|
-
return blockIndex;
|
1780
|
-
};
|
1781
|
-
const openThinkingBlockIfNeeded = (state$1, outputIndex, events$1) => {
|
1782
|
-
const key = getBlockKey(outputIndex, 0);
|
1783
|
-
let blockIndex = state$1.blockIndexByKey.get(key);
|
1784
|
-
if (blockIndex === void 0) {
|
1785
|
-
blockIndex = state$1.nextContentBlockIndex;
|
1786
|
-
state$1.nextContentBlockIndex += 1;
|
1787
|
-
state$1.blockIndexByKey.set(key, blockIndex);
|
1788
|
-
}
|
1789
|
-
if (!state$1.openBlocks.has(blockIndex)) {
|
1790
|
-
events$1.push({
|
1791
|
-
type: "content_block_start",
|
1792
|
-
index: blockIndex,
|
1793
|
-
content_block: {
|
1794
|
-
type: "thinking",
|
1795
|
-
thinking: ""
|
1796
|
-
}
|
1797
|
-
});
|
1798
|
-
state$1.openBlocks.add(blockIndex);
|
1799
|
-
}
|
1800
|
-
return blockIndex;
|
1801
|
-
};
|
1802
|
-
const closeBlockIfOpen = (state$1, blockIndex, events$1) => {
|
1803
|
-
if (!state$1.openBlocks.has(blockIndex)) return;
|
1804
|
-
events$1.push({
|
1805
|
-
type: "content_block_stop",
|
1806
|
-
index: blockIndex
|
1807
|
-
});
|
1808
|
-
state$1.openBlocks.delete(blockIndex);
|
1809
|
-
state$1.blockHasDelta.delete(blockIndex);
|
1810
|
-
};
|
1811
|
-
const closeAllOpenBlocks = (state$1, events$1) => {
|
1812
|
-
for (const blockIndex of state$1.openBlocks) closeBlockIfOpen(state$1, blockIndex, events$1);
|
1813
|
-
state$1.functionCallStateByOutputIndex.clear();
|
1814
|
-
};
|
1815
|
-
const buildErrorEvent = (message) => ({
|
1816
|
-
type: "error",
|
1817
|
-
error: {
|
1818
|
-
type: "api_error",
|
1819
|
-
message
|
1820
|
-
}
|
1821
|
-
});
|
1822
|
-
const getBlockKey = (outputIndex, contentIndex) => `${outputIndex}:${contentIndex}`;
|
1823
|
-
const openFunctionCallBlock = (state$1, params) => {
|
1824
|
-
const { outputIndex, toolCallId, name, events: events$1 } = params;
|
1825
|
-
let functionCallState = state$1.functionCallStateByOutputIndex.get(outputIndex);
|
1826
|
-
if (!functionCallState) {
|
1827
|
-
const blockIndex$1 = state$1.nextContentBlockIndex;
|
1828
|
-
state$1.nextContentBlockIndex += 1;
|
1829
|
-
functionCallState = {
|
1830
|
-
blockIndex: blockIndex$1,
|
1831
|
-
toolCallId: toolCallId ?? `tool_call_${blockIndex$1}`,
|
1832
|
-
name: name ?? "function"
|
1833
|
-
};
|
1834
|
-
state$1.functionCallStateByOutputIndex.set(outputIndex, functionCallState);
|
1835
|
-
}
|
1836
|
-
const { blockIndex } = functionCallState;
|
1837
|
-
if (!state$1.openBlocks.has(blockIndex)) {
|
1838
|
-
events$1.push({
|
1839
|
-
type: "content_block_start",
|
1840
|
-
index: blockIndex,
|
1841
|
-
content_block: {
|
1842
|
-
type: "tool_use",
|
1843
|
-
id: functionCallState.toolCallId,
|
1844
|
-
name: functionCallState.name,
|
1845
|
-
input: {}
|
1846
|
-
}
|
1847
|
-
});
|
1848
|
-
state$1.openBlocks.add(blockIndex);
|
1849
|
-
}
|
1850
|
-
return blockIndex;
|
1851
|
-
};
|
1852
|
-
const extractFunctionCallDetails = (rawEvent) => {
|
1853
|
-
const item = rawEvent.item;
|
1854
|
-
if (item.type !== "function_call") return;
|
1855
|
-
return {
|
1856
|
-
outputIndex: rawEvent.output_index,
|
1857
|
-
toolCallId: item.call_id,
|
1858
|
-
name: item.name,
|
1859
|
-
initialArguments: item.arguments
|
1860
|
-
};
|
1861
|
-
};
|
1862
|
-
|
1863
|
-
//#endregion
|
1864
|
-
//#region src/routes/responses/utils.ts
|
1865
|
-
const getResponsesRequestOptions = (payload) => {
|
1866
|
-
return {
|
1867
|
-
vision: hasVisionInput(payload),
|
1868
|
-
initiator: hasAgentInitiator(payload) ? "agent" : "user"
|
1869
|
-
};
|
1870
|
-
};
|
1871
|
-
const hasAgentInitiator = (payload) => getPayloadItems(payload).some((item) => {
|
1872
|
-
if (!("role" in item) || !item.role) return true;
|
1873
|
-
return (typeof item.role === "string" ? item.role.toLowerCase() : "") === "assistant";
|
1874
|
-
});
|
1875
|
-
const hasVisionInput = (payload) => {
|
1876
|
-
return getPayloadItems(payload).some((item) => containsVisionContent(item));
|
1877
|
-
};
|
1878
|
-
const getPayloadItems = (payload) => {
|
1879
|
-
const result = [];
|
1880
|
-
const { input } = payload;
|
1881
|
-
if (Array.isArray(input)) result.push(...input);
|
1882
|
-
return result;
|
1883
|
-
};
|
1884
|
-
const containsVisionContent = (value) => {
|
1885
|
-
if (!value) return false;
|
1886
|
-
if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
|
1887
|
-
if (typeof value !== "object") return false;
|
1888
|
-
const record = value;
|
1889
|
-
if ((typeof record.type === "string" ? record.type.toLowerCase() : void 0) === "input_image") return true;
|
1890
|
-
if (Array.isArray(record.content)) return record.content.some((entry) => containsVisionContent(entry));
|
1891
|
-
return false;
|
1892
|
-
};
|
1893
|
-
|
1894
1093
|
//#endregion
|
1895
1094
|
//#region src/routes/messages/stream-translation.ts
|
1896
1095
|
function isToolBlockOpen(state$1) {
|
@@ -2021,15 +1220,9 @@ async function handleCompletion(c) {
|
|
2021
1220
|
await checkRateLimit(state);
|
2022
1221
|
const anthropicPayload = await c.req.json();
|
2023
1222
|
consola.debug("Anthropic request payload:", JSON.stringify(anthropicPayload));
|
2024
|
-
const useResponsesApi = shouldUseResponsesApi(anthropicPayload.model);
|
2025
|
-
if (state.manualApprove) await awaitApproval();
|
2026
|
-
if (useResponsesApi) return await handleWithResponsesApi(c, anthropicPayload);
|
2027
|
-
return await handleWithChatCompletions(c, anthropicPayload);
|
2028
|
-
}
|
2029
|
-
const RESPONSES_ENDPOINT$1 = "/responses";
|
2030
|
-
const handleWithChatCompletions = async (c, anthropicPayload) => {
|
2031
1223
|
const openAIPayload = translateToOpenAI(anthropicPayload);
|
2032
1224
|
consola.debug("Translated OpenAI request payload:", JSON.stringify(openAIPayload));
|
1225
|
+
if (state.manualApprove) await awaitApproval();
|
2033
1226
|
const response = await createChatCompletions(openAIPayload);
|
2034
1227
|
if (isNonStreaming(response)) {
|
2035
1228
|
consola.debug("Non-streaming response from Copilot:", JSON.stringify(response).slice(-400));
|
@@ -2059,60 +1252,8 @@ const handleWithChatCompletions = async (c, anthropicPayload) => {
|
|
2059
1252
|
}
|
2060
1253
|
}
|
2061
1254
|
});
|
2062
|
-
}
|
2063
|
-
const handleWithResponsesApi = async (c, anthropicPayload) => {
|
2064
|
-
const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload);
|
2065
|
-
consola.debug("Translated Responses payload:", JSON.stringify(responsesPayload));
|
2066
|
-
const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
|
2067
|
-
const response = await createResponses(responsesPayload, {
|
2068
|
-
vision,
|
2069
|
-
initiator
|
2070
|
-
});
|
2071
|
-
if (responsesPayload.stream && isAsyncIterable$1(response)) {
|
2072
|
-
consola.debug("Streaming response from Copilot (Responses API)");
|
2073
|
-
return streamSSE(c, async (stream) => {
|
2074
|
-
const streamState = createResponsesStreamState();
|
2075
|
-
for await (const chunk of response) {
|
2076
|
-
if (chunk.event === "ping") {
|
2077
|
-
await stream.writeSSE({
|
2078
|
-
event: "ping",
|
2079
|
-
data: ""
|
2080
|
-
});
|
2081
|
-
continue;
|
2082
|
-
}
|
2083
|
-
const data = chunk.data;
|
2084
|
-
if (!data) continue;
|
2085
|
-
consola.debug("Responses raw stream event:", data);
|
2086
|
-
const events$1 = translateResponsesStreamEvent(JSON.parse(data), streamState);
|
2087
|
-
for (const event of events$1) {
|
2088
|
-
const eventData = JSON.stringify(event);
|
2089
|
-
consola.debug("Translated Anthropic event:", eventData);
|
2090
|
-
await stream.writeSSE({
|
2091
|
-
event: event.type,
|
2092
|
-
data: eventData
|
2093
|
-
});
|
2094
|
-
}
|
2095
|
-
}
|
2096
|
-
if (!streamState.messageCompleted) {
|
2097
|
-
consola.warn("Responses stream ended without completion; sending fallback message_stop");
|
2098
|
-
const fallback = { type: "message_stop" };
|
2099
|
-
await stream.writeSSE({
|
2100
|
-
event: fallback.type,
|
2101
|
-
data: JSON.stringify(fallback)
|
2102
|
-
});
|
2103
|
-
}
|
2104
|
-
});
|
2105
|
-
}
|
2106
|
-
consola.debug("Non-streaming Responses result:", JSON.stringify(response).slice(-400));
|
2107
|
-
const anthropicResponse = translateResponsesResultToAnthropic(response);
|
2108
|
-
consola.debug("Translated Anthropic response:", JSON.stringify(anthropicResponse));
|
2109
|
-
return c.json(anthropicResponse);
|
2110
|
-
};
|
2111
|
-
const shouldUseResponsesApi = (modelId) => {
|
2112
|
-
return (state.models?.data.find((model) => model.id === modelId))?.supported_endpoints?.includes(RESPONSES_ENDPOINT$1) ?? false;
|
2113
|
-
};
|
1255
|
+
}
|
2114
1256
|
const isNonStreaming = (response) => Object.hasOwn(response, "choices");
|
2115
|
-
const isAsyncIterable$1 = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
|
2116
1257
|
|
2117
1258
|
//#endregion
|
2118
1259
|
//#region src/routes/messages/route.ts
|
@@ -2157,68 +1298,6 @@ modelRoutes.get("/", async (c) => {
|
|
2157
1298
|
}
|
2158
1299
|
});
|
2159
1300
|
|
2160
|
-
//#endregion
|
2161
|
-
//#region src/routes/responses/handler.ts
|
2162
|
-
const RESPONSES_ENDPOINT = "/responses";
|
2163
|
-
const handleResponses = async (c) => {
|
2164
|
-
await checkRateLimit(state);
|
2165
|
-
const payload = await c.req.json();
|
2166
|
-
consola.debug("Responses request payload:", JSON.stringify(payload));
|
2167
|
-
if (!((state.models?.data.find((model) => model.id === payload.model))?.supported_endpoints?.includes(RESPONSES_ENDPOINT) ?? false)) return c.json({ error: {
|
2168
|
-
message: "This model does not support the responses endpoint. Please choose a different model.",
|
2169
|
-
type: "invalid_request_error"
|
2170
|
-
} }, 400);
|
2171
|
-
const { vision, initiator } = getResponsesRequestOptions(payload);
|
2172
|
-
if (state.manualApprove) await awaitApproval();
|
2173
|
-
const response = await createResponses(payload, {
|
2174
|
-
vision,
|
2175
|
-
initiator
|
2176
|
-
});
|
2177
|
-
if (isStreamingRequested(payload) && isAsyncIterable(response)) {
|
2178
|
-
consola.debug("Forwarding native Responses stream");
|
2179
|
-
return streamSSE(c, async (stream) => {
|
2180
|
-
const pingInterval = setInterval(async () => {
|
2181
|
-
try {
|
2182
|
-
await stream.writeSSE({
|
2183
|
-
event: "ping",
|
2184
|
-
data: JSON.stringify({ timestamp: Date.now() })
|
2185
|
-
});
|
2186
|
-
} catch (error) {
|
2187
|
-
consola.warn("Failed to send ping:", error);
|
2188
|
-
clearInterval(pingInterval);
|
2189
|
-
}
|
2190
|
-
}, 3e3);
|
2191
|
-
try {
|
2192
|
-
for await (const chunk of response) {
|
2193
|
-
consola.debug("Responses stream chunk:", JSON.stringify(chunk));
|
2194
|
-
await stream.writeSSE({
|
2195
|
-
id: chunk.id,
|
2196
|
-
event: chunk.event,
|
2197
|
-
data: chunk.data ?? ""
|
2198
|
-
});
|
2199
|
-
}
|
2200
|
-
} finally {
|
2201
|
-
clearInterval(pingInterval);
|
2202
|
-
}
|
2203
|
-
});
|
2204
|
-
}
|
2205
|
-
consola.debug("Forwarding native Responses result:", JSON.stringify(response).slice(-400));
|
2206
|
-
return c.json(response);
|
2207
|
-
};
|
2208
|
-
const isAsyncIterable = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
|
2209
|
-
const isStreamingRequested = (payload) => Boolean(payload.stream);
|
2210
|
-
|
2211
|
-
//#endregion
|
2212
|
-
//#region src/routes/responses/route.ts
|
2213
|
-
const responsesRoutes = new Hono();
|
2214
|
-
responsesRoutes.post("/", async (c) => {
|
2215
|
-
try {
|
2216
|
-
return await handleResponses(c);
|
2217
|
-
} catch (error) {
|
2218
|
-
return await forwardError(c, error);
|
2219
|
-
}
|
2220
|
-
});
|
2221
|
-
|
2222
1301
|
//#endregion
|
2223
1302
|
//#region src/routes/token/route.ts
|
2224
1303
|
const tokenRoute = new Hono();
|
@@ -2258,16 +1337,15 @@ server.route("/models", modelRoutes);
|
|
2258
1337
|
server.route("/embeddings", embeddingRoutes);
|
2259
1338
|
server.route("/usage", usageRoute);
|
2260
1339
|
server.route("/token", tokenRoute);
|
2261
|
-
server.route("/responses", responsesRoutes);
|
2262
1340
|
server.route("/v1/chat/completions", completionRoutes);
|
2263
1341
|
server.route("/v1/models", modelRoutes);
|
2264
1342
|
server.route("/v1/embeddings", embeddingRoutes);
|
2265
|
-
server.route("/v1/responses", responsesRoutes);
|
2266
1343
|
server.route("/v1/messages", messageRoutes);
|
2267
1344
|
|
2268
1345
|
//#endregion
|
2269
1346
|
//#region src/start.ts
|
2270
1347
|
async function runServer(options) {
|
1348
|
+
if (options.proxyEnv) initProxyFromEnv();
|
2271
1349
|
if (options.verbose) {
|
2272
1350
|
consola.level = 5;
|
2273
1351
|
consola.info("Verbose logging enabled");
|
@@ -2377,6 +1455,11 @@ const start = defineCommand({
|
|
2377
1455
|
type: "boolean",
|
2378
1456
|
default: false,
|
2379
1457
|
description: "Show GitHub and Copilot tokens on fetch and refresh"
|
1458
|
+
},
|
1459
|
+
"proxy-env": {
|
1460
|
+
type: "boolean",
|
1461
|
+
default: false,
|
1462
|
+
description: "Initialize proxy from environment variables"
|
2380
1463
|
}
|
2381
1464
|
},
|
2382
1465
|
run({ args }) {
|
@@ -2391,14 +1474,15 @@ const start = defineCommand({
|
|
2391
1474
|
rateLimitWait: args.wait,
|
2392
1475
|
githubToken: args["github-token"],
|
2393
1476
|
claudeCode: args["claude-code"],
|
2394
|
-
showToken: args["show-token"]
|
1477
|
+
showToken: args["show-token"],
|
1478
|
+
proxyEnv: args["proxy-env"]
|
2395
1479
|
});
|
2396
1480
|
}
|
2397
1481
|
});
|
2398
1482
|
|
2399
1483
|
//#endregion
|
2400
1484
|
//#region src/main.ts
|
2401
|
-
|
1485
|
+
await runMain(defineCommand({
|
2402
1486
|
meta: {
|
2403
1487
|
name: "copilot-api",
|
2404
1488
|
description: "A wrapper around GitHub Copilot API to make it OpenAI compatible, making it usable for other tools."
|
@@ -2409,9 +1493,7 @@ const main = defineCommand({
|
|
2409
1493
|
"check-usage": checkUsage,
|
2410
1494
|
debug
|
2411
1495
|
}
|
2412
|
-
});
|
2413
|
-
initProxyFromEnv();
|
2414
|
-
await runMain(main);
|
1496
|
+
}));
|
2415
1497
|
|
2416
1498
|
//#endregion
|
2417
1499
|
export { };
|