agno 2.3.16__py3-none-any.whl → 2.3.18__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 +2 -0
- agno/agent/agent.py +4 -53
- agno/agent/remote.py +351 -0
- agno/client/__init__.py +3 -0
- agno/client/os.py +2669 -0
- agno/db/base.py +20 -0
- agno/db/mongo/async_mongo.py +11 -0
- agno/db/mongo/mongo.py +10 -0
- agno/db/mysql/async_mysql.py +9 -0
- agno/db/mysql/mysql.py +9 -0
- agno/db/postgres/async_postgres.py +9 -0
- agno/db/postgres/postgres.py +9 -0
- agno/db/postgres/utils.py +3 -2
- agno/db/sqlite/async_sqlite.py +9 -0
- agno/db/sqlite/sqlite.py +11 -1
- agno/exceptions.py +23 -0
- agno/knowledge/chunking/semantic.py +123 -46
- agno/knowledge/reader/csv_reader.py +1 -1
- agno/knowledge/reader/field_labeled_csv_reader.py +1 -1
- agno/knowledge/reader/json_reader.py +1 -1
- agno/models/google/gemini.py +5 -0
- agno/os/app.py +108 -25
- agno/os/auth.py +25 -1
- agno/os/interfaces/a2a/a2a.py +7 -6
- agno/os/interfaces/a2a/router.py +13 -13
- agno/os/interfaces/agui/agui.py +5 -3
- agno/os/interfaces/agui/router.py +23 -16
- agno/os/interfaces/base.py +7 -7
- agno/os/interfaces/slack/router.py +6 -6
- agno/os/interfaces/slack/slack.py +7 -7
- agno/os/interfaces/whatsapp/router.py +29 -6
- agno/os/interfaces/whatsapp/whatsapp.py +11 -8
- agno/os/managers.py +326 -0
- agno/os/mcp.py +651 -79
- agno/os/router.py +125 -18
- agno/os/routers/agents/router.py +65 -22
- agno/os/routers/agents/schema.py +16 -4
- agno/os/routers/database.py +5 -0
- agno/os/routers/evals/evals.py +93 -11
- agno/os/routers/evals/utils.py +6 -6
- agno/os/routers/knowledge/knowledge.py +104 -16
- agno/os/routers/memory/memory.py +124 -7
- agno/os/routers/metrics/metrics.py +21 -4
- agno/os/routers/session/session.py +141 -12
- agno/os/routers/teams/router.py +40 -14
- agno/os/routers/teams/schema.py +12 -4
- agno/os/routers/traces/traces.py +54 -4
- agno/os/routers/workflows/router.py +223 -117
- agno/os/routers/workflows/schema.py +65 -1
- agno/os/schema.py +38 -12
- agno/os/utils.py +87 -166
- agno/remote/__init__.py +3 -0
- agno/remote/base.py +484 -0
- agno/run/workflow.py +1 -0
- agno/team/__init__.py +2 -0
- agno/team/remote.py +287 -0
- agno/team/team.py +25 -54
- agno/tracing/exporter.py +10 -6
- agno/tracing/setup.py +2 -1
- agno/utils/agent.py +58 -1
- agno/utils/http.py +68 -20
- agno/utils/os.py +0 -0
- agno/utils/remote.py +23 -0
- agno/vectordb/chroma/chromadb.py +452 -16
- agno/vectordb/pgvector/pgvector.py +7 -0
- agno/vectordb/redis/redisdb.py +1 -1
- agno/workflow/__init__.py +2 -0
- agno/workflow/agent.py +2 -2
- agno/workflow/remote.py +222 -0
- agno/workflow/types.py +0 -73
- agno/workflow/workflow.py +119 -68
- {agno-2.3.16.dist-info → agno-2.3.18.dist-info}/METADATA +1 -1
- {agno-2.3.16.dist-info → agno-2.3.18.dist-info}/RECORD +76 -66
- {agno-2.3.16.dist-info → agno-2.3.18.dist-info}/WHEEL +0 -0
- {agno-2.3.16.dist-info → agno-2.3.18.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.16.dist-info → agno-2.3.18.dist-info}/top_level.txt +0 -0
agno/os/router.py
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
212
|
+
unique_models = {}
|
|
213
|
+
|
|
214
|
+
# Collect models from local agents
|
|
187
215
|
if os.agents:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
agno/os/routers/agents/router.py
CHANGED
|
@@ -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=
|
|
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:
|
|
99
|
-
updated_tools:
|
|
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(
|
|
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
|
-
|
|
387
|
-
|
|
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 =
|
|
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
|
-
|
|
549
|
-
|
|
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
|
-
|
|
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
|
agno/os/routers/agents/schema.py
CHANGED
|
@@ -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=
|
|
285
|
+
input_schema=input_schema_dict,
|
|
274
286
|
)
|
agno/os/routers/database.py
CHANGED
|
@@ -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):
|