agno 2.3.16__py3-none-any.whl → 2.3.17__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.
Files changed (75) hide show
  1. agno/agent/__init__.py +2 -0
  2. agno/agent/agent.py +4 -53
  3. agno/agent/remote.py +351 -0
  4. agno/client/__init__.py +3 -0
  5. agno/client/os.py +2669 -0
  6. agno/db/base.py +20 -0
  7. agno/db/mongo/async_mongo.py +11 -0
  8. agno/db/mongo/mongo.py +10 -0
  9. agno/db/mysql/async_mysql.py +9 -0
  10. agno/db/mysql/mysql.py +9 -0
  11. agno/db/postgres/async_postgres.py +9 -0
  12. agno/db/postgres/postgres.py +9 -0
  13. agno/db/postgres/utils.py +3 -2
  14. agno/db/sqlite/async_sqlite.py +9 -0
  15. agno/db/sqlite/sqlite.py +11 -1
  16. agno/exceptions.py +23 -0
  17. agno/knowledge/chunking/semantic.py +123 -46
  18. agno/knowledge/reader/csv_reader.py +1 -1
  19. agno/knowledge/reader/field_labeled_csv_reader.py +1 -1
  20. agno/knowledge/reader/json_reader.py +1 -1
  21. agno/os/app.py +104 -23
  22. agno/os/auth.py +25 -1
  23. agno/os/interfaces/a2a/a2a.py +7 -6
  24. agno/os/interfaces/a2a/router.py +13 -13
  25. agno/os/interfaces/agui/agui.py +5 -3
  26. agno/os/interfaces/agui/router.py +23 -16
  27. agno/os/interfaces/base.py +7 -7
  28. agno/os/interfaces/slack/router.py +6 -6
  29. agno/os/interfaces/slack/slack.py +7 -7
  30. agno/os/interfaces/whatsapp/router.py +29 -6
  31. agno/os/interfaces/whatsapp/whatsapp.py +11 -8
  32. agno/os/managers.py +326 -0
  33. agno/os/mcp.py +651 -79
  34. agno/os/router.py +125 -18
  35. agno/os/routers/agents/router.py +65 -22
  36. agno/os/routers/agents/schema.py +16 -4
  37. agno/os/routers/database.py +5 -0
  38. agno/os/routers/evals/evals.py +93 -11
  39. agno/os/routers/evals/utils.py +6 -6
  40. agno/os/routers/knowledge/knowledge.py +104 -16
  41. agno/os/routers/memory/memory.py +124 -7
  42. agno/os/routers/metrics/metrics.py +21 -4
  43. agno/os/routers/session/session.py +141 -12
  44. agno/os/routers/teams/router.py +40 -14
  45. agno/os/routers/teams/schema.py +12 -4
  46. agno/os/routers/traces/traces.py +54 -4
  47. agno/os/routers/workflows/router.py +223 -117
  48. agno/os/routers/workflows/schema.py +65 -1
  49. agno/os/schema.py +38 -12
  50. agno/os/utils.py +87 -166
  51. agno/remote/__init__.py +3 -0
  52. agno/remote/base.py +484 -0
  53. agno/run/workflow.py +1 -0
  54. agno/team/__init__.py +2 -0
  55. agno/team/remote.py +287 -0
  56. agno/team/team.py +25 -54
  57. agno/tracing/exporter.py +10 -6
  58. agno/tracing/setup.py +2 -1
  59. agno/utils/agent.py +58 -1
  60. agno/utils/http.py +68 -20
  61. agno/utils/os.py +0 -0
  62. agno/utils/remote.py +23 -0
  63. agno/vectordb/chroma/chromadb.py +452 -16
  64. agno/vectordb/pgvector/pgvector.py +7 -0
  65. agno/vectordb/redis/redisdb.py +1 -1
  66. agno/workflow/__init__.py +2 -0
  67. agno/workflow/agent.py +2 -2
  68. agno/workflow/remote.py +222 -0
  69. agno/workflow/types.py +0 -73
  70. agno/workflow/workflow.py +119 -68
  71. {agno-2.3.16.dist-info → agno-2.3.17.dist-info}/METADATA +1 -1
  72. {agno-2.3.16.dist-info → agno-2.3.17.dist-info}/RECORD +75 -65
  73. {agno-2.3.16.dist-info → agno-2.3.17.dist-info}/WHEEL +0 -0
  74. {agno-2.3.16.dist-info → agno-2.3.17.dist-info}/licenses/LICENSE +0 -0
  75. {agno-2.3.16.dist-info → agno-2.3.17.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- from typing import TYPE_CHECKING, Any, AsyncGenerator, List, Optional
1
+ from typing import TYPE_CHECKING, Any, AsyncGenerator, List, Optional, Union
2
2
  from uuid import uuid4
3
3
 
4
4
  from fastapi import (
@@ -16,7 +16,7 @@ from fastapi.responses import JSONResponse, StreamingResponse
16
16
  from agno.exceptions import InputCheckError, OutputCheckError
17
17
  from agno.media import Audio, Image, Video
18
18
  from agno.media import File as FileMedia
19
- from agno.os.auth import get_authentication_dependency, require_resource_access
19
+ from agno.os.auth import get_auth_token_from_request, get_authentication_dependency, require_resource_access
20
20
  from agno.os.routers.teams.schema import TeamResponse
21
21
  from agno.os.schema import (
22
22
  BadRequestResponse,
@@ -36,6 +36,7 @@ from agno.os.utils import (
36
36
  process_video,
37
37
  )
38
38
  from agno.run.team import RunErrorEvent as TeamRunErrorEvent
39
+ from agno.team.remote import RemoteTeam
39
40
  from agno.team.team import Team
40
41
  from agno.utils.log import log_warning, logger
41
42
 
@@ -44,7 +45,7 @@ if TYPE_CHECKING:
44
45
 
45
46
 
46
47
  async def team_response_streamer(
47
- team: Team,
48
+ team: Union[Team, RemoteTeam],
48
49
  message: str,
49
50
  session_id: Optional[str] = None,
50
51
  user_id: Optional[str] = None,
@@ -53,6 +54,7 @@ async def team_response_streamer(
53
54
  videos: Optional[List[Video]] = None,
54
55
  files: Optional[List[FileMedia]] = None,
55
56
  background_tasks: Optional[BackgroundTasks] = None,
57
+ auth_token: Optional[str] = None,
56
58
  **kwargs: Any,
57
59
  ) -> AsyncGenerator:
58
60
  """Run the given team asynchronously and yield its response"""
@@ -61,6 +63,15 @@ async def team_response_streamer(
61
63
  if background_tasks is not None:
62
64
  kwargs["background_tasks"] = background_tasks
63
65
 
66
+ if "stream_events" in kwargs:
67
+ stream_events = kwargs.pop("stream_events")
68
+ else:
69
+ stream_events = True
70
+
71
+ # Pass auth_token for remote teams
72
+ if auth_token and isinstance(team, RemoteTeam):
73
+ kwargs["auth_token"] = auth_token
74
+
64
75
  run_response = team.arun(
65
76
  input=message,
66
77
  session_id=session_id,
@@ -70,7 +81,7 @@ async def team_response_streamer(
70
81
  videos=videos,
71
82
  files=files,
72
83
  stream=True,
73
- stream_events=True,
84
+ stream_events=stream_events,
74
85
  **kwargs,
75
86
  )
76
87
  async for run_response_chunk in run_response:
@@ -157,25 +168,25 @@ def get_team_router(
157
168
  ):
158
169
  kwargs = await get_request_kwargs(request, create_team_run)
159
170
 
160
- if hasattr(request.state, "user_id"):
171
+ if hasattr(request.state, "user_id") and request.state.user_id is not None:
161
172
  if user_id:
162
173
  log_warning("User ID parameter passed in both request state and kwargs, using request state")
163
174
  user_id = request.state.user_id
164
- if hasattr(request.state, "session_id"):
175
+ if hasattr(request.state, "session_id") and request.state.session_id is not None:
165
176
  if session_id:
166
177
  log_warning("Session ID parameter passed in both request state and kwargs, using request state")
167
178
  session_id = request.state.session_id
168
- if hasattr(request.state, "session_state"):
179
+ if hasattr(request.state, "session_state") and request.state.session_state is not None:
169
180
  session_state = request.state.session_state
170
181
  if "session_state" in kwargs:
171
182
  log_warning("Session state parameter passed in both request state and kwargs, using request state")
172
183
  kwargs["session_state"] = session_state
173
- if hasattr(request.state, "dependencies"):
184
+ if hasattr(request.state, "dependencies") and request.state.dependencies is not None:
174
185
  dependencies = request.state.dependencies
175
186
  if "dependencies" in kwargs:
176
187
  log_warning("Dependencies parameter passed in both request state and kwargs, using request state")
177
188
  kwargs["dependencies"] = dependencies
178
- if hasattr(request.state, "metadata"):
189
+ if hasattr(request.state, "metadata") and request.state.metadata is not None:
179
190
  metadata = request.state.metadata
180
191
  if "metadata" in kwargs:
181
192
  log_warning("Metadata parameter passed in both request state and kwargs, using request state")
@@ -246,6 +257,9 @@ def get_team_router(
246
257
  else:
247
258
  raise HTTPException(status_code=400, detail="Unsupported file type")
248
259
 
260
+ # Extract auth token for remote teams
261
+ auth_token = get_auth_token_from_request(request)
262
+
249
263
  if stream:
250
264
  return StreamingResponse(
251
265
  team_response_streamer(
@@ -258,11 +272,16 @@ def get_team_router(
258
272
  videos=base64_videos if base64_videos else None,
259
273
  files=document_files if document_files else None,
260
274
  background_tasks=background_tasks,
275
+ auth_token=auth_token,
261
276
  **kwargs,
262
277
  ),
263
278
  media_type="text/event-stream",
264
279
  )
265
280
  else:
281
+ # Pass auth_token for remote teams
282
+ if auth_token and isinstance(team, RemoteTeam):
283
+ kwargs["auth_token"] = auth_token
284
+
266
285
  try:
267
286
  run_response = await team.arun(
268
287
  input=message,
@@ -306,8 +325,9 @@ def get_team_router(
306
325
  if team is None:
307
326
  raise HTTPException(status_code=404, detail="Team not found")
308
327
 
309
- if not team.cancel_run(run_id=run_id):
310
- raise HTTPException(status_code=500, detail="Failed to cancel run")
328
+ cancelled = team.cancel_run(run_id=run_id)
329
+ if not cancelled:
330
+ raise HTTPException(status_code=500, detail="Failed to cancel run - run not found or already completed")
311
331
 
312
332
  return JSONResponse(content={}, status_code=200)
313
333
 
@@ -412,8 +432,11 @@ def get_team_router(
412
432
 
413
433
  teams = []
414
434
  for team in accessible_teams:
415
- team_response = await TeamResponse.from_team(team=team)
416
- teams.append(team_response)
435
+ if isinstance(team, RemoteTeam):
436
+ teams.append(await team.get_team_config())
437
+ else:
438
+ team_response = await TeamResponse.from_team(team=team)
439
+ teams.append(team_response)
417
440
 
418
441
  return teams
419
442
 
@@ -507,6 +530,9 @@ def get_team_router(
507
530
  if team is None:
508
531
  raise HTTPException(status_code=404, detail="Team not found")
509
532
 
510
- return await TeamResponse.from_team(team)
533
+ if isinstance(team, RemoteTeam):
534
+ return await team.get_team_config()
535
+ else:
536
+ return await TeamResponse.from_team(team=team)
511
537
 
512
538
  return router
@@ -8,7 +8,6 @@ from agno.os.routers.agents.schema import AgentResponse
8
8
  from agno.os.schema import ModelResponse
9
9
  from agno.os.utils import (
10
10
  format_team_tools,
11
- get_team_input_schema_dict,
12
11
  )
13
12
  from agno.run import RunContext
14
13
  from agno.run.team import TeamRunOutput
@@ -22,6 +21,7 @@ class TeamResponse(BaseModel):
22
21
  name: Optional[str] = None
23
22
  db_id: Optional[str] = None
24
23
  description: Optional[str] = None
24
+ role: Optional[str] = None
25
25
  model: Optional[ModelResponse] = None
26
26
  tools: Optional[Dict[str, Any]] = None
27
27
  sessions: Optional[Dict[str, Any]] = None
@@ -85,7 +85,6 @@ class TeamResponse(BaseModel):
85
85
  "use_json_mode": False,
86
86
  # Streaming defaults
87
87
  "stream_events": False,
88
- "stream_intermediate_steps": False,
89
88
  "stream_member_events": False,
90
89
  }
91
90
 
@@ -103,6 +102,13 @@ class TeamResponse(BaseModel):
103
102
  team_tools = _tools
104
103
  formatted_tools = format_team_tools(team_tools) if team_tools else None
105
104
 
105
+ input_schema_dict = None
106
+ if team.input_schema is not None:
107
+ try:
108
+ input_schema_dict = team.input_schema.model_json_schema()
109
+ except Exception:
110
+ pass
111
+
106
112
  model_name = team.model.name or team.model.__class__.__name__ if team.model else None
107
113
  model_provider = team.model.provider or team.model.__class__.__name__ if team.model else ""
108
114
  model_id = team.model.id if team.model else None
@@ -132,6 +138,7 @@ class TeamResponse(BaseModel):
132
138
  }
133
139
 
134
140
  knowledge_info = {
141
+ "db_id": team.knowledge.contents_db.id if team.knowledge and team.knowledge.contents_db else None,
135
142
  "knowledge_table": knowledge_table,
136
143
  "enable_agentic_knowledge_filters": team.enable_agentic_knowledge_filters,
137
144
  "knowledge_filters": team.knowledge_filters,
@@ -227,7 +234,6 @@ class TeamResponse(BaseModel):
227
234
  streaming_info = {
228
235
  "stream": team.stream,
229
236
  "stream_events": team.stream_events,
230
- "stream_intermediate_steps": team.stream_intermediate_steps,
231
237
  "stream_member_events": team.stream_member_events,
232
238
  }
233
239
 
@@ -253,6 +259,8 @@ class TeamResponse(BaseModel):
253
259
  id=team.id,
254
260
  name=team.name,
255
261
  db_id=team.db.id if team.db else None,
262
+ description=team.description,
263
+ role=team.role,
256
264
  model=ModelResponse(**_team_model_data) if _team_model_data else None,
257
265
  tools=filter_meaningful_config(tools_info, {}),
258
266
  sessions=filter_meaningful_config(sessions_info, team_defaults),
@@ -266,5 +274,5 @@ class TeamResponse(BaseModel):
266
274
  streaming=filter_meaningful_config(streaming_info, team_defaults),
267
275
  members=members if members else None,
268
276
  metadata=team.metadata,
269
- input_schema=get_team_input_schema_dict(team),
277
+ input_schema=input_schema_dict,
270
278
  )
@@ -1,11 +1,11 @@
1
1
  import logging
2
2
  from typing import Optional, Union
3
3
 
4
- from fastapi import Depends, HTTPException, Query
4
+ from fastapi import Depends, HTTPException, Query, Request
5
5
  from fastapi.routing import APIRouter
6
6
 
7
7
  from agno.db.base import AsyncBaseDb, BaseDb
8
- from agno.os.auth import get_authentication_dependency
8
+ from agno.os.auth import get_auth_token_from_request, get_authentication_dependency
9
9
  from agno.os.routers.traces.schemas import (
10
10
  TraceDetail,
11
11
  TraceNode,
@@ -23,13 +23,14 @@ from agno.os.schema import (
23
23
  )
24
24
  from agno.os.settings import AgnoAPISettings
25
25
  from agno.os.utils import get_db, parse_datetime_to_utc
26
+ from agno.remote.base import RemoteDb
26
27
  from agno.utils.log import log_error
27
28
 
28
29
  logger = logging.getLogger(__name__)
29
30
 
30
31
 
31
32
  def get_traces_router(
32
- dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]], settings: AgnoAPISettings = AgnoAPISettings(), **kwargs
33
+ dbs: dict[str, list[Union[BaseDb, AsyncBaseDb, RemoteDb]]], settings: AgnoAPISettings = AgnoAPISettings(), **kwargs
33
34
  ) -> APIRouter:
34
35
  """Create traces router with comprehensive OpenAPI documentation for trace endpoints."""
35
36
  router = APIRouter(
@@ -46,7 +47,7 @@ def get_traces_router(
46
47
  return attach_routes(router=router, dbs=dbs)
47
48
 
48
49
 
49
- def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]]) -> APIRouter:
50
+ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBaseDb, RemoteDb]]]) -> APIRouter:
50
51
  @router.get(
51
52
  "/traces",
52
53
  response_model=PaginatedResponse[TraceSummary],
@@ -109,6 +110,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
109
110
  },
110
111
  )
111
112
  async def get_traces(
113
+ request: Request,
112
114
  run_id: Optional[str] = Query(default=None, description="Filter by run ID"),
113
115
  session_id: Optional[str] = Query(default=None, description="Filter by session ID"),
114
116
  user_id: Optional[str] = Query(default=None, description="Filter by user ID"),
@@ -134,6 +136,25 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
134
136
  # Get database using db_id or default to first available
135
137
  db = await get_db(dbs, db_id)
136
138
 
139
+ if isinstance(db, RemoteDb):
140
+ auth_token = get_auth_token_from_request(request)
141
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
142
+ return await db.get_traces(
143
+ run_id=run_id,
144
+ session_id=session_id,
145
+ user_id=user_id,
146
+ agent_id=agent_id,
147
+ team_id=team_id,
148
+ workflow_id=workflow_id,
149
+ status=status,
150
+ start_time=start_time,
151
+ end_time=end_time,
152
+ limit=limit,
153
+ page=page,
154
+ db_id=db_id,
155
+ headers=headers,
156
+ )
157
+
137
158
  try:
138
159
  start_time_ms = time_module.time() * 1000
139
160
 
@@ -303,6 +324,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
303
324
  },
304
325
  )
305
326
  async def get_trace(
327
+ request: Request,
306
328
  trace_id: str,
307
329
  span_id: Optional[str] = Query(default=None, description="Optional: Span ID to retrieve specific span"),
308
330
  run_id: Optional[str] = Query(default=None, description="Optional: Run ID to retrieve trace for"),
@@ -312,6 +334,17 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
312
334
  # Get database using db_id or default to first available
313
335
  db = await get_db(dbs, db_id)
314
336
 
337
+ if isinstance(db, RemoteDb):
338
+ auth_token = get_auth_token_from_request(request)
339
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
340
+ return await db.get_trace(
341
+ trace_id=trace_id,
342
+ span_id=span_id,
343
+ run_id=run_id,
344
+ db_id=db_id,
345
+ headers=headers,
346
+ )
347
+
315
348
  try:
316
349
  # If span_id is provided, return just that span
317
350
  if span_id:
@@ -408,6 +441,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
408
441
  },
409
442
  )
410
443
  async def get_trace_stats(
444
+ request: Request,
411
445
  user_id: Optional[str] = Query(default=None, description="Filter by user ID"),
412
446
  agent_id: Optional[str] = Query(default=None, description="Filter by agent ID"),
413
447
  team_id: Optional[str] = Query(default=None, description="Filter by team ID"),
@@ -430,6 +464,22 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
430
464
  # Get database using db_id or default to first available
431
465
  db = await get_db(dbs, db_id)
432
466
 
467
+ if isinstance(db, RemoteDb):
468
+ auth_token = get_auth_token_from_request(request)
469
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
470
+ return await db.get_trace_session_stats(
471
+ user_id=user_id,
472
+ agent_id=agent_id,
473
+ team_id=team_id,
474
+ workflow_id=workflow_id,
475
+ start_time=start_time,
476
+ end_time=end_time,
477
+ limit=limit,
478
+ page=page,
479
+ db_id=db_id,
480
+ headers=headers,
481
+ )
482
+
433
483
  try:
434
484
  start_time_ms = time_module.time() * 1000
435
485