@jacobbd/relay-ai 0.12.3 → 0.12.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/README.md +14 -1
- package/dist/{chunk-C7TTIGMR.js → chunk-2LUDFIZX.js} +484 -12
- package/dist/chunk-2LUDFIZX.js.map +1 -0
- package/dist/{chunk-R427D6VA.js → chunk-S7HYE2FI.js} +155 -16
- package/dist/chunk-S7HYE2FI.js.map +1 -0
- package/dist/{chunk-3K2DFBNP.js → chunk-SJEJAHKL.js} +2 -2
- package/dist/cli.js +24 -15
- package/dist/cli.js.map +1 -1
- package/dist/core/index.js +655 -35
- package/dist/core/index.js.map +1 -1
- package/dist/{manual-models-MN2GWS4I.js → manual-models-WCK2QGLV.js} +3 -3
- package/dist/{ui-command-Q27TKD53.js → ui-command-7AZ7SLTB.js} +4 -4
- package/package.json +1 -1
- package/dist/chunk-C7TTIGMR.js.map +0 -1
- package/dist/chunk-R427D6VA.js.map +0 -1
- /package/dist/{chunk-3K2DFBNP.js.map → chunk-SJEJAHKL.js.map} +0 -0
- /package/dist/{manual-models-MN2GWS4I.js.map → manual-models-WCK2QGLV.js.map} +0 -0
- /package/dist/{ui-command-Q27TKD53.js.map → ui-command-7AZ7SLTB.js.map} +0 -0
|
@@ -78,6 +78,7 @@ import {
|
|
|
78
78
|
silenceSdkWarnings,
|
|
79
79
|
slugifyProviderId,
|
|
80
80
|
splitToolUseId,
|
|
81
|
+
sseChunk,
|
|
81
82
|
streamAnthropicResponse,
|
|
82
83
|
stripOneMContextSuffix,
|
|
83
84
|
supportsNativeOAuth,
|
|
@@ -85,7 +86,7 @@ import {
|
|
|
85
86
|
translateRequest,
|
|
86
87
|
upstreamHttpStatus,
|
|
87
88
|
validateCustomEndpointUrl
|
|
88
|
-
} from "./chunk-
|
|
89
|
+
} from "./chunk-2LUDFIZX.js";
|
|
89
90
|
|
|
90
91
|
// src/registry/google-model-id.ts
|
|
91
92
|
var GOOGLE_MODEL_PREFIX = "models/";
|
|
@@ -1901,7 +1902,7 @@ function estimateAnthropicInputTokens(body) {
|
|
|
1901
1902
|
}
|
|
1902
1903
|
|
|
1903
1904
|
// src/upstream-forward.ts
|
|
1904
|
-
import {
|
|
1905
|
+
import { once } from "events";
|
|
1905
1906
|
|
|
1906
1907
|
// src/server/auth.ts
|
|
1907
1908
|
function sanitizeCredential(value) {
|
|
@@ -1959,7 +1960,7 @@ async function fetchWithOAuthRetry(apiKey, request, refreshToken) {
|
|
|
1959
1960
|
response = await request(refreshed);
|
|
1960
1961
|
return { response, apiKey: refreshed, refreshed: true };
|
|
1961
1962
|
}
|
|
1962
|
-
async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWantsStream, inboundBeta, authType, log7, claudeCodeSessionId, extraHeaders, refreshToken, onTokenRefreshed) {
|
|
1963
|
+
async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWantsStream, inboundBeta, authType, log7, claudeCodeSessionId, extraHeaders, refreshToken, onTokenRefreshed, retryEmptyStream = false) {
|
|
1963
1964
|
const doFetch = (key) => fetch(messagesUrl, {
|
|
1964
1965
|
method: "POST",
|
|
1965
1966
|
headers: anthropicUpstreamHeaders(key, clientWantsStream, inboundBeta, authType, claudeCodeSessionId, extraHeaders),
|
|
@@ -1981,12 +1982,46 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
1981
1982
|
return;
|
|
1982
1983
|
}
|
|
1983
1984
|
if (clientWantsStream && upstreamRes.body) {
|
|
1985
|
+
const reader = upstreamRes.body.getReader();
|
|
1986
|
+
const first = await reader.read().catch(() => ({ done: true, value: void 0 }));
|
|
1987
|
+
if (first.done) {
|
|
1988
|
+
reader.releaseLock();
|
|
1989
|
+
if (retryEmptyStream) {
|
|
1990
|
+
log7?.("anthropic upstream returned an empty stream; retrying without streaming");
|
|
1991
|
+
await replayWithoutStreaming(
|
|
1992
|
+
res,
|
|
1993
|
+
messagesUrl,
|
|
1994
|
+
body,
|
|
1995
|
+
apiKey,
|
|
1996
|
+
inboundBeta,
|
|
1997
|
+
authType,
|
|
1998
|
+
claudeCodeSessionId,
|
|
1999
|
+
extraHeaders,
|
|
2000
|
+
log7
|
|
2001
|
+
);
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
1984
2005
|
res.writeHead(200, {
|
|
1985
2006
|
"Content-Type": "text/event-stream",
|
|
1986
2007
|
"Cache-Control": "no-cache",
|
|
1987
2008
|
"Connection": "keep-alive"
|
|
1988
2009
|
});
|
|
1989
|
-
|
|
2010
|
+
if (first.done) {
|
|
2011
|
+
res.end();
|
|
2012
|
+
return;
|
|
2013
|
+
}
|
|
2014
|
+
res.write(Buffer.from(first.value));
|
|
2015
|
+
try {
|
|
2016
|
+
while (true) {
|
|
2017
|
+
const next = await reader.read();
|
|
2018
|
+
if (next.done) break;
|
|
2019
|
+
if (!res.write(Buffer.from(next.value))) await once(res, "drain");
|
|
2020
|
+
}
|
|
2021
|
+
res.end();
|
|
2022
|
+
} catch {
|
|
2023
|
+
res.destroy();
|
|
2024
|
+
}
|
|
1990
2025
|
return;
|
|
1991
2026
|
}
|
|
1992
2027
|
if (!upstreamRes.body) {
|
|
@@ -2008,6 +2043,85 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
2008
2043
|
});
|
|
2009
2044
|
res.end(text4);
|
|
2010
2045
|
}
|
|
2046
|
+
async function replayWithoutStreaming(res, messagesUrl, body, apiKey, inboundBeta, authType, claudeCodeSessionId, extraHeaders, log7) {
|
|
2047
|
+
let retryRes;
|
|
2048
|
+
try {
|
|
2049
|
+
retryRes = await fetch(messagesUrl, {
|
|
2050
|
+
method: "POST",
|
|
2051
|
+
headers: anthropicUpstreamHeaders(apiKey, false, inboundBeta, authType, claudeCodeSessionId, extraHeaders),
|
|
2052
|
+
body: JSON.stringify({ ...body, stream: false })
|
|
2053
|
+
});
|
|
2054
|
+
} catch (err) {
|
|
2055
|
+
throw new UpstreamUnreachableError(err);
|
|
2056
|
+
}
|
|
2057
|
+
const text4 = await retryRes.text();
|
|
2058
|
+
if (!retryRes.ok) {
|
|
2059
|
+
log7?.(`anthropic upstream ${retryRes.status} on empty-stream retry: ${text4}`);
|
|
2060
|
+
res.writeHead(retryRes.status, { "Content-Type": retryRes.headers.get("content-type") || "application/json" });
|
|
2061
|
+
res.end(text4);
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
let message;
|
|
2065
|
+
try {
|
|
2066
|
+
message = JSON.parse(text4);
|
|
2067
|
+
} catch {
|
|
2068
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
2069
|
+
res.end(JSON.stringify({ type: "error", error: { type: "api_error", message: "Upstream response was not valid JSON" } }));
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
2072
|
+
res.writeHead(200, {
|
|
2073
|
+
"Content-Type": "text/event-stream",
|
|
2074
|
+
"Cache-Control": "no-cache",
|
|
2075
|
+
"Connection": "keep-alive"
|
|
2076
|
+
});
|
|
2077
|
+
writeMessageAsSse(res, message);
|
|
2078
|
+
}
|
|
2079
|
+
function writeMessageAsSse(res, message) {
|
|
2080
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
2081
|
+
res.write(sseChunk("message_start", {
|
|
2082
|
+
type: "message_start",
|
|
2083
|
+
message: { ...message, content: [], stop_reason: null, stop_sequence: null }
|
|
2084
|
+
}));
|
|
2085
|
+
content.forEach((block, index) => {
|
|
2086
|
+
const type = block.type;
|
|
2087
|
+
const opening = type === "text" ? { type: "text", text: "" } : type === "thinking" ? { type: "thinking", thinking: "", signature: "" } : type === "tool_use" ? { type: "tool_use", id: block.id, name: block.name, input: {} } : block;
|
|
2088
|
+
res.write(sseChunk("content_block_start", { type: "content_block_start", index, content_block: opening }));
|
|
2089
|
+
if (type === "text") {
|
|
2090
|
+
res.write(sseChunk("content_block_delta", {
|
|
2091
|
+
type: "content_block_delta",
|
|
2092
|
+
index,
|
|
2093
|
+
delta: { type: "text_delta", text: block.text ?? "" }
|
|
2094
|
+
}));
|
|
2095
|
+
} else if (type === "thinking") {
|
|
2096
|
+
res.write(sseChunk("content_block_delta", {
|
|
2097
|
+
type: "content_block_delta",
|
|
2098
|
+
index,
|
|
2099
|
+
delta: { type: "thinking_delta", thinking: block.thinking ?? "" }
|
|
2100
|
+
}));
|
|
2101
|
+
if (typeof block.signature === "string") {
|
|
2102
|
+
res.write(sseChunk("content_block_delta", {
|
|
2103
|
+
type: "content_block_delta",
|
|
2104
|
+
index,
|
|
2105
|
+
delta: { type: "signature_delta", signature: block.signature }
|
|
2106
|
+
}));
|
|
2107
|
+
}
|
|
2108
|
+
} else if (type === "tool_use") {
|
|
2109
|
+
res.write(sseChunk("content_block_delta", {
|
|
2110
|
+
type: "content_block_delta",
|
|
2111
|
+
index,
|
|
2112
|
+
delta: { type: "input_json_delta", partial_json: JSON.stringify(block.input ?? {}) }
|
|
2113
|
+
}));
|
|
2114
|
+
}
|
|
2115
|
+
res.write(sseChunk("content_block_stop", { type: "content_block_stop", index }));
|
|
2116
|
+
});
|
|
2117
|
+
res.write(sseChunk("message_delta", {
|
|
2118
|
+
type: "message_delta",
|
|
2119
|
+
delta: { stop_reason: message.stop_reason ?? "end_turn", stop_sequence: message.stop_sequence ?? null },
|
|
2120
|
+
usage: message.usage ?? {}
|
|
2121
|
+
}));
|
|
2122
|
+
res.write(sseChunk("message_stop", { type: "message_stop" }));
|
|
2123
|
+
res.end();
|
|
2124
|
+
}
|
|
2011
2125
|
|
|
2012
2126
|
// src/antigravity/anthropic-to-cloudcode.ts
|
|
2013
2127
|
import { randomUUID } from "crypto";
|
|
@@ -2859,7 +2973,8 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
|
|
|
2859
2973
|
route.refreshToken,
|
|
2860
2974
|
(refreshed) => {
|
|
2861
2975
|
route.apiKey = refreshed;
|
|
2862
|
-
}
|
|
2976
|
+
},
|
|
2977
|
+
true
|
|
2863
2978
|
);
|
|
2864
2979
|
} catch (err) {
|
|
2865
2980
|
const message = err instanceof UpstreamUnreachableError ? err.message : String(err);
|
|
@@ -3594,8 +3709,7 @@ function readModelsFromModelsDev(backendId, cache = loadModelsDevCache()) {
|
|
|
3594
3709
|
const id = entry.id ?? modelKey;
|
|
3595
3710
|
if (entry.status === "deprecated") continue;
|
|
3596
3711
|
const isFree = entry.cost !== void 0 && entry.cost.input === 0 && entry.cost.output === 0;
|
|
3597
|
-
const
|
|
3598
|
-
const modelFormat = backendId === "go" && rawFormat === "anthropic" ? "openai" : rawFormat;
|
|
3712
|
+
const modelFormat = classifyModelFormat(id, entry.provider?.npm);
|
|
3599
3713
|
result.set(id, {
|
|
3600
3714
|
id,
|
|
3601
3715
|
name: entry.name ?? id,
|
|
@@ -3630,10 +3744,7 @@ function mergeModels(apiIds, cache, backendId) {
|
|
|
3630
3744
|
const uniqueIds = Array.from(new Set(apiIds));
|
|
3631
3745
|
return uniqueIds.filter((id) => !shouldHideModel({ providerId: backendId, modelId: id, agent: "claude" })).map((id) => {
|
|
3632
3746
|
const cached = cache?.get(id);
|
|
3633
|
-
if (cached) {
|
|
3634
|
-
const modelFormat2 = backendId === "go" && cached.modelFormat === "anthropic" ? "openai" : cached.modelFormat;
|
|
3635
|
-
return { ...cached, sourceBackend: backendId, modelFormat: modelFormat2 };
|
|
3636
|
-
}
|
|
3747
|
+
if (cached) return { ...cached, sourceBackend: backendId };
|
|
3637
3748
|
const modelFormat = classifyModelFormat(id, void 0);
|
|
3638
3749
|
return {
|
|
3639
3750
|
id,
|
|
@@ -3919,7 +4030,30 @@ function buildCatalogRoutes(startingRoute, favorites, resolveRoute, max = MAX_MO
|
|
|
3919
4030
|
|
|
3920
4031
|
// src/registry/materialize.ts
|
|
3921
4032
|
init_provider_templates();
|
|
3922
|
-
|
|
4033
|
+
|
|
4034
|
+
// src/registry/model-protocol.ts
|
|
4035
|
+
function reconcileCachedModelProtocol(model, provider, metadata = loadModelsDevCache()) {
|
|
4036
|
+
if (model.source === "manual") return model;
|
|
4037
|
+
const isZenGo = provider.id === "zen" || provider.id === "go" || provider.templateId === "zen" || provider.templateId === "go";
|
|
4038
|
+
if (!isZenGo) return model;
|
|
4039
|
+
const metadataNpm = findModelsDevModel(provider.id, model.id, metadata)?.provider?.npm;
|
|
4040
|
+
if (!metadataNpm) return model;
|
|
4041
|
+
const format = classifyModelFormat(model.id, metadataNpm);
|
|
4042
|
+
if (format === "unsupported") return model;
|
|
4043
|
+
const backendId = provider.id === "go" || provider.templateId === "go" ? "go" : "zen";
|
|
4044
|
+
const apiUrl = model.apiUrl ?? provider.api.url ?? BACKENDS[backendId].baseUrl;
|
|
4045
|
+
return {
|
|
4046
|
+
...model,
|
|
4047
|
+
modelFormat: format,
|
|
4048
|
+
npm: metadataNpm,
|
|
4049
|
+
apiUrl
|
|
4050
|
+
};
|
|
4051
|
+
}
|
|
4052
|
+
|
|
4053
|
+
// src/registry/materialize.ts
|
|
4054
|
+
function cachedModelToLocal(cached, provider, metadata = loadModelsDevCache()) {
|
|
4055
|
+
const reconciled = reconcileCachedModelProtocol(cached, provider, metadata);
|
|
4056
|
+
cached = reconciled;
|
|
3923
4057
|
const freeStatus = classifyFreeStatus({
|
|
3924
4058
|
model: cached,
|
|
3925
4059
|
providerId: provider.id,
|
|
@@ -3941,20 +4075,24 @@ function cachedModelToLocal(cached, provider) {
|
|
|
3941
4075
|
interleavedReasoningField: cached.interleavedReasoningField
|
|
3942
4076
|
};
|
|
3943
4077
|
}
|
|
4078
|
+
const modelsDev = findModelsDevModel(provider.id, cached.id, metadata);
|
|
4079
|
+
const isZenGo = provider.id === "zen" || provider.id === "go" || provider.templateId === "zen" || provider.templateId === "go";
|
|
4080
|
+
const metadataNpm = !cached.source && isZenGo ? modelsDev?.provider?.npm : void 0;
|
|
3944
4081
|
const npm = cached.npm ?? provider.api.npm ?? "";
|
|
3945
|
-
const apiUrl = cached.apiUrl ?? provider.api.url ?? "";
|
|
4082
|
+
const apiUrl = cached.apiUrl ?? provider.api.url ?? (isZenGo ? BACKENDS[provider.id === "go" || provider.templateId === "go" ? "go" : "zen"].baseUrl : "");
|
|
3946
4083
|
const endpoint = resolveEndpoint(npm, apiUrl);
|
|
3947
4084
|
if (endpoint === null) return null;
|
|
3948
|
-
const modelsDev = findModelsDevModel(provider.id, cached.id);
|
|
3949
4085
|
const { id, upstreamModelId: upstreamModelId2 } = normalizeGoogleModelId(cached.id, npm);
|
|
3950
4086
|
const normalizedUpstream = normalizeGoogleModelId(cached.upstreamModelId ?? cached.id, npm).upstreamModelId;
|
|
3951
4087
|
const family = npm === "@ai-sdk/google" ? id.split(/[-/:]/)[0] ?? id : cached.family ?? "";
|
|
4088
|
+
const classifiedFormat = classifyModelFormat(cached.id, npm);
|
|
4089
|
+
const resolvedFormat = classifiedFormat === "anthropic" ? classifiedFormat : "openai";
|
|
3952
4090
|
return {
|
|
3953
4091
|
id,
|
|
3954
4092
|
name: npm === "@ai-sdk/google" ? normalizeGoogleDisplayName(cached.name, id) : cached.name,
|
|
3955
4093
|
family,
|
|
3956
4094
|
brand: npm === "@ai-sdk/google" ? deriveBrand(family) : cached.brand ?? deriveBrand(cached.family ?? ""),
|
|
3957
|
-
modelFormat: cached.modelFormat ?? endpoint.format,
|
|
4095
|
+
modelFormat: metadataNpm ? resolvedFormat : cached.modelFormat ?? endpoint.format,
|
|
3958
4096
|
upstreamModelId: normalizedUpstream,
|
|
3959
4097
|
baseUrl: endpoint.baseUrl,
|
|
3960
4098
|
completionsUrl: endpoint.completionsUrl,
|
|
@@ -8292,6 +8430,7 @@ function httpProxyModelId(providerId, modelId) {
|
|
|
8292
8430
|
return `${HTTP_PROXY_MODEL_PREFIX}${providerId}:${modelId}`;
|
|
8293
8431
|
}
|
|
8294
8432
|
function supportsClaudeTransparentMode(model) {
|
|
8433
|
+
if (model.modelFormat === "anthropic") return Boolean(model.baseUrl);
|
|
8295
8434
|
return model.modelFormat === "openai" && isSdkMigratedNpm(model.npm);
|
|
8296
8435
|
}
|
|
8297
8436
|
function buildHttpProxyRoutes(providers, favorites, selected, max = MAX_MODEL_CATALOG) {
|
|
@@ -9661,4 +9800,4 @@ export {
|
|
|
9661
9800
|
supportsClaudeTransparentMode,
|
|
9662
9801
|
buildHttpProxyRoutes
|
|
9663
9802
|
};
|
|
9664
|
-
//# sourceMappingURL=chunk-
|
|
9803
|
+
//# sourceMappingURL=chunk-S7HYE2FI.js.map
|