@jeffreycao/copilot-api 1.12.9 → 1.13.1

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,5 +1,5 @@
1
- import { C as setModelMappings, S as resolveMappedModel, T as PATHS, _ as isResponsesApiWebSocketEnabled, a as getExtraPromptForModel, c as getModelResponsesApiCompactThreshold$1, d as getReasoningEffortForModel, f as getSmallModel, g as isResponsesApiWebSearchEnabled, h as isResponsesApiContextManagementEnabled, i as getConfig, l as getProviderConfig, m as isMessagesApiEnabled, n as getAnthropicApiKey, o as getMessageApiWebSearchModel, r as getClaudeTokenMultiplier, s as getModelMappings, u as getRawProviderConfig } from "./config-B71_S_hQ.js";
2
- import { C as prepareMessageProxyHeaders, E as compactMessageSections, F as createPooledWebSocketStream, I as createWebSocketUrl, L as state, M as generateTraceId, N as requestContext, O as compactSystemPromptStarts, P as resolveTraceId$1, R as HTTPError, S as prepareInteractionHeaders, T as compactAutoContinuePromptStarts, _ as getCopilotUsage, b as copilotWebSocketHeaders, d as generateRequestIdFromPayload, f as getRootSessionId, g as sleep, h as parseUserIdMetadata, j as forwardCodexResponses, m as isNullish, p as getUUID, r as setupCodexToken, s as cacheModels, v as copilotBaseUrl, x as prepareForCompact, y as copilotHeaders, z as forwardError } from "./token-fpw8tAQS.js";
1
+ import { S as setModelMappings, a as getExtraPromptForModel, c as getModelResponsesApiCompactThreshold$1, d as getReasoningEffortForModel, f as getSmallModel, g as isResponsesApiWebSocketEnabled, h as isResponsesApiWebSearchEnabled, i as getConfig, l as getProviderConfig, m as isResponsesApiContextManagementEnabled, n as getAnthropicApiKey, o as getMessageApiWebSearchModel, p as isMessagesApiEnabled, r as getClaudeTokenMultiplier, s as getModelMappings, u as getRawProviderConfig, v as listEnabledProviders, w as PATHS, x as resolveMappedModel } from "./config-DODTXsGz.js";
2
+ import { B as forwardError, C as prepareInteractionHeaders, D as compactMessageSections, E as compactAutoContinuePromptStarts, F as resolveTraceId$1, I as createPooledWebSocketStream, L as createWebSocketUrl, M as forwardCodexResponses, N as generateTraceId, P as requestContext, R as state, S as prepareForCompact, _ as getCopilotUsage, b as copilotHeaders, d as generateRequestIdFromPayload, f as getRootSessionId, g as sleep, h as parseUserIdMetadata, k as compactSystemPromptStarts, m as isNullish, p as getUUID, r as setupCodexToken, w as prepareMessageProxyHeaders, x as copilotWebSocketHeaders, y as copilotBaseUrl, z as HTTPError } from "./token-DigcCxDR.js";
3
3
  import { a as isDeferredToolName, c as parseMcpToolSearchSentinel, d as shouldEnableResponsesToolSearch, i as isBridgeToolSearchName, l as resolveBridgeToolSearchName, o as listDeferredToolNames, r as formatToolSearchBridgeArguments, s as normalizeToolSearchBridgeArguments, t as BRIDGE_TOOL_SEARCH_NAME, u as selectDeferredToolsByNames } from "./tool-search-OX6iPJ9D.js";
4
4
  import consola from "consola";
5
5
  import { createHash } from "node:crypto";
@@ -485,6 +485,7 @@ var SqliteDbStore = class {
485
485
  //#region src/lib/token-usage/store.ts
486
486
  const DB_PATH_ENV = "COPILOT_API_SQLITE_DB_PATH";
487
487
  const DEFAULT_DB_FILENAME = "copilot-api.sqlite";
488
+ const COST_NANOS_PER_UNIT$1 = 1e9;
488
489
  let writeQueue = Promise.resolve();
489
490
  function getDbPath() {
490
491
  return process.env[DB_PATH_ENV] ?? path.join(PATHS.APP_DIR, DEFAULT_DB_FILENAME);
@@ -518,11 +519,19 @@ function initializeTokenUsageDb(db) {
518
519
  output_tokens INTEGER NOT NULL DEFAULT 0,
519
520
  cache_read_input_tokens INTEGER NOT NULL DEFAULT 0,
520
521
  cache_creation_input_tokens INTEGER NOT NULL DEFAULT 0,
521
- total_tokens INTEGER NOT NULL DEFAULT 0
522
+ total_tokens INTEGER NOT NULL DEFAULT 0,
523
+ total_nano_aiu INTEGER,
524
+ cost_currency TEXT,
525
+ total_cost_nanos INTEGER,
526
+ cost_source TEXT
522
527
  )
523
528
  `);
524
529
  ensureColumn(db, "user_id", "TEXT NOT NULL DEFAULT ''");
525
530
  ensureColumn(db, "total_tokens", "INTEGER NOT NULL DEFAULT 0");
531
+ ensureColumn(db, "total_nano_aiu", "INTEGER");
532
+ ensureColumn(db, "cost_currency", "TEXT");
533
+ ensureColumn(db, "total_cost_nanos", "INTEGER");
534
+ ensureColumn(db, "cost_source", "TEXT");
526
535
  db.exec(`
527
536
  CREATE INDEX IF NOT EXISTS idx_token_usage_events_created_at_ms
528
537
  ON token_usage_events(created_at_ms)
@@ -555,7 +564,7 @@ function normalizeOptionalToken(value) {
555
564
  return value === null || value === void 0 ? void 0 : normalizeToken(value);
556
565
  }
557
566
  function hasAnyToken(tokens) {
558
- return normalizeToken(tokens.input_tokens) > 0 || normalizeToken(tokens.output_tokens) > 0 || normalizeToken(tokens.cache_read_input_tokens) > 0 || normalizeToken(tokens.cache_creation_input_tokens) > 0 || normalizeToken(tokens.total_tokens) > 0;
567
+ return normalizeToken(tokens.input_tokens) > 0 || normalizeToken(tokens.output_tokens) > 0 || normalizeToken(tokens.cache_read_input_tokens) > 0 || normalizeToken(tokens.cache_creation_input_tokens) > 0 || normalizeToken(tokens.total_tokens) > 0 || normalizeToken(tokens.total_nano_aiu) > 0;
559
568
  }
560
569
  function resolveTotalTokens(input) {
561
570
  const explicitTotal = normalizeOptionalToken(input.total_tokens);
@@ -578,9 +587,13 @@ async function writeTokenUsageEvent(event) {
578
587
  output_tokens,
579
588
  cache_read_input_tokens,
580
589
  cache_creation_input_tokens,
581
- total_tokens
582
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
583
- `).run(event.created_at_ms, event.created_at_utc, event.trace_id, event.session_id, event.user_id, event.source, event.endpoint, event.provider_name, event.model, event.input_tokens, event.output_tokens, event.cache_read_input_tokens, event.cache_creation_input_tokens, event.total_tokens);
590
+ total_tokens,
591
+ total_nano_aiu,
592
+ cost_currency,
593
+ total_cost_nanos,
594
+ cost_source
595
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
596
+ `).run(event.created_at_ms, event.created_at_utc, event.trace_id, event.session_id, event.user_id, event.source, event.endpoint, event.provider_name, event.model, event.input_tokens, event.output_tokens, event.cache_read_input_tokens, event.cache_creation_input_tokens, event.total_tokens, event.total_nano_aiu, event.cost_currency, event.total_cost_nanos, event.cost_source);
584
597
  }
585
598
  function enqueueTokenUsageWrite(event) {
586
599
  if (!isTokenUsageStorageEnabled()) return;
@@ -651,20 +664,34 @@ function createEmptyTotals() {
651
664
  return {
652
665
  cache_creation_input_tokens: 0,
653
666
  cache_read_input_tokens: 0,
667
+ costs: [],
654
668
  input_tokens: 0,
655
669
  output_tokens: 0,
656
670
  request_count: 0,
671
+ total_nano_aiu: null,
657
672
  total_tokens: 0
658
673
  };
659
674
  }
660
675
  function addTotals(target, next) {
661
676
  target.cache_creation_input_tokens += next.cache_creation_input_tokens;
662
677
  target.cache_read_input_tokens += next.cache_read_input_tokens;
678
+ target.costs = mergeCosts(target.costs, next.costs);
663
679
  target.input_tokens += next.input_tokens;
664
680
  target.output_tokens += next.output_tokens;
665
681
  target.request_count += next.request_count;
682
+ target.total_nano_aiu = addNullableNumbers(target.total_nano_aiu, next.total_nano_aiu);
666
683
  target.total_tokens += next.total_tokens;
667
684
  }
685
+ function addNullableNumbers(current, next) {
686
+ if (current === null) return next;
687
+ if (next === null) return current;
688
+ return current + next;
689
+ }
690
+ function mergeCosts(current, next) {
691
+ const byCurrency = /* @__PURE__ */ new Map();
692
+ for (const cost of [...current, ...next]) byCurrency.set(cost.currency, (byCurrency.get(cost.currency) ?? 0) + cost.total_cost_nanos);
693
+ return [...byCurrency.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([currency, totalCostNanos]) => createCost(currency, totalCostNanos));
694
+ }
668
695
  function createEmptySummary(period) {
669
696
  const range = getPeriodRange(period);
670
697
  return {
@@ -729,19 +756,47 @@ function numberFromRow(row, key) {
729
756
  const value = row?.[key];
730
757
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
731
758
  }
732
- function totalsFromRow(row) {
759
+ function nullableNumberFromRow(row, key) {
760
+ const value = row?.[key];
761
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
762
+ }
763
+ function createCost(currency, totalCostNanos) {
764
+ return {
765
+ amount: totalCostNanos / COST_NANOS_PER_UNIT$1,
766
+ currency,
767
+ total_cost_nanos: totalCostNanos
768
+ };
769
+ }
770
+ function costFromRow(row) {
771
+ const currency = row.cost_currency;
772
+ const totalCostNanos = row.total_cost_nanos;
773
+ if (typeof currency !== "string" || !currency || typeof totalCostNanos !== "number" || !Number.isFinite(totalCostNanos)) return null;
774
+ return createCost(currency, totalCostNanos);
775
+ }
776
+ function eventCostFromRow(row) {
777
+ const cost = costFromRow(row);
778
+ const source = row.cost_source;
779
+ if (!cost || typeof source !== "string" || !source) return null;
780
+ return {
781
+ ...cost,
782
+ source
783
+ };
784
+ }
785
+ function totalsFromRow(row, costs = []) {
733
786
  return {
734
787
  cache_creation_input_tokens: numberFromRow(row, "cache_creation_input_tokens"),
735
788
  cache_read_input_tokens: numberFromRow(row, "cache_read_input_tokens"),
789
+ costs,
736
790
  input_tokens: numberFromRow(row, "input_tokens"),
737
791
  output_tokens: numberFromRow(row, "output_tokens"),
738
792
  request_count: numberFromRow(row, "request_count"),
793
+ total_nano_aiu: nullableNumberFromRow(row, "total_nano_aiu"),
739
794
  total_tokens: numberFromRow(row, "total_tokens")
740
795
  };
741
796
  }
742
- function modelSummaryFromRow(row) {
797
+ function modelSummaryFromRow(row, costs = []) {
743
798
  return {
744
- ...totalsFromRow(row),
799
+ ...totalsFromRow(row, costs),
745
800
  model: typeof row.model === "string" ? row.model : "unknown"
746
801
  };
747
802
  }
@@ -757,6 +812,7 @@ function usageEventFromRow(row) {
757
812
  return {
758
813
  cache_creation_input_tokens: numberFromRow(row, "cache_creation_input_tokens"),
759
814
  cache_read_input_tokens: numberFromRow(row, "cache_read_input_tokens"),
815
+ cost: eventCostFromRow(row),
760
816
  created_at_ms: numberFromRow(row, "created_at_ms"),
761
817
  created_at_utc: stringFromRow(row, "created_at_utc"),
762
818
  endpoint: stringFromRow(row, "endpoint"),
@@ -767,6 +823,7 @@ function usageEventFromRow(row) {
767
823
  provider_name: nullableStringFromRow(row, "provider_name"),
768
824
  session_id: stringFromRow(row, "session_id"),
769
825
  source: stringFromRow(row, "source"),
826
+ total_nano_aiu: nullableNumberFromRow(row, "total_nano_aiu"),
770
827
  total_tokens: numberFromRow(row, "total_tokens"),
771
828
  trace_id: stringFromRow(row, "trace_id"),
772
829
  user_id: stringFromRow(row, "user_id")
@@ -780,6 +837,7 @@ function getTotalsRow(db, range) {
780
837
  COALESCE(SUM(output_tokens), 0) AS output_tokens,
781
838
  COALESCE(SUM(cache_read_input_tokens), 0) AS cache_read_input_tokens,
782
839
  COALESCE(SUM(cache_creation_input_tokens), 0) AS cache_creation_input_tokens,
840
+ SUM(total_nano_aiu) AS total_nano_aiu,
783
841
  COALESCE(SUM(total_tokens), 0) AS total_tokens
784
842
  FROM token_usage_events
785
843
  WHERE created_at_ms >= ? AND created_at_ms < ?
@@ -794,6 +852,7 @@ function getModelRows(db, range) {
794
852
  COALESCE(SUM(output_tokens), 0) AS output_tokens,
795
853
  COALESCE(SUM(cache_read_input_tokens), 0) AS cache_read_input_tokens,
796
854
  COALESCE(SUM(cache_creation_input_tokens), 0) AS cache_creation_input_tokens,
855
+ SUM(total_nano_aiu) AS total_nano_aiu,
797
856
  COALESCE(SUM(total_tokens), 0) AS total_tokens
798
857
  FROM token_usage_events
799
858
  WHERE created_at_ms >= ? AND created_at_ms < ?
@@ -803,8 +862,56 @@ function getModelRows(db, range) {
803
862
  model ASC
804
863
  `).all(range.startMs, range.endMs);
805
864
  }
806
- function createDailyBucket(interval, rows) {
807
- const byModel = rows.map((row) => modelSummaryFromRow(row));
865
+ function getCostRows(db, range) {
866
+ return db.prepare(`
867
+ SELECT
868
+ cost_currency,
869
+ COALESCE(SUM(total_cost_nanos), 0) AS total_cost_nanos
870
+ FROM token_usage_events
871
+ WHERE
872
+ created_at_ms >= ?
873
+ AND created_at_ms < ?
874
+ AND cost_currency IS NOT NULL
875
+ AND total_cost_nanos IS NOT NULL
876
+ GROUP BY cost_currency
877
+ ORDER BY cost_currency ASC
878
+ `).all(range.startMs, range.endMs).flatMap((row) => {
879
+ const cost = costFromRow(row);
880
+ return cost ? [cost] : [];
881
+ });
882
+ }
883
+ function getModelCostMap(db, range) {
884
+ const rows = db.prepare(`
885
+ SELECT
886
+ model,
887
+ cost_currency,
888
+ COALESCE(SUM(total_cost_nanos), 0) AS total_cost_nanos
889
+ FROM token_usage_events
890
+ WHERE
891
+ created_at_ms >= ?
892
+ AND created_at_ms < ?
893
+ AND cost_currency IS NOT NULL
894
+ AND total_cost_nanos IS NOT NULL
895
+ GROUP BY model, cost_currency
896
+ ORDER BY model ASC, cost_currency ASC
897
+ `).all(range.startMs, range.endMs);
898
+ const costMap = /* @__PURE__ */ new Map();
899
+ for (const row of rows) {
900
+ const model = stringFromRow(row, "model") || "unknown";
901
+ const cost = costFromRow(row);
902
+ if (!cost) continue;
903
+ costMap.set(model, [...costMap.get(model) ?? [], cost]);
904
+ }
905
+ return costMap;
906
+ }
907
+ function getModelSummaries(db, range) {
908
+ const costMap = getModelCostMap(db, range);
909
+ return getModelRows(db, range).map((row) => {
910
+ const model = stringFromRow(row, "model") || "unknown";
911
+ return modelSummaryFromRow(row, costMap.get(model) ?? []);
912
+ });
913
+ }
914
+ function createDailyBucket(interval, byModel) {
808
915
  const totals = createEmptyTotals();
809
916
  for (const model of byModel) addTotals(totals, model);
810
917
  return {
@@ -822,10 +929,10 @@ async function getTokenUsageSummary(period) {
822
929
  const db = await getDb();
823
930
  const totalsRow = getTotalsRow(db, range);
824
931
  return {
825
- byModel: getModelRows(db, range).map((row) => modelSummaryFromRow(row)),
932
+ byModel: getModelSummaries(db, range),
826
933
  period,
827
934
  range: rangePayload(range),
828
- totals: totalsFromRow(totalsRow)
935
+ totals: totalsFromRow(totalsRow, getCostRows(db, range))
829
936
  };
830
937
  }
831
938
  async function getTokenUsageDailySummary(period) {
@@ -835,11 +942,11 @@ async function getTokenUsageDailySummary(period) {
835
942
  const db = await getDb();
836
943
  const intervals = createDailyIntervals(range);
837
944
  return {
838
- byModel: getModelRows(db, range).map((row) => modelSummaryFromRow(row)),
839
- days: intervals.map((interval) => createDailyBucket(interval, getModelRows(db, interval))),
945
+ byModel: getModelSummaries(db, range),
946
+ days: intervals.map((interval) => createDailyBucket(interval, getModelSummaries(db, interval))),
840
947
  period,
841
948
  range: rangePayload(range),
842
- totals: totalsFromRow(getTotalsRow(db, range))
949
+ totals: totalsFromRow(getTotalsRow(db, range), getCostRows(db, range))
843
950
  };
844
951
  }
845
952
  async function getTokenUsageEventsPage(input) {
@@ -871,7 +978,11 @@ async function getTokenUsageEventsPage(input) {
871
978
  output_tokens,
872
979
  cache_read_input_tokens,
873
980
  cache_creation_input_tokens,
874
- total_tokens
981
+ total_nano_aiu,
982
+ total_tokens,
983
+ cost_currency,
984
+ total_cost_nanos,
985
+ cost_source
875
986
  FROM token_usage_events
876
987
  WHERE created_at_ms >= ? AND created_at_ms < ?
877
988
  ORDER BY created_at_ms DESC, id DESC
@@ -904,6 +1015,174 @@ async function closeUsageStore() {
904
1015
  }
905
1016
  registerProcessCleanup(closeUsageStore);
906
1017
  //#endregion
1018
+ //#region src/lib/token-usage/pricing.ts
1019
+ const COST_NANOS_PER_UNIT = 1e9;
1020
+ const COST_NANOS_PER_TOKEN_AT_ONE_PER_MILLION = 1e3;
1021
+ const COPILOT_NANO_AIU_TO_COST_NANOS = COST_NANOS_PER_UNIT / 1e11;
1022
+ const BUILTIN_PROVIDER_CURRENCIES = {
1023
+ codex: "USD",
1024
+ dashscope: "CNY",
1025
+ deepseek: "CNY"
1026
+ };
1027
+ const BUILTIN_PROVIDER_PRICING = {
1028
+ codex: {
1029
+ "gpt-5.3-codex": {
1030
+ cachedInput: .175,
1031
+ input: 1.75,
1032
+ output: 14
1033
+ },
1034
+ "gpt-5.4": {
1035
+ cachedInput: .25,
1036
+ input: 2.5,
1037
+ output: 15
1038
+ },
1039
+ "gpt-5.4-mini": {
1040
+ cachedInput: .075,
1041
+ input: .75,
1042
+ output: 4.5
1043
+ },
1044
+ "gpt-5.5": {
1045
+ cachedInput: .5,
1046
+ input: 5,
1047
+ output: 30
1048
+ }
1049
+ },
1050
+ dashscope: {
1051
+ "glm-5.1": { tiers: [{
1052
+ cachedInput: 1.2,
1053
+ cacheCreationInput: 7.5,
1054
+ explicitCachedInput: .6,
1055
+ input: 6,
1056
+ maxInputTokens: 32e3,
1057
+ output: 24
1058
+ }, {
1059
+ cachedInput: 1.6,
1060
+ cacheCreationInput: 10,
1061
+ explicitCachedInput: .8,
1062
+ input: 8,
1063
+ maxInputTokens: 2e5,
1064
+ output: 28
1065
+ }] },
1066
+ "glm-5.2": {
1067
+ cachedInput: 2,
1068
+ cacheCreationInput: 10,
1069
+ explicitCachedInput: .8,
1070
+ input: 8,
1071
+ output: 28
1072
+ },
1073
+ "qwen3.7-max": {
1074
+ cachedInput: 2.4,
1075
+ cacheCreationInput: 15,
1076
+ explicitCachedInput: 1.2,
1077
+ input: 12,
1078
+ output: 36
1079
+ },
1080
+ "qwen3.7-plus": { tiers: [{
1081
+ cachedInput: .4,
1082
+ cacheCreationInput: 2.5,
1083
+ explicitCachedInput: .2,
1084
+ input: 2,
1085
+ maxInputTokens: 256e3,
1086
+ output: 8
1087
+ }, {
1088
+ cachedInput: 1.2,
1089
+ cacheCreationInput: 7.5,
1090
+ explicitCachedInput: .6,
1091
+ input: 6,
1092
+ maxInputTokens: 1e6,
1093
+ output: 24
1094
+ }] }
1095
+ },
1096
+ deepseek: {
1097
+ "deepseek-v4-flash": {
1098
+ cachedInput: .02,
1099
+ input: 1,
1100
+ output: 2
1101
+ },
1102
+ "deepseek-v4-pro": {
1103
+ cachedInput: .025,
1104
+ input: 3,
1105
+ output: 6
1106
+ }
1107
+ }
1108
+ };
1109
+ function resolveTokenUsageCost(input) {
1110
+ if (input.source === "copilot") return resolveCopilotCost(input);
1111
+ const providerName = input.providerName?.trim();
1112
+ if (!providerName) return null;
1113
+ const resolvedPricing = resolveProviderPricing(providerName, input.model, input.pricing);
1114
+ if (!resolvedPricing) return null;
1115
+ const pricing = resolvePricingTier(resolvedPricing.pricing, getInputTokenTotal(input));
1116
+ const currency = resolveProviderCurrency(providerName, input.pricingCurrency);
1117
+ if (!currency) return null;
1118
+ const inputPrice = normalizePrice(pricing.input);
1119
+ const outputPrice = normalizePrice(pricing.output);
1120
+ const cacheReadPrice = resolveCacheReadPrice(pricing, input);
1121
+ const cacheCreationPrice = resolveCacheCreationPrice(pricing);
1122
+ const totalCostNanos = costNanosForTokens(input.input_tokens, inputPrice) + costNanosForTokens(input.output_tokens, outputPrice) + costNanosForTokens(input.cache_read_input_tokens, cacheReadPrice) + costNanosForTokens(input.cache_creation_input_tokens, cacheCreationPrice);
1123
+ if (totalCostNanos <= 0) return null;
1124
+ return {
1125
+ currency,
1126
+ source: resolvedPricing.source,
1127
+ total_cost_nanos: totalCostNanos
1128
+ };
1129
+ }
1130
+ function resolveCopilotCost(input) {
1131
+ const totalNanoAiu = normalizeToken(input.total_nano_aiu);
1132
+ if (totalNanoAiu <= 0) return null;
1133
+ const totalCostNanos = Math.round(totalNanoAiu * COPILOT_NANO_AIU_TO_COST_NANOS);
1134
+ if (totalCostNanos <= 0) return null;
1135
+ return {
1136
+ currency: "USD",
1137
+ source: "copilot_aiu",
1138
+ total_cost_nanos: totalCostNanos
1139
+ };
1140
+ }
1141
+ function resolveProviderPricing(providerName, model, configuredPricing) {
1142
+ if (configuredPricing) return {
1143
+ pricing: configuredPricing,
1144
+ source: "config"
1145
+ };
1146
+ const builtinPricing = BUILTIN_PROVIDER_PRICING[providerName.toLowerCase()]?.[model.toLowerCase()];
1147
+ if (!builtinPricing) return null;
1148
+ return {
1149
+ pricing: builtinPricing,
1150
+ source: "builtin"
1151
+ };
1152
+ }
1153
+ function resolvePricingTier(pricing, inputTokenTotal) {
1154
+ const tiers = pricing.tiers?.filter((tier) => typeof tier === "object" && tier !== null).toSorted((a, b) => normalizeTierMax(a) - normalizeTierMax(b));
1155
+ const selectedTier = tiers?.find((tier) => inputTokenTotal <= normalizeTierMax(tier)) ?? tiers?.at(-1);
1156
+ return {
1157
+ ...pricing,
1158
+ ...selectedTier
1159
+ };
1160
+ }
1161
+ function normalizeTierMax(tier) {
1162
+ const maxInputTokens = tier.maxInputTokens;
1163
+ return typeof maxInputTokens === "number" && Number.isFinite(maxInputTokens) && maxInputTokens > 0 ? maxInputTokens : Number.POSITIVE_INFINITY;
1164
+ }
1165
+ function getInputTokenTotal(input) {
1166
+ return normalizeToken(input.input_tokens) + normalizeToken(input.cache_read_input_tokens) + normalizeToken(input.cache_creation_input_tokens);
1167
+ }
1168
+ function normalizePrice(value) {
1169
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
1170
+ }
1171
+ function resolveProviderCurrency(providerName, configuredCurrency) {
1172
+ return configuredCurrency?.trim().toUpperCase() || BUILTIN_PROVIDER_CURRENCIES[providerName.toLowerCase()] || null;
1173
+ }
1174
+ function resolveCacheCreationPrice(pricing) {
1175
+ return normalizePrice(pricing.cacheCreationInput);
1176
+ }
1177
+ function resolveCacheReadPrice(pricing, input) {
1178
+ if (input.cache_creation_input_tokens !== void 0 && input.cache_creation_input_tokens !== null) return normalizePrice(pricing.explicitCachedInput);
1179
+ return normalizePrice(pricing.cachedInput);
1180
+ }
1181
+ function costNanosForTokens(tokens, pricePerMillionTokens) {
1182
+ if (pricePerMillionTokens === null) return 0;
1183
+ return Math.round(normalizeToken(tokens) * pricePerMillionTokens * COST_NANOS_PER_TOKEN_AT_ONE_PER_MILLION);
1184
+ }
1185
+ //#endregion
907
1186
  //#region src/lib/token-usage/index.ts
908
1187
  const tokenUsageEventBus = new EventBus();
909
1188
  function resolveTraceId(traceId) {
@@ -919,9 +1198,12 @@ function resolveUserId(input) {
919
1198
  function toPersistedEvent(input) {
920
1199
  if (!hasAnyToken(input)) return null;
921
1200
  const now = /* @__PURE__ */ new Date();
1201
+ const cost = resolveTokenUsageCost(input);
922
1202
  return {
923
1203
  cache_creation_input_tokens: normalizeToken(input.cache_creation_input_tokens),
924
1204
  cache_read_input_tokens: normalizeToken(input.cache_read_input_tokens),
1205
+ cost_currency: cost?.currency ?? null,
1206
+ cost_source: cost?.source ?? null,
925
1207
  created_at_ms: now.getTime(),
926
1208
  created_at_utc: now.toISOString(),
927
1209
  endpoint: input.endpoint,
@@ -931,6 +1213,8 @@ function toPersistedEvent(input) {
931
1213
  provider_name: input.providerName?.trim() || null,
932
1214
  session_id: resolveTokenUsageSessionId(input.sessionId, input.fallbackSessionId),
933
1215
  source: input.source,
1216
+ total_nano_aiu: input.total_nano_aiu === void 0 || input.total_nano_aiu === null ? null : normalizeToken(input.total_nano_aiu),
1217
+ total_cost_nanos: cost?.total_cost_nanos ?? null,
934
1218
  total_tokens: resolveTotalTokens(input),
935
1219
  trace_id: resolveTraceId(input.traceId),
936
1220
  user_id: resolveUserId(input)
@@ -963,12 +1247,21 @@ function createProviderTokenUsageRecorder(options) {
963
1247
  });
964
1248
  }
965
1249
  function normalizeOpenAIUsage(usage) {
966
- const cachedTokens = normalizeToken(usage?.prompt_tokens_details?.cached_tokens);
967
- const cacheCreationTokens = normalizeToken(usage?.prompt_tokens_details?.cache_creation_input_tokens);
1250
+ if (usage && (Object.hasOwn(usage, "prompt_cache_hit_tokens") || Object.hasOwn(usage, "prompt_cache_miss_tokens"))) return {
1251
+ cache_read_input_tokens: normalizeToken(usage.prompt_cache_hit_tokens),
1252
+ input_tokens: normalizeToken(usage.prompt_cache_miss_tokens),
1253
+ output_tokens: normalizeToken(usage.completion_tokens),
1254
+ total_tokens: normalizeOptionalToken(usage.total_tokens)
1255
+ };
1256
+ const promptDetails = usage?.prompt_tokens_details;
1257
+ const hasCacheCreationTokens = Boolean(promptDetails && Object.hasOwn(promptDetails, "cache_creation_input_tokens"));
1258
+ const hasCachedTokens = Boolean(promptDetails && Object.hasOwn(promptDetails, "cached_tokens"));
1259
+ const cachedTokens = normalizeToken(promptDetails?.cached_tokens);
1260
+ const cacheCreationTokens = normalizeToken(promptDetails?.cache_creation_input_tokens);
968
1261
  const promptTokens = normalizeToken(usage?.prompt_tokens);
969
1262
  return {
970
- cache_creation_input_tokens: cacheCreationTokens,
971
- cache_read_input_tokens: cachedTokens,
1263
+ ...hasCacheCreationTokens && { cache_creation_input_tokens: cacheCreationTokens },
1264
+ ...hasCachedTokens && { cache_read_input_tokens: cachedTokens },
972
1265
  input_tokens: Math.max(0, promptTokens - cachedTokens - cacheCreationTokens),
973
1266
  output_tokens: normalizeToken(usage?.completion_tokens),
974
1267
  total_tokens: normalizeOptionalToken(usage?.total_tokens)
@@ -1104,7 +1397,7 @@ async function forwardProviderModels(providerConfig, requestHeaders) {
1104
1397
  }
1105
1398
  //#endregion
1106
1399
  //#region src/routes/provider/chat-completions/handler.ts
1107
- const logger$8 = createHandlerLogger("provider-chat-completions-handler");
1400
+ const logger$9 = createHandlerLogger("provider-chat-completions-handler");
1108
1401
  async function handleProviderChatCompletionsForProvider(c, options) {
1109
1402
  const { payload, provider } = options;
1110
1403
  const providerConfig = await resolveProviderConfig(provider);
@@ -1116,19 +1409,19 @@ async function handleProviderChatCompletionsForProvider(c, options) {
1116
1409
  applyProviderModelDefaults(payload, modelConfig);
1117
1410
  applyMissingExtraBody$1(payload, { extraBody: modelConfig?.extraBody });
1118
1411
  applyProviderStreamOptions(payload);
1119
- debugJson(logger$8, "provider.chat_completions.request", {
1412
+ debugJson(logger$9, "provider.chat_completions.request", {
1120
1413
  payload,
1121
1414
  provider
1122
1415
  });
1123
1416
  const upstreamResponse = await forwardProviderChatCompletions(providerConfig, payload, c.req.raw.headers);
1124
1417
  if (!upstreamResponse.ok) {
1125
- logger$8.error("Failed to create provider chat completions", {
1418
+ logger$9.error("Failed to create provider chat completions", {
1126
1419
  provider,
1127
1420
  statusCode: upstreamResponse.status
1128
1421
  });
1129
1422
  throw new HTTPError(`Failed to create ${provider} chat completions`, upstreamResponse);
1130
1423
  }
1131
- const recordUsage = createProviderChatCompletionsUsageRecorder(payload, provider);
1424
+ const recordUsage = createProviderChatCompletionsUsageRecorder(payload, provider, modelConfig, providerConfig.pricingCurrency);
1132
1425
  const contentType = upstreamResponse.headers.get("content-type") ?? "";
1133
1426
  if (Boolean(payload.stream) && contentType.includes("text/event-stream")) return streamProviderChatCompletions(c, upstreamResponse, {
1134
1427
  provider,
@@ -1136,7 +1429,7 @@ async function handleProviderChatCompletionsForProvider(c, options) {
1136
1429
  });
1137
1430
  const responseBody = await upstreamResponse.clone().json();
1138
1431
  recordUsage(normalizeOpenAIUsage(responseBody.usage));
1139
- debugJson(logger$8, "provider.chat_completions.response", responseBody);
1432
+ debugJson(logger$9, "provider.chat_completions.response", responseBody);
1140
1433
  return createProviderProxyResponse(upstreamResponse);
1141
1434
  }
1142
1435
  const applyProviderModelDefaults = (payload, modelConfig) => {
@@ -1154,18 +1447,20 @@ const applyProviderStreamOptions = (payload) => {
1154
1447
  include_usage: true
1155
1448
  };
1156
1449
  };
1157
- const createProviderChatCompletionsUsageRecorder = (payload, provider) => createProviderTokenUsageRecorder({
1450
+ const createProviderChatCompletionsUsageRecorder = (payload, provider, modelConfig, pricingCurrency) => createProviderTokenUsageRecorder({
1158
1451
  endpoint: "chat_completions",
1159
1452
  model: payload.model,
1453
+ pricing: modelConfig?.pricing,
1454
+ pricingCurrency,
1160
1455
  providerName: provider
1161
1456
  });
1162
1457
  const streamProviderChatCompletions = (c, upstreamResponse, options) => {
1163
- logger$8.debug("provider.chat_completions.streaming", { provider: options.provider });
1458
+ logger$9.debug("provider.chat_completions.streaming", { provider: options.provider });
1164
1459
  return streamSSE(c, async (stream) => {
1165
1460
  let usage = {};
1166
1461
  try {
1167
1462
  for await (const chunk of events(upstreamResponse)) {
1168
- debugJson(logger$8, "provider.chat_completions.stream_chunk", chunk);
1463
+ debugJson(logger$9, "provider.chat_completions.stream_chunk", chunk);
1169
1464
  if (chunk.data && chunk.data !== "[DONE]") {
1170
1465
  const parsedChunk = parseChatCompletionChunkData(chunk.data);
1171
1466
  if (parsedChunk?.usage) usage = normalizeOpenAIUsage(parsedChunk.usage);
@@ -1292,7 +1587,7 @@ const createChatCompletions = async (payload, options) => {
1292
1587
  };
1293
1588
  //#endregion
1294
1589
  //#region src/routes/chat-completions/handler.ts
1295
- const logger$7 = createHandlerLogger("chat-completions-handler");
1590
+ const logger$8 = createHandlerLogger("chat-completions-handler");
1296
1591
  async function handleCompletion$1(c) {
1297
1592
  let payload = await c.req.json();
1298
1593
  const requestedModel = payload.model;
@@ -1307,7 +1602,7 @@ async function handleCompletion$1(c) {
1307
1602
  });
1308
1603
  }
1309
1604
  await checkRateLimit(state);
1310
- debugJson(logger$7, "Request payload:", payload);
1605
+ debugJson(logger$8, "Request payload:", payload);
1311
1606
  const selectedModel = state.models?.data.find((model) => model.id === payload.model);
1312
1607
  if (state.manualApprove) await awaitApproval();
1313
1608
  if (isNullish(payload.max_tokens) && isNullish(payload.max_completion_tokens)) {
@@ -1315,16 +1610,16 @@ async function handleCompletion$1(c) {
1315
1610
  ...payload,
1316
1611
  max_tokens: selectedModel?.capabilities.limits.max_output_tokens
1317
1612
  };
1318
- debugJson(logger$7, "Set max_tokens to:", payload.max_tokens);
1613
+ debugJson(logger$8, "Set max_tokens to:", payload.max_tokens);
1319
1614
  }
1320
1615
  if (payload.model.includes("gpt")) {
1321
1616
  if (isNullish(payload.max_completion_tokens)) payload.max_completion_tokens = payload.max_tokens;
1322
1617
  delete payload.max_tokens;
1323
1618
  }
1324
1619
  const requestId = generateRequestIdFromPayload(payload);
1325
- logger$7.debug("Generated request ID:", requestId);
1620
+ logger$8.debug("Generated request ID:", requestId);
1326
1621
  const sessionId = getUUID(requestId);
1327
- logger$7.debug("Extracted session ID:", sessionId);
1622
+ logger$8.debug("Extracted session ID:", sessionId);
1328
1623
  const recordUsage = createCopilotTokenUsageRecorder({
1329
1624
  endpoint: "chat_completions",
1330
1625
  fallbackSessionId: sessionId,
@@ -1335,17 +1630,23 @@ async function handleCompletion$1(c) {
1335
1630
  sessionId
1336
1631
  });
1337
1632
  if (isNonStreaming$1(response)) {
1338
- debugJson(logger$7, "Non-streaming response:", response);
1339
- recordUsage(normalizeOpenAIUsage(response.usage));
1633
+ debugJson(logger$8, "Non-streaming response:", response);
1634
+ recordUsage({
1635
+ ...normalizeOpenAIUsage(response.usage),
1636
+ total_nano_aiu: normalizeOptionalToken(response.copilot_usage?.total_nano_aiu)
1637
+ });
1340
1638
  return c.json(response);
1341
1639
  }
1342
- logger$7.debug("Streaming response");
1640
+ logger$8.debug("Streaming response");
1343
1641
  return streamSSE(c, async (stream) => {
1344
1642
  let usage = {};
1345
1643
  for await (const chunk of response) {
1346
- debugJson(logger$7, "Streaming chunk:", chunk);
1644
+ debugJson(logger$8, "Streaming chunk:", chunk);
1347
1645
  const parsedChunk = parseChatCompletionChunk(chunk);
1348
- if (parsedChunk?.usage) usage = normalizeOpenAIUsage(parsedChunk.usage);
1646
+ if (parsedChunk?.usage || parsedChunk?.copilot_usage) usage = {
1647
+ ...normalizeOpenAIUsage(parsedChunk.usage),
1648
+ total_nano_aiu: normalizeOptionalToken(parsedChunk.copilot_usage?.total_nano_aiu)
1649
+ };
1349
1650
  await stream.writeSSE(chunk);
1350
1651
  }
1351
1652
  recordUsage(usage);
@@ -2448,7 +2749,7 @@ const prepareMessagesApiPayload = (payload, selectedModel) => {
2448
2749
  };
2449
2750
  //#endregion
2450
2751
  //#region src/routes/provider/messages/count-tokens-handler.ts
2451
- const logger$6 = createHandlerLogger("provider-count-tokens-handler");
2752
+ const logger$7 = createHandlerLogger("provider-count-tokens-handler");
2452
2753
  async function handleProviderCountTokens(c) {
2453
2754
  const provider = c.req.param("provider");
2454
2755
  return await handleProviderCountTokensForProvider(c, {
@@ -2471,7 +2772,7 @@ async function handleProviderCountTokensForProvider(c, options) {
2471
2772
  toolContentSupportType: modelConfig?.toolContentSupportType ?? []
2472
2773
  } : void 0), createFallbackModel(modelId));
2473
2774
  const finalTokenCount = tokenCount.input + tokenCount.output;
2474
- logger$6.debug("provider.count_tokens.success", {
2775
+ logger$7.debug("provider.count_tokens.success", {
2475
2776
  provider,
2476
2777
  model: anthropicPayload.model,
2477
2778
  input_tokens: finalTokenCount
@@ -3608,9 +3909,9 @@ const mapResponsesUsage = (response) => {
3608
3909
  ...response.usage?.input_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: response.usage.input_tokens_details.cached_tokens }
3609
3910
  };
3610
3911
  };
3611
- const isRecord = (value) => typeof value === "object" && value !== null;
3612
- const isResponseOutputText = (block) => isRecord(block) && "type" in block && block.type === "output_text";
3613
- const isResponseOutputRefusal = (block) => isRecord(block) && "type" in block && block.type === "refusal";
3912
+ const isRecord$1 = (value) => typeof value === "object" && value !== null;
3913
+ const isResponseOutputText = (block) => isRecord$1(block) && "type" in block && block.type === "output_text";
3914
+ const isResponseOutputRefusal = (block) => isRecord$1(block) && "type" in block && block.type === "refusal";
3614
3915
  const convertToolResultContent = (content) => {
3615
3916
  if (typeof content === "string") return content;
3616
3917
  if (Array.isArray(content)) {
@@ -4512,39 +4813,47 @@ const createWebSearchResponsesStreamCollection = () => ({
4512
4813
  textPartsByKey: /* @__PURE__ */ new Map()
4513
4814
  });
4514
4815
  const collectWebSearchResponsesStreamEvent = (event, state) => {
4515
- if (event.type === "response.created") {
4516
- state.createdResponse = getResponsesResult(event.response);
4517
- return;
4518
- }
4519
- if (isResponsesTerminalEvent(event)) {
4520
- state.terminalResponse = event.response;
4521
- return;
4522
- }
4523
- if (event.type === "response.output_item.added" || event.type === "response.output_item.done") {
4524
- const outputIndex = getNumber(event.output_index);
4525
- const item = getRecord(event.item);
4526
- if (outputIndex !== void 0 && item) state.outputItemsByIndex.set(outputIndex, item);
4527
- return;
4528
- }
4529
- if (event.type === "response.output_text.delta") {
4530
- const part = getOrCreateOutputTextPart(event, state);
4531
- const delta = getString(event.delta);
4532
- if (part && delta) part.text += delta;
4533
- return;
4534
- }
4535
- if (event.type === "response.output_text.done") {
4536
- const part = getOrCreateOutputTextPart(event, state);
4537
- const text = getString(event.text);
4538
- if (part && text !== void 0) part.text = text;
4539
- return;
4540
- }
4541
- if (event.type === "response.output_text.annotation.added") {
4542
- const part = getOrCreateOutputTextPart(event, state);
4543
- const annotation = event.annotation;
4544
- if (part && annotation !== void 0) part.annotations.push(annotation);
4545
- return;
4816
+ switch (event.type) {
4817
+ case "response.created":
4818
+ state.createdResponse = getResponsesResult(event.response);
4819
+ break;
4820
+ case "response.completed":
4821
+ case "response.failed":
4822
+ case "response.incomplete":
4823
+ if (isResponsesTerminalEvent(event)) {
4824
+ event.response.copilot_usage ??= event.copilot_usage;
4825
+ state.terminalResponse = event.response;
4826
+ }
4827
+ break;
4828
+ case "response.output_item.added":
4829
+ case "response.output_item.done": {
4830
+ const outputIndex = getNumber(event.output_index);
4831
+ const item = getRecord(event.item);
4832
+ if (outputIndex !== void 0 && item) state.outputItemsByIndex.set(outputIndex, item);
4833
+ break;
4834
+ }
4835
+ case "response.output_text.delta": {
4836
+ const part = getOrCreateOutputTextPart(event, state);
4837
+ const delta = getString(event.delta);
4838
+ if (part && delta) part.text += delta;
4839
+ break;
4840
+ }
4841
+ case "response.output_text.done": {
4842
+ const part = getOrCreateOutputTextPart(event, state);
4843
+ const text = getString(event.text);
4844
+ if (part && text !== void 0) part.text = text;
4845
+ break;
4846
+ }
4847
+ case "response.output_text.annotation.added": {
4848
+ const part = getOrCreateOutputTextPart(event, state);
4849
+ const annotation = event.annotation;
4850
+ if (part && annotation !== void 0) part.annotations.push(annotation);
4851
+ break;
4852
+ }
4853
+ case "response.content_part.done":
4854
+ collectDoneContentPart(event, state);
4855
+ break;
4546
4856
  }
4547
- if (event.type === "response.content_part.done") collectDoneContentPart(event, state);
4548
4857
  };
4549
4858
  const buildWebSearchResponsesStreamResult = (state) => {
4550
4859
  const response = state.terminalResponse ?? state.createdResponse;
@@ -4692,7 +5001,10 @@ const handleWebSearchViaResponses = async (c, payload, options) => {
4692
5001
  }) : upstreamResult;
4693
5002
  const { extract, response } = reconstructWebSearchResponse(payload, result, { requestId: options.requestId });
4694
5003
  debugJson(logger, `Web search via responses: ${extract.queries.length} quer(y/ies), ${extract.sources.length} source(s)`, result);
4695
- createUsageRecorder(payload, options.sessionId, webSearchModel)(normalizeResponsesUsage(result.usage));
5004
+ createUsageRecorder(payload, options.sessionId, webSearchModel)({
5005
+ ...normalizeResponsesUsage(result.usage),
5006
+ total_nano_aiu: normalizeOptionalToken(result.copilot_usage?.total_nano_aiu)
5007
+ });
4696
5008
  if (!wantsStream) return c.json(response);
4697
5009
  return streamSSE(c, async (stream) => {
4698
5010
  for (const event of buildSyntheticStreamEvents(response)) await stream.writeSSE({
@@ -4857,7 +5169,7 @@ function getModels() {
4857
5169
  }
4858
5170
  //#endregion
4859
5171
  //#region src/routes/provider/messages/handler.ts
4860
- const logger$5 = createHandlerLogger("provider-messages-handler");
5172
+ const logger$6 = createHandlerLogger("provider-messages-handler");
4861
5173
  const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
4862
5174
  const OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
4863
5175
  const OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES = new Set([
@@ -4882,7 +5194,7 @@ async function handleProviderMessagesForProvider(c, options) {
4882
5194
  } }, 404);
4883
5195
  try {
4884
5196
  const modelConfig = providerConfig.models?.[payload.model];
4885
- debugJson(logger$5, "provider.messages.request", {
5197
+ debugJson(logger$6, "provider.messages.request", {
4886
5198
  payload,
4887
5199
  provider
4888
5200
  });
@@ -4891,6 +5203,7 @@ async function handleProviderMessagesForProvider(c, options) {
4891
5203
  if (providerConfig.type === "openai-responses") {
4892
5204
  if (hasWebSearchServerTool(payload)) {
4893
5205
  if (isWebSearchOnlyRequest(payload)) return await handleOpenAIResponsesProviderWebSearchMessages(c, {
5206
+ modelConfig,
4894
5207
  payload,
4895
5208
  provider,
4896
5209
  providerConfig
@@ -4914,29 +5227,33 @@ async function handleProviderMessagesForProvider(c, options) {
4914
5227
  });
4915
5228
  }
4916
5229
  applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
4917
- debugJson(logger$5, "Translated provider.messages.request", {
5230
+ debugJson(logger$6, "Translated provider.messages.request", {
4918
5231
  payload,
4919
5232
  provider
4920
5233
  });
4921
5234
  const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers);
4922
5235
  if (!upstreamResponse.ok) {
4923
- logger$5.error("Failed to create responses", upstreamResponse);
5236
+ logger$6.error("Failed to create responses", upstreamResponse);
4924
5237
  throw new HTTPError("Failed to create responses", upstreamResponse);
4925
5238
  }
4926
5239
  const contentType = upstreamResponse.headers.get("content-type") ?? "";
4927
5240
  if (Boolean(payload.stream) && contentType.includes("text/event-stream")) return streamProviderMessages({
4928
5241
  c,
5242
+ modelConfig,
4929
5243
  payload,
5244
+ pricingCurrency: providerConfig.pricingCurrency,
4930
5245
  provider,
4931
5246
  upstreamResponse
4932
5247
  });
4933
5248
  return respondProviderMessagesJson(c, {
4934
5249
  body: await upstreamResponse.json(),
5250
+ modelConfig,
4935
5251
  payload,
5252
+ pricingCurrency: providerConfig.pricingCurrency,
4936
5253
  provider
4937
5254
  });
4938
5255
  } catch (error) {
4939
- logger$5.error("provider.messages.error", {
5256
+ logger$6.error("provider.messages.error", {
4940
5257
  provider,
4941
5258
  error
4942
5259
  });
@@ -4944,13 +5261,13 @@ async function handleProviderMessagesForProvider(c, options) {
4944
5261
  }
4945
5262
  }
4946
5263
  const handleOpenAIResponsesProviderWebSearchMessages = async (c, options) => {
4947
- const { payload, provider, providerConfig } = options;
5264
+ const { modelConfig, payload, provider, providerConfig } = options;
4948
5265
  const selectedModel = providerConfig.name === "codex" ? getModels().data.find((model) => model.id === payload.model) : void 0;
4949
5266
  const responsesPayload = prepareWebSearchResponsesPayload(payload);
4950
5267
  responsesPayload.stream = true;
4951
5268
  applyResponsesApiContextManagement(responsesPayload, selectedModel?.capabilities.limits.max_prompt_tokens);
4952
5269
  compactInputByLatestCompaction(responsesPayload);
4953
- debugJson(logger$5, "provider.messages.responses.web_search.request", {
5270
+ debugJson(logger$6, "provider.messages.responses.web_search.request", {
4954
5271
  payload: responsesPayload,
4955
5272
  provider
4956
5273
  });
@@ -4961,20 +5278,24 @@ const handleOpenAIResponsesProviderWebSearchMessages = async (c, options) => {
4961
5278
  errorMessagePrefix: `${provider} web search responses stream`,
4962
5279
  parseEvent: (data) => parseResponsesProviderStreamChunk(data, providerConfig),
4963
5280
  upstreamResponse,
4964
- logger: logger$5
5281
+ logger: logger$6
4965
5282
  }),
5283
+ modelConfig,
4966
5284
  payload,
5285
+ pricingCurrency: providerConfig.pricingCurrency,
4967
5286
  provider
4968
5287
  });
4969
5288
  return respondWebSearchProviderMessagesJson(c, {
4970
5289
  body: upstreamResponse,
5290
+ modelConfig,
4971
5291
  payload,
5292
+ pricingCurrency: providerConfig.pricingCurrency,
4972
5293
  provider
4973
5294
  });
4974
5295
  }
4975
5296
  const upstreamResponse = await forwardProviderResponses(providerConfig, responsesPayload, c.req.raw.headers);
4976
5297
  if (!upstreamResponse.ok) {
4977
- logger$5.error("Failed to create provider web search responses", {
5298
+ logger$6.error("Failed to create provider web search responses", {
4978
5299
  provider,
4979
5300
  upstreamResponse
4980
5301
  });
@@ -4985,24 +5306,28 @@ const handleOpenAIResponsesProviderWebSearchMessages = async (c, options) => {
4985
5306
  errorMessagePrefix: `${provider} web search responses stream`,
4986
5307
  parseEvent: (data) => parseResponsesProviderStreamChunk(data, providerConfig),
4987
5308
  upstreamResponse: events(upstreamResponse),
4988
- logger: logger$5
5309
+ logger: logger$6
4989
5310
  }),
5311
+ modelConfig,
4990
5312
  payload,
5313
+ pricingCurrency: providerConfig.pricingCurrency,
4991
5314
  provider
4992
5315
  });
4993
5316
  return respondWebSearchProviderMessagesJson(c, {
4994
5317
  body: await upstreamResponse.json(),
5318
+ modelConfig,
4995
5319
  payload,
5320
+ pricingCurrency: providerConfig.pricingCurrency,
4996
5321
  provider
4997
5322
  });
4998
5323
  };
4999
5324
  const handleOpenAIResponsesProviderMessages = async (c, options) => {
5000
- const { payload, provider, providerConfig } = options;
5325
+ const { modelConfig, payload, provider, providerConfig } = options;
5001
5326
  const selectedModel = providerConfig.name === "codex" ? getModels().data.find((model) => model.id === payload.model) : void 0;
5002
5327
  const responsesPayload = translateAnthropicMessagesToResponsesPayload(payload);
5003
5328
  applyResponsesApiContextManagement(responsesPayload, selectedModel?.capabilities.limits.max_prompt_tokens);
5004
5329
  compactInputByLatestCompaction(responsesPayload);
5005
- debugJson(logger$5, "provider.messages.responses.request", {
5330
+ debugJson(logger$6, "provider.messages.responses.request", {
5006
5331
  payload: responsesPayload,
5007
5332
  provider
5008
5333
  });
@@ -5010,33 +5335,41 @@ const handleOpenAIResponsesProviderMessages = async (c, options) => {
5010
5335
  const upstreamResponse = await forwardCodexResponses(responsesPayload, c.req.raw.headers, providerConfig.baseUrl);
5011
5336
  if (responsesPayload.stream && isResponsesStream$1(upstreamResponse)) return streamResponsesProviderMessages({
5012
5337
  c,
5338
+ modelConfig,
5013
5339
  payload,
5340
+ pricingCurrency: providerConfig.pricingCurrency,
5014
5341
  provider,
5015
5342
  providerConfig,
5016
5343
  upstreamResponse
5017
5344
  });
5018
5345
  return respondResponsesProviderMessagesJson(c, {
5019
5346
  body: upstreamResponse,
5347
+ modelConfig,
5020
5348
  payload,
5349
+ pricingCurrency: providerConfig.pricingCurrency,
5021
5350
  provider,
5022
5351
  providerConfig
5023
5352
  });
5024
5353
  }
5025
5354
  const upstreamResponse = await forwardProviderResponses(providerConfig, responsesPayload, c.req.raw.headers);
5026
5355
  if (!upstreamResponse.ok) {
5027
- logger$5.error("Failed to create provider responses", upstreamResponse);
5356
+ logger$6.error("Failed to create provider responses", upstreamResponse);
5028
5357
  throw new HTTPError("Failed to create provider responses", upstreamResponse);
5029
5358
  }
5030
5359
  if (responsesPayload.stream) return streamResponsesProviderMessages({
5031
5360
  c,
5361
+ modelConfig,
5032
5362
  payload,
5363
+ pricingCurrency: providerConfig.pricingCurrency,
5033
5364
  provider,
5034
5365
  providerConfig,
5035
5366
  upstreamResponse: events(upstreamResponse)
5036
5367
  });
5037
5368
  return respondResponsesProviderMessagesJson(c, {
5038
5369
  body: await upstreamResponse.json(),
5370
+ modelConfig,
5039
5371
  payload,
5372
+ pricingCurrency: providerConfig.pricingCurrency,
5040
5373
  provider,
5041
5374
  providerConfig
5042
5375
  });
@@ -5071,25 +5404,29 @@ const applyOpenAICompatibleExtraBodyThinkingBudget = (payload, options) => {
5071
5404
  const handleOpenAICompatibleProviderMessages = async (c, options) => {
5072
5405
  const { modelConfig, payload, provider, providerConfig } = options;
5073
5406
  const openAIPayload = createOpenAICompatiblePayload(payload, modelConfig);
5074
- debugJson(logger$5, "provider.messages.openai_compatible.request", {
5407
+ debugJson(logger$6, "provider.messages.openai_compatible.request", {
5075
5408
  payload: openAIPayload,
5076
5409
  provider
5077
5410
  });
5078
5411
  const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers);
5079
5412
  if (!upstreamResponse.ok) {
5080
- logger$5.error("Failed to create openai-compatible responses", upstreamResponse);
5413
+ logger$6.error("Failed to create openai-compatible responses", upstreamResponse);
5081
5414
  throw new HTTPError("Failed to create openai-compatible responses", upstreamResponse);
5082
5415
  }
5083
5416
  const contentType = upstreamResponse.headers.get("content-type") ?? "";
5084
5417
  if (Boolean(openAIPayload.stream) && contentType.includes("text/event-stream")) return streamOpenAICompatibleProviderMessages({
5085
5418
  c,
5419
+ modelConfig,
5086
5420
  payload,
5421
+ pricingCurrency: providerConfig.pricingCurrency,
5087
5422
  provider,
5088
5423
  upstreamResponse
5089
5424
  });
5090
5425
  return respondOpenAICompatibleProviderMessagesJson(c, {
5091
5426
  body: await upstreamResponse.json(),
5427
+ modelConfig,
5092
5428
  payload,
5429
+ pricingCurrency: providerConfig.pricingCurrency,
5093
5430
  provider
5094
5431
  });
5095
5432
  };
@@ -5158,13 +5495,13 @@ const applyContextCacheControl = (message) => {
5158
5495
  const setContextCacheControl = (part) => {
5159
5496
  part.cache_control = { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL };
5160
5497
  };
5161
- const streamProviderMessages = ({ c, payload, provider, upstreamResponse }) => {
5162
- logger$5.debug("provider.messages.streaming");
5163
- const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
5498
+ const streamProviderMessages = ({ c, modelConfig, payload, pricingCurrency, provider, upstreamResponse }) => {
5499
+ logger$6.debug("provider.messages.streaming");
5500
+ const recordUsage = createProviderMessagesUsageRecorder(payload, provider, modelConfig, pricingCurrency);
5164
5501
  return streamSSE(c, async (stream) => {
5165
5502
  let usage = {};
5166
5503
  for await (const chunk of events(upstreamResponse)) {
5167
- logger$5.debug("provider.messages.raw_stream_event:", chunk.data);
5504
+ logger$6.debug("provider.messages.raw_stream_event:", chunk.data);
5168
5505
  const eventName = chunk.event;
5169
5506
  if (eventName === "ping") {
5170
5507
  await stream.writeSSE({
@@ -5189,9 +5526,9 @@ const streamProviderMessages = ({ c, payload, provider, upstreamResponse }) => {
5189
5526
  recordUsage(usage);
5190
5527
  });
5191
5528
  };
5192
- const streamOpenAICompatibleProviderMessages = ({ c, payload, provider, upstreamResponse }) => {
5193
- logger$5.debug("provider.messages.openai_compatible.streaming");
5194
- const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
5529
+ const streamOpenAICompatibleProviderMessages = ({ c, modelConfig, payload, pricingCurrency, provider, upstreamResponse }) => {
5530
+ logger$6.debug("provider.messages.openai_compatible.streaming");
5531
+ const recordUsage = createProviderMessagesUsageRecorder(payload, provider, modelConfig, pricingCurrency);
5195
5532
  return streamSSE(c, async (stream) => {
5196
5533
  let usage = {};
5197
5534
  const streamState = {
@@ -5202,7 +5539,7 @@ const streamOpenAICompatibleProviderMessages = ({ c, payload, provider, upstream
5202
5539
  thinkingBlockOpen: false
5203
5540
  };
5204
5541
  for await (const chunk of events(upstreamResponse)) {
5205
- logger$5.debug("provider.messages.openai_compatible.raw_stream_event:", chunk.data);
5542
+ logger$6.debug("provider.messages.openai_compatible.raw_stream_event:", chunk.data);
5206
5543
  if (chunk.event === "ping") {
5207
5544
  await stream.writeSSE({
5208
5545
  event: "ping",
@@ -5220,7 +5557,7 @@ const streamOpenAICompatibleProviderMessages = ({ c, payload, provider, upstream
5220
5557
  const events = translateChunkToAnthropicEvents(parsed, streamState);
5221
5558
  for (const event of events) {
5222
5559
  const eventData = JSON.stringify(event);
5223
- debugLazy(logger$5, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
5560
+ debugLazy(logger$6, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
5224
5561
  await stream.writeSSE({
5225
5562
  event: event.type,
5226
5563
  data: eventData
@@ -5229,7 +5566,7 @@ const streamOpenAICompatibleProviderMessages = ({ c, payload, provider, upstream
5229
5566
  }
5230
5567
  for (const event of flushPendingAnthropicStreamEvents(streamState)) {
5231
5568
  const eventData = JSON.stringify(event);
5232
- debugLazy(logger$5, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
5569
+ debugLazy(logger$6, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
5233
5570
  await stream.writeSSE({
5234
5571
  event: event.type,
5235
5572
  data: eventData
@@ -5238,14 +5575,14 @@ const streamOpenAICompatibleProviderMessages = ({ c, payload, provider, upstream
5238
5575
  recordUsage(usage);
5239
5576
  });
5240
5577
  };
5241
- const streamResponsesProviderMessages = ({ c, payload, provider, providerConfig, upstreamResponse }) => {
5242
- logger$5.debug("provider.messages.responses.streaming", { provider });
5243
- const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
5578
+ const streamResponsesProviderMessages = ({ c, modelConfig, payload, pricingCurrency, provider, providerConfig, upstreamResponse }) => {
5579
+ logger$6.debug("provider.messages.responses.streaming", { provider });
5580
+ const recordUsage = createProviderMessagesUsageRecorder(payload, provider, modelConfig, pricingCurrency);
5244
5581
  return streamSSE(c, async (stream) => {
5245
5582
  let usage = {};
5246
5583
  const streamState = createResponsesStreamState({ toolSearchName: resolveBridgeToolSearchName(payload.tools) });
5247
5584
  for await (const chunk of upstreamResponse) {
5248
- logger$5.debug("provider.messages.responses.raw_stream_event:", chunk.data);
5585
+ logger$6.debug("provider.messages.responses.raw_stream_event:", chunk.data);
5249
5586
  if (chunk.event === "ping") {
5250
5587
  await stream.writeSSE({
5251
5588
  event: "ping",
@@ -5263,7 +5600,7 @@ const streamResponsesProviderMessages = ({ c, payload, provider, providerConfig,
5263
5600
  const events = translateResponsesStreamEvent(parsed, streamState);
5264
5601
  for (const event of events) {
5265
5602
  const eventData = JSON.stringify(event);
5266
- debugLazy(logger$5, () => ["provider.messages.responses.translated_event:", eventData]);
5603
+ debugLazy(logger$6, () => ["provider.messages.responses.translated_event:", eventData]);
5267
5604
  await stream.writeSSE({
5268
5605
  event: event.type,
5269
5606
  data: eventData
@@ -5287,7 +5624,7 @@ const parseOpenAICompatibleStreamChunk = (data) => {
5287
5624
  try {
5288
5625
  return JSON.parse(data);
5289
5626
  } catch (error) {
5290
- logger$5.error("provider.messages.openai_compatible.parse_chunk_error", {
5627
+ logger$6.error("provider.messages.openai_compatible.parse_chunk_error", {
5291
5628
  data,
5292
5629
  error
5293
5630
  });
@@ -5300,7 +5637,7 @@ const parseResponsesProviderStreamChunk = (data, providerConfig) => {
5300
5637
  if (providerConfig.name === "codex") logCodexRateLimitsEvent(parsed);
5301
5638
  return parsed;
5302
5639
  } catch (error) {
5303
- logger$5.error("provider.messages.responses.parse_chunk_error", {
5640
+ logger$6.error("provider.messages.responses.parse_chunk_error", {
5304
5641
  provider: providerConfig.name,
5305
5642
  data,
5306
5643
  error
@@ -5325,7 +5662,7 @@ const parseProviderStreamEvent = (data) => {
5325
5662
  usage: {}
5326
5663
  };
5327
5664
  } catch (error) {
5328
- logger$5.error("provider.messages.streaming.adjust_tokens_error", {
5665
+ logger$6.error("provider.messages.streaming.adjust_tokens_error", {
5329
5666
  error,
5330
5667
  originalData: data
5331
5668
  });
@@ -5333,31 +5670,31 @@ const parseProviderStreamEvent = (data) => {
5333
5670
  }
5334
5671
  };
5335
5672
  const respondProviderMessagesJson = (c, options) => {
5336
- const { body, payload, provider } = options;
5337
- createProviderMessagesUsageRecorder(payload, provider)(normalizeAnthropicUsage(body.usage));
5338
- debugJson(logger$5, "provider.messages.no_stream result:", body);
5673
+ const { body, modelConfig, payload, pricingCurrency, provider } = options;
5674
+ createProviderMessagesUsageRecorder(payload, provider, modelConfig, pricingCurrency)(normalizeAnthropicUsage(body.usage));
5675
+ debugJson(logger$6, "provider.messages.no_stream result:", body);
5339
5676
  return c.json(body);
5340
5677
  };
5341
5678
  const respondOpenAICompatibleProviderMessagesJson = (c, options) => {
5342
- const { body, payload, provider } = options;
5343
- createProviderMessagesUsageRecorder(payload, provider)(normalizeOpenAIUsage(body.usage));
5679
+ const { body, modelConfig, payload, pricingCurrency, provider } = options;
5680
+ createProviderMessagesUsageRecorder(payload, provider, modelConfig, pricingCurrency)(normalizeOpenAIUsage(body.usage));
5344
5681
  const anthropicResponse = translateToAnthropic(body);
5345
- debugJson(logger$5, "provider.messages.openai_compatible.no_stream result:", anthropicResponse);
5682
+ debugJson(logger$6, "provider.messages.openai_compatible.no_stream result:", anthropicResponse);
5346
5683
  return c.json(anthropicResponse);
5347
5684
  };
5348
5685
  const respondResponsesProviderMessagesJson = (c, options) => {
5349
- const { body, payload, provider, providerConfig } = options;
5350
- createProviderMessagesUsageRecorder(payload, provider)(normalizeResponsesUsage(body.usage));
5686
+ const { body, modelConfig, payload, pricingCurrency, provider, providerConfig } = options;
5687
+ createProviderMessagesUsageRecorder(payload, provider, modelConfig, pricingCurrency)(normalizeResponsesUsage(body.usage));
5351
5688
  const anthropicResponse = translateResponsesResultToAnthropic(body, { toolSearchName: resolveBridgeToolSearchName(payload.tools) });
5352
- debugJson(logger$5, "provider.messages.responses.no_stream result:", anthropicResponse);
5353
- if (providerConfig.name === "codex") logger$5.debug("provider.messages.codex.no_stream.result");
5689
+ debugJson(logger$6, "provider.messages.responses.no_stream result:", anthropicResponse);
5690
+ if (providerConfig.name === "codex") logger$6.debug("provider.messages.codex.no_stream.result");
5354
5691
  return c.json(anthropicResponse);
5355
5692
  };
5356
5693
  const respondWebSearchProviderMessagesJson = (c, options) => {
5357
- const { body, payload, provider } = options;
5358
- createProviderMessagesUsageRecorder(payload, provider)(normalizeResponsesUsage(body.usage));
5694
+ const { body, modelConfig, payload, pricingCurrency, provider } = options;
5695
+ createProviderMessagesUsageRecorder(payload, provider, modelConfig, pricingCurrency)(normalizeResponsesUsage(body.usage));
5359
5696
  const { extract, response } = reconstructWebSearchResponse(payload, body, { requestId: body.id || `${provider}:${payload.model}` });
5360
- logger$5.debug(`provider.messages.responses.web_search: ${extract.queries.length} quer(y/ies), ${extract.sources.length} source(s)`);
5697
+ logger$6.debug(`provider.messages.responses.web_search: ${extract.queries.length} quer(y/ies), ${extract.sources.length} source(s)`);
5361
5698
  if (!payload.stream) return c.json(response);
5362
5699
  return streamSSE(c, async (stream) => {
5363
5700
  for (const event of buildSyntheticStreamEvents(response)) await stream.writeSSE({
@@ -5366,9 +5703,11 @@ const respondWebSearchProviderMessagesJson = (c, options) => {
5366
5703
  });
5367
5704
  });
5368
5705
  };
5369
- const createProviderMessagesUsageRecorder = (payload, provider) => createProviderTokenUsageRecorder({
5706
+ const createProviderMessagesUsageRecorder = (payload, provider, modelConfig, pricingCurrency) => createProviderTokenUsageRecorder({
5370
5707
  endpoint: "provider_messages",
5371
5708
  model: payload.model,
5709
+ pricing: modelConfig?.pricing,
5710
+ pricingCurrency,
5372
5711
  providerName: provider,
5373
5712
  sessionId: parseUserIdMetadata(payload.metadata?.user_id).sessionId
5374
5713
  });
@@ -5454,7 +5793,10 @@ const handleWithChatCompletions = async (c, anthropicPayload, options) => {
5454
5793
  });
5455
5794
  if (isNonStreaming(response)) {
5456
5795
  debugJson(logger, "Non-streaming response from Copilot:", response);
5457
- recordUsage(normalizeOpenAIUsage(response.usage));
5796
+ recordUsage({
5797
+ ...normalizeOpenAIUsage(response.usage),
5798
+ total_nano_aiu: normalizeOptionalToken(response.copilot_usage?.total_nano_aiu)
5799
+ });
5458
5800
  const anthropicResponse = translateToAnthropic(response);
5459
5801
  debugJson(logger, "Translated Anthropic response:", anthropicResponse);
5460
5802
  return c.json(anthropicResponse);
@@ -5474,7 +5816,10 @@ const handleWithChatCompletions = async (c, anthropicPayload, options) => {
5474
5816
  if (rawEvent.data === "[DONE]") break;
5475
5817
  if (!rawEvent.data) continue;
5476
5818
  const chunk = JSON.parse(rawEvent.data);
5477
- if (chunk.usage) usage = normalizeOpenAIUsage(chunk.usage);
5819
+ if (chunk.usage || chunk.copilot_usage) usage = {
5820
+ ...normalizeOpenAIUsage(chunk.usage),
5821
+ total_nano_aiu: normalizeOptionalToken(chunk.copilot_usage?.total_nano_aiu)
5822
+ };
5478
5823
  const events = translateChunkToAnthropicEvents(chunk, streamState);
5479
5824
  for (const event of events) {
5480
5825
  const eventData = JSON.stringify(event);
@@ -5533,7 +5878,10 @@ const handleWithResponsesApi = async (c, anthropicPayload, options) => {
5533
5878
  if (!data) continue;
5534
5879
  debugLazy(logger, () => ["Responses raw stream event:", data]);
5535
5880
  const responseEvent = JSON.parse(data);
5536
- if (responseEvent.type === "response.completed" || responseEvent.type === "response.failed" || responseEvent.type === "response.incomplete") usage = normalizeResponsesUsage(responseEvent.response.usage);
5881
+ if (responseEvent.type === "response.completed" || responseEvent.type === "response.failed" || responseEvent.type === "response.incomplete") usage = {
5882
+ ...normalizeResponsesUsage(responseEvent.response.usage),
5883
+ total_nano_aiu: normalizeOptionalToken(responseEvent.copilot_usage?.total_nano_aiu)
5884
+ };
5537
5885
  const events = translateResponsesStreamEvent(responseEvent, streamState);
5538
5886
  for (const event of events) {
5539
5887
  const eventData = JSON.stringify(event);
@@ -5559,12 +5907,13 @@ const handleWithResponsesApi = async (c, anthropicPayload, options) => {
5559
5907
  recordUsage(usage);
5560
5908
  });
5561
5909
  }
5562
- debugJsonTail(logger, "Non-streaming Responses result:", {
5563
- value: response,
5564
- tailLength: 400
5565
- });
5910
+ debugJson(logger, "Non-streaming Responses result:", response);
5566
5911
  const anthropicResponse = translateResponsesResultToAnthropic(response, { toolSearchName: resolveBridgeToolSearchName(anthropicPayload.tools) });
5567
- recordUsage(normalizeResponsesUsage(response.usage));
5912
+ const responsesResult = response;
5913
+ recordUsage({
5914
+ ...normalizeResponsesUsage(responsesResult.usage),
5915
+ total_nano_aiu: normalizeOptionalToken(responsesResult.copilot_usage?.total_nano_aiu)
5916
+ });
5568
5917
  debugJson(logger, "Translated Anthropic response:", anthropicResponse);
5569
5918
  return c.json(anthropicResponse);
5570
5919
  };
@@ -5650,7 +5999,7 @@ const parseAnthropicStreamEvent = (data) => {
5650
5999
  };
5651
6000
  //#endregion
5652
6001
  //#region src/routes/messages/handler.ts
5653
- const logger$4 = createHandlerLogger("messages-handler");
6002
+ const logger$5 = createHandlerLogger("messages-handler");
5654
6003
  const messagesFlowHandlers = {
5655
6004
  handleWithChatCompletions,
5656
6005
  handleWithMessagesApi,
@@ -5662,7 +6011,7 @@ async function handleCompletion(c) {
5662
6011
  anthropicPayload.model = resolveMappedModel(anthropicPayload.model);
5663
6012
  if (anthropicPayload.model !== requestedModel) consola.debug(`Resolved model mapping: ${requestedModel} -> ${anthropicPayload.model}`);
5664
6013
  const webSearchResult = await tryHandleWebSearch(c, anthropicPayload, {
5665
- logger: logger$4,
6014
+ logger: logger$5,
5666
6015
  forwardToProvider: (ctx, payload, provider) => handleProviderMessagesForProvider(ctx, {
5667
6016
  payload,
5668
6017
  provider
@@ -5677,19 +6026,19 @@ async function handleCompletion(c) {
5677
6026
  provider: providerModelAlias.provider
5678
6027
  });
5679
6028
  }
5680
- debugJson(logger$4, "Anthropic request payload:", anthropicPayload);
6029
+ debugJson(logger$5, "Anthropic request payload:", anthropicPayload);
5681
6030
  normalizeSystemMessages(anthropicPayload);
5682
6031
  await checkRateLimit(state);
5683
6032
  sanitizeIdeTools(anthropicPayload);
5684
6033
  const subagentMarker = parseSubagentMarkerFromFirstUser(anthropicPayload);
5685
- if (subagentMarker) debugJson(logger$4, "Detected Subagent marker:", subagentMarker);
6034
+ if (subagentMarker) debugJson(logger$5, "Detected Subagent marker:", subagentMarker);
5686
6035
  let sessionId = getRootSessionId(anthropicPayload, c);
5687
6036
  const compactType = getCompactType(anthropicPayload);
5688
6037
  const anthropicBeta = c.req.header("anthropic-beta");
5689
- logger$4.debug("Anthropic Beta header:", anthropicBeta);
6038
+ logger$5.debug("Anthropic Beta header:", anthropicBeta);
5690
6039
  const noTools = !anthropicPayload.tools || anthropicPayload.tools.length === 0;
5691
6040
  if (anthropicBeta && noTools && compactType === 0) anthropicPayload.model = getSmallModel();
5692
- if (compactType) logger$4.debug("Compact request type:", compactType);
6041
+ if (compactType) logger$5.debug("Compact request type:", compactType);
5693
6042
  if (!state.tokenBasedBilling) {
5694
6043
  const lastMessageCacheControl = getLastMessageContentCacheControl(anthropicPayload.messages.at(-1));
5695
6044
  stripToolReferenceTurnBoundary(anthropicPayload);
@@ -5697,9 +6046,9 @@ async function handleCompletion(c) {
5697
6046
  applyLastMessageCacheControl(anthropicPayload, lastMessageCacheControl);
5698
6047
  }
5699
6048
  const requestId = generateRequestIdFromPayload(anthropicPayload, sessionId);
5700
- logger$4.debug("Generated request ID:", requestId);
6049
+ logger$5.debug("Generated request ID:", requestId);
5701
6050
  if (!sessionId) sessionId = getUUID(requestId);
5702
- logger$4.debug("Extracted session ID:", sessionId);
6051
+ logger$5.debug("Extracted session ID:", sessionId);
5703
6052
  if (state.manualApprove) await awaitApproval();
5704
6053
  const selectedModel = findEndpointModel(anthropicPayload.model);
5705
6054
  anthropicPayload.model = selectedModel?.id ?? anthropicPayload.model;
@@ -5710,7 +6059,7 @@ async function handleCompletion(c) {
5710
6059
  requestId,
5711
6060
  sessionId,
5712
6061
  compactType,
5713
- logger: logger$4
6062
+ logger: logger$5
5714
6063
  });
5715
6064
  if (shouldUseResponsesApi(selectedModel, compactType)) return await messagesFlowHandlers.handleWithResponsesApi(c, anthropicPayload, {
5716
6065
  subagentMarker,
@@ -5718,7 +6067,7 @@ async function handleCompletion(c) {
5718
6067
  requestId,
5719
6068
  sessionId,
5720
6069
  compactType,
5721
- logger: logger$4
6070
+ logger: logger$5
5722
6071
  });
5723
6072
  return await messagesFlowHandlers.handleWithChatCompletions(c, anthropicPayload, {
5724
6073
  subagentMarker,
@@ -5726,7 +6075,7 @@ async function handleCompletion(c) {
5726
6075
  requestId,
5727
6076
  sessionId,
5728
6077
  compactType,
5729
- logger: logger$4
6078
+ logger: logger$5
5730
6079
  });
5731
6080
  }
5732
6081
  const MESSAGES_ENDPOINT = "/v1/messages";
@@ -5757,24 +6106,89 @@ messageRoutes.post("/count_tokens", async (c) => {
5757
6106
  //#endregion
5758
6107
  //#region src/routes/models/route.ts
5759
6108
  const modelRoutes = new Hono();
5760
- modelRoutes.get("/", async (c) => {
6109
+ const logger$4 = createHandlerLogger("models-handler");
6110
+ const EPOCH_ISO = (/* @__PURE__ */ new Date(0)).toISOString();
6111
+ function isRecord(value) {
6112
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6113
+ }
6114
+ function normalizeCopilotModel(model) {
6115
+ const contextWindow = model.capabilities?.limits?.max_context_window_tokens ?? 0;
6116
+ const clientId = toClientModelId(model.id);
6117
+ return {
6118
+ claude_model_id: contextWindow >= 1e6 ? `${clientId}[1m]` : clientId,
6119
+ ...model,
6120
+ id: clientId,
6121
+ object: "model",
6122
+ type: "model",
6123
+ created: 0,
6124
+ created_at: EPOCH_ISO,
6125
+ owned_by: model.vendor,
6126
+ display_name: model.name
6127
+ };
6128
+ }
6129
+ function getStringField(model, field) {
6130
+ const value = model[field];
6131
+ return typeof value === "string" && value.trim() ? value : void 0;
6132
+ }
6133
+ function normalizeProviderModel(provider, model) {
6134
+ if (!isRecord(model)) return null;
6135
+ const rawId = getStringField(model, "id");
6136
+ if (!rawId) return null;
6137
+ const id = `${provider}/${rawId}`;
6138
+ const name = getStringField(model, "display_name") ?? getStringField(model, "name") ?? rawId;
6139
+ const ownedBy = getStringField(model, "owned_by") ?? getStringField(model, "vendor") ?? provider;
6140
+ return {
6141
+ ...model,
6142
+ id,
6143
+ object: getStringField(model, "object") ?? "model",
6144
+ type: getStringField(model, "type") ?? "model",
6145
+ created: typeof model.created === "number" ? model.created : 0,
6146
+ created_at: getStringField(model, "created_at") ?? EPOCH_ISO,
6147
+ owned_by: ownedBy,
6148
+ display_name: name
6149
+ };
6150
+ }
6151
+ async function getProviderModels(provider, requestHeaders) {
5761
6152
  try {
5762
- if (!state.models) await cacheModels();
5763
- const models = state.models?.data.map((model) => {
5764
- const contextWindow = model.capabilities?.limits?.max_context_window_tokens ?? 0;
5765
- const clientId = toClientModelId(model.id);
5766
- return {
5767
- claude_model_id: contextWindow >= 1e6 ? `${clientId}[1m]` : clientId,
5768
- ...model,
5769
- id: clientId,
5770
- object: "model",
5771
- type: "model",
5772
- created: 0,
5773
- created_at: (/* @__PURE__ */ new Date(0)).toISOString(),
5774
- owned_by: model.vendor,
5775
- display_name: model.name
5776
- };
6153
+ const providerConfig = await resolveProviderConfig(provider);
6154
+ if (!providerConfig) return [];
6155
+ if (providerConfig.name === "codex") return getModels().data.map((model) => normalizeProviderModel(providerConfig.name, model)).filter((model) => model !== null);
6156
+ const response = await forwardProviderModels(providerConfig, requestHeaders);
6157
+ if (!response.ok) {
6158
+ logger$4.warn("models.provider.skip_non_ok", {
6159
+ provider,
6160
+ statusCode: response.status
6161
+ });
6162
+ return [];
6163
+ }
6164
+ const body = await response.json();
6165
+ if (!isRecord(body) || !Array.isArray(body.data)) {
6166
+ logger$4.warn("models.provider.skip_invalid_body", { provider });
6167
+ return [];
6168
+ }
6169
+ return body.data.map((model) => normalizeProviderModel(providerConfig.name, model)).filter((model) => model !== null);
6170
+ } catch (error) {
6171
+ logger$4.warn("models.provider.skip_error", {
6172
+ provider,
6173
+ error
5777
6174
  });
6175
+ return [];
6176
+ }
6177
+ }
6178
+ async function getAggregatedModels(requestHeaders) {
6179
+ const copilotModels = state.models?.data.map(normalizeCopilotModel) ?? [];
6180
+ const providerModelsByProvider = await Promise.all(listEnabledProviders().map((provider) => getProviderModels(provider, requestHeaders)));
6181
+ const models = [...copilotModels, ...providerModelsByProvider.flat()];
6182
+ const seenModelIds = /* @__PURE__ */ new Set();
6183
+ return models.filter((model) => {
6184
+ if (seenModelIds.has(model.id)) return false;
6185
+ seenModelIds.add(model.id);
6186
+ return true;
6187
+ });
6188
+ }
6189
+ modelRoutes.get("/", async (c) => {
6190
+ try {
6191
+ const models = await getAggregatedModels(c.req.raw.headers);
5778
6192
  return c.json({
5779
6193
  object: "list",
5780
6194
  data: models,
@@ -5856,9 +6270,10 @@ async function handleProviderResponsesForProvider(c, options) {
5856
6270
  provider
5857
6271
  });
5858
6272
  compactInputByLatestCompaction(payload);
6273
+ const modelConfig = providerConfig.models?.[payload.model];
5859
6274
  if (providerConfig.name === "codex") {
5860
6275
  const upstreamResponse = await forwardCodexResponses(payload, c.req.raw.headers, providerConfig.baseUrl);
5861
- const recordUsage = createProviderResponsesUsageRecorder(payload, provider);
6276
+ const recordUsage = createProviderResponsesUsageRecorder(payload, provider, modelConfig, providerConfig.pricingCurrency);
5862
6277
  if (payload.stream && isResponsesStream(upstreamResponse)) return streamProviderResponses(c, upstreamResponse, {
5863
6278
  normalizeCodex: true,
5864
6279
  provider,
@@ -5870,7 +6285,7 @@ async function handleProviderResponsesForProvider(c, options) {
5870
6285
  }
5871
6286
  const upstreamResponse = await forwardProviderResponses(providerConfig, payload, c.req.raw.headers);
5872
6287
  if (!upstreamResponse.ok) throw new HTTPError(`Failed to create ${provider} responses`, upstreamResponse);
5873
- const recordUsage = createProviderResponsesUsageRecorder(payload, provider);
6288
+ const recordUsage = createProviderResponsesUsageRecorder(payload, provider, modelConfig, providerConfig.pricingCurrency);
5874
6289
  if (payload.stream) return streamProviderResponses(c, getResponsesEvents(upstreamResponse), {
5875
6290
  normalizeCodex: false,
5876
6291
  provider,
@@ -5879,11 +6294,13 @@ async function handleProviderResponsesForProvider(c, options) {
5879
6294
  recordUsage(normalizeResponsesUsage((await upstreamResponse.clone().json()).usage));
5880
6295
  return createProviderProxyResponse(upstreamResponse);
5881
6296
  }
5882
- const createProviderResponsesUsageRecorder = (payload, provider) => {
6297
+ const createProviderResponsesUsageRecorder = (payload, provider, modelConfig, pricingCurrency) => {
5883
6298
  const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim() || null;
5884
6299
  return createProviderTokenUsageRecorder({
5885
6300
  endpoint: "responses",
5886
6301
  model: payload.model,
6302
+ pricing: modelConfig?.pricing,
6303
+ pricingCurrency,
5887
6304
  providerName: provider,
5888
6305
  sessionId: sessionAffinity ?? ""
5889
6306
  });
@@ -6068,7 +6485,10 @@ const handleResponses = async (c) => {
6068
6485
  for await (const chunk of response) {
6069
6486
  debugJson(logger$1, "Responses stream chunk:", chunk);
6070
6487
  const parsedEvent = parseResponsesStreamEvent(chunk);
6071
- if (parsedEvent?.type === "response.completed" || parsedEvent?.type === "response.failed" || parsedEvent?.type === "response.incomplete") usage = normalizeResponsesUsage(parsedEvent.response.usage);
6488
+ if (parsedEvent?.type === "response.completed" || parsedEvent?.type === "response.failed" || parsedEvent?.type === "response.incomplete") usage = {
6489
+ ...normalizeResponsesUsage(parsedEvent.response.usage),
6490
+ total_nano_aiu: normalizeOptionalToken(parsedEvent.copilot_usage?.total_nano_aiu)
6491
+ };
6072
6492
  const processedData = fixStreamIds(chunk.data ?? "", chunk.event, idTracker);
6073
6493
  await stream.writeSSE({
6074
6494
  id: chunk.id,
@@ -6083,8 +6503,12 @@ const handleResponses = async (c) => {
6083
6503
  value: response,
6084
6504
  tailLength: 400
6085
6505
  });
6086
- recordUsage(normalizeResponsesUsage(response.usage));
6087
- return c.json(response);
6506
+ const result = response;
6507
+ recordUsage({
6508
+ ...normalizeResponsesUsage(result.usage),
6509
+ total_nano_aiu: normalizeOptionalToken(result.copilot_usage?.total_nano_aiu)
6510
+ });
6511
+ return c.json(result);
6088
6512
  };
6089
6513
  const isAsyncIterable = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
6090
6514
  const isStreamingRequested = (payload) => Boolean(payload.stream);
@@ -6254,4 +6678,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
6254
6678
  //#endregion
6255
6679
  export { server };
6256
6680
 
6257
- //# sourceMappingURL=server-34rnRg4k.js.map
6681
+ //# sourceMappingURL=server-s_0_JChu.js.map