agno 2.0.0rc2__py3-none-any.whl → 2.3.0__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 (331) hide show
  1. agno/agent/agent.py +6009 -2874
  2. agno/api/api.py +2 -0
  3. agno/api/os.py +1 -1
  4. agno/culture/__init__.py +3 -0
  5. agno/culture/manager.py +956 -0
  6. agno/db/async_postgres/__init__.py +3 -0
  7. agno/db/base.py +385 -6
  8. agno/db/dynamo/dynamo.py +388 -81
  9. agno/db/dynamo/schemas.py +47 -10
  10. agno/db/dynamo/utils.py +63 -4
  11. agno/db/firestore/firestore.py +435 -64
  12. agno/db/firestore/schemas.py +11 -0
  13. agno/db/firestore/utils.py +102 -4
  14. agno/db/gcs_json/gcs_json_db.py +384 -42
  15. agno/db/gcs_json/utils.py +60 -26
  16. agno/db/in_memory/in_memory_db.py +351 -66
  17. agno/db/in_memory/utils.py +60 -2
  18. agno/db/json/json_db.py +339 -48
  19. agno/db/json/utils.py +60 -26
  20. agno/db/migrations/manager.py +199 -0
  21. agno/db/migrations/v1_to_v2.py +510 -37
  22. agno/db/migrations/versions/__init__.py +0 -0
  23. agno/db/migrations/versions/v2_3_0.py +938 -0
  24. agno/db/mongo/__init__.py +15 -1
  25. agno/db/mongo/async_mongo.py +2036 -0
  26. agno/db/mongo/mongo.py +653 -76
  27. agno/db/mongo/schemas.py +13 -0
  28. agno/db/mongo/utils.py +80 -8
  29. agno/db/mysql/mysql.py +687 -25
  30. agno/db/mysql/schemas.py +61 -37
  31. agno/db/mysql/utils.py +60 -2
  32. agno/db/postgres/__init__.py +2 -1
  33. agno/db/postgres/async_postgres.py +2001 -0
  34. agno/db/postgres/postgres.py +676 -57
  35. agno/db/postgres/schemas.py +43 -18
  36. agno/db/postgres/utils.py +164 -2
  37. agno/db/redis/redis.py +344 -38
  38. agno/db/redis/schemas.py +18 -0
  39. agno/db/redis/utils.py +60 -2
  40. agno/db/schemas/__init__.py +2 -1
  41. agno/db/schemas/culture.py +120 -0
  42. agno/db/schemas/memory.py +13 -0
  43. agno/db/singlestore/schemas.py +26 -1
  44. agno/db/singlestore/singlestore.py +687 -53
  45. agno/db/singlestore/utils.py +60 -2
  46. agno/db/sqlite/__init__.py +2 -1
  47. agno/db/sqlite/async_sqlite.py +2371 -0
  48. agno/db/sqlite/schemas.py +24 -0
  49. agno/db/sqlite/sqlite.py +774 -85
  50. agno/db/sqlite/utils.py +168 -5
  51. agno/db/surrealdb/__init__.py +3 -0
  52. agno/db/surrealdb/metrics.py +292 -0
  53. agno/db/surrealdb/models.py +309 -0
  54. agno/db/surrealdb/queries.py +71 -0
  55. agno/db/surrealdb/surrealdb.py +1361 -0
  56. agno/db/surrealdb/utils.py +147 -0
  57. agno/db/utils.py +50 -22
  58. agno/eval/accuracy.py +50 -43
  59. agno/eval/performance.py +6 -3
  60. agno/eval/reliability.py +6 -3
  61. agno/eval/utils.py +33 -16
  62. agno/exceptions.py +68 -1
  63. agno/filters.py +354 -0
  64. agno/guardrails/__init__.py +6 -0
  65. agno/guardrails/base.py +19 -0
  66. agno/guardrails/openai.py +144 -0
  67. agno/guardrails/pii.py +94 -0
  68. agno/guardrails/prompt_injection.py +52 -0
  69. agno/integrations/discord/client.py +1 -0
  70. agno/knowledge/chunking/agentic.py +13 -10
  71. agno/knowledge/chunking/fixed.py +1 -1
  72. agno/knowledge/chunking/semantic.py +40 -8
  73. agno/knowledge/chunking/strategy.py +59 -15
  74. agno/knowledge/embedder/aws_bedrock.py +9 -4
  75. agno/knowledge/embedder/azure_openai.py +54 -0
  76. agno/knowledge/embedder/base.py +2 -0
  77. agno/knowledge/embedder/cohere.py +184 -5
  78. agno/knowledge/embedder/fastembed.py +1 -1
  79. agno/knowledge/embedder/google.py +79 -1
  80. agno/knowledge/embedder/huggingface.py +9 -4
  81. agno/knowledge/embedder/jina.py +63 -0
  82. agno/knowledge/embedder/mistral.py +78 -11
  83. agno/knowledge/embedder/nebius.py +1 -1
  84. agno/knowledge/embedder/ollama.py +13 -0
  85. agno/knowledge/embedder/openai.py +37 -65
  86. agno/knowledge/embedder/sentence_transformer.py +8 -4
  87. agno/knowledge/embedder/vllm.py +262 -0
  88. agno/knowledge/embedder/voyageai.py +69 -16
  89. agno/knowledge/knowledge.py +595 -187
  90. agno/knowledge/reader/base.py +9 -2
  91. agno/knowledge/reader/csv_reader.py +8 -10
  92. agno/knowledge/reader/docx_reader.py +5 -6
  93. agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
  94. agno/knowledge/reader/json_reader.py +6 -5
  95. agno/knowledge/reader/markdown_reader.py +13 -13
  96. agno/knowledge/reader/pdf_reader.py +43 -68
  97. agno/knowledge/reader/pptx_reader.py +101 -0
  98. agno/knowledge/reader/reader_factory.py +51 -6
  99. agno/knowledge/reader/s3_reader.py +3 -15
  100. agno/knowledge/reader/tavily_reader.py +194 -0
  101. agno/knowledge/reader/text_reader.py +13 -13
  102. agno/knowledge/reader/web_search_reader.py +2 -43
  103. agno/knowledge/reader/website_reader.py +43 -25
  104. agno/knowledge/reranker/__init__.py +3 -0
  105. agno/knowledge/types.py +9 -0
  106. agno/knowledge/utils.py +20 -0
  107. agno/media.py +339 -266
  108. agno/memory/manager.py +336 -82
  109. agno/models/aimlapi/aimlapi.py +2 -2
  110. agno/models/anthropic/claude.py +183 -37
  111. agno/models/aws/bedrock.py +52 -112
  112. agno/models/aws/claude.py +33 -1
  113. agno/models/azure/ai_foundry.py +33 -15
  114. agno/models/azure/openai_chat.py +25 -8
  115. agno/models/base.py +1011 -566
  116. agno/models/cerebras/cerebras.py +19 -13
  117. agno/models/cerebras/cerebras_openai.py +8 -5
  118. agno/models/cohere/chat.py +27 -1
  119. agno/models/cometapi/__init__.py +5 -0
  120. agno/models/cometapi/cometapi.py +57 -0
  121. agno/models/dashscope/dashscope.py +1 -0
  122. agno/models/deepinfra/deepinfra.py +2 -2
  123. agno/models/deepseek/deepseek.py +2 -2
  124. agno/models/fireworks/fireworks.py +2 -2
  125. agno/models/google/gemini.py +110 -37
  126. agno/models/groq/groq.py +28 -11
  127. agno/models/huggingface/huggingface.py +2 -1
  128. agno/models/internlm/internlm.py +2 -2
  129. agno/models/langdb/langdb.py +4 -4
  130. agno/models/litellm/chat.py +18 -1
  131. agno/models/litellm/litellm_openai.py +2 -2
  132. agno/models/llama_cpp/__init__.py +5 -0
  133. agno/models/llama_cpp/llama_cpp.py +22 -0
  134. agno/models/message.py +143 -4
  135. agno/models/meta/llama.py +27 -10
  136. agno/models/meta/llama_openai.py +5 -17
  137. agno/models/nebius/nebius.py +6 -6
  138. agno/models/nexus/__init__.py +3 -0
  139. agno/models/nexus/nexus.py +22 -0
  140. agno/models/nvidia/nvidia.py +2 -2
  141. agno/models/ollama/chat.py +60 -6
  142. agno/models/openai/chat.py +102 -43
  143. agno/models/openai/responses.py +103 -106
  144. agno/models/openrouter/openrouter.py +41 -3
  145. agno/models/perplexity/perplexity.py +4 -5
  146. agno/models/portkey/portkey.py +3 -3
  147. agno/models/requesty/__init__.py +5 -0
  148. agno/models/requesty/requesty.py +52 -0
  149. agno/models/response.py +81 -5
  150. agno/models/sambanova/sambanova.py +2 -2
  151. agno/models/siliconflow/__init__.py +5 -0
  152. agno/models/siliconflow/siliconflow.py +25 -0
  153. agno/models/together/together.py +2 -2
  154. agno/models/utils.py +254 -8
  155. agno/models/vercel/v0.py +2 -2
  156. agno/models/vertexai/__init__.py +0 -0
  157. agno/models/vertexai/claude.py +96 -0
  158. agno/models/vllm/vllm.py +1 -0
  159. agno/models/xai/xai.py +3 -2
  160. agno/os/app.py +543 -175
  161. agno/os/auth.py +24 -14
  162. agno/os/config.py +1 -0
  163. agno/os/interfaces/__init__.py +1 -0
  164. agno/os/interfaces/a2a/__init__.py +3 -0
  165. agno/os/interfaces/a2a/a2a.py +42 -0
  166. agno/os/interfaces/a2a/router.py +250 -0
  167. agno/os/interfaces/a2a/utils.py +924 -0
  168. agno/os/interfaces/agui/agui.py +23 -7
  169. agno/os/interfaces/agui/router.py +27 -3
  170. agno/os/interfaces/agui/utils.py +242 -142
  171. agno/os/interfaces/base.py +6 -2
  172. agno/os/interfaces/slack/router.py +81 -23
  173. agno/os/interfaces/slack/slack.py +29 -14
  174. agno/os/interfaces/whatsapp/router.py +11 -4
  175. agno/os/interfaces/whatsapp/whatsapp.py +14 -7
  176. agno/os/mcp.py +111 -54
  177. agno/os/middleware/__init__.py +7 -0
  178. agno/os/middleware/jwt.py +233 -0
  179. agno/os/router.py +556 -139
  180. agno/os/routers/evals/evals.py +71 -34
  181. agno/os/routers/evals/schemas.py +31 -31
  182. agno/os/routers/evals/utils.py +6 -5
  183. agno/os/routers/health.py +31 -0
  184. agno/os/routers/home.py +52 -0
  185. agno/os/routers/knowledge/knowledge.py +185 -38
  186. agno/os/routers/knowledge/schemas.py +82 -22
  187. agno/os/routers/memory/memory.py +158 -53
  188. agno/os/routers/memory/schemas.py +20 -16
  189. agno/os/routers/metrics/metrics.py +20 -8
  190. agno/os/routers/metrics/schemas.py +16 -16
  191. agno/os/routers/session/session.py +499 -38
  192. agno/os/schema.py +308 -198
  193. agno/os/utils.py +401 -41
  194. agno/reasoning/anthropic.py +80 -0
  195. agno/reasoning/azure_ai_foundry.py +2 -2
  196. agno/reasoning/deepseek.py +2 -2
  197. agno/reasoning/default.py +3 -1
  198. agno/reasoning/gemini.py +73 -0
  199. agno/reasoning/groq.py +2 -2
  200. agno/reasoning/ollama.py +2 -2
  201. agno/reasoning/openai.py +7 -2
  202. agno/reasoning/vertexai.py +76 -0
  203. agno/run/__init__.py +6 -0
  204. agno/run/agent.py +266 -112
  205. agno/run/base.py +53 -24
  206. agno/run/team.py +252 -111
  207. agno/run/workflow.py +156 -45
  208. agno/session/agent.py +105 -89
  209. agno/session/summary.py +65 -25
  210. agno/session/team.py +176 -96
  211. agno/session/workflow.py +406 -40
  212. agno/team/team.py +3854 -1692
  213. agno/tools/brightdata.py +3 -3
  214. agno/tools/cartesia.py +3 -5
  215. agno/tools/dalle.py +9 -8
  216. agno/tools/decorator.py +4 -2
  217. agno/tools/desi_vocal.py +2 -2
  218. agno/tools/duckduckgo.py +15 -11
  219. agno/tools/e2b.py +20 -13
  220. agno/tools/eleven_labs.py +26 -28
  221. agno/tools/exa.py +21 -16
  222. agno/tools/fal.py +4 -4
  223. agno/tools/file.py +153 -23
  224. agno/tools/file_generation.py +350 -0
  225. agno/tools/firecrawl.py +4 -4
  226. agno/tools/function.py +257 -37
  227. agno/tools/giphy.py +2 -2
  228. agno/tools/gmail.py +238 -14
  229. agno/tools/google_drive.py +270 -0
  230. agno/tools/googlecalendar.py +36 -8
  231. agno/tools/googlesheets.py +20 -5
  232. agno/tools/jira.py +20 -0
  233. agno/tools/knowledge.py +3 -3
  234. agno/tools/lumalab.py +3 -3
  235. agno/tools/mcp/__init__.py +10 -0
  236. agno/tools/mcp/mcp.py +331 -0
  237. agno/tools/mcp/multi_mcp.py +347 -0
  238. agno/tools/mcp/params.py +24 -0
  239. agno/tools/mcp_toolbox.py +284 -0
  240. agno/tools/mem0.py +11 -17
  241. agno/tools/memori.py +1 -53
  242. agno/tools/memory.py +419 -0
  243. agno/tools/models/azure_openai.py +2 -2
  244. agno/tools/models/gemini.py +3 -3
  245. agno/tools/models/groq.py +3 -5
  246. agno/tools/models/nebius.py +7 -7
  247. agno/tools/models_labs.py +25 -15
  248. agno/tools/notion.py +204 -0
  249. agno/tools/openai.py +4 -9
  250. agno/tools/opencv.py +3 -3
  251. agno/tools/parallel.py +314 -0
  252. agno/tools/replicate.py +7 -7
  253. agno/tools/scrapegraph.py +58 -31
  254. agno/tools/searxng.py +2 -2
  255. agno/tools/serper.py +2 -2
  256. agno/tools/slack.py +18 -3
  257. agno/tools/spider.py +2 -2
  258. agno/tools/tavily.py +146 -0
  259. agno/tools/whatsapp.py +1 -1
  260. agno/tools/workflow.py +278 -0
  261. agno/tools/yfinance.py +12 -11
  262. agno/utils/agent.py +820 -0
  263. agno/utils/audio.py +27 -0
  264. agno/utils/common.py +90 -1
  265. agno/utils/events.py +222 -7
  266. agno/utils/gemini.py +181 -23
  267. agno/utils/hooks.py +57 -0
  268. agno/utils/http.py +111 -0
  269. agno/utils/knowledge.py +12 -5
  270. agno/utils/log.py +1 -0
  271. agno/utils/mcp.py +95 -5
  272. agno/utils/media.py +188 -10
  273. agno/utils/merge_dict.py +22 -1
  274. agno/utils/message.py +60 -0
  275. agno/utils/models/claude.py +40 -11
  276. agno/utils/models/cohere.py +1 -1
  277. agno/utils/models/watsonx.py +1 -1
  278. agno/utils/openai.py +1 -1
  279. agno/utils/print_response/agent.py +105 -21
  280. agno/utils/print_response/team.py +103 -38
  281. agno/utils/print_response/workflow.py +251 -34
  282. agno/utils/reasoning.py +22 -1
  283. agno/utils/serialize.py +32 -0
  284. agno/utils/streamlit.py +16 -10
  285. agno/utils/string.py +41 -0
  286. agno/utils/team.py +98 -9
  287. agno/utils/tools.py +1 -1
  288. agno/vectordb/base.py +23 -4
  289. agno/vectordb/cassandra/cassandra.py +65 -9
  290. agno/vectordb/chroma/chromadb.py +182 -38
  291. agno/vectordb/clickhouse/clickhousedb.py +64 -11
  292. agno/vectordb/couchbase/couchbase.py +105 -10
  293. agno/vectordb/lancedb/lance_db.py +183 -135
  294. agno/vectordb/langchaindb/langchaindb.py +25 -7
  295. agno/vectordb/lightrag/lightrag.py +17 -3
  296. agno/vectordb/llamaindex/__init__.py +3 -0
  297. agno/vectordb/llamaindex/llamaindexdb.py +46 -7
  298. agno/vectordb/milvus/milvus.py +126 -9
  299. agno/vectordb/mongodb/__init__.py +7 -1
  300. agno/vectordb/mongodb/mongodb.py +112 -7
  301. agno/vectordb/pgvector/pgvector.py +142 -21
  302. agno/vectordb/pineconedb/pineconedb.py +80 -8
  303. agno/vectordb/qdrant/qdrant.py +125 -39
  304. agno/vectordb/redis/__init__.py +9 -0
  305. agno/vectordb/redis/redisdb.py +694 -0
  306. agno/vectordb/singlestore/singlestore.py +111 -25
  307. agno/vectordb/surrealdb/surrealdb.py +31 -5
  308. agno/vectordb/upstashdb/upstashdb.py +76 -8
  309. agno/vectordb/weaviate/weaviate.py +86 -15
  310. agno/workflow/__init__.py +2 -0
  311. agno/workflow/agent.py +299 -0
  312. agno/workflow/condition.py +112 -18
  313. agno/workflow/loop.py +69 -10
  314. agno/workflow/parallel.py +266 -118
  315. agno/workflow/router.py +110 -17
  316. agno/workflow/step.py +645 -136
  317. agno/workflow/steps.py +65 -6
  318. agno/workflow/types.py +71 -33
  319. agno/workflow/workflow.py +2113 -300
  320. agno-2.3.0.dist-info/METADATA +618 -0
  321. agno-2.3.0.dist-info/RECORD +577 -0
  322. agno-2.3.0.dist-info/licenses/LICENSE +201 -0
  323. agno/knowledge/reader/url_reader.py +0 -128
  324. agno/tools/googlesearch.py +0 -98
  325. agno/tools/mcp.py +0 -610
  326. agno/utils/models/aws_claude.py +0 -170
  327. agno-2.0.0rc2.dist-info/METADATA +0 -355
  328. agno-2.0.0rc2.dist-info/RECORD +0 -515
  329. agno-2.0.0rc2.dist-info/licenses/LICENSE +0 -375
  330. {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/WHEEL +0 -0
  331. {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,16 @@
1
1
  import logging
2
- from typing import List, Optional, Union
2
+ import time
3
+ from typing import Any, List, Optional, Union, cast
4
+ from uuid import uuid4
3
5
 
4
- from fastapi import APIRouter, Body, Depends, HTTPException, Path, Query
6
+ from fastapi import APIRouter, Body, Depends, HTTPException, Path, Query, Request
5
7
 
6
- from agno.db.base import BaseDb, SessionType
8
+ from agno.db.base import AsyncBaseDb, BaseDb, SessionType
7
9
  from agno.os.auth import get_authentication_dependency
8
10
  from agno.os.schema import (
9
11
  AgentSessionDetailSchema,
10
12
  BadRequestResponse,
13
+ CreateSessionRequest,
11
14
  DeleteSessionRequest,
12
15
  InternalServerErrorResponse,
13
16
  NotFoundResponse,
@@ -19,17 +22,21 @@ from agno.os.schema import (
19
22
  TeamRunSchema,
20
23
  TeamSessionDetailSchema,
21
24
  UnauthenticatedResponse,
25
+ UpdateSessionRequest,
22
26
  ValidationErrorResponse,
23
27
  WorkflowRunSchema,
24
28
  WorkflowSessionDetailSchema,
25
29
  )
26
30
  from agno.os.settings import AgnoAPISettings
27
31
  from agno.os.utils import get_db
32
+ from agno.session import AgentSession, TeamSession, WorkflowSession
28
33
 
29
34
  logger = logging.getLogger(__name__)
30
35
 
31
36
 
32
- def get_session_router(dbs: dict[str, BaseDb], settings: AgnoAPISettings = AgnoAPISettings()) -> APIRouter:
37
+ def get_session_router(
38
+ dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]], settings: AgnoAPISettings = AgnoAPISettings()
39
+ ) -> APIRouter:
33
40
  """Create session router with comprehensive OpenAPI documentation for session management endpoints."""
34
41
  session_router = APIRouter(
35
42
  dependencies=[Depends(get_authentication_dependency(settings))],
@@ -45,7 +52,7 @@ def get_session_router(dbs: dict[str, BaseDb], settings: AgnoAPISettings = AgnoA
45
52
  return attach_routes(router=session_router, dbs=dbs)
46
53
 
47
54
 
48
- def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
55
+ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]]) -> APIRouter:
49
56
  @router.get(
50
57
  "/sessions",
51
58
  response_model=PaginatedResponse[SessionSchema],
@@ -82,10 +89,12 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
82
89
  },
83
90
  },
84
91
  400: {"description": "Invalid session type or filter parameters", "model": BadRequestResponse},
92
+ 404: {"description": "Not found", "model": NotFoundResponse},
85
93
  422: {"description": "Validation error in query parameters", "model": ValidationErrorResponse},
86
94
  },
87
95
  )
88
96
  async def get_sessions(
97
+ request: Request,
89
98
  session_type: SessionType = Query(
90
99
  default=SessionType.AGENT,
91
100
  alias="type",
@@ -101,19 +110,41 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
101
110
  sort_by: Optional[str] = Query(default="created_at", description="Field to sort sessions by"),
102
111
  sort_order: Optional[SortOrder] = Query(default="desc", description="Sort order (asc or desc)"),
103
112
  db_id: Optional[str] = Query(default=None, description="Database ID to query sessions from"),
113
+ table: Optional[str] = Query(default=None, description="The database table to use"),
104
114
  ) -> PaginatedResponse[SessionSchema]:
105
- db = get_db(dbs, db_id)
106
- sessions, total_count = db.get_sessions(
107
- session_type=session_type,
108
- component_id=component_id,
109
- user_id=user_id,
110
- session_name=session_name,
111
- limit=limit,
112
- page=page,
113
- sort_by=sort_by,
114
- sort_order=sort_order,
115
- deserialize=False,
116
- )
115
+ try:
116
+ db = await get_db(dbs, db_id, table)
117
+ except Exception as e:
118
+ raise HTTPException(status_code=404, detail=f"{e}")
119
+
120
+ if hasattr(request.state, "user_id"):
121
+ user_id = request.state.user_id
122
+
123
+ if isinstance(db, AsyncBaseDb):
124
+ db = cast(AsyncBaseDb, db)
125
+ sessions, total_count = await db.get_sessions(
126
+ session_type=session_type,
127
+ component_id=component_id,
128
+ user_id=user_id,
129
+ session_name=session_name,
130
+ limit=limit,
131
+ page=page,
132
+ sort_by=sort_by,
133
+ sort_order=sort_order,
134
+ deserialize=False,
135
+ )
136
+ else:
137
+ sessions, total_count = db.get_sessions( # type: ignore
138
+ session_type=session_type,
139
+ component_id=component_id,
140
+ user_id=user_id,
141
+ session_name=session_name,
142
+ limit=limit,
143
+ page=page,
144
+ sort_by=sort_by,
145
+ sort_order=sort_order,
146
+ deserialize=False,
147
+ )
117
148
 
118
149
  return PaginatedResponse(
119
150
  data=[SessionSchema.from_dict(session) for session in sessions], # type: ignore
@@ -125,6 +156,133 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
125
156
  ),
126
157
  )
127
158
 
159
+ @router.post(
160
+ "/sessions",
161
+ response_model=Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema],
162
+ status_code=201,
163
+ operation_id="create_session",
164
+ summary="Create New Session",
165
+ description=(
166
+ "Create a new empty session with optional configuration. "
167
+ "Useful for pre-creating sessions with specific session_state, metadata, or other properties "
168
+ "before running any agent/team/workflow interactions. "
169
+ "The session can later be used by providing its session_id in run requests."
170
+ ),
171
+ responses={
172
+ 201: {
173
+ "description": "Session created successfully",
174
+ "content": {
175
+ "application/json": {
176
+ "examples": {
177
+ "agent_session_example": {
178
+ "summary": "Example created agent session",
179
+ "value": {
180
+ "user_id": "user-123",
181
+ "agent_session_id": "new-session-id",
182
+ "session_id": "new-session-id",
183
+ "session_name": "New Session",
184
+ "session_state": {"key": "value"},
185
+ "metadata": {"key": "value"},
186
+ "agent_id": "agent-1",
187
+ "created_at": "2025-10-21T12:00:00Z",
188
+ "updated_at": "2025-10-21T12:00:00Z",
189
+ },
190
+ }
191
+ }
192
+ }
193
+ },
194
+ },
195
+ 400: {"description": "Invalid request parameters", "model": BadRequestResponse},
196
+ 422: {"description": "Validation error", "model": ValidationErrorResponse},
197
+ 500: {"description": "Failed to create session", "model": InternalServerErrorResponse},
198
+ },
199
+ )
200
+ async def create_session(
201
+ request: Request,
202
+ session_type: SessionType = Query(
203
+ default=SessionType.AGENT, alias="type", description="Type of session to create (agent, team, or workflow)"
204
+ ),
205
+ create_session_request: CreateSessionRequest = Body(
206
+ default=CreateSessionRequest(), description="Session configuration data"
207
+ ),
208
+ db_id: Optional[str] = Query(default=None, description="Database ID to create session in"),
209
+ ) -> Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema]:
210
+ db = await get_db(dbs, db_id)
211
+
212
+ # Get user_id from request state if available (from auth middleware)
213
+ user_id = create_session_request.user_id
214
+ if hasattr(request.state, "user_id"):
215
+ user_id = request.state.user_id
216
+
217
+ # Generate session_id if not provided
218
+ session_id = create_session_request.session_id or str(uuid4())
219
+
220
+ # Prepare session_data with session_state and session_name
221
+ session_data: dict[str, Any] = {}
222
+ if create_session_request.session_state is not None:
223
+ session_data["session_state"] = create_session_request.session_state
224
+ if create_session_request.session_name is not None:
225
+ session_data["session_name"] = create_session_request.session_name
226
+
227
+ current_time = int(time.time())
228
+
229
+ # Create the appropriate session type
230
+ session: Union[AgentSession, TeamSession, WorkflowSession]
231
+ if session_type == SessionType.AGENT:
232
+ session = AgentSession(
233
+ session_id=session_id,
234
+ agent_id=create_session_request.agent_id,
235
+ user_id=user_id,
236
+ session_data=session_data if session_data else None,
237
+ metadata=create_session_request.metadata,
238
+ created_at=current_time,
239
+ updated_at=current_time,
240
+ )
241
+ elif session_type == SessionType.TEAM:
242
+ session = TeamSession(
243
+ session_id=session_id,
244
+ team_id=create_session_request.team_id,
245
+ user_id=user_id,
246
+ session_data=session_data if session_data else None,
247
+ metadata=create_session_request.metadata,
248
+ created_at=current_time,
249
+ updated_at=current_time,
250
+ )
251
+ elif session_type == SessionType.WORKFLOW:
252
+ session = WorkflowSession(
253
+ session_id=session_id,
254
+ workflow_id=create_session_request.workflow_id,
255
+ user_id=user_id,
256
+ session_data=session_data if session_data else None,
257
+ metadata=create_session_request.metadata,
258
+ created_at=current_time,
259
+ updated_at=current_time,
260
+ )
261
+ else:
262
+ raise HTTPException(status_code=400, detail=f"Invalid session type: {session_type}")
263
+
264
+ # Upsert the session to the database
265
+ try:
266
+ if isinstance(db, AsyncBaseDb):
267
+ db = cast(AsyncBaseDb, db)
268
+ created_session = await db.upsert_session(session, deserialize=True)
269
+ else:
270
+ created_session = db.upsert_session(session, deserialize=True)
271
+
272
+ if not created_session:
273
+ raise HTTPException(status_code=500, detail="Failed to create session")
274
+
275
+ # Return appropriate schema based on session type
276
+ if session_type == SessionType.AGENT:
277
+ return AgentSessionDetailSchema.from_session(created_session) # type: ignore
278
+ elif session_type == SessionType.TEAM:
279
+ return TeamSessionDetailSchema.from_session(created_session) # type: ignore
280
+ else:
281
+ return WorkflowSessionDetailSchema.from_session(created_session) # type: ignore
282
+ except Exception as e:
283
+ logger.error(f"Error creating session: {e}")
284
+ raise HTTPException(status_code=500, detail=f"Failed to create session: {str(e)}")
285
+
128
286
  @router.get(
129
287
  "/sessions/{session_id}",
130
288
  response_model=Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema],
@@ -213,16 +371,29 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
213
371
  },
214
372
  )
215
373
  async def get_session_by_id(
374
+ request: Request,
216
375
  session_id: str = Path(description="Session ID to retrieve"),
217
376
  session_type: SessionType = Query(
218
377
  default=SessionType.AGENT, description="Session type (agent, team, or workflow)", alias="type"
219
378
  ),
379
+ user_id: Optional[str] = Query(default=None, description="User ID to query session from"),
220
380
  db_id: Optional[str] = Query(default=None, description="Database ID to query session from"),
381
+ table: Optional[str] = Query(default=None, description="Table to query session from"),
221
382
  ) -> Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema]:
222
- db = get_db(dbs, db_id)
223
- session = db.get_session(session_id=session_id, session_type=session_type)
383
+ db = await get_db(dbs, db_id, table)
384
+
385
+ if hasattr(request.state, "user_id"):
386
+ user_id = request.state.user_id
387
+
388
+ if isinstance(db, AsyncBaseDb):
389
+ db = cast(AsyncBaseDb, db)
390
+ session = await db.get_session(session_id=session_id, session_type=session_type, user_id=user_id)
391
+ else:
392
+ session = db.get_session(session_id=session_id, session_type=session_type, user_id=user_id)
224
393
  if not session:
225
- raise HTTPException(status_code=404, detail=f"Session with id '{session_id}' not found")
394
+ raise HTTPException(
395
+ status_code=404, detail=f"{session_type.value.title()} Session with id '{session_id}' not found"
396
+ )
226
397
 
227
398
  if session_type == SessionType.AGENT:
228
399
  return AgentSessionDetailSchema.from_session(session) # type: ignore
@@ -233,13 +404,14 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
233
404
 
234
405
  @router.get(
235
406
  "/sessions/{session_id}/runs",
236
- response_model=Union[List[RunSchema], List[TeamRunSchema], List[WorkflowRunSchema]],
407
+ response_model=List[Union[RunSchema, TeamRunSchema, WorkflowRunSchema]],
237
408
  status_code=200,
238
409
  operation_id="get_session_runs",
239
410
  summary="Get Session Runs",
240
411
  description=(
241
- "Retrieve all runs (executions) for a specific session. Runs represent individual "
242
- "interactions or executions within a session. Response schema varies based on session type."
412
+ "Retrieve all runs (executions) for a specific session with optional timestamp filtering. "
413
+ "Runs represent individual interactions or executions within a session. "
414
+ "Response schema varies based on session type."
243
415
  ),
244
416
  responses={
245
417
  200: {
@@ -251,7 +423,8 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
251
423
  "summary": "Example completed run",
252
424
  "value": {
253
425
  "run_id": "fcdf50f0-7c32-4593-b2ef-68a558774340",
254
- "agent_session_id": "80056af0-c7a5-4d69-b6a2-c3eba9f040e0",
426
+ "parent_run_id": "80056af0-c7a5-4d69-b6a2-c3eba9f040e0",
427
+ "agent_id": "basic-agent",
255
428
  "user_id": "",
256
429
  "run_input": "Which tools do you have access to?",
257
430
  "content": "I don't have access to external tools or the internet. However, I can assist you with a wide range of topics by providing information, answering questions, and offering suggestions based on the knowledge I've been trained on. If there's anything specific you need help with, feel free to ask!",
@@ -346,32 +519,178 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
346
519
  },
347
520
  )
348
521
  async def get_session_runs(
522
+ request: Request,
349
523
  session_id: str = Path(description="Session ID to get runs from"),
350
524
  session_type: SessionType = Query(
351
525
  default=SessionType.AGENT, description="Session type (agent, team, or workflow)", alias="type"
352
526
  ),
527
+ user_id: Optional[str] = Query(default=None, description="User ID to query runs from"),
528
+ created_after: Optional[int] = Query(
529
+ default=None,
530
+ description="Filter runs created after this Unix timestamp (epoch time in seconds)",
531
+ ),
532
+ created_before: Optional[int] = Query(
533
+ default=None,
534
+ description="Filter runs created before this Unix timestamp (epoch time in seconds)",
535
+ ),
353
536
  db_id: Optional[str] = Query(default=None, description="Database ID to query runs from"),
354
- ) -> Union[List[RunSchema], List[TeamRunSchema], List[WorkflowRunSchema]]:
355
- db = get_db(dbs, db_id)
356
- session = db.get_session(session_id=session_id, session_type=session_type, deserialize=False)
537
+ table: Optional[str] = Query(default=None, description="Table to query runs from"),
538
+ ) -> List[Union[RunSchema, TeamRunSchema, WorkflowRunSchema]]:
539
+ db = await get_db(dbs, db_id, table)
540
+
541
+ if hasattr(request.state, "user_id"):
542
+ user_id = request.state.user_id
543
+
544
+ # Use timestamp filters directly (already in epoch format)
545
+ start_timestamp = created_after
546
+ end_timestamp = created_before
547
+
548
+ if isinstance(db, AsyncBaseDb):
549
+ db = cast(AsyncBaseDb, db)
550
+ session = await db.get_session(
551
+ session_id=session_id, session_type=session_type, user_id=user_id, deserialize=False
552
+ )
553
+ else:
554
+ session = db.get_session(
555
+ session_id=session_id, session_type=session_type, user_id=user_id, deserialize=False
556
+ )
557
+
357
558
  if not session:
358
559
  raise HTTPException(status_code=404, detail=f"Session with ID {session_id} not found")
359
560
 
360
561
  runs = session.get("runs") # type: ignore
361
562
  if not runs:
362
- raise HTTPException(status_code=404, detail=f"Session with ID {session_id} has no runs")
563
+ return []
564
+
565
+ # Filter runs by timestamp if specified
566
+ # TODO: Move this filtering into the DB layer
567
+ filtered_runs = []
568
+ for run in runs:
569
+ if start_timestamp or end_timestamp:
570
+ run_created_at = run.get("created_at")
571
+ if run_created_at:
572
+ # created_at is stored as epoch int
573
+ if start_timestamp and run_created_at < start_timestamp:
574
+ continue
575
+ if end_timestamp and run_created_at > end_timestamp:
576
+ continue
577
+
578
+ filtered_runs.append(run)
579
+
580
+ if not filtered_runs:
581
+ return []
582
+
583
+ run_responses: List[Union[RunSchema, TeamRunSchema, WorkflowRunSchema]] = []
363
584
 
364
585
  if session_type == SessionType.AGENT:
365
- return [RunSchema.from_dict(run) for run in runs]
586
+ return [RunSchema.from_dict(run) for run in filtered_runs]
366
587
 
367
588
  elif session_type == SessionType.TEAM:
368
- return [TeamRunSchema.from_dict(run) for run in runs]
589
+ for run in filtered_runs:
590
+ if run.get("agent_id") is not None:
591
+ run_responses.append(RunSchema.from_dict(run))
592
+ elif run.get("team_id") is not None:
593
+ run_responses.append(TeamRunSchema.from_dict(run))
594
+ return run_responses
369
595
 
370
596
  elif session_type == SessionType.WORKFLOW:
371
- return [WorkflowRunSchema.from_dict(run) for run in runs]
597
+ for run in filtered_runs:
598
+ if run.get("workflow_id") is not None:
599
+ run_responses.append(WorkflowRunSchema.from_dict(run))
600
+ elif run.get("team_id") is not None:
601
+ run_responses.append(TeamRunSchema.from_dict(run))
602
+ else:
603
+ run_responses.append(RunSchema.from_dict(run))
604
+ return run_responses
605
+ else:
606
+ raise HTTPException(status_code=400, detail=f"Invalid session type: {session_type}")
607
+
608
+ @router.get(
609
+ "/sessions/{session_id}/runs/{run_id}",
610
+ response_model=Union[RunSchema, TeamRunSchema, WorkflowRunSchema],
611
+ status_code=200,
612
+ operation_id="get_session_run",
613
+ summary="Get Run by ID",
614
+ description=(
615
+ "Retrieve a specific run by its ID from a session. Response schema varies based on the "
616
+ "run type (agent run, team run, or workflow run)."
617
+ ),
618
+ responses={
619
+ 200: {
620
+ "description": "Run retrieved successfully",
621
+ "content": {
622
+ "application/json": {
623
+ "examples": {
624
+ "agent_run": {
625
+ "summary": "Example agent run",
626
+ "value": {
627
+ "run_id": "fcdf50f0-7c32-4593-b2ef-68a558774340",
628
+ "parent_run_id": "80056af0-c7a5-4d69-b6a2-c3eba9f040e0",
629
+ "agent_id": "basic-agent",
630
+ "user_id": "user_123",
631
+ "run_input": "Which tools do you have access to?",
632
+ "content": "I don't have access to external tools.",
633
+ "created_at": 1728499200,
634
+ },
635
+ }
636
+ }
637
+ }
638
+ },
639
+ },
640
+ 404: {"description": "Session or run not found", "model": NotFoundResponse},
641
+ 422: {"description": "Invalid session type", "model": ValidationErrorResponse},
642
+ },
643
+ )
644
+ async def get_session_run(
645
+ request: Request,
646
+ session_id: str = Path(description="Session ID to get run from"),
647
+ run_id: str = Path(description="Run ID to retrieve"),
648
+ session_type: SessionType = Query(
649
+ default=SessionType.AGENT, description="Session type (agent, team, or workflow)", alias="type"
650
+ ),
651
+ user_id: Optional[str] = Query(default=None, description="User ID to query run from"),
652
+ db_id: Optional[str] = Query(default=None, description="Database ID to query run from"),
653
+ ) -> Union[RunSchema, TeamRunSchema, WorkflowRunSchema]:
654
+ db = await get_db(dbs, db_id)
655
+
656
+ if hasattr(request.state, "user_id"):
657
+ user_id = request.state.user_id
372
658
 
659
+ if isinstance(db, AsyncBaseDb):
660
+ db = cast(AsyncBaseDb, db)
661
+ session = await db.get_session(
662
+ session_id=session_id, session_type=session_type, user_id=user_id, deserialize=False
663
+ )
373
664
  else:
374
- return [RunSchema.from_dict(run) for run in runs]
665
+ session = db.get_session(
666
+ session_id=session_id, session_type=session_type, user_id=user_id, deserialize=False
667
+ )
668
+
669
+ if not session:
670
+ raise HTTPException(status_code=404, detail=f"Session with ID {session_id} not found")
671
+
672
+ runs = session.get("runs") # type: ignore
673
+ if not runs:
674
+ raise HTTPException(status_code=404, detail=f"Session with ID {session_id} has no runs")
675
+
676
+ # Find the specific run
677
+ # TODO: Move this filtering into the DB layer
678
+ target_run = None
679
+ for run in runs:
680
+ if run.get("run_id") == run_id:
681
+ target_run = run
682
+ break
683
+
684
+ if not target_run:
685
+ raise HTTPException(status_code=404, detail=f"Run with ID {run_id} not found in session {session_id}")
686
+
687
+ # Return the appropriate schema based on run type
688
+ if target_run.get("workflow_id") is not None:
689
+ return WorkflowRunSchema.from_dict(target_run)
690
+ elif target_run.get("team_id") is not None:
691
+ return TeamRunSchema.from_dict(target_run)
692
+ else:
693
+ return RunSchema.from_dict(target_run)
375
694
 
376
695
  @router.delete(
377
696
  "/sessions/{session_id}",
@@ -390,9 +709,14 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
390
709
  async def delete_session(
391
710
  session_id: str = Path(description="Session ID to delete"),
392
711
  db_id: Optional[str] = Query(default=None, description="Database ID to use for deletion"),
712
+ table: Optional[str] = Query(default=None, description="Table to use for deletion"),
393
713
  ) -> None:
394
- db = get_db(dbs, db_id)
395
- db.delete_session(session_id=session_id)
714
+ db = await get_db(dbs, db_id, table)
715
+ if isinstance(db, AsyncBaseDb):
716
+ db = cast(AsyncBaseDb, db)
717
+ await db.delete_session(session_id=session_id)
718
+ else:
719
+ db.delete_session(session_id=session_id)
396
720
 
397
721
  @router.delete(
398
722
  "/sessions",
@@ -418,12 +742,17 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
418
742
  default=SessionType.AGENT, description="Default session type filter", alias="type"
419
743
  ),
420
744
  db_id: Optional[str] = Query(default=None, description="Database ID to use for deletion"),
745
+ table: Optional[str] = Query(default=None, description="Table to use for deletion"),
421
746
  ) -> None:
422
747
  if len(request.session_ids) != len(request.session_types):
423
748
  raise HTTPException(status_code=400, detail="Session IDs and session types must have the same length")
424
749
 
425
- db = get_db(dbs, db_id)
426
- db.delete_sessions(session_ids=request.session_ids)
750
+ db = await get_db(dbs, db_id, table)
751
+ if isinstance(db, AsyncBaseDb):
752
+ db = cast(AsyncBaseDb, db)
753
+ await db.delete_sessions(session_ids=request.session_ids)
754
+ else:
755
+ db.delete_sessions(session_ids=request.session_ids)
427
756
 
428
757
  @router.post(
429
758
  "/sessions/{session_id}/rename",
@@ -520,9 +849,16 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
520
849
  ),
521
850
  session_name: str = Body(embed=True, description="New name for the session"),
522
851
  db_id: Optional[str] = Query(default=None, description="Database ID to use for rename operation"),
852
+ table: Optional[str] = Query(default=None, description="Table to use for rename operation"),
523
853
  ) -> Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema]:
524
- db = get_db(dbs, db_id)
525
- session = db.rename_session(session_id=session_id, session_type=session_type, session_name=session_name)
854
+ db = await get_db(dbs, db_id, table)
855
+ if isinstance(db, AsyncBaseDb):
856
+ db = cast(AsyncBaseDb, db)
857
+ session = await db.rename_session(
858
+ session_id=session_id, session_type=session_type, session_name=session_name
859
+ )
860
+ else:
861
+ session = db.rename_session(session_id=session_id, session_type=session_type, session_name=session_name)
526
862
  if not session:
527
863
  raise HTTPException(status_code=404, detail=f"Session with id '{session_id}' not found")
528
864
 
@@ -533,4 +869,129 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
533
869
  else:
534
870
  return WorkflowSessionDetailSchema.from_session(session) # type: ignore
535
871
 
872
+ @router.patch(
873
+ "/sessions/{session_id}",
874
+ response_model=Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema],
875
+ status_code=200,
876
+ operation_id="update_session",
877
+ summary="Update Session",
878
+ description=(
879
+ "Update session properties such as session_name, session_state, metadata, or summary. "
880
+ "Use this endpoint to modify the session name, update state, add metadata, or update the session summary."
881
+ ),
882
+ responses={
883
+ 200: {
884
+ "description": "Session updated successfully",
885
+ "content": {
886
+ "application/json": {
887
+ "examples": {
888
+ "update_summary": {
889
+ "summary": "Update session summary",
890
+ "value": {
891
+ "summary": {
892
+ "summary": "The user discussed project planning with the agent.",
893
+ "updated_at": "2025-10-21T14:30:00Z",
894
+ }
895
+ },
896
+ },
897
+ "update_metadata": {
898
+ "summary": "Update session metadata",
899
+ "value": {
900
+ "metadata": {
901
+ "tags": ["planning", "project"],
902
+ "priority": "high",
903
+ }
904
+ },
905
+ },
906
+ "update_session_name": {
907
+ "summary": "Update session name",
908
+ "value": {"session_name": "Updated Session Name"},
909
+ },
910
+ "update_session_state": {
911
+ "summary": "Update session state",
912
+ "value": {
913
+ "session_state": {
914
+ "step": "completed",
915
+ "context": "Project planning finished",
916
+ "progress": 100,
917
+ }
918
+ },
919
+ },
920
+ }
921
+ }
922
+ },
923
+ },
924
+ 404: {"description": "Session not found", "model": NotFoundResponse},
925
+ 422: {"description": "Invalid request", "model": ValidationErrorResponse},
926
+ 500: {"description": "Failed to update session", "model": InternalServerErrorResponse},
927
+ },
928
+ )
929
+ async def update_session(
930
+ request: Request,
931
+ session_id: str = Path(description="Session ID to update"),
932
+ session_type: SessionType = Query(
933
+ default=SessionType.AGENT, description="Session type (agent, team, or workflow)", alias="type"
934
+ ),
935
+ update_data: UpdateSessionRequest = Body(description="Session update data"),
936
+ user_id: Optional[str] = Query(default=None, description="User ID"),
937
+ db_id: Optional[str] = Query(default=None, description="Database ID to use for update operation"),
938
+ ) -> Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema]:
939
+ db = await get_db(dbs, db_id)
940
+
941
+ if hasattr(request.state, "user_id"):
942
+ user_id = request.state.user_id
943
+
944
+ # Get the existing session
945
+ if isinstance(db, AsyncBaseDb):
946
+ db = cast(AsyncBaseDb, db)
947
+ existing_session = await db.get_session(
948
+ session_id=session_id, session_type=session_type, user_id=user_id, deserialize=True
949
+ )
950
+ else:
951
+ existing_session = db.get_session(
952
+ session_id=session_id, session_type=session_type, user_id=user_id, deserialize=True
953
+ )
954
+
955
+ if not existing_session:
956
+ raise HTTPException(status_code=404, detail=f"Session with id '{session_id}' not found")
957
+
958
+ # Update session properties
959
+ # Handle session_name - stored in session_data
960
+ if update_data.session_name is not None:
961
+ if existing_session.session_data is None: # type: ignore
962
+ existing_session.session_data = {} # type: ignore
963
+ existing_session.session_data["session_name"] = update_data.session_name # type: ignore
964
+
965
+ # Handle session_state - stored in session_data
966
+ if update_data.session_state is not None:
967
+ if existing_session.session_data is None: # type: ignore
968
+ existing_session.session_data = {} # type: ignore
969
+ existing_session.session_data["session_state"] = update_data.session_state # type: ignore
970
+
971
+ if update_data.metadata is not None:
972
+ existing_session.metadata = update_data.metadata # type: ignore
973
+
974
+ if update_data.summary is not None:
975
+ from agno.session.summary import SessionSummary
976
+
977
+ existing_session.summary = SessionSummary.from_dict(update_data.summary) # type: ignore
978
+
979
+ # Upsert the updated session
980
+ if isinstance(db, AsyncBaseDb):
981
+ db = cast(AsyncBaseDb, db)
982
+ updated_session = await db.upsert_session(existing_session, deserialize=True) # type: ignore
983
+ else:
984
+ updated_session = db.upsert_session(existing_session, deserialize=True) # type: ignore
985
+
986
+ if not updated_session:
987
+ raise HTTPException(status_code=500, detail="Failed to update session")
988
+
989
+ # Return appropriate schema based on session type
990
+ if session_type == SessionType.AGENT:
991
+ return AgentSessionDetailSchema.from_session(updated_session) # type: ignore
992
+ elif session_type == SessionType.TEAM:
993
+ return TeamSessionDetailSchema.from_session(updated_session) # type: ignore
994
+ else:
995
+ return WorkflowSessionDetailSchema.from_session(updated_session) # type: ignore
996
+
536
997
  return router