agno 2.2.9__py3-none-any.whl → 2.2.11__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 (43) hide show
  1. agno/agent/agent.py +27 -5
  2. agno/db/dynamo/utils.py +1 -1
  3. agno/db/firestore/utils.py +1 -1
  4. agno/db/gcs_json/utils.py +1 -1
  5. agno/db/in_memory/utils.py +1 -1
  6. agno/db/json/utils.py +1 -1
  7. agno/db/mongo/utils.py +3 -3
  8. agno/db/mysql/utils.py +1 -1
  9. agno/db/postgres/utils.py +1 -1
  10. agno/db/redis/utils.py +1 -1
  11. agno/db/singlestore/utils.py +1 -1
  12. agno/db/sqlite/utils.py +1 -1
  13. agno/knowledge/chunking/agentic.py +8 -9
  14. agno/knowledge/chunking/strategy.py +59 -15
  15. agno/knowledge/embedder/sentence_transformer.py +6 -2
  16. agno/knowledge/reader/base.py +6 -2
  17. agno/knowledge/utils.py +20 -0
  18. agno/models/anthropic/claude.py +45 -9
  19. agno/models/base.py +4 -0
  20. agno/os/app.py +35 -19
  21. agno/os/routers/health.py +5 -3
  22. agno/os/routers/knowledge/knowledge.py +43 -17
  23. agno/os/routers/knowledge/schemas.py +4 -3
  24. agno/run/agent.py +11 -1
  25. agno/team/team.py +20 -3
  26. agno/tools/file_generation.py +4 -4
  27. agno/tools/gmail.py +179 -0
  28. agno/tools/parallel.py +314 -0
  29. agno/utils/models/claude.py +2 -1
  30. agno/workflow/agent.py +2 -2
  31. agno/workflow/condition.py +26 -4
  32. agno/workflow/loop.py +9 -0
  33. agno/workflow/parallel.py +39 -16
  34. agno/workflow/router.py +25 -4
  35. agno/workflow/step.py +163 -91
  36. agno/workflow/steps.py +9 -0
  37. agno/workflow/types.py +20 -1
  38. agno/workflow/workflow.py +117 -30
  39. {agno-2.2.9.dist-info → agno-2.2.11.dist-info}/METADATA +4 -1
  40. {agno-2.2.9.dist-info → agno-2.2.11.dist-info}/RECORD +43 -42
  41. {agno-2.2.9.dist-info → agno-2.2.11.dist-info}/WHEEL +0 -0
  42. {agno-2.2.9.dist-info → agno-2.2.11.dist-info}/licenses/LICENSE +0 -0
  43. {agno-2.2.9.dist-info → agno-2.2.11.dist-info}/top_level.txt +0 -0
agno/workflow/parallel.py CHANGED
@@ -7,6 +7,7 @@ from uuid import uuid4
7
7
 
8
8
  from agno.models.metrics import Metrics
9
9
  from agno.run.agent import RunOutputEvent
10
+ from agno.run.base import RunContext
10
11
  from agno.run.team import TeamRunOutputEvent
11
12
  from agno.run.workflow import (
12
13
  ParallelExecutionCompletedEvent,
@@ -200,6 +201,7 @@ class Parallel:
200
201
  user_id: Optional[str] = None,
201
202
  workflow_run_response: Optional[WorkflowRunOutput] = None,
202
203
  store_executor_outputs: bool = True,
204
+ run_context: Optional[RunContext] = None,
203
205
  session_state: Optional[Dict[str, Any]] = None,
204
206
  workflow_session: Optional[WorkflowSession] = None,
205
207
  add_workflow_history_to_steps: Optional[bool] = False,
@@ -214,10 +216,14 @@ class Parallel:
214
216
  # Create individual session_state copies for each step to prevent race conditions
215
217
  session_state_copies = []
216
218
  for _ in range(len(self.steps)):
217
- if session_state is not None:
218
- session_state_copies.append(deepcopy(session_state))
219
+ # If using run context, no need to deepcopy the state. We want the direct reference.
220
+ if run_context is not None and run_context.session_state is not None:
221
+ session_state_copies.append(run_context.session_state)
219
222
  else:
220
- session_state_copies.append({})
223
+ if session_state is not None:
224
+ session_state_copies.append(deepcopy(session_state))
225
+ else:
226
+ session_state_copies.append({})
221
227
 
222
228
  def execute_step_with_index(step_with_index):
223
229
  """Execute a single step and preserve its original index"""
@@ -235,6 +241,7 @@ class Parallel:
235
241
  workflow_session=workflow_session,
236
242
  add_workflow_history_to_steps=add_workflow_history_to_steps,
237
243
  num_history_runs=num_history_runs,
244
+ run_context=run_context,
238
245
  session_state=step_session_state,
239
246
  ) # type: ignore[union-attr]
240
247
  return idx, step_result, step_session_state
@@ -288,7 +295,7 @@ class Parallel:
288
295
  )
289
296
  )
290
297
 
291
- if session_state is not None:
298
+ if run_context is None and session_state is not None:
292
299
  merge_parallel_session_states(session_state, modified_session_states)
293
300
 
294
301
  # Sort by original index to preserve order
@@ -322,6 +329,7 @@ class Parallel:
322
329
  workflow_run_response: Optional[WorkflowRunOutput] = None,
323
330
  step_index: Optional[Union[int, tuple]] = None,
324
331
  store_executor_outputs: bool = True,
332
+ run_context: Optional[RunContext] = None,
325
333
  session_state: Optional[Dict[str, Any]] = None,
326
334
  parent_step_id: Optional[str] = None,
327
335
  workflow_session: Optional[WorkflowSession] = None,
@@ -338,10 +346,14 @@ class Parallel:
338
346
  # Create individual session_state copies for each step to prevent race conditions
339
347
  session_state_copies = []
340
348
  for _ in range(len(self.steps)):
341
- if session_state is not None:
342
- session_state_copies.append(deepcopy(session_state))
349
+ # If using run context, no need to deepcopy the state. We want the direct reference.
350
+ if run_context is not None and run_context.session_state is not None:
351
+ session_state_copies.append(run_context.session_state)
343
352
  else:
344
- session_state_copies.append({})
353
+ if session_state is not None:
354
+ session_state_copies.append(deepcopy(session_state))
355
+ else:
356
+ session_state_copies.append({})
345
357
 
346
358
  # Considering both stream_events and stream_intermediate_steps (deprecated)
347
359
  stream_events = stream_events or stream_intermediate_steps
@@ -468,7 +480,7 @@ class Parallel:
468
480
  logger.error(f"Future completion error: {e}")
469
481
 
470
482
  # Merge all session_state changes back into the original session_state
471
- if session_state is not None:
483
+ if run_context is None and session_state is not None:
472
484
  merge_parallel_session_states(session_state, modified_session_states)
473
485
 
474
486
  # Flatten step_results - handle steps that return List[StepOutput] (like Condition/Loop)
@@ -509,6 +521,7 @@ class Parallel:
509
521
  user_id: Optional[str] = None,
510
522
  workflow_run_response: Optional[WorkflowRunOutput] = None,
511
523
  store_executor_outputs: bool = True,
524
+ run_context: Optional[RunContext] = None,
512
525
  session_state: Optional[Dict[str, Any]] = None,
513
526
  workflow_session: Optional[WorkflowSession] = None,
514
527
  add_workflow_history_to_steps: Optional[bool] = False,
@@ -523,10 +536,14 @@ class Parallel:
523
536
  # Create individual session_state copies for each step to prevent race conditions
524
537
  session_state_copies = []
525
538
  for _ in range(len(self.steps)):
526
- if session_state is not None:
527
- session_state_copies.append(deepcopy(session_state))
539
+ # If using run context, no need to deepcopy the state. We want the direct reference.
540
+ if run_context is not None and run_context.session_state is not None:
541
+ session_state_copies.append(run_context.session_state)
528
542
  else:
529
- session_state_copies.append({})
543
+ if session_state is not None:
544
+ session_state_copies.append(deepcopy(session_state))
545
+ else:
546
+ session_state_copies.append({})
530
547
 
531
548
  async def execute_step_async_with_index(step_with_index):
532
549
  """Execute a single step asynchronously and preserve its original index"""
@@ -598,7 +615,7 @@ class Parallel:
598
615
  log_debug(f"Parallel step {step_name} completed")
599
616
 
600
617
  # Smart merge all session_state changes back into the original session_state
601
- if session_state is not None:
618
+ if run_context is None and session_state is not None:
602
619
  merge_parallel_session_states(session_state, modified_session_states)
603
620
 
604
621
  # Sort by original index to preserve order
@@ -632,6 +649,7 @@ class Parallel:
632
649
  workflow_run_response: Optional[WorkflowRunOutput] = None,
633
650
  step_index: Optional[Union[int, tuple]] = None,
634
651
  store_executor_outputs: bool = True,
652
+ run_context: Optional[RunContext] = None,
635
653
  session_state: Optional[Dict[str, Any]] = None,
636
654
  parent_step_id: Optional[str] = None,
637
655
  workflow_session: Optional[WorkflowSession] = None,
@@ -648,10 +666,14 @@ class Parallel:
648
666
  # Create individual session_state copies for each step to prevent race conditions
649
667
  session_state_copies = []
650
668
  for _ in range(len(self.steps)):
651
- if session_state is not None:
652
- session_state_copies.append(deepcopy(session_state))
669
+ # If using run context, no need to deepcopy the state. We want the direct reference.
670
+ if run_context is not None and run_context.session_state is not None:
671
+ session_state_copies.append(run_context.session_state)
653
672
  else:
654
- session_state_copies.append({})
673
+ if session_state is not None:
674
+ session_state_copies.append(deepcopy(session_state))
675
+ else:
676
+ session_state_copies.append({})
655
677
 
656
678
  # Considering both stream_events and stream_intermediate_steps (deprecated)
657
679
  stream_events = stream_events or stream_intermediate_steps
@@ -705,6 +727,7 @@ class Parallel:
705
727
  step_index=sub_step_index,
706
728
  store_executor_outputs=store_executor_outputs,
707
729
  session_state=step_session_state,
730
+ run_context=run_context,
708
731
  parent_step_id=parallel_step_id,
709
732
  workflow_session=workflow_session,
710
733
  add_workflow_history_to_steps=add_workflow_history_to_steps,
@@ -766,7 +789,7 @@ class Parallel:
766
789
  await asyncio.gather(*tasks, return_exceptions=True)
767
790
 
768
791
  # Merge all session_state changes back into the original session_state
769
- if session_state is not None:
792
+ if run_context is None and session_state is not None:
770
793
  merge_parallel_session_states(session_state, modified_session_states)
771
794
 
772
795
  # Flatten step_results - handle steps that return List[StepOutput] (like Condition/Loop)
agno/workflow/router.py CHANGED
@@ -4,6 +4,7 @@ from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List
4
4
  from uuid import uuid4
5
5
 
6
6
  from agno.run.agent import RunOutputEvent
7
+ from agno.run.base import RunContext
7
8
  from agno.run.team import TeamRunOutputEvent
8
9
  from agno.run.workflow import (
9
10
  RouterExecutionCompletedEvent,
@@ -171,6 +172,7 @@ class Router:
171
172
  session_id: Optional[str] = None,
172
173
  user_id: Optional[str] = None,
173
174
  workflow_run_response: Optional[WorkflowRunOutput] = None,
175
+ run_context: Optional[RunContext] = None,
174
176
  session_state: Optional[Dict[str, Any]] = None,
175
177
  store_executor_outputs: bool = True,
176
178
  workflow_session: Optional[WorkflowSession] = None,
@@ -185,7 +187,10 @@ class Router:
185
187
  self._prepare_steps()
186
188
 
187
189
  # Route to appropriate steps
188
- steps_to_execute = self._route_steps(step_input, session_state)
190
+ if run_context is not None and run_context.session_state is not None:
191
+ steps_to_execute = self._route_steps(step_input, session_state=run_context.session_state)
192
+ else:
193
+ steps_to_execute = self._route_steps(step_input, session_state=session_state)
189
194
  log_debug(f"Router {self.name}: Selected {len(steps_to_execute)} steps to execute")
190
195
 
191
196
  if not steps_to_execute:
@@ -209,6 +214,7 @@ class Router:
209
214
  user_id=user_id,
210
215
  workflow_run_response=workflow_run_response,
211
216
  store_executor_outputs=store_executor_outputs,
217
+ run_context=run_context,
212
218
  session_state=session_state,
213
219
  workflow_session=workflow_session,
214
220
  add_workflow_history_to_steps=add_workflow_history_to_steps,
@@ -266,6 +272,7 @@ class Router:
266
272
  step_input: StepInput,
267
273
  session_id: Optional[str] = None,
268
274
  user_id: Optional[str] = None,
275
+ run_context: Optional[RunContext] = None,
269
276
  session_state: Optional[Dict[str, Any]] = None,
270
277
  stream_events: bool = False,
271
278
  stream_intermediate_steps: bool = False,
@@ -286,7 +293,10 @@ class Router:
286
293
  router_step_id = str(uuid4())
287
294
 
288
295
  # Route to appropriate steps
289
- steps_to_execute = self._route_steps(step_input, session_state)
296
+ if run_context is not None and run_context.session_state is not None:
297
+ steps_to_execute = self._route_steps(step_input, session_state=run_context.session_state)
298
+ else:
299
+ steps_to_execute = self._route_steps(step_input, session_state=session_state)
290
300
  log_debug(f"Router {self.name}: Selected {len(steps_to_execute)} steps to execute")
291
301
 
292
302
  # Considering both stream_events and stream_intermediate_steps (deprecated)
@@ -341,6 +351,7 @@ class Router:
341
351
  workflow_run_response=workflow_run_response,
342
352
  step_index=step_index,
343
353
  store_executor_outputs=store_executor_outputs,
354
+ run_context=run_context,
344
355
  session_state=session_state,
345
356
  parent_step_id=router_step_id,
346
357
  workflow_session=workflow_session,
@@ -425,6 +436,7 @@ class Router:
425
436
  session_id: Optional[str] = None,
426
437
  user_id: Optional[str] = None,
427
438
  workflow_run_response: Optional[WorkflowRunOutput] = None,
439
+ run_context: Optional[RunContext] = None,
428
440
  session_state: Optional[Dict[str, Any]] = None,
429
441
  store_executor_outputs: bool = True,
430
442
  workflow_session: Optional[WorkflowSession] = None,
@@ -439,7 +451,10 @@ class Router:
439
451
  self._prepare_steps()
440
452
 
441
453
  # Route to appropriate steps
442
- steps_to_execute = await self._aroute_steps(step_input, session_state)
454
+ if run_context is not None and run_context.session_state is not None:
455
+ steps_to_execute = await self._aroute_steps(step_input, session_state=run_context.session_state)
456
+ else:
457
+ steps_to_execute = await self._aroute_steps(step_input, session_state=session_state)
443
458
  log_debug(f"Router {self.name} selected: {len(steps_to_execute)} steps to execute")
444
459
 
445
460
  if not steps_to_execute:
@@ -464,6 +479,7 @@ class Router:
464
479
  user_id=user_id,
465
480
  workflow_run_response=workflow_run_response,
466
481
  store_executor_outputs=store_executor_outputs,
482
+ run_context=run_context,
467
483
  session_state=session_state,
468
484
  workflow_session=workflow_session,
469
485
  add_workflow_history_to_steps=add_workflow_history_to_steps,
@@ -523,6 +539,7 @@ class Router:
523
539
  step_input: StepInput,
524
540
  session_id: Optional[str] = None,
525
541
  user_id: Optional[str] = None,
542
+ run_context: Optional[RunContext] = None,
526
543
  session_state: Optional[Dict[str, Any]] = None,
527
544
  stream_events: bool = False,
528
545
  stream_intermediate_steps: bool = False,
@@ -543,7 +560,10 @@ class Router:
543
560
  router_step_id = str(uuid4())
544
561
 
545
562
  # Route to appropriate steps
546
- steps_to_execute = await self._aroute_steps(step_input, session_state)
563
+ if run_context is not None and run_context.session_state is not None:
564
+ steps_to_execute = await self._aroute_steps(step_input, session_state=run_context.session_state)
565
+ else:
566
+ steps_to_execute = await self._aroute_steps(step_input, session_state=session_state)
547
567
  log_debug(f"Router {self.name} selected: {len(steps_to_execute)} steps to execute")
548
568
 
549
569
  # Considering both stream_events and stream_intermediate_steps (deprecated)
@@ -600,6 +620,7 @@ class Router:
600
620
  workflow_run_response=workflow_run_response,
601
621
  step_index=step_index,
602
622
  store_executor_outputs=store_executor_outputs,
623
+ run_context=run_context,
603
624
  session_state=session_state,
604
625
  parent_step_id=router_step_id,
605
626
  workflow_session=workflow_session,