agno 2.2.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (575) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +51 -0
  3. agno/agent/agent.py +10405 -0
  4. agno/api/__init__.py +0 -0
  5. agno/api/agent.py +28 -0
  6. agno/api/api.py +40 -0
  7. agno/api/evals.py +22 -0
  8. agno/api/os.py +17 -0
  9. agno/api/routes.py +13 -0
  10. agno/api/schemas/__init__.py +9 -0
  11. agno/api/schemas/agent.py +16 -0
  12. agno/api/schemas/evals.py +16 -0
  13. agno/api/schemas/os.py +14 -0
  14. agno/api/schemas/response.py +6 -0
  15. agno/api/schemas/team.py +16 -0
  16. agno/api/schemas/utils.py +21 -0
  17. agno/api/schemas/workflows.py +16 -0
  18. agno/api/settings.py +53 -0
  19. agno/api/team.py +30 -0
  20. agno/api/workflow.py +28 -0
  21. agno/cloud/aws/base.py +214 -0
  22. agno/cloud/aws/s3/__init__.py +2 -0
  23. agno/cloud/aws/s3/api_client.py +43 -0
  24. agno/cloud/aws/s3/bucket.py +195 -0
  25. agno/cloud/aws/s3/object.py +57 -0
  26. agno/culture/__init__.py +3 -0
  27. agno/culture/manager.py +956 -0
  28. agno/db/__init__.py +24 -0
  29. agno/db/async_postgres/__init__.py +3 -0
  30. agno/db/base.py +598 -0
  31. agno/db/dynamo/__init__.py +3 -0
  32. agno/db/dynamo/dynamo.py +2042 -0
  33. agno/db/dynamo/schemas.py +314 -0
  34. agno/db/dynamo/utils.py +743 -0
  35. agno/db/firestore/__init__.py +3 -0
  36. agno/db/firestore/firestore.py +1795 -0
  37. agno/db/firestore/schemas.py +140 -0
  38. agno/db/firestore/utils.py +376 -0
  39. agno/db/gcs_json/__init__.py +3 -0
  40. agno/db/gcs_json/gcs_json_db.py +1335 -0
  41. agno/db/gcs_json/utils.py +228 -0
  42. agno/db/in_memory/__init__.py +3 -0
  43. agno/db/in_memory/in_memory_db.py +1160 -0
  44. agno/db/in_memory/utils.py +230 -0
  45. agno/db/json/__init__.py +3 -0
  46. agno/db/json/json_db.py +1328 -0
  47. agno/db/json/utils.py +230 -0
  48. agno/db/migrations/__init__.py +0 -0
  49. agno/db/migrations/v1_to_v2.py +635 -0
  50. agno/db/mongo/__init__.py +17 -0
  51. agno/db/mongo/async_mongo.py +2026 -0
  52. agno/db/mongo/mongo.py +1982 -0
  53. agno/db/mongo/schemas.py +87 -0
  54. agno/db/mongo/utils.py +259 -0
  55. agno/db/mysql/__init__.py +3 -0
  56. agno/db/mysql/mysql.py +2308 -0
  57. agno/db/mysql/schemas.py +138 -0
  58. agno/db/mysql/utils.py +355 -0
  59. agno/db/postgres/__init__.py +4 -0
  60. agno/db/postgres/async_postgres.py +1927 -0
  61. agno/db/postgres/postgres.py +2260 -0
  62. agno/db/postgres/schemas.py +139 -0
  63. agno/db/postgres/utils.py +442 -0
  64. agno/db/redis/__init__.py +3 -0
  65. agno/db/redis/redis.py +1660 -0
  66. agno/db/redis/schemas.py +123 -0
  67. agno/db/redis/utils.py +346 -0
  68. agno/db/schemas/__init__.py +4 -0
  69. agno/db/schemas/culture.py +120 -0
  70. agno/db/schemas/evals.py +33 -0
  71. agno/db/schemas/knowledge.py +40 -0
  72. agno/db/schemas/memory.py +46 -0
  73. agno/db/schemas/metrics.py +0 -0
  74. agno/db/singlestore/__init__.py +3 -0
  75. agno/db/singlestore/schemas.py +130 -0
  76. agno/db/singlestore/singlestore.py +2272 -0
  77. agno/db/singlestore/utils.py +384 -0
  78. agno/db/sqlite/__init__.py +4 -0
  79. agno/db/sqlite/async_sqlite.py +2293 -0
  80. agno/db/sqlite/schemas.py +133 -0
  81. agno/db/sqlite/sqlite.py +2288 -0
  82. agno/db/sqlite/utils.py +431 -0
  83. agno/db/surrealdb/__init__.py +3 -0
  84. agno/db/surrealdb/metrics.py +292 -0
  85. agno/db/surrealdb/models.py +309 -0
  86. agno/db/surrealdb/queries.py +71 -0
  87. agno/db/surrealdb/surrealdb.py +1353 -0
  88. agno/db/surrealdb/utils.py +147 -0
  89. agno/db/utils.py +116 -0
  90. agno/debug.py +18 -0
  91. agno/eval/__init__.py +14 -0
  92. agno/eval/accuracy.py +834 -0
  93. agno/eval/performance.py +773 -0
  94. agno/eval/reliability.py +306 -0
  95. agno/eval/utils.py +119 -0
  96. agno/exceptions.py +161 -0
  97. agno/filters.py +354 -0
  98. agno/guardrails/__init__.py +6 -0
  99. agno/guardrails/base.py +19 -0
  100. agno/guardrails/openai.py +144 -0
  101. agno/guardrails/pii.py +94 -0
  102. agno/guardrails/prompt_injection.py +52 -0
  103. agno/integrations/__init__.py +0 -0
  104. agno/integrations/discord/__init__.py +3 -0
  105. agno/integrations/discord/client.py +203 -0
  106. agno/knowledge/__init__.py +5 -0
  107. agno/knowledge/chunking/__init__.py +0 -0
  108. agno/knowledge/chunking/agentic.py +79 -0
  109. agno/knowledge/chunking/document.py +91 -0
  110. agno/knowledge/chunking/fixed.py +57 -0
  111. agno/knowledge/chunking/markdown.py +151 -0
  112. agno/knowledge/chunking/recursive.py +63 -0
  113. agno/knowledge/chunking/row.py +39 -0
  114. agno/knowledge/chunking/semantic.py +86 -0
  115. agno/knowledge/chunking/strategy.py +165 -0
  116. agno/knowledge/content.py +74 -0
  117. agno/knowledge/document/__init__.py +5 -0
  118. agno/knowledge/document/base.py +58 -0
  119. agno/knowledge/embedder/__init__.py +5 -0
  120. agno/knowledge/embedder/aws_bedrock.py +343 -0
  121. agno/knowledge/embedder/azure_openai.py +210 -0
  122. agno/knowledge/embedder/base.py +23 -0
  123. agno/knowledge/embedder/cohere.py +323 -0
  124. agno/knowledge/embedder/fastembed.py +62 -0
  125. agno/knowledge/embedder/fireworks.py +13 -0
  126. agno/knowledge/embedder/google.py +258 -0
  127. agno/knowledge/embedder/huggingface.py +94 -0
  128. agno/knowledge/embedder/jina.py +182 -0
  129. agno/knowledge/embedder/langdb.py +22 -0
  130. agno/knowledge/embedder/mistral.py +206 -0
  131. agno/knowledge/embedder/nebius.py +13 -0
  132. agno/knowledge/embedder/ollama.py +154 -0
  133. agno/knowledge/embedder/openai.py +195 -0
  134. agno/knowledge/embedder/sentence_transformer.py +63 -0
  135. agno/knowledge/embedder/together.py +13 -0
  136. agno/knowledge/embedder/vllm.py +262 -0
  137. agno/knowledge/embedder/voyageai.py +165 -0
  138. agno/knowledge/knowledge.py +1988 -0
  139. agno/knowledge/reader/__init__.py +7 -0
  140. agno/knowledge/reader/arxiv_reader.py +81 -0
  141. agno/knowledge/reader/base.py +95 -0
  142. agno/knowledge/reader/csv_reader.py +166 -0
  143. agno/knowledge/reader/docx_reader.py +82 -0
  144. agno/knowledge/reader/field_labeled_csv_reader.py +292 -0
  145. agno/knowledge/reader/firecrawl_reader.py +201 -0
  146. agno/knowledge/reader/json_reader.py +87 -0
  147. agno/knowledge/reader/markdown_reader.py +137 -0
  148. agno/knowledge/reader/pdf_reader.py +431 -0
  149. agno/knowledge/reader/pptx_reader.py +101 -0
  150. agno/knowledge/reader/reader_factory.py +313 -0
  151. agno/knowledge/reader/s3_reader.py +89 -0
  152. agno/knowledge/reader/tavily_reader.py +194 -0
  153. agno/knowledge/reader/text_reader.py +115 -0
  154. agno/knowledge/reader/web_search_reader.py +372 -0
  155. agno/knowledge/reader/website_reader.py +455 -0
  156. agno/knowledge/reader/wikipedia_reader.py +59 -0
  157. agno/knowledge/reader/youtube_reader.py +78 -0
  158. agno/knowledge/remote_content/__init__.py +0 -0
  159. agno/knowledge/remote_content/remote_content.py +88 -0
  160. agno/knowledge/reranker/__init__.py +3 -0
  161. agno/knowledge/reranker/base.py +14 -0
  162. agno/knowledge/reranker/cohere.py +64 -0
  163. agno/knowledge/reranker/infinity.py +195 -0
  164. agno/knowledge/reranker/sentence_transformer.py +54 -0
  165. agno/knowledge/types.py +39 -0
  166. agno/knowledge/utils.py +189 -0
  167. agno/media.py +462 -0
  168. agno/memory/__init__.py +3 -0
  169. agno/memory/manager.py +1327 -0
  170. agno/models/__init__.py +0 -0
  171. agno/models/aimlapi/__init__.py +5 -0
  172. agno/models/aimlapi/aimlapi.py +45 -0
  173. agno/models/anthropic/__init__.py +5 -0
  174. agno/models/anthropic/claude.py +757 -0
  175. agno/models/aws/__init__.py +15 -0
  176. agno/models/aws/bedrock.py +701 -0
  177. agno/models/aws/claude.py +378 -0
  178. agno/models/azure/__init__.py +18 -0
  179. agno/models/azure/ai_foundry.py +485 -0
  180. agno/models/azure/openai_chat.py +131 -0
  181. agno/models/base.py +2175 -0
  182. agno/models/cerebras/__init__.py +12 -0
  183. agno/models/cerebras/cerebras.py +501 -0
  184. agno/models/cerebras/cerebras_openai.py +112 -0
  185. agno/models/cohere/__init__.py +5 -0
  186. agno/models/cohere/chat.py +389 -0
  187. agno/models/cometapi/__init__.py +5 -0
  188. agno/models/cometapi/cometapi.py +57 -0
  189. agno/models/dashscope/__init__.py +5 -0
  190. agno/models/dashscope/dashscope.py +91 -0
  191. agno/models/deepinfra/__init__.py +5 -0
  192. agno/models/deepinfra/deepinfra.py +28 -0
  193. agno/models/deepseek/__init__.py +5 -0
  194. agno/models/deepseek/deepseek.py +61 -0
  195. agno/models/defaults.py +1 -0
  196. agno/models/fireworks/__init__.py +5 -0
  197. agno/models/fireworks/fireworks.py +26 -0
  198. agno/models/google/__init__.py +5 -0
  199. agno/models/google/gemini.py +1085 -0
  200. agno/models/groq/__init__.py +5 -0
  201. agno/models/groq/groq.py +556 -0
  202. agno/models/huggingface/__init__.py +5 -0
  203. agno/models/huggingface/huggingface.py +491 -0
  204. agno/models/ibm/__init__.py +5 -0
  205. agno/models/ibm/watsonx.py +422 -0
  206. agno/models/internlm/__init__.py +3 -0
  207. agno/models/internlm/internlm.py +26 -0
  208. agno/models/langdb/__init__.py +1 -0
  209. agno/models/langdb/langdb.py +48 -0
  210. agno/models/litellm/__init__.py +14 -0
  211. agno/models/litellm/chat.py +468 -0
  212. agno/models/litellm/litellm_openai.py +25 -0
  213. agno/models/llama_cpp/__init__.py +5 -0
  214. agno/models/llama_cpp/llama_cpp.py +22 -0
  215. agno/models/lmstudio/__init__.py +5 -0
  216. agno/models/lmstudio/lmstudio.py +25 -0
  217. agno/models/message.py +434 -0
  218. agno/models/meta/__init__.py +12 -0
  219. agno/models/meta/llama.py +475 -0
  220. agno/models/meta/llama_openai.py +78 -0
  221. agno/models/metrics.py +120 -0
  222. agno/models/mistral/__init__.py +5 -0
  223. agno/models/mistral/mistral.py +432 -0
  224. agno/models/nebius/__init__.py +3 -0
  225. agno/models/nebius/nebius.py +54 -0
  226. agno/models/nexus/__init__.py +3 -0
  227. agno/models/nexus/nexus.py +22 -0
  228. agno/models/nvidia/__init__.py +5 -0
  229. agno/models/nvidia/nvidia.py +28 -0
  230. agno/models/ollama/__init__.py +5 -0
  231. agno/models/ollama/chat.py +441 -0
  232. agno/models/openai/__init__.py +9 -0
  233. agno/models/openai/chat.py +883 -0
  234. agno/models/openai/like.py +27 -0
  235. agno/models/openai/responses.py +1050 -0
  236. agno/models/openrouter/__init__.py +5 -0
  237. agno/models/openrouter/openrouter.py +66 -0
  238. agno/models/perplexity/__init__.py +5 -0
  239. agno/models/perplexity/perplexity.py +187 -0
  240. agno/models/portkey/__init__.py +3 -0
  241. agno/models/portkey/portkey.py +81 -0
  242. agno/models/requesty/__init__.py +5 -0
  243. agno/models/requesty/requesty.py +52 -0
  244. agno/models/response.py +199 -0
  245. agno/models/sambanova/__init__.py +5 -0
  246. agno/models/sambanova/sambanova.py +28 -0
  247. agno/models/siliconflow/__init__.py +5 -0
  248. agno/models/siliconflow/siliconflow.py +25 -0
  249. agno/models/together/__init__.py +5 -0
  250. agno/models/together/together.py +25 -0
  251. agno/models/utils.py +266 -0
  252. agno/models/vercel/__init__.py +3 -0
  253. agno/models/vercel/v0.py +26 -0
  254. agno/models/vertexai/__init__.py +0 -0
  255. agno/models/vertexai/claude.py +70 -0
  256. agno/models/vllm/__init__.py +3 -0
  257. agno/models/vllm/vllm.py +78 -0
  258. agno/models/xai/__init__.py +3 -0
  259. agno/models/xai/xai.py +113 -0
  260. agno/os/__init__.py +3 -0
  261. agno/os/app.py +876 -0
  262. agno/os/auth.py +57 -0
  263. agno/os/config.py +104 -0
  264. agno/os/interfaces/__init__.py +1 -0
  265. agno/os/interfaces/a2a/__init__.py +3 -0
  266. agno/os/interfaces/a2a/a2a.py +42 -0
  267. agno/os/interfaces/a2a/router.py +250 -0
  268. agno/os/interfaces/a2a/utils.py +924 -0
  269. agno/os/interfaces/agui/__init__.py +3 -0
  270. agno/os/interfaces/agui/agui.py +47 -0
  271. agno/os/interfaces/agui/router.py +144 -0
  272. agno/os/interfaces/agui/utils.py +534 -0
  273. agno/os/interfaces/base.py +25 -0
  274. agno/os/interfaces/slack/__init__.py +3 -0
  275. agno/os/interfaces/slack/router.py +148 -0
  276. agno/os/interfaces/slack/security.py +30 -0
  277. agno/os/interfaces/slack/slack.py +47 -0
  278. agno/os/interfaces/whatsapp/__init__.py +3 -0
  279. agno/os/interfaces/whatsapp/router.py +211 -0
  280. agno/os/interfaces/whatsapp/security.py +53 -0
  281. agno/os/interfaces/whatsapp/whatsapp.py +36 -0
  282. agno/os/mcp.py +292 -0
  283. agno/os/middleware/__init__.py +7 -0
  284. agno/os/middleware/jwt.py +233 -0
  285. agno/os/router.py +1763 -0
  286. agno/os/routers/__init__.py +3 -0
  287. agno/os/routers/evals/__init__.py +3 -0
  288. agno/os/routers/evals/evals.py +430 -0
  289. agno/os/routers/evals/schemas.py +142 -0
  290. agno/os/routers/evals/utils.py +162 -0
  291. agno/os/routers/health.py +31 -0
  292. agno/os/routers/home.py +52 -0
  293. agno/os/routers/knowledge/__init__.py +3 -0
  294. agno/os/routers/knowledge/knowledge.py +997 -0
  295. agno/os/routers/knowledge/schemas.py +178 -0
  296. agno/os/routers/memory/__init__.py +3 -0
  297. agno/os/routers/memory/memory.py +515 -0
  298. agno/os/routers/memory/schemas.py +62 -0
  299. agno/os/routers/metrics/__init__.py +3 -0
  300. agno/os/routers/metrics/metrics.py +190 -0
  301. agno/os/routers/metrics/schemas.py +47 -0
  302. agno/os/routers/session/__init__.py +3 -0
  303. agno/os/routers/session/session.py +997 -0
  304. agno/os/schema.py +1055 -0
  305. agno/os/settings.py +43 -0
  306. agno/os/utils.py +630 -0
  307. agno/py.typed +0 -0
  308. agno/reasoning/__init__.py +0 -0
  309. agno/reasoning/anthropic.py +80 -0
  310. agno/reasoning/azure_ai_foundry.py +67 -0
  311. agno/reasoning/deepseek.py +63 -0
  312. agno/reasoning/default.py +97 -0
  313. agno/reasoning/gemini.py +73 -0
  314. agno/reasoning/groq.py +71 -0
  315. agno/reasoning/helpers.py +63 -0
  316. agno/reasoning/ollama.py +67 -0
  317. agno/reasoning/openai.py +86 -0
  318. agno/reasoning/step.py +31 -0
  319. agno/reasoning/vertexai.py +76 -0
  320. agno/run/__init__.py +6 -0
  321. agno/run/agent.py +787 -0
  322. agno/run/base.py +229 -0
  323. agno/run/cancel.py +81 -0
  324. agno/run/messages.py +32 -0
  325. agno/run/team.py +753 -0
  326. agno/run/workflow.py +708 -0
  327. agno/session/__init__.py +10 -0
  328. agno/session/agent.py +295 -0
  329. agno/session/summary.py +265 -0
  330. agno/session/team.py +392 -0
  331. agno/session/workflow.py +205 -0
  332. agno/team/__init__.py +37 -0
  333. agno/team/team.py +8793 -0
  334. agno/tools/__init__.py +10 -0
  335. agno/tools/agentql.py +120 -0
  336. agno/tools/airflow.py +69 -0
  337. agno/tools/api.py +122 -0
  338. agno/tools/apify.py +314 -0
  339. agno/tools/arxiv.py +127 -0
  340. agno/tools/aws_lambda.py +53 -0
  341. agno/tools/aws_ses.py +66 -0
  342. agno/tools/baidusearch.py +89 -0
  343. agno/tools/bitbucket.py +292 -0
  344. agno/tools/brandfetch.py +213 -0
  345. agno/tools/bravesearch.py +106 -0
  346. agno/tools/brightdata.py +367 -0
  347. agno/tools/browserbase.py +209 -0
  348. agno/tools/calcom.py +255 -0
  349. agno/tools/calculator.py +151 -0
  350. agno/tools/cartesia.py +187 -0
  351. agno/tools/clickup.py +244 -0
  352. agno/tools/confluence.py +240 -0
  353. agno/tools/crawl4ai.py +158 -0
  354. agno/tools/csv_toolkit.py +185 -0
  355. agno/tools/dalle.py +110 -0
  356. agno/tools/daytona.py +475 -0
  357. agno/tools/decorator.py +262 -0
  358. agno/tools/desi_vocal.py +108 -0
  359. agno/tools/discord.py +161 -0
  360. agno/tools/docker.py +716 -0
  361. agno/tools/duckdb.py +379 -0
  362. agno/tools/duckduckgo.py +91 -0
  363. agno/tools/e2b.py +703 -0
  364. agno/tools/eleven_labs.py +196 -0
  365. agno/tools/email.py +67 -0
  366. agno/tools/evm.py +129 -0
  367. agno/tools/exa.py +396 -0
  368. agno/tools/fal.py +127 -0
  369. agno/tools/file.py +240 -0
  370. agno/tools/file_generation.py +350 -0
  371. agno/tools/financial_datasets.py +288 -0
  372. agno/tools/firecrawl.py +143 -0
  373. agno/tools/function.py +1187 -0
  374. agno/tools/giphy.py +93 -0
  375. agno/tools/github.py +1760 -0
  376. agno/tools/gmail.py +922 -0
  377. agno/tools/google_bigquery.py +117 -0
  378. agno/tools/google_drive.py +270 -0
  379. agno/tools/google_maps.py +253 -0
  380. agno/tools/googlecalendar.py +674 -0
  381. agno/tools/googlesearch.py +98 -0
  382. agno/tools/googlesheets.py +377 -0
  383. agno/tools/hackernews.py +77 -0
  384. agno/tools/jina.py +101 -0
  385. agno/tools/jira.py +170 -0
  386. agno/tools/knowledge.py +218 -0
  387. agno/tools/linear.py +426 -0
  388. agno/tools/linkup.py +58 -0
  389. agno/tools/local_file_system.py +90 -0
  390. agno/tools/lumalab.py +183 -0
  391. agno/tools/mcp/__init__.py +10 -0
  392. agno/tools/mcp/mcp.py +331 -0
  393. agno/tools/mcp/multi_mcp.py +347 -0
  394. agno/tools/mcp/params.py +24 -0
  395. agno/tools/mcp_toolbox.py +284 -0
  396. agno/tools/mem0.py +193 -0
  397. agno/tools/memori.py +339 -0
  398. agno/tools/memory.py +419 -0
  399. agno/tools/mlx_transcribe.py +139 -0
  400. agno/tools/models/__init__.py +0 -0
  401. agno/tools/models/azure_openai.py +190 -0
  402. agno/tools/models/gemini.py +203 -0
  403. agno/tools/models/groq.py +158 -0
  404. agno/tools/models/morph.py +186 -0
  405. agno/tools/models/nebius.py +124 -0
  406. agno/tools/models_labs.py +195 -0
  407. agno/tools/moviepy_video.py +349 -0
  408. agno/tools/neo4j.py +134 -0
  409. agno/tools/newspaper.py +46 -0
  410. agno/tools/newspaper4k.py +93 -0
  411. agno/tools/notion.py +204 -0
  412. agno/tools/openai.py +202 -0
  413. agno/tools/openbb.py +160 -0
  414. agno/tools/opencv.py +321 -0
  415. agno/tools/openweather.py +233 -0
  416. agno/tools/oxylabs.py +385 -0
  417. agno/tools/pandas.py +102 -0
  418. agno/tools/parallel.py +314 -0
  419. agno/tools/postgres.py +257 -0
  420. agno/tools/pubmed.py +188 -0
  421. agno/tools/python.py +205 -0
  422. agno/tools/reasoning.py +283 -0
  423. agno/tools/reddit.py +467 -0
  424. agno/tools/replicate.py +117 -0
  425. agno/tools/resend.py +62 -0
  426. agno/tools/scrapegraph.py +222 -0
  427. agno/tools/searxng.py +152 -0
  428. agno/tools/serpapi.py +116 -0
  429. agno/tools/serper.py +255 -0
  430. agno/tools/shell.py +53 -0
  431. agno/tools/slack.py +136 -0
  432. agno/tools/sleep.py +20 -0
  433. agno/tools/spider.py +116 -0
  434. agno/tools/sql.py +154 -0
  435. agno/tools/streamlit/__init__.py +0 -0
  436. agno/tools/streamlit/components.py +113 -0
  437. agno/tools/tavily.py +254 -0
  438. agno/tools/telegram.py +48 -0
  439. agno/tools/todoist.py +218 -0
  440. agno/tools/tool_registry.py +1 -0
  441. agno/tools/toolkit.py +146 -0
  442. agno/tools/trafilatura.py +388 -0
  443. agno/tools/trello.py +274 -0
  444. agno/tools/twilio.py +186 -0
  445. agno/tools/user_control_flow.py +78 -0
  446. agno/tools/valyu.py +228 -0
  447. agno/tools/visualization.py +467 -0
  448. agno/tools/webbrowser.py +28 -0
  449. agno/tools/webex.py +76 -0
  450. agno/tools/website.py +54 -0
  451. agno/tools/webtools.py +45 -0
  452. agno/tools/whatsapp.py +286 -0
  453. agno/tools/wikipedia.py +63 -0
  454. agno/tools/workflow.py +278 -0
  455. agno/tools/x.py +335 -0
  456. agno/tools/yfinance.py +257 -0
  457. agno/tools/youtube.py +184 -0
  458. agno/tools/zendesk.py +82 -0
  459. agno/tools/zep.py +454 -0
  460. agno/tools/zoom.py +382 -0
  461. agno/utils/__init__.py +0 -0
  462. agno/utils/agent.py +820 -0
  463. agno/utils/audio.py +49 -0
  464. agno/utils/certs.py +27 -0
  465. agno/utils/code_execution.py +11 -0
  466. agno/utils/common.py +132 -0
  467. agno/utils/dttm.py +13 -0
  468. agno/utils/enum.py +22 -0
  469. agno/utils/env.py +11 -0
  470. agno/utils/events.py +696 -0
  471. agno/utils/format_str.py +16 -0
  472. agno/utils/functions.py +166 -0
  473. agno/utils/gemini.py +426 -0
  474. agno/utils/hooks.py +57 -0
  475. agno/utils/http.py +74 -0
  476. agno/utils/json_schema.py +234 -0
  477. agno/utils/knowledge.py +36 -0
  478. agno/utils/location.py +19 -0
  479. agno/utils/log.py +255 -0
  480. agno/utils/mcp.py +214 -0
  481. agno/utils/media.py +352 -0
  482. agno/utils/merge_dict.py +41 -0
  483. agno/utils/message.py +118 -0
  484. agno/utils/models/__init__.py +0 -0
  485. agno/utils/models/ai_foundry.py +43 -0
  486. agno/utils/models/claude.py +358 -0
  487. agno/utils/models/cohere.py +87 -0
  488. agno/utils/models/llama.py +78 -0
  489. agno/utils/models/mistral.py +98 -0
  490. agno/utils/models/openai_responses.py +140 -0
  491. agno/utils/models/schema_utils.py +153 -0
  492. agno/utils/models/watsonx.py +41 -0
  493. agno/utils/openai.py +257 -0
  494. agno/utils/pickle.py +32 -0
  495. agno/utils/pprint.py +178 -0
  496. agno/utils/print_response/__init__.py +0 -0
  497. agno/utils/print_response/agent.py +842 -0
  498. agno/utils/print_response/team.py +1724 -0
  499. agno/utils/print_response/workflow.py +1668 -0
  500. agno/utils/prompts.py +111 -0
  501. agno/utils/reasoning.py +108 -0
  502. agno/utils/response.py +163 -0
  503. agno/utils/response_iterator.py +17 -0
  504. agno/utils/safe_formatter.py +24 -0
  505. agno/utils/serialize.py +32 -0
  506. agno/utils/shell.py +22 -0
  507. agno/utils/streamlit.py +487 -0
  508. agno/utils/string.py +231 -0
  509. agno/utils/team.py +139 -0
  510. agno/utils/timer.py +41 -0
  511. agno/utils/tools.py +102 -0
  512. agno/utils/web.py +23 -0
  513. agno/utils/whatsapp.py +305 -0
  514. agno/utils/yaml_io.py +25 -0
  515. agno/vectordb/__init__.py +3 -0
  516. agno/vectordb/base.py +127 -0
  517. agno/vectordb/cassandra/__init__.py +5 -0
  518. agno/vectordb/cassandra/cassandra.py +501 -0
  519. agno/vectordb/cassandra/extra_param_mixin.py +11 -0
  520. agno/vectordb/cassandra/index.py +13 -0
  521. agno/vectordb/chroma/__init__.py +5 -0
  522. agno/vectordb/chroma/chromadb.py +929 -0
  523. agno/vectordb/clickhouse/__init__.py +9 -0
  524. agno/vectordb/clickhouse/clickhousedb.py +835 -0
  525. agno/vectordb/clickhouse/index.py +9 -0
  526. agno/vectordb/couchbase/__init__.py +3 -0
  527. agno/vectordb/couchbase/couchbase.py +1442 -0
  528. agno/vectordb/distance.py +7 -0
  529. agno/vectordb/lancedb/__init__.py +6 -0
  530. agno/vectordb/lancedb/lance_db.py +995 -0
  531. agno/vectordb/langchaindb/__init__.py +5 -0
  532. agno/vectordb/langchaindb/langchaindb.py +163 -0
  533. agno/vectordb/lightrag/__init__.py +5 -0
  534. agno/vectordb/lightrag/lightrag.py +388 -0
  535. agno/vectordb/llamaindex/__init__.py +3 -0
  536. agno/vectordb/llamaindex/llamaindexdb.py +166 -0
  537. agno/vectordb/milvus/__init__.py +4 -0
  538. agno/vectordb/milvus/milvus.py +1182 -0
  539. agno/vectordb/mongodb/__init__.py +9 -0
  540. agno/vectordb/mongodb/mongodb.py +1417 -0
  541. agno/vectordb/pgvector/__init__.py +12 -0
  542. agno/vectordb/pgvector/index.py +23 -0
  543. agno/vectordb/pgvector/pgvector.py +1462 -0
  544. agno/vectordb/pineconedb/__init__.py +5 -0
  545. agno/vectordb/pineconedb/pineconedb.py +747 -0
  546. agno/vectordb/qdrant/__init__.py +5 -0
  547. agno/vectordb/qdrant/qdrant.py +1134 -0
  548. agno/vectordb/redis/__init__.py +9 -0
  549. agno/vectordb/redis/redisdb.py +694 -0
  550. agno/vectordb/search.py +7 -0
  551. agno/vectordb/singlestore/__init__.py +10 -0
  552. agno/vectordb/singlestore/index.py +41 -0
  553. agno/vectordb/singlestore/singlestore.py +763 -0
  554. agno/vectordb/surrealdb/__init__.py +3 -0
  555. agno/vectordb/surrealdb/surrealdb.py +699 -0
  556. agno/vectordb/upstashdb/__init__.py +5 -0
  557. agno/vectordb/upstashdb/upstashdb.py +718 -0
  558. agno/vectordb/weaviate/__init__.py +8 -0
  559. agno/vectordb/weaviate/index.py +15 -0
  560. agno/vectordb/weaviate/weaviate.py +1005 -0
  561. agno/workflow/__init__.py +23 -0
  562. agno/workflow/agent.py +299 -0
  563. agno/workflow/condition.py +738 -0
  564. agno/workflow/loop.py +735 -0
  565. agno/workflow/parallel.py +824 -0
  566. agno/workflow/router.py +702 -0
  567. agno/workflow/step.py +1432 -0
  568. agno/workflow/steps.py +592 -0
  569. agno/workflow/types.py +520 -0
  570. agno/workflow/workflow.py +4321 -0
  571. agno-2.2.13.dist-info/METADATA +614 -0
  572. agno-2.2.13.dist-info/RECORD +575 -0
  573. agno-2.2.13.dist-info/WHEEL +5 -0
  574. agno-2.2.13.dist-info/licenses/LICENSE +201 -0
  575. agno-2.2.13.dist-info/top_level.txt +1 -0
agno/os/settings.py ADDED
@@ -0,0 +1,43 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import List, Optional
4
+
5
+ from pydantic import Field, field_validator
6
+ from pydantic_settings import BaseSettings
7
+
8
+
9
+ class AgnoAPISettings(BaseSettings):
10
+ """App settings for API-based apps that can be set using environment variables.
11
+
12
+ Reference: https://pydantic-docs.helpmanual.io/usage/settings/
13
+ """
14
+
15
+ env: str = "dev"
16
+
17
+ # Set to False to disable docs server at /docs and /redoc
18
+ docs_enabled: bool = True
19
+
20
+ # Authentication settings
21
+ os_security_key: Optional[str] = Field(default=None, description="Bearer token for API authentication")
22
+
23
+ # Cors origin list to allow requests from.
24
+ # This list is set using the set_cors_origin_list validator
25
+ cors_origin_list: Optional[List[str]] = Field(default=None, validate_default=True)
26
+
27
+ @field_validator("cors_origin_list", mode="before")
28
+ def set_cors_origin_list(cls, cors_origin_list):
29
+ valid_cors = cors_origin_list or []
30
+
31
+ # Add Agno domains to cors origin list
32
+ valid_cors.extend(
33
+ [
34
+ "http://localhost:3000",
35
+ "https://agno.com",
36
+ "https://www.agno.com",
37
+ "https://app.agno.com",
38
+ "https://os-stg.agno.com",
39
+ "https://os.agno.com",
40
+ ]
41
+ )
42
+
43
+ return valid_cors
agno/os/utils.py ADDED
@@ -0,0 +1,630 @@
1
+ from typing import Any, Callable, Dict, List, Optional, Set, Union
2
+
3
+ from fastapi import FastAPI, HTTPException, UploadFile
4
+ from fastapi.routing import APIRoute, APIRouter
5
+ from pydantic import BaseModel
6
+ from starlette.middleware.cors import CORSMiddleware
7
+
8
+ from agno.agent.agent import Agent
9
+ from agno.db.base import AsyncBaseDb, BaseDb
10
+ from agno.knowledge.knowledge import Knowledge
11
+ from agno.media import Audio, Image, Video
12
+ from agno.media import File as FileMedia
13
+ from agno.models.message import Message
14
+ from agno.os.config import AgentOSConfig
15
+ from agno.team.team import Team
16
+ from agno.tools import Toolkit
17
+ from agno.tools.function import Function
18
+ from agno.utils.log import logger
19
+ from agno.workflow.workflow import Workflow
20
+
21
+
22
+ async def get_db(
23
+ dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]], db_id: Optional[str] = None, table: Optional[str] = None
24
+ ) -> Union[BaseDb, AsyncBaseDb]:
25
+ """Return the database with the given ID and/or table, or the first database if no ID/table is provided."""
26
+
27
+ if table and not db_id:
28
+ raise HTTPException(status_code=400, detail="The db_id query parameter is required when passing a table")
29
+
30
+ async def _has_table(db: Union[BaseDb, AsyncBaseDb], table_name: str) -> bool:
31
+ """Check if this database has the specified table (configured and actually exists)."""
32
+ # First check if table name is configured
33
+ is_configured = (
34
+ hasattr(db, "session_table_name")
35
+ and db.session_table_name == table_name
36
+ or hasattr(db, "memory_table_name")
37
+ and db.memory_table_name == table_name
38
+ or hasattr(db, "metrics_table_name")
39
+ and db.metrics_table_name == table_name
40
+ or hasattr(db, "eval_table_name")
41
+ and db.eval_table_name == table_name
42
+ or hasattr(db, "knowledge_table_name")
43
+ and db.knowledge_table_name == table_name
44
+ )
45
+
46
+ if not is_configured:
47
+ return False
48
+
49
+ # Then check if table actually exists in the database
50
+ try:
51
+ if isinstance(db, AsyncBaseDb):
52
+ # For async databases, await the check
53
+ return await db.table_exists(table_name)
54
+ else:
55
+ # For sync databases, call directly
56
+ return db.table_exists(table_name)
57
+ except (NotImplementedError, AttributeError):
58
+ # If table_exists not implemented, fall back to configuration check
59
+ return is_configured
60
+
61
+ # If db_id is provided, first find the database with that ID
62
+ if db_id:
63
+ target_db_list = dbs.get(db_id)
64
+ if not target_db_list:
65
+ raise HTTPException(status_code=404, detail=f"No database found with id '{db_id}'")
66
+
67
+ # If table is also specified, search through all databases with this ID to find one with the table
68
+ if table:
69
+ for db in target_db_list:
70
+ if await _has_table(db, table):
71
+ return db
72
+ raise HTTPException(status_code=404, detail=f"No database with id '{db_id}' has table '{table}'")
73
+
74
+ # If no table specified, return the first database with this ID
75
+ return target_db_list[0]
76
+
77
+ # Raise if multiple databases are provided but no db_id is provided
78
+ if len(dbs) > 1:
79
+ raise HTTPException(
80
+ status_code=400, detail="The db_id query parameter is required when using multiple databases"
81
+ )
82
+
83
+ # Return the first (and only) database
84
+ return next(db for dbs in dbs.values() for db in dbs)
85
+
86
+
87
+ def get_knowledge_instance_by_db_id(knowledge_instances: List[Knowledge], db_id: Optional[str] = None) -> Knowledge:
88
+ """Return the knowledge instance with the given ID, or the first knowledge instance if no ID is provided."""
89
+ if not db_id and len(knowledge_instances) == 1:
90
+ return next(iter(knowledge_instances))
91
+
92
+ if not db_id:
93
+ raise HTTPException(
94
+ status_code=400, detail="The db_id query parameter is required when using multiple databases"
95
+ )
96
+
97
+ for knowledge in knowledge_instances:
98
+ if knowledge.contents_db and knowledge.contents_db.id == db_id:
99
+ return knowledge
100
+
101
+ raise HTTPException(status_code=404, detail=f"Knowledge instance with id '{db_id}' not found")
102
+
103
+
104
+ def get_run_input(run_dict: Dict[str, Any], is_workflow_run: bool = False) -> str:
105
+ """Get the run input from the given run dictionary
106
+
107
+ Uses the RunInput/TeamRunInput object which stores the original user input.
108
+ """
109
+
110
+ # For agent or team runs, use the stored input_content
111
+ if not is_workflow_run and run_dict.get("input") is not None:
112
+ input_data = run_dict.get("input")
113
+ if isinstance(input_data, dict) and input_data.get("input_content") is not None:
114
+ return stringify_input_content(input_data["input_content"])
115
+
116
+ if is_workflow_run:
117
+ # Check the input field directly
118
+ if run_dict.get("input") is not None:
119
+ input_value = run_dict.get("input")
120
+ return str(input_value)
121
+
122
+ # Check the step executor runs for fallback
123
+ step_executor_runs = run_dict.get("step_executor_runs", [])
124
+ if step_executor_runs:
125
+ for message in reversed(step_executor_runs[0].get("messages", [])):
126
+ if message.get("role") == "user":
127
+ return message.get("content", "")
128
+
129
+ # Final fallback: scan messages
130
+ if run_dict.get("messages") is not None:
131
+ for message in reversed(run_dict["messages"]):
132
+ if message.get("role") == "user":
133
+ return message.get("content", "")
134
+
135
+ return ""
136
+
137
+
138
+ def get_session_name(session: Dict[str, Any]) -> str:
139
+ """Get the session name from the given session dictionary"""
140
+
141
+ # If session_data.session_name is set, return that
142
+ session_data = session.get("session_data")
143
+ if session_data is not None and session_data.get("session_name") is not None:
144
+ return session_data["session_name"]
145
+
146
+ # Otherwise use the original user message
147
+ else:
148
+ runs = session.get("runs", []) or []
149
+
150
+ # For teams, identify the first Team run and avoid using the first member's run
151
+ if session.get("session_type") == "team":
152
+ run = None
153
+ for r in runs:
154
+ # If agent_id is not present, it's a team run
155
+ if not r.get("agent_id"):
156
+ run = r
157
+ break
158
+
159
+ # Fallback to first run if no team run found
160
+ if run is None and runs:
161
+ run = runs[0]
162
+
163
+ elif session.get("session_type") == "workflow":
164
+ try:
165
+ workflow_run = runs[0]
166
+ workflow_input = workflow_run.get("input")
167
+ if isinstance(workflow_input, str):
168
+ return workflow_input
169
+ elif isinstance(workflow_input, dict):
170
+ try:
171
+ import json
172
+
173
+ return json.dumps(workflow_input)
174
+ except (TypeError, ValueError):
175
+ pass
176
+
177
+ workflow_name = session.get("workflow_data", {}).get("name")
178
+ return f"New {workflow_name} Session" if workflow_name else ""
179
+ except (KeyError, IndexError, TypeError):
180
+ return ""
181
+
182
+ # For agents, use the first run
183
+ else:
184
+ run = runs[0] if runs else None
185
+
186
+ if run is None:
187
+ return ""
188
+
189
+ if not isinstance(run, dict):
190
+ run = run.to_dict()
191
+
192
+ if run and run.get("messages"):
193
+ for message in run["messages"]:
194
+ if message["role"] == "user":
195
+ return message["content"]
196
+ return ""
197
+
198
+
199
+ def extract_input_media(run_dict: Dict[str, Any]) -> Dict[str, Any]:
200
+ input_media: Dict[str, List[Any]] = {
201
+ "images": [],
202
+ "videos": [],
203
+ "audios": [],
204
+ "files": [],
205
+ }
206
+
207
+ input = run_dict.get("input", {})
208
+ input_media["images"].extend(input.get("images", []))
209
+ input_media["videos"].extend(input.get("videos", []))
210
+ input_media["audios"].extend(input.get("audios", []))
211
+ input_media["files"].extend(input.get("files", []))
212
+
213
+ return input_media
214
+
215
+
216
+ def process_image(file: UploadFile) -> Image:
217
+ content = file.file.read()
218
+ if not content:
219
+ raise HTTPException(status_code=400, detail="Empty file")
220
+ return Image(content=content, format=extract_format(file), mime_type=file.content_type)
221
+
222
+
223
+ def process_audio(file: UploadFile) -> Audio:
224
+ content = file.file.read()
225
+ if not content:
226
+ raise HTTPException(status_code=400, detail="Empty file")
227
+ return Audio(content=content, format=extract_format(file), mime_type=file.content_type)
228
+
229
+
230
+ def process_video(file: UploadFile) -> Video:
231
+ content = file.file.read()
232
+ if not content:
233
+ raise HTTPException(status_code=400, detail="Empty file")
234
+ return Video(content=content, format=extract_format(file), mime_type=file.content_type)
235
+
236
+
237
+ def process_document(file: UploadFile) -> Optional[FileMedia]:
238
+ try:
239
+ content = file.file.read()
240
+ if not content:
241
+ raise HTTPException(status_code=400, detail="Empty file")
242
+ return FileMedia(
243
+ content=content, filename=file.filename, format=extract_format(file), mime_type=file.content_type
244
+ )
245
+ except Exception as e:
246
+ logger.error(f"Error processing document {file.filename}: {e}")
247
+ return None
248
+
249
+
250
+ def extract_format(file: UploadFile) -> Optional[str]:
251
+ """Extract the File format from file name or content_type."""
252
+ # Get the format from the filename
253
+ if file.filename and "." in file.filename:
254
+ return file.filename.split(".")[-1].lower()
255
+
256
+ # Fallback to the file content_type
257
+ if file.content_type:
258
+ return file.content_type.strip().split("/")[-1]
259
+
260
+ return None
261
+
262
+
263
+ def format_tools(agent_tools: List[Union[Dict[str, Any], Toolkit, Function, Callable]]):
264
+ formatted_tools: List[Dict] = []
265
+ if agent_tools is not None:
266
+ for tool in agent_tools:
267
+ if isinstance(tool, dict):
268
+ formatted_tools.append(tool)
269
+ elif isinstance(tool, Toolkit):
270
+ for _, f in tool.functions.items():
271
+ formatted_tools.append(f.to_dict())
272
+ elif isinstance(tool, Function):
273
+ formatted_tools.append(tool.to_dict())
274
+ elif callable(tool):
275
+ func = Function.from_callable(tool)
276
+ formatted_tools.append(func.to_dict())
277
+ else:
278
+ logger.warning(f"Unknown tool type: {type(tool)}")
279
+ return formatted_tools
280
+
281
+
282
+ def format_team_tools(team_tools: List[Union[Function, dict]]):
283
+ formatted_tools: List[Dict] = []
284
+ if team_tools is not None:
285
+ for tool in team_tools:
286
+ if isinstance(tool, dict):
287
+ formatted_tools.append(tool)
288
+ elif isinstance(tool, Function):
289
+ formatted_tools.append(tool.to_dict())
290
+ return formatted_tools
291
+
292
+
293
+ def get_agent_by_id(agent_id: str, agents: Optional[List[Agent]] = None) -> Optional[Agent]:
294
+ if agent_id is None or agents is None:
295
+ return None
296
+
297
+ for agent in agents:
298
+ if agent.id == agent_id:
299
+ return agent
300
+ return None
301
+
302
+
303
+ def get_team_by_id(team_id: str, teams: Optional[List[Team]] = None) -> Optional[Team]:
304
+ if team_id is None or teams is None:
305
+ return None
306
+
307
+ for team in teams:
308
+ if team.id == team_id:
309
+ return team
310
+ return None
311
+
312
+
313
+ def get_workflow_by_id(workflow_id: str, workflows: Optional[List[Workflow]] = None) -> Optional[Workflow]:
314
+ if workflow_id is None or workflows is None:
315
+ return None
316
+
317
+ for workflow in workflows:
318
+ if workflow.id == workflow_id:
319
+ return workflow
320
+ return None
321
+
322
+
323
+ # INPUT SCHEMA VALIDATIONS
324
+
325
+
326
+ def get_agent_input_schema_dict(agent: Agent) -> Optional[Dict[str, Any]]:
327
+ """Get input schema as dictionary for API responses"""
328
+
329
+ if agent.input_schema is not None:
330
+ try:
331
+ return agent.input_schema.model_json_schema()
332
+ except Exception:
333
+ return None
334
+
335
+ return None
336
+
337
+
338
+ def get_team_input_schema_dict(team: Team) -> Optional[Dict[str, Any]]:
339
+ """Get input schema as dictionary for API responses"""
340
+
341
+ if team.input_schema is not None:
342
+ try:
343
+ return team.input_schema.model_json_schema()
344
+ except Exception:
345
+ return None
346
+
347
+ return None
348
+
349
+
350
+ def get_workflow_input_schema_dict(workflow: Workflow) -> Optional[Dict[str, Any]]:
351
+ """Get input schema as dictionary for API responses"""
352
+
353
+ # Priority 1: Explicit input_schema (Pydantic model)
354
+ if workflow.input_schema is not None:
355
+ try:
356
+ return workflow.input_schema.model_json_schema()
357
+ except Exception:
358
+ return None
359
+
360
+ # Priority 2: Auto-generate from custom kwargs
361
+ if workflow.steps and callable(workflow.steps):
362
+ custom_params = workflow.run_parameters
363
+ if custom_params and len(custom_params) > 1: # More than just 'message'
364
+ return _generate_schema_from_params(custom_params)
365
+
366
+ # Priority 3: No schema (expects string message)
367
+ return None
368
+
369
+
370
+ def _generate_schema_from_params(params: Dict[str, Any]) -> Dict[str, Any]:
371
+ """Convert function parameters to JSON schema"""
372
+ properties: Dict[str, Any] = {}
373
+ required: List[str] = []
374
+
375
+ for param_name, param_info in params.items():
376
+ # Skip the default 'message' parameter for custom kwargs workflows
377
+ if param_name == "message":
378
+ continue
379
+
380
+ # Map Python types to JSON schema types
381
+ param_type = param_info.get("annotation", "str")
382
+ default_value = param_info.get("default")
383
+ is_required = param_info.get("required", False)
384
+
385
+ # Convert Python type annotations to JSON schema types
386
+ if param_type == "str":
387
+ properties[param_name] = {"type": "string"}
388
+ elif param_type == "bool":
389
+ properties[param_name] = {"type": "boolean"}
390
+ elif param_type == "int":
391
+ properties[param_name] = {"type": "integer"}
392
+ elif param_type == "float":
393
+ properties[param_name] = {"type": "number"}
394
+ elif "List" in str(param_type):
395
+ properties[param_name] = {"type": "array", "items": {"type": "string"}}
396
+ else:
397
+ properties[param_name] = {"type": "string"} # fallback
398
+
399
+ # Add default value if present
400
+ if default_value is not None:
401
+ properties[param_name]["default"] = default_value
402
+
403
+ # Add to required if no default value
404
+ if is_required and default_value is None:
405
+ required.append(param_name)
406
+
407
+ schema = {"type": "object", "properties": properties}
408
+
409
+ if required:
410
+ schema["required"] = required
411
+
412
+ return schema
413
+
414
+
415
+ def update_cors_middleware(app: FastAPI, new_origins: list):
416
+ existing_origins: List[str] = []
417
+
418
+ # TODO: Allow more options where CORS is properly merged and user can disable this behaviour
419
+
420
+ # Extract existing origins from current CORS middleware
421
+ for middleware in app.user_middleware:
422
+ if middleware.cls == CORSMiddleware:
423
+ if hasattr(middleware, "kwargs"):
424
+ origins_value = middleware.kwargs.get("allow_origins", [])
425
+ if isinstance(origins_value, list):
426
+ existing_origins = origins_value
427
+ else:
428
+ existing_origins = []
429
+ break
430
+ # Merge origins
431
+ merged_origins = list(set(new_origins + existing_origins))
432
+ final_origins = [origin for origin in merged_origins if origin != "*"]
433
+
434
+ # Remove existing CORS
435
+ app.user_middleware = [m for m in app.user_middleware if m.cls != CORSMiddleware]
436
+ app.middleware_stack = None
437
+
438
+ # Add updated CORS
439
+ app.add_middleware(
440
+ CORSMiddleware, # type: ignore
441
+ allow_origins=final_origins,
442
+ allow_credentials=True,
443
+ allow_methods=["*"],
444
+ allow_headers=["*"],
445
+ expose_headers=["*"],
446
+ )
447
+
448
+
449
+ def get_existing_route_paths(fastapi_app: FastAPI) -> Dict[str, List[str]]:
450
+ """Get all existing route paths and methods from the FastAPI app.
451
+
452
+ Returns:
453
+ Dict[str, List[str]]: Dictionary mapping paths to list of HTTP methods
454
+ """
455
+ existing_paths: Dict[str, Any] = {}
456
+ for route in fastapi_app.routes:
457
+ if isinstance(route, APIRoute):
458
+ path = route.path
459
+ methods = list(route.methods) if route.methods else []
460
+ if path in existing_paths:
461
+ existing_paths[path].extend(methods)
462
+ else:
463
+ existing_paths[path] = methods
464
+ return existing_paths
465
+
466
+
467
+ def find_conflicting_routes(fastapi_app: FastAPI, router: APIRouter) -> List[Dict[str, Any]]:
468
+ """Find conflicting routes in the FastAPI app.
469
+
470
+ Args:
471
+ fastapi_app: The FastAPI app with all existing routes
472
+ router: The APIRouter to add
473
+
474
+ Returns:
475
+ List[Dict[str, Any]]: List of conflicting routes
476
+ """
477
+ existing_paths = get_existing_route_paths(fastapi_app)
478
+
479
+ conflicts = []
480
+
481
+ for route in router.routes:
482
+ if isinstance(route, APIRoute):
483
+ full_path = route.path
484
+ route_methods = list(route.methods) if route.methods else []
485
+
486
+ if full_path in existing_paths:
487
+ conflicting_methods: Set[str] = set(route_methods) & set(existing_paths[full_path])
488
+ if conflicting_methods:
489
+ conflicts.append({"path": full_path, "methods": list(conflicting_methods), "route": route})
490
+ return conflicts
491
+
492
+
493
+ def load_yaml_config(config_file_path: str) -> AgentOSConfig:
494
+ """Load a YAML config file and return the configuration as an AgentOSConfig instance."""
495
+ from pathlib import Path
496
+
497
+ import yaml
498
+
499
+ # Validate that the path points to a YAML file
500
+ path = Path(config_file_path)
501
+ if path.suffix.lower() not in [".yaml", ".yml"]:
502
+ raise ValueError(f"Config file must have a .yaml or .yml extension, got: {config_file_path}")
503
+
504
+ # Load the YAML file
505
+ with open(config_file_path, "r") as f:
506
+ return AgentOSConfig.model_validate(yaml.safe_load(f))
507
+
508
+
509
+ def collect_mcp_tools_from_team(team: Team, mcp_tools: List[Any]) -> None:
510
+ """Recursively collect MCP tools from a team and its members."""
511
+ # Check the team tools
512
+ if team.tools:
513
+ for tool in team.tools:
514
+ type_name = type(tool).__name__
515
+ if type_name in ("MCPTools", "MultiMCPTools"):
516
+ if tool not in mcp_tools:
517
+ mcp_tools.append(tool)
518
+
519
+ # Recursively check team members
520
+ if team.members:
521
+ for member in team.members:
522
+ if isinstance(member, Agent):
523
+ if member.tools:
524
+ for tool in member.tools:
525
+ type_name = type(tool).__name__
526
+ if type_name in ("MCPTools", "MultiMCPTools"):
527
+ if tool not in mcp_tools:
528
+ mcp_tools.append(tool)
529
+
530
+ elif isinstance(member, Team):
531
+ # Recursively check nested team
532
+ collect_mcp_tools_from_team(member, mcp_tools)
533
+
534
+
535
+ def collect_mcp_tools_from_workflow(workflow: Workflow, mcp_tools: List[Any]) -> None:
536
+ """Recursively collect MCP tools from a workflow and its steps."""
537
+ from agno.workflow.steps import Steps
538
+
539
+ # Recursively check workflow steps
540
+ if workflow.steps:
541
+ if isinstance(workflow.steps, list):
542
+ # Handle list of steps
543
+ for step in workflow.steps:
544
+ collect_mcp_tools_from_workflow_step(step, mcp_tools)
545
+
546
+ elif isinstance(workflow.steps, Steps):
547
+ # Handle Steps container
548
+ if steps := workflow.steps.steps:
549
+ for step in steps:
550
+ collect_mcp_tools_from_workflow_step(step, mcp_tools)
551
+
552
+ elif callable(workflow.steps):
553
+ pass
554
+
555
+
556
+ def collect_mcp_tools_from_workflow_step(step: Any, mcp_tools: List[Any]) -> None:
557
+ """Collect MCP tools from a single workflow step."""
558
+ from agno.workflow.condition import Condition
559
+ from agno.workflow.loop import Loop
560
+ from agno.workflow.parallel import Parallel
561
+ from agno.workflow.router import Router
562
+ from agno.workflow.step import Step
563
+ from agno.workflow.steps import Steps
564
+
565
+ if isinstance(step, Step):
566
+ # Check step's agent
567
+ if step.agent:
568
+ if step.agent.tools:
569
+ for tool in step.agent.tools:
570
+ type_name = type(tool).__name__
571
+ if type_name in ("MCPTools", "MultiMCPTools"):
572
+ if tool not in mcp_tools:
573
+ mcp_tools.append(tool)
574
+ # Check step's team
575
+ if step.team:
576
+ collect_mcp_tools_from_team(step.team, mcp_tools)
577
+
578
+ elif isinstance(step, Steps):
579
+ if steps := step.steps:
580
+ for step in steps:
581
+ collect_mcp_tools_from_workflow_step(step, mcp_tools)
582
+
583
+ elif isinstance(step, (Parallel, Loop, Condition, Router)):
584
+ # These contain other steps - recursively check them
585
+ if hasattr(step, "steps") and step.steps:
586
+ for sub_step in step.steps:
587
+ collect_mcp_tools_from_workflow_step(sub_step, mcp_tools)
588
+
589
+ elif isinstance(step, Agent):
590
+ # Direct agent in workflow steps
591
+ if step.tools:
592
+ for tool in step.tools:
593
+ type_name = type(tool).__name__
594
+ if type_name in ("MCPTools", "MultiMCPTools"):
595
+ if tool not in mcp_tools:
596
+ mcp_tools.append(tool)
597
+
598
+ elif isinstance(step, Team):
599
+ # Direct team in workflow steps
600
+ collect_mcp_tools_from_team(step, mcp_tools)
601
+
602
+ elif isinstance(step, Workflow):
603
+ # Nested workflow
604
+ collect_mcp_tools_from_workflow(step, mcp_tools)
605
+
606
+
607
+ def stringify_input_content(input_content: Union[str, Dict[str, Any], List[Any], BaseModel]) -> str:
608
+ """Convert any given input_content into its string representation.
609
+
610
+ This handles both serialized (dict) and live (object) input_content formats.
611
+ """
612
+ import json
613
+
614
+ if isinstance(input_content, str):
615
+ return input_content
616
+ elif isinstance(input_content, Message):
617
+ return json.dumps(input_content.to_dict())
618
+ elif isinstance(input_content, dict):
619
+ return json.dumps(input_content, indent=2, default=str)
620
+ elif isinstance(input_content, list):
621
+ if input_content:
622
+ # Handle live Message objects
623
+ if isinstance(input_content[0], Message):
624
+ return json.dumps([m.to_dict() for m in input_content])
625
+ # Handle serialized Message dicts
626
+ elif isinstance(input_content[0], dict) and input_content[0].get("role") == "user":
627
+ return input_content[0].get("content", str(input_content))
628
+ return str(input_content)
629
+ else:
630
+ return str(input_content)
agno/py.typed ADDED
File without changes
File without changes