agno 2.2.1__py3-none-any.whl → 2.2.3__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 +735 -574
- agno/culture/manager.py +22 -24
- agno/db/async_postgres/__init__.py +1 -1
- agno/db/dynamo/dynamo.py +0 -2
- agno/db/firestore/firestore.py +0 -2
- agno/db/gcs_json/gcs_json_db.py +0 -4
- agno/db/gcs_json/utils.py +0 -24
- agno/db/in_memory/in_memory_db.py +0 -3
- agno/db/json/json_db.py +4 -10
- agno/db/json/utils.py +0 -24
- agno/db/mongo/__init__.py +15 -1
- agno/db/mongo/async_mongo.py +1999 -0
- agno/db/mongo/mongo.py +0 -2
- agno/db/mysql/mysql.py +0 -3
- agno/db/postgres/__init__.py +1 -1
- agno/db/{async_postgres → postgres}/async_postgres.py +19 -22
- agno/db/postgres/postgres.py +7 -10
- agno/db/postgres/utils.py +106 -2
- agno/db/redis/redis.py +0 -2
- agno/db/singlestore/singlestore.py +0 -3
- agno/db/sqlite/__init__.py +2 -1
- agno/db/sqlite/async_sqlite.py +2269 -0
- agno/db/sqlite/sqlite.py +0 -2
- agno/db/sqlite/utils.py +96 -0
- agno/db/surrealdb/surrealdb.py +0 -6
- agno/knowledge/knowledge.py +3 -3
- agno/knowledge/reader/reader_factory.py +16 -0
- agno/knowledge/reader/tavily_reader.py +194 -0
- agno/memory/manager.py +28 -25
- agno/models/anthropic/claude.py +63 -6
- agno/models/base.py +251 -32
- agno/models/response.py +69 -0
- agno/os/router.py +7 -5
- agno/os/routers/memory/memory.py +2 -1
- agno/os/routers/memory/schemas.py +5 -2
- agno/os/schema.py +25 -20
- agno/os/utils.py +9 -2
- agno/run/agent.py +23 -30
- agno/run/base.py +17 -1
- agno/run/team.py +23 -29
- agno/run/workflow.py +17 -12
- agno/session/agent.py +3 -0
- agno/session/summary.py +4 -1
- agno/session/team.py +1 -1
- agno/team/team.py +599 -367
- agno/tools/dalle.py +2 -4
- agno/tools/eleven_labs.py +23 -25
- agno/tools/function.py +40 -0
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +324 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/slack.py +18 -3
- agno/tools/tavily.py +146 -0
- agno/utils/agent.py +366 -1
- agno/utils/mcp.py +92 -2
- agno/utils/media.py +166 -1
- agno/utils/print_response/workflow.py +17 -1
- agno/utils/team.py +89 -1
- agno/workflow/step.py +0 -1
- agno/workflow/types.py +10 -15
- {agno-2.2.1.dist-info → agno-2.2.3.dist-info}/METADATA +28 -25
- {agno-2.2.1.dist-info → agno-2.2.3.dist-info}/RECORD +66 -62
- agno/db/async_postgres/schemas.py +0 -139
- agno/db/async_postgres/utils.py +0 -347
- agno/tools/mcp.py +0 -679
- {agno-2.2.1.dist-info → agno-2.2.3.dist-info}/WHEEL +0 -0
- {agno-2.2.1.dist-info → agno-2.2.3.dist-info}/licenses/LICENSE +0 -0
- {agno-2.2.1.dist-info → agno-2.2.3.dist-info}/top_level.txt +0 -0
agno/os/router.py
CHANGED
|
@@ -1082,7 +1082,8 @@ def get_base_router(
|
|
|
1082
1082
|
|
|
1083
1083
|
agents = []
|
|
1084
1084
|
for agent in os.agents:
|
|
1085
|
-
|
|
1085
|
+
agent_response = await AgentResponse.from_agent(agent=agent)
|
|
1086
|
+
agents.append(agent_response)
|
|
1086
1087
|
|
|
1087
1088
|
return agents
|
|
1088
1089
|
|
|
@@ -1129,7 +1130,7 @@ def get_base_router(
|
|
|
1129
1130
|
if agent is None:
|
|
1130
1131
|
raise HTTPException(status_code=404, detail="Agent not found")
|
|
1131
1132
|
|
|
1132
|
-
return AgentResponse.from_agent(agent)
|
|
1133
|
+
return await AgentResponse.from_agent(agent)
|
|
1133
1134
|
|
|
1134
1135
|
# -- Team routes ---
|
|
1135
1136
|
|
|
@@ -1414,7 +1415,8 @@ def get_base_router(
|
|
|
1414
1415
|
|
|
1415
1416
|
teams = []
|
|
1416
1417
|
for team in os.teams:
|
|
1417
|
-
|
|
1418
|
+
team_response = await TeamResponse.from_team(team=team)
|
|
1419
|
+
teams.append(team_response)
|
|
1418
1420
|
|
|
1419
1421
|
return teams
|
|
1420
1422
|
|
|
@@ -1507,7 +1509,7 @@ def get_base_router(
|
|
|
1507
1509
|
if team is None:
|
|
1508
1510
|
raise HTTPException(status_code=404, detail="Team not found")
|
|
1509
1511
|
|
|
1510
|
-
return TeamResponse.from_team(team)
|
|
1512
|
+
return await TeamResponse.from_team(team)
|
|
1511
1513
|
|
|
1512
1514
|
# -- Workflow routes ---
|
|
1513
1515
|
|
|
@@ -1580,7 +1582,7 @@ def get_base_router(
|
|
|
1580
1582
|
if workflow is None:
|
|
1581
1583
|
raise HTTPException(status_code=404, detail="Workflow not found")
|
|
1582
1584
|
|
|
1583
|
-
return WorkflowResponse.from_workflow(workflow)
|
|
1585
|
+
return await WorkflowResponse.from_workflow(workflow)
|
|
1584
1586
|
|
|
1585
1587
|
@router.post(
|
|
1586
1588
|
"/workflows/{workflow_id}/runs",
|
agno/os/routers/memory/memory.py
CHANGED
|
@@ -251,8 +251,9 @@ def attach_routes(router: APIRouter, dbs: dict[str, Union[BaseDb, AsyncBaseDb]])
|
|
|
251
251
|
deserialize=False,
|
|
252
252
|
)
|
|
253
253
|
|
|
254
|
+
memories = [UserMemorySchema.from_dict(user_memory) for user_memory in user_memories] # type: ignore
|
|
254
255
|
return PaginatedResponse(
|
|
255
|
-
data=[
|
|
256
|
+
data=[memory for memory in memories if memory is not None],
|
|
256
257
|
meta=PaginationInfo(
|
|
257
258
|
page=page,
|
|
258
259
|
limit=limit,
|
|
@@ -11,7 +11,7 @@ class DeleteMemoriesRequest(BaseModel):
|
|
|
11
11
|
|
|
12
12
|
class UserMemorySchema(BaseModel):
|
|
13
13
|
memory_id: str = Field(..., description="Unique identifier for the memory")
|
|
14
|
-
memory: str = Field(..., description="Memory content text"
|
|
14
|
+
memory: str = Field(..., description="Memory content text")
|
|
15
15
|
topics: Optional[List[str]] = Field(None, description="Topics or tags associated with the memory")
|
|
16
16
|
|
|
17
17
|
agent_id: Optional[str] = Field(None, description="Agent ID associated with this memory")
|
|
@@ -21,7 +21,10 @@ class UserMemorySchema(BaseModel):
|
|
|
21
21
|
updated_at: Optional[datetime] = Field(None, description="Timestamp when memory was last updated")
|
|
22
22
|
|
|
23
23
|
@classmethod
|
|
24
|
-
def from_dict(cls, memory_dict: Dict[str, Any]) -> "UserMemorySchema":
|
|
24
|
+
def from_dict(cls, memory_dict: Dict[str, Any]) -> Optional["UserMemorySchema"]:
|
|
25
|
+
if memory_dict["memory"] == "":
|
|
26
|
+
return None
|
|
27
|
+
|
|
25
28
|
return cls(
|
|
26
29
|
memory_id=memory_dict["memory_id"],
|
|
27
30
|
user_id=str(memory_dict["user_id"]),
|
agno/os/schema.py
CHANGED
|
@@ -183,7 +183,7 @@ class AgentResponse(BaseModel):
|
|
|
183
183
|
metadata: Optional[Dict[str, Any]] = Field(None, description="Additional metadata")
|
|
184
184
|
|
|
185
185
|
@classmethod
|
|
186
|
-
def from_agent(cls, agent: Agent) -> "AgentResponse":
|
|
186
|
+
async def from_agent(cls, agent: Agent) -> "AgentResponse":
|
|
187
187
|
def filter_meaningful_config(d: Dict[str, Any], defaults: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
188
188
|
"""Filter out fields that match their default values, keeping only meaningful user configurations"""
|
|
189
189
|
filtered = {}
|
|
@@ -243,10 +243,10 @@ class AgentResponse(BaseModel):
|
|
|
243
243
|
"stream_intermediate_steps": False,
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
agent_tools = agent.
|
|
246
|
+
agent_tools = await agent.aget_tools(
|
|
247
247
|
session=AgentSession(session_id=str(uuid4()), session_data={}),
|
|
248
248
|
run_response=RunOutput(run_id=str(uuid4())),
|
|
249
|
-
|
|
249
|
+
check_mcp_tools=False,
|
|
250
250
|
)
|
|
251
251
|
formatted_tools = format_tools(agent_tools) if agent_tools else None
|
|
252
252
|
|
|
@@ -415,7 +415,7 @@ class TeamResponse(BaseModel):
|
|
|
415
415
|
metadata: Optional[Dict[str, Any]] = Field(None, description="Additional metadata")
|
|
416
416
|
|
|
417
417
|
@classmethod
|
|
418
|
-
def from_team(cls, team: Team) -> "TeamResponse":
|
|
418
|
+
async def from_team(cls, team: Team) -> "TeamResponse":
|
|
419
419
|
def filter_meaningful_config(d: Dict[str, Any], defaults: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
420
420
|
"""Filter out fields that match their default values, keeping only meaningful user configurations"""
|
|
421
421
|
filtered = {}
|
|
@@ -466,15 +466,16 @@ class TeamResponse(BaseModel):
|
|
|
466
466
|
"stream_member_events": False,
|
|
467
467
|
}
|
|
468
468
|
|
|
469
|
-
team.
|
|
469
|
+
_tools = team._determine_tools_for_model(
|
|
470
470
|
model=team.model, # type: ignore
|
|
471
471
|
session=TeamSession(session_id=str(uuid4()), session_data={}),
|
|
472
472
|
run_response=TeamRunOutput(run_id=str(uuid4())),
|
|
473
473
|
async_mode=True,
|
|
474
474
|
session_state={},
|
|
475
475
|
team_run_context={},
|
|
476
|
+
check_mcp_tools=False,
|
|
476
477
|
)
|
|
477
|
-
team_tools =
|
|
478
|
+
team_tools = _tools
|
|
478
479
|
formatted_tools = format_team_tools(team_tools) if team_tools else None
|
|
479
480
|
|
|
480
481
|
model_name = team.model.name or team.model.__class__.__name__ if team.model else None
|
|
@@ -595,6 +596,15 @@ class TeamResponse(BaseModel):
|
|
|
595
596
|
if team.model and team.model.provider is not None:
|
|
596
597
|
_team_model_data["provider"] = team.model.provider
|
|
597
598
|
|
|
599
|
+
members: List[Union[AgentResponse, TeamResponse]] = []
|
|
600
|
+
for member in team.members:
|
|
601
|
+
if isinstance(member, Agent):
|
|
602
|
+
agent_response = await AgentResponse.from_agent(member)
|
|
603
|
+
members.append(agent_response)
|
|
604
|
+
if isinstance(member, Team):
|
|
605
|
+
team_response = await TeamResponse.from_team(member)
|
|
606
|
+
members.append(team_response)
|
|
607
|
+
|
|
598
608
|
return TeamResponse(
|
|
599
609
|
id=team.id,
|
|
600
610
|
name=team.name,
|
|
@@ -609,14 +619,7 @@ class TeamResponse(BaseModel):
|
|
|
609
619
|
system_message=filter_meaningful_config(system_message_info, team_defaults),
|
|
610
620
|
response_settings=filter_meaningful_config(response_settings_info, team_defaults),
|
|
611
621
|
streaming=filter_meaningful_config(streaming_info, team_defaults),
|
|
612
|
-
members=
|
|
613
|
-
AgentResponse.from_agent(member)
|
|
614
|
-
if isinstance(member, Agent)
|
|
615
|
-
else TeamResponse.from_team(member)
|
|
616
|
-
if isinstance(member, Team)
|
|
617
|
-
else None
|
|
618
|
-
for member in team.members
|
|
619
|
-
],
|
|
622
|
+
members=members if members else None,
|
|
620
623
|
metadata=team.metadata,
|
|
621
624
|
)
|
|
622
625
|
|
|
@@ -633,7 +636,7 @@ class WorkflowResponse(BaseModel):
|
|
|
633
636
|
metadata: Optional[Dict[str, Any]] = Field(None, description="Additional metadata")
|
|
634
637
|
|
|
635
638
|
@classmethod
|
|
636
|
-
def _resolve_agents_and_teams_recursively(cls, steps: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
639
|
+
async def _resolve_agents_and_teams_recursively(cls, steps: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
637
640
|
"""Parse Agents and Teams into AgentResponse and TeamResponse objects.
|
|
638
641
|
|
|
639
642
|
If the given steps have nested steps, recursively work on those."""
|
|
@@ -651,13 +654,15 @@ class WorkflowResponse(BaseModel):
|
|
|
651
654
|
for idx, step in enumerate(steps):
|
|
652
655
|
if step.get("agent"):
|
|
653
656
|
# Convert to dict and exclude fields that are None
|
|
654
|
-
|
|
657
|
+
agent_response = await AgentResponse.from_agent(step.get("agent")) # type: ignore
|
|
658
|
+
step["agent"] = agent_response.model_dump(exclude_none=True)
|
|
655
659
|
|
|
656
660
|
if step.get("team"):
|
|
657
|
-
|
|
661
|
+
team_response = await TeamResponse.from_team(step.get("team")) # type: ignore
|
|
662
|
+
step["team"] = team_response.model_dump(exclude_none=True)
|
|
658
663
|
|
|
659
664
|
if step.get("steps"):
|
|
660
|
-
step["steps"] = cls._resolve_agents_and_teams_recursively(step["steps"])
|
|
665
|
+
step["steps"] = await cls._resolve_agents_and_teams_recursively(step["steps"])
|
|
661
666
|
|
|
662
667
|
# Prune None values in the entire step
|
|
663
668
|
steps[idx] = _prune_none(step)
|
|
@@ -665,12 +670,12 @@ class WorkflowResponse(BaseModel):
|
|
|
665
670
|
return steps
|
|
666
671
|
|
|
667
672
|
@classmethod
|
|
668
|
-
def from_workflow(cls, workflow: Workflow) -> "WorkflowResponse":
|
|
673
|
+
async def from_workflow(cls, workflow: Workflow) -> "WorkflowResponse":
|
|
669
674
|
workflow_dict = workflow.to_dict()
|
|
670
675
|
steps = workflow_dict.get("steps")
|
|
671
676
|
|
|
672
677
|
if steps:
|
|
673
|
-
steps = cls._resolve_agents_and_teams_recursively(steps)
|
|
678
|
+
steps = await cls._resolve_agents_and_teams_recursively(steps)
|
|
674
679
|
|
|
675
680
|
return cls(
|
|
676
681
|
id=workflow.id,
|
agno/os/utils.py
CHANGED
|
@@ -233,8 +233,15 @@ def format_tools(agent_tools: List[Union[Dict[str, Any], Toolkit, Function, Call
|
|
|
233
233
|
return formatted_tools
|
|
234
234
|
|
|
235
235
|
|
|
236
|
-
def format_team_tools(team_tools: List[Function]):
|
|
237
|
-
|
|
236
|
+
def format_team_tools(team_tools: List[Union[Function, dict]]):
|
|
237
|
+
formatted_tools: List[Dict] = []
|
|
238
|
+
if team_tools is not None:
|
|
239
|
+
for tool in team_tools:
|
|
240
|
+
if isinstance(tool, dict):
|
|
241
|
+
formatted_tools.append(tool)
|
|
242
|
+
elif isinstance(tool, Function):
|
|
243
|
+
formatted_tools.append(tool.to_dict())
|
|
244
|
+
return formatted_tools
|
|
238
245
|
|
|
239
246
|
|
|
240
247
|
def get_agent_by_id(agent_id: str, agents: Optional[List[Agent]] = None) -> Optional[Agent]:
|
agno/run/agent.py
CHANGED
|
@@ -12,6 +12,13 @@ from agno.models.response import ToolExecution
|
|
|
12
12
|
from agno.reasoning.step import ReasoningStep
|
|
13
13
|
from agno.run.base import BaseRunOutputEvent, MessageReferences, RunStatus
|
|
14
14
|
from agno.utils.log import logger
|
|
15
|
+
from agno.utils.media import (
|
|
16
|
+
reconstruct_audio_list,
|
|
17
|
+
reconstruct_files,
|
|
18
|
+
reconstruct_images,
|
|
19
|
+
reconstruct_response_audio,
|
|
20
|
+
reconstruct_videos,
|
|
21
|
+
)
|
|
15
22
|
|
|
16
23
|
if TYPE_CHECKING:
|
|
17
24
|
from agno.session.summary import SessionSummary
|
|
@@ -86,21 +93,10 @@ class RunInput:
|
|
|
86
93
|
@classmethod
|
|
87
94
|
def from_dict(cls, data: Dict[str, Any]) -> "RunInput":
|
|
88
95
|
"""Create RunInput from dictionary"""
|
|
89
|
-
images =
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
videos = None
|
|
94
|
-
if data.get("videos"):
|
|
95
|
-
videos = [Video.model_validate(vid_data) for vid_data in data["videos"]]
|
|
96
|
-
|
|
97
|
-
audios = None
|
|
98
|
-
if data.get("audios"):
|
|
99
|
-
audios = [Audio.model_validate(aud_data) for aud_data in data["audios"]]
|
|
100
|
-
|
|
101
|
-
files = None
|
|
102
|
-
if data.get("files"):
|
|
103
|
-
files = [File.model_validate(file_data) for file_data in data["files"]]
|
|
96
|
+
images = reconstruct_images(data.get("images"))
|
|
97
|
+
videos = reconstruct_videos(data.get("videos"))
|
|
98
|
+
audios = reconstruct_audio_list(data.get("audios"))
|
|
99
|
+
files = reconstruct_files(data.get("files"))
|
|
104
100
|
|
|
105
101
|
return cls(
|
|
106
102
|
input_content=data.get("input_content", ""), images=images, videos=videos, audios=audios, files=files
|
|
@@ -679,20 +675,11 @@ class RunOutput:
|
|
|
679
675
|
tools = data.pop("tools", [])
|
|
680
676
|
tools = [ToolExecution.from_dict(tool) for tool in tools] if tools else None
|
|
681
677
|
|
|
682
|
-
images = data.pop("images", [])
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
audio = data.pop("audio", [])
|
|
689
|
-
audio = [Audio.model_validate(audio) for audio in audio] if audio else None
|
|
690
|
-
|
|
691
|
-
files = data.pop("files", [])
|
|
692
|
-
files = [File.model_validate(file) for file in files] if files else None
|
|
693
|
-
|
|
694
|
-
response_audio = data.pop("response_audio", None)
|
|
695
|
-
response_audio = Audio.model_validate(response_audio) if response_audio else None
|
|
678
|
+
images = reconstruct_images(data.pop("images", []))
|
|
679
|
+
videos = reconstruct_videos(data.pop("videos", []))
|
|
680
|
+
audio = reconstruct_audio_list(data.pop("audio", []))
|
|
681
|
+
files = reconstruct_files(data.pop("files", []))
|
|
682
|
+
response_audio = reconstruct_response_audio(data.pop("response_audio", None))
|
|
696
683
|
|
|
697
684
|
input_data = data.pop("input", None)
|
|
698
685
|
input_obj = None
|
|
@@ -720,6 +707,12 @@ class RunOutput:
|
|
|
720
707
|
if references is not None:
|
|
721
708
|
references = [MessageReferences.model_validate(reference) for reference in references]
|
|
722
709
|
|
|
710
|
+
# Filter data to only include fields that are actually defined in the RunOutput dataclass
|
|
711
|
+
from dataclasses import fields
|
|
712
|
+
|
|
713
|
+
supported_fields = {f.name for f in fields(cls)}
|
|
714
|
+
filtered_data = {k: v for k, v in data.items() if k in supported_fields}
|
|
715
|
+
|
|
723
716
|
return cls(
|
|
724
717
|
messages=messages,
|
|
725
718
|
metrics=metrics,
|
|
@@ -736,7 +729,7 @@ class RunOutput:
|
|
|
736
729
|
reasoning_steps=reasoning_steps,
|
|
737
730
|
reasoning_messages=reasoning_messages,
|
|
738
731
|
references=references,
|
|
739
|
-
**
|
|
732
|
+
**filtered_data,
|
|
740
733
|
)
|
|
741
734
|
|
|
742
735
|
def get_content_as_string(self, **kwargs) -> str:
|
agno/run/base.py
CHANGED
|
@@ -35,6 +35,7 @@ class BaseRunOutputEvent:
|
|
|
35
35
|
"reasoning_steps",
|
|
36
36
|
"references",
|
|
37
37
|
"additional_input",
|
|
38
|
+
"session_summary",
|
|
38
39
|
"metrics",
|
|
39
40
|
]
|
|
40
41
|
}
|
|
@@ -113,6 +114,9 @@ class BaseRunOutputEvent:
|
|
|
113
114
|
if hasattr(self, "metrics") and self.metrics is not None:
|
|
114
115
|
_dict["metrics"] = self.metrics.to_dict()
|
|
115
116
|
|
|
117
|
+
if hasattr(self, "session_summary") and self.session_summary is not None:
|
|
118
|
+
_dict["session_summary"] = self.session_summary.to_dict()
|
|
119
|
+
|
|
116
120
|
return _dict
|
|
117
121
|
|
|
118
122
|
def to_json(self, separators=(", ", ": "), indent: Optional[int] = 2) -> str:
|
|
@@ -173,7 +177,19 @@ class BaseRunOutputEvent:
|
|
|
173
177
|
if metrics:
|
|
174
178
|
data["metrics"] = Metrics(**metrics)
|
|
175
179
|
|
|
176
|
-
|
|
180
|
+
session_summary = data.pop("session_summary", None)
|
|
181
|
+
if session_summary:
|
|
182
|
+
from agno.session.summary import SessionSummary
|
|
183
|
+
|
|
184
|
+
data["session_summary"] = SessionSummary.from_dict(session_summary)
|
|
185
|
+
|
|
186
|
+
# Filter data to only include fields that are actually defined in the target class
|
|
187
|
+
from dataclasses import fields
|
|
188
|
+
|
|
189
|
+
supported_fields = {f.name for f in fields(cls)}
|
|
190
|
+
filtered_data = {k: v for k, v in data.items() if k in supported_fields}
|
|
191
|
+
|
|
192
|
+
return cls(**filtered_data)
|
|
177
193
|
|
|
178
194
|
@property
|
|
179
195
|
def is_paused(self):
|
agno/run/team.py
CHANGED
|
@@ -13,6 +13,13 @@ from agno.reasoning.step import ReasoningStep
|
|
|
13
13
|
from agno.run.agent import RunEvent, RunOutput, RunOutputEvent, run_output_event_from_dict
|
|
14
14
|
from agno.run.base import BaseRunOutputEvent, MessageReferences, RunStatus
|
|
15
15
|
from agno.utils.log import log_error
|
|
16
|
+
from agno.utils.media import (
|
|
17
|
+
reconstruct_audio_list,
|
|
18
|
+
reconstruct_files,
|
|
19
|
+
reconstruct_images,
|
|
20
|
+
reconstruct_response_audio,
|
|
21
|
+
reconstruct_videos,
|
|
22
|
+
)
|
|
16
23
|
|
|
17
24
|
|
|
18
25
|
@dataclass
|
|
@@ -82,21 +89,10 @@ class TeamRunInput:
|
|
|
82
89
|
@classmethod
|
|
83
90
|
def from_dict(cls, data: Dict[str, Any]) -> "TeamRunInput":
|
|
84
91
|
"""Create TeamRunInput from dictionary"""
|
|
85
|
-
images =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
videos = None
|
|
90
|
-
if data.get("videos"):
|
|
91
|
-
videos = [Video.model_validate(vid_data) for vid_data in data["videos"]]
|
|
92
|
-
|
|
93
|
-
audios = None
|
|
94
|
-
if data.get("audios"):
|
|
95
|
-
audios = [Audio.model_validate(aud_data) for aud_data in data["audios"]]
|
|
96
|
-
|
|
97
|
-
files = None
|
|
98
|
-
if data.get("files"):
|
|
99
|
-
files = [File.model_validate(file_data) for file_data in data["files"]]
|
|
92
|
+
images = reconstruct_images(data.get("images"))
|
|
93
|
+
videos = reconstruct_videos(data.get("videos"))
|
|
94
|
+
audios = reconstruct_audio_list(data.get("audios"))
|
|
95
|
+
files = reconstruct_files(data.get("files"))
|
|
100
96
|
|
|
101
97
|
return cls(
|
|
102
98
|
input_content=data.get("input_content", ""), images=images, videos=videos, audios=audios, files=files
|
|
@@ -646,23 +642,15 @@ class TeamRunOutput:
|
|
|
646
642
|
if references is not None:
|
|
647
643
|
references = [MessageReferences.model_validate(reference) for reference in references]
|
|
648
644
|
|
|
649
|
-
images = data.pop("images", [])
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
videos = [Video.model_validate(video) for video in videos] if videos else None
|
|
654
|
-
|
|
655
|
-
audio = data.pop("audio", [])
|
|
656
|
-
audio = [Audio.model_validate(audio) for audio in audio] if audio else None
|
|
657
|
-
|
|
658
|
-
files = data.pop("files", [])
|
|
659
|
-
files = [File.model_validate(file) for file in files] if files else None
|
|
645
|
+
images = reconstruct_images(data.pop("images", []))
|
|
646
|
+
videos = reconstruct_videos(data.pop("videos", []))
|
|
647
|
+
audio = reconstruct_audio_list(data.pop("audio", []))
|
|
648
|
+
files = reconstruct_files(data.pop("files", []))
|
|
660
649
|
|
|
661
650
|
tools = data.pop("tools", [])
|
|
662
651
|
tools = [ToolExecution.from_dict(tool) for tool in tools] if tools else None
|
|
663
652
|
|
|
664
|
-
response_audio = data.pop("response_audio", None)
|
|
665
|
-
response_audio = Audio.model_validate(response_audio) if response_audio else None
|
|
653
|
+
response_audio = reconstruct_response_audio(data.pop("response_audio", None))
|
|
666
654
|
|
|
667
655
|
input_data = data.pop("input", None)
|
|
668
656
|
input_obj = None
|
|
@@ -676,6 +664,12 @@ class TeamRunOutput:
|
|
|
676
664
|
citations = data.pop("citations", None)
|
|
677
665
|
citations = Citations.model_validate(citations) if citations else None
|
|
678
666
|
|
|
667
|
+
# Filter data to only include fields that are actually defined in the TeamRunOutput dataclass
|
|
668
|
+
from dataclasses import fields
|
|
669
|
+
|
|
670
|
+
supported_fields = {f.name for f in fields(cls)}
|
|
671
|
+
filtered_data = {k: v for k, v in data.items() if k in supported_fields}
|
|
672
|
+
|
|
679
673
|
return cls(
|
|
680
674
|
messages=messages,
|
|
681
675
|
metrics=metrics,
|
|
@@ -693,7 +687,7 @@ class TeamRunOutput:
|
|
|
693
687
|
citations=citations,
|
|
694
688
|
tools=tools,
|
|
695
689
|
events=events,
|
|
696
|
-
**
|
|
690
|
+
**filtered_data,
|
|
697
691
|
)
|
|
698
692
|
|
|
699
693
|
def get_content_as_string(self, **kwargs) -> str:
|
agno/run/workflow.py
CHANGED
|
@@ -9,6 +9,12 @@ from agno.media import Audio, Image, Video
|
|
|
9
9
|
from agno.run.agent import RunEvent, RunOutput, run_output_event_from_dict
|
|
10
10
|
from agno.run.base import BaseRunOutputEvent, RunStatus
|
|
11
11
|
from agno.run.team import TeamRunEvent, TeamRunOutput, team_run_output_event_from_dict
|
|
12
|
+
from agno.utils.media import (
|
|
13
|
+
reconstruct_audio_list,
|
|
14
|
+
reconstruct_images,
|
|
15
|
+
reconstruct_response_audio,
|
|
16
|
+
reconstruct_videos,
|
|
17
|
+
)
|
|
12
18
|
|
|
13
19
|
if TYPE_CHECKING:
|
|
14
20
|
from agno.workflow.types import StepOutput, WorkflowMetrics
|
|
@@ -600,17 +606,10 @@ class WorkflowRunOutput:
|
|
|
600
606
|
|
|
601
607
|
metadata = data.pop("metadata", None)
|
|
602
608
|
|
|
603
|
-
images = data.pop("images", [])
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
videos = [Video.model_validate(video) for video in videos] if videos else None
|
|
608
|
-
|
|
609
|
-
audio = data.pop("audio", [])
|
|
610
|
-
audio = [Audio.model_validate(audio) for audio in audio] if audio else None
|
|
611
|
-
|
|
612
|
-
response_audio = data.pop("response_audio", None)
|
|
613
|
-
response_audio = Audio.model_validate(response_audio) if response_audio else None
|
|
609
|
+
images = reconstruct_images(data.pop("images", []))
|
|
610
|
+
videos = reconstruct_videos(data.pop("videos", []))
|
|
611
|
+
audio = reconstruct_audio_list(data.pop("audio", []))
|
|
612
|
+
response_audio = reconstruct_response_audio(data.pop("response_audio", None))
|
|
614
613
|
|
|
615
614
|
events_data = data.pop("events", [])
|
|
616
615
|
final_events = []
|
|
@@ -633,6 +632,12 @@ class WorkflowRunOutput:
|
|
|
633
632
|
|
|
634
633
|
input_data = data.pop("input", None)
|
|
635
634
|
|
|
635
|
+
# Filter data to only include fields that are actually defined in the WorkflowRunOutput dataclass
|
|
636
|
+
from dataclasses import fields
|
|
637
|
+
|
|
638
|
+
supported_fields = {f.name for f in fields(cls)}
|
|
639
|
+
filtered_data = {k: v for k, v in data.items() if k in supported_fields}
|
|
640
|
+
|
|
636
641
|
return cls(
|
|
637
642
|
step_results=parsed_step_results,
|
|
638
643
|
metadata=metadata,
|
|
@@ -644,7 +649,7 @@ class WorkflowRunOutput:
|
|
|
644
649
|
metrics=workflow_metrics,
|
|
645
650
|
step_executor_runs=step_executor_runs,
|
|
646
651
|
input=input_data,
|
|
647
|
-
**
|
|
652
|
+
**filtered_data,
|
|
648
653
|
)
|
|
649
654
|
|
|
650
655
|
def get_content_as_string(self, **kwargs) -> str:
|
agno/session/agent.py
CHANGED
|
@@ -140,6 +140,9 @@ class AgentSession:
|
|
|
140
140
|
if team_id:
|
|
141
141
|
session_runs = [run for run in session_runs if hasattr(run, "team_id") and run.team_id == team_id] # type: ignore
|
|
142
142
|
|
|
143
|
+
# Skip any messages that might be part of members of teams (for session re-use)
|
|
144
|
+
session_runs = [run for run in session_runs if run.parent_run_id is None] # type: ignore
|
|
145
|
+
|
|
143
146
|
# Filter by status
|
|
144
147
|
session_runs = [run for run in session_runs if hasattr(run, "status") and run.status not in skip_status] # type: ignore
|
|
145
148
|
|
agno/session/summary.py
CHANGED
|
@@ -66,6 +66,9 @@ class SessionSummaryManager:
|
|
|
66
66
|
# Prompt used for session summary generation
|
|
67
67
|
session_summary_prompt: Optional[str] = None
|
|
68
68
|
|
|
69
|
+
# User message prompt for requesting the summary
|
|
70
|
+
summary_request_message: str = "Provide the summary of the conversation."
|
|
71
|
+
|
|
69
72
|
# Whether session summaries were created in the last run
|
|
70
73
|
summaries_updated: bool = False
|
|
71
74
|
|
|
@@ -152,7 +155,7 @@ class SessionSummaryManager:
|
|
|
152
155
|
|
|
153
156
|
return [
|
|
154
157
|
system_message,
|
|
155
|
-
Message(role="user", content=
|
|
158
|
+
Message(role="user", content=self.summary_request_message),
|
|
156
159
|
]
|
|
157
160
|
|
|
158
161
|
def _process_summary_response(self, summary_response, session_summary_model: "Model") -> Optional[SessionSummary]: # type: ignore
|
agno/session/team.py
CHANGED
|
@@ -150,7 +150,7 @@ class TeamSession:
|
|
|
150
150
|
session_runs = [run for run in session_runs if hasattr(run, "team_id") and run.team_id == team_id] # type: ignore
|
|
151
151
|
|
|
152
152
|
if not member_runs:
|
|
153
|
-
# Filter for the main team runs
|
|
153
|
+
# Filter for the top-level runs (main team runs or agent runs when sharing session)
|
|
154
154
|
session_runs = [run for run in session_runs if run.parent_run_id is None] # type: ignore
|
|
155
155
|
# Filter by status
|
|
156
156
|
session_runs = [run for run in session_runs if hasattr(run, "status") and run.status not in skip_status] # type: ignore
|