From a80bb413fee990348bcb550ee0f6b0469c4e95f5 Mon Sep 17 00:00:00 2001 From: mrhan <1309443685@qq.com> Date: Mon, 5 Jan 2026 19:27:20 +0800 Subject: [PATCH 1/2] fix: #156 --- src/lib/utils.ts | 9 ++++----- src/routes/messages/non-stream-translation.ts | 14 ++++++++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index cc80be66..641da9a9 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,9 +1,9 @@ import consola from "consola" -import { getModels } from "~/services/copilot/get-models" -import { getVSCodeVersion } from "~/services/get-vscode-version" +import {getModels} from "~/services/copilot/get-models" +import {getVSCodeVersion} from "~/services/get-vscode-version" -import { state } from "./state" +import {state} from "./state" export const sleep = (ms: number) => new Promise((resolve) => { @@ -14,8 +14,7 @@ export const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined export async function cacheModels(): Promise { - const models = await getModels() - state.models = models + state.models = await getModels() } export const cacheVSCodeVersion = async () => { diff --git a/src/routes/messages/non-stream-translation.ts b/src/routes/messages/non-stream-translation.ts index dc41e638..69502242 100644 --- a/src/routes/messages/non-stream-translation.ts +++ b/src/routes/messages/non-stream-translation.ts @@ -1,3 +1,5 @@ +import consola from "consola" + import { type ChatCompletionResponse, type ChatCompletionsPayload, @@ -48,11 +50,15 @@ export function translateToOpenAI( function translateModelName(model: string): string { // Subagent requests use a specific model number which Copilot doesn't support - if (model.startsWith("claude-sonnet-4-")) { - return model.replace(/^claude-sonnet-4-.*/, "claude-sonnet-4") - } else if (model.startsWith("claude-opus-")) { - return model.replace(/^claude-opus-4-.*/, "claude-opus-4") + if (model.startsWith("claude")) { + const newModel = model.replaceAll( + /(claude-(?:haiku|sonnet|opus)-\d+)-(\d+)(?:[.-]\d+)?/g, + "$1.$2", + ) + consola.log("Use Model:", model, newModel) + return newModel } + consola.log("Use Model:", model) return model } From ef55bdbb86bad9a67cb362b661320be913c47df5 Mon Sep 17 00:00:00 2001 From: mrhan <1309443685@qq.com> Date: Thu, 22 Jan 2026 21:00:48 +0800 Subject: [PATCH 2/2] fix: x-anthropic-billing-header --- package.json | 2 +- src/routes/chat-completions/handler.ts | 22 ++++++++++++++++++- src/routes/messages/count-tokens-handler.ts | 2 +- src/routes/messages/non-stream-translation.ts | 16 ++++++++++++-- .../copilot/create-chat-completions.ts | 1 + 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a5adbb8e..437b9096 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "copilot-api", - "version": "0.7.0", + "version": "0.7.2", "description": "Turn GitHub Copilot into OpenAI/Anthropic API compatible server. Usable with Claude Code!", "keywords": [ "proxy", diff --git a/src/routes/chat-completions/handler.ts b/src/routes/chat-completions/handler.ts index 04a5ae9e..05f98372 100644 --- a/src/routes/chat-completions/handler.ts +++ b/src/routes/chat-completions/handler.ts @@ -14,12 +14,32 @@ import { type ChatCompletionsPayload, } from "~/services/copilot/create-chat-completions" +function transferablePayload( + payload: ChatCompletionsPayload, +): ChatCompletionsPayload { + for (const message of payload.messages) { + consola.info(message.role, message.content, typeof message.content) + if ( + message.role === "system" + && typeof message.content === "string" + && message.content.startsWith("x-anthropic-billing-header") + ) { + message.content = message.content.replace( + /x-anthropic-billing-header: ?cc_version=.+; ?cc_entrypoint=\w+\n{0,2}/, + "", + ) + consola.info('包含"x-anthropic-billing-header"的system消息已被移除') + } + } + return payload +} + export async function handleCompletion(c: Context) { await checkRateLimit(state) let payload = await c.req.json() consola.debug("Request payload:", JSON.stringify(payload).slice(-400)) - + payload = transferablePayload(payload) // Find the selected model const selectedModel = state.models?.data.find( (model) => model.id === payload.model, diff --git a/src/routes/messages/count-tokens-handler.ts b/src/routes/messages/count-tokens-handler.ts index 2ec849cb..14b3b2d9 100644 --- a/src/routes/messages/count-tokens-handler.ts +++ b/src/routes/messages/count-tokens-handler.ts @@ -18,7 +18,7 @@ export async function handleCountTokens(c: Context) { const anthropicPayload = await c.req.json() const openAIPayload = translateToOpenAI(anthropicPayload) - + consola.log("OPENAI", JSON.stringify(openAIPayload, null, 2)) const selectedModel = state.models?.data.find( (model) => model.id === anthropicPayload.model, ) diff --git a/src/routes/messages/non-stream-translation.ts b/src/routes/messages/non-stream-translation.ts index 69502242..15b7d14a 100644 --- a/src/routes/messages/non-stream-translation.ts +++ b/src/routes/messages/non-stream-translation.ts @@ -66,8 +66,20 @@ function translateAnthropicMessagesToOpenAI( anthropicMessages: Array, system: string | Array | undefined, ): Array { - const systemMessages = handleSystemPrompt(system) - + let systemMessages = handleSystemPrompt(system) + systemMessages = systemMessages.map((it) => { + if ( + typeof it.content === "string" + && it.content.startsWith("x-anthropic-billing-header") + ) { + it.content = it.content.replace( + /x-anthropic-billing-header: ?cc_version=.+; ?cc_entrypoint=\w+\n{0,2}/, + "", + ) + consola.info('包含"x-anthropic-billing-header"的system消息已被移除') + } + return it + }) const otherMessages = anthropicMessages.flatMap((message) => message.role === "user" ? handleUserMessage(message) diff --git a/src/services/copilot/create-chat-completions.ts b/src/services/copilot/create-chat-completions.ts index 8534151d..4ace895e 100644 --- a/src/services/copilot/create-chat-completions.ts +++ b/src/services/copilot/create-chat-completions.ts @@ -36,6 +36,7 @@ export const createChatCompletions = async ( if (!response.ok) { consola.error("Failed to create chat completions", response) + // consola.info(JSON.stringify(payload, null, 2)) throw new HTTPError("Failed to create chat completions", response) }