Convert a ``Message`` (or any JSON-shaped object) to a single UTF-8 JSON line. Falls back to ``repr`` for non-serializable objects so a malformed message can't bring the writer down — corrupt lines are reader-tolerant per the format design.
(message: Any)
| 121 | |
| 122 | |
| 123 | def _serialize_message(message: Any) -> str: |
| 124 | """Convert a ``Message`` (or any JSON-shaped object) to a single |
| 125 | UTF-8 JSON line. Falls back to ``repr`` for non-serializable objects |
| 126 | so a malformed message can't bring the writer down — corrupt lines |
| 127 | are reader-tolerant per the format design. |
| 128 | """ |
| 129 | if is_dataclass(message) and not isinstance(message, type): |
| 130 | # Chunk-D N1 fold-in (widened from `(TypeError, ValueError)`): |
| 131 | # a pathological dataclass __reduce__/property could leak any |
| 132 | # exception class. The transcript writer is a non-essential |
| 133 | # persistence layer — no failure mode justifies bringing the |
| 134 | # whole agent run down. Catch broadly and fall through to repr. |
| 135 | try: |
| 136 | payload = asdict(message) |
| 137 | except Exception: |
| 138 | payload = {"_unserializable": repr(message)} |
| 139 | elif isinstance(message, dict): |
| 140 | payload = message |
| 141 | else: |
| 142 | # Last-resort: try to serialize a str() of it. |
| 143 | payload = {"_unserializable": repr(message)} |
| 144 | try: |
| 145 | # ``ensure_ascii=False`` keeps unicode readable in transcripts; |
| 146 | # ``separators`` with no spaces keeps lines compact. |
| 147 | return json.dumps(payload, ensure_ascii=False, separators=(",", ":")) |
| 148 | except Exception as exc: |
| 149 | return json.dumps({"_unserializable": repr(message), "_error": str(exc)}) |
| 150 | |
| 151 | |
| 152 | # --------------------------------------------------------------------------- |