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
@@ -108,14 +108,16 @@ class FileGenerationTools(Toolkit):
108
108
  # Save file to disk (if output_directory is set)
109
109
  file_path = self._save_file_to_disk(json_content, filename)
110
110
 
111
+ content_bytes = json_content.encode("utf-8")
112
+
111
113
  # Create FileArtifact
112
114
  file_artifact = File(
113
115
  id=str(uuid4()),
114
- content=json_content,
116
+ content=content_bytes,
115
117
  mime_type="application/json",
116
118
  file_type="json",
117
119
  filename=filename,
118
- size=len(json_content.encode("utf-8")),
120
+ size=len(content_bytes),
119
121
  filepath=file_path if file_path else None,
120
122
  )
121
123
 
@@ -195,14 +197,16 @@ class FileGenerationTools(Toolkit):
195
197
  # Save file to disk (if output_directory is set)
196
198
  file_path = self._save_file_to_disk(csv_content, filename)
197
199
 
200
+ content_bytes = csv_content.encode("utf-8")
201
+
198
202
  # Create FileArtifact
199
203
  file_artifact = File(
200
204
  id=str(uuid4()),
201
- content=csv_content,
205
+ content=content_bytes,
202
206
  mime_type="text/csv",
203
207
  file_type="csv",
204
208
  filename=filename,
205
- size=len(csv_content.encode("utf-8")),
209
+ size=len(content_bytes),
206
210
  filepath=file_path if file_path else None,
207
211
  )
208
212
 
@@ -325,14 +329,16 @@ class FileGenerationTools(Toolkit):
325
329
  # Save file to disk (if output_directory is set)
326
330
  file_path = self._save_file_to_disk(content, filename)
327
331
 
332
+ content_bytes = content.encode("utf-8")
333
+
328
334
  # Create FileArtifact
329
335
  file_artifact = File(
330
336
  id=str(uuid4()),
331
- content=content,
337
+ content=content_bytes,
332
338
  mime_type="text/plain",
333
339
  file_type="txt",
334
340
  filename=filename,
335
- size=len(content.encode("utf-8")),
341
+ size=len(content_bytes),
336
342
  filepath=file_path if file_path else None,
337
343
  )
338
344
 
agno/tools/firecrawl.py CHANGED
@@ -101,8 +101,10 @@ class FirecrawlTools(Toolkit):
101
101
  The results of the crawling.
102
102
  """
103
103
  params: Dict[str, Any] = {}
104
- if self.limit or limit:
105
- params["limit"] = self.limit or limit
104
+ if self.limit is not None:
105
+ params["limit"] = self.limit
106
+ elif limit is not None:
107
+ params["limit"] = limit
106
108
  if self.formats:
107
109
  params["scrape_options"] = ScrapeOptions(formats=self.formats) # type: ignore
108
110
 
@@ -129,15 +131,21 @@ class FirecrawlTools(Toolkit):
129
131
  limit (int): The maximum number of results to return.
130
132
  """
131
133
  params: Dict[str, Any] = {}
132
- if self.limit or limit:
133
- params["limit"] = self.limit or limit
134
+ if self.limit is not None:
135
+ params["limit"] = self.limit
136
+ elif limit is not None:
137
+ params["limit"] = limit
134
138
  if self.formats:
135
139
  params["scrape_options"] = ScrapeOptions(formats=self.formats) # type: ignore
136
140
  if self.search_params:
137
141
  params.update(self.search_params)
138
142
 
139
143
  search_result = self.app.search(query, **params)
140
- if search_result.success:
141
- return json.dumps(search_result.data, cls=CustomJSONEncoder)
144
+
145
+ if hasattr(search_result, "success"):
146
+ if search_result.success:
147
+ return json.dumps(search_result.data, cls=CustomJSONEncoder)
148
+ else:
149
+ return f"Error searching with the Firecrawl tool: {search_result.error}"
142
150
  else:
143
- return "Error searching with the Firecrawl tool: " + search_result.error
151
+ return json.dumps(search_result.model_dump(), cls=CustomJSONEncoder)
agno/tools/function.py CHANGED
@@ -54,9 +54,17 @@ class UserInputField:
54
54
 
55
55
  @classmethod
56
56
  def from_dict(cls, data: Dict[str, Any]) -> "UserInputField":
57
+ type_mapping = {"str": str, "int": int, "float": float, "bool": bool, "list": list, "dict": dict}
58
+ field_type_raw = data["field_type"]
59
+ if isinstance(field_type_raw, str):
60
+ field_type = type_mapping.get(field_type_raw, str)
61
+ elif isinstance(field_type_raw, type):
62
+ field_type = field_type_raw
63
+ else:
64
+ field_type = str
57
65
  return cls(
58
66
  name=data["name"],
59
- field_type=eval(data["field_type"]), # Convert string type name to actual type
67
+ field_type=field_type,
60
68
  description=data["description"],
61
69
  value=data["value"],
62
70
  )
@@ -125,10 +133,6 @@ class Function(BaseModel):
125
133
  _team: Optional[Any] = None
126
134
  # The run context that the function is associated with
127
135
  _run_context: Optional[RunContext] = None
128
- # The session state that the function is associated with
129
- _session_state: Optional[Dict[str, Any]] = None
130
- # The dependencies that the function is associated with
131
- _dependencies: Optional[Dict[str, Any]] = None
132
136
 
133
137
  # Media context that the function is associated with
134
138
  _images: Optional[Sequence[Image]] = None
@@ -142,6 +146,19 @@ class Function(BaseModel):
142
146
  include={"name", "description", "parameters", "strict", "requires_confirmation", "external_execution"},
143
147
  )
144
148
 
149
+ @classmethod
150
+ def from_dict(cls, data: Dict[str, Any]) -> "Function":
151
+ """Reconstruct a Function from a dictionary."""
152
+
153
+ return cls(
154
+ name=data.get("name"),
155
+ description=data.get("description"),
156
+ parameters=data.get("parameters"),
157
+ strict=data.get("strict"),
158
+ requires_confirmation=data.get("requires_confirmation", False),
159
+ external_execution=data.get("external_execution", False),
160
+ )
161
+
145
162
  def model_copy(self, *, deep: bool = False) -> "Function":
146
163
  """
147
164
  Override model_copy to handle callable fields that can't be deep copied (pickled).
@@ -201,10 +218,6 @@ class Function(BaseModel):
201
218
  del type_hints["team"]
202
219
  if "run_context" in sig.parameters and "run_context" in type_hints:
203
220
  del type_hints["run_context"]
204
- if "session_state" in sig.parameters and "session_state" in type_hints:
205
- del type_hints["session_state"]
206
- if "dependencies" in sig.parameters and "dependencies" in type_hints:
207
- del type_hints["dependencies"]
208
221
 
209
222
  # Remove media parameters from type hints as they are injected automatically
210
223
  if "images" in sig.parameters and "images" in type_hints:
@@ -227,8 +240,6 @@ class Function(BaseModel):
227
240
  "agent",
228
241
  "team",
229
242
  "run_context",
230
- "session_state",
231
- "dependencies",
232
243
  "self",
233
244
  "images",
234
245
  "videos",
@@ -268,8 +279,6 @@ class Function(BaseModel):
268
279
  "agent",
269
280
  "team",
270
281
  "run_context",
271
- "session_state",
272
- "dependencies",
273
282
  "self",
274
283
  "images",
275
284
  "videos",
@@ -288,8 +297,6 @@ class Function(BaseModel):
288
297
  "agent",
289
298
  "team",
290
299
  "run_context",
291
- "session_state",
292
- "dependencies",
293
300
  "self",
294
301
  "images",
295
302
  "videos",
@@ -346,10 +353,6 @@ class Function(BaseModel):
346
353
  del type_hints["team"]
347
354
  if "run_context" in sig.parameters and "run_context" in type_hints:
348
355
  del type_hints["run_context"]
349
- if "session_state" in sig.parameters and "session_state" in type_hints:
350
- del type_hints["session_state"]
351
- if "dependencies" in sig.parameters and "dependencies" in type_hints:
352
- del type_hints["dependencies"]
353
356
  if "images" in sig.parameters and "images" in type_hints:
354
357
  del type_hints["images"]
355
358
  if "videos" in sig.parameters and "videos" in type_hints:
@@ -366,8 +369,6 @@ class Function(BaseModel):
366
369
  "agent",
367
370
  "team",
368
371
  "run_context",
369
- "session_state",
370
- "dependencies",
371
372
  "self",
372
373
  "images",
373
374
  "videos",
@@ -480,10 +481,15 @@ class Function(BaseModel):
480
481
  # Don't wrap callables that are already wrapped with validate_call
481
482
  elif getattr(func, "_wrapped_for_validation", False):
482
483
  return func
483
- # Don't wrap functions with session_state parameter
484
- # session_state needs to be passed by reference, not copied by pydantic's validation
485
- elif "session_state" in signature(func).parameters:
484
+
485
+ # Don't wrap functions with framework-injected parameters
486
+ # These parameters (agent, team) are
487
+ # injected by the framework at runtime and shouldn't be validated by Pydantic
488
+ sig = signature(func)
489
+ framework_params = {"agent", "team"}
490
+ if framework_params & set(sig.parameters.keys()):
486
491
  return func
492
+
487
493
  # Wrap the callable with validate_call
488
494
  else:
489
495
  wrapped = validate_call(func, config=dict(arbitrary_types_allowed=True)) # type: ignore
@@ -538,8 +544,6 @@ class Function(BaseModel):
538
544
  "agent",
539
545
  "team",
540
546
  "run_context",
541
- "session_state",
542
- "dependencies",
543
547
  "images",
544
548
  "videos",
545
549
  "audios",
@@ -561,10 +565,6 @@ class Function(BaseModel):
561
565
  del copy_entrypoint_args["team"]
562
566
  if "run_context" in copy_entrypoint_args:
563
567
  del copy_entrypoint_args["run_context"]
564
- if "session_state" in copy_entrypoint_args:
565
- del copy_entrypoint_args["session_state"]
566
- if "dependencies" in copy_entrypoint_args:
567
- del copy_entrypoint_args["dependencies"]
568
568
  if "images" in copy_entrypoint_args:
569
569
  del copy_entrypoint_args["images"]
570
570
  if "videos" in copy_entrypoint_args:
@@ -691,21 +691,15 @@ class FunctionCall(BaseModel):
691
691
  from inspect import signature
692
692
 
693
693
  pre_hook_args = {}
694
- # Check if the pre-hook has and agent argument
694
+ # Check if the pre-hook has an agent argument
695
695
  if "agent" in signature(self.function.pre_hook).parameters:
696
696
  pre_hook_args["agent"] = self.function._agent
697
- # Check if the pre-hook has an team argument
697
+ # Check if the pre-hook has a team argument
698
698
  if "team" in signature(self.function.pre_hook).parameters:
699
699
  pre_hook_args["team"] = self.function._team
700
- # Check if the pre-hook has an session_state argument
700
+ # Check if the pre-hook has a run_context argument
701
701
  if "run_context" in signature(self.function.pre_hook).parameters:
702
702
  pre_hook_args["run_context"] = self.function._run_context
703
- # Check if the pre-hook has an session_state argument
704
- if "session_state" in signature(self.function.pre_hook).parameters:
705
- pre_hook_args["session_state"] = self.function._session_state
706
- # Check if the pre-hook has an dependencies argument
707
- if "dependencies" in signature(self.function.pre_hook).parameters:
708
- pre_hook_args["dependencies"] = self.function._dependencies
709
703
  # Check if the pre-hook has an fc argument
710
704
  if "fc" in signature(self.function.pre_hook).parameters:
711
705
  pre_hook_args["fc"] = self
@@ -725,21 +719,15 @@ class FunctionCall(BaseModel):
725
719
  from inspect import signature
726
720
 
727
721
  post_hook_args = {}
728
- # Check if the post-hook has and agent argument
722
+ # Check if the post-hook has an agent argument
729
723
  if "agent" in signature(self.function.post_hook).parameters:
730
724
  post_hook_args["agent"] = self.function._agent
731
- # Check if the post-hook has an team argument
725
+ # Check if the post-hook has a team argument
732
726
  if "team" in signature(self.function.post_hook).parameters:
733
727
  post_hook_args["team"] = self.function._team
734
- # Check if the post-hook has an session_state argument
728
+ # Check if the post-hook has a run_context argument
735
729
  if "run_context" in signature(self.function.post_hook).parameters:
736
730
  post_hook_args["run_context"] = self.function._run_context
737
- # Check if the post-hook has an session_state argument
738
- if "session_state" in signature(self.function.post_hook).parameters:
739
- post_hook_args["session_state"] = self.function._session_state
740
- # Check if the post-hook has an dependencies argument
741
- if "dependencies" in signature(self.function.post_hook).parameters:
742
- post_hook_args["dependencies"] = self.function._dependencies
743
731
  # Check if the post-hook has an fc argument
744
732
  if "fc" in signature(self.function.post_hook).parameters:
745
733
  post_hook_args["fc"] = self
@@ -760,18 +748,12 @@ class FunctionCall(BaseModel):
760
748
  # Check if the entrypoint has an agent argument
761
749
  if "agent" in signature(self.function.entrypoint).parameters: # type: ignore
762
750
  entrypoint_args["agent"] = self.function._agent
763
- # Check if the entrypoint has an team argument
751
+ # Check if the entrypoint has a team argument
764
752
  if "team" in signature(self.function.entrypoint).parameters: # type: ignore
765
753
  entrypoint_args["team"] = self.function._team
766
- # Check if the entrypoint has an run_context argument
754
+ # Check if the entrypoint has a run_context argument
767
755
  if "run_context" in signature(self.function.entrypoint).parameters: # type: ignore
768
756
  entrypoint_args["run_context"] = self.function._run_context
769
- # Check if the entrypoint has an session_state argument
770
- if "session_state" in signature(self.function.entrypoint).parameters: # type: ignore
771
- entrypoint_args["session_state"] = self.function._session_state
772
- # Check if the entrypoint has an dependencies argument
773
- if "dependencies" in signature(self.function.entrypoint).parameters: # type: ignore
774
- entrypoint_args["dependencies"] = self.function._dependencies
775
757
  # Check if the entrypoint has an fc argument
776
758
  if "fc" in signature(self.function.entrypoint).parameters: # type: ignore
777
759
  entrypoint_args["fc"] = self
@@ -795,18 +777,12 @@ class FunctionCall(BaseModel):
795
777
  # Check if the hook has an agent argument
796
778
  if "agent" in signature(hook).parameters:
797
779
  hook_args["agent"] = self.function._agent
798
- # Check if the hook has an team argument
780
+ # Check if the hook has a team argument
799
781
  if "team" in signature(hook).parameters:
800
782
  hook_args["team"] = self.function._team
801
- # Check if the hook has an run_context argument
783
+ # Check if the hook has a run_context argument
802
784
  if "run_context" in signature(hook).parameters:
803
785
  hook_args["run_context"] = self.function._run_context
804
- # Check if the hook has an session_state argument
805
- if "session_state" in signature(hook).parameters:
806
- hook_args["session_state"] = self.function._session_state
807
- # Check if the hook has an dependencies argument
808
- if "dependencies" in signature(hook).parameters:
809
- hook_args["dependencies"] = self.function._dependencies
810
786
  if "name" in signature(hook).parameters:
811
787
  hook_args["name"] = name
812
788
  if "function_name" in signature(hook).parameters:
@@ -897,7 +873,7 @@ class FunctionCall(BaseModel):
897
873
  return FunctionExecutionResult(status="success", result=cached_result)
898
874
 
899
875
  # Execute function
900
- execution_result = None
876
+ execution_result: FunctionExecutionResult
901
877
  exception_to_raise = None
902
878
 
903
879
  try:
@@ -908,21 +884,16 @@ class FunctionCall(BaseModel):
908
884
  else:
909
885
  result = self.function.entrypoint(**entrypoint_args, **self.arguments) # type: ignore
910
886
 
911
- updated_session_state = None
912
- if entrypoint_args.get("run_context") is not None:
913
- run_context = entrypoint_args.get("run_context")
914
- updated_session_state = (
915
- run_context.session_state
916
- if run_context is not None and run_context.session_state is not None
917
- else None
918
- )
919
- else:
920
- if self.function._session_state is not None:
921
- updated_session_state = self.function._session_state
922
-
923
887
  # Handle generator case
924
888
  if isgenerator(result):
925
889
  self.result = result # Store generator directly, can't cache
890
+ # For generators, don't capture updated_session_state yet -
891
+ # session_state is passed by reference, so mutations made during
892
+ # generator iteration are already reflected in the original dict.
893
+ # Returning None prevents stale state from being merged later.
894
+ execution_result = FunctionExecutionResult(
895
+ status="success", result=self.result, updated_session_state=None
896
+ )
926
897
  else:
927
898
  self.result = result
928
899
  # Only cache non-generator results
@@ -931,14 +902,24 @@ class FunctionCall(BaseModel):
931
902
  cache_file = self.function._get_cache_file_path(cache_key)
932
903
  self.function._save_to_cache(cache_file, self.result)
933
904
 
934
- execution_result = FunctionExecutionResult(
935
- status="success", result=self.result, updated_session_state=updated_session_state
936
- )
905
+ updated_session_state = None
906
+ if entrypoint_args.get("run_context") is not None:
907
+ run_context = entrypoint_args.get("run_context")
908
+ updated_session_state = (
909
+ run_context.session_state
910
+ if run_context is not None and run_context.session_state is not None
911
+ else None
912
+ )
913
+
914
+ execution_result = FunctionExecutionResult(
915
+ status="success", result=self.result, updated_session_state=updated_session_state
916
+ )
937
917
 
938
918
  except AgentRunException as e:
939
919
  log_debug(f"{e.__class__.__name__}: {e}")
940
920
  self.error = str(e)
941
921
  exception_to_raise = e
922
+ execution_result = FunctionExecutionResult(status="failure", error=str(e))
942
923
  except Exception as e:
943
924
  log_warning(f"Could not run function {self.get_call_str()}")
944
925
  log_exception(e)
@@ -948,10 +929,10 @@ class FunctionCall(BaseModel):
948
929
  finally:
949
930
  self._handle_post_hook()
950
931
 
951
- if exception_to_raise is not None:
952
- raise exception_to_raise
932
+ if exception_to_raise is not None:
933
+ raise exception_to_raise
953
934
 
954
- return execution_result # type: ignore[return-value]
935
+ return execution_result
955
936
 
956
937
  async def _handle_pre_hook_async(self):
957
938
  """Handles the async pre-hook for the function call."""
@@ -963,18 +944,12 @@ class FunctionCall(BaseModel):
963
944
  # Check if the pre-hook has an agent argument
964
945
  if "agent" in signature(self.function.pre_hook).parameters:
965
946
  pre_hook_args["agent"] = self.function._agent
966
- # Check if the pre-hook has an team argument
947
+ # Check if the pre-hook has a team argument
967
948
  if "team" in signature(self.function.pre_hook).parameters:
968
949
  pre_hook_args["team"] = self.function._team
969
- # Check if the pre-hook has an run_context argument
950
+ # Check if the pre-hook has a run_context argument
970
951
  if "run_context" in signature(self.function.pre_hook).parameters:
971
952
  pre_hook_args["run_context"] = self.function._run_context
972
- # Check if the pre-hook has an session_state argument
973
- if "session_state" in signature(self.function.pre_hook).parameters:
974
- pre_hook_args["session_state"] = self.function._session_state
975
- # Check if the pre-hook has an dependencies argument
976
- if "dependencies" in signature(self.function.pre_hook).parameters:
977
- pre_hook_args["dependencies"] = self.function._dependencies
978
953
  # Check if the pre-hook has an fc argument
979
954
  if "fc" in signature(self.function.pre_hook).parameters:
980
955
  pre_hook_args["fc"] = self
@@ -998,19 +973,12 @@ class FunctionCall(BaseModel):
998
973
  # Check if the post-hook has an agent argument
999
974
  if "agent" in signature(self.function.post_hook).parameters:
1000
975
  post_hook_args["agent"] = self.function._agent
1001
- # Check if the post-hook has an team argument
976
+ # Check if the post-hook has a team argument
1002
977
  if "team" in signature(self.function.post_hook).parameters:
1003
978
  post_hook_args["team"] = self.function._team
1004
- # Check if the post-hook has an run_context argument
979
+ # Check if the post-hook has a run_context argument
1005
980
  if "run_context" in signature(self.function.post_hook).parameters:
1006
981
  post_hook_args["run_context"] = self.function._run_context
1007
- # Check if the post-hook has an session_state argument
1008
- if "session_state" in signature(self.function.post_hook).parameters:
1009
- post_hook_args["session_state"] = self.function._session_state
1010
- # Check if the post-hook has an dependencies argument
1011
- if "dependencies" in signature(self.function.post_hook).parameters:
1012
- post_hook_args["dependencies"] = self.function._dependencies
1013
-
1014
982
  # Check if the post-hook has an fc argument
1015
983
  if "fc" in signature(self.function.post_hook).parameters:
1016
984
  post_hook_args["fc"] = self
@@ -1117,7 +1085,7 @@ class FunctionCall(BaseModel):
1117
1085
  return FunctionExecutionResult(status="success", result=cached_result)
1118
1086
 
1119
1087
  # Execute function
1120
- execution_result = None
1088
+ execution_result: FunctionExecutionResult
1121
1089
  exception_to_raise = None
1122
1090
 
1123
1091
  try:
@@ -1131,10 +1099,15 @@ class FunctionCall(BaseModel):
1131
1099
  else:
1132
1100
  result = self.function.entrypoint(**entrypoint_args, **self.arguments)
1133
1101
 
1102
+ # Handle both sync and async entrypoints
1134
1103
  if isasyncgenfunction(self.function.entrypoint):
1135
1104
  self.result = result # Store async generator directly
1105
+ elif iscoroutinefunction(self.function.entrypoint):
1106
+ self.result = await result # Await coroutine result
1107
+ elif isgeneratorfunction(self.function.entrypoint):
1108
+ self.result = result # Store sync generator directly
1136
1109
  else:
1137
- self.result = await result
1110
+ self.result = result # Sync function, result is already computed
1138
1111
 
1139
1112
  # Only cache if not a generator
1140
1113
  if self.function.cache_results and not (isgenerator(self.result) or isasyncgen(self.result)):
@@ -1142,14 +1115,21 @@ class FunctionCall(BaseModel):
1142
1115
  cache_file = self.function._get_cache_file_path(cache_key)
1143
1116
  self.function._save_to_cache(cache_file, self.result)
1144
1117
 
1145
- updated_session_state = None
1146
- if entrypoint_args.get("run_context") is not None:
1147
- run_context = entrypoint_args.get("run_context")
1148
- updated_session_state = (
1149
- run_context.session_state
1150
- if run_context is not None and run_context.session_state is not None
1151
- else None
1152
- )
1118
+ # For generators, don't capture updated_session_state -
1119
+ # session_state is passed by reference, so mutations made during
1120
+ # generator iteration are already reflected in the original dict.
1121
+ # Returning None prevents stale state from being merged later.
1122
+ if isgenerator(self.result) or isasyncgen(self.result):
1123
+ updated_session_state = None
1124
+ else:
1125
+ updated_session_state = None
1126
+ if entrypoint_args.get("run_context") is not None:
1127
+ run_context = entrypoint_args.get("run_context")
1128
+ updated_session_state = (
1129
+ run_context.session_state
1130
+ if run_context is not None and run_context.session_state is not None
1131
+ else None
1132
+ )
1153
1133
 
1154
1134
  execution_result = FunctionExecutionResult(
1155
1135
  status="success", result=self.result, updated_session_state=updated_session_state
@@ -1159,6 +1139,7 @@ class FunctionCall(BaseModel):
1159
1139
  log_debug(f"{e.__class__.__name__}: {e}")
1160
1140
  self.error = str(e)
1161
1141
  exception_to_raise = e
1142
+ execution_result = FunctionExecutionResult(status="failure", error=str(e))
1162
1143
  except Exception as e:
1163
1144
  log_warning(f"Could not run function {self.get_call_str()}")
1164
1145
  log_exception(e)
@@ -1171,10 +1152,10 @@ class FunctionCall(BaseModel):
1171
1152
  else:
1172
1153
  self._handle_post_hook()
1173
1154
 
1174
- if exception_to_raise is not None:
1175
- raise exception_to_raise
1155
+ if exception_to_raise is not None:
1156
+ raise exception_to_raise
1176
1157
 
1177
- return execution_result # type: ignore[return-value]
1158
+ return execution_result
1178
1159
 
1179
1160
 
1180
1161
  class ToolResult(BaseModel):
@@ -11,6 +11,15 @@ except ImportError:
11
11
  raise ImportError("`bigquery` not installed. Please install using `pip install google-cloud-bigquery`")
12
12
 
13
13
 
14
+ def _clean_sql(sql: str) -> str:
15
+ """Clean SQL query by normalizing whitespace while preserving token boundaries.
16
+
17
+ Replaces newlines with spaces (not empty strings) to prevent line comments
18
+ from swallowing subsequent SQL statements.
19
+ """
20
+ return sql.replace("\\n", " ").replace("\n", " ")
21
+
22
+
14
23
  class GoogleBigQueryTools(Toolkit):
15
24
  def __init__(
16
25
  self,
@@ -106,12 +115,12 @@ class GoogleBigQueryTools(Toolkit):
106
115
  """
107
116
  try:
108
117
  log_debug(f"Running Google SQL |\n{sql}")
109
- cleaned_query = sql.replace("\\n", " ").replace("\n", "").replace("\\", "")
118
+ cleaned_query = _clean_sql(sql)
110
119
  job_config = bigquery.QueryJobConfig(default_dataset=f"{self.project}.{self.dataset}")
111
120
  query_job = self.client.query(cleaned_query, job_config)
112
121
  results = query_job.result()
113
122
  results_str = str([dict(row) for row in results])
114
- return results_str.replace("\\", "").replace("\n", "")
123
+ return results_str.replace("\n", " ")
115
124
  except Exception as e:
116
125
  logger.error(f"Error while executing SQL: {e}")
117
126
  return ""
@@ -69,6 +69,7 @@ from pathlib import Path
69
69
  from typing import Any, List, Optional, Union
70
70
 
71
71
  from agno.tools import Toolkit
72
+ from agno.utils.log import log_error
72
73
 
73
74
  try:
74
75
  from google.auth.transport.requests import Request
@@ -202,7 +203,7 @@ class GoogleDriveTools(Toolkit):
202
203
  items = results.get("files", [])
203
204
  return items
204
205
  except Exception as error:
205
- print(f"Could not list files: {error}")
206
+ log_error(f"Could not list files: {error}")
206
207
  return []
207
208
 
208
209
  @authenticate
@@ -238,7 +239,7 @@ class GoogleDriveTools(Toolkit):
238
239
  )
239
240
  return uploaded_file
240
241
  except Exception as error:
241
- print(f"Could not upload file '{file_path}': {error}")
242
+ log_error(f"Could not upload file '{file_path}': {error}")
242
243
  return None
243
244
 
244
245
  @authenticate
@@ -266,5 +267,5 @@ class GoogleDriveTools(Toolkit):
266
267
  print(f"Download progress: {int(status.progress() * 100)}%.")
267
268
  return dest_path
268
269
  except Exception as error:
269
- print(f"Could not download file '{file_id}': {error}")
270
+ log_error(f"Could not download file '{file_id}': {error}")
270
271
  return None
agno/tools/knowledge.py CHANGED
@@ -1,9 +1,10 @@
1
1
  import json
2
2
  from textwrap import dedent
3
- from typing import Any, Dict, List, Optional
3
+ from typing import Any, List, Optional
4
4
 
5
5
  from agno.knowledge.document import Document
6
6
  from agno.knowledge.knowledge import Knowledge
7
+ from agno.run import RunContext
7
8
  from agno.tools import Toolkit
8
9
  from agno.utils.log import log_debug, log_error
9
10
 
@@ -55,7 +56,7 @@ class KnowledgeTools(Toolkit):
55
56
  **kwargs,
56
57
  )
57
58
 
58
- def think(self, session_state: Dict[str, Any], thought: str) -> str:
59
+ def think(self, run_context: RunContext, thought: str) -> str:
59
60
  """Use this tool as a scratchpad to reason about the question, refine your approach, brainstorm search terms, or revise your plan.
60
61
 
61
62
  Call `Think` whenever you need to figure out what to do next, analyze the user's question, or plan your approach.
@@ -71,8 +72,10 @@ class KnowledgeTools(Toolkit):
71
72
  log_debug(f"Thought: {thought}")
72
73
 
73
74
  # Add the thought to the Agent state
75
+ session_state = run_context.session_state
74
76
  if session_state is None:
75
77
  session_state = {}
78
+ run_context.session_state = session_state
76
79
  if "thoughts" not in session_state:
77
80
  session_state["thoughts"] = []
78
81
  session_state["thoughts"].append(thought)
@@ -89,7 +92,7 @@ class KnowledgeTools(Toolkit):
89
92
  log_error(f"Error recording thought: {e}")
90
93
  return f"Error recording thought: {e}"
91
94
 
92
- def search_knowledge(self, session_state: Dict[str, Any], query: str) -> str:
95
+ def search_knowledge(self, run_context: RunContext, query: str) -> str:
93
96
  """Use this tool to search the knowledge base for relevant information.
94
97
  After thinking through the question, use this tool as many times as needed to search for relevant information.
95
98
 
@@ -111,7 +114,7 @@ class KnowledgeTools(Toolkit):
111
114
  log_error(f"Error searching knowledge base: {e}")
112
115
  return f"Error searching knowledge base: {e}"
113
116
 
114
- def analyze(self, session_state: Dict[str, Any], analysis: str) -> str:
117
+ def analyze(self, run_context: RunContext, analysis: str) -> str:
115
118
  """Use this tool to evaluate whether the returned documents are correct and sufficient.
116
119
  If not, go back to "Think" or "Search" with refined queries.
117
120
 
@@ -125,8 +128,10 @@ class KnowledgeTools(Toolkit):
125
128
  log_debug(f"Analysis: {analysis}")
126
129
 
127
130
  # Add the thought to the Agent state
131
+ session_state = run_context.session_state
128
132
  if session_state is None:
129
133
  session_state = {}
134
+ run_context.session_state = session_state
130
135
  if "analysis" not in session_state:
131
136
  session_state["analysis"] = []
132
137
  session_state["analysis"].append(analysis)