agno 2.3.26__py3-none-any.whl → 2.4.0__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/__init__.py +4 -0
- agno/agent/agent.py +1368 -541
- agno/agent/remote.py +13 -0
- agno/db/base.py +339 -0
- agno/db/postgres/async_postgres.py +116 -12
- agno/db/postgres/postgres.py +1229 -25
- agno/db/postgres/schemas.py +48 -1
- agno/db/sqlite/async_sqlite.py +119 -4
- agno/db/sqlite/schemas.py +51 -0
- agno/db/sqlite/sqlite.py +1173 -13
- agno/db/utils.py +37 -1
- agno/knowledge/__init__.py +4 -0
- agno/knowledge/chunking/code.py +1 -1
- agno/knowledge/chunking/semantic.py +1 -1
- agno/knowledge/chunking/strategy.py +4 -0
- agno/knowledge/filesystem.py +412 -0
- agno/knowledge/knowledge.py +2767 -2254
- agno/knowledge/protocol.py +134 -0
- agno/knowledge/reader/arxiv_reader.py +2 -2
- agno/knowledge/reader/base.py +9 -7
- agno/knowledge/reader/csv_reader.py +5 -5
- agno/knowledge/reader/docx_reader.py +2 -2
- agno/knowledge/reader/field_labeled_csv_reader.py +2 -2
- agno/knowledge/reader/firecrawl_reader.py +2 -2
- agno/knowledge/reader/json_reader.py +2 -2
- agno/knowledge/reader/markdown_reader.py +2 -2
- agno/knowledge/reader/pdf_reader.py +5 -4
- agno/knowledge/reader/pptx_reader.py +2 -2
- agno/knowledge/reader/reader_factory.py +110 -0
- agno/knowledge/reader/s3_reader.py +2 -2
- agno/knowledge/reader/tavily_reader.py +2 -2
- agno/knowledge/reader/text_reader.py +2 -2
- agno/knowledge/reader/web_search_reader.py +2 -2
- agno/knowledge/reader/website_reader.py +5 -3
- agno/knowledge/reader/wikipedia_reader.py +2 -2
- agno/knowledge/reader/youtube_reader.py +2 -2
- agno/knowledge/utils.py +37 -29
- agno/learn/__init__.py +6 -0
- agno/learn/machine.py +35 -0
- agno/learn/schemas.py +82 -11
- agno/learn/stores/__init__.py +3 -0
- agno/learn/stores/decision_log.py +1156 -0
- agno/learn/stores/learned_knowledge.py +6 -6
- agno/models/anthropic/claude.py +24 -0
- agno/models/aws/bedrock.py +20 -0
- agno/models/base.py +48 -4
- agno/models/cohere/chat.py +25 -0
- agno/models/google/gemini.py +50 -5
- agno/models/litellm/chat.py +38 -0
- agno/models/openai/chat.py +7 -0
- agno/models/openrouter/openrouter.py +46 -0
- agno/models/response.py +16 -0
- agno/os/app.py +83 -44
- agno/os/middleware/__init__.py +2 -0
- agno/os/middleware/trailing_slash.py +27 -0
- agno/os/router.py +1 -0
- agno/os/routers/agents/router.py +29 -16
- agno/os/routers/agents/schema.py +6 -4
- agno/os/routers/components/__init__.py +3 -0
- agno/os/routers/components/components.py +466 -0
- agno/os/routers/evals/schemas.py +4 -3
- agno/os/routers/health.py +3 -3
- agno/os/routers/knowledge/knowledge.py +3 -3
- agno/os/routers/memory/schemas.py +4 -2
- agno/os/routers/metrics/metrics.py +9 -11
- agno/os/routers/metrics/schemas.py +10 -6
- agno/os/routers/registry/__init__.py +3 -0
- agno/os/routers/registry/registry.py +337 -0
- agno/os/routers/teams/router.py +20 -8
- agno/os/routers/teams/schema.py +6 -4
- agno/os/routers/traces/traces.py +5 -5
- agno/os/routers/workflows/router.py +38 -11
- agno/os/routers/workflows/schema.py +1 -1
- agno/os/schema.py +92 -26
- agno/os/utils.py +84 -19
- agno/reasoning/anthropic.py +2 -2
- agno/reasoning/azure_ai_foundry.py +2 -2
- agno/reasoning/deepseek.py +2 -2
- agno/reasoning/default.py +6 -7
- agno/reasoning/gemini.py +2 -2
- agno/reasoning/helpers.py +6 -7
- agno/reasoning/manager.py +4 -10
- agno/reasoning/ollama.py +2 -2
- agno/reasoning/openai.py +2 -2
- agno/reasoning/vertexai.py +2 -2
- agno/registry/__init__.py +3 -0
- agno/registry/registry.py +68 -0
- agno/run/agent.py +57 -0
- agno/run/base.py +7 -0
- agno/run/team.py +57 -0
- agno/skills/agent_skills.py +10 -3
- agno/team/__init__.py +3 -1
- agno/team/team.py +1145 -326
- agno/tools/duckduckgo.py +25 -71
- agno/tools/exa.py +0 -21
- agno/tools/function.py +35 -83
- agno/tools/knowledge.py +9 -4
- agno/tools/mem0.py +11 -10
- agno/tools/memory.py +47 -46
- agno/tools/parallel.py +0 -7
- agno/tools/reasoning.py +30 -23
- agno/tools/tavily.py +4 -1
- agno/tools/websearch.py +93 -0
- agno/tools/website.py +1 -1
- agno/tools/wikipedia.py +1 -1
- agno/tools/workflow.py +48 -47
- agno/utils/agent.py +42 -5
- agno/utils/events.py +160 -2
- agno/utils/print_response/agent.py +0 -31
- agno/utils/print_response/team.py +0 -2
- agno/utils/print_response/workflow.py +0 -2
- agno/utils/team.py +61 -11
- agno/vectordb/lancedb/lance_db.py +4 -1
- agno/vectordb/mongodb/mongodb.py +1 -1
- agno/vectordb/qdrant/qdrant.py +4 -4
- agno/workflow/__init__.py +3 -1
- agno/workflow/condition.py +0 -21
- agno/workflow/loop.py +0 -21
- agno/workflow/parallel.py +0 -21
- agno/workflow/router.py +0 -21
- agno/workflow/step.py +117 -24
- agno/workflow/steps.py +0 -21
- agno/workflow/workflow.py +427 -63
- {agno-2.3.26.dist-info → agno-2.4.0.dist-info}/METADATA +46 -76
- {agno-2.3.26.dist-info → agno-2.4.0.dist-info}/RECORD +128 -117
- {agno-2.3.26.dist-info → agno-2.4.0.dist-info}/WHEEL +0 -0
- {agno-2.3.26.dist-info → agno-2.4.0.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.26.dist-info → agno-2.4.0.dist-info}/top_level.txt +0 -0
agno/os/app.py
CHANGED
|
@@ -36,6 +36,7 @@ from agno.os.config import (
|
|
|
36
36
|
from agno.os.interfaces.base import BaseInterface
|
|
37
37
|
from agno.os.router import get_base_router, get_websocket_router
|
|
38
38
|
from agno.os.routers.agents import get_agent_router
|
|
39
|
+
from agno.os.routers.components import get_components_router
|
|
39
40
|
from agno.os.routers.database import get_database_router
|
|
40
41
|
from agno.os.routers.evals import get_eval_router
|
|
41
42
|
from agno.os.routers.health import get_health_router
|
|
@@ -43,6 +44,7 @@ from agno.os.routers.home import get_home_router
|
|
|
43
44
|
from agno.os.routers.knowledge import get_knowledge_router
|
|
44
45
|
from agno.os.routers.memory import get_memory_router
|
|
45
46
|
from agno.os.routers.metrics import get_metrics_router
|
|
47
|
+
from agno.os.routers.registry import get_registry_router
|
|
46
48
|
from agno.os.routers.session import get_session_router
|
|
47
49
|
from agno.os.routers.teams import get_team_router
|
|
48
50
|
from agno.os.routers.traces import get_traces_router
|
|
@@ -57,6 +59,7 @@ from agno.os.utils import (
|
|
|
57
59
|
setup_tracing_for_os,
|
|
58
60
|
update_cors_middleware,
|
|
59
61
|
)
|
|
62
|
+
from agno.registry import Registry
|
|
60
63
|
from agno.remote.base import RemoteDb, RemoteKnowledge
|
|
61
64
|
from agno.team import RemoteTeam, Team
|
|
62
65
|
from agno.utils.log import log_debug, log_error, log_info, log_warning
|
|
@@ -129,6 +132,7 @@ class AgentOS:
|
|
|
129
132
|
name: Optional[str] = None,
|
|
130
133
|
description: Optional[str] = None,
|
|
131
134
|
version: Optional[str] = None,
|
|
135
|
+
db: Optional[Union[BaseDb, AsyncBaseDb]] = None,
|
|
132
136
|
agents: Optional[List[Union[Agent, RemoteAgent]]] = None,
|
|
133
137
|
teams: Optional[List[Union[Team, RemoteTeam]]] = None,
|
|
134
138
|
workflows: Optional[List[Union[Workflow, RemoteWorkflow]]] = None,
|
|
@@ -145,10 +149,10 @@ class AgentOS:
|
|
|
145
149
|
base_app: Optional[FastAPI] = None,
|
|
146
150
|
on_route_conflict: Literal["preserve_agentos", "preserve_base_app", "error"] = "preserve_agentos",
|
|
147
151
|
tracing: bool = False,
|
|
148
|
-
tracing_db: Optional[Union[BaseDb, AsyncBaseDb]] = None,
|
|
149
152
|
auto_provision_dbs: bool = True,
|
|
150
153
|
run_hooks_in_background: bool = False,
|
|
151
154
|
telemetry: bool = True,
|
|
155
|
+
registry: Optional[Registry] = None,
|
|
152
156
|
):
|
|
153
157
|
"""Initialize AgentOS.
|
|
154
158
|
|
|
@@ -157,6 +161,7 @@ class AgentOS:
|
|
|
157
161
|
name: Name of the AgentOS instance
|
|
158
162
|
description: Description of the AgentOS instance
|
|
159
163
|
version: Version of the AgentOS instance
|
|
164
|
+
db: Default database for the AgentOS instance. Agents, teams and workflows with no db will use this one.
|
|
160
165
|
agents: List of agents to include in the OS
|
|
161
166
|
teams: List of teams to include in the OS
|
|
162
167
|
workflows: List of workflows to include in the OS
|
|
@@ -174,14 +179,13 @@ class AgentOS:
|
|
|
174
179
|
authorization_config: Configuration for the authorization middleware
|
|
175
180
|
cors_allowed_origins: List of allowed CORS origins (will be merged with default Agno domains)
|
|
176
181
|
tracing: If True, enables OpenTelemetry tracing for all agents and teams in the OS
|
|
177
|
-
tracing_db: Dedicated database for storing and reading traces. Recommended for multi-db setups.
|
|
178
|
-
If not provided and tracing=True, the first available db from agents/teams/workflows is used.
|
|
179
182
|
run_hooks_in_background: If True, run agent/team pre/post hooks as FastAPI background tasks (non-blocking)
|
|
180
183
|
telemetry: Whether to enable telemetry
|
|
184
|
+
registry: Optional registry to use for the AgentOS
|
|
181
185
|
|
|
182
186
|
"""
|
|
183
|
-
if not agents and not workflows and not teams and not knowledge:
|
|
184
|
-
raise ValueError("Either agents, teams, workflows
|
|
187
|
+
if not agents and not workflows and not teams and not knowledge and not db:
|
|
188
|
+
raise ValueError("Either agents, teams, workflows, knowledge bases or a database must be provided.")
|
|
185
189
|
|
|
186
190
|
self.config = load_yaml_config(config) if isinstance(config, str) else config
|
|
187
191
|
|
|
@@ -214,14 +218,16 @@ class AgentOS:
|
|
|
214
218
|
|
|
215
219
|
self.version = version
|
|
216
220
|
self.description = description
|
|
221
|
+
self.db = db
|
|
217
222
|
|
|
218
223
|
self.telemetry = telemetry
|
|
219
224
|
self.tracing = tracing
|
|
220
|
-
self.tracing_db = tracing_db
|
|
221
225
|
|
|
222
226
|
self.enable_mcp_server = enable_mcp_server
|
|
223
227
|
self.lifespan = lifespan
|
|
224
228
|
|
|
229
|
+
self.registry = registry
|
|
230
|
+
|
|
225
231
|
# RBAC
|
|
226
232
|
self.authorization = authorization
|
|
227
233
|
self.authorization_config = authorization_config
|
|
@@ -309,6 +315,12 @@ class AgentOS:
|
|
|
309
315
|
get_traces_router(dbs=self.dbs),
|
|
310
316
|
get_database_router(self, settings=self.settings),
|
|
311
317
|
]
|
|
318
|
+
# Add component and registry routers only if a sync db (BaseDb) is available
|
|
319
|
+
# Component routes require sync database operations
|
|
320
|
+
if self.db is not None and isinstance(self.db, BaseDb):
|
|
321
|
+
updated_routers.append(get_components_router(os_db=self.db, registry=self.registry))
|
|
322
|
+
if self.registry is not None:
|
|
323
|
+
updated_routers.append(get_registry_router(registry=self.registry))
|
|
312
324
|
|
|
313
325
|
# Clear all previously existing routes
|
|
314
326
|
app.router.routes = [
|
|
@@ -338,8 +350,8 @@ class AgentOS:
|
|
|
338
350
|
|
|
339
351
|
self._add_router(app, get_health_router(health_endpoint="/health"))
|
|
340
352
|
self._add_router(app, get_base_router(self, settings=self.settings))
|
|
341
|
-
self._add_router(app, get_agent_router(self, settings=self.settings))
|
|
342
|
-
self._add_router(app, get_team_router(self, settings=self.settings))
|
|
353
|
+
self._add_router(app, get_agent_router(self, settings=self.settings, registry=self.registry))
|
|
354
|
+
self._add_router(app, get_team_router(self, settings=self.settings, registry=self.registry))
|
|
343
355
|
self._add_router(app, get_workflow_router(self, settings=self.settings))
|
|
344
356
|
self._add_router(app, get_websocket_router(self, settings=self.settings))
|
|
345
357
|
|
|
@@ -418,6 +430,9 @@ class AgentOS:
|
|
|
418
430
|
for agent in self.agents:
|
|
419
431
|
if isinstance(agent, RemoteAgent):
|
|
420
432
|
continue
|
|
433
|
+
# Set the default db to agents without their own
|
|
434
|
+
if self.db is not None and agent.db is None:
|
|
435
|
+
agent.db = self.db
|
|
421
436
|
# Track all MCP tools to later handle their connection
|
|
422
437
|
if agent.tools:
|
|
423
438
|
for tool in agent.tools:
|
|
@@ -444,6 +459,11 @@ class AgentOS:
|
|
|
444
459
|
for team in self.teams:
|
|
445
460
|
if isinstance(team, RemoteTeam):
|
|
446
461
|
continue
|
|
462
|
+
|
|
463
|
+
# Set the default db to teams without their own
|
|
464
|
+
if self.db is not None and team.db is None:
|
|
465
|
+
team.db = self.db
|
|
466
|
+
|
|
447
467
|
# Track all MCP tools recursively
|
|
448
468
|
collect_mcp_tools_from_team(team, self.mcp_tools)
|
|
449
469
|
|
|
@@ -467,31 +487,34 @@ class AgentOS:
|
|
|
467
487
|
if not self.workflows:
|
|
468
488
|
return
|
|
469
489
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
490
|
+
for workflow in self.workflows:
|
|
491
|
+
if isinstance(workflow, RemoteWorkflow):
|
|
492
|
+
continue
|
|
493
|
+
# Set the default db to workflows without their own
|
|
494
|
+
if self.db is not None and workflow.db is None:
|
|
495
|
+
workflow.db = self.db
|
|
476
496
|
|
|
477
|
-
|
|
478
|
-
|
|
497
|
+
# Track MCP tools recursively in workflow members
|
|
498
|
+
collect_mcp_tools_from_workflow(workflow, self.mcp_tools)
|
|
479
499
|
|
|
480
|
-
|
|
481
|
-
workflow.
|
|
500
|
+
if not workflow.id:
|
|
501
|
+
workflow.id = generate_id_from_name(workflow.name)
|
|
482
502
|
|
|
483
|
-
|
|
484
|
-
|
|
503
|
+
# Required for the built-in routes to work
|
|
504
|
+
workflow.store_events = True
|
|
505
|
+
|
|
506
|
+
# Propagate run_hooks_in_background setting to workflow and all its step agents/teams
|
|
507
|
+
workflow.propagate_run_hooks_in_background(self.run_hooks_in_background)
|
|
485
508
|
|
|
486
509
|
def _setup_tracing(self) -> None:
|
|
487
510
|
"""Set up OpenTelemetry tracing for this AgentOS.
|
|
488
511
|
|
|
489
|
-
Uses
|
|
512
|
+
Uses the AgentOS db if provided, otherwise falls back to the first available
|
|
490
513
|
database from agents/teams/workflows.
|
|
491
514
|
"""
|
|
492
|
-
# Use
|
|
493
|
-
if self.
|
|
494
|
-
setup_tracing_for_os(db=self.
|
|
515
|
+
# Use AgentOS db if explicitly provided
|
|
516
|
+
if self.db is not None:
|
|
517
|
+
setup_tracing_for_os(db=self.db)
|
|
495
518
|
return
|
|
496
519
|
|
|
497
520
|
# Fall back to finding the first available database
|
|
@@ -517,7 +540,7 @@ class AgentOS:
|
|
|
517
540
|
if db is None:
|
|
518
541
|
log_warning(
|
|
519
542
|
"tracing=True but no database found. "
|
|
520
|
-
"Provide '
|
|
543
|
+
"Provide 'db' parameter to AgentOS or to at least one agent/team/workflow."
|
|
521
544
|
)
|
|
522
545
|
return
|
|
523
546
|
|
|
@@ -605,6 +628,12 @@ class AgentOS:
|
|
|
605
628
|
get_traces_router(dbs=self.dbs),
|
|
606
629
|
get_database_router(self, settings=self.settings),
|
|
607
630
|
]
|
|
631
|
+
# Add component and registry routers only if a sync db (BaseDb) is available
|
|
632
|
+
# Component routes require sync database operations
|
|
633
|
+
if self.db is not None and isinstance(self.db, BaseDb):
|
|
634
|
+
routers.append(get_components_router(os_db=self.db, registry=self.registry))
|
|
635
|
+
if self.registry is not None:
|
|
636
|
+
routers.append(get_registry_router(registry=self.registry))
|
|
608
637
|
|
|
609
638
|
for router in routers:
|
|
610
639
|
self._add_router(fastapi_app, router)
|
|
@@ -676,6 +705,11 @@ class AgentOS:
|
|
|
676
705
|
|
|
677
706
|
self._add_jwt_middleware(fastapi_app)
|
|
678
707
|
|
|
708
|
+
# Add trailing slash normalization middleware
|
|
709
|
+
from agno.os.middleware.trailing_slash import TrailingSlashMiddleware
|
|
710
|
+
|
|
711
|
+
fastapi_app.add_middleware(TrailingSlashMiddleware)
|
|
712
|
+
|
|
679
713
|
return fastapi_app
|
|
680
714
|
|
|
681
715
|
def _add_jwt_middleware(self, fastapi_app: FastAPI) -> None:
|
|
@@ -807,14 +841,16 @@ class AgentOS:
|
|
|
807
841
|
for agent in self.agents or []:
|
|
808
842
|
if agent.db:
|
|
809
843
|
self._register_db_with_validation(dbs, agent.db)
|
|
810
|
-
|
|
811
|
-
|
|
844
|
+
agent_contents_db = getattr(agent.knowledge, "contents_db", None) if agent.knowledge else None
|
|
845
|
+
if agent_contents_db:
|
|
846
|
+
self._register_db_with_validation(knowledge_dbs, agent_contents_db)
|
|
812
847
|
|
|
813
848
|
for team in self.teams or []:
|
|
814
849
|
if team.db:
|
|
815
850
|
self._register_db_with_validation(dbs, team.db)
|
|
816
|
-
|
|
817
|
-
|
|
851
|
+
team_contents_db = getattr(team.knowledge, "contents_db", None) if team.knowledge else None
|
|
852
|
+
if team_contents_db:
|
|
853
|
+
self._register_db_with_validation(knowledge_dbs, team_contents_db)
|
|
818
854
|
|
|
819
855
|
for workflow in self.workflows or []:
|
|
820
856
|
if workflow.db:
|
|
@@ -830,9 +866,9 @@ class AgentOS:
|
|
|
830
866
|
elif interface.team and interface.team.db:
|
|
831
867
|
self._register_db_with_validation(dbs, interface.team.db)
|
|
832
868
|
|
|
833
|
-
# Register
|
|
834
|
-
if self.
|
|
835
|
-
self._register_db_with_validation(dbs, self.
|
|
869
|
+
# Register AgentOS db if provided
|
|
870
|
+
if self.db is not None:
|
|
871
|
+
self._register_db_with_validation(dbs, self.db)
|
|
836
872
|
|
|
837
873
|
self.dbs = dbs
|
|
838
874
|
self.knowledge_dbs = knowledge_dbs
|
|
@@ -939,18 +975,21 @@ class AgentOS:
|
|
|
939
975
|
|
|
940
976
|
def _auto_discover_knowledge_instances(self) -> None:
|
|
941
977
|
"""Auto-discover the knowledge instances used by all contextual agents, teams and workflows."""
|
|
942
|
-
seen_ids = set()
|
|
978
|
+
seen_ids: set[str] = set()
|
|
943
979
|
knowledge_instances: List[Union[Knowledge, RemoteKnowledge]] = []
|
|
944
980
|
|
|
945
|
-
def _add_knowledge_if_not_duplicate(knowledge:
|
|
981
|
+
def _add_knowledge_if_not_duplicate(knowledge: Any) -> None:
|
|
946
982
|
"""Add knowledge instance if it's not already in the list (by object identity or db_id)."""
|
|
947
|
-
#
|
|
948
|
-
|
|
983
|
+
# Only handle Knowledge and RemoteKnowledge instances that have contents_db
|
|
984
|
+
contents_db = getattr(knowledge, "contents_db", None)
|
|
985
|
+
if not contents_db:
|
|
949
986
|
return
|
|
950
|
-
if
|
|
987
|
+
if contents_db.id in seen_ids:
|
|
951
988
|
return
|
|
952
|
-
seen_ids.add(
|
|
953
|
-
|
|
989
|
+
seen_ids.add(contents_db.id)
|
|
990
|
+
# Only append if it's a Knowledge or RemoteKnowledge instance
|
|
991
|
+
if isinstance(knowledge, (Knowledge, RemoteKnowledge)):
|
|
992
|
+
knowledge_instances.append(knowledge)
|
|
954
993
|
|
|
955
994
|
for agent in self.agents or []:
|
|
956
995
|
if agent.knowledge:
|
|
@@ -1080,13 +1119,13 @@ class AgentOS:
|
|
|
1080
1119
|
|
|
1081
1120
|
dbs_with_specific_config = [db.db_id for db in traces_config.dbs]
|
|
1082
1121
|
|
|
1083
|
-
# If
|
|
1084
|
-
if self.
|
|
1085
|
-
if self.
|
|
1122
|
+
# If AgentOS db is explicitly set, only use that database for traces
|
|
1123
|
+
if self.db is not None:
|
|
1124
|
+
if self.db.id not in dbs_with_specific_config:
|
|
1086
1125
|
traces_config.dbs.append(
|
|
1087
1126
|
DatabaseConfig(
|
|
1088
|
-
db_id=self.
|
|
1089
|
-
domain_config=TracesDomainConfig(display_name=self.
|
|
1127
|
+
db_id=self.db.id,
|
|
1128
|
+
domain_config=TracesDomainConfig(display_name=self.db.id),
|
|
1090
1129
|
)
|
|
1091
1130
|
)
|
|
1092
1131
|
else:
|
agno/os/middleware/__init__.py
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
2
|
+
from starlette.requests import Request
|
|
3
|
+
from starlette.responses import Response
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TrailingSlashMiddleware(BaseHTTPMiddleware):
|
|
7
|
+
"""
|
|
8
|
+
Middleware that strips trailing slashes from request paths.
|
|
9
|
+
|
|
10
|
+
This ensures that both /agents and /agents/ are handled identically
|
|
11
|
+
without requiring a redirect. Updates both 'path' and 'raw_path'
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
async def dispatch(self, request: Request, call_next) -> Response:
|
|
15
|
+
# Get the path from the request scope
|
|
16
|
+
path = request.scope.get("path", "")
|
|
17
|
+
|
|
18
|
+
# Strip trailing slash if path is not root "/"
|
|
19
|
+
if path != "/" and path.endswith("/"):
|
|
20
|
+
normalized_path = path.rstrip("/")
|
|
21
|
+
if normalized_path: # Ensure we don't end up with empty path
|
|
22
|
+
# Modify the scope to remove trailing slash
|
|
23
|
+
request.scope["path"] = normalized_path
|
|
24
|
+
# Update raw_path for ASGI spec compliance
|
|
25
|
+
request.scope["raw_path"] = normalized_path.encode("utf-8")
|
|
26
|
+
|
|
27
|
+
return await call_next(request)
|
agno/os/router.py
CHANGED
|
@@ -165,6 +165,7 @@ def get_base_router(
|
|
|
165
165
|
os_id=os.id or "Unnamed OS",
|
|
166
166
|
description=os.description,
|
|
167
167
|
available_models=os.config.available_models if os.config else [],
|
|
168
|
+
os_database=os.db.id if os.db else None,
|
|
168
169
|
databases=list({db.id for db_id, dbs in os.dbs.items() for db in dbs}),
|
|
169
170
|
chat=os.config.chat if os.config else None,
|
|
170
171
|
session=os._get_session_config(),
|
agno/os/routers/agents/router.py
CHANGED
|
@@ -16,6 +16,7 @@ from fastapi.responses import JSONResponse, StreamingResponse
|
|
|
16
16
|
|
|
17
17
|
from agno.agent.agent import Agent
|
|
18
18
|
from agno.agent.remote import RemoteAgent
|
|
19
|
+
from agno.db.base import BaseDb
|
|
19
20
|
from agno.exceptions import InputCheckError, OutputCheckError
|
|
20
21
|
from agno.media import Audio, Image, Video
|
|
21
22
|
from agno.media import File as FileMedia
|
|
@@ -38,6 +39,7 @@ from agno.os.utils import (
|
|
|
38
39
|
process_image,
|
|
39
40
|
process_video,
|
|
40
41
|
)
|
|
42
|
+
from agno.registry import Registry
|
|
41
43
|
from agno.run.agent import RunErrorEvent, RunOutput
|
|
42
44
|
from agno.utils.log import log_debug, log_error, log_warning
|
|
43
45
|
|
|
@@ -156,6 +158,7 @@ async def agent_continue_response_streamer(
|
|
|
156
158
|
def get_agent_router(
|
|
157
159
|
os: "AgentOS",
|
|
158
160
|
settings: AgnoAPISettings = AgnoAPISettings(),
|
|
161
|
+
registry: Optional[Registry] = None,
|
|
159
162
|
) -> APIRouter:
|
|
160
163
|
"""
|
|
161
164
|
Create the agent router with comprehensive OpenAPI documentation.
|
|
@@ -216,6 +219,7 @@ def get_agent_router(
|
|
|
216
219
|
session_id: Optional[str] = Form(None),
|
|
217
220
|
user_id: Optional[str] = Form(None),
|
|
218
221
|
files: Optional[List[UploadFile]] = File(None),
|
|
222
|
+
version: Optional[str] = Form(None),
|
|
219
223
|
):
|
|
220
224
|
kwargs = await get_request_kwargs(request, create_agent_run)
|
|
221
225
|
|
|
@@ -243,7 +247,9 @@ def get_agent_router(
|
|
|
243
247
|
log_warning("Metadata parameter passed in both request state and kwargs, using request state")
|
|
244
248
|
kwargs["metadata"] = metadata
|
|
245
249
|
|
|
246
|
-
agent = get_agent_by_id(
|
|
250
|
+
agent = get_agent_by_id(
|
|
251
|
+
agent_id, os.agents, os.db, registry, version=int(version) if version else None, create_fresh=True
|
|
252
|
+
)
|
|
247
253
|
if agent is None:
|
|
248
254
|
raise HTTPException(status_code=404, detail="Agent not found")
|
|
249
255
|
|
|
@@ -405,7 +411,7 @@ def get_agent_router(
|
|
|
405
411
|
agent_id: str,
|
|
406
412
|
run_id: str,
|
|
407
413
|
):
|
|
408
|
-
agent = get_agent_by_id(agent_id, os.agents, create_fresh=True)
|
|
414
|
+
agent = get_agent_by_id(agent_id=agent_id, agents=os.agents, db=os.db, registry=os.registry, create_fresh=True)
|
|
409
415
|
if agent is None:
|
|
410
416
|
raise HTTPException(status_code=404, detail="Agent not found")
|
|
411
417
|
|
|
@@ -464,7 +470,7 @@ def get_agent_router(
|
|
|
464
470
|
except json.JSONDecodeError:
|
|
465
471
|
raise HTTPException(status_code=400, detail="Invalid JSON in tools field")
|
|
466
472
|
|
|
467
|
-
agent = get_agent_by_id(agent_id, os.agents, create_fresh=True)
|
|
473
|
+
agent = get_agent_by_id(agent_id=agent_id, agents=os.agents, db=os.db, registry=os.registry, create_fresh=True)
|
|
468
474
|
if agent is None:
|
|
469
475
|
raise HTTPException(status_code=404, detail="Agent not found")
|
|
470
476
|
|
|
@@ -563,9 +569,6 @@ def get_agent_router(
|
|
|
563
569
|
)
|
|
564
570
|
async def get_agents(request: Request) -> List[AgentResponse]:
|
|
565
571
|
"""Return the list of all Agents present in the contextual OS"""
|
|
566
|
-
if os.agents is None:
|
|
567
|
-
return []
|
|
568
|
-
|
|
569
572
|
# Filter agents based on user's scopes (only if authorization is enabled)
|
|
570
573
|
if getattr(request.state, "authorization_enabled", False):
|
|
571
574
|
from agno.os.auth import filter_resources_by_access, get_accessible_resources
|
|
@@ -576,17 +579,27 @@ def get_agent_router(
|
|
|
576
579
|
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
|
577
580
|
|
|
578
581
|
# Limit results based on the user's access/scopes
|
|
579
|
-
accessible_agents = filter_resources_by_access(request, os.agents, "agents")
|
|
582
|
+
accessible_agents = filter_resources_by_access(request, os.agents or [], "agents")
|
|
580
583
|
else:
|
|
581
|
-
accessible_agents = os.agents
|
|
584
|
+
accessible_agents = os.agents or []
|
|
585
|
+
|
|
586
|
+
agents: List[AgentResponse] = []
|
|
587
|
+
if accessible_agents:
|
|
588
|
+
for agent in accessible_agents:
|
|
589
|
+
if isinstance(agent, RemoteAgent):
|
|
590
|
+
agents.append(await agent.get_agent_config())
|
|
591
|
+
else:
|
|
592
|
+
agent_response = await AgentResponse.from_agent(agent=agent)
|
|
593
|
+
agents.append(agent_response)
|
|
594
|
+
|
|
595
|
+
if os.db and isinstance(os.db, BaseDb):
|
|
596
|
+
from agno.agent.agent import get_agents
|
|
582
597
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
agent_response = await AgentResponse.from_agent(agent=agent)
|
|
589
|
-
agents.append(agent_response)
|
|
598
|
+
db_agents = get_agents(db=os.db, registry=registry)
|
|
599
|
+
if db_agents:
|
|
600
|
+
for db_agent in db_agents:
|
|
601
|
+
agent_response = await AgentResponse.from_agent(agent=db_agent)
|
|
602
|
+
agents.append(agent_response)
|
|
590
603
|
|
|
591
604
|
return agents
|
|
592
605
|
|
|
@@ -630,7 +643,7 @@ def get_agent_router(
|
|
|
630
643
|
dependencies=[Depends(require_resource_access("agents", "read", "agent_id"))],
|
|
631
644
|
)
|
|
632
645
|
async def get_agent(agent_id: str, request: Request) -> AgentResponse:
|
|
633
|
-
agent = get_agent_by_id(agent_id, os.agents, create_fresh=True)
|
|
646
|
+
agent = get_agent_by_id(agent_id=agent_id, agents=os.agents, db=os.db, registry=os.registry, create_fresh=True)
|
|
634
647
|
if agent is None:
|
|
635
648
|
raise HTTPException(status_code=404, detail="Agent not found")
|
|
636
649
|
|
agno/os/routers/agents/schema.py
CHANGED
|
@@ -65,7 +65,7 @@ class AgentResponse(BaseModel):
|
|
|
65
65
|
"enable_agentic_knowledge_filters": False,
|
|
66
66
|
# Memory defaults
|
|
67
67
|
"enable_agentic_memory": False,
|
|
68
|
-
"
|
|
68
|
+
"update_memory_on_run": False,
|
|
69
69
|
# Reasoning defaults
|
|
70
70
|
"reasoning": False,
|
|
71
71
|
"reasoning_min_steps": 1,
|
|
@@ -151,8 +151,9 @@ class AgentResponse(BaseModel):
|
|
|
151
151
|
"cache_session": agent.cache_session,
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
contents_db = getattr(agent.knowledge, "contents_db", None) if agent.knowledge else None
|
|
154
155
|
knowledge_info = {
|
|
155
|
-
"db_id":
|
|
156
|
+
"db_id": contents_db.id if contents_db else None,
|
|
156
157
|
"knowledge_table": knowledge_table,
|
|
157
158
|
"enable_agentic_knowledge_filters": agent.enable_agentic_knowledge_filters,
|
|
158
159
|
"knowledge_filters": agent.knowledge_filters,
|
|
@@ -163,9 +164,10 @@ class AgentResponse(BaseModel):
|
|
|
163
164
|
if agent.memory_manager is not None:
|
|
164
165
|
memory_info = {
|
|
165
166
|
"enable_agentic_memory": agent.enable_agentic_memory,
|
|
166
|
-
"
|
|
167
|
+
"update_memory_on_run": agent.update_memory_on_run,
|
|
168
|
+
"enable_user_memories": agent.enable_user_memories, # Soon to be deprecated. Use update_memory_on_run
|
|
167
169
|
"metadata": agent.metadata,
|
|
168
|
-
"memory_table": agent.db.memory_table_name if agent.db and agent.
|
|
170
|
+
"memory_table": agent.db.memory_table_name if agent.db and agent.update_memory_on_run else None,
|
|
169
171
|
}
|
|
170
172
|
|
|
171
173
|
if agent.memory_manager.model is not None:
|