agno 2.3.22__py3-none-any.whl → 2.3.24__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 (62) hide show
  1. agno/agent/agent.py +28 -1
  2. agno/agent/remote.py +1 -1
  3. agno/db/mongo/mongo.py +9 -1
  4. agno/db/mysql/async_mysql.py +5 -7
  5. agno/db/mysql/mysql.py +5 -7
  6. agno/db/mysql/schemas.py +39 -21
  7. agno/db/postgres/async_postgres.py +10 -2
  8. agno/db/postgres/postgres.py +5 -7
  9. agno/db/postgres/schemas.py +39 -21
  10. agno/db/singlestore/schemas.py +41 -21
  11. agno/db/singlestore/singlestore.py +14 -3
  12. agno/db/sqlite/async_sqlite.py +7 -2
  13. agno/db/sqlite/schemas.py +36 -21
  14. agno/db/sqlite/sqlite.py +3 -7
  15. agno/knowledge/chunking/markdown.py +94 -8
  16. agno/knowledge/chunking/semantic.py +2 -2
  17. agno/knowledge/knowledge.py +215 -207
  18. agno/models/base.py +32 -8
  19. agno/models/google/gemini.py +27 -4
  20. agno/os/routers/agents/router.py +1 -1
  21. agno/os/routers/evals/evals.py +2 -2
  22. agno/os/routers/knowledge/knowledge.py +21 -5
  23. agno/os/routers/knowledge/schemas.py +1 -1
  24. agno/os/routers/memory/memory.py +4 -4
  25. agno/os/routers/session/session.py +2 -2
  26. agno/os/routers/teams/router.py +2 -2
  27. agno/os/routers/traces/traces.py +3 -3
  28. agno/os/routers/workflows/router.py +1 -1
  29. agno/os/schema.py +1 -1
  30. agno/os/utils.py +1 -1
  31. agno/remote/base.py +1 -1
  32. agno/team/remote.py +1 -1
  33. agno/team/team.py +24 -4
  34. agno/tools/brandfetch.py +27 -18
  35. agno/tools/browserbase.py +150 -13
  36. agno/tools/crawl4ai.py +3 -0
  37. agno/tools/file.py +14 -13
  38. agno/tools/function.py +15 -2
  39. agno/tools/mcp/mcp.py +1 -0
  40. agno/tools/mlx_transcribe.py +10 -7
  41. agno/tools/python.py +14 -6
  42. agno/tools/toolkit.py +122 -23
  43. agno/vectordb/cassandra/cassandra.py +1 -1
  44. agno/vectordb/chroma/chromadb.py +1 -1
  45. agno/vectordb/clickhouse/clickhousedb.py +1 -1
  46. agno/vectordb/couchbase/couchbase.py +1 -1
  47. agno/vectordb/milvus/milvus.py +1 -1
  48. agno/vectordb/mongodb/mongodb.py +13 -3
  49. agno/vectordb/pgvector/pgvector.py +1 -1
  50. agno/vectordb/pineconedb/pineconedb.py +2 -2
  51. agno/vectordb/qdrant/qdrant.py +1 -1
  52. agno/vectordb/redis/redisdb.py +2 -2
  53. agno/vectordb/singlestore/singlestore.py +1 -1
  54. agno/vectordb/surrealdb/surrealdb.py +2 -2
  55. agno/vectordb/weaviate/weaviate.py +1 -1
  56. agno/workflow/remote.py +1 -1
  57. agno/workflow/workflow.py +14 -0
  58. {agno-2.3.22.dist-info → agno-2.3.24.dist-info}/METADATA +1 -1
  59. {agno-2.3.22.dist-info → agno-2.3.24.dist-info}/RECORD +62 -62
  60. {agno-2.3.22.dist-info → agno-2.3.24.dist-info}/WHEEL +0 -0
  61. {agno-2.3.22.dist-info → agno-2.3.24.dist-info}/licenses/LICENSE +0 -0
  62. {agno-2.3.22.dist-info → agno-2.3.24.dist-info}/top_level.txt +0 -0
agno/models/base.py CHANGED
@@ -1016,6 +1016,8 @@ class Model(ABC):
1016
1016
  model_response.extra.update(provider_response.extra)
1017
1017
  if provider_response.provider_data is not None:
1018
1018
  model_response.provider_data = provider_response.provider_data
1019
+ if provider_response.response_usage is not None:
1020
+ model_response.response_usage = provider_response.response_usage
1019
1021
 
1020
1022
  async def _aprocess_model_response(
1021
1023
  self,
@@ -1073,6 +1075,8 @@ class Model(ABC):
1073
1075
  model_response.extra.update(provider_response.extra)
1074
1076
  if provider_response.provider_data is not None:
1075
1077
  model_response.provider_data = provider_response.provider_data
1078
+ if provider_response.response_usage is not None:
1079
+ model_response.response_usage = provider_response.response_usage
1076
1080
 
1077
1081
  def _populate_assistant_message(
1078
1082
  self,
@@ -2026,10 +2030,20 @@ class Model(ABC):
2026
2030
  user_input_schema = []
2027
2031
  for input_field in fc.arguments.get("user_input_fields", []):
2028
2032
  field_type = input_field.get("field_type")
2029
- try:
2030
- python_type = eval(field_type) if isinstance(field_type, str) else field_type
2031
- except (NameError, SyntaxError):
2032
- python_type = str # Default to str if type is invalid
2033
+ if isinstance(field_type, str):
2034
+ type_mapping = {
2035
+ "str": str,
2036
+ "int": int,
2037
+ "float": float,
2038
+ "bool": bool,
2039
+ "list": list,
2040
+ "dict": dict,
2041
+ }
2042
+ python_type = type_mapping.get(field_type, str)
2043
+ elif isinstance(field_type, type):
2044
+ python_type = field_type
2045
+ else:
2046
+ python_type = str
2033
2047
  user_input_schema.append(
2034
2048
  UserInputField(
2035
2049
  name=input_field.get("field_name"),
@@ -2181,10 +2195,20 @@ class Model(ABC):
2181
2195
  user_input_schema = []
2182
2196
  for input_field in fc.arguments.get("user_input_fields", []):
2183
2197
  field_type = input_field.get("field_type")
2184
- try:
2185
- python_type = eval(field_type) if isinstance(field_type, str) else field_type
2186
- except (NameError, SyntaxError):
2187
- python_type = str # Default to str if type is invalid
2198
+ if isinstance(field_type, str):
2199
+ type_mapping = {
2200
+ "str": str,
2201
+ "int": int,
2202
+ "float": float,
2203
+ "bool": bool,
2204
+ "list": list,
2205
+ "dict": dict,
2206
+ }
2207
+ python_type = type_mapping.get(field_type, str)
2208
+ elif isinstance(field_type, type):
2209
+ python_type = field_type
2210
+ else:
2211
+ python_type = str
2188
2212
  user_input_schema.append(
2189
2213
  UserInputField(
2190
2214
  name=input_field.get("field_name"),
@@ -466,7 +466,12 @@ class Gemini(Model):
466
466
 
467
467
  except (ClientError, ServerError) as e:
468
468
  log_error(f"Error from Gemini API: {e}")
469
- error_message = str(e.response) if hasattr(e, "response") else str(e)
469
+ error_message = str(e)
470
+ if hasattr(e, "response"):
471
+ if hasattr(e.response, "text"):
472
+ error_message = e.response.text
473
+ else:
474
+ error_message = str(e.response)
470
475
  raise ModelProviderError(
471
476
  message=error_message,
472
477
  status_code=e.code if hasattr(e, "code") and e.code is not None else 502,
@@ -518,8 +523,14 @@ class Gemini(Model):
518
523
 
519
524
  except (ClientError, ServerError) as e:
520
525
  log_error(f"Error from Gemini API: {e}")
526
+ error_message = str(e)
527
+ if hasattr(e, "response"):
528
+ if hasattr(e.response, "text"):
529
+ error_message = e.response.text
530
+ else:
531
+ error_message = str(e.response)
521
532
  raise ModelProviderError(
522
- message=str(e.response) if hasattr(e, "response") else str(e),
533
+ message=error_message,
523
534
  status_code=e.code if hasattr(e, "code") and e.code is not None else 502,
524
535
  model_name=self.name,
525
536
  model_id=self.id,
@@ -574,8 +585,14 @@ class Gemini(Model):
574
585
 
575
586
  except (ClientError, ServerError) as e:
576
587
  log_error(f"Error from Gemini API: {e}")
588
+ error_message = str(e)
589
+ if hasattr(e, "response"):
590
+ if hasattr(e.response, "text"):
591
+ error_message = e.response.text
592
+ else:
593
+ error_message = str(e.response)
577
594
  raise ModelProviderError(
578
- message=str(e.response) if hasattr(e, "response") else str(e),
595
+ message=error_message,
579
596
  status_code=e.code if hasattr(e, "code") and e.code is not None else 502,
580
597
  model_name=self.name,
581
598
  model_id=self.id,
@@ -628,8 +645,14 @@ class Gemini(Model):
628
645
 
629
646
  except (ClientError, ServerError) as e:
630
647
  log_error(f"Error from Gemini API: {e}")
648
+ error_message = str(e)
649
+ if hasattr(e, "response"):
650
+ if hasattr(e.response, "text"):
651
+ error_message = e.response.text
652
+ else:
653
+ error_message = str(e.response)
631
654
  raise ModelProviderError(
632
- message=str(e.response) if hasattr(e, "response") else str(e),
655
+ message=error_message,
633
656
  status_code=e.code if hasattr(e, "code") and e.code is not None else 502,
634
657
  model_name=self.name,
635
658
  model_id=self.id,
@@ -409,7 +409,7 @@ def get_agent_router(
409
409
  if agent is None:
410
410
  raise HTTPException(status_code=404, detail="Agent not found")
411
411
 
412
- cancelled = agent.cancel_run(run_id=run_id)
412
+ cancelled = await agent.acancel_run(run_id=run_id)
413
413
  if not cancelled:
414
414
  raise HTTPException(status_code=500, detail="Failed to cancel run - run not found or already completed")
415
415
 
@@ -118,8 +118,8 @@ def attach_routes(
118
118
  model_id: Optional[str] = Query(default=None, description="Model ID"),
119
119
  filter_type: Optional[EvalFilterType] = Query(default=None, description="Filter type", alias="type"),
120
120
  eval_types: Optional[List[EvalType]] = Depends(parse_eval_types_filter),
121
- limit: Optional[int] = Query(default=20, description="Number of eval runs to return"),
122
- page: Optional[int] = Query(default=1, description="Page number"),
121
+ limit: Optional[int] = Query(default=20, description="Number of eval runs to return", ge=1),
122
+ page: Optional[int] = Query(default=1, description="Page number", ge=0),
123
123
  sort_by: Optional[str] = Query(default="created_at", description="Field to sort by"),
124
124
  sort_order: Optional[SortOrder] = Query(default="desc", description="Sort order (asc or desc)"),
125
125
  db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
@@ -5,6 +5,7 @@ from typing import Any, Dict, List, Optional, Union
5
5
 
6
6
  from fastapi import APIRouter, BackgroundTasks, Depends, File, Form, HTTPException, Path, Query, Request, UploadFile
7
7
 
8
+ from agno.db.base import AsyncBaseDb
8
9
  from agno.knowledge.content import Content, FileData
9
10
  from agno.knowledge.knowledge import Knowledge
10
11
  from agno.knowledge.reader import ReaderFactory
@@ -36,7 +37,7 @@ from agno.os.schema import (
36
37
  from agno.os.settings import AgnoAPISettings
37
38
  from agno.os.utils import get_knowledge_instance_by_db_id
38
39
  from agno.remote.base import RemoteKnowledge
39
- from agno.utils.log import log_debug, log_info
40
+ from agno.utils.log import log_debug, log_error, log_info
40
41
  from agno.utils.string import generate_id
41
42
 
42
43
  logger = logging.getLogger(__name__)
@@ -297,7 +298,17 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Union[Knowledge,
297
298
  else:
298
299
  raise HTTPException(status_code=400, detail=f"Invalid reader_id: {update_data.reader_id}")
299
300
 
300
- updated_content_dict = await knowledge.apatch_content(content)
301
+ # Use async patch method if contents_db is an AsyncBaseDb, otherwise use sync patch method
302
+ updated_content_dict = None
303
+ try:
304
+ if knowledge.contents_db is not None and isinstance(knowledge.contents_db, AsyncBaseDb):
305
+ updated_content_dict = await knowledge.apatch_content(content)
306
+ else:
307
+ updated_content_dict = knowledge.patch_content(content)
308
+ except Exception as e:
309
+ log_error(f"Error updating content: {str(e)}")
310
+ raise HTTPException(status_code=500, detail=f"Error updating content: {str(e)}")
311
+
301
312
  if not updated_content_dict:
302
313
  raise HTTPException(status_code=404, detail=f"Content not found: {content_id}")
303
314
 
@@ -344,8 +355,8 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Union[Knowledge,
344
355
  )
345
356
  async def get_content(
346
357
  request: Request,
347
- limit: Optional[int] = Query(default=20, description="Number of content entries to return"),
348
- page: Optional[int] = Query(default=1, description="Page number"),
358
+ limit: Optional[int] = Query(default=20, description="Number of content entries to return", ge=1),
359
+ page: Optional[int] = Query(default=1, description="Page number", ge=0),
349
360
  sort_by: Optional[str] = Query(default="created_at", description="Field to sort by"),
350
361
  sort_order: Optional[SortOrder] = Query(default="desc", description="Sort order (asc or desc)"),
351
362
  db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
@@ -1090,7 +1101,12 @@ async def process_content(
1090
1101
 
1091
1102
  content.status = KnowledgeContentStatus.FAILED
1092
1103
  content.status_message = str(e)
1093
- knowledge.patch_content(content)
1104
+ # Use async patch method if contents_db is an AsyncBaseDb, otherwise use sync patch method
1105
+ if knowledge.contents_db is not None and isinstance(knowledge.contents_db, AsyncBaseDb):
1106
+ await knowledge.apatch_content(content)
1107
+ else:
1108
+ knowledge.patch_content(content)
1109
+
1094
1110
  except Exception:
1095
1111
  # Swallow any secondary errors to avoid crashing the background task
1096
1112
  pass
@@ -156,7 +156,7 @@ class VectorSearchRequestSchema(BaseModel):
156
156
  class Meta(BaseModel):
157
157
  """Inline metadata schema for pagination."""
158
158
 
159
- limit: int = Field(20, description="Number of results per page", ge=1, le=100)
159
+ limit: int = Field(20, description="Number of results per page", ge=1)
160
160
  page: int = Field(1, description="Page number", ge=1)
161
161
 
162
162
  query: str = Field(..., description="The search query text")
@@ -256,8 +256,8 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
256
256
  team_id: Optional[str] = Query(default=None, description="Filter memories by team ID"),
257
257
  topics: Optional[List[str]] = Depends(parse_topics),
258
258
  search_content: Optional[str] = Query(default=None, description="Fuzzy search within memory content"),
259
- limit: Optional[int] = Query(default=20, description="Number of memories to return per page"),
260
- page: Optional[int] = Query(default=1, description="Page number for pagination"),
259
+ limit: Optional[int] = Query(default=20, description="Number of memories to return per page", ge=1),
260
+ page: Optional[int] = Query(default=1, description="Page number for pagination", ge=0),
261
261
  sort_by: Optional[str] = Query(default="updated_at", description="Field to sort memories by"),
262
262
  sort_order: Optional[SortOrder] = Query(default="desc", description="Sort order (asc or desc)"),
263
263
  db_id: Optional[str] = Query(default=None, description="Database ID to query memories from"),
@@ -558,8 +558,8 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
558
558
  )
559
559
  async def get_user_memory_stats(
560
560
  request: Request,
561
- limit: Optional[int] = Query(default=20, description="Number of user statistics to return per page"),
562
- page: Optional[int] = Query(default=1, description="Page number for pagination"),
561
+ limit: Optional[int] = Query(default=20, description="Number of user statistics to return per page", ge=1),
562
+ page: Optional[int] = Query(default=1, description="Page number for pagination", ge=0),
563
563
  db_id: Optional[str] = Query(default=None, description="Database ID to query statistics from"),
564
564
  table: Optional[str] = Query(default=None, description="Table to query statistics from"),
565
565
  ) -> PaginatedResponse[UserStatsSchema]:
@@ -107,8 +107,8 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
107
107
  ),
108
108
  user_id: Optional[str] = Query(default=None, description="Filter sessions by user ID"),
109
109
  session_name: Optional[str] = Query(default=None, description="Filter sessions by name (partial match)"),
110
- limit: Optional[int] = Query(default=20, description="Number of sessions to return per page"),
111
- page: Optional[int] = Query(default=1, description="Page number for pagination"),
110
+ limit: Optional[int] = Query(default=20, description="Number of sessions to return per page", ge=1),
111
+ page: Optional[int] = Query(default=1, description="Page number for pagination", ge=0),
112
112
  sort_by: Optional[str] = Query(default="created_at", description="Field to sort sessions by"),
113
113
  sort_order: Optional[SortOrder] = Query(default="desc", description="Sort order (asc or desc)"),
114
114
  db_id: Optional[str] = Query(default=None, description="Database ID to query sessions from"),
@@ -95,7 +95,7 @@ async def team_response_streamer(
95
95
  )
96
96
  yield format_sse_event(error_response)
97
97
 
98
- except Exception as e:
98
+ except BaseException as e:
99
99
  import traceback
100
100
 
101
101
  traceback.print_exc()
@@ -325,7 +325,7 @@ def get_team_router(
325
325
  if team is None:
326
326
  raise HTTPException(status_code=404, detail="Team not found")
327
327
 
328
- cancelled = team.cancel_run(run_id=run_id)
328
+ cancelled = await team.acancel_run(run_id=run_id)
329
329
  if not cancelled:
330
330
  raise HTTPException(status_code=500, detail="Failed to cancel run - run not found or already completed")
331
331
 
@@ -126,8 +126,8 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
126
126
  default=None,
127
127
  description="Filter traces ending before this time (ISO 8601 format with timezone, e.g., '2025-11-19T11:00:00Z' or '2025-11-19T16:30:00+05:30'). Times are converted to UTC for comparison.",
128
128
  ),
129
- page: int = Query(default=1, description="Page number (1-indexed)", ge=1),
130
- limit: int = Query(default=20, description="Number of traces per page", ge=1, le=100),
129
+ page: int = Query(default=1, description="Page number (1-indexed)", ge=0),
130
+ limit: int = Query(default=20, description="Number of traces per page", ge=1),
131
131
  db_id: Optional[str] = Query(default=None, description="Database ID to query traces from"),
132
132
  ):
133
133
  """Get list of traces with optional filters and pagination"""
@@ -455,7 +455,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
455
455
  description="Filter sessions with traces created before this time (ISO 8601 format with timezone, e.g., '2025-11-19T11:00:00Z' or '2025-11-19T16:30:00+05:30'). Times are converted to UTC for comparison.",
456
456
  ),
457
457
  page: int = Query(default=1, description="Page number (1-indexed)", ge=1),
458
- limit: int = Query(default=20, description="Number of sessions per page", ge=1, le=100),
458
+ limit: int = Query(default=20, description="Number of sessions per page", ge=1),
459
459
  db_id: Optional[str] = Query(default=None, description="Database ID to query statistics from"),
460
460
  ):
461
461
  """Get trace statistics grouped by session"""
@@ -721,7 +721,7 @@ def get_workflow_router(
721
721
  if workflow is None:
722
722
  raise HTTPException(status_code=404, detail="Workflow not found")
723
723
 
724
- cancelled = workflow.cancel_run(run_id=run_id)
724
+ cancelled = await workflow.acancel_run(run_id=run_id)
725
725
  if not cancelled:
726
726
  raise HTTPException(status_code=500, detail="Failed to cancel run - run not found or already completed")
727
727
 
agno/os/schema.py CHANGED
@@ -549,7 +549,7 @@ class SortOrder(str, Enum):
549
549
 
550
550
  class PaginationInfo(BaseModel):
551
551
  page: int = Field(0, description="Current page number (0-indexed)", ge=0)
552
- limit: int = Field(20, description="Number of items per page", ge=1, le=100)
552
+ limit: int = Field(20, description="Number of items per page", ge=1)
553
553
  total_pages: int = Field(0, description="Total number of pages", ge=0)
554
554
  total_count: int = Field(0, description="Total count of items", ge=0)
555
555
  search_time_ms: float = Field(0, description="Search execution time in milliseconds", ge=0)
agno/os/utils.py CHANGED
@@ -343,7 +343,7 @@ def get_session_name(session: Dict[str, Any]) -> str:
343
343
 
344
344
  run_input = r.get("input")
345
345
  if run_input is not None:
346
- return run_input.get("input_content")
346
+ return stringify_input_content(run_input)
347
347
 
348
348
  return ""
349
349
 
agno/remote/base.py CHANGED
@@ -577,5 +577,5 @@ class BaseRemote:
577
577
  raise NotImplementedError("acontinue_run method must be implemented by the subclass")
578
578
 
579
579
  @abstractmethod
580
- async def cancel_run(self, run_id: str) -> bool:
580
+ async def acancel_run(self, run_id: str) -> bool:
581
581
  raise NotImplementedError("cancel_run method must be implemented by the subclass")
agno/team/remote.py CHANGED
@@ -425,7 +425,7 @@ class RemoteTeam(BaseRemote):
425
425
  )
426
426
  return map_task_result_to_team_run_output(task_result, team_id=self.team_id, user_id=user_id)
427
427
 
428
- async def cancel_run(self, run_id: str, auth_token: Optional[str] = None) -> bool:
428
+ async def acancel_run(self, run_id: str, auth_token: Optional[str] = None) -> bool:
429
429
  """Cancel a running team execution.
430
430
 
431
431
  Args:
agno/team/team.py CHANGED
@@ -56,6 +56,9 @@ from agno.models.utils import get_model
56
56
  from agno.reasoning.step import NextAction, ReasoningStep, ReasoningSteps
57
57
  from agno.run import RunContext, RunStatus
58
58
  from agno.run.agent import RunEvent, RunOutput, RunOutputEvent
59
+ from agno.run.cancel import (
60
+ acancel_run as acancel_run_global,
61
+ )
59
62
  from agno.run.cancel import (
60
63
  acleanup_run,
61
64
  araise_if_cancelled,
@@ -1011,6 +1014,18 @@ class Team:
1011
1014
  """
1012
1015
  return cancel_run_global(run_id)
1013
1016
 
1017
+ @staticmethod
1018
+ async def acancel_run(run_id: str) -> bool:
1019
+ """Cancel a running team execution.
1020
+
1021
+ Args:
1022
+ run_id (str): The run_id to cancel.
1023
+
1024
+ Returns:
1025
+ bool: True if the run was found and marked for cancellation, False otherwise.
1026
+ """
1027
+ return await acancel_run_global(run_id)
1028
+
1014
1029
  async def _connect_mcp_tools(self) -> None:
1015
1030
  """Connect the MCP tools to the agent."""
1016
1031
  if self.tools is not None:
@@ -1554,6 +1569,7 @@ class Team:
1554
1569
  tools=_tools,
1555
1570
  tool_choice=self.tool_choice,
1556
1571
  tool_call_limit=self.tool_call_limit,
1572
+ run_response=run_response,
1557
1573
  send_media_to_model=self.send_media_to_model,
1558
1574
  compression_manager=self.compression_manager if self.compress_tool_results else None,
1559
1575
  )
@@ -2652,6 +2668,8 @@ class Team:
2652
2668
  """
2653
2669
  log_debug(f"Team Run Start: {run_response.run_id}", center=True)
2654
2670
 
2671
+ await aregister_run(run_context.run_id)
2672
+
2655
2673
  memory_task = None
2656
2674
 
2657
2675
  try:
@@ -3375,6 +3393,7 @@ class Team:
3375
3393
  tool_choice=self.tool_choice,
3376
3394
  tool_call_limit=self.tool_call_limit,
3377
3395
  stream_model_response=stream_model_response,
3396
+ run_response=run_response,
3378
3397
  send_media_to_model=self.send_media_to_model,
3379
3398
  compression_manager=self.compression_manager if self.compress_tool_results else None,
3380
3399
  ):
@@ -5367,7 +5386,8 @@ class Team:
5367
5386
 
5368
5387
  elif isinstance(tool, Toolkit):
5369
5388
  # For each function in the toolkit and process entrypoint
5370
- for name, _func in tool.functions.items():
5389
+ toolkit_functions = tool.get_async_functions() if async_mode else tool.get_functions()
5390
+ for name, _func in toolkit_functions.items():
5371
5391
  if name in _function_names:
5372
5392
  continue
5373
5393
  _function_names.append(name)
@@ -8429,7 +8449,7 @@ class Team:
8429
8449
  gen_session_name_prompt = "Team Conversation\n"
8430
8450
 
8431
8451
  # Get team session messages for generating the name
8432
- messages_for_generating_session_name = self.get_session_messages()
8452
+ messages_for_generating_session_name = session.get_messages()
8433
8453
 
8434
8454
  for message in messages_for_generating_session_name:
8435
8455
  gen_session_name_prompt += f"{message.role.upper()}: {message.content}\n"
@@ -8979,7 +8999,7 @@ class Team:
8979
8999
  log_warning("No valid filters remain after validation. Search will proceed without filters.")
8980
9000
 
8981
9001
  if invalid_keys == [] and valid_filters == {}:
8982
- log_warning("No valid filters provided. Search will proceed without filters.")
9002
+ log_debug("No valid filters provided. Search will proceed without filters.")
8983
9003
  filters = None
8984
9004
 
8985
9005
  if self.knowledge_retriever is not None and callable(self.knowledge_retriever):
@@ -9055,7 +9075,7 @@ class Team:
9055
9075
  log_warning("No valid filters remain after validation. Search will proceed without filters.")
9056
9076
 
9057
9077
  if invalid_keys == [] and valid_filters == {}:
9058
- log_warning("No valid filters provided. Search will proceed without filters.")
9078
+ log_debug("No valid filters provided. Search will proceed without filters.")
9059
9079
  filters = None
9060
9080
 
9061
9081
  if self.knowledge_retriever is not None and callable(self.knowledge_retriever):
agno/tools/brandfetch.py CHANGED
@@ -1,9 +1,10 @@
1
1
  """
2
- Going to contribute this to agno toolkits.
2
+ Brandfetch API toolkit for retrieving brand data and searching brands.
3
3
  """
4
4
 
5
+ import warnings
5
6
  from os import getenv
6
- from typing import Any, Optional
7
+ from typing import Any, List, Optional
7
8
 
8
9
  try:
9
10
  import httpx
@@ -31,8 +32,6 @@ class BrandfetchTools(Toolkit):
31
32
  all: bool - if True, will use all tools
32
33
  enable_search_by_identifier: bool - if True, will use search by identifier
33
34
  enable_search_by_brand: bool - if True, will use search by brand
34
- enable_asearch_by_identifier: bool - if True, will use async search by identifier
35
- enable_asearch_by_brand: bool - if True, will use async search by brand
36
35
  """
37
36
 
38
37
  def __init__(
@@ -44,9 +43,18 @@ class BrandfetchTools(Toolkit):
44
43
  enable_search_by_identifier: bool = True,
45
44
  enable_search_by_brand: bool = False,
46
45
  all: bool = False,
47
- async_tools: bool = False,
46
+ async_tools: bool = False, # Deprecated
48
47
  **kwargs,
49
48
  ):
49
+ # Handle deprecated async_tools parameter
50
+ if async_tools:
51
+ warnings.warn(
52
+ "The 'async_tools' parameter is deprecated and will be removed in a future version. "
53
+ "Async tools are now automatically used when calling agent.arun() or agent.aprint_response().",
54
+ DeprecationWarning,
55
+ stacklevel=2,
56
+ )
57
+
50
58
  self.api_key = api_key or getenv("BRANDFETCH_API_KEY")
51
59
  self.client_id = client_id or getenv("BRANDFETCH_CLIENT_ID")
52
60
  self.base_url = base_url
@@ -54,20 +62,21 @@ class BrandfetchTools(Toolkit):
54
62
  self.search_url = f"{self.base_url}/search"
55
63
  self.brand_url = f"{self.base_url}/brands"
56
64
 
57
- tools: list[Any] = []
58
- # Backward-compat mapping: prefer new enable_* flags, but honor legacy toggles
59
- if async_tools:
60
- if all or enable_search_by_identifier:
61
- tools.append(self.asearch_by_identifier)
62
- if all or enable_search_by_brand:
63
- tools.append(self.asearch_by_brand)
64
- else:
65
- if all or enable_search_by_identifier:
66
- tools.append(self.search_by_identifier)
67
- if all or enable_search_by_brand:
68
- tools.append(self.search_by_brand)
65
+ # Build tools lists
66
+ # sync tools: used by agent.run() and agent.print_response()
67
+ # async tools: used by agent.arun() and agent.aprint_response()
68
+ tools: List[Any] = []
69
+ async_tools_list: List[tuple] = []
70
+
71
+ if all or enable_search_by_identifier:
72
+ tools.append(self.search_by_identifier)
73
+ async_tools_list.append((self.asearch_by_identifier, "search_by_identifier"))
74
+ if all or enable_search_by_brand:
75
+ tools.append(self.search_by_brand)
76
+ async_tools_list.append((self.asearch_by_brand, "search_by_brand"))
77
+
69
78
  name = kwargs.pop("name", "brandfetch_tools")
70
- super().__init__(name=name, tools=tools, **kwargs)
79
+ super().__init__(name=name, tools=tools, async_tools=async_tools_list, **kwargs)
71
80
 
72
81
  async def asearch_by_identifier(self, identifier: str) -> dict[str, Any]:
73
82
  """