agno 0.1.2__py3-none-any.whl → 2.3.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (723) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +44 -5
  3. agno/agent/agent.py +10531 -2975
  4. agno/api/agent.py +14 -53
  5. agno/api/api.py +7 -46
  6. agno/api/evals.py +22 -0
  7. agno/api/os.py +17 -0
  8. agno/api/routes.py +6 -25
  9. agno/api/schemas/__init__.py +9 -0
  10. agno/api/schemas/agent.py +6 -9
  11. agno/api/schemas/evals.py +16 -0
  12. agno/api/schemas/os.py +14 -0
  13. agno/api/schemas/team.py +10 -10
  14. agno/api/schemas/utils.py +21 -0
  15. agno/api/schemas/workflows.py +16 -0
  16. agno/api/settings.py +53 -0
  17. agno/api/team.py +22 -26
  18. agno/api/workflow.py +28 -0
  19. agno/cloud/aws/base.py +214 -0
  20. agno/cloud/aws/s3/__init__.py +2 -0
  21. agno/cloud/aws/s3/api_client.py +43 -0
  22. agno/cloud/aws/s3/bucket.py +195 -0
  23. agno/cloud/aws/s3/object.py +57 -0
  24. agno/compression/__init__.py +3 -0
  25. agno/compression/manager.py +247 -0
  26. agno/culture/__init__.py +3 -0
  27. agno/culture/manager.py +956 -0
  28. agno/db/__init__.py +24 -0
  29. agno/db/async_postgres/__init__.py +3 -0
  30. agno/db/base.py +946 -0
  31. agno/db/dynamo/__init__.py +3 -0
  32. agno/db/dynamo/dynamo.py +2781 -0
  33. agno/db/dynamo/schemas.py +442 -0
  34. agno/db/dynamo/utils.py +743 -0
  35. agno/db/firestore/__init__.py +3 -0
  36. agno/db/firestore/firestore.py +2379 -0
  37. agno/db/firestore/schemas.py +181 -0
  38. agno/db/firestore/utils.py +376 -0
  39. agno/db/gcs_json/__init__.py +3 -0
  40. agno/db/gcs_json/gcs_json_db.py +1791 -0
  41. agno/db/gcs_json/utils.py +228 -0
  42. agno/db/in_memory/__init__.py +3 -0
  43. agno/db/in_memory/in_memory_db.py +1312 -0
  44. agno/db/in_memory/utils.py +230 -0
  45. agno/db/json/__init__.py +3 -0
  46. agno/db/json/json_db.py +1777 -0
  47. agno/db/json/utils.py +230 -0
  48. agno/db/migrations/manager.py +199 -0
  49. agno/db/migrations/v1_to_v2.py +635 -0
  50. agno/db/migrations/versions/v2_3_0.py +938 -0
  51. agno/db/mongo/__init__.py +17 -0
  52. agno/db/mongo/async_mongo.py +2760 -0
  53. agno/db/mongo/mongo.py +2597 -0
  54. agno/db/mongo/schemas.py +119 -0
  55. agno/db/mongo/utils.py +276 -0
  56. agno/db/mysql/__init__.py +4 -0
  57. agno/db/mysql/async_mysql.py +2912 -0
  58. agno/db/mysql/mysql.py +2923 -0
  59. agno/db/mysql/schemas.py +186 -0
  60. agno/db/mysql/utils.py +488 -0
  61. agno/db/postgres/__init__.py +4 -0
  62. agno/db/postgres/async_postgres.py +2579 -0
  63. agno/db/postgres/postgres.py +2870 -0
  64. agno/db/postgres/schemas.py +187 -0
  65. agno/db/postgres/utils.py +442 -0
  66. agno/db/redis/__init__.py +3 -0
  67. agno/db/redis/redis.py +2141 -0
  68. agno/db/redis/schemas.py +159 -0
  69. agno/db/redis/utils.py +346 -0
  70. agno/db/schemas/__init__.py +4 -0
  71. agno/db/schemas/culture.py +120 -0
  72. agno/db/schemas/evals.py +34 -0
  73. agno/db/schemas/knowledge.py +40 -0
  74. agno/db/schemas/memory.py +61 -0
  75. agno/db/singlestore/__init__.py +3 -0
  76. agno/db/singlestore/schemas.py +179 -0
  77. agno/db/singlestore/singlestore.py +2877 -0
  78. agno/db/singlestore/utils.py +384 -0
  79. agno/db/sqlite/__init__.py +4 -0
  80. agno/db/sqlite/async_sqlite.py +2911 -0
  81. agno/db/sqlite/schemas.py +181 -0
  82. agno/db/sqlite/sqlite.py +2908 -0
  83. agno/db/sqlite/utils.py +429 -0
  84. agno/db/surrealdb/__init__.py +3 -0
  85. agno/db/surrealdb/metrics.py +292 -0
  86. agno/db/surrealdb/models.py +334 -0
  87. agno/db/surrealdb/queries.py +71 -0
  88. agno/db/surrealdb/surrealdb.py +1908 -0
  89. agno/db/surrealdb/utils.py +147 -0
  90. agno/db/utils.py +118 -0
  91. agno/eval/__init__.py +24 -0
  92. agno/eval/accuracy.py +666 -276
  93. agno/eval/agent_as_judge.py +861 -0
  94. agno/eval/base.py +29 -0
  95. agno/eval/performance.py +779 -0
  96. agno/eval/reliability.py +241 -62
  97. agno/eval/utils.py +120 -0
  98. agno/exceptions.py +143 -1
  99. agno/filters.py +354 -0
  100. agno/guardrails/__init__.py +6 -0
  101. agno/guardrails/base.py +19 -0
  102. agno/guardrails/openai.py +144 -0
  103. agno/guardrails/pii.py +94 -0
  104. agno/guardrails/prompt_injection.py +52 -0
  105. agno/hooks/__init__.py +3 -0
  106. agno/hooks/decorator.py +164 -0
  107. agno/integrations/discord/__init__.py +3 -0
  108. agno/integrations/discord/client.py +203 -0
  109. agno/knowledge/__init__.py +5 -1
  110. agno/{document → knowledge}/chunking/agentic.py +22 -14
  111. agno/{document → knowledge}/chunking/document.py +2 -2
  112. agno/{document → knowledge}/chunking/fixed.py +7 -6
  113. agno/knowledge/chunking/markdown.py +151 -0
  114. agno/{document → knowledge}/chunking/recursive.py +15 -3
  115. agno/knowledge/chunking/row.py +39 -0
  116. agno/knowledge/chunking/semantic.py +91 -0
  117. agno/knowledge/chunking/strategy.py +165 -0
  118. agno/knowledge/content.py +74 -0
  119. agno/knowledge/document/__init__.py +5 -0
  120. agno/{document → knowledge/document}/base.py +12 -2
  121. agno/knowledge/embedder/__init__.py +5 -0
  122. agno/knowledge/embedder/aws_bedrock.py +343 -0
  123. agno/knowledge/embedder/azure_openai.py +210 -0
  124. agno/{embedder → knowledge/embedder}/base.py +8 -0
  125. agno/knowledge/embedder/cohere.py +323 -0
  126. agno/knowledge/embedder/fastembed.py +62 -0
  127. agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
  128. agno/knowledge/embedder/google.py +258 -0
  129. agno/knowledge/embedder/huggingface.py +94 -0
  130. agno/knowledge/embedder/jina.py +182 -0
  131. agno/knowledge/embedder/langdb.py +22 -0
  132. agno/knowledge/embedder/mistral.py +206 -0
  133. agno/knowledge/embedder/nebius.py +13 -0
  134. agno/knowledge/embedder/ollama.py +154 -0
  135. agno/knowledge/embedder/openai.py +195 -0
  136. agno/knowledge/embedder/sentence_transformer.py +63 -0
  137. agno/{embedder → knowledge/embedder}/together.py +1 -1
  138. agno/knowledge/embedder/vllm.py +262 -0
  139. agno/knowledge/embedder/voyageai.py +165 -0
  140. agno/knowledge/knowledge.py +3006 -0
  141. agno/knowledge/reader/__init__.py +7 -0
  142. agno/knowledge/reader/arxiv_reader.py +81 -0
  143. agno/knowledge/reader/base.py +95 -0
  144. agno/knowledge/reader/csv_reader.py +164 -0
  145. agno/knowledge/reader/docx_reader.py +82 -0
  146. agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
  147. agno/knowledge/reader/firecrawl_reader.py +201 -0
  148. agno/knowledge/reader/json_reader.py +88 -0
  149. agno/knowledge/reader/markdown_reader.py +137 -0
  150. agno/knowledge/reader/pdf_reader.py +431 -0
  151. agno/knowledge/reader/pptx_reader.py +101 -0
  152. agno/knowledge/reader/reader_factory.py +313 -0
  153. agno/knowledge/reader/s3_reader.py +89 -0
  154. agno/knowledge/reader/tavily_reader.py +193 -0
  155. agno/knowledge/reader/text_reader.py +127 -0
  156. agno/knowledge/reader/web_search_reader.py +325 -0
  157. agno/knowledge/reader/website_reader.py +455 -0
  158. agno/knowledge/reader/wikipedia_reader.py +91 -0
  159. agno/knowledge/reader/youtube_reader.py +78 -0
  160. agno/knowledge/remote_content/remote_content.py +88 -0
  161. agno/knowledge/reranker/__init__.py +3 -0
  162. agno/{reranker → knowledge/reranker}/base.py +1 -1
  163. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  164. agno/knowledge/reranker/infinity.py +195 -0
  165. agno/knowledge/reranker/sentence_transformer.py +54 -0
  166. agno/knowledge/types.py +39 -0
  167. agno/knowledge/utils.py +234 -0
  168. agno/media.py +439 -95
  169. agno/memory/__init__.py +16 -3
  170. agno/memory/manager.py +1474 -123
  171. agno/memory/strategies/__init__.py +15 -0
  172. agno/memory/strategies/base.py +66 -0
  173. agno/memory/strategies/summarize.py +196 -0
  174. agno/memory/strategies/types.py +37 -0
  175. agno/models/aimlapi/__init__.py +5 -0
  176. agno/models/aimlapi/aimlapi.py +62 -0
  177. agno/models/anthropic/__init__.py +4 -0
  178. agno/models/anthropic/claude.py +960 -496
  179. agno/models/aws/__init__.py +15 -0
  180. agno/models/aws/bedrock.py +686 -451
  181. agno/models/aws/claude.py +190 -183
  182. agno/models/azure/__init__.py +18 -1
  183. agno/models/azure/ai_foundry.py +489 -0
  184. agno/models/azure/openai_chat.py +89 -40
  185. agno/models/base.py +2477 -550
  186. agno/models/cerebras/__init__.py +12 -0
  187. agno/models/cerebras/cerebras.py +565 -0
  188. agno/models/cerebras/cerebras_openai.py +131 -0
  189. agno/models/cohere/__init__.py +4 -0
  190. agno/models/cohere/chat.py +306 -492
  191. agno/models/cometapi/__init__.py +5 -0
  192. agno/models/cometapi/cometapi.py +74 -0
  193. agno/models/dashscope/__init__.py +5 -0
  194. agno/models/dashscope/dashscope.py +90 -0
  195. agno/models/deepinfra/__init__.py +5 -0
  196. agno/models/deepinfra/deepinfra.py +45 -0
  197. agno/models/deepseek/__init__.py +4 -0
  198. agno/models/deepseek/deepseek.py +110 -9
  199. agno/models/fireworks/__init__.py +4 -0
  200. agno/models/fireworks/fireworks.py +19 -22
  201. agno/models/google/__init__.py +3 -7
  202. agno/models/google/gemini.py +1717 -662
  203. agno/models/google/utils.py +22 -0
  204. agno/models/groq/__init__.py +4 -0
  205. agno/models/groq/groq.py +391 -666
  206. agno/models/huggingface/__init__.py +4 -0
  207. agno/models/huggingface/huggingface.py +266 -538
  208. agno/models/ibm/__init__.py +5 -0
  209. agno/models/ibm/watsonx.py +432 -0
  210. agno/models/internlm/__init__.py +3 -0
  211. agno/models/internlm/internlm.py +20 -3
  212. agno/models/langdb/__init__.py +1 -0
  213. agno/models/langdb/langdb.py +60 -0
  214. agno/models/litellm/__init__.py +14 -0
  215. agno/models/litellm/chat.py +503 -0
  216. agno/models/litellm/litellm_openai.py +42 -0
  217. agno/models/llama_cpp/__init__.py +5 -0
  218. agno/models/llama_cpp/llama_cpp.py +22 -0
  219. agno/models/lmstudio/__init__.py +5 -0
  220. agno/models/lmstudio/lmstudio.py +25 -0
  221. agno/models/message.py +361 -39
  222. agno/models/meta/__init__.py +12 -0
  223. agno/models/meta/llama.py +502 -0
  224. agno/models/meta/llama_openai.py +79 -0
  225. agno/models/metrics.py +120 -0
  226. agno/models/mistral/__init__.py +4 -0
  227. agno/models/mistral/mistral.py +293 -393
  228. agno/models/nebius/__init__.py +3 -0
  229. agno/models/nebius/nebius.py +53 -0
  230. agno/models/nexus/__init__.py +3 -0
  231. agno/models/nexus/nexus.py +22 -0
  232. agno/models/nvidia/__init__.py +4 -0
  233. agno/models/nvidia/nvidia.py +22 -3
  234. agno/models/ollama/__init__.py +4 -2
  235. agno/models/ollama/chat.py +257 -492
  236. agno/models/openai/__init__.py +7 -0
  237. agno/models/openai/chat.py +725 -770
  238. agno/models/openai/like.py +16 -2
  239. agno/models/openai/responses.py +1121 -0
  240. agno/models/openrouter/__init__.py +4 -0
  241. agno/models/openrouter/openrouter.py +62 -5
  242. agno/models/perplexity/__init__.py +5 -0
  243. agno/models/perplexity/perplexity.py +203 -0
  244. agno/models/portkey/__init__.py +3 -0
  245. agno/models/portkey/portkey.py +82 -0
  246. agno/models/requesty/__init__.py +5 -0
  247. agno/models/requesty/requesty.py +69 -0
  248. agno/models/response.py +177 -7
  249. agno/models/sambanova/__init__.py +4 -0
  250. agno/models/sambanova/sambanova.py +23 -4
  251. agno/models/siliconflow/__init__.py +5 -0
  252. agno/models/siliconflow/siliconflow.py +42 -0
  253. agno/models/together/__init__.py +4 -0
  254. agno/models/together/together.py +21 -164
  255. agno/models/utils.py +266 -0
  256. agno/models/vercel/__init__.py +3 -0
  257. agno/models/vercel/v0.py +43 -0
  258. agno/models/vertexai/__init__.py +0 -1
  259. agno/models/vertexai/claude.py +190 -0
  260. agno/models/vllm/__init__.py +3 -0
  261. agno/models/vllm/vllm.py +83 -0
  262. agno/models/xai/__init__.py +2 -0
  263. agno/models/xai/xai.py +111 -7
  264. agno/os/__init__.py +3 -0
  265. agno/os/app.py +1027 -0
  266. agno/os/auth.py +244 -0
  267. agno/os/config.py +126 -0
  268. agno/os/interfaces/__init__.py +1 -0
  269. agno/os/interfaces/a2a/__init__.py +3 -0
  270. agno/os/interfaces/a2a/a2a.py +42 -0
  271. agno/os/interfaces/a2a/router.py +249 -0
  272. agno/os/interfaces/a2a/utils.py +924 -0
  273. agno/os/interfaces/agui/__init__.py +3 -0
  274. agno/os/interfaces/agui/agui.py +47 -0
  275. agno/os/interfaces/agui/router.py +147 -0
  276. agno/os/interfaces/agui/utils.py +574 -0
  277. agno/os/interfaces/base.py +25 -0
  278. agno/os/interfaces/slack/__init__.py +3 -0
  279. agno/os/interfaces/slack/router.py +148 -0
  280. agno/os/interfaces/slack/security.py +30 -0
  281. agno/os/interfaces/slack/slack.py +47 -0
  282. agno/os/interfaces/whatsapp/__init__.py +3 -0
  283. agno/os/interfaces/whatsapp/router.py +210 -0
  284. agno/os/interfaces/whatsapp/security.py +55 -0
  285. agno/os/interfaces/whatsapp/whatsapp.py +36 -0
  286. agno/os/mcp.py +293 -0
  287. agno/os/middleware/__init__.py +9 -0
  288. agno/os/middleware/jwt.py +797 -0
  289. agno/os/router.py +258 -0
  290. agno/os/routers/__init__.py +3 -0
  291. agno/os/routers/agents/__init__.py +3 -0
  292. agno/os/routers/agents/router.py +599 -0
  293. agno/os/routers/agents/schema.py +261 -0
  294. agno/os/routers/evals/__init__.py +3 -0
  295. agno/os/routers/evals/evals.py +450 -0
  296. agno/os/routers/evals/schemas.py +174 -0
  297. agno/os/routers/evals/utils.py +231 -0
  298. agno/os/routers/health.py +31 -0
  299. agno/os/routers/home.py +52 -0
  300. agno/os/routers/knowledge/__init__.py +3 -0
  301. agno/os/routers/knowledge/knowledge.py +1008 -0
  302. agno/os/routers/knowledge/schemas.py +178 -0
  303. agno/os/routers/memory/__init__.py +3 -0
  304. agno/os/routers/memory/memory.py +661 -0
  305. agno/os/routers/memory/schemas.py +88 -0
  306. agno/os/routers/metrics/__init__.py +3 -0
  307. agno/os/routers/metrics/metrics.py +190 -0
  308. agno/os/routers/metrics/schemas.py +47 -0
  309. agno/os/routers/session/__init__.py +3 -0
  310. agno/os/routers/session/session.py +997 -0
  311. agno/os/routers/teams/__init__.py +3 -0
  312. agno/os/routers/teams/router.py +512 -0
  313. agno/os/routers/teams/schema.py +257 -0
  314. agno/os/routers/traces/__init__.py +3 -0
  315. agno/os/routers/traces/schemas.py +414 -0
  316. agno/os/routers/traces/traces.py +499 -0
  317. agno/os/routers/workflows/__init__.py +3 -0
  318. agno/os/routers/workflows/router.py +624 -0
  319. agno/os/routers/workflows/schema.py +75 -0
  320. agno/os/schema.py +534 -0
  321. agno/os/scopes.py +469 -0
  322. agno/{playground → os}/settings.py +7 -15
  323. agno/os/utils.py +973 -0
  324. agno/reasoning/anthropic.py +80 -0
  325. agno/reasoning/azure_ai_foundry.py +67 -0
  326. agno/reasoning/deepseek.py +63 -0
  327. agno/reasoning/default.py +97 -0
  328. agno/reasoning/gemini.py +73 -0
  329. agno/reasoning/groq.py +71 -0
  330. agno/reasoning/helpers.py +24 -1
  331. agno/reasoning/ollama.py +67 -0
  332. agno/reasoning/openai.py +86 -0
  333. agno/reasoning/step.py +2 -1
  334. agno/reasoning/vertexai.py +76 -0
  335. agno/run/__init__.py +6 -0
  336. agno/run/agent.py +822 -0
  337. agno/run/base.py +247 -0
  338. agno/run/cancel.py +81 -0
  339. agno/run/requirement.py +181 -0
  340. agno/run/team.py +767 -0
  341. agno/run/workflow.py +708 -0
  342. agno/session/__init__.py +10 -0
  343. agno/session/agent.py +260 -0
  344. agno/session/summary.py +265 -0
  345. agno/session/team.py +342 -0
  346. agno/session/workflow.py +501 -0
  347. agno/table.py +10 -0
  348. agno/team/__init__.py +37 -0
  349. agno/team/team.py +9536 -0
  350. agno/tools/__init__.py +7 -0
  351. agno/tools/agentql.py +120 -0
  352. agno/tools/airflow.py +22 -12
  353. agno/tools/api.py +122 -0
  354. agno/tools/apify.py +276 -83
  355. agno/tools/{arxiv_toolkit.py → arxiv.py} +20 -12
  356. agno/tools/aws_lambda.py +28 -7
  357. agno/tools/aws_ses.py +66 -0
  358. agno/tools/baidusearch.py +11 -4
  359. agno/tools/bitbucket.py +292 -0
  360. agno/tools/brandfetch.py +213 -0
  361. agno/tools/bravesearch.py +106 -0
  362. agno/tools/brightdata.py +367 -0
  363. agno/tools/browserbase.py +209 -0
  364. agno/tools/calcom.py +32 -23
  365. agno/tools/calculator.py +24 -37
  366. agno/tools/cartesia.py +187 -0
  367. agno/tools/{clickup_tool.py → clickup.py} +17 -28
  368. agno/tools/confluence.py +91 -26
  369. agno/tools/crawl4ai.py +139 -43
  370. agno/tools/csv_toolkit.py +28 -22
  371. agno/tools/dalle.py +36 -22
  372. agno/tools/daytona.py +475 -0
  373. agno/tools/decorator.py +169 -14
  374. agno/tools/desi_vocal.py +23 -11
  375. agno/tools/discord.py +32 -29
  376. agno/tools/docker.py +716 -0
  377. agno/tools/duckdb.py +76 -81
  378. agno/tools/duckduckgo.py +43 -40
  379. agno/tools/e2b.py +703 -0
  380. agno/tools/eleven_labs.py +65 -54
  381. agno/tools/email.py +13 -5
  382. agno/tools/evm.py +129 -0
  383. agno/tools/exa.py +324 -42
  384. agno/tools/fal.py +39 -35
  385. agno/tools/file.py +196 -30
  386. agno/tools/file_generation.py +356 -0
  387. agno/tools/financial_datasets.py +288 -0
  388. agno/tools/firecrawl.py +108 -33
  389. agno/tools/function.py +960 -122
  390. agno/tools/giphy.py +34 -12
  391. agno/tools/github.py +1294 -97
  392. agno/tools/gmail.py +922 -0
  393. agno/tools/google_bigquery.py +117 -0
  394. agno/tools/google_drive.py +271 -0
  395. agno/tools/google_maps.py +253 -0
  396. agno/tools/googlecalendar.py +607 -107
  397. agno/tools/googlesheets.py +377 -0
  398. agno/tools/hackernews.py +20 -12
  399. agno/tools/jina.py +24 -14
  400. agno/tools/jira.py +48 -19
  401. agno/tools/knowledge.py +218 -0
  402. agno/tools/linear.py +82 -43
  403. agno/tools/linkup.py +58 -0
  404. agno/tools/local_file_system.py +15 -7
  405. agno/tools/lumalab.py +41 -26
  406. agno/tools/mcp/__init__.py +10 -0
  407. agno/tools/mcp/mcp.py +331 -0
  408. agno/tools/mcp/multi_mcp.py +347 -0
  409. agno/tools/mcp/params.py +24 -0
  410. agno/tools/mcp_toolbox.py +284 -0
  411. agno/tools/mem0.py +193 -0
  412. agno/tools/memory.py +419 -0
  413. agno/tools/mlx_transcribe.py +11 -9
  414. agno/tools/models/azure_openai.py +190 -0
  415. agno/tools/models/gemini.py +203 -0
  416. agno/tools/models/groq.py +158 -0
  417. agno/tools/models/morph.py +186 -0
  418. agno/tools/models/nebius.py +124 -0
  419. agno/tools/models_labs.py +163 -82
  420. agno/tools/moviepy_video.py +18 -13
  421. agno/tools/nano_banana.py +151 -0
  422. agno/tools/neo4j.py +134 -0
  423. agno/tools/newspaper.py +15 -4
  424. agno/tools/newspaper4k.py +19 -6
  425. agno/tools/notion.py +204 -0
  426. agno/tools/openai.py +181 -17
  427. agno/tools/openbb.py +27 -20
  428. agno/tools/opencv.py +321 -0
  429. agno/tools/openweather.py +233 -0
  430. agno/tools/oxylabs.py +385 -0
  431. agno/tools/pandas.py +25 -15
  432. agno/tools/parallel.py +314 -0
  433. agno/tools/postgres.py +238 -185
  434. agno/tools/pubmed.py +125 -13
  435. agno/tools/python.py +48 -35
  436. agno/tools/reasoning.py +283 -0
  437. agno/tools/reddit.py +207 -29
  438. agno/tools/redshift.py +406 -0
  439. agno/tools/replicate.py +69 -26
  440. agno/tools/resend.py +11 -6
  441. agno/tools/scrapegraph.py +179 -19
  442. agno/tools/searxng.py +23 -31
  443. agno/tools/serpapi.py +15 -10
  444. agno/tools/serper.py +255 -0
  445. agno/tools/shell.py +23 -12
  446. agno/tools/shopify.py +1519 -0
  447. agno/tools/slack.py +56 -14
  448. agno/tools/sleep.py +8 -6
  449. agno/tools/spider.py +35 -11
  450. agno/tools/spotify.py +919 -0
  451. agno/tools/sql.py +34 -19
  452. agno/tools/tavily.py +158 -8
  453. agno/tools/telegram.py +18 -8
  454. agno/tools/todoist.py +218 -0
  455. agno/tools/toolkit.py +134 -9
  456. agno/tools/trafilatura.py +388 -0
  457. agno/tools/trello.py +25 -28
  458. agno/tools/twilio.py +18 -9
  459. agno/tools/user_control_flow.py +78 -0
  460. agno/tools/valyu.py +228 -0
  461. agno/tools/visualization.py +467 -0
  462. agno/tools/webbrowser.py +28 -0
  463. agno/tools/webex.py +76 -0
  464. agno/tools/website.py +23 -19
  465. agno/tools/webtools.py +45 -0
  466. agno/tools/whatsapp.py +286 -0
  467. agno/tools/wikipedia.py +28 -19
  468. agno/tools/workflow.py +285 -0
  469. agno/tools/{twitter.py → x.py} +142 -46
  470. agno/tools/yfinance.py +41 -39
  471. agno/tools/youtube.py +34 -17
  472. agno/tools/zendesk.py +15 -5
  473. agno/tools/zep.py +454 -0
  474. agno/tools/zoom.py +86 -37
  475. agno/tracing/__init__.py +12 -0
  476. agno/tracing/exporter.py +157 -0
  477. agno/tracing/schemas.py +276 -0
  478. agno/tracing/setup.py +111 -0
  479. agno/utils/agent.py +938 -0
  480. agno/utils/audio.py +37 -1
  481. agno/utils/certs.py +27 -0
  482. agno/utils/code_execution.py +11 -0
  483. agno/utils/common.py +103 -20
  484. agno/utils/cryptography.py +22 -0
  485. agno/utils/dttm.py +33 -0
  486. agno/utils/events.py +700 -0
  487. agno/utils/functions.py +107 -37
  488. agno/utils/gemini.py +426 -0
  489. agno/utils/hooks.py +171 -0
  490. agno/utils/http.py +185 -0
  491. agno/utils/json_schema.py +159 -37
  492. agno/utils/knowledge.py +36 -0
  493. agno/utils/location.py +19 -0
  494. agno/utils/log.py +221 -8
  495. agno/utils/mcp.py +214 -0
  496. agno/utils/media.py +335 -14
  497. agno/utils/merge_dict.py +22 -1
  498. agno/utils/message.py +77 -2
  499. agno/utils/models/ai_foundry.py +50 -0
  500. agno/utils/models/claude.py +373 -0
  501. agno/utils/models/cohere.py +94 -0
  502. agno/utils/models/llama.py +85 -0
  503. agno/utils/models/mistral.py +100 -0
  504. agno/utils/models/openai_responses.py +140 -0
  505. agno/utils/models/schema_utils.py +153 -0
  506. agno/utils/models/watsonx.py +41 -0
  507. agno/utils/openai.py +257 -0
  508. agno/utils/pickle.py +1 -1
  509. agno/utils/pprint.py +124 -8
  510. agno/utils/print_response/agent.py +930 -0
  511. agno/utils/print_response/team.py +1914 -0
  512. agno/utils/print_response/workflow.py +1668 -0
  513. agno/utils/prompts.py +111 -0
  514. agno/utils/reasoning.py +108 -0
  515. agno/utils/response.py +163 -0
  516. agno/utils/serialize.py +32 -0
  517. agno/utils/shell.py +4 -4
  518. agno/utils/streamlit.py +487 -0
  519. agno/utils/string.py +204 -51
  520. agno/utils/team.py +139 -0
  521. agno/utils/timer.py +9 -2
  522. agno/utils/tokens.py +657 -0
  523. agno/utils/tools.py +19 -1
  524. agno/utils/whatsapp.py +305 -0
  525. agno/utils/yaml_io.py +3 -3
  526. agno/vectordb/__init__.py +2 -0
  527. agno/vectordb/base.py +87 -9
  528. agno/vectordb/cassandra/__init__.py +5 -1
  529. agno/vectordb/cassandra/cassandra.py +383 -27
  530. agno/vectordb/chroma/__init__.py +4 -0
  531. agno/vectordb/chroma/chromadb.py +748 -83
  532. agno/vectordb/clickhouse/__init__.py +7 -1
  533. agno/vectordb/clickhouse/clickhousedb.py +554 -53
  534. agno/vectordb/couchbase/__init__.py +3 -0
  535. agno/vectordb/couchbase/couchbase.py +1446 -0
  536. agno/vectordb/lancedb/__init__.py +5 -0
  537. agno/vectordb/lancedb/lance_db.py +730 -98
  538. agno/vectordb/langchaindb/__init__.py +5 -0
  539. agno/vectordb/langchaindb/langchaindb.py +163 -0
  540. agno/vectordb/lightrag/__init__.py +5 -0
  541. agno/vectordb/lightrag/lightrag.py +388 -0
  542. agno/vectordb/llamaindex/__init__.py +3 -0
  543. agno/vectordb/llamaindex/llamaindexdb.py +166 -0
  544. agno/vectordb/milvus/__init__.py +3 -0
  545. agno/vectordb/milvus/milvus.py +966 -78
  546. agno/vectordb/mongodb/__init__.py +9 -1
  547. agno/vectordb/mongodb/mongodb.py +1175 -172
  548. agno/vectordb/pgvector/__init__.py +8 -0
  549. agno/vectordb/pgvector/pgvector.py +599 -115
  550. agno/vectordb/pineconedb/__init__.py +5 -1
  551. agno/vectordb/pineconedb/pineconedb.py +406 -43
  552. agno/vectordb/qdrant/__init__.py +4 -0
  553. agno/vectordb/qdrant/qdrant.py +914 -61
  554. agno/vectordb/redis/__init__.py +9 -0
  555. agno/vectordb/redis/redisdb.py +682 -0
  556. agno/vectordb/singlestore/__init__.py +8 -1
  557. agno/vectordb/singlestore/singlestore.py +771 -0
  558. agno/vectordb/surrealdb/__init__.py +3 -0
  559. agno/vectordb/surrealdb/surrealdb.py +663 -0
  560. agno/vectordb/upstashdb/__init__.py +5 -0
  561. agno/vectordb/upstashdb/upstashdb.py +718 -0
  562. agno/vectordb/weaviate/__init__.py +8 -0
  563. agno/vectordb/weaviate/index.py +15 -0
  564. agno/vectordb/weaviate/weaviate.py +1009 -0
  565. agno/workflow/__init__.py +23 -1
  566. agno/workflow/agent.py +299 -0
  567. agno/workflow/condition.py +759 -0
  568. agno/workflow/loop.py +756 -0
  569. agno/workflow/parallel.py +853 -0
  570. agno/workflow/router.py +723 -0
  571. agno/workflow/step.py +1564 -0
  572. agno/workflow/steps.py +613 -0
  573. agno/workflow/types.py +556 -0
  574. agno/workflow/workflow.py +4327 -514
  575. agno-2.3.13.dist-info/METADATA +639 -0
  576. agno-2.3.13.dist-info/RECORD +613 -0
  577. {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +1 -1
  578. agno-2.3.13.dist-info/licenses/LICENSE +201 -0
  579. agno/api/playground.py +0 -91
  580. agno/api/schemas/playground.py +0 -22
  581. agno/api/schemas/user.py +0 -22
  582. agno/api/schemas/workspace.py +0 -46
  583. agno/api/user.py +0 -160
  584. agno/api/workspace.py +0 -151
  585. agno/cli/auth_server.py +0 -118
  586. agno/cli/config.py +0 -275
  587. agno/cli/console.py +0 -88
  588. agno/cli/credentials.py +0 -23
  589. agno/cli/entrypoint.py +0 -571
  590. agno/cli/operator.py +0 -355
  591. agno/cli/settings.py +0 -85
  592. agno/cli/ws/ws_cli.py +0 -817
  593. agno/constants.py +0 -13
  594. agno/document/__init__.py +0 -1
  595. agno/document/chunking/semantic.py +0 -47
  596. agno/document/chunking/strategy.py +0 -31
  597. agno/document/reader/__init__.py +0 -1
  598. agno/document/reader/arxiv_reader.py +0 -41
  599. agno/document/reader/base.py +0 -22
  600. agno/document/reader/csv_reader.py +0 -84
  601. agno/document/reader/docx_reader.py +0 -46
  602. agno/document/reader/firecrawl_reader.py +0 -99
  603. agno/document/reader/json_reader.py +0 -43
  604. agno/document/reader/pdf_reader.py +0 -219
  605. agno/document/reader/s3/pdf_reader.py +0 -46
  606. agno/document/reader/s3/text_reader.py +0 -51
  607. agno/document/reader/text_reader.py +0 -41
  608. agno/document/reader/website_reader.py +0 -175
  609. agno/document/reader/youtube_reader.py +0 -50
  610. agno/embedder/__init__.py +0 -1
  611. agno/embedder/azure_openai.py +0 -86
  612. agno/embedder/cohere.py +0 -72
  613. agno/embedder/fastembed.py +0 -37
  614. agno/embedder/google.py +0 -73
  615. agno/embedder/huggingface.py +0 -54
  616. agno/embedder/mistral.py +0 -80
  617. agno/embedder/ollama.py +0 -57
  618. agno/embedder/openai.py +0 -74
  619. agno/embedder/sentence_transformer.py +0 -38
  620. agno/embedder/voyageai.py +0 -64
  621. agno/eval/perf.py +0 -201
  622. agno/file/__init__.py +0 -1
  623. agno/file/file.py +0 -16
  624. agno/file/local/csv.py +0 -32
  625. agno/file/local/txt.py +0 -19
  626. agno/infra/app.py +0 -240
  627. agno/infra/base.py +0 -144
  628. agno/infra/context.py +0 -20
  629. agno/infra/db_app.py +0 -52
  630. agno/infra/resource.py +0 -205
  631. agno/infra/resources.py +0 -55
  632. agno/knowledge/agent.py +0 -230
  633. agno/knowledge/arxiv.py +0 -22
  634. agno/knowledge/combined.py +0 -22
  635. agno/knowledge/csv.py +0 -28
  636. agno/knowledge/csv_url.py +0 -19
  637. agno/knowledge/document.py +0 -20
  638. agno/knowledge/docx.py +0 -30
  639. agno/knowledge/json.py +0 -28
  640. agno/knowledge/langchain.py +0 -71
  641. agno/knowledge/llamaindex.py +0 -66
  642. agno/knowledge/pdf.py +0 -28
  643. agno/knowledge/pdf_url.py +0 -26
  644. agno/knowledge/s3/base.py +0 -60
  645. agno/knowledge/s3/pdf.py +0 -21
  646. agno/knowledge/s3/text.py +0 -23
  647. agno/knowledge/text.py +0 -30
  648. agno/knowledge/website.py +0 -88
  649. agno/knowledge/wikipedia.py +0 -31
  650. agno/knowledge/youtube.py +0 -22
  651. agno/memory/agent.py +0 -392
  652. agno/memory/classifier.py +0 -104
  653. agno/memory/db/__init__.py +0 -1
  654. agno/memory/db/base.py +0 -42
  655. agno/memory/db/mongodb.py +0 -189
  656. agno/memory/db/postgres.py +0 -203
  657. agno/memory/db/sqlite.py +0 -193
  658. agno/memory/memory.py +0 -15
  659. agno/memory/row.py +0 -36
  660. agno/memory/summarizer.py +0 -192
  661. agno/memory/summary.py +0 -19
  662. agno/memory/workflow.py +0 -38
  663. agno/models/google/gemini_openai.py +0 -26
  664. agno/models/ollama/hermes.py +0 -221
  665. agno/models/ollama/tools.py +0 -362
  666. agno/models/vertexai/gemini.py +0 -595
  667. agno/playground/__init__.py +0 -3
  668. agno/playground/async_router.py +0 -421
  669. agno/playground/deploy.py +0 -249
  670. agno/playground/operator.py +0 -92
  671. agno/playground/playground.py +0 -91
  672. agno/playground/schemas.py +0 -76
  673. agno/playground/serve.py +0 -55
  674. agno/playground/sync_router.py +0 -405
  675. agno/reasoning/agent.py +0 -68
  676. agno/run/response.py +0 -112
  677. agno/storage/agent/__init__.py +0 -0
  678. agno/storage/agent/base.py +0 -38
  679. agno/storage/agent/dynamodb.py +0 -350
  680. agno/storage/agent/json.py +0 -92
  681. agno/storage/agent/mongodb.py +0 -228
  682. agno/storage/agent/postgres.py +0 -367
  683. agno/storage/agent/session.py +0 -79
  684. agno/storage/agent/singlestore.py +0 -303
  685. agno/storage/agent/sqlite.py +0 -357
  686. agno/storage/agent/yaml.py +0 -93
  687. agno/storage/workflow/__init__.py +0 -0
  688. agno/storage/workflow/base.py +0 -40
  689. agno/storage/workflow/mongodb.py +0 -233
  690. agno/storage/workflow/postgres.py +0 -366
  691. agno/storage/workflow/session.py +0 -60
  692. agno/storage/workflow/sqlite.py +0 -359
  693. agno/tools/googlesearch.py +0 -88
  694. agno/utils/defaults.py +0 -57
  695. agno/utils/filesystem.py +0 -39
  696. agno/utils/git.py +0 -52
  697. agno/utils/json_io.py +0 -30
  698. agno/utils/load_env.py +0 -19
  699. agno/utils/py_io.py +0 -19
  700. agno/utils/pyproject.py +0 -18
  701. agno/utils/resource_filter.py +0 -31
  702. agno/vectordb/singlestore/s2vectordb.py +0 -390
  703. agno/vectordb/singlestore/s2vectordb2.py +0 -355
  704. agno/workspace/__init__.py +0 -0
  705. agno/workspace/config.py +0 -325
  706. agno/workspace/enums.py +0 -6
  707. agno/workspace/helpers.py +0 -48
  708. agno/workspace/operator.py +0 -758
  709. agno/workspace/settings.py +0 -63
  710. agno-0.1.2.dist-info/LICENSE +0 -375
  711. agno-0.1.2.dist-info/METADATA +0 -502
  712. agno-0.1.2.dist-info/RECORD +0 -352
  713. agno-0.1.2.dist-info/entry_points.txt +0 -3
  714. /agno/{cli → db/migrations}/__init__.py +0 -0
  715. /agno/{cli/ws → db/migrations/versions}/__init__.py +0 -0
  716. /agno/{document/chunking/__init__.py → db/schemas/metrics.py} +0 -0
  717. /agno/{document/reader/s3 → integrations}/__init__.py +0 -0
  718. /agno/{file/local → knowledge/chunking}/__init__.py +0 -0
  719. /agno/{infra → knowledge/remote_content}/__init__.py +0 -0
  720. /agno/{knowledge/s3 → tools/models}/__init__.py +0 -0
  721. /agno/{reranker → utils/models}/__init__.py +0 -0
  722. /agno/{storage → utils/print_response}/__init__.py +0 -0
  723. {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,624 @@
1
+ import json
2
+ from typing import TYPE_CHECKING, Any, AsyncGenerator, Dict, List, Optional, Union, cast
3
+ from uuid import uuid4
4
+
5
+ from fastapi import (
6
+ APIRouter,
7
+ BackgroundTasks,
8
+ Depends,
9
+ Form,
10
+ HTTPException,
11
+ Request,
12
+ WebSocket,
13
+ )
14
+ from fastapi.responses import JSONResponse, StreamingResponse
15
+ from pydantic import BaseModel
16
+
17
+ from agno.exceptions import InputCheckError, OutputCheckError
18
+ from agno.os.auth import get_authentication_dependency, require_resource_access, validate_websocket_token
19
+ from agno.os.routers.workflows.schema import WorkflowResponse
20
+ from agno.os.schema import (
21
+ BadRequestResponse,
22
+ InternalServerErrorResponse,
23
+ NotFoundResponse,
24
+ UnauthenticatedResponse,
25
+ ValidationErrorResponse,
26
+ WorkflowSummaryResponse,
27
+ )
28
+ from agno.os.settings import AgnoAPISettings
29
+ from agno.os.utils import (
30
+ format_sse_event,
31
+ get_request_kwargs,
32
+ get_workflow_by_id,
33
+ )
34
+ from agno.run.workflow import WorkflowErrorEvent, WorkflowRunOutput
35
+ from agno.utils.log import log_warning, logger
36
+ from agno.workflow.workflow import Workflow
37
+
38
+ if TYPE_CHECKING:
39
+ from agno.os.app import AgentOS
40
+
41
+
42
+ class WebSocketManager:
43
+ """Manages WebSocket connections for workflow runs"""
44
+
45
+ active_connections: Dict[str, WebSocket] # {run_id: websocket}
46
+ authenticated_connections: Dict[WebSocket, bool] # {websocket: is_authenticated}
47
+
48
+ def __init__(
49
+ self,
50
+ active_connections: Optional[Dict[str, WebSocket]] = None,
51
+ ):
52
+ # Store active connections: {run_id: websocket}
53
+ self.active_connections = active_connections or {}
54
+ # Track authentication state for each websocket
55
+ self.authenticated_connections = {}
56
+
57
+ async def connect(self, websocket: WebSocket, requires_auth: bool = True):
58
+ """Accept WebSocket connection"""
59
+ await websocket.accept()
60
+ logger.debug("WebSocket connected")
61
+
62
+ # If auth is not required, mark as authenticated immediately
63
+ self.authenticated_connections[websocket] = not requires_auth
64
+
65
+ # Send connection confirmation with auth requirement info
66
+ await websocket.send_text(
67
+ json.dumps(
68
+ {
69
+ "event": "connected",
70
+ "message": (
71
+ "Connected to workflow events. Please authenticate to continue."
72
+ if requires_auth
73
+ else "Connected to workflow events. Authentication not required."
74
+ ),
75
+ "requires_auth": requires_auth,
76
+ }
77
+ )
78
+ )
79
+
80
+ async def authenticate_websocket(self, websocket: WebSocket):
81
+ """Mark a WebSocket connection as authenticated"""
82
+ self.authenticated_connections[websocket] = True
83
+ logger.debug("WebSocket authenticated")
84
+
85
+ # Send authentication confirmation
86
+ await websocket.send_text(
87
+ json.dumps(
88
+ {
89
+ "event": "authenticated",
90
+ "message": "Authentication successful. You can now send commands.",
91
+ }
92
+ )
93
+ )
94
+
95
+ def is_authenticated(self, websocket: WebSocket) -> bool:
96
+ """Check if a WebSocket connection is authenticated"""
97
+ return self.authenticated_connections.get(websocket, False)
98
+
99
+ async def register_workflow_websocket(self, run_id: str, websocket: WebSocket):
100
+ """Register a workflow run with its WebSocket connection"""
101
+ self.active_connections[run_id] = websocket
102
+ logger.debug(f"Registered WebSocket for run_id: {run_id}")
103
+
104
+ async def disconnect_by_run_id(self, run_id: str):
105
+ """Remove WebSocket connection by run_id"""
106
+ if run_id in self.active_connections:
107
+ websocket = self.active_connections[run_id]
108
+ del self.active_connections[run_id]
109
+ # Clean up authentication state
110
+ if websocket in self.authenticated_connections:
111
+ del self.authenticated_connections[websocket]
112
+ logger.debug(f"WebSocket disconnected for run_id: {run_id}")
113
+
114
+ async def disconnect_websocket(self, websocket: WebSocket):
115
+ """Remove WebSocket connection and clean up all associated state"""
116
+ # Remove from authenticated connections
117
+ if websocket in self.authenticated_connections:
118
+ del self.authenticated_connections[websocket]
119
+
120
+ # Remove from active connections
121
+ runs_to_remove = [run_id for run_id, ws in self.active_connections.items() if ws == websocket]
122
+ for run_id in runs_to_remove:
123
+ del self.active_connections[run_id]
124
+
125
+ logger.debug("WebSocket disconnected and cleaned up")
126
+
127
+ async def get_websocket_for_run(self, run_id: str) -> Optional[WebSocket]:
128
+ """Get WebSocket connection for a workflow run"""
129
+ return self.active_connections.get(run_id)
130
+
131
+
132
+ # Global manager instance
133
+ websocket_manager = WebSocketManager(
134
+ active_connections={},
135
+ )
136
+
137
+
138
+ async def handle_workflow_via_websocket(websocket: WebSocket, message: dict, os: "AgentOS"):
139
+ """Handle workflow execution directly via WebSocket"""
140
+ try:
141
+ workflow_id = message.get("workflow_id")
142
+ session_id = message.get("session_id")
143
+ user_message = message.get("message", "")
144
+ user_id = message.get("user_id")
145
+
146
+ if not workflow_id:
147
+ await websocket.send_text(json.dumps({"event": "error", "error": "workflow_id is required"}))
148
+ return
149
+
150
+ # Get workflow from OS
151
+ workflow = get_workflow_by_id(workflow_id, os.workflows)
152
+ if not workflow:
153
+ await websocket.send_text(json.dumps({"event": "error", "error": f"Workflow {workflow_id} not found"}))
154
+ return
155
+
156
+ # Generate session_id if not provided
157
+ # Use workflow's default session_id if not provided in message
158
+ if not session_id:
159
+ if workflow.session_id:
160
+ session_id = workflow.session_id
161
+ else:
162
+ session_id = str(uuid4())
163
+
164
+ # Execute workflow in background with streaming
165
+ workflow_result = await workflow.arun( # type: ignore
166
+ input=user_message,
167
+ session_id=session_id,
168
+ user_id=user_id,
169
+ stream=True,
170
+ stream_events=True,
171
+ background=True,
172
+ websocket=websocket,
173
+ )
174
+
175
+ workflow_run_output = cast(WorkflowRunOutput, workflow_result)
176
+
177
+ await websocket_manager.register_workflow_websocket(workflow_run_output.run_id, websocket) # type: ignore
178
+
179
+ except (InputCheckError, OutputCheckError) as e:
180
+ await websocket.send_text(
181
+ json.dumps(
182
+ {
183
+ "event": "error",
184
+ "error": str(e),
185
+ "error_type": e.type,
186
+ "error_id": e.error_id,
187
+ "additional_data": e.additional_data,
188
+ }
189
+ )
190
+ )
191
+ except Exception as e:
192
+ logger.error(f"Error executing workflow via WebSocket: {e}")
193
+ error_payload = {
194
+ "event": "error",
195
+ "error": str(e),
196
+ "error_type": e.type if hasattr(e, "type") else None,
197
+ "error_id": e.error_id if hasattr(e, "error_id") else None,
198
+ }
199
+ error_payload = {k: v for k, v in error_payload.items() if v is not None}
200
+ await websocket.send_text(json.dumps(error_payload))
201
+
202
+
203
+ async def workflow_response_streamer(
204
+ workflow: Workflow,
205
+ input: Optional[Union[str, Dict[str, Any], List[Any], BaseModel]] = None,
206
+ session_id: Optional[str] = None,
207
+ user_id: Optional[str] = None,
208
+ background_tasks: Optional[BackgroundTasks] = None,
209
+ **kwargs: Any,
210
+ ) -> AsyncGenerator:
211
+ try:
212
+ # Pass background_tasks if provided
213
+ if background_tasks is not None:
214
+ kwargs["background_tasks"] = background_tasks
215
+
216
+ run_response = workflow.arun(
217
+ input=input,
218
+ session_id=session_id,
219
+ user_id=user_id,
220
+ stream=True,
221
+ stream_events=True,
222
+ **kwargs,
223
+ )
224
+
225
+ async for run_response_chunk in run_response:
226
+ yield format_sse_event(run_response_chunk) # type: ignore
227
+
228
+ except (InputCheckError, OutputCheckError) as e:
229
+ error_response = WorkflowErrorEvent(
230
+ error=str(e),
231
+ error_type=e.type,
232
+ error_id=e.error_id,
233
+ additional_data=e.additional_data,
234
+ )
235
+ yield format_sse_event(error_response)
236
+
237
+ except Exception as e:
238
+ import traceback
239
+
240
+ traceback.print_exc()
241
+ error_response = WorkflowErrorEvent(
242
+ error=str(e),
243
+ error_type=e.type if hasattr(e, "type") else None,
244
+ error_id=e.error_id if hasattr(e, "error_id") else None,
245
+ )
246
+ yield format_sse_event(error_response)
247
+ return
248
+
249
+
250
+ def get_websocket_router(
251
+ os: "AgentOS",
252
+ settings: AgnoAPISettings = AgnoAPISettings(),
253
+ ) -> APIRouter:
254
+ """
255
+ Create WebSocket router with support for both legacy (os_security_key) and JWT authentication.
256
+
257
+ WebSocket endpoints handle authentication internally via message-based auth.
258
+ Authentication methods (in order of precedence):
259
+ 1. JWT tokens - if JWTMiddleware is configured (via app.state.jwt_middleware)
260
+ 2. Legacy bearer token - if settings.os_security_key is set
261
+ 3. No authentication - if neither is configured
262
+
263
+ The JWT middleware instance is accessed from app.state.jwt_middleware, which is set
264
+ by AgentOS when authorization is enabled. This allows reusing the same validation
265
+ logic and loaded keys as the HTTP middleware.
266
+
267
+ Args:
268
+ os: The AgentOS instance
269
+ settings: API settings (includes os_security_key for legacy auth)
270
+ """
271
+ ws_router = APIRouter()
272
+
273
+ @ws_router.websocket(
274
+ "/workflows/ws",
275
+ name="workflow_websocket",
276
+ )
277
+ async def workflow_websocket_endpoint(websocket: WebSocket):
278
+ """WebSocket endpoint for receiving real-time workflow events"""
279
+ # Check if JWT validator is configured (set by AgentOS when authorization=True)
280
+ jwt_validator = getattr(websocket.app.state, "jwt_validator", None)
281
+ jwt_auth_enabled = jwt_validator is not None
282
+
283
+ # Determine auth requirements - JWT takes precedence over legacy
284
+ requires_auth = jwt_auth_enabled or bool(settings.os_security_key)
285
+
286
+ await websocket_manager.connect(websocket, requires_auth=requires_auth)
287
+
288
+ # Store user context from JWT auth
289
+ websocket_user_context: Dict[str, Any] = {}
290
+
291
+ try:
292
+ while True:
293
+ data = await websocket.receive_text()
294
+ message = json.loads(data)
295
+ action = message.get("action")
296
+
297
+ # Handle authentication first
298
+ if action == "authenticate":
299
+ token = message.get("token")
300
+ if not token:
301
+ await websocket.send_text(json.dumps({"event": "auth_error", "error": "Token is required"}))
302
+ continue
303
+
304
+ if jwt_auth_enabled and jwt_validator:
305
+ # Use JWT validator for token validation
306
+ try:
307
+ payload = jwt_validator.validate_token(token)
308
+ claims = jwt_validator.extract_claims(payload)
309
+ await websocket_manager.authenticate_websocket(websocket)
310
+
311
+ # Store user context from JWT
312
+ websocket_user_context["user_id"] = claims["user_id"]
313
+ websocket_user_context["scopes"] = claims["scopes"]
314
+ websocket_user_context["payload"] = payload
315
+
316
+ # Include user info in auth success message
317
+ await websocket.send_text(
318
+ json.dumps(
319
+ {
320
+ "event": "authenticated",
321
+ "message": "JWT authentication successful.",
322
+ "user_id": claims["user_id"],
323
+ }
324
+ )
325
+ )
326
+ except Exception as e:
327
+ error_msg = str(e) if str(e) else "Invalid token"
328
+ error_type = "expired" if "expired" in error_msg.lower() else "invalid_token"
329
+ await websocket.send_text(
330
+ json.dumps(
331
+ {
332
+ "event": "auth_error",
333
+ "error": error_msg,
334
+ "error_type": error_type,
335
+ }
336
+ )
337
+ )
338
+ continue
339
+ elif validate_websocket_token(token, settings):
340
+ # Legacy os_security_key authentication
341
+ await websocket_manager.authenticate_websocket(websocket)
342
+ else:
343
+ await websocket.send_text(json.dumps({"event": "auth_error", "error": "Invalid token"}))
344
+ continue
345
+
346
+ # Check authentication for all other actions (only when required)
347
+ elif requires_auth and not websocket_manager.is_authenticated(websocket):
348
+ auth_type = "JWT" if jwt_auth_enabled else "bearer token"
349
+ await websocket.send_text(
350
+ json.dumps(
351
+ {
352
+ "event": "auth_required",
353
+ "error": f"Authentication required. Send authenticate action with valid {auth_type}.",
354
+ }
355
+ )
356
+ )
357
+ continue
358
+
359
+ # Handle authenticated actions
360
+ elif action == "ping":
361
+ await websocket.send_text(json.dumps({"event": "pong"}))
362
+
363
+ elif action == "start-workflow":
364
+ # Add user context to message if available from JWT auth
365
+ if websocket_user_context:
366
+ if "user_id" not in message and websocket_user_context.get("user_id"):
367
+ message["user_id"] = websocket_user_context["user_id"]
368
+ # Handle workflow execution directly via WebSocket
369
+ await handle_workflow_via_websocket(websocket, message, os)
370
+
371
+ else:
372
+ await websocket.send_text(json.dumps({"event": "error", "error": f"Unknown action: {action}"}))
373
+
374
+ except Exception as e:
375
+ if "1012" not in str(e) and "1001" not in str(e):
376
+ logger.error(f"WebSocket error: {e}")
377
+ finally:
378
+ # Clean up the websocket connection
379
+ await websocket_manager.disconnect_websocket(websocket)
380
+
381
+ return ws_router
382
+
383
+
384
+ def get_workflow_router(
385
+ os: "AgentOS",
386
+ settings: AgnoAPISettings = AgnoAPISettings(),
387
+ ) -> APIRouter:
388
+ """Create the workflow router with comprehensive OpenAPI documentation."""
389
+ router = APIRouter(
390
+ dependencies=[Depends(get_authentication_dependency(settings))],
391
+ responses={
392
+ 400: {"description": "Bad Request", "model": BadRequestResponse},
393
+ 401: {"description": "Unauthorized", "model": UnauthenticatedResponse},
394
+ 404: {"description": "Not Found", "model": NotFoundResponse},
395
+ 422: {"description": "Validation Error", "model": ValidationErrorResponse},
396
+ 500: {"description": "Internal Server Error", "model": InternalServerErrorResponse},
397
+ },
398
+ )
399
+
400
+ @router.get(
401
+ "/workflows",
402
+ response_model=List[WorkflowSummaryResponse],
403
+ response_model_exclude_none=True,
404
+ tags=["Workflows"],
405
+ operation_id="get_workflows",
406
+ summary="List All Workflows",
407
+ description=(
408
+ "Retrieve a comprehensive list of all workflows configured in this OS instance.\n\n"
409
+ "**Return Information:**\n"
410
+ "- Workflow metadata (ID, name, description)\n"
411
+ "- Input schema requirements\n"
412
+ "- Step sequence and execution flow\n"
413
+ "- Associated agents and teams"
414
+ ),
415
+ responses={
416
+ 200: {
417
+ "description": "List of workflows retrieved successfully",
418
+ "content": {
419
+ "application/json": {
420
+ "example": [
421
+ {
422
+ "id": "content-creation-workflow",
423
+ "name": "Content Creation Workflow",
424
+ "description": "Automated content creation from blog posts to social media",
425
+ "db_id": "123",
426
+ }
427
+ ]
428
+ }
429
+ },
430
+ }
431
+ },
432
+ )
433
+ async def get_workflows(request: Request) -> List[WorkflowSummaryResponse]:
434
+ if os.workflows is None:
435
+ return []
436
+
437
+ # Filter workflows based on user's scopes (only if authorization is enabled)
438
+ if getattr(request.state, "authorization_enabled", False):
439
+ from agno.os.auth import filter_resources_by_access, get_accessible_resources
440
+
441
+ # Check if user has any workflow scopes at all
442
+ accessible_ids = get_accessible_resources(request, "workflows")
443
+ if not accessible_ids:
444
+ raise HTTPException(status_code=403, detail="Insufficient permissions")
445
+
446
+ accessible_workflows = filter_resources_by_access(request, os.workflows, "workflows")
447
+ else:
448
+ accessible_workflows = os.workflows
449
+
450
+ return [WorkflowSummaryResponse.from_workflow(workflow) for workflow in accessible_workflows]
451
+
452
+ @router.get(
453
+ "/workflows/{workflow_id}",
454
+ response_model=WorkflowResponse,
455
+ response_model_exclude_none=True,
456
+ tags=["Workflows"],
457
+ operation_id="get_workflow",
458
+ summary="Get Workflow Details",
459
+ description=("Retrieve detailed configuration and step information for a specific workflow."),
460
+ responses={
461
+ 200: {
462
+ "description": "Workflow details retrieved successfully",
463
+ "content": {
464
+ "application/json": {
465
+ "example": {
466
+ "id": "content-creation-workflow",
467
+ "name": "Content Creation Workflow",
468
+ "description": "Automated content creation from blog posts to social media",
469
+ "db_id": "123",
470
+ }
471
+ }
472
+ },
473
+ },
474
+ 404: {"description": "Workflow not found", "model": NotFoundResponse},
475
+ },
476
+ dependencies=[Depends(require_resource_access("workflows", "read", "workflow_id"))],
477
+ )
478
+ async def get_workflow(workflow_id: str, request: Request) -> WorkflowResponse:
479
+ workflow = get_workflow_by_id(workflow_id, os.workflows)
480
+ if workflow is None:
481
+ raise HTTPException(status_code=404, detail="Workflow not found")
482
+
483
+ return await WorkflowResponse.from_workflow(workflow)
484
+
485
+ @router.post(
486
+ "/workflows/{workflow_id}/runs",
487
+ tags=["Workflows"],
488
+ operation_id="create_workflow_run",
489
+ response_model_exclude_none=True,
490
+ summary="Execute Workflow",
491
+ description=(
492
+ "Execute a workflow with the provided input data. Workflows can run in streaming or batch mode.\n\n"
493
+ "**Execution Modes:**\n"
494
+ "- **Streaming (`stream=true`)**: Real-time step-by-step execution updates via SSE\n"
495
+ "- **Non-Streaming (`stream=false`)**: Complete workflow execution with final result\n\n"
496
+ "**Workflow Execution Process:**\n"
497
+ "1. Input validation against workflow schema\n"
498
+ "2. Sequential or parallel step execution based on workflow design\n"
499
+ "3. Data flow between steps with transformation\n"
500
+ "4. Error handling and automatic retries where configured\n"
501
+ "5. Final result compilation and response\n\n"
502
+ "**Session Management:**\n"
503
+ "Workflows support session continuity for stateful execution across multiple runs."
504
+ ),
505
+ responses={
506
+ 200: {
507
+ "description": "Workflow executed successfully",
508
+ "content": {
509
+ "text/event-stream": {
510
+ "example": 'event: RunStarted\ndata: {"content": "Hello!", "run_id": "123..."}\n\n'
511
+ },
512
+ },
513
+ },
514
+ 400: {"description": "Invalid input data or workflow configuration", "model": BadRequestResponse},
515
+ 404: {"description": "Workflow not found", "model": NotFoundResponse},
516
+ 500: {"description": "Workflow execution error", "model": InternalServerErrorResponse},
517
+ },
518
+ dependencies=[Depends(require_resource_access("workflows", "run", "workflow_id"))],
519
+ )
520
+ async def create_workflow_run(
521
+ workflow_id: str,
522
+ request: Request,
523
+ background_tasks: BackgroundTasks,
524
+ message: str = Form(...),
525
+ stream: bool = Form(True),
526
+ session_id: Optional[str] = Form(None),
527
+ user_id: Optional[str] = Form(None),
528
+ ):
529
+ kwargs = await get_request_kwargs(request, create_workflow_run)
530
+
531
+ if hasattr(request.state, "user_id"):
532
+ if user_id:
533
+ log_warning("User ID parameter passed in both request state and kwargs, using request state")
534
+ user_id = request.state.user_id
535
+ if hasattr(request.state, "session_id"):
536
+ if session_id:
537
+ log_warning("Session ID parameter passed in both request state and kwargs, using request state")
538
+ session_id = request.state.session_id
539
+ if hasattr(request.state, "session_state"):
540
+ session_state = request.state.session_state
541
+ if "session_state" in kwargs:
542
+ log_warning("Session state parameter passed in both request state and kwargs, using request state")
543
+ kwargs["session_state"] = session_state
544
+ if hasattr(request.state, "dependencies"):
545
+ dependencies = request.state.dependencies
546
+ if "dependencies" in kwargs:
547
+ log_warning("Dependencies parameter passed in both request state and kwargs, using request state")
548
+ kwargs["dependencies"] = dependencies
549
+ if hasattr(request.state, "metadata"):
550
+ metadata = request.state.metadata
551
+ if "metadata" in kwargs:
552
+ log_warning("Metadata parameter passed in both request state and kwargs, using request state")
553
+ kwargs["metadata"] = metadata
554
+
555
+ # Retrieve the workflow by ID
556
+ workflow = get_workflow_by_id(workflow_id, os.workflows)
557
+ if workflow is None:
558
+ raise HTTPException(status_code=404, detail="Workflow not found")
559
+
560
+ if session_id:
561
+ logger.debug(f"Continuing session: {session_id}")
562
+ else:
563
+ logger.debug("Creating new session")
564
+ session_id = str(uuid4())
565
+
566
+ # Return based on stream parameter
567
+ try:
568
+ if stream:
569
+ return StreamingResponse(
570
+ workflow_response_streamer(
571
+ workflow,
572
+ input=message,
573
+ session_id=session_id,
574
+ user_id=user_id,
575
+ background_tasks=background_tasks,
576
+ **kwargs,
577
+ ),
578
+ media_type="text/event-stream",
579
+ )
580
+ else:
581
+ run_response = await workflow.arun(
582
+ input=message,
583
+ session_id=session_id,
584
+ user_id=user_id,
585
+ stream=False,
586
+ background_tasks=background_tasks,
587
+ **kwargs,
588
+ )
589
+ return run_response.to_dict()
590
+
591
+ except InputCheckError as e:
592
+ raise HTTPException(status_code=400, detail=str(e))
593
+ except Exception as e:
594
+ # Handle unexpected runtime errors
595
+ raise HTTPException(status_code=500, detail=f"Error running workflow: {str(e)}")
596
+
597
+ @router.post(
598
+ "/workflows/{workflow_id}/runs/{run_id}/cancel",
599
+ tags=["Workflows"],
600
+ operation_id="cancel_workflow_run",
601
+ summary="Cancel Workflow Run",
602
+ description=(
603
+ "Cancel a currently executing workflow run, stopping all active steps and cleanup.\n"
604
+ "**Note:** Complex workflows with multiple parallel steps may take time to fully cancel."
605
+ ),
606
+ responses={
607
+ 200: {},
608
+ 404: {"description": "Workflow or run not found", "model": NotFoundResponse},
609
+ 500: {"description": "Failed to cancel workflow run", "model": InternalServerErrorResponse},
610
+ },
611
+ dependencies=[Depends(require_resource_access("workflows", "run", "workflow_id"))],
612
+ )
613
+ async def cancel_workflow_run(workflow_id: str, run_id: str):
614
+ workflow = get_workflow_by_id(workflow_id, os.workflows)
615
+
616
+ if workflow is None:
617
+ raise HTTPException(status_code=404, detail="Workflow not found")
618
+
619
+ if not workflow.cancel_run(run_id=run_id):
620
+ raise HTTPException(status_code=500, detail="Failed to cancel run")
621
+
622
+ return JSONResponse(content={}, status_code=200)
623
+
624
+ return router