agno 2.0.3__py3-none-any.whl → 2.0.5__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 +229 -164
- agno/db/dynamo/dynamo.py +8 -0
- agno/db/firestore/firestore.py +8 -0
- agno/db/gcs_json/gcs_json_db.py +9 -0
- agno/db/json/json_db.py +8 -0
- agno/db/migrations/v1_to_v2.py +191 -23
- agno/db/mongo/mongo.py +68 -0
- agno/db/mysql/mysql.py +13 -3
- agno/db/mysql/schemas.py +27 -27
- agno/db/postgres/postgres.py +19 -11
- agno/db/redis/redis.py +6 -0
- agno/db/singlestore/schemas.py +1 -1
- agno/db/singlestore/singlestore.py +8 -1
- agno/db/sqlite/sqlite.py +12 -3
- agno/integrations/discord/client.py +1 -0
- agno/knowledge/knowledge.py +92 -66
- agno/knowledge/reader/reader_factory.py +7 -3
- agno/knowledge/reader/web_search_reader.py +12 -6
- agno/models/base.py +2 -2
- agno/models/message.py +109 -0
- agno/models/openai/chat.py +3 -0
- agno/models/openai/responses.py +12 -0
- agno/models/response.py +5 -0
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +25 -0
- agno/os/app.py +164 -41
- agno/os/auth.py +24 -14
- agno/os/interfaces/agui/utils.py +98 -134
- agno/os/router.py +128 -55
- agno/os/routers/evals/utils.py +9 -9
- agno/os/routers/health.py +25 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/knowledge.py +11 -11
- agno/os/routers/session/session.py +24 -8
- agno/os/schema.py +29 -2
- agno/os/utils.py +0 -8
- agno/run/agent.py +3 -3
- agno/run/team.py +3 -3
- agno/run/workflow.py +64 -10
- agno/session/team.py +1 -0
- agno/team/team.py +189 -94
- agno/tools/duckduckgo.py +15 -11
- agno/tools/googlesearch.py +1 -1
- agno/tools/mem0.py +11 -17
- agno/tools/memory.py +34 -6
- agno/utils/common.py +90 -1
- agno/utils/streamlit.py +14 -8
- agno/utils/string.py +32 -0
- agno/utils/tools.py +1 -1
- agno/vectordb/chroma/chromadb.py +8 -2
- agno/workflow/step.py +115 -16
- agno/workflow/workflow.py +16 -13
- {agno-2.0.3.dist-info → agno-2.0.5.dist-info}/METADATA +6 -5
- {agno-2.0.3.dist-info → agno-2.0.5.dist-info}/RECORD +57 -54
- agno/knowledge/reader/url_reader.py +0 -128
- {agno-2.0.3.dist-info → agno-2.0.5.dist-info}/WHEEL +0 -0
- {agno-2.0.3.dist-info → agno-2.0.5.dist-info}/licenses/LICENSE +0 -0
- {agno-2.0.3.dist-info → agno-2.0.5.dist-info}/top_level.txt +0 -0
agno/os/routers/evals/utils.py
CHANGED
|
@@ -35,7 +35,7 @@ async def run_accuracy_eval(
|
|
|
35
35
|
name=eval_run_input.name,
|
|
36
36
|
)
|
|
37
37
|
|
|
38
|
-
result = accuracy_eval.
|
|
38
|
+
result = await accuracy_eval.arun(print_results=False, print_summary=False)
|
|
39
39
|
if not result:
|
|
40
40
|
raise HTTPException(status_code=500, detail="Failed to run accuracy evaluation")
|
|
41
41
|
|
|
@@ -60,16 +60,16 @@ async def run_performance_eval(
|
|
|
60
60
|
"""Run a performance evaluation for the given agent or team"""
|
|
61
61
|
if agent:
|
|
62
62
|
|
|
63
|
-
def run_component(): # type: ignore
|
|
64
|
-
return agent.
|
|
63
|
+
async def run_component(): # type: ignore
|
|
64
|
+
return await agent.arun(eval_run_input.input)
|
|
65
65
|
|
|
66
66
|
model_id = agent.model.id if agent and agent.model else None
|
|
67
67
|
model_provider = agent.model.provider if agent and agent.model else None
|
|
68
68
|
|
|
69
69
|
elif team:
|
|
70
70
|
|
|
71
|
-
def run_component():
|
|
72
|
-
return team.
|
|
71
|
+
async def run_component():
|
|
72
|
+
return await team.arun(eval_run_input.input)
|
|
73
73
|
|
|
74
74
|
model_id = team.model.id if team and team.model else None
|
|
75
75
|
model_provider = team.model.provider if team and team.model else None
|
|
@@ -85,7 +85,7 @@ async def run_performance_eval(
|
|
|
85
85
|
model_id=model_id,
|
|
86
86
|
model_provider=model_provider,
|
|
87
87
|
)
|
|
88
|
-
result = performance_eval.
|
|
88
|
+
result = await performance_eval.arun(print_results=False, print_summary=False)
|
|
89
89
|
if not result:
|
|
90
90
|
raise HTTPException(status_code=500, detail="Failed to run performance evaluation")
|
|
91
91
|
|
|
@@ -119,7 +119,7 @@ async def run_reliability_eval(
|
|
|
119
119
|
raise HTTPException(status_code=400, detail="expected_tool_calls is required for reliability evaluations")
|
|
120
120
|
|
|
121
121
|
if agent:
|
|
122
|
-
agent_response = agent.
|
|
122
|
+
agent_response = await agent.arun(eval_run_input.input)
|
|
123
123
|
reliability_eval = ReliabilityEval(
|
|
124
124
|
db=db,
|
|
125
125
|
name=eval_run_input.name,
|
|
@@ -130,7 +130,7 @@ async def run_reliability_eval(
|
|
|
130
130
|
model_provider = agent.model.provider if agent and agent.model else None
|
|
131
131
|
|
|
132
132
|
elif team:
|
|
133
|
-
team_response = team.
|
|
133
|
+
team_response = await team.arun(eval_run_input.input)
|
|
134
134
|
reliability_eval = ReliabilityEval(
|
|
135
135
|
db=db,
|
|
136
136
|
name=eval_run_input.name,
|
|
@@ -140,7 +140,7 @@ async def run_reliability_eval(
|
|
|
140
140
|
model_id = team.model.id if team and team.model else None
|
|
141
141
|
model_provider = team.model.provider if team and team.model else None
|
|
142
142
|
|
|
143
|
-
result = reliability_eval.
|
|
143
|
+
result = await reliability_eval.arun(print_results=False)
|
|
144
144
|
if not result:
|
|
145
145
|
raise HTTPException(status_code=500, detail="Failed to run reliability evaluation")
|
|
146
146
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from fastapi import APIRouter
|
|
2
|
+
|
|
3
|
+
from agno.os.schema import HealthResponse
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_health_router() -> APIRouter:
|
|
7
|
+
router = APIRouter(tags=["Health"])
|
|
8
|
+
|
|
9
|
+
@router.get(
|
|
10
|
+
"/health",
|
|
11
|
+
operation_id="health_check",
|
|
12
|
+
summary="Health Check",
|
|
13
|
+
description="Check the health status of the AgentOS API. Returns a simple status indicator.",
|
|
14
|
+
response_model=HealthResponse,
|
|
15
|
+
responses={
|
|
16
|
+
200: {
|
|
17
|
+
"description": "API is healthy and operational",
|
|
18
|
+
"content": {"application/json": {"example": {"status": "ok"}}},
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
)
|
|
22
|
+
async def health_check() -> HealthResponse:
|
|
23
|
+
return HealthResponse(status="ok")
|
|
24
|
+
|
|
25
|
+
return router
|
agno/os/routers/home.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from agno.os.app import AgentOS
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_home_router(os: "AgentOS") -> APIRouter:
|
|
10
|
+
router = APIRouter(tags=["Home"])
|
|
11
|
+
|
|
12
|
+
@router.get(
|
|
13
|
+
"/",
|
|
14
|
+
operation_id="get_api_info",
|
|
15
|
+
summary="API Information",
|
|
16
|
+
description=(
|
|
17
|
+
"Get basic information about this AgentOS API instance, including:\n\n"
|
|
18
|
+
"- API metadata and version\n"
|
|
19
|
+
"- Available capabilities overview\n"
|
|
20
|
+
"- Links to key endpoints and documentation"
|
|
21
|
+
),
|
|
22
|
+
responses={
|
|
23
|
+
200: {
|
|
24
|
+
"description": "API information retrieved successfully",
|
|
25
|
+
"content": {
|
|
26
|
+
"application/json": {
|
|
27
|
+
"examples": {
|
|
28
|
+
"home": {
|
|
29
|
+
"summary": "Example home response",
|
|
30
|
+
"value": {
|
|
31
|
+
"name": "AgentOS API",
|
|
32
|
+
"description": "AI Agent Operating System API",
|
|
33
|
+
"os_id": "demo-os",
|
|
34
|
+
"version": "1.0.0",
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
)
|
|
43
|
+
async def get_api_info():
|
|
44
|
+
"""Get basic API information and available capabilities"""
|
|
45
|
+
return {
|
|
46
|
+
"name": "AgentOS API",
|
|
47
|
+
"description": os.description or "AI Agent Operating System API",
|
|
48
|
+
"os_id": os.os_id or "agno-agentos",
|
|
49
|
+
"version": os.version or "1.0.0",
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return router
|
|
@@ -2,7 +2,6 @@ import json
|
|
|
2
2
|
import logging
|
|
3
3
|
import math
|
|
4
4
|
from typing import Dict, List, Optional
|
|
5
|
-
from uuid import uuid4
|
|
6
5
|
|
|
7
6
|
from fastapi import APIRouter, BackgroundTasks, Depends, File, Form, HTTPException, Path, Query, UploadFile
|
|
8
7
|
|
|
@@ -34,6 +33,7 @@ from agno.os.schema import (
|
|
|
34
33
|
from agno.os.settings import AgnoAPISettings
|
|
35
34
|
from agno.os.utils import get_knowledge_instance_by_db_id
|
|
36
35
|
from agno.utils.log import log_debug, log_info
|
|
36
|
+
from agno.utils.string import generate_id
|
|
37
37
|
|
|
38
38
|
logger = logging.getLogger(__name__)
|
|
39
39
|
|
|
@@ -102,8 +102,7 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
|
|
|
102
102
|
db_id: Optional[str] = Query(default=None, description="Database ID to use for content storage"),
|
|
103
103
|
):
|
|
104
104
|
knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
|
|
105
|
-
|
|
106
|
-
log_info(f"Adding content: {name}, {description}, {url}, {metadata} with ID: {content_id}")
|
|
105
|
+
log_info(f"Adding content: {name}, {description}, {url}, {metadata}")
|
|
107
106
|
|
|
108
107
|
parsed_metadata = None
|
|
109
108
|
if metadata:
|
|
@@ -166,10 +165,14 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
|
|
|
166
165
|
file_data=file_data,
|
|
167
166
|
size=file.size if file else None if text_content else None,
|
|
168
167
|
)
|
|
169
|
-
|
|
168
|
+
content_hash = knowledge._build_content_hash(content)
|
|
169
|
+
content.content_hash = content_hash
|
|
170
|
+
content.id = generate_id(content_hash)
|
|
171
|
+
|
|
172
|
+
background_tasks.add_task(process_content, knowledge, content, reader_id, chunker)
|
|
170
173
|
|
|
171
174
|
response = ContentResponseSchema(
|
|
172
|
-
id=
|
|
175
|
+
id=content.id,
|
|
173
176
|
name=name,
|
|
174
177
|
description=description,
|
|
175
178
|
metadata=parsed_metadata,
|
|
@@ -802,15 +805,13 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
|
|
|
802
805
|
|
|
803
806
|
async def process_content(
|
|
804
807
|
knowledge: Knowledge,
|
|
805
|
-
content_id: str,
|
|
806
808
|
content: Content,
|
|
807
809
|
reader_id: Optional[str] = None,
|
|
808
810
|
chunker: Optional[str] = None,
|
|
809
811
|
):
|
|
810
812
|
"""Background task to process the content"""
|
|
811
|
-
|
|
813
|
+
|
|
812
814
|
try:
|
|
813
|
-
content.id = content_id
|
|
814
815
|
if reader_id:
|
|
815
816
|
reader = None
|
|
816
817
|
if knowledge.readers and reader_id in knowledge.readers:
|
|
@@ -834,16 +835,15 @@ async def process_content(
|
|
|
834
835
|
|
|
835
836
|
log_debug(f"Using reader: {content.reader.__class__.__name__}")
|
|
836
837
|
await knowledge._load_content(content, upsert=False, skip_if_exists=True)
|
|
837
|
-
log_info(f"Content {
|
|
838
|
+
log_info(f"Content {content.id} processed successfully")
|
|
838
839
|
except Exception as e:
|
|
839
|
-
log_info(f"Error processing content
|
|
840
|
+
log_info(f"Error processing content: {e}")
|
|
840
841
|
# Mark content as failed in the contents DB
|
|
841
842
|
try:
|
|
842
843
|
from agno.knowledge.content import ContentStatus as KnowledgeContentStatus
|
|
843
844
|
|
|
844
845
|
content.status = KnowledgeContentStatus.FAILED
|
|
845
846
|
content.status_message = str(e)
|
|
846
|
-
content.id = content_id
|
|
847
847
|
knowledge.patch_content(content)
|
|
848
848
|
except Exception:
|
|
849
849
|
# Swallow any secondary errors to avoid crashing the background task
|
|
@@ -222,7 +222,9 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
222
222
|
db = get_db(dbs, db_id)
|
|
223
223
|
session = db.get_session(session_id=session_id, session_type=session_type)
|
|
224
224
|
if not session:
|
|
225
|
-
raise HTTPException(
|
|
225
|
+
raise HTTPException(
|
|
226
|
+
status_code=404, detail=f"{session_type.value.title()} Session with id '{session_id}' not found"
|
|
227
|
+
)
|
|
226
228
|
|
|
227
229
|
if session_type == SessionType.AGENT:
|
|
228
230
|
return AgentSessionDetailSchema.from_session(session) # type: ignore
|
|
@@ -233,7 +235,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
233
235
|
|
|
234
236
|
@router.get(
|
|
235
237
|
"/sessions/{session_id}/runs",
|
|
236
|
-
response_model=Union[
|
|
238
|
+
response_model=List[Union[RunSchema, TeamRunSchema, WorkflowRunSchema]],
|
|
237
239
|
status_code=200,
|
|
238
240
|
operation_id="get_session_runs",
|
|
239
241
|
summary="Get Session Runs",
|
|
@@ -251,7 +253,8 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
251
253
|
"summary": "Example completed run",
|
|
252
254
|
"value": {
|
|
253
255
|
"run_id": "fcdf50f0-7c32-4593-b2ef-68a558774340",
|
|
254
|
-
"
|
|
256
|
+
"parent_run_id": "80056af0-c7a5-4d69-b6a2-c3eba9f040e0",
|
|
257
|
+
"agent_id": "basic-agent",
|
|
255
258
|
"user_id": "",
|
|
256
259
|
"run_input": "Which tools do you have access to?",
|
|
257
260
|
"content": "I don't have access to external tools or the internet. However, I can assist you with a wide range of topics by providing information, answering questions, and offering suggestions based on the knowledge I've been trained on. If there's anything specific you need help with, feel free to ask!",
|
|
@@ -351,7 +354,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
351
354
|
default=SessionType.AGENT, description="Session type (agent, team, or workflow)", alias="type"
|
|
352
355
|
),
|
|
353
356
|
db_id: Optional[str] = Query(default=None, description="Database ID to query runs from"),
|
|
354
|
-
) -> Union[
|
|
357
|
+
) -> List[Union[RunSchema, TeamRunSchema, WorkflowRunSchema]]:
|
|
355
358
|
db = get_db(dbs, db_id)
|
|
356
359
|
session = db.get_session(session_id=session_id, session_type=session_type, deserialize=False)
|
|
357
360
|
if not session:
|
|
@@ -365,13 +368,26 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
365
368
|
return [RunSchema.from_dict(run) for run in runs]
|
|
366
369
|
|
|
367
370
|
elif session_type == SessionType.TEAM:
|
|
368
|
-
|
|
371
|
+
run_responses: List[Union[RunSchema, TeamRunSchema, WorkflowRunSchema]] = []
|
|
372
|
+
for run in runs:
|
|
373
|
+
if run.get("agent_id") is not None:
|
|
374
|
+
run_responses.append(RunSchema.from_dict(run))
|
|
375
|
+
elif run.get("team_id") is not None:
|
|
376
|
+
run_responses.append(TeamRunSchema.from_dict(run))
|
|
377
|
+
return run_responses
|
|
369
378
|
|
|
370
379
|
elif session_type == SessionType.WORKFLOW:
|
|
371
|
-
|
|
372
|
-
|
|
380
|
+
run_responses: List[Union[RunSchema, TeamRunSchema, WorkflowRunSchema]] = [] # type: ignore
|
|
381
|
+
for run in runs:
|
|
382
|
+
if run.get("workflow_id") is not None:
|
|
383
|
+
run_responses.append(WorkflowRunSchema.from_dict(run))
|
|
384
|
+
elif run.get("team_id") is not None:
|
|
385
|
+
run_responses.append(TeamRunSchema.from_dict(run))
|
|
386
|
+
else:
|
|
387
|
+
run_responses.append(RunSchema.from_dict(run))
|
|
388
|
+
return run_responses
|
|
373
389
|
else:
|
|
374
|
-
|
|
390
|
+
raise HTTPException(status_code=400, detail=f"Invalid session type: {session_type}")
|
|
375
391
|
|
|
376
392
|
@router.delete(
|
|
377
393
|
"/sessions/{session_id}",
|
agno/os/schema.py
CHANGED
|
@@ -765,6 +765,7 @@ class TeamSessionDetailSchema(BaseModel):
|
|
|
765
765
|
team_data: Optional[dict]
|
|
766
766
|
created_at: Optional[datetime]
|
|
767
767
|
updated_at: Optional[datetime]
|
|
768
|
+
total_tokens: Optional[int]
|
|
768
769
|
|
|
769
770
|
@classmethod
|
|
770
771
|
def from_session(cls, session: TeamSession) -> "TeamSessionDetailSchema":
|
|
@@ -826,17 +827,21 @@ class WorkflowSessionDetailSchema(BaseModel):
|
|
|
826
827
|
|
|
827
828
|
class RunSchema(BaseModel):
|
|
828
829
|
run_id: str
|
|
829
|
-
|
|
830
|
+
parent_run_id: Optional[str]
|
|
831
|
+
agent_id: Optional[str]
|
|
830
832
|
user_id: Optional[str]
|
|
831
833
|
run_input: Optional[str]
|
|
832
834
|
content: Optional[Union[str, dict]]
|
|
833
835
|
run_response_format: Optional[str]
|
|
834
836
|
reasoning_content: Optional[str]
|
|
837
|
+
reasoning_steps: Optional[List[dict]]
|
|
835
838
|
metrics: Optional[dict]
|
|
836
839
|
messages: Optional[List[dict]]
|
|
837
840
|
tools: Optional[List[dict]]
|
|
838
841
|
events: Optional[List[dict]]
|
|
839
842
|
created_at: Optional[datetime]
|
|
843
|
+
references: Optional[List[dict]]
|
|
844
|
+
reasoning_messages: Optional[List[dict]]
|
|
840
845
|
|
|
841
846
|
@classmethod
|
|
842
847
|
def from_dict(cls, run_dict: Dict[str, Any]) -> "RunSchema":
|
|
@@ -844,16 +849,20 @@ class RunSchema(BaseModel):
|
|
|
844
849
|
run_response_format = "text" if run_dict.get("content_type", "str") == "str" else "json"
|
|
845
850
|
return cls(
|
|
846
851
|
run_id=run_dict.get("run_id", ""),
|
|
847
|
-
|
|
852
|
+
parent_run_id=run_dict.get("parent_run_id", ""),
|
|
853
|
+
agent_id=run_dict.get("agent_id", ""),
|
|
848
854
|
user_id=run_dict.get("user_id", ""),
|
|
849
855
|
run_input=run_input,
|
|
850
856
|
content=run_dict.get("content", ""),
|
|
851
857
|
run_response_format=run_response_format,
|
|
852
858
|
reasoning_content=run_dict.get("reasoning_content", ""),
|
|
859
|
+
reasoning_steps=run_dict.get("reasoning_steps", []),
|
|
853
860
|
metrics=run_dict.get("metrics", {}),
|
|
854
861
|
messages=[message for message in run_dict.get("messages", [])] if run_dict.get("messages") else None,
|
|
855
862
|
tools=[tool for tool in run_dict.get("tools", [])] if run_dict.get("tools") else None,
|
|
856
863
|
events=[event for event in run_dict["events"]] if run_dict.get("events") else None,
|
|
864
|
+
references=run_dict.get("references", []),
|
|
865
|
+
reasoning_messages=run_dict.get("reasoning_messages", []),
|
|
857
866
|
created_at=datetime.fromtimestamp(run_dict.get("created_at", 0), tz=timezone.utc)
|
|
858
867
|
if run_dict.get("created_at") is not None
|
|
859
868
|
else None,
|
|
@@ -863,8 +872,10 @@ class RunSchema(BaseModel):
|
|
|
863
872
|
class TeamRunSchema(BaseModel):
|
|
864
873
|
run_id: str
|
|
865
874
|
parent_run_id: Optional[str]
|
|
875
|
+
team_id: Optional[str]
|
|
866
876
|
content: Optional[Union[str, dict]]
|
|
867
877
|
reasoning_content: Optional[str]
|
|
878
|
+
reasoning_steps: Optional[List[dict]]
|
|
868
879
|
run_input: Optional[str]
|
|
869
880
|
run_response_format: Optional[str]
|
|
870
881
|
metrics: Optional[dict]
|
|
@@ -872,6 +883,8 @@ class TeamRunSchema(BaseModel):
|
|
|
872
883
|
messages: Optional[List[dict]]
|
|
873
884
|
events: Optional[List[dict]]
|
|
874
885
|
created_at: Optional[datetime]
|
|
886
|
+
references: Optional[List[dict]]
|
|
887
|
+
reasoning_messages: Optional[List[dict]]
|
|
875
888
|
|
|
876
889
|
@classmethod
|
|
877
890
|
def from_dict(cls, run_dict: Dict[str, Any]) -> "TeamRunSchema":
|
|
@@ -880,10 +893,12 @@ class TeamRunSchema(BaseModel):
|
|
|
880
893
|
return cls(
|
|
881
894
|
run_id=run_dict.get("run_id", ""),
|
|
882
895
|
parent_run_id=run_dict.get("parent_run_id", ""),
|
|
896
|
+
team_id=run_dict.get("team_id", ""),
|
|
883
897
|
run_input=run_input,
|
|
884
898
|
content=run_dict.get("content", ""),
|
|
885
899
|
run_response_format=run_response_format,
|
|
886
900
|
reasoning_content=run_dict.get("reasoning_content", ""),
|
|
901
|
+
reasoning_steps=run_dict.get("reasoning_steps", []),
|
|
887
902
|
metrics=run_dict.get("metrics", {}),
|
|
888
903
|
messages=[message for message in run_dict.get("messages", [])] if run_dict.get("messages") else None,
|
|
889
904
|
tools=[tool for tool in run_dict.get("tools", [])] if run_dict.get("tools") else None,
|
|
@@ -891,12 +906,15 @@ class TeamRunSchema(BaseModel):
|
|
|
891
906
|
created_at=datetime.fromtimestamp(run_dict.get("created_at", 0), tz=timezone.utc)
|
|
892
907
|
if run_dict.get("created_at") is not None
|
|
893
908
|
else None,
|
|
909
|
+
references=run_dict.get("references", []),
|
|
910
|
+
reasoning_messages=run_dict.get("reasoning_messages", []),
|
|
894
911
|
)
|
|
895
912
|
|
|
896
913
|
|
|
897
914
|
class WorkflowRunSchema(BaseModel):
|
|
898
915
|
run_id: str
|
|
899
916
|
run_input: Optional[str]
|
|
917
|
+
workflow_id: Optional[str]
|
|
900
918
|
user_id: Optional[str]
|
|
901
919
|
content: Optional[Union[str, dict]]
|
|
902
920
|
content_type: Optional[str]
|
|
@@ -905,6 +923,10 @@ class WorkflowRunSchema(BaseModel):
|
|
|
905
923
|
step_executor_runs: Optional[list[dict]]
|
|
906
924
|
metrics: Optional[dict]
|
|
907
925
|
created_at: Optional[int]
|
|
926
|
+
reasoning_content: Optional[str]
|
|
927
|
+
reasoning_steps: Optional[List[dict]]
|
|
928
|
+
references: Optional[List[dict]]
|
|
929
|
+
reasoning_messages: Optional[List[dict]]
|
|
908
930
|
|
|
909
931
|
@classmethod
|
|
910
932
|
def from_dict(cls, run_response: Dict[str, Any]) -> "WorkflowRunSchema":
|
|
@@ -912,6 +934,7 @@ class WorkflowRunSchema(BaseModel):
|
|
|
912
934
|
return cls(
|
|
913
935
|
run_id=run_response.get("run_id", ""),
|
|
914
936
|
run_input=run_input,
|
|
937
|
+
workflow_id=run_response.get("workflow_id", ""),
|
|
915
938
|
user_id=run_response.get("user_id", ""),
|
|
916
939
|
content=run_response.get("content", ""),
|
|
917
940
|
content_type=run_response.get("content_type", ""),
|
|
@@ -920,6 +943,10 @@ class WorkflowRunSchema(BaseModel):
|
|
|
920
943
|
step_results=run_response.get("step_results", []),
|
|
921
944
|
step_executor_runs=run_response.get("step_executor_runs", []),
|
|
922
945
|
created_at=run_response["created_at"],
|
|
946
|
+
reasoning_content=run_response.get("reasoning_content", ""),
|
|
947
|
+
reasoning_steps=run_response.get("reasoning_steps", []),
|
|
948
|
+
references=run_response.get("references", []),
|
|
949
|
+
reasoning_messages=run_response.get("reasoning_messages", []),
|
|
923
950
|
)
|
|
924
951
|
|
|
925
952
|
|
agno/os/utils.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from typing import Any, Callable, Dict, List, Optional, Union
|
|
2
|
-
from uuid import uuid4
|
|
3
2
|
|
|
4
3
|
from fastapi import HTTPException, UploadFile
|
|
5
4
|
|
|
@@ -261,10 +260,3 @@ def _generate_schema_from_params(params: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
261
260
|
schema["required"] = required
|
|
262
261
|
|
|
263
262
|
return schema
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
def generate_id(name: Optional[str] = None) -> str:
|
|
267
|
-
if name:
|
|
268
|
-
return name.lower().replace(" ", "-").replace("_", "-")
|
|
269
|
-
else:
|
|
270
|
-
return str(uuid4())
|
agno/run/agent.py
CHANGED
|
@@ -559,7 +559,7 @@ class RunOutput:
|
|
|
559
559
|
events = [run_output_event_from_dict(event) for event in events] if events else None
|
|
560
560
|
|
|
561
561
|
messages = data.pop("messages", None)
|
|
562
|
-
messages = [Message.
|
|
562
|
+
messages = [Message.from_dict(message) for message in messages] if messages else None
|
|
563
563
|
|
|
564
564
|
citations = data.pop("citations", None)
|
|
565
565
|
citations = Citations.model_validate(citations) if citations else None
|
|
@@ -591,7 +591,7 @@ class RunOutput:
|
|
|
591
591
|
additional_input = data.pop("additional_input", None)
|
|
592
592
|
|
|
593
593
|
if additional_input is not None:
|
|
594
|
-
additional_input = [Message.
|
|
594
|
+
additional_input = [Message.from_dict(message) for message in additional_input]
|
|
595
595
|
|
|
596
596
|
reasoning_steps = data.pop("reasoning_steps", None)
|
|
597
597
|
if reasoning_steps is not None:
|
|
@@ -599,7 +599,7 @@ class RunOutput:
|
|
|
599
599
|
|
|
600
600
|
reasoning_messages = data.pop("reasoning_messages", None)
|
|
601
601
|
if reasoning_messages is not None:
|
|
602
|
-
reasoning_messages = [Message.
|
|
602
|
+
reasoning_messages = [Message.from_dict(message) for message in reasoning_messages]
|
|
603
603
|
|
|
604
604
|
references = data.pop("references", None)
|
|
605
605
|
if references is not None:
|
agno/run/team.py
CHANGED
|
@@ -519,7 +519,7 @@ class TeamRunOutput:
|
|
|
519
519
|
events = final_events
|
|
520
520
|
|
|
521
521
|
messages = data.pop("messages", None)
|
|
522
|
-
messages = [Message.
|
|
522
|
+
messages = [Message.from_dict(message) for message in messages] if messages else None
|
|
523
523
|
|
|
524
524
|
member_responses = data.pop("member_responses", [])
|
|
525
525
|
parsed_member_responses: List[Union["TeamRunOutput", RunOutput]] = []
|
|
@@ -532,7 +532,7 @@ class TeamRunOutput:
|
|
|
532
532
|
|
|
533
533
|
additional_input = data.pop("additional_input", None)
|
|
534
534
|
if additional_input is not None:
|
|
535
|
-
additional_input = [Message.
|
|
535
|
+
additional_input = [Message.from_dict(message) for message in additional_input]
|
|
536
536
|
|
|
537
537
|
reasoning_steps = data.pop("reasoning_steps", None)
|
|
538
538
|
if reasoning_steps is not None:
|
|
@@ -540,7 +540,7 @@ class TeamRunOutput:
|
|
|
540
540
|
|
|
541
541
|
reasoning_messages = data.pop("reasoning_messages", None)
|
|
542
542
|
if reasoning_messages is not None:
|
|
543
|
-
reasoning_messages = [Message.
|
|
543
|
+
reasoning_messages = [Message.from_dict(message) for message in reasoning_messages]
|
|
544
544
|
|
|
545
545
|
references = data.pop("references", None)
|
|
546
546
|
if references is not None:
|
agno/run/workflow.py
CHANGED
|
@@ -7,7 +7,7 @@ from pydantic import BaseModel
|
|
|
7
7
|
|
|
8
8
|
from agno.media import Audio, Image, Video
|
|
9
9
|
from agno.run.agent import RunOutput
|
|
10
|
-
from agno.run.base import RunStatus
|
|
10
|
+
from agno.run.base import BaseRunOutputEvent, RunStatus
|
|
11
11
|
from agno.run.team import TeamRunOutput
|
|
12
12
|
from agno.utils.log import log_error
|
|
13
13
|
|
|
@@ -53,7 +53,7 @@ class WorkflowRunEvent(str, Enum):
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
@dataclass
|
|
56
|
-
class BaseWorkflowRunOutputEvent:
|
|
56
|
+
class BaseWorkflowRunOutputEvent(BaseRunOutputEvent):
|
|
57
57
|
"""Base class for all workflow run response events"""
|
|
58
58
|
|
|
59
59
|
created_at: int = field(default_factory=lambda: int(time()))
|
|
@@ -75,19 +75,23 @@ class BaseWorkflowRunOutputEvent:
|
|
|
75
75
|
|
|
76
76
|
# Handle StepOutput fields that contain Message objects
|
|
77
77
|
if hasattr(self, "step_results") and self.step_results is not None:
|
|
78
|
-
_dict["step_results"] = [step.to_dict() for step in self.step_results]
|
|
78
|
+
_dict["step_results"] = [step.to_dict() if hasattr(step, "to_dict") else step for step in self.step_results]
|
|
79
79
|
|
|
80
80
|
if hasattr(self, "step_response") and self.step_response is not None:
|
|
81
|
-
_dict["step_response"] =
|
|
81
|
+
_dict["step_response"] = (
|
|
82
|
+
self.step_response.to_dict() if hasattr(self.step_response, "to_dict") else self.step_response
|
|
83
|
+
)
|
|
82
84
|
|
|
83
85
|
if hasattr(self, "iteration_results") and self.iteration_results is not None:
|
|
84
|
-
_dict["iteration_results"] = [
|
|
86
|
+
_dict["iteration_results"] = [
|
|
87
|
+
step.to_dict() if hasattr(step, "to_dict") else step for step in self.iteration_results
|
|
88
|
+
]
|
|
85
89
|
|
|
86
90
|
if hasattr(self, "all_results") and self.all_results is not None:
|
|
87
|
-
_dict["all_results"] = [
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
_dict["all_results"] = [
|
|
92
|
+
[step.to_dict() if hasattr(step, "to_dict") else step for step in iteration]
|
|
93
|
+
for iteration in self.all_results
|
|
94
|
+
]
|
|
91
95
|
|
|
92
96
|
return _dict
|
|
93
97
|
|
|
@@ -420,6 +424,39 @@ WorkflowRunOutputEvent = Union[
|
|
|
420
424
|
CustomEvent,
|
|
421
425
|
]
|
|
422
426
|
|
|
427
|
+
# Map event string to dataclass for workflow events
|
|
428
|
+
WORKFLOW_RUN_EVENT_TYPE_REGISTRY = {
|
|
429
|
+
WorkflowRunEvent.workflow_started.value: WorkflowStartedEvent,
|
|
430
|
+
WorkflowRunEvent.workflow_completed.value: WorkflowCompletedEvent,
|
|
431
|
+
WorkflowRunEvent.workflow_cancelled.value: WorkflowCancelledEvent,
|
|
432
|
+
WorkflowRunEvent.workflow_error.value: WorkflowErrorEvent,
|
|
433
|
+
WorkflowRunEvent.step_started.value: StepStartedEvent,
|
|
434
|
+
WorkflowRunEvent.step_completed.value: StepCompletedEvent,
|
|
435
|
+
WorkflowRunEvent.step_error.value: StepErrorEvent,
|
|
436
|
+
WorkflowRunEvent.loop_execution_started.value: LoopExecutionStartedEvent,
|
|
437
|
+
WorkflowRunEvent.loop_iteration_started.value: LoopIterationStartedEvent,
|
|
438
|
+
WorkflowRunEvent.loop_iteration_completed.value: LoopIterationCompletedEvent,
|
|
439
|
+
WorkflowRunEvent.loop_execution_completed.value: LoopExecutionCompletedEvent,
|
|
440
|
+
WorkflowRunEvent.parallel_execution_started.value: ParallelExecutionStartedEvent,
|
|
441
|
+
WorkflowRunEvent.parallel_execution_completed.value: ParallelExecutionCompletedEvent,
|
|
442
|
+
WorkflowRunEvent.condition_execution_started.value: ConditionExecutionStartedEvent,
|
|
443
|
+
WorkflowRunEvent.condition_execution_completed.value: ConditionExecutionCompletedEvent,
|
|
444
|
+
WorkflowRunEvent.router_execution_started.value: RouterExecutionStartedEvent,
|
|
445
|
+
WorkflowRunEvent.router_execution_completed.value: RouterExecutionCompletedEvent,
|
|
446
|
+
WorkflowRunEvent.steps_execution_started.value: StepsExecutionStartedEvent,
|
|
447
|
+
WorkflowRunEvent.steps_execution_completed.value: StepsExecutionCompletedEvent,
|
|
448
|
+
WorkflowRunEvent.step_output.value: StepOutputEvent,
|
|
449
|
+
WorkflowRunEvent.custom_event.value: CustomEvent,
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def workflow_run_output_event_from_dict(data: dict) -> BaseWorkflowRunOutputEvent:
|
|
454
|
+
event_type = data.get("event", "")
|
|
455
|
+
cls = WORKFLOW_RUN_EVENT_TYPE_REGISTRY.get(event_type)
|
|
456
|
+
if not cls:
|
|
457
|
+
raise ValueError(f"Unknown workflow event type: {event_type}")
|
|
458
|
+
return cls.from_dict(data) # type: ignore
|
|
459
|
+
|
|
423
460
|
|
|
424
461
|
@dataclass
|
|
425
462
|
class WorkflowRunOutput:
|
|
@@ -568,7 +605,24 @@ class WorkflowRunOutput:
|
|
|
568
605
|
response_audio = data.pop("response_audio", None)
|
|
569
606
|
response_audio = Audio.model_validate(response_audio) if response_audio else None
|
|
570
607
|
|
|
571
|
-
|
|
608
|
+
events_data = data.pop("events", [])
|
|
609
|
+
final_events = []
|
|
610
|
+
for event in events_data or []:
|
|
611
|
+
if "agent_id" in event:
|
|
612
|
+
# Agent event from agent step
|
|
613
|
+
from agno.run.agent import run_output_event_from_dict
|
|
614
|
+
|
|
615
|
+
event = run_output_event_from_dict(event)
|
|
616
|
+
elif "team_id" in event:
|
|
617
|
+
# Team event from team step
|
|
618
|
+
from agno.run.team import team_run_output_event_from_dict
|
|
619
|
+
|
|
620
|
+
event = team_run_output_event_from_dict(event)
|
|
621
|
+
else:
|
|
622
|
+
# Pure workflow event
|
|
623
|
+
event = workflow_run_output_event_from_dict(event)
|
|
624
|
+
final_events.append(event)
|
|
625
|
+
events = final_events
|
|
572
626
|
|
|
573
627
|
return cls(
|
|
574
628
|
step_results=parsed_step_results,
|