agno 2.2.13__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 (575) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +51 -0
  3. agno/agent/agent.py +10405 -0
  4. agno/api/__init__.py +0 -0
  5. agno/api/agent.py +28 -0
  6. agno/api/api.py +40 -0
  7. agno/api/evals.py +22 -0
  8. agno/api/os.py +17 -0
  9. agno/api/routes.py +13 -0
  10. agno/api/schemas/__init__.py +9 -0
  11. agno/api/schemas/agent.py +16 -0
  12. agno/api/schemas/evals.py +16 -0
  13. agno/api/schemas/os.py +14 -0
  14. agno/api/schemas/response.py +6 -0
  15. agno/api/schemas/team.py +16 -0
  16. agno/api/schemas/utils.py +21 -0
  17. agno/api/schemas/workflows.py +16 -0
  18. agno/api/settings.py +53 -0
  19. agno/api/team.py +30 -0
  20. agno/api/workflow.py +28 -0
  21. agno/cloud/aws/base.py +214 -0
  22. agno/cloud/aws/s3/__init__.py +2 -0
  23. agno/cloud/aws/s3/api_client.py +43 -0
  24. agno/cloud/aws/s3/bucket.py +195 -0
  25. agno/cloud/aws/s3/object.py +57 -0
  26. agno/culture/__init__.py +3 -0
  27. agno/culture/manager.py +956 -0
  28. agno/db/__init__.py +24 -0
  29. agno/db/async_postgres/__init__.py +3 -0
  30. agno/db/base.py +598 -0
  31. agno/db/dynamo/__init__.py +3 -0
  32. agno/db/dynamo/dynamo.py +2042 -0
  33. agno/db/dynamo/schemas.py +314 -0
  34. agno/db/dynamo/utils.py +743 -0
  35. agno/db/firestore/__init__.py +3 -0
  36. agno/db/firestore/firestore.py +1795 -0
  37. agno/db/firestore/schemas.py +140 -0
  38. agno/db/firestore/utils.py +376 -0
  39. agno/db/gcs_json/__init__.py +3 -0
  40. agno/db/gcs_json/gcs_json_db.py +1335 -0
  41. agno/db/gcs_json/utils.py +228 -0
  42. agno/db/in_memory/__init__.py +3 -0
  43. agno/db/in_memory/in_memory_db.py +1160 -0
  44. agno/db/in_memory/utils.py +230 -0
  45. agno/db/json/__init__.py +3 -0
  46. agno/db/json/json_db.py +1328 -0
  47. agno/db/json/utils.py +230 -0
  48. agno/db/migrations/__init__.py +0 -0
  49. agno/db/migrations/v1_to_v2.py +635 -0
  50. agno/db/mongo/__init__.py +17 -0
  51. agno/db/mongo/async_mongo.py +2026 -0
  52. agno/db/mongo/mongo.py +1982 -0
  53. agno/db/mongo/schemas.py +87 -0
  54. agno/db/mongo/utils.py +259 -0
  55. agno/db/mysql/__init__.py +3 -0
  56. agno/db/mysql/mysql.py +2308 -0
  57. agno/db/mysql/schemas.py +138 -0
  58. agno/db/mysql/utils.py +355 -0
  59. agno/db/postgres/__init__.py +4 -0
  60. agno/db/postgres/async_postgres.py +1927 -0
  61. agno/db/postgres/postgres.py +2260 -0
  62. agno/db/postgres/schemas.py +139 -0
  63. agno/db/postgres/utils.py +442 -0
  64. agno/db/redis/__init__.py +3 -0
  65. agno/db/redis/redis.py +1660 -0
  66. agno/db/redis/schemas.py +123 -0
  67. agno/db/redis/utils.py +346 -0
  68. agno/db/schemas/__init__.py +4 -0
  69. agno/db/schemas/culture.py +120 -0
  70. agno/db/schemas/evals.py +33 -0
  71. agno/db/schemas/knowledge.py +40 -0
  72. agno/db/schemas/memory.py +46 -0
  73. agno/db/schemas/metrics.py +0 -0
  74. agno/db/singlestore/__init__.py +3 -0
  75. agno/db/singlestore/schemas.py +130 -0
  76. agno/db/singlestore/singlestore.py +2272 -0
  77. agno/db/singlestore/utils.py +384 -0
  78. agno/db/sqlite/__init__.py +4 -0
  79. agno/db/sqlite/async_sqlite.py +2293 -0
  80. agno/db/sqlite/schemas.py +133 -0
  81. agno/db/sqlite/sqlite.py +2288 -0
  82. agno/db/sqlite/utils.py +431 -0
  83. agno/db/surrealdb/__init__.py +3 -0
  84. agno/db/surrealdb/metrics.py +292 -0
  85. agno/db/surrealdb/models.py +309 -0
  86. agno/db/surrealdb/queries.py +71 -0
  87. agno/db/surrealdb/surrealdb.py +1353 -0
  88. agno/db/surrealdb/utils.py +147 -0
  89. agno/db/utils.py +116 -0
  90. agno/debug.py +18 -0
  91. agno/eval/__init__.py +14 -0
  92. agno/eval/accuracy.py +834 -0
  93. agno/eval/performance.py +773 -0
  94. agno/eval/reliability.py +306 -0
  95. agno/eval/utils.py +119 -0
  96. agno/exceptions.py +161 -0
  97. agno/filters.py +354 -0
  98. agno/guardrails/__init__.py +6 -0
  99. agno/guardrails/base.py +19 -0
  100. agno/guardrails/openai.py +144 -0
  101. agno/guardrails/pii.py +94 -0
  102. agno/guardrails/prompt_injection.py +52 -0
  103. agno/integrations/__init__.py +0 -0
  104. agno/integrations/discord/__init__.py +3 -0
  105. agno/integrations/discord/client.py +203 -0
  106. agno/knowledge/__init__.py +5 -0
  107. agno/knowledge/chunking/__init__.py +0 -0
  108. agno/knowledge/chunking/agentic.py +79 -0
  109. agno/knowledge/chunking/document.py +91 -0
  110. agno/knowledge/chunking/fixed.py +57 -0
  111. agno/knowledge/chunking/markdown.py +151 -0
  112. agno/knowledge/chunking/recursive.py +63 -0
  113. agno/knowledge/chunking/row.py +39 -0
  114. agno/knowledge/chunking/semantic.py +86 -0
  115. agno/knowledge/chunking/strategy.py +165 -0
  116. agno/knowledge/content.py +74 -0
  117. agno/knowledge/document/__init__.py +5 -0
  118. agno/knowledge/document/base.py +58 -0
  119. agno/knowledge/embedder/__init__.py +5 -0
  120. agno/knowledge/embedder/aws_bedrock.py +343 -0
  121. agno/knowledge/embedder/azure_openai.py +210 -0
  122. agno/knowledge/embedder/base.py +23 -0
  123. agno/knowledge/embedder/cohere.py +323 -0
  124. agno/knowledge/embedder/fastembed.py +62 -0
  125. agno/knowledge/embedder/fireworks.py +13 -0
  126. agno/knowledge/embedder/google.py +258 -0
  127. agno/knowledge/embedder/huggingface.py +94 -0
  128. agno/knowledge/embedder/jina.py +182 -0
  129. agno/knowledge/embedder/langdb.py +22 -0
  130. agno/knowledge/embedder/mistral.py +206 -0
  131. agno/knowledge/embedder/nebius.py +13 -0
  132. agno/knowledge/embedder/ollama.py +154 -0
  133. agno/knowledge/embedder/openai.py +195 -0
  134. agno/knowledge/embedder/sentence_transformer.py +63 -0
  135. agno/knowledge/embedder/together.py +13 -0
  136. agno/knowledge/embedder/vllm.py +262 -0
  137. agno/knowledge/embedder/voyageai.py +165 -0
  138. agno/knowledge/knowledge.py +1988 -0
  139. agno/knowledge/reader/__init__.py +7 -0
  140. agno/knowledge/reader/arxiv_reader.py +81 -0
  141. agno/knowledge/reader/base.py +95 -0
  142. agno/knowledge/reader/csv_reader.py +166 -0
  143. agno/knowledge/reader/docx_reader.py +82 -0
  144. agno/knowledge/reader/field_labeled_csv_reader.py +292 -0
  145. agno/knowledge/reader/firecrawl_reader.py +201 -0
  146. agno/knowledge/reader/json_reader.py +87 -0
  147. agno/knowledge/reader/markdown_reader.py +137 -0
  148. agno/knowledge/reader/pdf_reader.py +431 -0
  149. agno/knowledge/reader/pptx_reader.py +101 -0
  150. agno/knowledge/reader/reader_factory.py +313 -0
  151. agno/knowledge/reader/s3_reader.py +89 -0
  152. agno/knowledge/reader/tavily_reader.py +194 -0
  153. agno/knowledge/reader/text_reader.py +115 -0
  154. agno/knowledge/reader/web_search_reader.py +372 -0
  155. agno/knowledge/reader/website_reader.py +455 -0
  156. agno/knowledge/reader/wikipedia_reader.py +59 -0
  157. agno/knowledge/reader/youtube_reader.py +78 -0
  158. agno/knowledge/remote_content/__init__.py +0 -0
  159. agno/knowledge/remote_content/remote_content.py +88 -0
  160. agno/knowledge/reranker/__init__.py +3 -0
  161. agno/knowledge/reranker/base.py +14 -0
  162. agno/knowledge/reranker/cohere.py +64 -0
  163. agno/knowledge/reranker/infinity.py +195 -0
  164. agno/knowledge/reranker/sentence_transformer.py +54 -0
  165. agno/knowledge/types.py +39 -0
  166. agno/knowledge/utils.py +189 -0
  167. agno/media.py +462 -0
  168. agno/memory/__init__.py +3 -0
  169. agno/memory/manager.py +1327 -0
  170. agno/models/__init__.py +0 -0
  171. agno/models/aimlapi/__init__.py +5 -0
  172. agno/models/aimlapi/aimlapi.py +45 -0
  173. agno/models/anthropic/__init__.py +5 -0
  174. agno/models/anthropic/claude.py +757 -0
  175. agno/models/aws/__init__.py +15 -0
  176. agno/models/aws/bedrock.py +701 -0
  177. agno/models/aws/claude.py +378 -0
  178. agno/models/azure/__init__.py +18 -0
  179. agno/models/azure/ai_foundry.py +485 -0
  180. agno/models/azure/openai_chat.py +131 -0
  181. agno/models/base.py +2175 -0
  182. agno/models/cerebras/__init__.py +12 -0
  183. agno/models/cerebras/cerebras.py +501 -0
  184. agno/models/cerebras/cerebras_openai.py +112 -0
  185. agno/models/cohere/__init__.py +5 -0
  186. agno/models/cohere/chat.py +389 -0
  187. agno/models/cometapi/__init__.py +5 -0
  188. agno/models/cometapi/cometapi.py +57 -0
  189. agno/models/dashscope/__init__.py +5 -0
  190. agno/models/dashscope/dashscope.py +91 -0
  191. agno/models/deepinfra/__init__.py +5 -0
  192. agno/models/deepinfra/deepinfra.py +28 -0
  193. agno/models/deepseek/__init__.py +5 -0
  194. agno/models/deepseek/deepseek.py +61 -0
  195. agno/models/defaults.py +1 -0
  196. agno/models/fireworks/__init__.py +5 -0
  197. agno/models/fireworks/fireworks.py +26 -0
  198. agno/models/google/__init__.py +5 -0
  199. agno/models/google/gemini.py +1085 -0
  200. agno/models/groq/__init__.py +5 -0
  201. agno/models/groq/groq.py +556 -0
  202. agno/models/huggingface/__init__.py +5 -0
  203. agno/models/huggingface/huggingface.py +491 -0
  204. agno/models/ibm/__init__.py +5 -0
  205. agno/models/ibm/watsonx.py +422 -0
  206. agno/models/internlm/__init__.py +3 -0
  207. agno/models/internlm/internlm.py +26 -0
  208. agno/models/langdb/__init__.py +1 -0
  209. agno/models/langdb/langdb.py +48 -0
  210. agno/models/litellm/__init__.py +14 -0
  211. agno/models/litellm/chat.py +468 -0
  212. agno/models/litellm/litellm_openai.py +25 -0
  213. agno/models/llama_cpp/__init__.py +5 -0
  214. agno/models/llama_cpp/llama_cpp.py +22 -0
  215. agno/models/lmstudio/__init__.py +5 -0
  216. agno/models/lmstudio/lmstudio.py +25 -0
  217. agno/models/message.py +434 -0
  218. agno/models/meta/__init__.py +12 -0
  219. agno/models/meta/llama.py +475 -0
  220. agno/models/meta/llama_openai.py +78 -0
  221. agno/models/metrics.py +120 -0
  222. agno/models/mistral/__init__.py +5 -0
  223. agno/models/mistral/mistral.py +432 -0
  224. agno/models/nebius/__init__.py +3 -0
  225. agno/models/nebius/nebius.py +54 -0
  226. agno/models/nexus/__init__.py +3 -0
  227. agno/models/nexus/nexus.py +22 -0
  228. agno/models/nvidia/__init__.py +5 -0
  229. agno/models/nvidia/nvidia.py +28 -0
  230. agno/models/ollama/__init__.py +5 -0
  231. agno/models/ollama/chat.py +441 -0
  232. agno/models/openai/__init__.py +9 -0
  233. agno/models/openai/chat.py +883 -0
  234. agno/models/openai/like.py +27 -0
  235. agno/models/openai/responses.py +1050 -0
  236. agno/models/openrouter/__init__.py +5 -0
  237. agno/models/openrouter/openrouter.py +66 -0
  238. agno/models/perplexity/__init__.py +5 -0
  239. agno/models/perplexity/perplexity.py +187 -0
  240. agno/models/portkey/__init__.py +3 -0
  241. agno/models/portkey/portkey.py +81 -0
  242. agno/models/requesty/__init__.py +5 -0
  243. agno/models/requesty/requesty.py +52 -0
  244. agno/models/response.py +199 -0
  245. agno/models/sambanova/__init__.py +5 -0
  246. agno/models/sambanova/sambanova.py +28 -0
  247. agno/models/siliconflow/__init__.py +5 -0
  248. agno/models/siliconflow/siliconflow.py +25 -0
  249. agno/models/together/__init__.py +5 -0
  250. agno/models/together/together.py +25 -0
  251. agno/models/utils.py +266 -0
  252. agno/models/vercel/__init__.py +3 -0
  253. agno/models/vercel/v0.py +26 -0
  254. agno/models/vertexai/__init__.py +0 -0
  255. agno/models/vertexai/claude.py +70 -0
  256. agno/models/vllm/__init__.py +3 -0
  257. agno/models/vllm/vllm.py +78 -0
  258. agno/models/xai/__init__.py +3 -0
  259. agno/models/xai/xai.py +113 -0
  260. agno/os/__init__.py +3 -0
  261. agno/os/app.py +876 -0
  262. agno/os/auth.py +57 -0
  263. agno/os/config.py +104 -0
  264. agno/os/interfaces/__init__.py +1 -0
  265. agno/os/interfaces/a2a/__init__.py +3 -0
  266. agno/os/interfaces/a2a/a2a.py +42 -0
  267. agno/os/interfaces/a2a/router.py +250 -0
  268. agno/os/interfaces/a2a/utils.py +924 -0
  269. agno/os/interfaces/agui/__init__.py +3 -0
  270. agno/os/interfaces/agui/agui.py +47 -0
  271. agno/os/interfaces/agui/router.py +144 -0
  272. agno/os/interfaces/agui/utils.py +534 -0
  273. agno/os/interfaces/base.py +25 -0
  274. agno/os/interfaces/slack/__init__.py +3 -0
  275. agno/os/interfaces/slack/router.py +148 -0
  276. agno/os/interfaces/slack/security.py +30 -0
  277. agno/os/interfaces/slack/slack.py +47 -0
  278. agno/os/interfaces/whatsapp/__init__.py +3 -0
  279. agno/os/interfaces/whatsapp/router.py +211 -0
  280. agno/os/interfaces/whatsapp/security.py +53 -0
  281. agno/os/interfaces/whatsapp/whatsapp.py +36 -0
  282. agno/os/mcp.py +292 -0
  283. agno/os/middleware/__init__.py +7 -0
  284. agno/os/middleware/jwt.py +233 -0
  285. agno/os/router.py +1763 -0
  286. agno/os/routers/__init__.py +3 -0
  287. agno/os/routers/evals/__init__.py +3 -0
  288. agno/os/routers/evals/evals.py +430 -0
  289. agno/os/routers/evals/schemas.py +142 -0
  290. agno/os/routers/evals/utils.py +162 -0
  291. agno/os/routers/health.py +31 -0
  292. agno/os/routers/home.py +52 -0
  293. agno/os/routers/knowledge/__init__.py +3 -0
  294. agno/os/routers/knowledge/knowledge.py +997 -0
  295. agno/os/routers/knowledge/schemas.py +178 -0
  296. agno/os/routers/memory/__init__.py +3 -0
  297. agno/os/routers/memory/memory.py +515 -0
  298. agno/os/routers/memory/schemas.py +62 -0
  299. agno/os/routers/metrics/__init__.py +3 -0
  300. agno/os/routers/metrics/metrics.py +190 -0
  301. agno/os/routers/metrics/schemas.py +47 -0
  302. agno/os/routers/session/__init__.py +3 -0
  303. agno/os/routers/session/session.py +997 -0
  304. agno/os/schema.py +1055 -0
  305. agno/os/settings.py +43 -0
  306. agno/os/utils.py +630 -0
  307. agno/py.typed +0 -0
  308. agno/reasoning/__init__.py +0 -0
  309. agno/reasoning/anthropic.py +80 -0
  310. agno/reasoning/azure_ai_foundry.py +67 -0
  311. agno/reasoning/deepseek.py +63 -0
  312. agno/reasoning/default.py +97 -0
  313. agno/reasoning/gemini.py +73 -0
  314. agno/reasoning/groq.py +71 -0
  315. agno/reasoning/helpers.py +63 -0
  316. agno/reasoning/ollama.py +67 -0
  317. agno/reasoning/openai.py +86 -0
  318. agno/reasoning/step.py +31 -0
  319. agno/reasoning/vertexai.py +76 -0
  320. agno/run/__init__.py +6 -0
  321. agno/run/agent.py +787 -0
  322. agno/run/base.py +229 -0
  323. agno/run/cancel.py +81 -0
  324. agno/run/messages.py +32 -0
  325. agno/run/team.py +753 -0
  326. agno/run/workflow.py +708 -0
  327. agno/session/__init__.py +10 -0
  328. agno/session/agent.py +295 -0
  329. agno/session/summary.py +265 -0
  330. agno/session/team.py +392 -0
  331. agno/session/workflow.py +205 -0
  332. agno/team/__init__.py +37 -0
  333. agno/team/team.py +8793 -0
  334. agno/tools/__init__.py +10 -0
  335. agno/tools/agentql.py +120 -0
  336. agno/tools/airflow.py +69 -0
  337. agno/tools/api.py +122 -0
  338. agno/tools/apify.py +314 -0
  339. agno/tools/arxiv.py +127 -0
  340. agno/tools/aws_lambda.py +53 -0
  341. agno/tools/aws_ses.py +66 -0
  342. agno/tools/baidusearch.py +89 -0
  343. agno/tools/bitbucket.py +292 -0
  344. agno/tools/brandfetch.py +213 -0
  345. agno/tools/bravesearch.py +106 -0
  346. agno/tools/brightdata.py +367 -0
  347. agno/tools/browserbase.py +209 -0
  348. agno/tools/calcom.py +255 -0
  349. agno/tools/calculator.py +151 -0
  350. agno/tools/cartesia.py +187 -0
  351. agno/tools/clickup.py +244 -0
  352. agno/tools/confluence.py +240 -0
  353. agno/tools/crawl4ai.py +158 -0
  354. agno/tools/csv_toolkit.py +185 -0
  355. agno/tools/dalle.py +110 -0
  356. agno/tools/daytona.py +475 -0
  357. agno/tools/decorator.py +262 -0
  358. agno/tools/desi_vocal.py +108 -0
  359. agno/tools/discord.py +161 -0
  360. agno/tools/docker.py +716 -0
  361. agno/tools/duckdb.py +379 -0
  362. agno/tools/duckduckgo.py +91 -0
  363. agno/tools/e2b.py +703 -0
  364. agno/tools/eleven_labs.py +196 -0
  365. agno/tools/email.py +67 -0
  366. agno/tools/evm.py +129 -0
  367. agno/tools/exa.py +396 -0
  368. agno/tools/fal.py +127 -0
  369. agno/tools/file.py +240 -0
  370. agno/tools/file_generation.py +350 -0
  371. agno/tools/financial_datasets.py +288 -0
  372. agno/tools/firecrawl.py +143 -0
  373. agno/tools/function.py +1187 -0
  374. agno/tools/giphy.py +93 -0
  375. agno/tools/github.py +1760 -0
  376. agno/tools/gmail.py +922 -0
  377. agno/tools/google_bigquery.py +117 -0
  378. agno/tools/google_drive.py +270 -0
  379. agno/tools/google_maps.py +253 -0
  380. agno/tools/googlecalendar.py +674 -0
  381. agno/tools/googlesearch.py +98 -0
  382. agno/tools/googlesheets.py +377 -0
  383. agno/tools/hackernews.py +77 -0
  384. agno/tools/jina.py +101 -0
  385. agno/tools/jira.py +170 -0
  386. agno/tools/knowledge.py +218 -0
  387. agno/tools/linear.py +426 -0
  388. agno/tools/linkup.py +58 -0
  389. agno/tools/local_file_system.py +90 -0
  390. agno/tools/lumalab.py +183 -0
  391. agno/tools/mcp/__init__.py +10 -0
  392. agno/tools/mcp/mcp.py +331 -0
  393. agno/tools/mcp/multi_mcp.py +347 -0
  394. agno/tools/mcp/params.py +24 -0
  395. agno/tools/mcp_toolbox.py +284 -0
  396. agno/tools/mem0.py +193 -0
  397. agno/tools/memori.py +339 -0
  398. agno/tools/memory.py +419 -0
  399. agno/tools/mlx_transcribe.py +139 -0
  400. agno/tools/models/__init__.py +0 -0
  401. agno/tools/models/azure_openai.py +190 -0
  402. agno/tools/models/gemini.py +203 -0
  403. agno/tools/models/groq.py +158 -0
  404. agno/tools/models/morph.py +186 -0
  405. agno/tools/models/nebius.py +124 -0
  406. agno/tools/models_labs.py +195 -0
  407. agno/tools/moviepy_video.py +349 -0
  408. agno/tools/neo4j.py +134 -0
  409. agno/tools/newspaper.py +46 -0
  410. agno/tools/newspaper4k.py +93 -0
  411. agno/tools/notion.py +204 -0
  412. agno/tools/openai.py +202 -0
  413. agno/tools/openbb.py +160 -0
  414. agno/tools/opencv.py +321 -0
  415. agno/tools/openweather.py +233 -0
  416. agno/tools/oxylabs.py +385 -0
  417. agno/tools/pandas.py +102 -0
  418. agno/tools/parallel.py +314 -0
  419. agno/tools/postgres.py +257 -0
  420. agno/tools/pubmed.py +188 -0
  421. agno/tools/python.py +205 -0
  422. agno/tools/reasoning.py +283 -0
  423. agno/tools/reddit.py +467 -0
  424. agno/tools/replicate.py +117 -0
  425. agno/tools/resend.py +62 -0
  426. agno/tools/scrapegraph.py +222 -0
  427. agno/tools/searxng.py +152 -0
  428. agno/tools/serpapi.py +116 -0
  429. agno/tools/serper.py +255 -0
  430. agno/tools/shell.py +53 -0
  431. agno/tools/slack.py +136 -0
  432. agno/tools/sleep.py +20 -0
  433. agno/tools/spider.py +116 -0
  434. agno/tools/sql.py +154 -0
  435. agno/tools/streamlit/__init__.py +0 -0
  436. agno/tools/streamlit/components.py +113 -0
  437. agno/tools/tavily.py +254 -0
  438. agno/tools/telegram.py +48 -0
  439. agno/tools/todoist.py +218 -0
  440. agno/tools/tool_registry.py +1 -0
  441. agno/tools/toolkit.py +146 -0
  442. agno/tools/trafilatura.py +388 -0
  443. agno/tools/trello.py +274 -0
  444. agno/tools/twilio.py +186 -0
  445. agno/tools/user_control_flow.py +78 -0
  446. agno/tools/valyu.py +228 -0
  447. agno/tools/visualization.py +467 -0
  448. agno/tools/webbrowser.py +28 -0
  449. agno/tools/webex.py +76 -0
  450. agno/tools/website.py +54 -0
  451. agno/tools/webtools.py +45 -0
  452. agno/tools/whatsapp.py +286 -0
  453. agno/tools/wikipedia.py +63 -0
  454. agno/tools/workflow.py +278 -0
  455. agno/tools/x.py +335 -0
  456. agno/tools/yfinance.py +257 -0
  457. agno/tools/youtube.py +184 -0
  458. agno/tools/zendesk.py +82 -0
  459. agno/tools/zep.py +454 -0
  460. agno/tools/zoom.py +382 -0
  461. agno/utils/__init__.py +0 -0
  462. agno/utils/agent.py +820 -0
  463. agno/utils/audio.py +49 -0
  464. agno/utils/certs.py +27 -0
  465. agno/utils/code_execution.py +11 -0
  466. agno/utils/common.py +132 -0
  467. agno/utils/dttm.py +13 -0
  468. agno/utils/enum.py +22 -0
  469. agno/utils/env.py +11 -0
  470. agno/utils/events.py +696 -0
  471. agno/utils/format_str.py +16 -0
  472. agno/utils/functions.py +166 -0
  473. agno/utils/gemini.py +426 -0
  474. agno/utils/hooks.py +57 -0
  475. agno/utils/http.py +74 -0
  476. agno/utils/json_schema.py +234 -0
  477. agno/utils/knowledge.py +36 -0
  478. agno/utils/location.py +19 -0
  479. agno/utils/log.py +255 -0
  480. agno/utils/mcp.py +214 -0
  481. agno/utils/media.py +352 -0
  482. agno/utils/merge_dict.py +41 -0
  483. agno/utils/message.py +118 -0
  484. agno/utils/models/__init__.py +0 -0
  485. agno/utils/models/ai_foundry.py +43 -0
  486. agno/utils/models/claude.py +358 -0
  487. agno/utils/models/cohere.py +87 -0
  488. agno/utils/models/llama.py +78 -0
  489. agno/utils/models/mistral.py +98 -0
  490. agno/utils/models/openai_responses.py +140 -0
  491. agno/utils/models/schema_utils.py +153 -0
  492. agno/utils/models/watsonx.py +41 -0
  493. agno/utils/openai.py +257 -0
  494. agno/utils/pickle.py +32 -0
  495. agno/utils/pprint.py +178 -0
  496. agno/utils/print_response/__init__.py +0 -0
  497. agno/utils/print_response/agent.py +842 -0
  498. agno/utils/print_response/team.py +1724 -0
  499. agno/utils/print_response/workflow.py +1668 -0
  500. agno/utils/prompts.py +111 -0
  501. agno/utils/reasoning.py +108 -0
  502. agno/utils/response.py +163 -0
  503. agno/utils/response_iterator.py +17 -0
  504. agno/utils/safe_formatter.py +24 -0
  505. agno/utils/serialize.py +32 -0
  506. agno/utils/shell.py +22 -0
  507. agno/utils/streamlit.py +487 -0
  508. agno/utils/string.py +231 -0
  509. agno/utils/team.py +139 -0
  510. agno/utils/timer.py +41 -0
  511. agno/utils/tools.py +102 -0
  512. agno/utils/web.py +23 -0
  513. agno/utils/whatsapp.py +305 -0
  514. agno/utils/yaml_io.py +25 -0
  515. agno/vectordb/__init__.py +3 -0
  516. agno/vectordb/base.py +127 -0
  517. agno/vectordb/cassandra/__init__.py +5 -0
  518. agno/vectordb/cassandra/cassandra.py +501 -0
  519. agno/vectordb/cassandra/extra_param_mixin.py +11 -0
  520. agno/vectordb/cassandra/index.py +13 -0
  521. agno/vectordb/chroma/__init__.py +5 -0
  522. agno/vectordb/chroma/chromadb.py +929 -0
  523. agno/vectordb/clickhouse/__init__.py +9 -0
  524. agno/vectordb/clickhouse/clickhousedb.py +835 -0
  525. agno/vectordb/clickhouse/index.py +9 -0
  526. agno/vectordb/couchbase/__init__.py +3 -0
  527. agno/vectordb/couchbase/couchbase.py +1442 -0
  528. agno/vectordb/distance.py +7 -0
  529. agno/vectordb/lancedb/__init__.py +6 -0
  530. agno/vectordb/lancedb/lance_db.py +995 -0
  531. agno/vectordb/langchaindb/__init__.py +5 -0
  532. agno/vectordb/langchaindb/langchaindb.py +163 -0
  533. agno/vectordb/lightrag/__init__.py +5 -0
  534. agno/vectordb/lightrag/lightrag.py +388 -0
  535. agno/vectordb/llamaindex/__init__.py +3 -0
  536. agno/vectordb/llamaindex/llamaindexdb.py +166 -0
  537. agno/vectordb/milvus/__init__.py +4 -0
  538. agno/vectordb/milvus/milvus.py +1182 -0
  539. agno/vectordb/mongodb/__init__.py +9 -0
  540. agno/vectordb/mongodb/mongodb.py +1417 -0
  541. agno/vectordb/pgvector/__init__.py +12 -0
  542. agno/vectordb/pgvector/index.py +23 -0
  543. agno/vectordb/pgvector/pgvector.py +1462 -0
  544. agno/vectordb/pineconedb/__init__.py +5 -0
  545. agno/vectordb/pineconedb/pineconedb.py +747 -0
  546. agno/vectordb/qdrant/__init__.py +5 -0
  547. agno/vectordb/qdrant/qdrant.py +1134 -0
  548. agno/vectordb/redis/__init__.py +9 -0
  549. agno/vectordb/redis/redisdb.py +694 -0
  550. agno/vectordb/search.py +7 -0
  551. agno/vectordb/singlestore/__init__.py +10 -0
  552. agno/vectordb/singlestore/index.py +41 -0
  553. agno/vectordb/singlestore/singlestore.py +763 -0
  554. agno/vectordb/surrealdb/__init__.py +3 -0
  555. agno/vectordb/surrealdb/surrealdb.py +699 -0
  556. agno/vectordb/upstashdb/__init__.py +5 -0
  557. agno/vectordb/upstashdb/upstashdb.py +718 -0
  558. agno/vectordb/weaviate/__init__.py +8 -0
  559. agno/vectordb/weaviate/index.py +15 -0
  560. agno/vectordb/weaviate/weaviate.py +1005 -0
  561. agno/workflow/__init__.py +23 -0
  562. agno/workflow/agent.py +299 -0
  563. agno/workflow/condition.py +738 -0
  564. agno/workflow/loop.py +735 -0
  565. agno/workflow/parallel.py +824 -0
  566. agno/workflow/router.py +702 -0
  567. agno/workflow/step.py +1432 -0
  568. agno/workflow/steps.py +592 -0
  569. agno/workflow/types.py +520 -0
  570. agno/workflow/workflow.py +4321 -0
  571. agno-2.2.13.dist-info/METADATA +614 -0
  572. agno-2.2.13.dist-info/RECORD +575 -0
  573. agno-2.2.13.dist-info/WHEEL +5 -0
  574. agno-2.2.13.dist-info/licenses/LICENSE +201 -0
  575. agno-2.2.13.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1724 @@
1
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Set, Union, get_args
2
+
3
+ from pydantic import BaseModel
4
+
5
+ from agno.filters import FilterExpr
6
+ from agno.media import Audio, File, Image, Video
7
+ from agno.models.message import Message
8
+ from agno.models.response import ToolExecution
9
+ from agno.reasoning.step import ReasoningStep
10
+ from agno.run.agent import RunOutput
11
+ from agno.run.team import TeamRunEvent, TeamRunOutput, TeamRunOutputEvent
12
+ from agno.utils.log import log_warning
13
+ from agno.utils.message import get_text_from_message
14
+ from agno.utils.response import build_reasoning_step_panel, create_panel, escape_markdown_tags, format_tool_calls
15
+ from agno.utils.timer import Timer
16
+
17
+ if TYPE_CHECKING:
18
+ from agno.team.team import Team
19
+
20
+
21
+ def print_response(
22
+ team: "Team",
23
+ input: Union[List, Dict, str, Message, BaseModel, List[Message]],
24
+ console: Optional[Any] = None,
25
+ show_message: bool = True,
26
+ show_reasoning: bool = True,
27
+ show_full_reasoning: bool = False,
28
+ tags_to_include_in_markdown: Optional[Set[str]] = None,
29
+ session_id: Optional[str] = None,
30
+ session_state: Optional[Dict[str, Any]] = None,
31
+ user_id: Optional[str] = None,
32
+ audio: Optional[Sequence[Audio]] = None,
33
+ images: Optional[Sequence[Image]] = None,
34
+ videos: Optional[Sequence[Video]] = None,
35
+ files: Optional[Sequence[File]] = None,
36
+ markdown: bool = False,
37
+ knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
38
+ add_history_to_context: Optional[bool] = None,
39
+ dependencies: Optional[Dict[str, Any]] = None,
40
+ add_dependencies_to_context: Optional[bool] = None,
41
+ add_session_state_to_context: Optional[bool] = None,
42
+ metadata: Optional[Dict[str, Any]] = None,
43
+ debug_mode: Optional[bool] = None,
44
+ **kwargs: Any,
45
+ ) -> None:
46
+ import textwrap
47
+
48
+ from rich.console import Group
49
+ from rich.json import JSON
50
+ from rich.live import Live
51
+ from rich.markdown import Markdown
52
+ from rich.status import Status
53
+ from rich.text import Text
54
+
55
+ from agno.utils.response import format_tool_calls
56
+
57
+ if not tags_to_include_in_markdown:
58
+ tags_to_include_in_markdown = {"think", "thinking"}
59
+
60
+ with Live(console=console) as live_console:
61
+ status = Status("Thinking...", spinner="aesthetic", speed=0.4, refresh_per_second=10)
62
+ live_console.update(status)
63
+
64
+ response_timer = Timer()
65
+ response_timer.start()
66
+ # Panels to be rendered
67
+ panels = [status]
68
+ # First render the message panel if the message is not None
69
+ if input and show_message:
70
+ # Convert message to a panel
71
+ message_content = get_text_from_message(input)
72
+ message_panel = create_panel(
73
+ content=Text(message_content, style="green"),
74
+ title="Message",
75
+ border_style="cyan",
76
+ )
77
+ panels.append(message_panel)
78
+ live_console.update(Group(*panels))
79
+
80
+ # Run the agent
81
+ run_response: TeamRunOutput = team.run( # type: ignore
82
+ input=input,
83
+ images=images,
84
+ audio=audio,
85
+ videos=videos,
86
+ files=files,
87
+ stream=False,
88
+ session_id=session_id,
89
+ session_state=session_state,
90
+ user_id=user_id,
91
+ knowledge_filters=knowledge_filters,
92
+ add_history_to_context=add_history_to_context,
93
+ dependencies=dependencies,
94
+ add_dependencies_to_context=add_dependencies_to_context,
95
+ add_session_state_to_context=add_session_state_to_context,
96
+ metadata=metadata,
97
+ debug_mode=debug_mode,
98
+ **kwargs,
99
+ )
100
+ response_timer.stop()
101
+
102
+ if run_response.input is not None and run_response.input.input_content != input:
103
+ # Input was modified during the run
104
+ panels = [status]
105
+ if show_message:
106
+ # Convert message to a panel
107
+ message_content = get_text_from_message(run_response.input.input_content)
108
+ message_panel = create_panel(
109
+ content=Text(message_content, style="green"),
110
+ title="Message",
111
+ border_style="cyan",
112
+ )
113
+ panels.append(message_panel) # type: ignore
114
+ live_console.update(Group(*panels))
115
+
116
+ team_markdown = False
117
+ member_markdown = {}
118
+ if markdown:
119
+ for member in team.members:
120
+ if member.id is not None:
121
+ member_markdown[member.id] = True
122
+ team_markdown = True
123
+
124
+ if team.output_schema is not None:
125
+ team_markdown = False
126
+
127
+ for member in team.members:
128
+ if member.output_schema is not None and member.id is not None:
129
+ member_markdown[member.id] = False # type: ignore
130
+
131
+ # Handle reasoning
132
+ reasoning_steps = []
133
+ if isinstance(run_response, TeamRunOutput) and run_response.reasoning_steps is not None:
134
+ reasoning_steps = run_response.reasoning_steps
135
+
136
+ if len(reasoning_steps) > 0 and show_reasoning:
137
+ # Create panels for reasoning steps
138
+ for i, step in enumerate(reasoning_steps, 1):
139
+ reasoning_panel = build_reasoning_step_panel(i, step, show_full_reasoning)
140
+ panels.append(reasoning_panel)
141
+ live_console.update(Group(*panels))
142
+
143
+ if isinstance(run_response, TeamRunOutput) and run_response.reasoning_content is not None and show_reasoning:
144
+ # Create panel for thinking
145
+ thinking_panel = create_panel(
146
+ content=Text(run_response.reasoning_content),
147
+ title=f"Thinking ({response_timer.elapsed:.1f}s)",
148
+ border_style="green",
149
+ )
150
+ panels.append(thinking_panel)
151
+ live_console.update(Group(*panels))
152
+
153
+ if isinstance(run_response, TeamRunOutput):
154
+ # Handle member responses
155
+ if team.show_members_responses:
156
+ for member_response in run_response.member_responses:
157
+ # Handle member reasoning
158
+ reasoning_steps = []
159
+ if isinstance(member_response, RunOutput) and member_response.reasoning_steps is not None:
160
+ reasoning_steps.extend(member_response.reasoning_steps)
161
+
162
+ if len(reasoning_steps) > 0 and show_reasoning:
163
+ # Create panels for reasoning steps
164
+ for i, step in enumerate(reasoning_steps, 1):
165
+ member_reasoning_panel = build_reasoning_step_panel(
166
+ i, step, show_full_reasoning, color="magenta"
167
+ )
168
+ panels.append(member_reasoning_panel)
169
+
170
+ # Add tool calls panel for member if available
171
+ if hasattr(member_response, "tools") and member_response.tools:
172
+ member_name = None
173
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
174
+ member_name = team._get_member_name(member_response.agent_id)
175
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
176
+ member_name = team._get_member_name(member_response.team_id)
177
+
178
+ if member_name:
179
+ formatted_calls = format_tool_calls(member_response.tools)
180
+ if formatted_calls:
181
+ console_width = console.width if console else 80
182
+ panel_width = console_width + 30
183
+
184
+ lines = []
185
+ for call in formatted_calls:
186
+ wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
187
+ lines.append(wrapped_call)
188
+
189
+ tool_calls_text = "\n\n".join(lines)
190
+
191
+ member_tool_calls_panel = create_panel(
192
+ content=tool_calls_text,
193
+ title=f"{member_name} Tool Calls",
194
+ border_style="yellow",
195
+ )
196
+ panels.append(member_tool_calls_panel)
197
+ live_console.update(Group(*panels))
198
+
199
+ show_markdown = False
200
+ if member_markdown:
201
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
202
+ show_markdown = member_markdown.get(member_response.agent_id, False)
203
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
204
+ show_markdown = member_markdown.get(member_response.team_id, False)
205
+
206
+ member_response_content: Union[str, JSON, Markdown] = _parse_response_content( # type: ignore
207
+ member_response,
208
+ tags_to_include_in_markdown,
209
+ show_markdown=show_markdown,
210
+ )
211
+
212
+ # Create panel for member response
213
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
214
+ member_response_panel = create_panel(
215
+ content=member_response_content,
216
+ title=f"{team._get_member_name(member_response.agent_id)} Response",
217
+ border_style="magenta",
218
+ )
219
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
220
+ member_response_panel = create_panel(
221
+ content=member_response_content,
222
+ title=f"{team._get_member_name(member_response.team_id)} Response",
223
+ border_style="magenta",
224
+ )
225
+ panels.append(member_response_panel)
226
+
227
+ if member_response.citations is not None and member_response.citations.urls is not None:
228
+ md_content = "\n".join(
229
+ f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
230
+ for i, citation in enumerate(member_response.citations.urls)
231
+ if citation.url # Only include citations with valid URLs
232
+ )
233
+ if md_content: # Only create panel if there are citations
234
+ citations_panel = create_panel(
235
+ content=Markdown(md_content),
236
+ title="Citations",
237
+ border_style="magenta",
238
+ )
239
+ panels.append(citations_panel)
240
+
241
+ live_console.update(Group(*panels))
242
+
243
+ # Add team level tool calls panel if available
244
+ if run_response.tools:
245
+ formatted_calls = format_tool_calls(run_response.tools)
246
+ if formatted_calls:
247
+ console_width = console.width if console else 80
248
+ # Allow for panel borders and padding
249
+ panel_width = console_width + 30
250
+
251
+ lines = []
252
+ for call in formatted_calls:
253
+ wrapped_call = textwrap.fill(
254
+ f"• {call}", width=panel_width, subsequent_indent=" "
255
+ ) # Indent continuation lines
256
+ lines.append(wrapped_call)
257
+
258
+ # Join with blank lines between items
259
+ tool_calls_text = "\n\n".join(lines)
260
+
261
+ team_tool_calls_panel = create_panel(
262
+ content=tool_calls_text,
263
+ title="Team Tool Calls",
264
+ border_style="yellow",
265
+ )
266
+ panels.append(team_tool_calls_panel)
267
+ live_console.update(Group(*panels))
268
+
269
+ response_content_batch: Union[str, JSON, Markdown] = _parse_response_content( # type: ignore
270
+ run_response, tags_to_include_in_markdown, show_markdown=team_markdown
271
+ )
272
+
273
+ # Create panel for response
274
+ response_panel = create_panel(
275
+ content=response_content_batch,
276
+ title=f"Response ({response_timer.elapsed:.1f}s)",
277
+ border_style="blue",
278
+ )
279
+ panels.append(response_panel)
280
+
281
+ # Add citations
282
+ if run_response.citations is not None and run_response.citations.urls is not None:
283
+ md_content = "\n".join(
284
+ f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
285
+ for i, citation in enumerate(run_response.citations.urls)
286
+ if citation.url # Only include citations with valid URLs
287
+ )
288
+ if md_content: # Only create panel if there are citations
289
+ citations_panel = create_panel(
290
+ content=Markdown(md_content),
291
+ title="Citations",
292
+ border_style="green",
293
+ )
294
+ panels.append(citations_panel)
295
+
296
+ if team.memory_manager is not None:
297
+ if team.memory_manager.memories_updated:
298
+ memory_panel = create_panel(
299
+ content=Text("Memories updated"),
300
+ title="Memories",
301
+ border_style="green",
302
+ )
303
+ panels.append(memory_panel)
304
+
305
+ if team.session_summary_manager is not None and team.session_summary_manager.summaries_updated:
306
+ summary_panel = create_panel(
307
+ content=Text("Session summary updated"),
308
+ title="Session Summary",
309
+ border_style="green",
310
+ )
311
+ panels.append(summary_panel)
312
+ team.session_summary_manager.summaries_updated = False
313
+
314
+ # Final update to remove the "Thinking..." status
315
+ panels = [p for p in panels if not isinstance(p, Status)]
316
+ live_console.update(Group(*panels))
317
+
318
+
319
+ def print_response_stream(
320
+ team: "Team",
321
+ input: Union[List, Dict, str, Message, BaseModel, List[Message]],
322
+ console: Optional[Any] = None,
323
+ show_message: bool = True,
324
+ show_reasoning: bool = True,
325
+ show_full_reasoning: bool = False,
326
+ tags_to_include_in_markdown: Optional[Set[str]] = None,
327
+ session_id: Optional[str] = None,
328
+ session_state: Optional[Dict[str, Any]] = None,
329
+ user_id: Optional[str] = None,
330
+ audio: Optional[Sequence[Audio]] = None,
331
+ images: Optional[Sequence[Image]] = None,
332
+ videos: Optional[Sequence[Video]] = None,
333
+ files: Optional[Sequence[File]] = None,
334
+ markdown: bool = False,
335
+ stream_events: bool = False,
336
+ stream_intermediate_steps: bool = False, # type: ignore
337
+ knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
338
+ add_history_to_context: Optional[bool] = None,
339
+ dependencies: Optional[Dict[str, Any]] = None,
340
+ add_dependencies_to_context: Optional[bool] = None,
341
+ add_session_state_to_context: Optional[bool] = None,
342
+ metadata: Optional[Dict[str, Any]] = None,
343
+ debug_mode: Optional[bool] = None,
344
+ **kwargs: Any,
345
+ ) -> None:
346
+ import textwrap
347
+
348
+ from rich.console import Group
349
+ from rich.json import JSON
350
+ from rich.live import Live
351
+ from rich.markdown import Markdown
352
+ from rich.status import Status
353
+ from rich.text import Text
354
+
355
+ from agno.utils.response import format_tool_calls
356
+
357
+ if not tags_to_include_in_markdown:
358
+ tags_to_include_in_markdown = {"think", "thinking"}
359
+
360
+ stream_events = True # With streaming print response, we need to stream intermediate steps
361
+
362
+ _response_content: str = ""
363
+ _response_reasoning_content: str = ""
364
+ reasoning_steps: List[ReasoningStep] = []
365
+
366
+ # Track tool calls by member and team
367
+ member_tool_calls = {} # type: ignore
368
+ team_tool_calls = [] # type: ignore
369
+
370
+ # Track processed tool calls to avoid duplicates
371
+ processed_tool_calls = set()
372
+
373
+ with Live(console=console) as live_console:
374
+ status = Status("Thinking...", spinner="aesthetic", speed=0.4, refresh_per_second=10)
375
+ live_console.update(status)
376
+ response_timer = Timer()
377
+ response_timer.start()
378
+ # Flag which indicates if the panels should be rendered
379
+ render = False
380
+ # Panels to be rendered
381
+ panels = [status]
382
+ # First render the message panel if the message is not None
383
+ if input and show_message:
384
+ render = True
385
+ # Convert message to a panel
386
+ message_content = get_text_from_message(input)
387
+ message_panel = create_panel(
388
+ content=Text(message_content, style="green"),
389
+ title="Message",
390
+ border_style="cyan",
391
+ )
392
+ panels.append(message_panel)
393
+ if render:
394
+ live_console.update(Group(*panels))
395
+
396
+ # Get response from the team
397
+ stream_resp = team.run( # type: ignore
398
+ input=input,
399
+ audio=audio,
400
+ images=images,
401
+ videos=videos,
402
+ files=files,
403
+ stream=True,
404
+ stream_events=stream_events,
405
+ session_id=session_id,
406
+ session_state=session_state,
407
+ user_id=user_id,
408
+ knowledge_filters=knowledge_filters,
409
+ add_history_to_context=add_history_to_context,
410
+ dependencies=dependencies,
411
+ add_dependencies_to_context=add_dependencies_to_context,
412
+ add_session_state_to_context=add_session_state_to_context,
413
+ metadata=metadata,
414
+ debug_mode=debug_mode,
415
+ yield_run_response=True,
416
+ **kwargs,
417
+ )
418
+
419
+ input_content = get_text_from_message(input)
420
+
421
+ team_markdown = None
422
+ member_markdown = {}
423
+
424
+ # Dict to track member response panels by member_id
425
+ member_response_panels = {}
426
+
427
+ final_run_response = None
428
+ for resp in stream_resp:
429
+ if team_markdown is None:
430
+ if markdown:
431
+ team_markdown = True
432
+ else:
433
+ team_markdown = False
434
+
435
+ if team.output_schema is not None:
436
+ team_markdown = False
437
+
438
+ if isinstance(resp, TeamRunOutput):
439
+ final_run_response = resp
440
+ continue
441
+
442
+ if isinstance(resp, tuple(get_args(TeamRunOutputEvent))):
443
+ if resp.event == TeamRunEvent.run_content:
444
+ if isinstance(resp.content, str):
445
+ _response_content += resp.content
446
+ elif team.output_schema is not None and isinstance(resp.content, BaseModel):
447
+ try:
448
+ _response_content = JSON(resp.content.model_dump_json(exclude_none=True), indent=2) # type: ignore
449
+ except Exception as e:
450
+ log_warning(f"Failed to convert response to JSON: {e}")
451
+ if hasattr(resp, "reasoning_content") and resp.reasoning_content is not None: # type: ignore
452
+ _response_reasoning_content += resp.reasoning_content # type: ignore
453
+ if hasattr(resp, "reasoning_steps") and resp.reasoning_steps is not None: # type: ignore
454
+ reasoning_steps = resp.reasoning_steps # type: ignore
455
+
456
+ if resp.event == TeamRunEvent.pre_hook_completed: # type: ignore
457
+ if resp.run_input is not None: # type: ignore
458
+ input_content = get_text_from_message(resp.run_input.input_content) # type: ignore
459
+
460
+ # Collect team tool calls, avoiding duplicates
461
+ if resp.event == TeamRunEvent.tool_call_completed and resp.tool: # type: ignore
462
+ tool = resp.tool # type: ignore
463
+ # Generate a unique ID for this tool call
464
+ if tool.tool_call_id:
465
+ tool_id = tool.tool_call_id
466
+ else:
467
+ tool_id = str(hash(str(tool)))
468
+ if tool_id not in processed_tool_calls:
469
+ processed_tool_calls.add(tool_id)
470
+ team_tool_calls.append(tool)
471
+
472
+ # Collect member tool calls, avoiding duplicates
473
+ if hasattr(resp, "member_responses") and resp.member_responses:
474
+ for member_response in resp.member_responses:
475
+ member_id = None
476
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
477
+ member_id = member_response.agent_id
478
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
479
+ member_id = member_response.team_id
480
+
481
+ if member_id and hasattr(member_response, "tools") and member_response.tools:
482
+ if member_id not in member_tool_calls:
483
+ member_tool_calls[member_id] = []
484
+
485
+ for tool in member_response.tools:
486
+ # Generate a unique ID for this tool call
487
+ if tool.tool_call_id:
488
+ tool_id = tool.tool_call_id
489
+ else:
490
+ tool_id = str(hash(str(tool)))
491
+ if tool_id not in processed_tool_calls:
492
+ processed_tool_calls.add(tool_id)
493
+ member_tool_calls[member_id].append(tool)
494
+
495
+ response_content_stream: Union[str, Markdown] = _response_content
496
+ # Escape special tags before markdown conversion
497
+ if team_markdown:
498
+ escaped_content = escape_markdown_tags(_response_content, tags_to_include_in_markdown)
499
+ response_content_stream = Markdown(escaped_content)
500
+
501
+ # Create new panels for each chunk
502
+ panels = []
503
+
504
+ if input_content and show_message:
505
+ render = True
506
+ # Convert message to a panel
507
+ message_panel = create_panel(
508
+ content=Text(input_content, style="green"),
509
+ title="Message",
510
+ border_style="cyan",
511
+ )
512
+ panels.append(message_panel)
513
+
514
+ if len(reasoning_steps) > 0 and show_reasoning:
515
+ render = True
516
+ # Create panels for reasoning steps
517
+ for i, step in enumerate(reasoning_steps, 1):
518
+ reasoning_panel = build_reasoning_step_panel(i, step, show_full_reasoning)
519
+ panels.append(reasoning_panel)
520
+
521
+ if len(_response_reasoning_content) > 0 and show_reasoning:
522
+ render = True
523
+ # Create panel for thinking
524
+ thinking_panel = create_panel(
525
+ content=Text(_response_reasoning_content),
526
+ title=f"Thinking ({response_timer.elapsed:.1f}s)",
527
+ border_style="green",
528
+ )
529
+ panels.append(thinking_panel)
530
+ elif _response_content == "":
531
+ # Keep showing status if no content yet
532
+ panels.append(status)
533
+
534
+ # Process member responses and their tool calls
535
+ for member_response in resp.member_responses if hasattr(resp, "member_responses") else []:
536
+ member_id = None
537
+ member_name = "Team Member"
538
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
539
+ member_id = member_response.agent_id
540
+ member_name = team._get_member_name(member_id)
541
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
542
+ member_id = member_response.team_id
543
+
544
+ member_name = team._get_member_name(member_id)
545
+
546
+ # If we have tool calls for this member, display them
547
+ if member_id in member_tool_calls and member_tool_calls[member_id]:
548
+ formatted_calls = format_tool_calls(member_tool_calls[member_id])
549
+ if formatted_calls:
550
+ console_width = console.width if console else 80
551
+ panel_width = console_width + 30
552
+
553
+ lines = []
554
+ for call in formatted_calls:
555
+ wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
556
+ lines.append(wrapped_call)
557
+
558
+ tool_calls_text = "\n\n".join(lines)
559
+
560
+ member_tool_calls_panel = create_panel(
561
+ content=tool_calls_text,
562
+ title=f"{member_name} Tool Calls",
563
+ border_style="yellow",
564
+ )
565
+ panels.append(member_tool_calls_panel)
566
+
567
+ # Process member response content
568
+ if team.show_members_responses and member_id is not None:
569
+ show_markdown = False
570
+ if markdown:
571
+ show_markdown = True
572
+
573
+ member_response_content = _parse_response_content(
574
+ member_response,
575
+ tags_to_include_in_markdown,
576
+ show_markdown=show_markdown,
577
+ )
578
+
579
+ member_response_panel = create_panel(
580
+ content=member_response_content,
581
+ title=f"{member_name} Response",
582
+ border_style="magenta",
583
+ )
584
+
585
+ panels.append(member_response_panel)
586
+
587
+ # Store for reference
588
+ if member_id is not None:
589
+ member_response_panels[member_id] = member_response_panel
590
+
591
+ # Add team tool calls panel if available (before the team response)
592
+ if team_tool_calls:
593
+ formatted_calls = format_tool_calls(team_tool_calls)
594
+ if formatted_calls:
595
+ console_width = console.width if console else 80
596
+ panel_width = console_width + 30
597
+
598
+ lines = []
599
+ # Create a set to track already added calls by their string representation
600
+ added_calls = set()
601
+ for call in formatted_calls:
602
+ if call not in added_calls:
603
+ added_calls.add(call)
604
+ # Wrap the call text to fit within the panel
605
+ wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
606
+ lines.append(wrapped_call)
607
+
608
+ # Join with blank lines between items
609
+ tool_calls_text = "\n\n".join(lines)
610
+
611
+ team_tool_calls_panel = create_panel(
612
+ content=tool_calls_text,
613
+ title="Team Tool Calls",
614
+ border_style="yellow",
615
+ )
616
+ panels.append(team_tool_calls_panel)
617
+
618
+ # Add the team response panel at the end
619
+ if response_content_stream:
620
+ render = True
621
+ # Create panel for response
622
+ response_panel = create_panel(
623
+ content=response_content_stream,
624
+ title=f"Response ({response_timer.elapsed:.1f}s)",
625
+ border_style="blue",
626
+ )
627
+ panels.append(response_panel)
628
+
629
+ if render or len(panels) > 0:
630
+ live_console.update(Group(*panels))
631
+
632
+ response_timer.stop()
633
+ run_response = final_run_response
634
+
635
+ # Add citations
636
+ if hasattr(resp, "citations") and resp.citations is not None and resp.citations.urls is not None:
637
+ md_content = "\n".join(
638
+ f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
639
+ for i, citation in enumerate(resp.citations.urls)
640
+ if citation.url # Only include citations with valid URLs
641
+ )
642
+ if md_content: # Only create panel if there are citations
643
+ citations_panel = create_panel(
644
+ content=Markdown(md_content),
645
+ title="Citations",
646
+ border_style="green",
647
+ )
648
+ panels.append(citations_panel)
649
+ live_console.update(Group(*panels))
650
+
651
+ if team.memory_manager is not None:
652
+ if team.memory_manager.memories_updated:
653
+ memory_panel = create_panel(
654
+ content=Text("Memories updated"),
655
+ title="Memories",
656
+ border_style="green",
657
+ )
658
+ panels.append(memory_panel)
659
+ live_console.update(Group(*panels))
660
+
661
+ if team.session_summary_manager is not None and team.session_summary_manager.summaries_updated:
662
+ summary_panel = create_panel(
663
+ content=Text("Session summary updated"),
664
+ title="Session Summary",
665
+ border_style="green",
666
+ )
667
+ panels.append(summary_panel)
668
+ live_console.update(Group(*panels))
669
+ team.session_summary_manager.summaries_updated = False
670
+
671
+ # Final update to remove the "Thinking..." status
672
+ panels = [p for p in panels if not isinstance(p, Status)]
673
+
674
+ if markdown:
675
+ for member in team.members:
676
+ if member.id is not None:
677
+ member_markdown[member.id] = True
678
+
679
+ for member in team.members:
680
+ if member.output_schema is not None and member.id is not None:
681
+ member_markdown[member.id] = False # type: ignore
682
+
683
+ # Final panels assembly - we'll recreate the panels from scratch to ensure correct order
684
+ final_panels = []
685
+
686
+ # Start with the message
687
+ if input_content and show_message:
688
+ message_panel = create_panel(
689
+ content=Text(input_content, style="green"),
690
+ title="Message",
691
+ border_style="cyan",
692
+ )
693
+ final_panels.append(message_panel)
694
+
695
+ # Add reasoning steps
696
+ if reasoning_steps and show_reasoning:
697
+ for i, step in enumerate(reasoning_steps, 1):
698
+ reasoning_panel = build_reasoning_step_panel(i, step, show_full_reasoning)
699
+ final_panels.append(reasoning_panel)
700
+
701
+ # Add thinking panel if available
702
+ if _response_reasoning_content and show_reasoning:
703
+ thinking_panel = create_panel(
704
+ content=Text(_response_reasoning_content),
705
+ title=f"Thinking ({response_timer.elapsed:.1f}s)",
706
+ border_style="green",
707
+ )
708
+ final_panels.append(thinking_panel)
709
+
710
+ # Add member tool calls and responses in correct order
711
+ if run_response is not None and hasattr(run_response, "member_responses"):
712
+ for i, member_response in enumerate(run_response.member_responses): # type: ignore
713
+ member_id = None
714
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
715
+ member_id = member_response.agent_id
716
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
717
+ member_id = member_response.team_id
718
+
719
+ if member_id:
720
+ # First add tool calls if any
721
+ if member_id in member_tool_calls and member_tool_calls[member_id]:
722
+ formatted_calls = format_tool_calls(member_tool_calls[member_id])
723
+ if formatted_calls:
724
+ console_width = console.width if console else 80
725
+ panel_width = console_width + 30
726
+
727
+ lines = []
728
+ for call in formatted_calls:
729
+ wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
730
+ lines.append(wrapped_call)
731
+
732
+ tool_calls_text = "\n\n".join(lines)
733
+
734
+ member_name = team._get_member_name(member_id)
735
+ member_tool_calls_panel = create_panel(
736
+ content=tool_calls_text,
737
+ title=f"{member_name} Tool Calls",
738
+ border_style="yellow",
739
+ )
740
+ final_panels.append(member_tool_calls_panel)
741
+
742
+ # Add reasoning steps if any
743
+ reasoning_steps = []
744
+ if member_response.reasoning_steps is not None:
745
+ reasoning_steps = member_response.reasoning_steps
746
+ if reasoning_steps and show_reasoning:
747
+ for j, step in enumerate(reasoning_steps, 1):
748
+ member_reasoning_panel = build_reasoning_step_panel(
749
+ j, step, show_full_reasoning, color="magenta"
750
+ )
751
+ final_panels.append(member_reasoning_panel)
752
+
753
+ # Then add response
754
+ show_markdown = False
755
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
756
+ show_markdown = member_markdown.get(member_response.agent_id, False)
757
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
758
+ show_markdown = member_markdown.get(member_response.team_id, False)
759
+
760
+ member_response_content = _parse_response_content( # type: ignore
761
+ member_response,
762
+ tags_to_include_in_markdown,
763
+ show_markdown=show_markdown,
764
+ )
765
+
766
+ member_name = "Team Member"
767
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
768
+ member_name = team._get_member_name(member_response.agent_id)
769
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
770
+ member_name = team._get_member_name(member_response.team_id)
771
+
772
+ member_response_panel = create_panel(
773
+ content=member_response_content,
774
+ title=f"{member_name} Response",
775
+ border_style="magenta",
776
+ )
777
+ final_panels.append(member_response_panel)
778
+
779
+ # Add citations if any
780
+ if member_response.citations is not None and member_response.citations.urls is not None:
781
+ md_content = "\n".join(
782
+ f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
783
+ for i, citation in enumerate(member_response.citations.urls)
784
+ if citation.url # Only include citations with valid URLs
785
+ )
786
+ if md_content: # Only create panel if there are citations
787
+ citations_panel = create_panel(
788
+ content=Markdown(md_content),
789
+ title="Citations",
790
+ border_style="magenta",
791
+ )
792
+ final_panels.append(citations_panel)
793
+
794
+ # Add team tool calls before team response
795
+ if team_tool_calls:
796
+ formatted_calls = format_tool_calls(team_tool_calls)
797
+ if formatted_calls:
798
+ console_width = console.width if console else 80
799
+ panel_width = console_width + 30
800
+
801
+ lines = []
802
+ # Create a set to track already added calls by their string representation
803
+ added_calls = set()
804
+ for call in formatted_calls:
805
+ if call not in added_calls:
806
+ added_calls.add(call)
807
+ # Wrap the call text to fit within the panel
808
+ wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
809
+ lines.append(wrapped_call)
810
+
811
+ tool_calls_text = "\n\n".join(lines)
812
+
813
+ team_tool_calls_panel = create_panel(
814
+ content=tool_calls_text,
815
+ title="Team Tool Calls",
816
+ border_style="yellow",
817
+ )
818
+ final_panels.append(team_tool_calls_panel)
819
+
820
+ # Add team response
821
+ if _response_content:
822
+ response_content_stream = _response_content
823
+ if team_markdown:
824
+ escaped_content = escape_markdown_tags(_response_content, tags_to_include_in_markdown)
825
+ response_content_stream = Markdown(escaped_content)
826
+
827
+ response_panel = create_panel(
828
+ content=response_content_stream,
829
+ title=f"Response ({response_timer.elapsed:.1f}s)",
830
+ border_style="blue",
831
+ )
832
+ final_panels.append(response_panel)
833
+
834
+ # Add team citations
835
+ if hasattr(resp, "citations") and resp.citations is not None and resp.citations.urls is not None:
836
+ md_content = "\n".join(
837
+ f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
838
+ for i, citation in enumerate(resp.citations.urls)
839
+ if citation.url # Only include citations with valid URLs
840
+ )
841
+ if md_content: # Only create panel if there are citations
842
+ citations_panel = create_panel(
843
+ content=Markdown(md_content),
844
+ title="Citations",
845
+ border_style="green",
846
+ )
847
+ final_panels.append(citations_panel)
848
+
849
+ # Final update with correctly ordered panels
850
+ live_console.update(Group(*final_panels))
851
+
852
+
853
+ async def aprint_response(
854
+ team: "Team",
855
+ input: Union[List, Dict, str, Message, BaseModel, List[Message]],
856
+ console: Optional[Any] = None,
857
+ show_message: bool = True,
858
+ show_reasoning: bool = True,
859
+ show_full_reasoning: bool = False,
860
+ tags_to_include_in_markdown: Optional[Set[str]] = None,
861
+ session_id: Optional[str] = None,
862
+ session_state: Optional[Dict[str, Any]] = None,
863
+ user_id: Optional[str] = None,
864
+ audio: Optional[Sequence[Audio]] = None,
865
+ images: Optional[Sequence[Image]] = None,
866
+ videos: Optional[Sequence[Video]] = None,
867
+ files: Optional[Sequence[File]] = None,
868
+ markdown: bool = False,
869
+ knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
870
+ add_history_to_context: Optional[bool] = None,
871
+ dependencies: Optional[Dict[str, Any]] = None,
872
+ add_dependencies_to_context: Optional[bool] = None,
873
+ add_session_state_to_context: Optional[bool] = None,
874
+ metadata: Optional[Dict[str, Any]] = None,
875
+ debug_mode: Optional[bool] = None,
876
+ **kwargs: Any,
877
+ ) -> None:
878
+ import textwrap
879
+
880
+ from rich.console import Group
881
+ from rich.json import JSON
882
+ from rich.live import Live
883
+ from rich.markdown import Markdown
884
+ from rich.status import Status
885
+ from rich.text import Text
886
+
887
+ from agno.utils.response import format_tool_calls
888
+
889
+ if not tags_to_include_in_markdown:
890
+ tags_to_include_in_markdown = {"think", "thinking"}
891
+
892
+ with Live(console=console) as live_console:
893
+ status = Status("Thinking...", spinner="aesthetic", speed=0.4, refresh_per_second=10)
894
+ live_console.update(status)
895
+
896
+ response_timer = Timer()
897
+ response_timer.start()
898
+ # Panels to be rendered
899
+ panels = [status]
900
+ # First render the message panel if the message is not None
901
+ if input and show_message:
902
+ # Convert message to a panel
903
+ message_content = get_text_from_message(input)
904
+ message_panel = create_panel(
905
+ content=Text(message_content, style="green"),
906
+ title="Message",
907
+ border_style="cyan",
908
+ )
909
+ panels.append(message_panel)
910
+ live_console.update(Group(*panels))
911
+
912
+ # Run the agent
913
+ run_response: TeamRunOutput = await team.arun( # type: ignore
914
+ input=input,
915
+ images=images,
916
+ audio=audio,
917
+ videos=videos,
918
+ files=files,
919
+ stream=False,
920
+ session_id=session_id,
921
+ session_state=session_state,
922
+ user_id=user_id,
923
+ knowledge_filters=knowledge_filters,
924
+ add_history_to_context=add_history_to_context,
925
+ dependencies=dependencies,
926
+ add_dependencies_to_context=add_dependencies_to_context,
927
+ add_session_state_to_context=add_session_state_to_context,
928
+ metadata=metadata,
929
+ debug_mode=debug_mode,
930
+ **kwargs,
931
+ )
932
+ response_timer.stop()
933
+
934
+ if run_response.input is not None and run_response.input.input_content != input:
935
+ # Input was modified during the run
936
+ panels = [status]
937
+ if show_message:
938
+ # Convert message to a panel
939
+ message_content = get_text_from_message(run_response.input.input_content)
940
+ message_panel = create_panel(
941
+ content=Text(message_content, style="green"),
942
+ title="Message",
943
+ border_style="cyan",
944
+ )
945
+ panels.append(message_panel) # type: ignore
946
+ live_console.update(Group(*panels))
947
+
948
+ team_markdown = False
949
+ member_markdown = {}
950
+ if markdown:
951
+ for member in team.members:
952
+ if member.id is not None:
953
+ member_markdown[member.id] = True
954
+ team_markdown = True
955
+
956
+ if team.output_schema is not None:
957
+ team_markdown = False
958
+
959
+ for member in team.members:
960
+ if member.output_schema is not None and member.id is not None:
961
+ member_markdown[member.id] = False # type: ignore
962
+
963
+ # Handle reasoning
964
+ reasoning_steps = []
965
+ if isinstance(run_response, TeamRunOutput) and run_response.reasoning_steps is not None:
966
+ reasoning_steps = run_response.reasoning_steps
967
+
968
+ if len(reasoning_steps) > 0 and show_reasoning:
969
+ # Create panels for reasoning steps
970
+ for i, step in enumerate(reasoning_steps, 1):
971
+ reasoning_panel = build_reasoning_step_panel(i, step, show_full_reasoning)
972
+ panels.append(reasoning_panel)
973
+ live_console.update(Group(*panels))
974
+
975
+ if isinstance(run_response, TeamRunOutput) and run_response.reasoning_content is not None and show_reasoning:
976
+ # Create panel for thinking
977
+ thinking_panel = create_panel(
978
+ content=Text(run_response.reasoning_content),
979
+ title=f"Thinking ({response_timer.elapsed:.1f}s)",
980
+ border_style="green",
981
+ )
982
+ panels.append(thinking_panel)
983
+ live_console.update(Group(*panels))
984
+
985
+ if isinstance(run_response, TeamRunOutput):
986
+ # Handle member responses
987
+ if team.show_members_responses:
988
+ for member_response in run_response.member_responses:
989
+ # Handle member reasoning
990
+ reasoning_steps = []
991
+ if isinstance(member_response, RunOutput) and member_response.reasoning_steps is not None:
992
+ reasoning_steps.extend(member_response.reasoning_steps)
993
+
994
+ if len(reasoning_steps) > 0 and show_reasoning:
995
+ # Create panels for reasoning steps
996
+ for i, step in enumerate(reasoning_steps, 1):
997
+ member_reasoning_panel = build_reasoning_step_panel(
998
+ i, step, show_full_reasoning, color="magenta"
999
+ )
1000
+ panels.append(member_reasoning_panel)
1001
+
1002
+ # Add tool calls panel for member if available
1003
+ if hasattr(member_response, "tools") and member_response.tools:
1004
+ member_name = None
1005
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
1006
+ member_name = team._get_member_name(member_response.agent_id)
1007
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
1008
+ member_name = team._get_member_name(member_response.team_id)
1009
+
1010
+ if member_name:
1011
+ # Format tool calls
1012
+ formatted_calls = format_tool_calls(member_response.tools)
1013
+ if formatted_calls:
1014
+ console_width = console.width if console else 80
1015
+ panel_width = console_width + 30
1016
+
1017
+ lines = []
1018
+ for call in formatted_calls:
1019
+ wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
1020
+ lines.append(wrapped_call)
1021
+
1022
+ tool_calls_text = "\n\n".join(lines)
1023
+
1024
+ member_tool_calls_panel = create_panel(
1025
+ content=tool_calls_text,
1026
+ title=f"{member_name} Tool Calls",
1027
+ border_style="yellow",
1028
+ )
1029
+ panels.append(member_tool_calls_panel)
1030
+ live_console.update(Group(*panels))
1031
+
1032
+ show_markdown = False
1033
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
1034
+ show_markdown = member_markdown.get(member_response.agent_id, False)
1035
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
1036
+ show_markdown = member_markdown.get(member_response.team_id, False)
1037
+
1038
+ member_response_content: Union[str, JSON, Markdown] = _parse_response_content( # type: ignore
1039
+ member_response,
1040
+ tags_to_include_in_markdown,
1041
+ show_markdown=show_markdown,
1042
+ )
1043
+
1044
+ # Create panel for member response
1045
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
1046
+ member_response_panel = create_panel(
1047
+ content=member_response_content,
1048
+ title=f"{team._get_member_name(member_response.agent_id)} Response",
1049
+ border_style="magenta",
1050
+ )
1051
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
1052
+ member_response_panel = create_panel(
1053
+ content=member_response_content,
1054
+ title=f"{team._get_member_name(member_response.team_id)} Response",
1055
+ border_style="magenta",
1056
+ )
1057
+ panels.append(member_response_panel)
1058
+
1059
+ if member_response.citations is not None and member_response.citations.urls is not None:
1060
+ md_content = "\n".join(
1061
+ f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
1062
+ for i, citation in enumerate(member_response.citations.urls)
1063
+ if citation.url # Only include citations with valid URLs
1064
+ )
1065
+ if md_content:
1066
+ citations_panel = create_panel(
1067
+ content=Markdown(md_content),
1068
+ title="Citations",
1069
+ border_style="magenta",
1070
+ )
1071
+ panels.append(citations_panel)
1072
+
1073
+ live_console.update(Group(*panels))
1074
+
1075
+ # Add team level tool calls panel if available
1076
+ if run_response.tools:
1077
+ formatted_calls = format_tool_calls(run_response.tools)
1078
+ if formatted_calls:
1079
+ console_width = console.width if console else 80
1080
+ # Allow for panel borders and padding
1081
+ panel_width = console_width + 30
1082
+
1083
+ lines = []
1084
+ for call in formatted_calls:
1085
+ # Wrap the call text to fit within the panel
1086
+ wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
1087
+ lines.append(wrapped_call)
1088
+
1089
+ tool_calls_text = "\n\n".join(lines)
1090
+
1091
+ team_tool_calls_panel = create_panel(
1092
+ content=tool_calls_text,
1093
+ title="Team Tool Calls",
1094
+ border_style="yellow",
1095
+ )
1096
+ panels.append(team_tool_calls_panel)
1097
+ live_console.update(Group(*panels))
1098
+
1099
+ response_content_batch: Union[str, JSON, Markdown] = _parse_response_content( # type: ignore
1100
+ run_response, tags_to_include_in_markdown, show_markdown=team_markdown
1101
+ )
1102
+
1103
+ # Create panel for response
1104
+ response_panel = create_panel(
1105
+ content=response_content_batch,
1106
+ title=f"Response ({response_timer.elapsed:.1f}s)",
1107
+ border_style="blue",
1108
+ )
1109
+ panels.append(response_panel)
1110
+
1111
+ # Add citations
1112
+ if run_response.citations is not None and run_response.citations.urls is not None:
1113
+ md_content = "\n".join(
1114
+ f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
1115
+ for i, citation in enumerate(run_response.citations.urls)
1116
+ if citation.url # Only include citations with valid URLs
1117
+ )
1118
+ if md_content: # Only create panel if there are citations
1119
+ citations_panel = create_panel(
1120
+ content=Markdown(md_content),
1121
+ title="Citations",
1122
+ border_style="green",
1123
+ )
1124
+ panels.append(citations_panel)
1125
+
1126
+ if team.memory_manager is not None:
1127
+ if team.memory_manager.memories_updated:
1128
+ memory_panel = create_panel(
1129
+ content=Text("Memories updated"),
1130
+ title="Memories",
1131
+ border_style="green",
1132
+ )
1133
+ panels.append(memory_panel)
1134
+
1135
+ if team.session_summary_manager is not None and team.session_summary_manager.summaries_updated:
1136
+ summary_panel = create_panel(
1137
+ content=Text("Session summary updated"),
1138
+ title="Session Summary",
1139
+ border_style="green",
1140
+ )
1141
+ panels.append(summary_panel)
1142
+ team.session_summary_manager.summaries_updated = False
1143
+
1144
+ # Final update to remove the "Thinking..." status
1145
+ panels = [p for p in panels if not isinstance(p, Status)]
1146
+ live_console.update(Group(*panels))
1147
+
1148
+
1149
+ async def aprint_response_stream(
1150
+ team: "Team",
1151
+ input: Union[List, Dict, str, Message, BaseModel, List[Message]],
1152
+ console: Optional[Any] = None,
1153
+ show_message: bool = True,
1154
+ show_reasoning: bool = True,
1155
+ show_full_reasoning: bool = False,
1156
+ tags_to_include_in_markdown: Optional[Set[str]] = None,
1157
+ session_id: Optional[str] = None,
1158
+ session_state: Optional[Dict[str, Any]] = None,
1159
+ user_id: Optional[str] = None,
1160
+ audio: Optional[Sequence[Audio]] = None,
1161
+ images: Optional[Sequence[Image]] = None,
1162
+ videos: Optional[Sequence[Video]] = None,
1163
+ files: Optional[Sequence[File]] = None,
1164
+ markdown: bool = False,
1165
+ stream_events: bool = False,
1166
+ stream_intermediate_steps: bool = False, # type: ignore
1167
+ knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
1168
+ add_history_to_context: Optional[bool] = None,
1169
+ dependencies: Optional[Dict[str, Any]] = None,
1170
+ add_dependencies_to_context: Optional[bool] = None,
1171
+ add_session_state_to_context: Optional[bool] = None,
1172
+ metadata: Optional[Dict[str, Any]] = None,
1173
+ debug_mode: Optional[bool] = None,
1174
+ **kwargs: Any,
1175
+ ) -> None:
1176
+ import textwrap
1177
+
1178
+ from rich.console import Group
1179
+ from rich.json import JSON
1180
+ from rich.live import Live
1181
+ from rich.markdown import Markdown
1182
+ from rich.status import Status
1183
+ from rich.text import Text
1184
+
1185
+ if not tags_to_include_in_markdown:
1186
+ tags_to_include_in_markdown = {"think", "thinking"}
1187
+
1188
+ stream_events = True # With streaming print response, we need to stream intermediate steps
1189
+
1190
+ _response_content: str = ""
1191
+ _response_reasoning_content: str = ""
1192
+ reasoning_steps: List[ReasoningStep] = []
1193
+
1194
+ # Track tool calls by member and team
1195
+ member_tool_calls = {} # type: ignore
1196
+ team_tool_calls: List[ToolExecution] = []
1197
+
1198
+ # Track processed tool calls to avoid duplicates
1199
+ processed_tool_calls = set()
1200
+
1201
+ # Initialize final_panels here
1202
+ final_panels = [] # type: ignore
1203
+
1204
+ with Live(console=console) as live_console:
1205
+ status = Status("Thinking...", spinner="aesthetic", speed=0.4, refresh_per_second=10)
1206
+ live_console.update(status)
1207
+ response_timer = Timer()
1208
+ response_timer.start()
1209
+ # Flag which indicates if the panels should be rendered
1210
+ render = False
1211
+ # Panels to be rendered
1212
+ panels = [status]
1213
+ # First render the message panel if the message is not None
1214
+ if input and show_message:
1215
+ render = True
1216
+ # Convert message to a panel
1217
+ message_content = get_text_from_message(input)
1218
+ message_panel = create_panel(
1219
+ content=Text(message_content, style="green"),
1220
+ title="Message",
1221
+ border_style="cyan",
1222
+ )
1223
+ panels.append(message_panel)
1224
+ if render:
1225
+ live_console.update(Group(*panels))
1226
+
1227
+ # Get response from the team
1228
+ team_markdown = None
1229
+ member_markdown = {}
1230
+
1231
+ # Dict to track member response panels by member_id
1232
+ member_response_panels = {}
1233
+
1234
+ input_content = get_text_from_message(input)
1235
+
1236
+ final_run_response = None
1237
+ async for resp in team.arun( # type: ignore
1238
+ input=input,
1239
+ audio=audio,
1240
+ images=images,
1241
+ videos=videos,
1242
+ files=files,
1243
+ stream=True,
1244
+ stream_events=stream_events,
1245
+ session_id=session_id,
1246
+ session_state=session_state,
1247
+ user_id=user_id,
1248
+ knowledge_filters=knowledge_filters,
1249
+ add_history_to_context=add_history_to_context,
1250
+ add_dependencies_to_context=add_dependencies_to_context,
1251
+ add_session_state_to_context=add_session_state_to_context,
1252
+ dependencies=dependencies,
1253
+ metadata=metadata,
1254
+ debug_mode=debug_mode,
1255
+ yield_run_response=True,
1256
+ **kwargs,
1257
+ ):
1258
+ if team_markdown is None:
1259
+ if markdown:
1260
+ team_markdown = True
1261
+ else:
1262
+ team_markdown = False
1263
+
1264
+ if team.output_schema is not None:
1265
+ team_markdown = False
1266
+
1267
+ if isinstance(resp, TeamRunOutput):
1268
+ final_run_response = resp
1269
+ continue
1270
+
1271
+ if isinstance(resp, tuple(get_args(TeamRunOutputEvent))):
1272
+ if resp.event == TeamRunEvent.run_content:
1273
+ if isinstance(resp.content, str):
1274
+ _response_content += resp.content
1275
+ elif team.output_schema is not None and isinstance(resp.content, BaseModel):
1276
+ try:
1277
+ _response_content = JSON(resp.content.model_dump_json(exclude_none=True), indent=2) # type: ignore
1278
+ except Exception as e:
1279
+ log_warning(f"Failed to convert response to JSON: {e}")
1280
+ if hasattr(resp, "reasoning_content") and resp.reasoning_content is not None: # type: ignore
1281
+ _response_reasoning_content += resp.reasoning_content # type: ignore
1282
+ if hasattr(resp, "reasoning_steps") and resp.reasoning_steps is not None: # type: ignore
1283
+ reasoning_steps = resp.reasoning_steps # type: ignore
1284
+
1285
+ if resp.event == TeamRunEvent.pre_hook_completed: # type: ignore
1286
+ if resp.run_input is not None: # type: ignore
1287
+ input_content = get_text_from_message(resp.run_input.input_content) # type: ignore
1288
+
1289
+ # Collect team tool calls, avoiding duplicates
1290
+ if resp.event == TeamRunEvent.tool_call_completed and resp.tool: # type: ignore
1291
+ tool = resp.tool # type: ignore
1292
+ # Generate a unique ID for this tool call
1293
+ if tool.tool_call_id is not None:
1294
+ tool_id = tool.tool_call_id
1295
+ else:
1296
+ tool_id = str(hash(str(tool)))
1297
+ if tool_id not in processed_tool_calls:
1298
+ processed_tool_calls.add(tool_id)
1299
+ team_tool_calls.append(tool)
1300
+
1301
+ # Collect member tool calls, avoiding duplicates
1302
+ if hasattr(resp, "member_responses") and resp.member_responses:
1303
+ for member_response in resp.member_responses:
1304
+ member_id = None
1305
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
1306
+ member_id = member_response.agent_id
1307
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
1308
+ member_id = member_response.team_id
1309
+
1310
+ if member_id and hasattr(member_response, "tools") and member_response.tools:
1311
+ if member_id not in member_tool_calls:
1312
+ member_tool_calls[member_id] = []
1313
+
1314
+ for tool in member_response.tools:
1315
+ if tool.tool_call_id is not None:
1316
+ tool_id = tool.tool_call_id
1317
+ else:
1318
+ tool_id = str(hash(str(tool)))
1319
+ if tool_id not in processed_tool_calls:
1320
+ processed_tool_calls.add(tool_id)
1321
+ member_tool_calls[member_id].append(tool)
1322
+
1323
+ response_content_stream: Union[str, Markdown] = _response_content
1324
+ # Escape special tags before markdown conversion
1325
+ if team_markdown:
1326
+ escaped_content = escape_markdown_tags(_response_content, tags_to_include_in_markdown)
1327
+ response_content_stream = Markdown(escaped_content)
1328
+
1329
+ # Create new panels for each chunk
1330
+ panels = []
1331
+
1332
+ if input_content and show_message:
1333
+ render = True
1334
+ # Convert message to a panel
1335
+ message_panel = create_panel(
1336
+ content=Text(input_content, style="green"),
1337
+ title="Message",
1338
+ border_style="cyan",
1339
+ )
1340
+ panels.append(message_panel)
1341
+
1342
+ if len(reasoning_steps) > 0 and show_reasoning:
1343
+ render = True
1344
+ # Create panels for reasoning steps
1345
+ for i, step in enumerate(reasoning_steps, 1):
1346
+ reasoning_panel = build_reasoning_step_panel(i, step, show_full_reasoning)
1347
+ panels.append(reasoning_panel)
1348
+
1349
+ if len(_response_reasoning_content) > 0 and show_reasoning:
1350
+ render = True
1351
+ # Create panel for thinking
1352
+ thinking_panel = create_panel(
1353
+ content=Text(_response_reasoning_content),
1354
+ title=f"Thinking ({response_timer.elapsed:.1f}s)",
1355
+ border_style="green",
1356
+ )
1357
+ panels.append(thinking_panel)
1358
+ elif _response_content == "":
1359
+ # Keep showing status if no content yet
1360
+ panels.append(status)
1361
+
1362
+ # Process member responses and their tool calls
1363
+ for member_response in resp.member_responses if hasattr(resp, "member_responses") else []:
1364
+ member_id = None
1365
+ member_name = "Team Member"
1366
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
1367
+ member_id = member_response.agent_id
1368
+ member_name = team._get_member_name(member_id)
1369
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
1370
+ member_id = member_response.team_id
1371
+
1372
+ member_name = team._get_member_name(member_id)
1373
+
1374
+ # If we have tool calls for this member, display them
1375
+ if member_id in member_tool_calls and member_tool_calls[member_id]:
1376
+ formatted_calls = format_tool_calls(member_tool_calls[member_id])
1377
+ if formatted_calls:
1378
+ console_width = console.width if console else 80
1379
+ panel_width = console_width + 30
1380
+
1381
+ lines = []
1382
+ for call in formatted_calls:
1383
+ wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
1384
+ lines.append(wrapped_call)
1385
+
1386
+ tool_calls_text = "\n\n".join(lines)
1387
+
1388
+ member_tool_calls_panel = create_panel(
1389
+ content=tool_calls_text,
1390
+ title=f"{member_name} Tool Calls",
1391
+ border_style="yellow",
1392
+ )
1393
+ panels.append(member_tool_calls_panel)
1394
+
1395
+ # Process member response content
1396
+ if team.show_members_responses and member_id is not None:
1397
+ show_markdown = False
1398
+ if markdown:
1399
+ show_markdown = True
1400
+
1401
+ member_response_content = _parse_response_content(
1402
+ member_response,
1403
+ tags_to_include_in_markdown,
1404
+ show_markdown=show_markdown,
1405
+ )
1406
+
1407
+ member_response_panel = create_panel(
1408
+ content=member_response_content,
1409
+ title=f"{member_name} Response",
1410
+ border_style="magenta",
1411
+ )
1412
+
1413
+ panels.append(member_response_panel)
1414
+
1415
+ # Store for reference
1416
+ if member_id is not None:
1417
+ member_response_panels[member_id] = member_response_panel
1418
+
1419
+ # Add team tool calls panel if available (before the team response)
1420
+ if team_tool_calls:
1421
+ formatted_calls = format_tool_calls(team_tool_calls)
1422
+ if formatted_calls:
1423
+ console_width = console.width if console else 80
1424
+ panel_width = console_width + 30
1425
+
1426
+ lines = []
1427
+ # Create a set to track already added calls by their string representation
1428
+ added_calls = set()
1429
+ for call in formatted_calls:
1430
+ if call not in added_calls:
1431
+ added_calls.add(call)
1432
+ # Wrap the call text to fit within the panel
1433
+ wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
1434
+ lines.append(wrapped_call)
1435
+
1436
+ # Join with blank lines between items
1437
+ tool_calls_text = "\n\n".join(lines)
1438
+
1439
+ team_tool_calls_panel = create_panel(
1440
+ content=tool_calls_text,
1441
+ title="Team Tool Calls",
1442
+ border_style="yellow",
1443
+ )
1444
+ panels.append(team_tool_calls_panel)
1445
+
1446
+ # Add the team response panel at the end
1447
+ if response_content_stream:
1448
+ render = True
1449
+ # Create panel for response
1450
+ response_panel = create_panel(
1451
+ content=response_content_stream,
1452
+ title=f"Response ({response_timer.elapsed:.1f}s)",
1453
+ border_style="blue",
1454
+ )
1455
+ panels.append(response_panel)
1456
+
1457
+ if render or len(panels) > 0:
1458
+ live_console.update(Group(*panels))
1459
+
1460
+ response_timer.stop()
1461
+
1462
+ run_response = final_run_response
1463
+
1464
+ # Add citations
1465
+ if hasattr(resp, "citations") and resp.citations is not None and resp.citations.urls is not None:
1466
+ md_content = "\n".join(
1467
+ f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
1468
+ for i, citation in enumerate(resp.citations.urls)
1469
+ if citation.url # Only include citations with valid URLs
1470
+ )
1471
+ if md_content: # Only create panel if there are citations
1472
+ citations_panel = create_panel(
1473
+ content=Markdown(md_content),
1474
+ title="Citations",
1475
+ border_style="green",
1476
+ )
1477
+ panels.append(citations_panel)
1478
+ live_console.update(Group(*panels))
1479
+
1480
+ if team.memory_manager is not None:
1481
+ if team.memory_manager.memories_updated:
1482
+ memory_panel = create_panel(
1483
+ content=Text("Memories updated"),
1484
+ title="Memories",
1485
+ border_style="green",
1486
+ )
1487
+ panels.append(memory_panel)
1488
+ live_console.update(Group(*panels))
1489
+
1490
+ if team.session_summary_manager is not None and team.session_summary_manager.summaries_updated:
1491
+ summary_panel = create_panel(
1492
+ content=Text("Session summary updated"),
1493
+ title="Session Summary",
1494
+ border_style="green",
1495
+ )
1496
+ panels.append(summary_panel)
1497
+ live_console.update(Group(*panels))
1498
+ team.session_summary_manager.summaries_updated = False
1499
+
1500
+ # Final update to remove the "Thinking..." status
1501
+ panels = [p for p in panels if not isinstance(p, Status)]
1502
+
1503
+ if markdown:
1504
+ for member in team.members:
1505
+ if member.id is not None:
1506
+ member_markdown[member.id] = True # type: ignore
1507
+
1508
+ for member in team.members:
1509
+ if member.output_schema is not None and member.id is not None:
1510
+ member_markdown[member.id] = False # type: ignore
1511
+
1512
+ # Final panels assembly - we'll recreate the panels from scratch to ensure correct order
1513
+ final_panels = []
1514
+
1515
+ # Start with the message
1516
+ if input_content and show_message:
1517
+ message_panel = create_panel(
1518
+ content=Text(input_content, style="green"),
1519
+ title="Message",
1520
+ border_style="cyan",
1521
+ )
1522
+ final_panels.append(message_panel)
1523
+
1524
+ # Add reasoning steps
1525
+ if reasoning_steps and show_reasoning:
1526
+ for i, step in enumerate(reasoning_steps, 1):
1527
+ reasoning_panel = build_reasoning_step_panel(i, step, show_full_reasoning)
1528
+ final_panels.append(reasoning_panel)
1529
+
1530
+ # Add thinking panel if available
1531
+ if _response_reasoning_content and show_reasoning:
1532
+ thinking_panel = create_panel(
1533
+ content=Text(_response_reasoning_content),
1534
+ title=f"Thinking ({response_timer.elapsed:.1f}s)",
1535
+ border_style="green",
1536
+ )
1537
+ final_panels.append(thinking_panel)
1538
+
1539
+ # Add member tool calls and responses in correct order
1540
+ if run_response is not None and hasattr(run_response, "member_responses"):
1541
+ for i, member_response in enumerate(run_response.member_responses):
1542
+ member_id = None
1543
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
1544
+ member_id = member_response.agent_id
1545
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
1546
+ member_id = member_response.team_id
1547
+
1548
+ # Print tool calls
1549
+ if member_id:
1550
+ # First add tool calls if any
1551
+ if member_id in member_tool_calls and member_tool_calls[member_id]:
1552
+ formatted_calls = format_tool_calls(member_tool_calls[member_id])
1553
+ if formatted_calls:
1554
+ console_width = console.width if console else 80
1555
+ panel_width = console_width + 30
1556
+
1557
+ lines = []
1558
+ # Create a set to track already added calls by their string representation
1559
+ added_calls = set()
1560
+ for call in formatted_calls:
1561
+ if call not in added_calls:
1562
+ added_calls.add(call)
1563
+ # Wrap the call text to fit within the panel
1564
+ wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
1565
+ lines.append(wrapped_call)
1566
+
1567
+ tool_calls_text = "\n\n".join(lines)
1568
+
1569
+ member_name = team._get_member_name(member_id)
1570
+ member_tool_calls_panel = create_panel(
1571
+ content=tool_calls_text,
1572
+ title=f"{member_name} Tool Calls",
1573
+ border_style="yellow",
1574
+ )
1575
+ final_panels.append(member_tool_calls_panel)
1576
+
1577
+ # Add reasoning steps if any
1578
+ reasoning_steps = []
1579
+ if member_response.reasoning_steps is not None:
1580
+ reasoning_steps = member_response.reasoning_steps
1581
+ if reasoning_steps and show_reasoning:
1582
+ for j, step in enumerate(reasoning_steps, 1):
1583
+ member_reasoning_panel = build_reasoning_step_panel(
1584
+ j, step, show_full_reasoning, color="magenta"
1585
+ )
1586
+ final_panels.append(member_reasoning_panel)
1587
+
1588
+ # Add reasoning steps if any
1589
+ reasoning_steps = []
1590
+ if hasattr(member_response, "reasoning_steps") and member_response.reasoning_steps is not None:
1591
+ reasoning_steps = member_response.reasoning_steps
1592
+ if reasoning_steps and show_reasoning:
1593
+ for j, step in enumerate(reasoning_steps, 1):
1594
+ member_reasoning_panel = build_reasoning_step_panel(
1595
+ j, step, show_full_reasoning, color="magenta"
1596
+ )
1597
+ final_panels.append(member_reasoning_panel)
1598
+
1599
+ # Then add response
1600
+ show_markdown = False
1601
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
1602
+ show_markdown = member_markdown.get(member_response.agent_id, False)
1603
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
1604
+ show_markdown = member_markdown.get(member_response.team_id, False)
1605
+
1606
+ member_response_content = _parse_response_content( # type: ignore
1607
+ member_response,
1608
+ tags_to_include_in_markdown,
1609
+ show_markdown=show_markdown,
1610
+ )
1611
+
1612
+ member_name = "Team Member"
1613
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
1614
+ member_name = team._get_member_name(member_response.agent_id)
1615
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
1616
+ member_name = team._get_member_name(member_response.team_id)
1617
+
1618
+ member_response_panel = create_panel(
1619
+ content=member_response_content,
1620
+ title=f"{member_name} Response",
1621
+ border_style="magenta",
1622
+ )
1623
+ final_panels.append(member_response_panel)
1624
+
1625
+ # Add citations if any
1626
+ if member_response.citations is not None and member_response.citations.urls is not None:
1627
+ md_content = "\n".join(
1628
+ f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
1629
+ for i, citation in enumerate(member_response.citations.urls)
1630
+ if citation.url # Only include citations with valid URLs
1631
+ )
1632
+ if md_content: # Only create panel if there are citations
1633
+ citations_panel = create_panel(
1634
+ content=Markdown(md_content),
1635
+ title="Citations",
1636
+ border_style="magenta",
1637
+ )
1638
+ final_panels.append(citations_panel)
1639
+
1640
+ # Add team tool calls before team response
1641
+ if team_tool_calls:
1642
+ formatted_calls = format_tool_calls(team_tool_calls)
1643
+ if formatted_calls:
1644
+ console_width = console.width if console else 80
1645
+ panel_width = console_width + 30
1646
+
1647
+ lines = []
1648
+ # Create a set to track already added calls by their string representation
1649
+ added_calls = set()
1650
+ for call in formatted_calls:
1651
+ if call not in added_calls:
1652
+ added_calls.add(call)
1653
+ # Wrap the call text to fit within the panel
1654
+ wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
1655
+ lines.append(wrapped_call)
1656
+
1657
+ tool_calls_text = "\n\n".join(lines)
1658
+
1659
+ team_tool_calls_panel = create_panel(
1660
+ content=tool_calls_text,
1661
+ title="Team Tool Calls",
1662
+ border_style="yellow",
1663
+ )
1664
+ final_panels.append(team_tool_calls_panel)
1665
+
1666
+ # Add team response
1667
+ if _response_content:
1668
+ response_content_stream = _response_content
1669
+ if team_markdown:
1670
+ escaped_content = escape_markdown_tags(_response_content, tags_to_include_in_markdown)
1671
+ response_content_stream = Markdown(escaped_content)
1672
+
1673
+ response_panel = create_panel(
1674
+ content=response_content_stream,
1675
+ title=f"Response ({response_timer.elapsed:.1f}s)",
1676
+ border_style="blue",
1677
+ )
1678
+ final_panels.append(response_panel)
1679
+
1680
+ # Add team citations
1681
+ if hasattr(resp, "citations") and resp.citations is not None and resp.citations.urls is not None:
1682
+ md_content = "\n".join(
1683
+ f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
1684
+ for i, citation in enumerate(resp.citations.urls)
1685
+ if citation.url # Only include citations with valid URLs
1686
+ )
1687
+ if md_content: # Only create panel if there are citations
1688
+ citations_panel = create_panel(
1689
+ content=Markdown(md_content),
1690
+ title="Citations",
1691
+ border_style="green",
1692
+ )
1693
+ final_panels.append(citations_panel)
1694
+
1695
+ # Final update with correctly ordered panels
1696
+ live_console.update(Group(*final_panels))
1697
+
1698
+
1699
+ def _parse_response_content(
1700
+ run_response: Union[TeamRunOutput, RunOutput],
1701
+ tags_to_include_in_markdown: Set[str],
1702
+ show_markdown: bool = True,
1703
+ ) -> Any:
1704
+ from rich.json import JSON
1705
+ from rich.markdown import Markdown
1706
+
1707
+ if isinstance(run_response.content, str):
1708
+ if show_markdown:
1709
+ escaped_content = escape_markdown_tags(run_response.content, tags_to_include_in_markdown)
1710
+ return Markdown(escaped_content)
1711
+ else:
1712
+ return run_response.get_content_as_string(indent=4)
1713
+ elif isinstance(run_response.content, BaseModel):
1714
+ try:
1715
+ return JSON(run_response.content.model_dump_json(exclude_none=True), indent=2)
1716
+ except Exception as e:
1717
+ log_warning(f"Failed to convert response to JSON: {e}")
1718
+ else:
1719
+ import json
1720
+
1721
+ try:
1722
+ return JSON(json.dumps(run_response.content), indent=4)
1723
+ except Exception as e:
1724
+ log_warning(f"Failed to convert response to JSON: {e}")