agno 2.0.3__py3-none-any.whl → 2.0.5__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 (58) hide show
  1. agno/agent/agent.py +229 -164
  2. agno/db/dynamo/dynamo.py +8 -0
  3. agno/db/firestore/firestore.py +8 -0
  4. agno/db/gcs_json/gcs_json_db.py +9 -0
  5. agno/db/json/json_db.py +8 -0
  6. agno/db/migrations/v1_to_v2.py +191 -23
  7. agno/db/mongo/mongo.py +68 -0
  8. agno/db/mysql/mysql.py +13 -3
  9. agno/db/mysql/schemas.py +27 -27
  10. agno/db/postgres/postgres.py +19 -11
  11. agno/db/redis/redis.py +6 -0
  12. agno/db/singlestore/schemas.py +1 -1
  13. agno/db/singlestore/singlestore.py +8 -1
  14. agno/db/sqlite/sqlite.py +12 -3
  15. agno/integrations/discord/client.py +1 -0
  16. agno/knowledge/knowledge.py +92 -66
  17. agno/knowledge/reader/reader_factory.py +7 -3
  18. agno/knowledge/reader/web_search_reader.py +12 -6
  19. agno/models/base.py +2 -2
  20. agno/models/message.py +109 -0
  21. agno/models/openai/chat.py +3 -0
  22. agno/models/openai/responses.py +12 -0
  23. agno/models/response.py +5 -0
  24. agno/models/siliconflow/__init__.py +5 -0
  25. agno/models/siliconflow/siliconflow.py +25 -0
  26. agno/os/app.py +164 -41
  27. agno/os/auth.py +24 -14
  28. agno/os/interfaces/agui/utils.py +98 -134
  29. agno/os/router.py +128 -55
  30. agno/os/routers/evals/utils.py +9 -9
  31. agno/os/routers/health.py +25 -0
  32. agno/os/routers/home.py +52 -0
  33. agno/os/routers/knowledge/knowledge.py +11 -11
  34. agno/os/routers/session/session.py +24 -8
  35. agno/os/schema.py +29 -2
  36. agno/os/utils.py +0 -8
  37. agno/run/agent.py +3 -3
  38. agno/run/team.py +3 -3
  39. agno/run/workflow.py +64 -10
  40. agno/session/team.py +1 -0
  41. agno/team/team.py +189 -94
  42. agno/tools/duckduckgo.py +15 -11
  43. agno/tools/googlesearch.py +1 -1
  44. agno/tools/mem0.py +11 -17
  45. agno/tools/memory.py +34 -6
  46. agno/utils/common.py +90 -1
  47. agno/utils/streamlit.py +14 -8
  48. agno/utils/string.py +32 -0
  49. agno/utils/tools.py +1 -1
  50. agno/vectordb/chroma/chromadb.py +8 -2
  51. agno/workflow/step.py +115 -16
  52. agno/workflow/workflow.py +16 -13
  53. {agno-2.0.3.dist-info → agno-2.0.5.dist-info}/METADATA +6 -5
  54. {agno-2.0.3.dist-info → agno-2.0.5.dist-info}/RECORD +57 -54
  55. agno/knowledge/reader/url_reader.py +0 -128
  56. {agno-2.0.3.dist-info → agno-2.0.5.dist-info}/WHEEL +0 -0
  57. {agno-2.0.3.dist-info → agno-2.0.5.dist-info}/licenses/LICENSE +0 -0
  58. {agno-2.0.3.dist-info → agno-2.0.5.dist-info}/top_level.txt +0 -0
agno/team/team.py CHANGED
@@ -55,6 +55,7 @@ from agno.run.team import TeamRunEvent, TeamRunInput, TeamRunOutput, TeamRunOutp
55
55
  from agno.session import SessionSummaryManager, TeamSession
56
56
  from agno.tools import Toolkit
57
57
  from agno.tools.function import Function
58
+ from agno.utils.common import is_typed_dict, validate_typed_dict
58
59
  from agno.utils.events import (
59
60
  create_team_memory_update_completed_event,
60
61
  create_team_memory_update_started_event,
@@ -103,7 +104,7 @@ from agno.utils.response import (
103
104
  generator_wrapper,
104
105
  )
105
106
  from agno.utils.safe_formatter import SafeFormatter
106
- from agno.utils.string import parse_response_model_str
107
+ from agno.utils.string import generate_id_from_name, parse_response_model_str
107
108
  from agno.utils.team import format_member_agent_task, get_member_id
108
109
  from agno.utils.timer import Timer
109
110
 
@@ -579,10 +580,7 @@ class Team:
579
580
  If the name is not provided, generate a random UUID.
580
581
  """
581
582
  if self.id is None:
582
- if self.name is not None:
583
- self.id = self.name.lower().replace(" ", "-")
584
- else:
585
- self.id = str(uuid4())
583
+ self.id = generate_id_from_name(self.name)
586
584
 
587
585
  def _set_debug(self, debug_mode: Optional[bool] = None) -> None:
588
586
  if self.debug_mode or debug_mode or getenv("AGNO_DEBUG", "false").lower() == "true":
@@ -621,8 +619,6 @@ class Team:
621
619
  if isinstance(input, BaseModel):
622
620
  if isinstance(input, self.input_schema):
623
621
  try:
624
- # Re-validate to catch any field validation errors
625
- input.model_validate(input.model_dump())
626
622
  return input
627
623
  except Exception as e:
628
624
  raise ValueError(f"BaseModel validation failed: {str(e)}")
@@ -633,8 +629,13 @@ class Team:
633
629
  # Case 2: Message is a dict
634
630
  elif isinstance(input, dict):
635
631
  try:
636
- validated_model = self.input_schema(**input)
637
- return validated_model
632
+ # Check if the schema is a TypedDict
633
+ if is_typed_dict(self.input_schema):
634
+ validated_dict = validate_typed_dict(input, self.input_schema)
635
+ return validated_dict
636
+ else:
637
+ validated_model = self.input_schema(**input)
638
+ return validated_model
638
639
  except Exception as e:
639
640
  raise ValueError(f"Failed to parse dict into {self.input_schema.__name__}: {str(e)}")
640
641
 
@@ -655,8 +656,6 @@ class Team:
655
656
  member.set_id()
656
657
 
657
658
  elif isinstance(member, Team):
658
- if member.id is None:
659
- member.id = str(uuid4())
660
659
  member.parent_team_id = self.id
661
660
  for sub_member in member.members:
662
661
  self._initialize_member(sub_member, debug_mode=debug_mode)
@@ -806,9 +805,9 @@ class Team:
806
805
  1. Reason about the task(s) if reasoning is enabled
807
806
  2. Get a response from the model
808
807
  3. Update Team Memory
809
- 5. Save session to storage
810
- 6. Parse any structured outputs
811
- 7. Log the team run
808
+ 4. Add RunOutput to Team Session
809
+ 5. Calculate session metrics
810
+ 6. Save session to storage
812
811
  """
813
812
  log_debug(f"Team Run Start: {run_response.run_id}", center=True)
814
813
 
@@ -849,10 +848,15 @@ class Team:
849
848
  else:
850
849
  self._scrub_media_from_run_output(run_response)
851
850
 
852
- # 4. Add the RunOutput to Team Session
851
+ run_response.status = RunStatus.completed
852
+
853
+ # Parse team response model
854
+ self._convert_response_to_structured_format(run_response=run_response)
855
+
856
+ # 3. Add the RunOutput to Team Session
853
857
  session.upsert_run(run_response=run_response)
854
858
 
855
- # 5. Update Team Memory
859
+ # 4. Update Team Memory
856
860
  response_iterator = self._make_memories_and_summaries(
857
861
  run_response=run_response,
858
862
  run_messages=run_messages,
@@ -864,11 +868,6 @@ class Team:
864
868
  # 5. Calculate session metrics
865
869
  self._update_session_metrics(session=session)
866
870
 
867
- run_response.status = RunStatus.completed
868
-
869
- # 5. Parse team response model
870
- self._convert_response_to_structured_format(run_response=run_response)
871
-
872
871
  # 6. Save session to storage
873
872
  self.save_session(session=session)
874
873
 
@@ -899,8 +898,9 @@ class Team:
899
898
  1. Reason about the task(s) if reasoning is enabled
900
899
  2. Get a response from the model
901
900
  3. Update Team Memory
902
- 4. Save session to storage
903
- 5. Log Team Run
901
+ 4. Add RunOutput to Team Session
902
+ 5. Calculate session metrics
903
+ 6. Save session to storage
904
904
  """
905
905
 
906
906
  log_debug(f"Team Run Start: {run_response.run_id}", center=True)
@@ -973,10 +973,12 @@ class Team:
973
973
  session=session, run_response=run_response, stream_intermediate_steps=stream_intermediate_steps
974
974
  )
975
975
 
976
- # 4. Add the run to memory
976
+ run_response.status = RunStatus.completed
977
+
978
+ # 3. Add the run to Team Session
977
979
  session.upsert_run(run_response=run_response)
978
980
 
979
- # 5. Update Team Memory
981
+ # 4. Update Team Memory
980
982
  yield from self._make_memories_and_summaries(
981
983
  run_response=run_response,
982
984
  run_messages=run_messages,
@@ -987,8 +989,6 @@ class Team:
987
989
  # 5. Calculate session metrics
988
990
  self._update_session_metrics(session=session)
989
991
 
990
- run_response.status = RunStatus.completed
991
-
992
992
  completed_event = self._handle_event(
993
993
  create_team_run_completed_event(
994
994
  from_run_response=run_response,
@@ -1338,35 +1338,72 @@ class Team:
1338
1338
  async def _arun(
1339
1339
  self,
1340
1340
  run_response: TeamRunOutput,
1341
- run_messages: RunMessages,
1341
+ input_message: Union[str, List, Dict, Message, BaseModel, List[Message]],
1342
1342
  session: TeamSession,
1343
+ session_state: Optional[Dict[str, Any]] = None,
1343
1344
  user_id: Optional[str] = None,
1345
+ images: Optional[Sequence[Image]] = None,
1346
+ videos: Optional[Sequence[Video]] = None,
1347
+ audio: Optional[Sequence[Audio]] = None,
1348
+ files: Optional[Sequence[File]] = None,
1349
+ knowledge_filters: Optional[Dict[str, Any]] = None,
1350
+ add_history_to_context: Optional[bool] = None,
1351
+ add_dependencies_to_context: Optional[bool] = None,
1352
+ add_session_state_to_context: Optional[bool] = None,
1353
+ metadata: Optional[Dict[str, Any]] = None,
1344
1354
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
1355
+ dependencies: Optional[Dict[str, Any]] = None,
1356
+ **kwargs: Any,
1345
1357
  ) -> TeamRunOutput:
1346
1358
  """Run the Team and return the response.
1347
1359
 
1348
1360
  Steps:
1349
- 1. Reason about the task(s) if reasoning is enabled
1350
- 2. Get a response from the model
1351
- 3. Update Team Memory
1352
- 5. Save session to storage
1353
- 6. Parse any structured outputs
1354
- 7. Log the team run
1361
+ 1. Resolve dependencies
1362
+ 2. Prepare run messages
1363
+ 3. Reason about the task(s) if reasoning is enabled
1364
+ 4. Get a response from the model
1365
+ 5. Update Team Memory
1366
+ 6. Add RunOutput to Team Session
1367
+ 7. Calculate session metrics
1368
+ 8. Save session to storage
1355
1369
  """
1356
- self.model = cast(Model, self.model)
1370
+ # 1. Resolve callable dependencies if present
1371
+ if dependencies is not None:
1372
+ await self._aresolve_run_dependencies(dependencies=dependencies)
1373
+
1374
+ # 2. Prepare run messages
1375
+ run_messages = self._get_run_messages(
1376
+ run_response=run_response,
1377
+ session=session,
1378
+ session_state=session_state,
1379
+ user_id=user_id,
1380
+ input_message=input_message,
1381
+ audio=audio,
1382
+ images=images,
1383
+ videos=videos,
1384
+ files=files,
1385
+ knowledge_filters=knowledge_filters,
1386
+ add_history_to_context=add_history_to_context,
1387
+ dependencies=dependencies,
1388
+ add_dependencies_to_context=add_dependencies_to_context,
1389
+ add_session_state_to_context=add_session_state_to_context,
1390
+ metadata=metadata,
1391
+ **kwargs,
1392
+ )
1357
1393
 
1394
+ self.model = cast(Model, self.model)
1358
1395
  log_debug(f"Team Run Start: {run_response.run_id}", center=True)
1359
1396
 
1360
1397
  # Register run for cancellation tracking
1361
1398
  register_run(run_response.run_id) # type: ignore
1362
1399
 
1363
- # 1. Reason about the task(s) if reasoning is enabled
1400
+ # 3. Reason about the task(s) if reasoning is enabled
1364
1401
  await self._ahandle_reasoning(run_response=run_response, run_messages=run_messages)
1365
1402
 
1366
1403
  # Check for cancellation before model call
1367
1404
  raise_if_cancelled(run_response.run_id) # type: ignore
1368
1405
 
1369
- # 2. Get the model response for the team leader
1406
+ # 4. Get the model response for the team leader
1370
1407
  model_response = await self.model.aresponse(
1371
1408
  messages=run_messages.messages,
1372
1409
  tools=self._tools_for_model,
@@ -1393,10 +1430,15 @@ class Team:
1393
1430
  else:
1394
1431
  self._scrub_media_from_run_output(run_response)
1395
1432
 
1396
- # 3. Add the run to memory
1433
+ run_response.status = RunStatus.completed
1434
+
1435
+ # Parse team response model
1436
+ self._convert_response_to_structured_format(run_response=run_response)
1437
+
1438
+ # 5. Add the run to memory
1397
1439
  session.upsert_run(run_response=run_response)
1398
1440
 
1399
- # 4. Update Team Memory
1441
+ # 6. Update Team Memory
1400
1442
  async for _ in self._amake_memories_and_summaries(
1401
1443
  run_response=run_response,
1402
1444
  session=session,
@@ -1405,18 +1447,13 @@ class Team:
1405
1447
  ):
1406
1448
  pass
1407
1449
 
1408
- # 5. Calculate session metrics
1450
+ # 7. Calculate session metrics
1409
1451
  self._update_session_metrics(session=session)
1410
1452
 
1411
- run_response.status = RunStatus.completed
1412
-
1413
- # 6. Parse team response model
1414
- self._convert_response_to_structured_format(run_response=run_response)
1415
-
1416
- # 7. Save session to storage
1453
+ # 8. Save session to storage
1417
1454
  self.save_session(session=session)
1418
1455
 
1419
- # 8. Log Team Telemetry
1456
+ # Log Team Telemetry
1420
1457
  await self._alog_team_telemetry(session_id=session.session_id, run_id=run_response.run_id)
1421
1458
 
1422
1459
  log_debug(f"Team Run End: {run_response.run_id}", center=True, symbol="*")
@@ -1429,25 +1466,64 @@ class Team:
1429
1466
  async def _arun_stream(
1430
1467
  self,
1431
1468
  run_response: TeamRunOutput,
1432
- run_messages: RunMessages,
1469
+ input_message: Union[str, List, Dict, Message, BaseModel, List[Message]],
1433
1470
  session: TeamSession,
1471
+ session_state: Optional[Dict[str, Any]] = None,
1434
1472
  user_id: Optional[str] = None,
1473
+ images: Optional[Sequence[Image]] = None,
1474
+ videos: Optional[Sequence[Video]] = None,
1475
+ audio: Optional[Sequence[Audio]] = None,
1476
+ files: Optional[Sequence[File]] = None,
1477
+ knowledge_filters: Optional[Dict[str, Any]] = None,
1478
+ add_history_to_context: Optional[bool] = None,
1479
+ add_dependencies_to_context: Optional[bool] = None,
1480
+ add_session_state_to_context: Optional[bool] = None,
1481
+ metadata: Optional[Dict[str, Any]] = None,
1435
1482
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
1483
+ dependencies: Optional[Dict[str, Any]] = None,
1436
1484
  stream_intermediate_steps: bool = False,
1437
1485
  workflow_context: Optional[Dict] = None,
1438
1486
  yield_run_response: bool = False,
1487
+ **kwargs: Any,
1439
1488
  ) -> AsyncIterator[Union[TeamRunOutputEvent, RunOutputEvent, TeamRunOutput]]:
1440
1489
  """Run the Team and return the response.
1441
1490
 
1442
1491
  Steps:
1443
- 1. Reason about the task(s) if reasoning is enabled
1444
- 2. Get a response from the model
1445
- 3. Update Team Memory
1446
- 4. Save session to storage
1447
- 5. Log Team Run
1492
+ 1. Resolve dependencies
1493
+ 2. Prepare run messages
1494
+ 3. Reason about the task(s) if reasoning is enabled
1495
+ 4. Get a response from the model
1496
+ 5. Update Team Memory
1497
+ 6. Add RunOutput to Team Session
1498
+ 7. Calculate session metrics
1499
+ 8. Save session to storage
1448
1500
  """
1449
- log_debug(f"Team Run Start: {run_response.run_id}", center=True)
1450
1501
 
1502
+ # 1. Resolve callable dependencies if present
1503
+ if dependencies is not None:
1504
+ await self._aresolve_run_dependencies(dependencies=dependencies)
1505
+
1506
+ # 2. Prepare run messages
1507
+ run_messages = self._get_run_messages(
1508
+ run_response=run_response,
1509
+ session=session,
1510
+ session_state=session_state,
1511
+ user_id=user_id,
1512
+ input_message=input_message,
1513
+ audio=audio,
1514
+ images=images,
1515
+ videos=videos,
1516
+ files=files,
1517
+ knowledge_filters=knowledge_filters,
1518
+ add_history_to_context=add_history_to_context,
1519
+ dependencies=dependencies,
1520
+ add_dependencies_to_context=add_dependencies_to_context,
1521
+ add_session_state_to_context=add_session_state_to_context,
1522
+ metadata=metadata,
1523
+ **kwargs,
1524
+ )
1525
+
1526
+ log_debug(f"Team Run Start: {run_response.run_id}", center=True)
1451
1527
  # Register run for cancellation tracking
1452
1528
  register_run(run_response.run_id) # type: ignore
1453
1529
 
@@ -1458,7 +1534,7 @@ class Team:
1458
1534
  create_team_run_started_event(from_run_response=run_response), run_response, workflow_context
1459
1535
  )
1460
1536
 
1461
- # 1. Reason about the task(s) if reasoning is enabled
1537
+ # 3. Reason about the task(s) if reasoning is enabled
1462
1538
  async for item in self._ahandle_reasoning_stream(run_response=run_response, run_messages=run_messages):
1463
1539
  raise_if_cancelled(run_response.run_id) # type: ignore
1464
1540
  yield item
@@ -1466,7 +1542,7 @@ class Team:
1466
1542
  # Check for cancellation before model processing
1467
1543
  raise_if_cancelled(run_response.run_id) # type: ignore
1468
1544
 
1469
- # 2. Get a response from the model
1545
+ # 4. Get a response from the model
1470
1546
  if self.output_model is None:
1471
1547
  async for event in self._ahandle_model_response_stream(
1472
1548
  session=session,
@@ -1518,10 +1594,12 @@ class Team:
1518
1594
  ):
1519
1595
  yield event
1520
1596
 
1521
- # 3. Add the run to memory
1597
+ run_response.status = RunStatus.completed
1598
+
1599
+ # 5. Add the run to Team Session
1522
1600
  session.upsert_run(run_response=run_response)
1523
1601
 
1524
- # 4. Update Team Memory
1602
+ # 6. Update Team Memory
1525
1603
  async for event in self._amake_memories_and_summaries(
1526
1604
  run_response=run_response,
1527
1605
  session=session,
@@ -1530,16 +1608,14 @@ class Team:
1530
1608
  ):
1531
1609
  yield event
1532
1610
 
1533
- # 5. Calculate session metrics
1611
+ # 7. Calculate session metrics
1534
1612
  self._update_session_metrics(session=session)
1535
1613
 
1536
- run_response.status = RunStatus.completed
1537
-
1538
1614
  completed_event = self._handle_event(
1539
1615
  create_team_run_completed_event(from_run_response=run_response), run_response, workflow_context
1540
1616
  )
1541
1617
 
1542
- # 6. Save session to storage
1618
+ # 8. Save session to storage
1543
1619
  self.save_session(session=session)
1544
1620
 
1545
1621
  if stream_intermediate_steps:
@@ -1548,7 +1624,7 @@ class Team:
1548
1624
  if yield_run_response:
1549
1625
  yield run_response
1550
1626
 
1551
- # 7. Log Team Telemetry
1627
+ # Log Team Telemetry
1552
1628
  await self._alog_team_telemetry(session_id=session.session_id, run_id=run_response.run_id)
1553
1629
 
1554
1630
  log_debug(f"Team Run End: {run_response.run_id}", center=True, symbol="*")
@@ -1681,10 +1757,6 @@ class Team:
1681
1757
  # Determine run dependencies (runtime override takes priority)
1682
1758
  run_dependencies = dependencies if dependencies is not None else self.dependencies
1683
1759
 
1684
- # Resolve callable dependencies if present
1685
- if run_dependencies is not None:
1686
- self._resolve_run_dependencies(dependencies=run_dependencies)
1687
-
1688
1760
  # Determine runtime context parameters
1689
1761
  add_dependencies = (
1690
1762
  add_dependencies_to_context if add_dependencies_to_context is not None else self.add_dependencies_to_context
@@ -1781,43 +1853,49 @@ class Team:
1781
1853
  for attempt in range(num_attempts):
1782
1854
  # Run the team
1783
1855
  try:
1784
- run_messages = self._get_run_messages(
1785
- run_response=run_response,
1786
- session=team_session, # type: ignore
1787
- session_state=session_state,
1788
- user_id=user_id,
1789
- input_message=validated_input,
1790
- audio=audio,
1791
- images=images,
1792
- videos=videos,
1793
- files=files,
1794
- knowledge_filters=effective_filters,
1795
- add_history_to_context=add_history,
1796
- dependencies=run_dependencies,
1797
- add_dependencies_to_context=add_dependencies,
1798
- add_session_state_to_context=add_session_state,
1799
- **kwargs,
1800
- )
1801
-
1802
1856
  if stream:
1803
1857
  response_iterator = self._arun_stream(
1804
1858
  run_response=run_response,
1805
- run_messages=run_messages,
1859
+ input_message=validated_input,
1806
1860
  session=team_session, # type: ignore
1861
+ session_state=session_state,
1807
1862
  user_id=user_id,
1863
+ audio=audio,
1864
+ images=images,
1865
+ videos=videos,
1866
+ files=files,
1867
+ knowledge_filters=effective_filters,
1868
+ add_history_to_context=add_history,
1869
+ add_dependencies_to_context=add_dependencies,
1870
+ add_session_state_to_context=add_session_state,
1871
+ metadata=metadata,
1808
1872
  response_format=response_format,
1873
+ dependencies=run_dependencies,
1809
1874
  stream_intermediate_steps=stream_intermediate_steps,
1810
1875
  workflow_context=workflow_context,
1811
1876
  yield_run_response=yield_run_response,
1877
+ **kwargs,
1812
1878
  )
1813
1879
  return response_iterator # type: ignore
1814
1880
  else:
1815
1881
  return self._arun( # type: ignore
1816
1882
  run_response=run_response,
1817
- run_messages=run_messages,
1883
+ input_message=validated_input,
1818
1884
  session=team_session, # type: ignore
1819
1885
  user_id=user_id,
1886
+ session_state=session_state,
1887
+ audio=audio,
1888
+ images=images,
1889
+ videos=videos,
1890
+ files=files,
1891
+ knowledge_filters=effective_filters,
1892
+ add_history_to_context=add_history,
1893
+ add_dependencies_to_context=add_dependencies,
1894
+ add_session_state_to_context=add_session_state,
1895
+ metadata=metadata,
1820
1896
  response_format=response_format,
1897
+ dependencies=run_dependencies,
1898
+ **kwargs,
1821
1899
  )
1822
1900
 
1823
1901
  except ModelProviderError as e:
@@ -2284,11 +2362,13 @@ class Team:
2284
2362
  tc.tool_call_id: i for i, tc in enumerate(run_response.tools) if tc.tool_call_id is not None
2285
2363
  }
2286
2364
  # Process tool calls
2287
- for tool_call_dict in tool_executions_list:
2288
- tool_call_id = tool_call_dict.tool_call_id or ""
2365
+ for tool_execution in tool_executions_list:
2366
+ tool_call_id = tool_execution.tool_call_id or ""
2289
2367
  index = tool_call_index_map.get(tool_call_id)
2290
2368
  if index is not None:
2291
- run_response.tools[index] = tool_call_dict
2369
+ if run_response.tools[index].child_run_id is not None:
2370
+ tool_execution.child_run_id = run_response.tools[index].child_run_id
2371
+ run_response.tools[index] = tool_execution
2292
2372
  else:
2293
2373
  run_response.tools = tool_executions_list
2294
2374
 
@@ -3731,7 +3811,7 @@ class Team:
3731
3811
 
3732
3812
  try:
3733
3813
  sig = signature(value)
3734
- resolved_value = value(agent=self) if "agent" in sig.parameters else value()
3814
+ resolved_value = value(team=self) if "team" in sig.parameters else value()
3735
3815
 
3736
3816
  if iscoroutine(resolved_value):
3737
3817
  resolved_value = await resolved_value
@@ -4392,8 +4472,8 @@ class Team:
4392
4472
  f"<additional_context>\n{self.additional_context.strip()}\n</additional_context>\n\n"
4393
4473
  )
4394
4474
 
4395
- if self.add_session_state_to_context:
4396
- system_message_content += f"<session_state>\n{session_state}\n</session_state>\n\n"
4475
+ if self.add_session_state_to_context and session_state is not None:
4476
+ system_message_content += self._get_formatted_session_state_for_system_message(session_state)
4397
4477
 
4398
4478
  # Add the JSON output prompt if output_schema is provided and structured_outputs is False
4399
4479
  if (
@@ -4406,6 +4486,9 @@ class Team:
4406
4486
 
4407
4487
  return Message(role=self.system_message_role, content=system_message_content.strip())
4408
4488
 
4489
+ def _get_formatted_session_state_for_system_message(self, session_state: Dict[str, Any]) -> str:
4490
+ return f"\n<session_state>\n{session_state}\n</session_state>\n\n"
4491
+
4409
4492
  def _get_run_messages(
4410
4493
  self,
4411
4494
  *,
@@ -4597,7 +4680,13 @@ class Team:
4597
4680
  # If message is provided as a dict, try to validate it as a Message
4598
4681
  elif isinstance(input_message, dict):
4599
4682
  try:
4600
- return Message.model_validate(input_message)
4683
+ if self.input_schema and is_typed_dict(self.input_schema):
4684
+ import json
4685
+
4686
+ content = json.dumps(input_message, indent=2, ensure_ascii=False)
4687
+ return Message(role="user", content=content)
4688
+ else:
4689
+ return Message.model_validate(input_message)
4601
4690
  except Exception as e:
4602
4691
  log_warning(f"Failed to validate input: {e}")
4603
4692
 
@@ -5158,6 +5247,12 @@ class Team:
5158
5247
  if member_agent_run_response is not None:
5159
5248
  member_agent_run_response.parent_run_id = run_response.run_id # type: ignore
5160
5249
 
5250
+ # Update the top-level team run_response tool call to have the run_id of the member run
5251
+ if run_response.tools is not None:
5252
+ for tool in run_response.tools:
5253
+ if tool.tool_name and tool.tool_name.lower() == "delegate_task_to_member":
5254
+ tool.child_run_id = member_agent_run_response.run_id # type: ignore
5255
+
5161
5256
  # Update the team run context
5162
5257
  member_name = member_agent.name if member_agent.name else member_agent.id if member_agent.id else "Unknown"
5163
5258
  if isinstance(member_agent_task, str):
agno/tools/duckduckgo.py CHANGED
@@ -12,14 +12,16 @@ except ImportError:
12
12
 
13
13
  class DuckDuckGoTools(Toolkit):
14
14
  """
15
- DuckDuckGo is a toolkit for searching DuckDuckGo easily.
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.
16
17
  Args:
17
- search (bool): Enable DuckDuckGo search function.
18
- news (bool): Enable DuckDuckGo news function.
18
+ enable_search (bool): Enable DDGS search function.
19
+ enable_news (bool): Enable DDGS news function.
19
20
  modifier (Optional[str]): A modifier to be used in the search request.
20
21
  fixed_max_results (Optional[int]): A fixed number of maximum results.
21
22
  proxy (Optional[str]): Proxy to be used in the search request.
22
23
  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.
23
25
 
24
26
  """
25
27
 
@@ -28,6 +30,7 @@ class DuckDuckGoTools(Toolkit):
28
30
  enable_search: bool = True,
29
31
  enable_news: bool = True,
30
32
  all: bool = False,
33
+ backend: str = "duckduckgo",
31
34
  modifier: Optional[str] = None,
32
35
  fixed_max_results: Optional[int] = None,
33
36
  proxy: Optional[str] = None,
@@ -40,6 +43,7 @@ class DuckDuckGoTools(Toolkit):
40
43
  self.fixed_max_results: Optional[int] = fixed_max_results
41
44
  self.modifier: Optional[str] = modifier
42
45
  self.verify_ssl: bool = verify_ssl
46
+ self.backend: str = backend
43
47
 
44
48
  tools: List[Any] = []
45
49
  if all or enable_search:
@@ -50,38 +54,38 @@ class DuckDuckGoTools(Toolkit):
50
54
  super().__init__(name="duckduckgo", tools=tools, **kwargs)
51
55
 
52
56
  def duckduckgo_search(self, query: str, max_results: int = 5) -> str:
53
- """Use this function to search DuckDuckGo for a query.
57
+ """Use this function to search DDGS for a query.
54
58
 
55
59
  Args:
56
60
  query(str): The query to search for.
57
61
  max_results (optional, default=5): The maximum number of results to return.
58
62
 
59
63
  Returns:
60
- The result from DuckDuckGo.
64
+ The result from DDGS.
61
65
  """
62
66
  actual_max_results = self.fixed_max_results or max_results
63
67
  search_query = f"{self.modifier} {query}" if self.modifier else query
64
68
 
65
- log_debug(f"Searching DDG for: {search_query}")
69
+ log_debug(f"Searching DDG for: {search_query} using backend: {self.backend}")
66
70
  with DDGS(proxy=self.proxy, timeout=self.timeout, verify=self.verify_ssl) as ddgs:
67
- results = ddgs.text(query=search_query, max_results=actual_max_results)
71
+ results = ddgs.text(query=search_query, max_results=actual_max_results, backend=self.backend)
68
72
 
69
73
  return json.dumps(results, indent=2)
70
74
 
71
75
  def duckduckgo_news(self, query: str, max_results: int = 5) -> str:
72
- """Use this function to get the latest news from DuckDuckGo.
76
+ """Use this function to get the latest news from DDGS.
73
77
 
74
78
  Args:
75
79
  query(str): The query to search for.
76
80
  max_results (optional, default=5): The maximum number of results to return.
77
81
 
78
82
  Returns:
79
- The latest news from DuckDuckGo.
83
+ The latest news from DDGS.
80
84
  """
81
85
  actual_max_results = self.fixed_max_results or max_results
82
86
 
83
- log_debug(f"Searching DDG news for: {query}")
87
+ log_debug(f"Searching DDG news for: {query} using backend: {self.backend}")
84
88
  with DDGS(proxy=self.proxy, timeout=self.timeout, verify=self.verify_ssl) as ddgs:
85
- results = ddgs.news(query=query, max_results=actual_max_results)
89
+ results = ddgs.news(query=query, max_results=actual_max_results, backend=self.backend)
86
90
 
87
91
  return json.dumps(results, indent=2)
@@ -82,7 +82,7 @@ class GoogleSearchTools(Toolkit):
82
82
  log_debug(f"Searching Google [{language}] for: {query}")
83
83
 
84
84
  # Perform Google search using the googlesearch-python package
85
- results = list(search(query, num=max_results, lang=language))
85
+ results = list(search(query, num_results=max_results, lang=language))
86
86
 
87
87
  # Collect the search results
88
88
  res: List[Dict[str, str]] = []