@dianshuv/copilot-api 0.14.0 → 0.15.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.
Files changed (2) hide show
  1. package/dist/main.mjs +103 -3
  2. package/package.json +1 -1
package/dist/main.mjs CHANGED
@@ -1086,7 +1086,7 @@ const logout = defineCommand({
1086
1086
 
1087
1087
  //#endregion
1088
1088
  //#region package.json
1089
- var version = "0.14.0";
1089
+ var version = "0.15.0";
1090
1090
 
1091
1091
  //#endregion
1092
1092
  //#region src/lib/event-loop-lag.ts
@@ -6944,6 +6944,74 @@ function supportsDirectAnthropicApi(modelId) {
6944
6944
  return resolveAnthropicModelForDirectPath(modelId) !== void 0;
6945
6945
  }
6946
6946
 
6947
+ //#endregion
6948
+ //#region src/routes/messages/cache-control-injector.ts
6949
+ function blockHasCacheControl(block) {
6950
+ if (typeof block !== "object" || block === null) return false;
6951
+ if (!("cache_control" in block)) return false;
6952
+ const cc = block.cache_control;
6953
+ return cc !== void 0 && cc !== null;
6954
+ }
6955
+ function toolsHaveCacheControl(tools) {
6956
+ if (!Array.isArray(tools)) return false;
6957
+ return tools.some((t) => blockHasCacheControl(t));
6958
+ }
6959
+ function toolResultInnerHasCacheControl(block) {
6960
+ if (!Array.isArray(block.content)) return false;
6961
+ return block.content.some((inner) => blockHasCacheControl(inner));
6962
+ }
6963
+ function messagesHaveCacheControl(messages) {
6964
+ if (!Array.isArray(messages)) return false;
6965
+ for (const message of messages) {
6966
+ if (!Array.isArray(message.content)) continue;
6967
+ for (const block of message.content) {
6968
+ if (blockHasCacheControl(block)) return true;
6969
+ if (block.type === "tool_result" && toolResultInnerHasCacheControl(block)) return true;
6970
+ }
6971
+ }
6972
+ return false;
6973
+ }
6974
+ function systemHasCacheControl(system) {
6975
+ if (!Array.isArray(system)) return false;
6976
+ return system.some((b) => blockHasCacheControl(b));
6977
+ }
6978
+ function hasAnyCacheControl(payload) {
6979
+ return systemHasCacheControl(payload.system) || toolsHaveCacheControl(payload.tools) || messagesHaveCacheControl(payload.messages);
6980
+ }
6981
+ function injectSystemCacheControl(payload) {
6982
+ if (hasAnyCacheControl(payload)) return;
6983
+ if (payload.system === void 0 || payload.system === null) return;
6984
+ if (typeof payload.system === "string") {
6985
+ if (payload.system.length === 0) return;
6986
+ payload.system = [{
6987
+ type: "text",
6988
+ text: payload.system,
6989
+ cache_control: { type: "ephemeral" }
6990
+ }];
6991
+ return;
6992
+ }
6993
+ if (payload.system.length === 0) return;
6994
+ const tail = payload.system.at(-1);
6995
+ if (!tail) return;
6996
+ tail.cache_control = { type: "ephemeral" };
6997
+ }
6998
+
6999
+ //#endregion
7000
+ //#region src/routes/messages/date-normalizer.ts
7001
+ const dateTrackingRegex = /(# currentDate\r?\n)Today['’ʼʹ]s date is (\d{4})[/-](\d{2})[/-](\d{2})\.(\r?\n|$)/g;
7002
+ function normalizeClaudeCodeDate(text) {
7003
+ return text.replaceAll(dateTrackingRegex, "$1Today's date is $2-$3-$4.$5");
7004
+ }
7005
+ function normalizeSystemPromptDate(payload) {
7006
+ if (typeof payload.system === "string") {
7007
+ payload.system = normalizeClaudeCodeDate(payload.system);
7008
+ return;
7009
+ }
7010
+ if (Array.isArray(payload.system)) {
7011
+ for (const block of payload.system) if (typeof block.text === "string") block.text = normalizeClaudeCodeDate(block.text);
7012
+ }
7013
+ }
7014
+
6947
7015
  //#endregion
6948
7016
  //#region src/lib/stream-keepalive.ts
6949
7017
  /** SSE comment line used as a keepalive heartbeat. */
@@ -8805,7 +8873,11 @@ async function handleCompletion(c) {
8805
8873
  const subagentMarker = parseSubagentMarkerFromFirstUser(sanitizedPayload);
8806
8874
  const initiatorOverride = subagentMarker ? "agent" : void 0;
8807
8875
  if (subagentMarker) consola.debug("Detected Subagent marker:", JSON.stringify(subagentMarker));
8808
- if (supportsDirectAnthropicApi(sanitizedPayload.model)) return handleDirectAnthropicCompletion(c, sanitizedPayload, ctx, initiatorOverride);
8876
+ if (supportsDirectAnthropicApi(sanitizedPayload.model)) {
8877
+ normalizeSystemPromptDate(sanitizedPayload);
8878
+ injectSystemCacheControl(sanitizedPayload);
8879
+ return handleDirectAnthropicCompletion(c, sanitizedPayload, ctx, initiatorOverride);
8880
+ }
8809
8881
  return handleTranslatedCompletion(c, sanitizedPayload, ctx, initiatorOverride);
8810
8882
  }
8811
8883
  /**
@@ -8938,6 +9010,31 @@ modelRoutes.get("/", async (c) => {
8938
9010
  }
8939
9011
  });
8940
9012
 
9013
+ //#endregion
9014
+ //#region src/lib/prompt-cache-key.ts
9015
+ const CLIENT_NAME_PATTERNS = [
9016
+ [/^claude[-_]?(?:code|cli)$/i, "claude-code"],
9017
+ [/^codex$/i, "codex"],
9018
+ [/^cursor$/i, "cursor"],
9019
+ [/^aider$/i, "aider"],
9020
+ [/^copilot$/i, "copilot"]
9021
+ ];
9022
+ const UA_MAIN_TOKEN = /^([\w.-]+)/;
9023
+ function extractClientName(userAgent) {
9024
+ if (!userAgent) return "unknown";
9025
+ const mainToken = UA_MAIN_TOKEN.exec(userAgent)?.[1]?.toLowerCase();
9026
+ if (!mainToken) return "unknown";
9027
+ for (const [pattern, name] of CLIENT_NAME_PATTERNS) if (pattern.test(mainToken)) return name;
9028
+ return mainToken.slice(0, 32);
9029
+ }
9030
+ function buildPromptCacheKey(clientName) {
9031
+ return `copilot-api:${clientName}`;
9032
+ }
9033
+ function injectPromptCacheKey(payload, clientName) {
9034
+ if (payload.prompt_cache_key !== void 0 && payload.prompt_cache_key !== null && payload.prompt_cache_key !== "") return;
9035
+ payload.prompt_cache_key = buildPromptCacheKey(clientName);
9036
+ }
9037
+
8941
9038
  //#endregion
8942
9039
  //#region src/services/copilot/create-responses.ts
8943
9040
  const createResponses = async (payload, { vision, initiator, resolvedModel }) => {
@@ -9195,14 +9292,17 @@ const TERMINAL_EVENTS = new Set([
9195
9292
  "error"
9196
9293
  ]);
9197
9294
  const handleResponses = async (c) => {
9295
+ const rawPayload = await c.req.json();
9296
+ const clientName = extractClientName(c.req.header("user-agent"));
9198
9297
  const { ctx, payload } = createEntryContext({
9199
9298
  c,
9200
- rawPayload: await c.req.json(),
9299
+ rawPayload,
9201
9300
  endpoint: "openai",
9202
9301
  normalizePayload: (p) => {
9203
9302
  const np = state.normalizeResponsesCallIds ? normalizeCallIds(p) : p;
9204
9303
  useFunctionApplyPatch(np);
9205
9304
  filterUnsupportedBuiltins(np);
9305
+ injectPromptCacheKey(np, clientName);
9206
9306
  return np;
9207
9307
  },
9208
9308
  buildHistoryRequest: (p) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dianshuv/copilot-api",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Turn GitHub Copilot into OpenAI/Anthropic API compatible server. Usable with Claude Code!",
5
5
  "author": "dianshuv",
6
6
  "type": "module",