agno 2.1.7__py3-none-any.whl → 2.1.9__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.
agno/team/team.py CHANGED
@@ -862,6 +862,9 @@ class Team:
862
862
  run_response: TeamRunOutput,
863
863
  run_input: TeamRunInput,
864
864
  session: TeamSession,
865
+ session_state: Optional[Dict[str, Any]] = None,
866
+ dependencies: Optional[Dict[str, Any]] = None,
867
+ metadata: Optional[Dict[str, Any]] = None,
865
868
  user_id: Optional[str] = None,
866
869
  debug_mode: Optional[bool] = None,
867
870
  **kwargs: Any,
@@ -876,6 +879,9 @@ class Team:
876
879
  "team": self,
877
880
  "session": session,
878
881
  "user_id": user_id,
882
+ "metadata": metadata,
883
+ "session_state": session_state,
884
+ "dependencies": dependencies,
879
885
  "debug_mode": debug_mode or self.debug_mode,
880
886
  }
881
887
  all_args.update(kwargs)
@@ -918,6 +924,9 @@ class Team:
918
924
  run_response: TeamRunOutput,
919
925
  run_input: TeamRunInput,
920
926
  session: TeamSession,
927
+ session_state: Optional[Dict[str, Any]] = None,
928
+ dependencies: Optional[Dict[str, Any]] = None,
929
+ metadata: Optional[Dict[str, Any]] = None,
921
930
  user_id: Optional[str] = None,
922
931
  debug_mode: Optional[bool] = None,
923
932
  **kwargs: Any,
@@ -932,6 +941,9 @@ class Team:
932
941
  "team": self,
933
942
  "session": session,
934
943
  "user_id": user_id,
944
+ "session_state": session_state,
945
+ "dependencies": dependencies,
946
+ "metadata": metadata,
935
947
  "debug_mode": debug_mode or self.debug_mode,
936
948
  }
937
949
  all_args.update(kwargs)
@@ -977,6 +989,9 @@ class Team:
977
989
  hooks: Optional[List[Callable[..., Any]]],
978
990
  run_output: TeamRunOutput,
979
991
  session: TeamSession,
992
+ session_state: Optional[Dict[str, Any]] = None,
993
+ dependencies: Optional[Dict[str, Any]] = None,
994
+ metadata: Optional[Dict[str, Any]] = None,
980
995
  user_id: Optional[str] = None,
981
996
  debug_mode: Optional[bool] = None,
982
997
  **kwargs: Any,
@@ -991,6 +1006,9 @@ class Team:
991
1006
  "team": self,
992
1007
  "session": session,
993
1008
  "user_id": user_id,
1009
+ "session_state": session_state,
1010
+ "dependencies": dependencies,
1011
+ "metadata": metadata,
994
1012
  "debug_mode": debug_mode or self.debug_mode,
995
1013
  }
996
1014
  all_args.update(kwargs)
@@ -1014,6 +1032,9 @@ class Team:
1014
1032
  run_output: TeamRunOutput,
1015
1033
  session: TeamSession,
1016
1034
  user_id: Optional[str] = None,
1035
+ session_state: Optional[Dict[str, Any]] = None,
1036
+ dependencies: Optional[Dict[str, Any]] = None,
1037
+ metadata: Optional[Dict[str, Any]] = None,
1017
1038
  debug_mode: Optional[bool] = None,
1018
1039
  **kwargs: Any,
1019
1040
  ) -> None:
@@ -1027,6 +1048,9 @@ class Team:
1027
1048
  "team": self,
1028
1049
  "session": session,
1029
1050
  "user_id": user_id,
1051
+ "session_state": session_state,
1052
+ "dependencies": dependencies,
1053
+ "metadata": metadata,
1030
1054
  "debug_mode": debug_mode or self.debug_mode,
1031
1055
  }
1032
1056
  all_args.update(kwargs)
@@ -1091,6 +1115,9 @@ class Team:
1091
1115
  run_response=run_response,
1092
1116
  run_input=run_input,
1093
1117
  session=session,
1118
+ session_state=session_state,
1119
+ dependencies=dependencies,
1120
+ metadata=metadata,
1094
1121
  user_id=user_id,
1095
1122
  debug_mode=debug_mode,
1096
1123
  **kwargs,
@@ -1182,26 +1209,29 @@ class Team:
1182
1209
  else:
1183
1210
  self._scrub_media_from_run_output(run_response)
1184
1211
 
1185
- run_response.status = RunStatus.completed
1186
-
1187
1212
  # Parse team response model
1188
1213
  self._convert_response_to_structured_format(run_response=run_response)
1189
1214
 
1190
- # Set the run duration
1191
- if run_response.metrics:
1192
- run_response.metrics.stop_timer()
1193
-
1194
1215
  # 6. Execute post-hooks after output is generated but before response is returned
1195
1216
  if self.post_hooks is not None:
1196
1217
  self._execute_post_hooks(
1197
1218
  hooks=self.post_hooks, # type: ignore
1198
1219
  run_output=run_response,
1199
1220
  session=session,
1221
+ session_state=session_state,
1222
+ dependencies=dependencies,
1223
+ metadata=metadata,
1200
1224
  user_id=user_id,
1201
1225
  debug_mode=debug_mode,
1202
1226
  **kwargs,
1203
1227
  )
1204
1228
 
1229
+ run_response.status = RunStatus.completed
1230
+
1231
+ # Set the run duration
1232
+ if run_response.metrics:
1233
+ run_response.metrics.stop_timer()
1234
+
1205
1235
  # 7. Add the RunOutput to Team Session
1206
1236
  session.upsert_run(run_response=run_response)
1207
1237
 
@@ -1278,6 +1308,9 @@ class Team:
1278
1308
  run_response=run_response,
1279
1309
  run_input=run_input,
1280
1310
  session=session,
1311
+ session_state=session_state,
1312
+ dependencies=dependencies,
1313
+ metadata=metadata,
1281
1314
  user_id=user_id,
1282
1315
  debug_mode=debug_mode,
1283
1316
  **kwargs,
@@ -1396,14 +1429,25 @@ class Team:
1396
1429
  session=session, run_response=run_response, stream_intermediate_steps=stream_intermediate_steps
1397
1430
  )
1398
1431
 
1399
- run_response.status = RunStatus.completed
1432
+ # Execute post-hooks after output is generated but before response is returned
1433
+ if self.post_hooks is not None:
1434
+ self._execute_post_hooks(
1435
+ hooks=self.post_hooks, # type: ignore
1436
+ run_output=run_response,
1437
+ session_state=session_state,
1438
+ dependencies=dependencies,
1439
+ metadata=metadata,
1440
+ session=session,
1441
+ user_id=user_id,
1442
+ debug_mode=debug_mode,
1443
+ **kwargs,
1444
+ )
1400
1445
 
1446
+ run_response.status = RunStatus.completed
1401
1447
  # Set the run duration
1402
1448
  if run_response.metrics:
1403
1449
  run_response.metrics.stop_timer()
1404
1450
 
1405
- # TODO: For now we don't run post-hooks during streaming
1406
-
1407
1451
  # 5. Add the run to Team Session
1408
1452
  session.upsert_run(run_response=run_response)
1409
1453
 
@@ -1630,11 +1674,11 @@ class Team:
1630
1674
  )
1631
1675
  self.model = cast(Model, self.model)
1632
1676
 
1633
- if metadata is not None:
1634
- if self.metadata is not None:
1635
- merge_dictionaries(metadata, self.metadata)
1636
- else:
1677
+ if self.metadata is not None:
1678
+ if metadata is None:
1637
1679
  metadata = self.metadata
1680
+ else:
1681
+ merge_dictionaries(metadata, self.metadata)
1638
1682
 
1639
1683
  # Create a new run_response for this attempt
1640
1684
  run_response = TeamRunOutput(
@@ -1799,6 +1843,9 @@ class Team:
1799
1843
 
1800
1844
  register_run(run_response.run_id) # type: ignore
1801
1845
 
1846
+ if dependencies is not None:
1847
+ await self._aresolve_run_dependencies(dependencies=dependencies)
1848
+
1802
1849
  # 1. Read or create session. Reads from the database if provided.
1803
1850
  if self._has_async_db():
1804
1851
  team_session = await self._aread_or_create_session(session_id=session_id, user_id=user_id)
@@ -1820,6 +1867,9 @@ class Team:
1820
1867
  session=team_session,
1821
1868
  user_id=user_id,
1822
1869
  debug_mode=debug_mode,
1870
+ session_state=session_state,
1871
+ dependencies=dependencies,
1872
+ metadata=metadata,
1823
1873
  **kwargs,
1824
1874
  )
1825
1875
 
@@ -1907,21 +1957,35 @@ class Team:
1907
1957
  else:
1908
1958
  self._scrub_media_from_run_output(run_response)
1909
1959
 
1910
- # 9. Add the run to memory
1911
- team_session.upsert_run(run_response=run_response)
1960
+ # 11. Parse team response model
1961
+ self._convert_response_to_structured_format(run_response=run_response)
1912
1962
 
1913
- # 10. Calculate session metrics
1914
- self._update_session_metrics(session=team_session)
1963
+ # Execute post-hooks after output is generated but before response is returned
1964
+ if self.post_hooks is not None:
1965
+ await self._aexecute_post_hooks(
1966
+ hooks=self.post_hooks, # type: ignore
1967
+ run_output=run_response,
1968
+ session=team_session,
1969
+ user_id=user_id,
1970
+ debug_mode=debug_mode,
1971
+ session_state=session_state,
1972
+ dependencies=dependencies,
1973
+ metadata=metadata,
1974
+ **kwargs,
1975
+ )
1915
1976
 
1916
1977
  run_response.status = RunStatus.completed
1917
1978
 
1918
- # 11. Parse team response model
1919
- self._convert_response_to_structured_format(run_response=run_response)
1920
-
1921
1979
  # Set the run duration
1922
1980
  if run_response.metrics:
1923
1981
  run_response.metrics.stop_timer()
1924
1982
 
1983
+ # 9. Add the run to memory
1984
+ team_session.upsert_run(run_response=run_response)
1985
+
1986
+ # 10. Calculate session metrics
1987
+ self._update_session_metrics(session=team_session)
1988
+
1925
1989
  # 12. Update Team Memory
1926
1990
  async for _ in self._amake_memories_and_summaries(
1927
1991
  run_response=run_response,
@@ -1944,17 +2008,6 @@ class Team:
1944
2008
  # Log Team Telemetry
1945
2009
  await self._alog_team_telemetry(session_id=team_session.session_id, run_id=run_response.run_id)
1946
2010
 
1947
- # 15. Execute post-hooks after output is generated but before response is returned
1948
- if self.post_hooks is not None:
1949
- await self._aexecute_post_hooks(
1950
- hooks=self.post_hooks, # type: ignore
1951
- run_output=run_response,
1952
- session=team_session,
1953
- user_id=user_id,
1954
- debug_mode=debug_mode,
1955
- **kwargs,
1956
- )
1957
-
1958
2011
  log_debug(f"Team Run End: {run_response.run_id}", center=True, symbol="*")
1959
2012
 
1960
2013
  cleanup_run(run_response.run_id) # type: ignore
@@ -2005,7 +2058,7 @@ class Team:
2005
2058
 
2006
2059
  # 1. Resolve dependencies
2007
2060
  if dependencies is not None:
2008
- self._resolve_run_dependencies(dependencies=dependencies)
2061
+ await self._aresolve_run_dependencies(dependencies=dependencies)
2009
2062
 
2010
2063
  # 2. Read or create session. Reads from the database if provided.
2011
2064
  if self._has_async_db():
@@ -2028,6 +2081,9 @@ class Team:
2028
2081
  session=team_session,
2029
2082
  user_id=user_id,
2030
2083
  debug_mode=debug_mode,
2084
+ session_state=session_state,
2085
+ dependencies=dependencies,
2086
+ metadata=metadata,
2031
2087
  **kwargs,
2032
2088
  )
2033
2089
  async for pre_hook_event in pre_hook_iterator:
@@ -2143,6 +2199,24 @@ class Team:
2143
2199
  ):
2144
2200
  yield event
2145
2201
 
2202
+ # Execute post-hooks after output is generated but before response is returned
2203
+ if self.post_hooks is not None:
2204
+ await self._aexecute_post_hooks(
2205
+ hooks=self.post_hooks, # type: ignore
2206
+ run_output=run_response,
2207
+ session_state=session_state,
2208
+ dependencies=dependencies,
2209
+ metadata=metadata,
2210
+ session=team_session,
2211
+ user_id=user_id,
2212
+ debug_mode=debug_mode,
2213
+ **kwargs,
2214
+ )
2215
+
2216
+ # Set the run duration
2217
+ if run_response.metrics:
2218
+ run_response.metrics.stop_timer()
2219
+
2146
2220
  run_response.status = RunStatus.completed
2147
2221
 
2148
2222
  # 10. Add the run to memory
@@ -2353,12 +2427,12 @@ class Team:
2353
2427
  )
2354
2428
 
2355
2429
  self.model = cast(Model, self.model)
2356
-
2357
- if metadata is not None:
2358
- if self.metadata is not None:
2359
- merge_dictionaries(metadata, self.metadata)
2360
- else:
2430
+
2431
+ if self.metadata is not None:
2432
+ if metadata is None:
2361
2433
  metadata = self.metadata
2434
+ else:
2435
+ merge_dictionaries(metadata, self.metadata)
2362
2436
 
2363
2437
  # Get knowledge filters
2364
2438
  effective_filters = knowledge_filters
@@ -4,7 +4,7 @@ import uuid
4
4
  from functools import wraps
5
5
  from os import getenv
6
6
  from pathlib import Path
7
- from typing import Any, Dict, List, Optional
7
+ from typing import Any, Dict, List, Optional, cast
8
8
 
9
9
  from agno.tools import Toolkit
10
10
  from agno.utils.log import log_debug, log_error, log_info
@@ -164,8 +164,10 @@ class GoogleCalendarTools(Toolkit):
164
164
  )
165
165
 
166
166
  try:
167
+ service = cast(Resource, self.service)
168
+
167
169
  events_result = (
168
- self.service.events() # type: ignore
170
+ service.events()
169
171
  .list(
170
172
  calendarId=self.calendar_id,
171
173
  timeMin=start_date,
@@ -194,6 +196,7 @@ class GoogleCalendarTools(Toolkit):
194
196
  timezone: Optional[str] = "UTC",
195
197
  attendees: Optional[List[str]] = None,
196
198
  add_google_meet_link: Optional[bool] = False,
199
+ notify_attendees: Optional[bool] = False,
197
200
  ) -> str:
198
201
  """
199
202
  Create a new event in the Google Calendar.
@@ -207,6 +210,7 @@ class GoogleCalendarTools(Toolkit):
207
210
  timezone (Optional[str]): Timezone for the event (default: UTC)
208
211
  attendees (Optional[List[str]]): List of email addresses of the attendees
209
212
  add_google_meet_link (Optional[bool]): Whether to add a Google Meet video link to the event
213
+ notify_attendees (Optional[bool]): Whether to send email notifications to attendees (default: False)
210
214
 
211
215
  Returns:
212
216
  str: JSON string containing the created Google Calendar event or error message
@@ -241,12 +245,18 @@ class GoogleCalendarTools(Toolkit):
241
245
  # Remove None values
242
246
  event = {k: v for k, v in event.items() if v is not None}
243
247
 
248
+ # Determine sendUpdates value based on notify_attendees parameter
249
+ send_updates = "all" if notify_attendees and attendees else "none"
250
+
251
+ service = cast(Resource, self.service)
252
+
244
253
  event_result = (
245
- self.service.events() # type: ignore
254
+ service.events()
246
255
  .insert(
247
256
  calendarId=self.calendar_id,
248
257
  body=event,
249
258
  conferenceDataVersion=1 if add_google_meet_link else 0,
259
+ sendUpdates=send_updates,
250
260
  )
251
261
  .execute()
252
262
  )
@@ -267,6 +277,7 @@ class GoogleCalendarTools(Toolkit):
267
277
  end_date: Optional[str] = None,
268
278
  timezone: Optional[str] = None,
269
279
  attendees: Optional[List[str]] = None,
280
+ notify_attendees: Optional[bool] = False,
270
281
  ) -> str:
271
282
  """
272
283
  Update an existing event in the Google Calendar.
@@ -280,13 +291,16 @@ class GoogleCalendarTools(Toolkit):
280
291
  end_date (Optional[str]): New end date and time in ISO format (YYYY-MM-DDTHH:MM:SS)
281
292
  timezone (Optional[str]): New timezone for the event
282
293
  attendees (Optional[List[str]]): Updated list of attendee email addresses
294
+ notify_attendees (Optional[bool]): Whether to send email notifications to attendees (default: False)
283
295
 
284
296
  Returns:
285
297
  str: JSON string containing the updated Google Calendar event or error message
286
298
  """
287
299
  try:
300
+ service = cast(Resource, self.service)
301
+
288
302
  # First get the existing event to preserve its structure
289
- event = self.service.events().get(calendarId=self.calendar_id, eventId=event_id).execute() # type: ignore
303
+ event = service.events().get(calendarId=self.calendar_id, eventId=event_id).execute()
290
304
 
291
305
  # Update only the fields that are provided
292
306
  if title is not None:
@@ -317,9 +331,15 @@ class GoogleCalendarTools(Toolkit):
317
331
  except ValueError:
318
332
  return json.dumps({"error": f"Invalid end datetime format: {end_date}. Use ISO format."})
319
333
 
334
+ # Determine sendUpdates value based on notify_attendees parameter
335
+ send_updates = "all" if notify_attendees and attendees else "none"
336
+
320
337
  # Update the event
338
+
321
339
  updated_event = (
322
- self.service.events().update(calendarId=self.calendar_id, eventId=event_id, body=event).execute() # type: ignore
340
+ service.events()
341
+ .update(calendarId=self.calendar_id, eventId=event_id, body=event, sendUpdates=send_updates)
342
+ .execute()
323
343
  )
324
344
 
325
345
  log_debug(f"Event {event_id} updated successfully.")
@@ -329,18 +349,24 @@ class GoogleCalendarTools(Toolkit):
329
349
  return json.dumps({"error": f"An error occurred: {error}"})
330
350
 
331
351
  @authenticate
332
- def delete_event(self, event_id: str) -> str:
352
+ def delete_event(self, event_id: str, notify_attendees: Optional[bool] = True) -> str:
333
353
  """
334
354
  Delete an event from the Google Calendar.
335
355
 
336
356
  Args:
337
357
  event_id (str): ID of the event to delete
358
+ notify_attendees (Optional[bool]): Whether to send email notifications to attendees (default: False)
338
359
 
339
360
  Returns:
340
361
  str: JSON string containing success message or error message
341
362
  """
342
363
  try:
343
- self.service.events().delete(calendarId=self.calendar_id, eventId=event_id).execute() # type: ignore
364
+ # Determine sendUpdates value based on notify_attendees parameter
365
+ send_updates = "all" if notify_attendees else "none"
366
+
367
+ service = cast(Resource, self.service)
368
+
369
+ service.events().delete(calendarId=self.calendar_id, eventId=event_id, sendUpdates=send_updates).execute()
344
370
 
345
371
  log_debug(f"Event {event_id} deleted successfully.")
346
372
  return json.dumps({"success": True, "message": f"Event {event_id} deleted successfully."})
@@ -366,6 +392,8 @@ class GoogleCalendarTools(Toolkit):
366
392
  str: JSON string containing all Google Calendar events or error message
367
393
  """
368
394
  try:
395
+ service = cast(Resource, self.service)
396
+
369
397
  params = {
370
398
  "calendarId": self.calendar_id,
371
399
  "maxResults": min(max_results, 100),
@@ -412,7 +440,7 @@ class GoogleCalendarTools(Toolkit):
412
440
  if page_token:
413
441
  params["pageToken"] = page_token
414
442
 
415
- events_result = self.service.events().list(**params).execute() # type: ignore
443
+ events_result = service.events().list(**params).execute()
416
444
  all_events.extend(events_result.get("items", []))
417
445
 
418
446
  page_token = events_result.get("nextPageToken")
agno/tools/jira.py CHANGED
@@ -22,6 +22,7 @@ class JiraTools(Toolkit):
22
22
  enable_create_issue: bool = True,
23
23
  enable_search_issues: bool = True,
24
24
  enable_add_comment: bool = True,
25
+ enable_add_worklog: bool = True,
25
26
  all: bool = False,
26
27
  **kwargs,
27
28
  ):
@@ -55,6 +56,8 @@ class JiraTools(Toolkit):
55
56
  tools.append(self.search_issues)
56
57
  if enable_add_comment or all:
57
58
  tools.append(self.add_comment)
59
+ if enable_add_worklog or all:
60
+ tools.append(self.add_worklog)
58
61
 
59
62
  super().__init__(name="jira_tools", tools=tools, **kwargs)
60
63
 
@@ -148,3 +151,20 @@ class JiraTools(Toolkit):
148
151
  except Exception as e:
149
152
  logger.error(f"Error adding comment to issue {issue_key}: {e}")
150
153
  return json.dumps({"error": str(e)})
154
+
155
+ def add_worklog(self, issue_key: str, time_spent: str, comment: Optional[str] = None) -> str:
156
+ """
157
+ Adds a worklog entry to log time spent on a specific Jira issue.
158
+
159
+ :param issue_key: The key of the issue to log work against (e.g., 'PROJ-123').
160
+ :param time_spent: The amount of time spent. Use Jira's format, e.g., '2h', '30m', '1d 4h'.
161
+ :param comment: An optional comment describing the work done.
162
+ :return: A JSON string indicating success or containing an error message.
163
+ """
164
+ try:
165
+ self.jira.add_worklog(issue=issue_key, timeSpent=time_spent, comment=comment)
166
+ log_debug(f"Worklog of '{time_spent}' added to issue {issue_key}")
167
+ return json.dumps({"status": "success", "issue_key": issue_key, "time_spent": time_spent})
168
+ except Exception as e:
169
+ logger.error(f"Error adding worklog to issue {issue_key}: {e}")
170
+ return json.dumps({"error": str(e)})
@@ -111,13 +111,16 @@ class Condition:
111
111
  audio=current_audio + all_audio,
112
112
  )
113
113
 
114
- def _evaluate_condition(self, step_input: StepInput) -> bool:
114
+ def _evaluate_condition(self, step_input: StepInput, session_state: Optional[Dict[str, Any]] = None) -> bool:
115
115
  """Evaluate the condition and return boolean result"""
116
116
  if isinstance(self.evaluator, bool):
117
117
  return self.evaluator
118
118
 
119
119
  if callable(self.evaluator):
120
- result = self.evaluator(step_input)
120
+ if session_state is not None and self._evaluator_has_session_state_param():
121
+ result = self.evaluator(step_input, session_state=session_state) # type: ignore[call-arg]
122
+ else:
123
+ result = self.evaluator(step_input)
121
124
 
122
125
  if isinstance(result, bool):
123
126
  return result
@@ -127,16 +130,24 @@ class Condition:
127
130
 
128
131
  return False
129
132
 
130
- async def _aevaluate_condition(self, step_input: StepInput) -> bool:
133
+ async def _aevaluate_condition(self, step_input: StepInput, session_state: Optional[Dict[str, Any]] = None) -> bool:
131
134
  """Async version of condition evaluation"""
132
135
  if isinstance(self.evaluator, bool):
133
136
  return self.evaluator
134
137
 
135
138
  if callable(self.evaluator):
139
+ has_session_state = session_state is not None and self._evaluator_has_session_state_param()
140
+
136
141
  if inspect.iscoroutinefunction(self.evaluator):
137
- result = await self.evaluator(step_input)
142
+ if has_session_state:
143
+ result = await self.evaluator(step_input, session_state=session_state) # type: ignore[call-arg]
144
+ else:
145
+ result = await self.evaluator(step_input)
138
146
  else:
139
- result = self.evaluator(step_input)
147
+ if has_session_state:
148
+ result = self.evaluator(step_input, session_state=session_state) # type: ignore[call-arg]
149
+ else:
150
+ result = self.evaluator(step_input)
140
151
 
141
152
  if isinstance(result, bool):
142
153
  return result
@@ -146,6 +157,17 @@ class Condition:
146
157
 
147
158
  return False
148
159
 
160
+ def _evaluator_has_session_state_param(self) -> bool:
161
+ """Check if the evaluator function has a session_state parameter"""
162
+ if not callable(self.evaluator):
163
+ return False
164
+
165
+ try:
166
+ sig = inspect.signature(self.evaluator)
167
+ return "session_state" in sig.parameters
168
+ except Exception:
169
+ return False
170
+
149
171
  def execute(
150
172
  self,
151
173
  step_input: StepInput,
@@ -166,7 +188,7 @@ class Condition:
166
188
  self._prepare_steps()
167
189
 
168
190
  # Evaluate the condition
169
- condition_result = self._evaluate_condition(step_input)
191
+ condition_result = self._evaluate_condition(step_input, session_state)
170
192
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
171
193
 
172
194
  if not condition_result:
@@ -275,7 +297,7 @@ class Condition:
275
297
  self._prepare_steps()
276
298
 
277
299
  # Evaluate the condition
278
- condition_result = self._evaluate_condition(step_input)
300
+ condition_result = self._evaluate_condition(step_input, session_state)
279
301
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
280
302
 
281
303
  if stream_intermediate_steps and workflow_run_response:
@@ -434,7 +456,7 @@ class Condition:
434
456
  self._prepare_steps()
435
457
 
436
458
  # Evaluate the condition
437
- condition_result = await self._aevaluate_condition(step_input)
459
+ condition_result = await self._aevaluate_condition(step_input, session_state)
438
460
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
439
461
 
440
462
  if not condition_result:
@@ -543,7 +565,7 @@ class Condition:
543
565
  self._prepare_steps()
544
566
 
545
567
  # Evaluate the condition
546
- condition_result = await self._aevaluate_condition(step_input)
568
+ condition_result = await self._aevaluate_condition(step_input, session_state)
547
569
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
548
570
 
549
571
  if stream_intermediate_steps and workflow_run_response:
agno/workflow/router.py CHANGED
@@ -108,10 +108,13 @@ class Router:
108
108
  audio=current_audio + all_audio,
109
109
  )
110
110
 
111
- def _route_steps(self, step_input: StepInput) -> List[Step]: # type: ignore[return-value]
111
+ def _route_steps(self, step_input: StepInput, session_state: Optional[Dict[str, Any]] = None) -> List[Step]: # type: ignore[return-value]
112
112
  """Route to the appropriate steps based on input"""
113
113
  if callable(self.selector):
114
- result = self.selector(step_input)
114
+ if session_state is not None and self._selector_has_session_state_param():
115
+ result = self.selector(step_input, session_state) # type: ignore[call-arg]
116
+ else:
117
+ result = self.selector(step_input)
115
118
 
116
119
  # Handle the result based on its type
117
120
  if isinstance(result, Step):
@@ -124,13 +127,21 @@ class Router:
124
127
 
125
128
  return []
126
129
 
127
- async def _aroute_steps(self, step_input: StepInput) -> List[Step]: # type: ignore[return-value]
130
+ async def _aroute_steps(self, step_input: StepInput, session_state: Optional[Dict[str, Any]] = None) -> List[Step]: # type: ignore[return-value]
128
131
  """Async version of step routing"""
129
132
  if callable(self.selector):
133
+ has_session_state = session_state is not None and self._selector_has_session_state_param()
134
+
130
135
  if inspect.iscoroutinefunction(self.selector):
131
- result = await self.selector(step_input)
136
+ if has_session_state:
137
+ result = await self.selector(step_input, session_state) # type: ignore[call-arg]
138
+ else:
139
+ result = await self.selector(step_input)
132
140
  else:
133
- result = self.selector(step_input)
141
+ if has_session_state:
142
+ result = self.selector(step_input, session_state) # type: ignore[call-arg]
143
+ else:
144
+ result = self.selector(step_input)
134
145
 
135
146
  # Handle the result based on its type
136
147
  if isinstance(result, Step):
@@ -143,6 +154,17 @@ class Router:
143
154
 
144
155
  return []
145
156
 
157
+ def _selector_has_session_state_param(self) -> bool:
158
+ """Check if the selector function has a session_state parameter"""
159
+ if not callable(self.selector):
160
+ return False
161
+
162
+ try:
163
+ sig = inspect.signature(self.selector)
164
+ return "session_state" in sig.parameters
165
+ except Exception:
166
+ return False
167
+
146
168
  def execute(
147
169
  self,
148
170
  step_input: StepInput,
@@ -163,7 +185,7 @@ class Router:
163
185
  self._prepare_steps()
164
186
 
165
187
  # Route to appropriate steps
166
- steps_to_execute = self._route_steps(step_input)
188
+ steps_to_execute = self._route_steps(step_input, session_state)
167
189
  log_debug(f"Router {self.name}: Selected {len(steps_to_execute)} steps to execute")
168
190
 
169
191
  if not steps_to_execute:
@@ -263,7 +285,7 @@ class Router:
263
285
  router_step_id = str(uuid4())
264
286
 
265
287
  # Route to appropriate steps
266
- steps_to_execute = self._route_steps(step_input)
288
+ steps_to_execute = self._route_steps(step_input, session_state)
267
289
  log_debug(f"Router {self.name}: Selected {len(steps_to_execute)} steps to execute")
268
290
 
269
291
  if stream_intermediate_steps and workflow_run_response:
@@ -413,7 +435,7 @@ class Router:
413
435
  self._prepare_steps()
414
436
 
415
437
  # Route to appropriate steps
416
- steps_to_execute = await self._aroute_steps(step_input)
438
+ steps_to_execute = await self._aroute_steps(step_input, session_state)
417
439
  log_debug(f"Router {self.name} selected: {len(steps_to_execute)} steps to execute")
418
440
 
419
441
  if not steps_to_execute:
@@ -516,7 +538,7 @@ class Router:
516
538
  router_step_id = str(uuid4())
517
539
 
518
540
  # Route to appropriate steps
519
- steps_to_execute = await self._aroute_steps(step_input)
541
+ steps_to_execute = await self._aroute_steps(step_input, session_state)
520
542
  log_debug(f"Router {self.name} selected: {len(steps_to_execute)} steps to execute")
521
543
 
522
544
  if stream_intermediate_steps and workflow_run_response: