@dianshuv/copilot-api 0.7.1 → 0.7.3
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 +53 -16
- 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.3";
|
|
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);
|
|
@@ -3681,10 +3696,23 @@ function completeTracking(trackingId, inputTokens, outputTokens, queueWaitMs, re
|
|
|
3681
3696
|
stopReason: analytics.stopReason
|
|
3682
3697
|
});
|
|
3683
3698
|
}
|
|
3699
|
+
function formatError(error) {
|
|
3700
|
+
if (error instanceof Error) return error.message || error.name;
|
|
3701
|
+
if (typeof error === "string") return error;
|
|
3702
|
+
try {
|
|
3703
|
+
const s = JSON.stringify(error);
|
|
3704
|
+
if (s && s !== "{}") return s;
|
|
3705
|
+
} catch {}
|
|
3706
|
+
try {
|
|
3707
|
+
return String(error);
|
|
3708
|
+
} catch {
|
|
3709
|
+
return "Unknown error";
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3684
3712
|
/** Fail TUI tracking */
|
|
3685
3713
|
function failTracking(trackingId, error) {
|
|
3686
3714
|
if (!trackingId) return;
|
|
3687
|
-
requestTracker.failRequest(trackingId, error
|
|
3715
|
+
requestTracker.failRequest(trackingId, formatError(error));
|
|
3688
3716
|
}
|
|
3689
3717
|
/**
|
|
3690
3718
|
* Create a marker to prepend to responses indicating auto-truncation occurred.
|
|
@@ -3707,7 +3735,7 @@ function recordStreamError(opts) {
|
|
|
3707
3735
|
input_tokens: 0,
|
|
3708
3736
|
output_tokens: 0
|
|
3709
3737
|
},
|
|
3710
|
-
error: error
|
|
3738
|
+
error: formatError(error),
|
|
3711
3739
|
content: null
|
|
3712
3740
|
}, Date.now() - ctx.startTime);
|
|
3713
3741
|
}
|
|
@@ -3846,7 +3874,7 @@ async function handleCompletion$1(c) {
|
|
|
3846
3874
|
async function executeRequest(opts) {
|
|
3847
3875
|
const { c, payload, selectedModel, ctx, trackingId } = opts;
|
|
3848
3876
|
try {
|
|
3849
|
-
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createChatCompletions(payload));
|
|
3877
|
+
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createChatCompletions(payload, { resolvedModel: selectedModel }));
|
|
3850
3878
|
ctx.queueWaitMs = queueWaitMs;
|
|
3851
3879
|
if (isNonStreaming(response)) return handleNonStreamingResponse$1(c, response, ctx, payload);
|
|
3852
3880
|
consola.debug("Streaming response");
|
|
@@ -4503,7 +4531,7 @@ async function handleGeminiGenerate(c, model, isStream) {
|
|
|
4503
4531
|
trackingId,
|
|
4504
4532
|
startTime
|
|
4505
4533
|
};
|
|
4506
|
-
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createChatCompletions(payload));
|
|
4534
|
+
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createChatCompletions(payload, { resolvedModel: selectedModel }));
|
|
4507
4535
|
ctx.queueWaitMs = queueWaitMs;
|
|
4508
4536
|
if (isNonStreaming(response)) return handleNonStreamResponse(c, response, model, ctx, payload);
|
|
4509
4537
|
consola.debug("Streaming Gemini response");
|
|
@@ -7629,7 +7657,7 @@ async function handleDirectAnthropicStreamingResponse(opts) {
|
|
|
7629
7657
|
toolCount: anthropicPayload.tools?.length ?? 0
|
|
7630
7658
|
});
|
|
7631
7659
|
} catch (error) {
|
|
7632
|
-
consola.error("Direct Anthropic stream error:", error);
|
|
7660
|
+
consola.error("Direct Anthropic stream error:", formatError(error), error);
|
|
7633
7661
|
recordStreamError({
|
|
7634
7662
|
acc,
|
|
7635
7663
|
fallbackModel: anthropicPayload.model,
|
|
@@ -7704,7 +7732,10 @@ async function handleTranslatedCompletion(c, anthropicPayload, ctx, initiatorOve
|
|
|
7704
7732
|
if (truncateResult) ctx.truncateResult = truncateResult;
|
|
7705
7733
|
if (state.manualApprove) await awaitApproval();
|
|
7706
7734
|
try {
|
|
7707
|
-
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createChatCompletions(openAIPayload, {
|
|
7735
|
+
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createChatCompletions(openAIPayload, {
|
|
7736
|
+
initiator: initiatorOverride,
|
|
7737
|
+
resolvedModel: selectedModel
|
|
7738
|
+
}));
|
|
7708
7739
|
ctx.queueWaitMs = queueWaitMs;
|
|
7709
7740
|
if (isNonStreaming(response)) return handleNonStreamingResponse({
|
|
7710
7741
|
c,
|
|
@@ -7812,7 +7843,7 @@ async function handleStreamingResponse(opts) {
|
|
|
7812
7843
|
toolCount: anthropicPayload.tools?.length ?? 0
|
|
7813
7844
|
});
|
|
7814
7845
|
} catch (error) {
|
|
7815
|
-
consola.error("Stream error:", error);
|
|
7846
|
+
consola.error("Stream error:", formatError(error), error);
|
|
7816
7847
|
recordStreamError({
|
|
7817
7848
|
acc,
|
|
7818
7849
|
fallbackModel: anthropicPayload.model,
|
|
@@ -8058,10 +8089,14 @@ modelRoutes.get("/", async (c) => {
|
|
|
8058
8089
|
|
|
8059
8090
|
//#endregion
|
|
8060
8091
|
//#region src/services/copilot/create-responses.ts
|
|
8061
|
-
const createResponses = async (payload, { vision, initiator }) => {
|
|
8092
|
+
const createResponses = async (payload, { vision, initiator, resolvedModel }) => {
|
|
8062
8093
|
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
8094
|
+
const modelSupportsVision = resolvedModel?.capabilities?.supports?.vision !== false;
|
|
8063
8095
|
const headers = {
|
|
8064
|
-
...copilotHeaders(state, {
|
|
8096
|
+
...copilotHeaders(state, {
|
|
8097
|
+
vision: vision && modelSupportsVision,
|
|
8098
|
+
modelRequestHeaders: resolvedModel?.request_headers
|
|
8099
|
+
}),
|
|
8065
8100
|
"X-Initiator": initiator
|
|
8066
8101
|
};
|
|
8067
8102
|
payload.service_tier = null;
|
|
@@ -8324,7 +8359,8 @@ const handleResponses = async (c) => {
|
|
|
8324
8359
|
trackingId,
|
|
8325
8360
|
startTime
|
|
8326
8361
|
};
|
|
8327
|
-
|
|
8362
|
+
const selectedModel = findModelById(payload.model);
|
|
8363
|
+
if (!(selectedModel?.supported_endpoints?.includes(RESPONSES_ENDPOINT) ?? false)) {
|
|
8328
8364
|
recordErrorResponse(ctx, model, /* @__PURE__ */ new Error("This model does not support the responses endpoint."));
|
|
8329
8365
|
return c.json({ error: {
|
|
8330
8366
|
message: "This model does not support the responses endpoint. Please choose a different model.",
|
|
@@ -8336,7 +8372,8 @@ const handleResponses = async (c) => {
|
|
|
8336
8372
|
try {
|
|
8337
8373
|
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createResponses(payload, {
|
|
8338
8374
|
vision,
|
|
8339
|
-
initiator
|
|
8375
|
+
initiator,
|
|
8376
|
+
resolvedModel: selectedModel
|
|
8340
8377
|
}));
|
|
8341
8378
|
ctx.queueWaitMs = queueWaitMs;
|
|
8342
8379
|
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.3",
|
|
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/",
|