aip-agents-binary 0.5.20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) hide show
  1. aip_agents/__init__.py +65 -0
  2. aip_agents/a2a/__init__.py +19 -0
  3. aip_agents/a2a/server/__init__.py +10 -0
  4. aip_agents/a2a/server/base_executor.py +1086 -0
  5. aip_agents/a2a/server/google_adk_executor.py +198 -0
  6. aip_agents/a2a/server/langflow_executor.py +180 -0
  7. aip_agents/a2a/server/langgraph_executor.py +270 -0
  8. aip_agents/a2a/types.py +232 -0
  9. aip_agents/agent/__init__.py +27 -0
  10. aip_agents/agent/base_agent.py +970 -0
  11. aip_agents/agent/base_langgraph_agent.py +2942 -0
  12. aip_agents/agent/google_adk_agent.py +926 -0
  13. aip_agents/agent/google_adk_constants.py +6 -0
  14. aip_agents/agent/hitl/__init__.py +24 -0
  15. aip_agents/agent/hitl/config.py +28 -0
  16. aip_agents/agent/hitl/langgraph_hitl_mixin.py +515 -0
  17. aip_agents/agent/hitl/manager.py +532 -0
  18. aip_agents/agent/hitl/models.py +18 -0
  19. aip_agents/agent/hitl/prompt/__init__.py +9 -0
  20. aip_agents/agent/hitl/prompt/base.py +42 -0
  21. aip_agents/agent/hitl/prompt/deferred.py +73 -0
  22. aip_agents/agent/hitl/registry.py +149 -0
  23. aip_agents/agent/interface.py +138 -0
  24. aip_agents/agent/interfaces.py +65 -0
  25. aip_agents/agent/langflow_agent.py +464 -0
  26. aip_agents/agent/langgraph_memory_enhancer_agent.py +433 -0
  27. aip_agents/agent/langgraph_react_agent.py +2514 -0
  28. aip_agents/agent/system_instruction_context.py +34 -0
  29. aip_agents/clients/__init__.py +10 -0
  30. aip_agents/clients/langflow/__init__.py +10 -0
  31. aip_agents/clients/langflow/client.py +477 -0
  32. aip_agents/clients/langflow/types.py +18 -0
  33. aip_agents/constants.py +23 -0
  34. aip_agents/credentials/manager.py +132 -0
  35. aip_agents/examples/__init__.py +5 -0
  36. aip_agents/examples/compare_streaming_client.py +783 -0
  37. aip_agents/examples/compare_streaming_server.py +142 -0
  38. aip_agents/examples/demo_memory_recall.py +401 -0
  39. aip_agents/examples/hello_world_a2a_google_adk_client.py +49 -0
  40. aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
  41. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
  42. aip_agents/examples/hello_world_a2a_google_adk_server.py +79 -0
  43. aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
  44. aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
  45. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
  46. aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
  47. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
  48. aip_agents/examples/hello_world_a2a_langchain_reference_server.py +105 -0
  49. aip_agents/examples/hello_world_a2a_langchain_server.py +79 -0
  50. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +78 -0
  51. aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
  52. aip_agents/examples/hello_world_a2a_langflow_server.py +82 -0
  53. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
  54. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
  55. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +92 -0
  56. aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
  57. aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
  58. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
  59. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
  60. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
  61. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
  62. aip_agents/examples/hello_world_a2a_langgraph_server.py +84 -0
  63. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +79 -0
  64. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +132 -0
  65. aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
  66. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
  67. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +251 -0
  68. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
  69. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +80 -0
  70. aip_agents/examples/hello_world_google_adk.py +41 -0
  71. aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
  72. aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
  73. aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
  74. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
  75. aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
  76. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
  77. aip_agents/examples/hello_world_google_adk_stream.py +44 -0
  78. aip_agents/examples/hello_world_langchain.py +28 -0
  79. aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
  80. aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
  81. aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
  82. aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
  83. aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
  84. aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
  85. aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
  86. aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
  87. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
  88. aip_agents/examples/hello_world_langchain_stream.py +36 -0
  89. aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
  90. aip_agents/examples/hello_world_langflow_agent.py +163 -0
  91. aip_agents/examples/hello_world_langgraph.py +39 -0
  92. aip_agents/examples/hello_world_langgraph_bosa_twitter.py +41 -0
  93. aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
  94. aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
  95. aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
  96. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
  97. aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
  98. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
  99. aip_agents/examples/hello_world_langgraph_stream.py +43 -0
  100. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
  101. aip_agents/examples/hello_world_model_switch_cli.py +210 -0
  102. aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
  103. aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
  104. aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
  105. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
  106. aip_agents/examples/hello_world_pii_logger.py +21 -0
  107. aip_agents/examples/hello_world_sentry.py +133 -0
  108. aip_agents/examples/hello_world_step_limits.py +273 -0
  109. aip_agents/examples/hello_world_stock_a2a_server.py +103 -0
  110. aip_agents/examples/hello_world_tool_output_client.py +46 -0
  111. aip_agents/examples/hello_world_tool_output_server.py +114 -0
  112. aip_agents/examples/hitl_demo.py +724 -0
  113. aip_agents/examples/mcp_configs/configs.py +63 -0
  114. aip_agents/examples/mcp_servers/common.py +76 -0
  115. aip_agents/examples/mcp_servers/mcp_name.py +29 -0
  116. aip_agents/examples/mcp_servers/mcp_server_http.py +19 -0
  117. aip_agents/examples/mcp_servers/mcp_server_sse.py +19 -0
  118. aip_agents/examples/mcp_servers/mcp_server_stdio.py +19 -0
  119. aip_agents/examples/mcp_servers/mcp_time.py +10 -0
  120. aip_agents/examples/pii_demo_langgraph_client.py +69 -0
  121. aip_agents/examples/pii_demo_langgraph_server.py +126 -0
  122. aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
  123. aip_agents/examples/pii_demo_multi_agent_server.py +247 -0
  124. aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
  125. aip_agents/examples/todolist_planning_a2a_langgraph_server.py +88 -0
  126. aip_agents/examples/tools/__init__.py +27 -0
  127. aip_agents/examples/tools/adk_arithmetic_tools.py +36 -0
  128. aip_agents/examples/tools/adk_weather_tool.py +60 -0
  129. aip_agents/examples/tools/data_generator_tool.py +103 -0
  130. aip_agents/examples/tools/data_visualization_tool.py +312 -0
  131. aip_agents/examples/tools/image_artifact_tool.py +136 -0
  132. aip_agents/examples/tools/langchain_arithmetic_tools.py +26 -0
  133. aip_agents/examples/tools/langchain_currency_exchange_tool.py +88 -0
  134. aip_agents/examples/tools/langchain_graph_artifact_tool.py +172 -0
  135. aip_agents/examples/tools/langchain_weather_tool.py +48 -0
  136. aip_agents/examples/tools/langgraph_streaming_tool.py +130 -0
  137. aip_agents/examples/tools/mock_retrieval_tool.py +56 -0
  138. aip_agents/examples/tools/pii_demo_tools.py +189 -0
  139. aip_agents/examples/tools/random_chart_tool.py +142 -0
  140. aip_agents/examples/tools/serper_tool.py +202 -0
  141. aip_agents/examples/tools/stock_tools.py +82 -0
  142. aip_agents/examples/tools/table_generator_tool.py +167 -0
  143. aip_agents/examples/tools/time_tool.py +82 -0
  144. aip_agents/examples/tools/weather_forecast_tool.py +38 -0
  145. aip_agents/executor/agent_executor.py +473 -0
  146. aip_agents/executor/base.py +48 -0
  147. aip_agents/mcp/__init__.py +1 -0
  148. aip_agents/mcp/client/__init__.py +14 -0
  149. aip_agents/mcp/client/base_mcp_client.py +369 -0
  150. aip_agents/mcp/client/connection_manager.py +193 -0
  151. aip_agents/mcp/client/google_adk/__init__.py +11 -0
  152. aip_agents/mcp/client/google_adk/client.py +381 -0
  153. aip_agents/mcp/client/langchain/__init__.py +11 -0
  154. aip_agents/mcp/client/langchain/client.py +265 -0
  155. aip_agents/mcp/client/persistent_session.py +359 -0
  156. aip_agents/mcp/client/session_pool.py +351 -0
  157. aip_agents/mcp/client/transports.py +215 -0
  158. aip_agents/mcp/utils/__init__.py +7 -0
  159. aip_agents/mcp/utils/config_validator.py +139 -0
  160. aip_agents/memory/__init__.py +14 -0
  161. aip_agents/memory/adapters/__init__.py +10 -0
  162. aip_agents/memory/adapters/base_adapter.py +717 -0
  163. aip_agents/memory/adapters/mem0.py +84 -0
  164. aip_agents/memory/base.py +84 -0
  165. aip_agents/memory/constants.py +49 -0
  166. aip_agents/memory/factory.py +86 -0
  167. aip_agents/memory/guidance.py +20 -0
  168. aip_agents/memory/simple_memory.py +47 -0
  169. aip_agents/middleware/__init__.py +17 -0
  170. aip_agents/middleware/base.py +88 -0
  171. aip_agents/middleware/manager.py +128 -0
  172. aip_agents/middleware/todolist.py +274 -0
  173. aip_agents/schema/__init__.py +69 -0
  174. aip_agents/schema/a2a.py +56 -0
  175. aip_agents/schema/agent.py +111 -0
  176. aip_agents/schema/hitl.py +157 -0
  177. aip_agents/schema/langgraph.py +37 -0
  178. aip_agents/schema/model_id.py +97 -0
  179. aip_agents/schema/step_limit.py +108 -0
  180. aip_agents/schema/storage.py +40 -0
  181. aip_agents/sentry/__init__.py +11 -0
  182. aip_agents/sentry/sentry.py +151 -0
  183. aip_agents/storage/__init__.py +41 -0
  184. aip_agents/storage/base.py +85 -0
  185. aip_agents/storage/clients/__init__.py +12 -0
  186. aip_agents/storage/clients/minio_client.py +318 -0
  187. aip_agents/storage/config.py +62 -0
  188. aip_agents/storage/providers/__init__.py +15 -0
  189. aip_agents/storage/providers/base.py +106 -0
  190. aip_agents/storage/providers/memory.py +114 -0
  191. aip_agents/storage/providers/object_storage.py +214 -0
  192. aip_agents/tools/__init__.py +33 -0
  193. aip_agents/tools/bosa_tools.py +105 -0
  194. aip_agents/tools/browser_use/__init__.py +82 -0
  195. aip_agents/tools/browser_use/action_parser.py +103 -0
  196. aip_agents/tools/browser_use/browser_use_tool.py +1112 -0
  197. aip_agents/tools/browser_use/llm_config.py +120 -0
  198. aip_agents/tools/browser_use/minio_storage.py +198 -0
  199. aip_agents/tools/browser_use/schemas.py +119 -0
  200. aip_agents/tools/browser_use/session.py +76 -0
  201. aip_agents/tools/browser_use/session_errors.py +132 -0
  202. aip_agents/tools/browser_use/steel_session_recording.py +317 -0
  203. aip_agents/tools/browser_use/streaming.py +813 -0
  204. aip_agents/tools/browser_use/structured_data_parser.py +257 -0
  205. aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
  206. aip_agents/tools/browser_use/types.py +78 -0
  207. aip_agents/tools/code_sandbox/__init__.py +26 -0
  208. aip_agents/tools/code_sandbox/constant.py +13 -0
  209. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +257 -0
  210. aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +411 -0
  211. aip_agents/tools/constants.py +165 -0
  212. aip_agents/tools/document_loader/__init__.py +44 -0
  213. aip_agents/tools/document_loader/base_reader.py +302 -0
  214. aip_agents/tools/document_loader/docx_reader_tool.py +68 -0
  215. aip_agents/tools/document_loader/excel_reader_tool.py +171 -0
  216. aip_agents/tools/document_loader/pdf_reader_tool.py +79 -0
  217. aip_agents/tools/document_loader/pdf_splitter.py +169 -0
  218. aip_agents/tools/gl_connector/__init__.py +5 -0
  219. aip_agents/tools/gl_connector/tool.py +351 -0
  220. aip_agents/tools/memory_search/__init__.py +22 -0
  221. aip_agents/tools/memory_search/base.py +200 -0
  222. aip_agents/tools/memory_search/mem0.py +258 -0
  223. aip_agents/tools/memory_search/schema.py +48 -0
  224. aip_agents/tools/memory_search_tool.py +26 -0
  225. aip_agents/tools/time_tool.py +117 -0
  226. aip_agents/tools/tool_config_injector.py +300 -0
  227. aip_agents/tools/web_search/__init__.py +15 -0
  228. aip_agents/tools/web_search/serper_tool.py +187 -0
  229. aip_agents/types/__init__.py +70 -0
  230. aip_agents/types/a2a_events.py +13 -0
  231. aip_agents/utils/__init__.py +79 -0
  232. aip_agents/utils/a2a_connector.py +1757 -0
  233. aip_agents/utils/artifact_helpers.py +502 -0
  234. aip_agents/utils/constants.py +22 -0
  235. aip_agents/utils/datetime/__init__.py +34 -0
  236. aip_agents/utils/datetime/normalization.py +231 -0
  237. aip_agents/utils/datetime/timezone.py +206 -0
  238. aip_agents/utils/env_loader.py +27 -0
  239. aip_agents/utils/event_handler_registry.py +58 -0
  240. aip_agents/utils/file_prompt_utils.py +176 -0
  241. aip_agents/utils/final_response_builder.py +211 -0
  242. aip_agents/utils/formatter_llm_client.py +231 -0
  243. aip_agents/utils/langgraph/__init__.py +19 -0
  244. aip_agents/utils/langgraph/converter.py +128 -0
  245. aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
  246. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
  247. aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
  248. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1071 -0
  249. aip_agents/utils/langgraph/tool_output_management.py +967 -0
  250. aip_agents/utils/logger.py +195 -0
  251. aip_agents/utils/metadata/__init__.py +27 -0
  252. aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
  253. aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
  254. aip_agents/utils/metadata/activity_narrative/builder.py +817 -0
  255. aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
  256. aip_agents/utils/metadata/activity_narrative/context.py +49 -0
  257. aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
  258. aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
  259. aip_agents/utils/metadata/schemas/__init__.py +16 -0
  260. aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
  261. aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
  262. aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
  263. aip_agents/utils/metadata_helper.py +358 -0
  264. aip_agents/utils/name_preprocessor/__init__.py +17 -0
  265. aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
  266. aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
  267. aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
  268. aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
  269. aip_agents/utils/pii/__init__.py +25 -0
  270. aip_agents/utils/pii/pii_handler.py +397 -0
  271. aip_agents/utils/pii/pii_helper.py +207 -0
  272. aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
  273. aip_agents/utils/reference_helper.py +273 -0
  274. aip_agents/utils/sse_chunk_transformer.py +831 -0
  275. aip_agents/utils/step_limit_manager.py +265 -0
  276. aip_agents/utils/token_usage_helper.py +156 -0
  277. aip_agents_binary-0.5.20.dist-info/METADATA +681 -0
  278. aip_agents_binary-0.5.20.dist-info/RECORD +280 -0
  279. aip_agents_binary-0.5.20.dist-info/WHEEL +5 -0
  280. aip_agents_binary-0.5.20.dist-info/top_level.txt +1 -0
@@ -0,0 +1,813 @@
1
+ """Helpers for assembling streaming responses emitted by the browser-use tool.
2
+
3
+ Authors:
4
+ Raymond Christopher (raymond.christopher@gdplabs.id)
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import re
10
+ from collections.abc import Callable, Iterable, Mapping
11
+ from textwrap import shorten
12
+ from typing import Any, Literal
13
+
14
+ from browser_use import Agent
15
+ from browser_use.agent.views import ActionResult, AgentBrain
16
+ from gllm_inference.schema import Message, MessageRole
17
+
18
+ from aip_agents.tools.browser_use.types import StreamingResponse, ToolCallInfo
19
+ from aip_agents.types import A2AStreamEventType
20
+ from aip_agents.utils.formatter_llm_client import (
21
+ FormatterInvocationError,
22
+ FormatterInvokerUnavailableError,
23
+ get_formatter_llm_client,
24
+ )
25
+ from aip_agents.utils.logger import get_logger
26
+ from aip_agents.utils.metadata.activity_metadata_helper import _create_activity_info
27
+ from aip_agents.utils.metadata.schemas.thinking_schema import ThinkingDataType
28
+ from aip_agents.utils.metadata.thinking_metadata_helper import _create_thinking_info
29
+ from aip_agents.utils.metadata_helper import Kind, MetadataFieldKeys, Status
30
+
31
+ TASK_COMPLETED_MESSAGE = "Task completed"
32
+ PROCESSING_MESSAGE = "Working on your request..."
33
+ PRIMARY_TOOL_NAME = "browser_use_tool"
34
+
35
+ THINKING_SUMMARY_SYSTEM_PROMPT = (
36
+ "You summarize the latest progress made by an automation agent. Provide Markdown with a bold, concise headline "
37
+ "describing the key outcome, followed by one to three bullet points that highlight notable actions or findings. "
38
+ "Stay factual, avoid internal identifiers, and keep the explanation user-friendly."
39
+ )
40
+
41
+ logger = get_logger(__name__)
42
+ _formatter_llm_client = get_formatter_llm_client()
43
+
44
+
45
+ def _normalize_segment(value: str) -> str:
46
+ """Collapse whitespace and strip the provided segment.
47
+
48
+ Args:
49
+ value: The text segment to normalize.
50
+
51
+ Returns:
52
+ The normalized text segment.
53
+ """
54
+ return re.sub(r"\s+", " ", value or "").strip()
55
+
56
+
57
+ def _first_sentence(value: str) -> str:
58
+ """Return the first sentence-like segment from the provided text.
59
+
60
+ Args:
61
+ value: The text to extract the first sentence from.
62
+
63
+ Returns:
64
+ The first sentence from the text, or empty string if no text provided.
65
+ """
66
+ if not value:
67
+ return ""
68
+ sentence, *_ = re.split(r"(?<=[.!?])\s+", value, maxsplit=1)
69
+ return sentence.strip()
70
+
71
+
72
+ def _format_completion_content(tool_names: list[str]) -> str:
73
+ """Mirror agent completion content formatting for consistency with A2A events.
74
+
75
+ Args:
76
+ tool_names: Ordered collection of tool names involved in the step.
77
+
78
+ Returns:
79
+ A descriptive completion string (e.g., ``"Completed tools: browser_use_tool"``).
80
+ """
81
+ unique_tool_names = list(dict.fromkeys(tool_names))
82
+ has_delegation_tools = any(name.startswith("delegate_to") for name in unique_tool_names)
83
+ content_prefix = "Completed sub-agents:" if has_delegation_tools else "Completed tools:"
84
+ return f"{content_prefix} {', '.join(unique_tool_names)}"
85
+
86
+
87
+ def _format_tool_display_name(tool_name: str) -> str:
88
+ """Convert a snake_case tool identifier into a human-friendly display name.
89
+
90
+ Args:
91
+ tool_name: Raw tool identifier, typically snake_case.
92
+
93
+ Returns:
94
+ A human-friendly display name with capitalized words.
95
+ """
96
+ parts = tool_name.replace("_", " ").split()
97
+ return " ".join(part.capitalize() if not part.isupper() else part for part in parts)
98
+
99
+
100
+ def _build_completion_activity_message(tool_info: dict[str, Any], final_output: str) -> str:
101
+ """Create a succinct completion headline for activity metadata.
102
+
103
+ Args:
104
+ tool_info: Dictionary containing tool metadata information.
105
+ final_output: The final output text returned by the tool.
106
+
107
+ Returns:
108
+ Formatted completion headline string.
109
+ """
110
+ display_name = _format_tool_display_name(_resolve_tool_name(tool_info))
111
+ headline = _summarize_final_headline(final_output) or f"{display_name} task completed."
112
+ detail = _summarize_final_detail(final_output, display_name)
113
+ return f"**{headline}**\n\n{detail}"
114
+
115
+
116
+ def _summarize_final_headline(final_output: str) -> str:
117
+ """Return a concise headline extracted from the final output string.
118
+
119
+ Args:
120
+ final_output: The final output text returned by the tool.
121
+
122
+ Returns:
123
+ A concise headline string, or empty string if no output provided.
124
+ """
125
+ if not final_output:
126
+ return ""
127
+
128
+ for raw_line in final_output.splitlines():
129
+ line = _normalize_segment(raw_line)
130
+ if not line:
131
+ continue
132
+ candidate = _first_sentence(line) or line
133
+ if "|" in candidate:
134
+ candidate = candidate.split("|", 1)[0].strip()
135
+ candidate = candidate.rstrip(":")
136
+ if candidate:
137
+ return candidate
138
+ return ""
139
+
140
+
141
+ def _summarize_final_detail(final_output: str, display_name: str) -> str:
142
+ """Return a short detail sentence summarizing completion.
143
+
144
+ Args:
145
+ final_output: The final output text returned by the tool.
146
+ display_name: Human-friendly display name of the tool.
147
+
148
+ Returns:
149
+ A short detail sentence summarizing completion.
150
+ """
151
+ if not final_output:
152
+ return f"Finished via {display_name}; no additional summary provided."
153
+
154
+ lines = [line.strip() for line in final_output.splitlines() if line.strip()]
155
+ if len(lines) > 1:
156
+ second_line = _normalize_segment(lines[1])
157
+ if second_line and not second_line.startswith("|"):
158
+ return second_line
159
+ return f"Finished via {display_name}; see the shared output for details."
160
+
161
+
162
+ def _resolve_tool_name(tool_info: dict[str, Any]) -> str:
163
+ """Return the most specific tool name present in ``tool_info``.
164
+
165
+ Args:
166
+ tool_info: Metadata dictionary emitted by browser-use steps.
167
+
168
+ Returns:
169
+ str: Tool name suitable for display, defaulting to ``PRIMARY_TOOL_NAME``.
170
+ """
171
+ if not isinstance(tool_info, dict):
172
+ return PRIMARY_TOOL_NAME
173
+
174
+ name = tool_info.get("name")
175
+ if _is_valid_tool_name(name):
176
+ return name # type: ignore[return-value]
177
+
178
+ tool_calls = tool_info.get("tool_calls")
179
+ if not isinstance(tool_calls, list):
180
+ return PRIMARY_TOOL_NAME
181
+
182
+ for entry in tool_calls:
183
+ candidate = entry.get("name") if isinstance(entry, dict) else None
184
+ if _is_valid_tool_name(candidate):
185
+ return candidate # type: ignore[return-value]
186
+
187
+ return PRIMARY_TOOL_NAME
188
+
189
+
190
+ def _is_valid_tool_name(name: Any) -> bool:
191
+ """Return True when the provided name is a non-empty string.
192
+
193
+ Args:
194
+ name: Candidate tool name.
195
+
196
+ Returns:
197
+ bool: True when ``name`` is a non-empty string.
198
+ """
199
+ return isinstance(name, str) and bool(name.strip())
200
+
201
+
202
+ def create_step_response(
203
+ agent: Agent,
204
+ tool_calls: list[ToolCallInfo],
205
+ is_done: bool,
206
+ content: str,
207
+ thinking_message: str | None,
208
+ ) -> StreamingResponse:
209
+ """Compose the per-step payload emitted during browser-use streaming.
210
+
211
+ Args:
212
+ agent: Browser-use agent producing the step output.
213
+ tool_calls: Tool call descriptors extracted from the step.
214
+ is_done: Flag indicating whether this is the final step in the run.
215
+ content: High-level status text describing the step progress.
216
+ thinking_message: Optional preformatted thinking summary to attach.
217
+
218
+ Returns:
219
+ StreamingResponse: Serialized payload for the current streaming step.
220
+ """
221
+ tool_calls_dict = [{"name": tc.name, "args": tc.args, "output": tc.output} for tc in tool_calls]
222
+
223
+ if is_done:
224
+ final_tool = _get_done_tool_for_final_response(agent, tool_calls_dict)
225
+ final_output = final_tool.get("output") or TASK_COMPLETED_MESSAGE
226
+ tool_info = {
227
+ "name": PRIMARY_TOOL_NAME,
228
+ "args": final_tool.get("args", {}),
229
+ "output": final_output,
230
+ "execution_time": final_tool.get("execution_time"),
231
+ "tool_calls": tool_calls_dict,
232
+ }
233
+ completion_message = _build_completion_activity_message(tool_info, final_output)
234
+ logger.info(
235
+ "Browser-use completion message generated: headline=%r detail=%r",
236
+ completion_message.split("\n\n", 1)[0].strip("*"),
237
+ completion_message.split("\n\n", 1)[1] if "\n\n" in completion_message else "",
238
+ )
239
+ activity_info = _create_activity_info({"message": completion_message})
240
+ logger.debug("Browser-use completion activity info payload: %s", activity_info)
241
+ metadata = {
242
+ MetadataFieldKeys.KIND: Kind.FINAL_THINKING_STEP,
243
+ MetadataFieldKeys.STATUS: Status.FINISHED,
244
+ MetadataFieldKeys.TOOL_INFO: tool_info,
245
+ }
246
+ return StreamingResponse(
247
+ event_type=A2AStreamEventType.STATUS_UPDATE,
248
+ content=final_output,
249
+ thinking_and_activity_info=activity_info,
250
+ is_final=False,
251
+ tool_info=tool_info,
252
+ metadata=metadata,
253
+ )
254
+
255
+ tool_info = {"tool_calls": tool_calls_dict, "status": "running"}
256
+ thinking_text = thinking_message or _build_fallback_thinking_message(content, tool_calls_dict)
257
+ thinking_info = _create_thinking_info(thinking_text)
258
+ metadata = {
259
+ MetadataFieldKeys.KIND: Kind.AGENT_THINKING_STEP,
260
+ MetadataFieldKeys.STATUS: Status.RUNNING,
261
+ MetadataFieldKeys.TOOL_INFO: tool_info,
262
+ }
263
+
264
+ return StreamingResponse(
265
+ event_type=A2AStreamEventType.STATUS_UPDATE,
266
+ content=content,
267
+ thinking_and_activity_info=thinking_info,
268
+ is_final=False,
269
+ tool_info=tool_info,
270
+ metadata=metadata,
271
+ )
272
+
273
+
274
+ async def generate_thinking_message(
275
+ content: str,
276
+ tool_calls: list[dict[str, Any]],
277
+ *,
278
+ is_final: bool,
279
+ ) -> str | None:
280
+ """Generate a user-facing thinking summary using the formatter LLM when available.
281
+
282
+ Args:
283
+ content: High-level status text describing the step progress.
284
+ tool_calls: Serialized tool call dictionaries with outputs.
285
+ is_final: Whether the task has completed.
286
+
287
+ Returns:
288
+ Markdown-formatted summary string, or ``None`` when not applicable.
289
+ """
290
+ if is_final:
291
+ return None
292
+
293
+ summary = await _summarize_with_llm(content, tool_calls)
294
+ if summary:
295
+ return summary
296
+
297
+ return _build_fallback_thinking_message(content, tool_calls)
298
+
299
+
300
+ def _build_fallback_thinking_message(content: str, tool_calls: list[dict[str, Any]]) -> str:
301
+ """Fallback deterministic summary when the formatter LLM is unavailable.
302
+
303
+ Args:
304
+ content: High-level status text describing the step progress.
305
+ tool_calls: Serialized tool call dictionaries with outputs.
306
+
307
+ Returns:
308
+ Markdown summary string built from deterministic heuristics.
309
+ """
310
+ headline = _normalize_segment(_first_sentence(content)) or "Working on your request"
311
+ details = "\n".join(_summarize_tool_details(tool_calls))
312
+ return f"**{headline}**\n\n{details}"
313
+
314
+
315
+ def _tool_display_and_output(
316
+ call: dict[str, Any],
317
+ *,
318
+ width: int,
319
+ fallback: str,
320
+ ) -> tuple[str, str] | None:
321
+ """Extract a display name and truncated output snippet for a tool call.
322
+
323
+ Args:
324
+ call: Serialized tool call dictionary.
325
+ width: Character width for truncating the output snippet.
326
+ fallback: Placeholder text when the call lacks textual output.
327
+
328
+ Returns:
329
+ tuple[str, str] | None: (display name, snippet) pair when parsable, otherwise None.
330
+ """
331
+ if not isinstance(call, dict):
332
+ return None
333
+
334
+ name = _format_tool_display_name(str(call.get("name", PRIMARY_TOOL_NAME)))
335
+ output = call.get("output")
336
+ if isinstance(output, str) and output.strip():
337
+ snippet = shorten(_normalize_segment(output), width=width, placeholder="…")
338
+ else:
339
+ snippet = fallback
340
+ return name, snippet
341
+
342
+
343
+ def _summarize_tool_details(tool_calls: list[dict[str, Any]]) -> list[str]:
344
+ """Return bullet-friendly summaries for each tool call.
345
+
346
+ Args:
347
+ tool_calls: Serialized tool call dictionaries with outputs.
348
+
349
+ Returns:
350
+ list[str]: Markdown bullet strings describing tool outputs.
351
+ """
352
+ detail_lines: list[str] = []
353
+ for call in tool_calls:
354
+ result = _tool_display_and_output(call, width=240, fallback="running")
355
+ if not result:
356
+ continue
357
+ name, snippet = result
358
+ detail_lines.append(f"- {name}: {snippet}")
359
+ if not detail_lines:
360
+ detail_lines.append("- Awaiting tool output")
361
+ return detail_lines
362
+
363
+
364
+ async def _summarize_with_llm(content: str, tool_calls: list[dict[str, Any]]) -> str | None:
365
+ """Attempt to summarize progress with the formatter LLM.
366
+
367
+ Args:
368
+ content: High-level status text describing the step progress.
369
+ tool_calls: Serialized tool call dictionaries with outputs.
370
+
371
+ Returns:
372
+ Markdown-formatted summary string, or ``None`` when LLM is unavailable or fails.
373
+ """
374
+ messages = _build_thinking_messages(content, tool_calls)
375
+ try:
376
+ response = await _formatter_llm_client.invoke(messages=messages)
377
+ except FormatterInvokerUnavailableError:
378
+ logger.warning("Unable to initialize thinking summary LLM; DEFAULT_MODEL_FORMATTER is unset")
379
+ return None
380
+ except FormatterInvocationError as exc: # noqa: BLE001
381
+ logger.warning("Thinking summary LLM invocation failed: %s", exc)
382
+ return None
383
+
384
+ response_text = _coerce_response_text(response)
385
+ return response_text or None
386
+
387
+
388
+ def _build_thinking_messages(content: str, tool_calls: list[dict[str, Any]]) -> list[Message]:
389
+ """Compose the message list sent to the formatter LLM.
390
+
391
+ Args:
392
+ content: High-level status text describing the step progress.
393
+ tool_calls: Serialized tool call dictionaries with outputs.
394
+
395
+ Returns:
396
+ list[Message]: LangChain message objects forming the prompt.
397
+ """
398
+ formatted_tools = _format_tool_context(tool_calls)
399
+ user_sections = [
400
+ f"Current status: {content}",
401
+ "Latest tool outputs:",
402
+ formatted_tools or "- No tool output available yet.",
403
+ (
404
+ "Produce Markdown with a bold first sentence capturing the key progress so far, "
405
+ "followed by one to three bullet points highlighting notable actions or findings."
406
+ ),
407
+ ]
408
+ user_prompt = "\n\n".join(user_sections)
409
+ return [
410
+ Message(role=MessageRole.SYSTEM, contents=[THINKING_SUMMARY_SYSTEM_PROMPT]),
411
+ Message(role=MessageRole.USER, contents=[user_prompt]),
412
+ ]
413
+
414
+
415
+ def _format_tool_context(tool_calls: list[dict[str, Any]]) -> str:
416
+ """Format tool calls as enumerated Markdown lines for the LLM prompt.
417
+
418
+ Args:
419
+ tool_calls: Serialized tool call dictionaries with outputs.
420
+
421
+ Returns:
422
+ str: Human-readable summary of recent tool activity.
423
+ """
424
+ lines: list[str] = []
425
+ for idx, call in enumerate(tool_calls, start=1):
426
+ result = _tool_display_and_output(call, width=300, fallback="(no output)")
427
+ if not result:
428
+ continue
429
+ name, snippet = result
430
+ lines.append(f"{idx}. {name}: {snippet}")
431
+ return "\n".join(lines)
432
+
433
+
434
+ def _coerce_response_text(response: Any) -> str:
435
+ """Normalize formatter responses into a plain string.
436
+
437
+ Args:
438
+ response: Formatter output which may be a string, mapping, or object.
439
+
440
+ Returns:
441
+ str: Stripped textual representation.
442
+ """
443
+ if isinstance(response, str):
444
+ return response.strip()
445
+ if isinstance(response, Mapping):
446
+ for key in ("content", "text", "message"):
447
+ value = response.get(key)
448
+ if isinstance(value, str):
449
+ return value.strip()
450
+ return ""
451
+ content_value = getattr(response, "content", None)
452
+ if isinstance(content_value, str):
453
+ return content_value.strip()
454
+ if response is None:
455
+ return ""
456
+ return str(response).strip()
457
+
458
+
459
+ def create_error_response(error_message: str, recording_url: str = "") -> dict:
460
+ """Create a standardized error response.
461
+
462
+ Args:
463
+ error_message: The error message to include.
464
+ recording_url: The recording URL if available.
465
+
466
+ Returns:
467
+ dict: Standardized error response.
468
+ """
469
+ activity_info = _create_activity_info({"message": f"**Error**\n\n{error_message}"})
470
+ metadata: dict[str, Any] = {}
471
+ if recording_url:
472
+ metadata["recording_url"] = recording_url
473
+
474
+ response = StreamingResponse(
475
+ event_type=A2AStreamEventType.TOOL_RESULT,
476
+ content=f"Error: {error_message}",
477
+ thinking_and_activity_info=activity_info,
478
+ is_final=True,
479
+ tool_info={"tool_calls": []},
480
+ metadata=metadata,
481
+ )
482
+
483
+ return response.to_dict()
484
+
485
+
486
+ def _compose_thinking_message(agent: Agent, summary: str | None = None) -> str:
487
+ """Compose a combined memory/thinking message from agent state.
488
+
489
+ Args:
490
+ agent: Browser-use agent instance containing state information.
491
+ summary: Optional high-level summary to headline the thinking message.
492
+
493
+ Returns:
494
+ Formatted Markdown message combining thinking and memory state.
495
+ """
496
+ thinking_text, memory_text = _extract_state_texts(agent)
497
+ summary_headline = _summarize_headline(summary)
498
+ headline = _build_thinking_headline(summary_headline, thinking_text, memory_text)
499
+ if not headline:
500
+ return "**Working on your request…**"
501
+
502
+ message = f"**{headline}**"
503
+ remainder = _compose_remainder_block(summary_headline, thinking_text, memory_text)
504
+ if remainder:
505
+ message += f"\n\n{remainder}"
506
+ return message
507
+
508
+
509
+ def _extract_state_texts(agent: Agent) -> tuple[str, str]:
510
+ """Return normalized thinking and memory text from the agent state.
511
+
512
+ Args:
513
+ agent: Browser-use agent instance containing state information.
514
+
515
+ Returns:
516
+ Tuple of (thinking_text, memory_text) both normalized strings.
517
+ """
518
+ thinking_text = ""
519
+ memory_text = ""
520
+
521
+ if agent.state.last_model_output and agent.state.last_model_output.current_state:
522
+ current_state: AgentBrain = agent.state.last_model_output.current_state
523
+ memory_text = _normalize_segment(current_state.memory)
524
+ thinking_text = _normalize_segment(current_state.thinking)
525
+
526
+ return thinking_text, memory_text
527
+
528
+
529
+ def _summarize_headline(summary: str | None) -> str:
530
+ """Return a single-sentence headline from the summary output.
531
+
532
+ Args:
533
+ summary: Raw Markdown output summary.
534
+
535
+ Returns:
536
+ str: Headline text truncated for display.
537
+ """
538
+ if not summary:
539
+ return ""
540
+ return shorten(_normalize_segment(_first_sentence(summary)), width=200, placeholder="…")
541
+
542
+
543
+ def _build_thinking_headline(
544
+ summary_headline: str,
545
+ thinking_text: str,
546
+ memory_text: str,
547
+ ) -> str:
548
+ """Build a fallback headline from thinking/memory when summary is absent.
549
+
550
+ Args:
551
+ summary_headline: Headline derived from the final summary.
552
+ thinking_text: Normalized thinking text emitted by the agent.
553
+ memory_text: Normalized scratchpad/memory text emitted by the agent.
554
+
555
+ Returns:
556
+ str: Headline string for the thinking panel.
557
+ """
558
+ focus_source = thinking_text or memory_text
559
+ headline_source = _normalize_segment(_first_sentence(focus_source)) if focus_source else ""
560
+
561
+ return summary_headline or (shorten(headline_source, width=200, placeholder="…") if headline_source else "")
562
+
563
+
564
+ def _compose_remainder_block(summary_headline: str, thinking_text: str, memory_text: str) -> str:
565
+ """Compose the supporting paragraph following the headline.
566
+
567
+ Args:
568
+ summary_headline: Headline text already chosen.
569
+ thinking_text: Normalized thinking text.
570
+ memory_text: Normalized memory text.
571
+
572
+ Returns:
573
+ str: Supporting text block or empty string.
574
+ """
575
+ remainder_text = _compute_remainder_text(thinking_text, memory_text)
576
+ if not remainder_text:
577
+ return ""
578
+
579
+ shortened_remainder = shorten(remainder_text, width=240, placeholder="…")
580
+ if summary_headline:
581
+ duplicate = (
582
+ summary_headline.lower() in shortened_remainder.lower()
583
+ or shortened_remainder.lower() in summary_headline.lower()
584
+ )
585
+ if duplicate:
586
+ return ""
587
+ return shortened_remainder
588
+
589
+
590
+ def _compute_remainder_text(thinking_text: str, memory_text: str) -> str:
591
+ """Derive remainder text after the headline sentence.
592
+
593
+ Args:
594
+ thinking_text: Normalized thinking text.
595
+ memory_text: Normalized memory text.
596
+
597
+ Returns:
598
+ str: Multi-sentence remainder text for display.
599
+ """
600
+ focus_source = thinking_text or memory_text
601
+ if not focus_source:
602
+ return ""
603
+
604
+ focus_sentence_raw = _first_sentence(focus_source)
605
+ remainder_text = focus_source[len(focus_sentence_raw) :].strip()
606
+ if not remainder_text and memory_text and memory_text != focus_source:
607
+ remainder_text = memory_text
608
+ return _normalize_segment(remainder_text)
609
+
610
+
611
+ def generate_step_content(tool_calls: list[ToolCallInfo], is_done: bool) -> str:
612
+ """Return user-friendly status text derived from tool call outputs.
613
+
614
+ Args:
615
+ tool_calls: Tool call descriptors extracted from the step.
616
+ is_done: Flag indicating whether this is the final step in the run.
617
+
618
+ Returns:
619
+ User-friendly status text string.
620
+ """
621
+ if not tool_calls:
622
+ return TASK_COMPLETED_MESSAGE if is_done else PROCESSING_MESSAGE
623
+
624
+ if is_done:
625
+ return TASK_COMPLETED_MESSAGE
626
+
627
+ return _get_progress_message_from_tool_calls(tool_calls)
628
+
629
+
630
+ def _get_final_output_from_tool_calls(tool_calls: list[ToolCallInfo]) -> str:
631
+ """Extract final output from tool calls when task is done.
632
+
633
+ Args:
634
+ tool_calls: List of tool calls to extract output from.
635
+
636
+ Returns:
637
+ str: Final output string.
638
+ """
639
+ for tool_call in reversed(tool_calls):
640
+ if tool_call.name == "done" and tool_call.output:
641
+ return tool_call.output
642
+
643
+ for tool_call in reversed(tool_calls):
644
+ if tool_call.output:
645
+ return tool_call.output
646
+
647
+ return TASK_COMPLETED_MESSAGE
648
+
649
+
650
+ def _get_progress_message_from_tool_calls(tool_calls: list[ToolCallInfo]) -> str:
651
+ """Generate progress message from tool calls.
652
+
653
+ Args:
654
+ tool_calls: List of tool calls to generate progress message from.
655
+
656
+ Returns:
657
+ str: Progress message string.
658
+ """
659
+ tool_names = [tc.name for tc in tool_calls if tc.name]
660
+ if tool_names:
661
+ return f"Completed {', '.join(tool_names)}"
662
+ return PROCESSING_MESSAGE
663
+
664
+
665
+ def _get_done_tool_for_final_response(agent: Agent, tool_calls_dict: list[dict]) -> dict[str, Any]:
666
+ """Find or create the 'done' tool for final response.
667
+
668
+ Args:
669
+ agent: The browser-use agent.
670
+ tool_calls_dict: List of tool call dictionaries.
671
+
672
+ Returns:
673
+ dict[str, Any]: The 'done' tool dictionary for final response.
674
+ """
675
+ for tool_call in tool_calls_dict:
676
+ if tool_call.get("name") == "done":
677
+ return tool_call
678
+
679
+ final_response = _extract_final_response(agent.state.last_result)
680
+ extracted_content = ""
681
+
682
+ if final_response and final_response.get("extracted_content"):
683
+ extracted_content = final_response["extracted_content"]
684
+ elif tool_calls_dict:
685
+ extracted_content = tool_calls_dict[-1]["output"]
686
+
687
+ return {"name": "done", "args": {}, "output": extracted_content or TASK_COMPLETED_MESSAGE}
688
+
689
+
690
+ def _extract_final_response(last_result: Iterable[ActionResult] | None) -> dict[str, Any] | None:
691
+ """Extract final response data from ActionResult when is_done=True.
692
+
693
+ Args:
694
+ last_result: Iterable of ActionResult objects from the agent's last operation.
695
+
696
+ Returns:
697
+ Dictionary containing serialized final response data, or ``None`` if no result found.
698
+ """
699
+ results = list(last_result or [])
700
+ done_result = _first_matching_result(results, lambda item: getattr(item, "is_done", False))
701
+ if done_result:
702
+ return _serialize_action_result(done_result, bool(done_result.success))
703
+
704
+ extracted_result = _first_matching_result(reversed(results), lambda item: getattr(item, "extracted_content", ""))
705
+ if extracted_result:
706
+ return _serialize_action_result(extracted_result, True)
707
+ return None
708
+
709
+
710
+ def _first_matching_result(iterable: Iterable[Any], predicate: Callable[[Any], bool]) -> Any | None:
711
+ """Return the first item in ``iterable`` that satisfies ``predicate``.
712
+
713
+ Args:
714
+ iterable: Sequence or generator of results.
715
+ predicate: Callable returning True for the desired item.
716
+
717
+ Returns:
718
+ Any | None: Matching item or None when not found.
719
+ """
720
+ for item in iterable:
721
+ if predicate(item):
722
+ return item
723
+ return None
724
+
725
+
726
+ def _serialize_action_result(result: ActionResult, success: bool) -> dict[str, Any]:
727
+ """Normalize an ``ActionResult`` to a dict for streaming responses.
728
+
729
+ Args:
730
+ result: ActionResult emitted by browser-use.
731
+ success: Whether the action is considered successful.
732
+
733
+ Returns:
734
+ dict[str, Any]: Structured payload capturing success/error information.
735
+ """
736
+ return {
737
+ "success": success,
738
+ "extracted_content": getattr(result, "extracted_content", "") or "",
739
+ "error": getattr(result, "error", None),
740
+ }
741
+
742
+
743
+ def yield_iframe_activity(url: str, content: str) -> dict:
744
+ """Create and return an iframe activity streaming response.
745
+
746
+ Args:
747
+ url: The URL to display in the iframe.
748
+ content: The content message for the response.
749
+
750
+ Returns:
751
+ dict: Streaming response dictionary.
752
+ """
753
+ activity_info = _create_activity_info({"type": "iframe", "message": url})
754
+ return StreamingResponse(
755
+ event_type=A2AStreamEventType.STATUS_UPDATE,
756
+ content=content,
757
+ thinking_and_activity_info=activity_info,
758
+ is_final=False,
759
+ metadata={MetadataFieldKeys.KIND: Kind.AGENT_THINKING_STEP},
760
+ ).to_dict()
761
+
762
+
763
+ def yield_status_message(content: str) -> dict:
764
+ """Create a status update event notifying clients about recovery attempts.
765
+
766
+ Args:
767
+ content: The status message content to include in the event.
768
+
769
+ Returns:
770
+ dict: Streaming response dictionary for the status update event.
771
+ """
772
+ thinking_info = _create_thinking_info(content)
773
+ return StreamingResponse(
774
+ event_type=A2AStreamEventType.STATUS_UPDATE,
775
+ content=content,
776
+ thinking_and_activity_info=thinking_info,
777
+ is_final=False,
778
+ metadata={MetadataFieldKeys.KIND: Kind.AGENT_THINKING_STEP, MetadataFieldKeys.STATUS: Status.RUNNING},
779
+ ).to_dict()
780
+
781
+
782
+ def yield_thinking_marker(marker_type: Literal["start", "end"]) -> dict:
783
+ """Create and return a thinking marker streaming response.
784
+
785
+ Args:
786
+ marker_type: Either 'start' or 'end' to indicate thinking phase.
787
+
788
+ Returns:
789
+ dict: Streaming response dictionary.
790
+ """
791
+ data_type = ThinkingDataType.THINKING_START if marker_type == "start" else ThinkingDataType.THINKING_END
792
+ content = f"Thinking {marker_type}"
793
+ thinking_info = _create_thinking_info(message="", data_type=data_type.value)
794
+ return StreamingResponse(
795
+ event_type=A2AStreamEventType.STATUS_UPDATE,
796
+ content=content,
797
+ thinking_and_activity_info=thinking_info,
798
+ is_final=False,
799
+ metadata={MetadataFieldKeys.KIND: Kind.AGENT_THINKING_STEP},
800
+ ).to_dict()
801
+
802
+
803
+ __all__ = [
804
+ "PROCESSING_MESSAGE",
805
+ "TASK_COMPLETED_MESSAGE",
806
+ "create_error_response",
807
+ "create_step_response",
808
+ "generate_step_content",
809
+ "generate_thinking_message",
810
+ "yield_iframe_activity",
811
+ "yield_status_message",
812
+ "yield_thinking_marker",
813
+ ]