agno 2.3.7__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.
Files changed (71) hide show
  1. agno/agent/agent.py +391 -335
  2. agno/db/mongo/async_mongo.py +0 -24
  3. agno/db/mongo/mongo.py +0 -16
  4. agno/db/mysql/__init__.py +2 -1
  5. agno/db/mysql/async_mysql.py +2888 -0
  6. agno/db/mysql/mysql.py +17 -27
  7. agno/db/mysql/utils.py +139 -6
  8. agno/db/postgres/async_postgres.py +10 -26
  9. agno/db/postgres/postgres.py +7 -25
  10. agno/db/redis/redis.py +0 -4
  11. agno/db/schemas/evals.py +1 -0
  12. agno/db/singlestore/singlestore.py +5 -12
  13. agno/db/sqlite/async_sqlite.py +2 -26
  14. agno/db/sqlite/sqlite.py +0 -20
  15. agno/eval/__init__.py +10 -0
  16. agno/eval/agent_as_judge.py +860 -0
  17. agno/eval/base.py +29 -0
  18. agno/eval/utils.py +2 -1
  19. agno/exceptions.py +7 -0
  20. agno/knowledge/embedder/openai.py +8 -8
  21. agno/knowledge/knowledge.py +1142 -176
  22. agno/media.py +22 -6
  23. agno/models/aws/claude.py +8 -7
  24. agno/models/base.py +160 -11
  25. agno/models/deepseek/deepseek.py +67 -0
  26. agno/models/google/gemini.py +65 -11
  27. agno/models/google/utils.py +22 -0
  28. agno/models/message.py +2 -0
  29. agno/models/openai/chat.py +4 -0
  30. agno/models/openai/responses.py +3 -2
  31. agno/os/app.py +64 -74
  32. agno/os/interfaces/a2a/router.py +3 -4
  33. agno/os/interfaces/a2a/utils.py +1 -1
  34. agno/os/interfaces/agui/router.py +2 -0
  35. agno/os/middleware/jwt.py +8 -6
  36. agno/os/router.py +3 -1607
  37. agno/os/routers/agents/__init__.py +3 -0
  38. agno/os/routers/agents/router.py +581 -0
  39. agno/os/routers/agents/schema.py +261 -0
  40. agno/os/routers/evals/evals.py +26 -6
  41. agno/os/routers/evals/schemas.py +34 -2
  42. agno/os/routers/evals/utils.py +101 -20
  43. agno/os/routers/knowledge/knowledge.py +1 -1
  44. agno/os/routers/teams/__init__.py +3 -0
  45. agno/os/routers/teams/router.py +496 -0
  46. agno/os/routers/teams/schema.py +257 -0
  47. agno/os/routers/workflows/__init__.py +3 -0
  48. agno/os/routers/workflows/router.py +545 -0
  49. agno/os/routers/workflows/schema.py +75 -0
  50. agno/os/schema.py +1 -559
  51. agno/os/utils.py +139 -2
  52. agno/team/team.py +159 -100
  53. agno/tools/file_generation.py +12 -6
  54. agno/tools/firecrawl.py +15 -7
  55. agno/tools/workflow.py +8 -1
  56. agno/utils/hooks.py +64 -5
  57. agno/utils/http.py +2 -2
  58. agno/utils/media.py +11 -1
  59. agno/utils/print_response/agent.py +8 -0
  60. agno/utils/print_response/team.py +8 -0
  61. agno/vectordb/pgvector/pgvector.py +88 -51
  62. agno/workflow/parallel.py +11 -5
  63. agno/workflow/step.py +17 -5
  64. agno/workflow/types.py +38 -2
  65. agno/workflow/workflow.py +12 -4
  66. {agno-2.3.7.dist-info → agno-2.3.9.dist-info}/METADATA +8 -3
  67. {agno-2.3.7.dist-info → agno-2.3.9.dist-info}/RECORD +70 -58
  68. agno/tools/memori.py +0 -339
  69. {agno-2.3.7.dist-info → agno-2.3.9.dist-info}/WHEEL +0 -0
  70. {agno-2.3.7.dist-info → agno-2.3.9.dist-info}/licenses/LICENSE +0 -0
  71. {agno-2.3.7.dist-info → agno-2.3.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,3 @@
1
+ from agno.os.routers.agents.router import get_agent_router
2
+
3
+ __all__ = ["get_agent_router"]
@@ -0,0 +1,581 @@
1
+ import json
2
+ from typing import TYPE_CHECKING, Any, AsyncGenerator, List, Optional, cast
3
+ from uuid import uuid4
4
+
5
+ from fastapi import (
6
+ APIRouter,
7
+ BackgroundTasks,
8
+ Depends,
9
+ File,
10
+ Form,
11
+ HTTPException,
12
+ Request,
13
+ UploadFile,
14
+ )
15
+ from fastapi.responses import JSONResponse, StreamingResponse
16
+
17
+ from agno.agent.agent import Agent
18
+ from agno.exceptions import InputCheckError, OutputCheckError
19
+ from agno.media import Audio, Image, Video
20
+ from agno.media import File as FileMedia
21
+ from agno.os.auth import get_authentication_dependency
22
+ from agno.os.routers.agents.schema import AgentResponse
23
+ from agno.os.schema import (
24
+ BadRequestResponse,
25
+ InternalServerErrorResponse,
26
+ NotFoundResponse,
27
+ UnauthenticatedResponse,
28
+ ValidationErrorResponse,
29
+ )
30
+ from agno.os.settings import AgnoAPISettings
31
+ from agno.os.utils import (
32
+ format_sse_event,
33
+ get_agent_by_id,
34
+ get_request_kwargs,
35
+ process_audio,
36
+ process_document,
37
+ process_image,
38
+ process_video,
39
+ )
40
+ from agno.run.agent import RunErrorEvent, RunOutput
41
+ from agno.utils.log import log_debug, log_error, log_warning
42
+
43
+ if TYPE_CHECKING:
44
+ from agno.os.app import AgentOS
45
+
46
+
47
+ async def agent_response_streamer(
48
+ agent: Agent,
49
+ message: str,
50
+ session_id: Optional[str] = None,
51
+ user_id: Optional[str] = None,
52
+ images: Optional[List[Image]] = None,
53
+ audio: Optional[List[Audio]] = None,
54
+ videos: Optional[List[Video]] = None,
55
+ files: Optional[List[FileMedia]] = None,
56
+ background_tasks: Optional[BackgroundTasks] = None,
57
+ **kwargs: Any,
58
+ ) -> AsyncGenerator:
59
+ try:
60
+ # Pass background_tasks if provided
61
+ if background_tasks is not None:
62
+ kwargs["background_tasks"] = background_tasks
63
+
64
+ run_response = agent.arun(
65
+ input=message,
66
+ session_id=session_id,
67
+ user_id=user_id,
68
+ images=images,
69
+ audio=audio,
70
+ videos=videos,
71
+ files=files,
72
+ stream=True,
73
+ stream_events=True,
74
+ **kwargs,
75
+ )
76
+ async for run_response_chunk in run_response:
77
+ yield format_sse_event(run_response_chunk) # type: ignore
78
+ except (InputCheckError, OutputCheckError) as e:
79
+ error_response = RunErrorEvent(
80
+ content=str(e),
81
+ error_type=e.type,
82
+ error_id=e.error_id,
83
+ additional_data=e.additional_data,
84
+ )
85
+ yield format_sse_event(error_response)
86
+ except Exception as e:
87
+ import traceback
88
+
89
+ traceback.print_exc(limit=3)
90
+ error_response = RunErrorEvent(
91
+ content=str(e),
92
+ )
93
+ yield format_sse_event(error_response)
94
+
95
+
96
+ async def agent_continue_response_streamer(
97
+ agent: Agent,
98
+ run_id: Optional[str] = None,
99
+ updated_tools: Optional[List] = None,
100
+ session_id: Optional[str] = None,
101
+ user_id: Optional[str] = None,
102
+ background_tasks: Optional[BackgroundTasks] = None,
103
+ ) -> AsyncGenerator:
104
+ try:
105
+ continue_response = agent.acontinue_run(
106
+ run_id=run_id,
107
+ updated_tools=updated_tools,
108
+ session_id=session_id,
109
+ user_id=user_id,
110
+ stream=True,
111
+ stream_events=True,
112
+ background_tasks=background_tasks,
113
+ )
114
+ async for run_response_chunk in continue_response:
115
+ yield format_sse_event(run_response_chunk) # type: ignore
116
+ except (InputCheckError, OutputCheckError) as e:
117
+ error_response = RunErrorEvent(
118
+ content=str(e),
119
+ error_type=e.type,
120
+ error_id=e.error_id,
121
+ additional_data=e.additional_data,
122
+ )
123
+ yield format_sse_event(error_response)
124
+
125
+ except Exception as e:
126
+ import traceback
127
+
128
+ traceback.print_exc(limit=3)
129
+ error_response = RunErrorEvent(
130
+ content=str(e),
131
+ error_type=e.type if hasattr(e, "type") else None,
132
+ error_id=e.error_id if hasattr(e, "error_id") else None,
133
+ )
134
+ yield format_sse_event(error_response)
135
+ return
136
+
137
+
138
+ def get_agent_router(
139
+ os: "AgentOS",
140
+ settings: AgnoAPISettings = AgnoAPISettings(),
141
+ ) -> APIRouter:
142
+ """
143
+ Create the agent router with comprehensive OpenAPI documentation.
144
+ """
145
+ router = APIRouter(
146
+ dependencies=[Depends(get_authentication_dependency(settings))],
147
+ responses={
148
+ 400: {"description": "Bad Request", "model": BadRequestResponse},
149
+ 401: {"description": "Unauthorized", "model": UnauthenticatedResponse},
150
+ 404: {"description": "Not Found", "model": NotFoundResponse},
151
+ 422: {"description": "Validation Error", "model": ValidationErrorResponse},
152
+ 500: {"description": "Internal Server Error", "model": InternalServerErrorResponse},
153
+ },
154
+ )
155
+
156
+ @router.post(
157
+ "/agents/{agent_id}/runs",
158
+ tags=["Agents"],
159
+ operation_id="create_agent_run",
160
+ response_model_exclude_none=True,
161
+ summary="Create Agent Run",
162
+ description=(
163
+ "Execute an agent with a message and optional media files. Supports both streaming and non-streaming responses.\n\n"
164
+ "**Features:**\n"
165
+ "- Text message input with optional session management\n"
166
+ "- Multi-media support: images (PNG, JPEG, WebP), audio (WAV, MP3), video (MP4, WebM, etc.)\n"
167
+ "- Document processing: PDF, CSV, DOCX, TXT, JSON\n"
168
+ "- Real-time streaming responses with Server-Sent Events (SSE)\n"
169
+ "- User and session context preservation\n\n"
170
+ "**Streaming Response:**\n"
171
+ "When `stream=true`, returns SSE events with `event` and `data` fields."
172
+ ),
173
+ responses={
174
+ 200: {
175
+ "description": "Agent run executed successfully",
176
+ "content": {
177
+ "text/event-stream": {
178
+ "examples": {
179
+ "event_stream": {
180
+ "summary": "Example event stream response",
181
+ "value": 'event: RunStarted\ndata: {"content": "Hello!", "run_id": "123..."}\n\n',
182
+ }
183
+ }
184
+ },
185
+ },
186
+ },
187
+ 400: {"description": "Invalid request or unsupported file type", "model": BadRequestResponse},
188
+ 404: {"description": "Agent not found", "model": NotFoundResponse},
189
+ },
190
+ )
191
+ async def create_agent_run(
192
+ agent_id: str,
193
+ request: Request,
194
+ background_tasks: BackgroundTasks,
195
+ message: str = Form(...),
196
+ stream: bool = Form(False),
197
+ session_id: Optional[str] = Form(None),
198
+ user_id: Optional[str] = Form(None),
199
+ files: Optional[List[UploadFile]] = File(None),
200
+ ):
201
+ kwargs = await get_request_kwargs(request, create_agent_run)
202
+
203
+ if hasattr(request.state, "user_id"):
204
+ if user_id:
205
+ log_warning("User ID parameter passed in both request state and kwargs, using request state")
206
+ user_id = request.state.user_id
207
+ if hasattr(request.state, "session_id"):
208
+ if session_id:
209
+ log_warning("Session ID parameter passed in both request state and kwargs, using request state")
210
+ session_id = request.state.session_id
211
+ if hasattr(request.state, "session_state"):
212
+ session_state = request.state.session_state
213
+ if "session_state" in kwargs:
214
+ log_warning("Session state parameter passed in both request state and kwargs, using request state")
215
+ kwargs["session_state"] = session_state
216
+ if hasattr(request.state, "dependencies"):
217
+ dependencies = request.state.dependencies
218
+ if "dependencies" in kwargs:
219
+ log_warning("Dependencies parameter passed in both request state and kwargs, using request state")
220
+ kwargs["dependencies"] = dependencies
221
+ if hasattr(request.state, "metadata"):
222
+ metadata = request.state.metadata
223
+ if "metadata" in kwargs:
224
+ log_warning("Metadata parameter passed in both request state and kwargs, using request state")
225
+ kwargs["metadata"] = metadata
226
+
227
+ agent = get_agent_by_id(agent_id, os.agents)
228
+ if agent is None:
229
+ raise HTTPException(status_code=404, detail="Agent not found")
230
+
231
+ if session_id is None or session_id == "":
232
+ log_debug("Creating new session")
233
+ session_id = str(uuid4())
234
+
235
+ base64_images: List[Image] = []
236
+ base64_audios: List[Audio] = []
237
+ base64_videos: List[Video] = []
238
+ input_files: List[FileMedia] = []
239
+
240
+ if files:
241
+ for file in files:
242
+ if file.content_type in [
243
+ "image/png",
244
+ "image/jpeg",
245
+ "image/jpg",
246
+ "image/gif",
247
+ "image/webp",
248
+ "image/bmp",
249
+ "image/tiff",
250
+ "image/tif",
251
+ "image/avif",
252
+ ]:
253
+ try:
254
+ base64_image = process_image(file)
255
+ base64_images.append(base64_image)
256
+ except Exception as e:
257
+ log_error(f"Error processing image {file.filename}: {e}")
258
+ continue
259
+ elif file.content_type in [
260
+ "audio/wav",
261
+ "audio/wave",
262
+ "audio/mp3",
263
+ "audio/mpeg",
264
+ "audio/ogg",
265
+ "audio/mp4",
266
+ "audio/m4a",
267
+ "audio/aac",
268
+ "audio/flac",
269
+ ]:
270
+ try:
271
+ audio = process_audio(file)
272
+ base64_audios.append(audio)
273
+ except Exception as e:
274
+ log_error(f"Error processing audio {file.filename} with content type {file.content_type}: {e}")
275
+ continue
276
+ elif file.content_type in [
277
+ "video/x-flv",
278
+ "video/quicktime",
279
+ "video/mpeg",
280
+ "video/mpegs",
281
+ "video/mpgs",
282
+ "video/mpg",
283
+ "video/mpg",
284
+ "video/mp4",
285
+ "video/webm",
286
+ "video/wmv",
287
+ "video/3gpp",
288
+ ]:
289
+ try:
290
+ base64_video = process_video(file)
291
+ base64_videos.append(base64_video)
292
+ except Exception as e:
293
+ log_error(f"Error processing video {file.filename}: {e}")
294
+ continue
295
+ elif file.content_type in [
296
+ "application/pdf",
297
+ "application/json",
298
+ "application/x-javascript",
299
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
300
+ "text/javascript",
301
+ "application/x-python",
302
+ "text/x-python",
303
+ "text/plain",
304
+ "text/html",
305
+ "text/css",
306
+ "text/md",
307
+ "text/csv",
308
+ "text/xml",
309
+ "text/rtf",
310
+ ]:
311
+ # Process document files
312
+ try:
313
+ input_file = process_document(file)
314
+ if input_file is not None:
315
+ input_files.append(input_file)
316
+ except Exception as e:
317
+ log_error(f"Error processing file {file.filename}: {e}")
318
+ continue
319
+ else:
320
+ raise HTTPException(status_code=400, detail="Unsupported file type")
321
+
322
+ if stream:
323
+ return StreamingResponse(
324
+ agent_response_streamer(
325
+ agent,
326
+ message,
327
+ session_id=session_id,
328
+ user_id=user_id,
329
+ images=base64_images if base64_images else None,
330
+ audio=base64_audios if base64_audios else None,
331
+ videos=base64_videos if base64_videos else None,
332
+ files=input_files if input_files else None,
333
+ background_tasks=background_tasks,
334
+ **kwargs,
335
+ ),
336
+ media_type="text/event-stream",
337
+ )
338
+ else:
339
+ try:
340
+ run_response = cast(
341
+ RunOutput,
342
+ await agent.arun(
343
+ input=message,
344
+ session_id=session_id,
345
+ user_id=user_id,
346
+ images=base64_images if base64_images else None,
347
+ audio=base64_audios if base64_audios else None,
348
+ videos=base64_videos if base64_videos else None,
349
+ files=input_files if input_files else None,
350
+ stream=False,
351
+ background_tasks=background_tasks,
352
+ **kwargs,
353
+ ),
354
+ )
355
+ return run_response.to_dict()
356
+
357
+ except InputCheckError as e:
358
+ raise HTTPException(status_code=400, detail=str(e))
359
+
360
+ @router.post(
361
+ "/agents/{agent_id}/runs/{run_id}/cancel",
362
+ tags=["Agents"],
363
+ operation_id="cancel_agent_run",
364
+ response_model_exclude_none=True,
365
+ summary="Cancel Agent Run",
366
+ description=(
367
+ "Cancel a currently executing agent run. This will attempt to stop the agent's execution gracefully.\n\n"
368
+ "**Note:** Cancellation may not be immediate for all operations."
369
+ ),
370
+ responses={
371
+ 200: {},
372
+ 404: {"description": "Agent not found", "model": NotFoundResponse},
373
+ 500: {"description": "Failed to cancel run", "model": InternalServerErrorResponse},
374
+ },
375
+ )
376
+ async def cancel_agent_run(
377
+ agent_id: str,
378
+ run_id: str,
379
+ ):
380
+ agent = get_agent_by_id(agent_id, os.agents)
381
+ if agent is None:
382
+ raise HTTPException(status_code=404, detail="Agent not found")
383
+
384
+ if not agent.cancel_run(run_id=run_id):
385
+ raise HTTPException(status_code=500, detail="Failed to cancel run")
386
+
387
+ return JSONResponse(content={}, status_code=200)
388
+
389
+ @router.post(
390
+ "/agents/{agent_id}/runs/{run_id}/continue",
391
+ tags=["Agents"],
392
+ operation_id="continue_agent_run",
393
+ response_model_exclude_none=True,
394
+ summary="Continue Agent Run",
395
+ description=(
396
+ "Continue a paused or incomplete agent run with updated tool results.\n\n"
397
+ "**Use Cases:**\n"
398
+ "- Resume execution after tool approval/rejection\n"
399
+ "- Provide manual tool execution results\n\n"
400
+ "**Tools Parameter:**\n"
401
+ "JSON string containing array of tool execution objects with results."
402
+ ),
403
+ responses={
404
+ 200: {
405
+ "description": "Agent run continued successfully",
406
+ "content": {
407
+ "text/event-stream": {
408
+ "example": 'event: RunContent\ndata: {"created_at": 1757348314, "run_id": "123..."}\n\n'
409
+ },
410
+ },
411
+ },
412
+ 400: {"description": "Invalid JSON in tools field or invalid tool structure", "model": BadRequestResponse},
413
+ 404: {"description": "Agent not found", "model": NotFoundResponse},
414
+ },
415
+ )
416
+ async def continue_agent_run(
417
+ agent_id: str,
418
+ run_id: str,
419
+ request: Request,
420
+ background_tasks: BackgroundTasks,
421
+ tools: str = Form(...), # JSON string of tools
422
+ session_id: Optional[str] = Form(None),
423
+ user_id: Optional[str] = Form(None),
424
+ stream: bool = Form(True),
425
+ ):
426
+ if hasattr(request.state, "user_id"):
427
+ user_id = request.state.user_id
428
+ if hasattr(request.state, "session_id"):
429
+ session_id = request.state.session_id
430
+
431
+ # Parse the JSON string manually
432
+ try:
433
+ tools_data = json.loads(tools) if tools else None
434
+ except json.JSONDecodeError:
435
+ raise HTTPException(status_code=400, detail="Invalid JSON in tools field")
436
+
437
+ agent = get_agent_by_id(agent_id, os.agents)
438
+ if agent is None:
439
+ raise HTTPException(status_code=404, detail="Agent not found")
440
+
441
+ if session_id is None or session_id == "":
442
+ log_warning(
443
+ "Continuing run without session_id. This might lead to unexpected behavior if session context is important."
444
+ )
445
+
446
+ # Convert tools dict to ToolExecution objects if provided
447
+ updated_tools = None
448
+ if tools_data:
449
+ try:
450
+ from agno.models.response import ToolExecution
451
+
452
+ updated_tools = [ToolExecution.from_dict(tool) for tool in tools_data]
453
+ except Exception as e:
454
+ raise HTTPException(status_code=400, detail=f"Invalid structure or content for tools: {str(e)}")
455
+
456
+ if stream:
457
+ return StreamingResponse(
458
+ agent_continue_response_streamer(
459
+ agent,
460
+ run_id=run_id, # run_id from path
461
+ updated_tools=updated_tools,
462
+ session_id=session_id,
463
+ user_id=user_id,
464
+ background_tasks=background_tasks,
465
+ ),
466
+ media_type="text/event-stream",
467
+ )
468
+ else:
469
+ try:
470
+ run_response_obj = cast(
471
+ RunOutput,
472
+ await agent.acontinue_run(
473
+ run_id=run_id, # run_id from path
474
+ updated_tools=updated_tools,
475
+ session_id=session_id,
476
+ user_id=user_id,
477
+ stream=False,
478
+ background_tasks=background_tasks,
479
+ ),
480
+ )
481
+ return run_response_obj.to_dict()
482
+
483
+ except InputCheckError as e:
484
+ raise HTTPException(status_code=400, detail=str(e))
485
+
486
+ @router.get(
487
+ "/agents",
488
+ response_model=List[AgentResponse],
489
+ response_model_exclude_none=True,
490
+ tags=["Agents"],
491
+ operation_id="get_agents",
492
+ summary="List All Agents",
493
+ description=(
494
+ "Retrieve a comprehensive list of all agents configured in this OS instance.\n\n"
495
+ "**Returns:**\n"
496
+ "- Agent metadata (ID, name, description)\n"
497
+ "- Model configuration and capabilities\n"
498
+ "- Available tools and their configurations\n"
499
+ "- Session, knowledge, memory, and reasoning settings\n"
500
+ "- Only meaningful (non-default) configurations are included"
501
+ ),
502
+ responses={
503
+ 200: {
504
+ "description": "List of agents retrieved successfully",
505
+ "content": {
506
+ "application/json": {
507
+ "example": [
508
+ {
509
+ "id": "main-agent",
510
+ "name": "Main Agent",
511
+ "db_id": "c6bf0644-feb8-4930-a305-380dae5ad6aa",
512
+ "model": {"name": "OpenAIChat", "model": "gpt-4o", "provider": "OpenAI"},
513
+ "tools": None,
514
+ "sessions": {"session_table": "agno_sessions"},
515
+ "knowledge": {"knowledge_table": "main_knowledge"},
516
+ "system_message": {"markdown": True, "add_datetime_to_context": True},
517
+ }
518
+ ]
519
+ }
520
+ },
521
+ }
522
+ },
523
+ )
524
+ async def get_agents() -> List[AgentResponse]:
525
+ """Return the list of all Agents present in the contextual OS"""
526
+ if os.agents is None:
527
+ return []
528
+
529
+ agents = []
530
+ for agent in os.agents:
531
+ agent_response = await AgentResponse.from_agent(agent=agent)
532
+ agents.append(agent_response)
533
+
534
+ return agents
535
+
536
+ @router.get(
537
+ "/agents/{agent_id}",
538
+ response_model=AgentResponse,
539
+ response_model_exclude_none=True,
540
+ tags=["Agents"],
541
+ operation_id="get_agent",
542
+ summary="Get Agent Details",
543
+ description=(
544
+ "Retrieve detailed configuration and capabilities of a specific agent.\n\n"
545
+ "**Returns comprehensive agent information including:**\n"
546
+ "- Model configuration and provider details\n"
547
+ "- Complete tool inventory and configurations\n"
548
+ "- Session management settings\n"
549
+ "- Knowledge base and memory configurations\n"
550
+ "- Reasoning capabilities and settings\n"
551
+ "- System prompts and response formatting options"
552
+ ),
553
+ responses={
554
+ 200: {
555
+ "description": "Agent details retrieved successfully",
556
+ "content": {
557
+ "application/json": {
558
+ "example": {
559
+ "id": "main-agent",
560
+ "name": "Main Agent",
561
+ "db_id": "9e064c70-6821-4840-a333-ce6230908a70",
562
+ "model": {"name": "OpenAIChat", "model": "gpt-4o", "provider": "OpenAI"},
563
+ "tools": None,
564
+ "sessions": {"session_table": "agno_sessions"},
565
+ "knowledge": {"knowledge_table": "main_knowledge"},
566
+ "system_message": {"markdown": True, "add_datetime_to_context": True},
567
+ }
568
+ }
569
+ },
570
+ },
571
+ 404: {"description": "Agent not found", "model": NotFoundResponse},
572
+ },
573
+ )
574
+ async def get_agent(agent_id: str) -> AgentResponse:
575
+ agent = get_agent_by_id(agent_id, os.agents)
576
+ if agent is None:
577
+ raise HTTPException(status_code=404, detail="Agent not found")
578
+
579
+ return await AgentResponse.from_agent(agent)
580
+
581
+ return router