@relayplane/proxy 1.9.14 → 1.9.16
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standalone-proxy.d.ts","sourceRoot":"","sources":["../src/standalone-proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAOlC,OAAO,KAAK,EAAE,QAAQ,IAAI,YAAY,EAAY,MAAM,kBAAkB,CAAC;AAE3E,KAAK,QAAQ,GAAG,YAAY,GACxB,YAAY,GACZ,UAAU,GACV,MAAM,GACN,SAAS,GACT,UAAU,GACV,WAAW,GACX,YAAY,GACZ,QAAQ,CAAC;AAOb,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"standalone-proxy.d.ts","sourceRoot":"","sources":["../src/standalone-proxy.ts"],"names":[],"mappings":"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"}
|
package/dist/standalone-proxy.js
CHANGED
|
@@ -109,6 +109,12 @@ const PROXY_VERSION = (() => {
|
|
|
109
109
|
return '0.0.0';
|
|
110
110
|
}
|
|
111
111
|
})();
|
|
112
|
+
/** Returns true when the model is a Haiku variant (does not support extended thinking) */
|
|
113
|
+
function isHaikuModel(model) {
|
|
114
|
+
return model.includes('haiku');
|
|
115
|
+
}
|
|
116
|
+
/** Beta flags that OAT tokens (sk-ant-oat*) do not support */
|
|
117
|
+
const OAT_UNSUPPORTED_BETA_FLAGS = new Set(['max-tokens-3-5-sonnet-2025-04-14']);
|
|
112
118
|
let latestProxyVersionCache = { value: null, checkedAt: 0 };
|
|
113
119
|
const LATEST_PROXY_VERSION_TTL_MS = 30 * 60 * 1000;
|
|
114
120
|
async function getLatestProxyVersion() {
|
|
@@ -1238,6 +1244,23 @@ function buildAnthropicHeadersWithAuth(ctx, apiKey, isMaxToken, isRerouted) {
|
|
|
1238
1244
|
headers['anthropic-beta'] = `${existing},${ctx.betaHeaders}`;
|
|
1239
1245
|
}
|
|
1240
1246
|
}
|
|
1247
|
+
// Strip beta flags unsupported by OAT tokens
|
|
1248
|
+
if (headers['anthropic-beta']) {
|
|
1249
|
+
const token = ctx.authHeader?.replace(/^Bearer\s+/i, '') ?? ctx.apiKeyHeader ?? apiKey ?? '';
|
|
1250
|
+
if (token.startsWith('sk-ant-oat')) {
|
|
1251
|
+
const cleaned = headers['anthropic-beta']
|
|
1252
|
+
.split(',')
|
|
1253
|
+
.map(b => b.trim())
|
|
1254
|
+
.filter(b => !OAT_UNSUPPORTED_BETA_FLAGS.has(b))
|
|
1255
|
+
.join(',');
|
|
1256
|
+
if (cleaned) {
|
|
1257
|
+
headers['anthropic-beta'] = cleaned;
|
|
1258
|
+
}
|
|
1259
|
+
else {
|
|
1260
|
+
delete headers['anthropic-beta'];
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1241
1264
|
// Pass through OAuth identity headers (required by Anthropic for OAuth token validation)
|
|
1242
1265
|
if (ctx.userAgent) {
|
|
1243
1266
|
headers['user-agent'] = ctx.userAgent;
|
|
@@ -1276,6 +1299,23 @@ function buildAnthropicHeaders(ctx, envApiKey) {
|
|
|
1276
1299
|
headers['anthropic-beta'] = `${existing},${ctx.betaHeaders}`;
|
|
1277
1300
|
}
|
|
1278
1301
|
}
|
|
1302
|
+
// Strip beta flags unsupported by OAT tokens
|
|
1303
|
+
if (headers['anthropic-beta']) {
|
|
1304
|
+
const token = ctx.authHeader?.replace(/^Bearer\s+/i, '') ?? ctx.apiKeyHeader ?? envApiKey ?? '';
|
|
1305
|
+
if (token.startsWith('sk-ant-oat')) {
|
|
1306
|
+
const cleaned = headers['anthropic-beta']
|
|
1307
|
+
.split(',')
|
|
1308
|
+
.map(b => b.trim())
|
|
1309
|
+
.filter(b => !OAT_UNSUPPORTED_BETA_FLAGS.has(b))
|
|
1310
|
+
.join(',');
|
|
1311
|
+
if (cleaned) {
|
|
1312
|
+
headers['anthropic-beta'] = cleaned;
|
|
1313
|
+
}
|
|
1314
|
+
else {
|
|
1315
|
+
delete headers['anthropic-beta'];
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1279
1319
|
// Pass through OAuth identity headers (required by Anthropic for OAuth token validation)
|
|
1280
1320
|
if (ctx.userAgent) {
|
|
1281
1321
|
headers['user-agent'] = ctx.userAgent;
|
|
@@ -4741,6 +4781,8 @@ async function startProxy(config = {}) {
|
|
|
4741
4781
|
// ── End rate limit check ──
|
|
4742
4782
|
const startTime = Date.now();
|
|
4743
4783
|
let nativeResponseData;
|
|
4784
|
+
let _strippedThinking = false;
|
|
4785
|
+
let _strippedBetaFlags = [];
|
|
4744
4786
|
try {
|
|
4745
4787
|
if (useCascade && cascadeConfig) {
|
|
4746
4788
|
const cascadeResult = await cascadeRequest(cascadeConfig, async (modelName) => {
|
|
@@ -4754,12 +4796,27 @@ async function startProxy(config = {}) {
|
|
|
4754
4796
|
if (proxyConfig.reliability?.cooldowns?.enabled && !cooldownManager.isAvailable(resolved.provider)) {
|
|
4755
4797
|
throw new CooldownError(resolved.provider);
|
|
4756
4798
|
}
|
|
4757
|
-
|
|
4799
|
+
let attemptBody = { ...requestBody, model: resolved.model };
|
|
4800
|
+
if ((isHaikuModel(resolved.model) || isHaikuModel(requestedModel)) && 'thinking' in attemptBody) {
|
|
4801
|
+
const { thinking: _t, ...rest } = attemptBody;
|
|
4802
|
+
attemptBody = rest;
|
|
4803
|
+
_strippedThinking = true;
|
|
4804
|
+
log(`Stripped thinking from request (${resolved.model} does not support extended thinking, originally requested: ${requestedModel})`);
|
|
4805
|
+
}
|
|
4758
4806
|
// Hybrid auth: use MAX token for Opus models, API key for others
|
|
4759
4807
|
const modelAuth = getAuthForModel(resolved.model, proxyConfig.auth, useAnthropicEnvKey);
|
|
4760
4808
|
if (modelAuth.isMax) {
|
|
4761
4809
|
log(`Using MAX token for ${resolved.model}`);
|
|
4762
4810
|
}
|
|
4811
|
+
// Log OAT beta flag stripping if applicable
|
|
4812
|
+
const cascadeEffectiveToken = ctx.authHeader?.replace(/^Bearer\s+/i, '') ?? ctx.apiKeyHeader ?? modelAuth.apiKey ?? '';
|
|
4813
|
+
const cascadeLocalStrippedBeta = cascadeEffectiveToken.startsWith('sk-ant-oat') && ctx.betaHeaders
|
|
4814
|
+
? ctx.betaHeaders.split(',').map(b => b.trim()).filter(b => OAT_UNSUPPORTED_BETA_FLAGS.has(b))
|
|
4815
|
+
: [];
|
|
4816
|
+
if (cascadeLocalStrippedBeta.length > 0) {
|
|
4817
|
+
_strippedBetaFlags = cascadeLocalStrippedBeta;
|
|
4818
|
+
log(`Stripped OAT-unsupported beta flags from request: ${cascadeLocalStrippedBeta.join(', ')}`);
|
|
4819
|
+
}
|
|
4763
4820
|
const isCascadeRerouted = resolved.model !== originalModel;
|
|
4764
4821
|
const providerResponse = await forwardNativeAnthropicRequest(attemptBody, ctx, modelAuth.apiKey, modelAuth.isMax, isCascadeRerouted);
|
|
4765
4822
|
const responseData = (await providerResponse.json());
|
|
@@ -4776,7 +4833,12 @@ async function startProxy(config = {}) {
|
|
|
4776
4833
|
}, log);
|
|
4777
4834
|
const cascadeResponseModel = checkResponseModelMismatch(cascadeResult.responseData, cascadeResult.model, cascadeResult.provider, log);
|
|
4778
4835
|
const cascadeRpHeaders = buildRelayPlaneResponseHeaders(cascadeResult.model, originalModel ?? 'unknown', complexity, cascadeResult.provider, 'cascade');
|
|
4779
|
-
|
|
4836
|
+
const cascadeStrippedRpHeaders = {};
|
|
4837
|
+
if (_strippedThinking)
|
|
4838
|
+
cascadeStrippedRpHeaders['X-RelayPlane-Stripped-Thinking'] = 'true';
|
|
4839
|
+
if (_strippedBetaFlags.length > 0)
|
|
4840
|
+
cascadeStrippedRpHeaders['X-RelayPlane-Stripped-Beta'] = _strippedBetaFlags.join(',');
|
|
4841
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...cascadeRpHeaders, ...cascadeStrippedRpHeaders });
|
|
4780
4842
|
res.end(JSON.stringify(cascadeResult.responseData));
|
|
4781
4843
|
targetProvider = cascadeResult.provider;
|
|
4782
4844
|
targetModel = cascadeResult.model;
|
|
@@ -4798,7 +4860,32 @@ async function startProxy(config = {}) {
|
|
|
4798
4860
|
const _nativeReqCtx = _poolSelectedToken
|
|
4799
4861
|
? { ...effectiveCtx, authHeader: undefined, apiKeyHeader: undefined }
|
|
4800
4862
|
: effectiveCtx;
|
|
4801
|
-
|
|
4863
|
+
// Strip thinking from body when routing to (or from) Haiku models.
|
|
4864
|
+
// Haiku does not support extended thinking. Also strip when the original
|
|
4865
|
+
// requested model was Haiku but routing.mode overrode it to a different
|
|
4866
|
+
// model — the user's intent was a non-thinking model, so thinking params
|
|
4867
|
+
// (which may also be malformed, e.g. budget_tokens < 1024) should be dropped.
|
|
4868
|
+
let _nativeReqBody = { ...requestBody, model: finalModel };
|
|
4869
|
+
if ((isHaikuModel(finalModel) || isHaikuModel(requestedModel)) && 'thinking' in _nativeReqBody) {
|
|
4870
|
+
const { thinking: _t, ...rest } = _nativeReqBody;
|
|
4871
|
+
_nativeReqBody = rest;
|
|
4872
|
+
_strippedThinking = true;
|
|
4873
|
+
log(`Stripped thinking from request (${finalModel} does not support extended thinking, originally requested: ${requestedModel})`);
|
|
4874
|
+
}
|
|
4875
|
+
// Log OAT beta flag stripping if applicable
|
|
4876
|
+
const _nativeEffectiveToken = _poolSelectedToken
|
|
4877
|
+
|| effectiveCtx.authHeader?.replace(/^Bearer\s+/i, '')
|
|
4878
|
+
|| effectiveCtx.apiKeyHeader
|
|
4879
|
+
|| modelAuth.apiKey
|
|
4880
|
+
|| '';
|
|
4881
|
+
const _nativeStrippedBeta = _nativeEffectiveToken.startsWith('sk-ant-oat') && effectiveCtx.betaHeaders
|
|
4882
|
+
? effectiveCtx.betaHeaders.split(',').map(b => b.trim()).filter(b => OAT_UNSUPPORTED_BETA_FLAGS.has(b))
|
|
4883
|
+
: [];
|
|
4884
|
+
if (_nativeStrippedBeta.length > 0) {
|
|
4885
|
+
_strippedBetaFlags = _nativeStrippedBeta;
|
|
4886
|
+
log(`Stripped OAT-unsupported beta flags from request: ${_nativeStrippedBeta.join(', ')}`);
|
|
4887
|
+
}
|
|
4888
|
+
let providerResponse = await forwardNativeAnthropicRequest(_nativeReqBody, _nativeReqCtx, modelAuth.apiKey, modelAuth.isMax, isRerouted);
|
|
4802
4889
|
// Token pool: on 429, record and retry with next available token
|
|
4803
4890
|
if (providerResponse.status === 429 && _poolSelectedToken) {
|
|
4804
4891
|
const _poolRetryAfterHeader = providerResponse.headers.get('retry-after');
|
|
@@ -4809,7 +4896,7 @@ async function startProxy(config = {}) {
|
|
|
4809
4896
|
log(`[TokenPool] 429 on token …${_poolSelectedToken.slice(-8)} — retrying with "${_nextPoolToken.label}"`);
|
|
4810
4897
|
_poolSelectedToken = _nextPoolToken.apiKey;
|
|
4811
4898
|
const _retryCtx = { ...ctx, authHeader: undefined, apiKeyHeader: undefined };
|
|
4812
|
-
providerResponse = await forwardNativeAnthropicRequest(
|
|
4899
|
+
providerResponse = await forwardNativeAnthropicRequest(_nativeReqBody, _retryCtx, _nextPoolToken.apiKey, _nextPoolToken.isOat, isRerouted);
|
|
4813
4900
|
}
|
|
4814
4901
|
}
|
|
4815
4902
|
// Token pool: learn rate limits from upstream response headers
|
|
@@ -4875,6 +4962,11 @@ async function startProxy(config = {}) {
|
|
|
4875
4962
|
}
|
|
4876
4963
|
if (isStreaming) {
|
|
4877
4964
|
const nativeStreamRpHeaders = buildRelayPlaneResponseHeaders(targetModel || requestedModel, originalModel ?? 'unknown', complexity, targetProvider, routingMode);
|
|
4965
|
+
const nativeStreamStrippedHeaders = {};
|
|
4966
|
+
if (_strippedThinking)
|
|
4967
|
+
nativeStreamStrippedHeaders['X-RelayPlane-Stripped-Thinking'] = 'true';
|
|
4968
|
+
if (_strippedBetaFlags.length > 0)
|
|
4969
|
+
nativeStreamStrippedHeaders['X-RelayPlane-Stripped-Beta'] = _strippedBetaFlags.join(',');
|
|
4878
4970
|
res.writeHead(providerResponse.status, {
|
|
4879
4971
|
'Content-Type': 'text/event-stream',
|
|
4880
4972
|
'Cache-Control': 'no-cache',
|
|
@@ -4883,6 +4975,7 @@ async function startProxy(config = {}) {
|
|
|
4883
4975
|
'X-Relay-Trace-Id': nativeTraceId,
|
|
4884
4976
|
'X-Relay-Memory-Hits': String((0, osmosis_store_js_1.countAtomsForSession)(nativeSessionId)),
|
|
4885
4977
|
...nativeStreamRpHeaders,
|
|
4978
|
+
...nativeStreamStrippedHeaders,
|
|
4886
4979
|
});
|
|
4887
4980
|
const reader = providerResponse.body?.getReader();
|
|
4888
4981
|
let streamTokensIn = 0;
|
|
@@ -4976,7 +5069,12 @@ async function startProxy(config = {}) {
|
|
|
4976
5069
|
});
|
|
4977
5070
|
log(`Cache STORE for ${targetModel || requestedModel} (hash: ${cacheHash.slice(0, 8)})`);
|
|
4978
5071
|
}
|
|
4979
|
-
|
|
5072
|
+
const nativeStrippedHeaders = {};
|
|
5073
|
+
if (_strippedThinking)
|
|
5074
|
+
nativeStrippedHeaders['X-RelayPlane-Stripped-Thinking'] = 'true';
|
|
5075
|
+
if (_strippedBetaFlags.length > 0)
|
|
5076
|
+
nativeStrippedHeaders['X-RelayPlane-Stripped-Beta'] = _strippedBetaFlags.join(',');
|
|
5077
|
+
res.writeHead(providerResponse.status, { 'Content-Type': 'application/json', 'X-RelayPlane-Cache': nativeCacheHeader, 'X-Relay-Trace-Id': nativeTraceId, 'X-Relay-Memory-Hits': String((0, osmosis_store_js_1.countAtomsForSession)(nativeSessionId)), ...nativeRpHeaders, ...nativeStrippedHeaders });
|
|
4980
5078
|
res.end(JSON.stringify(nativeResponseData));
|
|
4981
5079
|
}
|
|
4982
5080
|
}
|