mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
## Problem When Gemini Flash returns a safety-filtered response (finish_reason: content_filter, empty content), isEmptyContentResponse() misclassifies it as a fake-success empty response and returns HTTP 502. This triggers the combo fallback chain and account cooldown escalation (5s → 10s → 20s → 40s), even though the response is a legitimate terminal state. ## Root cause errorClassifier.ts line 14: LEGIT_EMPTY_OPENAI_FINISH only exempts "length" and "tool_calls". The "content_filter" finish reason (mapped from Gemini's SAFETY/PROHIBITED_CONTENT) is not exempted, so safety-filtered responses are treated as empty content failures. ## Fix Add "content_filter" to LEGIT_EMPTY_OPENAI_FINISH so safety-filtered responses pass through as valid (though filtered) completions. ## Testing - 10/10 unit tests pass (empty-content-stopreason-3572.test.ts) including 2 new content_filter test cases - E2E: hot-patched OmniRoute v3.8.48 on X500, verified the previously failing prompt (4.6KB review) now returns valid content instead of empty-content 502 Signed-off-by: Minxi Hou <houminxi@gmail.com>
90 lines
3.0 KiB
TypeScript
90 lines
3.0 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import { isEmptyContentResponse } from "../../open-sse/services/errorClassifier.ts";
|
|
|
|
// #3572 — A valid max_tokens-truncated upstream response (HTTP 200, a legitimate
|
|
// terminal stop_reason/finish_reason, empty content) must NOT be misclassified as
|
|
// an empty/silent-failure response (which gets rewritten into a synthetic 502).
|
|
// The empty-content guard must only fire when content is empty AND there is no
|
|
// legitimate terminal stop_reason — i.e. a genuine fake-success / silent failure.
|
|
|
|
test("#3572 Claude: empty content + stop_reason=max_tokens is NOT empty-failure", () => {
|
|
assert.equal(
|
|
isEmptyContentResponse({
|
|
type: "message",
|
|
role: "assistant",
|
|
content: [],
|
|
stop_reason: "max_tokens",
|
|
usage: { output_tokens: 1 },
|
|
}),
|
|
false
|
|
);
|
|
});
|
|
|
|
test("#3572 Claude: empty content + stop_reason=tool_use is NOT empty-failure", () => {
|
|
assert.equal(isEmptyContentResponse({ content: [], stop_reason: "tool_use" }), false);
|
|
});
|
|
|
|
test("#3572 Claude: empty content with NO stop_reason IS still empty-failure", () => {
|
|
assert.equal(isEmptyContentResponse({ content: [] }), true);
|
|
assert.equal(isEmptyContentResponse({ content: [], stop_reason: null }), true);
|
|
});
|
|
|
|
test("#3572 Claude: empty content + stop_reason=end_turn stays flagged (fake-success guard preserved)", () => {
|
|
assert.equal(isEmptyContentResponse({ content: [], stop_reason: "end_turn" }), true);
|
|
});
|
|
|
|
test("#3572 OpenAI: empty content + finish_reason=length is NOT empty-failure", () => {
|
|
assert.equal(
|
|
isEmptyContentResponse({
|
|
choices: [{ index: 0, message: { content: "" }, finish_reason: "length" }],
|
|
}),
|
|
false
|
|
);
|
|
});
|
|
|
|
test("#3572 OpenAI: empty delta + finish_reason=length (stream chunk) is NOT empty-failure", () => {
|
|
assert.equal(
|
|
isEmptyContentResponse({
|
|
choices: [{ index: 0, delta: { content: "" }, finish_reason: "length" }],
|
|
}),
|
|
false
|
|
);
|
|
});
|
|
|
|
test("#3572 OpenAI: empty content + finish_reason=stop stays flagged (fake-success guard preserved)", () => {
|
|
assert.equal(
|
|
isEmptyContentResponse({
|
|
choices: [{ index: 0, message: { content: "" }, finish_reason: "stop" }],
|
|
}),
|
|
true
|
|
);
|
|
});
|
|
|
|
test("#3572 OpenAI: empty content + finish_reason=content_filter is NOT empty-failure (safety-filtered response)", () => {
|
|
assert.equal(
|
|
isEmptyContentResponse({
|
|
choices: [{ index: 0, message: { content: "" }, finish_reason: "content_filter" }],
|
|
}),
|
|
false
|
|
);
|
|
});
|
|
|
|
test("#3572 OpenAI: empty content + finish_reason=content_filter (stream chunk) is NOT empty-failure", () => {
|
|
assert.equal(
|
|
isEmptyContentResponse({
|
|
choices: [{ index: 0, delta: { content: "" }, finish_reason: "content_filter" }],
|
|
}),
|
|
false
|
|
);
|
|
});
|
|
|
|
test("#3572 regression: non-empty content is never flagged", () => {
|
|
assert.equal(isEmptyContentResponse({ content: [{ type: "text", text: "hi" }] }), false);
|
|
assert.equal(
|
|
isEmptyContentResponse({ choices: [{ message: { content: "hi" }, finish_reason: "length" }] }),
|
|
false
|
|
);
|
|
});
|