@dianshuv/copilot-api 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/main.mjs +50 -24
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -32,6 +32,16 @@ copilot-api start
|
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
## Development
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
# Start the server (foreground, production mode)
|
|
39
|
+
make up
|
|
40
|
+
|
|
41
|
+
# Stop the server (graceful shutdown)
|
|
42
|
+
make down
|
|
43
|
+
```
|
|
44
|
+
|
|
35
45
|
## Command Reference
|
|
36
46
|
|
|
37
47
|
| Command | Description |
|
package/dist/main.mjs
CHANGED
|
@@ -1213,7 +1213,7 @@ const patchClaude = defineCommand({
|
|
|
1213
1213
|
|
|
1214
1214
|
//#endregion
|
|
1215
1215
|
//#region package.json
|
|
1216
|
-
var version = "0.7.
|
|
1216
|
+
var version = "0.7.2";
|
|
1217
1217
|
|
|
1218
1218
|
//#endregion
|
|
1219
1219
|
//#region src/lib/adaptive-rate-limiter.ts
|
|
@@ -3252,13 +3252,28 @@ const getTokenCount = async (payload, model) => {
|
|
|
3252
3252
|
|
|
3253
3253
|
//#endregion
|
|
3254
3254
|
//#region src/services/copilot/create-chat-completions.ts
|
|
3255
|
+
const GPT_MODEL_PATTERN = /^gpt-/i;
|
|
3255
3256
|
const createChatCompletions = async (payload, options) => {
|
|
3256
3257
|
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
3257
|
-
const
|
|
3258
|
-
const
|
|
3258
|
+
const vendor = options?.resolvedModel?.vendor;
|
|
3259
|
+
const isOpenAIVendor = vendor === "OpenAI" || vendor === "Azure OpenAI";
|
|
3260
|
+
const isLikelyGPT = !options?.resolvedModel && GPT_MODEL_PATTERN.test(payload.model);
|
|
3261
|
+
let wire = payload;
|
|
3262
|
+
if (isOpenAIVendor || isLikelyGPT) {
|
|
3263
|
+
const { max_tokens, max_completion_tokens, ...rest } = payload;
|
|
3264
|
+
const effective = max_completion_tokens ?? max_tokens;
|
|
3265
|
+
wire = {
|
|
3266
|
+
...rest,
|
|
3267
|
+
...effective !== null && effective !== void 0 && { max_completion_tokens: effective }
|
|
3268
|
+
};
|
|
3269
|
+
}
|
|
3270
|
+
const enableVision = wire.messages.some((x) => typeof x.content !== "string" && x.content?.some((x) => x.type === "image_url"));
|
|
3271
|
+
const isAgentCall = wire.messages.some((msg) => ["assistant", "tool"].includes(msg.role));
|
|
3272
|
+
const modelSupportsVision = options?.resolvedModel?.capabilities?.supports?.vision !== false;
|
|
3259
3273
|
const headers = {
|
|
3260
3274
|
...copilotHeaders(state, {
|
|
3261
|
-
vision: enableVision,
|
|
3275
|
+
vision: enableVision && modelSupportsVision,
|
|
3276
|
+
modelRequestHeaders: options?.resolvedModel?.request_headers,
|
|
3262
3277
|
intent: isAgentCall ? "conversation-agent" : "conversation-panel"
|
|
3263
3278
|
}),
|
|
3264
3279
|
"X-Initiator": options?.initiator ?? (isAgentCall ? "agent" : "user")
|
|
@@ -3266,7 +3281,7 @@ const createChatCompletions = async (payload, options) => {
|
|
|
3266
3281
|
const response = await fetch(`${copilotBaseUrl(state)}/chat/completions`, {
|
|
3267
3282
|
method: "POST",
|
|
3268
3283
|
headers,
|
|
3269
|
-
body: JSON.stringify(
|
|
3284
|
+
body: JSON.stringify(wire)
|
|
3270
3285
|
});
|
|
3271
3286
|
if (!response.ok) {
|
|
3272
3287
|
consola.error("Failed to create chat completions", response);
|
|
@@ -3846,7 +3861,7 @@ async function handleCompletion$1(c) {
|
|
|
3846
3861
|
async function executeRequest(opts) {
|
|
3847
3862
|
const { c, payload, selectedModel, ctx, trackingId } = opts;
|
|
3848
3863
|
try {
|
|
3849
|
-
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createChatCompletions(payload));
|
|
3864
|
+
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createChatCompletions(payload, { resolvedModel: selectedModel }));
|
|
3850
3865
|
ctx.queueWaitMs = queueWaitMs;
|
|
3851
3866
|
if (isNonStreaming(response)) return handleNonStreamingResponse$1(c, response, ctx, payload);
|
|
3852
3867
|
consola.debug("Streaming response");
|
|
@@ -4503,7 +4518,7 @@ async function handleGeminiGenerate(c, model, isStream) {
|
|
|
4503
4518
|
trackingId,
|
|
4504
4519
|
startTime
|
|
4505
4520
|
};
|
|
4506
|
-
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createChatCompletions(payload));
|
|
4521
|
+
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createChatCompletions(payload, { resolvedModel: selectedModel }));
|
|
4507
4522
|
ctx.queueWaitMs = queueWaitMs;
|
|
4508
4523
|
if (isNonStreaming(response)) return handleNonStreamResponse(c, response, model, ctx, payload);
|
|
4509
4524
|
consola.debug("Streaming Gemini response");
|
|
@@ -4587,25 +4602,27 @@ function handleNonStreamResponse(c, response, model, ctx, payload) {
|
|
|
4587
4602
|
//#endregion
|
|
4588
4603
|
//#region src/routes/gemini/model-alias.ts
|
|
4589
4604
|
/**
|
|
4590
|
-
* Maps Gemini model names
|
|
4591
|
-
* to equivalent models that are.
|
|
4605
|
+
* Maps Gemini model names to equivalent models available on GitHub Copilot.
|
|
4592
4606
|
*
|
|
4593
|
-
*
|
|
4594
|
-
* and gemini-2.5-flash, which Copilot doesn't serve. We map them to
|
|
4595
|
-
* the closest available flash model.
|
|
4607
|
+
* Two types of aliases:
|
|
4596
4608
|
*
|
|
4597
|
-
*
|
|
4598
|
-
* the
|
|
4599
|
-
*
|
|
4609
|
+
* - **Forced**: Always applied regardless of Copilot model availability.
|
|
4610
|
+
* Use when the old model name should never reach the backend.
|
|
4611
|
+
*
|
|
4612
|
+
* - **Conditional**: Only applied when the requested model is absent from
|
|
4613
|
+
* the Copilot model list, so if Copilot adds native support the request
|
|
4614
|
+
* goes through unchanged.
|
|
4600
4615
|
*/
|
|
4601
|
-
const
|
|
4616
|
+
const GEMINI_FORCED_ALIASES = { "gemini-2.5-pro": "gemini-3.1-pro-preview" };
|
|
4617
|
+
const GEMINI_CONDITIONAL_ALIASES = {
|
|
4602
4618
|
"gemini-2.5-flash-lite": "gemini-3-flash-preview",
|
|
4603
4619
|
"gemini-2.5-flash": "gemini-3-flash-preview"
|
|
4604
4620
|
};
|
|
4605
4621
|
function resolveGeminiModelAlias(model) {
|
|
4606
|
-
if (
|
|
4607
|
-
if (
|
|
4608
|
-
return
|
|
4622
|
+
if (model in GEMINI_FORCED_ALIASES) return GEMINI_FORCED_ALIASES[model];
|
|
4623
|
+
if (!(model in GEMINI_CONDITIONAL_ALIASES)) return model;
|
|
4624
|
+
if (findModelById(model)) return model;
|
|
4625
|
+
return GEMINI_CONDITIONAL_ALIASES[model];
|
|
4609
4626
|
}
|
|
4610
4627
|
|
|
4611
4628
|
//#endregion
|
|
@@ -7702,7 +7719,10 @@ async function handleTranslatedCompletion(c, anthropicPayload, ctx, initiatorOve
|
|
|
7702
7719
|
if (truncateResult) ctx.truncateResult = truncateResult;
|
|
7703
7720
|
if (state.manualApprove) await awaitApproval();
|
|
7704
7721
|
try {
|
|
7705
|
-
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createChatCompletions(openAIPayload, {
|
|
7722
|
+
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createChatCompletions(openAIPayload, {
|
|
7723
|
+
initiator: initiatorOverride,
|
|
7724
|
+
resolvedModel: selectedModel
|
|
7725
|
+
}));
|
|
7706
7726
|
ctx.queueWaitMs = queueWaitMs;
|
|
7707
7727
|
if (isNonStreaming(response)) return handleNonStreamingResponse({
|
|
7708
7728
|
c,
|
|
@@ -8056,10 +8076,14 @@ modelRoutes.get("/", async (c) => {
|
|
|
8056
8076
|
|
|
8057
8077
|
//#endregion
|
|
8058
8078
|
//#region src/services/copilot/create-responses.ts
|
|
8059
|
-
const createResponses = async (payload, { vision, initiator }) => {
|
|
8079
|
+
const createResponses = async (payload, { vision, initiator, resolvedModel }) => {
|
|
8060
8080
|
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
8081
|
+
const modelSupportsVision = resolvedModel?.capabilities?.supports?.vision !== false;
|
|
8061
8082
|
const headers = {
|
|
8062
|
-
...copilotHeaders(state, {
|
|
8083
|
+
...copilotHeaders(state, {
|
|
8084
|
+
vision: vision && modelSupportsVision,
|
|
8085
|
+
modelRequestHeaders: resolvedModel?.request_headers
|
|
8086
|
+
}),
|
|
8063
8087
|
"X-Initiator": initiator
|
|
8064
8088
|
};
|
|
8065
8089
|
payload.service_tier = null;
|
|
@@ -8322,7 +8346,8 @@ const handleResponses = async (c) => {
|
|
|
8322
8346
|
trackingId,
|
|
8323
8347
|
startTime
|
|
8324
8348
|
};
|
|
8325
|
-
|
|
8349
|
+
const selectedModel = findModelById(payload.model);
|
|
8350
|
+
if (!(selectedModel?.supported_endpoints?.includes(RESPONSES_ENDPOINT) ?? false)) {
|
|
8326
8351
|
recordErrorResponse(ctx, model, /* @__PURE__ */ new Error("This model does not support the responses endpoint."));
|
|
8327
8352
|
return c.json({ error: {
|
|
8328
8353
|
message: "This model does not support the responses endpoint. Please choose a different model.",
|
|
@@ -8334,7 +8359,8 @@ const handleResponses = async (c) => {
|
|
|
8334
8359
|
try {
|
|
8335
8360
|
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createResponses(payload, {
|
|
8336
8361
|
vision,
|
|
8337
|
-
initiator
|
|
8362
|
+
initiator,
|
|
8363
|
+
resolvedModel: selectedModel
|
|
8338
8364
|
}));
|
|
8339
8365
|
ctx.queueWaitMs = queueWaitMs;
|
|
8340
8366
|
if (isStreamingRequested(payload) && isAsyncIterable(response)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dianshuv/copilot-api",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Turn GitHub Copilot into OpenAI/Anthropic API compatible server. Usable with Claude Code!",
|
|
5
5
|
"author": "dianshuv",
|
|
6
6
|
"type": "module",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"prepare": "npm run build && (command -v bun >/dev/null 2>&1 && simple-git-hooks || true)",
|
|
21
21
|
"prepublishOnly": "npm run typecheck && npm run lint:all && npm run test",
|
|
22
22
|
"release": "npm publish --access public --//registry.npmjs.org/:_authToken=$NPM_TOKEN",
|
|
23
|
-
"start": "NODE_ENV=production bun run ./src/main.ts",
|
|
23
|
+
"start": "NODE_ENV=production bun run ./src/main.ts start",
|
|
24
24
|
"test": "bun test tests/*.test.ts",
|
|
25
25
|
"test:all": "bun test tests/*.test.ts && bun test tests/integration/",
|
|
26
26
|
"test:integration": "bun test tests/integration/",
|