agno 2.3.26__py3-none-any.whl → 2.4.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 (128) hide show
  1. agno/agent/__init__.py +4 -0
  2. agno/agent/agent.py +1368 -541
  3. agno/agent/remote.py +13 -0
  4. agno/db/base.py +339 -0
  5. agno/db/postgres/async_postgres.py +116 -12
  6. agno/db/postgres/postgres.py +1229 -25
  7. agno/db/postgres/schemas.py +48 -1
  8. agno/db/sqlite/async_sqlite.py +119 -4
  9. agno/db/sqlite/schemas.py +51 -0
  10. agno/db/sqlite/sqlite.py +1173 -13
  11. agno/db/utils.py +37 -1
  12. agno/knowledge/__init__.py +4 -0
  13. agno/knowledge/chunking/code.py +1 -1
  14. agno/knowledge/chunking/semantic.py +1 -1
  15. agno/knowledge/chunking/strategy.py +4 -0
  16. agno/knowledge/filesystem.py +412 -0
  17. agno/knowledge/knowledge.py +2767 -2254
  18. agno/knowledge/protocol.py +134 -0
  19. agno/knowledge/reader/arxiv_reader.py +2 -2
  20. agno/knowledge/reader/base.py +9 -7
  21. agno/knowledge/reader/csv_reader.py +5 -5
  22. agno/knowledge/reader/docx_reader.py +2 -2
  23. agno/knowledge/reader/field_labeled_csv_reader.py +2 -2
  24. agno/knowledge/reader/firecrawl_reader.py +2 -2
  25. agno/knowledge/reader/json_reader.py +2 -2
  26. agno/knowledge/reader/markdown_reader.py +2 -2
  27. agno/knowledge/reader/pdf_reader.py +5 -4
  28. agno/knowledge/reader/pptx_reader.py +2 -2
  29. agno/knowledge/reader/reader_factory.py +110 -0
  30. agno/knowledge/reader/s3_reader.py +2 -2
  31. agno/knowledge/reader/tavily_reader.py +2 -2
  32. agno/knowledge/reader/text_reader.py +2 -2
  33. agno/knowledge/reader/web_search_reader.py +2 -2
  34. agno/knowledge/reader/website_reader.py +5 -3
  35. agno/knowledge/reader/wikipedia_reader.py +2 -2
  36. agno/knowledge/reader/youtube_reader.py +2 -2
  37. agno/knowledge/utils.py +37 -29
  38. agno/learn/__init__.py +6 -0
  39. agno/learn/machine.py +35 -0
  40. agno/learn/schemas.py +82 -11
  41. agno/learn/stores/__init__.py +3 -0
  42. agno/learn/stores/decision_log.py +1156 -0
  43. agno/learn/stores/learned_knowledge.py +6 -6
  44. agno/models/anthropic/claude.py +24 -0
  45. agno/models/aws/bedrock.py +20 -0
  46. agno/models/base.py +48 -4
  47. agno/models/cohere/chat.py +25 -0
  48. agno/models/google/gemini.py +50 -5
  49. agno/models/litellm/chat.py +38 -0
  50. agno/models/openai/chat.py +7 -0
  51. agno/models/openrouter/openrouter.py +46 -0
  52. agno/models/response.py +16 -0
  53. agno/os/app.py +83 -44
  54. agno/os/middleware/__init__.py +2 -0
  55. agno/os/middleware/trailing_slash.py +27 -0
  56. agno/os/router.py +1 -0
  57. agno/os/routers/agents/router.py +29 -16
  58. agno/os/routers/agents/schema.py +6 -4
  59. agno/os/routers/components/__init__.py +3 -0
  60. agno/os/routers/components/components.py +466 -0
  61. agno/os/routers/evals/schemas.py +4 -3
  62. agno/os/routers/health.py +3 -3
  63. agno/os/routers/knowledge/knowledge.py +3 -3
  64. agno/os/routers/memory/schemas.py +4 -2
  65. agno/os/routers/metrics/metrics.py +9 -11
  66. agno/os/routers/metrics/schemas.py +10 -6
  67. agno/os/routers/registry/__init__.py +3 -0
  68. agno/os/routers/registry/registry.py +337 -0
  69. agno/os/routers/teams/router.py +20 -8
  70. agno/os/routers/teams/schema.py +6 -4
  71. agno/os/routers/traces/traces.py +5 -5
  72. agno/os/routers/workflows/router.py +38 -11
  73. agno/os/routers/workflows/schema.py +1 -1
  74. agno/os/schema.py +92 -26
  75. agno/os/utils.py +84 -19
  76. agno/reasoning/anthropic.py +2 -2
  77. agno/reasoning/azure_ai_foundry.py +2 -2
  78. agno/reasoning/deepseek.py +2 -2
  79. agno/reasoning/default.py +6 -7
  80. agno/reasoning/gemini.py +2 -2
  81. agno/reasoning/helpers.py +6 -7
  82. agno/reasoning/manager.py +4 -10
  83. agno/reasoning/ollama.py +2 -2
  84. agno/reasoning/openai.py +2 -2
  85. agno/reasoning/vertexai.py +2 -2
  86. agno/registry/__init__.py +3 -0
  87. agno/registry/registry.py +68 -0
  88. agno/run/agent.py +57 -0
  89. agno/run/base.py +7 -0
  90. agno/run/team.py +57 -0
  91. agno/skills/agent_skills.py +10 -3
  92. agno/team/__init__.py +3 -1
  93. agno/team/team.py +1145 -326
  94. agno/tools/duckduckgo.py +25 -71
  95. agno/tools/exa.py +0 -21
  96. agno/tools/function.py +35 -83
  97. agno/tools/knowledge.py +9 -4
  98. agno/tools/mem0.py +11 -10
  99. agno/tools/memory.py +47 -46
  100. agno/tools/parallel.py +0 -7
  101. agno/tools/reasoning.py +30 -23
  102. agno/tools/tavily.py +4 -1
  103. agno/tools/websearch.py +93 -0
  104. agno/tools/website.py +1 -1
  105. agno/tools/wikipedia.py +1 -1
  106. agno/tools/workflow.py +48 -47
  107. agno/utils/agent.py +42 -5
  108. agno/utils/events.py +160 -2
  109. agno/utils/print_response/agent.py +0 -31
  110. agno/utils/print_response/team.py +0 -2
  111. agno/utils/print_response/workflow.py +0 -2
  112. agno/utils/team.py +61 -11
  113. agno/vectordb/lancedb/lance_db.py +4 -1
  114. agno/vectordb/mongodb/mongodb.py +1 -1
  115. agno/vectordb/qdrant/qdrant.py +4 -4
  116. agno/workflow/__init__.py +3 -1
  117. agno/workflow/condition.py +0 -21
  118. agno/workflow/loop.py +0 -21
  119. agno/workflow/parallel.py +0 -21
  120. agno/workflow/router.py +0 -21
  121. agno/workflow/step.py +117 -24
  122. agno/workflow/steps.py +0 -21
  123. agno/workflow/workflow.py +427 -63
  124. {agno-2.3.26.dist-info → agno-2.4.0.dist-info}/METADATA +46 -76
  125. {agno-2.3.26.dist-info → agno-2.4.0.dist-info}/RECORD +128 -117
  126. {agno-2.3.26.dist-info → agno-2.4.0.dist-info}/WHEEL +0 -0
  127. {agno-2.3.26.dist-info → agno-2.4.0.dist-info}/licenses/LICENSE +0 -0
  128. {agno-2.3.26.dist-info → agno-2.4.0.dist-info}/top_level.txt +0 -0
agno/tools/duckduckgo.py CHANGED
@@ -1,36 +1,26 @@
1
- import json
2
- from typing import Any, List, Optional
1
+ from typing import Optional
3
2
 
4
- from agno.tools import Toolkit
5
- from agno.utils.log import log_debug
3
+ from agno.tools.websearch import WebSearchTools
6
4
 
7
- try:
8
- from ddgs import DDGS
9
- except ImportError:
10
- raise ImportError("`ddgs` not installed. Please install using `pip install ddgs`")
11
5
 
12
-
13
- class DuckDuckGoTools(Toolkit):
6
+ class DuckDuckGoTools(WebSearchTools):
14
7
  """
15
- DuckDuckGo is a toolkit for searching using DuckDuckGo easily.
16
- It uses the meta-search library DDGS, so it also has access to other backends.
8
+ DuckDuckGoTools is a convenience wrapper around WebSearchTools with the backend
9
+ defaulting to "duckduckgo".
17
10
  Args:
18
- enable_search (bool): Enable DDGS search function.
19
- enable_news (bool): Enable DDGS news function.
20
- modifier (Optional[str]): A modifier to be used in the search request.
11
+ enable_search (bool): Enable web search function.
12
+ enable_news (bool): Enable news search function.
13
+ modifier (Optional[str]): A modifier to be prepended to search queries.
21
14
  fixed_max_results (Optional[int]): A fixed number of maximum results.
22
- proxy (Optional[str]): Proxy to be used in the search request.
15
+ proxy (Optional[str]): Proxy to be used for requests.
23
16
  timeout (Optional[int]): The maximum number of seconds to wait for a response.
24
- backend (Optional[str]): The backend to be used in the search request.
25
-
17
+ verify_ssl (bool): Whether to verify SSL certificates.
26
18
  """
27
19
 
28
20
  def __init__(
29
21
  self,
30
22
  enable_search: bool = True,
31
23
  enable_news: bool = True,
32
- all: bool = False,
33
- backend: str = "duckduckgo",
34
24
  modifier: Optional[str] = None,
35
25
  fixed_max_results: Optional[int] = None,
36
26
  proxy: Optional[str] = None,
@@ -38,54 +28,18 @@ class DuckDuckGoTools(Toolkit):
38
28
  verify_ssl: bool = True,
39
29
  **kwargs,
40
30
  ):
41
- self.proxy: Optional[str] = proxy
42
- self.timeout: Optional[int] = timeout
43
- self.fixed_max_results: Optional[int] = fixed_max_results
44
- self.modifier: Optional[str] = modifier
45
- self.verify_ssl: bool = verify_ssl
46
- self.backend: str = backend
47
-
48
- tools: List[Any] = []
49
- if all or enable_search:
50
- tools.append(self.duckduckgo_search)
51
- if all or enable_news:
52
- tools.append(self.duckduckgo_news)
53
-
54
- super().__init__(name="duckduckgo", tools=tools, **kwargs)
55
-
56
- def duckduckgo_search(self, query: str, max_results: int = 5) -> str:
57
- """Use this function to search DDGS for a query.
58
-
59
- Args:
60
- query(str): The query to search for.
61
- max_results (optional, default=5): The maximum number of results to return.
62
-
63
- Returns:
64
- The result from DDGS.
65
- """
66
- actual_max_results = self.fixed_max_results or max_results
67
- search_query = f"{self.modifier} {query}" if self.modifier else query
68
-
69
- log_debug(f"Searching DDG for: {search_query} using backend: {self.backend}")
70
- with DDGS(proxy=self.proxy, timeout=self.timeout, verify=self.verify_ssl) as ddgs:
71
- results = ddgs.text(query=search_query, max_results=actual_max_results, backend=self.backend)
72
-
73
- return json.dumps(results, indent=2)
74
-
75
- def duckduckgo_news(self, query: str, max_results: int = 5) -> str:
76
- """Use this function to get the latest news from DDGS.
77
-
78
- Args:
79
- query(str): The query to search for.
80
- max_results (optional, default=5): The maximum number of results to return.
81
-
82
- Returns:
83
- The latest news from DDGS.
84
- """
85
- actual_max_results = self.fixed_max_results or max_results
86
-
87
- log_debug(f"Searching DDG news for: {query} using backend: {self.backend}")
88
- with DDGS(proxy=self.proxy, timeout=self.timeout, verify=self.verify_ssl) as ddgs:
89
- results = ddgs.news(query=query, max_results=actual_max_results, backend=self.backend)
90
-
91
- return json.dumps(results, indent=2)
31
+ super().__init__(
32
+ enable_search=enable_search,
33
+ enable_news=enable_news,
34
+ backend="duckduckgo",
35
+ modifier=modifier,
36
+ fixed_max_results=fixed_max_results,
37
+ proxy=proxy,
38
+ timeout=timeout,
39
+ verify_ssl=verify_ssl,
40
+ **kwargs,
41
+ )
42
+
43
+ # Backward compatibility aliases for old method names
44
+ self.duckduckgo_search = self.web_search
45
+ self.duckduckgo_news = self.search_news
agno/tools/exa.py CHANGED
@@ -27,14 +27,12 @@ class ExaTools(Toolkit):
27
27
  all (bool): Enable all tools. Overrides individual flags when True. Default is False.
28
28
  text (bool): Retrieve text content from results. Default is True.
29
29
  text_length_limit (int): Max length of text content per result. Default is 1000.
30
- highlights (bool): Include highlighted snippets. Deprecated since it was removed in the Exa API. It will be removed from Agno in a future release.
31
30
  api_key (Optional[str]): Exa API key. Retrieved from `EXA_API_KEY` env variable if not provided.
32
31
  num_results (Optional[int]): Default number of search results. Overrides individual searches if set.
33
32
  start_crawl_date (Optional[str]): Include results crawled on/after this date (`YYYY-MM-DD`).
34
33
  end_crawl_date (Optional[str]): Include results crawled on/before this date (`YYYY-MM-DD`).
35
34
  start_published_date (Optional[str]): Include results published on/after this date (`YYYY-MM-DD`).
36
35
  end_published_date (Optional[str]): Include results published on/before this date (`YYYY-MM-DD`).
37
- use_autoprompt (Optional[bool]): Enable autoprompt features in queries. Deprecated since it was removed in the Exa API. It will be removed from Agno in a future release.
38
36
  type (Optional[str]): Specify content type (e.g., article, blog, video).
39
37
  category (Optional[str]): Filter results by category. Options are "company", "research paper", "news", "pdf", "github", "tweet", "personal site", "linkedin profile", "financial report".
40
38
  include_domains (Optional[List[str]]): Restrict results to these domains.
@@ -54,7 +52,6 @@ class ExaTools(Toolkit):
54
52
  all: bool = False,
55
53
  text: bool = True,
56
54
  text_length_limit: int = 1000,
57
- highlights: Optional[bool] = None, # Deprecated
58
55
  summary: bool = False,
59
56
  api_key: Optional[str] = None,
60
57
  num_results: Optional[int] = None,
@@ -63,7 +60,6 @@ class ExaTools(Toolkit):
63
60
  end_crawl_date: Optional[str] = None,
64
61
  start_published_date: Optional[str] = None,
65
62
  end_published_date: Optional[str] = None,
66
- use_autoprompt: Optional[bool] = None,
67
63
  type: Optional[str] = None,
68
64
  category: Optional[str] = None,
69
65
  include_domains: Optional[List[str]] = None,
@@ -85,23 +81,6 @@ class ExaTools(Toolkit):
85
81
  self.text: bool = text
86
82
  self.text_length_limit: int = text_length_limit
87
83
 
88
- if highlights:
89
- import warnings
90
-
91
- warnings.warn(
92
- "The 'highlights' parameter is deprecated since it was removed in the Exa API. It will be removed from Agno in a future release.",
93
- DeprecationWarning,
94
- stacklevel=2,
95
- )
96
- if use_autoprompt:
97
- import warnings
98
-
99
- warnings.warn(
100
- "The 'use_autoprompt' parameter is deprecated since it was removed in the Exa API. It will be removed from Agno in a future release.",
101
- DeprecationWarning,
102
- stacklevel=2,
103
- )
104
-
105
84
  self.summary: bool = summary
106
85
  self.num_results: Optional[int] = num_results
107
86
  self.livecrawl: str = livecrawl
agno/tools/function.py CHANGED
@@ -133,10 +133,6 @@ class Function(BaseModel):
133
133
  _team: Optional[Any] = None
134
134
  # The run context that the function is associated with
135
135
  _run_context: Optional[RunContext] = None
136
- # The session state that the function is associated with
137
- _session_state: Optional[Dict[str, Any]] = None
138
- # The dependencies that the function is associated with
139
- _dependencies: Optional[Dict[str, Any]] = None
140
136
 
141
137
  # Media context that the function is associated with
142
138
  _images: Optional[Sequence[Image]] = None
@@ -150,6 +146,19 @@ class Function(BaseModel):
150
146
  include={"name", "description", "parameters", "strict", "requires_confirmation", "external_execution"},
151
147
  )
152
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
+
153
162
  def model_copy(self, *, deep: bool = False) -> "Function":
154
163
  """
155
164
  Override model_copy to handle callable fields that can't be deep copied (pickled).
@@ -209,10 +218,6 @@ class Function(BaseModel):
209
218
  del type_hints["team"]
210
219
  if "run_context" in sig.parameters and "run_context" in type_hints:
211
220
  del type_hints["run_context"]
212
- if "session_state" in sig.parameters and "session_state" in type_hints:
213
- del type_hints["session_state"]
214
- if "dependencies" in sig.parameters and "dependencies" in type_hints:
215
- del type_hints["dependencies"]
216
221
 
217
222
  # Remove media parameters from type hints as they are injected automatically
218
223
  if "images" in sig.parameters and "images" in type_hints:
@@ -235,8 +240,6 @@ class Function(BaseModel):
235
240
  "agent",
236
241
  "team",
237
242
  "run_context",
238
- "session_state",
239
- "dependencies",
240
243
  "self",
241
244
  "images",
242
245
  "videos",
@@ -276,8 +279,6 @@ class Function(BaseModel):
276
279
  "agent",
277
280
  "team",
278
281
  "run_context",
279
- "session_state",
280
- "dependencies",
281
282
  "self",
282
283
  "images",
283
284
  "videos",
@@ -296,8 +297,6 @@ class Function(BaseModel):
296
297
  "agent",
297
298
  "team",
298
299
  "run_context",
299
- "session_state",
300
- "dependencies",
301
300
  "self",
302
301
  "images",
303
302
  "videos",
@@ -354,10 +353,6 @@ class Function(BaseModel):
354
353
  del type_hints["team"]
355
354
  if "run_context" in sig.parameters and "run_context" in type_hints:
356
355
  del type_hints["run_context"]
357
- if "session_state" in sig.parameters and "session_state" in type_hints:
358
- del type_hints["session_state"]
359
- if "dependencies" in sig.parameters and "dependencies" in type_hints:
360
- del type_hints["dependencies"]
361
356
  if "images" in sig.parameters and "images" in type_hints:
362
357
  del type_hints["images"]
363
358
  if "videos" in sig.parameters and "videos" in type_hints:
@@ -374,8 +369,6 @@ class Function(BaseModel):
374
369
  "agent",
375
370
  "team",
376
371
  "run_context",
377
- "session_state",
378
- "dependencies",
379
372
  "self",
380
373
  "images",
381
374
  "videos",
@@ -488,10 +481,15 @@ class Function(BaseModel):
488
481
  # Don't wrap callables that are already wrapped with validate_call
489
482
  elif getattr(func, "_wrapped_for_validation", False):
490
483
  return func
491
- # Don't wrap functions with session_state parameter
492
- # session_state needs to be passed by reference, not copied by pydantic's validation
493
- 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()):
494
491
  return func
492
+
495
493
  # Wrap the callable with validate_call
496
494
  else:
497
495
  wrapped = validate_call(func, config=dict(arbitrary_types_allowed=True)) # type: ignore
@@ -546,8 +544,6 @@ class Function(BaseModel):
546
544
  "agent",
547
545
  "team",
548
546
  "run_context",
549
- "session_state",
550
- "dependencies",
551
547
  "images",
552
548
  "videos",
553
549
  "audios",
@@ -569,10 +565,6 @@ class Function(BaseModel):
569
565
  del copy_entrypoint_args["team"]
570
566
  if "run_context" in copy_entrypoint_args:
571
567
  del copy_entrypoint_args["run_context"]
572
- if "session_state" in copy_entrypoint_args:
573
- del copy_entrypoint_args["session_state"]
574
- if "dependencies" in copy_entrypoint_args:
575
- del copy_entrypoint_args["dependencies"]
576
568
  if "images" in copy_entrypoint_args:
577
569
  del copy_entrypoint_args["images"]
578
570
  if "videos" in copy_entrypoint_args:
@@ -699,21 +691,15 @@ class FunctionCall(BaseModel):
699
691
  from inspect import signature
700
692
 
701
693
  pre_hook_args = {}
702
- # Check if the pre-hook has and agent argument
694
+ # Check if the pre-hook has an agent argument
703
695
  if "agent" in signature(self.function.pre_hook).parameters:
704
696
  pre_hook_args["agent"] = self.function._agent
705
- # Check if the pre-hook has an team argument
697
+ # Check if the pre-hook has a team argument
706
698
  if "team" in signature(self.function.pre_hook).parameters:
707
699
  pre_hook_args["team"] = self.function._team
708
- # Check if the pre-hook has an session_state argument
700
+ # Check if the pre-hook has a run_context argument
709
701
  if "run_context" in signature(self.function.pre_hook).parameters:
710
702
  pre_hook_args["run_context"] = self.function._run_context
711
- # Check if the pre-hook has an session_state argument
712
- if "session_state" in signature(self.function.pre_hook).parameters:
713
- pre_hook_args["session_state"] = self.function._session_state
714
- # Check if the pre-hook has an dependencies argument
715
- if "dependencies" in signature(self.function.pre_hook).parameters:
716
- pre_hook_args["dependencies"] = self.function._dependencies
717
703
  # Check if the pre-hook has an fc argument
718
704
  if "fc" in signature(self.function.pre_hook).parameters:
719
705
  pre_hook_args["fc"] = self
@@ -733,21 +719,15 @@ class FunctionCall(BaseModel):
733
719
  from inspect import signature
734
720
 
735
721
  post_hook_args = {}
736
- # Check if the post-hook has and agent argument
722
+ # Check if the post-hook has an agent argument
737
723
  if "agent" in signature(self.function.post_hook).parameters:
738
724
  post_hook_args["agent"] = self.function._agent
739
- # Check if the post-hook has an team argument
725
+ # Check if the post-hook has a team argument
740
726
  if "team" in signature(self.function.post_hook).parameters:
741
727
  post_hook_args["team"] = self.function._team
742
- # Check if the post-hook has an session_state argument
728
+ # Check if the post-hook has a run_context argument
743
729
  if "run_context" in signature(self.function.post_hook).parameters:
744
730
  post_hook_args["run_context"] = self.function._run_context
745
- # Check if the post-hook has an session_state argument
746
- if "session_state" in signature(self.function.post_hook).parameters:
747
- post_hook_args["session_state"] = self.function._session_state
748
- # Check if the post-hook has an dependencies argument
749
- if "dependencies" in signature(self.function.post_hook).parameters:
750
- post_hook_args["dependencies"] = self.function._dependencies
751
731
  # Check if the post-hook has an fc argument
752
732
  if "fc" in signature(self.function.post_hook).parameters:
753
733
  post_hook_args["fc"] = self
@@ -768,18 +748,12 @@ class FunctionCall(BaseModel):
768
748
  # Check if the entrypoint has an agent argument
769
749
  if "agent" in signature(self.function.entrypoint).parameters: # type: ignore
770
750
  entrypoint_args["agent"] = self.function._agent
771
- # Check if the entrypoint has an team argument
751
+ # Check if the entrypoint has a team argument
772
752
  if "team" in signature(self.function.entrypoint).parameters: # type: ignore
773
753
  entrypoint_args["team"] = self.function._team
774
- # Check if the entrypoint has an run_context argument
754
+ # Check if the entrypoint has a run_context argument
775
755
  if "run_context" in signature(self.function.entrypoint).parameters: # type: ignore
776
756
  entrypoint_args["run_context"] = self.function._run_context
777
- # Check if the entrypoint has an session_state argument
778
- if "session_state" in signature(self.function.entrypoint).parameters: # type: ignore
779
- entrypoint_args["session_state"] = self.function._session_state
780
- # Check if the entrypoint has an dependencies argument
781
- if "dependencies" in signature(self.function.entrypoint).parameters: # type: ignore
782
- entrypoint_args["dependencies"] = self.function._dependencies
783
757
  # Check if the entrypoint has an fc argument
784
758
  if "fc" in signature(self.function.entrypoint).parameters: # type: ignore
785
759
  entrypoint_args["fc"] = self
@@ -803,18 +777,12 @@ class FunctionCall(BaseModel):
803
777
  # Check if the hook has an agent argument
804
778
  if "agent" in signature(hook).parameters:
805
779
  hook_args["agent"] = self.function._agent
806
- # Check if the hook has an team argument
780
+ # Check if the hook has a team argument
807
781
  if "team" in signature(hook).parameters:
808
782
  hook_args["team"] = self.function._team
809
- # Check if the hook has an run_context argument
783
+ # Check if the hook has a run_context argument
810
784
  if "run_context" in signature(hook).parameters:
811
785
  hook_args["run_context"] = self.function._run_context
812
- # Check if the hook has an session_state argument
813
- if "session_state" in signature(hook).parameters:
814
- hook_args["session_state"] = self.function._session_state
815
- # Check if the hook has an dependencies argument
816
- if "dependencies" in signature(hook).parameters:
817
- hook_args["dependencies"] = self.function._dependencies
818
786
  if "name" in signature(hook).parameters:
819
787
  hook_args["name"] = name
820
788
  if "function_name" in signature(hook).parameters:
@@ -942,9 +910,6 @@ class FunctionCall(BaseModel):
942
910
  if run_context is not None and run_context.session_state is not None
943
911
  else None
944
912
  )
945
- else:
946
- if self.function._session_state is not None:
947
- updated_session_state = self.function._session_state
948
913
 
949
914
  execution_result = FunctionExecutionResult(
950
915
  status="success", result=self.result, updated_session_state=updated_session_state
@@ -979,18 +944,12 @@ class FunctionCall(BaseModel):
979
944
  # Check if the pre-hook has an agent argument
980
945
  if "agent" in signature(self.function.pre_hook).parameters:
981
946
  pre_hook_args["agent"] = self.function._agent
982
- # Check if the pre-hook has an team argument
947
+ # Check if the pre-hook has a team argument
983
948
  if "team" in signature(self.function.pre_hook).parameters:
984
949
  pre_hook_args["team"] = self.function._team
985
- # Check if the pre-hook has an run_context argument
950
+ # Check if the pre-hook has a run_context argument
986
951
  if "run_context" in signature(self.function.pre_hook).parameters:
987
952
  pre_hook_args["run_context"] = self.function._run_context
988
- # Check if the pre-hook has an session_state argument
989
- if "session_state" in signature(self.function.pre_hook).parameters:
990
- pre_hook_args["session_state"] = self.function._session_state
991
- # Check if the pre-hook has an dependencies argument
992
- if "dependencies" in signature(self.function.pre_hook).parameters:
993
- pre_hook_args["dependencies"] = self.function._dependencies
994
953
  # Check if the pre-hook has an fc argument
995
954
  if "fc" in signature(self.function.pre_hook).parameters:
996
955
  pre_hook_args["fc"] = self
@@ -1014,19 +973,12 @@ class FunctionCall(BaseModel):
1014
973
  # Check if the post-hook has an agent argument
1015
974
  if "agent" in signature(self.function.post_hook).parameters:
1016
975
  post_hook_args["agent"] = self.function._agent
1017
- # Check if the post-hook has an team argument
976
+ # Check if the post-hook has a team argument
1018
977
  if "team" in signature(self.function.post_hook).parameters:
1019
978
  post_hook_args["team"] = self.function._team
1020
- # Check if the post-hook has an run_context argument
979
+ # Check if the post-hook has a run_context argument
1021
980
  if "run_context" in signature(self.function.post_hook).parameters:
1022
981
  post_hook_args["run_context"] = self.function._run_context
1023
- # Check if the post-hook has an session_state argument
1024
- if "session_state" in signature(self.function.post_hook).parameters:
1025
- post_hook_args["session_state"] = self.function._session_state
1026
- # Check if the post-hook has an dependencies argument
1027
- if "dependencies" in signature(self.function.post_hook).parameters:
1028
- post_hook_args["dependencies"] = self.function._dependencies
1029
-
1030
982
  # Check if the post-hook has an fc argument
1031
983
  if "fc" in signature(self.function.post_hook).parameters:
1032
984
  post_hook_args["fc"] = self
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)
agno/tools/mem0.py CHANGED
@@ -2,6 +2,7 @@ import json
2
2
  from os import getenv
3
3
  from typing import Any, Dict, List, Optional, Union
4
4
 
5
+ from agno.run import RunContext
5
6
  from agno.tools import Toolkit
6
7
  from agno.utils.log import log_debug, log_error, log_warning
7
8
 
@@ -68,13 +69,13 @@ class Mem0Tools(Toolkit):
68
69
  def _get_user_id(
69
70
  self,
70
71
  method_name: str,
71
- session_state: Dict[str, Any],
72
+ run_context: RunContext,
72
73
  ) -> str:
73
74
  """Resolve the user ID"""
74
75
  resolved_user_id = self.user_id
75
76
  if not resolved_user_id:
76
77
  try:
77
- resolved_user_id = session_state.get("current_user_id")
78
+ resolved_user_id = run_context.user_id
78
79
  except Exception:
79
80
  pass
80
81
  if not resolved_user_id:
@@ -85,7 +86,7 @@ class Mem0Tools(Toolkit):
85
86
 
86
87
  def add_memory(
87
88
  self,
88
- session_state,
89
+ run_context: RunContext,
89
90
  content: Union[str, Dict[str, str]],
90
91
  ) -> str:
91
92
  """Add facts to the user's memory.
@@ -98,7 +99,7 @@ class Mem0Tools(Toolkit):
98
99
  str: JSON-encoded Mem0 response or an error message.
99
100
  """
100
101
 
101
- resolved_user_id = self._get_user_id("add_memory", session_state=session_state)
102
+ resolved_user_id = self._get_user_id("add_memory", run_context=run_context)
102
103
  if isinstance(resolved_user_id, str) and resolved_user_id.startswith("Error in add_memory:"):
103
104
  return resolved_user_id
104
105
  try:
@@ -121,12 +122,12 @@ class Mem0Tools(Toolkit):
121
122
 
122
123
  def search_memory(
123
124
  self,
124
- session_state: Dict[str, Any],
125
+ run_context: RunContext,
125
126
  query: str,
126
127
  ) -> str:
127
128
  """Semantic search for *query* across the user's stored memories."""
128
129
 
129
- resolved_user_id = self._get_user_id("search_memory", session_state=session_state)
130
+ resolved_user_id = self._get_user_id("search_memory", run_context=run_context)
130
131
  if isinstance(resolved_user_id, str) and resolved_user_id.startswith("Error in search_memory:"):
131
132
  return resolved_user_id
132
133
  try:
@@ -151,10 +152,10 @@ class Mem0Tools(Toolkit):
151
152
  log_error(f"Error searching memory: {e}")
152
153
  return f"Error searching memory: {e}"
153
154
 
154
- def get_all_memories(self, session_state: Dict[str, Any]) -> str:
155
+ def get_all_memories(self, run_context: RunContext) -> str:
155
156
  """Return **all** memories for the current user as a JSON string."""
156
157
 
157
- resolved_user_id = self._get_user_id("get_all_memories", session_state=session_state)
158
+ resolved_user_id = self._get_user_id("get_all_memories", run_context=run_context)
158
159
  if isinstance(resolved_user_id, str) and resolved_user_id.startswith("Error in get_all_memories:"):
159
160
  return resolved_user_id
160
161
  try:
@@ -177,10 +178,10 @@ class Mem0Tools(Toolkit):
177
178
  log_error(f"Error getting all memories: {e}")
178
179
  return f"Error getting all memories: {e}"
179
180
 
180
- def delete_all_memories(self, session_state: Dict[str, Any]) -> str:
181
+ def delete_all_memories(self, run_context: RunContext) -> str:
181
182
  """Delete *all* memories associated with the current user"""
182
183
 
183
- resolved_user_id = self._get_user_id("delete_all_memories", session_state=session_state)
184
+ resolved_user_id = self._get_user_id("delete_all_memories", run_context=run_context)
184
185
  if isinstance(resolved_user_id, str) and resolved_user_id.startswith("Error in delete_all_memories:"):
185
186
  error_msg = resolved_user_id
186
187
  log_error(error_msg)