@omnicross/core 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/dist/ApiConverter.cjs +2 -2
  2. package/dist/ApiConverter.js +1 -1
  3. package/dist/{CompletionService-DdWaAxq0.d.ts → CompletionService-C3BX0Ovy.d.ts} +4 -3
  4. package/dist/{CompletionService-D3a47vdU.d.cts → CompletionService-kNA17cfI.d.cts} +4 -3
  5. package/dist/auth/GeminiCodeAssistProjectResolver.cjs +7 -5
  6. package/dist/auth/GeminiCodeAssistProjectResolver.js +5 -3
  7. package/dist/{chunk-HRRYG2AX.js → chunk-23S5OPIO.js} +10 -1
  8. package/dist/chunk-25GXJCEZ.cjs +40 -0
  9. package/dist/chunk-2JYRIADS.js +40 -0
  10. package/dist/chunk-3Z5BQZFM.cjs +52 -0
  11. package/dist/{chunk-4VAVYUZS.cjs → chunk-4VLRHHFE.cjs} +82 -40
  12. package/dist/{chunk-VACWOIRE.cjs → chunk-4VZX5F4T.cjs} +39 -15
  13. package/dist/{chunk-FTQ7IQUC.js → chunk-6CL5HTPQ.js} +52 -10
  14. package/dist/chunk-6QQG7YG3.cjs +153 -0
  15. package/dist/{chunk-SU2GCLAC.cjs → chunk-6VUQPOV3.cjs} +359 -60
  16. package/dist/chunk-7AK7B36B.cjs +44 -0
  17. package/dist/{chunk-5ZQBEOMD.js → chunk-AT7B7SOZ.js} +25 -2
  18. package/dist/{chunk-IK3KE3AU.js → chunk-DE2SRBFZ.js} +1 -1
  19. package/dist/chunk-DLXZEZWO.cjs +285 -0
  20. package/dist/{chunk-UHGVBFU3.js → chunk-EBLFCNPY.js} +38 -14
  21. package/dist/chunk-EJLH6NFT.cjs +166 -0
  22. package/dist/chunk-HVBBDBS3.cjs +350 -0
  23. package/dist/{chunk-7Y47UCF2.js → chunk-IFXJ2BG7.js} +5 -38
  24. package/dist/{chunk-46ZIFWYG.cjs → chunk-IIRPUVPH.cjs} +7 -1
  25. package/dist/{chunk-YGXJGRFI.cjs → chunk-KBM2W6UY.cjs} +111 -24
  26. package/dist/{chunk-OCEUVQHW.cjs → chunk-KCVJWJST.cjs} +73 -3
  27. package/dist/chunk-LB6EBS6Y.js +69 -0
  28. package/dist/{chunk-C7PSERMH.js → chunk-LVUCAWMK.js} +1 -1
  29. package/dist/chunk-MKF46GNZ.js +350 -0
  30. package/dist/{chunk-HYN75H6G.cjs → chunk-MUHWHQVM.cjs} +1 -1
  31. package/dist/{chunk-Z7Y2NPHY.js → chunk-N5T4M4GU.js} +1 -1
  32. package/dist/{chunk-TIER4SUC.js → chunk-OEWZUX4G.js} +346 -47
  33. package/dist/chunk-OF3BECU2.cjs +69 -0
  34. package/dist/{chunk-RSJ3TWJQ.cjs → chunk-OKMJLEXX.cjs} +5 -5
  35. package/dist/chunk-OPRS4IQZ.js +153 -0
  36. package/dist/{chunk-XK5CIRMQ.js → chunk-SIZQQD6T.js} +72 -2
  37. package/dist/{chunk-DKPXN34V.cjs → chunk-SQYVORNP.cjs} +1334 -510
  38. package/dist/chunk-SYQF7OIR.js +52 -0
  39. package/dist/{chunk-S4WBV25P.js → chunk-TOVPMMHM.js} +111 -24
  40. package/dist/{chunk-QYTT6GE3.js → chunk-UDCC7EMR.js} +1093 -269
  41. package/dist/{chunk-E3UCVHLP.cjs → chunk-UNWKG6EI.cjs} +5 -38
  42. package/dist/{chunk-ZLX3KGXX.js → chunk-UX7VWA46.js} +135 -23
  43. package/dist/{chunk-7YN22L2H.cjs → chunk-V4TXMEJS.cjs} +5 -5
  44. package/dist/chunk-VRFBB5DB.js +44 -0
  45. package/dist/chunk-WB6PH56J.js +166 -0
  46. package/dist/{chunk-G2UTNSGU.js → chunk-WNKWAEUR.js} +7 -1
  47. package/dist/{chunk-SUEGJ75D.cjs → chunk-WYTEQ4Q5.cjs} +25 -2
  48. package/dist/{chunk-5ENKBSWO.js → chunk-YJVHCHQX.js} +42 -6
  49. package/dist/{chunk-VQY5W4YW.cjs → chunk-YK57NMIF.cjs} +44 -8
  50. package/dist/{chunk-KFI44N2R.cjs → chunk-YNJNTCSQ.cjs} +10 -1
  51. package/dist/completion/ApiKeyPoolService.cjs +12 -0
  52. package/dist/completion/ApiKeyPoolService.d.cts +6 -0
  53. package/dist/completion/ApiKeyPoolService.d.ts +6 -0
  54. package/dist/completion/ApiKeyPoolService.js +12 -0
  55. package/dist/completion/CompletionService.cjs +27 -24
  56. package/dist/completion/CompletionService.d.cts +2 -1
  57. package/dist/completion/CompletionService.d.ts +2 -1
  58. package/dist/completion/CompletionService.js +26 -23
  59. package/dist/completion.cjs +27 -24
  60. package/dist/completion.d.cts +2 -1
  61. package/dist/completion.d.ts +2 -1
  62. package/dist/completion.js +26 -23
  63. package/dist/index.cjs +53 -29
  64. package/dist/index.d.cts +8 -8
  65. package/dist/index.d.ts +8 -8
  66. package/dist/index.js +56 -32
  67. package/dist/{keyPolicy-Cp0FntZl.d.cts → keyPolicy-zdWTu3CQ.d.cts} +1 -1
  68. package/dist/{keyPolicy-Cp0FntZl.d.ts → keyPolicy-zdWTu3CQ.d.ts} +1 -1
  69. package/dist/outbound-api/auditCapture.cjs +2 -2
  70. package/dist/outbound-api/auditCapture.d.cts +5 -3
  71. package/dist/outbound-api/auditCapture.d.ts +5 -3
  72. package/dist/outbound-api/auditCapture.js +1 -1
  73. package/dist/outbound-api/quotaWarn.d.cts +1 -1
  74. package/dist/outbound-api/quotaWarn.d.ts +1 -1
  75. package/dist/outbound-api/routeResolver.cjs +2 -2
  76. package/dist/outbound-api/routeResolver.d.cts +4 -3
  77. package/dist/outbound-api/routeResolver.d.ts +4 -3
  78. package/dist/outbound-api/routeResolver.js +1 -1
  79. package/dist/outbound-api/subscriptionRegistryPort.d.cts +1 -0
  80. package/dist/outbound-api/subscriptionRegistryPort.d.ts +1 -0
  81. package/dist/outbound-api/types.d.cts +3 -2
  82. package/dist/outbound-api/types.d.ts +3 -2
  83. package/dist/outbound-api.cjs +49 -25
  84. package/dist/outbound-api.d.cts +127 -85
  85. package/dist/outbound-api.d.ts +127 -85
  86. package/dist/outbound-api.js +52 -28
  87. package/dist/pipeline/AccountAllowanceScheduling.cjs +15 -0
  88. package/dist/pipeline/AccountAllowanceScheduling.d.cts +90 -0
  89. package/dist/pipeline/AccountAllowanceScheduling.d.ts +90 -0
  90. package/dist/pipeline/AccountAllowanceScheduling.js +15 -0
  91. package/dist/pipeline/AccountAllowanceStore.cjs +20 -0
  92. package/dist/pipeline/AccountAllowanceStore.d.cts +77 -0
  93. package/dist/pipeline/AccountAllowanceStore.d.ts +77 -0
  94. package/dist/pipeline/AccountAllowanceStore.js +20 -0
  95. package/dist/pipeline/AuthSource.d.cts +11 -2
  96. package/dist/pipeline/AuthSource.d.ts +11 -2
  97. package/dist/pipeline/BoundAccountSelectionError.cjs +10 -0
  98. package/dist/pipeline/BoundAccountSelectionError.d.cts +28 -0
  99. package/dist/pipeline/BoundAccountSelectionError.d.ts +28 -0
  100. package/dist/pipeline/BoundAccountSelectionError.js +10 -0
  101. package/dist/pipeline/LlmConfigProviderAuth.d.cts +1 -0
  102. package/dist/pipeline/LlmConfigProviderAuth.d.ts +1 -0
  103. package/dist/pipeline/SubscriptionAccountHealth.cjs +4 -2
  104. package/dist/pipeline/SubscriptionAccountHealth.d.cts +34 -6
  105. package/dist/pipeline/SubscriptionAccountHealth.d.ts +34 -6
  106. package/dist/pipeline/SubscriptionAccountHealth.js +3 -1
  107. package/dist/pipeline/SubscriptionAuthSource.cjs +5 -2
  108. package/dist/pipeline/SubscriptionAuthSource.d.cts +1 -0
  109. package/dist/pipeline/SubscriptionAuthSource.d.ts +1 -0
  110. package/dist/pipeline/SubscriptionAuthSource.js +4 -1
  111. package/dist/pipeline/SubscriptionAuthStrategy.d.cts +8 -3
  112. package/dist/pipeline/SubscriptionAuthStrategy.d.ts +8 -3
  113. package/dist/pipeline/executeProviderCall.cjs +2 -2
  114. package/dist/pipeline/executeProviderCall.d.cts +8 -3
  115. package/dist/pipeline/executeProviderCall.d.ts +8 -3
  116. package/dist/pipeline/executeProviderCall.js +1 -1
  117. package/dist/pipeline/resolveProviderChain.d.cts +2 -3
  118. package/dist/pipeline/resolveProviderChain.d.ts +2 -3
  119. package/dist/pipeline/resolveSubscriptionChain.d.cts +1 -0
  120. package/dist/pipeline/resolveSubscriptionChain.d.ts +1 -0
  121. package/dist/pipeline/upstreamFetch.cjs +4 -2
  122. package/dist/pipeline/upstreamFetch.d.cts +6 -0
  123. package/dist/pipeline/upstreamFetch.d.ts +6 -0
  124. package/dist/pipeline/upstreamFetch.js +3 -1
  125. package/dist/pipeline/upstreamTrace.cjs +14 -0
  126. package/dist/pipeline/upstreamTrace.d.cts +85 -0
  127. package/dist/pipeline/upstreamTrace.d.ts +85 -0
  128. package/dist/pipeline/upstreamTrace.js +14 -0
  129. package/dist/ports/pricing-store.d.cts +4 -3
  130. package/dist/ports/pricing-store.d.ts +4 -3
  131. package/dist/ports.d.cts +4 -3
  132. package/dist/ports.d.ts +4 -3
  133. package/dist/provider-proxy/ProviderProxy.cjs +27 -24
  134. package/dist/provider-proxy/ProviderProxy.d.cts +1 -0
  135. package/dist/provider-proxy/ProviderProxy.d.ts +1 -0
  136. package/dist/provider-proxy/ProviderProxy.js +26 -23
  137. package/dist/provider-proxy/ingress/providerProxyShared.cjs +31 -24
  138. package/dist/provider-proxy/ingress/providerProxyShared.d.cts +50 -5
  139. package/dist/provider-proxy/ingress/providerProxyShared.d.ts +50 -5
  140. package/dist/provider-proxy/ingress/providerProxyShared.js +30 -23
  141. package/dist/provider-proxy/matchText.cjs +4 -2
  142. package/dist/provider-proxy/matchText.d.cts +41 -6
  143. package/dist/provider-proxy/matchText.d.ts +41 -6
  144. package/dist/provider-proxy/matchText.js +3 -1
  145. package/dist/provider-proxy/types.d.cts +30 -2
  146. package/dist/provider-proxy/types.d.ts +30 -2
  147. package/dist/provider-proxy.cjs +27 -24
  148. package/dist/provider-proxy.d.cts +1 -0
  149. package/dist/provider-proxy.d.ts +1 -0
  150. package/dist/provider-proxy.js +26 -23
  151. package/dist/{routeResolver-C4T7yClx.d.ts → routeResolver-CHTv_Bs5.d.ts} +1 -1
  152. package/dist/{routeResolver-CB3tvJKy.d.cts → routeResolver-kepxnfcC.d.cts} +1 -1
  153. package/dist/transformer/TransformerChainExecutor.cjs +2 -2
  154. package/dist/transformer/TransformerChainExecutor.js +1 -1
  155. package/dist/transformer/transformers/AnthropicTransformer.cjs +2 -2
  156. package/dist/transformer/transformers/AnthropicTransformer.js +1 -1
  157. package/dist/transformer/transformers/GeminiCodeAssistTransformer.cjs +3 -3
  158. package/dist/transformer/transformers/GeminiCodeAssistTransformer.js +2 -2
  159. package/dist/transformer/transformers/GeminiTransformer.cjs +3 -3
  160. package/dist/transformer/transformers/GeminiTransformer.js +2 -2
  161. package/dist/transformer/transformers/OpenAIResponseTransformer.cjs +2 -2
  162. package/dist/transformer/transformers/OpenAIResponseTransformer.d.cts +1 -1
  163. package/dist/transformer/transformers/OpenAIResponseTransformer.d.ts +1 -1
  164. package/dist/transformer/transformers/OpenAIResponseTransformer.js +1 -1
  165. package/dist/transformer/transformers/OpenAITransformer.cjs +6 -0
  166. package/dist/transformer/transformers/OpenAITransformer.d.cts +68 -0
  167. package/dist/transformer/transformers/OpenAITransformer.d.ts +68 -0
  168. package/dist/transformer/transformers/OpenAITransformer.js +6 -0
  169. package/dist/transformer/transformers.cjs +8 -14
  170. package/dist/transformer/transformers.d.cts +12 -48
  171. package/dist/transformer/transformers.d.ts +12 -48
  172. package/dist/transformer/transformers.js +11 -17
  173. package/dist/transformer/types.d.cts +17 -0
  174. package/dist/transformer/types.d.ts +17 -0
  175. package/dist/transformer.cjs +11 -12
  176. package/dist/transformer.d.cts +1 -2
  177. package/dist/transformer.d.ts +1 -2
  178. package/dist/transformer.js +11 -12
  179. package/dist/{types-B8Arpxn0.d.cts → types-H3wGZSOo.d.cts} +134 -7
  180. package/dist/{types-lgPYQuZS.d.ts → types-hsoKrVQS.d.ts} +134 -7
  181. package/dist/usage/pricing-engine.cjs +2 -2
  182. package/dist/usage/pricing-engine.d.cts +9 -1
  183. package/dist/usage/pricing-engine.d.ts +9 -1
  184. package/dist/usage/pricing-engine.js +1 -1
  185. package/dist/usage.cjs +2 -2
  186. package/dist/usage.js +1 -1
  187. package/package.json +1 -1
  188. package/dist/chunk-C72S6TTV.cjs +0 -49
  189. package/dist/chunk-DWKQZ2SK.cjs +0 -254
  190. package/dist/chunk-IAXWICZU.js +0 -69
  191. package/dist/chunk-QWBWGODS.cjs +0 -69
  192. package/dist/chunk-RGTR7CIO.cjs +0 -235
  193. package/dist/chunk-VPBAUQ2M.js +0 -235
  194. package/dist/chunk-YMFAJFQT.cjs +0 -173
  195. package/dist/chunk-Z7LUNECE.js +0 -49
  196. package/dist/chunk-ZJZAOKI7.js +0 -254
  197. package/dist/transformer/transformers/OpenCodeGoTransformer.cjs +0 -6
  198. package/dist/transformer/transformers/OpenCodeGoTransformer.d.cts +0 -51
  199. package/dist/transformer/transformers/OpenCodeGoTransformer.d.ts +0 -51
  200. package/dist/transformer/transformers/OpenCodeGoTransformer.js +0 -6
  201. package/dist/transformer/transformers/ReasoningTransformer.cjs +0 -6
  202. package/dist/transformer/transformers/ReasoningTransformer.d.cts +0 -47
  203. package/dist/transformer/transformers/ReasoningTransformer.d.ts +0 -47
  204. package/dist/transformer/transformers/ReasoningTransformer.js +0 -6
@@ -11,11 +11,6 @@ var _chunkXSVNC2UDcjs = require('./chunk-XSVNC2UD.cjs');
11
11
 
12
12
  // src/outbound-api/auditCapture.ts
13
13
  var _crypto = require('crypto');
14
- function isStreamingContentType(res) {
15
- const ct = res.getHeader("content-type");
16
- const value = Array.isArray(ct) ? ct.join(";") : String(_nullishCoalesce(ct, () => ( "")));
17
- return value.toLowerCase().includes("text/event-stream");
18
- }
19
14
  function resolveClientIp(req, trustForwardedFor) {
20
15
  if (trustForwardedFor) {
21
16
  const xff = req.headers["x-forwarded-for"];
@@ -27,18 +22,11 @@ function resolveClientIp(req, trustForwardedFor) {
27
22
  }
28
23
  return _nullishCoalesce(_optionalChain([req, 'access', _6 => _6.socket, 'optionalAccess', _7 => _7.remoteAddress]), () => ( void 0));
29
24
  }
30
- function truncateToBytes(text, maxBytes) {
31
- const buf = Buffer.from(text, "utf8");
32
- if (buf.length <= maxBytes) return text;
33
- return buf.subarray(0, maxBytes).toString("utf8");
34
- }
35
25
  function beginAuditCapture(req, res, now) {
36
26
  const config = _chunkAEOZIDEBcjs.getAuditCaptureConfig.call(void 0, );
37
27
  if (!config) return null;
38
28
  let rawRequestBody;
39
29
  const responseChunks = [];
40
- let responseBytes = 0;
41
- let streamDetected = false;
42
30
  let finished = false;
43
31
  const ctx = {
44
32
  setRequestBody(raw) {
@@ -46,19 +34,9 @@ function beginAuditCapture(req, res, now) {
46
34
  }
47
35
  };
48
36
  if (config.captureBodies) {
49
- installResponseCapture(res, config.maxBodyBytes, {
37
+ installResponseCapture(res, {
50
38
  push(chunk) {
51
- responseBytes += chunk.length;
52
39
  responseChunks.push(chunk);
53
- },
54
- get bytes() {
55
- return responseBytes;
56
- },
57
- markStream() {
58
- streamDetected = true;
59
- },
60
- get isStream() {
61
- return streamDetected;
62
40
  }
63
41
  });
64
42
  }
@@ -92,9 +70,9 @@ function beginAuditCapture(req, res, now) {
92
70
  if (ctx.error) record.error = _chunkFCR77GYMcjs.redactAuditText.call(void 0, ctx.error);
93
71
  if (config.captureBodies) {
94
72
  if (rawRequestBody != null && rawRequestBody.length > 0) {
95
- record.requestBody = _chunkFCR77GYMcjs.redactAuditText.call(void 0, truncateToBytes(rawRequestBody, config.maxBodyBytes));
73
+ record.requestBody = _chunkFCR77GYMcjs.redactAuditText.call(void 0, rawRequestBody);
96
74
  }
97
- if (!streamDetected && responseChunks.length > 0) {
75
+ if (responseChunks.length > 0) {
98
76
  const body = Buffer.concat(responseChunks).toString("utf8");
99
77
  record.responseBody = _chunkFCR77GYMcjs.redactAuditText.call(void 0, body);
100
78
  }
@@ -106,24 +84,13 @@ function beginAuditCapture(req, res, now) {
106
84
  res.once("close", finalize);
107
85
  return ctx;
108
86
  }
109
- function installResponseCapture(res, maxBodyBytes, sink) {
110
- let contentTypeChecked = false;
87
+ function installResponseCapture(res, sink) {
111
88
  const capture = (chunk) => {
112
- if (sink.isStream) return;
113
- if (!contentTypeChecked) {
114
- contentTypeChecked = true;
115
- if (isStreamingContentType(res)) {
116
- sink.markStream();
117
- return;
118
- }
119
- }
120
- if (sink.bytes >= maxBodyBytes) return;
121
89
  let buf = null;
122
90
  if (Buffer.isBuffer(chunk)) buf = chunk;
123
91
  else if (typeof chunk === "string") buf = Buffer.from(chunk, "utf8");
124
92
  if (!buf || buf.length === 0) return;
125
- const remaining = maxBodyBytes - sink.bytes;
126
- sink.push(buf.length > remaining ? buf.subarray(0, remaining) : buf);
93
+ sink.push(buf);
127
94
  };
128
95
  const originalWrite = res.write.bind(res);
129
96
  const originalEnd = res.end.bind(res);
@@ -1,5 +1,8 @@
1
1
  // src/usage/pricing-engine.ts
2
- import { DEFAULT_LITELLM_PRICING_URL } from "@omnicross/contracts/pricing-types";
2
+ import {
3
+ DEFAULT_LITELLM_PRICING_URL,
4
+ DEFAULT_OPENROUTER_PRICING_URL
5
+ } from "@omnicross/contracts/pricing-types";
3
6
  var PricingEngine = class {
4
7
  constructor(store, logger, options = {}) {
5
8
  this.store = store;
@@ -19,6 +22,8 @@ var PricingEngine = class {
19
22
  */
20
23
  cacheByModel = /* @__PURE__ */ new Map();
21
24
  cacheLoaded = false;
25
+ /** Coalesce manual and background refreshes to prevent a catalog stampede. */
26
+ refreshInFlight = null;
22
27
  // ===== Cache management =====
23
28
  /** Lazy-load and cache the full pricing table. */
24
29
  async ensureCache() {
@@ -27,7 +32,7 @@ var PricingEngine = class {
27
32
  this.cache = new Map(rows.map((r) => [keyOf(r.providerId, r.modelId), r]));
28
33
  this.cacheByModel = /* @__PURE__ */ new Map();
29
34
  for (const r of rows) {
30
- if (r.providerId === "*") continue;
35
+ if (r.providerId === "*" || r.providerId === "openrouter" || r.source === "openrouter") continue;
31
36
  if (!this.cacheByModel.has(r.modelId)) this.cacheByModel.set(r.modelId, r);
32
37
  }
33
38
  this.cacheLoaded = true;
@@ -90,40 +95,82 @@ var PricingEngine = class {
90
95
  true
91
96
  );
92
97
  this.cache.set(keyOf(updated.providerId, updated.modelId), updated);
93
- if (updated.providerId !== "*") this.cacheByModel.set(updated.modelId, updated);
98
+ if (updated.providerId !== "*" && updated.providerId !== "openrouter" && updated.source !== "openrouter") {
99
+ this.cacheByModel.set(updated.modelId, updated);
100
+ }
94
101
  return updated;
95
102
  }
96
103
  // ===== Source refresh =====
97
104
  async fetchLatestFromSource() {
105
+ if (this.refreshInFlight) return this.refreshInFlight;
106
+ const refresh = this.refreshSources();
107
+ this.refreshInFlight = refresh;
108
+ try {
109
+ return await refresh;
110
+ } finally {
111
+ if (this.refreshInFlight === refresh) this.refreshInFlight = null;
112
+ }
113
+ }
114
+ async refreshSources() {
98
115
  const sourceUrl = this.options.sourceUrl ?? DEFAULT_LITELLM_PRICING_URL;
116
+ const openRouterSourceUrl = this.options.openRouterSourceUrl ?? DEFAULT_OPENROUTER_PRICING_URL;
99
117
  const fetchImpl = this.options.fetchImpl ?? fetch;
118
+ const timeoutMs = this.options.fetchTimeoutMs ?? 1e4;
100
119
  const fetchedAt = Date.now();
101
- let json;
102
- try {
103
- const res = await fetchImpl(sourceUrl);
104
- if (!res.ok) {
105
- throw new Error(`Pricing-source fetch failed with status ${res.status}`);
120
+ const [litellm, openrouter] = await Promise.all([
121
+ fetchCatalog(fetchImpl, sourceUrl, timeoutMs, parseLitellmJson),
122
+ fetchCatalog(fetchImpl, openRouterSourceUrl, timeoutMs, parseOpenRouterJson)
123
+ ]);
124
+ const sources = [
125
+ sourceResult("litellm", sourceUrl, litellm),
126
+ sourceResult("openrouter", openRouterSourceUrl, openrouter)
127
+ ];
128
+ for (const source of sources) {
129
+ if (source.status === "failed") {
130
+ this.logger.warn(`[PricingEngine] ${source.source} pricing-source fetch failed`, {
131
+ error: source.error
132
+ });
106
133
  }
107
- json = await res.json();
108
- } catch (e) {
109
- this.logger.warn("[PricingEngine] pricing-source fetch failed", {
110
- error: e instanceof Error ? e.message : String(e)
134
+ }
135
+ if (!litellm.ok && !openrouter.ok) {
136
+ throw new Error(
137
+ `Pricing-source fetch failed: LiteLLM (${litellm.error}); OpenRouter (${openrouter.error})`
138
+ );
139
+ }
140
+ const applied = [];
141
+ const conflicts = [];
142
+ if (litellm.ok) {
143
+ const result = await this.store.bulkApplyFromSource(litellm.entries, "litellm");
144
+ applied.push(...result.applied);
145
+ conflicts.push(
146
+ ...result.conflicts.map((c) => ({
147
+ providerId: c.current.providerId,
148
+ modelId: c.current.modelId,
149
+ current: c.current,
150
+ incoming: c.incoming
151
+ }))
152
+ );
153
+ sources[0].appliedCount = result.applied.length;
154
+ }
155
+ if (openrouter.ok) {
156
+ const current = new Map(
157
+ (await this.store.getAll()).map((row) => [keyOf(row.providerId, row.modelId), row])
158
+ );
159
+ const supplemental = openrouter.entries.filter((incoming) => {
160
+ const row = current.get(keyOf(incoming.providerId, incoming.modelId));
161
+ return !row || row.source === "openrouter" || row.source === "builtin";
111
162
  });
112
- throw e;
163
+ const result = await this.store.bulkApplyFromSource(supplemental, "openrouter");
164
+ applied.push(...result.applied);
165
+ sources[1].appliedCount = result.applied.length;
113
166
  }
114
- const incoming = parseLitellmJson(json);
115
- const { applied, conflicts } = await this.store.bulkApplyFromSource(incoming);
116
- await this.invalidateCache();
167
+ if (applied.length > 0) await this.invalidateCache();
117
168
  return {
118
169
  applied,
119
- conflicts: conflicts.map((c) => ({
120
- providerId: c.current.providerId,
121
- modelId: c.current.modelId,
122
- current: c.current,
123
- incoming: c.incoming
124
- })),
170
+ conflicts,
125
171
  fetchedAt,
126
- sourceUrl
172
+ sourceUrl,
173
+ sources
127
174
  };
128
175
  }
129
176
  // ===== Conflict resolution =====
@@ -147,8 +194,12 @@ var PROVIDER_ALIASES = {
147
194
  azure: "openai"
148
195
  };
149
196
  var parseLitellmJson = (json) => {
197
+ if (!json || typeof json !== "object" || Array.isArray(json)) {
198
+ throw new Error("invalid LiteLLM catalog shape");
199
+ }
150
200
  const out = [];
151
201
  for (const [modelId, entry] of Object.entries(json)) {
202
+ if (modelId === "sample_spec") continue;
152
203
  if (!entry || typeof entry !== "object") continue;
153
204
  if (entry.input_cost_per_token == null || entry.output_cost_per_token == null) {
154
205
  continue;
@@ -167,6 +218,67 @@ var parseLitellmJson = (json) => {
167
218
  }
168
219
  return out;
169
220
  };
221
+ var parseOpenRouterJson = (json) => {
222
+ if (!json || !Array.isArray(json.data)) {
223
+ throw new Error("invalid OpenRouter catalog shape");
224
+ }
225
+ const out = [];
226
+ for (const model of json.data) {
227
+ const modelId = typeof model?.id === "string" ? model.id.trim() : "";
228
+ const input = parseUnitPrice(model?.pricing?.prompt);
229
+ const output = parseUnitPrice(model?.pricing?.completion);
230
+ if (!modelId || input === null || output === null) continue;
231
+ const cacheRead = parseUnitPrice(model.pricing?.input_cache_read);
232
+ const cacheWrite = parseUnitPrice(model.pricing?.input_cache_write);
233
+ out.push({
234
+ providerId: "openrouter",
235
+ modelId,
236
+ inputPricePer1m: input * 1e6,
237
+ outputPricePer1m: output * 1e6,
238
+ cacheReadPricePer1m: cacheRead === null ? null : cacheRead * 1e6,
239
+ cacheWritePricePer1m: cacheWrite === null ? null : cacheWrite * 1e6
240
+ });
241
+ }
242
+ return out;
243
+ };
244
+ function parseUnitPrice(value) {
245
+ if (value === "" || value === null || value === void 0) return null;
246
+ const parsed = typeof value === "number" ? value : Number(value);
247
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
248
+ }
249
+ async function fetchCatalog(fetchImpl, url, timeoutMs, parse) {
250
+ const controller = new AbortController();
251
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
252
+ timer.unref?.();
253
+ try {
254
+ const response = await fetchImpl(url, { signal: controller.signal });
255
+ if (!response.ok) throw new Error(`status ${response.status}`);
256
+ const entries = parse(await response.json());
257
+ if (entries.length === 0) throw new Error("pricing catalog contained no usable entries");
258
+ return { ok: true, entries };
259
+ } catch (error) {
260
+ const message = controller.signal.aborted ? `timeout after ${timeoutMs}ms` : error instanceof Error ? error.message : String(error);
261
+ return { ok: false, error: message };
262
+ } finally {
263
+ clearTimeout(timer);
264
+ }
265
+ }
266
+ function sourceResult(source, sourceUrl, attempt) {
267
+ return attempt.ok ? {
268
+ source,
269
+ sourceUrl,
270
+ status: "applied",
271
+ parsedCount: attempt.entries.length,
272
+ appliedCount: 0
273
+ } : {
274
+ source,
275
+ sourceUrl,
276
+ status: "failed",
277
+ parsedCount: 0,
278
+ appliedCount: 0,
279
+ error: attempt.error
280
+ };
281
+ }
170
282
 
171
283
  export {
172
284
  PricingEngine
@@ -3,7 +3,7 @@
3
3
 
4
4
 
5
5
 
6
- var _chunkYGXJGRFIcjs = require('./chunk-YGXJGRFI.cjs');
6
+ var _chunkKBM2W6UYcjs = require('./chunk-KBM2W6UY.cjs');
7
7
 
8
8
  // src/transformer/transformers/GeminiTransformer.ts
9
9
  var GeminiTransformer = (_class = class {
@@ -44,7 +44,7 @@ var GeminiTransformer = (_class = class {
44
44
  * Also builds the correct URL for the Gemini API
45
45
  */
46
46
  async transformRequestIn(request, provider, _context) {
47
- const body = _chunkYGXJGRFIcjs.buildRequestBody.call(void 0, request);
47
+ const body = _chunkKBM2W6UYcjs.buildRequestBody.call(void 0, request);
48
48
  const action = request.stream ? "streamGenerateContent?alt=sse" : "generateContent";
49
49
  const url = new URL(`./${request.model}:${action}`, provider.baseUrl);
50
50
  const headers = {};
@@ -65,20 +65,20 @@ var GeminiTransformer = (_class = class {
65
65
  * (For requests coming into the Gemini endpoint)
66
66
  */
67
67
  async transformRequestOut(request, _context) {
68
- return _chunkYGXJGRFIcjs.transformRequestOut.call(void 0, request);
68
+ return _chunkKBM2W6UYcjs.transformRequestOut.call(void 0, request);
69
69
  }
70
70
  /**
71
71
  * Transform Gemini response to OpenAI-compatible format
72
72
  */
73
73
  async transformResponseOut(response, _context) {
74
- return _chunkYGXJGRFIcjs.transformResponseOut.call(void 0, response, this.name, this.logger);
74
+ return _chunkKBM2W6UYcjs.transformResponseOut.call(void 0, response, this.name, this.logger);
75
75
  }
76
76
  /**
77
77
  * Transform OpenAI-compatible response back to Gemini format
78
78
  * (For endpoint mode — returning Gemini-format responses to the client)
79
79
  */
80
80
  async transformResponseIn(response, _context) {
81
- return _chunkYGXJGRFIcjs.transformResponseIn.call(void 0, response, this.logger);
81
+ return _chunkKBM2W6UYcjs.transformResponseIn.call(void 0, response, this.logger);
82
82
  }
83
83
  }, _class.__initStatic(), _class);
84
84
 
@@ -0,0 +1,44 @@
1
+ import {
2
+ GeminiTransformer
3
+ } from "./chunk-LVUCAWMK.js";
4
+ import {
5
+ OpenAIResponseTransformer
6
+ } from "./chunk-OEWZUX4G.js";
7
+ import {
8
+ OpenAITransformer
9
+ } from "./chunk-LB6EBS6Y.js";
10
+ import {
11
+ AnthropicTransformer
12
+ } from "./chunk-6CL5HTPQ.js";
13
+ import {
14
+ GeminiCodeAssistTransformer
15
+ } from "./chunk-N5T4M4GU.js";
16
+
17
+ // src/transformer/transformers/index.ts
18
+ var BuiltinTransformers = {
19
+ OpenAITransformer,
20
+ GeminiTransformer,
21
+ GeminiCodeAssistTransformer,
22
+ AnthropicTransformer,
23
+ OpenAIResponseTransformer
24
+ };
25
+ function getBuiltinTransformers() {
26
+ return BuiltinTransformers;
27
+ }
28
+ var BUILTIN_TRANSFORMER_NAMES = [
29
+ "openai",
30
+ "gemini",
31
+ "gemini-code-assist",
32
+ "anthropic",
33
+ "openai-response"
34
+ ];
35
+ async function registerBuiltinTransformers(service) {
36
+ await service.initialize(getBuiltinTransformers());
37
+ }
38
+
39
+ export {
40
+ BuiltinTransformers,
41
+ getBuiltinTransformers,
42
+ BUILTIN_TRANSFORMER_NAMES,
43
+ registerBuiltinTransformers
44
+ };
@@ -0,0 +1,166 @@
1
+ // src/provider-proxy/matchText.ts
2
+ var MATCH_TEXT_PER_MESSAGE_CAP = 8192;
3
+ var MATCH_TEXT_RECENT_MESSAGES = 6;
4
+ function flattenMatchText(value) {
5
+ if (typeof value === "string") return value;
6
+ if (Array.isArray(value)) {
7
+ const parts = [];
8
+ for (const item of value) {
9
+ const text = flattenMatchText(item);
10
+ if (text) parts.push(text);
11
+ }
12
+ return parts.join("\n");
13
+ }
14
+ if (value && typeof value === "object") {
15
+ const obj = value;
16
+ if (obj.type === "tool_result" && obj.content !== void 0) {
17
+ return flattenMatchText(obj.content);
18
+ }
19
+ if (typeof obj.text === "string") return obj.text;
20
+ }
21
+ return "";
22
+ }
23
+ function collectMatchText(anthropicBody) {
24
+ const messages = Array.isArray(anthropicBody.messages) ? anthropicBody.messages : [];
25
+ const slices = [];
26
+ const sys = flattenMatchText(anthropicBody.system).trim();
27
+ if (sys) slices.push(sys.slice(0, MATCH_TEXT_PER_MESSAGE_CAP));
28
+ const recent = [];
29
+ for (let i = messages.length - 1; i >= 0 && recent.length < MATCH_TEXT_RECENT_MESSAGES; i--) {
30
+ const message = messages[i];
31
+ if (!message || typeof message !== "object") continue;
32
+ const role = message.role;
33
+ if (role !== "user" && role !== "system") continue;
34
+ const text = flattenMatchText(message.content).trim();
35
+ if (text) recent.push(text.slice(0, MATCH_TEXT_PER_MESSAGE_CAP));
36
+ }
37
+ for (let i = recent.length - 1; i >= 0; i--) slices.push(recent[i]);
38
+ return slices;
39
+ }
40
+ function deriveSubscriptionSessionKey(anthropicBody) {
41
+ const system = flattenMatchText(anthropicBody.system).trim().slice(0, MATCH_TEXT_PER_MESSAGE_CAP);
42
+ const messages = Array.isArray(anthropicBody.messages) ? anthropicBody.messages : [];
43
+ let firstUser = "";
44
+ for (const message of messages) {
45
+ if (message && typeof message === "object" && message.role === "user") {
46
+ firstUser = flattenMatchText(message.content).trim().slice(0, MATCH_TEXT_PER_MESSAGE_CAP);
47
+ break;
48
+ }
49
+ }
50
+ const anchor = `${system}\0${firstUser}`;
51
+ if (!system && !firstUser) return void 0;
52
+ return fnv1a(anchor);
53
+ }
54
+ var SESSION_ID_CAP = 1024;
55
+ function deriveGatewaySessionKey(body, headers = {}, options = {}) {
56
+ const sessionHeader = firstHeaderValue(headers, ["session-id", "session_id", "x-session-id"]);
57
+ if (sessionHeader) {
58
+ return { key: fnv1a(sessionHeader), source: "session-header" };
59
+ }
60
+ const threadHeader = firstHeaderValue(headers, ["thread-id", "thread_id", "x-thread-id"]);
61
+ if (threadHeader) {
62
+ return { key: fnv1a(threadHeader), source: "thread-header" };
63
+ }
64
+ const bodySessionId = scalarSessionValue(body.session_id);
65
+ if (bodySessionId) {
66
+ return { key: fnv1a(bodySessionId), source: "body-session-id" };
67
+ }
68
+ const bodyThreadId = scalarSessionValue(body.thread_id ?? body.threadId);
69
+ if (bodyThreadId) {
70
+ return { key: fnv1a(bodyThreadId), source: "body-thread-id" };
71
+ }
72
+ const conversationId = scalarSessionValue(body.conversation_id);
73
+ if (conversationId) {
74
+ return { key: fnv1a(conversationId), source: "body-session-id" };
75
+ }
76
+ const promptCacheKey = scalarSessionValue(body.prompt_cache_key);
77
+ if (promptCacheKey) {
78
+ return { key: fnv1a(promptCacheKey), source: "prompt-cache-key" };
79
+ }
80
+ const fingerprint = deriveResponsesContentFingerprint(body);
81
+ if (fingerprint) {
82
+ return { key: fnv1a(fingerprint), source: "content-fingerprint" };
83
+ }
84
+ const endpoint = options.endpoint?.trim() || "responses";
85
+ const fallback = options.fallbackKey?.trim() || "anonymous";
86
+ return {
87
+ key: fnv1a(`${endpoint}\0${fallback}`),
88
+ source: "api-key-fallback"
89
+ };
90
+ }
91
+ function firstHeaderValue(headers, names) {
92
+ const byLower = /* @__PURE__ */ new Map();
93
+ for (const [name, value] of Object.entries(headers)) {
94
+ if (typeof value === "string" || Array.isArray(value)) {
95
+ const lower = name.toLowerCase();
96
+ if (!byLower.has(lower)) byLower.set(lower, value);
97
+ }
98
+ }
99
+ for (const name of names) {
100
+ const value = byLower.get(name);
101
+ const values = Array.isArray(value) ? value : [value];
102
+ for (const item of values) {
103
+ if (typeof item !== "string") continue;
104
+ const trimmed = item.trim();
105
+ if (trimmed) return trimmed.slice(0, SESSION_ID_CAP);
106
+ }
107
+ }
108
+ return void 0;
109
+ }
110
+ function scalarSessionValue(value) {
111
+ if (typeof value !== "string" && typeof value !== "number") return void 0;
112
+ const trimmed = String(value).trim();
113
+ return trimmed ? trimmed.slice(0, SESSION_ID_CAP) : void 0;
114
+ }
115
+ function deriveResponsesContentFingerprint(body) {
116
+ const stable = [];
117
+ const instructions = flattenMatchText(body.instructions).trim();
118
+ if (instructions) stable.push(instructions.slice(0, MATCH_TEXT_PER_MESSAGE_CAP));
119
+ const input = body.input;
120
+ let firstUser = "";
121
+ const visitItem = (item) => {
122
+ if (typeof item === "string") {
123
+ if (!firstUser) firstUser = item.trim().slice(0, MATCH_TEXT_PER_MESSAGE_CAP);
124
+ return;
125
+ }
126
+ if (!item || typeof item !== "object") return;
127
+ const record = item;
128
+ const role = typeof record.role === "string" ? record.role.toLowerCase() : "";
129
+ const content = record.content ?? record.text ?? record.input;
130
+ const text = flattenMatchText(content !== void 0 ? content : record).trim();
131
+ if (!text) return;
132
+ if (role === "developer" || role === "system") {
133
+ stable.push(text.slice(0, MATCH_TEXT_PER_MESSAGE_CAP));
134
+ } else if (role === "user" || !role) {
135
+ if (!firstUser) firstUser = text.slice(0, MATCH_TEXT_PER_MESSAGE_CAP);
136
+ }
137
+ };
138
+ if (Array.isArray(input)) {
139
+ for (const item of input) visitItem(item);
140
+ } else if (input !== void 0) {
141
+ visitItem(input);
142
+ }
143
+ if (stable.length === 0) {
144
+ const legacySystem = flattenMatchText(body.system).trim();
145
+ if (legacySystem) stable.push(legacySystem.slice(0, MATCH_TEXT_PER_MESSAGE_CAP));
146
+ }
147
+ if (stable.length === 0 && !firstUser) return void 0;
148
+ return `${stable.join("\0")}\0${firstUser}`;
149
+ }
150
+ function fnv1a(str) {
151
+ let h = 2166136261;
152
+ for (let i = 0; i < str.length; i++) {
153
+ h ^= str.charCodeAt(i);
154
+ h = Math.imul(h, 16777619);
155
+ }
156
+ return (h >>> 0).toString(16).padStart(8, "0");
157
+ }
158
+
159
+ export {
160
+ MATCH_TEXT_PER_MESSAGE_CAP,
161
+ MATCH_TEXT_RECENT_MESSAGES,
162
+ flattenMatchText,
163
+ collectMatchText,
164
+ deriveSubscriptionSessionKey,
165
+ deriveGatewaySessionKey
166
+ };
@@ -134,7 +134,13 @@ var TransformerChainExecutor = class {
134
134
  const { endpointTransformer } = options;
135
135
  const context = {
136
136
  logger: this.logger,
137
- providerName: provider.name
137
+ providerName: provider.name,
138
+ // The unified REQUEST this response answers. Response encoders need it to
139
+ // restore request-scoped protocol state that the chat wire cannot carry —
140
+ // e.g. which codex tools were declared `custom` (see
141
+ // `UnifiedChatRequestMeta.codexTools`), which decides whether a tool call
142
+ // is encoded back as a `function_call` or a `custom_tool_call` item.
143
+ req: request
138
144
  };
139
145
  let finalResponse = response;
140
146
  const bypass = this.shouldBypassTransformers(chain, endpointTransformer, request);
@@ -59,6 +59,26 @@ function hasThinkingEnabled(request) {
59
59
  }
60
60
  return false;
61
61
  }
62
+ function flattenToolResultContent(content) {
63
+ if (typeof content === "string") return content;
64
+ if (content == null) return "";
65
+ if (!Array.isArray(content)) return JSON.stringify(content);
66
+ const parts = [];
67
+ for (const block of content) {
68
+ if (typeof block === "string") {
69
+ parts.push(block);
70
+ continue;
71
+ }
72
+ if (!block || typeof block !== "object") continue;
73
+ const typed = block;
74
+ if (typed.type === "image" || typed.type === "image_url") {
75
+ parts.push("[image omitted]");
76
+ } else if (typeof typed.text === "string") {
77
+ parts.push(typed.text);
78
+ }
79
+ }
80
+ return parts.filter((text) => text !== "").join("\n");
81
+ }
62
82
 
63
83
  // src/api-converter/anthropic-to-openai.ts
64
84
  function convertAnthropicToOpenAI(response) {
@@ -158,7 +178,7 @@ function convertAnthropicRequestToOpenAI(request) {
158
178
  });
159
179
  } else if (part.type === "tool_result") {
160
180
  toolResultId = part.tool_use_id;
161
- toolResultContent = typeof part.content === "string" ? part.content : JSON.stringify(part.content);
181
+ toolResultContent = flattenToolResultContent(part.content);
162
182
  }
163
183
  }
164
184
  if (toolResultId && toolResultContent !== void 0) {
@@ -368,7 +388,10 @@ function convertOpenAIToAnthropic(request, config) {
368
388
  const toolResult = {
369
389
  type: "tool_result",
370
390
  tool_use_id: msg.tool_call_id,
371
- content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
391
+ // An OpenAI tool message is normally a plain string, but a parts array
392
+ // must be flattened to text rather than stringified into a literal
393
+ // `[{"type":"text",…}]` envelope the model then has to read.
394
+ content: flattenToolResultContent(msg.content)
372
395
  };
373
396
  const lastMsg = messages[messages.length - 1];
374
397
  if (lastMsg && lastMsg.role === "user" && Array.isArray(lastMsg.content)) {