Check whether a tool result carries a ui_patch structuredContent.
(result: Any)
| 1071 | |
| 1072 | @staticmethod |
| 1073 | def _result_contains_patch(result: Any) -> bool: |
| 1074 | """Check whether a tool result carries a ui_patch structuredContent.""" |
| 1075 | try: |
| 1076 | # Unwrap middleware/ToolCallResult wrappers |
| 1077 | raw = result |
| 1078 | seen: set[int] = set() |
| 1079 | while hasattr(raw, "result") and not isinstance(raw, (dict, str)): |
| 1080 | rid = id(raw) |
| 1081 | if rid in seen: |
| 1082 | break |
| 1083 | seen.add(rid) |
| 1084 | raw = raw.result |
| 1085 | |
| 1086 | # Check Pydantic model with structuredContent attr |
| 1087 | if not isinstance(raw, dict) and hasattr(raw, "structuredContent"): |
| 1088 | sc = raw.structuredContent |
| 1089 | if isinstance(sc, dict) and sc.get("type") == "ui_patch": |
| 1090 | return True |
| 1091 | |
| 1092 | if isinstance(raw, dict): |
| 1093 | # Direct structuredContent field |
| 1094 | sc = raw.get("structuredContent") |
| 1095 | if isinstance(sc, dict) and sc.get("type") == "ui_patch": |
| 1096 | return True |
| 1097 | |
| 1098 | # Recover from content text blocks (MCP backwards-compat) |
| 1099 | content = raw.get("content") |
| 1100 | if content is not None and hasattr(content, "content"): |
| 1101 | content = content.content |
| 1102 | if isinstance(content, list): |
| 1103 | for block in content: |
| 1104 | if isinstance(block, dict) and block.get("type") == "text": |
| 1105 | text = block.get("text", "") |
| 1106 | if isinstance(text, str) and '"ui_patch"' in text: |
| 1107 | try: |
| 1108 | parsed = json.loads(text) |
| 1109 | if isinstance(parsed, dict): |
| 1110 | if parsed.get("type") == "ui_patch": |
| 1111 | return True |
| 1112 | psc = parsed.get("structuredContent") |
| 1113 | if ( |
| 1114 | isinstance(psc, dict) |
| 1115 | and psc.get("type") == "ui_patch" |
| 1116 | ): |
| 1117 | return True |
| 1118 | except (json.JSONDecodeError, TypeError): |
| 1119 | pass |
| 1120 | except Exception as e: |
| 1121 | logger.debug("Error checking UI result: %s", e) |
| 1122 | return False |
| 1123 | |
| 1124 | @staticmethod |
| 1125 | def _extract_result_view_url(result: Any) -> str | None: |