fix(combo): exempt content_filter from empty-content detection (#7973)

## 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>
This commit is contained in:
Bob.Hou
2026-07-22 01:34:47 -04:00
committed by GitHub
parent 146abb2164
commit ff320cbfd5
2 changed files with 19 additions and 1 deletions

View File

@@ -11,7 +11,7 @@ import { getProviderCategory, getRegistryEntry } from "../config/providerRegistr
// NOT a silent "fake success" failure. Used to avoid rewriting a valid HTTP 200
// (e.g. a Claude Code `max_tokens: 1` connectivity ping) into a synthetic 502.
const LEGIT_EMPTY_CLAUDE_STOP = new Set(["max_tokens", "tool_use"]);
const LEGIT_EMPTY_OPENAI_FINISH = new Set(["length", "tool_calls"]);
const LEGIT_EMPTY_OPENAI_FINISH = new Set(["length", "tool_calls", "content_filter"]);
export function isEmptyContentResponse(responseBody: unknown): boolean {
if (!responseBody || typeof responseBody !== "object") return false;

View File

@@ -62,6 +62,24 @@ test("#3572 OpenAI: empty content + finish_reason=stop stays flagged (fake-succe
);
});
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(