@omnicross/core 0.4.4 → 0.4.5
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/auth/GeminiCodeAssistProjectResolver.cjs +4 -4
- package/dist/auth/GeminiCodeAssistProjectResolver.js +3 -3
- package/dist/{chunk-MPFOC7AT.js → chunk-24W3DVGI.js} +1 -1
- package/dist/{chunk-GWC7HCZI.cjs → chunk-2DMOOZ4F.cjs} +1 -1
- package/dist/{chunk-NKVZ6Z3Y.cjs → chunk-4HMAS74X.cjs} +8 -3
- package/dist/{chunk-VFFCV4J6.cjs → chunk-CXRJMNJJ.cjs} +12 -6
- package/dist/{chunk-E5RCCGE7.cjs → chunk-DF5JXK36.cjs} +2 -2
- package/dist/{chunk-CGEZQC2G.cjs → chunk-F3TT5XPV.cjs} +2 -2
- package/dist/{chunk-EE7VP4O3.js → chunk-HQ7C3VB6.js} +1 -1
- package/dist/{chunk-ZJJBITUN.js → chunk-IAWLTZUP.js} +2 -2
- package/dist/{chunk-YV4JSBPA.js → chunk-L4L77G3X.js} +570 -96
- package/dist/{chunk-NTF7TLT7.js → chunk-LBIXTHP7.js} +11 -5
- package/dist/{chunk-UOOLU64V.cjs → chunk-LVQOCIES.cjs} +2 -2
- package/dist/{chunk-RX3BCX53.cjs → chunk-NCCEWVWY.cjs} +974 -500
- package/dist/{chunk-K6J2RIEX.js → chunk-NDJSLJIA.js} +1 -1
- package/dist/{chunk-F6P4DIBL.js → chunk-TLYFFUW4.js} +1 -1
- package/dist/{chunk-GL6XM527.js → chunk-TX7ZM4OI.js} +8 -3
- package/dist/{chunk-NCZDUBC2.js → chunk-WYUXJKMK.js} +1 -1
- package/dist/{chunk-NDIBVTBZ.cjs → chunk-XYFCNDJK.cjs} +2 -2
- package/dist/{chunk-X6GHTNDL.cjs → chunk-ZG57GAVU.cjs} +7 -7
- package/dist/completion/CompletionService.cjs +10 -10
- package/dist/completion/CompletionService.js +9 -9
- package/dist/completion.cjs +10 -10
- package/dist/completion.js +9 -9
- package/dist/index.cjs +12 -10
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +11 -9
- package/dist/outbound-api/routeResolver.d.cts +2 -2
- package/dist/outbound-api/routeResolver.d.ts +2 -2
- package/dist/outbound-api/types.d.cts +2 -2
- package/dist/outbound-api/types.d.ts +2 -2
- package/dist/outbound-api.cjs +12 -10
- package/dist/outbound-api.d.cts +46 -5
- package/dist/outbound-api.d.ts +46 -5
- package/dist/outbound-api.js +11 -9
- package/dist/pipeline/AccountAllowanceScheduling.d.cts +1 -1
- package/dist/pipeline/AccountAllowanceScheduling.d.ts +1 -1
- package/dist/pipeline/AccountRouteActivity.cjs +2 -2
- package/dist/pipeline/AccountRouteActivity.d.cts +33 -8
- package/dist/pipeline/AccountRouteActivity.d.ts +33 -8
- package/dist/pipeline/AccountRouteActivity.js +1 -1
- package/dist/pipeline/upstreamFetch.cjs +3 -3
- package/dist/pipeline/upstreamFetch.d.cts +15 -1
- package/dist/pipeline/upstreamFetch.d.ts +15 -1
- package/dist/pipeline/upstreamFetch.js +2 -2
- package/dist/ports.d.cts +1 -1
- package/dist/ports.d.ts +1 -1
- package/dist/provider-proxy/ProviderProxy.cjs +10 -10
- package/dist/provider-proxy/ProviderProxy.js +9 -9
- package/dist/provider-proxy/identity/codexCliHeaders.d.cts +6 -4
- package/dist/provider-proxy/identity/codexCliHeaders.d.ts +6 -4
- package/dist/provider-proxy/identity/openCodeGoHeaders.cjs +2 -2
- package/dist/provider-proxy/identity/openCodeGoHeaders.js +1 -1
- package/dist/provider-proxy/ingress/providerProxyShared.cjs +12 -10
- package/dist/provider-proxy/ingress/providerProxyShared.d.cts +19 -1
- package/dist/provider-proxy/ingress/providerProxyShared.d.ts +19 -1
- package/dist/provider-proxy/ingress/providerProxyShared.js +11 -9
- package/dist/provider-proxy.cjs +10 -10
- package/dist/provider-proxy.d.cts +1 -1
- package/dist/provider-proxy.d.ts +1 -1
- package/dist/provider-proxy.js +9 -9
- package/dist/{routeResolver-C2z0pp-q.d.cts → routeResolver-BdNJvTZP.d.cts} +1 -1
- package/dist/{routeResolver-BLTleO4H.d.ts → routeResolver-zVoCx9r3.d.ts} +1 -1
- package/dist/transformer/transformers/AntigravityTransformer.cjs +6 -6
- package/dist/transformer/transformers/AntigravityTransformer.js +5 -5
- package/dist/transformer/transformers/antigravityFailover.cjs +7 -7
- package/dist/transformer/transformers/antigravityFailover.js +6 -6
- package/dist/transformer/transformers/antigravityIdentity.cjs +4 -4
- package/dist/transformer/transformers/antigravityIdentity.js +3 -3
- package/dist/transformer/transformers.cjs +8 -8
- package/dist/transformer/transformers.js +7 -7
- package/dist/transformer.cjs +8 -8
- package/dist/transformer.js +7 -7
- package/dist/{types-C6m-Db26.d.cts → types-Dt1bgHPg.d.cts} +63 -2
- package/dist/{types-Da735j2d.d.ts → types-IRPv4i1Y.d.ts} +63 -2
- package/package.json +2 -2
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
fetchWithAntigravityFailover,
|
|
13
13
|
maybeAntigravityFailoverUrl
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-NDJSLJIA.js";
|
|
15
15
|
import {
|
|
16
16
|
GeminiTransformer
|
|
17
17
|
} from "./chunk-OXBGNEYR.js";
|
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
} from "./chunk-XX6NQJMA.js";
|
|
54
54
|
import {
|
|
55
55
|
extractOpenCodeSessionHeader
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-WYUXJKMK.js";
|
|
57
57
|
import {
|
|
58
58
|
NEVER_FORWARD_HEADERS,
|
|
59
59
|
fillMissingHeaders,
|
|
@@ -147,19 +147,20 @@ import {
|
|
|
147
147
|
} from "./chunk-5NJLQ6T6.js";
|
|
148
148
|
import {
|
|
149
149
|
getAntigravityProjectResolver
|
|
150
|
-
} from "./chunk-
|
|
150
|
+
} from "./chunk-HQ7C3VB6.js";
|
|
151
151
|
import {
|
|
152
|
+
extractReasoningIntent,
|
|
152
153
|
resolveReasoningPlan
|
|
153
154
|
} from "./chunk-ZLNHPUFA.js";
|
|
154
155
|
import {
|
|
155
156
|
fetchUpstream
|
|
156
|
-
} from "./chunk-
|
|
157
|
+
} from "./chunk-LBIXTHP7.js";
|
|
157
158
|
import {
|
|
158
159
|
getSharedAccountAllowanceStore
|
|
159
160
|
} from "./chunk-FYPKKMB3.js";
|
|
160
161
|
import {
|
|
161
162
|
getSharedAccountRouteActivity
|
|
162
|
-
} from "./chunk-
|
|
163
|
+
} from "./chunk-TX7ZM4OI.js";
|
|
163
164
|
import {
|
|
164
165
|
DEFAULT_IMAGE_API_LIMITS,
|
|
165
166
|
ImageGenerationError,
|
|
@@ -953,6 +954,21 @@ async function resolvePoolBoundKey(deps, providerId, provider, sessionId, prefer
|
|
|
953
954
|
}
|
|
954
955
|
return resolveApiKey(resolveProviderEndpoint(provider).apiKey);
|
|
955
956
|
}
|
|
957
|
+
function buildByoRouteActivityMeta(deps, providerId, sessionId) {
|
|
958
|
+
return {
|
|
959
|
+
providerId,
|
|
960
|
+
resolveKeyId: () => {
|
|
961
|
+
if (!sessionId) return void 0;
|
|
962
|
+
const pool = deps.apiKeyPool;
|
|
963
|
+
if (!pool || typeof pool.getKeyIdForSession !== "function") return void 0;
|
|
964
|
+
try {
|
|
965
|
+
return pool.getKeyIdForSession(providerId, sessionId) ?? void 0;
|
|
966
|
+
} catch {
|
|
967
|
+
return void 0;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
}
|
|
956
972
|
|
|
957
973
|
// src/provider-proxy/providerProxyRouteMap.ts
|
|
958
974
|
import { randomBytes } from "crypto";
|
|
@@ -1666,6 +1682,31 @@ function collectText(value) {
|
|
|
1666
1682
|
function proxyProviderId(plan) {
|
|
1667
1683
|
return plan.isSubscription ? plan.transformerProvider.name : "byo";
|
|
1668
1684
|
}
|
|
1685
|
+
function buildAnthropicRouteActivity(plan, proxyAccountId, model, onRecorded) {
|
|
1686
|
+
if (!plan.isSubscription) {
|
|
1687
|
+
const routeSessionId = plan.byoSessionId || void 0;
|
|
1688
|
+
return {
|
|
1689
|
+
providerId: plan.byoActivity?.providerId ?? plan.transformerProvider.name,
|
|
1690
|
+
credentialKind: "provider-key",
|
|
1691
|
+
keyId: plan.byoActivity?.resolveKeyId(),
|
|
1692
|
+
endpoint: "messages",
|
|
1693
|
+
sessionKey: routeSessionId,
|
|
1694
|
+
sessionSource: routeSessionId ? "route-session-id" : "none",
|
|
1695
|
+
model,
|
|
1696
|
+
onRecorded
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
return {
|
|
1700
|
+
providerId: proxyProviderId(plan),
|
|
1701
|
+
credentialKind: "subscription-account",
|
|
1702
|
+
accountId: proxyAccountId,
|
|
1703
|
+
endpoint: "messages",
|
|
1704
|
+
sessionKey: plan.sessionKey,
|
|
1705
|
+
sessionSource: plan.sessionKey ? "content-fingerprint" : "none",
|
|
1706
|
+
model,
|
|
1707
|
+
onRecorded
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1669
1710
|
async function runPipeline(anthropicBody, plan, reportSelection, signal, callerOpenCodeSession) {
|
|
1670
1711
|
const executor = getSharedExecutor();
|
|
1671
1712
|
const endpointTransformer = getAnthropicEndpointTransformer();
|
|
@@ -1715,15 +1756,14 @@ async function runPipeline(anthropicBody, plan, reportSelection, signal, callerO
|
|
|
1715
1756
|
{
|
|
1716
1757
|
providerId: proxyProviderId(plan),
|
|
1717
1758
|
accountId: proxyAccountId,
|
|
1718
|
-
routeActivity:
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
onRecorded: (record5) => {
|
|
1759
|
+
routeActivity: buildAnthropicRouteActivity(
|
|
1760
|
+
plan,
|
|
1761
|
+
proxyAccountId,
|
|
1762
|
+
resolvedModel,
|
|
1763
|
+
(record5) => {
|
|
1724
1764
|
activityRecordId = record5.id;
|
|
1725
1765
|
}
|
|
1726
|
-
|
|
1766
|
+
)
|
|
1727
1767
|
}
|
|
1728
1768
|
).then((r) => {
|
|
1729
1769
|
rawStatus = r.status;
|
|
@@ -1786,15 +1826,14 @@ async function runSubscriptionSameFormatFetch(rawBody, plan, reportSelection, op
|
|
|
1786
1826
|
{
|
|
1787
1827
|
providerId: proxyProviderId(plan),
|
|
1788
1828
|
accountId: proxyAccountId,
|
|
1789
|
-
routeActivity:
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
onRecorded: (record5) => {
|
|
1829
|
+
routeActivity: buildAnthropicRouteActivity(
|
|
1830
|
+
plan,
|
|
1831
|
+
proxyAccountId,
|
|
1832
|
+
outboundModel,
|
|
1833
|
+
(record5) => {
|
|
1795
1834
|
activityRecordId = record5.id;
|
|
1796
1835
|
}
|
|
1797
|
-
|
|
1836
|
+
)
|
|
1798
1837
|
}
|
|
1799
1838
|
);
|
|
1800
1839
|
return {
|
|
@@ -2818,7 +2857,7 @@ async function resolveResponsesRouteProfile(route, deps, resolvedModel) {
|
|
|
2818
2857
|
}
|
|
2819
2858
|
async function buildResponsesCallPlan(route, deps, resolved, resolvedModel, isStream, sessionKey, sessionSource, requestHeaders, affinity) {
|
|
2820
2859
|
if (route.authMode === "byo") {
|
|
2821
|
-
return buildByoPlan(route, deps, resolved, resolvedModel, isStream, affinity);
|
|
2860
|
+
return buildByoPlan(route, deps, resolved, resolvedModel, isStream, sessionKey, sessionSource, affinity);
|
|
2822
2861
|
}
|
|
2823
2862
|
return buildSubscriptionPlan2(
|
|
2824
2863
|
route,
|
|
@@ -2832,7 +2871,7 @@ async function buildResponsesCallPlan(route, deps, resolved, resolvedModel, isSt
|
|
|
2832
2871
|
affinity
|
|
2833
2872
|
);
|
|
2834
2873
|
}
|
|
2835
|
-
async function buildByoPlan(route, deps, resolved, resolvedModel, isStream, affinity) {
|
|
2874
|
+
async function buildByoPlan(route, deps, resolved, resolvedModel, isStream, sessionKey, sessionSource, affinity) {
|
|
2836
2875
|
const providerId = route.providerId;
|
|
2837
2876
|
const provider = resolved.provider;
|
|
2838
2877
|
const affinityKeyId = affinity?.credential.kind === "byo-key" ? affinity.credential.id : void 0;
|
|
@@ -2883,7 +2922,11 @@ async function buildByoPlan(route, deps, resolved, resolvedModel, isStream, affi
|
|
|
2883
2922
|
resolveCredential: () => {
|
|
2884
2923
|
const keyId = route.sessionId && typeof deps.apiKeyPool?.getKeyIdForSession === "function" ? deps.apiKeyPool.getKeyIdForSession(providerId, route.sessionId) : null;
|
|
2885
2924
|
return keyId ? { kind: "byo-key", id: keyId } : credential;
|
|
2886
|
-
}
|
|
2925
|
+
},
|
|
2926
|
+
byoActivity: buildByoRouteActivityMeta(deps, providerId, route.sessionId),
|
|
2927
|
+
byoSessionId: route.sessionId ?? null,
|
|
2928
|
+
sessionKey,
|
|
2929
|
+
sessionSource
|
|
2887
2930
|
};
|
|
2888
2931
|
}
|
|
2889
2932
|
async function buildSubscriptionPlan2(route, deps, resolved, resolvedModel, isStream, sessionKey, sessionSource, requestHeaders, affinity) {
|
|
@@ -2985,6 +3028,32 @@ async function executeResponsesUpstream(body, plan, operation, signal) {
|
|
|
2985
3028
|
}
|
|
2986
3029
|
return result;
|
|
2987
3030
|
}
|
|
3031
|
+
function buildResponsesRouteActivity(plan, accountId, actualModel, onRecorded) {
|
|
3032
|
+
if (plan.proxyProviderId === "byo") {
|
|
3033
|
+
const contentKey = plan.sessionKey?.trim() || void 0;
|
|
3034
|
+
const routeSessionId = plan.byoSessionId || void 0;
|
|
3035
|
+
return {
|
|
3036
|
+
providerId: plan.byoActivity?.providerId ?? plan.providerIdentity,
|
|
3037
|
+
credentialKind: "provider-key",
|
|
3038
|
+
keyId: plan.byoActivity?.resolveKeyId(),
|
|
3039
|
+
endpoint: "responses",
|
|
3040
|
+
sessionKey: contentKey ?? routeSessionId,
|
|
3041
|
+
sessionSource: contentKey ? plan.sessionSource ?? "none" : routeSessionId ? "route-session-id" : "none",
|
|
3042
|
+
model: actualModel,
|
|
3043
|
+
onRecorded
|
|
3044
|
+
};
|
|
3045
|
+
}
|
|
3046
|
+
return {
|
|
3047
|
+
providerId: plan.proxyProviderId,
|
|
3048
|
+
credentialKind: "subscription-account",
|
|
3049
|
+
accountId,
|
|
3050
|
+
endpoint: "responses",
|
|
3051
|
+
sessionKey: plan.sessionKey,
|
|
3052
|
+
sessionSource: plan.sessionSource ?? "none",
|
|
3053
|
+
model: actualModel,
|
|
3054
|
+
onRecorded
|
|
3055
|
+
};
|
|
3056
|
+
}
|
|
2988
3057
|
async function applyPlanAuth(body, plan) {
|
|
2989
3058
|
let accountId;
|
|
2990
3059
|
let actualModel = plan.resolvedModel;
|
|
@@ -3004,6 +3073,7 @@ async function applyPlanAuth(body, plan) {
|
|
|
3004
3073
|
});
|
|
3005
3074
|
body.model = actualModel;
|
|
3006
3075
|
plan.transformerProvider.models = [actualModel];
|
|
3076
|
+
if (plan.proxyProviderId === "codex") body.store = false;
|
|
3007
3077
|
return { headers, accountId, actualModel };
|
|
3008
3078
|
}
|
|
3009
3079
|
function decorateCodexHeaders(headers, plan) {
|
|
@@ -3025,15 +3095,9 @@ async function runNative(body, plan, operation, signal) {
|
|
|
3025
3095
|
{
|
|
3026
3096
|
providerId: plan.proxyProviderId,
|
|
3027
3097
|
accountId,
|
|
3028
|
-
routeActivity: plan
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
sessionSource: plan.sessionSource ?? "none",
|
|
3032
|
-
model: actualModel,
|
|
3033
|
-
onRecorded: (record5) => {
|
|
3034
|
-
activityRecordId = record5.id;
|
|
3035
|
-
}
|
|
3036
|
-
}
|
|
3098
|
+
routeActivity: buildResponsesRouteActivity(plan, accountId, actualModel, (record5) => {
|
|
3099
|
+
activityRecordId = record5.id;
|
|
3100
|
+
})
|
|
3037
3101
|
}
|
|
3038
3102
|
);
|
|
3039
3103
|
return {
|
|
@@ -3078,15 +3142,9 @@ async function runReduced(body, plan, signal) {
|
|
|
3078
3142
|
{
|
|
3079
3143
|
providerId: plan.proxyProviderId,
|
|
3080
3144
|
accountId,
|
|
3081
|
-
routeActivity: plan
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
sessionSource: plan.sessionSource ?? "none",
|
|
3085
|
-
model: actualModel,
|
|
3086
|
-
onRecorded: (record5) => {
|
|
3087
|
-
activityRecordId = record5.id;
|
|
3088
|
-
}
|
|
3089
|
-
}
|
|
3145
|
+
routeActivity: buildResponsesRouteActivity(plan, accountId, actualModel, (record5) => {
|
|
3146
|
+
activityRecordId = record5.id;
|
|
3147
|
+
})
|
|
3090
3148
|
}
|
|
3091
3149
|
);
|
|
3092
3150
|
const upstream = plan.proxyProviderId === "antigravity" ? await fetchWithAntigravityFailover(url, send, signal) : await send(url);
|
|
@@ -4329,7 +4387,9 @@ async function buildByoPlan2(res, route, deps, resolvedModel, isStream) {
|
|
|
4329
4387
|
isSubscription: false,
|
|
4330
4388
|
provider,
|
|
4331
4389
|
apiKey,
|
|
4332
|
-
extendedContextEnabled: route.anthropicSdkHints?.extendedContext?.enabled ?? false
|
|
4390
|
+
extendedContextEnabled: route.anthropicSdkHints?.extendedContext?.enabled ?? false,
|
|
4391
|
+
byoActivity: buildByoRouteActivityMeta(deps, providerId, route.sessionId),
|
|
4392
|
+
byoSessionId: route.sessionId ?? null
|
|
4333
4393
|
};
|
|
4334
4394
|
}
|
|
4335
4395
|
async function runSameFormatFetch(bodyToSend, plan, options, keyOverride, urlOverride) {
|
|
@@ -4355,12 +4415,30 @@ async function runSameFormatFetch(bodyToSend, plan, options, keyOverride, urlOve
|
|
|
4355
4415
|
headers["anthropic-version"] = callerVersion || DEFAULT_ANTHROPIC_VERSION;
|
|
4356
4416
|
const url = urlOverride ?? buildProviderApiUrl(provider, { model: resolvedModel, stream: isStream });
|
|
4357
4417
|
console.info(`[ProviderProxy:anthropic] (same-format) -> ${url} model=${resolvedModel} stream=${isStream}`);
|
|
4418
|
+
let activityRecordId;
|
|
4358
4419
|
const response = await fetchUpstream(
|
|
4359
4420
|
url,
|
|
4360
4421
|
{ method: "POST", headers, body: bodyToSend, signal: options.signal },
|
|
4361
|
-
{
|
|
4422
|
+
{
|
|
4423
|
+
providerId: "byo",
|
|
4424
|
+
routeActivity: {
|
|
4425
|
+
providerId: plan.byoActivity?.providerId ?? provider.name,
|
|
4426
|
+
credentialKind: "provider-key",
|
|
4427
|
+
// LIVE resolution: a pool rebind retry re-invokes this fetch, and the
|
|
4428
|
+
// binding has already rotated, so the retried attempt attributes the
|
|
4429
|
+
// NEW key. `keyOverride` is a key STRING (never recorded).
|
|
4430
|
+
keyId: plan.byoActivity?.resolveKeyId(),
|
|
4431
|
+
endpoint: "messages",
|
|
4432
|
+
sessionKey: plan.byoSessionId || void 0,
|
|
4433
|
+
sessionSource: plan.byoSessionId ? "route-session-id" : "none",
|
|
4434
|
+
model: resolvedModel,
|
|
4435
|
+
onRecorded: (record5) => {
|
|
4436
|
+
activityRecordId = record5.id;
|
|
4437
|
+
}
|
|
4438
|
+
}
|
|
4439
|
+
}
|
|
4362
4440
|
);
|
|
4363
|
-
return { response, rawStatus: response.status };
|
|
4441
|
+
return { response, rawStatus: response.status, activityRecordId };
|
|
4364
4442
|
}
|
|
4365
4443
|
function resolveSameFormatBody(anthropicBody, rawBody, plan) {
|
|
4366
4444
|
let originalModel;
|
|
@@ -4903,7 +4981,9 @@ async function buildByoPlan3(res, route, deps, resolvedModel, isStream) {
|
|
|
4903
4981
|
resolvedModel,
|
|
4904
4982
|
isStream,
|
|
4905
4983
|
resolveUrl: (config) => config.url instanceof URL ? config.url.toString() : byoUrl,
|
|
4906
|
-
upstreamUrl: byoUrl
|
|
4984
|
+
upstreamUrl: byoUrl,
|
|
4985
|
+
byoActivity: buildByoRouteActivityMeta(deps, providerId, route.sessionId),
|
|
4986
|
+
byoSessionId: route.sessionId ?? null
|
|
4907
4987
|
};
|
|
4908
4988
|
}
|
|
4909
4989
|
async function runPipeline2(geminiBody, plan) {
|
|
@@ -4934,7 +5014,18 @@ async function runPipeline2(geminiBody, plan) {
|
|
|
4934
5014
|
return fetchUpstream(
|
|
4935
5015
|
url,
|
|
4936
5016
|
{ method: "POST", headers, body: JSON.stringify(body) },
|
|
4937
|
-
{
|
|
5017
|
+
{
|
|
5018
|
+
providerId: "byo",
|
|
5019
|
+
routeActivity: {
|
|
5020
|
+
providerId: plan.byoActivity?.providerId ?? transformerProvider.name,
|
|
5021
|
+
credentialKind: "provider-key",
|
|
5022
|
+
keyId: plan.byoActivity?.resolveKeyId(),
|
|
5023
|
+
endpoint: "generateContent",
|
|
5024
|
+
sessionKey: plan.byoSessionId || void 0,
|
|
5025
|
+
sessionSource: plan.byoSessionId ? "route-session-id" : "none",
|
|
5026
|
+
model: resolvedModel
|
|
5027
|
+
}
|
|
5028
|
+
}
|
|
4938
5029
|
).then((r) => {
|
|
4939
5030
|
rawStatus = r.status;
|
|
4940
5031
|
return r;
|
|
@@ -5118,7 +5209,9 @@ async function buildByoPlan4(res, route, deps, resolvedModel, isStream) {
|
|
|
5118
5209
|
resolveUrl: (config) => config.url instanceof URL ? config.url.toString() : byoUrl,
|
|
5119
5210
|
upstreamUrl: byoUrl,
|
|
5120
5211
|
proxyProviderId: "byo",
|
|
5121
|
-
isSubscription: false
|
|
5212
|
+
isSubscription: false,
|
|
5213
|
+
byoActivity: buildByoRouteActivityMeta(deps, providerId, route.sessionId),
|
|
5214
|
+
routeSessionId: route.sessionId ?? null
|
|
5122
5215
|
};
|
|
5123
5216
|
}
|
|
5124
5217
|
async function buildSubscriptionPlan3(res, route, deps, resolvedModel, isStream) {
|
|
@@ -5165,7 +5258,8 @@ async function buildSubscriptionPlan3(res, route, deps, resolvedModel, isStream)
|
|
|
5165
5258
|
resolveUrl: (config) => config.url instanceof URL ? config.url.toString() : typeof config.url === "string" ? config.url : upstreamUrl,
|
|
5166
5259
|
upstreamUrl,
|
|
5167
5260
|
proxyProviderId: providerId,
|
|
5168
|
-
isSubscription: true
|
|
5261
|
+
isSubscription: true,
|
|
5262
|
+
routeSessionId: route.sessionId ?? null
|
|
5169
5263
|
};
|
|
5170
5264
|
}
|
|
5171
5265
|
async function runPipeline3(chatBody, plan) {
|
|
@@ -5201,10 +5295,31 @@ async function runPipeline3(chatBody, plan) {
|
|
|
5201
5295
|
},
|
|
5202
5296
|
fetchFn: (url, headers, body) => {
|
|
5203
5297
|
console.log(`[ProviderProxy:chat] -> ${url} model=${resolvedModel} stream=${isStream}`);
|
|
5298
|
+
const sessionKey = plan.routeSessionId || void 0;
|
|
5204
5299
|
return fetchUpstream(
|
|
5205
5300
|
url,
|
|
5206
5301
|
{ method: "POST", headers, body: JSON.stringify(body) },
|
|
5207
|
-
{
|
|
5302
|
+
{
|
|
5303
|
+
providerId: plan.proxyProviderId,
|
|
5304
|
+
accountId: proxyAccountId,
|
|
5305
|
+
routeActivity: plan.isSubscription ? {
|
|
5306
|
+
providerId: plan.proxyProviderId,
|
|
5307
|
+
credentialKind: "subscription-account",
|
|
5308
|
+
accountId: proxyAccountId,
|
|
5309
|
+
endpoint: "chat",
|
|
5310
|
+
sessionKey,
|
|
5311
|
+
sessionSource: sessionKey ? "route-session-id" : "none",
|
|
5312
|
+
model: resolvedModel
|
|
5313
|
+
} : {
|
|
5314
|
+
providerId: plan.byoActivity?.providerId ?? transformerProvider.name,
|
|
5315
|
+
credentialKind: "provider-key",
|
|
5316
|
+
keyId: plan.byoActivity?.resolveKeyId(),
|
|
5317
|
+
endpoint: "chat",
|
|
5318
|
+
sessionKey,
|
|
5319
|
+
sessionSource: sessionKey ? "route-session-id" : "none",
|
|
5320
|
+
model: resolvedModel
|
|
5321
|
+
}
|
|
5322
|
+
}
|
|
5208
5323
|
).then((r) => {
|
|
5209
5324
|
rawStatus = r.status;
|
|
5210
5325
|
return r;
|
|
@@ -5983,17 +6098,22 @@ function wildcardMatches(pattern, value) {
|
|
|
5983
6098
|
const escaped = pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")).join(".*");
|
|
5984
6099
|
return new RegExp(`^${escaped}$`, "iu").test(value);
|
|
5985
6100
|
}
|
|
5986
|
-
function
|
|
6101
|
+
function resolveGatewayModelMappingRow(mappings, requestedModel) {
|
|
5987
6102
|
if (!nonBlank(requestedModel)) return void 0;
|
|
5988
6103
|
const wanted = requestedModel.trim();
|
|
5989
6104
|
const exact = (mappings ?? []).find(
|
|
5990
6105
|
(mapping) => nonBlank(mapping.source) && !mapping.source.includes("*") && mapping.source.trim().toLocaleLowerCase() === wanted.toLocaleLowerCase()
|
|
5991
6106
|
);
|
|
5992
|
-
if (exact && nonBlank(exact.target))
|
|
6107
|
+
if (exact && nonBlank(exact.target)) {
|
|
6108
|
+
return { target: exact.target.trim(), effort: exact.effort };
|
|
6109
|
+
}
|
|
5993
6110
|
const wildcard = (mappings ?? []).find(
|
|
5994
6111
|
(mapping) => nonBlank(mapping.source) && mapping.source.includes("*") && wildcardMatches(mapping.source.trim(), wanted)
|
|
5995
6112
|
);
|
|
5996
|
-
return wildcard && nonBlank(wildcard.target) ? wildcard.target.trim() : void 0;
|
|
6113
|
+
return wildcard && nonBlank(wildcard.target) ? { target: wildcard.target.trim(), effort: wildcard.effort } : void 0;
|
|
6114
|
+
}
|
|
6115
|
+
function resolveGatewayModelMapping(mappings, requestedModel) {
|
|
6116
|
+
return resolveGatewayModelMappingRow(mappings, requestedModel)?.target;
|
|
5997
6117
|
}
|
|
5998
6118
|
function routeCanServe(binding, requestedModel, role) {
|
|
5999
6119
|
if (binding.modelMode === "passthrough") return nonBlank(requestedModel);
|
|
@@ -6067,9 +6187,11 @@ function gatewayBindingToEndpointConfig(binding, requestedModel) {
|
|
|
6067
6187
|
useSubscription: target.kind !== "provider"
|
|
6068
6188
|
};
|
|
6069
6189
|
const usesGenericModelHandling = binding.modelMode === "passthrough" || Boolean(binding.modelMappings?.length);
|
|
6070
|
-
const
|
|
6190
|
+
const winningMapping = binding.modelMode === "passthrough" ? void 0 : resolveGatewayModelMappingRow(binding.modelMappings, requestedModel);
|
|
6191
|
+
const dynamicModel = binding.modelMode === "passthrough" ? requestedModel : winningMapping?.target;
|
|
6071
6192
|
if (usesGenericModelHandling) {
|
|
6072
6193
|
if (nonBlank(dynamicModel)) applySingleModel(config, binding, dynamicModel);
|
|
6194
|
+
if (winningMapping?.effort) config.reasoningEffort = winningMapping.effort;
|
|
6073
6195
|
} else if (isKindMappedEndpoint(binding.endpoint)) {
|
|
6074
6196
|
config.modelMap = Object.fromEntries(
|
|
6075
6197
|
Object.entries(binding.modelMap ?? {}).map(([kind, ref]) => [kind, targetRef(target, ref)])
|
|
@@ -8494,6 +8616,7 @@ function normalizeBindingTarget(raw) {
|
|
|
8494
8616
|
}
|
|
8495
8617
|
return null;
|
|
8496
8618
|
}
|
|
8619
|
+
var MAPPING_EFFORT_MAX_LENGTH = 32;
|
|
8497
8620
|
function normalizeGatewayModelMappings(raw) {
|
|
8498
8621
|
if (!Array.isArray(raw)) return [];
|
|
8499
8622
|
const mappings = [];
|
|
@@ -8503,7 +8626,9 @@ function normalizeGatewayModelMappings(raw) {
|
|
|
8503
8626
|
const source = typeof value.source === "string" ? value.source.trim() : "";
|
|
8504
8627
|
const target = typeof value.target === "string" ? value.target.trim() : "";
|
|
8505
8628
|
if (!source || !target) continue;
|
|
8506
|
-
|
|
8629
|
+
const rawEffort = typeof value.effort === "string" ? value.effort.trim() : "";
|
|
8630
|
+
const effort = rawEffort && rawEffort.length <= MAPPING_EFFORT_MAX_LENGTH ? rawEffort : void 0;
|
|
8631
|
+
mappings.push(effort ? { source, target, effort } : { source, target });
|
|
8507
8632
|
}
|
|
8508
8633
|
return mappings;
|
|
8509
8634
|
}
|
|
@@ -8795,6 +8920,61 @@ function deriveAuditSessionKey(body, headers = {}, options = {}) {
|
|
|
8795
8920
|
return gateway.key;
|
|
8796
8921
|
}
|
|
8797
8922
|
|
|
8923
|
+
// src/outbound-api/mappingEffortInjection.ts
|
|
8924
|
+
function isPlainObject(value) {
|
|
8925
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
8926
|
+
}
|
|
8927
|
+
function hasOpenAiWireReasoningIntent(body) {
|
|
8928
|
+
return extractReasoningIntent({
|
|
8929
|
+
model: typeof body["model"] === "string" ? body["model"] : "",
|
|
8930
|
+
reasoning: isPlainObject(body["reasoning"]) ? body["reasoning"] : void 0,
|
|
8931
|
+
reasoning_effort: body["reasoning_effort"]
|
|
8932
|
+
}) !== void 0;
|
|
8933
|
+
}
|
|
8934
|
+
function injectMappingEffortDefault(endpoint, effort, body) {
|
|
8935
|
+
if (!effort) return false;
|
|
8936
|
+
switch (endpoint) {
|
|
8937
|
+
case "chat":
|
|
8938
|
+
if (hasOpenAiWireReasoningIntent(body)) {
|
|
8939
|
+
return false;
|
|
8940
|
+
}
|
|
8941
|
+
body["reasoning_effort"] = effort;
|
|
8942
|
+
return true;
|
|
8943
|
+
case "responses": {
|
|
8944
|
+
if (hasOpenAiWireReasoningIntent(body)) {
|
|
8945
|
+
return false;
|
|
8946
|
+
}
|
|
8947
|
+
const reasoning = body["reasoning"];
|
|
8948
|
+
body["reasoning"] = {
|
|
8949
|
+
...isPlainObject(reasoning) ? reasoning : {},
|
|
8950
|
+
effort
|
|
8951
|
+
};
|
|
8952
|
+
return true;
|
|
8953
|
+
}
|
|
8954
|
+
case "messages": {
|
|
8955
|
+
if (body["thinking"] !== void 0) return false;
|
|
8956
|
+
if (effort === "none") return false;
|
|
8957
|
+
body["thinking"] = { type: "adaptive" };
|
|
8958
|
+
const outputConfig = body["output_config"];
|
|
8959
|
+
body["output_config"] = {
|
|
8960
|
+
...isPlainObject(outputConfig) ? outputConfig : {},
|
|
8961
|
+
effort
|
|
8962
|
+
};
|
|
8963
|
+
return true;
|
|
8964
|
+
}
|
|
8965
|
+
case "gemini": {
|
|
8966
|
+
const generationConfig = body["generationConfig"];
|
|
8967
|
+
const config = isPlainObject(generationConfig) ? generationConfig : {};
|
|
8968
|
+
if (config["thinkingConfig"] !== void 0) return false;
|
|
8969
|
+
body["generationConfig"] = {
|
|
8970
|
+
...config,
|
|
8971
|
+
thinkingConfig: { thinkingLevel: effort }
|
|
8972
|
+
};
|
|
8973
|
+
return true;
|
|
8974
|
+
}
|
|
8975
|
+
}
|
|
8976
|
+
}
|
|
8977
|
+
|
|
8798
8978
|
// src/outbound-api/outboundApiKeyAuth.ts
|
|
8799
8979
|
import { createHash as createHash3, randomBytes as randomBytes4 } from "crypto";
|
|
8800
8980
|
|
|
@@ -8981,8 +9161,23 @@ function toVerifiedKey(row) {
|
|
|
8981
9161
|
if (rateLimit) key.rateLimit = rateLimit;
|
|
8982
9162
|
const modelRestriction = extractModelRestriction(row);
|
|
8983
9163
|
if (modelRestriction) key.modelRestriction = modelRestriction;
|
|
9164
|
+
const directTarget = extractBoundUpstream(row);
|
|
9165
|
+
if (directTarget) key.boundUpstream = directTarget;
|
|
8984
9166
|
return key;
|
|
8985
9167
|
}
|
|
9168
|
+
function extractBoundUpstream(row) {
|
|
9169
|
+
if (row.boundUpstream && typeof row.boundUpstream === "object") {
|
|
9170
|
+
const target = row.boundUpstream;
|
|
9171
|
+
if (target.kind === "provider" && typeof target.providerId === "string" && target.providerId || target.kind === "account-pool" && typeof target.providerId === "string" && target.providerId || target.kind === "account" && target.providerId && target.accountId || target.kind === "account-group" && target.providerId && target.group) {
|
|
9172
|
+
return target;
|
|
9173
|
+
}
|
|
9174
|
+
return void 0;
|
|
9175
|
+
}
|
|
9176
|
+
if (typeof row.boundUpstreamProviderId === "string" && row.boundUpstreamProviderId) {
|
|
9177
|
+
return { kind: "provider", providerId: row.boundUpstreamProviderId };
|
|
9178
|
+
}
|
|
9179
|
+
return void 0;
|
|
9180
|
+
}
|
|
8986
9181
|
async function verifyKey(db, presentedKey, now = Date.now()) {
|
|
8987
9182
|
if (!presentedKey) return { status: "invalid" };
|
|
8988
9183
|
const trimmed = presentedKey.trim();
|
|
@@ -9908,7 +10103,102 @@ function resolveModelsShape(modelsShape, allowedEndpoints) {
|
|
|
9908
10103
|
if (!allowedEndpoints || allowedEndpoints.length === 0) return "anthropic";
|
|
9909
10104
|
return allowedEndpoints.includes("messages") ? "anthropic" : "openai";
|
|
9910
10105
|
}
|
|
10106
|
+
var UPSTREAM_MODELS_TTL_MS = 6e4;
|
|
10107
|
+
var UPSTREAM_MODELS_NEGATIVE_TTL_MS = 5e3;
|
|
10108
|
+
var UPSTREAM_MODELS_TIMEOUT_MS = 1e4;
|
|
10109
|
+
var upstreamModelsCache = /* @__PURE__ */ new Map();
|
|
10110
|
+
var upstreamModelsInFlight = /* @__PURE__ */ new Map();
|
|
10111
|
+
function resolveProviderKeyLiteral(apiKey) {
|
|
10112
|
+
if (!apiKey) return "";
|
|
10113
|
+
if (apiKey.startsWith("$")) return process.env[apiKey.slice(1)] || "";
|
|
10114
|
+
return apiKey;
|
|
10115
|
+
}
|
|
10116
|
+
function buildProviderModelsUrl(baseUrl, format) {
|
|
10117
|
+
const base = baseUrl.trim().replace(/\/+$/, "");
|
|
10118
|
+
if (!base) return null;
|
|
10119
|
+
if (format === "google") {
|
|
10120
|
+
return /\/models$/.test(base) ? base : `${base}/models`;
|
|
10121
|
+
}
|
|
10122
|
+
if (format === "anthropic") {
|
|
10123
|
+
if (base.endsWith("/messages")) return `${base.slice(0, -"/messages".length)}/models`;
|
|
10124
|
+
if (/\/v\d+$/.test(base)) return `${base}/models`;
|
|
10125
|
+
return `${base}/v1/models`;
|
|
10126
|
+
}
|
|
10127
|
+
const completionSuffix = format === "openai-response" ? "/responses" : "/chat/completions";
|
|
10128
|
+
const root = base.endsWith(completionSuffix) ? base.slice(0, -completionSuffix.length) : base;
|
|
10129
|
+
if (root !== base || /\/v\d+$/.test(base)) return `${root}/models`;
|
|
10130
|
+
return `${base}/v1/models`;
|
|
10131
|
+
}
|
|
10132
|
+
async function fetchUpstreamModelsUncached(provider) {
|
|
10133
|
+
const format = resolveApiFormat(provider);
|
|
10134
|
+
const url = buildProviderModelsUrl(provider.api_base_url ?? "", format);
|
|
10135
|
+
if (!url) return [];
|
|
10136
|
+
const headers = getProviderHeaders(provider, resolveProviderKeyLiteral(provider.api_key));
|
|
10137
|
+
delete headers["Content-Type"];
|
|
10138
|
+
headers["Accept"] = "application/json";
|
|
10139
|
+
const response = await fetchUpstream(
|
|
10140
|
+
url,
|
|
10141
|
+
{ method: "GET", headers, signal: AbortSignal.timeout(UPSTREAM_MODELS_TIMEOUT_MS) },
|
|
10142
|
+
{ providerId: "byo" }
|
|
10143
|
+
);
|
|
10144
|
+
if (!response.ok) return [];
|
|
10145
|
+
const body = await response.json();
|
|
10146
|
+
const fromData = Array.isArray(body?.data) ? body.data.map((entry) => typeof entry?.id === "string" ? entry.id.trim() : "").filter((id) => id !== "") : [];
|
|
10147
|
+
if (fromData.length > 0) return fromData;
|
|
10148
|
+
return Array.isArray(body?.models) ? body.models.map(
|
|
10149
|
+
(entry) => typeof entry?.name === "string" ? entry.name.replace(/^models\//, "").trim() : ""
|
|
10150
|
+
).filter((id) => id !== "") : [];
|
|
10151
|
+
}
|
|
10152
|
+
async function discoverUpstreamModels(provider) {
|
|
10153
|
+
const cached = upstreamModelsCache.get(provider.id);
|
|
10154
|
+
if (cached) {
|
|
10155
|
+
const ttl = cached.ok ? UPSTREAM_MODELS_TTL_MS : UPSTREAM_MODELS_NEGATIVE_TTL_MS;
|
|
10156
|
+
if (Date.now() - cached.at < ttl) return cached.ids;
|
|
10157
|
+
upstreamModelsCache.delete(provider.id);
|
|
10158
|
+
}
|
|
10159
|
+
const existing = upstreamModelsInFlight.get(provider.id);
|
|
10160
|
+
if (existing) return existing;
|
|
10161
|
+
const pending = (async () => {
|
|
10162
|
+
let ids = [];
|
|
10163
|
+
try {
|
|
10164
|
+
ids = await fetchUpstreamModelsUncached(provider);
|
|
10165
|
+
} catch {
|
|
10166
|
+
ids = [];
|
|
10167
|
+
}
|
|
10168
|
+
upstreamModelsCache.set(provider.id, { at: Date.now(), ids, ok: ids.length > 0 });
|
|
10169
|
+
return ids;
|
|
10170
|
+
})();
|
|
10171
|
+
upstreamModelsInFlight.set(provider.id, pending);
|
|
10172
|
+
try {
|
|
10173
|
+
return await pending;
|
|
10174
|
+
} finally {
|
|
10175
|
+
upstreamModelsInFlight.delete(provider.id);
|
|
10176
|
+
}
|
|
10177
|
+
}
|
|
10178
|
+
async function passthroughProviderModelIds(llmConfig, providerId) {
|
|
10179
|
+
const provider = await llmConfig.getProvider(providerId);
|
|
10180
|
+
if (!provider) return [];
|
|
10181
|
+
const configured = provider.models ?? [];
|
|
10182
|
+
return configured.length > 0 ? configured : await discoverUpstreamModels(provider);
|
|
10183
|
+
}
|
|
9911
10184
|
var ANTHROPIC_MODELS_CREATED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
10185
|
+
function hasWildcardMapping(binding) {
|
|
10186
|
+
return (binding.modelMappings ?? []).some(
|
|
10187
|
+
(mapping) => mapping.source.includes("*") && mapping.target.trim() !== ""
|
|
10188
|
+
);
|
|
10189
|
+
}
|
|
10190
|
+
async function pushTargetCatalog(llmConfig, target, push) {
|
|
10191
|
+
if (target.kind === "provider") {
|
|
10192
|
+
for (const id of await passthroughProviderModelIds(llmConfig, target.providerId)) {
|
|
10193
|
+
push(id);
|
|
10194
|
+
}
|
|
10195
|
+
} else if (Object.prototype.hasOwnProperty.call(SUBSCRIPTION_MODEL_CATALOG, target.providerId)) {
|
|
10196
|
+
for (const id of SUBSCRIPTION_MODEL_CATALOG[target.providerId]) {
|
|
10197
|
+
push(id);
|
|
10198
|
+
}
|
|
10199
|
+
}
|
|
10200
|
+
}
|
|
10201
|
+
var MODEL_LIST_ENDPOINTS = ["chat", "responses", "messages", "gemini"];
|
|
9912
10202
|
function parseModelsLimit(url) {
|
|
9913
10203
|
const query = url?.split("?")[1];
|
|
9914
10204
|
if (!query) return void 0;
|
|
@@ -9917,8 +10207,7 @@ function parseModelsLimit(url) {
|
|
|
9917
10207
|
const n = Number.parseInt(raw, 10);
|
|
9918
10208
|
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
9919
10209
|
}
|
|
9920
|
-
async function writeModelsListAnthropic(res, llmConfig, config, apiKeyId, allowedEndpoints, limit) {
|
|
9921
|
-
void allowedEndpoints;
|
|
10210
|
+
async function writeModelsListAnthropic(res, llmConfig, config, apiKeyId, allowedEndpoints, limit, directTarget) {
|
|
9922
10211
|
const entries2 = [];
|
|
9923
10212
|
const seen = /* @__PURE__ */ new Set();
|
|
9924
10213
|
const push = (id, displayName) => {
|
|
@@ -9932,29 +10221,37 @@ async function writeModelsListAnthropic(res, llmConfig, config, apiKeyId, allowe
|
|
|
9932
10221
|
const modelId = ref ? parseModelRef(ref)?.modelId : void 0;
|
|
9933
10222
|
return modelId ? `${name} (via ${modelId})` : void 0;
|
|
9934
10223
|
};
|
|
9935
|
-
for (const
|
|
9936
|
-
if (
|
|
9937
|
-
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
9942
|
-
|
|
10224
|
+
for (const endpoint of MODEL_LIST_ENDPOINTS) {
|
|
10225
|
+
if (allowedEndpoints && !allowedEndpoints.includes(endpoint)) continue;
|
|
10226
|
+
for (const binding of candidateGatewayBindings(config.bindings, apiKeyId, endpoint)) {
|
|
10227
|
+
if (binding.modelMode === "passthrough") {
|
|
10228
|
+
await pushTargetCatalog(llmConfig, binding.target, push);
|
|
10229
|
+
continue;
|
|
10230
|
+
}
|
|
10231
|
+
if (binding.modelMappings?.length) {
|
|
10232
|
+
for (const mapping of binding.modelMappings) {
|
|
10233
|
+
const source = mapping.source.trim();
|
|
10234
|
+
if (source === "" || source.includes("*")) continue;
|
|
10235
|
+
push(source, viaLabel(source, mapping.target));
|
|
10236
|
+
}
|
|
10237
|
+
if (hasWildcardMapping(binding)) {
|
|
10238
|
+
await pushTargetCatalog(llmConfig, binding.target, push);
|
|
10239
|
+
for (const mapping of binding.modelMappings ?? []) {
|
|
10240
|
+
if (!mapping.source.includes("*") || mapping.target.trim() === "") continue;
|
|
10241
|
+
push(parseModelRef(mapping.target)?.modelId ?? mapping.target.trim());
|
|
10242
|
+
}
|
|
9943
10243
|
}
|
|
10244
|
+
continue;
|
|
9944
10245
|
}
|
|
9945
|
-
|
|
9946
|
-
|
|
9947
|
-
|
|
9948
|
-
for (const mapping of binding.modelMappings) {
|
|
9949
|
-
const source = mapping.source.trim();
|
|
9950
|
-
if (source === "" || source.includes("*")) continue;
|
|
9951
|
-
push(source, viaLabel(source, mapping.target));
|
|
10246
|
+
for (const [kind, ref] of Object.entries(binding.modelMap ?? {})) {
|
|
10247
|
+
if (typeof ref !== "string" || ref.trim() === "") continue;
|
|
10248
|
+
push(kind, viaLabel(kind, ref));
|
|
9952
10249
|
}
|
|
9953
|
-
continue;
|
|
9954
10250
|
}
|
|
9955
|
-
|
|
9956
|
-
|
|
9957
|
-
|
|
10251
|
+
}
|
|
10252
|
+
if (directTarget?.kind === "provider") {
|
|
10253
|
+
for (const id of await passthroughProviderModelIds(llmConfig, directTarget.providerId)) {
|
|
10254
|
+
push(id);
|
|
9958
10255
|
}
|
|
9959
10256
|
}
|
|
9960
10257
|
const limited = limit !== void 0 ? entries2.slice(0, limit) : entries2;
|
|
@@ -9996,29 +10293,172 @@ function handleOauthUsageProxy(res, config, apiKeyId) {
|
|
|
9996
10293
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
9997
10294
|
res.end(JSON.stringify(body));
|
|
9998
10295
|
}
|
|
9999
|
-
|
|
10296
|
+
function directUpstreamRoot(baseUrl, format) {
|
|
10297
|
+
const base = baseUrl.trim().replace(/\/+$/, "");
|
|
10298
|
+
if (!base) return null;
|
|
10299
|
+
if (format === "google") return base.replace(/\/models$/, "");
|
|
10300
|
+
if (format === "anthropic") return base.replace(/\/messages$/, "");
|
|
10301
|
+
if (format === "openai-response") return base.replace(/\/responses$/, "");
|
|
10302
|
+
return base.replace(/\/chat\/completions$/, "");
|
|
10303
|
+
}
|
|
10304
|
+
function directUpstreamUrl(provider, requestUrl) {
|
|
10305
|
+
const root = directUpstreamRoot(
|
|
10306
|
+
provider.api_base_url ?? "",
|
|
10307
|
+
resolveApiFormat(provider)
|
|
10308
|
+
);
|
|
10309
|
+
if (!root) return null;
|
|
10310
|
+
const raw = requestUrl ?? "/";
|
|
10311
|
+
const queryIndex = raw.indexOf("?");
|
|
10312
|
+
const path = (queryIndex >= 0 ? raw.slice(0, queryIndex) : raw) || "/";
|
|
10313
|
+
const query = queryIndex >= 0 ? raw.slice(queryIndex) : "";
|
|
10314
|
+
const versionSegment = /\/v\d+[a-z]*$/.exec(root)?.[0];
|
|
10315
|
+
let tail = path;
|
|
10316
|
+
if (versionSegment) {
|
|
10317
|
+
if (path === versionSegment) tail = "";
|
|
10318
|
+
else if (path.startsWith(`${versionSegment}/`)) tail = path.slice(versionSegment.length);
|
|
10319
|
+
}
|
|
10320
|
+
return `${root}${tail}${query}`;
|
|
10321
|
+
}
|
|
10322
|
+
function directUpstreamBinding(target, apiKeyId, priority = 1e3) {
|
|
10323
|
+
return {
|
|
10324
|
+
id: `direct:${apiKeyId}`,
|
|
10325
|
+
name: "direct upstream",
|
|
10326
|
+
enabled: true,
|
|
10327
|
+
keyScope: "selected",
|
|
10328
|
+
apiKeyIds: [apiKeyId],
|
|
10329
|
+
endpoint: "messages",
|
|
10330
|
+
target,
|
|
10331
|
+
priority,
|
|
10332
|
+
fallback: "fail",
|
|
10333
|
+
modelMode: "passthrough"
|
|
10334
|
+
};
|
|
10335
|
+
}
|
|
10336
|
+
async function relayDirectUpstream(req, res, deps, verified, config, concurrencyGate, audit, preReadBody) {
|
|
10337
|
+
const direct = verified.boundUpstream;
|
|
10338
|
+
const providerId = direct?.kind === "provider" ? direct.providerId : "";
|
|
10339
|
+
const provider = await deps.llmConfig.getProvider(providerId);
|
|
10340
|
+
if (!provider) {
|
|
10341
|
+
writeJsonError(
|
|
10342
|
+
res,
|
|
10343
|
+
503,
|
|
10344
|
+
`direct upstream provider '${providerId}' is not configured for this key`
|
|
10345
|
+
);
|
|
10346
|
+
return;
|
|
10347
|
+
}
|
|
10348
|
+
const url = directUpstreamUrl(provider, req.url);
|
|
10349
|
+
if (!url) {
|
|
10350
|
+
writeJsonError(res, 503, `direct upstream provider '${providerId}' has no base URL`);
|
|
10351
|
+
return;
|
|
10352
|
+
}
|
|
10353
|
+
const concurrencyLimit = verified.maxConcurrency;
|
|
10354
|
+
let releaseConcurrency = null;
|
|
10355
|
+
if (typeof concurrencyLimit === "number" && concurrencyLimit > 0) {
|
|
10356
|
+
const cq = config.concurrencyQueue ?? DEFAULT_CONCURRENCY_QUEUE;
|
|
10357
|
+
const acquisition = concurrencyGate.acquire(verified.id, concurrencyLimit, {
|
|
10358
|
+
maxQueueSizeFactor: cq.maxQueueSizeFactor,
|
|
10359
|
+
minQueueSize: cq.minQueueSize,
|
|
10360
|
+
waitTimeoutMs: cq.waitTimeoutMs
|
|
10361
|
+
});
|
|
10362
|
+
const cancelOnClose = () => acquisition.cancel();
|
|
10363
|
+
res.once("close", cancelOnClose);
|
|
10364
|
+
let slot;
|
|
10365
|
+
try {
|
|
10366
|
+
slot = await acquisition.granted;
|
|
10367
|
+
} catch (err) {
|
|
10368
|
+
res.removeListener("close", cancelOnClose);
|
|
10369
|
+
if (isConcurrencyRejection(err)) {
|
|
10370
|
+
writeJsonError(res, 429, "Concurrency limit exceeded", { "Retry-After": "5" });
|
|
10371
|
+
return;
|
|
10372
|
+
}
|
|
10373
|
+
throw err;
|
|
10374
|
+
}
|
|
10375
|
+
res.removeListener("close", cancelOnClose);
|
|
10376
|
+
const release2 = () => slot.release();
|
|
10377
|
+
releaseConcurrency = release2;
|
|
10378
|
+
res.once("close", release2);
|
|
10379
|
+
}
|
|
10380
|
+
try {
|
|
10381
|
+
const rawBody = preReadBody !== void 0 ? preReadBody : await readBody3(req);
|
|
10382
|
+
if (audit) {
|
|
10383
|
+
audit.setRequestBody(rawBody);
|
|
10384
|
+
try {
|
|
10385
|
+
audit.sessionKey = deriveAuditSessionKey(
|
|
10386
|
+
rawBody ? JSON.parse(rawBody) : {},
|
|
10387
|
+
req.headers,
|
|
10388
|
+
{ fallbackKey: verified.id, endpoint: "direct" }
|
|
10389
|
+
);
|
|
10390
|
+
} catch {
|
|
10391
|
+
}
|
|
10392
|
+
}
|
|
10393
|
+
const headers = getProviderHeaders(provider, resolveProviderKeyLiteral(provider.api_key));
|
|
10394
|
+
delete headers["Content-Type"];
|
|
10395
|
+
const clientContentType = req.headers["content-type"];
|
|
10396
|
+
if (typeof clientContentType === "string" && clientContentType) {
|
|
10397
|
+
headers["Content-Type"] = clientContentType;
|
|
10398
|
+
}
|
|
10399
|
+
const clientAccept = req.headers["accept"];
|
|
10400
|
+
if (typeof clientAccept === "string" && clientAccept) headers["Accept"] = clientAccept;
|
|
10401
|
+
const hasRequestBody = req.method !== "GET" && req.method !== "HEAD";
|
|
10402
|
+
const upstream = await fetchUpstream(
|
|
10403
|
+
url,
|
|
10404
|
+
{
|
|
10405
|
+
method: req.method ?? "GET",
|
|
10406
|
+
headers,
|
|
10407
|
+
...hasRequestBody ? { body: rawBody } : {},
|
|
10408
|
+
// Client hang-up aborts the upstream call (no egress after cancel).
|
|
10409
|
+
signal: requestLifecycleSignal(req, res)
|
|
10410
|
+
},
|
|
10411
|
+
{ providerId: "byo" }
|
|
10412
|
+
);
|
|
10413
|
+
if (audit) audit.provider = provider.id;
|
|
10414
|
+
const responseHeaders = {};
|
|
10415
|
+
const contentType = upstream.headers.get("content-type");
|
|
10416
|
+
if (contentType) responseHeaders["Content-Type"] = contentType;
|
|
10417
|
+
res.writeHead(upstream.status, responseHeaders);
|
|
10418
|
+
if (!upstream.body) {
|
|
10419
|
+
res.end();
|
|
10420
|
+
return;
|
|
10421
|
+
}
|
|
10422
|
+
const bodyStream = Readable2.fromWeb(
|
|
10423
|
+
upstream.body
|
|
10424
|
+
);
|
|
10425
|
+
bodyStream.on("error", () => {
|
|
10426
|
+
res.destroy();
|
|
10427
|
+
});
|
|
10428
|
+
bodyStream.pipe(res);
|
|
10429
|
+
await new Promise((resolve) => {
|
|
10430
|
+
res.once("close", resolve);
|
|
10431
|
+
});
|
|
10432
|
+
} catch (err) {
|
|
10433
|
+
const message = serializeError(err);
|
|
10434
|
+
if (deps.logger) deps.logger.error("[OutboundApi] direct relay error:", message);
|
|
10435
|
+
else console.error("[OutboundApi] direct relay error:", message);
|
|
10436
|
+
emitWebhookEvent({ kind: "server.error", at: Date.now(), message });
|
|
10437
|
+
if (audit) audit.error = message;
|
|
10438
|
+
if (!res.headersSent) writeJsonError(res, 502, message);
|
|
10439
|
+
else res.destroy();
|
|
10440
|
+
}
|
|
10441
|
+
}
|
|
10442
|
+
async function writeModelsList(res, llmConfig, config, apiKeyId, allowedEndpoints, imageModels = [], directTarget) {
|
|
10000
10443
|
const modelIds = [];
|
|
10001
|
-
for (const endpoint of
|
|
10444
|
+
for (const endpoint of MODEL_LIST_ENDPOINTS) {
|
|
10002
10445
|
if (allowedEndpoints && !allowedEndpoints.includes(endpoint)) continue;
|
|
10003
10446
|
for (const binding of candidateGatewayBindings(config.bindings, apiKeyId, endpoint)) {
|
|
10004
10447
|
if (binding.modelMode === "passthrough") {
|
|
10005
|
-
|
|
10006
|
-
const provider = await llmConfig.getProvider(binding.target.providerId);
|
|
10007
|
-
modelIds.push(...provider?.models ?? []);
|
|
10008
|
-
} else if (Object.prototype.hasOwnProperty.call(
|
|
10009
|
-
SUBSCRIPTION_MODEL_CATALOG,
|
|
10010
|
-
binding.target.providerId
|
|
10011
|
-
)) {
|
|
10012
|
-
modelIds.push(
|
|
10013
|
-
...SUBSCRIPTION_MODEL_CATALOG[binding.target.providerId]
|
|
10014
|
-
);
|
|
10015
|
-
}
|
|
10448
|
+
await pushTargetCatalog(llmConfig, binding.target, (id) => modelIds.push(id));
|
|
10016
10449
|
continue;
|
|
10017
10450
|
}
|
|
10018
10451
|
if (binding.modelMappings?.length) {
|
|
10019
10452
|
modelIds.push(
|
|
10020
10453
|
...binding.modelMappings.map((mapping) => mapping.source.trim()).filter((source) => source !== "" && !source.includes("*"))
|
|
10021
10454
|
);
|
|
10455
|
+
if (hasWildcardMapping(binding)) {
|
|
10456
|
+
await pushTargetCatalog(llmConfig, binding.target, (id) => modelIds.push(id));
|
|
10457
|
+
for (const mapping of binding.modelMappings ?? []) {
|
|
10458
|
+
if (!mapping.source.includes("*") || mapping.target.trim() === "") continue;
|
|
10459
|
+
modelIds.push(parseModelRef(mapping.target)?.modelId ?? mapping.target.trim());
|
|
10460
|
+
}
|
|
10461
|
+
}
|
|
10022
10462
|
continue;
|
|
10023
10463
|
}
|
|
10024
10464
|
const endpointConfig = gatewayBindingToEndpointConfig(binding);
|
|
@@ -10035,6 +10475,9 @@ async function writeModelsList(res, llmConfig, config, apiKeyId, allowedEndpoint
|
|
|
10035
10475
|
);
|
|
10036
10476
|
}
|
|
10037
10477
|
}
|
|
10478
|
+
if (directTarget?.kind === "provider") {
|
|
10479
|
+
modelIds.push(...await passthroughProviderModelIds(llmConfig, directTarget.providerId));
|
|
10480
|
+
}
|
|
10038
10481
|
modelIds.push(...imageModels);
|
|
10039
10482
|
const seen = /* @__PURE__ */ new Set();
|
|
10040
10483
|
const data = modelIds.filter((modelId) => {
|
|
@@ -10129,6 +10572,16 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
|
|
|
10129
10572
|
});
|
|
10130
10573
|
return;
|
|
10131
10574
|
}
|
|
10575
|
+
const directProvider = verified.boundUpstream?.kind === "provider" ? verified.boundUpstream : null;
|
|
10576
|
+
if (verified.boundUpstream && !directProvider) {
|
|
10577
|
+
config = {
|
|
10578
|
+
...config,
|
|
10579
|
+
bindings: [
|
|
10580
|
+
...config.bindings ?? [],
|
|
10581
|
+
directUpstreamBinding(verified.boundUpstream, verified.id, 1e3)
|
|
10582
|
+
]
|
|
10583
|
+
};
|
|
10584
|
+
}
|
|
10132
10585
|
if (isOauthUsagePath) {
|
|
10133
10586
|
if (req.method !== "GET") {
|
|
10134
10587
|
writeJsonError(res, 404, `Unsupported: ${req.method} ${req.url}`);
|
|
@@ -10192,7 +10645,8 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
|
|
|
10192
10645
|
config,
|
|
10193
10646
|
verified.id,
|
|
10194
10647
|
verified.allowedEndpoints,
|
|
10195
|
-
parseModelsLimit(req.url)
|
|
10648
|
+
parseModelsLimit(req.url),
|
|
10649
|
+
verified.boundUpstream
|
|
10196
10650
|
);
|
|
10197
10651
|
} else {
|
|
10198
10652
|
let imageModels = [];
|
|
@@ -10208,21 +10662,26 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
|
|
|
10208
10662
|
config,
|
|
10209
10663
|
verified.id,
|
|
10210
10664
|
verified.allowedEndpoints,
|
|
10211
|
-
imageModels
|
|
10665
|
+
imageModels,
|
|
10666
|
+
verified.boundUpstream
|
|
10212
10667
|
);
|
|
10213
10668
|
}
|
|
10214
10669
|
return;
|
|
10215
10670
|
}
|
|
10216
10671
|
const endpoint = selectEndpoint(req.method, req.url);
|
|
10217
10672
|
if (!endpoint) {
|
|
10673
|
+
if (directProvider) {
|
|
10674
|
+
await relayDirectUpstream(req, res, deps, verified, config, concurrencyGate, audit);
|
|
10675
|
+
return;
|
|
10676
|
+
}
|
|
10218
10677
|
writeJsonError(res, 404, `Unsupported: ${req.method} ${req.url}`);
|
|
10219
10678
|
return;
|
|
10220
10679
|
}
|
|
10221
|
-
if (verified.allowedEndpoints && !verified.allowedEndpoints.includes(endpoint)) {
|
|
10680
|
+
if (verified.allowedEndpoints && !verified.boundUpstream && !verified.allowedEndpoints.includes(endpoint)) {
|
|
10222
10681
|
writeJsonError(res, 403, "API key is not allowed to access this endpoint");
|
|
10223
10682
|
return;
|
|
10224
10683
|
}
|
|
10225
|
-
if (candidateGatewayBindings(config.bindings, verified.id, endpoint).length === 0) {
|
|
10684
|
+
if (candidateGatewayBindings(config.bindings, verified.id, endpoint).length === 0 && !directProvider) {
|
|
10226
10685
|
writeJsonError(res, 503, `endpoint '${endpoint}' has no downstream route for this key`);
|
|
10227
10686
|
return;
|
|
10228
10687
|
}
|
|
@@ -10316,22 +10775,31 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
|
|
|
10316
10775
|
role
|
|
10317
10776
|
});
|
|
10318
10777
|
if (bindingResolution.source === "none") {
|
|
10778
|
+
if (directProvider) {
|
|
10779
|
+
await relayDirectUpstream(req, res, deps, verified, config, concurrencyGate, audit, rawBody);
|
|
10780
|
+
return;
|
|
10781
|
+
}
|
|
10319
10782
|
writeJsonError(res, 503, `endpoint '${endpoint}' has no downstream route for this key`);
|
|
10320
10783
|
return;
|
|
10321
10784
|
}
|
|
10322
10785
|
const effectiveEndpointConfig = bindingResolution.config;
|
|
10323
10786
|
const bindingAffinitySuffix = `:${bindingResolution.binding.id}`;
|
|
10324
10787
|
const sessionId = deps.proxyDeps.apiKeyPool ? `outbound:${verified.id}${bindingAffinitySuffix}` : null;
|
|
10788
|
+
let replayBody = rawBody;
|
|
10789
|
+
if (!isCountTokens && effectiveEndpointConfig.reasoningEffort && injectMappingEffortDefault(endpoint, effectiveEndpointConfig.reasoningEffort, parsedBody)) {
|
|
10790
|
+
replayBody = JSON.stringify(parsedBody);
|
|
10791
|
+
}
|
|
10325
10792
|
const hostedImageGenerationAllowed = verified.allowedEndpoints.includes("images");
|
|
10793
|
+
const listResolvedModel = endpoint === "chat" && bindingResolution.binding.modelMode === "mapped" ? resolveGatewayModelMappingRow(bindingResolution.binding.modelMappings, requestedModel)?.target ?? requestedModel : requestedModel;
|
|
10326
10794
|
const resolved = isKindMappedEndpoint(endpoint) || endpoint === "chat" ? await resolveRoute({
|
|
10327
10795
|
config: effectiveEndpointConfig,
|
|
10328
10796
|
ingressFormat,
|
|
10329
10797
|
llmConfig: deps.llmConfig,
|
|
10330
10798
|
sessionId,
|
|
10331
|
-
//
|
|
10332
|
-
// kind-mapped endpoints it selects the kind AND is stamped onto
|
|
10333
|
-
// `route.requestedModel
|
|
10334
|
-
requestedModel,
|
|
10799
|
+
// The id the route resolves against: the client's ORIGINAL id for
|
|
10800
|
+
// kind-mapped endpoints (it selects the kind AND is stamped onto
|
|
10801
|
+
// `route.requestedModel`), the mapping-resolved target for chat.
|
|
10802
|
+
requestedModel: listResolvedModel,
|
|
10335
10803
|
// Attribution: stamp the verified named-key id onto the route.
|
|
10336
10804
|
apiKeyId: verified.id,
|
|
10337
10805
|
hostedImageGenerationAllowed
|
|
@@ -10346,6 +10814,10 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
|
|
|
10346
10814
|
hostedImageGenerationAllowed
|
|
10347
10815
|
});
|
|
10348
10816
|
if (!resolved.ok) {
|
|
10817
|
+
if (directProvider) {
|
|
10818
|
+
await relayDirectUpstream(req, res, deps, verified, config, concurrencyGate, audit, rawBody);
|
|
10819
|
+
return;
|
|
10820
|
+
}
|
|
10349
10821
|
writeJsonError(res, resolved.error.status, resolved.error.message);
|
|
10350
10822
|
return;
|
|
10351
10823
|
}
|
|
@@ -10407,7 +10879,7 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
|
|
|
10407
10879
|
);
|
|
10408
10880
|
try {
|
|
10409
10881
|
shimAuthHeader(req, token);
|
|
10410
|
-
const replay = makeReplayRequest(req,
|
|
10882
|
+
const replay = makeReplayRequest(req, replayBody);
|
|
10411
10883
|
await routeRequest(replay, res, routeMap, deps.proxyDeps);
|
|
10412
10884
|
} catch (err) {
|
|
10413
10885
|
if (isBoundAccountSelectionError(err)) {
|
|
@@ -13108,6 +13580,7 @@ export {
|
|
|
13108
13580
|
writeError,
|
|
13109
13581
|
writeBoundAccountError,
|
|
13110
13582
|
resolvePoolBoundKey,
|
|
13583
|
+
buildByoRouteActivityMeta,
|
|
13111
13584
|
DEFAULT_ROUTE_IDLE_MS,
|
|
13112
13585
|
ProviderProxyRouteMap,
|
|
13113
13586
|
previousResponseNotFound,
|
|
@@ -13187,6 +13660,7 @@ export {
|
|
|
13187
13660
|
loadServerConfig,
|
|
13188
13661
|
saveServerConfig,
|
|
13189
13662
|
mergeServerConfig,
|
|
13663
|
+
injectMappingEffortDefault,
|
|
13190
13664
|
computeKeyExpiry,
|
|
13191
13665
|
checkKeyQuota,
|
|
13192
13666
|
randomBase62,
|