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.
- agno/agent/agent.py +264 -155
- agno/api/schemas/agent.py +1 -0
- agno/api/schemas/team.py +1 -0
- agno/app/base.py +0 -22
- agno/app/discord/client.py +134 -56
- agno/app/fastapi/app.py +0 -11
- agno/app/playground/app.py +3 -24
- agno/app/playground/async_router.py +97 -28
- agno/app/playground/operator.py +25 -19
- agno/app/playground/schemas.py +1 -0
- agno/app/playground/sync_router.py +93 -26
- agno/document/reader/gcs/__init__.py +0 -0
- agno/document/reader/gcs/pdf_reader.py +44 -0
- agno/embedder/langdb.py +9 -5
- agno/knowledge/document.py +199 -8
- agno/knowledge/gcs/__init__.py +0 -0
- agno/knowledge/gcs/base.py +39 -0
- agno/knowledge/gcs/pdf.py +21 -0
- agno/models/langdb/langdb.py +8 -5
- agno/run/base.py +2 -0
- agno/run/response.py +4 -4
- agno/run/team.py +6 -6
- agno/run/v2/__init__.py +0 -0
- agno/run/v2/workflow.py +563 -0
- agno/storage/base.py +4 -4
- agno/storage/dynamodb.py +74 -10
- agno/storage/firestore.py +6 -1
- agno/storage/gcs_json.py +8 -2
- agno/storage/json.py +20 -5
- agno/storage/mongodb.py +14 -5
- agno/storage/mysql.py +56 -17
- agno/storage/postgres.py +55 -13
- agno/storage/redis.py +25 -5
- agno/storage/session/__init__.py +3 -1
- agno/storage/session/agent.py +3 -0
- agno/storage/session/team.py +3 -0
- agno/storage/session/v2/__init__.py +5 -0
- agno/storage/session/v2/workflow.py +89 -0
- agno/storage/singlestore.py +74 -12
- agno/storage/sqlite.py +64 -18
- agno/storage/yaml.py +26 -6
- agno/team/team.py +198 -243
- agno/tools/scrapegraph.py +8 -10
- agno/utils/log.py +12 -0
- agno/utils/message.py +5 -1
- agno/utils/openai.py +20 -5
- agno/utils/pprint.py +32 -8
- agno/workflow/v2/__init__.py +21 -0
- agno/workflow/v2/condition.py +554 -0
- agno/workflow/v2/loop.py +602 -0
- agno/workflow/v2/parallel.py +659 -0
- agno/workflow/v2/router.py +521 -0
- agno/workflow/v2/step.py +861 -0
- agno/workflow/v2/steps.py +465 -0
- agno/workflow/v2/types.py +347 -0
- agno/workflow/v2/workflow.py +3134 -0
- agno/workflow/workflow.py +15 -147
- {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/METADATA +1 -1
- {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/RECORD +63 -45
- {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/WHEEL +0 -0
- {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/entry_points.txt +0 -0
- {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/licenses/LICENSE +0 -0
- {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/top_level.txt +0 -0
agno/storage/dynamodb.py
CHANGED
|
@@ -7,6 +7,7 @@ from agno.storage.base import Storage
|
|
|
7
7
|
from agno.storage.session import Session
|
|
8
8
|
from agno.storage.session.agent import AgentSession
|
|
9
9
|
from agno.storage.session.team import TeamSession
|
|
10
|
+
from agno.storage.session.v2.workflow import WorkflowSession as WorkflowSessionV2
|
|
10
11
|
from agno.storage.session.workflow import WorkflowSession
|
|
11
12
|
from agno.utils.log import log_debug, log_info, logger
|
|
12
13
|
|
|
@@ -28,7 +29,7 @@ class DynamoDbStorage(Storage):
|
|
|
28
29
|
aws_secret_access_key: Optional[str] = None,
|
|
29
30
|
endpoint_url: Optional[str] = None,
|
|
30
31
|
create_table_if_not_exists: bool = True,
|
|
31
|
-
mode: Optional[Literal["agent", "team", "workflow"]] = "agent",
|
|
32
|
+
mode: Optional[Literal["agent", "team", "workflow", "workflow_v2"]] = "agent",
|
|
32
33
|
):
|
|
33
34
|
"""
|
|
34
35
|
Initialize the DynamoDbStorage.
|
|
@@ -41,7 +42,7 @@ class DynamoDbStorage(Storage):
|
|
|
41
42
|
aws_secret_access_key (Optional[str]): AWS secret access key.
|
|
42
43
|
endpoint_url (Optional[str]): The complete URL to use for the constructed client.
|
|
43
44
|
create_table_if_not_exists (bool): Whether to create the table if it does not exist.
|
|
44
|
-
mode (Optional[Literal["agent", "team", "workflow"]]): The mode of the storage.
|
|
45
|
+
mode (Optional[Literal["agent", "team", "workflow", "workflow_v2"]]): The mode of the storage.
|
|
45
46
|
"""
|
|
46
47
|
super().__init__(mode)
|
|
47
48
|
self.table_name = table_name
|
|
@@ -79,12 +80,12 @@ class DynamoDbStorage(Storage):
|
|
|
79
80
|
log_debug(f"Initialized DynamoDbStorage with table '{self.table_name}'")
|
|
80
81
|
|
|
81
82
|
@property
|
|
82
|
-
def mode(self) -> Literal["agent", "team", "workflow"]:
|
|
83
|
+
def mode(self) -> Literal["agent", "team", "workflow", "workflow_v2"]:
|
|
83
84
|
"""Get the mode of the storage."""
|
|
84
85
|
return super().mode
|
|
85
86
|
|
|
86
87
|
@mode.setter
|
|
87
|
-
def mode(self, value: Optional[Literal["agent", "team", "workflow"]]) -> None:
|
|
88
|
+
def mode(self, value: Optional[Literal["agent", "team", "workflow", "workflow_v2"]]) -> None:
|
|
88
89
|
"""Set the mode and refresh the table if mode changes."""
|
|
89
90
|
super(DynamoDbStorage, type(self)).mode.fset(self, value) # type: ignore
|
|
90
91
|
if value is not None:
|
|
@@ -125,7 +126,13 @@ class DynamoDbStorage(Storage):
|
|
|
125
126
|
{"AttributeName": "workflow_id", "AttributeType": "S"},
|
|
126
127
|
{"AttributeName": "created_at", "AttributeType": "N"},
|
|
127
128
|
]
|
|
128
|
-
|
|
129
|
+
elif self.mode == "workflow_v2":
|
|
130
|
+
attribute_definitions = [
|
|
131
|
+
{"AttributeName": "session_id", "AttributeType": "S"},
|
|
132
|
+
{"AttributeName": "user_id", "AttributeType": "S"},
|
|
133
|
+
{"AttributeName": "workflow_id", "AttributeType": "S"},
|
|
134
|
+
{"AttributeName": "created_at", "AttributeType": "N"},
|
|
135
|
+
]
|
|
129
136
|
secondary_indexes = [
|
|
130
137
|
{
|
|
131
138
|
"IndexName": "user_id-index",
|
|
@@ -185,7 +192,21 @@ class DynamoDbStorage(Storage):
|
|
|
185
192
|
},
|
|
186
193
|
}
|
|
187
194
|
)
|
|
188
|
-
|
|
195
|
+
elif self.mode == "workflow_v2":
|
|
196
|
+
secondary_indexes.append(
|
|
197
|
+
{
|
|
198
|
+
"IndexName": "workflow_id-index",
|
|
199
|
+
"KeySchema": [
|
|
200
|
+
{"AttributeName": "workflow_id", "KeyType": "HASH"},
|
|
201
|
+
{"AttributeName": "created_at", "KeyType": "RANGE"},
|
|
202
|
+
],
|
|
203
|
+
"Projection": {"ProjectionType": "ALL"},
|
|
204
|
+
"ProvisionedThroughput": {
|
|
205
|
+
"ReadCapacityUnits": 5,
|
|
206
|
+
"WriteCapacityUnits": 5,
|
|
207
|
+
},
|
|
208
|
+
}
|
|
209
|
+
)
|
|
189
210
|
# Create the table
|
|
190
211
|
self.table = self.dynamodb.create_table(
|
|
191
212
|
TableName=self.table_name,
|
|
@@ -229,6 +250,8 @@ class DynamoDbStorage(Storage):
|
|
|
229
250
|
return TeamSession.from_dict(item)
|
|
230
251
|
elif self.mode == "workflow":
|
|
231
252
|
return WorkflowSession.from_dict(item)
|
|
253
|
+
elif self.mode == "workflow_v2":
|
|
254
|
+
return WorkflowSessionV2.from_dict(item)
|
|
232
255
|
except Exception as e:
|
|
233
256
|
logger.error(f"Error reading session_id '{session_id}' with user_id '{user_id}': {e}")
|
|
234
257
|
return None
|
|
@@ -323,7 +346,13 @@ class DynamoDbStorage(Storage):
|
|
|
323
346
|
KeyConditionExpression=Key("user_id").eq(user_id),
|
|
324
347
|
ProjectionExpression="session_id, workflow_id, user_id, memory, workflow_data, session_data, extra_data, created_at, updated_at",
|
|
325
348
|
)
|
|
326
|
-
|
|
349
|
+
elif self.mode == "workflow_v2":
|
|
350
|
+
# Query using user_id index
|
|
351
|
+
response = self.table.query(
|
|
352
|
+
IndexName="user_id-index",
|
|
353
|
+
KeyConditionExpression=Key("user_id").eq(user_id),
|
|
354
|
+
ProjectionExpression="session_id, workflow_id, user_id, workflow_name, runs, workflow_data, session_data, extra_data, created_at, updated_at",
|
|
355
|
+
)
|
|
327
356
|
items = response.get("Items", []) # type: ignore
|
|
328
357
|
for item in items:
|
|
329
358
|
item = self._deserialize_item(item)
|
|
@@ -356,6 +385,13 @@ class DynamoDbStorage(Storage):
|
|
|
356
385
|
KeyConditionExpression=Key("workflow_id").eq(entity_id),
|
|
357
386
|
ProjectionExpression="session_id, workflow_id, user_id, memory, workflow_data, session_data, extra_data, created_at, updated_at",
|
|
358
387
|
)
|
|
388
|
+
elif self.mode == "workflow_v2":
|
|
389
|
+
# Query using workflow_id index
|
|
390
|
+
response = self.table.query(
|
|
391
|
+
IndexName="workflow_id-index",
|
|
392
|
+
KeyConditionExpression=Key("workflow_id").eq(entity_id),
|
|
393
|
+
ProjectionExpression="session_id, workflow_id, user_id, workflow_name, runs, workflow_data, session_data, extra_data, created_at, updated_at",
|
|
394
|
+
)
|
|
359
395
|
items = response.get("Items", []) # type: ignore
|
|
360
396
|
for item in items:
|
|
361
397
|
item = self._deserialize_item(item)
|
|
@@ -379,6 +415,10 @@ class DynamoDbStorage(Storage):
|
|
|
379
415
|
response = self.table.scan(
|
|
380
416
|
ProjectionExpression="session_id, workflow_id, user_id, memory, workflow_data, session_data, extra_data, created_at, updated_at"
|
|
381
417
|
)
|
|
418
|
+
elif self.mode == "workflow_v2":
|
|
419
|
+
response = self.table.scan(
|
|
420
|
+
ProjectionExpression="session_id, workflow_id, user_id, workflow_name, runs, workflow_data, session_data, extra_data, created_at, updated_at"
|
|
421
|
+
)
|
|
382
422
|
items = response.get("Items", [])
|
|
383
423
|
for item in items:
|
|
384
424
|
item = self._deserialize_item(item)
|
|
@@ -437,6 +477,14 @@ class DynamoDbStorage(Storage):
|
|
|
437
477
|
ScanIndexForward=False,
|
|
438
478
|
Limit=limit if limit is not None else None,
|
|
439
479
|
)
|
|
480
|
+
elif self.mode == "workflow_v2":
|
|
481
|
+
response = self.table.query(
|
|
482
|
+
IndexName="user_id-index",
|
|
483
|
+
KeyConditionExpression=Key("user_id").eq(user_id),
|
|
484
|
+
ProjectionExpression="session_id, workflow_id, user_id, workflow_name, runs, workflow_data, session_data, extra_data, created_at, updated_at",
|
|
485
|
+
ScanIndexForward=False,
|
|
486
|
+
Limit=limit if limit is not None else None,
|
|
487
|
+
)
|
|
440
488
|
elif entity_id is not None:
|
|
441
489
|
if self.mode == "agent":
|
|
442
490
|
response = self.table.query(
|
|
@@ -462,6 +510,14 @@ class DynamoDbStorage(Storage):
|
|
|
462
510
|
ScanIndexForward=False,
|
|
463
511
|
Limit=limit if limit is not None else None,
|
|
464
512
|
)
|
|
513
|
+
elif self.mode == "workflow_v2":
|
|
514
|
+
response = self.table.query(
|
|
515
|
+
IndexName="workflow_id-index",
|
|
516
|
+
KeyConditionExpression=Key("workflow_id").eq(entity_id),
|
|
517
|
+
ProjectionExpression="session_id, workflow_id, user_id, workflow_name, runs, workflow_data, session_data, extra_data, created_at, updated_at",
|
|
518
|
+
ScanIndexForward=False,
|
|
519
|
+
Limit=limit if limit is not None else None,
|
|
520
|
+
)
|
|
465
521
|
else:
|
|
466
522
|
# If no filters, scan the table and sort by created_at
|
|
467
523
|
if self.mode == "agent":
|
|
@@ -479,7 +535,11 @@ class DynamoDbStorage(Storage):
|
|
|
479
535
|
ProjectionExpression="session_id, workflow_id, user_id, memory, workflow_data, session_data, extra_data, created_at, updated_at",
|
|
480
536
|
Limit=limit if limit is not None else None,
|
|
481
537
|
)
|
|
482
|
-
|
|
538
|
+
elif self.mode == "workflow_v2":
|
|
539
|
+
response = self.table.scan(
|
|
540
|
+
ProjectionExpression="session_id, workflow_id, user_id, workflow_name, runs, workflow_data, session_data, extra_data, created_at, updated_at",
|
|
541
|
+
Limit=limit if limit is not None else None,
|
|
542
|
+
)
|
|
483
543
|
items = response.get("Items", [])
|
|
484
544
|
for item in items:
|
|
485
545
|
item = self._deserialize_item(item)
|
|
@@ -491,7 +551,8 @@ class DynamoDbStorage(Storage):
|
|
|
491
551
|
session = TeamSession.from_dict(item)
|
|
492
552
|
elif self.mode == "workflow":
|
|
493
553
|
session = WorkflowSession.from_dict(item)
|
|
494
|
-
|
|
554
|
+
elif self.mode == "workflow_v2":
|
|
555
|
+
session = WorkflowSessionV2.from_dict(item)
|
|
495
556
|
if session is not None:
|
|
496
557
|
sessions.append(session)
|
|
497
558
|
|
|
@@ -511,7 +572,10 @@ class DynamoDbStorage(Storage):
|
|
|
511
572
|
Optional[Session]: The upserted Session, or None if operation failed.
|
|
512
573
|
"""
|
|
513
574
|
try:
|
|
514
|
-
|
|
575
|
+
if self.mode == "workflow_v2":
|
|
576
|
+
item = session.to_dict()
|
|
577
|
+
else:
|
|
578
|
+
item = asdict(session)
|
|
515
579
|
|
|
516
580
|
# Add timestamps
|
|
517
581
|
current_time = int(time.time())
|
agno/storage/firestore.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, logger
|
|
11
12
|
|
|
@@ -29,7 +30,7 @@ class FirestoreStorage(Storage):
|
|
|
29
30
|
db_name: Optional[str] = "(default)",
|
|
30
31
|
project_id: Optional[str] = None,
|
|
31
32
|
client: Optional[Client] = None,
|
|
32
|
-
mode: Optional[Literal["agent", "team", "workflow"]] = "agent",
|
|
33
|
+
mode: Optional[Literal["agent", "team", "workflow", "workflow_v2"]] = "agent",
|
|
33
34
|
):
|
|
34
35
|
super().__init__(mode)
|
|
35
36
|
self.collection_name = collection_name
|
|
@@ -94,6 +95,8 @@ class FirestoreStorage(Storage):
|
|
|
94
95
|
query = query.where(filter=FieldFilter("team_id", "==", entity_id))
|
|
95
96
|
elif self.mode == "workflow":
|
|
96
97
|
query = query.where(filter=FieldFilter("workflow_id", "==", entity_id))
|
|
98
|
+
elif self.mode == "workflow_v2":
|
|
99
|
+
query = query.where(filter=FieldFilter("workflow_id", "==", entity_id))
|
|
97
100
|
|
|
98
101
|
return query
|
|
99
102
|
|
|
@@ -109,6 +112,8 @@ class FirestoreStorage(Storage):
|
|
|
109
112
|
return TeamSession.from_dict(doc_data)
|
|
110
113
|
elif self.mode == "workflow":
|
|
111
114
|
return WorkflowSession.from_dict(doc_data)
|
|
115
|
+
elif self.mode == "workflow_v2":
|
|
116
|
+
return WorkflowSessionV2.from_dict(doc_data)
|
|
112
117
|
except Exception as e:
|
|
113
118
|
logger.error(f"Error parsing session data: {e}")
|
|
114
119
|
return None
|
agno/storage/gcs_json.py
CHANGED
|
@@ -6,6 +6,7 @@ from agno.storage.json import JsonStorage, 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 logger
|
|
11
12
|
|
|
@@ -35,7 +36,7 @@ class GCSJsonStorage(JsonStorage):
|
|
|
35
36
|
self,
|
|
36
37
|
bucket_name: str,
|
|
37
38
|
prefix: Optional[str] = "",
|
|
38
|
-
mode: Optional[Literal["agent", "team", "workflow"]] = "agent",
|
|
39
|
+
mode: Optional[Literal["agent", "team", "workflow", "workflow_v2"]] = "agent",
|
|
39
40
|
project: Optional[str] = None,
|
|
40
41
|
location: Optional[str] = None,
|
|
41
42
|
credentials: Optional[Any] = None,
|
|
@@ -104,6 +105,8 @@ class GCSJsonStorage(JsonStorage):
|
|
|
104
105
|
return TeamSession.from_dict(data)
|
|
105
106
|
elif self.mode == "workflow":
|
|
106
107
|
return WorkflowSession.from_dict(data)
|
|
108
|
+
elif self.mode == "workflow_v2":
|
|
109
|
+
return WorkflowSessionV2.from_dict(data)
|
|
107
110
|
return None
|
|
108
111
|
|
|
109
112
|
def get_all_session_ids(self, user_id: Optional[str] = None, entity_id: Optional[str] = None) -> List[str]:
|
|
@@ -136,6 +139,8 @@ class GCSJsonStorage(JsonStorage):
|
|
|
136
139
|
session = TeamSession.from_dict(data)
|
|
137
140
|
elif self.mode == "workflow":
|
|
138
141
|
session = WorkflowSession.from_dict(data)
|
|
142
|
+
elif self.mode == "workflow_v2":
|
|
143
|
+
session = WorkflowSessionV2.from_dict(data)
|
|
139
144
|
if session is not None:
|
|
140
145
|
sessions.append(session)
|
|
141
146
|
except Exception as e:
|
|
@@ -199,7 +204,8 @@ class GCSJsonStorage(JsonStorage):
|
|
|
199
204
|
session = TeamSession.from_dict(data)
|
|
200
205
|
elif self.mode == "workflow":
|
|
201
206
|
session = WorkflowSession.from_dict(data)
|
|
202
|
-
|
|
207
|
+
elif self.mode == "workflow_v2":
|
|
208
|
+
session = WorkflowSessionV2.from_dict(data)
|
|
203
209
|
if session is not None:
|
|
204
210
|
sessions.append(session)
|
|
205
211
|
|
agno/storage/json.py
CHANGED
|
@@ -8,12 +8,15 @@ from agno.storage.base import Storage
|
|
|
8
8
|
from agno.storage.session import Session
|
|
9
9
|
from agno.storage.session.agent import AgentSession
|
|
10
10
|
from agno.storage.session.team import TeamSession
|
|
11
|
+
from agno.storage.session.v2.workflow import WorkflowSession as WorkflowSessionV2
|
|
11
12
|
from agno.storage.session.workflow import WorkflowSession
|
|
12
13
|
from agno.utils.log import logger
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class JsonStorage(Storage):
|
|
16
|
-
def __init__(
|
|
17
|
+
def __init__(
|
|
18
|
+
self, dir_path: Union[str, Path], mode: Optional[Literal["agent", "team", "workflow", "workflow_v2"]] = "agent"
|
|
19
|
+
):
|
|
17
20
|
super().__init__(mode)
|
|
18
21
|
self.dir_path = Path(dir_path)
|
|
19
22
|
self.dir_path.mkdir(parents=True, exist_ok=True)
|
|
@@ -42,6 +45,8 @@ class JsonStorage(Storage):
|
|
|
42
45
|
return TeamSession.from_dict(data)
|
|
43
46
|
elif self.mode == "workflow":
|
|
44
47
|
return WorkflowSession.from_dict(data)
|
|
48
|
+
elif self.mode == "workflow_v2":
|
|
49
|
+
return WorkflowSessionV2.from_dict(data)
|
|
45
50
|
except FileNotFoundError:
|
|
46
51
|
return None
|
|
47
52
|
|
|
@@ -70,6 +75,8 @@ class JsonStorage(Storage):
|
|
|
70
75
|
session_ids.append(data["session_id"])
|
|
71
76
|
elif self.mode == "workflow" and data["workflow_id"] == entity_id:
|
|
72
77
|
session_ids.append(data["session_id"])
|
|
78
|
+
elif self.mode == "workflow_v2" and data["workflow_id"] == entity_id:
|
|
79
|
+
session_ids.append(data["session_id"])
|
|
73
80
|
else:
|
|
74
81
|
# No filters applied, add all session_ids
|
|
75
82
|
session_ids.append(data["session_id"])
|
|
@@ -107,7 +114,8 @@ class JsonStorage(Storage):
|
|
|
107
114
|
_session = TeamSession.from_dict(data)
|
|
108
115
|
elif self.mode == "workflow" and data["workflow_id"] == entity_id:
|
|
109
116
|
_session = WorkflowSession.from_dict(data)
|
|
110
|
-
|
|
117
|
+
elif self.mode == "workflow_v2" and data["workflow_id"] == entity_id:
|
|
118
|
+
_session = WorkflowSessionV2.from_dict(data)
|
|
111
119
|
if _session:
|
|
112
120
|
sessions.append(_session)
|
|
113
121
|
else:
|
|
@@ -118,6 +126,8 @@ class JsonStorage(Storage):
|
|
|
118
126
|
_session = TeamSession.from_dict(data)
|
|
119
127
|
elif self.mode == "workflow":
|
|
120
128
|
_session = WorkflowSession.from_dict(data)
|
|
129
|
+
elif self.mode == "workflow_v2":
|
|
130
|
+
_session = WorkflowSessionV2.from_dict(data)
|
|
121
131
|
if _session:
|
|
122
132
|
sessions.append(_session)
|
|
123
133
|
return sessions
|
|
@@ -158,7 +168,8 @@ class JsonStorage(Storage):
|
|
|
158
168
|
continue
|
|
159
169
|
elif self.mode == "workflow" and data["workflow_id"] != entity_id:
|
|
160
170
|
continue
|
|
161
|
-
|
|
171
|
+
elif self.mode == "workflow_v2" and data["workflow_id"] != entity_id:
|
|
172
|
+
continue
|
|
162
173
|
# Store with created_at for sorting
|
|
163
174
|
created_at = data.get("created_at", 0)
|
|
164
175
|
session_data.append((created_at, data))
|
|
@@ -181,7 +192,8 @@ class JsonStorage(Storage):
|
|
|
181
192
|
session = TeamSession.from_dict(data)
|
|
182
193
|
elif self.mode == "workflow":
|
|
183
194
|
session = WorkflowSession.from_dict(data)
|
|
184
|
-
|
|
195
|
+
elif self.mode == "workflow_v2":
|
|
196
|
+
session = WorkflowSessionV2.from_dict(data)
|
|
185
197
|
if session is not None:
|
|
186
198
|
sessions.append(session)
|
|
187
199
|
|
|
@@ -190,7 +202,10 @@ class JsonStorage(Storage):
|
|
|
190
202
|
def upsert(self, session: Session) -> Optional[Session]:
|
|
191
203
|
"""Insert or update a Session in storage."""
|
|
192
204
|
try:
|
|
193
|
-
|
|
205
|
+
if self.mode == "workflow_v2":
|
|
206
|
+
data = session.to_dict()
|
|
207
|
+
else:
|
|
208
|
+
data = asdict(session)
|
|
194
209
|
data["updated_at"] = int(time.time())
|
|
195
210
|
if "created_at" not in data:
|
|
196
211
|
data["created_at"] = data["updated_at"]
|
agno/storage/mongodb.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, logger
|
|
11
12
|
|
|
@@ -25,7 +26,7 @@ class MongoDbStorage(Storage):
|
|
|
25
26
|
db_url: Optional[str] = None,
|
|
26
27
|
db_name: str = "agno",
|
|
27
28
|
client: Optional[MongoClient] = None,
|
|
28
|
-
mode: Optional[Literal["agent", "team", "workflow"]] = "agent",
|
|
29
|
+
mode: Optional[Literal["agent", "team", "workflow", "workflow_v2"]] = "agent",
|
|
29
30
|
):
|
|
30
31
|
"""
|
|
31
32
|
This class provides agent storage using MongoDB.
|
|
@@ -64,6 +65,8 @@ class MongoDbStorage(Storage):
|
|
|
64
65
|
self.collection.create_index("team_id")
|
|
65
66
|
elif self.mode == "workflow":
|
|
66
67
|
self.collection.create_index("workflow_id")
|
|
68
|
+
elif self.mode == "workflow_v2":
|
|
69
|
+
self.collection.create_index("workflow_id")
|
|
67
70
|
except PyMongoError as e:
|
|
68
71
|
logger.error(f"Error creating indexes: {e}")
|
|
69
72
|
raise
|
|
@@ -91,6 +94,8 @@ class MongoDbStorage(Storage):
|
|
|
91
94
|
return TeamSession.from_dict(doc)
|
|
92
95
|
elif self.mode == "workflow":
|
|
93
96
|
return WorkflowSession.from_dict(doc)
|
|
97
|
+
elif self.mode == "workflow_v2":
|
|
98
|
+
return WorkflowSessionV2.from_dict(doc)
|
|
94
99
|
return None
|
|
95
100
|
except PyMongoError as e:
|
|
96
101
|
logger.error(f"Error reading session: {e}")
|
|
@@ -115,7 +120,8 @@ class MongoDbStorage(Storage):
|
|
|
115
120
|
query["team_id"] = entity_id
|
|
116
121
|
elif self.mode == "workflow":
|
|
117
122
|
query["workflow_id"] = entity_id
|
|
118
|
-
|
|
123
|
+
elif self.mode == "workflow_v2":
|
|
124
|
+
query["workflow_id"] = entity_id
|
|
119
125
|
cursor = self.collection.find(query, {"session_id": 1}).sort("created_at", -1)
|
|
120
126
|
|
|
121
127
|
return [str(doc["session_id"]) for doc in cursor]
|
|
@@ -142,7 +148,8 @@ class MongoDbStorage(Storage):
|
|
|
142
148
|
query["team_id"] = entity_id
|
|
143
149
|
elif self.mode == "workflow":
|
|
144
150
|
query["workflow_id"] = entity_id
|
|
145
|
-
|
|
151
|
+
elif self.mode == "workflow_v2":
|
|
152
|
+
query["workflow_id"] = entity_id
|
|
146
153
|
cursor = self.collection.find(query).sort("created_at", -1)
|
|
147
154
|
sessions: List[Session] = []
|
|
148
155
|
for doc in cursor:
|
|
@@ -193,7 +200,8 @@ class MongoDbStorage(Storage):
|
|
|
193
200
|
query["team_id"] = entity_id
|
|
194
201
|
elif self.mode == "workflow":
|
|
195
202
|
query["workflow_id"] = entity_id
|
|
196
|
-
|
|
203
|
+
elif self.mode == "workflow_v2":
|
|
204
|
+
query["workflow_id"] = entity_id
|
|
197
205
|
# Execute query with sort and limit
|
|
198
206
|
cursor = self.collection.find(query)
|
|
199
207
|
cursor = cursor.sort("created_at", -1) # Sort by created_at descending
|
|
@@ -212,7 +220,8 @@ class MongoDbStorage(Storage):
|
|
|
212
220
|
session = TeamSession.from_dict(doc)
|
|
213
221
|
elif self.mode == "workflow":
|
|
214
222
|
session = WorkflowSession.from_dict(doc)
|
|
215
|
-
|
|
223
|
+
elif self.mode == "workflow_v2":
|
|
224
|
+
session = WorkflowSessionV2.from_dict(doc)
|
|
216
225
|
if session is not None:
|
|
217
226
|
sessions.append(session)
|
|
218
227
|
|
agno/storage/mysql.py
CHANGED
|
@@ -5,6 +5,7 @@ from agno.storage.base import Storage
|
|
|
5
5
|
from agno.storage.session import Session
|
|
6
6
|
from agno.storage.session.agent import AgentSession
|
|
7
7
|
from agno.storage.session.team import TeamSession
|
|
8
|
+
from agno.storage.session.v2.workflow import WorkflowSession as WorkflowSessionV2
|
|
8
9
|
from agno.storage.session.workflow import WorkflowSession
|
|
9
10
|
from agno.utils.log import log_debug, log_info, log_warning, logger
|
|
10
11
|
|
|
@@ -29,7 +30,7 @@ class MySQLStorage(Storage):
|
|
|
29
30
|
db_engine: Optional[Engine] = None,
|
|
30
31
|
schema_version: int = 1,
|
|
31
32
|
auto_upgrade_schema: bool = False,
|
|
32
|
-
mode: Optional[Literal["agent", "team", "workflow"]] = "agent",
|
|
33
|
+
mode: Optional[Literal["agent", "team", "workflow", "workflow_v2"]] = "agent",
|
|
33
34
|
):
|
|
34
35
|
"""
|
|
35
36
|
This class provides agent storage using a MySQL table.
|
|
@@ -46,7 +47,7 @@ class MySQLStorage(Storage):
|
|
|
46
47
|
db_engine (Optional[Engine]): The SQLAlchemy database engine to use.
|
|
47
48
|
schema_version (int): Version of the schema. Defaults to 1.
|
|
48
49
|
auto_upgrade_schema (bool): Whether to automatically upgrade the schema.
|
|
49
|
-
mode (Optional[Literal["agent", "team", "workflow"]]): The mode of the storage.
|
|
50
|
+
mode (Optional[Literal["agent", "team", "workflow", "workflow_v2"]]): The mode of the storage.
|
|
50
51
|
Raises:
|
|
51
52
|
ValueError: If neither db_url nor db_engine is provided.
|
|
52
53
|
"""
|
|
@@ -79,12 +80,12 @@ class MySQLStorage(Storage):
|
|
|
79
80
|
log_debug(f"Created MySQLStorage: '{self.schema}.{self.table_name}'")
|
|
80
81
|
|
|
81
82
|
@property
|
|
82
|
-
def mode(self) -> Literal["agent", "team", "workflow"]:
|
|
83
|
+
def mode(self) -> Literal["agent", "team", "workflow", "workflow_v2"]:
|
|
83
84
|
"""Get the mode of the storage."""
|
|
84
85
|
return super().mode
|
|
85
86
|
|
|
86
87
|
@mode.setter
|
|
87
|
-
def mode(self, value: Optional[Literal["agent", "team", "workflow"]]) -> None:
|
|
88
|
+
def mode(self, value: Optional[Literal["agent", "team", "workflow", "workflow_v2"]]) -> None:
|
|
88
89
|
"""Set the mode and refresh the table if mode changes."""
|
|
89
90
|
super(MySQLStorage, type(self)).mode.fset(self, value) # type: ignore
|
|
90
91
|
if value is not None:
|
|
@@ -127,7 +128,11 @@ class MySQLStorage(Storage):
|
|
|
127
128
|
Column("workflow_id", String(255), index=True),
|
|
128
129
|
Column("workflow_data", JSON),
|
|
129
130
|
]
|
|
130
|
-
|
|
131
|
+
elif self.mode == "workflow_v2":
|
|
132
|
+
specific_columns = [
|
|
133
|
+
Column("workflow_id", String(255), index=True),
|
|
134
|
+
Column("workflow_data", JSON),
|
|
135
|
+
]
|
|
131
136
|
# Create table with all columns
|
|
132
137
|
table = Table(
|
|
133
138
|
self.table_name,
|
|
@@ -278,6 +283,8 @@ class MySQLStorage(Storage):
|
|
|
278
283
|
return TeamSession.from_dict(result._mapping) if result is not None else None
|
|
279
284
|
elif self.mode == "workflow":
|
|
280
285
|
return WorkflowSession.from_dict(result._mapping) if result is not None else None
|
|
286
|
+
elif self.mode == "workflow_v2":
|
|
287
|
+
return WorkflowSessionV2.from_dict(result._mapping) if result is not None else None
|
|
281
288
|
except Exception as e:
|
|
282
289
|
if "doesn't exist" in str(e) or "doesn't exist" in str(e):
|
|
283
290
|
log_debug(f"Table does not exist: {self.table.name}")
|
|
@@ -311,7 +318,8 @@ class MySQLStorage(Storage):
|
|
|
311
318
|
stmt = stmt.where(self.table.c.team_id == entity_id)
|
|
312
319
|
elif self.mode == "workflow":
|
|
313
320
|
stmt = stmt.where(self.table.c.workflow_id == entity_id)
|
|
314
|
-
|
|
321
|
+
elif self.mode == "workflow_v2":
|
|
322
|
+
stmt = stmt.where(self.table.c.workflow_id == entity_id)
|
|
315
323
|
# order by created_at desc
|
|
316
324
|
stmt = stmt.order_by(self.table.c.created_at.desc())
|
|
317
325
|
# execute query
|
|
@@ -346,7 +354,9 @@ class MySQLStorage(Storage):
|
|
|
346
354
|
stmt = stmt.where(self.table.c.agent_id == entity_id)
|
|
347
355
|
elif self.mode == "team":
|
|
348
356
|
stmt = stmt.where(self.table.c.team_id == entity_id)
|
|
349
|
-
|
|
357
|
+
elif self.mode == "workflow":
|
|
358
|
+
stmt = stmt.where(self.table.c.workflow_id == entity_id)
|
|
359
|
+
elif self.mode == "workflow_v2":
|
|
350
360
|
stmt = stmt.where(self.table.c.workflow_id == entity_id)
|
|
351
361
|
# order by created_at desc
|
|
352
362
|
stmt = stmt.order_by(self.table.c.created_at.desc())
|
|
@@ -357,8 +367,10 @@ class MySQLStorage(Storage):
|
|
|
357
367
|
return [AgentSession.from_dict(row._mapping) for row in rows] # type: ignore
|
|
358
368
|
elif self.mode == "team":
|
|
359
369
|
return [TeamSession.from_dict(row._mapping) for row in rows] # type: ignore
|
|
360
|
-
|
|
370
|
+
elif self.mode == "workflow":
|
|
361
371
|
return [WorkflowSession.from_dict(row._mapping) for row in rows] # type: ignore
|
|
372
|
+
elif self.mode == "workflow_v2":
|
|
373
|
+
return [WorkflowSessionV2.from_dict(row._mapping) for row in rows] # type: ignore[misc]
|
|
362
374
|
else:
|
|
363
375
|
return []
|
|
364
376
|
except Exception as e:
|
|
@@ -399,7 +411,8 @@ class MySQLStorage(Storage):
|
|
|
399
411
|
stmt = stmt.where(self.table.c.team_id == entity_id)
|
|
400
412
|
elif self.mode == "workflow":
|
|
401
413
|
stmt = stmt.where(self.table.c.workflow_id == entity_id)
|
|
402
|
-
|
|
414
|
+
elif self.mode == "workflow_v2":
|
|
415
|
+
stmt = stmt.where(self.table.c.workflow_id == entity_id)
|
|
403
416
|
# Order by created_at desc and limit results
|
|
404
417
|
stmt = stmt.order_by(self.table.c.created_at.desc())
|
|
405
418
|
if limit is not None:
|
|
@@ -417,7 +430,8 @@ class MySQLStorage(Storage):
|
|
|
417
430
|
session = TeamSession.from_dict(row._mapping) # type: ignore
|
|
418
431
|
elif self.mode == "workflow":
|
|
419
432
|
session = WorkflowSession.from_dict(row._mapping) # type: ignore
|
|
420
|
-
|
|
433
|
+
elif self.mode == "workflow_v2":
|
|
434
|
+
session = WorkflowSessionV2.from_dict(row._mapping) # type: ignore
|
|
421
435
|
if session is not None:
|
|
422
436
|
sessions.append(session)
|
|
423
437
|
return sessions
|
|
@@ -494,7 +508,7 @@ class MySQLStorage(Storage):
|
|
|
494
508
|
agent_id=session.agent_id, # type: ignore
|
|
495
509
|
team_session_id=session.team_session_id, # type: ignore
|
|
496
510
|
user_id=session.user_id,
|
|
497
|
-
memory=session
|
|
511
|
+
memory=getattr(session, "memory", None),
|
|
498
512
|
agent_data=session.agent_data, # type: ignore
|
|
499
513
|
session_data=session.session_data,
|
|
500
514
|
extra_data=session.extra_data,
|
|
@@ -505,7 +519,7 @@ class MySQLStorage(Storage):
|
|
|
505
519
|
agent_id=session.agent_id, # type: ignore
|
|
506
520
|
team_session_id=session.team_session_id, # type: ignore
|
|
507
521
|
user_id=session.user_id,
|
|
508
|
-
memory=session
|
|
522
|
+
memory=getattr(session, "memory", None),
|
|
509
523
|
agent_data=session.agent_data, # type: ignore
|
|
510
524
|
session_data=session.session_data,
|
|
511
525
|
extra_data=session.extra_data,
|
|
@@ -517,7 +531,7 @@ class MySQLStorage(Storage):
|
|
|
517
531
|
team_id=session.team_id, # type: ignore
|
|
518
532
|
user_id=session.user_id,
|
|
519
533
|
team_session_id=session.team_session_id, # type: ignore
|
|
520
|
-
memory=session
|
|
534
|
+
memory=getattr(session, "memory", None),
|
|
521
535
|
team_data=session.team_data, # type: ignore
|
|
522
536
|
session_data=session.session_data,
|
|
523
537
|
extra_data=session.extra_data,
|
|
@@ -528,18 +542,18 @@ class MySQLStorage(Storage):
|
|
|
528
542
|
team_id=session.team_id, # type: ignore
|
|
529
543
|
user_id=session.user_id,
|
|
530
544
|
team_session_id=session.team_session_id, # type: ignore
|
|
531
|
-
memory=session
|
|
545
|
+
memory=getattr(session, "memory", None),
|
|
532
546
|
team_data=session.team_data, # type: ignore
|
|
533
547
|
session_data=session.session_data,
|
|
534
548
|
extra_data=session.extra_data,
|
|
535
549
|
updated_at=int(time.time()),
|
|
536
550
|
)
|
|
537
|
-
|
|
551
|
+
elif self.mode == "workflow":
|
|
538
552
|
stmt = mysql.insert(self.table).values(
|
|
539
553
|
session_id=session.session_id,
|
|
540
554
|
workflow_id=session.workflow_id, # type: ignore
|
|
541
555
|
user_id=session.user_id,
|
|
542
|
-
memory=session
|
|
556
|
+
memory=getattr(session, "memory", None),
|
|
543
557
|
workflow_data=session.workflow_data, # type: ignore
|
|
544
558
|
session_data=session.session_data,
|
|
545
559
|
extra_data=session.extra_data,
|
|
@@ -549,13 +563,38 @@ class MySQLStorage(Storage):
|
|
|
549
563
|
stmt = stmt.on_duplicate_key_update(
|
|
550
564
|
workflow_id=session.workflow_id, # type: ignore
|
|
551
565
|
user_id=session.user_id,
|
|
552
|
-
memory=session
|
|
566
|
+
memory=getattr(session, "memory", None),
|
|
553
567
|
workflow_data=session.workflow_data, # type: ignore
|
|
554
568
|
session_data=session.session_data,
|
|
555
569
|
extra_data=session.extra_data,
|
|
556
570
|
updated_at=int(time.time()),
|
|
557
571
|
)
|
|
572
|
+
elif self.mode == "workflow_v2":
|
|
573
|
+
# Convert session to dict to ensure proper serialization
|
|
574
|
+
session_dict = session.to_dict()
|
|
558
575
|
|
|
576
|
+
stmt = mysql.insert(self.table).values(
|
|
577
|
+
session_id=session.session_id,
|
|
578
|
+
workflow_id=session.workflow_id, # type: ignore
|
|
579
|
+
workflow_name=session.workflow_name, # type: ignore
|
|
580
|
+
user_id=session.user_id,
|
|
581
|
+
runs=session_dict.get("runs"),
|
|
582
|
+
workflow_data=session.workflow_data, # type: ignore
|
|
583
|
+
session_data=session.session_data,
|
|
584
|
+
extra_data=session.extra_data,
|
|
585
|
+
)
|
|
586
|
+
# Define the upsert if the session_id already exists
|
|
587
|
+
# See: https://docs.sqlalchemy.org/en/20/dialects/mysql.html#insert-on-duplicate-key-update
|
|
588
|
+
stmt = stmt.on_duplicate_key_update(
|
|
589
|
+
workflow_id=session.workflow_id, # type: ignore
|
|
590
|
+
workflow_name=session.workflow_name, # type: ignore
|
|
591
|
+
user_id=session.user_id,
|
|
592
|
+
runs=session_dict.get("runs"), # type: ignore
|
|
593
|
+
workflow_data=session.workflow_data, # type: ignore
|
|
594
|
+
session_data=session.session_data,
|
|
595
|
+
extra_data=session.extra_data,
|
|
596
|
+
updated_at=int(time.time()),
|
|
597
|
+
)
|
|
559
598
|
sess.execute(stmt)
|
|
560
599
|
except Exception as e:
|
|
561
600
|
if create_and_retry and not self.table_exists():
|