@omnicross/core 0.1.5 → 0.1.7
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 +8 -6
- package/dist/auth/GeminiCodeAssistProjectResolver.js +6 -4
- package/dist/{chunk-3Z5BQZFM.cjs → chunk-235MXAGX.cjs} +3 -1
- package/dist/{chunk-HRRYG2AX.js → chunk-23S5OPIO.js} +10 -1
- package/dist/chunk-25GXJCEZ.cjs +40 -0
- package/dist/chunk-2E42SAW3.cjs +323 -0
- package/dist/chunk-2JYRIADS.js +40 -0
- package/dist/chunk-2Z4XM3H5.cjs +44 -0
- package/dist/{chunk-BODMAL6G.js → chunk-32O35CIF.js} +271 -91
- package/dist/chunk-3SCOLIPT.cjs +63 -0
- package/dist/{chunk-SYQF7OIR.js → chunk-3WD4HTR2.js} +2 -0
- package/dist/{chunk-CGKLE6IW.cjs → chunk-4BB343BI.cjs} +1489 -520
- package/dist/{chunk-VACWOIRE.cjs → chunk-4VZX5F4T.cjs} +39 -15
- package/dist/{chunk-5ZQBEOMD.js → chunk-5NJLQ6T6.js} +46 -12
- package/dist/chunk-5RSZYQJH.cjs +62 -0
- package/dist/chunk-6QQG7YG3.cjs +153 -0
- package/dist/{chunk-IFXJ2BG7.js → chunk-7C7DET6S.js} +51 -6
- package/dist/{chunk-IK3KE3AU.js → chunk-DE2SRBFZ.js} +1 -1
- package/dist/{chunk-FH2P4OCD.js → chunk-E3COOKUP.js} +1240 -271
- package/dist/{chunk-UHGVBFU3.js → chunk-EBLFCNPY.js} +38 -14
- package/dist/chunk-FU7EAR73.js +62 -0
- package/dist/chunk-FXR6N3SR.js +323 -0
- package/dist/{chunk-SUEGJ75D.cjs → chunk-GDIXXKF2.cjs} +46 -12
- package/dist/chunk-GFIZ6LQI.js +170 -0
- package/dist/chunk-GL6XM527.js +61 -0
- package/dist/chunk-HVBBDBS3.cjs +350 -0
- package/dist/{chunk-46ZIFWYG.cjs → chunk-IIRPUVPH.cjs} +7 -1
- package/dist/chunk-J3QQ6JS5.cjs +170 -0
- package/dist/{chunk-JWVTXXPX.cjs → chunk-KBM2W6UY.cjs} +8 -4
- package/dist/chunk-LB6EBS6Y.js +69 -0
- package/dist/{chunk-IWQAX3CS.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-MYYR3HZL.js +44 -0
- package/dist/{chunk-MFZ5437A.js → chunk-N5T4M4GU.js} +1 -1
- package/dist/{chunk-5ENKBSWO.js → chunk-N7OOJI5V.js} +88 -8
- package/dist/chunk-NKVZ6Z3Y.cjs +61 -0
- package/dist/chunk-OF3BECU2.cjs +69 -0
- package/dist/{chunk-SNYBEXLB.cjs → chunk-OJGQKHN5.cjs} +3 -1
- package/dist/{chunk-DDMRUL76.cjs → chunk-OKMJLEXX.cjs} +5 -5
- package/dist/chunk-OPRS4IQZ.js +153 -0
- package/dist/chunk-OTZC4TNH.cjs +79 -0
- package/dist/{chunk-3Q6Z6JTO.cjs → chunk-OVQBNVSV.cjs} +287 -107
- package/dist/{chunk-4XMYPW46.cjs → chunk-OXHNSZ53.cjs} +84 -44
- package/dist/{chunk-C6OV7GCC.js → chunk-PEGTIPB3.js} +52 -12
- package/dist/chunk-PO3XB56Z.js +79 -0
- package/dist/{chunk-D3TDKWRP.js → chunk-RJ5ZGNI3.js} +54 -5
- package/dist/{chunk-PG7PS2NN.cjs → chunk-RJUONLUQ.cjs} +59 -10
- package/dist/{chunk-BPKCU575.js → chunk-SOO562NM.js} +3 -1
- package/dist/{chunk-6QZGDLL4.js → chunk-TOVPMMHM.js} +5 -1
- package/dist/{chunk-K52THW2C.cjs → chunk-V4TXMEJS.cjs} +5 -5
- package/dist/{chunk-G2UTNSGU.js → chunk-WNKWAEUR.js} +7 -1
- package/dist/{chunk-UNWKG6EI.cjs → chunk-XPIOJQS2.cjs} +51 -6
- package/dist/chunk-XXFYLL7R.js +63 -0
- package/dist/{chunk-KFI44N2R.cjs → chunk-YNJNTCSQ.cjs} +10 -1
- package/dist/{chunk-VQY5W4YW.cjs → chunk-YWL7YZWS.cjs} +90 -10
- 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 +30 -24
- package/dist/completion/CompletionService.d.cts +3 -1
- package/dist/completion/CompletionService.d.ts +3 -1
- package/dist/completion/CompletionService.js +29 -23
- package/dist/completion.cjs +32 -26
- package/dist/completion.d.cts +3 -1
- package/dist/completion.d.ts +3 -1
- package/dist/completion.js +31 -25
- package/dist/index.cjs +56 -29
- package/dist/index.d.cts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +61 -34
- 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 +3 -4
- package/dist/outbound-api/auditCapture.d.ts +3 -4
- 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 +5 -3
- package/dist/outbound-api/routeResolver.d.ts +5 -3
- package/dist/outbound-api/routeResolver.js +1 -1
- package/dist/outbound-api/subscriptionRegistryPort.d.cts +2 -0
- package/dist/outbound-api/subscriptionRegistryPort.d.ts +2 -0
- package/dist/outbound-api/types.d.cts +4 -2
- package/dist/outbound-api/types.d.ts +4 -2
- package/dist/outbound-api.cjs +52 -25
- package/dist/outbound-api.d.cts +131 -87
- package/dist/outbound-api.d.ts +131 -87
- package/dist/outbound-api.js +55 -28
- package/dist/pipeline/AccountAllowanceScheduling.cjs +15 -0
- package/dist/pipeline/AccountAllowanceScheduling.d.cts +91 -0
- package/dist/pipeline/AccountAllowanceScheduling.d.ts +91 -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/AccountRouteActivity.cjs +10 -0
- package/dist/pipeline/AccountRouteActivity.d.cts +61 -0
- package/dist/pipeline/AccountRouteActivity.d.ts +61 -0
- package/dist/pipeline/AccountRouteActivity.js +10 -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/ServerOverloadCounter.cjs +16 -0
- package/dist/pipeline/ServerOverloadCounter.d.cts +77 -0
- package/dist/pipeline/ServerOverloadCounter.d.ts +77 -0
- package/dist/pipeline/ServerOverloadCounter.js +16 -0
- package/dist/pipeline/SubscriptionAccountHealth.cjs +6 -2
- package/dist/pipeline/SubscriptionAccountHealth.d.cts +65 -8
- package/dist/pipeline/SubscriptionAccountHealth.d.ts +65 -8
- package/dist/pipeline/SubscriptionAccountHealth.js +5 -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 +5 -3
- package/dist/pipeline/upstreamFetch.d.cts +35 -6
- package/dist/pipeline/upstreamFetch.d.ts +35 -6
- package/dist/pipeline/upstreamFetch.js +4 -2
- package/dist/pipeline/upstreamTrace.cjs +4 -2
- package/dist/pipeline/upstreamTrace.d.cts +22 -8
- package/dist/pipeline/upstreamTrace.d.ts +22 -8
- package/dist/pipeline/upstreamTrace.js +3 -1
- 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 +30 -24
- package/dist/provider-proxy/ProviderProxy.d.cts +2 -0
- package/dist/provider-proxy/ProviderProxy.d.ts +2 -0
- package/dist/provider-proxy/ProviderProxy.js +29 -23
- package/dist/provider-proxy/identity/codexCliHeaders.cjs +10 -0
- package/dist/provider-proxy/identity/codexCliHeaders.d.cts +45 -0
- package/dist/provider-proxy/identity/codexCliHeaders.d.ts +45 -0
- package/dist/provider-proxy/identity/codexCliHeaders.js +10 -0
- package/dist/provider-proxy/ingress/providerProxyShared.cjs +32 -24
- package/dist/provider-proxy/ingress/providerProxyShared.d.cts +44 -5
- package/dist/provider-proxy/ingress/providerProxyShared.d.ts +44 -5
- package/dist/provider-proxy/ingress/providerProxyShared.js +31 -23
- package/dist/provider-proxy/matchText.cjs +4 -2
- package/dist/provider-proxy/matchText.d.cts +39 -6
- package/dist/provider-proxy/matchText.d.ts +39 -6
- package/dist/provider-proxy/matchText.js +3 -1
- package/dist/provider-proxy/types.d.cts +35 -2
- package/dist/provider-proxy/types.d.ts +35 -2
- package/dist/provider-proxy.cjs +30 -24
- package/dist/provider-proxy.d.cts +2 -0
- package/dist/provider-proxy.d.ts +2 -0
- package/dist/provider-proxy.js +29 -23
- package/dist/{routeResolver-C4T7yClx.d.ts → routeResolver-BxOfunbm.d.ts} +1 -1
- package/dist/{routeResolver-CB3tvJKy.d.cts → routeResolver-D5OzD0iM.d.cts} +1 -1
- package/dist/transformer/TransformerChainExecutor.cjs +2 -2
- package/dist/transformer/TransformerChainExecutor.js +1 -1
- package/dist/transformer/transformers/AnthropicTransformer.cjs +3 -2
- package/dist/transformer/transformers/AnthropicTransformer.js +2 -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 +3 -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 +2 -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 +9 -14
- package/dist/transformer/transformers.d.cts +12 -48
- package/dist/transformer/transformers.d.ts +12 -48
- package/dist/transformer/transformers.js +11 -16
- package/dist/transformer/types.d.cts +17 -0
- package/dist/transformer/types.d.ts +17 -0
- package/dist/transformer.cjs +12 -12
- package/dist/transformer.d.cts +1 -2
- package/dist/transformer.d.ts +1 -2
- package/dist/transformer.js +12 -12
- package/dist/{types-lgPYQuZS.d.ts → types-CqZD77rS.d.ts} +173 -7
- package/dist/{types-B8Arpxn0.d.cts → types-gjJ5lJ5n.d.cts} +173 -7
- package/dist/usage/pricing-engine.cjs +2 -2
- package/dist/usage/pricing-engine.d.cts +29 -1
- package/dist/usage/pricing-engine.d.ts +29 -1
- package/dist/usage/pricing-engine.js +1 -1
- package/dist/usage/usage-recorder.cjs +2 -2
- package/dist/usage/usage-recorder.d.cts +5 -1
- package/dist/usage/usage-recorder.d.ts +5 -1
- package/dist/usage/usage-recorder.js +1 -1
- package/dist/usage.cjs +3 -3
- package/dist/usage.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-7FEBOPJO.cjs +0 -50
- package/dist/chunk-DWKQZ2SK.cjs +0 -254
- package/dist/chunk-IAXWICZU.js +0 -69
- package/dist/chunk-IB3FDI4O.js +0 -50
- package/dist/chunk-OQWKZCXP.js +0 -242
- package/dist/chunk-QWBWGODS.cjs +0 -69
- package/dist/chunk-YMFAJFQT.cjs +0 -173
- package/dist/chunk-ZCIQ3L3R.cjs +0 -242
- package/dist/chunk-ZJZAOKI7.js +0 -254
- package/dist/chunk-ZLX3KGXX.js +0 -173
- 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
|
@@ -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,62 @@
|
|
|
1
|
+
// src/transformer/transformers/utils/usage-mapping.ts
|
|
2
|
+
function num(obj, key) {
|
|
3
|
+
const v = obj?.[key];
|
|
4
|
+
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
5
|
+
}
|
|
6
|
+
function detailCachedTokens(obj, detailKey) {
|
|
7
|
+
const detail = obj?.[detailKey];
|
|
8
|
+
return num(detail, "cached_tokens");
|
|
9
|
+
}
|
|
10
|
+
function anthropicUsageToChatUsage(usage) {
|
|
11
|
+
if (!usage) return void 0;
|
|
12
|
+
const input = num(usage, "input_tokens");
|
|
13
|
+
const output = num(usage, "output_tokens");
|
|
14
|
+
const cacheRead = num(usage, "cache_read_input_tokens");
|
|
15
|
+
const cacheCreation = num(usage, "cache_creation_input_tokens");
|
|
16
|
+
const promptTokens = input + cacheRead + cacheCreation;
|
|
17
|
+
const out = {
|
|
18
|
+
prompt_tokens: promptTokens,
|
|
19
|
+
completion_tokens: output,
|
|
20
|
+
total_tokens: promptTokens + output
|
|
21
|
+
};
|
|
22
|
+
if (cacheRead > 0) {
|
|
23
|
+
out.prompt_tokens_details = { cached_tokens: cacheRead };
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
function chatUsageToResponsesUsage(usage) {
|
|
28
|
+
if (!usage) return void 0;
|
|
29
|
+
const prompt = num(usage, "prompt_tokens");
|
|
30
|
+
const completion = num(usage, "completion_tokens");
|
|
31
|
+
const out = {
|
|
32
|
+
input_tokens: prompt,
|
|
33
|
+
output_tokens: completion,
|
|
34
|
+
total_tokens: num(usage, "total_tokens") || prompt + completion
|
|
35
|
+
};
|
|
36
|
+
const cacheRead = detailCachedTokens(usage, "prompt_tokens_details");
|
|
37
|
+
if (cacheRead > 0) {
|
|
38
|
+
out.input_tokens_details = { cached_tokens: cacheRead };
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
function responsesUsageToChatUsage(usage) {
|
|
43
|
+
if (!usage) return void 0;
|
|
44
|
+
const input = num(usage, "input_tokens");
|
|
45
|
+
const output = num(usage, "output_tokens");
|
|
46
|
+
const out = {
|
|
47
|
+
prompt_tokens: input,
|
|
48
|
+
completion_tokens: output,
|
|
49
|
+
total_tokens: num(usage, "total_tokens") || input + output
|
|
50
|
+
};
|
|
51
|
+
const cacheRead = detailCachedTokens(usage, "input_tokens_details");
|
|
52
|
+
if (cacheRead > 0) {
|
|
53
|
+
out.prompt_tokens_details = { cached_tokens: cacheRead };
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
anthropicUsageToChatUsage,
|
|
60
|
+
chatUsageToResponsesUsage,
|
|
61
|
+
responsesUsageToChatUsage
|
|
62
|
+
};
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
// src/usage/pricing-engine.ts
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_LITELLM_PRICING_URL,
|
|
4
|
+
DEFAULT_OPENROUTER_PRICING_URL
|
|
5
|
+
} from "@omnicross/contracts/pricing-types";
|
|
6
|
+
var PricingEngine = class {
|
|
7
|
+
constructor(store, logger, options = {}) {
|
|
8
|
+
this.store = store;
|
|
9
|
+
this.logger = logger;
|
|
10
|
+
this.options = options;
|
|
11
|
+
}
|
|
12
|
+
store;
|
|
13
|
+
logger;
|
|
14
|
+
options;
|
|
15
|
+
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
|
+
cacheByModel = /* @__PURE__ */ new Map();
|
|
24
|
+
/**
|
|
25
|
+
* Last-resort fuzzy index: `[normalizedModelId, entry]` pairs over the same
|
|
26
|
+
* non-wildcard, non-openrouter rows as `cacheByModel`. Used when even the
|
|
27
|
+
* model-name fallback misses — e.g. usage recorded under a bare model id
|
|
28
|
+
* (`glm-5.2`) while the catalog only knows prefixed slugs
|
|
29
|
+
* (`cloudflare/@cf/zai-org/glm-5.2`). This is an ESTIMATE: the matched row
|
|
30
|
+
* may be an aggregate-route price rather than the direct-vendor rate.
|
|
31
|
+
*/
|
|
32
|
+
fuzzyEntries = [];
|
|
33
|
+
cacheLoaded = false;
|
|
34
|
+
/** Coalesce manual and background refreshes to prevent a catalog stampede. */
|
|
35
|
+
refreshInFlight = null;
|
|
36
|
+
// ===== Cache management =====
|
|
37
|
+
/** Lazy-load and cache the full pricing table. */
|
|
38
|
+
async ensureCache() {
|
|
39
|
+
if (this.cacheLoaded) return;
|
|
40
|
+
const rows = await this.store.getAll();
|
|
41
|
+
this.cache = new Map(rows.map((r) => [keyOf(r.providerId, r.modelId), r]));
|
|
42
|
+
this.cacheByModel = /* @__PURE__ */ new Map();
|
|
43
|
+
this.fuzzyEntries = [];
|
|
44
|
+
for (const r of rows) {
|
|
45
|
+
if (r.providerId === "*" || r.providerId === "openrouter" || r.source === "openrouter") continue;
|
|
46
|
+
if (!this.cacheByModel.has(r.modelId)) this.cacheByModel.set(r.modelId, r);
|
|
47
|
+
this.fuzzyEntries.push([normalizeModelId(r.modelId), r]);
|
|
48
|
+
}
|
|
49
|
+
this.cacheLoaded = true;
|
|
50
|
+
}
|
|
51
|
+
/** Force a cache refresh — call after any write. */
|
|
52
|
+
async invalidateCache() {
|
|
53
|
+
this.cacheLoaded = false;
|
|
54
|
+
await this.ensureCache();
|
|
55
|
+
}
|
|
56
|
+
// ===== Read API =====
|
|
57
|
+
async getAll() {
|
|
58
|
+
await this.ensureCache();
|
|
59
|
+
return Array.from(this.cache.values());
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Look up a price for (provider, model). Resolution order:
|
|
63
|
+
* 1. exact (providerId, modelId)
|
|
64
|
+
* 2. wildcard provider ('*', modelId)
|
|
65
|
+
* 3. model name alone (any provider) — tolerates a runtime-alias vs
|
|
66
|
+
* canonical provider-id mismatch for the same model.
|
|
67
|
+
* 4. normalized fuzzy match (last resort) — matches a bare id like
|
|
68
|
+
* `glm-5.2` against prefixed catalog slugs like
|
|
69
|
+
* `cloudflare/@cf/zai-org/glm-5.2`. ESTIMATE only: the matched row may
|
|
70
|
+
* be an aggregate-route price, not the direct-vendor rate.
|
|
71
|
+
*/
|
|
72
|
+
async getEntry(providerId, modelId) {
|
|
73
|
+
await this.ensureCache();
|
|
74
|
+
const exact = this.cache.get(keyOf(providerId, modelId)) ?? this.cache.get(keyOf("*", modelId)) ?? this.cacheByModel.get(modelId) ?? null;
|
|
75
|
+
if (exact) return exact;
|
|
76
|
+
return this.fuzzyMatch(modelId);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Normalized substring match over the catalog's non-openrouter rows. Lowercases
|
|
80
|
+
* and strips all non-alphanumerics, then matches bidirectionally, preferring
|
|
81
|
+
* the shortest catalog key (tightest match) to reduce over-matching version
|
|
82
|
+
* variants. Returns null when nothing matches.
|
|
83
|
+
*/
|
|
84
|
+
fuzzyMatch(modelId) {
|
|
85
|
+
const normQuery = normalizeModelId(modelId);
|
|
86
|
+
if (!normQuery) return null;
|
|
87
|
+
let best = null;
|
|
88
|
+
let bestLen = Infinity;
|
|
89
|
+
for (const [key, entry] of this.fuzzyEntries) {
|
|
90
|
+
if (!key || !key.includes(normQuery) && !normQuery.includes(key)) continue;
|
|
91
|
+
if (key.length < bestLen) {
|
|
92
|
+
best = entry;
|
|
93
|
+
bestLen = key.length;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return best;
|
|
97
|
+
}
|
|
98
|
+
// ===== Cost calculation =====
|
|
99
|
+
/**
|
|
100
|
+
* Compute the cost (and cache savings) for a single response.
|
|
101
|
+
*
|
|
102
|
+
* Cost formula (USD):
|
|
103
|
+
* cost = (input * inputPrice
|
|
104
|
+
* + output * outputPrice
|
|
105
|
+
* + cacheRead * (cacheReadPrice ?? inputPrice)
|
|
106
|
+
* + cacheWrite * (cacheWritePrice ?? inputPrice)
|
|
107
|
+
* ) / 1e6
|
|
108
|
+
*
|
|
109
|
+
* Cache savings:
|
|
110
|
+
* savings = cacheRead * (inputPrice - cacheReadPrice) / 1e6
|
|
111
|
+
* — clamped at 0 if cacheReadPrice is null or ≥ inputPrice.
|
|
112
|
+
*/
|
|
113
|
+
async calculateCost(providerId, modelId, usage) {
|
|
114
|
+
const entry = await this.getEntry(providerId, modelId);
|
|
115
|
+
if (!entry) {
|
|
116
|
+
return { costUsd: 0, costSavedByCacheUsd: 0 };
|
|
117
|
+
}
|
|
118
|
+
const ip = entry.inputPricePer1m;
|
|
119
|
+
const op = entry.outputPricePer1m;
|
|
120
|
+
const crp = entry.cacheReadPricePer1m ?? ip;
|
|
121
|
+
const cwp = entry.cacheWritePricePer1m ?? ip;
|
|
122
|
+
const costUsd = (usage.inputTokens * ip + usage.outputTokens * op + usage.cacheReadTokens * crp + usage.cacheCreationTokens * cwp) / 1e6;
|
|
123
|
+
const savingsRate = entry.cacheReadPricePer1m == null ? 0 : Math.max(0, ip - crp);
|
|
124
|
+
const costSavedByCacheUsd = usage.cacheReadTokens * savingsRate / 1e6;
|
|
125
|
+
return { costUsd, costSavedByCacheUsd };
|
|
126
|
+
}
|
|
127
|
+
// ===== Manual upsert (used by inline editors) =====
|
|
128
|
+
async upsertManual(input) {
|
|
129
|
+
const updated = await this.store.upsert(
|
|
130
|
+
input,
|
|
131
|
+
/* asUserEdit */
|
|
132
|
+
true
|
|
133
|
+
);
|
|
134
|
+
this.cache.set(keyOf(updated.providerId, updated.modelId), updated);
|
|
135
|
+
if (updated.providerId !== "*" && updated.providerId !== "openrouter" && updated.source !== "openrouter") {
|
|
136
|
+
this.cacheByModel.set(updated.modelId, updated);
|
|
137
|
+
}
|
|
138
|
+
return updated;
|
|
139
|
+
}
|
|
140
|
+
// ===== Source refresh =====
|
|
141
|
+
async fetchLatestFromSource() {
|
|
142
|
+
if (this.refreshInFlight) return this.refreshInFlight;
|
|
143
|
+
const refresh = this.refreshSources();
|
|
144
|
+
this.refreshInFlight = refresh;
|
|
145
|
+
try {
|
|
146
|
+
return await refresh;
|
|
147
|
+
} finally {
|
|
148
|
+
if (this.refreshInFlight === refresh) this.refreshInFlight = null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async refreshSources() {
|
|
152
|
+
const sourceUrl = this.options.sourceUrl ?? DEFAULT_LITELLM_PRICING_URL;
|
|
153
|
+
const openRouterSourceUrl = this.options.openRouterSourceUrl ?? DEFAULT_OPENROUTER_PRICING_URL;
|
|
154
|
+
const fetchImpl = this.options.fetchImpl ?? fetch;
|
|
155
|
+
const timeoutMs = this.options.fetchTimeoutMs ?? 1e4;
|
|
156
|
+
const fetchedAt = Date.now();
|
|
157
|
+
const [litellm, openrouter] = await Promise.all([
|
|
158
|
+
fetchCatalog(fetchImpl, sourceUrl, timeoutMs, parseLitellmJson),
|
|
159
|
+
fetchCatalog(fetchImpl, openRouterSourceUrl, timeoutMs, parseOpenRouterJson)
|
|
160
|
+
]);
|
|
161
|
+
const sources = [
|
|
162
|
+
sourceResult("litellm", sourceUrl, litellm),
|
|
163
|
+
sourceResult("openrouter", openRouterSourceUrl, openrouter)
|
|
164
|
+
];
|
|
165
|
+
for (const source of sources) {
|
|
166
|
+
if (source.status === "failed") {
|
|
167
|
+
this.logger.warn(`[PricingEngine] ${source.source} pricing-source fetch failed`, {
|
|
168
|
+
error: source.error
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (!litellm.ok && !openrouter.ok) {
|
|
173
|
+
throw new Error(
|
|
174
|
+
`Pricing-source fetch failed: LiteLLM (${litellm.error}); OpenRouter (${openrouter.error})`
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
const applied = [];
|
|
178
|
+
const conflicts = [];
|
|
179
|
+
if (litellm.ok) {
|
|
180
|
+
const result = await this.store.bulkApplyFromSource(litellm.entries, "litellm");
|
|
181
|
+
applied.push(...result.applied);
|
|
182
|
+
conflicts.push(
|
|
183
|
+
...result.conflicts.map((c) => ({
|
|
184
|
+
providerId: c.current.providerId,
|
|
185
|
+
modelId: c.current.modelId,
|
|
186
|
+
current: c.current,
|
|
187
|
+
incoming: c.incoming
|
|
188
|
+
}))
|
|
189
|
+
);
|
|
190
|
+
sources[0].appliedCount = result.applied.length;
|
|
191
|
+
}
|
|
192
|
+
if (openrouter.ok) {
|
|
193
|
+
const current = new Map(
|
|
194
|
+
(await this.store.getAll()).map((row) => [keyOf(row.providerId, row.modelId), row])
|
|
195
|
+
);
|
|
196
|
+
const supplemental = openrouter.entries.filter((incoming) => {
|
|
197
|
+
const row = current.get(keyOf(incoming.providerId, incoming.modelId));
|
|
198
|
+
return !row || row.source === "openrouter" || row.source === "builtin";
|
|
199
|
+
});
|
|
200
|
+
const result = await this.store.bulkApplyFromSource(supplemental, "openrouter");
|
|
201
|
+
applied.push(...result.applied);
|
|
202
|
+
sources[1].appliedCount = result.applied.length;
|
|
203
|
+
}
|
|
204
|
+
if (applied.length > 0) await this.invalidateCache();
|
|
205
|
+
return {
|
|
206
|
+
applied,
|
|
207
|
+
conflicts,
|
|
208
|
+
fetchedAt,
|
|
209
|
+
sourceUrl,
|
|
210
|
+
sources
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
// ===== Conflict resolution =====
|
|
214
|
+
async resolveConflicts(decisions, pendingIncoming) {
|
|
215
|
+
const resolutions = [];
|
|
216
|
+
for (const d of decisions) {
|
|
217
|
+
const incoming = pendingIncoming.get(keyOf(d.providerId, d.modelId));
|
|
218
|
+
if (!incoming) continue;
|
|
219
|
+
resolutions.push({ incoming, action: d.action });
|
|
220
|
+
}
|
|
221
|
+
const result = await this.store.applyResolutions(resolutions);
|
|
222
|
+
await this.invalidateCache();
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
var keyOf = (providerId, modelId) => `${providerId}::${modelId}`;
|
|
227
|
+
var normalizeModelId = (modelId) => (modelId ?? "").toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
228
|
+
var PROVIDER_ALIASES = {
|
|
229
|
+
vertex_ai: "gemini",
|
|
230
|
+
google: "gemini",
|
|
231
|
+
bedrock: "anthropic",
|
|
232
|
+
azure: "openai"
|
|
233
|
+
};
|
|
234
|
+
var parseLitellmJson = (json) => {
|
|
235
|
+
if (!json || typeof json !== "object" || Array.isArray(json)) {
|
|
236
|
+
throw new Error("invalid LiteLLM catalog shape");
|
|
237
|
+
}
|
|
238
|
+
const out = [];
|
|
239
|
+
for (const [modelId, entry] of Object.entries(json)) {
|
|
240
|
+
if (modelId === "sample_spec") continue;
|
|
241
|
+
if (!entry || typeof entry !== "object") continue;
|
|
242
|
+
if (entry.input_cost_per_token == null || entry.output_cost_per_token == null) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const rawProvider = entry.litellm_provider ?? "";
|
|
246
|
+
const providerId = PROVIDER_ALIASES[rawProvider] ?? rawProvider;
|
|
247
|
+
if (!providerId) continue;
|
|
248
|
+
out.push({
|
|
249
|
+
providerId,
|
|
250
|
+
modelId,
|
|
251
|
+
inputPricePer1m: entry.input_cost_per_token * 1e6,
|
|
252
|
+
outputPricePer1m: entry.output_cost_per_token * 1e6,
|
|
253
|
+
cacheReadPricePer1m: entry.cache_read_input_token_cost != null ? entry.cache_read_input_token_cost * 1e6 : null,
|
|
254
|
+
cacheWritePricePer1m: entry.cache_creation_input_token_cost != null ? entry.cache_creation_input_token_cost * 1e6 : null
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
return out;
|
|
258
|
+
};
|
|
259
|
+
var parseOpenRouterJson = (json) => {
|
|
260
|
+
if (!json || !Array.isArray(json.data)) {
|
|
261
|
+
throw new Error("invalid OpenRouter catalog shape");
|
|
262
|
+
}
|
|
263
|
+
const out = [];
|
|
264
|
+
for (const model of json.data) {
|
|
265
|
+
const modelId = typeof model?.id === "string" ? model.id.trim() : "";
|
|
266
|
+
const input = parseUnitPrice(model?.pricing?.prompt);
|
|
267
|
+
const output = parseUnitPrice(model?.pricing?.completion);
|
|
268
|
+
if (!modelId || input === null || output === null) continue;
|
|
269
|
+
const cacheRead = parseUnitPrice(model.pricing?.input_cache_read);
|
|
270
|
+
const cacheWrite = parseUnitPrice(model.pricing?.input_cache_write);
|
|
271
|
+
out.push({
|
|
272
|
+
providerId: "openrouter",
|
|
273
|
+
modelId,
|
|
274
|
+
inputPricePer1m: input * 1e6,
|
|
275
|
+
outputPricePer1m: output * 1e6,
|
|
276
|
+
cacheReadPricePer1m: cacheRead === null ? null : cacheRead * 1e6,
|
|
277
|
+
cacheWritePricePer1m: cacheWrite === null ? null : cacheWrite * 1e6
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
return out;
|
|
281
|
+
};
|
|
282
|
+
function parseUnitPrice(value) {
|
|
283
|
+
if (value === "" || value === null || value === void 0) return null;
|
|
284
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
285
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
|
|
286
|
+
}
|
|
287
|
+
async function fetchCatalog(fetchImpl, url, timeoutMs, parse) {
|
|
288
|
+
const controller = new AbortController();
|
|
289
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
290
|
+
timer.unref?.();
|
|
291
|
+
try {
|
|
292
|
+
const response = await fetchImpl(url, { signal: controller.signal });
|
|
293
|
+
if (!response.ok) throw new Error(`status ${response.status}`);
|
|
294
|
+
const entries = parse(await response.json());
|
|
295
|
+
if (entries.length === 0) throw new Error("pricing catalog contained no usable entries");
|
|
296
|
+
return { ok: true, entries };
|
|
297
|
+
} catch (error) {
|
|
298
|
+
const message = controller.signal.aborted ? `timeout after ${timeoutMs}ms` : error instanceof Error ? error.message : String(error);
|
|
299
|
+
return { ok: false, error: message };
|
|
300
|
+
} finally {
|
|
301
|
+
clearTimeout(timer);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function sourceResult(source, sourceUrl, attempt) {
|
|
305
|
+
return attempt.ok ? {
|
|
306
|
+
source,
|
|
307
|
+
sourceUrl,
|
|
308
|
+
status: "applied",
|
|
309
|
+
parsedCount: attempt.entries.length,
|
|
310
|
+
appliedCount: 0
|
|
311
|
+
} : {
|
|
312
|
+
source,
|
|
313
|
+
sourceUrl,
|
|
314
|
+
status: "failed",
|
|
315
|
+
parsedCount: 0,
|
|
316
|
+
appliedCount: 0,
|
|
317
|
+
error: attempt.error
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export {
|
|
322
|
+
PricingEngine
|
|
323
|
+
};
|
|
@@ -59,8 +59,42 @@ 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
|
|
84
|
+
function anthropicUsageToChatUsage(usage) {
|
|
85
|
+
const input = usage.input_tokens || 0;
|
|
86
|
+
const output = usage.output_tokens || 0;
|
|
87
|
+
const cacheRead = usage.cache_read_input_tokens || 0;
|
|
88
|
+
const cacheCreation = usage.cache_creation_input_tokens || 0;
|
|
89
|
+
const promptTokens = input + cacheRead + cacheCreation;
|
|
90
|
+
const out = {
|
|
91
|
+
prompt_tokens: promptTokens,
|
|
92
|
+
completion_tokens: output,
|
|
93
|
+
total_tokens: promptTokens + output
|
|
94
|
+
};
|
|
95
|
+
if (cacheRead > 0) out.prompt_tokens_details = { cached_tokens: cacheRead };
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
64
98
|
function convertAnthropicToOpenAI(response) {
|
|
65
99
|
const content = [];
|
|
66
100
|
const toolCalls = [];
|
|
@@ -98,11 +132,7 @@ function convertAnthropicToOpenAI(response) {
|
|
|
98
132
|
finish_reason: finishReason
|
|
99
133
|
}
|
|
100
134
|
],
|
|
101
|
-
usage:
|
|
102
|
-
prompt_tokens: response.usage.input_tokens,
|
|
103
|
-
completion_tokens: response.usage.output_tokens,
|
|
104
|
-
total_tokens: response.usage.input_tokens + response.usage.output_tokens
|
|
105
|
-
}
|
|
135
|
+
usage: anthropicUsageToChatUsage(response.usage)
|
|
106
136
|
};
|
|
107
137
|
if (reasoningContent) {
|
|
108
138
|
result.reasoning_content = reasoningContent;
|
|
@@ -158,7 +188,7 @@ function convertAnthropicRequestToOpenAI(request) {
|
|
|
158
188
|
});
|
|
159
189
|
} else if (part.type === "tool_result") {
|
|
160
190
|
toolResultId = part.tool_use_id;
|
|
161
|
-
toolResultContent =
|
|
191
|
+
toolResultContent = flattenToolResultContent(part.content);
|
|
162
192
|
}
|
|
163
193
|
}
|
|
164
194
|
if (toolResultId && toolResultContent !== void 0) {
|
|
@@ -332,11 +362,12 @@ async function* convertAnthropicStreamToOpenAI(anthropicStream) {
|
|
|
332
362
|
delta: {},
|
|
333
363
|
finish_reason: finishReason
|
|
334
364
|
}],
|
|
335
|
-
usage: event.usage ? {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
365
|
+
usage: event.usage ? anthropicUsageToChatUsage({
|
|
366
|
+
input_tokens: event.usage.input_tokens || 0,
|
|
367
|
+
output_tokens: event.usage.output_tokens || 0,
|
|
368
|
+
cache_read_input_tokens: event.usage.cache_read_input_tokens,
|
|
369
|
+
cache_creation_input_tokens: event.usage.cache_creation_input_tokens
|
|
370
|
+
}) : void 0
|
|
340
371
|
})}
|
|
341
372
|
|
|
342
373
|
`;
|
|
@@ -368,7 +399,10 @@ function convertOpenAIToAnthropic(request, config) {
|
|
|
368
399
|
const toolResult = {
|
|
369
400
|
type: "tool_result",
|
|
370
401
|
tool_use_id: msg.tool_call_id,
|
|
371
|
-
|
|
402
|
+
// An OpenAI tool message is normally a plain string, but a parts array
|
|
403
|
+
// must be flattened to text rather than stringified into a literal
|
|
404
|
+
// `[{"type":"text",…}]` envelope the model then has to read.
|
|
405
|
+
content: flattenToolResultContent(msg.content)
|
|
372
406
|
};
|
|
373
407
|
const lastMsg = messages[messages.length - 1];
|
|
374
408
|
if (lastMsg && lastMsg.role === "user" && Array.isArray(lastMsg.content)) {
|