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.
- 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/os/app.py +104 -23
- 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.17.dist-info}/METADATA +1 -1
- {agno-2.3.16.dist-info → agno-2.3.17.dist-info}/RECORD +75 -65
- {agno-2.3.16.dist-info → agno-2.3.17.dist-info}/WHEEL +0 -0
- {agno-2.3.16.dist-info → agno-2.3.17.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.16.dist-info → agno-2.3.17.dist-info}/top_level.txt +0 -0
agno/os/routers/memory/memory.py
CHANGED
|
@@ -9,7 +9,7 @@ from fastapi.routing import APIRouter
|
|
|
9
9
|
from agno.db.base import AsyncBaseDb, BaseDb
|
|
10
10
|
from agno.db.schemas import UserMemory
|
|
11
11
|
from agno.models.utils import get_model
|
|
12
|
-
from agno.os.auth import get_authentication_dependency
|
|
12
|
+
from agno.os.auth import get_auth_token_from_request, get_authentication_dependency
|
|
13
13
|
from agno.os.routers.memory.schemas import (
|
|
14
14
|
DeleteMemoriesRequest,
|
|
15
15
|
OptimizeMemoriesRequest,
|
|
@@ -30,12 +30,13 @@ from agno.os.schema import (
|
|
|
30
30
|
)
|
|
31
31
|
from agno.os.settings import AgnoAPISettings
|
|
32
32
|
from agno.os.utils import get_db
|
|
33
|
+
from agno.remote.base import RemoteDb
|
|
33
34
|
|
|
34
35
|
logger = logging.getLogger(__name__)
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
def get_memory_router(
|
|
38
|
-
dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]], settings: AgnoAPISettings = AgnoAPISettings(), **kwargs
|
|
39
|
+
dbs: dict[str, list[Union[BaseDb, AsyncBaseDb, RemoteDb]]], settings: AgnoAPISettings = AgnoAPISettings(), **kwargs
|
|
39
40
|
) -> APIRouter:
|
|
40
41
|
"""Create memory router with comprehensive OpenAPI documentation for user memory management endpoints."""
|
|
41
42
|
router = APIRouter(
|
|
@@ -52,7 +53,7 @@ def get_memory_router(
|
|
|
52
53
|
return attach_routes(router=router, dbs=dbs)
|
|
53
54
|
|
|
54
55
|
|
|
55
|
-
def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]]) -> APIRouter:
|
|
56
|
+
def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBaseDb, RemoteDb]]]) -> APIRouter:
|
|
56
57
|
@router.post(
|
|
57
58
|
"/memories",
|
|
58
59
|
response_model=UserMemorySchema,
|
|
@@ -90,7 +91,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
90
91
|
db_id: Optional[str] = Query(default=None, description="Database ID to use for memory storage"),
|
|
91
92
|
table: Optional[str] = Query(default=None, description="Table to use for memory storage"),
|
|
92
93
|
) -> UserMemorySchema:
|
|
93
|
-
if hasattr(request.state, "user_id"):
|
|
94
|
+
if hasattr(request.state, "user_id") and request.state.user_id is not None:
|
|
94
95
|
user_id = request.state.user_id
|
|
95
96
|
payload.user_id = user_id
|
|
96
97
|
|
|
@@ -99,6 +100,18 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
99
100
|
|
|
100
101
|
db = await get_db(dbs, db_id, table)
|
|
101
102
|
|
|
103
|
+
if isinstance(db, RemoteDb):
|
|
104
|
+
auth_token = get_auth_token_from_request(request)
|
|
105
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
106
|
+
return await db.create_memory(
|
|
107
|
+
memory=payload.memory,
|
|
108
|
+
topics=payload.topics or [],
|
|
109
|
+
user_id=payload.user_id,
|
|
110
|
+
db_id=db_id,
|
|
111
|
+
table=table,
|
|
112
|
+
headers=headers,
|
|
113
|
+
)
|
|
114
|
+
|
|
102
115
|
if isinstance(db, AsyncBaseDb):
|
|
103
116
|
db = cast(AsyncBaseDb, db)
|
|
104
117
|
user_memory = await db.upsert_user_memory(
|
|
@@ -139,12 +152,24 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
139
152
|
},
|
|
140
153
|
)
|
|
141
154
|
async def delete_memory(
|
|
155
|
+
request: Request,
|
|
142
156
|
memory_id: str = Path(description="Memory ID to delete"),
|
|
143
157
|
user_id: Optional[str] = Query(default=None, description="User ID to delete memory for"),
|
|
144
158
|
db_id: Optional[str] = Query(default=None, description="Database ID to use for deletion"),
|
|
145
159
|
table: Optional[str] = Query(default=None, description="Table to use for deletion"),
|
|
146
160
|
) -> None:
|
|
147
161
|
db = await get_db(dbs, db_id, table)
|
|
162
|
+
|
|
163
|
+
if isinstance(db, RemoteDb):
|
|
164
|
+
auth_token = get_auth_token_from_request(request)
|
|
165
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
166
|
+
return await db.delete_memory(
|
|
167
|
+
memory_id=memory_id,
|
|
168
|
+
user_id=user_id,
|
|
169
|
+
db_id=db_id,
|
|
170
|
+
table=table,
|
|
171
|
+
headers=headers,
|
|
172
|
+
)
|
|
148
173
|
if isinstance(db, AsyncBaseDb):
|
|
149
174
|
db = cast(AsyncBaseDb, db)
|
|
150
175
|
await db.delete_user_memory(memory_id=memory_id, user_id=user_id)
|
|
@@ -167,11 +192,24 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
167
192
|
},
|
|
168
193
|
)
|
|
169
194
|
async def delete_memories(
|
|
195
|
+
http_request: Request,
|
|
170
196
|
request: DeleteMemoriesRequest,
|
|
171
197
|
db_id: Optional[str] = Query(default=None, description="Database ID to use for deletion"),
|
|
172
198
|
table: Optional[str] = Query(default=None, description="Table to use for deletion"),
|
|
173
199
|
) -> None:
|
|
174
200
|
db = await get_db(dbs, db_id, table)
|
|
201
|
+
|
|
202
|
+
if isinstance(db, RemoteDb):
|
|
203
|
+
auth_token = get_auth_token_from_request(http_request)
|
|
204
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
205
|
+
return await db.delete_memories(
|
|
206
|
+
memory_ids=request.memory_ids,
|
|
207
|
+
user_id=request.user_id,
|
|
208
|
+
db_id=db_id,
|
|
209
|
+
table=table,
|
|
210
|
+
headers=headers,
|
|
211
|
+
)
|
|
212
|
+
|
|
175
213
|
if isinstance(db, AsyncBaseDb):
|
|
176
214
|
db = cast(AsyncBaseDb, db)
|
|
177
215
|
await db.delete_user_memories(memory_ids=request.memory_ids, user_id=request.user_id)
|
|
@@ -227,9 +265,27 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
227
265
|
) -> PaginatedResponse[UserMemorySchema]:
|
|
228
266
|
db = await get_db(dbs, db_id, table)
|
|
229
267
|
|
|
230
|
-
if hasattr(request.state, "user_id"):
|
|
268
|
+
if hasattr(request.state, "user_id") and request.state.user_id is not None:
|
|
231
269
|
user_id = request.state.user_id
|
|
232
270
|
|
|
271
|
+
if isinstance(db, RemoteDb):
|
|
272
|
+
auth_token = get_auth_token_from_request(request)
|
|
273
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
274
|
+
return await db.get_memories(
|
|
275
|
+
user_id=user_id,
|
|
276
|
+
agent_id=agent_id,
|
|
277
|
+
team_id=team_id,
|
|
278
|
+
topics=topics,
|
|
279
|
+
search_content=search_content,
|
|
280
|
+
limit=limit,
|
|
281
|
+
page=page,
|
|
282
|
+
sort_by=sort_by,
|
|
283
|
+
sort_order=sort_order.value if sort_order else "desc",
|
|
284
|
+
db_id=db_id,
|
|
285
|
+
table=table,
|
|
286
|
+
headers=headers,
|
|
287
|
+
)
|
|
288
|
+
|
|
233
289
|
if isinstance(db, AsyncBaseDb):
|
|
234
290
|
db = cast(AsyncBaseDb, db)
|
|
235
291
|
user_memories, total_count = await db.get_user_memories(
|
|
@@ -305,9 +361,20 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
305
361
|
) -> UserMemorySchema:
|
|
306
362
|
db = await get_db(dbs, db_id, table)
|
|
307
363
|
|
|
308
|
-
if hasattr(request.state, "user_id"):
|
|
364
|
+
if hasattr(request.state, "user_id") and request.state.user_id is not None:
|
|
309
365
|
user_id = request.state.user_id
|
|
310
366
|
|
|
367
|
+
if isinstance(db, RemoteDb):
|
|
368
|
+
auth_token = get_auth_token_from_request(request)
|
|
369
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
370
|
+
return await db.get_memory(
|
|
371
|
+
memory_id=memory_id,
|
|
372
|
+
user_id=user_id,
|
|
373
|
+
db_id=db_id,
|
|
374
|
+
table=table,
|
|
375
|
+
headers=headers,
|
|
376
|
+
)
|
|
377
|
+
|
|
311
378
|
if isinstance(db, AsyncBaseDb):
|
|
312
379
|
db = cast(AsyncBaseDb, db)
|
|
313
380
|
user_memory = await db.get_user_memory(memory_id=memory_id, user_id=user_id, deserialize=False)
|
|
@@ -350,10 +417,21 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
350
417
|
},
|
|
351
418
|
)
|
|
352
419
|
async def get_topics(
|
|
420
|
+
request: Request,
|
|
353
421
|
db_id: Optional[str] = Query(default=None, description="Database ID to query topics from"),
|
|
354
422
|
table: Optional[str] = Query(default=None, description="Table to query topics from"),
|
|
355
423
|
) -> List[str]:
|
|
356
424
|
db = await get_db(dbs, db_id, table)
|
|
425
|
+
|
|
426
|
+
if isinstance(db, RemoteDb):
|
|
427
|
+
auth_token = get_auth_token_from_request(request)
|
|
428
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
429
|
+
return await db.get_memory_topics(
|
|
430
|
+
db_id=db_id,
|
|
431
|
+
table=table,
|
|
432
|
+
headers=headers,
|
|
433
|
+
)
|
|
434
|
+
|
|
357
435
|
if isinstance(db, AsyncBaseDb):
|
|
358
436
|
db = cast(AsyncBaseDb, db)
|
|
359
437
|
return await db.get_all_memory_topics()
|
|
@@ -400,7 +478,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
400
478
|
db_id: Optional[str] = Query(default=None, description="Database ID to use for update"),
|
|
401
479
|
table: Optional[str] = Query(default=None, description="Table to use for update"),
|
|
402
480
|
) -> UserMemorySchema:
|
|
403
|
-
if hasattr(request.state, "user_id"):
|
|
481
|
+
if hasattr(request.state, "user_id") and request.state.user_id is not None:
|
|
404
482
|
user_id = request.state.user_id
|
|
405
483
|
payload.user_id = user_id
|
|
406
484
|
|
|
@@ -409,6 +487,19 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
409
487
|
|
|
410
488
|
db = await get_db(dbs, db_id, table)
|
|
411
489
|
|
|
490
|
+
if isinstance(db, RemoteDb):
|
|
491
|
+
auth_token = get_auth_token_from_request(request)
|
|
492
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
493
|
+
return await db.update_memory(
|
|
494
|
+
memory_id=memory_id,
|
|
495
|
+
user_id=payload.user_id,
|
|
496
|
+
memory=payload.memory,
|
|
497
|
+
topics=payload.topics or [],
|
|
498
|
+
db_id=db_id,
|
|
499
|
+
table=table,
|
|
500
|
+
headers=headers,
|
|
501
|
+
)
|
|
502
|
+
|
|
412
503
|
if isinstance(db, AsyncBaseDb):
|
|
413
504
|
db = cast(AsyncBaseDb, db)
|
|
414
505
|
user_memory = await db.upsert_user_memory(
|
|
@@ -466,12 +557,25 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
466
557
|
},
|
|
467
558
|
)
|
|
468
559
|
async def get_user_memory_stats(
|
|
560
|
+
request: Request,
|
|
469
561
|
limit: Optional[int] = Query(default=20, description="Number of user statistics to return per page"),
|
|
470
562
|
page: Optional[int] = Query(default=1, description="Page number for pagination"),
|
|
471
563
|
db_id: Optional[str] = Query(default=None, description="Database ID to query statistics from"),
|
|
472
564
|
table: Optional[str] = Query(default=None, description="Table to query statistics from"),
|
|
473
565
|
) -> PaginatedResponse[UserStatsSchema]:
|
|
474
566
|
db = await get_db(dbs, db_id, table)
|
|
567
|
+
|
|
568
|
+
if isinstance(db, RemoteDb):
|
|
569
|
+
auth_token = get_auth_token_from_request(request)
|
|
570
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
571
|
+
return await db.get_user_memory_stats(
|
|
572
|
+
limit=limit,
|
|
573
|
+
page=page,
|
|
574
|
+
db_id=db_id,
|
|
575
|
+
table=table,
|
|
576
|
+
headers=headers,
|
|
577
|
+
)
|
|
578
|
+
|
|
475
579
|
try:
|
|
476
580
|
# Ensure limit and page are integers
|
|
477
581
|
limit = int(limit) if limit is not None else 20
|
|
@@ -549,6 +653,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
549
653
|
},
|
|
550
654
|
)
|
|
551
655
|
async def optimize_memories(
|
|
656
|
+
http_request: Request,
|
|
552
657
|
request: OptimizeMemoriesRequest,
|
|
553
658
|
db_id: Optional[str] = Query(default=None, description="Database ID to use for optimization"),
|
|
554
659
|
table: Optional[str] = Query(default=None, description="Table to use for optimization"),
|
|
@@ -561,6 +666,18 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
561
666
|
# Get database instance
|
|
562
667
|
db = await get_db(dbs, db_id, table)
|
|
563
668
|
|
|
669
|
+
if isinstance(db, RemoteDb):
|
|
670
|
+
auth_token = get_auth_token_from_request(http_request)
|
|
671
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
672
|
+
return await db.optimize_memories(
|
|
673
|
+
user_id=request.user_id,
|
|
674
|
+
model=request.model,
|
|
675
|
+
apply=request.apply,
|
|
676
|
+
db_id=db_id,
|
|
677
|
+
table=table,
|
|
678
|
+
headers=headers,
|
|
679
|
+
)
|
|
680
|
+
|
|
564
681
|
# Create memory manager with optional model
|
|
565
682
|
if request.model:
|
|
566
683
|
try:
|
|
@@ -2,11 +2,11 @@ import logging
|
|
|
2
2
|
from datetime import date, datetime, timezone
|
|
3
3
|
from typing import List, Optional, Union, cast
|
|
4
4
|
|
|
5
|
-
from fastapi import Depends, HTTPException, Query
|
|
5
|
+
from fastapi import Depends, HTTPException, Query, Request
|
|
6
6
|
from fastapi.routing import APIRouter
|
|
7
7
|
|
|
8
8
|
from agno.db.base import AsyncBaseDb, BaseDb
|
|
9
|
-
from agno.os.auth import get_authentication_dependency
|
|
9
|
+
from agno.os.auth import get_auth_token_from_request, get_authentication_dependency
|
|
10
10
|
from agno.os.routers.metrics.schemas import DayAggregatedMetrics, MetricsResponse
|
|
11
11
|
from agno.os.schema import (
|
|
12
12
|
BadRequestResponse,
|
|
@@ -17,12 +17,13 @@ from agno.os.schema import (
|
|
|
17
17
|
)
|
|
18
18
|
from agno.os.settings import AgnoAPISettings
|
|
19
19
|
from agno.os.utils import get_db
|
|
20
|
+
from agno.remote.base import RemoteDb
|
|
20
21
|
|
|
21
22
|
logger = logging.getLogger(__name__)
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
def get_metrics_router(
|
|
25
|
-
dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]], settings: AgnoAPISettings = AgnoAPISettings(), **kwargs
|
|
26
|
+
dbs: dict[str, list[Union[BaseDb, AsyncBaseDb, RemoteDb]]], settings: AgnoAPISettings = AgnoAPISettings(), **kwargs
|
|
26
27
|
) -> APIRouter:
|
|
27
28
|
"""Create metrics router with comprehensive OpenAPI documentation for system metrics and analytics endpoints."""
|
|
28
29
|
router = APIRouter(
|
|
@@ -39,7 +40,7 @@ def get_metrics_router(
|
|
|
39
40
|
return attach_routes(router=router, dbs=dbs)
|
|
40
41
|
|
|
41
42
|
|
|
42
|
-
def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]]) -> APIRouter:
|
|
43
|
+
def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBaseDb, RemoteDb]]]) -> APIRouter:
|
|
43
44
|
@router.get(
|
|
44
45
|
"/metrics",
|
|
45
46
|
response_model=MetricsResponse,
|
|
@@ -92,6 +93,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
92
93
|
},
|
|
93
94
|
)
|
|
94
95
|
async def get_metrics(
|
|
96
|
+
request: Request,
|
|
95
97
|
starting_date: Optional[date] = Query(
|
|
96
98
|
default=None, description="Starting date for metrics range (YYYY-MM-DD format)"
|
|
97
99
|
),
|
|
@@ -103,6 +105,14 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
103
105
|
) -> MetricsResponse:
|
|
104
106
|
try:
|
|
105
107
|
db = await get_db(dbs, db_id, table)
|
|
108
|
+
|
|
109
|
+
if isinstance(db, RemoteDb):
|
|
110
|
+
auth_token = get_auth_token_from_request(request)
|
|
111
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
112
|
+
return await db.get_metrics(
|
|
113
|
+
starting_date=starting_date, ending_date=ending_date, db_id=db_id, table=table, headers=headers
|
|
114
|
+
)
|
|
115
|
+
|
|
106
116
|
if isinstance(db, AsyncBaseDb):
|
|
107
117
|
db = cast(AsyncBaseDb, db)
|
|
108
118
|
metrics, latest_updated_at = await db.get_metrics(starting_date=starting_date, ending_date=ending_date)
|
|
@@ -169,11 +179,18 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
169
179
|
},
|
|
170
180
|
)
|
|
171
181
|
async def calculate_metrics(
|
|
182
|
+
request: Request,
|
|
172
183
|
db_id: Optional[str] = Query(default=None, description="Database ID to use for metrics calculation"),
|
|
173
184
|
table: Optional[str] = Query(default=None, description="Table to use for metrics calculation"),
|
|
174
185
|
) -> List[DayAggregatedMetrics]:
|
|
175
186
|
try:
|
|
176
187
|
db = await get_db(dbs, db_id, table)
|
|
188
|
+
|
|
189
|
+
if isinstance(db, RemoteDb):
|
|
190
|
+
auth_token = get_auth_token_from_request(request)
|
|
191
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
192
|
+
return await db.refresh_metrics(db_id=db_id, table=table, headers=headers)
|
|
193
|
+
|
|
177
194
|
if isinstance(db, AsyncBaseDb):
|
|
178
195
|
db = cast(AsyncBaseDb, db)
|
|
179
196
|
result = await db.calculate_metrics()
|
|
@@ -6,7 +6,7 @@ from uuid import uuid4
|
|
|
6
6
|
from fastapi import APIRouter, Body, Depends, HTTPException, Path, Query, Request
|
|
7
7
|
|
|
8
8
|
from agno.db.base import AsyncBaseDb, BaseDb, SessionType
|
|
9
|
-
from agno.os.auth import get_authentication_dependency
|
|
9
|
+
from agno.os.auth import get_auth_token_from_request, get_authentication_dependency
|
|
10
10
|
from agno.os.schema import (
|
|
11
11
|
AgentSessionDetailSchema,
|
|
12
12
|
BadRequestResponse,
|
|
@@ -29,13 +29,14 @@ from agno.os.schema import (
|
|
|
29
29
|
)
|
|
30
30
|
from agno.os.settings import AgnoAPISettings
|
|
31
31
|
from agno.os.utils import get_db
|
|
32
|
+
from agno.remote.base import RemoteDb
|
|
32
33
|
from agno.session import AgentSession, TeamSession, WorkflowSession
|
|
33
34
|
|
|
34
35
|
logger = logging.getLogger(__name__)
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
def get_session_router(
|
|
38
|
-
dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]], settings: AgnoAPISettings = AgnoAPISettings()
|
|
39
|
+
dbs: dict[str, list[Union[BaseDb, AsyncBaseDb, RemoteDb]]], settings: AgnoAPISettings = AgnoAPISettings()
|
|
39
40
|
) -> APIRouter:
|
|
40
41
|
"""Create session router with comprehensive OpenAPI documentation for session management endpoints."""
|
|
41
42
|
session_router = APIRouter(
|
|
@@ -52,7 +53,7 @@ def get_session_router(
|
|
|
52
53
|
return attach_routes(router=session_router, dbs=dbs)
|
|
53
54
|
|
|
54
55
|
|
|
55
|
-
def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]]) -> APIRouter:
|
|
56
|
+
def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBaseDb, RemoteDb]]]) -> APIRouter:
|
|
56
57
|
@router.get(
|
|
57
58
|
"/sessions",
|
|
58
59
|
response_model=PaginatedResponse[SessionSchema],
|
|
@@ -64,6 +65,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
64
65
|
"Supports filtering by session type (agent, team, workflow), component, user, and name. "
|
|
65
66
|
"Sessions represent conversation histories and execution contexts."
|
|
66
67
|
),
|
|
68
|
+
response_model_exclude_none=True,
|
|
67
69
|
responses={
|
|
68
70
|
200: {
|
|
69
71
|
"description": "Sessions retrieved successfully",
|
|
@@ -117,9 +119,26 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
117
119
|
except Exception as e:
|
|
118
120
|
raise HTTPException(status_code=404, detail=f"{e}")
|
|
119
121
|
|
|
120
|
-
if hasattr(request.state, "user_id"):
|
|
122
|
+
if hasattr(request.state, "user_id") and request.state.user_id is not None:
|
|
121
123
|
user_id = request.state.user_id
|
|
122
124
|
|
|
125
|
+
if isinstance(db, RemoteDb):
|
|
126
|
+
auth_token = get_auth_token_from_request(request)
|
|
127
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
128
|
+
return await db.get_sessions(
|
|
129
|
+
session_type=session_type,
|
|
130
|
+
component_id=component_id,
|
|
131
|
+
user_id=user_id,
|
|
132
|
+
session_name=session_name,
|
|
133
|
+
limit=limit,
|
|
134
|
+
page=page,
|
|
135
|
+
sort_by=sort_by,
|
|
136
|
+
sort_order=sort_order.value if sort_order else None,
|
|
137
|
+
db_id=db_id,
|
|
138
|
+
table=table,
|
|
139
|
+
headers=headers,
|
|
140
|
+
)
|
|
141
|
+
|
|
123
142
|
if isinstance(db, AsyncBaseDb):
|
|
124
143
|
db = cast(AsyncBaseDb, db)
|
|
125
144
|
sessions, total_count = await db.get_sessions(
|
|
@@ -168,6 +187,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
168
187
|
"before running any agent/team/workflow interactions. "
|
|
169
188
|
"The session can later be used by providing its session_id in run requests."
|
|
170
189
|
),
|
|
190
|
+
response_model_exclude_none=True,
|
|
171
191
|
responses={
|
|
172
192
|
201: {
|
|
173
193
|
"description": "Session created successfully",
|
|
@@ -211,9 +231,26 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
211
231
|
|
|
212
232
|
# Get user_id from request state if available (from auth middleware)
|
|
213
233
|
user_id = create_session_request.user_id
|
|
214
|
-
if hasattr(request.state, "user_id"):
|
|
234
|
+
if hasattr(request.state, "user_id") and request.state.user_id is not None:
|
|
215
235
|
user_id = request.state.user_id
|
|
216
236
|
|
|
237
|
+
if isinstance(db, RemoteDb):
|
|
238
|
+
auth_token = get_auth_token_from_request(request)
|
|
239
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
240
|
+
return await db.create_session(
|
|
241
|
+
session_type=session_type,
|
|
242
|
+
session_id=create_session_request.session_id,
|
|
243
|
+
session_name=create_session_request.session_name,
|
|
244
|
+
session_state=create_session_request.session_state,
|
|
245
|
+
metadata=create_session_request.metadata,
|
|
246
|
+
user_id=user_id,
|
|
247
|
+
agent_id=create_session_request.agent_id,
|
|
248
|
+
team_id=create_session_request.team_id,
|
|
249
|
+
workflow_id=create_session_request.workflow_id,
|
|
250
|
+
db_id=db_id,
|
|
251
|
+
headers=headers,
|
|
252
|
+
)
|
|
253
|
+
|
|
217
254
|
# Generate session_id if not provided
|
|
218
255
|
session_id = create_session_request.session_id or str(uuid4())
|
|
219
256
|
|
|
@@ -293,6 +330,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
293
330
|
"Retrieve detailed information about a specific session including metadata, configuration, "
|
|
294
331
|
"and run history. Response schema varies based on session type (agent, team, or workflow)."
|
|
295
332
|
),
|
|
333
|
+
response_model_exclude_none=True,
|
|
296
334
|
responses={
|
|
297
335
|
200: {
|
|
298
336
|
"description": "Session details retrieved successfully",
|
|
@@ -382,9 +420,21 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
382
420
|
) -> Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema]:
|
|
383
421
|
db = await get_db(dbs, db_id, table)
|
|
384
422
|
|
|
385
|
-
if hasattr(request.state, "user_id"):
|
|
423
|
+
if hasattr(request.state, "user_id") and request.state.user_id is not None:
|
|
386
424
|
user_id = request.state.user_id
|
|
387
425
|
|
|
426
|
+
if isinstance(db, RemoteDb):
|
|
427
|
+
auth_token = get_auth_token_from_request(request)
|
|
428
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
429
|
+
return await db.get_session(
|
|
430
|
+
session_id=session_id,
|
|
431
|
+
session_type=session_type,
|
|
432
|
+
user_id=user_id,
|
|
433
|
+
db_id=db_id,
|
|
434
|
+
table=table,
|
|
435
|
+
headers=headers,
|
|
436
|
+
)
|
|
437
|
+
|
|
388
438
|
if isinstance(db, AsyncBaseDb):
|
|
389
439
|
db = cast(AsyncBaseDb, db)
|
|
390
440
|
session = await db.get_session(session_id=session_id, session_type=session_type, user_id=user_id)
|
|
@@ -413,6 +463,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
413
463
|
"Runs represent individual interactions or executions within a session. "
|
|
414
464
|
"Response schema varies based on session type."
|
|
415
465
|
),
|
|
466
|
+
response_model_exclude_none=True,
|
|
416
467
|
responses={
|
|
417
468
|
200: {
|
|
418
469
|
"description": "Session runs retrieved successfully",
|
|
@@ -538,9 +589,23 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
538
589
|
) -> List[Union[RunSchema, TeamRunSchema, WorkflowRunSchema]]:
|
|
539
590
|
db = await get_db(dbs, db_id, table)
|
|
540
591
|
|
|
541
|
-
if hasattr(request.state, "user_id"):
|
|
592
|
+
if hasattr(request.state, "user_id") and request.state.user_id is not None:
|
|
542
593
|
user_id = request.state.user_id
|
|
543
594
|
|
|
595
|
+
if isinstance(db, RemoteDb):
|
|
596
|
+
auth_token = get_auth_token_from_request(request)
|
|
597
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
598
|
+
return await db.get_session_runs(
|
|
599
|
+
session_id=session_id,
|
|
600
|
+
session_type=session_type,
|
|
601
|
+
user_id=user_id,
|
|
602
|
+
created_after=created_after,
|
|
603
|
+
created_before=created_before,
|
|
604
|
+
db_id=db_id,
|
|
605
|
+
table=table,
|
|
606
|
+
headers=headers,
|
|
607
|
+
)
|
|
608
|
+
|
|
544
609
|
# Use timestamp filters directly (already in epoch format)
|
|
545
610
|
start_timestamp = created_after
|
|
546
611
|
end_timestamp = created_before
|
|
@@ -650,12 +715,26 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
650
715
|
),
|
|
651
716
|
user_id: Optional[str] = Query(default=None, description="User ID to query run from"),
|
|
652
717
|
db_id: Optional[str] = Query(default=None, description="Database ID to query run from"),
|
|
718
|
+
table: Optional[str] = Query(default=None, description="Table to query run from"),
|
|
653
719
|
) -> Union[RunSchema, TeamRunSchema, WorkflowRunSchema]:
|
|
654
720
|
db = await get_db(dbs, db_id)
|
|
655
721
|
|
|
656
|
-
if hasattr(request.state, "user_id"):
|
|
722
|
+
if hasattr(request.state, "user_id") and request.state.user_id is not None:
|
|
657
723
|
user_id = request.state.user_id
|
|
658
724
|
|
|
725
|
+
if isinstance(db, RemoteDb):
|
|
726
|
+
auth_token = get_auth_token_from_request(request)
|
|
727
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
728
|
+
return await db.get_session_run(
|
|
729
|
+
session_id=session_id,
|
|
730
|
+
run_id=run_id,
|
|
731
|
+
session_type=session_type,
|
|
732
|
+
user_id=user_id,
|
|
733
|
+
db_id=db_id,
|
|
734
|
+
table=table,
|
|
735
|
+
headers=headers,
|
|
736
|
+
)
|
|
737
|
+
|
|
659
738
|
if isinstance(db, AsyncBaseDb):
|
|
660
739
|
db = cast(AsyncBaseDb, db)
|
|
661
740
|
session = await db.get_session(
|
|
@@ -707,11 +786,19 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
707
786
|
},
|
|
708
787
|
)
|
|
709
788
|
async def delete_session(
|
|
789
|
+
request: Request,
|
|
710
790
|
session_id: str = Path(description="Session ID to delete"),
|
|
711
791
|
db_id: Optional[str] = Query(default=None, description="Database ID to use for deletion"),
|
|
712
792
|
table: Optional[str] = Query(default=None, description="Table to use for deletion"),
|
|
713
793
|
) -> None:
|
|
714
794
|
db = await get_db(dbs, db_id, table)
|
|
795
|
+
|
|
796
|
+
if isinstance(db, RemoteDb):
|
|
797
|
+
auth_token = get_auth_token_from_request(request)
|
|
798
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
799
|
+
await db.delete_session(session_id=session_id, db_id=db_id, table=table, headers=headers)
|
|
800
|
+
return
|
|
801
|
+
|
|
715
802
|
if isinstance(db, AsyncBaseDb):
|
|
716
803
|
db = cast(AsyncBaseDb, db)
|
|
717
804
|
await db.delete_session(session_id=session_id)
|
|
@@ -737,10 +824,8 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
737
824
|
},
|
|
738
825
|
)
|
|
739
826
|
async def delete_sessions(
|
|
827
|
+
http_request: Request,
|
|
740
828
|
request: DeleteSessionRequest,
|
|
741
|
-
session_type: SessionType = Query(
|
|
742
|
-
default=SessionType.AGENT, description="Default session type filter", alias="type"
|
|
743
|
-
),
|
|
744
829
|
db_id: Optional[str] = Query(default=None, description="Database ID to use for deletion"),
|
|
745
830
|
table: Optional[str] = Query(default=None, description="Table to use for deletion"),
|
|
746
831
|
) -> None:
|
|
@@ -748,6 +833,19 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
748
833
|
raise HTTPException(status_code=400, detail="Session IDs and session types must have the same length")
|
|
749
834
|
|
|
750
835
|
db = await get_db(dbs, db_id, table)
|
|
836
|
+
|
|
837
|
+
if isinstance(db, RemoteDb):
|
|
838
|
+
auth_token = get_auth_token_from_request(http_request)
|
|
839
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
840
|
+
await db.delete_sessions(
|
|
841
|
+
session_ids=request.session_ids,
|
|
842
|
+
session_types=request.session_types,
|
|
843
|
+
db_id=db_id,
|
|
844
|
+
table=table,
|
|
845
|
+
headers=headers,
|
|
846
|
+
)
|
|
847
|
+
return
|
|
848
|
+
|
|
751
849
|
if isinstance(db, AsyncBaseDb):
|
|
752
850
|
db = cast(AsyncBaseDb, db)
|
|
753
851
|
await db.delete_sessions(session_ids=request.session_ids)
|
|
@@ -843,6 +941,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
843
941
|
},
|
|
844
942
|
)
|
|
845
943
|
async def rename_session(
|
|
944
|
+
request: Request,
|
|
846
945
|
session_id: str = Path(description="Session ID to rename"),
|
|
847
946
|
session_type: SessionType = Query(
|
|
848
947
|
default=SessionType.AGENT, description="Session type (agent, team, or workflow)", alias="type"
|
|
@@ -852,6 +951,19 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
852
951
|
table: Optional[str] = Query(default=None, description="Table to use for rename operation"),
|
|
853
952
|
) -> Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema]:
|
|
854
953
|
db = await get_db(dbs, db_id, table)
|
|
954
|
+
|
|
955
|
+
if isinstance(db, RemoteDb):
|
|
956
|
+
auth_token = get_auth_token_from_request(request)
|
|
957
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
958
|
+
return await db.rename_session(
|
|
959
|
+
session_id=session_id,
|
|
960
|
+
session_name=session_name,
|
|
961
|
+
session_type=session_type,
|
|
962
|
+
db_id=db_id,
|
|
963
|
+
table=table,
|
|
964
|
+
headers=headers,
|
|
965
|
+
)
|
|
966
|
+
|
|
855
967
|
if isinstance(db, AsyncBaseDb):
|
|
856
968
|
db = cast(AsyncBaseDb, db)
|
|
857
969
|
session = await db.rename_session(
|
|
@@ -935,12 +1047,29 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
|
|
|
935
1047
|
update_data: UpdateSessionRequest = Body(description="Session update data"),
|
|
936
1048
|
user_id: Optional[str] = Query(default=None, description="User ID"),
|
|
937
1049
|
db_id: Optional[str] = Query(default=None, description="Database ID to use for update operation"),
|
|
1050
|
+
table: Optional[str] = Query(default=None, description="Table to use for update operation"),
|
|
938
1051
|
) -> Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema]:
|
|
939
1052
|
db = await get_db(dbs, db_id)
|
|
940
1053
|
|
|
941
|
-
if hasattr(request.state, "user_id"):
|
|
1054
|
+
if hasattr(request.state, "user_id") and request.state.user_id is not None:
|
|
942
1055
|
user_id = request.state.user_id
|
|
943
1056
|
|
|
1057
|
+
if isinstance(db, RemoteDb):
|
|
1058
|
+
auth_token = get_auth_token_from_request(request)
|
|
1059
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
|
|
1060
|
+
return await db.update_session(
|
|
1061
|
+
session_id=session_id,
|
|
1062
|
+
session_type=session_type,
|
|
1063
|
+
session_name=update_data.session_name,
|
|
1064
|
+
session_state=update_data.session_state,
|
|
1065
|
+
metadata=update_data.metadata,
|
|
1066
|
+
summary=update_data.summary,
|
|
1067
|
+
user_id=user_id,
|
|
1068
|
+
db_id=db_id,
|
|
1069
|
+
table=table,
|
|
1070
|
+
headers=headers,
|
|
1071
|
+
)
|
|
1072
|
+
|
|
944
1073
|
# Get the existing session
|
|
945
1074
|
if isinstance(db, AsyncBaseDb):
|
|
946
1075
|
db = cast(AsyncBaseDb, db)
|