agno 2.2.10__py3-none-any.whl → 2.2.12__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 (73) hide show
  1. agno/agent/agent.py +75 -48
  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/mysql.py +1 -1
  9. agno/db/mysql/utils.py +1 -1
  10. agno/db/postgres/utils.py +1 -1
  11. agno/db/redis/utils.py +1 -1
  12. agno/db/singlestore/singlestore.py +1 -1
  13. agno/db/singlestore/utils.py +1 -1
  14. agno/db/sqlite/async_sqlite.py +1 -1
  15. agno/db/sqlite/sqlite.py +1 -1
  16. agno/db/sqlite/utils.py +1 -1
  17. agno/filters.py +354 -0
  18. agno/knowledge/chunking/agentic.py +8 -9
  19. agno/knowledge/chunking/strategy.py +59 -15
  20. agno/knowledge/embedder/sentence_transformer.py +6 -2
  21. agno/knowledge/knowledge.py +43 -22
  22. agno/knowledge/reader/base.py +6 -2
  23. agno/knowledge/utils.py +20 -0
  24. agno/models/anthropic/claude.py +45 -9
  25. agno/models/base.py +4 -0
  26. agno/os/app.py +23 -7
  27. agno/os/interfaces/slack/router.py +53 -33
  28. agno/os/interfaces/slack/slack.py +9 -1
  29. agno/os/router.py +25 -1
  30. agno/os/routers/health.py +5 -3
  31. agno/os/routers/knowledge/knowledge.py +43 -17
  32. agno/os/routers/knowledge/schemas.py +4 -3
  33. agno/run/agent.py +11 -1
  34. agno/run/base.py +3 -2
  35. agno/session/agent.py +10 -5
  36. agno/team/team.py +57 -18
  37. agno/tools/file_generation.py +4 -4
  38. agno/tools/gmail.py +179 -0
  39. agno/tools/parallel.py +314 -0
  40. agno/utils/agent.py +22 -17
  41. agno/utils/gemini.py +15 -5
  42. agno/utils/knowledge.py +12 -5
  43. agno/utils/log.py +1 -0
  44. agno/utils/models/claude.py +2 -1
  45. agno/utils/print_response/agent.py +5 -4
  46. agno/utils/print_response/team.py +5 -4
  47. agno/vectordb/base.py +2 -4
  48. agno/vectordb/cassandra/cassandra.py +12 -5
  49. agno/vectordb/chroma/chromadb.py +10 -4
  50. agno/vectordb/clickhouse/clickhousedb.py +12 -4
  51. agno/vectordb/couchbase/couchbase.py +12 -3
  52. agno/vectordb/lancedb/lance_db.py +69 -144
  53. agno/vectordb/langchaindb/langchaindb.py +13 -4
  54. agno/vectordb/lightrag/lightrag.py +8 -3
  55. agno/vectordb/llamaindex/llamaindexdb.py +10 -4
  56. agno/vectordb/milvus/milvus.py +16 -5
  57. agno/vectordb/mongodb/mongodb.py +14 -3
  58. agno/vectordb/pgvector/pgvector.py +73 -15
  59. agno/vectordb/pineconedb/pineconedb.py +6 -2
  60. agno/vectordb/qdrant/qdrant.py +25 -13
  61. agno/vectordb/redis/redisdb.py +37 -30
  62. agno/vectordb/singlestore/singlestore.py +9 -4
  63. agno/vectordb/surrealdb/surrealdb.py +13 -3
  64. agno/vectordb/upstashdb/upstashdb.py +8 -5
  65. agno/vectordb/weaviate/weaviate.py +29 -12
  66. agno/workflow/step.py +3 -2
  67. agno/workflow/types.py +20 -1
  68. agno/workflow/workflow.py +103 -14
  69. {agno-2.2.10.dist-info → agno-2.2.12.dist-info}/METADATA +4 -1
  70. {agno-2.2.10.dist-info → agno-2.2.12.dist-info}/RECORD +73 -71
  71. {agno-2.2.10.dist-info → agno-2.2.12.dist-info}/WHEEL +0 -0
  72. {agno-2.2.10.dist-info → agno-2.2.12.dist-info}/licenses/LICENSE +0 -0
  73. {agno-2.2.10.dist-info → agno-2.2.12.dist-info}/top_level.txt +0 -0
agno/workflow/workflow.py CHANGED
@@ -1136,7 +1136,11 @@ class Workflow:
1136
1136
  else:
1137
1137
  return len(self.steps)
1138
1138
 
1139
- def _aggregate_workflow_metrics(self, step_results: List[Union[StepOutput, List[StepOutput]]]) -> WorkflowMetrics:
1139
+ def _aggregate_workflow_metrics(
1140
+ self,
1141
+ step_results: List[Union[StepOutput, List[StepOutput]]],
1142
+ current_workflow_metrics: Optional[WorkflowMetrics] = None,
1143
+ ) -> WorkflowMetrics:
1140
1144
  """Aggregate metrics from all step responses into structured workflow metrics"""
1141
1145
  steps_dict = {}
1142
1146
 
@@ -1164,8 +1168,13 @@ class Workflow:
1164
1168
  for step_result in step_results:
1165
1169
  process_step_output(cast(StepOutput, step_result))
1166
1170
 
1171
+ duration = None
1172
+ if current_workflow_metrics and current_workflow_metrics.duration is not None:
1173
+ duration = current_workflow_metrics.duration
1174
+
1167
1175
  return WorkflowMetrics(
1168
1176
  steps=steps_dict,
1177
+ duration=duration,
1169
1178
  )
1170
1179
 
1171
1180
  def _call_custom_function(self, func: Callable, execution_input: WorkflowExecutionInput, **kwargs: Any) -> Any:
@@ -1316,7 +1325,14 @@ class Workflow:
1316
1325
 
1317
1326
  # Update the workflow_run_response with completion data
1318
1327
  if collected_step_outputs:
1319
- workflow_run_response.metrics = self._aggregate_workflow_metrics(collected_step_outputs)
1328
+ # Stop the timer for the Run duration
1329
+ if workflow_run_response.metrics:
1330
+ workflow_run_response.metrics.stop_timer()
1331
+
1332
+ workflow_run_response.metrics = self._aggregate_workflow_metrics(
1333
+ collected_step_outputs,
1334
+ workflow_run_response.metrics, # type: ignore[arg-type]
1335
+ )
1320
1336
  last_output = cast(StepOutput, collected_step_outputs[-1])
1321
1337
 
1322
1338
  # Use deepest nested content if this is a container (Steps/Router/Loop/etc.)
@@ -1361,6 +1377,10 @@ class Workflow:
1361
1377
  raise e
1362
1378
 
1363
1379
  finally:
1380
+ # Stop timer on error
1381
+ if workflow_run_response.metrics:
1382
+ workflow_run_response.metrics.stop_timer()
1383
+
1364
1384
  self._update_session_metrics(session=session, workflow_run_response=workflow_run_response)
1365
1385
  session.upsert_run(run=workflow_run_response)
1366
1386
  self.save_session(session=session)
@@ -1551,7 +1571,14 @@ class Workflow:
1551
1571
 
1552
1572
  # Update the workflow_run_response with completion data
1553
1573
  if collected_step_outputs:
1554
- workflow_run_response.metrics = self._aggregate_workflow_metrics(collected_step_outputs)
1574
+ # Stop the timer for the Run duration
1575
+ if workflow_run_response.metrics:
1576
+ workflow_run_response.metrics.stop_timer()
1577
+
1578
+ workflow_run_response.metrics = self._aggregate_workflow_metrics(
1579
+ collected_step_outputs,
1580
+ workflow_run_response.metrics, # type: ignore[arg-type]
1581
+ )
1555
1582
  last_output = cast(StepOutput, collected_step_outputs[-1])
1556
1583
 
1557
1584
  # Use deepest nested content if this is a container (Steps/Router/Loop/etc.)
@@ -1618,7 +1645,14 @@ class Workflow:
1618
1645
  # Preserve all progress (completed steps + partial step) before cancellation
1619
1646
  if collected_step_outputs:
1620
1647
  workflow_run_response.step_results = collected_step_outputs
1621
- workflow_run_response.metrics = self._aggregate_workflow_metrics(collected_step_outputs)
1648
+ # Stop the timer for the Run duration
1649
+ if workflow_run_response.metrics:
1650
+ workflow_run_response.metrics.stop_timer()
1651
+
1652
+ workflow_run_response.metrics = self._aggregate_workflow_metrics(
1653
+ collected_step_outputs,
1654
+ workflow_run_response.metrics, # type: ignore[arg-type]
1655
+ )
1622
1656
 
1623
1657
  cancelled_event = WorkflowCancelledEvent(
1624
1658
  run_id=workflow_run_response.run_id or "",
@@ -1660,6 +1694,10 @@ class Workflow:
1660
1694
  )
1661
1695
  yield self._handle_event(workflow_completed_event, workflow_run_response)
1662
1696
 
1697
+ # Stop timer on error
1698
+ if workflow_run_response.metrics:
1699
+ workflow_run_response.metrics.stop_timer()
1700
+
1663
1701
  # Store the completed workflow response
1664
1702
  self._update_session_metrics(session=session, workflow_run_response=workflow_run_response)
1665
1703
  session.upsert_run(run=workflow_run_response)
@@ -1863,7 +1901,14 @@ class Workflow:
1863
1901
 
1864
1902
  # Update the workflow_run_response with completion data
1865
1903
  if collected_step_outputs:
1866
- workflow_run_response.metrics = self._aggregate_workflow_metrics(collected_step_outputs)
1904
+ # Stop the timer for the Run duration
1905
+ if workflow_run_response.metrics:
1906
+ workflow_run_response.metrics.stop_timer()
1907
+
1908
+ workflow_run_response.metrics = self._aggregate_workflow_metrics(
1909
+ collected_step_outputs,
1910
+ workflow_run_response.metrics, # type: ignore[arg-type]
1911
+ )
1867
1912
  last_output = cast(StepOutput, collected_step_outputs[-1])
1868
1913
 
1869
1914
  # Use deepest nested content if this is a container (Steps/Router/Loop/etc.)
@@ -1903,6 +1948,10 @@ class Workflow:
1903
1948
  workflow_run_response.content = f"Workflow execution failed: {e}"
1904
1949
  raise e
1905
1950
 
1951
+ # Stop timer on error
1952
+ if workflow_run_response.metrics:
1953
+ workflow_run_response.metrics.stop_timer()
1954
+
1906
1955
  self._update_session_metrics(session=workflow_session, workflow_run_response=workflow_run_response)
1907
1956
  workflow_session.upsert_run(run=workflow_run_response)
1908
1957
  if self._has_async_db():
@@ -2114,7 +2163,14 @@ class Workflow:
2114
2163
 
2115
2164
  # Update the workflow_run_response with completion data
2116
2165
  if collected_step_outputs:
2117
- workflow_run_response.metrics = self._aggregate_workflow_metrics(collected_step_outputs)
2166
+ # Stop the timer for the Run duration
2167
+ if workflow_run_response.metrics:
2168
+ workflow_run_response.metrics.stop_timer()
2169
+
2170
+ workflow_run_response.metrics = self._aggregate_workflow_metrics(
2171
+ collected_step_outputs,
2172
+ workflow_run_response.metrics, # type: ignore[arg-type]
2173
+ )
2118
2174
  last_output = cast(StepOutput, collected_step_outputs[-1])
2119
2175
 
2120
2176
  # Use deepest nested content if this is a container (Steps/Router/Loop/etc.)
@@ -2181,7 +2237,14 @@ class Workflow:
2181
2237
  # Preserve all progress (completed steps + partial step) before cancellation
2182
2238
  if collected_step_outputs:
2183
2239
  workflow_run_response.step_results = collected_step_outputs
2184
- workflow_run_response.metrics = self._aggregate_workflow_metrics(collected_step_outputs)
2240
+ # Stop the timer for the Run duration
2241
+ if workflow_run_response.metrics:
2242
+ workflow_run_response.metrics.stop_timer()
2243
+
2244
+ workflow_run_response.metrics = self._aggregate_workflow_metrics(
2245
+ collected_step_outputs,
2246
+ workflow_run_response.metrics, # type: ignore[arg-type]
2247
+ )
2185
2248
 
2186
2249
  cancelled_event = WorkflowCancelledEvent(
2187
2250
  run_id=workflow_run_response.run_id or "",
@@ -2227,6 +2290,10 @@ class Workflow:
2227
2290
  )
2228
2291
  yield self._handle_event(workflow_completed_event, workflow_run_response, websocket_handler=websocket_handler)
2229
2292
 
2293
+ # Stop timer on error
2294
+ if workflow_run_response.metrics:
2295
+ workflow_run_response.metrics.stop_timer()
2296
+
2230
2297
  # Store the completed workflow response
2231
2298
  self._update_session_metrics(session=workflow_session, workflow_run_response=workflow_run_response)
2232
2299
  workflow_session.upsert_run(run=workflow_run_response)
@@ -2288,6 +2355,10 @@ class Workflow:
2288
2355
  status=RunStatus.pending,
2289
2356
  )
2290
2357
 
2358
+ # Start the run metrics timer
2359
+ workflow_run_response.metrics = WorkflowMetrics(steps={})
2360
+ workflow_run_response.metrics.start_timer()
2361
+
2291
2362
  # Store PENDING response immediately
2292
2363
  workflow_session.upsert_run(run=workflow_run_response)
2293
2364
  if self._has_async_db():
@@ -2402,6 +2473,10 @@ class Workflow:
2402
2473
  status=RunStatus.pending,
2403
2474
  )
2404
2475
 
2476
+ # Start the run metrics timer
2477
+ workflow_run_response.metrics = WorkflowMetrics(steps={})
2478
+ workflow_run_response.metrics.start_timer()
2479
+
2405
2480
  # Prepare execution input
2406
2481
  inputs = WorkflowExecutionInput(
2407
2482
  input=input,
@@ -2475,10 +2550,13 @@ class Workflow:
2475
2550
  # Return SAME object that will be updated by background execution
2476
2551
  return workflow_run_response
2477
2552
 
2478
- async def aget_run(self, run_id: str) -> Optional[WorkflowRunOutput]:
2553
+ async def aget_run(self, run_id: str, session_id: Optional[str] = None) -> Optional[WorkflowRunOutput]:
2479
2554
  """Get the status and details of a background workflow run - SIMPLIFIED"""
2480
- if self.db is not None and self.session_id is not None:
2481
- session = await self.db.aget_session(session_id=self.session_id, session_type=SessionType.WORKFLOW) # type: ignore
2555
+ # Use provided session_id or fall back to self.session_id
2556
+ _session_id = session_id if session_id is not None else self.session_id
2557
+
2558
+ if self.db is not None and _session_id is not None:
2559
+ session = await self.db.aget_session(session_id=_session_id, session_type=SessionType.WORKFLOW) # type: ignore
2482
2560
  if session and isinstance(session, WorkflowSession) and session.runs:
2483
2561
  # Find the run by ID
2484
2562
  for run in session.runs:
@@ -2487,10 +2565,13 @@ class Workflow:
2487
2565
 
2488
2566
  return None
2489
2567
 
2490
- def get_run(self, run_id: str) -> Optional[WorkflowRunOutput]:
2568
+ def get_run(self, run_id: str, session_id: Optional[str] = None) -> Optional[WorkflowRunOutput]:
2491
2569
  """Get the status and details of a background workflow run - SIMPLIFIED"""
2492
- if self.db is not None and self.session_id is not None:
2493
- session = self.db.get_session(session_id=self.session_id, session_type=SessionType.WORKFLOW)
2570
+ # Use provided session_id or fall back to self.session_id
2571
+ _session_id = session_id if session_id is not None else self.session_id
2572
+
2573
+ if self.db is not None and _session_id is not None:
2574
+ session = self.db.get_session(session_id=_session_id, session_type=SessionType.WORKFLOW)
2494
2575
  if session and isinstance(session, WorkflowSession) and session.runs:
2495
2576
  # Find the run by ID
2496
2577
  for run in session.runs:
@@ -3445,6 +3526,10 @@ class Workflow:
3445
3526
  created_at=int(datetime.now().timestamp()),
3446
3527
  )
3447
3528
 
3529
+ # Start the run metrics timer
3530
+ workflow_run_response.metrics = WorkflowMetrics(steps={})
3531
+ workflow_run_response.metrics.start_timer()
3532
+
3448
3533
  if stream:
3449
3534
  return self._execute_stream(
3450
3535
  session=workflow_session,
@@ -3632,6 +3717,10 @@ class Workflow:
3632
3717
  created_at=int(datetime.now().timestamp()),
3633
3718
  )
3634
3719
 
3720
+ # Start the run metrics timer
3721
+ workflow_run_response.metrics = WorkflowMetrics(steps={})
3722
+ workflow_run_response.metrics.start_timer()
3723
+
3635
3724
  if stream:
3636
3725
  return self._aexecute_stream( # type: ignore
3637
3726
  execution_input=inputs,
@@ -3978,7 +4067,7 @@ class Workflow:
3978
4067
 
3979
4068
  # If workflow has metrics, convert and add them to session metrics
3980
4069
  if workflow_run_response.metrics:
3981
- run_session_metrics = self._calculate_session_metrics_from_workflow_metrics(workflow_run_response.metrics)
4070
+ run_session_metrics = self._calculate_session_metrics_from_workflow_metrics(workflow_run_response.metrics) # type: ignore[arg-type]
3982
4071
 
3983
4072
  session_metrics += run_session_metrics
3984
4073
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agno
3
- Version: 2.2.10
3
+ Version: 2.2.12
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
@@ -165,6 +165,8 @@ Provides-Extra: notion
165
165
  Requires-Dist: notion-client; extra == "notion"
166
166
  Provides-Extra: opencv
167
167
  Requires-Dist: opencv-python; extra == "opencv"
168
+ Provides-Extra: parallel
169
+ Requires-Dist: parallel-web; extra == "parallel"
168
170
  Provides-Extra: psycopg
169
171
  Requires-Dist: psycopg-binary; extra == "psycopg"
170
172
  Requires-Dist: psycopg; extra == "psycopg"
@@ -316,6 +318,7 @@ Requires-Dist: agno[mcp]; extra == "tools"
316
318
  Requires-Dist: agno[browserbase]; extra == "tools"
317
319
  Requires-Dist: agno[agentql]; extra == "tools"
318
320
  Requires-Dist: agno[opencv]; extra == "tools"
321
+ Requires-Dist: agno[parallel]; extra == "tools"
319
322
  Requires-Dist: agno[scrapegraph]; extra == "tools"
320
323
  Requires-Dist: agno[valyu]; extra == "tools"
321
324
  Requires-Dist: agno[confluence]; extra == "tools"