## Description
Fixes#36400
During streaming, `AIMessageChunk.__add__` list-concatenates
`reasoning_details` in `additional_kwargs`, fragmenting a single entry
into many. When `_convert_message_to_dict()` serializes conversation
history back to the OpenRouter API for the next turn, these fragmented
entries cause `BadRequestResponseError`.
### Changes
- Add `_merge_reasoning_details()` helper that merges consecutive
entries sharing the same `type` and `index` (streaming fragments) while
preserving distinct entries (legitimate non-streaming data)
- Metadata from later fragments (e.g. `signature`) is preserved in the
merged result
- Entries without `index` are never merged (safe for non-streaming
responses)
- Call `_merge_reasoning_details()` in `_convert_message_to_dict()`
before serializing `reasoning_details`
### Why merge instead of drop?
Non-streaming users (`invoke()`) rely on `reasoning_details` for
structured metadata (`type`, `signature`, `format`, `index`). Dropping
it entirely would be a regression. This approach fixes streaming while
preserving non-streaming functionality, similar to `langchain-openai`'s
`_implode_reasoning_blocks()`.
## Test plan
- [x] Fragmented entries (same type + same index) are merged into one
- [x] Distinct entries (different index) are preserved separately
- [x] Entries without index are never merged
- [x] Metadata from later fragments (e.g. signature) is preserved
- [x] Single-entry lists pass through unchanged
- [x] Round-trip (dict → message → dict) works correctly
- [x] All 210 unit tests pass
---------
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>