agno 1.8.1__py3-none-any.whl → 2.0.0__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 (590) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +19 -27
  3. agno/agent/agent.py +3143 -4170
  4. agno/api/agent.py +11 -67
  5. agno/api/api.py +5 -46
  6. agno/api/evals.py +8 -19
  7. agno/api/os.py +17 -0
  8. agno/api/routes.py +6 -41
  9. agno/api/schemas/__init__.py +9 -0
  10. agno/api/schemas/agent.py +5 -21
  11. agno/api/schemas/evals.py +7 -16
  12. agno/api/schemas/os.py +14 -0
  13. agno/api/schemas/team.py +5 -21
  14. agno/api/schemas/utils.py +21 -0
  15. agno/api/schemas/workflows.py +11 -7
  16. agno/api/settings.py +53 -0
  17. agno/api/team.py +11 -66
  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/db/__init__.py +24 -0
  25. agno/db/base.py +245 -0
  26. agno/db/dynamo/__init__.py +3 -0
  27. agno/db/dynamo/dynamo.py +1743 -0
  28. agno/db/dynamo/schemas.py +278 -0
  29. agno/db/dynamo/utils.py +684 -0
  30. agno/db/firestore/__init__.py +3 -0
  31. agno/db/firestore/firestore.py +1432 -0
  32. agno/db/firestore/schemas.py +130 -0
  33. agno/db/firestore/utils.py +278 -0
  34. agno/db/gcs_json/__init__.py +3 -0
  35. agno/db/gcs_json/gcs_json_db.py +1001 -0
  36. agno/db/gcs_json/utils.py +194 -0
  37. agno/db/in_memory/__init__.py +3 -0
  38. agno/db/in_memory/in_memory_db.py +882 -0
  39. agno/db/in_memory/utils.py +172 -0
  40. agno/db/json/__init__.py +3 -0
  41. agno/db/json/json_db.py +1045 -0
  42. agno/db/json/utils.py +196 -0
  43. agno/db/migrations/v1_to_v2.py +162 -0
  44. agno/db/mongo/__init__.py +3 -0
  45. agno/db/mongo/mongo.py +1416 -0
  46. agno/db/mongo/schemas.py +77 -0
  47. agno/db/mongo/utils.py +204 -0
  48. agno/db/mysql/__init__.py +3 -0
  49. agno/db/mysql/mysql.py +1719 -0
  50. agno/db/mysql/schemas.py +124 -0
  51. agno/db/mysql/utils.py +297 -0
  52. agno/db/postgres/__init__.py +3 -0
  53. agno/db/postgres/postgres.py +1710 -0
  54. agno/db/postgres/schemas.py +124 -0
  55. agno/db/postgres/utils.py +280 -0
  56. agno/db/redis/__init__.py +3 -0
  57. agno/db/redis/redis.py +1367 -0
  58. agno/db/redis/schemas.py +109 -0
  59. agno/db/redis/utils.py +288 -0
  60. agno/db/schemas/__init__.py +3 -0
  61. agno/db/schemas/evals.py +33 -0
  62. agno/db/schemas/knowledge.py +40 -0
  63. agno/db/schemas/memory.py +46 -0
  64. agno/db/singlestore/__init__.py +3 -0
  65. agno/db/singlestore/schemas.py +116 -0
  66. agno/db/singlestore/singlestore.py +1712 -0
  67. agno/db/singlestore/utils.py +326 -0
  68. agno/db/sqlite/__init__.py +3 -0
  69. agno/db/sqlite/schemas.py +119 -0
  70. agno/db/sqlite/sqlite.py +1676 -0
  71. agno/db/sqlite/utils.py +268 -0
  72. agno/db/utils.py +88 -0
  73. agno/eval/__init__.py +14 -0
  74. agno/eval/accuracy.py +154 -48
  75. agno/eval/performance.py +88 -23
  76. agno/eval/reliability.py +73 -20
  77. agno/eval/utils.py +23 -13
  78. agno/integrations/discord/__init__.py +3 -0
  79. agno/{app → integrations}/discord/client.py +15 -11
  80. agno/knowledge/__init__.py +2 -2
  81. agno/{document → knowledge}/chunking/agentic.py +2 -2
  82. agno/{document → knowledge}/chunking/document.py +2 -2
  83. agno/{document → knowledge}/chunking/fixed.py +3 -3
  84. agno/{document → knowledge}/chunking/markdown.py +2 -2
  85. agno/{document → knowledge}/chunking/recursive.py +2 -2
  86. agno/{document → knowledge}/chunking/row.py +2 -2
  87. agno/knowledge/chunking/semantic.py +59 -0
  88. agno/knowledge/chunking/strategy.py +121 -0
  89. agno/knowledge/content.py +74 -0
  90. agno/knowledge/document/__init__.py +5 -0
  91. agno/{document → knowledge/document}/base.py +12 -2
  92. agno/knowledge/embedder/__init__.py +5 -0
  93. agno/{embedder → knowledge/embedder}/aws_bedrock.py +127 -1
  94. agno/{embedder → knowledge/embedder}/azure_openai.py +65 -1
  95. agno/{embedder → knowledge/embedder}/base.py +6 -0
  96. agno/{embedder → knowledge/embedder}/cohere.py +72 -1
  97. agno/{embedder → knowledge/embedder}/fastembed.py +17 -1
  98. agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
  99. agno/{embedder → knowledge/embedder}/google.py +74 -1
  100. agno/{embedder → knowledge/embedder}/huggingface.py +36 -2
  101. agno/{embedder → knowledge/embedder}/jina.py +48 -2
  102. agno/knowledge/embedder/langdb.py +22 -0
  103. agno/knowledge/embedder/mistral.py +139 -0
  104. agno/{embedder → knowledge/embedder}/nebius.py +1 -1
  105. agno/{embedder → knowledge/embedder}/ollama.py +54 -3
  106. agno/knowledge/embedder/openai.py +223 -0
  107. agno/{embedder → knowledge/embedder}/sentence_transformer.py +16 -1
  108. agno/{embedder → knowledge/embedder}/together.py +1 -1
  109. agno/{embedder → knowledge/embedder}/voyageai.py +49 -1
  110. agno/knowledge/knowledge.py +1551 -0
  111. agno/knowledge/reader/__init__.py +7 -0
  112. agno/{document → knowledge}/reader/arxiv_reader.py +32 -4
  113. agno/knowledge/reader/base.py +88 -0
  114. agno/{document → knowledge}/reader/csv_reader.py +47 -65
  115. agno/knowledge/reader/docx_reader.py +83 -0
  116. agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
  117. agno/{document → knowledge}/reader/json_reader.py +30 -9
  118. agno/{document → knowledge}/reader/markdown_reader.py +58 -9
  119. agno/{document → knowledge}/reader/pdf_reader.py +71 -126
  120. agno/knowledge/reader/reader_factory.py +268 -0
  121. agno/knowledge/reader/s3_reader.py +101 -0
  122. agno/{document → knowledge}/reader/text_reader.py +31 -10
  123. agno/knowledge/reader/url_reader.py +128 -0
  124. agno/knowledge/reader/web_search_reader.py +366 -0
  125. agno/{document → knowledge}/reader/website_reader.py +37 -10
  126. agno/knowledge/reader/wikipedia_reader.py +59 -0
  127. agno/knowledge/reader/youtube_reader.py +78 -0
  128. agno/knowledge/remote_content/remote_content.py +88 -0
  129. agno/{reranker → knowledge/reranker}/base.py +1 -1
  130. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  131. agno/{reranker → knowledge/reranker}/infinity.py +2 -2
  132. agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
  133. agno/knowledge/types.py +30 -0
  134. agno/knowledge/utils.py +169 -0
  135. agno/media.py +269 -268
  136. agno/memory/__init__.py +2 -10
  137. agno/memory/manager.py +1003 -148
  138. agno/models/aimlapi/__init__.py +2 -2
  139. agno/models/aimlapi/aimlapi.py +6 -6
  140. agno/models/anthropic/claude.py +131 -131
  141. agno/models/aws/bedrock.py +110 -182
  142. agno/models/aws/claude.py +64 -18
  143. agno/models/azure/ai_foundry.py +73 -23
  144. agno/models/base.py +346 -290
  145. agno/models/cerebras/cerebras.py +84 -27
  146. agno/models/cohere/chat.py +106 -98
  147. agno/models/google/gemini.py +105 -46
  148. agno/models/groq/groq.py +97 -35
  149. agno/models/huggingface/huggingface.py +92 -27
  150. agno/models/ibm/watsonx.py +72 -13
  151. agno/models/litellm/chat.py +85 -13
  152. agno/models/message.py +46 -151
  153. agno/models/meta/llama.py +85 -49
  154. agno/models/metrics.py +120 -0
  155. agno/models/mistral/mistral.py +90 -21
  156. agno/models/ollama/__init__.py +0 -2
  157. agno/models/ollama/chat.py +85 -47
  158. agno/models/openai/chat.py +154 -37
  159. agno/models/openai/responses.py +178 -105
  160. agno/models/perplexity/perplexity.py +26 -2
  161. agno/models/portkey/portkey.py +0 -7
  162. agno/models/response.py +15 -9
  163. agno/models/utils.py +20 -0
  164. agno/models/vercel/__init__.py +2 -2
  165. agno/models/vercel/v0.py +1 -1
  166. agno/models/vllm/__init__.py +2 -2
  167. agno/models/vllm/vllm.py +3 -3
  168. agno/models/xai/xai.py +10 -10
  169. agno/os/__init__.py +3 -0
  170. agno/os/app.py +497 -0
  171. agno/os/auth.py +47 -0
  172. agno/os/config.py +103 -0
  173. agno/os/interfaces/agui/__init__.py +3 -0
  174. agno/os/interfaces/agui/agui.py +31 -0
  175. agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
  176. agno/{app → os/interfaces}/agui/utils.py +77 -33
  177. agno/os/interfaces/base.py +21 -0
  178. agno/os/interfaces/slack/__init__.py +3 -0
  179. agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
  180. agno/os/interfaces/slack/slack.py +32 -0
  181. agno/os/interfaces/whatsapp/__init__.py +3 -0
  182. agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
  183. agno/os/interfaces/whatsapp/whatsapp.py +29 -0
  184. agno/os/mcp.py +235 -0
  185. agno/os/router.py +1400 -0
  186. agno/os/routers/__init__.py +3 -0
  187. agno/os/routers/evals/__init__.py +3 -0
  188. agno/os/routers/evals/evals.py +393 -0
  189. agno/os/routers/evals/schemas.py +142 -0
  190. agno/os/routers/evals/utils.py +161 -0
  191. agno/os/routers/knowledge/__init__.py +3 -0
  192. agno/os/routers/knowledge/knowledge.py +850 -0
  193. agno/os/routers/knowledge/schemas.py +118 -0
  194. agno/os/routers/memory/__init__.py +3 -0
  195. agno/os/routers/memory/memory.py +410 -0
  196. agno/os/routers/memory/schemas.py +58 -0
  197. agno/os/routers/metrics/__init__.py +3 -0
  198. agno/os/routers/metrics/metrics.py +178 -0
  199. agno/os/routers/metrics/schemas.py +47 -0
  200. agno/os/routers/session/__init__.py +3 -0
  201. agno/os/routers/session/session.py +536 -0
  202. agno/os/schema.py +945 -0
  203. agno/{app/playground → os}/settings.py +7 -15
  204. agno/os/utils.py +270 -0
  205. agno/reasoning/azure_ai_foundry.py +4 -4
  206. agno/reasoning/deepseek.py +4 -4
  207. agno/reasoning/default.py +6 -11
  208. agno/reasoning/groq.py +4 -4
  209. agno/reasoning/helpers.py +4 -6
  210. agno/reasoning/ollama.py +4 -4
  211. agno/reasoning/openai.py +4 -4
  212. agno/run/agent.py +633 -0
  213. agno/run/base.py +53 -77
  214. agno/run/cancel.py +81 -0
  215. agno/run/team.py +243 -96
  216. agno/run/workflow.py +550 -12
  217. agno/session/__init__.py +10 -0
  218. agno/session/agent.py +244 -0
  219. agno/session/summary.py +225 -0
  220. agno/session/team.py +262 -0
  221. agno/{storage/session/v2 → session}/workflow.py +47 -24
  222. agno/team/__init__.py +15 -16
  223. agno/team/team.py +3260 -4824
  224. agno/tools/agentql.py +14 -5
  225. agno/tools/airflow.py +9 -4
  226. agno/tools/api.py +7 -3
  227. agno/tools/apify.py +2 -46
  228. agno/tools/arxiv.py +8 -3
  229. agno/tools/aws_lambda.py +7 -5
  230. agno/tools/aws_ses.py +7 -1
  231. agno/tools/baidusearch.py +4 -1
  232. agno/tools/bitbucket.py +4 -4
  233. agno/tools/brandfetch.py +14 -11
  234. agno/tools/bravesearch.py +4 -1
  235. agno/tools/brightdata.py +43 -23
  236. agno/tools/browserbase.py +13 -4
  237. agno/tools/calcom.py +12 -10
  238. agno/tools/calculator.py +10 -27
  239. agno/tools/cartesia.py +20 -17
  240. agno/tools/{clickup_tool.py → clickup.py} +12 -25
  241. agno/tools/confluence.py +8 -8
  242. agno/tools/crawl4ai.py +7 -1
  243. agno/tools/csv_toolkit.py +9 -8
  244. agno/tools/dalle.py +22 -12
  245. agno/tools/daytona.py +13 -16
  246. agno/tools/decorator.py +6 -3
  247. agno/tools/desi_vocal.py +17 -8
  248. agno/tools/discord.py +11 -8
  249. agno/tools/docker.py +30 -42
  250. agno/tools/duckdb.py +34 -53
  251. agno/tools/duckduckgo.py +8 -7
  252. agno/tools/e2b.py +62 -62
  253. agno/tools/eleven_labs.py +36 -29
  254. agno/tools/email.py +4 -1
  255. agno/tools/evm.py +7 -1
  256. agno/tools/exa.py +19 -14
  257. agno/tools/fal.py +30 -30
  258. agno/tools/file.py +9 -8
  259. agno/tools/financial_datasets.py +25 -44
  260. agno/tools/firecrawl.py +22 -22
  261. agno/tools/function.py +127 -18
  262. agno/tools/giphy.py +23 -11
  263. agno/tools/github.py +48 -126
  264. agno/tools/gmail.py +45 -61
  265. agno/tools/google_bigquery.py +7 -6
  266. agno/tools/google_maps.py +11 -26
  267. agno/tools/googlesearch.py +7 -2
  268. agno/tools/googlesheets.py +21 -17
  269. agno/tools/hackernews.py +9 -5
  270. agno/tools/jina.py +5 -4
  271. agno/tools/jira.py +18 -9
  272. agno/tools/knowledge.py +31 -32
  273. agno/tools/linear.py +19 -34
  274. agno/tools/linkup.py +5 -1
  275. agno/tools/local_file_system.py +8 -5
  276. agno/tools/lumalab.py +32 -20
  277. agno/tools/mcp.py +1 -2
  278. agno/tools/mem0.py +18 -12
  279. agno/tools/memori.py +14 -10
  280. agno/tools/mlx_transcribe.py +3 -2
  281. agno/tools/models/azure_openai.py +33 -15
  282. agno/tools/models/gemini.py +59 -32
  283. agno/tools/models/groq.py +30 -23
  284. agno/tools/models/nebius.py +28 -12
  285. agno/tools/models_labs.py +40 -16
  286. agno/tools/moviepy_video.py +7 -6
  287. agno/tools/neo4j.py +10 -8
  288. agno/tools/newspaper.py +7 -2
  289. agno/tools/newspaper4k.py +8 -3
  290. agno/tools/openai.py +58 -32
  291. agno/tools/openbb.py +12 -11
  292. agno/tools/opencv.py +63 -47
  293. agno/tools/openweather.py +14 -12
  294. agno/tools/pandas.py +11 -3
  295. agno/tools/postgres.py +4 -12
  296. agno/tools/pubmed.py +4 -1
  297. agno/tools/python.py +9 -22
  298. agno/tools/reasoning.py +35 -27
  299. agno/tools/reddit.py +11 -26
  300. agno/tools/replicate.py +55 -42
  301. agno/tools/resend.py +4 -1
  302. agno/tools/scrapegraph.py +15 -14
  303. agno/tools/searxng.py +10 -23
  304. agno/tools/serpapi.py +6 -3
  305. agno/tools/serper.py +13 -4
  306. agno/tools/shell.py +9 -2
  307. agno/tools/slack.py +12 -11
  308. agno/tools/sleep.py +3 -2
  309. agno/tools/spider.py +24 -4
  310. agno/tools/sql.py +7 -6
  311. agno/tools/tavily.py +6 -4
  312. agno/tools/telegram.py +12 -4
  313. agno/tools/todoist.py +11 -31
  314. agno/tools/toolkit.py +1 -1
  315. agno/tools/trafilatura.py +22 -6
  316. agno/tools/trello.py +9 -22
  317. agno/tools/twilio.py +10 -3
  318. agno/tools/user_control_flow.py +6 -1
  319. agno/tools/valyu.py +34 -5
  320. agno/tools/visualization.py +19 -28
  321. agno/tools/webbrowser.py +4 -3
  322. agno/tools/webex.py +11 -7
  323. agno/tools/website.py +15 -46
  324. agno/tools/webtools.py +12 -4
  325. agno/tools/whatsapp.py +5 -9
  326. agno/tools/wikipedia.py +20 -13
  327. agno/tools/x.py +14 -13
  328. agno/tools/yfinance.py +13 -40
  329. agno/tools/youtube.py +26 -20
  330. agno/tools/zendesk.py +7 -2
  331. agno/tools/zep.py +10 -7
  332. agno/tools/zoom.py +10 -9
  333. agno/utils/common.py +1 -19
  334. agno/utils/events.py +100 -123
  335. agno/utils/gemini.py +32 -2
  336. agno/utils/knowledge.py +29 -0
  337. agno/utils/log.py +54 -4
  338. agno/utils/mcp.py +68 -10
  339. agno/utils/media.py +39 -0
  340. agno/utils/message.py +12 -1
  341. agno/utils/models/aws_claude.py +1 -1
  342. agno/utils/models/claude.py +47 -4
  343. agno/utils/models/cohere.py +1 -1
  344. agno/utils/models/mistral.py +8 -7
  345. agno/utils/models/schema_utils.py +3 -3
  346. agno/utils/models/watsonx.py +1 -1
  347. agno/utils/openai.py +1 -1
  348. agno/utils/pprint.py +33 -32
  349. agno/utils/print_response/agent.py +779 -0
  350. agno/utils/print_response/team.py +1669 -0
  351. agno/utils/print_response/workflow.py +1451 -0
  352. agno/utils/prompts.py +14 -14
  353. agno/utils/reasoning.py +87 -0
  354. agno/utils/response.py +42 -42
  355. agno/utils/streamlit.py +481 -0
  356. agno/utils/string.py +8 -22
  357. agno/utils/team.py +50 -0
  358. agno/utils/timer.py +2 -2
  359. agno/vectordb/base.py +33 -21
  360. agno/vectordb/cassandra/cassandra.py +287 -23
  361. agno/vectordb/chroma/chromadb.py +482 -59
  362. agno/vectordb/clickhouse/clickhousedb.py +270 -63
  363. agno/vectordb/couchbase/couchbase.py +309 -29
  364. agno/vectordb/lancedb/lance_db.py +360 -21
  365. agno/vectordb/langchaindb/__init__.py +5 -0
  366. agno/vectordb/langchaindb/langchaindb.py +145 -0
  367. agno/vectordb/lightrag/__init__.py +5 -0
  368. agno/vectordb/lightrag/lightrag.py +374 -0
  369. agno/vectordb/llamaindex/llamaindexdb.py +127 -0
  370. agno/vectordb/milvus/milvus.py +242 -32
  371. agno/vectordb/mongodb/mongodb.py +200 -24
  372. agno/vectordb/pgvector/pgvector.py +319 -37
  373. agno/vectordb/pineconedb/pineconedb.py +221 -27
  374. agno/vectordb/qdrant/qdrant.py +334 -14
  375. agno/vectordb/singlestore/singlestore.py +286 -29
  376. agno/vectordb/surrealdb/surrealdb.py +187 -7
  377. agno/vectordb/upstashdb/upstashdb.py +342 -26
  378. agno/vectordb/weaviate/weaviate.py +227 -165
  379. agno/workflow/__init__.py +17 -13
  380. agno/workflow/{v2/condition.py → condition.py} +135 -32
  381. agno/workflow/{v2/loop.py → loop.py} +115 -28
  382. agno/workflow/{v2/parallel.py → parallel.py} +138 -108
  383. agno/workflow/{v2/router.py → router.py} +133 -32
  384. agno/workflow/{v2/step.py → step.py} +207 -49
  385. agno/workflow/{v2/steps.py → steps.py} +147 -66
  386. agno/workflow/types.py +482 -0
  387. agno/workflow/workflow.py +2410 -696
  388. agno-2.0.0.dist-info/METADATA +494 -0
  389. agno-2.0.0.dist-info/RECORD +515 -0
  390. agno-2.0.0.dist-info/licenses/LICENSE +201 -0
  391. agno/agent/metrics.py +0 -107
  392. agno/api/app.py +0 -35
  393. agno/api/playground.py +0 -92
  394. agno/api/schemas/app.py +0 -12
  395. agno/api/schemas/playground.py +0 -22
  396. agno/api/schemas/user.py +0 -35
  397. agno/api/schemas/workspace.py +0 -46
  398. agno/api/user.py +0 -160
  399. agno/api/workflows.py +0 -33
  400. agno/api/workspace.py +0 -175
  401. agno/app/agui/__init__.py +0 -3
  402. agno/app/agui/app.py +0 -17
  403. agno/app/agui/sync_router.py +0 -120
  404. agno/app/base.py +0 -186
  405. agno/app/discord/__init__.py +0 -3
  406. agno/app/fastapi/__init__.py +0 -3
  407. agno/app/fastapi/app.py +0 -107
  408. agno/app/fastapi/async_router.py +0 -457
  409. agno/app/fastapi/sync_router.py +0 -448
  410. agno/app/playground/app.py +0 -228
  411. agno/app/playground/async_router.py +0 -1050
  412. agno/app/playground/deploy.py +0 -249
  413. agno/app/playground/operator.py +0 -183
  414. agno/app/playground/schemas.py +0 -220
  415. agno/app/playground/serve.py +0 -55
  416. agno/app/playground/sync_router.py +0 -1042
  417. agno/app/playground/utils.py +0 -46
  418. agno/app/settings.py +0 -15
  419. agno/app/slack/__init__.py +0 -3
  420. agno/app/slack/app.py +0 -19
  421. agno/app/slack/sync_router.py +0 -92
  422. agno/app/utils.py +0 -54
  423. agno/app/whatsapp/__init__.py +0 -3
  424. agno/app/whatsapp/app.py +0 -15
  425. agno/app/whatsapp/sync_router.py +0 -197
  426. agno/cli/auth_server.py +0 -249
  427. agno/cli/config.py +0 -274
  428. agno/cli/console.py +0 -88
  429. agno/cli/credentials.py +0 -23
  430. agno/cli/entrypoint.py +0 -571
  431. agno/cli/operator.py +0 -357
  432. agno/cli/settings.py +0 -96
  433. agno/cli/ws/ws_cli.py +0 -817
  434. agno/constants.py +0 -13
  435. agno/document/__init__.py +0 -5
  436. agno/document/chunking/semantic.py +0 -45
  437. agno/document/chunking/strategy.py +0 -31
  438. agno/document/reader/__init__.py +0 -5
  439. agno/document/reader/base.py +0 -47
  440. agno/document/reader/docx_reader.py +0 -60
  441. agno/document/reader/gcs/pdf_reader.py +0 -44
  442. agno/document/reader/s3/pdf_reader.py +0 -59
  443. agno/document/reader/s3/text_reader.py +0 -63
  444. agno/document/reader/url_reader.py +0 -59
  445. agno/document/reader/youtube_reader.py +0 -58
  446. agno/embedder/__init__.py +0 -5
  447. agno/embedder/langdb.py +0 -80
  448. agno/embedder/mistral.py +0 -82
  449. agno/embedder/openai.py +0 -78
  450. agno/file/__init__.py +0 -5
  451. agno/file/file.py +0 -16
  452. agno/file/local/csv.py +0 -32
  453. agno/file/local/txt.py +0 -19
  454. agno/infra/app.py +0 -240
  455. agno/infra/base.py +0 -144
  456. agno/infra/context.py +0 -20
  457. agno/infra/db_app.py +0 -52
  458. agno/infra/resource.py +0 -205
  459. agno/infra/resources.py +0 -55
  460. agno/knowledge/agent.py +0 -702
  461. agno/knowledge/arxiv.py +0 -33
  462. agno/knowledge/combined.py +0 -36
  463. agno/knowledge/csv.py +0 -144
  464. agno/knowledge/csv_url.py +0 -124
  465. agno/knowledge/document.py +0 -223
  466. agno/knowledge/docx.py +0 -137
  467. agno/knowledge/firecrawl.py +0 -34
  468. agno/knowledge/gcs/__init__.py +0 -0
  469. agno/knowledge/gcs/base.py +0 -39
  470. agno/knowledge/gcs/pdf.py +0 -125
  471. agno/knowledge/json.py +0 -137
  472. agno/knowledge/langchain.py +0 -71
  473. agno/knowledge/light_rag.py +0 -273
  474. agno/knowledge/llamaindex.py +0 -66
  475. agno/knowledge/markdown.py +0 -154
  476. agno/knowledge/pdf.py +0 -164
  477. agno/knowledge/pdf_bytes.py +0 -42
  478. agno/knowledge/pdf_url.py +0 -148
  479. agno/knowledge/s3/__init__.py +0 -0
  480. agno/knowledge/s3/base.py +0 -64
  481. agno/knowledge/s3/pdf.py +0 -33
  482. agno/knowledge/s3/text.py +0 -34
  483. agno/knowledge/text.py +0 -141
  484. agno/knowledge/url.py +0 -46
  485. agno/knowledge/website.py +0 -179
  486. agno/knowledge/wikipedia.py +0 -32
  487. agno/knowledge/youtube.py +0 -35
  488. agno/memory/agent.py +0 -423
  489. agno/memory/classifier.py +0 -104
  490. agno/memory/db/__init__.py +0 -5
  491. agno/memory/db/base.py +0 -42
  492. agno/memory/db/mongodb.py +0 -189
  493. agno/memory/db/postgres.py +0 -203
  494. agno/memory/db/sqlite.py +0 -193
  495. agno/memory/memory.py +0 -22
  496. agno/memory/row.py +0 -36
  497. agno/memory/summarizer.py +0 -201
  498. agno/memory/summary.py +0 -19
  499. agno/memory/team.py +0 -415
  500. agno/memory/v2/__init__.py +0 -2
  501. agno/memory/v2/db/__init__.py +0 -1
  502. agno/memory/v2/db/base.py +0 -42
  503. agno/memory/v2/db/firestore.py +0 -339
  504. agno/memory/v2/db/mongodb.py +0 -196
  505. agno/memory/v2/db/postgres.py +0 -214
  506. agno/memory/v2/db/redis.py +0 -187
  507. agno/memory/v2/db/schema.py +0 -54
  508. agno/memory/v2/db/sqlite.py +0 -209
  509. agno/memory/v2/manager.py +0 -437
  510. agno/memory/v2/memory.py +0 -1097
  511. agno/memory/v2/schema.py +0 -55
  512. agno/memory/v2/summarizer.py +0 -215
  513. agno/memory/workflow.py +0 -38
  514. agno/models/ollama/tools.py +0 -430
  515. agno/models/qwen/__init__.py +0 -5
  516. agno/playground/__init__.py +0 -10
  517. agno/playground/deploy.py +0 -3
  518. agno/playground/playground.py +0 -3
  519. agno/playground/serve.py +0 -3
  520. agno/playground/settings.py +0 -3
  521. agno/reranker/__init__.py +0 -0
  522. agno/run/response.py +0 -467
  523. agno/run/v2/__init__.py +0 -0
  524. agno/run/v2/workflow.py +0 -567
  525. agno/storage/__init__.py +0 -0
  526. agno/storage/agent/__init__.py +0 -0
  527. agno/storage/agent/dynamodb.py +0 -1
  528. agno/storage/agent/json.py +0 -1
  529. agno/storage/agent/mongodb.py +0 -1
  530. agno/storage/agent/postgres.py +0 -1
  531. agno/storage/agent/singlestore.py +0 -1
  532. agno/storage/agent/sqlite.py +0 -1
  533. agno/storage/agent/yaml.py +0 -1
  534. agno/storage/base.py +0 -60
  535. agno/storage/dynamodb.py +0 -673
  536. agno/storage/firestore.py +0 -297
  537. agno/storage/gcs_json.py +0 -261
  538. agno/storage/in_memory.py +0 -234
  539. agno/storage/json.py +0 -237
  540. agno/storage/mongodb.py +0 -328
  541. agno/storage/mysql.py +0 -685
  542. agno/storage/postgres.py +0 -682
  543. agno/storage/redis.py +0 -336
  544. agno/storage/session/__init__.py +0 -16
  545. agno/storage/session/agent.py +0 -64
  546. agno/storage/session/team.py +0 -63
  547. agno/storage/session/v2/__init__.py +0 -5
  548. agno/storage/session/workflow.py +0 -61
  549. agno/storage/singlestore.py +0 -606
  550. agno/storage/sqlite.py +0 -646
  551. agno/storage/workflow/__init__.py +0 -0
  552. agno/storage/workflow/mongodb.py +0 -1
  553. agno/storage/workflow/postgres.py +0 -1
  554. agno/storage/workflow/sqlite.py +0 -1
  555. agno/storage/yaml.py +0 -241
  556. agno/tools/thinking.py +0 -73
  557. agno/utils/defaults.py +0 -57
  558. agno/utils/filesystem.py +0 -39
  559. agno/utils/git.py +0 -52
  560. agno/utils/json_io.py +0 -30
  561. agno/utils/load_env.py +0 -19
  562. agno/utils/py_io.py +0 -19
  563. agno/utils/pyproject.py +0 -18
  564. agno/utils/resource_filter.py +0 -31
  565. agno/workflow/v2/__init__.py +0 -21
  566. agno/workflow/v2/types.py +0 -357
  567. agno/workflow/v2/workflow.py +0 -3312
  568. agno/workspace/__init__.py +0 -0
  569. agno/workspace/config.py +0 -325
  570. agno/workspace/enums.py +0 -6
  571. agno/workspace/helpers.py +0 -52
  572. agno/workspace/operator.py +0 -757
  573. agno/workspace/settings.py +0 -158
  574. agno-1.8.1.dist-info/METADATA +0 -982
  575. agno-1.8.1.dist-info/RECORD +0 -566
  576. agno-1.8.1.dist-info/entry_points.txt +0 -3
  577. agno-1.8.1.dist-info/licenses/LICENSE +0 -375
  578. /agno/{app → db/migrations}/__init__.py +0 -0
  579. /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
  580. /agno/{cli → integrations}/__init__.py +0 -0
  581. /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
  582. /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
  583. /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
  584. /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
  585. /agno/{app → os/interfaces}/slack/security.py +0 -0
  586. /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
  587. /agno/{file/local → utils/print_response}/__init__.py +0 -0
  588. /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
  589. {agno-1.8.1.dist-info → agno-2.0.0.dist-info}/WHEEL +0 -0
  590. {agno-1.8.1.dist-info → agno-2.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,850 @@
1
+ import json
2
+ import logging
3
+ import math
4
+ from typing import Dict, List, Optional
5
+ from uuid import uuid4
6
+
7
+ from fastapi import APIRouter, BackgroundTasks, Depends, File, Form, HTTPException, Path, Query, UploadFile
8
+
9
+ from agno.knowledge.content import Content, FileData
10
+ from agno.knowledge.knowledge import Knowledge
11
+ from agno.knowledge.reader import ReaderFactory
12
+ from agno.knowledge.reader.base import Reader
13
+ from agno.knowledge.utils import get_all_chunkers_info, get_all_readers_info, get_content_types_to_readers_mapping
14
+ from agno.os.auth import get_authentication_dependency
15
+ from agno.os.routers.knowledge.schemas import (
16
+ ChunkerSchema,
17
+ ConfigResponseSchema,
18
+ ContentResponseSchema,
19
+ ContentStatus,
20
+ ContentStatusResponse,
21
+ ContentUpdateSchema,
22
+ ReaderSchema,
23
+ )
24
+ from agno.os.schema import (
25
+ BadRequestResponse,
26
+ InternalServerErrorResponse,
27
+ NotFoundResponse,
28
+ PaginatedResponse,
29
+ PaginationInfo,
30
+ SortOrder,
31
+ UnauthenticatedResponse,
32
+ ValidationErrorResponse,
33
+ )
34
+ from agno.os.settings import AgnoAPISettings
35
+ from agno.os.utils import get_knowledge_instance_by_db_id
36
+ from agno.utils.log import log_debug, log_info
37
+
38
+ logger = logging.getLogger(__name__)
39
+
40
+
41
+ def get_knowledge_router(
42
+ knowledge_instances: List[Knowledge], settings: AgnoAPISettings = AgnoAPISettings()
43
+ ) -> APIRouter:
44
+ """Create knowledge router with comprehensive OpenAPI documentation for content management endpoints."""
45
+ router = APIRouter(
46
+ dependencies=[Depends(get_authentication_dependency(settings))],
47
+ tags=["Knowledge"],
48
+ responses={
49
+ 400: {"description": "Bad Request", "model": BadRequestResponse},
50
+ 401: {"description": "Unauthorized", "model": UnauthenticatedResponse},
51
+ 404: {"description": "Not Found", "model": NotFoundResponse},
52
+ 422: {"description": "Validation Error", "model": ValidationErrorResponse},
53
+ 500: {"description": "Internal Server Error", "model": InternalServerErrorResponse},
54
+ },
55
+ )
56
+ return attach_routes(router=router, knowledge_instances=knowledge_instances)
57
+
58
+
59
+ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> APIRouter:
60
+ @router.post(
61
+ "/knowledge/content",
62
+ response_model=ContentResponseSchema,
63
+ status_code=202,
64
+ operation_id="upload_content",
65
+ summary="Upload Content",
66
+ description=(
67
+ "Upload content to the knowledge base. Supports file uploads, text content, or URLs. "
68
+ "Content is processed asynchronously in the background. Supports custom readers and chunking strategies."
69
+ ),
70
+ responses={
71
+ 202: {
72
+ "description": "Content upload accepted for processing",
73
+ "content": {
74
+ "application/json": {
75
+ "example": {
76
+ "id": "content-123",
77
+ "name": "example-document.pdf",
78
+ "description": "Sample document for processing",
79
+ "metadata": {"category": "documentation", "priority": "high"},
80
+ "status": "processing",
81
+ }
82
+ }
83
+ },
84
+ },
85
+ 400: {
86
+ "description": "Invalid request - malformed metadata or missing content",
87
+ "model": BadRequestResponse,
88
+ },
89
+ 422: {"description": "Validation error in form data", "model": ValidationErrorResponse},
90
+ },
91
+ )
92
+ async def upload_content(
93
+ background_tasks: BackgroundTasks,
94
+ name: Optional[str] = Form(None, description="Content name (auto-generated from file/URL if not provided)"),
95
+ description: Optional[str] = Form(None, description="Content description for context"),
96
+ url: Optional[str] = Form(None, description="URL to fetch content from (JSON array or single URL string)"),
97
+ metadata: Optional[str] = Form(None, description="JSON metadata object for additional content properties"),
98
+ file: Optional[UploadFile] = File(None, description="File to upload for processing"),
99
+ text_content: Optional[str] = Form(None, description="Raw text content to process"),
100
+ reader_id: Optional[str] = Form(None, description="ID of the reader to use for content processing"),
101
+ chunker: Optional[str] = Form(None, description="Chunking strategy to apply during processing"),
102
+ db_id: Optional[str] = Query(default=None, description="Database ID to use for content storage"),
103
+ ):
104
+ knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
105
+ content_id = str(uuid4())
106
+ log_info(f"Adding content: {name}, {description}, {url}, {metadata} with ID: {content_id}")
107
+
108
+ parsed_metadata = None
109
+ if metadata:
110
+ try:
111
+ parsed_metadata = json.loads(metadata)
112
+ except json.JSONDecodeError:
113
+ # If it's not valid JSON, treat as a simple key-value pair
114
+ parsed_metadata = {"value": metadata} if metadata != "string" else None
115
+ if file:
116
+ content_bytes = await file.read()
117
+ elif text_content:
118
+ content_bytes = text_content.encode("utf-8")
119
+ else:
120
+ content_bytes = None
121
+
122
+ parsed_urls = None
123
+ if url and url.strip():
124
+ try:
125
+ parsed_urls = json.loads(url)
126
+ log_debug(f"Parsed URLs: {parsed_urls}")
127
+ except json.JSONDecodeError:
128
+ # If it's not valid JSON, treat as a single URL string
129
+ parsed_urls = url
130
+
131
+ # # Parse metadata with proper error handling
132
+ parsed_metadata = None
133
+ if metadata:
134
+ try:
135
+ parsed_metadata = json.loads(metadata)
136
+ except json.JSONDecodeError:
137
+ # If it's not valid JSON, treat as a simple key-value pair
138
+ parsed_metadata = {"value": metadata}
139
+
140
+ if text_content:
141
+ file_data = FileData(
142
+ content=content_bytes,
143
+ type="manual",
144
+ )
145
+ elif file:
146
+ file_data = FileData(
147
+ content=content_bytes,
148
+ type=file.content_type if file.content_type else None,
149
+ filename=file.filename,
150
+ size=file.size,
151
+ )
152
+ else:
153
+ file_data = None
154
+
155
+ if not name:
156
+ if file and file.filename:
157
+ name = file.filename
158
+ elif url:
159
+ name = parsed_urls
160
+
161
+ content = Content(
162
+ name=name,
163
+ description=description,
164
+ url=parsed_urls,
165
+ metadata=parsed_metadata,
166
+ file_data=file_data,
167
+ size=file.size if file else None if text_content else None,
168
+ )
169
+ background_tasks.add_task(process_content, knowledge, content_id, content, reader_id, chunker)
170
+
171
+ response = ContentResponseSchema(
172
+ id=content_id,
173
+ name=name,
174
+ description=description,
175
+ metadata=parsed_metadata,
176
+ status=ContentStatus.PROCESSING,
177
+ )
178
+ return response
179
+
180
+ @router.patch(
181
+ "/knowledge/content/{content_id}",
182
+ response_model=ContentResponseSchema,
183
+ status_code=200,
184
+ operation_id="update_content",
185
+ summary="Update Content",
186
+ description=(
187
+ "Update content properties such as name, description, metadata, or processing configuration. "
188
+ "Allows modification of existing content without re-uploading."
189
+ ),
190
+ responses={
191
+ 200: {
192
+ "description": "Content updated successfully",
193
+ "content": {
194
+ "application/json": {
195
+ "example": {
196
+ "id": "3c2fc685-d451-4d47-b0c0-b9a544c672b7",
197
+ "name": "example.pdf",
198
+ "description": "",
199
+ "type": "application/pdf",
200
+ "size": "251261",
201
+ "linked_to": None,
202
+ "metadata": {},
203
+ "access_count": 1,
204
+ "status": "completed",
205
+ "status_message": "",
206
+ "created_at": "2025-09-08T15:22:53Z",
207
+ "updated_at": "2025-09-08T15:22:54Z",
208
+ }
209
+ }
210
+ },
211
+ },
212
+ 400: {
213
+ "description": "Invalid request - malformed metadata or invalid reader_id",
214
+ "model": BadRequestResponse,
215
+ },
216
+ 404: {"description": "Content not found", "model": NotFoundResponse},
217
+ },
218
+ )
219
+ async def update_content(
220
+ content_id: str = Path(..., description="Content ID"),
221
+ name: Optional[str] = Form(None, description="Content name"),
222
+ description: Optional[str] = Form(None, description="Content description"),
223
+ metadata: Optional[str] = Form(None, description="Content metadata as JSON string"),
224
+ reader_id: Optional[str] = Form(None, description="ID of the reader to use for processing"),
225
+ db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
226
+ ) -> Optional[ContentResponseSchema]:
227
+ knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
228
+
229
+ # Parse metadata JSON string if provided
230
+ parsed_metadata = None
231
+ if metadata and metadata.strip():
232
+ try:
233
+ parsed_metadata = json.loads(metadata)
234
+ except json.JSONDecodeError:
235
+ raise HTTPException(status_code=400, detail="Invalid JSON format for metadata")
236
+
237
+ # Create ContentUpdateSchema object from form data
238
+ update_data = ContentUpdateSchema(
239
+ name=name if name and name.strip() else None,
240
+ description=description if description and description.strip() else None,
241
+ metadata=parsed_metadata,
242
+ reader_id=reader_id if reader_id and reader_id.strip() else None,
243
+ )
244
+
245
+ content = Content(
246
+ id=content_id,
247
+ name=update_data.name,
248
+ description=update_data.description,
249
+ metadata=update_data.metadata,
250
+ )
251
+
252
+ if update_data.reader_id:
253
+ if knowledge.readers and update_data.reader_id in knowledge.readers:
254
+ content.reader = knowledge.readers[update_data.reader_id]
255
+ else:
256
+ raise HTTPException(status_code=400, detail=f"Invalid reader_id: {update_data.reader_id}")
257
+
258
+ updated_content_dict = knowledge.patch_content(content)
259
+ if not updated_content_dict:
260
+ raise HTTPException(status_code=404, detail=f"Content not found: {content_id}")
261
+
262
+ return ContentResponseSchema.from_dict(updated_content_dict)
263
+
264
+ @router.get(
265
+ "/knowledge/content",
266
+ response_model=PaginatedResponse[ContentResponseSchema],
267
+ status_code=200,
268
+ operation_id="get_content",
269
+ summary="List Content",
270
+ description=(
271
+ "Retrieve paginated list of all content in the knowledge base with filtering and sorting options. "
272
+ "Filter by status, content type, or metadata properties."
273
+ ),
274
+ responses={
275
+ 200: {
276
+ "description": "Content list retrieved successfully",
277
+ "content": {
278
+ "application/json": {
279
+ "example": {
280
+ "data": [
281
+ {
282
+ "id": "3c2fc685-d451-4d47-b0c0-b9a544c672b7",
283
+ "name": "example.pdf",
284
+ "description": "",
285
+ "type": "application/pdf",
286
+ "size": "251261",
287
+ "linked_to": None,
288
+ "metadata": {},
289
+ "access_count": 1,
290
+ "status": "completed",
291
+ "status_message": "",
292
+ "created_at": "2025-09-08T15:22:53Z",
293
+ "updated_at": "2025-09-08T15:22:54Z",
294
+ },
295
+ ],
296
+ "meta": {"page": 1, "limit": 20, "total_pages": 1, "total_count": 2},
297
+ }
298
+ }
299
+ },
300
+ }
301
+ },
302
+ )
303
+ def get_content(
304
+ limit: Optional[int] = Query(default=20, description="Number of content entries to return"),
305
+ page: Optional[int] = Query(default=1, description="Page number"),
306
+ sort_by: Optional[str] = Query(default="created_at", description="Field to sort by"),
307
+ sort_order: Optional[SortOrder] = Query(default="desc", description="Sort order (asc or desc)"),
308
+ db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
309
+ ) -> PaginatedResponse[ContentResponseSchema]:
310
+ knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
311
+ contents, count = knowledge.get_content(limit=limit, page=page, sort_by=sort_by, sort_order=sort_order)
312
+
313
+ return PaginatedResponse(
314
+ data=[
315
+ ContentResponseSchema.from_dict(
316
+ {
317
+ "id": content.id,
318
+ "name": content.name,
319
+ "description": content.description,
320
+ "file_type": content.file_type,
321
+ "size": content.size,
322
+ "metadata": content.metadata,
323
+ "status": content.status,
324
+ "status_message": content.status_message,
325
+ "created_at": content.created_at,
326
+ "updated_at": content.updated_at,
327
+ }
328
+ )
329
+ for content in contents
330
+ ],
331
+ meta=PaginationInfo(
332
+ page=page,
333
+ limit=limit,
334
+ total_count=count,
335
+ total_pages=math.ceil(count / limit) if limit is not None and limit > 0 else 0,
336
+ ),
337
+ )
338
+
339
+ @router.get(
340
+ "/knowledge/content/{content_id}",
341
+ response_model=ContentResponseSchema,
342
+ status_code=200,
343
+ operation_id="get_content_by_id",
344
+ summary="Get Content by ID",
345
+ description="Retrieve detailed information about a specific content item including processing status and metadata.",
346
+ responses={
347
+ 200: {
348
+ "description": "Content details retrieved successfully",
349
+ "content": {
350
+ "application/json": {
351
+ "example": {
352
+ "id": "3c2fc685-d451-4d47-b0c0-b9a544c672b7",
353
+ "name": "example.pdf",
354
+ "description": "",
355
+ "type": "application/pdf",
356
+ "size": "251261",
357
+ "linked_to": None,
358
+ "metadata": {},
359
+ "access_count": 1,
360
+ "status": "completed",
361
+ "status_message": "",
362
+ "created_at": "2025-09-08T15:22:53Z",
363
+ "updated_at": "2025-09-08T15:22:54Z",
364
+ }
365
+ }
366
+ },
367
+ },
368
+ 404: {"description": "Content not found", "model": NotFoundResponse},
369
+ },
370
+ )
371
+ def get_content_by_id(
372
+ content_id: str,
373
+ db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
374
+ ) -> ContentResponseSchema:
375
+ log_info(f"Getting content by id: {content_id}")
376
+ knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
377
+ content = knowledge.get_content_by_id(content_id=content_id)
378
+ if not content:
379
+ raise HTTPException(status_code=404, detail=f"Content not found: {content_id}")
380
+ response = ContentResponseSchema.from_dict(
381
+ {
382
+ "id": content_id,
383
+ "name": content.name,
384
+ "description": content.description,
385
+ "file_type": content.file_type,
386
+ "size": len(content.file_data.content) if content.file_data and content.file_data.content else 0,
387
+ "metadata": content.metadata,
388
+ "status": content.status,
389
+ "status_message": content.status_message,
390
+ "created_at": content.created_at,
391
+ "updated_at": content.updated_at,
392
+ }
393
+ )
394
+
395
+ return response
396
+
397
+ @router.delete(
398
+ "/knowledge/content/{content_id}",
399
+ response_model=ContentResponseSchema,
400
+ status_code=200,
401
+ response_model_exclude_none=True,
402
+ operation_id="delete_content_by_id",
403
+ summary="Delete Content by ID",
404
+ description="Permanently remove a specific content item from the knowledge base. This action cannot be undone.",
405
+ responses={
406
+ 200: {},
407
+ 404: {"description": "Content not found", "model": NotFoundResponse},
408
+ 500: {"description": "Failed to delete content", "model": InternalServerErrorResponse},
409
+ },
410
+ )
411
+ def delete_content_by_id(
412
+ content_id: str,
413
+ db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
414
+ ) -> ContentResponseSchema:
415
+ knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
416
+ knowledge.remove_content_by_id(content_id=content_id)
417
+ log_info(f"Deleting content by id: {content_id}")
418
+
419
+ return ContentResponseSchema(
420
+ id=content_id,
421
+ )
422
+
423
+ @router.delete(
424
+ "/knowledge/content",
425
+ status_code=200,
426
+ operation_id="delete_all_content",
427
+ summary="Delete All Content",
428
+ description=(
429
+ "Permanently remove all content from the knowledge base. This is a destructive operation that "
430
+ "cannot be undone. Use with extreme caution."
431
+ ),
432
+ responses={
433
+ 200: {},
434
+ 500: {"description": "Failed to delete all content", "model": InternalServerErrorResponse},
435
+ },
436
+ )
437
+ def delete_all_content(
438
+ db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
439
+ ):
440
+ knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
441
+ log_info("Deleting all content")
442
+ knowledge.remove_all_content()
443
+
444
+ return "success"
445
+
446
+ @router.get(
447
+ "/knowledge/content/{content_id}/status",
448
+ status_code=200,
449
+ response_model=ContentStatusResponse,
450
+ operation_id="get_content_status",
451
+ summary="Get Content Status",
452
+ description=(
453
+ "Retrieve the current processing status of a content item. Useful for monitoring "
454
+ "asynchronous content processing progress and identifying any processing errors."
455
+ ),
456
+ responses={
457
+ 200: {
458
+ "description": "Content status retrieved successfully",
459
+ "content": {
460
+ "application/json": {
461
+ "examples": {
462
+ "completed": {
463
+ "summary": "Example completed content status",
464
+ "value": {
465
+ "status": "completed",
466
+ "status_message": "",
467
+ },
468
+ }
469
+ }
470
+ }
471
+ },
472
+ },
473
+ 404: {"description": "Content not found", "model": NotFoundResponse},
474
+ },
475
+ )
476
+ def get_content_status(
477
+ content_id: str,
478
+ db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
479
+ ) -> ContentStatusResponse:
480
+ log_info(f"Getting content status: {content_id}")
481
+ knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
482
+ knowledge_status, status_message = knowledge.get_content_status(content_id=content_id)
483
+
484
+ # Handle the case where content is not found
485
+ if knowledge_status is None:
486
+ return ContentStatusResponse(
487
+ status=ContentStatus.FAILED, status_message=status_message or "Content not found"
488
+ )
489
+
490
+ # Convert knowledge ContentStatus to schema ContentStatus (they have same values)
491
+ if hasattr(knowledge_status, "value"):
492
+ status_value = knowledge_status.value
493
+ else:
494
+ status_value = str(knowledge_status)
495
+
496
+ # Convert string status to ContentStatus enum if needed (for backward compatibility and mocks)
497
+ if isinstance(status_value, str):
498
+ try:
499
+ status = ContentStatus(status_value.lower())
500
+ except ValueError:
501
+ # Handle legacy or unknown statuses gracefully
502
+ if "failed" in status_value.lower():
503
+ status = ContentStatus.FAILED
504
+ elif "completed" in status_value.lower():
505
+ status = ContentStatus.COMPLETED
506
+ else:
507
+ status = ContentStatus.PROCESSING
508
+ else:
509
+ status = ContentStatus.PROCESSING
510
+
511
+ return ContentStatusResponse(status=status, status_message=status_message or "")
512
+
513
+ @router.get(
514
+ "/knowledge/config",
515
+ status_code=200,
516
+ operation_id="get_knowledge_config",
517
+ summary="Get Knowledge Configuration",
518
+ description=(
519
+ "Retrieve available readers, chunkers, and configuration options for content processing. "
520
+ "This endpoint provides metadata about supported file types, processing strategies, and filters."
521
+ ),
522
+ responses={
523
+ 200: {
524
+ "description": "Knowledge configuration retrieved successfully",
525
+ "content": {
526
+ "application/json": {
527
+ "example": {
528
+ "readers": {
529
+ "website": {
530
+ "id": "website",
531
+ "name": "WebsiteReader",
532
+ "description": "Reads website files",
533
+ "chunkers": [
534
+ "AgenticChunker",
535
+ "DocumentChunker",
536
+ "RecursiveChunker",
537
+ "SemanticChunker",
538
+ "FixedSizeChunker",
539
+ ],
540
+ },
541
+ "firecrawl": {
542
+ "id": "firecrawl",
543
+ "name": "FirecrawlReader",
544
+ "description": "Reads firecrawl files",
545
+ "chunkers": [
546
+ "SemanticChunker",
547
+ "FixedSizeChunker",
548
+ "AgenticChunker",
549
+ "DocumentChunker",
550
+ "RecursiveChunker",
551
+ ],
552
+ },
553
+ "youtube": {
554
+ "id": "youtube",
555
+ "name": "YoutubeReader",
556
+ "description": "Reads youtube files",
557
+ "chunkers": [
558
+ "RecursiveChunker",
559
+ "AgenticChunker",
560
+ "DocumentChunker",
561
+ "SemanticChunker",
562
+ "FixedSizeChunker",
563
+ ],
564
+ },
565
+ "web_search": {
566
+ "id": "web_search",
567
+ "name": "WebSearchReader",
568
+ "description": "Reads web_search files",
569
+ "chunkers": [
570
+ "AgenticChunker",
571
+ "DocumentChunker",
572
+ "RecursiveChunker",
573
+ "SemanticChunker",
574
+ "FixedSizeChunker",
575
+ ],
576
+ },
577
+ "arxiv": {
578
+ "id": "arxiv",
579
+ "name": "ArxivReader",
580
+ "description": "Reads arxiv files",
581
+ "chunkers": [
582
+ "FixedSizeChunker",
583
+ "AgenticChunker",
584
+ "DocumentChunker",
585
+ "RecursiveChunker",
586
+ "SemanticChunker",
587
+ ],
588
+ },
589
+ "csv": {
590
+ "id": "csv",
591
+ "name": "CsvReader",
592
+ "description": "Reads csv files",
593
+ "chunkers": [
594
+ "RowChunker",
595
+ "FixedSizeChunker",
596
+ "AgenticChunker",
597
+ "DocumentChunker",
598
+ "RecursiveChunker",
599
+ ],
600
+ },
601
+ "docx": {
602
+ "id": "docx",
603
+ "name": "DocxReader",
604
+ "description": "Reads docx files",
605
+ "chunkers": [
606
+ "DocumentChunker",
607
+ "FixedSizeChunker",
608
+ "SemanticChunker",
609
+ "AgenticChunker",
610
+ "RecursiveChunker",
611
+ ],
612
+ },
613
+ "gcs": {
614
+ "id": "gcs",
615
+ "name": "GcsReader",
616
+ "description": "Reads gcs files",
617
+ "chunkers": [
618
+ "FixedSizeChunker",
619
+ "AgenticChunker",
620
+ "DocumentChunker",
621
+ "RecursiveChunker",
622
+ "SemanticChunker",
623
+ ],
624
+ },
625
+ "json": {
626
+ "id": "json",
627
+ "name": "JsonReader",
628
+ "description": "Reads json files",
629
+ "chunkers": [
630
+ "FixedSizeChunker",
631
+ "AgenticChunker",
632
+ "DocumentChunker",
633
+ "RecursiveChunker",
634
+ "SemanticChunker",
635
+ ],
636
+ },
637
+ "markdown": {
638
+ "id": "markdown",
639
+ "name": "MarkdownReader",
640
+ "description": "Reads markdown files",
641
+ "chunkers": [
642
+ "MarkdownChunker",
643
+ "DocumentChunker",
644
+ "AgenticChunker",
645
+ "RecursiveChunker",
646
+ "SemanticChunker",
647
+ "FixedSizeChunker",
648
+ ],
649
+ },
650
+ "pdf": {
651
+ "id": "pdf",
652
+ "name": "PdfReader",
653
+ "description": "Reads pdf files",
654
+ "chunkers": [
655
+ "DocumentChunker",
656
+ "FixedSizeChunker",
657
+ "AgenticChunker",
658
+ "SemanticChunker",
659
+ "RecursiveChunker",
660
+ ],
661
+ },
662
+ "text": {
663
+ "id": "text",
664
+ "name": "TextReader",
665
+ "description": "Reads text files",
666
+ "chunkers": [
667
+ "FixedSizeChunker",
668
+ "AgenticChunker",
669
+ "DocumentChunker",
670
+ "RecursiveChunker",
671
+ "SemanticChunker",
672
+ ],
673
+ },
674
+ },
675
+ "readersForType": {
676
+ "url": [
677
+ "url",
678
+ "website",
679
+ "firecrawl",
680
+ "youtube",
681
+ "web_search",
682
+ "gcs",
683
+ ],
684
+ "youtube": ["youtube"],
685
+ "text": ["web_search"],
686
+ "topic": ["arxiv"],
687
+ "file": ["csv", "gcs"],
688
+ ".csv": ["csv"],
689
+ ".xlsx": ["csv"],
690
+ ".xls": ["csv"],
691
+ ".docx": ["docx"],
692
+ ".doc": ["docx"],
693
+ ".json": ["json"],
694
+ ".md": ["markdown"],
695
+ ".pdf": ["pdf"],
696
+ ".txt": ["text"],
697
+ },
698
+ "chunkers": {
699
+ "AgenticChunker": {
700
+ "key": "AgenticChunker",
701
+ "name": "AgenticChunker",
702
+ "description": "Chunking strategy that uses an LLM to determine natural breakpoints in the text",
703
+ },
704
+ "DocumentChunker": {
705
+ "key": "DocumentChunker",
706
+ "name": "DocumentChunker",
707
+ "description": "A chunking strategy that splits text based on document structure like paragraphs and sections",
708
+ },
709
+ "RecursiveChunker": {
710
+ "key": "RecursiveChunker",
711
+ "name": "RecursiveChunker",
712
+ "description": "Chunking strategy that recursively splits text into chunks by finding natural break points",
713
+ },
714
+ "SemanticChunker": {
715
+ "key": "SemanticChunker",
716
+ "name": "SemanticChunker",
717
+ "description": "Chunking strategy that splits text into semantic chunks using chonkie",
718
+ },
719
+ "FixedSizeChunker": {
720
+ "key": "FixedSizeChunker",
721
+ "name": "FixedSizeChunker",
722
+ "description": "Chunking strategy that splits text into fixed-size chunks with optional overlap",
723
+ },
724
+ "RowChunker": {
725
+ "key": "RowChunker",
726
+ "name": "RowChunker",
727
+ "description": "RowChunking chunking strategy",
728
+ },
729
+ "MarkdownChunker": {
730
+ "key": "MarkdownChunker",
731
+ "name": "MarkdownChunker",
732
+ "description": "A chunking strategy that splits markdown based on structure like headers, paragraphs and sections",
733
+ },
734
+ },
735
+ "filters": ["filter_tag_1", "filter_tag2"],
736
+ }
737
+ }
738
+ },
739
+ }
740
+ },
741
+ )
742
+ def get_config(
743
+ db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
744
+ ) -> ConfigResponseSchema:
745
+ knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
746
+
747
+ # Get factory readers info
748
+ readers_info = get_all_readers_info()
749
+ reader_schemas = {}
750
+ # Add factory readers
751
+ for reader_info in readers_info:
752
+ reader_schemas[reader_info["id"]] = ReaderSchema(
753
+ id=reader_info["id"],
754
+ name=reader_info["name"],
755
+ description=reader_info.get("description"),
756
+ chunkers=reader_info.get("chunking_strategies", []),
757
+ )
758
+
759
+ # Add custom readers from knowledge.readers
760
+ readers_dict: Dict[str, Reader] = knowledge.get_readers() or {}
761
+ if readers_dict:
762
+ for reader_id, reader in readers_dict.items():
763
+ # Get chunking strategies from the reader
764
+ chunking_strategies = []
765
+ try:
766
+ strategies = reader.get_supported_chunking_strategies()
767
+ chunking_strategies = [strategy.value for strategy in strategies]
768
+ except Exception:
769
+ chunking_strategies = []
770
+
771
+ # Check if this reader ID already exists in factory readers
772
+ if reader_id not in reader_schemas:
773
+ reader_schemas[reader_id] = ReaderSchema(
774
+ id=reader_id,
775
+ name=getattr(reader, "name", reader.__class__.__name__),
776
+ description=getattr(reader, "description", f"Custom {reader.__class__.__name__}"),
777
+ chunkers=chunking_strategies,
778
+ )
779
+
780
+ # Get content types to readers mapping
781
+ types_of_readers = get_content_types_to_readers_mapping()
782
+ chunkers_list = get_all_chunkers_info()
783
+
784
+ # Convert chunkers list to dictionary format expected by schema
785
+ chunkers_dict = {}
786
+ for chunker_info in chunkers_list:
787
+ chunker_key = chunker_info.get("key")
788
+ if chunker_key:
789
+ chunkers_dict[chunker_key] = ChunkerSchema(
790
+ key=chunker_key, name=chunker_info.get("name"), description=chunker_info.get("description")
791
+ )
792
+
793
+ return ConfigResponseSchema(
794
+ readers=reader_schemas,
795
+ readersForType=types_of_readers,
796
+ chunkers=chunkers_dict,
797
+ filters=knowledge.get_filters(),
798
+ )
799
+
800
+ return router
801
+
802
+
803
+ async def process_content(
804
+ knowledge: Knowledge,
805
+ content_id: str,
806
+ content: Content,
807
+ reader_id: Optional[str] = None,
808
+ chunker: Optional[str] = None,
809
+ ):
810
+ """Background task to process the content"""
811
+ log_info(f"Processing content {content_id}")
812
+ try:
813
+ content.id = content_id
814
+ if reader_id:
815
+ reader = None
816
+ if knowledge.readers and reader_id in knowledge.readers:
817
+ reader = knowledge.readers[reader_id]
818
+ else:
819
+ key = reader_id.lower().strip().replace("-", "_").replace(" ", "_")
820
+ candidates = [key] + ([key[:-6]] if key.endswith("reader") else [])
821
+ for cand in candidates:
822
+ try:
823
+ reader = ReaderFactory.create_reader(cand)
824
+ log_debug(f"Resolved reader: {reader.__class__.__name__}")
825
+ break
826
+ except Exception:
827
+ continue
828
+ if reader:
829
+ content.reader = reader
830
+ if chunker and content.reader:
831
+ # Set the chunker name on the reader - let the reader handle it internally
832
+ content.reader.set_chunking_strategy_from_string(chunker)
833
+ log_debug(f"Set chunking strategy: {chunker}")
834
+
835
+ log_debug(f"Using reader: {content.reader.__class__.__name__}")
836
+ await knowledge._load_content(content, upsert=False, skip_if_exists=True)
837
+ log_info(f"Content {content_id} processed successfully")
838
+ except Exception as e:
839
+ log_info(f"Error processing content {content_id}: {e}")
840
+ # Mark content as failed in the contents DB
841
+ try:
842
+ from agno.knowledge.content import ContentStatus as KnowledgeContentStatus
843
+
844
+ content.status = KnowledgeContentStatus.FAILED
845
+ content.status_message = str(e)
846
+ content.id = content_id
847
+ knowledge.patch_content(content)
848
+ except Exception:
849
+ # Swallow any secondary errors to avoid crashing the background task
850
+ pass