agno 1.7.2__py3-none-any.whl → 1.7.4__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 (63) hide show
  1. agno/agent/agent.py +264 -155
  2. agno/api/schemas/agent.py +1 -0
  3. agno/api/schemas/team.py +1 -0
  4. agno/app/base.py +0 -22
  5. agno/app/discord/client.py +134 -56
  6. agno/app/fastapi/app.py +0 -11
  7. agno/app/playground/app.py +3 -24
  8. agno/app/playground/async_router.py +97 -28
  9. agno/app/playground/operator.py +25 -19
  10. agno/app/playground/schemas.py +1 -0
  11. agno/app/playground/sync_router.py +93 -26
  12. agno/document/reader/gcs/__init__.py +0 -0
  13. agno/document/reader/gcs/pdf_reader.py +44 -0
  14. agno/embedder/langdb.py +9 -5
  15. agno/knowledge/document.py +199 -8
  16. agno/knowledge/gcs/__init__.py +0 -0
  17. agno/knowledge/gcs/base.py +39 -0
  18. agno/knowledge/gcs/pdf.py +21 -0
  19. agno/models/langdb/langdb.py +8 -5
  20. agno/run/base.py +2 -0
  21. agno/run/response.py +4 -4
  22. agno/run/team.py +6 -6
  23. agno/run/v2/__init__.py +0 -0
  24. agno/run/v2/workflow.py +563 -0
  25. agno/storage/base.py +4 -4
  26. agno/storage/dynamodb.py +74 -10
  27. agno/storage/firestore.py +6 -1
  28. agno/storage/gcs_json.py +8 -2
  29. agno/storage/json.py +20 -5
  30. agno/storage/mongodb.py +14 -5
  31. agno/storage/mysql.py +56 -17
  32. agno/storage/postgres.py +55 -13
  33. agno/storage/redis.py +25 -5
  34. agno/storage/session/__init__.py +3 -1
  35. agno/storage/session/agent.py +3 -0
  36. agno/storage/session/team.py +3 -0
  37. agno/storage/session/v2/__init__.py +5 -0
  38. agno/storage/session/v2/workflow.py +89 -0
  39. agno/storage/singlestore.py +74 -12
  40. agno/storage/sqlite.py +64 -18
  41. agno/storage/yaml.py +26 -6
  42. agno/team/team.py +198 -243
  43. agno/tools/scrapegraph.py +8 -10
  44. agno/utils/log.py +12 -0
  45. agno/utils/message.py +5 -1
  46. agno/utils/openai.py +20 -5
  47. agno/utils/pprint.py +32 -8
  48. agno/workflow/v2/__init__.py +21 -0
  49. agno/workflow/v2/condition.py +554 -0
  50. agno/workflow/v2/loop.py +602 -0
  51. agno/workflow/v2/parallel.py +659 -0
  52. agno/workflow/v2/router.py +521 -0
  53. agno/workflow/v2/step.py +861 -0
  54. agno/workflow/v2/steps.py +465 -0
  55. agno/workflow/v2/types.py +347 -0
  56. agno/workflow/v2/workflow.py +3134 -0
  57. agno/workflow/workflow.py +15 -147
  58. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/METADATA +1 -1
  59. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/RECORD +63 -45
  60. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/WHEEL +0 -0
  61. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/entry_points.txt +0 -0
  62. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/licenses/LICENSE +0 -0
  63. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/top_level.txt +0 -0
agno/storage/sqlite.py CHANGED
@@ -6,6 +6,7 @@ from agno.storage.base import Storage
6
6
  from agno.storage.session import Session
7
7
  from agno.storage.session.agent import AgentSession
8
8
  from agno.storage.session.team import TeamSession
9
+ from agno.storage.session.v2.workflow import WorkflowSession as WorkflowSessionV2
9
10
  from agno.storage.session.workflow import WorkflowSession
10
11
  from agno.utils.log import log_debug, log_info, log_warning, logger
11
12
 
@@ -32,7 +33,7 @@ class SqliteStorage(Storage):
32
33
  db_engine: Optional[Engine] = None,
33
34
  schema_version: int = 1,
34
35
  auto_upgrade_schema: bool = False,
35
- mode: Optional[Literal["agent", "team", "workflow"]] = "agent",
36
+ mode: Optional[Literal["agent", "team", "workflow", "workflow_v2"]] = "agent",
36
37
  ):
37
38
  """
38
39
  This class provides agent storage using a sqlite database.
@@ -84,12 +85,12 @@ class SqliteStorage(Storage):
84
85
  self.table: Table = self.get_table()
85
86
 
86
87
  @property
87
- def mode(self) -> Optional[Literal["agent", "team", "workflow"]]:
88
+ def mode(self) -> Optional[Literal["agent", "team", "workflow", "workflow_v2"]]:
88
89
  """Get the mode of the storage."""
89
90
  return super().mode
90
91
 
91
92
  @mode.setter
92
- def mode(self, value: Optional[Literal["agent", "team", "workflow"]]) -> None:
93
+ def mode(self, value: Optional[Literal["agent", "team", "workflow", "workflow_v2"]]) -> None:
93
94
  """Set the mode and refresh the table if mode changes."""
94
95
  super(SqliteStorage, type(self)).mode.fset(self, value) # type: ignore
95
96
  if value is not None:
@@ -131,6 +132,14 @@ class SqliteStorage(Storage):
131
132
  Column("workflow_id", String, index=True),
132
133
  Column("workflow_data", sqlite.JSON),
133
134
  ]
135
+ elif self.mode == "workflow_v2":
136
+ specific_columns = [
137
+ Column("workflow_id", String, index=True),
138
+ Column("workflow_name", String, index=True),
139
+ Column("workflow_data", sqlite.JSON),
140
+ # Store WorkflowRunResponse objects
141
+ Column("runs", sqlite.JSON),
142
+ ]
134
143
 
135
144
  # Create table with all columns
136
145
  table = Table(
@@ -241,6 +250,8 @@ class SqliteStorage(Storage):
241
250
  return TeamSession.from_dict(result._mapping) if result is not None else None # type: ignore
242
251
  elif self.mode == "workflow":
243
252
  return WorkflowSession.from_dict(result._mapping) if result is not None else None # type: ignore
253
+ elif self.mode == "workflow_v2":
254
+ return WorkflowSessionV2.from_dict(result._mapping) if result is not None else None # type: ignore
244
255
  except Exception as e:
245
256
  if "no such table" in str(e):
246
257
  log_debug(f"Table does not exist: {self.table.name}")
@@ -273,6 +284,8 @@ class SqliteStorage(Storage):
273
284
  stmt = stmt.where(self.table.c.team_id == entity_id)
274
285
  elif self.mode == "workflow":
275
286
  stmt = stmt.where(self.table.c.workflow_id == entity_id)
287
+ elif self.mode == "workflow_v2":
288
+ stmt = stmt.where(self.table.c.workflow_id == entity_id)
276
289
  # order by created_at desc
277
290
  stmt = stmt.order_by(self.table.c.created_at.desc())
278
291
  # execute query
@@ -308,11 +321,11 @@ class SqliteStorage(Storage):
308
321
  stmt = stmt.where(self.table.c.agent_id == entity_id)
309
322
  elif self.mode == "team":
310
323
  stmt = stmt.where(self.table.c.team_id == entity_id)
311
- elif self.mode == "workflow":
324
+ elif self.mode in ["workflow", "workflow_v2"]:
312
325
  stmt = stmt.where(self.table.c.workflow_id == entity_id)
313
326
  # order by created_at desc
314
327
  stmt = stmt.order_by(self.table.c.created_at.desc())
315
- # execute query
328
+
316
329
  rows = sess.execute(stmt).fetchall()
317
330
  if rows is not None:
318
331
  if self.mode == "agent":
@@ -321,6 +334,8 @@ class SqliteStorage(Storage):
321
334
  return [TeamSession.from_dict(row._mapping) for row in rows] # type: ignore
322
335
  elif self.mode == "workflow":
323
336
  return [WorkflowSession.from_dict(row._mapping) for row in rows] # type: ignore
337
+ elif self.mode == "workflow_v2":
338
+ return [WorkflowSessionV2.from_dict(row._mapping) for row in rows] # type: ignore
324
339
  else:
325
340
  return []
326
341
  except Exception as e:
@@ -341,7 +356,7 @@ class SqliteStorage(Storage):
341
356
  Get the last N sessions, ordered by created_at descending.
342
357
 
343
358
  Args:
344
- num_history_sessions: Number of most recent sessions to return
359
+ limit: Number of most recent sessions to return
345
360
  user_id: Filter by user ID
346
361
  entity_id: Filter by entity ID (agent_id, team_id, or workflow_id)
347
362
 
@@ -359,7 +374,7 @@ class SqliteStorage(Storage):
359
374
  stmt = stmt.where(self.table.c.agent_id == entity_id)
360
375
  elif self.mode == "team":
361
376
  stmt = stmt.where(self.table.c.team_id == entity_id)
362
- elif self.mode == "workflow":
377
+ elif self.mode in ["workflow", "workflow_v2"]:
363
378
  stmt = stmt.where(self.table.c.workflow_id == entity_id)
364
379
 
365
380
  # Order by created_at desc and limit to num_history_sessions
@@ -370,12 +385,14 @@ class SqliteStorage(Storage):
370
385
  # Execute query
371
386
  rows = sess.execute(stmt).fetchall()
372
387
  if rows is not None:
373
- if self.mode == "agent":
388
+ if self.mode == "agent": # type: ignore
374
389
  return [AgentSession.from_dict(row._mapping) for row in rows] # type: ignore
375
390
  elif self.mode == "team":
376
391
  return [TeamSession.from_dict(row._mapping) for row in rows] # type: ignore
377
392
  elif self.mode == "workflow":
378
393
  return [WorkflowSession.from_dict(row._mapping) for row in rows] # type: ignore
394
+ elif self.mode == "workflow_v2":
395
+ return [WorkflowSessionV2.from_dict(row._mapping) for row in rows] # type: ignore
379
396
  return []
380
397
  except Exception as e:
381
398
  if "no such table" in str(e):
@@ -437,26 +454,25 @@ class SqliteStorage(Storage):
437
454
  agent_id=session.agent_id, # type: ignore
438
455
  team_session_id=session.team_session_id, # type: ignore
439
456
  user_id=session.user_id,
440
- memory=session.memory,
457
+ memory=getattr(session, "memory", None),
441
458
  agent_data=session.agent_data, # type: ignore
442
459
  session_data=session.session_data,
443
460
  extra_data=session.extra_data,
444
461
  )
445
462
 
446
463
  # Define the upsert if the session_id already exists
447
- # See: https://docs.sqlalchemy.org/en/20/dialects/sqlite.html#insert-on-conflict-upsert
448
464
  stmt = stmt.on_conflict_do_update(
449
465
  index_elements=["session_id"],
450
466
  set_=dict(
451
467
  agent_id=session.agent_id, # type: ignore
452
468
  team_session_id=session.team_session_id, # type: ignore
453
469
  user_id=session.user_id,
454
- memory=session.memory,
470
+ memory=getattr(session, "memory", None),
455
471
  agent_data=session.agent_data, # type: ignore
456
472
  session_data=session.session_data,
457
473
  extra_data=session.extra_data,
458
474
  updated_at=int(time.time()),
459
- ), # The updated value for each column
475
+ ),
460
476
  )
461
477
  elif self.mode == "team":
462
478
  # Create an insert statement
@@ -465,7 +481,7 @@ class SqliteStorage(Storage):
465
481
  team_id=session.team_id, # type: ignore
466
482
  user_id=session.user_id,
467
483
  team_session_id=session.team_session_id, # type: ignore
468
- memory=session.memory,
484
+ memory=getattr(session, "memory", None),
469
485
  team_data=session.team_data, # type: ignore
470
486
  session_data=session.session_data,
471
487
  extra_data=session.extra_data,
@@ -479,12 +495,12 @@ class SqliteStorage(Storage):
479
495
  team_id=session.team_id, # type: ignore
480
496
  user_id=session.user_id,
481
497
  team_session_id=session.team_session_id, # type: ignore
482
- memory=session.memory,
498
+ memory=getattr(session, "memory", None),
483
499
  team_data=session.team_data, # type: ignore
484
500
  session_data=session.session_data,
485
501
  extra_data=session.extra_data,
486
502
  updated_at=int(time.time()),
487
- ), # The updated value for each column
503
+ ),
488
504
  )
489
505
  elif self.mode == "workflow":
490
506
  # Create an insert statement
@@ -492,7 +508,7 @@ class SqliteStorage(Storage):
492
508
  session_id=session.session_id,
493
509
  workflow_id=session.workflow_id, # type: ignore
494
510
  user_id=session.user_id,
495
- memory=session.memory,
511
+ memory=getattr(session, "memory", None),
496
512
  workflow_data=session.workflow_data, # type: ignore
497
513
  session_data=session.session_data,
498
514
  extra_data=session.extra_data,
@@ -505,12 +521,42 @@ class SqliteStorage(Storage):
505
521
  set_=dict(
506
522
  workflow_id=session.workflow_id, # type: ignore
507
523
  user_id=session.user_id,
508
- memory=session.memory,
524
+ memory=getattr(session, "memory", None),
525
+ workflow_data=session.workflow_data, # type: ignore
526
+ session_data=session.session_data,
527
+ extra_data=session.extra_data,
528
+ updated_at=int(time.time()),
529
+ ),
530
+ )
531
+ elif self.mode == "workflow_v2":
532
+ # Convert session to dict to ensure proper serialization
533
+ session_dict = session.to_dict()
534
+
535
+ # Create an insert statement for WorkflowSessionV2
536
+ stmt = sqlite.insert(self.table).values(
537
+ session_id=session.session_id,
538
+ workflow_id=session.workflow_id, # type: ignore
539
+ workflow_name=session.workflow_name, # type: ignore
540
+ user_id=session.user_id,
541
+ runs=session_dict.get("runs"),
542
+ workflow_data=session.workflow_data, # type: ignore
543
+ session_data=session.session_data,
544
+ extra_data=session.extra_data,
545
+ )
546
+
547
+ # Define the upsert if the session_id already exists
548
+ stmt = stmt.on_conflict_do_update(
549
+ index_elements=["session_id"],
550
+ set_=dict(
551
+ workflow_id=session.workflow_id, # type: ignore
552
+ workflow_name=session.workflow_name, # type: ignore
553
+ user_id=session.user_id,
554
+ runs=session_dict.get("runs"),
509
555
  workflow_data=session.workflow_data, # type: ignore
510
556
  session_data=session.session_data,
511
557
  extra_data=session.extra_data,
512
558
  updated_at=int(time.time()),
513
- ), # The updated value for each column
559
+ ),
514
560
  )
515
561
 
516
562
  sess.execute(stmt)
agno/storage/yaml.py CHANGED
@@ -9,12 +9,15 @@ from agno.storage.base import Storage
9
9
  from agno.storage.session import Session
10
10
  from agno.storage.session.agent import AgentSession
11
11
  from agno.storage.session.team import TeamSession
12
+ from agno.storage.session.v2.workflow import WorkflowSession as WorkflowSessionV2
12
13
  from agno.storage.session.workflow import WorkflowSession
13
14
  from agno.utils.log import logger
14
15
 
15
16
 
16
17
  class YamlStorage(Storage):
17
- def __init__(self, dir_path: Union[str, Path], mode: Optional[Literal["agent", "team", "workflow"]] = "agent"):
18
+ def __init__(
19
+ self, dir_path: Union[str, Path], mode: Optional[Literal["agent", "team", "workflow", "workflow_v2"]] = "agent"
20
+ ):
18
21
  super().__init__(mode)
19
22
  self.dir_path = Path(dir_path)
20
23
  self.dir_path.mkdir(parents=True, exist_ok=True)
@@ -43,6 +46,8 @@ class YamlStorage(Storage):
43
46
  return TeamSession.from_dict(data)
44
47
  elif self.mode == "workflow":
45
48
  return WorkflowSession.from_dict(data)
49
+ elif self.mode == "workflow_v2":
50
+ return WorkflowSessionV2.from_dict(data)
46
51
  except FileNotFoundError:
47
52
  return None
48
53
 
@@ -62,6 +67,8 @@ class YamlStorage(Storage):
62
67
  self.mode == "workflow" and data["workflow_id"] == entity_id and data["user_id"] == user_id
63
68
  ):
64
69
  session_ids.append(data["session_id"])
70
+ elif self.mode == "workflow_v2" and data["workflow_id"] == entity_id:
71
+ session_ids.append(data["session_id"])
65
72
  elif user_id and data["user_id"] == user_id:
66
73
  session_ids.append(data["session_id"])
67
74
  elif entity_id:
@@ -71,6 +78,8 @@ class YamlStorage(Storage):
71
78
  session_ids.append(data["session_id"])
72
79
  elif self.mode == "workflow" and data["workflow_id"] == entity_id:
73
80
  session_ids.append(data["session_id"])
81
+ elif self.mode == "workflow_v2" and data["workflow_id"] == entity_id:
82
+ session_ids.append(data["session_id"])
74
83
  else:
75
84
  # No filters applied, add all session_ids
76
85
  session_ids.append(data["session_id"])
@@ -93,6 +102,8 @@ class YamlStorage(Storage):
93
102
  self.mode == "workflow" and data["workflow_id"] == entity_id and data["user_id"] == user_id
94
103
  ):
95
104
  _session = WorkflowSession.from_dict(data)
105
+ elif self.mode == "workflow_v2" and data["workflow_id"] == entity_id:
106
+ _session = WorkflowSessionV2.from_dict(data)
96
107
  elif user_id and data["user_id"] == user_id:
97
108
  if self.mode == "agent":
98
109
  _session = AgentSession.from_dict(data)
@@ -100,6 +111,8 @@ class YamlStorage(Storage):
100
111
  _session = TeamSession.from_dict(data)
101
112
  elif self.mode == "workflow":
102
113
  _session = WorkflowSession.from_dict(data)
114
+ elif self.mode == "workflow_v2":
115
+ _session = WorkflowSessionV2.from_dict(data)
103
116
  elif entity_id:
104
117
  if self.mode == "agent" and data["agent_id"] == entity_id:
105
118
  _session = AgentSession.from_dict(data)
@@ -107,7 +120,8 @@ class YamlStorage(Storage):
107
120
  _session = TeamSession.from_dict(data)
108
121
  elif self.mode == "workflow" and data["workflow_id"] == entity_id:
109
122
  _session = WorkflowSession.from_dict(data)
110
-
123
+ elif self.mode == "workflow_v2" and data["workflow_id"] == entity_id:
124
+ _session = WorkflowSessionV2.from_dict(data)
111
125
  if _session:
112
126
  sessions.append(_session)
113
127
  else:
@@ -118,6 +132,8 @@ class YamlStorage(Storage):
118
132
  _session = TeamSession.from_dict(data)
119
133
  elif self.mode == "workflow":
120
134
  _session = WorkflowSession.from_dict(data)
135
+ elif self.mode == "workflow_v2":
136
+ _session = WorkflowSessionV2.from_dict(data)
121
137
  if _session:
122
138
  sessions.append(_session)
123
139
  return sessions
@@ -158,7 +174,8 @@ class YamlStorage(Storage):
158
174
  continue
159
175
  elif self.mode == "workflow" and data["workflow_id"] != entity_id:
160
176
  continue
161
-
177
+ elif self.mode == "workflow_v2" and data["workflow_id"] != entity_id:
178
+ continue
162
179
  # Store with created_at for sorting
163
180
  created_at = data.get("created_at", 0)
164
181
  session_data.append((created_at, data))
@@ -181,7 +198,8 @@ class YamlStorage(Storage):
181
198
  session = TeamSession.from_dict(data)
182
199
  elif self.mode == "workflow":
183
200
  session = WorkflowSession.from_dict(data)
184
-
201
+ elif self.mode == "workflow_v2":
202
+ session = WorkflowSessionV2.from_dict(data)
185
203
  if session is not None:
186
204
  sessions.append(session)
187
205
 
@@ -190,11 +208,13 @@ class YamlStorage(Storage):
190
208
  def upsert(self, session: Session) -> Optional[Session]:
191
209
  """Insert or update an Session in storage."""
192
210
  try:
193
- data = asdict(session)
211
+ if self.mode == "workflow_v2":
212
+ data = session.to_dict()
213
+ else:
214
+ data = asdict(session)
194
215
  data["updated_at"] = int(time.time())
195
216
  if "created_at" not in data:
196
217
  data["created_at"] = data["updated_at"]
197
-
198
218
  with open(self.dir_path / f"{session.session_id}.yaml", "w", encoding="utf-8") as f:
199
219
  f.write(self.serialize(data))
200
220
  return session