When working with LLM/chat “auxiliary” content (reasoning, tool calls/results, structured content blocks, and usage/metadata), enforce structural invariants so provider/streaming/truncation logic can’t silently corrupt meaning.

Practical rules:

Example (reasoning normalization + preserving empty semantics):

from typing import Any

def normalize_reasoning(reasoning: Any) -> str | None:
    if reasoning is None:
        return None
    if isinstance(reasoning, str):
        return reasoning  # preserve ""
    if isinstance(reasoning, list):
        if not reasoning:  # empty list => None
            return None
        parts: list[str] = []
        for item in reasoning:
            if isinstance(item, dict):
                # support multiple provider shapes
                v = item.get("thinking") if "thinking" in item else item.get("content")
                parts.append("" if v == "" else str(v) if v is not None else str(item))
            else:
                parts.append(str(item))
        return "\n".join(parts)
    return str(reasoning)

Apply the same “invariant-first” mindset to tool-call/tool-result pairing and streaming chunk finalization: write code so the internal model never allows impossible states (or it is corrected immediately), and back it with targeted tests.