agno 2.1.7__py3-none-any.whl → 2.1.8__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/agent/agent.py +174 -37
- agno/os/routers/knowledge/knowledge.py +4 -4
- agno/os/schema.py +6 -6
- agno/team/team.py +102 -28
- agno/tools/googlecalendar.py +43 -8
- agno/tools/jira.py +20 -0
- agno/workflow/step.py +51 -27
- {agno-2.1.7.dist-info → agno-2.1.8.dist-info}/METADATA +1 -1
- {agno-2.1.7.dist-info → agno-2.1.8.dist-info}/RECORD +12 -12
- {agno-2.1.7.dist-info → agno-2.1.8.dist-info}/WHEEL +0 -0
- {agno-2.1.7.dist-info → agno-2.1.8.dist-info}/licenses/LICENSE +0 -0
- {agno-2.1.7.dist-info → agno-2.1.8.dist-info}/top_level.txt +0 -0
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
|
-
|
|
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
|
|
|
@@ -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
|
-
#
|
|
1911
|
-
|
|
1960
|
+
# 11. Parse team response model
|
|
1961
|
+
self._convert_response_to_structured_format(run_response=run_response)
|
|
1912
1962
|
|
|
1913
|
-
#
|
|
1914
|
-
self.
|
|
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.
|
|
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
|
+
self._execute_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
|
agno/tools/googlecalendar.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 =
|
|
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,18 @@ 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
|
-
|
|
340
|
+
service.events().update(
|
|
341
|
+
calendarId=self.calendar_id,
|
|
342
|
+
eventId=event_id,
|
|
343
|
+
body=event,
|
|
344
|
+
sendUpdates=send_updates
|
|
345
|
+
).execute()
|
|
323
346
|
)
|
|
324
347
|
|
|
325
348
|
log_debug(f"Event {event_id} updated successfully.")
|
|
@@ -329,18 +352,28 @@ class GoogleCalendarTools(Toolkit):
|
|
|
329
352
|
return json.dumps({"error": f"An error occurred: {error}"})
|
|
330
353
|
|
|
331
354
|
@authenticate
|
|
332
|
-
def delete_event(self, event_id: str) -> str:
|
|
355
|
+
def delete_event(self, event_id: str, notify_attendees: Optional[bool] = True) -> str:
|
|
333
356
|
"""
|
|
334
357
|
Delete an event from the Google Calendar.
|
|
335
358
|
|
|
336
359
|
Args:
|
|
337
360
|
event_id (str): ID of the event to delete
|
|
361
|
+
notify_attendees (Optional[bool]): Whether to send email notifications to attendees (default: False)
|
|
338
362
|
|
|
339
363
|
Returns:
|
|
340
364
|
str: JSON string containing success message or error message
|
|
341
365
|
"""
|
|
342
366
|
try:
|
|
343
|
-
|
|
367
|
+
# Determine sendUpdates value based on notify_attendees parameter
|
|
368
|
+
send_updates = "all" if notify_attendees else "none"
|
|
369
|
+
|
|
370
|
+
service = cast(Resource, self.service)
|
|
371
|
+
|
|
372
|
+
service.events().delete(
|
|
373
|
+
calendarId=self.calendar_id,
|
|
374
|
+
eventId=event_id,
|
|
375
|
+
sendUpdates=send_updates
|
|
376
|
+
).execute()
|
|
344
377
|
|
|
345
378
|
log_debug(f"Event {event_id} deleted successfully.")
|
|
346
379
|
return json.dumps({"success": True, "message": f"Event {event_id} deleted successfully."})
|
|
@@ -366,6 +399,8 @@ class GoogleCalendarTools(Toolkit):
|
|
|
366
399
|
str: JSON string containing all Google Calendar events or error message
|
|
367
400
|
"""
|
|
368
401
|
try:
|
|
402
|
+
service = cast(Resource, self.service)
|
|
403
|
+
|
|
369
404
|
params = {
|
|
370
405
|
"calendarId": self.calendar_id,
|
|
371
406
|
"maxResults": min(max_results, 100),
|
|
@@ -412,7 +447,7 @@ class GoogleCalendarTools(Toolkit):
|
|
|
412
447
|
if page_token:
|
|
413
448
|
params["pageToken"] = page_token
|
|
414
449
|
|
|
415
|
-
events_result =
|
|
450
|
+
events_result = service.events().list(**params).execute()
|
|
416
451
|
all_events.extend(events_result.get("items", []))
|
|
417
452
|
|
|
418
453
|
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)})
|
agno/workflow/step.py
CHANGED
|
@@ -5,6 +5,7 @@ from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List
|
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
|
|
7
7
|
from pydantic import BaseModel
|
|
8
|
+
from typing_extensions import TypeGuard
|
|
8
9
|
|
|
9
10
|
from agno.agent import Agent
|
|
10
11
|
from agno.media import Audio, Image, Video
|
|
@@ -191,9 +192,8 @@ class Step:
|
|
|
191
192
|
session_state: Optional[Dict[str, Any]] = None,
|
|
192
193
|
) -> Any:
|
|
193
194
|
"""Call custom async function with session_state support if the function accepts it"""
|
|
194
|
-
import inspect
|
|
195
195
|
|
|
196
|
-
if
|
|
196
|
+
if _is_async_generator_function(func):
|
|
197
197
|
if session_state is not None and self._function_has_session_state_param():
|
|
198
198
|
return func(step_input, session_state)
|
|
199
199
|
else:
|
|
@@ -231,16 +231,16 @@ class Step:
|
|
|
231
231
|
try:
|
|
232
232
|
response: Union[RunOutput, TeamRunOutput, StepOutput]
|
|
233
233
|
if self._executor_type == "function":
|
|
234
|
-
if
|
|
235
|
-
self.active_executor
|
|
236
|
-
):
|
|
234
|
+
if _is_async_callable(self.active_executor) or _is_async_generator_function(self.active_executor):
|
|
237
235
|
raise ValueError("Cannot use async function with synchronous execution")
|
|
238
|
-
if
|
|
236
|
+
if _is_generator_function(self.active_executor):
|
|
239
237
|
content = ""
|
|
240
238
|
final_response = None
|
|
241
239
|
try:
|
|
242
240
|
for chunk in self._call_custom_function(
|
|
243
|
-
self.active_executor,
|
|
241
|
+
self.active_executor,
|
|
242
|
+
step_input,
|
|
243
|
+
session_state_copy, # type: ignore[arg-type]
|
|
244
244
|
): # type: ignore
|
|
245
245
|
if (
|
|
246
246
|
hasattr(chunk, "content")
|
|
@@ -368,9 +368,7 @@ class Step:
|
|
|
368
368
|
return False
|
|
369
369
|
|
|
370
370
|
try:
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
sig = signature(self.active_executor) # type: ignore
|
|
371
|
+
sig = inspect.signature(self.active_executor) # type: ignore
|
|
374
372
|
return "session_state" in sig.parameters
|
|
375
373
|
except Exception:
|
|
376
374
|
return False
|
|
@@ -449,12 +447,10 @@ class Step:
|
|
|
449
447
|
if self._executor_type == "function":
|
|
450
448
|
log_debug(f"Executing function executor for step: {self.name}")
|
|
451
449
|
|
|
452
|
-
if
|
|
453
|
-
self.active_executor
|
|
454
|
-
):
|
|
450
|
+
if _is_async_callable(self.active_executor) or _is_async_generator_function(self.active_executor):
|
|
455
451
|
raise ValueError("Cannot use async function with synchronous execution")
|
|
456
452
|
|
|
457
|
-
if
|
|
453
|
+
if _is_generator_function(self.active_executor):
|
|
458
454
|
log_debug("Function returned iterable, streaming events")
|
|
459
455
|
content = ""
|
|
460
456
|
try:
|
|
@@ -652,17 +648,17 @@ class Step:
|
|
|
652
648
|
for attempt in range(self.max_retries + 1):
|
|
653
649
|
try:
|
|
654
650
|
if self._executor_type == "function":
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
if inspect.isgeneratorfunction(self.active_executor) or inspect.isasyncgenfunction(
|
|
651
|
+
if _is_generator_function(self.active_executor) or _is_async_generator_function(
|
|
658
652
|
self.active_executor
|
|
659
653
|
):
|
|
660
654
|
content = ""
|
|
661
655
|
final_response = None
|
|
662
656
|
try:
|
|
663
|
-
if
|
|
657
|
+
if _is_generator_function(self.active_executor):
|
|
664
658
|
iterator = self._call_custom_function(
|
|
665
|
-
self.active_executor,
|
|
659
|
+
self.active_executor,
|
|
660
|
+
step_input,
|
|
661
|
+
session_state_copy, # type: ignore[arg-type]
|
|
666
662
|
) # type: ignore
|
|
667
663
|
for chunk in iterator: # type: ignore
|
|
668
664
|
if (
|
|
@@ -676,9 +672,11 @@ class Step:
|
|
|
676
672
|
if isinstance(chunk, StepOutput):
|
|
677
673
|
final_response = chunk
|
|
678
674
|
else:
|
|
679
|
-
if
|
|
675
|
+
if _is_async_generator_function(self.active_executor):
|
|
680
676
|
iterator = await self._acall_custom_function(
|
|
681
|
-
self.active_executor,
|
|
677
|
+
self.active_executor,
|
|
678
|
+
step_input,
|
|
679
|
+
session_state_copy, # type: ignore[arg-type]
|
|
682
680
|
) # type: ignore
|
|
683
681
|
async for chunk in iterator: # type: ignore
|
|
684
682
|
if (
|
|
@@ -705,7 +703,7 @@ class Step:
|
|
|
705
703
|
else:
|
|
706
704
|
response = StepOutput(content=content)
|
|
707
705
|
else:
|
|
708
|
-
if
|
|
706
|
+
if _is_async_callable(self.active_executor):
|
|
709
707
|
result = await self._acall_custom_function(
|
|
710
708
|
self.active_executor, step_input, session_state_copy
|
|
711
709
|
) # type: ignore
|
|
@@ -854,14 +852,15 @@ class Step:
|
|
|
854
852
|
|
|
855
853
|
if self._executor_type == "function":
|
|
856
854
|
log_debug(f"Executing async function executor for step: {self.name}")
|
|
857
|
-
import inspect
|
|
858
855
|
|
|
859
856
|
# Check if the function is an async generator
|
|
860
|
-
if
|
|
857
|
+
if _is_async_generator_function(self.active_executor):
|
|
861
858
|
content = ""
|
|
862
859
|
# It's an async generator - iterate over it
|
|
863
860
|
iterator = await self._acall_custom_function(
|
|
864
|
-
self.active_executor,
|
|
861
|
+
self.active_executor,
|
|
862
|
+
step_input,
|
|
863
|
+
session_state_copy, # type: ignore[arg-type]
|
|
865
864
|
) # type: ignore
|
|
866
865
|
async for event in iterator: # type: ignore
|
|
867
866
|
if (
|
|
@@ -885,14 +884,14 @@ class Step:
|
|
|
885
884
|
yield enriched_event # type: ignore[misc]
|
|
886
885
|
if not final_response:
|
|
887
886
|
final_response = StepOutput(content=content)
|
|
888
|
-
elif
|
|
887
|
+
elif _is_async_callable(self.active_executor):
|
|
889
888
|
# It's a regular async function - await it
|
|
890
889
|
result = await self._acall_custom_function(self.active_executor, step_input, session_state_copy) # type: ignore
|
|
891
890
|
if isinstance(result, StepOutput):
|
|
892
891
|
final_response = result
|
|
893
892
|
else:
|
|
894
893
|
final_response = StepOutput(content=str(result))
|
|
895
|
-
elif
|
|
894
|
+
elif _is_generator_function(self.active_executor):
|
|
896
895
|
content = ""
|
|
897
896
|
# It's a regular generator function - iterate over it
|
|
898
897
|
iterator = self._call_custom_function(self.active_executor, step_input, session_state_copy) # type: ignore
|
|
@@ -1252,3 +1251,28 @@ class Step:
|
|
|
1252
1251
|
continue
|
|
1253
1252
|
|
|
1254
1253
|
return videos
|
|
1254
|
+
|
|
1255
|
+
|
|
1256
|
+
def _is_async_callable(obj: Any) -> TypeGuard[Callable[..., Any]]:
|
|
1257
|
+
"""Checks if obj is an async callable (coroutine function or callable with async __call__)"""
|
|
1258
|
+
return inspect.iscoroutinefunction(obj) or (callable(obj) and inspect.iscoroutinefunction(obj.__call__))
|
|
1259
|
+
|
|
1260
|
+
|
|
1261
|
+
def _is_generator_function(obj: Any) -> TypeGuard[Callable[..., Any]]:
|
|
1262
|
+
"""Checks if obj is a generator function, including callable class instances with generator __call__ methods"""
|
|
1263
|
+
if inspect.isgeneratorfunction(obj):
|
|
1264
|
+
return True
|
|
1265
|
+
# Check if it's a callable class instance with a generator __call__ method
|
|
1266
|
+
if callable(obj) and hasattr(obj, "__call__"):
|
|
1267
|
+
return inspect.isgeneratorfunction(obj.__call__)
|
|
1268
|
+
return False
|
|
1269
|
+
|
|
1270
|
+
|
|
1271
|
+
def _is_async_generator_function(obj: Any) -> TypeGuard[Callable[..., Any]]:
|
|
1272
|
+
"""Checks if obj is an async generator function, including callable class instances"""
|
|
1273
|
+
if inspect.isasyncgenfunction(obj):
|
|
1274
|
+
return True
|
|
1275
|
+
# Check if it's a callable class instance with an async generator __call__ method
|
|
1276
|
+
if callable(obj) and hasattr(obj, "__call__"):
|
|
1277
|
+
return inspect.isasyncgenfunction(obj.__call__)
|
|
1278
|
+
return False
|