fix(qoder): handle code 110 billing blocks and preserve SSE error status
- Match code 110 (billing daily count exceeded) alongside 112/10605/pricingUrl in isBillingBlock, parsing JSON safely and accepting numeric/string codes - Accept numeric strings for statusCodeValue and object bodies in envelope peek - Emit structured 403 quota error chunk instead of synthetic assistant text when a billing envelope appears mid-stream - Preserve upstream HTTP status in handleForcedSSEToJson when error chunk carries a valid 400-599 status - Add unit tests for code-110 detection, mid-stream billing envelopes, and false-positive guard
This commit is contained in:
@@ -82,6 +82,148 @@ describe("wrapQoderSSE billing detection", () => {
|
||||
expect(wrapped.ok).toBe(false);
|
||||
});
|
||||
|
||||
it("returns 403 response when first frame is billing block (code 110 string)", async () => {
|
||||
const billingEnv = JSON.stringify({
|
||||
statusCodeValue: 403,
|
||||
body: '{"code":"110","message":"Billing daily count exceeded"}',
|
||||
});
|
||||
const upstream = `data: ${billingEnv}\n\n`;
|
||||
|
||||
const wrapped = await wrapQoderSSE(makeResponse([upstream]), "qoder/qfmodel");
|
||||
|
||||
expect(wrapped.status).toBe(403);
|
||||
expect(wrapped.ok).toBe(false);
|
||||
const json = await wrapped.json();
|
||||
expect(json.error.message).toContain("Billing daily count exceeded");
|
||||
});
|
||||
|
||||
it("returns 403 response when first frame is billing block (code 110 numeric)", async () => {
|
||||
const billingEnv = JSON.stringify({
|
||||
statusCodeValue: 403,
|
||||
body: '{"code":110,"message":"Billing daily count exceeded"}',
|
||||
});
|
||||
const upstream = `data: ${billingEnv}\n\n`;
|
||||
|
||||
const wrapped = await wrapQoderSSE(makeResponse([upstream]), "qoder/qfmodel");
|
||||
|
||||
expect(wrapped.status).toBe(403);
|
||||
expect(wrapped.ok).toBe(false);
|
||||
});
|
||||
it("returns 403 response when statusCodeValue is string \"403\" (code 110)", async () => {
|
||||
const billingEnv = JSON.stringify({
|
||||
statusCodeValue: "403",
|
||||
body: '{"code":"110","message":"Billing daily count exceeded"}',
|
||||
});
|
||||
const upstream = `data: ${billingEnv}\n\n`;
|
||||
|
||||
const wrapped = await wrapQoderSSE(makeResponse([upstream]), "qoder/qfmodel");
|
||||
|
||||
expect(wrapped.status).toBe(403);
|
||||
expect(wrapped.ok).toBe(false);
|
||||
const json = await wrapped.json();
|
||||
expect(json.error.message).toContain("Billing daily count exceeded");
|
||||
});
|
||||
|
||||
it("emits structured 403 error chunk for object-body billing after a data frame (peek miss)", async () => {
|
||||
const okEnv = JSON.stringify({
|
||||
statusCodeValue: 200,
|
||||
body: JSON.stringify({ choices: [{ delta: { content: "hi" } }] }),
|
||||
});
|
||||
const billingEnv = JSON.stringify({
|
||||
statusCodeValue: 403,
|
||||
body: { code: "110", message: "Billing daily count exceeded" },
|
||||
});
|
||||
const upstream = `data: ${okEnv}\n\ndata: ${billingEnv}\n\n`;
|
||||
|
||||
const wrapped = await wrapQoderSSE(makeResponse([upstream]), "qoder/qfmodel");
|
||||
|
||||
const reader = wrapped.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buf = "";
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buf += decoder.decode(value, { stream: true });
|
||||
}
|
||||
buf += decoder.decode();
|
||||
|
||||
expect(buf).not.toContain("[qoder error");
|
||||
const errLine = buf.split("\n").find((l) => l.includes('"error"'));
|
||||
expect(errLine).toBeDefined();
|
||||
const errChunk = JSON.parse(errLine.slice(5).trim());
|
||||
expect(errChunk.error.status).toBe(403);
|
||||
expect(errChunk.error.message).toContain("Billing daily count exceeded");
|
||||
expect(errChunk.choices).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
it("does not treat legitimate assistant text mentioning code 110 as billing", async () => {
|
||||
const inner = JSON.stringify({
|
||||
choices: [{ delta: { content: "error 110 means billing daily count exceeded in docs" } }],
|
||||
});
|
||||
const successEnv = JSON.stringify({ statusCodeValue: 200, body: inner });
|
||||
const upstream = `data: ${successEnv}\n\n`;
|
||||
|
||||
const wrapped = await wrapQoderSSE(makeResponse([upstream]), "qoder/qfmodel");
|
||||
|
||||
expect(wrapped.status).toBe(200);
|
||||
const reader = wrapped.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buf = "";
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buf += decoder.decode(value, { stream: true });
|
||||
}
|
||||
buf += decoder.decode();
|
||||
|
||||
expect(buf).toContain("billing daily count exceeded");
|
||||
expect(buf).not.toContain("[qoder error");
|
||||
});
|
||||
|
||||
it("emits structured 403 error chunk for billing envelope after a data frame (peek miss)", async () => {
|
||||
const okEnv = JSON.stringify({
|
||||
statusCodeValue: 200,
|
||||
body: JSON.stringify({ choices: [{ delta: { content: "hi" } }] }),
|
||||
});
|
||||
const billingEnv = JSON.stringify({
|
||||
statusCodeValue: 403,
|
||||
body: '{"code":"110","message":"Billing daily count exceeded"}',
|
||||
});
|
||||
const upstream = `data: ${okEnv}\n\ndata: ${billingEnv}\n\n`;
|
||||
|
||||
const wrapped = await wrapQoderSSE(makeResponse([upstream]), "qoder/qfmodel");
|
||||
|
||||
const reader = wrapped.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buf = "";
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buf += decoder.decode(value, { stream: true });
|
||||
}
|
||||
buf += decoder.decode();
|
||||
|
||||
expect(buf).not.toContain("[qoder error");
|
||||
const errLine = buf.split("\n").find((l) => l.includes('"error"'));
|
||||
expect(errLine).toBeDefined();
|
||||
const errChunk = JSON.parse(errLine.slice(5).trim());
|
||||
expect(errChunk.error.status).toBe(403);
|
||||
expect(errChunk.error.message).toContain("Billing daily count exceeded");
|
||||
});
|
||||
|
||||
it("emits structured 403 error chunk for object-body billing envelope (peek miss)", async () => {
|
||||
const billingEnv = JSON.stringify({
|
||||
statusCodeValue: 403,
|
||||
body: { code: "110", message: "Billing daily count exceeded" },
|
||||
});
|
||||
const upstream = `data: ${billingEnv}\n\n`;
|
||||
|
||||
const wrapped = await wrapQoderSSE(makeResponse([upstream]), "qoder/qfmodel");
|
||||
|
||||
expect(wrapped.status).toBe(403);
|
||||
});
|
||||
|
||||
it("returns 403 response when first frame has pricingUrl", async () => {
|
||||
const billingEnv = JSON.stringify({
|
||||
statusCodeValue: 402,
|
||||
|
||||
Reference in New Issue
Block a user