Nothing crashed. That is what made this one interesting. In Microsoft Agent Framework, a streamed Gemini call could finish with no model name at all, while the same call without streaming reported it correctly.

The invisible effect

The non-streaming parser fell back to the client's model when Gemini sent no model_version. The streaming parser, 33 lines further down, passed chunk.model_version straight through. Chunks often carry none, so ChatResponse.model ended up as None.

The observability layer reads that field for gen_ai.response.model, which is a dimension of the token and duration histograms. Absent keys are dropped before the metrics are recorded. On Gemini streams, the per-model breakdown simply disappeared, with zero errors.

Version one: a single line

model=chunk.model_version or self.model,

Parity with the non-streaming path, one line. My pull request description already asked the uncomfortable question: is the client default really the right fallback, when a call can override the model through its options?

Hole one: the wrong model

The automated reviewer answered it with an example. A gemini-2.5-pro request sent through a client configured for gemini-2.5-flash would be labelled flash. The fallback had to be the model resolved for this request, not the client default.

Hole two: the right model, overwritten

Then a maintainer found the subtler one. If an early chunk reports gemini-2.5-pro-002 and the final usage chunk carries no version, applying the fallback on every chunk replaces the observed version with the requested alias. The aggregator keeps the last non-None value, so one trailing chunk was enough.

What merged

fallback_model = model
...
if chunk.model_version:
    fallback_model = chunk.model_version
yield self._process_chunk(chunk, fallback_model)

The fallback is now scoped to the stream: once the server names a version, later chunks that omit it keep that version, and before that the resolved request model is used. Four tests pin the four cases: model from the chunk, fallback to the configured model, fallback to the requested model, and a version seen earlier that must survive.

What I take from it

Review is not there to catch typos. It catches the second-order behaviour of a change, the case where a correct line meets an aggregator three files away. I asked the right question in my description and still answered it wrong in my code. The fix got better because two other readers did not let it through.

Ayoub Hammoudi, Software engineer, full-stack and AI platforms. github.com/Ayoubhm07