@jeffreycao/copilot-api 1.10.0 → 1.10.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.
@@ -1,6 +1,6 @@
1
1
  import { t as PATHS } from "./paths-DC-mqCY3.js";
2
- import { D as generateTraceId, E as prepareMessageProxyHeaders, I as state, M as compactMessageSections, O as requestContext, P as compactSystemPromptStarts, T as prepareInteractionHeaders, _ as copilotWebSocketHeaders, c as getUUID, d as sleep, f as getCopilotUsage, g as copilotHeaders, h as copilotBaseUrl, j as compactAutoContinuePromptStarts, k as resolveTraceId$1, l as isNullish, m as forwardError, n as cacheModels, o as generateRequestIdFromPayload, p as HTTPError, s as getRootSessionId, u as parseUserIdMetadata, w as prepareForCompact } from "./utils-C5ej0z8n.js";
3
- import { a as getConfig, c as getReasoningEffortForModel, d as isResponsesApiContextManagementModel, f as isResponsesApiWebSearchEnabled, i as getClaudeTokenMultiplier, l as getSmallModel, o as getExtraPromptForModel, p as isResponsesApiWebSocketEnabled, r as getAnthropicApiKey, s as getProviderConfig, t as getProxyEnvDispatcher, u as isMessagesApiEnabled } from "./proxy-De0Po8kG.js";
2
+ import { D as generateTraceId, E as prepareMessageProxyHeaders, I as state, M as compactMessageSections, O as requestContext, P as compactSystemPromptStarts, T as prepareInteractionHeaders, _ as copilotWebSocketHeaders, c as getUUID, d as sleep, f as getCopilotUsage, g as copilotHeaders, h as copilotBaseUrl, j as compactAutoContinuePromptStarts, k as resolveTraceId$1, l as isNullish, m as forwardError, n as cacheModels, o as generateRequestIdFromPayload, p as HTTPError, s as getRootSessionId, u as parseUserIdMetadata, w as prepareForCompact } from "./utils-jHLgqAq2.js";
3
+ import { _ as setModelMappings, a as getConfig, c as getProviderConfig, d as isMessagesApiEnabled, f as isResponsesApiContextManagementModel, g as resolveMappedModel, i as getClaudeTokenMultiplier, l as getReasoningEffortForModel, m as isResponsesApiWebSocketEnabled, o as getExtraPromptForModel, p as isResponsesApiWebSearchEnabled, r as getAnthropicApiKey, s as getModelMappings, t as getProxyEnvDispatcher, u as getSmallModel } from "./proxy-DQLzdeq3.js";
4
4
  import consola from "consola";
5
5
  import fs from "node:fs/promises";
6
6
  import path from "node:path";
@@ -12,6 +12,7 @@ import fs$1, { readFileSync } from "node:fs";
12
12
  import { streamSSE } from "hono/streaming";
13
13
  import util from "node:util";
14
14
  import { events } from "fetch-event-stream";
15
+ import { z } from "zod";
15
16
  import { WebSocket } from "undici";
16
17
  //#region src/lib/request-auth.ts
17
18
  function normalizeApiKeys(apiKeys) {
@@ -26,6 +27,14 @@ function normalizeApiKeys(apiKeys) {
26
27
  function getConfiguredApiKeys() {
27
28
  return normalizeApiKeys(getConfig().auth?.apiKeys);
28
29
  }
30
+ function normalizeApiKey(apiKey) {
31
+ if (typeof apiKey !== "string") return null;
32
+ return apiKey.trim() || null;
33
+ }
34
+ function getConfiguredAdminApiKeys() {
35
+ const adminApiKey = normalizeApiKey(getConfig().auth?.adminApiKey);
36
+ return adminApiKey ? [adminApiKey] : [];
37
+ }
29
38
  function extractRequestApiKey(c) {
30
39
  const xApiKey = c.req.header("x-api-key")?.trim();
31
40
  if (xApiKey) return xApiKey;
@@ -46,11 +55,14 @@ function createAuthMiddleware(options = {}) {
46
55
  const getApiKeys = options.getApiKeys ?? getConfiguredApiKeys;
47
56
  const allowUnauthenticatedPaths = options.allowUnauthenticatedPaths ?? ["/"];
48
57
  const allowOptionsBypass = options.allowOptionsBypass ?? true;
58
+ const allowWhenNoApiKeys = options.allowWhenNoApiKeys ?? true;
59
+ const shouldSkipPath = options.shouldSkipPath ?? (() => false);
49
60
  return async (c, next) => {
50
61
  if (allowOptionsBypass && c.req.method === "OPTIONS") return next();
62
+ if (shouldSkipPath(c.req.path)) return next();
51
63
  if (allowUnauthenticatedPaths.includes(c.req.path)) return next();
52
64
  const apiKeys = getApiKeys();
53
- if (apiKeys.length === 0) return next();
65
+ if (apiKeys.length === 0) return allowWhenNoApiKeys ? next() : createUnauthorizedResponse(c);
54
66
  const requestApiKey = extractRequestApiKey(c);
55
67
  if (!requestApiKey || !apiKeys.includes(requestApiKey)) return createUnauthorizedResponse(c);
56
68
  return next();
@@ -504,19 +516,14 @@ async function flushTokenUsageEvents() {
504
516
  }
505
517
  function getPeriodRange(period, now = /* @__PURE__ */ new Date()) {
506
518
  const start = new Date(now);
519
+ start.setHours(0, 0, 0, 0);
507
520
  switch (period) {
508
- case "day":
509
- start.setHours(0, 0, 0, 0);
510
- break;
511
- case "week": {
512
- const daysSinceMonday = (start.getDay() + 6) % 7;
513
- start.setDate(start.getDate() - daysSinceMonday);
514
- start.setHours(0, 0, 0, 0);
521
+ case "day": break;
522
+ case "week":
523
+ start.setDate(start.getDate() - 6);
515
524
  break;
516
- }
517
525
  case "month":
518
- start.setDate(1);
519
- start.setHours(0, 0, 0, 0);
526
+ start.setDate(start.getDate() - 29);
520
527
  break;
521
528
  default: break;
522
529
  }
@@ -529,7 +536,7 @@ function getPeriodRange(period, now = /* @__PURE__ */ new Date()) {
529
536
  end.setDate(end.getDate() + 7);
530
537
  break;
531
538
  case "month":
532
- end.setMonth(end.getMonth() + 1);
539
+ end.setDate(end.getDate() + 30);
533
540
  break;
534
541
  default: break;
535
542
  }
@@ -538,6 +545,26 @@ function getPeriodRange(period, now = /* @__PURE__ */ new Date()) {
538
545
  startMs: start.getTime()
539
546
  };
540
547
  }
548
+ function formatLocalDate(date) {
549
+ return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
550
+ }
551
+ function createDailyIntervals(range) {
552
+ const intervals = [];
553
+ const cursor = new Date(range.startMs);
554
+ while (cursor.getTime() < range.endMs) {
555
+ const startMs = cursor.getTime();
556
+ const next = new Date(cursor);
557
+ next.setDate(next.getDate() + 1);
558
+ const endMs = Math.min(next.getTime(), range.endMs);
559
+ intervals.push({
560
+ date: formatLocalDate(cursor),
561
+ endMs,
562
+ startMs
563
+ });
564
+ cursor.setTime(endMs);
565
+ }
566
+ return intervals;
567
+ }
541
568
  function createEmptyTotals() {
542
569
  return {
543
570
  cache_creation_input_tokens: 0,
@@ -548,6 +575,14 @@ function createEmptyTotals() {
548
575
  total_tokens: 0
549
576
  };
550
577
  }
578
+ function addTotals(target, next) {
579
+ target.cache_creation_input_tokens += next.cache_creation_input_tokens;
580
+ target.cache_read_input_tokens += next.cache_read_input_tokens;
581
+ target.input_tokens += next.input_tokens;
582
+ target.output_tokens += next.output_tokens;
583
+ target.request_count += next.request_count;
584
+ target.total_tokens += next.total_tokens;
585
+ }
551
586
  function createEmptySummary(period) {
552
587
  const range = getPeriodRange(period);
553
588
  return {
@@ -562,6 +597,27 @@ function createEmptySummary(period) {
562
597
  totals: createEmptyTotals()
563
598
  };
564
599
  }
600
+ function createEmptyDailySummary(period) {
601
+ const range = getPeriodRange(period);
602
+ return {
603
+ byModel: [],
604
+ days: createDailyIntervals(range).map((interval) => ({
605
+ byModel: [],
606
+ date: interval.date,
607
+ end_ms: interval.endMs,
608
+ start_ms: interval.startMs,
609
+ totals: createEmptyTotals()
610
+ })),
611
+ period,
612
+ range: {
613
+ end_ms: range.endMs,
614
+ end_utc: new Date(range.endMs).toISOString(),
615
+ start_ms: range.startMs,
616
+ start_utc: new Date(range.startMs).toISOString()
617
+ },
618
+ totals: createEmptyTotals()
619
+ };
620
+ }
565
621
  function createEmptyEventsPage(input) {
566
622
  const range = getPeriodRange(input.period);
567
623
  return {
@@ -579,6 +635,14 @@ function createEmptyEventsPage(input) {
579
635
  total_pages: 1
580
636
  };
581
637
  }
638
+ function rangePayload(range) {
639
+ return {
640
+ end_ms: range.endMs,
641
+ end_utc: new Date(range.endMs).toISOString(),
642
+ start_ms: range.startMs,
643
+ start_utc: new Date(range.startMs).toISOString()
644
+ };
645
+ }
582
646
  function numberFromRow(row, key) {
583
647
  const value = row?.[key];
584
648
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
@@ -593,6 +657,12 @@ function totalsFromRow(row) {
593
657
  total_tokens: numberFromRow(row, "total_tokens")
594
658
  };
595
659
  }
660
+ function modelSummaryFromRow(row) {
661
+ return {
662
+ ...totalsFromRow(row),
663
+ model: typeof row.model === "string" ? row.model : "unknown"
664
+ };
665
+ }
596
666
  function stringFromRow(row, key) {
597
667
  const value = row[key];
598
668
  return typeof value === "string" ? value : "";
@@ -620,12 +690,8 @@ function usageEventFromRow(row) {
620
690
  user_id: stringFromRow(row, "user_id")
621
691
  };
622
692
  }
623
- async function getTokenUsageSummary(period) {
624
- if (!isTokenUsageStorageEnabled()) return createEmptySummary(period);
625
- await flushTokenUsageEvents();
626
- const range = getPeriodRange(period);
627
- const db = await getDb();
628
- const totalsRow = db.prepare(`
693
+ function getTotalsRow(db, range) {
694
+ return db.prepare(`
629
695
  SELECT
630
696
  COUNT(*) AS request_count,
631
697
  COALESCE(SUM(input_tokens), 0) AS input_tokens,
@@ -636,8 +702,9 @@ async function getTokenUsageSummary(period) {
636
702
  FROM token_usage_events
637
703
  WHERE created_at_ms >= ? AND created_at_ms < ?
638
704
  `).get(range.startMs, range.endMs);
639
- return {
640
- byModel: db.prepare(`
705
+ }
706
+ function getModelRows(db, range) {
707
+ return db.prepare(`
641
708
  SELECT
642
709
  model,
643
710
  COUNT(*) AS request_count,
@@ -652,20 +719,47 @@ async function getTokenUsageSummary(period) {
652
719
  ORDER BY
653
720
  total_tokens DESC,
654
721
  model ASC
655
- `).all(range.startMs, range.endMs).map((row) => ({
656
- ...totalsFromRow(row),
657
- model: typeof row.model === "string" ? row.model : "unknown"
658
- })),
722
+ `).all(range.startMs, range.endMs);
723
+ }
724
+ function createDailyBucket(interval, rows) {
725
+ const byModel = rows.map((row) => modelSummaryFromRow(row));
726
+ const totals = createEmptyTotals();
727
+ for (const model of byModel) addTotals(totals, model);
728
+ return {
729
+ byModel,
730
+ date: interval.date,
731
+ end_ms: interval.endMs,
732
+ start_ms: interval.startMs,
733
+ totals
734
+ };
735
+ }
736
+ async function getTokenUsageSummary(period) {
737
+ if (!isTokenUsageStorageEnabled()) return createEmptySummary(period);
738
+ await flushTokenUsageEvents();
739
+ const range = getPeriodRange(period);
740
+ const db = await getDb();
741
+ const totalsRow = getTotalsRow(db, range);
742
+ return {
743
+ byModel: getModelRows(db, range).map((row) => modelSummaryFromRow(row)),
659
744
  period,
660
- range: {
661
- end_ms: range.endMs,
662
- end_utc: new Date(range.endMs).toISOString(),
663
- start_ms: range.startMs,
664
- start_utc: new Date(range.startMs).toISOString()
665
- },
745
+ range: rangePayload(range),
666
746
  totals: totalsFromRow(totalsRow)
667
747
  };
668
748
  }
749
+ async function getTokenUsageDailySummary(period) {
750
+ if (!isTokenUsageStorageEnabled()) return createEmptyDailySummary(period);
751
+ await flushTokenUsageEvents();
752
+ const range = getPeriodRange(period);
753
+ const db = await getDb();
754
+ const intervals = createDailyIntervals(range);
755
+ return {
756
+ byModel: getModelRows(db, range).map((row) => modelSummaryFromRow(row)),
757
+ days: intervals.map((interval) => createDailyBucket(interval, getModelRows(db, interval))),
758
+ period,
759
+ range: rangePayload(range),
760
+ totals: totalsFromRow(getTotalsRow(db, range))
761
+ };
762
+ }
669
763
  async function getTokenUsageEventsPage(input) {
670
764
  if (!isTokenUsageStorageEnabled()) return createEmptyEventsPage(input);
671
765
  await flushTokenUsageEvents();
@@ -1003,6 +1097,32 @@ completionRoutes.post("/", async (c) => {
1003
1097
  }
1004
1098
  });
1005
1099
  //#endregion
1100
+ //#region src/routes/admin/config/route.ts
1101
+ const configRoutes = new Hono();
1102
+ const modelMappingsRequestSchema = z.object({ modelMappings: z.record(z.string(), z.string()) });
1103
+ configRoutes.get("/model-mappings", (c) => {
1104
+ return c.json({
1105
+ configPath: PATHS.CONFIG_PATH,
1106
+ modelMappings: getModelMappings()
1107
+ });
1108
+ });
1109
+ configRoutes.post("/model-mappings", async (c) => {
1110
+ try {
1111
+ const parseResult = modelMappingsRequestSchema.safeParse(await c.req.json());
1112
+ if (!parseResult.success) return c.json({ error: {
1113
+ message: parseResult.error.issues[0]?.message ?? "Invalid request body.",
1114
+ type: "invalid_request_error"
1115
+ } }, 400);
1116
+ const updatedModelMappings = setModelMappings(parseResult.data.modelMappings);
1117
+ return c.json({
1118
+ configPath: PATHS.CONFIG_PATH,
1119
+ modelMappings: updatedModelMappings
1120
+ });
1121
+ } catch (error) {
1122
+ return await forwardError(c, error);
1123
+ }
1124
+ });
1125
+ //#endregion
1006
1126
  //#region src/services/copilot/create-embeddings.ts
1007
1127
  const createEmbeddings = async (payload) => {
1008
1128
  if (!state.copilotToken) throw new Error("Copilot token not found");
@@ -1737,6 +1857,7 @@ async function countTokensViaAnthropic(c, payload) {
1737
1857
  */
1738
1858
  async function handleCountTokens(c) {
1739
1859
  const anthropicPayload = await c.req.json();
1860
+ anthropicPayload.model = resolveMappedModel(anthropicPayload.model);
1740
1861
  const providerModelAlias = parseProviderModelAlias(anthropicPayload.model);
1741
1862
  if (providerModelAlias) {
1742
1863
  anthropicPayload.model = providerModelAlias.model;
@@ -2823,14 +2944,23 @@ const MESSAGE_TYPE = "message";
2823
2944
  const COMPACTION_SIGNATURE_PREFIX = "cm1#";
2824
2945
  const COMPACTION_SIGNATURE_SEPARATOR = "@";
2825
2946
  const THINKING_TEXT = "Thinking...";
2826
- const translateAnthropicMessagesToResponsesPayload = (payload) => {
2947
+ const buildPromptCacheKey = (basePromptCacheKey, subagentAgentId) => {
2948
+ if (!basePromptCacheKey) return null;
2949
+ const normalizedSubagentAgentId = subagentAgentId?.trim() || null;
2950
+ if (!normalizedSubagentAgentId) return basePromptCacheKey;
2951
+ return `${basePromptCacheKey}:agent:${normalizedSubagentAgentId}`;
2952
+ };
2953
+ const translateAnthropicMessagesToResponsesPayload = (payload, subagentAgentId) => {
2827
2954
  const input = [];
2828
2955
  const applyPhase = shouldApplyPhase(payload.model);
2829
2956
  for (const message of payload.messages) input.push(...translateMessage(message, payload.model, applyPhase));
2957
+ const hasOriginalTools = Array.isArray(payload.tools) && payload.tools.length > 0;
2830
2958
  const translatedTools = convertAnthropicTools(payload.tools);
2831
2959
  const toolChoice = convertAnthropicToolChoice(payload.tool_choice);
2832
- const { sessionId: promptCacheKey } = parseUserIdMetadata(payload.metadata?.user_id);
2833
- return {
2960
+ const { sessionId: metadataPromptCacheKey } = parseUserIdMetadata(payload.metadata?.user_id);
2961
+ const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim() || null;
2962
+ const promptCacheKey = buildPromptCacheKey(metadataPromptCacheKey ?? sessionAffinity, subagentAgentId);
2963
+ const responsesPayload = {
2834
2964
  model: payload.model,
2835
2965
  input,
2836
2966
  instructions: translateSystemPrompt(payload.system, payload.model),
@@ -2840,7 +2970,6 @@ const translateAnthropicMessagesToResponsesPayload = (payload) => {
2840
2970
  tools: translatedTools,
2841
2971
  tool_choice: toolChoice,
2842
2972
  metadata: payload.metadata ? { ...payload.metadata } : null,
2843
- prompt_cache_key: promptCacheKey,
2844
2973
  stream: payload.stream ?? null,
2845
2974
  store: false,
2846
2975
  parallel_tool_calls: true,
@@ -2850,6 +2979,8 @@ const translateAnthropicMessagesToResponsesPayload = (payload) => {
2850
2979
  },
2851
2980
  include: ["reasoning.encrypted_content"]
2852
2981
  };
2982
+ if (hasOriginalTools) responsesPayload.prompt_cache_key = promptCacheKey;
2983
+ return responsesPayload;
2853
2984
  };
2854
2985
  const encodeCompactionCarrierSignature = (compaction) => {
2855
2986
  return `${COMPACTION_SIGNATURE_PREFIX}${compaction.encrypted_content}${COMPACTION_SIGNATURE_SEPARATOR}${compaction.id}`;
@@ -2965,8 +3096,8 @@ const resolveAssistantPhase = (_model, content, applyPhase) => {
2965
3096
  if (!content.some((block) => block.type === "text")) return;
2966
3097
  return content.some((block) => block.type === "tool_use") ? "commentary" : "final_answer";
2967
3098
  };
2968
- const shouldApplyPhase = (model) => {
2969
- return getExtraPromptForModel(model).includes("## Intermediary updates");
3099
+ const shouldApplyPhase = (_model) => {
3100
+ return true;
2970
3101
  };
2971
3102
  const createTextContent = (text) => ({
2972
3103
  type: "input_text",
@@ -4034,7 +4165,7 @@ const handleWithChatCompletions = async (c, anthropicPayload, options) => {
4034
4165
  };
4035
4166
  const handleWithResponsesApi = async (c, anthropicPayload, options) => {
4036
4167
  const { logger, selectedModel, ...requestOptions } = options;
4037
- const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload);
4168
+ const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload, requestOptions.subagentMarker?.agent_id);
4038
4169
  const recordUsage = createCopilotUsageRecorder({
4039
4170
  endpoint: "responses",
4040
4171
  fallbackSessionId: requestOptions.sessionId,
@@ -4240,6 +4371,9 @@ const messagesFlowHandlers = {
4240
4371
  };
4241
4372
  async function handleCompletion(c) {
4242
4373
  const anthropicPayload = await c.req.json();
4374
+ const requestedModel = anthropicPayload.model;
4375
+ anthropicPayload.model = resolveMappedModel(anthropicPayload.model);
4376
+ if (anthropicPayload.model !== requestedModel) logger$3.debug(`Resolved model mapping: ${requestedModel} -> ${anthropicPayload.model}`);
4243
4377
  const providerModelAlias = parseProviderModelAlias(anthropicPayload.model);
4244
4378
  if (providerModelAlias) {
4245
4379
  anthropicPayload.model = providerModelAlias.model;
@@ -4580,6 +4714,10 @@ tokenUsageRoute.get("/", async (c) => {
4580
4714
  const summary = await getTokenUsageSummary(parsePeriod(c.req.query("period")));
4581
4715
  return c.json(summary);
4582
4716
  });
4717
+ tokenUsageRoute.get("/daily", async (c) => {
4718
+ const summary = await getTokenUsageDailySummary(parsePeriod(c.req.query("period")));
4719
+ return c.json(summary);
4720
+ });
4583
4721
  tokenUsageRoute.get("/events", async (c) => {
4584
4722
  const period = parsePeriod(c.req.query("period"));
4585
4723
  const eventsPage = await getTokenUsageEventsPage({
@@ -4621,11 +4759,19 @@ const server = new Hono();
4621
4759
  server.use(traceIdMiddleware);
4622
4760
  server.use(logger());
4623
4761
  server.use(cors());
4624
- server.use("*", createAuthMiddleware({ allowUnauthenticatedPaths: [
4625
- "/",
4626
- "/usage-viewer",
4627
- "/usage-viewer/"
4628
- ] }));
4762
+ server.use("*", createAuthMiddleware({
4763
+ allowUnauthenticatedPaths: [
4764
+ "/",
4765
+ "/usage-viewer",
4766
+ "/usage-viewer/"
4767
+ ],
4768
+ shouldSkipPath: (path) => path.startsWith("/admin/")
4769
+ }));
4770
+ server.use("/admin/*", createAuthMiddleware({
4771
+ getApiKeys: getConfiguredAdminApiKeys,
4772
+ allowUnauthenticatedPaths: [],
4773
+ allowWhenNoApiKeys: false
4774
+ }));
4629
4775
  server.get("/", (c) => c.text("Server running"));
4630
4776
  server.get("/usage-viewer", (c) => {
4631
4777
  const usageViewerFileUrl = new URL("../pages/index.html", import.meta.url);
@@ -4633,6 +4779,7 @@ server.get("/usage-viewer", (c) => {
4633
4779
  });
4634
4780
  server.get("/usage-viewer/", (c) => c.redirect("/usage-viewer", 301));
4635
4781
  server.route("/chat/completions", completionRoutes);
4782
+ server.route("/admin/config", configRoutes);
4636
4783
  server.route("/models", modelRoutes);
4637
4784
  server.route("/embeddings", embeddingRoutes);
4638
4785
  server.route("/usage", usageRoute);
@@ -4649,4 +4796,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
4649
4796
  //#endregion
4650
4797
  export { server };
4651
4798
 
4652
- //# sourceMappingURL=server-BnKth1Jp.js.map
4799
+ //# sourceMappingURL=server-csGHkK-m.js.map