@ashsec/copilot-api 0.7.7 → 0.7.10
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/main.js +212 -61
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -17,6 +17,85 @@ import { Hono } from "hono";
|
|
|
17
17
|
import { cors } from "hono/cors";
|
|
18
18
|
import { streamSSE } from "hono/streaming";
|
|
19
19
|
|
|
20
|
+
//#region package.json
|
|
21
|
+
var name = "@ashsec/copilot-api";
|
|
22
|
+
var version = "0.7.10";
|
|
23
|
+
var description = "Turn GitHub Copilot into OpenAI/Anthropic API compatible server. Usable with Claude Code!";
|
|
24
|
+
var keywords = [
|
|
25
|
+
"proxy",
|
|
26
|
+
"github-copilot",
|
|
27
|
+
"openai-compatible"
|
|
28
|
+
];
|
|
29
|
+
var homepage = "https://github.com/ericc-ch/copilot-api";
|
|
30
|
+
var bugs = "https://github.com/ericc-ch/copilot-api/issues";
|
|
31
|
+
var repository = {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/ericc-ch/copilot-api.git"
|
|
34
|
+
};
|
|
35
|
+
var author = "Erick Christian <erickchristian48@gmail.com>";
|
|
36
|
+
var type = "module";
|
|
37
|
+
var bin = { "copilot-api": "./dist/main.js" };
|
|
38
|
+
var files = ["dist"];
|
|
39
|
+
var scripts = {
|
|
40
|
+
"build": "tsdown",
|
|
41
|
+
"dev": "bun run --watch ./src/main.ts",
|
|
42
|
+
"knip": "knip-bun",
|
|
43
|
+
"lint": "eslint --cache",
|
|
44
|
+
"lint:all": "eslint --cache .",
|
|
45
|
+
"prepack": "bun run build",
|
|
46
|
+
"prepare": "simple-git-hooks",
|
|
47
|
+
"release": "bumpp && bun publish --access public",
|
|
48
|
+
"start": "NODE_ENV=production bun run ./src/main.ts",
|
|
49
|
+
"typecheck": "tsc"
|
|
50
|
+
};
|
|
51
|
+
var simple_git_hooks = { "pre-commit": "bunx lint-staged" };
|
|
52
|
+
var lint_staged = { "*": "bun run lint --fix" };
|
|
53
|
+
var dependencies = {
|
|
54
|
+
"citty": "^0.1.6",
|
|
55
|
+
"clipboardy": "^5.0.0",
|
|
56
|
+
"consola": "^3.4.2",
|
|
57
|
+
"fetch-event-stream": "^0.1.5",
|
|
58
|
+
"gpt-tokenizer": "^3.0.1",
|
|
59
|
+
"hono": "^4.9.9",
|
|
60
|
+
"proxy-from-env": "^1.1.0",
|
|
61
|
+
"srvx": "^0.8.9",
|
|
62
|
+
"tiny-invariant": "^1.3.3",
|
|
63
|
+
"undici": "^7.16.0",
|
|
64
|
+
"zod": "^4.1.11"
|
|
65
|
+
};
|
|
66
|
+
var devDependencies = {
|
|
67
|
+
"@echristian/eslint-config": "^0.0.54",
|
|
68
|
+
"@types/bun": "^1.2.23",
|
|
69
|
+
"@types/proxy-from-env": "^1.0.4",
|
|
70
|
+
"bumpp": "^10.2.3",
|
|
71
|
+
"eslint": "^9.37.0",
|
|
72
|
+
"knip": "^5.64.1",
|
|
73
|
+
"lint-staged": "^16.2.3",
|
|
74
|
+
"prettier-plugin-packagejson": "^2.5.19",
|
|
75
|
+
"simple-git-hooks": "^2.13.1",
|
|
76
|
+
"tsdown": "^0.15.6",
|
|
77
|
+
"typescript": "^5.9.3"
|
|
78
|
+
};
|
|
79
|
+
var package_default = {
|
|
80
|
+
name,
|
|
81
|
+
version,
|
|
82
|
+
description,
|
|
83
|
+
keywords,
|
|
84
|
+
homepage,
|
|
85
|
+
bugs,
|
|
86
|
+
repository,
|
|
87
|
+
author,
|
|
88
|
+
type,
|
|
89
|
+
bin,
|
|
90
|
+
files,
|
|
91
|
+
scripts,
|
|
92
|
+
"simple-git-hooks": simple_git_hooks,
|
|
93
|
+
"lint-staged": lint_staged,
|
|
94
|
+
dependencies,
|
|
95
|
+
devDependencies
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
//#endregion
|
|
20
99
|
//#region src/lib/paths.ts
|
|
21
100
|
const APP_DIR = path.join(os.homedir(), ".local", "share", "copilot-api");
|
|
22
101
|
const GITHUB_TOKEN_PATH = path.join(APP_DIR, "github_token");
|
|
@@ -575,13 +654,13 @@ const checkUsage = defineCommand({
|
|
|
575
654
|
const premiumUsed = premiumTotal - premium.remaining;
|
|
576
655
|
const premiumPercentUsed = premiumTotal > 0 ? premiumUsed / premiumTotal * 100 : 0;
|
|
577
656
|
const premiumPercentRemaining = premium.percent_remaining;
|
|
578
|
-
function summarizeQuota(name, snap) {
|
|
579
|
-
if (!snap) return `${name}: N/A`;
|
|
657
|
+
function summarizeQuota(name$1, snap) {
|
|
658
|
+
if (!snap) return `${name$1}: N/A`;
|
|
580
659
|
const total = snap.entitlement;
|
|
581
660
|
const used = total - snap.remaining;
|
|
582
661
|
const percentUsed = total > 0 ? used / total * 100 : 0;
|
|
583
662
|
const percentRemaining = snap.percent_remaining;
|
|
584
|
-
return `${name}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`;
|
|
663
|
+
return `${name$1}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`;
|
|
585
664
|
}
|
|
586
665
|
const premiumLine = `Premium: ${premiumUsed}/${premiumTotal} used (${premiumPercentUsed.toFixed(1)}% used, ${premiumPercentRemaining.toFixed(1)}% remaining)`;
|
|
587
666
|
const chatLine = summarizeQuota("Chat", usage.quota_snapshots.chat);
|
|
@@ -656,11 +735,11 @@ async function getUserReplacements() {
|
|
|
656
735
|
/**
|
|
657
736
|
* Add a new user replacement rule
|
|
658
737
|
*/
|
|
659
|
-
async function addReplacement(pattern, replacement, isRegex = false, name) {
|
|
738
|
+
async function addReplacement(pattern, replacement, isRegex = false, name$1) {
|
|
660
739
|
await ensureLoaded();
|
|
661
740
|
const rule = {
|
|
662
741
|
id: `user-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
663
|
-
name,
|
|
742
|
+
name: name$1,
|
|
664
743
|
pattern,
|
|
665
744
|
replacement,
|
|
666
745
|
isRegex,
|
|
@@ -811,11 +890,11 @@ async function applyReplacementsToPayload(payload) {
|
|
|
811
890
|
//#region src/config.ts
|
|
812
891
|
function formatRule(rule, index) {
|
|
813
892
|
const status = rule.enabled ? "✓" : "✗";
|
|
814
|
-
const type = rule.isRegex ? "regex" : "string";
|
|
893
|
+
const type$1 = rule.isRegex ? "regex" : "string";
|
|
815
894
|
const system = rule.isSystem ? " [system]" : "";
|
|
816
|
-
const name = rule.name ? ` "${rule.name}"` : "";
|
|
895
|
+
const name$1 = rule.name ? ` "${rule.name}"` : "";
|
|
817
896
|
const replacement = rule.replacement || "(empty)";
|
|
818
|
-
return `${index + 1}. [${status}] (${type})${system}${name} "${rule.pattern}" → "${replacement}"`;
|
|
897
|
+
return `${index + 1}. [${status}] (${type$1})${system}${name$1} "${rule.pattern}" → "${replacement}"`;
|
|
819
898
|
}
|
|
820
899
|
async function listReplacements() {
|
|
821
900
|
const all = await getAllReplacements();
|
|
@@ -828,11 +907,11 @@ async function listReplacements() {
|
|
|
828
907
|
console.log();
|
|
829
908
|
}
|
|
830
909
|
async function addNewReplacement() {
|
|
831
|
-
const name = await consola.prompt("Name (optional, short description):", {
|
|
910
|
+
const name$1 = await consola.prompt("Name (optional, short description):", {
|
|
832
911
|
type: "text",
|
|
833
912
|
default: ""
|
|
834
913
|
});
|
|
835
|
-
if (typeof name === "symbol") {
|
|
914
|
+
if (typeof name$1 === "symbol") {
|
|
836
915
|
consola.info("Cancelled.");
|
|
837
916
|
return;
|
|
838
917
|
}
|
|
@@ -869,7 +948,7 @@ async function addNewReplacement() {
|
|
|
869
948
|
consola.info("Cancelled.");
|
|
870
949
|
return;
|
|
871
950
|
}
|
|
872
|
-
const rule = await addReplacement(pattern, replacement, matchType === "regex", name || void 0);
|
|
951
|
+
const rule = await addReplacement(pattern, replacement, matchType === "regex", name$1 || void 0);
|
|
873
952
|
consola.success(`Added rule: ${rule.name || rule.id}`);
|
|
874
953
|
}
|
|
875
954
|
async function editExistingReplacement() {
|
|
@@ -897,11 +976,11 @@ async function editExistingReplacement() {
|
|
|
897
976
|
}
|
|
898
977
|
consola.info(`\nEditing rule: ${rule.name || rule.id}`);
|
|
899
978
|
consola.info("Press Enter to keep current value.\n");
|
|
900
|
-
const name = await consola.prompt("Name:", {
|
|
979
|
+
const name$1 = await consola.prompt("Name:", {
|
|
901
980
|
type: "text",
|
|
902
981
|
default: rule.name || ""
|
|
903
982
|
});
|
|
904
|
-
if (typeof name === "symbol") {
|
|
983
|
+
if (typeof name$1 === "symbol") {
|
|
905
984
|
consola.info("Cancelled.");
|
|
906
985
|
return;
|
|
907
986
|
}
|
|
@@ -943,7 +1022,7 @@ async function editExistingReplacement() {
|
|
|
943
1022
|
return;
|
|
944
1023
|
}
|
|
945
1024
|
const updated = await updateReplacement(selected, {
|
|
946
|
-
name: name || void 0,
|
|
1025
|
+
name: name$1 || void 0,
|
|
947
1026
|
pattern,
|
|
948
1027
|
replacement,
|
|
949
1028
|
isRegex: matchType === "regex"
|
|
@@ -1128,9 +1207,9 @@ async function checkTokenExists() {
|
|
|
1128
1207
|
}
|
|
1129
1208
|
}
|
|
1130
1209
|
async function getDebugInfo() {
|
|
1131
|
-
const [version, tokenExists] = await Promise.all([getPackageVersion(), checkTokenExists()]);
|
|
1210
|
+
const [version$1, tokenExists] = await Promise.all([getPackageVersion(), checkTokenExists()]);
|
|
1132
1211
|
return {
|
|
1133
|
-
version,
|
|
1212
|
+
version: version$1,
|
|
1134
1213
|
runtime: getRuntimeInfo(),
|
|
1135
1214
|
paths: {
|
|
1136
1215
|
APP_DIR: PATHS.APP_DIR,
|
|
@@ -1922,7 +2001,7 @@ function translateAnthropicToolChoiceToOpenAI(anthropicToolChoice) {
|
|
|
1922
2001
|
default: return;
|
|
1923
2002
|
}
|
|
1924
2003
|
}
|
|
1925
|
-
function translateToAnthropic(response) {
|
|
2004
|
+
function translateToAnthropic(response, originalModel) {
|
|
1926
2005
|
const allTextBlocks = [];
|
|
1927
2006
|
const allToolUseBlocks = [];
|
|
1928
2007
|
let stopReason = null;
|
|
@@ -1938,7 +2017,7 @@ function translateToAnthropic(response) {
|
|
|
1938
2017
|
id: response.id,
|
|
1939
2018
|
type: "message",
|
|
1940
2019
|
role: "assistant",
|
|
1941
|
-
model: response.model,
|
|
2020
|
+
model: originalModel ?? response.model,
|
|
1942
2021
|
content: [...allTextBlocks, ...allToolUseBlocks],
|
|
1943
2022
|
stop_reason: mapOpenAIStopReasonToAnthropic(stopReason),
|
|
1944
2023
|
stop_sequence: null,
|
|
@@ -2012,12 +2091,57 @@ function isToolBlockOpen(state$1) {
|
|
|
2012
2091
|
if (!state$1.contentBlockOpen) return false;
|
|
2013
2092
|
return Object.values(state$1.toolCalls).some((tc) => tc.anthropicBlockIndex === state$1.contentBlockIndex);
|
|
2014
2093
|
}
|
|
2015
|
-
function
|
|
2094
|
+
function createMessageDeltaEvents(finishReason, usage) {
|
|
2095
|
+
const stopReason = mapOpenAIStopReasonToAnthropic(finishReason);
|
|
2096
|
+
console.log(`[stream-translation] Creating message_delta with stop_reason: ${stopReason}, finishReason: ${finishReason}`);
|
|
2097
|
+
return [{
|
|
2098
|
+
type: "message_delta",
|
|
2099
|
+
delta: {
|
|
2100
|
+
stop_reason: stopReason,
|
|
2101
|
+
stop_sequence: null
|
|
2102
|
+
},
|
|
2103
|
+
usage: {
|
|
2104
|
+
input_tokens: usage.prompt_tokens,
|
|
2105
|
+
output_tokens: usage.completion_tokens,
|
|
2106
|
+
cache_creation_input_tokens: 0,
|
|
2107
|
+
cache_read_input_tokens: usage.cached_tokens
|
|
2108
|
+
}
|
|
2109
|
+
}, { type: "message_stop" }];
|
|
2110
|
+
}
|
|
2111
|
+
function createFallbackMessageDeltaEvents(state$1) {
|
|
2112
|
+
if (state$1.messageDeltaSent) return [];
|
|
2113
|
+
if (state$1.pendingFinishReason) {
|
|
2114
|
+
const usage = state$1.pendingUsage ?? {
|
|
2115
|
+
prompt_tokens: 0,
|
|
2116
|
+
completion_tokens: 0,
|
|
2117
|
+
cached_tokens: 0
|
|
2118
|
+
};
|
|
2119
|
+
return createMessageDeltaEvents(state$1.pendingFinishReason, usage);
|
|
2120
|
+
}
|
|
2121
|
+
return [];
|
|
2122
|
+
}
|
|
2123
|
+
function translateChunkToAnthropicEvents(chunk, state$1, originalModel) {
|
|
2016
2124
|
const events$1 = [];
|
|
2125
|
+
if (chunk.usage) {
|
|
2126
|
+
state$1.pendingUsage = {
|
|
2127
|
+
prompt_tokens: chunk.usage.prompt_tokens ?? 0,
|
|
2128
|
+
completion_tokens: chunk.usage.completion_tokens ?? 0,
|
|
2129
|
+
cached_tokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? 0
|
|
2130
|
+
};
|
|
2131
|
+
if (state$1.pendingFinishReason && !state$1.messageDeltaSent) {
|
|
2132
|
+
events$1.push(...createMessageDeltaEvents(state$1.pendingFinishReason, state$1.pendingUsage));
|
|
2133
|
+
state$1.messageDeltaSent = true;
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2017
2136
|
if (chunk.choices.length === 0) return events$1;
|
|
2018
2137
|
const choice = chunk.choices[0];
|
|
2019
2138
|
const { delta } = choice;
|
|
2020
2139
|
if (!state$1.messageStartSent) {
|
|
2140
|
+
const usage = state$1.pendingUsage ?? {
|
|
2141
|
+
prompt_tokens: 0,
|
|
2142
|
+
completion_tokens: 0,
|
|
2143
|
+
cached_tokens: 0
|
|
2144
|
+
};
|
|
2021
2145
|
events$1.push({
|
|
2022
2146
|
type: "message_start",
|
|
2023
2147
|
message: {
|
|
@@ -2025,9 +2149,15 @@ function translateChunkToAnthropicEvents(chunk, state$1) {
|
|
|
2025
2149
|
type: "message",
|
|
2026
2150
|
role: "assistant",
|
|
2027
2151
|
content: [],
|
|
2028
|
-
model: chunk.model,
|
|
2152
|
+
model: originalModel ?? chunk.model,
|
|
2029
2153
|
stop_reason: null,
|
|
2030
|
-
stop_sequence: null
|
|
2154
|
+
stop_sequence: null,
|
|
2155
|
+
usage: {
|
|
2156
|
+
input_tokens: usage.prompt_tokens,
|
|
2157
|
+
output_tokens: usage.completion_tokens,
|
|
2158
|
+
cache_creation_input_tokens: 0,
|
|
2159
|
+
cache_read_input_tokens: usage.cached_tokens
|
|
2160
|
+
}
|
|
2031
2161
|
}
|
|
2032
2162
|
});
|
|
2033
2163
|
state$1.messageStartSent = true;
|
|
@@ -2109,28 +2239,44 @@ function translateChunkToAnthropicEvents(chunk, state$1) {
|
|
|
2109
2239
|
});
|
|
2110
2240
|
state$1.contentBlockOpen = false;
|
|
2111
2241
|
}
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
usage: {
|
|
2122
|
-
input_tokens: inputTokens,
|
|
2123
|
-
output_tokens: outputTokens,
|
|
2124
|
-
cache_creation_input_tokens: 0,
|
|
2125
|
-
cache_read_input_tokens: cachedTokens
|
|
2126
|
-
}
|
|
2127
|
-
}, { type: "message_stop" });
|
|
2242
|
+
if (chunk.usage || state$1.pendingUsage) {
|
|
2243
|
+
const usage = {
|
|
2244
|
+
prompt_tokens: chunk.usage?.prompt_tokens ?? state$1.pendingUsage?.prompt_tokens ?? 0,
|
|
2245
|
+
completion_tokens: chunk.usage?.completion_tokens ?? state$1.pendingUsage?.completion_tokens ?? 0,
|
|
2246
|
+
cached_tokens: chunk.usage?.prompt_tokens_details?.cached_tokens ?? state$1.pendingUsage?.cached_tokens ?? 0
|
|
2247
|
+
};
|
|
2248
|
+
events$1.push(...createMessageDeltaEvents(choice.finish_reason, usage));
|
|
2249
|
+
state$1.messageDeltaSent = true;
|
|
2250
|
+
} else state$1.pendingFinishReason = choice.finish_reason;
|
|
2128
2251
|
}
|
|
2129
2252
|
return events$1;
|
|
2130
2253
|
}
|
|
2131
2254
|
|
|
2132
2255
|
//#endregion
|
|
2133
2256
|
//#region src/routes/messages/handler.ts
|
|
2257
|
+
/** Collect all chunks and extract usage data */
|
|
2258
|
+
async function collectChunksWithUsage(eventStream) {
|
|
2259
|
+
const chunks = [];
|
|
2260
|
+
let usage = null;
|
|
2261
|
+
for await (const event of eventStream) {
|
|
2262
|
+
if (!event.data || event.data === "[DONE]") continue;
|
|
2263
|
+
try {
|
|
2264
|
+
const chunk = JSON.parse(event.data);
|
|
2265
|
+
chunks.push(chunk);
|
|
2266
|
+
if (chunk.usage) usage = {
|
|
2267
|
+
prompt_tokens: chunk.usage.prompt_tokens,
|
|
2268
|
+
completion_tokens: chunk.usage.completion_tokens,
|
|
2269
|
+
cached_tokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? 0
|
|
2270
|
+
};
|
|
2271
|
+
} catch (error) {
|
|
2272
|
+
consola.error("Failed to parse chunk:", error, event.data);
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
return {
|
|
2276
|
+
chunks,
|
|
2277
|
+
usage
|
|
2278
|
+
};
|
|
2279
|
+
}
|
|
2134
2280
|
async function handleCompletion(c) {
|
|
2135
2281
|
await checkRateLimit(state);
|
|
2136
2282
|
const anthropicPayload = await c.req.json();
|
|
@@ -2141,7 +2287,6 @@ async function handleCompletion(c) {
|
|
|
2141
2287
|
...openAIPayload,
|
|
2142
2288
|
model: normalizeModelName(openAIPayload.model)
|
|
2143
2289
|
};
|
|
2144
|
-
consola.debug("Translated OpenAI request payload:", JSON.stringify(openAIPayload));
|
|
2145
2290
|
if (state.manualApprove) await awaitApproval();
|
|
2146
2291
|
const isAzureModel = isAzureOpenAIModel(openAIPayload.model);
|
|
2147
2292
|
if (isAzureModel) {
|
|
@@ -2162,33 +2307,38 @@ async function handleCompletion(c) {
|
|
|
2162
2307
|
};
|
|
2163
2308
|
const eventStream = isAzureModel ? await createAzureOpenAIChatCompletions(state.azureOpenAIConfig, streamPayload) : await createChatCompletions(streamPayload);
|
|
2164
2309
|
return streamSSE(c, async (stream) => {
|
|
2310
|
+
const { chunks, usage } = await collectChunksWithUsage(eventStream);
|
|
2311
|
+
consola.debug(`[stream] Collected ${chunks.length} chunks, usage:`, usage);
|
|
2312
|
+
if (usage) setRequestContext(c, {
|
|
2313
|
+
inputTokens: usage.prompt_tokens,
|
|
2314
|
+
outputTokens: usage.completion_tokens
|
|
2315
|
+
});
|
|
2165
2316
|
const streamState = {
|
|
2166
2317
|
messageStartSent: false,
|
|
2167
2318
|
contentBlockOpen: false,
|
|
2168
2319
|
contentBlockIndex: 0,
|
|
2169
|
-
toolCalls: {}
|
|
2320
|
+
toolCalls: {},
|
|
2321
|
+
pendingUsage: usage ?? void 0
|
|
2170
2322
|
};
|
|
2171
|
-
for
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
consola.debug("Anthropic event:", JSON.stringify(anthropicEvent));
|
|
2179
|
-
await stream.writeSSE({
|
|
2180
|
-
event: anthropicEvent.type,
|
|
2181
|
-
data: JSON.stringify(anthropicEvent)
|
|
2182
|
-
});
|
|
2183
|
-
}
|
|
2184
|
-
if (chunk.usage) setRequestContext(c, {
|
|
2185
|
-
inputTokens: chunk.usage.prompt_tokens,
|
|
2186
|
-
outputTokens: chunk.usage.completion_tokens
|
|
2323
|
+
for (const chunk of chunks) {
|
|
2324
|
+
const events$1 = translateChunkToAnthropicEvents(chunk, streamState, anthropicPayload.model);
|
|
2325
|
+
for (const evt of events$1) {
|
|
2326
|
+
consola.debug(`[stream] Emitting event: ${evt.type}`);
|
|
2327
|
+
await stream.writeSSE({
|
|
2328
|
+
event: evt.type,
|
|
2329
|
+
data: JSON.stringify(evt)
|
|
2187
2330
|
});
|
|
2188
|
-
} catch (error) {
|
|
2189
|
-
consola.error("Failed to parse chunk:", error, event.data);
|
|
2190
2331
|
}
|
|
2191
2332
|
}
|
|
2333
|
+
const fallbackEvents = createFallbackMessageDeltaEvents(streamState);
|
|
2334
|
+
consola.debug(`[stream] Fallback events: ${fallbackEvents.length}, messageDeltaSent: ${streamState.messageDeltaSent}`);
|
|
2335
|
+
for (const evt of fallbackEvents) {
|
|
2336
|
+
consola.debug(`[stream] Emitting fallback event: ${evt.type}`);
|
|
2337
|
+
await stream.writeSSE({
|
|
2338
|
+
event: evt.type,
|
|
2339
|
+
data: JSON.stringify(evt)
|
|
2340
|
+
});
|
|
2341
|
+
}
|
|
2192
2342
|
});
|
|
2193
2343
|
}
|
|
2194
2344
|
const nonStreamPayload = {
|
|
@@ -2196,13 +2346,11 @@ async function handleCompletion(c) {
|
|
|
2196
2346
|
stream: false
|
|
2197
2347
|
};
|
|
2198
2348
|
const response = isAzureModel ? await createAzureOpenAIChatCompletions(state.azureOpenAIConfig, nonStreamPayload) : await createChatCompletions(nonStreamPayload);
|
|
2199
|
-
consola.debug("Response from upstream:", JSON.stringify(response).slice(-400));
|
|
2200
2349
|
if (response.usage) setRequestContext(c, {
|
|
2201
2350
|
inputTokens: response.usage.prompt_tokens,
|
|
2202
2351
|
outputTokens: response.usage.completion_tokens
|
|
2203
2352
|
});
|
|
2204
|
-
const anthropicResponse = translateToAnthropic(response);
|
|
2205
|
-
consola.debug("Translated Anthropic response:", JSON.stringify(anthropicResponse));
|
|
2353
|
+
const anthropicResponse = translateToAnthropic(response, anthropicPayload.model);
|
|
2206
2354
|
return c.json(anthropicResponse);
|
|
2207
2355
|
}
|
|
2208
2356
|
|
|
@@ -2345,6 +2493,7 @@ server.route("/v1/messages", messageRoutes);
|
|
|
2345
2493
|
//#endregion
|
|
2346
2494
|
//#region src/start.ts
|
|
2347
2495
|
async function runServer(options) {
|
|
2496
|
+
consola.info(`copilot-api v${package_default.version}`);
|
|
2348
2497
|
if (options.insecure) {
|
|
2349
2498
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
2350
2499
|
consola.warn("SSL certificate verification disabled (insecure mode)");
|
|
@@ -2407,7 +2556,8 @@ async function runServer(options) {
|
|
|
2407
2556
|
consola.box(`🌐 Usage Viewer: https://ericc-ch.github.io/copilot-api?endpoint=${serverUrl}/usage`);
|
|
2408
2557
|
serve({
|
|
2409
2558
|
fetch: server.fetch,
|
|
2410
|
-
port: options.port
|
|
2559
|
+
port: options.port,
|
|
2560
|
+
bun: { idleTimeout: 255 }
|
|
2411
2561
|
});
|
|
2412
2562
|
}
|
|
2413
2563
|
const start = defineCommand({
|
|
@@ -2508,6 +2658,7 @@ const start = defineCommand({
|
|
|
2508
2658
|
const main = defineCommand({
|
|
2509
2659
|
meta: {
|
|
2510
2660
|
name: "copilot-api",
|
|
2661
|
+
version: package_default.version,
|
|
2511
2662
|
description: "A wrapper around GitHub Copilot API to make it OpenAI compatible, making it usable for other tools."
|
|
2512
2663
|
},
|
|
2513
2664
|
subCommands: {
|