88d1d6206f
* fix(streaming): handle completed responses with empty/None choices The streaming fallback guard added in #55932 recognized a completed response object only when its `choices` was a non-empty list. But an adapter can return a completed response whose `choices` is `None` or an empty list (an error / content-filter / terminal frame) — still a whole, non-iterable response, not a token stream. Those shapes fell through to `for chunk in stream` and crashed with 'types.SimpleNamespace' object is not iterable which is exactly issue #55933 (MoA `openai-codex` aggregator on TUI/Desktop, where a stream consumer forces the streaming path). Broaden the guard to discriminate on the PRESENCE of a `choices` attribute (a genuine provider Stream object exposes none), disable streaming for the session, and return the completed object so the outer loop's normal invalid-response validation handles empty/None choices via its retry path instead of iterating. Based on the diagnosis in #56525 by @spiky02plateau (that PR normalized the MoA aggregator return with a one-shot chunk iterator; the common text/tool-call crash was already fixed at this seam by #55932, so this extends the existing guard to cover only the remaining empty/None-choices gap). Fixes #55933 * refactor(streaming): simplify empty-choices guard body and parametrize tests Post-review cleanup (no behavior change): - Inline the single-use `response_choices` local and drop the redundant `if first_choice is not None else None` guard (getattr(None, ...) already returns the default safely). - Collapse the two near-identical empty/None-choices regression tests into one `@pytest.mark.parametrize` case. Mutation-verified: reverting the guard to the old non-empty-list condition still makes both parametrized cases fail with the historical 'types.SimpleNamespace' object is not iterable. --------- Co-authored-by: spiky02plateau <155588579+spiky02plateau@users.noreply.github.com>