agno 2.3.3__py3-none-any.whl → 2.3.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 (108) hide show
  1. agno/agent/agent.py +177 -41
  2. agno/culture/manager.py +2 -2
  3. agno/db/base.py +330 -8
  4. agno/db/dynamo/dynamo.py +722 -2
  5. agno/db/dynamo/schemas.py +127 -0
  6. agno/db/firestore/firestore.py +573 -1
  7. agno/db/firestore/schemas.py +40 -0
  8. agno/db/gcs_json/gcs_json_db.py +446 -1
  9. agno/db/in_memory/in_memory_db.py +143 -1
  10. agno/db/json/json_db.py +438 -1
  11. agno/db/mongo/async_mongo.py +522 -0
  12. agno/db/mongo/mongo.py +523 -1
  13. agno/db/mongo/schemas.py +29 -0
  14. agno/db/mysql/mysql.py +536 -3
  15. agno/db/mysql/schemas.py +38 -0
  16. agno/db/postgres/async_postgres.py +546 -14
  17. agno/db/postgres/postgres.py +535 -2
  18. agno/db/postgres/schemas.py +38 -0
  19. agno/db/redis/redis.py +468 -1
  20. agno/db/redis/schemas.py +32 -0
  21. agno/db/singlestore/schemas.py +38 -0
  22. agno/db/singlestore/singlestore.py +523 -1
  23. agno/db/sqlite/async_sqlite.py +548 -9
  24. agno/db/sqlite/schemas.py +38 -0
  25. agno/db/sqlite/sqlite.py +537 -5
  26. agno/db/sqlite/utils.py +6 -8
  27. agno/db/surrealdb/models.py +25 -0
  28. agno/db/surrealdb/surrealdb.py +548 -1
  29. agno/eval/accuracy.py +10 -4
  30. agno/eval/performance.py +10 -4
  31. agno/eval/reliability.py +22 -13
  32. agno/exceptions.py +11 -0
  33. agno/hooks/__init__.py +3 -0
  34. agno/hooks/decorator.py +164 -0
  35. agno/knowledge/chunking/semantic.py +2 -2
  36. agno/models/aimlapi/aimlapi.py +17 -0
  37. agno/models/anthropic/claude.py +19 -12
  38. agno/models/aws/bedrock.py +3 -4
  39. agno/models/aws/claude.py +5 -1
  40. agno/models/azure/ai_foundry.py +2 -2
  41. agno/models/azure/openai_chat.py +8 -0
  42. agno/models/cerebras/cerebras.py +61 -4
  43. agno/models/cerebras/cerebras_openai.py +17 -0
  44. agno/models/cohere/chat.py +5 -1
  45. agno/models/cometapi/cometapi.py +18 -1
  46. agno/models/dashscope/dashscope.py +2 -3
  47. agno/models/deepinfra/deepinfra.py +18 -1
  48. agno/models/deepseek/deepseek.py +2 -3
  49. agno/models/fireworks/fireworks.py +18 -1
  50. agno/models/google/gemini.py +8 -2
  51. agno/models/groq/groq.py +5 -2
  52. agno/models/internlm/internlm.py +18 -1
  53. agno/models/langdb/langdb.py +13 -1
  54. agno/models/litellm/chat.py +2 -2
  55. agno/models/litellm/litellm_openai.py +18 -1
  56. agno/models/meta/llama_openai.py +19 -2
  57. agno/models/nebius/nebius.py +2 -3
  58. agno/models/nvidia/nvidia.py +20 -3
  59. agno/models/openai/chat.py +17 -2
  60. agno/models/openai/responses.py +17 -2
  61. agno/models/openrouter/openrouter.py +21 -2
  62. agno/models/perplexity/perplexity.py +17 -1
  63. agno/models/portkey/portkey.py +7 -6
  64. agno/models/requesty/requesty.py +19 -2
  65. agno/models/response.py +2 -1
  66. agno/models/sambanova/sambanova.py +20 -3
  67. agno/models/siliconflow/siliconflow.py +19 -2
  68. agno/models/together/together.py +20 -3
  69. agno/models/vercel/v0.py +20 -3
  70. agno/models/vllm/vllm.py +19 -14
  71. agno/models/xai/xai.py +19 -2
  72. agno/os/app.py +104 -0
  73. agno/os/config.py +13 -0
  74. agno/os/interfaces/whatsapp/router.py +0 -1
  75. agno/os/mcp.py +1 -0
  76. agno/os/router.py +31 -0
  77. agno/os/routers/traces/__init__.py +3 -0
  78. agno/os/routers/traces/schemas.py +414 -0
  79. agno/os/routers/traces/traces.py +499 -0
  80. agno/os/schema.py +22 -1
  81. agno/os/utils.py +57 -0
  82. agno/run/agent.py +1 -0
  83. agno/run/base.py +17 -0
  84. agno/run/team.py +4 -0
  85. agno/session/team.py +1 -0
  86. agno/table.py +10 -0
  87. agno/team/team.py +215 -65
  88. agno/tools/function.py +10 -8
  89. agno/tools/nano_banana.py +1 -1
  90. agno/tracing/__init__.py +12 -0
  91. agno/tracing/exporter.py +157 -0
  92. agno/tracing/schemas.py +276 -0
  93. agno/tracing/setup.py +111 -0
  94. agno/utils/agent.py +4 -4
  95. agno/utils/hooks.py +56 -1
  96. agno/vectordb/qdrant/qdrant.py +22 -22
  97. agno/workflow/condition.py +8 -0
  98. agno/workflow/loop.py +8 -0
  99. agno/workflow/parallel.py +8 -0
  100. agno/workflow/router.py +8 -0
  101. agno/workflow/step.py +20 -0
  102. agno/workflow/steps.py +8 -0
  103. agno/workflow/workflow.py +83 -17
  104. {agno-2.3.3.dist-info → agno-2.3.5.dist-info}/METADATA +2 -2
  105. {agno-2.3.3.dist-info → agno-2.3.5.dist-info}/RECORD +108 -98
  106. {agno-2.3.3.dist-info → agno-2.3.5.dist-info}/WHEEL +0 -0
  107. {agno-2.3.3.dist-info → agno-2.3.5.dist-info}/licenses/LICENSE +0 -0
  108. {agno-2.3.3.dist-info → agno-2.3.5.dist-info}/top_level.txt +0 -0
agno/workflow/workflow.py CHANGED
@@ -186,6 +186,9 @@ class Workflow:
186
186
  # Deprecated. Use stream_events instead.
187
187
  stream_intermediate_steps: bool = False
188
188
 
189
+ # If True, run hooks as FastAPI background tasks (non-blocking). Set by AgentOS.
190
+ _run_hooks_in_background: bool = False
191
+
189
192
  def __init__(
190
193
  self,
191
194
  id: Optional[str] = None,
@@ -198,6 +201,7 @@ class Workflow:
198
201
  session_state: Optional[Dict[str, Any]] = None,
199
202
  overwrite_db_session_state: bool = False,
200
203
  user_id: Optional[str] = None,
204
+ debug_level: Literal[1, 2] = 1,
201
205
  debug_mode: Optional[bool] = False,
202
206
  stream: Optional[bool] = None,
203
207
  stream_events: bool = False,
@@ -223,6 +227,7 @@ class Workflow:
223
227
  self.overwrite_db_session_state = overwrite_db_session_state
224
228
  self.user_id = user_id
225
229
  self.debug_mode = debug_mode
230
+ self.debug_level = debug_level
226
231
  self.store_events = store_events
227
232
  self.events_to_skip = events_to_skip or []
228
233
  self.stream = stream
@@ -1105,9 +1110,12 @@ class Workflow:
1105
1110
  """Set debug mode and configure logging"""
1106
1111
  if self.debug_mode or getenv("AGNO_DEBUG", "false").lower() == "true":
1107
1112
  use_workflow_logger()
1113
+ debug_level: Literal[1, 2] = (
1114
+ cast(Literal[1, 2], int(env)) if (env := getenv("AGNO_DEBUG_LEVEL")) in ("1", "2") else self.debug_level
1115
+ )
1108
1116
 
1109
1117
  self.debug_mode = True
1110
- set_log_level_to_debug(source_type="workflow")
1118
+ set_log_level_to_debug(source_type="workflow", level=debug_level)
1111
1119
 
1112
1120
  # Propagate to steps - only if steps is iterable (not callable)
1113
1121
  if self.steps and not callable(self.steps):
@@ -1282,14 +1290,13 @@ class Workflow:
1282
1290
  execution_input: WorkflowExecutionInput,
1283
1291
  workflow_run_response: WorkflowRunOutput,
1284
1292
  run_context: RunContext,
1293
+ background_tasks: Optional[Any] = None,
1285
1294
  **kwargs: Any,
1286
1295
  ) -> WorkflowRunOutput:
1287
1296
  """Execute a specific pipeline by name synchronously"""
1288
1297
  from inspect import isasyncgenfunction, iscoroutinefunction, isgeneratorfunction
1289
1298
 
1290
1299
  workflow_run_response.status = RunStatus.running
1291
- if workflow_run_response.run_id:
1292
- register_run(workflow_run_response.run_id) # type: ignore
1293
1300
 
1294
1301
  if callable(self.steps):
1295
1302
  if iscoroutinefunction(self.steps) or isasyncgenfunction(self.steps):
@@ -1355,6 +1362,7 @@ class Workflow:
1355
1362
  if self.add_workflow_history_to_steps
1356
1363
  else None,
1357
1364
  num_history_runs=self.num_history_runs,
1365
+ background_tasks=background_tasks,
1358
1366
  )
1359
1367
 
1360
1368
  # Check for cancellation after step execution
@@ -1455,6 +1463,7 @@ class Workflow:
1455
1463
  workflow_run_response: WorkflowRunOutput,
1456
1464
  run_context: RunContext,
1457
1465
  stream_events: bool = False,
1466
+ background_tasks: Optional[Any] = None,
1458
1467
  **kwargs: Any,
1459
1468
  ) -> Iterator[WorkflowRunOutputEvent]:
1460
1469
  """Execute a specific pipeline by name with event streaming"""
@@ -1462,10 +1471,6 @@ class Workflow:
1462
1471
 
1463
1472
  workflow_run_response.status = RunStatus.running
1464
1473
 
1465
- # Register run for cancellation tracking
1466
- if workflow_run_response.run_id:
1467
- register_run(workflow_run_response.run_id)
1468
-
1469
1474
  workflow_started_event = WorkflowStartedEvent(
1470
1475
  run_id=workflow_run_response.run_id or "",
1471
1476
  workflow_name=workflow_run_response.workflow_name,
@@ -1552,6 +1557,7 @@ class Workflow:
1552
1557
  if self.add_workflow_history_to_steps
1553
1558
  else None,
1554
1559
  num_history_runs=self.num_history_runs,
1560
+ background_tasks=background_tasks,
1555
1561
  ):
1556
1562
  raise_if_cancelled(workflow_run_response.run_id) # type: ignore
1557
1563
 
@@ -1843,6 +1849,7 @@ class Workflow:
1843
1849
  execution_input: WorkflowExecutionInput,
1844
1850
  workflow_run_response: WorkflowRunOutput,
1845
1851
  run_context: RunContext,
1852
+ background_tasks: Optional[Any] = None,
1846
1853
  **kwargs: Any,
1847
1854
  ) -> WorkflowRunOutput:
1848
1855
  """Execute a specific pipeline by name asynchronously"""
@@ -1855,10 +1862,6 @@ class Workflow:
1855
1862
 
1856
1863
  workflow_run_response.status = RunStatus.running
1857
1864
 
1858
- # Register run for cancellation tracking
1859
- if workflow_run_response.run_id:
1860
- register_run(workflow_run_response.run_id) # type: ignore
1861
-
1862
1865
  if callable(self.steps):
1863
1866
  # Execute the workflow with the custom executor
1864
1867
  content = ""
@@ -1931,6 +1934,7 @@ class Workflow:
1931
1934
  if self.add_workflow_history_to_steps
1932
1935
  else None,
1933
1936
  num_history_runs=self.num_history_runs,
1937
+ background_tasks=background_tasks,
1934
1938
  )
1935
1939
 
1936
1940
  # Check for cancellation after step execution
@@ -2031,6 +2035,7 @@ class Workflow:
2031
2035
  run_context: RunContext,
2032
2036
  stream_events: bool = False,
2033
2037
  websocket_handler: Optional[WebSocketHandler] = None,
2038
+ background_tasks: Optional[Any] = None,
2034
2039
  **kwargs: Any,
2035
2040
  ) -> AsyncIterator[WorkflowRunOutputEvent]:
2036
2041
  """Execute a specific pipeline by name with event streaming"""
@@ -2043,10 +2048,6 @@ class Workflow:
2043
2048
 
2044
2049
  workflow_run_response.status = RunStatus.running
2045
2050
 
2046
- # Register run for cancellation tracking
2047
- if workflow_run_response.run_id:
2048
- register_run(workflow_run_response.run_id)
2049
-
2050
2051
  workflow_started_event = WorkflowStartedEvent(
2051
2052
  run_id=workflow_run_response.run_id or "",
2052
2053
  workflow_name=workflow_run_response.workflow_name,
@@ -2141,6 +2142,7 @@ class Workflow:
2141
2142
  if self.add_workflow_history_to_steps
2142
2143
  else None,
2143
2144
  num_history_runs=self.num_history_runs,
2145
+ background_tasks=background_tasks,
2144
2146
  ):
2145
2147
  if workflow_run_response.run_id:
2146
2148
  raise_if_cancelled(workflow_run_response.run_id)
@@ -3457,6 +3459,7 @@ class Workflow:
3457
3459
  stream_events: Optional[bool] = None,
3458
3460
  stream_intermediate_steps: Optional[bool] = None,
3459
3461
  background: Optional[bool] = False,
3462
+ background_tasks: Optional[Any] = None,
3460
3463
  ) -> WorkflowRunOutput: ...
3461
3464
 
3462
3465
  @overload
@@ -3475,6 +3478,7 @@ class Workflow:
3475
3478
  stream_events: Optional[bool] = None,
3476
3479
  stream_intermediate_steps: Optional[bool] = None,
3477
3480
  background: Optional[bool] = False,
3481
+ background_tasks: Optional[Any] = None,
3478
3482
  ) -> Iterator[WorkflowRunOutputEvent]: ...
3479
3483
 
3480
3484
  def run(
@@ -3492,20 +3496,23 @@ class Workflow:
3492
3496
  stream_events: Optional[bool] = None,
3493
3497
  stream_intermediate_steps: Optional[bool] = None,
3494
3498
  background: Optional[bool] = False,
3499
+ background_tasks: Optional[Any] = None,
3495
3500
  **kwargs: Any,
3496
3501
  ) -> Union[WorkflowRunOutput, Iterator[WorkflowRunOutputEvent]]:
3497
3502
  """Execute the workflow synchronously with optional streaming"""
3498
3503
  if self._has_async_db():
3499
3504
  raise Exception("`run()` is not supported with an async DB. Please use `arun()`.")
3500
3505
 
3506
+ # Create a run_id for this specific run and register immediately for cancellation tracking
3507
+ run_id = str(uuid4())
3508
+ register_run(run_id)
3509
+
3501
3510
  input = self._validate_input(input)
3502
3511
  if background:
3503
3512
  raise RuntimeError("Background execution is not supported for sync run()")
3504
3513
 
3505
3514
  self._set_debug()
3506
3515
 
3507
- run_id = str(uuid4())
3508
-
3509
3516
  self.initialize_workflow()
3510
3517
  session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
3511
3518
 
@@ -3592,6 +3599,7 @@ class Workflow:
3592
3599
  workflow_run_response=workflow_run_response,
3593
3600
  stream_events=stream_events,
3594
3601
  run_context=run_context,
3602
+ background_tasks=background_tasks,
3595
3603
  **kwargs,
3596
3604
  )
3597
3605
  else:
@@ -3600,6 +3608,7 @@ class Workflow:
3600
3608
  execution_input=inputs, # type: ignore[arg-type]
3601
3609
  workflow_run_response=workflow_run_response,
3602
3610
  run_context=run_context,
3611
+ background_tasks=background_tasks,
3603
3612
  **kwargs,
3604
3613
  )
3605
3614
 
@@ -3620,6 +3629,7 @@ class Workflow:
3620
3629
  stream_intermediate_steps: Optional[bool] = None,
3621
3630
  background: Optional[bool] = False,
3622
3631
  websocket: Optional[WebSocket] = None,
3632
+ background_tasks: Optional[Any] = None,
3623
3633
  ) -> WorkflowRunOutput: ...
3624
3634
 
3625
3635
  @overload
@@ -3639,6 +3649,7 @@ class Workflow:
3639
3649
  stream_intermediate_steps: Optional[bool] = None,
3640
3650
  background: Optional[bool] = False,
3641
3651
  websocket: Optional[WebSocket] = None,
3652
+ background_tasks: Optional[Any] = None,
3642
3653
  ) -> AsyncIterator[WorkflowRunOutputEvent]: ...
3643
3654
 
3644
3655
  def arun( # type: ignore
@@ -3657,6 +3668,7 @@ class Workflow:
3657
3668
  stream_intermediate_steps: Optional[bool] = False,
3658
3669
  background: Optional[bool] = False,
3659
3670
  websocket: Optional[WebSocket] = None,
3671
+ background_tasks: Optional[Any] = None,
3660
3672
  **kwargs: Any,
3661
3673
  ) -> Union[WorkflowRunOutput, AsyncIterator[WorkflowRunOutputEvent]]:
3662
3674
  """Execute the workflow synchronously with optional streaming"""
@@ -3715,7 +3727,9 @@ class Workflow:
3715
3727
 
3716
3728
  self._set_debug()
3717
3729
 
3730
+ # Create a run_id for this specific run and register immediately for cancellation tracking
3718
3731
  run_id = str(uuid4())
3732
+ register_run(run_id)
3719
3733
 
3720
3734
  self.initialize_workflow()
3721
3735
  session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
@@ -3793,6 +3807,7 @@ class Workflow:
3793
3807
  files=files,
3794
3808
  session_state=session_state,
3795
3809
  run_context=run_context,
3810
+ background_tasks=background_tasks,
3796
3811
  **kwargs,
3797
3812
  )
3798
3813
  else:
@@ -3805,6 +3820,7 @@ class Workflow:
3805
3820
  files=files,
3806
3821
  session_state=session_state,
3807
3822
  run_context=run_context,
3823
+ background_tasks=background_tasks,
3808
3824
  **kwargs,
3809
3825
  )
3810
3826
 
@@ -4153,6 +4169,56 @@ class Workflow:
4153
4169
  if hasattr(member, "workflow_id"):
4154
4170
  member.workflow_id = self.id
4155
4171
 
4172
+ def propagate_run_hooks_in_background(self, run_in_background: bool = True) -> None:
4173
+ """
4174
+ Propagate _run_hooks_in_background setting to this workflow and all agents/teams in steps.
4175
+
4176
+ This method sets _run_hooks_in_background on the workflow and all agents/teams
4177
+ within its steps, including nested teams and their members.
4178
+
4179
+ Args:
4180
+ run_in_background: Whether hooks should run in background. Defaults to True.
4181
+ """
4182
+ self._run_hooks_in_background = run_in_background
4183
+
4184
+ if not self.steps or callable(self.steps):
4185
+ return
4186
+
4187
+ steps_list = self.steps.steps if isinstance(self.steps, Steps) else self.steps
4188
+
4189
+ for step in steps_list:
4190
+ self._propagate_hooks_to_step(step, run_in_background)
4191
+
4192
+ def _propagate_hooks_to_step(self, step: Any, run_in_background: bool) -> None:
4193
+ """Recursively propagate _run_hooks_in_background to a step and its nested content."""
4194
+ # Handle Step objects with active executor
4195
+ if hasattr(step, "active_executor") and step.active_executor:
4196
+ executor = step.active_executor
4197
+ # If it's a team, use its propagation method
4198
+ if hasattr(executor, "propagate_run_hooks_in_background"):
4199
+ executor.propagate_run_hooks_in_background(run_in_background)
4200
+ elif hasattr(executor, "_run_hooks_in_background"):
4201
+ executor._run_hooks_in_background = run_in_background
4202
+
4203
+ # Handle agent/team directly on step
4204
+ if hasattr(step, "agent") and step.agent:
4205
+ if hasattr(step.agent, "_run_hooks_in_background"):
4206
+ step.agent._run_hooks_in_background = run_in_background
4207
+ if hasattr(step, "team") and step.team:
4208
+ # Use team's method to propagate to all nested members
4209
+ if hasattr(step.team, "propagate_run_hooks_in_background"):
4210
+ step.team.propagate_run_hooks_in_background(run_in_background)
4211
+ elif hasattr(step.team, "_run_hooks_in_background"):
4212
+ step.team._run_hooks_in_background = run_in_background
4213
+
4214
+ # Handle nested primitives - check 'steps' and 'choices' attributes
4215
+ for attr_name in ["steps", "choices"]:
4216
+ if hasattr(step, attr_name):
4217
+ attr_value = getattr(step, attr_name)
4218
+ if attr_value and isinstance(attr_value, list):
4219
+ for nested_step in attr_value:
4220
+ self._propagate_hooks_to_step(nested_step, run_in_background)
4221
+
4156
4222
  ###########################################################################
4157
4223
  # Telemetry functions
4158
4224
  ###########################################################################
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agno
3
- Version: 2.3.3
3
+ Version: 2.3.5
4
4
  Summary: Agno: a lightweight library for building Multi-Agent Systems
5
5
  Author-email: Ashpreet Bedi <ashpreet@agno.com>
6
6
  Project-URL: homepage, https://agno.com
@@ -153,7 +153,7 @@ Requires-Dist: mcp>=1.9.2; extra == "mcp"
153
153
  Provides-Extra: mem0
154
154
  Requires-Dist: mem0ai; extra == "mem0"
155
155
  Provides-Extra: memori
156
- Requires-Dist: memorisdk; extra == "memori"
156
+ Requires-Dist: memorisdk==2.3.3; extra == "memori"
157
157
  Provides-Extra: newspaper
158
158
  Requires-Dist: newspaper4k; extra == "newspaper"
159
159
  Requires-Dist: lxml_html_clean; extra == "newspaper"