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
agno/os/router.py CHANGED
@@ -1,12 +1,17 @@
1
- from typing import TYPE_CHECKING, List, Union, cast
1
+ import json
2
+ from typing import TYPE_CHECKING, List, cast
2
3
 
3
4
  from fastapi import (
4
5
  APIRouter,
5
6
  Depends,
7
+ HTTPException,
8
+ WebSocket,
6
9
  )
7
10
 
8
- from agno.agent.agent import Agent
9
- from agno.os.auth import get_authentication_dependency
11
+ from agno.exceptions import RemoteServerUnavailableError
12
+ from agno.os.auth import get_authentication_dependency, validate_websocket_token
13
+ from agno.os.managers import websocket_manager
14
+ from agno.os.routers.workflows.router import handle_workflow_subscription, handle_workflow_via_websocket
10
15
  from agno.os.schema import (
11
16
  AgentSummaryResponse,
12
17
  BadRequestResponse,
@@ -21,7 +26,7 @@ from agno.os.schema import (
21
26
  WorkflowSummaryResponse,
22
27
  )
23
28
  from agno.os.settings import AgnoAPISettings
24
- from agno.team.team import Team
29
+ from agno.utils.log import logger
25
30
 
26
31
  if TYPE_CHECKING:
27
32
  from agno.os.app import AgentOS
@@ -135,6 +140,27 @@ def get_base_router(
135
140
  },
136
141
  )
137
142
  async def config() -> ConfigResponse:
143
+ try:
144
+ agent_summaries = []
145
+ if os.agents:
146
+ for agent in os.agents:
147
+ agent_summaries.append(AgentSummaryResponse.from_agent(agent))
148
+
149
+ team_summaries = []
150
+ if os.teams:
151
+ for team in os.teams:
152
+ team_summaries.append(TeamSummaryResponse.from_team(team))
153
+
154
+ workflow_summaries = []
155
+ if os.workflows:
156
+ for workflow in os.workflows:
157
+ workflow_summaries.append(WorkflowSummaryResponse.from_workflow(workflow))
158
+ except RemoteServerUnavailableError as e:
159
+ raise HTTPException(
160
+ status_code=502,
161
+ detail=f"Failed to fetch config from remote AgentOS: {e}",
162
+ )
163
+
138
164
  return ConfigResponse(
139
165
  os_id=os.id or "Unnamed OS",
140
166
  description=os.description,
@@ -146,10 +172,10 @@ def get_base_router(
146
172
  knowledge=os._get_knowledge_config(),
147
173
  evals=os._get_evals_config(),
148
174
  metrics=os._get_metrics_config(),
175
+ agents=agent_summaries,
176
+ teams=team_summaries,
177
+ workflows=workflow_summaries,
149
178
  traces=os._get_traces_config(),
150
- agents=[AgentSummaryResponse.from_agent(agent) for agent in os.agents] if os.agents else [],
151
- teams=[TeamSummaryResponse.from_team(team) for team in os.teams] if os.teams else [],
152
- workflows=[WorkflowSummaryResponse.from_workflow(w) for w in os.workflows] if os.workflows else [],
153
179
  interfaces=[
154
180
  InterfaceResponse(type=interface.type, version=interface.version, route=interface.prefix)
155
181
  for interface in os.interfaces
@@ -183,20 +209,101 @@ def get_base_router(
183
209
  )
184
210
  async def get_models() -> List[Model]:
185
211
  """Return the list of all models used by agents and teams in the contextual OS"""
186
- all_components: List[Union[Agent, Team]] = []
212
+ unique_models = {}
213
+
214
+ # Collect models from local agents
187
215
  if os.agents:
188
- all_components.extend(os.agents)
189
- if os.teams:
190
- all_components.extend(os.teams)
216
+ for agent in os.agents:
217
+ model = cast(Model, agent.model)
218
+ if model and model.id is not None and model.provider is not None:
219
+ key = (model.id, model.provider)
220
+ if key not in unique_models:
221
+ unique_models[key] = Model(id=model.id, provider=model.provider)
191
222
 
192
- unique_models = {}
193
- for item in all_components:
194
- model = cast(Model, item.model)
195
- if model.id is not None and model.provider is not None:
196
- key = (model.id, model.provider)
197
- if key not in unique_models:
198
- unique_models[key] = Model(id=model.id, provider=model.provider)
223
+ # Collect models from local teams
224
+ if os.teams:
225
+ for team in os.teams:
226
+ model = cast(Model, team.model)
227
+ if model and model.id is not None and model.provider is not None:
228
+ key = (model.id, model.provider)
229
+ if key not in unique_models:
230
+ unique_models[key] = Model(id=model.id, provider=model.provider)
199
231
 
200
232
  return list(unique_models.values())
201
233
 
202
234
  return router
235
+
236
+
237
+ def get_websocket_router(
238
+ os: "AgentOS",
239
+ settings: AgnoAPISettings = AgnoAPISettings(),
240
+ ) -> APIRouter:
241
+ """
242
+ Create WebSocket router without HTTP authentication dependencies.
243
+ WebSocket endpoints handle authentication internally via message-based auth.
244
+ """
245
+ ws_router = APIRouter()
246
+
247
+ @ws_router.websocket(
248
+ "/workflows/ws",
249
+ name="workflow_websocket",
250
+ )
251
+ async def workflow_websocket_endpoint(websocket: WebSocket):
252
+ """WebSocket endpoint for receiving real-time workflow events"""
253
+ requires_auth = bool(settings.os_security_key)
254
+ await websocket_manager.connect(websocket, requires_auth=requires_auth)
255
+
256
+ try:
257
+ while True:
258
+ data = await websocket.receive_text()
259
+ message = json.loads(data)
260
+ action = message.get("action")
261
+
262
+ # Handle authentication first
263
+ if action == "authenticate":
264
+ token = message.get("token")
265
+ if not token:
266
+ await websocket.send_text(json.dumps({"event": "auth_error", "error": "Token is required"}))
267
+ continue
268
+
269
+ if validate_websocket_token(token, settings):
270
+ await websocket_manager.authenticate_websocket(websocket)
271
+ else:
272
+ await websocket.send_text(json.dumps({"event": "auth_error", "error": "Invalid token"}))
273
+ continue
274
+
275
+ # Check authentication for all other actions (only when required)
276
+ elif requires_auth and not websocket_manager.is_authenticated(websocket):
277
+ await websocket.send_text(
278
+ json.dumps(
279
+ {
280
+ "event": "auth_required",
281
+ "error": "Authentication required. Send authenticate action with valid token.",
282
+ }
283
+ )
284
+ )
285
+ continue
286
+
287
+ # Handle authenticated actions
288
+ elif action == "ping":
289
+ await websocket.send_text(json.dumps({"event": "pong"}))
290
+
291
+ elif action == "start-workflow":
292
+ # Handle workflow execution directly via WebSocket
293
+ await handle_workflow_via_websocket(websocket, message, os)
294
+
295
+ elif action == "reconnect":
296
+ # Subscribe/reconnect to an existing workflow run
297
+ await handle_workflow_subscription(websocket, message, os)
298
+
299
+ else:
300
+ await websocket.send_text(json.dumps({"event": "error", "error": f"Unknown action: {action}"}))
301
+
302
+ except Exception as e:
303
+ if "1012" not in str(e) and "1001" not in str(e):
304
+ logger.error(f"WebSocket error: {e}")
305
+ finally:
306
+ # Clean up the websocket connection
307
+ await websocket_manager.disconnect_websocket(websocket)
308
+
309
+ return ws_router
@@ -1,5 +1,5 @@
1
1
  import json
2
- from typing import TYPE_CHECKING, Any, AsyncGenerator, List, Optional, cast
2
+ from typing import TYPE_CHECKING, Any, AsyncGenerator, List, Optional, Union, cast
3
3
  from uuid import uuid4
4
4
 
5
5
  from fastapi import (
@@ -15,10 +15,11 @@ from fastapi import (
15
15
  from fastapi.responses import JSONResponse, StreamingResponse
16
16
 
17
17
  from agno.agent.agent import Agent
18
+ from agno.agent.remote import RemoteAgent
18
19
  from agno.exceptions import InputCheckError, OutputCheckError
19
20
  from agno.media import Audio, Image, Video
20
21
  from agno.media import File as FileMedia
21
- from agno.os.auth import get_authentication_dependency, require_resource_access
22
+ from agno.os.auth import get_auth_token_from_request, get_authentication_dependency, require_resource_access
22
23
  from agno.os.routers.agents.schema import AgentResponse
23
24
  from agno.os.schema import (
24
25
  BadRequestResponse,
@@ -45,7 +46,7 @@ if TYPE_CHECKING:
45
46
 
46
47
 
47
48
  async def agent_response_streamer(
48
- agent: Agent,
49
+ agent: Union[Agent, RemoteAgent],
49
50
  message: str,
50
51
  session_id: Optional[str] = None,
51
52
  user_id: Optional[str] = None,
@@ -54,6 +55,7 @@ async def agent_response_streamer(
54
55
  videos: Optional[List[Video]] = None,
55
56
  files: Optional[List[FileMedia]] = None,
56
57
  background_tasks: Optional[BackgroundTasks] = None,
58
+ auth_token: Optional[str] = None,
57
59
  **kwargs: Any,
58
60
  ) -> AsyncGenerator:
59
61
  try:
@@ -61,6 +63,15 @@ async def agent_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 agents
72
+ if auth_token and isinstance(agent, RemoteAgent):
73
+ kwargs["auth_token"] = auth_token
74
+
64
75
  run_response = agent.arun(
65
76
  input=message,
66
77
  session_id=session_id,
@@ -70,7 +81,7 @@ async def agent_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:
@@ -94,14 +105,20 @@ async def agent_response_streamer(
94
105
 
95
106
 
96
107
  async def agent_continue_response_streamer(
97
- agent: Agent,
98
- run_id: Optional[str] = None,
99
- updated_tools: Optional[List] = None,
108
+ agent: Union[Agent, RemoteAgent],
109
+ run_id: str,
110
+ updated_tools: List,
100
111
  session_id: Optional[str] = None,
101
112
  user_id: Optional[str] = None,
102
113
  background_tasks: Optional[BackgroundTasks] = None,
114
+ auth_token: Optional[str] = None,
103
115
  ) -> AsyncGenerator:
104
116
  try:
117
+ # Build kwargs for remote agent auth
118
+ extra_kwargs: dict = {}
119
+ if auth_token and isinstance(agent, RemoteAgent):
120
+ extra_kwargs["auth_token"] = auth_token
121
+
105
122
  continue_response = agent.acontinue_run(
106
123
  run_id=run_id,
107
124
  updated_tools=updated_tools,
@@ -110,6 +127,7 @@ async def agent_continue_response_streamer(
110
127
  stream=True,
111
128
  stream_events=True,
112
129
  background_tasks=background_tasks,
130
+ **extra_kwargs,
113
131
  )
114
132
  async for run_response_chunk in continue_response:
115
133
  yield format_sse_event(run_response_chunk) # type: ignore
@@ -194,32 +212,32 @@ def get_agent_router(
194
212
  request: Request,
195
213
  background_tasks: BackgroundTasks,
196
214
  message: str = Form(...),
197
- stream: bool = Form(False),
215
+ stream: bool = Form(True),
198
216
  session_id: Optional[str] = Form(None),
199
217
  user_id: Optional[str] = Form(None),
200
218
  files: Optional[List[UploadFile]] = File(None),
201
219
  ):
202
220
  kwargs = await get_request_kwargs(request, create_agent_run)
203
221
 
204
- if hasattr(request.state, "user_id"):
222
+ if hasattr(request.state, "user_id") and request.state.user_id is not None:
205
223
  if user_id:
206
224
  log_warning("User ID parameter passed in both request state and kwargs, using request state")
207
225
  user_id = request.state.user_id
208
- if hasattr(request.state, "session_id"):
226
+ if hasattr(request.state, "session_id") and request.state.session_id is not None:
209
227
  if session_id:
210
228
  log_warning("Session ID parameter passed in both request state and kwargs, using request state")
211
229
  session_id = request.state.session_id
212
- if hasattr(request.state, "session_state"):
230
+ if hasattr(request.state, "session_state") and request.state.session_state is not None:
213
231
  session_state = request.state.session_state
214
232
  if "session_state" in kwargs:
215
233
  log_warning("Session state parameter passed in both request state and kwargs, using request state")
216
234
  kwargs["session_state"] = session_state
217
- if hasattr(request.state, "dependencies"):
235
+ if hasattr(request.state, "dependencies") and request.state.dependencies is not None:
218
236
  dependencies = request.state.dependencies
219
237
  if "dependencies" in kwargs:
220
238
  log_warning("Dependencies parameter passed in both request state and kwargs, using request state")
221
239
  kwargs["dependencies"] = dependencies
222
- if hasattr(request.state, "metadata"):
240
+ if hasattr(request.state, "metadata") and request.state.metadata is not None:
223
241
  metadata = request.state.metadata
224
242
  if "metadata" in kwargs:
225
243
  log_warning("Metadata parameter passed in both request state and kwargs, using request state")
@@ -320,6 +338,9 @@ def get_agent_router(
320
338
  else:
321
339
  raise HTTPException(status_code=400, detail="Unsupported file type")
322
340
 
341
+ # Extract auth token for remote agents
342
+ auth_token = get_auth_token_from_request(request)
343
+
323
344
  if stream:
324
345
  return StreamingResponse(
325
346
  agent_response_streamer(
@@ -332,11 +353,16 @@ def get_agent_router(
332
353
  videos=base64_videos if base64_videos else None,
333
354
  files=input_files if input_files else None,
334
355
  background_tasks=background_tasks,
356
+ auth_token=auth_token,
335
357
  **kwargs,
336
358
  ),
337
359
  media_type="text/event-stream",
338
360
  )
339
361
  else:
362
+ # Pass auth_token for remote agents
363
+ if auth_token and isinstance(agent, RemoteAgent):
364
+ kwargs["auth_token"] = auth_token
365
+
340
366
  try:
341
367
  run_response = cast(
342
368
  RunOutput,
@@ -383,8 +409,9 @@ def get_agent_router(
383
409
  if agent is None:
384
410
  raise HTTPException(status_code=404, detail="Agent not found")
385
411
 
386
- if not agent.cancel_run(run_id=run_id):
387
- raise HTTPException(status_code=500, detail="Failed to cancel run")
412
+ cancelled = agent.cancel_run(run_id=run_id)
413
+ if not cancelled:
414
+ raise HTTPException(status_code=500, detail="Failed to cancel run - run not found or already completed")
388
415
 
389
416
  return JSONResponse(content={}, status_code=200)
390
417
 
@@ -426,9 +453,9 @@ def get_agent_router(
426
453
  user_id: Optional[str] = Form(None),
427
454
  stream: bool = Form(True),
428
455
  ):
429
- if hasattr(request.state, "user_id"):
456
+ if hasattr(request.state, "user_id") and request.state.user_id is not None:
430
457
  user_id = request.state.user_id
431
- if hasattr(request.state, "session_id"):
458
+ if hasattr(request.state, "session_id") and request.state.session_id is not None:
432
459
  session_id = request.state.session_id
433
460
 
434
461
  # Parse the JSON string manually
@@ -447,7 +474,7 @@ def get_agent_router(
447
474
  )
448
475
 
449
476
  # Convert tools dict to ToolExecution objects if provided
450
- updated_tools = None
477
+ updated_tools = []
451
478
  if tools_data:
452
479
  try:
453
480
  from agno.models.response import ToolExecution
@@ -456,6 +483,9 @@ def get_agent_router(
456
483
  except Exception as e:
457
484
  raise HTTPException(status_code=400, detail=f"Invalid structure or content for tools: {str(e)}")
458
485
 
486
+ # Extract auth token for remote agents
487
+ auth_token = get_auth_token_from_request(request)
488
+
459
489
  if stream:
460
490
  return StreamingResponse(
461
491
  agent_continue_response_streamer(
@@ -465,20 +495,27 @@ def get_agent_router(
465
495
  session_id=session_id,
466
496
  user_id=user_id,
467
497
  background_tasks=background_tasks,
498
+ auth_token=auth_token,
468
499
  ),
469
500
  media_type="text/event-stream",
470
501
  )
471
502
  else:
503
+ # Build extra kwargs for remote agent auth
504
+ extra_kwargs: dict = {}
505
+ if auth_token and isinstance(agent, RemoteAgent):
506
+ extra_kwargs["auth_token"] = auth_token
507
+
472
508
  try:
473
509
  run_response_obj = cast(
474
510
  RunOutput,
475
- await agent.acontinue_run(
511
+ await agent.acontinue_run( # type: ignore
476
512
  run_id=run_id, # run_id from path
477
513
  updated_tools=updated_tools,
478
514
  session_id=session_id,
479
515
  user_id=user_id,
480
516
  stream=False,
481
517
  background_tasks=background_tasks,
518
+ **extra_kwargs,
482
519
  ),
483
520
  )
484
521
  return run_response_obj.to_dict()
@@ -545,8 +582,11 @@ def get_agent_router(
545
582
 
546
583
  agents = []
547
584
  for agent in accessible_agents:
548
- agent_response = await AgentResponse.from_agent(agent=agent)
549
- agents.append(agent_response)
585
+ if isinstance(agent, RemoteAgent):
586
+ agents.append(await agent.get_agent_config())
587
+ else:
588
+ agent_response = await AgentResponse.from_agent(agent=agent)
589
+ agents.append(agent_response)
550
590
 
551
591
  return agents
552
592
 
@@ -594,6 +634,9 @@ def get_agent_router(
594
634
  if agent is None:
595
635
  raise HTTPException(status_code=404, detail="Agent not found")
596
636
 
597
- return await AgentResponse.from_agent(agent)
637
+ if isinstance(agent, RemoteAgent):
638
+ return await agent.get_agent_config()
639
+ else:
640
+ return await AgentResponse.from_agent(agent=agent)
598
641
 
599
642
  return router
@@ -8,7 +8,6 @@ from agno.models.message import Message
8
8
  from agno.os.schema import ModelResponse
9
9
  from agno.os.utils import (
10
10
  format_tools,
11
- get_agent_input_schema_dict,
12
11
  )
13
12
  from agno.run import RunContext
14
13
  from agno.run.agent import RunOutput
@@ -20,6 +19,8 @@ class AgentResponse(BaseModel):
20
19
  id: Optional[str] = None
21
20
  name: Optional[str] = None
22
21
  db_id: Optional[str] = None
22
+ description: Optional[str] = None
23
+ role: Optional[str] = None
23
24
  model: Optional[ModelResponse] = None
24
25
  tools: Optional[Dict[str, Any]] = None
25
26
  sessions: Optional[Dict[str, Any]] = None
@@ -93,7 +94,6 @@ class AgentResponse(BaseModel):
93
94
  "use_json_mode": False,
94
95
  # Streaming defaults
95
96
  "stream_events": False,
96
- "stream_intermediate_steps": False,
97
97
  }
98
98
 
99
99
  session_id = str(uuid4())
@@ -110,6 +110,16 @@ class AgentResponse(BaseModel):
110
110
  if additional_input and isinstance(additional_input[0], Message):
111
111
  additional_input = [message.to_dict() for message in additional_input] # type: ignore
112
112
 
113
+ input_schema_dict = None
114
+ if agent.input_schema is not None:
115
+ if isinstance(agent.input_schema, dict):
116
+ input_schema_dict = agent.input_schema
117
+ else:
118
+ try:
119
+ input_schema_dict = agent.input_schema.model_json_schema()
120
+ except Exception:
121
+ pass
122
+
113
123
  # Build model only if it has at least one non-null field
114
124
  model_name = agent.model.name if (agent.model and agent.model.name) else None
115
125
  model_provider = agent.model.provider if (agent.model and agent.model.provider) else None
@@ -142,6 +152,7 @@ class AgentResponse(BaseModel):
142
152
  }
143
153
 
144
154
  knowledge_info = {
155
+ "db_id": agent.knowledge.contents_db.id if agent.knowledge and agent.knowledge.contents_db else None,
145
156
  "knowledge_table": knowledge_table,
146
157
  "enable_agentic_knowledge_filters": agent.enable_agentic_knowledge_filters,
147
158
  "knowledge_filters": agent.knowledge_filters,
@@ -250,13 +261,14 @@ class AgentResponse(BaseModel):
250
261
  streaming_info = {
251
262
  "stream": agent.stream,
252
263
  "stream_events": agent.stream_events,
253
- "stream_intermediate_steps": agent.stream_intermediate_steps,
254
264
  }
255
265
 
256
266
  return AgentResponse(
257
267
  id=agent.id,
258
268
  name=agent.name,
259
269
  db_id=agent.db.id if agent.db else None,
270
+ description=agent.description,
271
+ role=agent.role,
260
272
  model=ModelResponse(**_agent_model_data) if _agent_model_data else None,
261
273
  tools=filter_meaningful_config(tools_info, {}),
262
274
  sessions=filter_meaningful_config(sessions_info, agent_defaults),
@@ -270,5 +282,5 @@ class AgentResponse(BaseModel):
270
282
  streaming=filter_meaningful_config(streaming_info, agent_defaults),
271
283
  introduction=agent.introduction,
272
284
  metadata=agent.metadata,
273
- input_schema=get_agent_input_schema_dict(agent),
285
+ input_schema=input_schema_dict,
274
286
  )
@@ -22,6 +22,8 @@ from agno.os.settings import AgnoAPISettings
22
22
  from agno.os.utils import (
23
23
  get_db,
24
24
  )
25
+ from agno.remote.base import RemoteDb
26
+ from agno.utils.log import log_info
25
27
 
26
28
  if TYPE_CHECKING:
27
29
  from agno.os.app import AgentOS
@@ -45,6 +47,9 @@ def get_database_router(
45
47
 
46
48
  async def _migrate_single_db(db, target_version: Optional[str] = None) -> None:
47
49
  """Migrate a single database."""
50
+ if isinstance(db, RemoteDb):
51
+ log_info("Skipping logs for remote DB")
52
+
48
53
  if target_version:
49
54
  # Use the session table as proxy for the database schema version
50
55
  if isinstance(db, AsyncBaseDb):