agno 2.2.13__py3-none-any.whl → 2.4.3__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 (383) hide show
  1. agno/agent/__init__.py +6 -0
  2. agno/agent/agent.py +5252 -3145
  3. agno/agent/remote.py +525 -0
  4. agno/api/api.py +2 -0
  5. agno/client/__init__.py +3 -0
  6. agno/client/a2a/__init__.py +10 -0
  7. agno/client/a2a/client.py +554 -0
  8. agno/client/a2a/schemas.py +112 -0
  9. agno/client/a2a/utils.py +369 -0
  10. agno/client/os.py +2669 -0
  11. agno/compression/__init__.py +3 -0
  12. agno/compression/manager.py +247 -0
  13. agno/culture/manager.py +2 -2
  14. agno/db/base.py +927 -6
  15. agno/db/dynamo/dynamo.py +788 -2
  16. agno/db/dynamo/schemas.py +128 -0
  17. agno/db/dynamo/utils.py +26 -3
  18. agno/db/firestore/firestore.py +674 -50
  19. agno/db/firestore/schemas.py +41 -0
  20. agno/db/firestore/utils.py +25 -10
  21. agno/db/gcs_json/gcs_json_db.py +506 -3
  22. agno/db/gcs_json/utils.py +14 -2
  23. agno/db/in_memory/in_memory_db.py +203 -4
  24. agno/db/in_memory/utils.py +14 -2
  25. agno/db/json/json_db.py +498 -2
  26. agno/db/json/utils.py +14 -2
  27. agno/db/migrations/manager.py +199 -0
  28. agno/db/migrations/utils.py +19 -0
  29. agno/db/migrations/v1_to_v2.py +54 -16
  30. agno/db/migrations/versions/__init__.py +0 -0
  31. agno/db/migrations/versions/v2_3_0.py +977 -0
  32. agno/db/mongo/async_mongo.py +1013 -39
  33. agno/db/mongo/mongo.py +684 -4
  34. agno/db/mongo/schemas.py +48 -0
  35. agno/db/mongo/utils.py +17 -0
  36. agno/db/mysql/__init__.py +2 -1
  37. agno/db/mysql/async_mysql.py +2958 -0
  38. agno/db/mysql/mysql.py +722 -53
  39. agno/db/mysql/schemas.py +77 -11
  40. agno/db/mysql/utils.py +151 -8
  41. agno/db/postgres/async_postgres.py +1254 -137
  42. agno/db/postgres/postgres.py +2316 -93
  43. agno/db/postgres/schemas.py +153 -21
  44. agno/db/postgres/utils.py +22 -7
  45. agno/db/redis/redis.py +531 -3
  46. agno/db/redis/schemas.py +36 -0
  47. agno/db/redis/utils.py +31 -15
  48. agno/db/schemas/evals.py +1 -0
  49. agno/db/schemas/memory.py +20 -9
  50. agno/db/singlestore/schemas.py +70 -1
  51. agno/db/singlestore/singlestore.py +737 -74
  52. agno/db/singlestore/utils.py +13 -3
  53. agno/db/sqlite/async_sqlite.py +1069 -89
  54. agno/db/sqlite/schemas.py +133 -1
  55. agno/db/sqlite/sqlite.py +2203 -165
  56. agno/db/sqlite/utils.py +21 -11
  57. agno/db/surrealdb/models.py +25 -0
  58. agno/db/surrealdb/surrealdb.py +603 -1
  59. agno/db/utils.py +60 -0
  60. agno/eval/__init__.py +26 -3
  61. agno/eval/accuracy.py +25 -12
  62. agno/eval/agent_as_judge.py +871 -0
  63. agno/eval/base.py +29 -0
  64. agno/eval/performance.py +10 -4
  65. agno/eval/reliability.py +22 -13
  66. agno/eval/utils.py +2 -1
  67. agno/exceptions.py +42 -0
  68. agno/hooks/__init__.py +3 -0
  69. agno/hooks/decorator.py +164 -0
  70. agno/integrations/discord/client.py +13 -2
  71. agno/knowledge/__init__.py +4 -0
  72. agno/knowledge/chunking/code.py +90 -0
  73. agno/knowledge/chunking/document.py +65 -4
  74. agno/knowledge/chunking/fixed.py +4 -1
  75. agno/knowledge/chunking/markdown.py +102 -11
  76. agno/knowledge/chunking/recursive.py +2 -2
  77. agno/knowledge/chunking/semantic.py +130 -48
  78. agno/knowledge/chunking/strategy.py +18 -0
  79. agno/knowledge/embedder/azure_openai.py +0 -1
  80. agno/knowledge/embedder/google.py +1 -1
  81. agno/knowledge/embedder/mistral.py +1 -1
  82. agno/knowledge/embedder/nebius.py +1 -1
  83. agno/knowledge/embedder/openai.py +16 -12
  84. agno/knowledge/filesystem.py +412 -0
  85. agno/knowledge/knowledge.py +4261 -1199
  86. agno/knowledge/protocol.py +134 -0
  87. agno/knowledge/reader/arxiv_reader.py +3 -2
  88. agno/knowledge/reader/base.py +9 -7
  89. agno/knowledge/reader/csv_reader.py +91 -42
  90. agno/knowledge/reader/docx_reader.py +9 -10
  91. agno/knowledge/reader/excel_reader.py +225 -0
  92. agno/knowledge/reader/field_labeled_csv_reader.py +38 -48
  93. agno/knowledge/reader/firecrawl_reader.py +3 -2
  94. agno/knowledge/reader/json_reader.py +16 -22
  95. agno/knowledge/reader/markdown_reader.py +15 -14
  96. agno/knowledge/reader/pdf_reader.py +33 -28
  97. agno/knowledge/reader/pptx_reader.py +9 -10
  98. agno/knowledge/reader/reader_factory.py +135 -1
  99. agno/knowledge/reader/s3_reader.py +8 -16
  100. agno/knowledge/reader/tavily_reader.py +3 -3
  101. agno/knowledge/reader/text_reader.py +15 -14
  102. agno/knowledge/reader/utils/__init__.py +17 -0
  103. agno/knowledge/reader/utils/spreadsheet.py +114 -0
  104. agno/knowledge/reader/web_search_reader.py +8 -65
  105. agno/knowledge/reader/website_reader.py +16 -13
  106. agno/knowledge/reader/wikipedia_reader.py +36 -3
  107. agno/knowledge/reader/youtube_reader.py +3 -2
  108. agno/knowledge/remote_content/__init__.py +33 -0
  109. agno/knowledge/remote_content/config.py +266 -0
  110. agno/knowledge/remote_content/remote_content.py +105 -17
  111. agno/knowledge/utils.py +76 -22
  112. agno/learn/__init__.py +71 -0
  113. agno/learn/config.py +463 -0
  114. agno/learn/curate.py +185 -0
  115. agno/learn/machine.py +725 -0
  116. agno/learn/schemas.py +1114 -0
  117. agno/learn/stores/__init__.py +38 -0
  118. agno/learn/stores/decision_log.py +1156 -0
  119. agno/learn/stores/entity_memory.py +3275 -0
  120. agno/learn/stores/learned_knowledge.py +1583 -0
  121. agno/learn/stores/protocol.py +117 -0
  122. agno/learn/stores/session_context.py +1217 -0
  123. agno/learn/stores/user_memory.py +1495 -0
  124. agno/learn/stores/user_profile.py +1220 -0
  125. agno/learn/utils.py +209 -0
  126. agno/media.py +22 -6
  127. agno/memory/__init__.py +14 -1
  128. agno/memory/manager.py +223 -8
  129. agno/memory/strategies/__init__.py +15 -0
  130. agno/memory/strategies/base.py +66 -0
  131. agno/memory/strategies/summarize.py +196 -0
  132. agno/memory/strategies/types.py +37 -0
  133. agno/models/aimlapi/aimlapi.py +17 -0
  134. agno/models/anthropic/claude.py +434 -59
  135. agno/models/aws/bedrock.py +121 -20
  136. agno/models/aws/claude.py +131 -274
  137. agno/models/azure/ai_foundry.py +10 -6
  138. agno/models/azure/openai_chat.py +33 -10
  139. agno/models/base.py +1162 -561
  140. agno/models/cerebras/cerebras.py +120 -24
  141. agno/models/cerebras/cerebras_openai.py +21 -2
  142. agno/models/cohere/chat.py +65 -6
  143. agno/models/cometapi/cometapi.py +18 -1
  144. agno/models/dashscope/dashscope.py +2 -3
  145. agno/models/deepinfra/deepinfra.py +18 -1
  146. agno/models/deepseek/deepseek.py +69 -3
  147. agno/models/fireworks/fireworks.py +18 -1
  148. agno/models/google/gemini.py +959 -89
  149. agno/models/google/utils.py +22 -0
  150. agno/models/groq/groq.py +48 -18
  151. agno/models/huggingface/huggingface.py +17 -6
  152. agno/models/ibm/watsonx.py +16 -6
  153. agno/models/internlm/internlm.py +18 -1
  154. agno/models/langdb/langdb.py +13 -1
  155. agno/models/litellm/chat.py +88 -9
  156. agno/models/litellm/litellm_openai.py +18 -1
  157. agno/models/message.py +24 -5
  158. agno/models/meta/llama.py +40 -13
  159. agno/models/meta/llama_openai.py +22 -21
  160. agno/models/metrics.py +12 -0
  161. agno/models/mistral/mistral.py +8 -4
  162. agno/models/n1n/__init__.py +3 -0
  163. agno/models/n1n/n1n.py +57 -0
  164. agno/models/nebius/nebius.py +6 -7
  165. agno/models/nvidia/nvidia.py +20 -3
  166. agno/models/ollama/__init__.py +2 -0
  167. agno/models/ollama/chat.py +17 -6
  168. agno/models/ollama/responses.py +100 -0
  169. agno/models/openai/__init__.py +2 -0
  170. agno/models/openai/chat.py +117 -26
  171. agno/models/openai/open_responses.py +46 -0
  172. agno/models/openai/responses.py +110 -32
  173. agno/models/openrouter/__init__.py +2 -0
  174. agno/models/openrouter/openrouter.py +67 -2
  175. agno/models/openrouter/responses.py +146 -0
  176. agno/models/perplexity/perplexity.py +19 -1
  177. agno/models/portkey/portkey.py +7 -6
  178. agno/models/requesty/requesty.py +19 -2
  179. agno/models/response.py +20 -2
  180. agno/models/sambanova/sambanova.py +20 -3
  181. agno/models/siliconflow/siliconflow.py +19 -2
  182. agno/models/together/together.py +20 -3
  183. agno/models/vercel/v0.py +20 -3
  184. agno/models/vertexai/claude.py +124 -4
  185. agno/models/vllm/vllm.py +19 -14
  186. agno/models/xai/xai.py +19 -2
  187. agno/os/app.py +467 -137
  188. agno/os/auth.py +253 -5
  189. agno/os/config.py +22 -0
  190. agno/os/interfaces/a2a/a2a.py +7 -6
  191. agno/os/interfaces/a2a/router.py +635 -26
  192. agno/os/interfaces/a2a/utils.py +32 -33
  193. agno/os/interfaces/agui/agui.py +5 -3
  194. agno/os/interfaces/agui/router.py +26 -16
  195. agno/os/interfaces/agui/utils.py +97 -57
  196. agno/os/interfaces/base.py +7 -7
  197. agno/os/interfaces/slack/router.py +16 -7
  198. agno/os/interfaces/slack/slack.py +7 -7
  199. agno/os/interfaces/whatsapp/router.py +35 -7
  200. agno/os/interfaces/whatsapp/security.py +3 -1
  201. agno/os/interfaces/whatsapp/whatsapp.py +11 -8
  202. agno/os/managers.py +326 -0
  203. agno/os/mcp.py +652 -79
  204. agno/os/middleware/__init__.py +4 -0
  205. agno/os/middleware/jwt.py +718 -115
  206. agno/os/middleware/trailing_slash.py +27 -0
  207. agno/os/router.py +105 -1558
  208. agno/os/routers/agents/__init__.py +3 -0
  209. agno/os/routers/agents/router.py +655 -0
  210. agno/os/routers/agents/schema.py +288 -0
  211. agno/os/routers/components/__init__.py +3 -0
  212. agno/os/routers/components/components.py +475 -0
  213. agno/os/routers/database.py +155 -0
  214. agno/os/routers/evals/evals.py +111 -18
  215. agno/os/routers/evals/schemas.py +38 -5
  216. agno/os/routers/evals/utils.py +80 -11
  217. agno/os/routers/health.py +3 -3
  218. agno/os/routers/knowledge/knowledge.py +284 -35
  219. agno/os/routers/knowledge/schemas.py +14 -2
  220. agno/os/routers/memory/memory.py +274 -11
  221. agno/os/routers/memory/schemas.py +44 -3
  222. agno/os/routers/metrics/metrics.py +30 -15
  223. agno/os/routers/metrics/schemas.py +10 -6
  224. agno/os/routers/registry/__init__.py +3 -0
  225. agno/os/routers/registry/registry.py +337 -0
  226. agno/os/routers/session/session.py +143 -14
  227. agno/os/routers/teams/__init__.py +3 -0
  228. agno/os/routers/teams/router.py +550 -0
  229. agno/os/routers/teams/schema.py +280 -0
  230. agno/os/routers/traces/__init__.py +3 -0
  231. agno/os/routers/traces/schemas.py +414 -0
  232. agno/os/routers/traces/traces.py +549 -0
  233. agno/os/routers/workflows/__init__.py +3 -0
  234. agno/os/routers/workflows/router.py +757 -0
  235. agno/os/routers/workflows/schema.py +139 -0
  236. agno/os/schema.py +157 -584
  237. agno/os/scopes.py +469 -0
  238. agno/os/settings.py +3 -0
  239. agno/os/utils.py +574 -185
  240. agno/reasoning/anthropic.py +85 -1
  241. agno/reasoning/azure_ai_foundry.py +93 -1
  242. agno/reasoning/deepseek.py +102 -2
  243. agno/reasoning/default.py +6 -7
  244. agno/reasoning/gemini.py +87 -3
  245. agno/reasoning/groq.py +109 -2
  246. agno/reasoning/helpers.py +6 -7
  247. agno/reasoning/manager.py +1238 -0
  248. agno/reasoning/ollama.py +93 -1
  249. agno/reasoning/openai.py +115 -1
  250. agno/reasoning/vertexai.py +85 -1
  251. agno/registry/__init__.py +3 -0
  252. agno/registry/registry.py +68 -0
  253. agno/remote/__init__.py +3 -0
  254. agno/remote/base.py +581 -0
  255. agno/run/__init__.py +2 -4
  256. agno/run/agent.py +134 -19
  257. agno/run/base.py +49 -1
  258. agno/run/cancel.py +65 -52
  259. agno/run/cancellation_management/__init__.py +9 -0
  260. agno/run/cancellation_management/base.py +78 -0
  261. agno/run/cancellation_management/in_memory_cancellation_manager.py +100 -0
  262. agno/run/cancellation_management/redis_cancellation_manager.py +236 -0
  263. agno/run/requirement.py +181 -0
  264. agno/run/team.py +111 -19
  265. agno/run/workflow.py +2 -1
  266. agno/session/agent.py +57 -92
  267. agno/session/summary.py +1 -1
  268. agno/session/team.py +62 -115
  269. agno/session/workflow.py +353 -57
  270. agno/skills/__init__.py +17 -0
  271. agno/skills/agent_skills.py +377 -0
  272. agno/skills/errors.py +32 -0
  273. agno/skills/loaders/__init__.py +4 -0
  274. agno/skills/loaders/base.py +27 -0
  275. agno/skills/loaders/local.py +216 -0
  276. agno/skills/skill.py +65 -0
  277. agno/skills/utils.py +107 -0
  278. agno/skills/validator.py +277 -0
  279. agno/table.py +10 -0
  280. agno/team/__init__.py +5 -1
  281. agno/team/remote.py +447 -0
  282. agno/team/team.py +3769 -2202
  283. agno/tools/brandfetch.py +27 -18
  284. agno/tools/browserbase.py +225 -16
  285. agno/tools/crawl4ai.py +3 -0
  286. agno/tools/duckduckgo.py +25 -71
  287. agno/tools/exa.py +0 -21
  288. agno/tools/file.py +14 -13
  289. agno/tools/file_generation.py +12 -6
  290. agno/tools/firecrawl.py +15 -7
  291. agno/tools/function.py +94 -113
  292. agno/tools/google_bigquery.py +11 -2
  293. agno/tools/google_drive.py +4 -3
  294. agno/tools/knowledge.py +9 -4
  295. agno/tools/mcp/mcp.py +301 -18
  296. agno/tools/mcp/multi_mcp.py +269 -14
  297. agno/tools/mem0.py +11 -10
  298. agno/tools/memory.py +47 -46
  299. agno/tools/mlx_transcribe.py +10 -7
  300. agno/tools/models/nebius.py +5 -5
  301. agno/tools/models_labs.py +20 -10
  302. agno/tools/nano_banana.py +151 -0
  303. agno/tools/parallel.py +0 -7
  304. agno/tools/postgres.py +76 -36
  305. agno/tools/python.py +14 -6
  306. agno/tools/reasoning.py +30 -23
  307. agno/tools/redshift.py +406 -0
  308. agno/tools/shopify.py +1519 -0
  309. agno/tools/spotify.py +919 -0
  310. agno/tools/tavily.py +4 -1
  311. agno/tools/toolkit.py +253 -18
  312. agno/tools/websearch.py +93 -0
  313. agno/tools/website.py +1 -1
  314. agno/tools/wikipedia.py +1 -1
  315. agno/tools/workflow.py +56 -48
  316. agno/tools/yfinance.py +12 -11
  317. agno/tracing/__init__.py +12 -0
  318. agno/tracing/exporter.py +161 -0
  319. agno/tracing/schemas.py +276 -0
  320. agno/tracing/setup.py +112 -0
  321. agno/utils/agent.py +251 -10
  322. agno/utils/cryptography.py +22 -0
  323. agno/utils/dttm.py +33 -0
  324. agno/utils/events.py +264 -7
  325. agno/utils/hooks.py +111 -3
  326. agno/utils/http.py +161 -2
  327. agno/utils/mcp.py +49 -8
  328. agno/utils/media.py +22 -1
  329. agno/utils/models/ai_foundry.py +9 -2
  330. agno/utils/models/claude.py +20 -5
  331. agno/utils/models/cohere.py +9 -2
  332. agno/utils/models/llama.py +9 -2
  333. agno/utils/models/mistral.py +4 -2
  334. agno/utils/os.py +0 -0
  335. agno/utils/print_response/agent.py +99 -16
  336. agno/utils/print_response/team.py +223 -24
  337. agno/utils/print_response/workflow.py +0 -2
  338. agno/utils/prompts.py +8 -6
  339. agno/utils/remote.py +23 -0
  340. agno/utils/response.py +1 -13
  341. agno/utils/string.py +91 -2
  342. agno/utils/team.py +62 -12
  343. agno/utils/tokens.py +657 -0
  344. agno/vectordb/base.py +15 -2
  345. agno/vectordb/cassandra/cassandra.py +1 -1
  346. agno/vectordb/chroma/__init__.py +2 -1
  347. agno/vectordb/chroma/chromadb.py +468 -23
  348. agno/vectordb/clickhouse/clickhousedb.py +1 -1
  349. agno/vectordb/couchbase/couchbase.py +6 -2
  350. agno/vectordb/lancedb/lance_db.py +7 -38
  351. agno/vectordb/lightrag/lightrag.py +7 -6
  352. agno/vectordb/milvus/milvus.py +118 -84
  353. agno/vectordb/mongodb/__init__.py +2 -1
  354. agno/vectordb/mongodb/mongodb.py +14 -31
  355. agno/vectordb/pgvector/pgvector.py +120 -66
  356. agno/vectordb/pineconedb/pineconedb.py +2 -19
  357. agno/vectordb/qdrant/__init__.py +2 -1
  358. agno/vectordb/qdrant/qdrant.py +33 -56
  359. agno/vectordb/redis/__init__.py +2 -1
  360. agno/vectordb/redis/redisdb.py +19 -31
  361. agno/vectordb/singlestore/singlestore.py +17 -9
  362. agno/vectordb/surrealdb/surrealdb.py +2 -38
  363. agno/vectordb/weaviate/__init__.py +2 -1
  364. agno/vectordb/weaviate/weaviate.py +7 -3
  365. agno/workflow/__init__.py +5 -1
  366. agno/workflow/agent.py +2 -2
  367. agno/workflow/condition.py +12 -10
  368. agno/workflow/loop.py +28 -9
  369. agno/workflow/parallel.py +21 -13
  370. agno/workflow/remote.py +362 -0
  371. agno/workflow/router.py +12 -9
  372. agno/workflow/step.py +261 -36
  373. agno/workflow/steps.py +12 -8
  374. agno/workflow/types.py +40 -77
  375. agno/workflow/workflow.py +939 -213
  376. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/METADATA +134 -181
  377. agno-2.4.3.dist-info/RECORD +677 -0
  378. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/WHEEL +1 -1
  379. agno/tools/googlesearch.py +0 -98
  380. agno/tools/memori.py +0 -339
  381. agno-2.2.13.dist-info/RECORD +0 -575
  382. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/licenses/LICENSE +0 -0
  383. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,337 @@
1
+ import time
2
+ from typing import Any, Dict, List, Literal, Optional
3
+
4
+ from fastapi import APIRouter, Depends, HTTPException, Query
5
+ from pydantic import BaseModel, Field
6
+
7
+ from agno.os.auth import get_authentication_dependency
8
+ from agno.os.schema import (
9
+ BadRequestResponse,
10
+ InternalServerErrorResponse,
11
+ NotFoundResponse,
12
+ PaginatedResponse,
13
+ PaginationInfo,
14
+ UnauthenticatedResponse,
15
+ ValidationErrorResponse,
16
+ )
17
+ from agno.os.settings import AgnoAPISettings
18
+ from agno.registry import Registry
19
+ from agno.tools.function import Function
20
+ from agno.tools.toolkit import Toolkit
21
+ from agno.utils.log import log_error
22
+
23
+ ComponentType = Literal["tool", "toolkit", "model", "db", "vector_db", "schema"]
24
+
25
+
26
+ # ============================================
27
+ # Response Schema
28
+ # ============================================
29
+
30
+
31
+ class ComponentResponse(BaseModel):
32
+ name: str
33
+ component_type: ComponentType
34
+ description: Optional[str] = None
35
+ metadata: Dict[str, Any] = Field(default_factory=dict)
36
+ # Tool-specific fields (matching config format)
37
+ parameters: Optional[Dict[str, Any]] = None
38
+ requires_confirmation: Optional[bool] = None
39
+ external_execution: Optional[bool] = None
40
+
41
+
42
+ # ============================================
43
+ # Router
44
+ # ============================================
45
+
46
+
47
+ def get_registry_router(registry: Registry, settings: AgnoAPISettings = AgnoAPISettings()) -> APIRouter:
48
+ router = APIRouter(
49
+ dependencies=[Depends(get_authentication_dependency(settings))],
50
+ tags=["Registry"],
51
+ responses={
52
+ 400: {"description": "Bad Request", "model": BadRequestResponse},
53
+ 401: {"description": "Unauthorized", "model": UnauthenticatedResponse},
54
+ 404: {"description": "Not Found", "model": NotFoundResponse},
55
+ 422: {"description": "Validation Error", "model": ValidationErrorResponse},
56
+ 500: {"description": "Internal Server Error", "model": InternalServerErrorResponse},
57
+ },
58
+ )
59
+ return attach_routes(router=router, registry=registry)
60
+
61
+
62
+ def attach_routes(router: APIRouter, registry: Registry) -> APIRouter:
63
+ def _safe_str(v: Any) -> Optional[str]:
64
+ if v is None:
65
+ return None
66
+ if isinstance(v, str):
67
+ s = v.strip()
68
+ return s or None
69
+ return str(v)
70
+
71
+ def _safe_name(obj: Any, fallback: str) -> str:
72
+ n = getattr(obj, "name", None)
73
+ n = _safe_str(n)
74
+ return n or fallback
75
+
76
+ def _class_path(obj: Any) -> str:
77
+ cls = obj.__class__
78
+ return f"{cls.__module__}.{cls.__name__}"
79
+
80
+ def _maybe_jsonable(value: Any) -> Any:
81
+ # Best-effort: keep only data that is likely JSON serializable
82
+ # If your Function.parameters is a Pydantic model or custom object, this avoids 500s.
83
+ if value is None:
84
+ return None
85
+ if isinstance(value, (str, int, float, bool)):
86
+ return value
87
+ if isinstance(value, list):
88
+ return [_maybe_jsonable(x) for x in value]
89
+ if isinstance(value, dict):
90
+ out: Dict[str, Any] = {}
91
+ for k, v in value.items():
92
+ out[str(k)] = _maybe_jsonable(v)
93
+ return out
94
+ # Fallback to string to avoid serialization errors
95
+ return str(value)
96
+
97
+ def _get_all_components(include_schema: bool) -> List[ComponentResponse]:
98
+ components: List[ComponentResponse] = []
99
+
100
+ # Tools
101
+ for tool in getattr(registry, "tools", []) or []:
102
+ if isinstance(tool, Toolkit):
103
+ toolkit_name = _safe_name(tool, fallback=tool.__class__.__name__)
104
+ functions = getattr(tool, "functions", {}) or {}
105
+
106
+ components.append(
107
+ ComponentResponse(
108
+ name=toolkit_name,
109
+ component_type="toolkit",
110
+ description=_safe_str(getattr(tool, "description", None)),
111
+ metadata={
112
+ "class_path": _class_path(tool),
113
+ "functions": sorted(list(functions.keys())),
114
+ },
115
+ )
116
+ )
117
+
118
+ # Also expose individual functions within toolkit
119
+ for func in functions.values():
120
+ func_name = _safe_name(func, fallback=func.__class__.__name__)
121
+ # Check if function requires confirmation or external execution
122
+ # First check function-level settings, then toolkit-level settings
123
+ requires_confirmation = getattr(func, "requires_confirmation", None)
124
+ external_execution = getattr(func, "external_execution", None)
125
+
126
+ # If not set on function, check toolkit settings
127
+ if requires_confirmation is None and hasattr(tool, "requires_confirmation_tools"):
128
+ requires_confirmation = func_name in (tool.requires_confirmation_tools or [])
129
+ if external_execution is None and hasattr(tool, "external_execution_required_tools"):
130
+ external_execution = func_name in (tool.external_execution_required_tools or [])
131
+
132
+ # Get parameters - ensure they're processed if needed
133
+ func_params = func.parameters
134
+ # If parameters are empty/default and function has entrypoint, try to process it
135
+ default_params = {"type": "object", "properties": {}, "required": []}
136
+ if func_params == default_params and func.entrypoint and not func.skip_entrypoint_processing:
137
+ try:
138
+ # Create a copy to avoid modifying the original
139
+ func_copy = func.model_copy(deep=False)
140
+ func_copy.process_entrypoint(strict=False)
141
+ func_params = func_copy.parameters
142
+ except Exception:
143
+ # If processing fails, use original parameters
144
+ pass
145
+
146
+ components.append(
147
+ ComponentResponse(
148
+ name=func_name,
149
+ component_type="tool",
150
+ description=_safe_str(getattr(func, "description", None)),
151
+ parameters=_maybe_jsonable(func_params),
152
+ requires_confirmation=requires_confirmation,
153
+ external_execution=external_execution,
154
+ metadata={
155
+ "class_path": _class_path(func),
156
+ "toolkit": toolkit_name,
157
+ "has_entrypoint": bool(getattr(func, "entrypoint", None)),
158
+ },
159
+ )
160
+ )
161
+
162
+ elif isinstance(tool, Function):
163
+ func_name = _safe_name(tool, fallback=tool.__class__.__name__)
164
+ requires_confirmation = getattr(tool, "requires_confirmation", None)
165
+ external_execution = getattr(tool, "external_execution", None)
166
+
167
+ # Get parameters - ensure they're processed if needed
168
+ func_params = tool.parameters
169
+ # If parameters are empty/default and function has entrypoint, try to process it
170
+ default_params = {"type": "object", "properties": {}, "required": []}
171
+ if func_params == default_params and tool.entrypoint and not tool.skip_entrypoint_processing:
172
+ try:
173
+ # Create a copy to avoid modifying the original
174
+ tool_copy = tool.model_copy(deep=False)
175
+ tool_copy.process_entrypoint(strict=False)
176
+ func_params = tool_copy.parameters
177
+ except Exception:
178
+ # If processing fails, use original parameters
179
+ pass
180
+
181
+ components.append(
182
+ ComponentResponse(
183
+ name=func_name,
184
+ component_type="tool",
185
+ description=_safe_str(getattr(tool, "description", None)),
186
+ parameters=_maybe_jsonable(func_params),
187
+ requires_confirmation=requires_confirmation,
188
+ external_execution=external_execution,
189
+ metadata={
190
+ "class_path": _class_path(tool),
191
+ "has_entrypoint": bool(getattr(tool, "entrypoint", None)),
192
+ },
193
+ )
194
+ )
195
+
196
+ elif callable(tool):
197
+ call_name = getattr(tool, "__name__", None) or tool.__class__.__name__
198
+ components.append(
199
+ ComponentResponse(
200
+ name=str(call_name),
201
+ component_type="tool",
202
+ description=_safe_str(getattr(tool, "__doc__", None)),
203
+ metadata={"class_path": _class_path(tool)},
204
+ )
205
+ )
206
+
207
+ # Models
208
+ for model in getattr(registry, "models", []) or []:
209
+ model_name = (
210
+ _safe_str(getattr(model, "id", None))
211
+ or _safe_str(getattr(model, "name", None))
212
+ or model.__class__.__name__
213
+ )
214
+ components.append(
215
+ ComponentResponse(
216
+ name=model_name,
217
+ component_type="model",
218
+ description=_safe_str(getattr(model, "description", None)),
219
+ metadata={
220
+ "class_path": _class_path(model),
221
+ "provider": _safe_str(getattr(model, "provider", None)),
222
+ "model_id": _safe_str(getattr(model, "id", None)),
223
+ },
224
+ )
225
+ )
226
+
227
+ # Databases
228
+ for db in getattr(registry, "dbs", []) or []:
229
+ db_name = (
230
+ _safe_str(getattr(db, "name", None))
231
+ or _safe_str(getattr(db, "id", None))
232
+ or _safe_str(getattr(db, "table_name", None))
233
+ or db.__class__.__name__
234
+ )
235
+ components.append(
236
+ ComponentResponse(
237
+ name=db_name,
238
+ component_type="db",
239
+ metadata={
240
+ "class_path": _class_path(db),
241
+ "db_id": _safe_str(getattr(db, "id", None)),
242
+ "table_name": _safe_str(getattr(db, "table_name", None)),
243
+ },
244
+ )
245
+ )
246
+
247
+ # Vector databases
248
+ for vdb in getattr(registry, "vector_dbs", []) or []:
249
+ vdb_name = (
250
+ _safe_str(getattr(vdb, "name", None))
251
+ or _safe_str(getattr(vdb, "id", None))
252
+ or _safe_str(getattr(vdb, "collection", None))
253
+ or _safe_str(getattr(vdb, "table_name", None))
254
+ or vdb.__class__.__name__
255
+ )
256
+ components.append(
257
+ ComponentResponse(
258
+ name=vdb_name,
259
+ component_type="vector_db",
260
+ metadata={
261
+ "class_path": _class_path(vdb),
262
+ "vector_db_id": _safe_str(getattr(vdb, "id", None)),
263
+ "collection": _safe_str(getattr(vdb, "collection", None)),
264
+ "table_name": _safe_str(getattr(vdb, "table_name", None)),
265
+ },
266
+ )
267
+ )
268
+
269
+ # Schemas
270
+ for schema in getattr(registry, "schemas", []) or []:
271
+ schema_name = schema.__name__
272
+ meta: Dict[str, Any] = {"class_path": _class_path(schema)}
273
+ if include_schema:
274
+ try:
275
+ meta["schema"] = schema.model_json_schema() if hasattr(schema, "model_json_schema") else {}
276
+ except Exception as e:
277
+ meta["schema_error"] = str(e)
278
+
279
+ components.append(
280
+ ComponentResponse(
281
+ name=schema_name,
282
+ component_type="schema",
283
+ metadata=meta,
284
+ )
285
+ )
286
+
287
+ # Stable ordering helps pagination
288
+ components.sort(key=lambda c: (c.component_type, c.name))
289
+ return components
290
+
291
+ @router.get(
292
+ "/registry",
293
+ response_model=PaginatedResponse[ComponentResponse],
294
+ response_model_exclude_none=True,
295
+ status_code=200,
296
+ operation_id="list_registry",
297
+ summary="List Registry",
298
+ description="List all components in the registry with optional filtering.",
299
+ )
300
+ async def list_registry(
301
+ component_type: Optional[ComponentType] = Query(None, description="Filter by component type"),
302
+ name: Optional[str] = Query(None, description="Filter by name (partial match)"),
303
+ include_schema: bool = Query(False, description="Include JSON schema for schema components"),
304
+ page: int = Query(1, ge=1, description="Page number"),
305
+ limit: int = Query(20, ge=1, le=100, description="Items per page"),
306
+ ) -> PaginatedResponse[ComponentResponse]:
307
+ try:
308
+ start_time_ms = time.time() * 1000
309
+ components = _get_all_components(include_schema=include_schema)
310
+
311
+ if component_type:
312
+ components = [c for c in components if c.component_type == component_type]
313
+
314
+ if name:
315
+ needle = name.lower().strip()
316
+ components = [c for c in components if needle in c.name.lower()]
317
+
318
+ total_count = len(components)
319
+ total_pages = (total_count + limit - 1) // limit if limit > 0 else 0
320
+ start_idx = (page - 1) * limit
321
+ paginated = components[start_idx : start_idx + limit]
322
+
323
+ return PaginatedResponse(
324
+ data=paginated,
325
+ meta=PaginationInfo(
326
+ page=page,
327
+ limit=limit,
328
+ total_pages=total_pages,
329
+ total_count=total_count,
330
+ search_time_ms=round(time.time() * 1000 - start_time_ms, 2),
331
+ ),
332
+ )
333
+ except Exception as e:
334
+ log_error(f"Error listing components: {e}")
335
+ raise HTTPException(status_code=500, detail=str(e))
336
+
337
+ return router
@@ -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",
@@ -105,8 +107,8 @@ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBase
105
107
  ),
106
108
  user_id: Optional[str] = Query(default=None, description="Filter sessions by user ID"),
107
109
  session_name: Optional[str] = Query(default=None, description="Filter sessions by name (partial match)"),
108
- limit: Optional[int] = Query(default=20, description="Number of sessions to return per page"),
109
- page: Optional[int] = Query(default=1, description="Page number for pagination"),
110
+ limit: Optional[int] = Query(default=20, description="Number of sessions to return per page", ge=1),
111
+ page: Optional[int] = Query(default=1, description="Page number for pagination", ge=0),
110
112
  sort_by: Optional[str] = Query(default="created_at", description="Field to sort sessions by"),
111
113
  sort_order: Optional[SortOrder] = Query(default="desc", description="Sort order (asc or desc)"),
112
114
  db_id: Optional[str] = Query(default=None, description="Database ID to query sessions from"),
@@ -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)
@@ -0,0 +1,3 @@
1
+ from agno.os.routers.teams.router import get_team_router
2
+
3
+ __all__ = ["get_team_router"]