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/app.py ADDED
@@ -0,0 +1,876 @@
1
+ from contextlib import asynccontextmanager
2
+ from functools import partial
3
+ from os import getenv
4
+ from typing import Any, Dict, List, Literal, Optional, Tuple, Union
5
+ from uuid import uuid4
6
+
7
+ from fastapi import APIRouter, FastAPI, HTTPException
8
+ from fastapi.responses import JSONResponse
9
+ from fastapi.routing import APIRoute
10
+ from rich import box
11
+ from rich.panel import Panel
12
+ from starlette.requests import Request
13
+
14
+ from agno.agent.agent import Agent
15
+ from agno.db.base import AsyncBaseDb, BaseDb
16
+ from agno.knowledge.knowledge import Knowledge
17
+ from agno.os.config import (
18
+ AgentOSConfig,
19
+ DatabaseConfig,
20
+ EvalsConfig,
21
+ EvalsDomainConfig,
22
+ KnowledgeConfig,
23
+ KnowledgeDomainConfig,
24
+ MemoryConfig,
25
+ MemoryDomainConfig,
26
+ MetricsConfig,
27
+ MetricsDomainConfig,
28
+ SessionConfig,
29
+ SessionDomainConfig,
30
+ )
31
+ from agno.os.interfaces.base import BaseInterface
32
+ from agno.os.router import get_base_router, get_websocket_router
33
+ from agno.os.routers.evals import get_eval_router
34
+ from agno.os.routers.health import get_health_router
35
+ from agno.os.routers.home import get_home_router
36
+ from agno.os.routers.knowledge import get_knowledge_router
37
+ from agno.os.routers.memory import get_memory_router
38
+ from agno.os.routers.metrics import get_metrics_router
39
+ from agno.os.routers.session import get_session_router
40
+ from agno.os.settings import AgnoAPISettings
41
+ from agno.os.utils import (
42
+ collect_mcp_tools_from_team,
43
+ collect_mcp_tools_from_workflow,
44
+ find_conflicting_routes,
45
+ load_yaml_config,
46
+ update_cors_middleware,
47
+ )
48
+ from agno.team.team import Team
49
+ from agno.utils.log import log_debug, log_error, log_warning
50
+ from agno.utils.string import generate_id, generate_id_from_name
51
+ from agno.workflow.workflow import Workflow
52
+
53
+
54
+ @asynccontextmanager
55
+ async def mcp_lifespan(_, mcp_tools):
56
+ """Manage MCP connection lifecycle inside a FastAPI app"""
57
+ # Startup logic: connect to all contextual MCP servers
58
+ for tool in mcp_tools:
59
+ await tool.connect()
60
+
61
+ yield
62
+
63
+ # Shutdown logic: Close all contextual MCP connections
64
+ for tool in mcp_tools:
65
+ await tool.close()
66
+
67
+
68
+ def _combine_app_lifespans(lifespans: list) -> Any:
69
+ """Combine multiple FastAPI app lifespan context managers into one."""
70
+ if len(lifespans) == 1:
71
+ return lifespans[0]
72
+
73
+ from contextlib import asynccontextmanager
74
+
75
+ @asynccontextmanager
76
+ async def combined_lifespan(app):
77
+ async def _run_nested(index: int):
78
+ if index >= len(lifespans):
79
+ yield
80
+ return
81
+
82
+ async with lifespans[index](app):
83
+ async for _ in _run_nested(index + 1):
84
+ yield
85
+
86
+ async for _ in _run_nested(0):
87
+ yield
88
+
89
+ return combined_lifespan
90
+
91
+
92
+ class AgentOS:
93
+ def __init__(
94
+ self,
95
+ id: Optional[str] = None,
96
+ name: Optional[str] = None,
97
+ description: Optional[str] = None,
98
+ version: Optional[str] = None,
99
+ agents: Optional[List[Agent]] = None,
100
+ teams: Optional[List[Team]] = None,
101
+ workflows: Optional[List[Workflow]] = None,
102
+ knowledge: Optional[List[Knowledge]] = None,
103
+ interfaces: Optional[List[BaseInterface]] = None,
104
+ a2a_interface: bool = False,
105
+ config: Optional[Union[str, AgentOSConfig]] = None,
106
+ settings: Optional[AgnoAPISettings] = None,
107
+ lifespan: Optional[Any] = None,
108
+ enable_mcp_server: bool = False,
109
+ base_app: Optional[FastAPI] = None,
110
+ on_route_conflict: Literal["preserve_agentos", "preserve_base_app", "error"] = "preserve_agentos",
111
+ telemetry: bool = True,
112
+ auto_provision_dbs: bool = True,
113
+ os_id: Optional[str] = None, # Deprecated
114
+ enable_mcp: bool = False, # Deprecated
115
+ fastapi_app: Optional[FastAPI] = None, # Deprecated
116
+ replace_routes: Optional[bool] = None, # Deprecated
117
+ ):
118
+ """Initialize AgentOS.
119
+
120
+ Args:
121
+ id: Unique identifier for this AgentOS instance
122
+ name: Name of the AgentOS instance
123
+ description: Description of the AgentOS instance
124
+ version: Version of the AgentOS instance
125
+ agents: List of agents to include in the OS
126
+ teams: List of teams to include in the OS
127
+ workflows: List of workflows to include in the OS
128
+ knowledge: List of knowledge bases to include in the OS
129
+ interfaces: List of interfaces to include in the OS
130
+ a2a_interface: Whether to expose the OS agents and teams in an A2A server
131
+ config: Configuration file path or AgentOSConfig instance
132
+ settings: API settings for the OS
133
+ lifespan: Optional lifespan context manager for the FastAPI app
134
+ enable_mcp_server: Whether to enable MCP (Model Context Protocol)
135
+ base_app: Optional base FastAPI app to use for the AgentOS. All routes and middleware will be added to this app.
136
+ on_route_conflict: What to do when a route conflict is detected in case a custom base_app is provided.
137
+ telemetry: Whether to enable telemetry
138
+
139
+ """
140
+ if not agents and not workflows and not teams and not knowledge:
141
+ raise ValueError("Either agents, teams, workflows or knowledge bases must be provided.")
142
+
143
+ self.config = load_yaml_config(config) if isinstance(config, str) else config
144
+
145
+ self.agents: Optional[List[Agent]] = agents
146
+ self.workflows: Optional[List[Workflow]] = workflows
147
+ self.teams: Optional[List[Team]] = teams
148
+ self.interfaces = interfaces or []
149
+ self.a2a_interface = a2a_interface
150
+ self.knowledge = knowledge
151
+ self.settings: AgnoAPISettings = settings or AgnoAPISettings()
152
+ self.auto_provision_dbs = auto_provision_dbs
153
+ self._app_set = False
154
+
155
+ if base_app:
156
+ self.base_app: Optional[FastAPI] = base_app
157
+ self._app_set = True
158
+ self.on_route_conflict = on_route_conflict
159
+ elif fastapi_app:
160
+ self.base_app = fastapi_app
161
+ self._app_set = True
162
+ if replace_routes is not None:
163
+ self.on_route_conflict = "preserve_agentos" if replace_routes else "preserve_base_app"
164
+ else:
165
+ self.on_route_conflict = on_route_conflict
166
+ else:
167
+ self.base_app = None
168
+ self._app_set = False
169
+ self.on_route_conflict = on_route_conflict
170
+
171
+ self.interfaces = interfaces or []
172
+
173
+ self.name = name
174
+
175
+ self.id = id or os_id
176
+ if not self.id:
177
+ self.id = generate_id(self.name) if self.name else str(uuid4())
178
+
179
+ self.version = version
180
+ self.description = description
181
+
182
+ self.telemetry = telemetry
183
+
184
+ self.enable_mcp_server = enable_mcp or enable_mcp_server
185
+ self.lifespan = lifespan
186
+
187
+ # List of all MCP tools used inside the AgentOS
188
+ self.mcp_tools: List[Any] = []
189
+ self._mcp_app: Optional[Any] = None
190
+
191
+ self._initialize_agents()
192
+ self._initialize_teams()
193
+ self._initialize_workflows()
194
+
195
+ if self.telemetry:
196
+ from agno.api.os import OSLaunch, log_os_telemetry
197
+
198
+ log_os_telemetry(launch=OSLaunch(os_id=self.id, data=self._get_telemetry_data()))
199
+
200
+ def _add_agent_os_to_lifespan_function(self, lifespan):
201
+ """
202
+ Inspect a lifespan function and wrap it to pass agent_os if it accepts it.
203
+
204
+ Returns:
205
+ A wrapped lifespan that passes agent_os if the lifespan function expects it.
206
+ """
207
+ # Getting the actual function inside the lifespan
208
+ lifespan_function = lifespan
209
+ if hasattr(lifespan, "__wrapped__"):
210
+ lifespan_function = lifespan.__wrapped__
211
+
212
+ try:
213
+ from inspect import signature
214
+
215
+ # Inspecting the lifespan function signature to find its parameters
216
+ sig = signature(lifespan_function)
217
+ params = list(sig.parameters.keys())
218
+
219
+ # If the lifespan function expects the 'agent_os' parameter, add it
220
+ if "agent_os" in params:
221
+ return partial(lifespan, agent_os=self)
222
+ else:
223
+ return lifespan
224
+
225
+ except (ValueError, TypeError):
226
+ return lifespan
227
+
228
+ def resync(self, app: FastAPI) -> None:
229
+ """Resync the AgentOS to discover, initialize and configure: agents, teams, workflows, databases and knowledge bases."""
230
+ self._initialize_agents()
231
+ self._initialize_teams()
232
+ self._initialize_workflows()
233
+ self._auto_discover_databases()
234
+ self._auto_discover_knowledge_instances()
235
+
236
+ if self.enable_mcp_server:
237
+ from agno.os.mcp import get_mcp_server
238
+
239
+ self._mcp_app = get_mcp_server(self)
240
+
241
+ self._reprovision_routers(app=app)
242
+
243
+ def _reprovision_routers(self, app: FastAPI) -> None:
244
+ """Re-provision all routes for the AgentOS."""
245
+ updated_routers = [
246
+ get_session_router(dbs=self.dbs),
247
+ get_metrics_router(dbs=self.dbs),
248
+ get_knowledge_router(knowledge_instances=self.knowledge_instances),
249
+ get_memory_router(dbs=self.dbs),
250
+ get_eval_router(dbs=self.dbs, agents=self.agents, teams=self.teams),
251
+ ]
252
+
253
+ # Clear all previously existing routes
254
+ app.router.routes = [
255
+ route
256
+ for route in app.router.routes
257
+ if hasattr(route, "path")
258
+ and route.path in ["/docs", "/redoc", "/openapi.json", "/docs/oauth2-redirect"]
259
+ or route.path.startswith("/mcp") # type: ignore
260
+ ]
261
+
262
+ # Add the built-in routes
263
+ self._add_built_in_routes(app=app)
264
+
265
+ # Add the updated routes
266
+ for router in updated_routers:
267
+ self._add_router(app, router)
268
+
269
+ # Mount MCP if needed
270
+ if self.enable_mcp_server and self._mcp_app:
271
+ app.mount("/", self._mcp_app)
272
+
273
+ def _add_built_in_routes(self, app: FastAPI) -> None:
274
+ """Add all AgentOSbuilt-in routes to the given app."""
275
+ # Add the home router if MCP server is not enabled
276
+ if not self.enable_mcp_server:
277
+ self._add_router(app, get_home_router(self))
278
+
279
+ self._add_router(app, get_health_router(health_endpoint="/health"))
280
+ self._add_router(app, get_base_router(self, settings=self.settings))
281
+ self._add_router(app, get_websocket_router(self, settings=self.settings))
282
+
283
+ # Add A2A interface if relevant
284
+ has_a2a_interface = False
285
+ for interface in self.interfaces:
286
+ if not has_a2a_interface and interface.__class__.__name__ == "A2A":
287
+ has_a2a_interface = True
288
+ interface_router = interface.get_router()
289
+ self._add_router(app, interface_router)
290
+ if self.a2a_interface and not has_a2a_interface:
291
+ from agno.os.interfaces.a2a import A2A
292
+
293
+ a2a_interface = A2A(agents=self.agents, teams=self.teams, workflows=self.workflows)
294
+ self.interfaces.append(a2a_interface)
295
+ self._add_router(app, a2a_interface.get_router())
296
+
297
+ def _make_app(self, lifespan: Optional[Any] = None) -> FastAPI:
298
+ # Adjust the FastAPI app lifespan to handle MCP connections if relevant
299
+ app_lifespan = lifespan
300
+ if self.mcp_tools is not None:
301
+ mcp_tools_lifespan = partial(mcp_lifespan, mcp_tools=self.mcp_tools)
302
+ # If there is already a lifespan, combine it with the MCP lifespan
303
+ if lifespan is not None:
304
+ # Combine both lifespans
305
+ @asynccontextmanager
306
+ async def combined_lifespan(app: FastAPI):
307
+ # Run both lifespans
308
+ async with lifespan(app): # type: ignore
309
+ async with mcp_tools_lifespan(app): # type: ignore
310
+ yield
311
+
312
+ app_lifespan = combined_lifespan
313
+ else:
314
+ app_lifespan = mcp_tools_lifespan
315
+
316
+ return FastAPI(
317
+ title=self.name or "Agno AgentOS",
318
+ version=self.version or "1.0.0",
319
+ description=self.description or "An agent operating system.",
320
+ docs_url="/docs" if self.settings.docs_enabled else None,
321
+ redoc_url="/redoc" if self.settings.docs_enabled else None,
322
+ openapi_url="/openapi.json" if self.settings.docs_enabled else None,
323
+ lifespan=app_lifespan,
324
+ )
325
+
326
+ def _initialize_agents(self) -> None:
327
+ """Initialize and configure all agents for AgentOS usage."""
328
+ if not self.agents:
329
+ return
330
+
331
+ for agent in self.agents:
332
+ # Track all MCP tools to later handle their connection
333
+ if agent.tools:
334
+ for tool in agent.tools:
335
+ # Checking if the tool is a MCPTools or MultiMCPTools instance
336
+ type_name = type(tool).__name__
337
+ if type_name in ("MCPTools", "MultiMCPTools"):
338
+ if tool not in self.mcp_tools:
339
+ self.mcp_tools.append(tool)
340
+
341
+ agent.initialize_agent()
342
+
343
+ # Required for the built-in routes to work
344
+ agent.store_events = True
345
+
346
+ def _initialize_teams(self) -> None:
347
+ """Initialize and configure all teams for AgentOS usage."""
348
+ if not self.teams:
349
+ return
350
+
351
+ for team in self.teams:
352
+ # Track all MCP tools recursively
353
+ collect_mcp_tools_from_team(team, self.mcp_tools)
354
+
355
+ team.initialize_team()
356
+
357
+ for member in team.members:
358
+ if isinstance(member, Agent):
359
+ member.team_id = None
360
+ member.initialize_agent()
361
+ elif isinstance(member, Team):
362
+ member.initialize_team()
363
+
364
+ # Required for the built-in routes to work
365
+ team.store_events = True
366
+
367
+ def _initialize_workflows(self) -> None:
368
+ """Initialize and configure all workflows for AgentOS usage."""
369
+ if not self.workflows:
370
+ return
371
+
372
+ if self.workflows:
373
+ for workflow in self.workflows:
374
+ # Track MCP tools recursively in workflow members
375
+ collect_mcp_tools_from_workflow(workflow, self.mcp_tools)
376
+
377
+ if not workflow.id:
378
+ workflow.id = generate_id_from_name(workflow.name)
379
+
380
+ # Required for the built-in routes to work
381
+ workflow.store_events = True
382
+
383
+ def get_app(self) -> FastAPI:
384
+ if self.base_app:
385
+ fastapi_app = self.base_app
386
+
387
+ # Initialize MCP server if enabled
388
+ if self.enable_mcp_server:
389
+ from agno.os.mcp import get_mcp_server
390
+
391
+ self._mcp_app = get_mcp_server(self)
392
+
393
+ # Collect all lifespans that need to be combined
394
+ lifespans = []
395
+
396
+ # The user provided lifespan
397
+ if self.lifespan:
398
+ # Wrap the user lifespan with agent_os parameter
399
+ wrapped_lifespan = self._add_agent_os_to_lifespan_function(self.lifespan)
400
+ lifespans.append(wrapped_lifespan)
401
+
402
+ # The provided app's existing lifespan
403
+ if fastapi_app.router.lifespan_context:
404
+ lifespans.append(fastapi_app.router.lifespan_context)
405
+
406
+ # The MCP tools lifespan
407
+ if self.mcp_tools:
408
+ lifespans.append(partial(mcp_lifespan, mcp_tools=self.mcp_tools))
409
+
410
+ # The /mcp server lifespan
411
+ if self.enable_mcp_server and self._mcp_app:
412
+ lifespans.append(self._mcp_app.lifespan)
413
+
414
+ # Combine lifespans and set them in the app
415
+ if lifespans:
416
+ fastapi_app.router.lifespan_context = _combine_app_lifespans(lifespans)
417
+
418
+ else:
419
+ if self.enable_mcp_server:
420
+ from contextlib import asynccontextmanager
421
+
422
+ from agno.os.mcp import get_mcp_server
423
+
424
+ self._mcp_app = get_mcp_server(self)
425
+
426
+ final_lifespan = self._mcp_app.lifespan # type: ignore
427
+ if self.lifespan is not None:
428
+ # Wrap the user lifespan with agent_os parameter
429
+ wrapped_lifespan = self._add_agent_os_to_lifespan_function(self.lifespan)
430
+
431
+ # Combine both lifespans
432
+ @asynccontextmanager
433
+ async def combined_lifespan(app: FastAPI):
434
+ # Run both lifespans
435
+ async with wrapped_lifespan(app): # type: ignore
436
+ async with self._mcp_app.lifespan(app): # type: ignore
437
+ yield
438
+
439
+ final_lifespan = combined_lifespan # type: ignore
440
+
441
+ fastapi_app = self._make_app(lifespan=final_lifespan)
442
+ else:
443
+ # Wrap the user lifespan with agent_os parameter
444
+ wrapped_user_lifespan = None
445
+ if self.lifespan is not None:
446
+ wrapped_user_lifespan = self._add_agent_os_to_lifespan_function(self.lifespan)
447
+
448
+ fastapi_app = self._make_app(lifespan=wrapped_user_lifespan)
449
+
450
+ self._add_built_in_routes(app=fastapi_app)
451
+
452
+ self._auto_discover_databases()
453
+ self._auto_discover_knowledge_instances()
454
+
455
+ routers = [
456
+ get_session_router(dbs=self.dbs),
457
+ get_memory_router(dbs=self.dbs),
458
+ get_eval_router(dbs=self.dbs, agents=self.agents, teams=self.teams),
459
+ get_metrics_router(dbs=self.dbs),
460
+ get_knowledge_router(knowledge_instances=self.knowledge_instances),
461
+ ]
462
+
463
+ for router in routers:
464
+ self._add_router(fastapi_app, router)
465
+
466
+ # Mount MCP if needed
467
+ if self.enable_mcp_server and self._mcp_app:
468
+ fastapi_app.mount("/", self._mcp_app)
469
+
470
+ if not self._app_set:
471
+
472
+ @fastapi_app.exception_handler(HTTPException)
473
+ async def http_exception_handler(_, exc: HTTPException) -> JSONResponse:
474
+ log_error(f"HTTP exception: {exc.status_code} {exc.detail}")
475
+ return JSONResponse(
476
+ status_code=exc.status_code,
477
+ content={"detail": str(exc.detail)},
478
+ )
479
+
480
+ @fastapi_app.exception_handler(Exception)
481
+ async def general_exception_handler(_: Request, exc: Exception) -> JSONResponse:
482
+ import traceback
483
+
484
+ log_error(f"Unhandled exception:\n{traceback.format_exc(limit=5)}")
485
+
486
+ return JSONResponse(
487
+ status_code=getattr(exc, "status_code", 500),
488
+ content={"detail": str(exc)},
489
+ )
490
+
491
+ # Update CORS middleware
492
+ update_cors_middleware(fastapi_app, self.settings.cors_origin_list) # type: ignore
493
+
494
+ return fastapi_app
495
+
496
+ def get_routes(self) -> List[Any]:
497
+ """Retrieve all routes from the FastAPI app.
498
+
499
+ Returns:
500
+ List[Any]: List of routes included in the FastAPI app.
501
+ """
502
+ app = self.get_app()
503
+
504
+ return app.routes
505
+
506
+ def _add_router(self, fastapi_app: FastAPI, router: APIRouter) -> None:
507
+ """Add a router to the FastAPI app, avoiding route conflicts.
508
+
509
+ Args:
510
+ router: The APIRouter to add
511
+ """
512
+
513
+ conflicts = find_conflicting_routes(fastapi_app, router)
514
+ conflicting_routes = [conflict["route"] for conflict in conflicts]
515
+
516
+ if conflicts and self._app_set:
517
+ if self.on_route_conflict == "preserve_base_app":
518
+ # Skip conflicting AgentOS routes, prefer user's existing routes
519
+ for conflict in conflicts:
520
+ methods_str = ", ".join(conflict["methods"]) # type: ignore
521
+ log_debug(
522
+ f"Skipping conflicting AgentOS route: {methods_str} {conflict['path']} - "
523
+ f"Using existing custom route instead"
524
+ )
525
+
526
+ # Create a new router without the conflicting routes
527
+ filtered_router = APIRouter()
528
+ for route in router.routes:
529
+ if route not in conflicting_routes:
530
+ filtered_router.routes.append(route)
531
+
532
+ # Use the filtered router if it has any routes left
533
+ if filtered_router.routes:
534
+ fastapi_app.include_router(filtered_router)
535
+
536
+ elif self.on_route_conflict == "preserve_agentos":
537
+ # Log warnings but still add all routes (AgentOS routes will override)
538
+ for conflict in conflicts:
539
+ methods_str = ", ".join(conflict["methods"]) # type: ignore
540
+ log_warning(
541
+ f"Route conflict detected: {methods_str} {conflict['path']} - "
542
+ f"AgentOS route will override existing custom route"
543
+ )
544
+
545
+ # Remove conflicting routes
546
+ for route in fastapi_app.routes:
547
+ for conflict in conflicts:
548
+ if isinstance(route, APIRoute):
549
+ if route.path == conflict["path"] and list(route.methods) == list(conflict["methods"]): # type: ignore
550
+ fastapi_app.routes.pop(fastapi_app.routes.index(route))
551
+
552
+ fastapi_app.include_router(router)
553
+
554
+ elif self.on_route_conflict == "error":
555
+ conflicting_paths = [conflict["path"] for conflict in conflicts]
556
+ raise ValueError(f"Route conflict detected: {conflicting_paths}")
557
+
558
+ else:
559
+ # No conflicts, add router normally
560
+ fastapi_app.include_router(router)
561
+
562
+ def _get_telemetry_data(self) -> Dict[str, Any]:
563
+ """Get the telemetry data for the OS"""
564
+ return {
565
+ "agents": [agent.id for agent in self.agents] if self.agents else None,
566
+ "teams": [team.id for team in self.teams] if self.teams else None,
567
+ "workflows": [workflow.id for workflow in self.workflows] if self.workflows else None,
568
+ "interfaces": [interface.type for interface in self.interfaces] if self.interfaces else None,
569
+ }
570
+
571
+ def _auto_discover_databases(self) -> None:
572
+ """Auto-discover and initialize the databases used by all contextual agents, teams and workflows."""
573
+
574
+ dbs: Dict[str, List[Union[BaseDb, AsyncBaseDb]]] = {}
575
+ knowledge_dbs: Dict[
576
+ str, List[Union[BaseDb, AsyncBaseDb]]
577
+ ] = {} # Track databases specifically used for knowledge
578
+
579
+ for agent in self.agents or []:
580
+ if agent.db:
581
+ self._register_db_with_validation(dbs, agent.db)
582
+ if agent.knowledge and agent.knowledge.contents_db:
583
+ self._register_db_with_validation(knowledge_dbs, agent.knowledge.contents_db)
584
+
585
+ for team in self.teams or []:
586
+ if team.db:
587
+ self._register_db_with_validation(dbs, team.db)
588
+ if team.knowledge and team.knowledge.contents_db:
589
+ self._register_db_with_validation(knowledge_dbs, team.knowledge.contents_db)
590
+
591
+ for workflow in self.workflows or []:
592
+ if workflow.db:
593
+ self._register_db_with_validation(dbs, workflow.db)
594
+
595
+ for knowledge_base in self.knowledge or []:
596
+ if knowledge_base.contents_db:
597
+ self._register_db_with_validation(knowledge_dbs, knowledge_base.contents_db)
598
+
599
+ for interface in self.interfaces or []:
600
+ if interface.agent and interface.agent.db:
601
+ self._register_db_with_validation(dbs, interface.agent.db)
602
+ elif interface.team and interface.team.db:
603
+ self._register_db_with_validation(dbs, interface.team.db)
604
+
605
+ self.dbs = dbs
606
+ self.knowledge_dbs = knowledge_dbs
607
+
608
+ # Initialize/scaffold all discovered databases
609
+ if self.auto_provision_dbs:
610
+ import asyncio
611
+ import concurrent.futures
612
+
613
+ try:
614
+ # If we're already in an event loop, run in a separate thread
615
+ asyncio.get_running_loop()
616
+
617
+ def run_in_new_loop():
618
+ new_loop = asyncio.new_event_loop()
619
+ asyncio.set_event_loop(new_loop)
620
+ try:
621
+ return new_loop.run_until_complete(self._initialize_databases())
622
+ finally:
623
+ new_loop.close()
624
+
625
+ with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
626
+ future = executor.submit(run_in_new_loop)
627
+ future.result() # Wait for completion
628
+
629
+ except RuntimeError:
630
+ # No event loop running, use asyncio.run
631
+ asyncio.run(self._initialize_databases())
632
+
633
+ async def _initialize_databases(self) -> None:
634
+ """Initialize all discovered databases and create all Agno tables that don't exist yet."""
635
+ from itertools import chain
636
+
637
+ # Collect all database instances and remove duplicates by identity
638
+ unique_dbs = list(
639
+ {
640
+ id(db): db
641
+ for db in chain(
642
+ chain.from_iterable(self.dbs.values()), chain.from_iterable(self.knowledge_dbs.values())
643
+ )
644
+ }.values()
645
+ )
646
+
647
+ # Separate sync and async databases
648
+ sync_dbs: List[Tuple[str, BaseDb]] = []
649
+ async_dbs: List[Tuple[str, AsyncBaseDb]] = []
650
+
651
+ for db in unique_dbs:
652
+ target = async_dbs if isinstance(db, AsyncBaseDb) else sync_dbs
653
+ target.append((db.id, db)) # type: ignore
654
+
655
+ # Initialize sync databases
656
+ for db_id, db in sync_dbs:
657
+ try:
658
+ if hasattr(db, "_create_all_tables") and callable(getattr(db, "_create_all_tables")):
659
+ db._create_all_tables()
660
+ else:
661
+ log_debug(f"No table initialization needed for {db.__class__.__name__}")
662
+
663
+ except Exception as e:
664
+ log_warning(f"Failed to initialize {db.__class__.__name__} (id: {db_id}): {e}")
665
+
666
+ # Initialize async databases
667
+ for db_id, db in async_dbs:
668
+ try:
669
+ log_debug(f"Initializing async {db.__class__.__name__} (id: {db_id})")
670
+
671
+ if hasattr(db, "_create_all_tables") and callable(getattr(db, "_create_all_tables")):
672
+ await db._create_all_tables()
673
+ else:
674
+ log_debug(f"No table initialization needed for async {db.__class__.__name__}")
675
+
676
+ except Exception as e:
677
+ log_warning(f"Failed to initialize async database {db.__class__.__name__} (id: {db_id}): {e}")
678
+
679
+ def _get_db_table_names(self, db: BaseDb) -> Dict[str, str]:
680
+ """Get the table names for a database"""
681
+ table_names = {
682
+ "session_table_name": db.session_table_name,
683
+ "culture_table_name": db.culture_table_name,
684
+ "memory_table_name": db.memory_table_name,
685
+ "metrics_table_name": db.metrics_table_name,
686
+ "evals_table_name": db.eval_table_name,
687
+ "knowledge_table_name": db.knowledge_table_name,
688
+ }
689
+ return {k: v for k, v in table_names.items() if v is not None}
690
+
691
+ def _register_db_with_validation(
692
+ self, registered_dbs: Dict[str, List[Union[BaseDb, AsyncBaseDb]]], db: Union[BaseDb, AsyncBaseDb]
693
+ ) -> None:
694
+ """Register a database in the contextual OS after validating it is not conflicting with registered databases"""
695
+ if db.id in registered_dbs:
696
+ registered_dbs[db.id].append(db)
697
+ else:
698
+ registered_dbs[db.id] = [db]
699
+
700
+ def _auto_discover_knowledge_instances(self) -> None:
701
+ """Auto-discover the knowledge instances used by all contextual agents, teams and workflows."""
702
+ seen_ids = set()
703
+ knowledge_instances: List[Knowledge] = []
704
+
705
+ def _add_knowledge_if_not_duplicate(knowledge: "Knowledge") -> None:
706
+ """Add knowledge instance if it's not already in the list (by object identity or db_id)."""
707
+ # Use database ID if available, otherwise use object ID as fallback
708
+ if not knowledge.contents_db:
709
+ return
710
+ if knowledge.contents_db.id in seen_ids:
711
+ return
712
+ seen_ids.add(knowledge.contents_db.id)
713
+ knowledge_instances.append(knowledge)
714
+
715
+ for agent in self.agents or []:
716
+ if agent.knowledge:
717
+ _add_knowledge_if_not_duplicate(agent.knowledge)
718
+
719
+ for team in self.teams or []:
720
+ if team.knowledge:
721
+ _add_knowledge_if_not_duplicate(team.knowledge)
722
+
723
+ for knowledge_base in self.knowledge or []:
724
+ _add_knowledge_if_not_duplicate(knowledge_base)
725
+
726
+ self.knowledge_instances = knowledge_instances
727
+
728
+ def _get_session_config(self) -> SessionConfig:
729
+ session_config = self.config.session if self.config and self.config.session else SessionConfig()
730
+
731
+ if session_config.dbs is None:
732
+ session_config.dbs = []
733
+
734
+ dbs_with_specific_config = [db.db_id for db in session_config.dbs]
735
+ for db_id, dbs in self.dbs.items():
736
+ if db_id not in dbs_with_specific_config:
737
+ # Collect unique table names from all databases with the same id
738
+ unique_tables = list(set(db.session_table_name for db in dbs))
739
+ session_config.dbs.append(
740
+ DatabaseConfig(
741
+ db_id=db_id,
742
+ domain_config=SessionDomainConfig(display_name=db_id),
743
+ tables=unique_tables,
744
+ )
745
+ )
746
+
747
+ return session_config
748
+
749
+ def _get_memory_config(self) -> MemoryConfig:
750
+ memory_config = self.config.memory if self.config and self.config.memory else MemoryConfig()
751
+
752
+ if memory_config.dbs is None:
753
+ memory_config.dbs = []
754
+
755
+ dbs_with_specific_config = [db.db_id for db in memory_config.dbs]
756
+
757
+ for db_id, dbs in self.dbs.items():
758
+ if db_id not in dbs_with_specific_config:
759
+ # Collect unique table names from all databases with the same id
760
+ unique_tables = list(set(db.memory_table_name for db in dbs))
761
+ memory_config.dbs.append(
762
+ DatabaseConfig(
763
+ db_id=db_id,
764
+ domain_config=MemoryDomainConfig(display_name=db_id),
765
+ tables=unique_tables,
766
+ )
767
+ )
768
+
769
+ return memory_config
770
+
771
+ def _get_knowledge_config(self) -> KnowledgeConfig:
772
+ knowledge_config = self.config.knowledge if self.config and self.config.knowledge else KnowledgeConfig()
773
+
774
+ if knowledge_config.dbs is None:
775
+ knowledge_config.dbs = []
776
+
777
+ dbs_with_specific_config = [db.db_id for db in knowledge_config.dbs]
778
+
779
+ # Only add databases that are actually used for knowledge contents
780
+ for db_id in self.knowledge_dbs.keys():
781
+ if db_id not in dbs_with_specific_config:
782
+ knowledge_config.dbs.append(
783
+ DatabaseConfig(
784
+ db_id=db_id,
785
+ domain_config=KnowledgeDomainConfig(display_name=db_id),
786
+ )
787
+ )
788
+
789
+ return knowledge_config
790
+
791
+ def _get_metrics_config(self) -> MetricsConfig:
792
+ metrics_config = self.config.metrics if self.config and self.config.metrics else MetricsConfig()
793
+
794
+ if metrics_config.dbs is None:
795
+ metrics_config.dbs = []
796
+
797
+ dbs_with_specific_config = [db.db_id for db in metrics_config.dbs]
798
+
799
+ for db_id, dbs in self.dbs.items():
800
+ if db_id not in dbs_with_specific_config:
801
+ # Collect unique table names from all databases with the same id
802
+ unique_tables = list(set(db.metrics_table_name for db in dbs))
803
+ metrics_config.dbs.append(
804
+ DatabaseConfig(
805
+ db_id=db_id,
806
+ domain_config=MetricsDomainConfig(display_name=db_id),
807
+ tables=unique_tables,
808
+ )
809
+ )
810
+
811
+ return metrics_config
812
+
813
+ def _get_evals_config(self) -> EvalsConfig:
814
+ evals_config = self.config.evals if self.config and self.config.evals else EvalsConfig()
815
+
816
+ if evals_config.dbs is None:
817
+ evals_config.dbs = []
818
+
819
+ dbs_with_specific_config = [db.db_id for db in evals_config.dbs]
820
+
821
+ for db_id, dbs in self.dbs.items():
822
+ if db_id not in dbs_with_specific_config:
823
+ # Collect unique table names from all databases with the same id
824
+ unique_tables = list(set(db.eval_table_name for db in dbs))
825
+ evals_config.dbs.append(
826
+ DatabaseConfig(
827
+ db_id=db_id,
828
+ domain_config=EvalsDomainConfig(display_name=db_id),
829
+ tables=unique_tables,
830
+ )
831
+ )
832
+
833
+ return evals_config
834
+
835
+ def serve(
836
+ self,
837
+ app: Union[str, FastAPI],
838
+ *,
839
+ host: str = "localhost",
840
+ port: int = 7777,
841
+ reload: bool = False,
842
+ workers: Optional[int] = None,
843
+ access_log: bool = False,
844
+ **kwargs,
845
+ ):
846
+ import uvicorn
847
+
848
+ if getenv("AGNO_API_RUNTIME", "").lower() == "stg":
849
+ public_endpoint = "https://os-stg.agno.com/"
850
+ else:
851
+ public_endpoint = "https://os.agno.com/"
852
+
853
+ # Create a terminal panel to announce OS initialization and provide useful info
854
+ from rich.align import Align
855
+ from rich.console import Console, Group
856
+
857
+ panel_group = [
858
+ Align.center(f"[bold cyan]{public_endpoint}[/bold cyan]"),
859
+ Align.center(f"\n\n[bold dark_orange]OS running on:[/bold dark_orange] http://{host}:{port}"),
860
+ ]
861
+ if bool(self.settings.os_security_key):
862
+ panel_group.append(Align.center("\n\n[bold chartreuse3]:lock: Security Enabled[/bold chartreuse3]"))
863
+
864
+ console = Console()
865
+ console.print(
866
+ Panel(
867
+ Group(*panel_group),
868
+ title="AgentOS",
869
+ expand=False,
870
+ border_style="dark_orange",
871
+ box=box.DOUBLE_EDGE,
872
+ padding=(2, 2),
873
+ )
874
+ )
875
+
876
+ uvicorn.run(app=app, host=host, port=port, reload=reload, workers=workers, access_log=access_log, **kwargs)