diff --git a/src/rest.ts b/src/rest.ts index cfb38be..1620f6e 100644 --- a/src/rest.ts +++ b/src/rest.ts @@ -170,6 +170,11 @@ rest.post("/v1/infer", async (c) => { }).catch(() => null) if (!upstream || !upstream.ok) { const detail = upstream ? await upstream.text().catch(() => "") : "upstream unreachable" + if (upstream && upstream.status >= 400 && upstream.status < 500) { + // upstream rejected the request itself (unknown model, task not + // served, oversized input) — pass the status + detail through + return errorResponse(upstream.status, "inference_rejected", detail.slice(0, 500)) + } return errorResponse(502, "inference_upstream", detail.slice(0, 500)) } const result = (await upstream.json()) as Record diff --git a/test/rest.test.ts b/test/rest.test.ts index 041cf7b..22ea9aa 100644 --- a/test/rest.test.ts +++ b/test/rest.test.ts @@ -163,6 +163,40 @@ describe("REST models + inference", () => { expect(missing.status).toBe(404) }) + it("POST /v1/infer passes upstream 4xx status through (not 502)", async () => { + const stubEnv = { + ASSETS: assets, + ML_ENDPOINT: "https://ml-stub.example", + ML_TOKEN: "t", + } + const originalFetch = globalThis.fetch + globalThis.fetch = (async (url: string | URL | Request) => { + if (String(url).includes("ml-stub.example")) { + return new Response( + JSON.stringify({ detail: "model x task translit is not served" }), + { status: 400, headers: { "content-type": "application/json" } }, + ) + } + return originalFetch(url as RequestInfo) + }) as typeof fetch + try { + const res = await app.request( + "https://example.org/v1/infer", + { + method: "POST", + headers: JSON_HEADERS, + body: JSON.stringify({ model: "khm-latn-1.0", input: "x" }), + }, + stubEnv, + ) + expect(res.status).toBe(400) + const body = (await res.json()) as { error: { code: string } } + expect(body.error.code).not.toBe("inference_upstream") + } finally { + globalThis.fetch = originalFetch + } + }) + it("POST /v1/infer validates without an upstream configured", async () => { const unconfigured = await post("/v1/infer", { model: "heb-diac-1.0", input: "x" }) expect(unconfigured.status).toBe(503)