agno 2.2.13__py3-none-any.whl → 2.4.3__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 (383) hide show
  1. agno/agent/__init__.py +6 -0
  2. agno/agent/agent.py +5252 -3145
  3. agno/agent/remote.py +525 -0
  4. agno/api/api.py +2 -0
  5. agno/client/__init__.py +3 -0
  6. agno/client/a2a/__init__.py +10 -0
  7. agno/client/a2a/client.py +554 -0
  8. agno/client/a2a/schemas.py +112 -0
  9. agno/client/a2a/utils.py +369 -0
  10. agno/client/os.py +2669 -0
  11. agno/compression/__init__.py +3 -0
  12. agno/compression/manager.py +247 -0
  13. agno/culture/manager.py +2 -2
  14. agno/db/base.py +927 -6
  15. agno/db/dynamo/dynamo.py +788 -2
  16. agno/db/dynamo/schemas.py +128 -0
  17. agno/db/dynamo/utils.py +26 -3
  18. agno/db/firestore/firestore.py +674 -50
  19. agno/db/firestore/schemas.py +41 -0
  20. agno/db/firestore/utils.py +25 -10
  21. agno/db/gcs_json/gcs_json_db.py +506 -3
  22. agno/db/gcs_json/utils.py +14 -2
  23. agno/db/in_memory/in_memory_db.py +203 -4
  24. agno/db/in_memory/utils.py +14 -2
  25. agno/db/json/json_db.py +498 -2
  26. agno/db/json/utils.py +14 -2
  27. agno/db/migrations/manager.py +199 -0
  28. agno/db/migrations/utils.py +19 -0
  29. agno/db/migrations/v1_to_v2.py +54 -16
  30. agno/db/migrations/versions/__init__.py +0 -0
  31. agno/db/migrations/versions/v2_3_0.py +977 -0
  32. agno/db/mongo/async_mongo.py +1013 -39
  33. agno/db/mongo/mongo.py +684 -4
  34. agno/db/mongo/schemas.py +48 -0
  35. agno/db/mongo/utils.py +17 -0
  36. agno/db/mysql/__init__.py +2 -1
  37. agno/db/mysql/async_mysql.py +2958 -0
  38. agno/db/mysql/mysql.py +722 -53
  39. agno/db/mysql/schemas.py +77 -11
  40. agno/db/mysql/utils.py +151 -8
  41. agno/db/postgres/async_postgres.py +1254 -137
  42. agno/db/postgres/postgres.py +2316 -93
  43. agno/db/postgres/schemas.py +153 -21
  44. agno/db/postgres/utils.py +22 -7
  45. agno/db/redis/redis.py +531 -3
  46. agno/db/redis/schemas.py +36 -0
  47. agno/db/redis/utils.py +31 -15
  48. agno/db/schemas/evals.py +1 -0
  49. agno/db/schemas/memory.py +20 -9
  50. agno/db/singlestore/schemas.py +70 -1
  51. agno/db/singlestore/singlestore.py +737 -74
  52. agno/db/singlestore/utils.py +13 -3
  53. agno/db/sqlite/async_sqlite.py +1069 -89
  54. agno/db/sqlite/schemas.py +133 -1
  55. agno/db/sqlite/sqlite.py +2203 -165
  56. agno/db/sqlite/utils.py +21 -11
  57. agno/db/surrealdb/models.py +25 -0
  58. agno/db/surrealdb/surrealdb.py +603 -1
  59. agno/db/utils.py +60 -0
  60. agno/eval/__init__.py +26 -3
  61. agno/eval/accuracy.py +25 -12
  62. agno/eval/agent_as_judge.py +871 -0
  63. agno/eval/base.py +29 -0
  64. agno/eval/performance.py +10 -4
  65. agno/eval/reliability.py +22 -13
  66. agno/eval/utils.py +2 -1
  67. agno/exceptions.py +42 -0
  68. agno/hooks/__init__.py +3 -0
  69. agno/hooks/decorator.py +164 -0
  70. agno/integrations/discord/client.py +13 -2
  71. agno/knowledge/__init__.py +4 -0
  72. agno/knowledge/chunking/code.py +90 -0
  73. agno/knowledge/chunking/document.py +65 -4
  74. agno/knowledge/chunking/fixed.py +4 -1
  75. agno/knowledge/chunking/markdown.py +102 -11
  76. agno/knowledge/chunking/recursive.py +2 -2
  77. agno/knowledge/chunking/semantic.py +130 -48
  78. agno/knowledge/chunking/strategy.py +18 -0
  79. agno/knowledge/embedder/azure_openai.py +0 -1
  80. agno/knowledge/embedder/google.py +1 -1
  81. agno/knowledge/embedder/mistral.py +1 -1
  82. agno/knowledge/embedder/nebius.py +1 -1
  83. agno/knowledge/embedder/openai.py +16 -12
  84. agno/knowledge/filesystem.py +412 -0
  85. agno/knowledge/knowledge.py +4261 -1199
  86. agno/knowledge/protocol.py +134 -0
  87. agno/knowledge/reader/arxiv_reader.py +3 -2
  88. agno/knowledge/reader/base.py +9 -7
  89. agno/knowledge/reader/csv_reader.py +91 -42
  90. agno/knowledge/reader/docx_reader.py +9 -10
  91. agno/knowledge/reader/excel_reader.py +225 -0
  92. agno/knowledge/reader/field_labeled_csv_reader.py +38 -48
  93. agno/knowledge/reader/firecrawl_reader.py +3 -2
  94. agno/knowledge/reader/json_reader.py +16 -22
  95. agno/knowledge/reader/markdown_reader.py +15 -14
  96. agno/knowledge/reader/pdf_reader.py +33 -28
  97. agno/knowledge/reader/pptx_reader.py +9 -10
  98. agno/knowledge/reader/reader_factory.py +135 -1
  99. agno/knowledge/reader/s3_reader.py +8 -16
  100. agno/knowledge/reader/tavily_reader.py +3 -3
  101. agno/knowledge/reader/text_reader.py +15 -14
  102. agno/knowledge/reader/utils/__init__.py +17 -0
  103. agno/knowledge/reader/utils/spreadsheet.py +114 -0
  104. agno/knowledge/reader/web_search_reader.py +8 -65
  105. agno/knowledge/reader/website_reader.py +16 -13
  106. agno/knowledge/reader/wikipedia_reader.py +36 -3
  107. agno/knowledge/reader/youtube_reader.py +3 -2
  108. agno/knowledge/remote_content/__init__.py +33 -0
  109. agno/knowledge/remote_content/config.py +266 -0
  110. agno/knowledge/remote_content/remote_content.py +105 -17
  111. agno/knowledge/utils.py +76 -22
  112. agno/learn/__init__.py +71 -0
  113. agno/learn/config.py +463 -0
  114. agno/learn/curate.py +185 -0
  115. agno/learn/machine.py +725 -0
  116. agno/learn/schemas.py +1114 -0
  117. agno/learn/stores/__init__.py +38 -0
  118. agno/learn/stores/decision_log.py +1156 -0
  119. agno/learn/stores/entity_memory.py +3275 -0
  120. agno/learn/stores/learned_knowledge.py +1583 -0
  121. agno/learn/stores/protocol.py +117 -0
  122. agno/learn/stores/session_context.py +1217 -0
  123. agno/learn/stores/user_memory.py +1495 -0
  124. agno/learn/stores/user_profile.py +1220 -0
  125. agno/learn/utils.py +209 -0
  126. agno/media.py +22 -6
  127. agno/memory/__init__.py +14 -1
  128. agno/memory/manager.py +223 -8
  129. agno/memory/strategies/__init__.py +15 -0
  130. agno/memory/strategies/base.py +66 -0
  131. agno/memory/strategies/summarize.py +196 -0
  132. agno/memory/strategies/types.py +37 -0
  133. agno/models/aimlapi/aimlapi.py +17 -0
  134. agno/models/anthropic/claude.py +434 -59
  135. agno/models/aws/bedrock.py +121 -20
  136. agno/models/aws/claude.py +131 -274
  137. agno/models/azure/ai_foundry.py +10 -6
  138. agno/models/azure/openai_chat.py +33 -10
  139. agno/models/base.py +1162 -561
  140. agno/models/cerebras/cerebras.py +120 -24
  141. agno/models/cerebras/cerebras_openai.py +21 -2
  142. agno/models/cohere/chat.py +65 -6
  143. agno/models/cometapi/cometapi.py +18 -1
  144. agno/models/dashscope/dashscope.py +2 -3
  145. agno/models/deepinfra/deepinfra.py +18 -1
  146. agno/models/deepseek/deepseek.py +69 -3
  147. agno/models/fireworks/fireworks.py +18 -1
  148. agno/models/google/gemini.py +959 -89
  149. agno/models/google/utils.py +22 -0
  150. agno/models/groq/groq.py +48 -18
  151. agno/models/huggingface/huggingface.py +17 -6
  152. agno/models/ibm/watsonx.py +16 -6
  153. agno/models/internlm/internlm.py +18 -1
  154. agno/models/langdb/langdb.py +13 -1
  155. agno/models/litellm/chat.py +88 -9
  156. agno/models/litellm/litellm_openai.py +18 -1
  157. agno/models/message.py +24 -5
  158. agno/models/meta/llama.py +40 -13
  159. agno/models/meta/llama_openai.py +22 -21
  160. agno/models/metrics.py +12 -0
  161. agno/models/mistral/mistral.py +8 -4
  162. agno/models/n1n/__init__.py +3 -0
  163. agno/models/n1n/n1n.py +57 -0
  164. agno/models/nebius/nebius.py +6 -7
  165. agno/models/nvidia/nvidia.py +20 -3
  166. agno/models/ollama/__init__.py +2 -0
  167. agno/models/ollama/chat.py +17 -6
  168. agno/models/ollama/responses.py +100 -0
  169. agno/models/openai/__init__.py +2 -0
  170. agno/models/openai/chat.py +117 -26
  171. agno/models/openai/open_responses.py +46 -0
  172. agno/models/openai/responses.py +110 -32
  173. agno/models/openrouter/__init__.py +2 -0
  174. agno/models/openrouter/openrouter.py +67 -2
  175. agno/models/openrouter/responses.py +146 -0
  176. agno/models/perplexity/perplexity.py +19 -1
  177. agno/models/portkey/portkey.py +7 -6
  178. agno/models/requesty/requesty.py +19 -2
  179. agno/models/response.py +20 -2
  180. agno/models/sambanova/sambanova.py +20 -3
  181. agno/models/siliconflow/siliconflow.py +19 -2
  182. agno/models/together/together.py +20 -3
  183. agno/models/vercel/v0.py +20 -3
  184. agno/models/vertexai/claude.py +124 -4
  185. agno/models/vllm/vllm.py +19 -14
  186. agno/models/xai/xai.py +19 -2
  187. agno/os/app.py +467 -137
  188. agno/os/auth.py +253 -5
  189. agno/os/config.py +22 -0
  190. agno/os/interfaces/a2a/a2a.py +7 -6
  191. agno/os/interfaces/a2a/router.py +635 -26
  192. agno/os/interfaces/a2a/utils.py +32 -33
  193. agno/os/interfaces/agui/agui.py +5 -3
  194. agno/os/interfaces/agui/router.py +26 -16
  195. agno/os/interfaces/agui/utils.py +97 -57
  196. agno/os/interfaces/base.py +7 -7
  197. agno/os/interfaces/slack/router.py +16 -7
  198. agno/os/interfaces/slack/slack.py +7 -7
  199. agno/os/interfaces/whatsapp/router.py +35 -7
  200. agno/os/interfaces/whatsapp/security.py +3 -1
  201. agno/os/interfaces/whatsapp/whatsapp.py +11 -8
  202. agno/os/managers.py +326 -0
  203. agno/os/mcp.py +652 -79
  204. agno/os/middleware/__init__.py +4 -0
  205. agno/os/middleware/jwt.py +718 -115
  206. agno/os/middleware/trailing_slash.py +27 -0
  207. agno/os/router.py +105 -1558
  208. agno/os/routers/agents/__init__.py +3 -0
  209. agno/os/routers/agents/router.py +655 -0
  210. agno/os/routers/agents/schema.py +288 -0
  211. agno/os/routers/components/__init__.py +3 -0
  212. agno/os/routers/components/components.py +475 -0
  213. agno/os/routers/database.py +155 -0
  214. agno/os/routers/evals/evals.py +111 -18
  215. agno/os/routers/evals/schemas.py +38 -5
  216. agno/os/routers/evals/utils.py +80 -11
  217. agno/os/routers/health.py +3 -3
  218. agno/os/routers/knowledge/knowledge.py +284 -35
  219. agno/os/routers/knowledge/schemas.py +14 -2
  220. agno/os/routers/memory/memory.py +274 -11
  221. agno/os/routers/memory/schemas.py +44 -3
  222. agno/os/routers/metrics/metrics.py +30 -15
  223. agno/os/routers/metrics/schemas.py +10 -6
  224. agno/os/routers/registry/__init__.py +3 -0
  225. agno/os/routers/registry/registry.py +337 -0
  226. agno/os/routers/session/session.py +143 -14
  227. agno/os/routers/teams/__init__.py +3 -0
  228. agno/os/routers/teams/router.py +550 -0
  229. agno/os/routers/teams/schema.py +280 -0
  230. agno/os/routers/traces/__init__.py +3 -0
  231. agno/os/routers/traces/schemas.py +414 -0
  232. agno/os/routers/traces/traces.py +549 -0
  233. agno/os/routers/workflows/__init__.py +3 -0
  234. agno/os/routers/workflows/router.py +757 -0
  235. agno/os/routers/workflows/schema.py +139 -0
  236. agno/os/schema.py +157 -584
  237. agno/os/scopes.py +469 -0
  238. agno/os/settings.py +3 -0
  239. agno/os/utils.py +574 -185
  240. agno/reasoning/anthropic.py +85 -1
  241. agno/reasoning/azure_ai_foundry.py +93 -1
  242. agno/reasoning/deepseek.py +102 -2
  243. agno/reasoning/default.py +6 -7
  244. agno/reasoning/gemini.py +87 -3
  245. agno/reasoning/groq.py +109 -2
  246. agno/reasoning/helpers.py +6 -7
  247. agno/reasoning/manager.py +1238 -0
  248. agno/reasoning/ollama.py +93 -1
  249. agno/reasoning/openai.py +115 -1
  250. agno/reasoning/vertexai.py +85 -1
  251. agno/registry/__init__.py +3 -0
  252. agno/registry/registry.py +68 -0
  253. agno/remote/__init__.py +3 -0
  254. agno/remote/base.py +581 -0
  255. agno/run/__init__.py +2 -4
  256. agno/run/agent.py +134 -19
  257. agno/run/base.py +49 -1
  258. agno/run/cancel.py +65 -52
  259. agno/run/cancellation_management/__init__.py +9 -0
  260. agno/run/cancellation_management/base.py +78 -0
  261. agno/run/cancellation_management/in_memory_cancellation_manager.py +100 -0
  262. agno/run/cancellation_management/redis_cancellation_manager.py +236 -0
  263. agno/run/requirement.py +181 -0
  264. agno/run/team.py +111 -19
  265. agno/run/workflow.py +2 -1
  266. agno/session/agent.py +57 -92
  267. agno/session/summary.py +1 -1
  268. agno/session/team.py +62 -115
  269. agno/session/workflow.py +353 -57
  270. agno/skills/__init__.py +17 -0
  271. agno/skills/agent_skills.py +377 -0
  272. agno/skills/errors.py +32 -0
  273. agno/skills/loaders/__init__.py +4 -0
  274. agno/skills/loaders/base.py +27 -0
  275. agno/skills/loaders/local.py +216 -0
  276. agno/skills/skill.py +65 -0
  277. agno/skills/utils.py +107 -0
  278. agno/skills/validator.py +277 -0
  279. agno/table.py +10 -0
  280. agno/team/__init__.py +5 -1
  281. agno/team/remote.py +447 -0
  282. agno/team/team.py +3769 -2202
  283. agno/tools/brandfetch.py +27 -18
  284. agno/tools/browserbase.py +225 -16
  285. agno/tools/crawl4ai.py +3 -0
  286. agno/tools/duckduckgo.py +25 -71
  287. agno/tools/exa.py +0 -21
  288. agno/tools/file.py +14 -13
  289. agno/tools/file_generation.py +12 -6
  290. agno/tools/firecrawl.py +15 -7
  291. agno/tools/function.py +94 -113
  292. agno/tools/google_bigquery.py +11 -2
  293. agno/tools/google_drive.py +4 -3
  294. agno/tools/knowledge.py +9 -4
  295. agno/tools/mcp/mcp.py +301 -18
  296. agno/tools/mcp/multi_mcp.py +269 -14
  297. agno/tools/mem0.py +11 -10
  298. agno/tools/memory.py +47 -46
  299. agno/tools/mlx_transcribe.py +10 -7
  300. agno/tools/models/nebius.py +5 -5
  301. agno/tools/models_labs.py +20 -10
  302. agno/tools/nano_banana.py +151 -0
  303. agno/tools/parallel.py +0 -7
  304. agno/tools/postgres.py +76 -36
  305. agno/tools/python.py +14 -6
  306. agno/tools/reasoning.py +30 -23
  307. agno/tools/redshift.py +406 -0
  308. agno/tools/shopify.py +1519 -0
  309. agno/tools/spotify.py +919 -0
  310. agno/tools/tavily.py +4 -1
  311. agno/tools/toolkit.py +253 -18
  312. agno/tools/websearch.py +93 -0
  313. agno/tools/website.py +1 -1
  314. agno/tools/wikipedia.py +1 -1
  315. agno/tools/workflow.py +56 -48
  316. agno/tools/yfinance.py +12 -11
  317. agno/tracing/__init__.py +12 -0
  318. agno/tracing/exporter.py +161 -0
  319. agno/tracing/schemas.py +276 -0
  320. agno/tracing/setup.py +112 -0
  321. agno/utils/agent.py +251 -10
  322. agno/utils/cryptography.py +22 -0
  323. agno/utils/dttm.py +33 -0
  324. agno/utils/events.py +264 -7
  325. agno/utils/hooks.py +111 -3
  326. agno/utils/http.py +161 -2
  327. agno/utils/mcp.py +49 -8
  328. agno/utils/media.py +22 -1
  329. agno/utils/models/ai_foundry.py +9 -2
  330. agno/utils/models/claude.py +20 -5
  331. agno/utils/models/cohere.py +9 -2
  332. agno/utils/models/llama.py +9 -2
  333. agno/utils/models/mistral.py +4 -2
  334. agno/utils/os.py +0 -0
  335. agno/utils/print_response/agent.py +99 -16
  336. agno/utils/print_response/team.py +223 -24
  337. agno/utils/print_response/workflow.py +0 -2
  338. agno/utils/prompts.py +8 -6
  339. agno/utils/remote.py +23 -0
  340. agno/utils/response.py +1 -13
  341. agno/utils/string.py +91 -2
  342. agno/utils/team.py +62 -12
  343. agno/utils/tokens.py +657 -0
  344. agno/vectordb/base.py +15 -2
  345. agno/vectordb/cassandra/cassandra.py +1 -1
  346. agno/vectordb/chroma/__init__.py +2 -1
  347. agno/vectordb/chroma/chromadb.py +468 -23
  348. agno/vectordb/clickhouse/clickhousedb.py +1 -1
  349. agno/vectordb/couchbase/couchbase.py +6 -2
  350. agno/vectordb/lancedb/lance_db.py +7 -38
  351. agno/vectordb/lightrag/lightrag.py +7 -6
  352. agno/vectordb/milvus/milvus.py +118 -84
  353. agno/vectordb/mongodb/__init__.py +2 -1
  354. agno/vectordb/mongodb/mongodb.py +14 -31
  355. agno/vectordb/pgvector/pgvector.py +120 -66
  356. agno/vectordb/pineconedb/pineconedb.py +2 -19
  357. agno/vectordb/qdrant/__init__.py +2 -1
  358. agno/vectordb/qdrant/qdrant.py +33 -56
  359. agno/vectordb/redis/__init__.py +2 -1
  360. agno/vectordb/redis/redisdb.py +19 -31
  361. agno/vectordb/singlestore/singlestore.py +17 -9
  362. agno/vectordb/surrealdb/surrealdb.py +2 -38
  363. agno/vectordb/weaviate/__init__.py +2 -1
  364. agno/vectordb/weaviate/weaviate.py +7 -3
  365. agno/workflow/__init__.py +5 -1
  366. agno/workflow/agent.py +2 -2
  367. agno/workflow/condition.py +12 -10
  368. agno/workflow/loop.py +28 -9
  369. agno/workflow/parallel.py +21 -13
  370. agno/workflow/remote.py +362 -0
  371. agno/workflow/router.py +12 -9
  372. agno/workflow/step.py +261 -36
  373. agno/workflow/steps.py +12 -8
  374. agno/workflow/types.py +40 -77
  375. agno/workflow/workflow.py +939 -213
  376. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/METADATA +134 -181
  377. agno-2.4.3.dist-info/RECORD +677 -0
  378. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/WHEEL +1 -1
  379. agno/tools/googlesearch.py +0 -98
  380. agno/tools/memori.py +0 -339
  381. agno-2.2.13.dist-info/RECORD +0 -575
  382. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/licenses/LICENSE +0 -0
  383. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,134 @@
1
+ """
2
+ Knowledge Protocol
3
+ ==================
4
+ Defines the minimal interface that knowledge implementations must implement.
5
+
6
+ This protocol enables:
7
+ - Custom knowledge bases to be used with agents
8
+ - Each implementation defines its own tools and context
9
+ - Flexible tool naming (not forced to use 'search')
10
+ - Type safety with Protocol typing
11
+ """
12
+
13
+ from typing import Callable, List, Protocol, runtime_checkable
14
+
15
+ from agno.knowledge.document import Document
16
+
17
+
18
+ @runtime_checkable
19
+ class KnowledgeProtocol(Protocol):
20
+ """Minimal protocol for knowledge implementations.
21
+
22
+ Enables custom knowledge bases to be used with agents.
23
+ Each implementation defines what tools it exposes and what
24
+ context/instructions it provides to the agent.
25
+
26
+ Required methods:
27
+ - build_context(): Return instructions for the agent's system prompt
28
+ - get_tools(): Return tools to expose to the agent
29
+ - aget_tools(): Async version of get_tools
30
+
31
+ Optional methods:
32
+ - retrieve(): Default retrieval for context injection (add_knowledge_to_context)
33
+ - aretrieve(): Async version of retrieve
34
+
35
+ Example:
36
+ ```python
37
+ from agno.knowledge.protocol import KnowledgeProtocol
38
+ from agno.knowledge.document import Document
39
+
40
+ class MyKnowledge:
41
+ def build_context(self, **kwargs) -> str:
42
+ return "Use search_docs to find information."
43
+
44
+ def get_tools(self, **kwargs) -> List[Callable]:
45
+ return [self.search_docs]
46
+
47
+ async def aget_tools(self, **kwargs) -> List[Callable]:
48
+ return [self.search_docs]
49
+
50
+ def search_docs(self, query: str) -> str:
51
+ # Your search implementation
52
+ return "Results for: " + query
53
+
54
+ # Optional: for add_knowledge_to_context feature
55
+ def retrieve(self, query: str, **kwargs) -> List[Document]:
56
+ results = self._internal_search(query)
57
+ return [Document(content=r) for r in results]
58
+
59
+ # MyKnowledge satisfies KnowledgeProtocol
60
+ agent = Agent(knowledge=MyKnowledge())
61
+ ```
62
+ """
63
+
64
+ def build_context(self, **kwargs) -> str:
65
+ """Build context string for the agent's system prompt.
66
+
67
+ Returns instructions about how to use this knowledge,
68
+ what tools are available, and any usage guidelines.
69
+
70
+ Args:
71
+ **kwargs: Context including enable_agentic_filters, etc.
72
+
73
+ Returns:
74
+ Formatted context string to inject into system prompt.
75
+ """
76
+ ...
77
+
78
+ def get_tools(self, **kwargs) -> List[Callable]:
79
+ """Get tools to expose to the agent.
80
+
81
+ Returns callable tools that the agent can use to interact
82
+ with this knowledge. Each implementation decides what
83
+ tools make sense (e.g., search, grep, list_files, query_db).
84
+
85
+ Args:
86
+ **kwargs: Context including run_response, run_context,
87
+ async_mode, enable_agentic_filters, agent, etc.
88
+
89
+ Returns:
90
+ List of callable tools.
91
+ """
92
+ ...
93
+
94
+ async def aget_tools(self, **kwargs) -> List[Callable]:
95
+ """Async version of get_tools.
96
+
97
+ Args:
98
+ **kwargs: Same as get_tools.
99
+
100
+ Returns:
101
+ List of callable tools.
102
+ """
103
+ ...
104
+
105
+ # Optional methods - used by add_knowledge_to_context feature
106
+ # Implementations that don't support context injection can omit these
107
+
108
+ def retrieve(self, query: str, **kwargs) -> List[Document]:
109
+ """Retrieve documents for context injection.
110
+
111
+ Used by the add_knowledge_to_context feature to pre-fetch
112
+ relevant documents into the user message. This is optional;
113
+ if not implemented, add_knowledge_to_context will be skipped.
114
+
115
+ Args:
116
+ query: The query string.
117
+ **kwargs: Additional parameters (max_results, filters, etc.)
118
+
119
+ Returns:
120
+ List of Document objects.
121
+ """
122
+ ...
123
+
124
+ async def aretrieve(self, query: str, **kwargs) -> List[Document]:
125
+ """Async version of retrieve.
126
+
127
+ Args:
128
+ query: The query string.
129
+ **kwargs: Additional parameters.
130
+
131
+ Returns:
132
+ List of Document objects.
133
+ """
134
+ ...
@@ -17,9 +17,10 @@ class ArxivReader(Reader):
17
17
  sort_by: arxiv.SortCriterion = arxiv.SortCriterion.Relevance
18
18
 
19
19
  @classmethod
20
- def get_supported_chunking_strategies(self) -> List[ChunkingStrategyType]:
20
+ def get_supported_chunking_strategies(cls) -> List[ChunkingStrategyType]:
21
21
  """Get the list of supported chunking strategies for Arxiv readers."""
22
22
  return [
23
+ ChunkingStrategyType.CODE_CHUNKER,
23
24
  ChunkingStrategyType.FIXED_SIZE_CHUNKER,
24
25
  ChunkingStrategyType.AGENTIC_CHUNKER,
25
26
  ChunkingStrategyType.DOCUMENT_CHUNKER,
@@ -28,7 +29,7 @@ class ArxivReader(Reader):
28
29
  ]
29
30
 
30
31
  @classmethod
31
- def get_supported_content_types(self) -> List[ContentType]:
32
+ def get_supported_content_types(cls) -> List[ContentType]:
32
33
  return [ContentType.TOPIC]
33
34
 
34
35
  def __init__(
@@ -73,11 +73,17 @@ class Reader:
73
73
  def chunk_document(self, document: Document) -> List[Document]:
74
74
  if self.chunking_strategy is None:
75
75
  self.chunking_strategy = FixedSizeChunking(chunk_size=self.chunk_size)
76
- return self.chunking_strategy.chunk(document) # type: ignore
76
+ return self.chunking_strategy.chunk(document)
77
+
78
+ async def achunk_document(self, document: Document) -> List[Document]:
79
+ """Async version of chunk_document."""
80
+ if self.chunking_strategy is None:
81
+ self.chunking_strategy = FixedSizeChunking(chunk_size=self.chunk_size)
82
+ return await self.chunking_strategy.achunk(document)
77
83
 
78
84
  async def chunk_documents_async(self, documents: List[Document]) -> List[Document]:
79
85
  """
80
- Asynchronously chunk a list of documents using the instance's chunk_document method.
86
+ Asynchronously chunk a list of documents.
81
87
 
82
88
  Args:
83
89
  documents: List of documents to be chunked.
@@ -85,11 +91,7 @@ class Reader:
85
91
  Returns:
86
92
  A flattened list of chunked documents.
87
93
  """
88
-
89
- async def _chunk_document_async(doc: Document) -> List[Document]:
90
- return await asyncio.to_thread(self.chunk_document, doc)
91
-
92
94
  # Process chunking in parallel for all documents
93
- chunked_lists = await asyncio.gather(*[_chunk_document_async(doc) for doc in documents])
95
+ chunked_lists = await asyncio.gather(*[self.achunk_document(doc) for doc in documents])
94
96
  # Flatten the result
95
97
  return [chunk for sublist in chunked_lists for chunk in sublist]
@@ -14,21 +14,42 @@ from agno.knowledge.chunking.row import RowChunking
14
14
  from agno.knowledge.chunking.strategy import ChunkingStrategy, ChunkingStrategyType
15
15
  from agno.knowledge.document.base import Document
16
16
  from agno.knowledge.reader.base import Reader
17
+ from agno.knowledge.reader.utils import stringify_cell_value
17
18
  from agno.knowledge.types import ContentType
18
- from agno.utils.log import logger
19
+ from agno.utils.log import log_debug, log_error
19
20
 
20
21
 
21
22
  class CSVReader(Reader):
22
- """Reader for CSV files"""
23
+ """Reader for CSV files.
24
+
25
+ Converts CSV files to documents with optional chunking support.
26
+ For Excel files (.xlsx, .xls), use ExcelReader instead.
27
+
28
+ Args:
29
+ chunking_strategy: Strategy for chunking documents. Default is RowChunking.
30
+ **kwargs: Additional arguments passed to base Reader.
31
+
32
+ Example:
33
+ ```python
34
+ from agno.knowledge.reader.csv_reader import CSVReader
35
+
36
+ reader = CSVReader()
37
+ docs = reader.read("data.csv")
38
+
39
+ # Custom delimiter
40
+ docs = reader.read("data.tsv", delimiter="\\t")
41
+ ```
42
+ """
23
43
 
24
44
  def __init__(self, chunking_strategy: Optional[ChunkingStrategy] = RowChunking(), **kwargs):
25
45
  super().__init__(chunking_strategy=chunking_strategy, **kwargs)
26
46
 
27
47
  @classmethod
28
- def get_supported_chunking_strategies(self) -> List[ChunkingStrategyType]:
48
+ def get_supported_chunking_strategies(cls) -> List[ChunkingStrategyType]:
29
49
  """Get the list of supported chunking strategies for CSV readers."""
30
50
  return [
31
51
  ChunkingStrategyType.ROW_CHUNKER,
52
+ ChunkingStrategyType.CODE_CHUNKER,
32
53
  ChunkingStrategyType.FIXED_SIZE_CHUNKER,
33
54
  ChunkingStrategyType.AGENTIC_CHUNKER,
34
55
  ChunkingStrategyType.DOCUMENT_CHUNKER,
@@ -36,39 +57,54 @@ class CSVReader(Reader):
36
57
  ]
37
58
 
38
59
  @classmethod
39
- def get_supported_content_types(self) -> List[ContentType]:
40
- return [ContentType.CSV, ContentType.XLSX, ContentType.XLS]
60
+ def get_supported_content_types(cls) -> List[ContentType]:
61
+ """Get the list of supported content types."""
62
+ return [ContentType.CSV]
41
63
 
42
64
  def read(
43
65
  self, file: Union[Path, IO[Any]], delimiter: str = ",", quotechar: str = '"', name: Optional[str] = None
44
66
  ) -> List[Document]:
67
+ """Read a CSV file and return a list of documents.
68
+
69
+ Args:
70
+ file: Path to CSV file or file-like object.
71
+ delimiter: CSV field delimiter. Default is comma.
72
+ quotechar: CSV quote character. Default is double quote.
73
+ name: Optional name override for the document.
74
+
75
+ Returns:
76
+ List of Document objects.
77
+
78
+ Raises:
79
+ FileNotFoundError: If the file path doesn't exist.
80
+ """
45
81
  try:
46
82
  if isinstance(file, Path):
47
83
  if not file.exists():
48
84
  raise FileNotFoundError(f"Could not find file: {file}")
49
- logger.info(f"Reading: {file}")
50
- file_content = file.open(newline="", mode="r", encoding=self.encoding or "utf-8")
85
+ log_debug(f"Reading: {file}")
86
+ csv_name = name or file.stem
87
+ file_content: Union[io.TextIOWrapper, io.StringIO] = file.open(
88
+ newline="", mode="r", encoding=self.encoding or "utf-8"
89
+ )
51
90
  else:
52
- logger.info(f"Reading retrieved file: {name or file.name}")
91
+ log_debug(f"Reading retrieved file: {getattr(file, 'name', 'BytesIO')}")
92
+ csv_name = name or getattr(file, "name", "csv_file").split(".")[0]
53
93
  file.seek(0)
54
- file_content = io.StringIO(file.read().decode("utf-8")) # type: ignore
55
-
56
- csv_name = name or (
57
- Path(file.name).stem
58
- if isinstance(file, Path)
59
- else (getattr(file, "name", "csv_file").split(".")[0] if hasattr(file, "name") else "csv_file")
60
- )
61
- csv_content = ""
94
+ file_content = io.StringIO(file.read().decode(self.encoding or "utf-8"))
95
+
96
+ csv_lines: List[str] = []
62
97
  with file_content as csvfile:
63
98
  csv_reader = csv.reader(csvfile, delimiter=delimiter, quotechar=quotechar)
64
99
  for row in csv_reader:
65
- csv_content += ", ".join(row) + "\n"
100
+ # Normalize line endings in CSV cells to preserve row integrity
101
+ csv_lines.append(", ".join(stringify_cell_value(cell) for cell in row))
66
102
 
67
103
  documents = [
68
104
  Document(
69
105
  name=csv_name,
70
106
  id=str(uuid4()),
71
- content=csv_content,
107
+ content="\n".join(csv_lines),
72
108
  )
73
109
  ]
74
110
  if self.chunk:
@@ -77,8 +113,15 @@ class CSVReader(Reader):
77
113
  chunked_documents.extend(self.chunk_document(document))
78
114
  return chunked_documents
79
115
  return documents
116
+ except FileNotFoundError:
117
+ raise
118
+ except UnicodeDecodeError as e:
119
+ file_desc = getattr(file, "name", str(file)) if isinstance(file, IO) else file
120
+ log_error(f"Encoding error reading {file_desc}: {e}. Try specifying a different encoding.")
121
+ return []
80
122
  except Exception as e:
81
- logger.error(f"Error reading: {getattr(file, 'name', str(file)) if isinstance(file, IO) else file}: {e}")
123
+ file_desc = getattr(file, "name", str(file)) if isinstance(file, IO) else file
124
+ log_error(f"Error reading {file_desc}: {e}")
82
125
  return []
83
126
 
84
127
  async def async_read(
@@ -89,36 +132,35 @@ class CSVReader(Reader):
89
132
  page_size: int = 1000,
90
133
  name: Optional[str] = None,
91
134
  ) -> List[Document]:
92
- """
93
- Read a CSV file asynchronously, processing batches of rows concurrently.
135
+ """Read a CSV file asynchronously, processing batches of rows concurrently.
94
136
 
95
137
  Args:
96
- file: Path or file-like object
97
- delimiter: CSV delimiter
98
- quotechar: CSV quote character
99
- page_size: Number of rows per page
138
+ file: Path to CSV file or file-like object.
139
+ delimiter: CSV field delimiter. Default is comma.
140
+ quotechar: CSV quote character. Default is double quote.
141
+ page_size: Number of rows per page for large files.
142
+ name: Optional name override for the document.
100
143
 
101
144
  Returns:
102
- List of Document objects
145
+ List of Document objects.
146
+
147
+ Raises:
148
+ FileNotFoundError: If the file path doesn't exist.
103
149
  """
104
150
  try:
105
151
  if isinstance(file, Path):
106
152
  if not file.exists():
107
153
  raise FileNotFoundError(f"Could not find file: {file}")
108
- logger.info(f"Reading async: {file}")
109
- async with aiofiles.open(file, mode="r", encoding="utf-8", newline="") as file_content:
154
+ log_debug(f"Reading async: {file}")
155
+ async with aiofiles.open(file, mode="r", encoding=self.encoding or "utf-8", newline="") as file_content:
110
156
  content = await file_content.read()
111
157
  file_content_io = io.StringIO(content)
158
+ csv_name = name or file.stem
112
159
  else:
113
- logger.info(f"Reading retrieved file async: {file.name}")
160
+ log_debug(f"Reading retrieved file async: {getattr(file, 'name', 'BytesIO')}")
114
161
  file.seek(0)
115
- file_content_io = io.StringIO(file.read().decode("utf-8")) # type: ignore
116
-
117
- csv_name = name or (
118
- Path(file.name).stem
119
- if isinstance(file, Path)
120
- else (getattr(file, "name", "csv_file").split(".")[0] if hasattr(file, "name") else "csv_file")
121
- )
162
+ file_content_io = io.StringIO(file.read().decode(self.encoding or "utf-8"))
163
+ csv_name = name or getattr(file, "name", "csv_file").split(".")[0]
122
164
 
123
165
  file_content_io.seek(0)
124
166
  csv_reader = csv.reader(file_content_io, delimiter=delimiter, quotechar=quotechar)
@@ -126,7 +168,8 @@ class CSVReader(Reader):
126
168
  total_rows = len(rows)
127
169
 
128
170
  if total_rows <= 10:
129
- csv_content = " ".join(", ".join(row) for row in rows)
171
+ # Small files: single document
172
+ csv_content = " ".join(", ".join(stringify_cell_value(cell) for cell in row) for row in rows)
130
173
  documents = [
131
174
  Document(
132
175
  name=csv_name,
@@ -135,14 +178,15 @@ class CSVReader(Reader):
135
178
  )
136
179
  ]
137
180
  else:
181
+ # Large files: paginate and process in parallel
138
182
  pages = []
139
183
  for i in range(0, total_rows, page_size):
140
184
  pages.append(rows[i : i + page_size])
141
185
 
142
186
  async def _process_page(page_number: int, page_rows: List[List[str]]) -> Document:
143
- """Process a page of rows into a document"""
187
+ """Process a page of rows into a document."""
144
188
  start_row = (page_number - 1) * page_size + 1
145
- page_content = " ".join(", ".join(row) for row in page_rows)
189
+ page_content = " ".join(", ".join(stringify_cell_value(cell) for cell in row) for row in page_rows)
146
190
 
147
191
  return Document(
148
192
  name=csv_name,
@@ -159,8 +203,13 @@ class CSVReader(Reader):
159
203
  documents = await self.chunk_documents_async(documents)
160
204
 
161
205
  return documents
206
+ except FileNotFoundError:
207
+ raise
208
+ except UnicodeDecodeError as e:
209
+ file_desc = getattr(file, "name", str(file)) if isinstance(file, IO) else file
210
+ log_error(f"Encoding error reading {file_desc}: {e}. Try specifying a different encoding.")
211
+ return []
162
212
  except Exception as e:
163
- logger.error(
164
- f"Error reading async: {getattr(file, 'name', str(file)) if isinstance(file, IO) else file}: {e}"
165
- )
213
+ file_desc = getattr(file, "name", str(file)) if isinstance(file, IO) else file
214
+ log_error(f"Error reading {file_desc}: {e}")
166
215
  return []
@@ -8,7 +8,7 @@ from agno.knowledge.chunking.strategy import ChunkingStrategy, ChunkingStrategyT
8
8
  from agno.knowledge.document.base import Document
9
9
  from agno.knowledge.reader.base import Reader
10
10
  from agno.knowledge.types import ContentType
11
- from agno.utils.log import log_info, logger
11
+ from agno.utils.log import log_debug, log_error
12
12
 
13
13
  try:
14
14
  from docx import Document as DocxDocument # type: ignore
@@ -23,10 +23,11 @@ class DocxReader(Reader):
23
23
  super().__init__(chunking_strategy=chunking_strategy, **kwargs)
24
24
 
25
25
  @classmethod
26
- def get_supported_chunking_strategies(self) -> List[ChunkingStrategyType]:
26
+ def get_supported_chunking_strategies(cls) -> List[ChunkingStrategyType]:
27
27
  """Get the list of supported chunking strategies for DOCX readers."""
28
28
  return [
29
29
  ChunkingStrategyType.DOCUMENT_CHUNKER,
30
+ ChunkingStrategyType.CODE_CHUNKER,
30
31
  ChunkingStrategyType.FIXED_SIZE_CHUNKER,
31
32
  ChunkingStrategyType.SEMANTIC_CHUNKER,
32
33
  ChunkingStrategyType.AGENTIC_CHUNKER,
@@ -34,7 +35,7 @@ class DocxReader(Reader):
34
35
  ]
35
36
 
36
37
  @classmethod
37
- def get_supported_content_types(self) -> List[ContentType]:
38
+ def get_supported_content_types(cls) -> List[ContentType]:
38
39
  return [ContentType.DOCX, ContentType.DOC]
39
40
 
40
41
  def read(self, file: Union[Path, IO[Any]], name: Optional[str] = None) -> List[Document]:
@@ -43,15 +44,13 @@ class DocxReader(Reader):
43
44
  if isinstance(file, Path):
44
45
  if not file.exists():
45
46
  raise FileNotFoundError(f"Could not find file: {file}")
46
- log_info(f"Reading: {file}")
47
+ log_debug(f"Reading: {file}")
47
48
  docx_document = DocxDocument(str(file))
48
49
  doc_name = name or file.stem
49
50
  else:
50
- log_info(f"Reading uploaded file: {getattr(file, 'name', 'docx_file')}")
51
+ log_debug(f"Reading uploaded file: {getattr(file, 'name', 'BytesIO')}")
51
52
  docx_document = DocxDocument(file)
52
- doc_name = name or (
53
- getattr(file, "name", "docx_file").split(".")[0] if hasattr(file, "name") else "docx_file"
54
- )
53
+ doc_name = name or getattr(file, "name", "docx_file").split(".")[0]
55
54
 
56
55
  doc_content = "\n\n".join([para.text for para in docx_document.paragraphs])
57
56
 
@@ -70,7 +69,7 @@ class DocxReader(Reader):
70
69
  return documents
71
70
 
72
71
  except Exception as e:
73
- logger.error(f"Error reading file: {e}")
72
+ log_error(f"Error reading file: {e}")
74
73
  return []
75
74
 
76
75
  async def async_read(self, file: Union[Path, IO[Any]], name: Optional[str] = None) -> List[Document]:
@@ -78,5 +77,5 @@ class DocxReader(Reader):
78
77
  try:
79
78
  return await asyncio.to_thread(self.read, file, name)
80
79
  except Exception as e:
81
- logger.error(f"Error reading file asynchronously: {e}")
80
+ log_error(f"Error reading file asynchronously: {e}")
82
81
  return []