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.
Files changed (76) 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/models/google/gemini.py +5 -0
  22. agno/os/app.py +108 -25
  23. agno/os/auth.py +25 -1
  24. agno/os/interfaces/a2a/a2a.py +7 -6
  25. agno/os/interfaces/a2a/router.py +13 -13
  26. agno/os/interfaces/agui/agui.py +5 -3
  27. agno/os/interfaces/agui/router.py +23 -16
  28. agno/os/interfaces/base.py +7 -7
  29. agno/os/interfaces/slack/router.py +6 -6
  30. agno/os/interfaces/slack/slack.py +7 -7
  31. agno/os/interfaces/whatsapp/router.py +29 -6
  32. agno/os/interfaces/whatsapp/whatsapp.py +11 -8
  33. agno/os/managers.py +326 -0
  34. agno/os/mcp.py +651 -79
  35. agno/os/router.py +125 -18
  36. agno/os/routers/agents/router.py +65 -22
  37. agno/os/routers/agents/schema.py +16 -4
  38. agno/os/routers/database.py +5 -0
  39. agno/os/routers/evals/evals.py +93 -11
  40. agno/os/routers/evals/utils.py +6 -6
  41. agno/os/routers/knowledge/knowledge.py +104 -16
  42. agno/os/routers/memory/memory.py +124 -7
  43. agno/os/routers/metrics/metrics.py +21 -4
  44. agno/os/routers/session/session.py +141 -12
  45. agno/os/routers/teams/router.py +40 -14
  46. agno/os/routers/teams/schema.py +12 -4
  47. agno/os/routers/traces/traces.py +54 -4
  48. agno/os/routers/workflows/router.py +223 -117
  49. agno/os/routers/workflows/schema.py +65 -1
  50. agno/os/schema.py +38 -12
  51. agno/os/utils.py +87 -166
  52. agno/remote/__init__.py +3 -0
  53. agno/remote/base.py +484 -0
  54. agno/run/workflow.py +1 -0
  55. agno/team/__init__.py +2 -0
  56. agno/team/remote.py +287 -0
  57. agno/team/team.py +25 -54
  58. agno/tracing/exporter.py +10 -6
  59. agno/tracing/setup.py +2 -1
  60. agno/utils/agent.py +58 -1
  61. agno/utils/http.py +68 -20
  62. agno/utils/os.py +0 -0
  63. agno/utils/remote.py +23 -0
  64. agno/vectordb/chroma/chromadb.py +452 -16
  65. agno/vectordb/pgvector/pgvector.py +7 -0
  66. agno/vectordb/redis/redisdb.py +1 -1
  67. agno/workflow/__init__.py +2 -0
  68. agno/workflow/agent.py +2 -2
  69. agno/workflow/remote.py +222 -0
  70. agno/workflow/types.py +0 -73
  71. agno/workflow/workflow.py +119 -68
  72. {agno-2.3.16.dist-info → agno-2.3.18.dist-info}/METADATA +1 -1
  73. {agno-2.3.16.dist-info → agno-2.3.18.dist-info}/RECORD +76 -66
  74. {agno-2.3.16.dist-info → agno-2.3.18.dist-info}/WHEEL +0 -0
  75. {agno-2.3.16.dist-info → agno-2.3.18.dist-info}/licenses/LICENSE +0 -0
  76. {agno-2.3.16.dist-info → agno-2.3.18.dist-info}/top_level.txt +0 -0
@@ -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)