fix(provider): skip Azure content-filter preamble frames - #3817
Merged
Conversation
Azure OpenAI opens a stream with an informational content-filter report
before any content:
{"choices":[],"created":0,"id":"","model":"","object":"",
"prompt_filter_results":[{"prompt_index":0,"content_filter_results":{...}}]}
Empty `choices`, no `usage`. The parser required usage on any content-free
frame and failed the whole stream with "empty choices event had no usage".
An empty `choices` array simply carries no content. Usage-only frames use the
same shape and were already tolerated; the preamble is the same case. Skip the
frame. A missing `choices` key stays rejected -- that is a malformed event
rather than a content-free one.
No user is affected today: gpt-5.4 and gpt-5.5 are the only models observed
emitting the preamble, and both route to the Responses API instead
(openai-provider.ts:1308, isReasoningCapableGpt5 matches gpt-5.<n> for n>=2).
Loki confirms zero occurrences in 30 days, and the only stream error in
veryfront-server over 7 days is the Kimi one fixed in #3815.
It is still worth closing. Kimi is Azure-routed and does use this parser, so
enabling content filtering on that Foundry deployment would break it instantly
in exactly the way #3815 just repaired.
Tests replay the verbatim captured Azure preamble.
Fixes veryfront/veryfront-issue-inbox#543
|
Important Review available on request
Reviews should be triggered manually for repositories with fewer than 10 stars. Select Trigger review above or comment ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
📦 Client bundle boundary
A server module in a client graph aborts hydration in the browser. New leaks fail CI; known leaks are tracked in |
Keep 0.1.1241 so merging this does not trigger a publish. The release will be cut separately.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #3815, from sweeping all 16 catalog models against the live gateway.
No version bump — this PR does not cut a release. Stays at
0.1.1241; the release is cut separately.Problem
Azure OpenAI opens a stream with an informational content-filter report before any content. Captured live from
https://api.veryfront.org/ai/gateway/openai/v1/chat/completionson 2026-08-17 — first data chunk for bothgpt-5.4andgpt-5.5:{"choices":[],"created":0,"id":"","model":"","object":"", "prompt_filter_results":[{"prompt_index":0,"content_filter_results":{ "hate":{"filtered":false,"severity":"safe"}, "jailbreak":{"detected":false,"filtered":false}, "self_harm":{"filtered":false,"severity":"safe"}, "sexual":{"filtered":false,"severity":"safe"}, "violence":{"filtered":false,"severity":"safe"}}}]}Empty
choices, nousage. The parser required usage on any content-free frame and failed the whole stream:gpt-5.2does not emit it — it is not on the Azure route.Honest severity: latent, not an outage
No user is affected today, and I want that on the record rather than overstated.
gpt-5.4andgpt-5.5never reach this parser:openai-provider.ts:1308-1310routes reasoning models to the Responses API, and both qualify (isReasoningCapableGpt5matchesgpt-5.<n>for n >= 2,src/provider/shared/openai-reasoning.ts:29-40;supportsDefaultReasoningParamsis true forveryfront-cloud, same file :16-19).Loki corroborates:
{service_name="veryfront-server"} |= "empty choices event had no usage"over 30d → 0 hitsinvalid successful streaminveryfront-serverover 7d → 45 hits, allreasoning delta was malformed(the Restore Kimi-compatible streams when providers send null deltas #3815 bug). No other variant.Why fix it anyway
Kimi is Azure-routed and does use this parser.
terraform/staging/variables.tfdeployskimi-k26-staging/kimi-k25-stagingon Azure AI Foundry, and Moonshot models go through Chat Completions, not Responses. Kimi does not emit a content-filter preamble today — but that is an Azure deployment setting, not a property of the model. Enabling content filtering there would break Kimi instantly, in exactly the way #3815 just repaired.Secondary: the guard is wrong on the merits. An empty
choicesarray simply carries no content. Usage-only frames use the same shape and were already tolerated; this is the same case. There was no coverage at all —grep -rn prompt_filter_resultsoverextensions/andsrc/returned nothing.Fix
Skip content-free frames instead of demanding usage. A missing
choiceskey stays rejected — that is a malformed event rather than a content-free one, and that guard keeps its test.Two files, 41 insertions.
Verification
TDD red → green: the new test fails on
mainwith the exact production error, then passes.Replaying the verbatim captured streams:
Kimi captures still parse fully after this change — text stream 23 reasoning deltas, tool stream through to
tool-call+finish.deno test extensions/ext-llm-openai/ src/utils/version.test.ts— 11 passed (178 steps), 0 failed.Fixes veryfront/veryfront-issue-inbox#543