@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.
- package/dist/ApiConverter.cjs +2 -2
- package/dist/ApiConverter.js +1 -1
- package/dist/{CompletionService-DdWaAxq0.d.ts → CompletionService-C3BX0Ovy.d.ts} +4 -3
- package/dist/{CompletionService-D3a47vdU.d.cts → CompletionService-kNA17cfI.d.cts} +4 -3
- package/dist/auth/GeminiCodeAssistProjectResolver.cjs +7 -5
- package/dist/auth/GeminiCodeAssistProjectResolver.js +5 -3
- package/dist/{chunk-HRRYG2AX.js → chunk-23S5OPIO.js} +10 -1
- package/dist/chunk-25GXJCEZ.cjs +40 -0
- package/dist/chunk-2JYRIADS.js +40 -0
- package/dist/chunk-3Z5BQZFM.cjs +52 -0
- package/dist/{chunk-4VAVYUZS.cjs → chunk-4VLRHHFE.cjs} +82 -40
- package/dist/{chunk-VACWOIRE.cjs → chunk-4VZX5F4T.cjs} +39 -15
- package/dist/{chunk-FTQ7IQUC.js → chunk-6CL5HTPQ.js} +52 -10
- package/dist/chunk-6QQG7YG3.cjs +153 -0
- package/dist/{chunk-SU2GCLAC.cjs → chunk-6VUQPOV3.cjs} +359 -60
- package/dist/chunk-7AK7B36B.cjs +44 -0
- package/dist/{chunk-5ZQBEOMD.js → chunk-AT7B7SOZ.js} +25 -2
- package/dist/{chunk-IK3KE3AU.js → chunk-DE2SRBFZ.js} +1 -1
- package/dist/chunk-DLXZEZWO.cjs +285 -0
- package/dist/{chunk-UHGVBFU3.js → chunk-EBLFCNPY.js} +38 -14
- package/dist/chunk-EJLH6NFT.cjs +166 -0
- package/dist/chunk-HVBBDBS3.cjs +350 -0
- package/dist/{chunk-7Y47UCF2.js → chunk-IFXJ2BG7.js} +5 -38
- package/dist/{chunk-46ZIFWYG.cjs → chunk-IIRPUVPH.cjs} +7 -1
- package/dist/{chunk-YGXJGRFI.cjs → chunk-KBM2W6UY.cjs} +111 -24
- package/dist/{chunk-OCEUVQHW.cjs → chunk-KCVJWJST.cjs} +73 -3
- package/dist/chunk-LB6EBS6Y.js +69 -0
- package/dist/{chunk-C7PSERMH.js → chunk-LVUCAWMK.js} +1 -1
- package/dist/chunk-MKF46GNZ.js +350 -0
- package/dist/{chunk-HYN75H6G.cjs → chunk-MUHWHQVM.cjs} +1 -1
- package/dist/{chunk-Z7Y2NPHY.js → chunk-N5T4M4GU.js} +1 -1
- package/dist/{chunk-TIER4SUC.js → chunk-OEWZUX4G.js} +346 -47
- package/dist/chunk-OF3BECU2.cjs +69 -0
- package/dist/{chunk-RSJ3TWJQ.cjs → chunk-OKMJLEXX.cjs} +5 -5
- package/dist/chunk-OPRS4IQZ.js +153 -0
- package/dist/{chunk-XK5CIRMQ.js → chunk-SIZQQD6T.js} +72 -2
- package/dist/{chunk-DKPXN34V.cjs → chunk-SQYVORNP.cjs} +1334 -510
- package/dist/chunk-SYQF7OIR.js +52 -0
- package/dist/{chunk-S4WBV25P.js → chunk-TOVPMMHM.js} +111 -24
- package/dist/{chunk-QYTT6GE3.js → chunk-UDCC7EMR.js} +1093 -269
- package/dist/{chunk-E3UCVHLP.cjs → chunk-UNWKG6EI.cjs} +5 -38
- package/dist/{chunk-ZLX3KGXX.js → chunk-UX7VWA46.js} +135 -23
- package/dist/{chunk-7YN22L2H.cjs → chunk-V4TXMEJS.cjs} +5 -5
- package/dist/chunk-VRFBB5DB.js +44 -0
- package/dist/chunk-WB6PH56J.js +166 -0
- package/dist/{chunk-G2UTNSGU.js → chunk-WNKWAEUR.js} +7 -1
- package/dist/{chunk-SUEGJ75D.cjs → chunk-WYTEQ4Q5.cjs} +25 -2
- package/dist/{chunk-5ENKBSWO.js → chunk-YJVHCHQX.js} +42 -6
- package/dist/{chunk-VQY5W4YW.cjs → chunk-YK57NMIF.cjs} +44 -8
- package/dist/{chunk-KFI44N2R.cjs → chunk-YNJNTCSQ.cjs} +10 -1
- package/dist/completion/ApiKeyPoolService.cjs +12 -0
- package/dist/completion/ApiKeyPoolService.d.cts +6 -0
- package/dist/completion/ApiKeyPoolService.d.ts +6 -0
- package/dist/completion/ApiKeyPoolService.js +12 -0
- package/dist/completion/CompletionService.cjs +27 -24
- package/dist/completion/CompletionService.d.cts +2 -1
- package/dist/completion/CompletionService.d.ts +2 -1
- package/dist/completion/CompletionService.js +26 -23
- package/dist/completion.cjs +27 -24
- package/dist/completion.d.cts +2 -1
- package/dist/completion.d.ts +2 -1
- package/dist/completion.js +26 -23
- package/dist/index.cjs +53 -29
- package/dist/index.d.cts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +56 -32
- package/dist/{keyPolicy-Cp0FntZl.d.cts → keyPolicy-zdWTu3CQ.d.cts} +1 -1
- package/dist/{keyPolicy-Cp0FntZl.d.ts → keyPolicy-zdWTu3CQ.d.ts} +1 -1
- package/dist/outbound-api/auditCapture.cjs +2 -2
- package/dist/outbound-api/auditCapture.d.cts +5 -3
- package/dist/outbound-api/auditCapture.d.ts +5 -3
- package/dist/outbound-api/auditCapture.js +1 -1
- package/dist/outbound-api/quotaWarn.d.cts +1 -1
- package/dist/outbound-api/quotaWarn.d.ts +1 -1
- package/dist/outbound-api/routeResolver.cjs +2 -2
- package/dist/outbound-api/routeResolver.d.cts +4 -3
- package/dist/outbound-api/routeResolver.d.ts +4 -3
- package/dist/outbound-api/routeResolver.js +1 -1
- package/dist/outbound-api/subscriptionRegistryPort.d.cts +1 -0
- package/dist/outbound-api/subscriptionRegistryPort.d.ts +1 -0
- package/dist/outbound-api/types.d.cts +3 -2
- package/dist/outbound-api/types.d.ts +3 -2
- package/dist/outbound-api.cjs +49 -25
- package/dist/outbound-api.d.cts +127 -85
- package/dist/outbound-api.d.ts +127 -85
- package/dist/outbound-api.js +52 -28
- package/dist/pipeline/AccountAllowanceScheduling.cjs +15 -0
- package/dist/pipeline/AccountAllowanceScheduling.d.cts +90 -0
- package/dist/pipeline/AccountAllowanceScheduling.d.ts +90 -0
- package/dist/pipeline/AccountAllowanceScheduling.js +15 -0
- package/dist/pipeline/AccountAllowanceStore.cjs +20 -0
- package/dist/pipeline/AccountAllowanceStore.d.cts +77 -0
- package/dist/pipeline/AccountAllowanceStore.d.ts +77 -0
- package/dist/pipeline/AccountAllowanceStore.js +20 -0
- package/dist/pipeline/AuthSource.d.cts +11 -2
- package/dist/pipeline/AuthSource.d.ts +11 -2
- package/dist/pipeline/BoundAccountSelectionError.cjs +10 -0
- package/dist/pipeline/BoundAccountSelectionError.d.cts +28 -0
- package/dist/pipeline/BoundAccountSelectionError.d.ts +28 -0
- package/dist/pipeline/BoundAccountSelectionError.js +10 -0
- package/dist/pipeline/LlmConfigProviderAuth.d.cts +1 -0
- package/dist/pipeline/LlmConfigProviderAuth.d.ts +1 -0
- package/dist/pipeline/SubscriptionAccountHealth.cjs +4 -2
- package/dist/pipeline/SubscriptionAccountHealth.d.cts +34 -6
- package/dist/pipeline/SubscriptionAccountHealth.d.ts +34 -6
- package/dist/pipeline/SubscriptionAccountHealth.js +3 -1
- package/dist/pipeline/SubscriptionAuthSource.cjs +5 -2
- package/dist/pipeline/SubscriptionAuthSource.d.cts +1 -0
- package/dist/pipeline/SubscriptionAuthSource.d.ts +1 -0
- package/dist/pipeline/SubscriptionAuthSource.js +4 -1
- package/dist/pipeline/SubscriptionAuthStrategy.d.cts +8 -3
- package/dist/pipeline/SubscriptionAuthStrategy.d.ts +8 -3
- package/dist/pipeline/executeProviderCall.cjs +2 -2
- package/dist/pipeline/executeProviderCall.d.cts +8 -3
- package/dist/pipeline/executeProviderCall.d.ts +8 -3
- package/dist/pipeline/executeProviderCall.js +1 -1
- package/dist/pipeline/resolveProviderChain.d.cts +2 -3
- package/dist/pipeline/resolveProviderChain.d.ts +2 -3
- package/dist/pipeline/resolveSubscriptionChain.d.cts +1 -0
- package/dist/pipeline/resolveSubscriptionChain.d.ts +1 -0
- package/dist/pipeline/upstreamFetch.cjs +4 -2
- package/dist/pipeline/upstreamFetch.d.cts +6 -0
- package/dist/pipeline/upstreamFetch.d.ts +6 -0
- package/dist/pipeline/upstreamFetch.js +3 -1
- package/dist/pipeline/upstreamTrace.cjs +14 -0
- package/dist/pipeline/upstreamTrace.d.cts +85 -0
- package/dist/pipeline/upstreamTrace.d.ts +85 -0
- package/dist/pipeline/upstreamTrace.js +14 -0
- package/dist/ports/pricing-store.d.cts +4 -3
- package/dist/ports/pricing-store.d.ts +4 -3
- package/dist/ports.d.cts +4 -3
- package/dist/ports.d.ts +4 -3
- package/dist/provider-proxy/ProviderProxy.cjs +27 -24
- package/dist/provider-proxy/ProviderProxy.d.cts +1 -0
- package/dist/provider-proxy/ProviderProxy.d.ts +1 -0
- package/dist/provider-proxy/ProviderProxy.js +26 -23
- package/dist/provider-proxy/ingress/providerProxyShared.cjs +31 -24
- package/dist/provider-proxy/ingress/providerProxyShared.d.cts +50 -5
- package/dist/provider-proxy/ingress/providerProxyShared.d.ts +50 -5
- package/dist/provider-proxy/ingress/providerProxyShared.js +30 -23
- package/dist/provider-proxy/matchText.cjs +4 -2
- package/dist/provider-proxy/matchText.d.cts +41 -6
- package/dist/provider-proxy/matchText.d.ts +41 -6
- package/dist/provider-proxy/matchText.js +3 -1
- package/dist/provider-proxy/types.d.cts +30 -2
- package/dist/provider-proxy/types.d.ts +30 -2
- package/dist/provider-proxy.cjs +27 -24
- package/dist/provider-proxy.d.cts +1 -0
- package/dist/provider-proxy.d.ts +1 -0
- package/dist/provider-proxy.js +26 -23
- package/dist/{routeResolver-C4T7yClx.d.ts → routeResolver-CHTv_Bs5.d.ts} +1 -1
- package/dist/{routeResolver-CB3tvJKy.d.cts → routeResolver-kepxnfcC.d.cts} +1 -1
- package/dist/transformer/TransformerChainExecutor.cjs +2 -2
- package/dist/transformer/TransformerChainExecutor.js +1 -1
- package/dist/transformer/transformers/AnthropicTransformer.cjs +2 -2
- package/dist/transformer/transformers/AnthropicTransformer.js +1 -1
- package/dist/transformer/transformers/GeminiCodeAssistTransformer.cjs +3 -3
- package/dist/transformer/transformers/GeminiCodeAssistTransformer.js +2 -2
- package/dist/transformer/transformers/GeminiTransformer.cjs +3 -3
- package/dist/transformer/transformers/GeminiTransformer.js +2 -2
- package/dist/transformer/transformers/OpenAIResponseTransformer.cjs +2 -2
- package/dist/transformer/transformers/OpenAIResponseTransformer.d.cts +1 -1
- package/dist/transformer/transformers/OpenAIResponseTransformer.d.ts +1 -1
- package/dist/transformer/transformers/OpenAIResponseTransformer.js +1 -1
- package/dist/transformer/transformers/OpenAITransformer.cjs +6 -0
- package/dist/transformer/transformers/OpenAITransformer.d.cts +68 -0
- package/dist/transformer/transformers/OpenAITransformer.d.ts +68 -0
- package/dist/transformer/transformers/OpenAITransformer.js +6 -0
- package/dist/transformer/transformers.cjs +8 -14
- package/dist/transformer/transformers.d.cts +12 -48
- package/dist/transformer/transformers.d.ts +12 -48
- package/dist/transformer/transformers.js +11 -17
- package/dist/transformer/types.d.cts +17 -0
- package/dist/transformer/types.d.ts +17 -0
- package/dist/transformer.cjs +11 -12
- package/dist/transformer.d.cts +1 -2
- package/dist/transformer.d.ts +1 -2
- package/dist/transformer.js +11 -12
- package/dist/{types-B8Arpxn0.d.cts → types-H3wGZSOo.d.cts} +134 -7
- package/dist/{types-lgPYQuZS.d.ts → types-hsoKrVQS.d.ts} +134 -7
- package/dist/usage/pricing-engine.cjs +2 -2
- package/dist/usage/pricing-engine.d.cts +9 -1
- package/dist/usage/pricing-engine.d.ts +9 -1
- package/dist/usage/pricing-engine.js +1 -1
- package/dist/usage.cjs +2 -2
- package/dist/usage.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-C72S6TTV.cjs +0 -49
- package/dist/chunk-DWKQZ2SK.cjs +0 -254
- package/dist/chunk-IAXWICZU.js +0 -69
- package/dist/chunk-QWBWGODS.cjs +0 -69
- package/dist/chunk-RGTR7CIO.cjs +0 -235
- package/dist/chunk-VPBAUQ2M.js +0 -235
- package/dist/chunk-YMFAJFQT.cjs +0 -173
- package/dist/chunk-Z7LUNECE.js +0 -49
- package/dist/chunk-ZJZAOKI7.js +0 -254
- package/dist/transformer/transformers/OpenCodeGoTransformer.cjs +0 -6
- package/dist/transformer/transformers/OpenCodeGoTransformer.d.cts +0 -51
- package/dist/transformer/transformers/OpenCodeGoTransformer.d.ts +0 -51
- package/dist/transformer/transformers/OpenCodeGoTransformer.js +0 -6
- package/dist/transformer/transformers/ReasoningTransformer.cjs +0 -6
- package/dist/transformer/transformers/ReasoningTransformer.d.cts +0 -47
- package/dist/transformer/transformers/ReasoningTransformer.d.ts +0 -47
- package/dist/transformer/transformers/ReasoningTransformer.js +0 -6
|
@@ -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 =
|
|
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
|
-
|
|
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)) {
|
|
@@ -28,7 +28,7 @@ async function executeProviderCall(ctx) {
|
|
|
28
28
|
}
|
|
29
29
|
const fetched = await fetchFn(url, headers, finalBody);
|
|
30
30
|
let response = fetched;
|
|
31
|
-
if (runResponseChain) {
|
|
31
|
+
if (runResponseChain && fetched.ok) {
|
|
32
32
|
response = await executor.executeResponseChain(
|
|
33
33
|
// Preserve the EXACT first arg each site passed: proxy → requestBody,
|
|
34
34
|
// unified ingresses → their pre-transform request. The caller supplies
|
|
@@ -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;
|