@omnicross/core 0.1.5 → 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 (190) 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 +6 -5
  6. package/dist/auth/GeminiCodeAssistProjectResolver.js +4 -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-4XMYPW46.cjs → chunk-4VLRHHFE.cjs} +75 -33
  11. package/dist/{chunk-VACWOIRE.cjs → chunk-4VZX5F4T.cjs} +39 -15
  12. package/dist/{chunk-C6OV7GCC.js → chunk-6CL5HTPQ.js} +44 -2
  13. package/dist/chunk-6QQG7YG3.cjs +153 -0
  14. package/dist/{chunk-3Q6Z6JTO.cjs → chunk-6VUQPOV3.cjs} +277 -86
  15. package/dist/chunk-7AK7B36B.cjs +44 -0
  16. package/dist/{chunk-5ZQBEOMD.js → chunk-AT7B7SOZ.js} +25 -2
  17. package/dist/{chunk-IK3KE3AU.js → chunk-DE2SRBFZ.js} +1 -1
  18. package/dist/chunk-DLXZEZWO.cjs +285 -0
  19. package/dist/{chunk-UHGVBFU3.js → chunk-EBLFCNPY.js} +38 -14
  20. package/dist/chunk-EJLH6NFT.cjs +166 -0
  21. package/dist/chunk-HVBBDBS3.cjs +350 -0
  22. package/dist/{chunk-46ZIFWYG.cjs → chunk-IIRPUVPH.cjs} +7 -1
  23. package/dist/{chunk-JWVTXXPX.cjs → chunk-KBM2W6UY.cjs} +8 -4
  24. package/dist/{chunk-PG7PS2NN.cjs → chunk-KCVJWJST.cjs} +14 -2
  25. package/dist/chunk-LB6EBS6Y.js +69 -0
  26. package/dist/{chunk-IWQAX3CS.js → chunk-LVUCAWMK.js} +1 -1
  27. package/dist/chunk-MKF46GNZ.js +350 -0
  28. package/dist/{chunk-HYN75H6G.cjs → chunk-MUHWHQVM.cjs} +1 -1
  29. package/dist/{chunk-MFZ5437A.js → chunk-N5T4M4GU.js} +1 -1
  30. package/dist/{chunk-BODMAL6G.js → chunk-OEWZUX4G.js} +262 -71
  31. package/dist/chunk-OF3BECU2.cjs +69 -0
  32. package/dist/{chunk-DDMRUL76.cjs → chunk-OKMJLEXX.cjs} +5 -5
  33. package/dist/chunk-OPRS4IQZ.js +153 -0
  34. package/dist/{chunk-D3TDKWRP.js → chunk-SIZQQD6T.js} +14 -2
  35. package/dist/{chunk-CGKLE6IW.cjs → chunk-SQYVORNP.cjs} +1219 -510
  36. package/dist/{chunk-6QZGDLL4.js → chunk-TOVPMMHM.js} +5 -1
  37. package/dist/{chunk-FH2P4OCD.js → chunk-UDCC7EMR.js} +976 -267
  38. package/dist/{chunk-ZLX3KGXX.js → chunk-UX7VWA46.js} +135 -23
  39. package/dist/{chunk-K52THW2C.cjs → chunk-V4TXMEJS.cjs} +5 -5
  40. package/dist/chunk-VRFBB5DB.js +44 -0
  41. package/dist/chunk-WB6PH56J.js +166 -0
  42. package/dist/{chunk-G2UTNSGU.js → chunk-WNKWAEUR.js} +7 -1
  43. package/dist/{chunk-SUEGJ75D.cjs → chunk-WYTEQ4Q5.cjs} +25 -2
  44. package/dist/{chunk-5ENKBSWO.js → chunk-YJVHCHQX.js} +42 -6
  45. package/dist/{chunk-VQY5W4YW.cjs → chunk-YK57NMIF.cjs} +44 -8
  46. package/dist/{chunk-KFI44N2R.cjs → chunk-YNJNTCSQ.cjs} +10 -1
  47. package/dist/completion/ApiKeyPoolService.cjs +12 -0
  48. package/dist/completion/ApiKeyPoolService.d.cts +6 -0
  49. package/dist/completion/ApiKeyPoolService.d.ts +6 -0
  50. package/dist/completion/ApiKeyPoolService.js +12 -0
  51. package/dist/completion/CompletionService.cjs +25 -23
  52. package/dist/completion/CompletionService.d.cts +2 -1
  53. package/dist/completion/CompletionService.d.ts +2 -1
  54. package/dist/completion/CompletionService.js +24 -22
  55. package/dist/completion.cjs +25 -23
  56. package/dist/completion.d.cts +2 -1
  57. package/dist/completion.d.ts +2 -1
  58. package/dist/completion.js +24 -22
  59. package/dist/index.cjs +49 -26
  60. package/dist/index.d.cts +8 -8
  61. package/dist/index.d.ts +8 -8
  62. package/dist/index.js +52 -29
  63. package/dist/{keyPolicy-Cp0FntZl.d.ts → keyPolicy-zdWTu3CQ.d.cts} +1 -1
  64. package/dist/{keyPolicy-Cp0FntZl.d.cts → keyPolicy-zdWTu3CQ.d.ts} +1 -1
  65. package/dist/outbound-api/quotaWarn.d.cts +1 -1
  66. package/dist/outbound-api/quotaWarn.d.ts +1 -1
  67. package/dist/outbound-api/routeResolver.cjs +2 -2
  68. package/dist/outbound-api/routeResolver.d.cts +4 -3
  69. package/dist/outbound-api/routeResolver.d.ts +4 -3
  70. package/dist/outbound-api/routeResolver.js +1 -1
  71. package/dist/outbound-api/subscriptionRegistryPort.d.cts +1 -0
  72. package/dist/outbound-api/subscriptionRegistryPort.d.ts +1 -0
  73. package/dist/outbound-api/types.d.cts +3 -2
  74. package/dist/outbound-api/types.d.ts +3 -2
  75. package/dist/outbound-api.cjs +47 -24
  76. package/dist/outbound-api.d.cts +127 -85
  77. package/dist/outbound-api.d.ts +127 -85
  78. package/dist/outbound-api.js +50 -27
  79. package/dist/pipeline/AccountAllowanceScheduling.cjs +15 -0
  80. package/dist/pipeline/AccountAllowanceScheduling.d.cts +90 -0
  81. package/dist/pipeline/AccountAllowanceScheduling.d.ts +90 -0
  82. package/dist/pipeline/AccountAllowanceScheduling.js +15 -0
  83. package/dist/pipeline/AccountAllowanceStore.cjs +20 -0
  84. package/dist/pipeline/AccountAllowanceStore.d.cts +77 -0
  85. package/dist/pipeline/AccountAllowanceStore.d.ts +77 -0
  86. package/dist/pipeline/AccountAllowanceStore.js +20 -0
  87. package/dist/pipeline/AuthSource.d.cts +11 -2
  88. package/dist/pipeline/AuthSource.d.ts +11 -2
  89. package/dist/pipeline/BoundAccountSelectionError.cjs +10 -0
  90. package/dist/pipeline/BoundAccountSelectionError.d.cts +28 -0
  91. package/dist/pipeline/BoundAccountSelectionError.d.ts +28 -0
  92. package/dist/pipeline/BoundAccountSelectionError.js +10 -0
  93. package/dist/pipeline/LlmConfigProviderAuth.d.cts +1 -0
  94. package/dist/pipeline/LlmConfigProviderAuth.d.ts +1 -0
  95. package/dist/pipeline/SubscriptionAccountHealth.cjs +4 -2
  96. package/dist/pipeline/SubscriptionAccountHealth.d.cts +34 -6
  97. package/dist/pipeline/SubscriptionAccountHealth.d.ts +34 -6
  98. package/dist/pipeline/SubscriptionAccountHealth.js +3 -1
  99. package/dist/pipeline/SubscriptionAuthSource.cjs +5 -2
  100. package/dist/pipeline/SubscriptionAuthSource.d.cts +1 -0
  101. package/dist/pipeline/SubscriptionAuthSource.d.ts +1 -0
  102. package/dist/pipeline/SubscriptionAuthSource.js +4 -1
  103. package/dist/pipeline/SubscriptionAuthStrategy.d.cts +8 -3
  104. package/dist/pipeline/SubscriptionAuthStrategy.d.ts +8 -3
  105. package/dist/pipeline/executeProviderCall.cjs +2 -2
  106. package/dist/pipeline/executeProviderCall.d.cts +8 -3
  107. package/dist/pipeline/executeProviderCall.d.ts +8 -3
  108. package/dist/pipeline/executeProviderCall.js +1 -1
  109. package/dist/pipeline/resolveProviderChain.d.cts +2 -3
  110. package/dist/pipeline/resolveProviderChain.d.ts +2 -3
  111. package/dist/pipeline/resolveSubscriptionChain.d.cts +1 -0
  112. package/dist/pipeline/resolveSubscriptionChain.d.ts +1 -0
  113. package/dist/pipeline/upstreamFetch.cjs +3 -2
  114. package/dist/pipeline/upstreamFetch.js +2 -1
  115. package/dist/ports/pricing-store.d.cts +4 -3
  116. package/dist/ports/pricing-store.d.ts +4 -3
  117. package/dist/ports.d.cts +4 -3
  118. package/dist/ports.d.ts +4 -3
  119. package/dist/provider-proxy/ProviderProxy.cjs +25 -23
  120. package/dist/provider-proxy/ProviderProxy.d.cts +1 -0
  121. package/dist/provider-proxy/ProviderProxy.d.ts +1 -0
  122. package/dist/provider-proxy/ProviderProxy.js +24 -22
  123. package/dist/provider-proxy/ingress/providerProxyShared.cjs +27 -23
  124. package/dist/provider-proxy/ingress/providerProxyShared.d.cts +34 -5
  125. package/dist/provider-proxy/ingress/providerProxyShared.d.ts +34 -5
  126. package/dist/provider-proxy/ingress/providerProxyShared.js +26 -22
  127. package/dist/provider-proxy/matchText.cjs +4 -2
  128. package/dist/provider-proxy/matchText.d.cts +41 -6
  129. package/dist/provider-proxy/matchText.d.ts +41 -6
  130. package/dist/provider-proxy/matchText.js +3 -1
  131. package/dist/provider-proxy/types.d.cts +30 -2
  132. package/dist/provider-proxy/types.d.ts +30 -2
  133. package/dist/provider-proxy.cjs +25 -23
  134. package/dist/provider-proxy.d.cts +1 -0
  135. package/dist/provider-proxy.d.ts +1 -0
  136. package/dist/provider-proxy.js +24 -22
  137. package/dist/{routeResolver-C4T7yClx.d.ts → routeResolver-CHTv_Bs5.d.ts} +1 -1
  138. package/dist/{routeResolver-CB3tvJKy.d.cts → routeResolver-kepxnfcC.d.cts} +1 -1
  139. package/dist/transformer/TransformerChainExecutor.cjs +2 -2
  140. package/dist/transformer/TransformerChainExecutor.js +1 -1
  141. package/dist/transformer/transformers/AnthropicTransformer.cjs +2 -2
  142. package/dist/transformer/transformers/AnthropicTransformer.js +1 -1
  143. package/dist/transformer/transformers/GeminiCodeAssistTransformer.cjs +3 -3
  144. package/dist/transformer/transformers/GeminiCodeAssistTransformer.js +2 -2
  145. package/dist/transformer/transformers/GeminiTransformer.cjs +3 -3
  146. package/dist/transformer/transformers/GeminiTransformer.js +2 -2
  147. package/dist/transformer/transformers/OpenAIResponseTransformer.cjs +2 -2
  148. package/dist/transformer/transformers/OpenAIResponseTransformer.d.cts +1 -1
  149. package/dist/transformer/transformers/OpenAIResponseTransformer.d.ts +1 -1
  150. package/dist/transformer/transformers/OpenAIResponseTransformer.js +1 -1
  151. package/dist/transformer/transformers/OpenAITransformer.cjs +6 -0
  152. package/dist/transformer/transformers/OpenAITransformer.d.cts +68 -0
  153. package/dist/transformer/transformers/OpenAITransformer.d.ts +68 -0
  154. package/dist/transformer/transformers/OpenAITransformer.js +6 -0
  155. package/dist/transformer/transformers.cjs +8 -14
  156. package/dist/transformer/transformers.d.cts +12 -48
  157. package/dist/transformer/transformers.d.ts +12 -48
  158. package/dist/transformer/transformers.js +12 -18
  159. package/dist/transformer/types.d.cts +17 -0
  160. package/dist/transformer/types.d.ts +17 -0
  161. package/dist/transformer.cjs +10 -11
  162. package/dist/transformer.d.cts +1 -2
  163. package/dist/transformer.d.ts +1 -2
  164. package/dist/transformer.js +9 -10
  165. package/dist/{types-B8Arpxn0.d.cts → types-H3wGZSOo.d.cts} +134 -7
  166. package/dist/{types-lgPYQuZS.d.ts → types-hsoKrVQS.d.ts} +134 -7
  167. package/dist/usage/pricing-engine.cjs +2 -2
  168. package/dist/usage/pricing-engine.d.cts +9 -1
  169. package/dist/usage/pricing-engine.d.ts +9 -1
  170. package/dist/usage/pricing-engine.js +1 -1
  171. package/dist/usage.cjs +2 -2
  172. package/dist/usage.js +1 -1
  173. package/package.json +1 -1
  174. package/dist/chunk-7FEBOPJO.cjs +0 -50
  175. package/dist/chunk-DWKQZ2SK.cjs +0 -254
  176. package/dist/chunk-IAXWICZU.js +0 -69
  177. package/dist/chunk-IB3FDI4O.js +0 -50
  178. package/dist/chunk-OQWKZCXP.js +0 -242
  179. package/dist/chunk-QWBWGODS.cjs +0 -69
  180. package/dist/chunk-YMFAJFQT.cjs +0 -173
  181. package/dist/chunk-ZCIQ3L3R.cjs +0 -242
  182. package/dist/chunk-ZJZAOKI7.js +0 -254
  183. package/dist/transformer/transformers/OpenCodeGoTransformer.cjs +0 -6
  184. package/dist/transformer/transformers/OpenCodeGoTransformer.d.cts +0 -51
  185. package/dist/transformer/transformers/OpenCodeGoTransformer.d.ts +0 -51
  186. package/dist/transformer/transformers/OpenCodeGoTransformer.js +0 -6
  187. package/dist/transformer/transformers/ReasoningTransformer.cjs +0 -6
  188. package/dist/transformer/transformers/ReasoningTransformer.d.cts +0 -47
  189. package/dist/transformer/transformers/ReasoningTransformer.d.ts +0 -47
  190. package/dist/transformer/transformers/ReasoningTransformer.js +0 -6
@@ -0,0 +1,285 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;// src/usage/pricing-engine.ts
2
+
3
+
4
+
5
+ var _pricingtypes = require('@omnicross/contracts/pricing-types');
6
+ var PricingEngine = (_class = class {
7
+ constructor(store, logger, options = {}) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);
8
+ this.store = store;
9
+ this.logger = logger;
10
+ this.options = options;
11
+ }
12
+
13
+
14
+
15
+ __init() {this.cache = /* @__PURE__ */ new Map()}
16
+ /**
17
+ * Secondary index keyed by modelId alone. Used as a fallback when no exact
18
+ * (providerId, modelId) match exists — usage may be recorded under a
19
+ * runtime provider alias while pricing rows use the canonical provider id,
20
+ * so matching by model name lets the price still resolve. First
21
+ * non-wildcard entry per model wins.
22
+ */
23
+ __init2() {this.cacheByModel = /* @__PURE__ */ new Map()}
24
+ __init3() {this.cacheLoaded = false}
25
+ /** Coalesce manual and background refreshes to prevent a catalog stampede. */
26
+ __init4() {this.refreshInFlight = null}
27
+ // ===== Cache management =====
28
+ /** Lazy-load and cache the full pricing table. */
29
+ async ensureCache() {
30
+ if (this.cacheLoaded) return;
31
+ const rows = await this.store.getAll();
32
+ this.cache = new Map(rows.map((r) => [keyOf(r.providerId, r.modelId), r]));
33
+ this.cacheByModel = /* @__PURE__ */ new Map();
34
+ for (const r of rows) {
35
+ if (r.providerId === "*" || r.providerId === "openrouter" || r.source === "openrouter") continue;
36
+ if (!this.cacheByModel.has(r.modelId)) this.cacheByModel.set(r.modelId, r);
37
+ }
38
+ this.cacheLoaded = true;
39
+ }
40
+ /** Force a cache refresh — call after any write. */
41
+ async invalidateCache() {
42
+ this.cacheLoaded = false;
43
+ await this.ensureCache();
44
+ }
45
+ // ===== Read API =====
46
+ async getAll() {
47
+ await this.ensureCache();
48
+ return Array.from(this.cache.values());
49
+ }
50
+ /**
51
+ * Look up a price for (provider, model). Resolution order:
52
+ * 1. exact (providerId, modelId)
53
+ * 2. wildcard provider ('*', modelId)
54
+ * 3. model name alone (any provider) — tolerates a runtime-alias vs
55
+ * canonical provider-id mismatch for the same model.
56
+ */
57
+ async getEntry(providerId, modelId) {
58
+ await this.ensureCache();
59
+ return _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(this.cache.get(keyOf(providerId, modelId)), () => ( this.cache.get(keyOf("*", modelId)))), () => ( this.cacheByModel.get(modelId))), () => ( null));
60
+ }
61
+ // ===== Cost calculation =====
62
+ /**
63
+ * Compute the cost (and cache savings) for a single response.
64
+ *
65
+ * Cost formula (USD):
66
+ * cost = (input * inputPrice
67
+ * + output * outputPrice
68
+ * + cacheRead * (cacheReadPrice ?? inputPrice)
69
+ * + cacheWrite * (cacheWritePrice ?? inputPrice)
70
+ * ) / 1e6
71
+ *
72
+ * Cache savings:
73
+ * savings = cacheRead * (inputPrice - cacheReadPrice) / 1e6
74
+ * — clamped at 0 if cacheReadPrice is null or ≥ inputPrice.
75
+ */
76
+ async calculateCost(providerId, modelId, usage) {
77
+ const entry = await this.getEntry(providerId, modelId);
78
+ if (!entry) {
79
+ return { costUsd: 0, costSavedByCacheUsd: 0 };
80
+ }
81
+ const ip = entry.inputPricePer1m;
82
+ const op = entry.outputPricePer1m;
83
+ const crp = _nullishCoalesce(entry.cacheReadPricePer1m, () => ( ip));
84
+ const cwp = _nullishCoalesce(entry.cacheWritePricePer1m, () => ( ip));
85
+ const costUsd = (usage.inputTokens * ip + usage.outputTokens * op + usage.cacheReadTokens * crp + usage.cacheCreationTokens * cwp) / 1e6;
86
+ const savingsRate = entry.cacheReadPricePer1m == null ? 0 : Math.max(0, ip - crp);
87
+ const costSavedByCacheUsd = usage.cacheReadTokens * savingsRate / 1e6;
88
+ return { costUsd, costSavedByCacheUsd };
89
+ }
90
+ // ===== Manual upsert (used by inline editors) =====
91
+ async upsertManual(input) {
92
+ const updated = await this.store.upsert(
93
+ input,
94
+ /* asUserEdit */
95
+ true
96
+ );
97
+ this.cache.set(keyOf(updated.providerId, updated.modelId), updated);
98
+ if (updated.providerId !== "*" && updated.providerId !== "openrouter" && updated.source !== "openrouter") {
99
+ this.cacheByModel.set(updated.modelId, updated);
100
+ }
101
+ return updated;
102
+ }
103
+ // ===== Source refresh =====
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() {
115
+ const sourceUrl = _nullishCoalesce(this.options.sourceUrl, () => ( _pricingtypes.DEFAULT_LITELLM_PRICING_URL));
116
+ const openRouterSourceUrl = _nullishCoalesce(this.options.openRouterSourceUrl, () => ( _pricingtypes.DEFAULT_OPENROUTER_PRICING_URL));
117
+ const fetchImpl = _nullishCoalesce(this.options.fetchImpl, () => ( fetch));
118
+ const timeoutMs = _nullishCoalesce(this.options.fetchTimeoutMs, () => ( 1e4));
119
+ const fetchedAt = Date.now();
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
+ });
133
+ }
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";
162
+ });
163
+ const result = await this.store.bulkApplyFromSource(supplemental, "openrouter");
164
+ applied.push(...result.applied);
165
+ sources[1].appliedCount = result.applied.length;
166
+ }
167
+ if (applied.length > 0) await this.invalidateCache();
168
+ return {
169
+ applied,
170
+ conflicts,
171
+ fetchedAt,
172
+ sourceUrl,
173
+ sources
174
+ };
175
+ }
176
+ // ===== Conflict resolution =====
177
+ async resolveConflicts(decisions, pendingIncoming) {
178
+ const resolutions = [];
179
+ for (const d of decisions) {
180
+ const incoming = pendingIncoming.get(keyOf(d.providerId, d.modelId));
181
+ if (!incoming) continue;
182
+ resolutions.push({ incoming, action: d.action });
183
+ }
184
+ const result = await this.store.applyResolutions(resolutions);
185
+ await this.invalidateCache();
186
+ return result;
187
+ }
188
+ }, _class);
189
+ var keyOf = (providerId, modelId) => `${providerId}::${modelId}`;
190
+ var PROVIDER_ALIASES = {
191
+ vertex_ai: "gemini",
192
+ google: "gemini",
193
+ bedrock: "anthropic",
194
+ azure: "openai"
195
+ };
196
+ var parseLitellmJson = (json) => {
197
+ if (!json || typeof json !== "object" || Array.isArray(json)) {
198
+ throw new Error("invalid LiteLLM catalog shape");
199
+ }
200
+ const out = [];
201
+ for (const [modelId, entry] of Object.entries(json)) {
202
+ if (modelId === "sample_spec") continue;
203
+ if (!entry || typeof entry !== "object") continue;
204
+ if (entry.input_cost_per_token == null || entry.output_cost_per_token == null) {
205
+ continue;
206
+ }
207
+ const rawProvider = _nullishCoalesce(entry.litellm_provider, () => ( ""));
208
+ const providerId = _nullishCoalesce(PROVIDER_ALIASES[rawProvider], () => ( rawProvider));
209
+ if (!providerId) continue;
210
+ out.push({
211
+ providerId,
212
+ modelId,
213
+ inputPricePer1m: entry.input_cost_per_token * 1e6,
214
+ outputPricePer1m: entry.output_cost_per_token * 1e6,
215
+ cacheReadPricePer1m: entry.cache_read_input_token_cost != null ? entry.cache_read_input_token_cost * 1e6 : null,
216
+ cacheWritePricePer1m: entry.cache_creation_input_token_cost != null ? entry.cache_creation_input_token_cost * 1e6 : null
217
+ });
218
+ }
219
+ return out;
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 _optionalChain([model, 'optionalAccess', _ => _.id]) === "string" ? model.id.trim() : "";
228
+ const input = parseUnitPrice(_optionalChain([model, 'optionalAccess', _2 => _2.pricing, 'optionalAccess', _3 => _3.prompt]));
229
+ const output = parseUnitPrice(_optionalChain([model, 'optionalAccess', _4 => _4.pricing, 'optionalAccess', _5 => _5.completion]));
230
+ if (!modelId || input === null || output === null) continue;
231
+ const cacheRead = parseUnitPrice(_optionalChain([model, 'access', _6 => _6.pricing, 'optionalAccess', _7 => _7.input_cache_read]));
232
+ const cacheWrite = parseUnitPrice(_optionalChain([model, 'access', _8 => _8.pricing, 'optionalAccess', _9 => _9.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
+ _optionalChain([timer, 'access', _10 => _10.unref, 'optionalCall', _11 => _11()]);
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
+ }
282
+
283
+
284
+
285
+ exports.PricingEngine = PricingEngine;
@@ -89,18 +89,6 @@ function validateEndpointModelConfig(config) {
89
89
  (kind) => !isNonBlankRef(map[kind])
90
90
  );
91
91
  }
92
- function validateServerModelConfig(config) {
93
- const errors = [];
94
- for (const endpoint of Object.keys(ENDPOINT_MODEL_KINDS)) {
95
- const ep = config.endpoints.find((e) => e.endpoint === endpoint);
96
- const declared = modelKindsForEndpoint(endpoint);
97
- const missingKinds = ep ? validateEndpointModelConfig(ep) : [...declared];
98
- if (missingKinds.length > 0 && missingKinds.length < declared.length) {
99
- errors.push({ endpoint, missingKinds });
100
- }
101
- }
102
- return errors;
103
- }
104
92
 
105
93
  // src/outbound-api/modelPrefixDispatch.ts
106
94
  function classifyModelPrefix(model) {
@@ -149,6 +137,34 @@ function endpointSupportsSubscription(endpoint) {
149
137
  }
150
138
 
151
139
  // src/outbound-api/routeResolver.ts
140
+ function bindDelegatedSubscriptionProfile(profile, config) {
141
+ const preferredAccountId = config.boundAccountId;
142
+ const preferredAccountGroup = config.boundAccountGroup;
143
+ const boundAccountFallbackPolicy = config.boundAccountFallbackPolicy;
144
+ if (!preferredAccountId && !preferredAccountGroup) return profile;
145
+ const strategy = profile.authStrategy;
146
+ return {
147
+ ...profile,
148
+ authStrategy: {
149
+ kind: strategy.kind,
150
+ providerId: strategy.providerId,
151
+ applyHeaders(headers, hints) {
152
+ return strategy.applyHeaders(headers, {
153
+ ...hints,
154
+ preferredAccountId,
155
+ preferredAccountGroup,
156
+ boundAccountFallbackPolicy
157
+ });
158
+ },
159
+ onUnauthorized(sessionKey) {
160
+ return strategy.onUnauthorized(sessionKey);
161
+ },
162
+ describeStatus() {
163
+ return strategy.describeStatus();
164
+ }
165
+ }
166
+ };
167
+ }
152
168
  function parseModelRef(ref) {
153
169
  if (!ref) return null;
154
170
  const idx = ref.indexOf(",");
@@ -267,6 +283,8 @@ async function resolveRoute(args) {
267
283
  apiKey: resolveApiKey(provider.api_key),
268
284
  isOfficialProvider,
269
285
  passThrough: false,
286
+ preferredKeyId: config.boundKeyId,
287
+ boundKeyFallbackPolicy: config.boundKeyFallbackPolicy,
270
288
  attribution: { sessionId, apiKeyId: args.apiKeyId ?? null }
271
289
  };
272
290
  }
@@ -277,6 +295,8 @@ async function resolveRoute(args) {
277
295
  ingressFormat,
278
296
  authMode: "byo",
279
297
  providerId,
298
+ preferredKeyId: config.boundKeyId,
299
+ boundKeyFallbackPolicy: config.boundKeyFallbackPolicy,
280
300
  // Passthrough gate: the client's ORIGINAL requested id (kind-mapped only).
281
301
  requestedModel,
282
302
  // Usage attribution: the verified named-key id (undefined for internal traffic).
@@ -321,7 +341,10 @@ async function resolveSubscriptionRoute(args) {
321
341
  apiKey: "",
322
342
  isOfficialProvider: false,
323
343
  passThrough: false,
324
- subscriptionProfile: profile,
344
+ subscriptionProfile: bindDelegatedSubscriptionProfile(profile, config),
345
+ preferredAccountId: config.boundAccountId,
346
+ preferredAccountGroup: config.boundAccountGroup,
347
+ boundAccountFallbackPolicy: config.boundAccountFallbackPolicy,
325
348
  attribution: { sessionId, apiKeyId: apiKeyId ?? null }
326
349
  };
327
350
  const subscriptionConfig = providerId === "opencodego" ? await registry?.getOpenCodeGoConfig?.() : void 0;
@@ -353,6 +376,8 @@ async function resolveSubscriptionRoute(args) {
353
376
  subscriptionConfig,
354
377
  // Per-request preferred account (provider/subscription duality); undefined ⇒ pool.
355
378
  preferredAccountId: config.boundAccountId,
379
+ preferredAccountGroup: config.boundAccountGroup,
380
+ boundAccountFallbackPolicy: config.boundAccountFallbackPolicy,
356
381
  anthropicSdkHints: hints
357
382
  };
358
383
  return { ok: true, route, usedRole: effectiveRole };
@@ -373,7 +398,6 @@ export {
373
398
  modelKindsForEndpoint,
374
399
  detectModelKind,
375
400
  validateEndpointModelConfig,
376
- validateServerModelConfig,
377
401
  classifyModelPrefix,
378
402
  resolvePrefixTarget,
379
403
  SUBSCRIPTION_PROVIDER_IDS,
@@ -0,0 +1,166 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// 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(_nullishCoalesce(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 = _optionalChain([options, 'access', _ => _.endpoint, 'optionalAccess', _2 => _2.trim, 'call', _3 => _3()]) || "responses";
85
+ const fallback = _optionalChain([options, 'access', _4 => _4.fallbackKey, 'optionalAccess', _5 => _5.trim, 'call', _6 => _6()]) || "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 = _nullishCoalesce(_nullishCoalesce(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
+
160
+
161
+
162
+
163
+
164
+
165
+
166
+ exports.MATCH_TEXT_PER_MESSAGE_CAP = MATCH_TEXT_PER_MESSAGE_CAP; exports.MATCH_TEXT_RECENT_MESSAGES = MATCH_TEXT_RECENT_MESSAGES; exports.flattenMatchText = flattenMatchText; exports.collectMatchText = collectMatchText; exports.deriveSubscriptionSessionKey = deriveSubscriptionSessionKey; exports.deriveGatewaySessionKey = deriveGatewaySessionKey;