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
agno/db/base.py CHANGED
@@ -1,9 +1,12 @@
1
1
  from abc import ABC, abstractmethod
2
- from datetime import date
2
+ from datetime import date, datetime
3
3
  from enum import Enum
4
- from typing import Any, Dict, List, Optional, Tuple, Union
4
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
5
5
  from uuid import uuid4
6
6
 
7
+ if TYPE_CHECKING:
8
+ from agno.tracing.schemas import Span, Trace
9
+
7
10
  from agno.db.schemas import UserMemory
8
11
  from agno.db.schemas.culture import CulturalKnowledge
9
12
  from agno.db.schemas.evals import EvalFilterType, EvalRunRecord, EvalType
@@ -17,9 +20,18 @@ class SessionType(str, Enum):
17
20
  WORKFLOW = "workflow"
18
21
 
19
22
 
23
+ class ComponentType(str, Enum):
24
+ AGENT = "agent"
25
+ TEAM = "team"
26
+ WORKFLOW = "workflow"
27
+
28
+
20
29
  class BaseDb(ABC):
21
30
  """Base abstract class for all our Database implementations."""
22
31
 
32
+ # We assume the database to be up to date with the 2.0.0 release
33
+ default_schema_version = "2.0.0"
34
+
23
35
  def __init__(
24
36
  self,
25
37
  session_table: Optional[str] = None,
@@ -28,6 +40,13 @@ class BaseDb(ABC):
28
40
  metrics_table: Optional[str] = None,
29
41
  eval_table: Optional[str] = None,
30
42
  knowledge_table: Optional[str] = None,
43
+ traces_table: Optional[str] = None,
44
+ spans_table: Optional[str] = None,
45
+ versions_table: Optional[str] = None,
46
+ components_table: Optional[str] = None,
47
+ component_configs_table: Optional[str] = None,
48
+ component_links_table: Optional[str] = None,
49
+ learnings_table: Optional[str] = None,
31
50
  id: Optional[str] = None,
32
51
  ):
33
52
  self.id = id or str(uuid4())
@@ -37,6 +56,54 @@ class BaseDb(ABC):
37
56
  self.metrics_table_name = metrics_table or "agno_metrics"
38
57
  self.eval_table_name = eval_table or "agno_eval_runs"
39
58
  self.knowledge_table_name = knowledge_table or "agno_knowledge"
59
+ self.trace_table_name = traces_table or "agno_traces"
60
+ self.span_table_name = spans_table or "agno_spans"
61
+ self.versions_table_name = versions_table or "agno_schema_versions"
62
+ self.components_table_name = components_table or "agno_components"
63
+ self.component_configs_table_name = component_configs_table or "agno_component_configs"
64
+ self.component_links_table_name = component_links_table or "agno_component_links"
65
+ self.learnings_table_name = learnings_table or "agno_learnings"
66
+
67
+ def to_dict(self) -> Dict[str, Any]:
68
+ """
69
+ Serialize common DB fields (table names + id). Subclasses may extend this.
70
+ """
71
+ return {
72
+ "id": self.id,
73
+ "session_table": self.session_table_name,
74
+ "culture_table": self.culture_table_name,
75
+ "memory_table": self.memory_table_name,
76
+ "metrics_table": self.metrics_table_name,
77
+ "eval_table": self.eval_table_name,
78
+ "knowledge_table": self.knowledge_table_name,
79
+ "traces_table": self.trace_table_name,
80
+ "spans_table": self.span_table_name,
81
+ "versions_table": self.versions_table_name,
82
+ "components_table": self.components_table_name,
83
+ "component_configs_table": self.component_configs_table_name,
84
+ "component_links_table": self.component_links_table_name,
85
+ }
86
+
87
+ @classmethod
88
+ def from_dict(cls, data: Dict[str, Any]) -> "BaseDb":
89
+ """
90
+ Reconstruct using only fields defined in BaseDb. Subclasses should override this.
91
+ """
92
+ return cls(
93
+ session_table=data.get("session_table"),
94
+ culture_table=data.get("culture_table"),
95
+ memory_table=data.get("memory_table"),
96
+ metrics_table=data.get("metrics_table"),
97
+ eval_table=data.get("eval_table"),
98
+ knowledge_table=data.get("knowledge_table"),
99
+ traces_table=data.get("traces_table"),
100
+ spans_table=data.get("spans_table"),
101
+ versions_table=data.get("versions_table"),
102
+ components_table=data.get("components_table"),
103
+ component_configs_table=data.get("component_configs_table"),
104
+ component_links_table=data.get("component_links_table"),
105
+ id=data.get("id"),
106
+ )
40
107
 
41
108
  @abstractmethod
42
109
  def table_exists(self, table_name: str) -> bool:
@@ -46,6 +113,24 @@ class BaseDb(ABC):
46
113
  """Create all tables for this database."""
47
114
  pass
48
115
 
116
+ def close(self) -> None:
117
+ """Close database connections and release resources.
118
+
119
+ Override in subclasses to properly dispose of connection pools.
120
+ Should be called during application shutdown.
121
+ """
122
+ pass
123
+
124
+ # --- Schema Version ---
125
+ @abstractmethod
126
+ def get_latest_schema_version(self, table_name: str):
127
+ raise NotImplementedError
128
+
129
+ @abstractmethod
130
+ def upsert_schema_version(self, table_name: str, version: str):
131
+ """Upsert the schema version into the database."""
132
+ raise NotImplementedError
133
+
49
134
  # --- Sessions ---
50
135
  @abstractmethod
51
136
  def delete_session(self, session_id: str) -> bool:
@@ -284,6 +369,159 @@ class BaseDb(ABC):
284
369
  ) -> Optional[Union[EvalRunRecord, Dict[str, Any]]]:
285
370
  raise NotImplementedError
286
371
 
372
+ # --- Traces ---
373
+ @abstractmethod
374
+ def upsert_trace(self, trace: "Trace") -> None:
375
+ """Create or update a single trace record in the database.
376
+
377
+ Args:
378
+ trace: The Trace object to store (one per trace_id).
379
+ """
380
+ raise NotImplementedError
381
+
382
+ @abstractmethod
383
+ def get_trace(
384
+ self,
385
+ trace_id: Optional[str] = None,
386
+ run_id: Optional[str] = None,
387
+ session_id: Optional[str] = None,
388
+ user_id: Optional[str] = None,
389
+ agent_id: Optional[str] = None,
390
+ ):
391
+ """Get a single trace by trace_id or other filters.
392
+
393
+ Args:
394
+ trace_id: The unique trace identifier.
395
+ run_id: Filter by run ID (returns first match).
396
+ session_id: Filter by session ID (returns first match).
397
+ user_id: Filter by user ID (returns first match).
398
+ agent_id: Filter by agent ID (returns first match).
399
+
400
+ Returns:
401
+ Optional[Trace]: The trace if found, None otherwise.
402
+
403
+ Note:
404
+ If multiple filters are provided, trace_id takes precedence.
405
+ For other filters, the most recent trace is returned.
406
+ """
407
+ raise NotImplementedError
408
+
409
+ @abstractmethod
410
+ def get_traces(
411
+ self,
412
+ run_id: Optional[str] = None,
413
+ session_id: Optional[str] = None,
414
+ user_id: Optional[str] = None,
415
+ agent_id: Optional[str] = None,
416
+ team_id: Optional[str] = None,
417
+ workflow_id: Optional[str] = None,
418
+ status: Optional[str] = None,
419
+ start_time: Optional[datetime] = None,
420
+ end_time: Optional[datetime] = None,
421
+ limit: Optional[int] = 20,
422
+ page: Optional[int] = 1,
423
+ ) -> tuple[List, int]:
424
+ """Get traces matching the provided filters with pagination.
425
+
426
+ Args:
427
+ run_id: Filter by run ID.
428
+ session_id: Filter by session ID.
429
+ user_id: Filter by user ID.
430
+ agent_id: Filter by agent ID.
431
+ team_id: Filter by team ID.
432
+ workflow_id: Filter by workflow ID.
433
+ status: Filter by status (OK, ERROR).
434
+ start_time: Filter traces starting after this datetime.
435
+ end_time: Filter traces ending before this datetime.
436
+ limit: Maximum number of traces to return per page.
437
+ page: Page number (1-indexed).
438
+
439
+ Returns:
440
+ tuple[List[Trace], int]: Tuple of (list of matching traces with datetime fields, total count).
441
+ """
442
+ raise NotImplementedError
443
+
444
+ @abstractmethod
445
+ def get_trace_stats(
446
+ self,
447
+ user_id: Optional[str] = None,
448
+ agent_id: Optional[str] = None,
449
+ team_id: Optional[str] = None,
450
+ workflow_id: Optional[str] = None,
451
+ start_time: Optional[datetime] = None,
452
+ end_time: Optional[datetime] = None,
453
+ limit: Optional[int] = 20,
454
+ page: Optional[int] = 1,
455
+ ) -> tuple[List[Dict[str, Any]], int]:
456
+ """Get trace statistics grouped by session.
457
+
458
+ Args:
459
+ user_id: Filter by user ID.
460
+ agent_id: Filter by agent ID.
461
+ team_id: Filter by team ID.
462
+ workflow_id: Filter by workflow ID.
463
+ start_time: Filter sessions with traces created after this datetime.
464
+ end_time: Filter sessions with traces created before this datetime.
465
+ limit: Maximum number of sessions to return per page.
466
+ page: Page number (1-indexed).
467
+
468
+ Returns:
469
+ tuple[List[Dict], int]: Tuple of (list of session stats dicts, total count).
470
+ Each dict contains: session_id, user_id, agent_id, team_id, total_traces,
471
+ first_trace_at (datetime), last_trace_at (datetime).
472
+ """
473
+ raise NotImplementedError
474
+
475
+ # --- Spans ---
476
+ @abstractmethod
477
+ def create_span(self, span: "Span") -> None:
478
+ """Create a single span in the database.
479
+
480
+ Args:
481
+ span: The Span object to store.
482
+ """
483
+ raise NotImplementedError
484
+
485
+ @abstractmethod
486
+ def create_spans(self, spans: List) -> None:
487
+ """Create multiple spans in the database as a batch.
488
+
489
+ Args:
490
+ spans: List of Span objects to store.
491
+ """
492
+ raise NotImplementedError
493
+
494
+ @abstractmethod
495
+ def get_span(self, span_id: str):
496
+ """Get a single span by its span_id.
497
+
498
+ Args:
499
+ span_id: The unique span identifier.
500
+
501
+ Returns:
502
+ Optional[Span]: The span if found, None otherwise.
503
+ """
504
+ raise NotImplementedError
505
+
506
+ @abstractmethod
507
+ def get_spans(
508
+ self,
509
+ trace_id: Optional[str] = None,
510
+ parent_span_id: Optional[str] = None,
511
+ limit: Optional[int] = 1000,
512
+ ) -> List:
513
+ """Get spans matching the provided filters.
514
+
515
+ Args:
516
+ trace_id: Filter by trace ID.
517
+ parent_span_id: Filter by parent span ID.
518
+ limit: Maximum number of spans to return.
519
+
520
+ Returns:
521
+ List[Span]: List of matching spans.
522
+ """
523
+ raise NotImplementedError
524
+
287
525
  # --- Cultural Knowledge ---
288
526
  @abstractmethod
289
527
  def clear_cultural_knowledge(self) -> None:
@@ -314,6 +552,397 @@ class BaseDb(ABC):
314
552
  def upsert_cultural_knowledge(self, cultural_knowledge: CulturalKnowledge) -> Optional[CulturalKnowledge]:
315
553
  raise NotImplementedError
316
554
 
555
+ # --- Components (Optional) ---
556
+ # These methods are optional. Override in subclasses to enable component persistence.
557
+ def get_component(
558
+ self,
559
+ component_id: str,
560
+ component_type: Optional[ComponentType] = None,
561
+ ) -> Optional[Dict[str, Any]]:
562
+ """Get a component by ID.
563
+
564
+ Args:
565
+ component_id: The component ID.
566
+ component_type: Optional filter by type (agent|team|workflow).
567
+
568
+ Returns:
569
+ Component dictionary or None if not found.
570
+ """
571
+ raise NotImplementedError
572
+
573
+ def upsert_component(
574
+ self,
575
+ component_id: str,
576
+ component_type: Optional[ComponentType] = None,
577
+ name: Optional[str] = None,
578
+ description: Optional[str] = None,
579
+ metadata: Optional[Dict[str, Any]] = None,
580
+ ) -> Dict[str, Any]:
581
+ """Create or update a component.
582
+
583
+ Args:
584
+ component_id: Unique identifier.
585
+ component_type: Type (agent|team|workflow). Required for create, optional for update.
586
+ name: Display name.
587
+ description: Optional description.
588
+ metadata: Optional metadata dict.
589
+
590
+ Returns:
591
+ Created/updated component dictionary.
592
+
593
+ Raises:
594
+ ValueError: If creating and component_type is not provided.
595
+ """
596
+ raise NotImplementedError
597
+
598
+ def delete_component(
599
+ self,
600
+ component_id: str,
601
+ hard_delete: bool = False,
602
+ ) -> bool:
603
+ """Delete a component and all its configs/links.
604
+
605
+ Args:
606
+ component_id: The component ID.
607
+ hard_delete: If True, permanently delete. Otherwise soft-delete.
608
+
609
+ Returns:
610
+ True if deleted, False if not found or already deleted.
611
+ """
612
+ raise NotImplementedError
613
+
614
+ def list_components(
615
+ self,
616
+ component_type: Optional[ComponentType] = None,
617
+ include_deleted: bool = False,
618
+ limit: int = 20,
619
+ offset: int = 0,
620
+ ) -> Tuple[List[Dict[str, Any]], int]:
621
+ """List components with pagination.
622
+
623
+ Args:
624
+ component_type: Filter by type (agent|team|workflow).
625
+ include_deleted: Include soft-deleted components.
626
+ limit: Maximum number of items to return.
627
+ offset: Number of items to skip.
628
+
629
+ Returns:
630
+ Tuple of (list of component dicts, total count).
631
+ """
632
+ raise NotImplementedError
633
+
634
+ def create_component_with_config(
635
+ self,
636
+ component_id: str,
637
+ component_type: ComponentType,
638
+ name: Optional[str],
639
+ config: Dict[str, Any],
640
+ description: Optional[str] = None,
641
+ metadata: Optional[Dict[str, Any]] = None,
642
+ label: Optional[str] = None,
643
+ stage: str = "draft",
644
+ notes: Optional[str] = None,
645
+ links: Optional[List[Dict[str, Any]]] = None,
646
+ ) -> Tuple[Dict[str, Any], Dict[str, Any]]:
647
+ """Create a component with its initial config atomically.
648
+
649
+ Args:
650
+ component_id: Unique identifier.
651
+ component_type: Type (agent|team|workflow).
652
+ name: Display name.
653
+ config: The config data.
654
+ description: Optional description.
655
+ metadata: Optional metadata dict.
656
+ label: Optional config label.
657
+ stage: "draft" or "published".
658
+ notes: Optional notes.
659
+ links: Optional list of links. Each must have child_version set.
660
+
661
+ Returns:
662
+ Tuple of (component dict, config dict).
663
+
664
+ Raises:
665
+ ValueError: If component already exists, invalid stage, or link missing child_version.
666
+ """
667
+ raise NotImplementedError
668
+
669
+ # --- Component Configs (Optional) ---
670
+ def get_config(
671
+ self,
672
+ component_id: str,
673
+ version: Optional[int] = None,
674
+ label: Optional[str] = None,
675
+ ) -> Optional[Dict[str, Any]]:
676
+ """Get a config by component ID and version or label.
677
+
678
+ Args:
679
+ component_id: The component ID.
680
+ version: Specific version number. If None, uses current.
681
+ label: Config label to lookup. Ignored if version is provided.
682
+
683
+ Returns:
684
+ Config dictionary or None if not found.
685
+ """
686
+ raise NotImplementedError
687
+
688
+ def upsert_config(
689
+ self,
690
+ component_id: str,
691
+ config: Optional[Dict[str, Any]] = None,
692
+ version: Optional[int] = None,
693
+ label: Optional[str] = None,
694
+ stage: Optional[str] = None,
695
+ notes: Optional[str] = None,
696
+ links: Optional[List[Dict[str, Any]]] = None,
697
+ ) -> Dict[str, Any]:
698
+ """Create or update a config version for a component.
699
+
700
+ Rules:
701
+ - Draft configs can be edited freely
702
+ - Published configs are immutable
703
+ - Publishing a config automatically sets it as current_version
704
+
705
+ Args:
706
+ component_id: The component ID.
707
+ config: The config data. Required for create, optional for update.
708
+ version: If None, creates new version. If provided, updates that version.
709
+ label: Optional human-readable label.
710
+ stage: "draft" or "published". Defaults to "draft" for new configs.
711
+ notes: Optional notes.
712
+ links: Optional list of links. Each link must have child_version set.
713
+
714
+ Returns:
715
+ Created/updated config dictionary.
716
+
717
+ Raises:
718
+ ValueError: If component doesn't exist, version not found, label conflict,
719
+ or attempting to update a published config.
720
+ """
721
+ raise NotImplementedError
722
+
723
+ def delete_config(
724
+ self,
725
+ component_id: str,
726
+ version: int,
727
+ ) -> bool:
728
+ """Delete a specific config version.
729
+
730
+ Only draft configs can be deleted. Published configs are immutable.
731
+ Cannot delete the current version.
732
+
733
+ Args:
734
+ component_id: The component ID.
735
+ version: The version to delete.
736
+
737
+ Returns:
738
+ True if deleted, False if not found.
739
+
740
+ Raises:
741
+ ValueError: If attempting to delete a published or current config.
742
+ """
743
+ raise NotImplementedError
744
+
745
+ def list_configs(
746
+ self,
747
+ component_id: str,
748
+ include_config: bool = False,
749
+ ) -> List[Dict[str, Any]]:
750
+ """List all config versions for a component.
751
+
752
+ Args:
753
+ component_id: The component ID.
754
+ include_config: If True, include full config blob. Otherwise just metadata.
755
+
756
+ Returns:
757
+ List of config dictionaries, newest first.
758
+ Returns empty list if component not found or deleted.
759
+ """
760
+ raise NotImplementedError
761
+
762
+ def set_current_version(
763
+ self,
764
+ component_id: str,
765
+ version: int,
766
+ ) -> bool:
767
+ """Set a specific published version as current.
768
+
769
+ Only published configs can be set as current. This is used for
770
+ rollback scenarios where you want to switch to a previous
771
+ published version.
772
+
773
+ Args:
774
+ component_id: The component ID.
775
+ version: The version to set as current (must be published).
776
+
777
+ Returns:
778
+ True if successful, False if component or version not found.
779
+
780
+ Raises:
781
+ ValueError: If attempting to set a draft config as current.
782
+ """
783
+ raise NotImplementedError
784
+
785
+ # --- Component Links (Optional) ---
786
+ def get_links(
787
+ self,
788
+ component_id: str,
789
+ version: int,
790
+ link_kind: Optional[str] = None,
791
+ ) -> List[Dict[str, Any]]:
792
+ """Get links for a config version.
793
+
794
+ Args:
795
+ component_id: The component ID.
796
+ version: The config version.
797
+ link_kind: Optional filter by link kind (member|step).
798
+
799
+ Returns:
800
+ List of link dictionaries, ordered by position.
801
+ """
802
+ raise NotImplementedError
803
+
804
+ def get_dependents(
805
+ self,
806
+ component_id: str,
807
+ version: Optional[int] = None,
808
+ ) -> List[Dict[str, Any]]:
809
+ """Find all components that reference this component.
810
+
811
+ Args:
812
+ component_id: The component ID to find dependents of.
813
+ version: Optional specific version. If None, finds links to any version.
814
+
815
+ Returns:
816
+ List of link dictionaries showing what depends on this component.
817
+ """
818
+ raise NotImplementedError
819
+
820
+ def load_component_graph(
821
+ self,
822
+ component_id: str,
823
+ version: Optional[int] = None,
824
+ label: Optional[str] = None,
825
+ ) -> Optional[Dict[str, Any]]:
826
+ """Load a component with its full resolved graph.
827
+
828
+ Handles cycles by returning a stub with cycle_detected=True.
829
+
830
+ Args:
831
+ component_id: The component ID.
832
+ version: Specific version or None for current.
833
+ label: Optional label of the component.
834
+
835
+ Returns:
836
+ Dictionary with component, config, children, and resolved_versions.
837
+ Returns None if component not found.
838
+ """
839
+ raise NotImplementedError
840
+
841
+ # --- Learnings ---
842
+ @abstractmethod
843
+ def get_learning(
844
+ self,
845
+ learning_type: str,
846
+ user_id: Optional[str] = None,
847
+ agent_id: Optional[str] = None,
848
+ team_id: Optional[str] = None,
849
+ session_id: Optional[str] = None,
850
+ namespace: Optional[str] = None,
851
+ entity_id: Optional[str] = None,
852
+ entity_type: Optional[str] = None,
853
+ ) -> Optional[Dict[str, Any]]:
854
+ """Retrieve a learning record.
855
+
856
+ Args:
857
+ learning_type: Type of learning ('user_profile', 'session_context', etc.)
858
+ user_id: Filter by user ID.
859
+ agent_id: Filter by agent ID.
860
+ team_id: Filter by team ID.
861
+ session_id: Filter by session ID.
862
+ namespace: Filter by namespace ('user', 'global', or custom).
863
+ entity_id: Filter by entity ID (for entity-specific learnings).
864
+ entity_type: Filter by entity type ('person', 'company', etc.).
865
+
866
+ Returns:
867
+ Dict with 'content' key containing the learning data, or None.
868
+ """
869
+ raise NotImplementedError
870
+
871
+ @abstractmethod
872
+ def upsert_learning(
873
+ self,
874
+ id: str,
875
+ learning_type: str,
876
+ content: Dict[str, Any],
877
+ user_id: Optional[str] = None,
878
+ agent_id: Optional[str] = None,
879
+ team_id: Optional[str] = None,
880
+ session_id: Optional[str] = None,
881
+ namespace: Optional[str] = None,
882
+ entity_id: Optional[str] = None,
883
+ entity_type: Optional[str] = None,
884
+ metadata: Optional[Dict[str, Any]] = None,
885
+ ) -> None:
886
+ """Insert or update a learning record.
887
+
888
+ Args:
889
+ id: Unique identifier for the learning.
890
+ learning_type: Type of learning ('user_profile', 'session_context', etc.)
891
+ content: The learning content as a dict.
892
+ user_id: Associated user ID.
893
+ agent_id: Associated agent ID.
894
+ team_id: Associated team ID.
895
+ session_id: Associated session ID.
896
+ namespace: Namespace for scoping ('user', 'global', or custom).
897
+ entity_id: Associated entity ID (for entity-specific learnings).
898
+ entity_type: Entity type ('person', 'company', etc.).
899
+ metadata: Optional metadata.
900
+ """
901
+ raise NotImplementedError
902
+
903
+ @abstractmethod
904
+ def delete_learning(self, id: str) -> bool:
905
+ """Delete a learning record.
906
+
907
+ Args:
908
+ id: The learning ID to delete.
909
+
910
+ Returns:
911
+ True if deleted, False otherwise.
912
+ """
913
+ raise NotImplementedError
914
+
915
+ @abstractmethod
916
+ def get_learnings(
917
+ self,
918
+ learning_type: Optional[str] = None,
919
+ user_id: Optional[str] = None,
920
+ agent_id: Optional[str] = None,
921
+ team_id: Optional[str] = None,
922
+ session_id: Optional[str] = None,
923
+ namespace: Optional[str] = None,
924
+ entity_id: Optional[str] = None,
925
+ entity_type: Optional[str] = None,
926
+ limit: Optional[int] = None,
927
+ ) -> List[Dict[str, Any]]:
928
+ """Get multiple learning records.
929
+
930
+ Args:
931
+ learning_type: Filter by learning type.
932
+ user_id: Filter by user ID.
933
+ agent_id: Filter by agent ID.
934
+ team_id: Filter by team ID.
935
+ session_id: Filter by session ID.
936
+ namespace: Filter by namespace ('user', 'global', or custom).
937
+ entity_id: Filter by entity ID (for entity-specific learnings).
938
+ entity_type: Filter by entity type ('person', 'company', etc.).
939
+ limit: Maximum number of records to return.
940
+
941
+ Returns:
942
+ List of learning records.
943
+ """
944
+ raise NotImplementedError
945
+
317
946
 
318
947
  class AsyncBaseDb(ABC):
319
948
  """Base abstract class for all our async database implementations."""
@@ -326,7 +955,11 @@ class AsyncBaseDb(ABC):
326
955
  metrics_table: Optional[str] = None,
327
956
  eval_table: Optional[str] = None,
328
957
  knowledge_table: Optional[str] = None,
958
+ traces_table: Optional[str] = None,
959
+ spans_table: Optional[str] = None,
329
960
  culture_table: Optional[str] = None,
961
+ versions_table: Optional[str] = None,
962
+ learnings_table: Optional[str] = None,
330
963
  ):
331
964
  self.id = id or str(uuid4())
332
965
  self.session_table_name = session_table or "agno_sessions"
@@ -334,7 +967,23 @@ class AsyncBaseDb(ABC):
334
967
  self.metrics_table_name = metrics_table or "agno_metrics"
335
968
  self.eval_table_name = eval_table or "agno_eval_runs"
336
969
  self.knowledge_table_name = knowledge_table or "agno_knowledge"
970
+ self.trace_table_name = traces_table or "agno_traces"
971
+ self.span_table_name = spans_table or "agno_spans"
337
972
  self.culture_table_name = culture_table or "agno_culture"
973
+ self.versions_table_name = versions_table or "agno_schema_versions"
974
+ self.learnings_table_name = learnings_table or "agno_learnings"
975
+
976
+ async def _create_all_tables(self) -> None:
977
+ """Create all tables for this database. Override in subclasses."""
978
+ pass
979
+
980
+ async def close(self) -> None:
981
+ """Close database connections and release resources.
982
+
983
+ Override in subclasses to properly dispose of connection pools.
984
+ Should be called during application shutdown.
985
+ """
986
+ pass
338
987
 
339
988
  @abstractmethod
340
989
  async def table_exists(self, table_name: str) -> bool:
@@ -351,6 +1000,15 @@ class AsyncBaseDb(ABC):
351
1000
  """
352
1001
  raise NotImplementedError
353
1002
 
1003
+ @abstractmethod
1004
+ async def get_latest_schema_version(self, table_name: str) -> str:
1005
+ raise NotImplementedError
1006
+
1007
+ @abstractmethod
1008
+ async def upsert_schema_version(self, table_name: str, version: str):
1009
+ """Upsert the schema version into the database."""
1010
+ raise NotImplementedError
1011
+
354
1012
  # --- Sessions ---
355
1013
  @abstractmethod
356
1014
  async def delete_session(self, session_id: str) -> bool:
@@ -373,7 +1031,7 @@ class AsyncBaseDb(ABC):
373
1031
  @abstractmethod
374
1032
  async def get_sessions(
375
1033
  self,
376
- session_type: SessionType,
1034
+ session_type: Optional[SessionType] = None,
377
1035
  user_id: Optional[str] = None,
378
1036
  component_id: Optional[str] = None,
379
1037
  session_name: Optional[str] = None,
@@ -567,6 +1225,159 @@ class AsyncBaseDb(ABC):
567
1225
  ) -> Optional[Union[EvalRunRecord, Dict[str, Any]]]:
568
1226
  raise NotImplementedError
569
1227
 
1228
+ # --- Traces ---
1229
+ @abstractmethod
1230
+ async def upsert_trace(self, trace) -> None:
1231
+ """Create or update a single trace record in the database.
1232
+
1233
+ Args:
1234
+ trace: The Trace object to update (one per trace_id).
1235
+ """
1236
+ raise NotImplementedError
1237
+
1238
+ @abstractmethod
1239
+ async def get_trace(
1240
+ self,
1241
+ trace_id: Optional[str] = None,
1242
+ run_id: Optional[str] = None,
1243
+ session_id: Optional[str] = None,
1244
+ user_id: Optional[str] = None,
1245
+ agent_id: Optional[str] = None,
1246
+ ):
1247
+ """Get a single trace by trace_id or other filters.
1248
+
1249
+ Args:
1250
+ trace_id: The unique trace identifier.
1251
+ run_id: Filter by run ID (returns first match).
1252
+ session_id: Filter by session ID (returns first match).
1253
+ user_id: Filter by user ID (returns first match).
1254
+ agent_id: Filter by agent ID (returns first match).
1255
+
1256
+ Returns:
1257
+ Optional[Trace]: The trace if found, None otherwise.
1258
+
1259
+ Note:
1260
+ If multiple filters are provided, trace_id takes precedence.
1261
+ For other filters, the most recent trace is returned.
1262
+ """
1263
+ raise NotImplementedError
1264
+
1265
+ @abstractmethod
1266
+ async def get_traces(
1267
+ self,
1268
+ run_id: Optional[str] = None,
1269
+ session_id: Optional[str] = None,
1270
+ user_id: Optional[str] = None,
1271
+ agent_id: Optional[str] = None,
1272
+ team_id: Optional[str] = None,
1273
+ workflow_id: Optional[str] = None,
1274
+ status: Optional[str] = None,
1275
+ start_time: Optional[datetime] = None,
1276
+ end_time: Optional[datetime] = None,
1277
+ limit: Optional[int] = 20,
1278
+ page: Optional[int] = 1,
1279
+ ) -> tuple[List, int]:
1280
+ """Get traces matching the provided filters with pagination.
1281
+
1282
+ Args:
1283
+ run_id: Filter by run ID.
1284
+ session_id: Filter by session ID.
1285
+ user_id: Filter by user ID.
1286
+ agent_id: Filter by agent ID.
1287
+ team_id: Filter by team ID.
1288
+ workflow_id: Filter by workflow ID.
1289
+ status: Filter by status (OK, ERROR).
1290
+ start_time: Filter traces starting after this datetime.
1291
+ end_time: Filter traces ending before this datetime.
1292
+ limit: Maximum number of traces to return per page.
1293
+ page: Page number (1-indexed).
1294
+
1295
+ Returns:
1296
+ tuple[List[Trace], int]: Tuple of (list of matching traces with datetime fields, total count).
1297
+ """
1298
+ raise NotImplementedError
1299
+
1300
+ @abstractmethod
1301
+ async def get_trace_stats(
1302
+ self,
1303
+ user_id: Optional[str] = None,
1304
+ agent_id: Optional[str] = None,
1305
+ team_id: Optional[str] = None,
1306
+ workflow_id: Optional[str] = None,
1307
+ start_time: Optional[datetime] = None,
1308
+ end_time: Optional[datetime] = None,
1309
+ limit: Optional[int] = 20,
1310
+ page: Optional[int] = 1,
1311
+ ) -> tuple[List[Dict[str, Any]], int]:
1312
+ """Get trace statistics grouped by session.
1313
+
1314
+ Args:
1315
+ user_id: Filter by user ID.
1316
+ agent_id: Filter by agent ID.
1317
+ team_id: Filter by team ID.
1318
+ workflow_id: Filter by workflow ID.
1319
+ start_time: Filter sessions with traces created after this datetime.
1320
+ end_time: Filter sessions with traces created before this datetime.
1321
+ limit: Maximum number of sessions to return per page.
1322
+ page: Page number (1-indexed).
1323
+
1324
+ Returns:
1325
+ tuple[List[Dict], int]: Tuple of (list of session stats dicts, total count).
1326
+ Each dict contains: session_id, user_id, agent_id, team_id, total_traces,
1327
+ first_trace_at (datetime), last_trace_at (datetime).
1328
+ """
1329
+ raise NotImplementedError
1330
+
1331
+ # --- Spans ---
1332
+ @abstractmethod
1333
+ async def create_span(self, span) -> None:
1334
+ """Create a single span in the database.
1335
+
1336
+ Args:
1337
+ span: The Span object to store.
1338
+ """
1339
+ raise NotImplementedError
1340
+
1341
+ @abstractmethod
1342
+ async def create_spans(self, spans: List) -> None:
1343
+ """Create multiple spans in the database as a batch.
1344
+
1345
+ Args:
1346
+ spans: List of Span objects to store.
1347
+ """
1348
+ raise NotImplementedError
1349
+
1350
+ @abstractmethod
1351
+ async def get_span(self, span_id: str):
1352
+ """Get a single span by its span_id.
1353
+
1354
+ Args:
1355
+ span_id: The unique span identifier.
1356
+
1357
+ Returns:
1358
+ Optional[Span]: The span if found, None otherwise.
1359
+ """
1360
+ raise NotImplementedError
1361
+
1362
+ @abstractmethod
1363
+ async def get_spans(
1364
+ self,
1365
+ trace_id: Optional[str] = None,
1366
+ parent_span_id: Optional[str] = None,
1367
+ limit: Optional[int] = 1000,
1368
+ ) -> List:
1369
+ """Get spans matching the provided filters.
1370
+
1371
+ Args:
1372
+ trace_id: Filter by trace ID.
1373
+ parent_span_id: Filter by parent span ID.
1374
+ limit: Maximum number of spans to return.
1375
+
1376
+ Returns:
1377
+ List[Span]: List of matching spans.
1378
+ """
1379
+ raise NotImplementedError
1380
+
570
1381
  # --- Cultural Notions ---
571
1382
  @abstractmethod
572
1383
  async def clear_cultural_knowledge(self) -> None:
@@ -577,22 +1388,132 @@ class AsyncBaseDb(ABC):
577
1388
  raise NotImplementedError
578
1389
 
579
1390
  @abstractmethod
580
- async def get_cultural_knowledge(self, id: str) -> Optional[CulturalKnowledge]:
1391
+ async def get_cultural_knowledge(
1392
+ self, id: str, deserialize: Optional[bool] = True
1393
+ ) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
581
1394
  raise NotImplementedError
582
1395
 
583
1396
  @abstractmethod
584
1397
  async def get_all_cultural_knowledge(
585
1398
  self,
1399
+ agent_id: Optional[str] = None,
1400
+ team_id: Optional[str] = None,
586
1401
  name: Optional[str] = None,
587
1402
  limit: Optional[int] = None,
588
1403
  page: Optional[int] = None,
589
1404
  sort_by: Optional[str] = None,
590
1405
  sort_order: Optional[str] = None,
1406
+ deserialize: Optional[bool] = True,
1407
+ ) -> Union[List[CulturalKnowledge], Tuple[List[Dict[str, Any]], int]]:
1408
+ raise NotImplementedError
1409
+
1410
+ @abstractmethod
1411
+ async def upsert_cultural_knowledge(
1412
+ self, cultural_knowledge: CulturalKnowledge, deserialize: Optional[bool] = True
1413
+ ) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
1414
+ raise NotImplementedError
1415
+
1416
+ # --- Learnings ---
1417
+ @abstractmethod
1418
+ async def get_learning(
1419
+ self,
1420
+ learning_type: str,
1421
+ user_id: Optional[str] = None,
591
1422
  agent_id: Optional[str] = None,
592
1423
  team_id: Optional[str] = None,
593
- ) -> Optional[List[CulturalKnowledge]]:
1424
+ session_id: Optional[str] = None,
1425
+ namespace: Optional[str] = None,
1426
+ entity_id: Optional[str] = None,
1427
+ entity_type: Optional[str] = None,
1428
+ ) -> Optional[Dict[str, Any]]:
1429
+ """Async retrieve a learning record.
1430
+
1431
+ Args:
1432
+ learning_type: Type of learning ('user_profile', 'session_context', etc.)
1433
+ user_id: Filter by user ID.
1434
+ agent_id: Filter by agent ID.
1435
+ team_id: Filter by team ID.
1436
+ session_id: Filter by session ID.
1437
+ namespace: Filter by namespace ('user', 'global', or custom).
1438
+ entity_id: Filter by entity ID (for entity-specific learnings).
1439
+ entity_type: Filter by entity type ('person', 'company', etc.).
1440
+
1441
+ Returns:
1442
+ Dict with 'content' key containing the learning data, or None.
1443
+ """
1444
+ raise NotImplementedError
1445
+
1446
+ @abstractmethod
1447
+ async def upsert_learning(
1448
+ self,
1449
+ id: str,
1450
+ learning_type: str,
1451
+ content: Dict[str, Any],
1452
+ user_id: Optional[str] = None,
1453
+ agent_id: Optional[str] = None,
1454
+ team_id: Optional[str] = None,
1455
+ session_id: Optional[str] = None,
1456
+ namespace: Optional[str] = None,
1457
+ entity_id: Optional[str] = None,
1458
+ entity_type: Optional[str] = None,
1459
+ metadata: Optional[Dict[str, Any]] = None,
1460
+ ) -> None:
1461
+ """Async insert or update a learning record.
1462
+
1463
+ Args:
1464
+ id: Unique identifier for the learning.
1465
+ learning_type: Type of learning ('user_profile', 'session_context', etc.)
1466
+ content: The learning content as a dict.
1467
+ user_id: Associated user ID.
1468
+ agent_id: Associated agent ID.
1469
+ team_id: Associated team ID.
1470
+ session_id: Associated session ID.
1471
+ namespace: Namespace for scoping ('user', 'global', or custom).
1472
+ entity_id: Associated entity ID (for entity-specific learnings).
1473
+ entity_type: Entity type ('person', 'company', etc.).
1474
+ metadata: Optional metadata.
1475
+ """
1476
+ raise NotImplementedError
1477
+
1478
+ @abstractmethod
1479
+ async def delete_learning(self, id: str) -> bool:
1480
+ """Async delete a learning record.
1481
+
1482
+ Args:
1483
+ id: The learning ID to delete.
1484
+
1485
+ Returns:
1486
+ True if deleted, False otherwise.
1487
+ """
594
1488
  raise NotImplementedError
595
1489
 
596
1490
  @abstractmethod
597
- async def upsert_cultural_knowledge(self, cultural_knowledge: CulturalKnowledge) -> Optional[CulturalKnowledge]:
1491
+ async def get_learnings(
1492
+ self,
1493
+ learning_type: Optional[str] = None,
1494
+ user_id: Optional[str] = None,
1495
+ agent_id: Optional[str] = None,
1496
+ team_id: Optional[str] = None,
1497
+ session_id: Optional[str] = None,
1498
+ namespace: Optional[str] = None,
1499
+ entity_id: Optional[str] = None,
1500
+ entity_type: Optional[str] = None,
1501
+ limit: Optional[int] = None,
1502
+ ) -> List[Dict[str, Any]]:
1503
+ """Async get multiple learning records.
1504
+
1505
+ Args:
1506
+ learning_type: Filter by learning type.
1507
+ user_id: Filter by user ID.
1508
+ agent_id: Filter by agent ID.
1509
+ team_id: Filter by team ID.
1510
+ session_id: Filter by session ID.
1511
+ namespace: Filter by namespace ('user', 'global', or custom).
1512
+ entity_id: Filter by entity ID (for entity-specific learnings).
1513
+ entity_type: Filter by entity type ('person', 'company', etc.).
1514
+ limit: Maximum number of records to return.
1515
+
1516
+ Returns:
1517
+ List of learning records.
1518
+ """
598
1519
  raise NotImplementedError