fix: route deepseek codex via moonbridge

This commit is contained in:
Codex
2026-05-26 10:27:25 +08:00
parent 0ba5b2c352
commit 986d8f5c07
6 changed files with 278 additions and 33 deletions
+104 -3
View File
@@ -36,6 +36,10 @@ const server = createServer(async (clientReq, clientRes) => {
await sendCodexModels(clientReq, clientRes, url);
return;
}
if (clientReq.method === "POST" && url.pathname === "/v1/responses") {
await proxyResponsesRequest(clientReq, clientRes, url);
return;
}
await proxyRequest(clientReq, clientRes, url);
} catch (error) {
sendJson(clientRes, 502, {
@@ -53,18 +57,55 @@ server.listen(port, "0.0.0.0", () => {
});
async function sendReadiness(res) {
const upstream = await fetchUpstreamJson("/health/readiness").catch((error) => ({ ok: false, status: 502, payload: { error: error.message } }));
const upstream = await fetchUpstreamReadiness();
sendJson(res, upstream.ok ? 200 : 503, {
...healthPayload(),
status: upstream.ok ? "ready" : "degraded",
upstream: {
ok: upstream.ok,
status: upstream.status,
source: upstream.source,
payload: upstream.payload
}
});
}
async function fetchUpstreamReadiness() {
const health = await fetchUpstreamJson("/health/readiness")
.then((result) => ({ ...result, source: "/health/readiness" }))
.catch((error) => ({ ok: false, status: 502, source: "/health/readiness", payload: { error: error.message } }));
if (health.ok) return health;
const models = await fetchUpstreamJson("/v1/models")
.then((result) => ({ ...result, source: "/v1/models" }))
.catch((error) => ({ ok: false, status: 502, source: "/v1/models", payload: { error: error.message } }));
const modelCount = codexModelsFromUpstream(models.payload).length;
if (models.ok && modelCount > 0) {
return {
...models,
payload: {
modelCount,
fallbackFrom: {
source: health.source,
status: health.status
}
}
};
}
return {
...health,
payload: {
...health.payload,
modelProbe: {
ok: models.ok,
status: models.status,
modelCount
}
}
};
}
async function sendCodexModels(clientReq, clientRes, url) {
const upstream = await fetchUpstreamJson(`${url.pathname}${url.search}`, clientReq.headers).catch((error) => ({ ok: false, status: 502, payload: { error: error.message } }));
const models = codexModelsFromUpstream(upstream.payload);
@@ -73,7 +114,7 @@ async function sendCodexModels(clientReq, clientRes, url) {
upstream: {
ok: upstream.ok,
status: upstream.status,
source: upstream.ok ? "litellm" : "fallback"
source: upstream.ok ? "upstream" : "fallback"
}
});
}
@@ -126,6 +167,65 @@ async function proxyRequest(clientReq, clientRes, url) {
upstreamReq.end(body);
}
async function proxyResponsesRequest(clientReq, clientRes, url) {
const startedAt = Date.now();
const originalBody = await readRequestBody(clientReq);
const { body, headers, decompressed, sanitizedToolTypes } = decodeRequestBody(originalBody, clientReq.headers, url.pathname);
await proxyPreparedBody({
clientReq,
clientRes,
url,
body,
headers,
startedAt,
logExtra: { decompressed, sanitizedToolTypes }
});
}
async function proxyPreparedBody({ clientReq, clientRes, url, body, headers, startedAt, logExtra = {} }) {
const targetUrl = new URL(`${url.pathname}${url.search}`, upstreamBaseUrl);
const requestImpl = targetUrl.protocol === "https:" ? httpsRequest : httpRequest;
const upstreamReq = requestImpl({
protocol: targetUrl.protocol,
hostname: targetUrl.hostname,
port: targetUrl.port || (targetUrl.protocol === "https:" ? 443 : 80),
method: clientReq.method,
path: `${targetUrl.pathname}${targetUrl.search}`,
headers
}, (upstreamRes) => {
const responseHeaders = filterResponseHeaders(upstreamRes.headers);
clientRes.writeHead(upstreamRes.statusCode || 502, responseHeaders);
upstreamRes.pipe(clientRes);
upstreamRes.on("end", () => {
process.stderr.write(JSON.stringify({
serviceId,
method: clientReq.method,
path: url.pathname,
status: upstreamRes.statusCode || null,
elapsedMs: Date.now() - startedAt,
...logExtra
}) + "\n");
});
});
upstreamReq.on("error", (error) => {
if (!clientRes.headersSent) {
sendJson(clientRes, 502, {
error: {
code: "deepseek_bridge_upstream_error",
message: "DeepSeek bridge upstream request failed",
reason: error.message
}
});
} else {
clientRes.end();
}
});
upstreamReq.end(body);
}
function decodeRequestBody(body, headers, pathname = "") {
const encoding = String(headers["content-encoding"] || "").trim().toLowerCase();
const decoded = encoding === "zstd" ? Buffer.from(decompress(new Uint8Array(body))) : body;
@@ -255,7 +355,8 @@ function healthPayload() {
status: "ok",
upstream: upstreamBaseUrl.origin,
model: defaultModel,
zstdRequestBody: "decode"
zstdRequestBody: "decode",
responsesTools: "drop-non-function"
};
}
@@ -30,11 +30,18 @@ test("DeepSeek Responses bridge decompresses Codex zstd body and maps models cat
body: zstdPayload
});
assert.equal(response.status, 200);
assert.equal(upstream.captured.url, "/v1/responses");
assert.equal(upstream.captured.encoding, null);
const forwarded = JSON.parse(upstream.captured.body);
assert.equal(forwarded.model, "deepseek-chat");
assert.deepEqual(forwarded.tools.map((tool) => tool.type), ["function", "function"]);
const readinessResponse = await fetch(`http://127.0.0.1:${bridge.port}/health/readiness`);
assert.equal(readinessResponse.status, 200);
const readiness = await readinessResponse.json();
assert.equal(readiness.upstream.source, "/v1/models");
assert.equal(readiness.upstream.payload.modelCount, 1);
const modelsResponse = await fetch(`http://127.0.0.1:${bridge.port}/v1/models?client_version=test`);
assert.equal(modelsResponse.status, 200);
const models = await modelsResponse.json();
@@ -49,6 +56,11 @@ test("DeepSeek Responses bridge decompresses Codex zstd body and maps models cat
async function startUpstream() {
let captured = null;
const server = createServer(async (request, response) => {
if ((request.url || "").startsWith("/health/readiness")) {
response.writeHead(404, { "content-type": "application/json" });
response.end(JSON.stringify({ error: "not implemented" }));
return;
}
if ((request.url || "").startsWith("/v1/models")) {
response.writeHead(200, { "content-type": "application/json" });
response.end(JSON.stringify({ data: [{ id: "deepseek-chat" }] }));