Conclusion first: your JSON will diff clean on the first day of the migration, and the server-sent stream underneath it will still hand you truncated answers, half-assembled tool calls, and a finish_reason that never fires. Transport is the layer that fails quietly, because nothing raises and nothing looks wrong until an agent loop calls a function with {} as its arguments.
This entry in the migration diary is about that layer: recording real streams as byte fixtures, reducing every provider's chunk shape into one event model, and gating the cutover on structural equality rather than a spot check you ran once in a notebook.
Why the body passes while the stream fails
A non-streaming request returns a document you can validate, diff, and archive; a streaming request returns a sequence of deltas whose contract lives in details that most API references never fully specify.…