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