agno 2.3.8__py3-none-any.whl → 2.3.9__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 +134 -82
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2888 -0
- agno/db/mysql/mysql.py +17 -8
- agno/db/mysql/utils.py +139 -6
- agno/db/postgres/async_postgres.py +10 -5
- agno/db/postgres/postgres.py +7 -2
- agno/db/schemas/evals.py +1 -0
- agno/db/singlestore/singlestore.py +5 -1
- agno/db/sqlite/async_sqlite.py +2 -2
- agno/eval/__init__.py +10 -0
- agno/eval/agent_as_judge.py +860 -0
- agno/eval/base.py +29 -0
- agno/eval/utils.py +2 -1
- agno/exceptions.py +7 -0
- agno/knowledge/embedder/openai.py +8 -8
- agno/knowledge/knowledge.py +1142 -176
- agno/media.py +22 -6
- agno/models/aws/claude.py +8 -7
- agno/models/base.py +27 -1
- agno/models/deepseek/deepseek.py +67 -0
- agno/models/google/gemini.py +65 -11
- agno/models/google/utils.py +22 -0
- agno/models/message.py +2 -0
- agno/models/openai/chat.py +4 -0
- agno/os/app.py +64 -74
- agno/os/interfaces/a2a/router.py +3 -4
- agno/os/interfaces/agui/router.py +2 -0
- agno/os/router.py +3 -1607
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +581 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/evals.py +26 -6
- agno/os/routers/evals/schemas.py +34 -2
- agno/os/routers/evals/utils.py +101 -20
- agno/os/routers/knowledge/knowledge.py +1 -1
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +496 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +545 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +1 -559
- agno/os/utils.py +139 -2
- agno/team/team.py +73 -16
- agno/tools/file_generation.py +12 -6
- agno/tools/firecrawl.py +15 -7
- agno/utils/hooks.py +64 -5
- agno/utils/http.py +2 -2
- agno/utils/media.py +11 -1
- agno/utils/print_response/agent.py +8 -0
- agno/utils/print_response/team.py +8 -0
- agno/vectordb/pgvector/pgvector.py +88 -51
- agno/workflow/parallel.py +3 -3
- agno/workflow/step.py +14 -2
- agno/workflow/types.py +38 -2
- agno/workflow/workflow.py +12 -4
- {agno-2.3.8.dist-info → agno-2.3.9.dist-info}/METADATA +7 -2
- {agno-2.3.8.dist-info → agno-2.3.9.dist-info}/RECORD +62 -49
- {agno-2.3.8.dist-info → agno-2.3.9.dist-info}/WHEEL +0 -0
- {agno-2.3.8.dist-info → agno-2.3.9.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.8.dist-info → agno-2.3.9.dist-info}/top_level.txt +0 -0
agno/os/app.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from contextlib import asynccontextmanager
|
|
2
2
|
from functools import partial
|
|
3
3
|
from os import getenv
|
|
4
|
-
from typing import Any, Dict, List, Literal, Optional,
|
|
4
|
+
from typing import Any, Dict, List, Literal, Optional, Union
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
|
|
7
7
|
from fastapi import APIRouter, FastAPI, HTTPException
|
|
@@ -31,7 +31,8 @@ from agno.os.config import (
|
|
|
31
31
|
TracesDomainConfig,
|
|
32
32
|
)
|
|
33
33
|
from agno.os.interfaces.base import BaseInterface
|
|
34
|
-
from agno.os.router import get_base_router
|
|
34
|
+
from agno.os.router import get_base_router
|
|
35
|
+
from agno.os.routers.agents import get_agent_router
|
|
35
36
|
from agno.os.routers.evals import get_eval_router
|
|
36
37
|
from agno.os.routers.health import get_health_router
|
|
37
38
|
from agno.os.routers.home import get_home_router
|
|
@@ -39,7 +40,9 @@ from agno.os.routers.knowledge import get_knowledge_router
|
|
|
39
40
|
from agno.os.routers.memory import get_memory_router
|
|
40
41
|
from agno.os.routers.metrics import get_metrics_router
|
|
41
42
|
from agno.os.routers.session import get_session_router
|
|
43
|
+
from agno.os.routers.teams import get_team_router
|
|
42
44
|
from agno.os.routers.traces import get_traces_router
|
|
45
|
+
from agno.os.routers.workflows import get_websocket_router, get_workflow_router
|
|
43
46
|
from agno.os.settings import AgnoAPISettings
|
|
44
47
|
from agno.os.utils import (
|
|
45
48
|
collect_mcp_tools_from_team,
|
|
@@ -69,6 +72,15 @@ async def mcp_lifespan(_, mcp_tools):
|
|
|
69
72
|
await tool.close()
|
|
70
73
|
|
|
71
74
|
|
|
75
|
+
@asynccontextmanager
|
|
76
|
+
async def db_lifespan(app: FastAPI, agent_os: "AgentOS"):
|
|
77
|
+
"""Initializes databases in the event loop"""
|
|
78
|
+
if agent_os.auto_provision_dbs:
|
|
79
|
+
agent_os._initialize_sync_databases()
|
|
80
|
+
await agent_os._initialize_async_databases()
|
|
81
|
+
yield
|
|
82
|
+
|
|
83
|
+
|
|
72
84
|
def _combine_app_lifespans(lifespans: list) -> Any:
|
|
73
85
|
"""Combine multiple FastAPI app lifespan context managers into one."""
|
|
74
86
|
if len(lifespans) == 1:
|
|
@@ -287,6 +299,9 @@ class AgentOS:
|
|
|
287
299
|
|
|
288
300
|
self._add_router(app, get_health_router(health_endpoint="/health"))
|
|
289
301
|
self._add_router(app, get_base_router(self, settings=self.settings))
|
|
302
|
+
self._add_router(app, get_agent_router(self, settings=self.settings))
|
|
303
|
+
self._add_router(app, get_team_router(self, settings=self.settings))
|
|
304
|
+
self._add_router(app, get_workflow_router(self, settings=self.settings))
|
|
290
305
|
self._add_router(app, get_websocket_router(self, settings=self.settings))
|
|
291
306
|
|
|
292
307
|
# Add A2A interface if relevant
|
|
@@ -469,41 +484,36 @@ class AgentOS:
|
|
|
469
484
|
if self.enable_mcp_server and self._mcp_app:
|
|
470
485
|
lifespans.append(self._mcp_app.lifespan)
|
|
471
486
|
|
|
487
|
+
# The async database lifespan
|
|
488
|
+
lifespans.append(partial(db_lifespan, agent_os=self))
|
|
489
|
+
|
|
472
490
|
# Combine lifespans and set them in the app
|
|
473
491
|
if lifespans:
|
|
474
492
|
fastapi_app.router.lifespan_context = _combine_app_lifespans(lifespans)
|
|
475
493
|
|
|
476
494
|
else:
|
|
477
|
-
|
|
478
|
-
from contextlib import asynccontextmanager
|
|
479
|
-
|
|
480
|
-
from agno.os.mcp import get_mcp_server
|
|
495
|
+
lifespans = []
|
|
481
496
|
|
|
482
|
-
|
|
497
|
+
# User provided lifespan
|
|
498
|
+
if self.lifespan:
|
|
499
|
+
lifespans.append(self._add_agent_os_to_lifespan_function(self.lifespan))
|
|
483
500
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
wrapped_lifespan = self._add_agent_os_to_lifespan_function(self.lifespan)
|
|
501
|
+
# MCP tools lifespan
|
|
502
|
+
if self.mcp_tools:
|
|
503
|
+
lifespans.append(partial(mcp_lifespan, mcp_tools=self.mcp_tools))
|
|
488
504
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
# Run both lifespans
|
|
493
|
-
async with wrapped_lifespan(app): # type: ignore
|
|
494
|
-
async with self._mcp_app.lifespan(app): # type: ignore
|
|
495
|
-
yield
|
|
505
|
+
# MCP server lifespan
|
|
506
|
+
if self.enable_mcp_server:
|
|
507
|
+
from agno.os.mcp import get_mcp_server
|
|
496
508
|
|
|
497
|
-
|
|
509
|
+
self._mcp_app = get_mcp_server(self)
|
|
510
|
+
lifespans.append(self._mcp_app.lifespan)
|
|
498
511
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
# Wrap the user lifespan with agent_os parameter
|
|
502
|
-
wrapped_user_lifespan = None
|
|
503
|
-
if self.lifespan is not None:
|
|
504
|
-
wrapped_user_lifespan = self._add_agent_os_to_lifespan_function(self.lifespan)
|
|
512
|
+
# Async database initialization lifespan
|
|
513
|
+
lifespans.append(partial(db_lifespan, agent_os=self)) # type: ignore
|
|
505
514
|
|
|
506
|
-
|
|
515
|
+
final_lifespan = _combine_app_lifespans(lifespans) if lifespans else None
|
|
516
|
+
fastapi_app = self._make_app(lifespan=final_lifespan)
|
|
507
517
|
|
|
508
518
|
self._add_built_in_routes(app=fastapi_app)
|
|
509
519
|
|
|
@@ -668,36 +678,14 @@ class AgentOS:
|
|
|
668
678
|
self.dbs = dbs
|
|
669
679
|
self.knowledge_dbs = knowledge_dbs
|
|
670
680
|
|
|
671
|
-
# Initialize
|
|
681
|
+
# Initialize all discovered databases
|
|
672
682
|
if self.auto_provision_dbs:
|
|
673
|
-
|
|
674
|
-
import concurrent.futures
|
|
683
|
+
self._pending_async_db_init = True
|
|
675
684
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
asyncio.get_running_loop()
|
|
679
|
-
|
|
680
|
-
def run_in_new_loop():
|
|
681
|
-
new_loop = asyncio.new_event_loop()
|
|
682
|
-
asyncio.set_event_loop(new_loop)
|
|
683
|
-
try:
|
|
684
|
-
return new_loop.run_until_complete(self._initialize_databases())
|
|
685
|
-
finally:
|
|
686
|
-
new_loop.close()
|
|
687
|
-
|
|
688
|
-
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
|
|
689
|
-
future = executor.submit(run_in_new_loop)
|
|
690
|
-
future.result() # Wait for completion
|
|
691
|
-
|
|
692
|
-
except RuntimeError:
|
|
693
|
-
# No event loop running, use asyncio.run
|
|
694
|
-
asyncio.run(self._initialize_databases())
|
|
695
|
-
|
|
696
|
-
async def _initialize_databases(self) -> None:
|
|
697
|
-
"""Initialize all discovered databases and create all Agno tables that don't exist yet."""
|
|
685
|
+
def _initialize_sync_databases(self) -> None:
|
|
686
|
+
"""Initialize sync databases."""
|
|
698
687
|
from itertools import chain
|
|
699
688
|
|
|
700
|
-
# Collect all database instances and remove duplicates by identity
|
|
701
689
|
unique_dbs = list(
|
|
702
690
|
{
|
|
703
691
|
id(db): db
|
|
@@ -707,37 +695,39 @@ class AgentOS:
|
|
|
707
695
|
}.values()
|
|
708
696
|
)
|
|
709
697
|
|
|
710
|
-
# Separate sync and async databases
|
|
711
|
-
sync_dbs: List[Tuple[str, BaseDb]] = []
|
|
712
|
-
async_dbs: List[Tuple[str, AsyncBaseDb]] = []
|
|
713
|
-
|
|
714
698
|
for db in unique_dbs:
|
|
715
|
-
|
|
716
|
-
|
|
699
|
+
if isinstance(db, AsyncBaseDb):
|
|
700
|
+
continue # Skip async dbs
|
|
717
701
|
|
|
718
|
-
# Initialize sync databases
|
|
719
|
-
for db_id, db in sync_dbs:
|
|
720
702
|
try:
|
|
721
|
-
if hasattr(db, "_create_all_tables") and callable(
|
|
703
|
+
if hasattr(db, "_create_all_tables") and callable(db._create_all_tables):
|
|
722
704
|
db._create_all_tables()
|
|
723
|
-
else:
|
|
724
|
-
log_debug(f"No table initialization needed for {db.__class__.__name__}")
|
|
725
|
-
|
|
726
705
|
except Exception as e:
|
|
727
|
-
log_warning(f"Failed to initialize {db.__class__.__name__} (id: {
|
|
706
|
+
log_warning(f"Failed to initialize {db.__class__.__name__} (id: {db.id}): {e}")
|
|
728
707
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
try:
|
|
732
|
-
log_debug(f"Initializing async {db.__class__.__name__} (id: {db_id})")
|
|
708
|
+
async def _initialize_async_databases(self) -> None:
|
|
709
|
+
"""Initialize async databases."""
|
|
733
710
|
|
|
734
|
-
|
|
735
|
-
await db._create_all_tables()
|
|
736
|
-
else:
|
|
737
|
-
log_debug(f"No table initialization needed for async {db.__class__.__name__}")
|
|
711
|
+
from itertools import chain
|
|
738
712
|
|
|
713
|
+
unique_dbs = list(
|
|
714
|
+
{
|
|
715
|
+
id(db): db
|
|
716
|
+
for db in chain(
|
|
717
|
+
chain.from_iterable(self.dbs.values()), chain.from_iterable(self.knowledge_dbs.values())
|
|
718
|
+
)
|
|
719
|
+
}.values()
|
|
720
|
+
)
|
|
721
|
+
|
|
722
|
+
for db in unique_dbs:
|
|
723
|
+
if not isinstance(db, AsyncBaseDb):
|
|
724
|
+
continue # Skip sync dbs
|
|
725
|
+
|
|
726
|
+
try:
|
|
727
|
+
if hasattr(db, "_create_all_tables") and callable(db._create_all_tables):
|
|
728
|
+
await db._create_all_tables()
|
|
739
729
|
except Exception as e:
|
|
740
|
-
log_warning(f"Failed to initialize async
|
|
730
|
+
log_warning(f"Failed to initialize async {db.__class__.__name__} (id: {db.id}): {e}")
|
|
741
731
|
|
|
742
732
|
def _get_db_table_names(self, db: BaseDb) -> Dict[str, str]:
|
|
743
733
|
"""Get the table names for a database"""
|
agno/os/interfaces/a2a/router.py
CHANGED
|
@@ -19,8 +19,7 @@ from agno.os.interfaces.a2a.utils import (
|
|
|
19
19
|
map_run_output_to_a2a_task,
|
|
20
20
|
stream_a2a_response_with_error_handling,
|
|
21
21
|
)
|
|
22
|
-
from agno.os.
|
|
23
|
-
from agno.os.utils import get_agent_by_id, get_team_by_id, get_workflow_by_id
|
|
22
|
+
from agno.os.utils import get_agent_by_id, get_request_kwargs, get_team_by_id, get_workflow_by_id
|
|
24
23
|
from agno.team import Team
|
|
25
24
|
from agno.workflow import Workflow
|
|
26
25
|
|
|
@@ -75,7 +74,7 @@ def attach_routes(
|
|
|
75
74
|
)
|
|
76
75
|
async def a2a_send_message(request: Request):
|
|
77
76
|
request_body = await request.json()
|
|
78
|
-
kwargs = await
|
|
77
|
+
kwargs = await get_request_kwargs(request, a2a_send_message)
|
|
79
78
|
|
|
80
79
|
# 1. Get the Agent, Team, or Workflow to run
|
|
81
80
|
agent_id = request_body.get("params", {}).get("message", {}).get("agentId") or request.headers.get("X-Agent-ID")
|
|
@@ -181,7 +180,7 @@ def attach_routes(
|
|
|
181
180
|
)
|
|
182
181
|
async def a2a_stream_message(request: Request):
|
|
183
182
|
request_body = await request.json()
|
|
184
|
-
kwargs = await
|
|
183
|
+
kwargs = await get_request_kwargs(request, a2a_stream_message)
|
|
185
184
|
|
|
186
185
|
# 1. Get the Agent, Team, or Workflow to run
|
|
187
186
|
agent_id = request_body.get("params", {}).get("message", {}).get("agentId")
|
|
@@ -52,6 +52,7 @@ async def run_agent(agent: Agent, run_input: RunAgentInput) -> AsyncIterator[Bas
|
|
|
52
52
|
stream_events=True,
|
|
53
53
|
user_id=user_id,
|
|
54
54
|
session_state=session_state,
|
|
55
|
+
run_id=run_id,
|
|
55
56
|
)
|
|
56
57
|
|
|
57
58
|
# Stream the response content in AG-UI format
|
|
@@ -92,6 +93,7 @@ async def run_team(team: Team, input: RunAgentInput) -> AsyncIterator[BaseEvent]
|
|
|
92
93
|
stream_steps=True,
|
|
93
94
|
user_id=user_id,
|
|
94
95
|
session_state=session_state,
|
|
96
|
+
run_id=run_id,
|
|
95
97
|
)
|
|
96
98
|
|
|
97
99
|
# Stream the response content in AG-UI format
|