agno 2.1.2__py3-none-any.whl → 2.3.13__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 (314) hide show
  1. agno/agent/agent.py +5540 -2273
  2. agno/api/api.py +2 -0
  3. agno/api/os.py +1 -1
  4. agno/compression/__init__.py +3 -0
  5. agno/compression/manager.py +247 -0
  6. agno/culture/__init__.py +3 -0
  7. agno/culture/manager.py +956 -0
  8. agno/db/async_postgres/__init__.py +3 -0
  9. agno/db/base.py +689 -6
  10. agno/db/dynamo/dynamo.py +933 -37
  11. agno/db/dynamo/schemas.py +174 -10
  12. agno/db/dynamo/utils.py +63 -4
  13. agno/db/firestore/firestore.py +831 -9
  14. agno/db/firestore/schemas.py +51 -0
  15. agno/db/firestore/utils.py +102 -4
  16. agno/db/gcs_json/gcs_json_db.py +660 -12
  17. agno/db/gcs_json/utils.py +60 -26
  18. agno/db/in_memory/in_memory_db.py +287 -14
  19. agno/db/in_memory/utils.py +60 -2
  20. agno/db/json/json_db.py +590 -14
  21. agno/db/json/utils.py +60 -26
  22. agno/db/migrations/manager.py +199 -0
  23. agno/db/migrations/v1_to_v2.py +43 -13
  24. agno/db/migrations/versions/__init__.py +0 -0
  25. agno/db/migrations/versions/v2_3_0.py +938 -0
  26. agno/db/mongo/__init__.py +15 -1
  27. agno/db/mongo/async_mongo.py +2760 -0
  28. agno/db/mongo/mongo.py +879 -11
  29. agno/db/mongo/schemas.py +42 -0
  30. agno/db/mongo/utils.py +80 -8
  31. agno/db/mysql/__init__.py +2 -1
  32. agno/db/mysql/async_mysql.py +2912 -0
  33. agno/db/mysql/mysql.py +946 -68
  34. agno/db/mysql/schemas.py +72 -10
  35. agno/db/mysql/utils.py +198 -7
  36. agno/db/postgres/__init__.py +2 -1
  37. agno/db/postgres/async_postgres.py +2579 -0
  38. agno/db/postgres/postgres.py +942 -57
  39. agno/db/postgres/schemas.py +81 -18
  40. agno/db/postgres/utils.py +164 -2
  41. agno/db/redis/redis.py +671 -7
  42. agno/db/redis/schemas.py +50 -0
  43. agno/db/redis/utils.py +65 -7
  44. agno/db/schemas/__init__.py +2 -1
  45. agno/db/schemas/culture.py +120 -0
  46. agno/db/schemas/evals.py +1 -0
  47. agno/db/schemas/memory.py +17 -2
  48. agno/db/singlestore/schemas.py +63 -0
  49. agno/db/singlestore/singlestore.py +949 -83
  50. agno/db/singlestore/utils.py +60 -2
  51. agno/db/sqlite/__init__.py +2 -1
  52. agno/db/sqlite/async_sqlite.py +2911 -0
  53. agno/db/sqlite/schemas.py +62 -0
  54. agno/db/sqlite/sqlite.py +965 -46
  55. agno/db/sqlite/utils.py +169 -8
  56. agno/db/surrealdb/__init__.py +3 -0
  57. agno/db/surrealdb/metrics.py +292 -0
  58. agno/db/surrealdb/models.py +334 -0
  59. agno/db/surrealdb/queries.py +71 -0
  60. agno/db/surrealdb/surrealdb.py +1908 -0
  61. agno/db/surrealdb/utils.py +147 -0
  62. agno/db/utils.py +2 -0
  63. agno/eval/__init__.py +10 -0
  64. agno/eval/accuracy.py +75 -55
  65. agno/eval/agent_as_judge.py +861 -0
  66. agno/eval/base.py +29 -0
  67. agno/eval/performance.py +16 -7
  68. agno/eval/reliability.py +28 -16
  69. agno/eval/utils.py +35 -17
  70. agno/exceptions.py +27 -2
  71. agno/filters.py +354 -0
  72. agno/guardrails/prompt_injection.py +1 -0
  73. agno/hooks/__init__.py +3 -0
  74. agno/hooks/decorator.py +164 -0
  75. agno/integrations/discord/client.py +1 -1
  76. agno/knowledge/chunking/agentic.py +13 -10
  77. agno/knowledge/chunking/fixed.py +4 -1
  78. agno/knowledge/chunking/semantic.py +9 -4
  79. agno/knowledge/chunking/strategy.py +59 -15
  80. agno/knowledge/embedder/fastembed.py +1 -1
  81. agno/knowledge/embedder/nebius.py +1 -1
  82. agno/knowledge/embedder/ollama.py +8 -0
  83. agno/knowledge/embedder/openai.py +8 -8
  84. agno/knowledge/embedder/sentence_transformer.py +6 -2
  85. agno/knowledge/embedder/vllm.py +262 -0
  86. agno/knowledge/knowledge.py +1618 -318
  87. agno/knowledge/reader/base.py +6 -2
  88. agno/knowledge/reader/csv_reader.py +8 -10
  89. agno/knowledge/reader/docx_reader.py +5 -6
  90. agno/knowledge/reader/field_labeled_csv_reader.py +16 -20
  91. agno/knowledge/reader/json_reader.py +5 -4
  92. agno/knowledge/reader/markdown_reader.py +8 -8
  93. agno/knowledge/reader/pdf_reader.py +17 -19
  94. agno/knowledge/reader/pptx_reader.py +101 -0
  95. agno/knowledge/reader/reader_factory.py +32 -3
  96. agno/knowledge/reader/s3_reader.py +3 -3
  97. agno/knowledge/reader/tavily_reader.py +193 -0
  98. agno/knowledge/reader/text_reader.py +22 -10
  99. agno/knowledge/reader/web_search_reader.py +1 -48
  100. agno/knowledge/reader/website_reader.py +10 -10
  101. agno/knowledge/reader/wikipedia_reader.py +33 -1
  102. agno/knowledge/types.py +1 -0
  103. agno/knowledge/utils.py +72 -7
  104. agno/media.py +22 -6
  105. agno/memory/__init__.py +14 -1
  106. agno/memory/manager.py +544 -83
  107. agno/memory/strategies/__init__.py +15 -0
  108. agno/memory/strategies/base.py +66 -0
  109. agno/memory/strategies/summarize.py +196 -0
  110. agno/memory/strategies/types.py +37 -0
  111. agno/models/aimlapi/aimlapi.py +17 -0
  112. agno/models/anthropic/claude.py +515 -40
  113. agno/models/aws/bedrock.py +102 -21
  114. agno/models/aws/claude.py +131 -274
  115. agno/models/azure/ai_foundry.py +41 -19
  116. agno/models/azure/openai_chat.py +39 -8
  117. agno/models/base.py +1249 -525
  118. agno/models/cerebras/cerebras.py +91 -21
  119. agno/models/cerebras/cerebras_openai.py +21 -2
  120. agno/models/cohere/chat.py +40 -6
  121. agno/models/cometapi/cometapi.py +18 -1
  122. agno/models/dashscope/dashscope.py +2 -3
  123. agno/models/deepinfra/deepinfra.py +18 -1
  124. agno/models/deepseek/deepseek.py +69 -3
  125. agno/models/fireworks/fireworks.py +18 -1
  126. agno/models/google/gemini.py +877 -80
  127. agno/models/google/utils.py +22 -0
  128. agno/models/groq/groq.py +51 -18
  129. agno/models/huggingface/huggingface.py +17 -6
  130. agno/models/ibm/watsonx.py +16 -6
  131. agno/models/internlm/internlm.py +18 -1
  132. agno/models/langdb/langdb.py +13 -1
  133. agno/models/litellm/chat.py +44 -9
  134. agno/models/litellm/litellm_openai.py +18 -1
  135. agno/models/message.py +28 -5
  136. agno/models/meta/llama.py +47 -14
  137. agno/models/meta/llama_openai.py +22 -17
  138. agno/models/mistral/mistral.py +8 -4
  139. agno/models/nebius/nebius.py +6 -7
  140. agno/models/nvidia/nvidia.py +20 -3
  141. agno/models/ollama/chat.py +24 -8
  142. agno/models/openai/chat.py +104 -29
  143. agno/models/openai/responses.py +101 -81
  144. agno/models/openrouter/openrouter.py +60 -3
  145. agno/models/perplexity/perplexity.py +17 -1
  146. agno/models/portkey/portkey.py +7 -6
  147. agno/models/requesty/requesty.py +24 -4
  148. agno/models/response.py +73 -2
  149. agno/models/sambanova/sambanova.py +20 -3
  150. agno/models/siliconflow/siliconflow.py +19 -2
  151. agno/models/together/together.py +20 -3
  152. agno/models/utils.py +254 -8
  153. agno/models/vercel/v0.py +20 -3
  154. agno/models/vertexai/__init__.py +0 -0
  155. agno/models/vertexai/claude.py +190 -0
  156. agno/models/vllm/vllm.py +19 -14
  157. agno/models/xai/xai.py +19 -2
  158. agno/os/app.py +549 -152
  159. agno/os/auth.py +190 -3
  160. agno/os/config.py +23 -0
  161. agno/os/interfaces/a2a/router.py +8 -11
  162. agno/os/interfaces/a2a/utils.py +1 -1
  163. agno/os/interfaces/agui/router.py +18 -3
  164. agno/os/interfaces/agui/utils.py +152 -39
  165. agno/os/interfaces/slack/router.py +55 -37
  166. agno/os/interfaces/slack/slack.py +9 -1
  167. agno/os/interfaces/whatsapp/router.py +0 -1
  168. agno/os/interfaces/whatsapp/security.py +3 -1
  169. agno/os/mcp.py +110 -52
  170. agno/os/middleware/__init__.py +2 -0
  171. agno/os/middleware/jwt.py +676 -112
  172. agno/os/router.py +40 -1478
  173. agno/os/routers/agents/__init__.py +3 -0
  174. agno/os/routers/agents/router.py +599 -0
  175. agno/os/routers/agents/schema.py +261 -0
  176. agno/os/routers/evals/evals.py +96 -39
  177. agno/os/routers/evals/schemas.py +65 -33
  178. agno/os/routers/evals/utils.py +80 -10
  179. agno/os/routers/health.py +10 -4
  180. agno/os/routers/knowledge/knowledge.py +196 -38
  181. agno/os/routers/knowledge/schemas.py +82 -22
  182. agno/os/routers/memory/memory.py +279 -52
  183. agno/os/routers/memory/schemas.py +46 -17
  184. agno/os/routers/metrics/metrics.py +20 -8
  185. agno/os/routers/metrics/schemas.py +16 -16
  186. agno/os/routers/session/session.py +462 -34
  187. agno/os/routers/teams/__init__.py +3 -0
  188. agno/os/routers/teams/router.py +512 -0
  189. agno/os/routers/teams/schema.py +257 -0
  190. agno/os/routers/traces/__init__.py +3 -0
  191. agno/os/routers/traces/schemas.py +414 -0
  192. agno/os/routers/traces/traces.py +499 -0
  193. agno/os/routers/workflows/__init__.py +3 -0
  194. agno/os/routers/workflows/router.py +624 -0
  195. agno/os/routers/workflows/schema.py +75 -0
  196. agno/os/schema.py +256 -693
  197. agno/os/scopes.py +469 -0
  198. agno/os/utils.py +514 -36
  199. agno/reasoning/anthropic.py +80 -0
  200. agno/reasoning/gemini.py +73 -0
  201. agno/reasoning/openai.py +5 -0
  202. agno/reasoning/vertexai.py +76 -0
  203. agno/run/__init__.py +6 -0
  204. agno/run/agent.py +155 -32
  205. agno/run/base.py +55 -3
  206. agno/run/requirement.py +181 -0
  207. agno/run/team.py +125 -38
  208. agno/run/workflow.py +72 -18
  209. agno/session/agent.py +102 -89
  210. agno/session/summary.py +56 -15
  211. agno/session/team.py +164 -90
  212. agno/session/workflow.py +405 -40
  213. agno/table.py +10 -0
  214. agno/team/team.py +3974 -1903
  215. agno/tools/dalle.py +2 -4
  216. agno/tools/eleven_labs.py +23 -25
  217. agno/tools/exa.py +21 -16
  218. agno/tools/file.py +153 -23
  219. agno/tools/file_generation.py +16 -10
  220. agno/tools/firecrawl.py +15 -7
  221. agno/tools/function.py +193 -38
  222. agno/tools/gmail.py +238 -14
  223. agno/tools/google_drive.py +271 -0
  224. agno/tools/googlecalendar.py +36 -8
  225. agno/tools/googlesheets.py +20 -5
  226. agno/tools/jira.py +20 -0
  227. agno/tools/mcp/__init__.py +10 -0
  228. agno/tools/mcp/mcp.py +331 -0
  229. agno/tools/mcp/multi_mcp.py +347 -0
  230. agno/tools/mcp/params.py +24 -0
  231. agno/tools/mcp_toolbox.py +3 -3
  232. agno/tools/models/nebius.py +5 -5
  233. agno/tools/models_labs.py +20 -10
  234. agno/tools/nano_banana.py +151 -0
  235. agno/tools/notion.py +204 -0
  236. agno/tools/parallel.py +314 -0
  237. agno/tools/postgres.py +76 -36
  238. agno/tools/redshift.py +406 -0
  239. agno/tools/scrapegraph.py +1 -1
  240. agno/tools/shopify.py +1519 -0
  241. agno/tools/slack.py +18 -3
  242. agno/tools/spotify.py +919 -0
  243. agno/tools/tavily.py +146 -0
  244. agno/tools/toolkit.py +25 -0
  245. agno/tools/workflow.py +8 -1
  246. agno/tools/yfinance.py +12 -11
  247. agno/tracing/__init__.py +12 -0
  248. agno/tracing/exporter.py +157 -0
  249. agno/tracing/schemas.py +276 -0
  250. agno/tracing/setup.py +111 -0
  251. agno/utils/agent.py +938 -0
  252. agno/utils/cryptography.py +22 -0
  253. agno/utils/dttm.py +33 -0
  254. agno/utils/events.py +151 -3
  255. agno/utils/gemini.py +15 -5
  256. agno/utils/hooks.py +118 -4
  257. agno/utils/http.py +113 -2
  258. agno/utils/knowledge.py +12 -5
  259. agno/utils/log.py +1 -0
  260. agno/utils/mcp.py +92 -2
  261. agno/utils/media.py +187 -1
  262. agno/utils/merge_dict.py +3 -3
  263. agno/utils/message.py +60 -0
  264. agno/utils/models/ai_foundry.py +9 -2
  265. agno/utils/models/claude.py +49 -14
  266. agno/utils/models/cohere.py +9 -2
  267. agno/utils/models/llama.py +9 -2
  268. agno/utils/models/mistral.py +4 -2
  269. agno/utils/print_response/agent.py +109 -16
  270. agno/utils/print_response/team.py +223 -30
  271. agno/utils/print_response/workflow.py +251 -34
  272. agno/utils/streamlit.py +1 -1
  273. agno/utils/team.py +98 -9
  274. agno/utils/tokens.py +657 -0
  275. agno/vectordb/base.py +39 -7
  276. agno/vectordb/cassandra/cassandra.py +21 -5
  277. agno/vectordb/chroma/chromadb.py +43 -12
  278. agno/vectordb/clickhouse/clickhousedb.py +21 -5
  279. agno/vectordb/couchbase/couchbase.py +29 -5
  280. agno/vectordb/lancedb/lance_db.py +92 -181
  281. agno/vectordb/langchaindb/langchaindb.py +24 -4
  282. agno/vectordb/lightrag/lightrag.py +17 -3
  283. agno/vectordb/llamaindex/llamaindexdb.py +25 -5
  284. agno/vectordb/milvus/milvus.py +50 -37
  285. agno/vectordb/mongodb/__init__.py +7 -1
  286. agno/vectordb/mongodb/mongodb.py +36 -30
  287. agno/vectordb/pgvector/pgvector.py +201 -77
  288. agno/vectordb/pineconedb/pineconedb.py +41 -23
  289. agno/vectordb/qdrant/qdrant.py +67 -54
  290. agno/vectordb/redis/__init__.py +9 -0
  291. agno/vectordb/redis/redisdb.py +682 -0
  292. agno/vectordb/singlestore/singlestore.py +50 -29
  293. agno/vectordb/surrealdb/surrealdb.py +31 -41
  294. agno/vectordb/upstashdb/upstashdb.py +34 -6
  295. agno/vectordb/weaviate/weaviate.py +53 -14
  296. agno/workflow/__init__.py +2 -0
  297. agno/workflow/agent.py +299 -0
  298. agno/workflow/condition.py +120 -18
  299. agno/workflow/loop.py +77 -10
  300. agno/workflow/parallel.py +231 -143
  301. agno/workflow/router.py +118 -17
  302. agno/workflow/step.py +609 -170
  303. agno/workflow/steps.py +73 -6
  304. agno/workflow/types.py +96 -21
  305. agno/workflow/workflow.py +2039 -262
  306. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/METADATA +201 -66
  307. agno-2.3.13.dist-info/RECORD +613 -0
  308. agno/tools/googlesearch.py +0 -98
  309. agno/tools/mcp.py +0 -679
  310. agno/tools/memori.py +0 -339
  311. agno-2.1.2.dist-info/RECORD +0 -543
  312. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +0 -0
  313. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/licenses/LICENSE +0 -0
  314. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
agno/db/base.py CHANGED
@@ -1,10 +1,14 @@
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
11
+ from agno.db.schemas.culture import CulturalKnowledge
8
12
  from agno.db.schemas.evals import EvalFilterType, EvalRunRecord, EvalType
9
13
  from agno.db.schemas.knowledge import KnowledgeRow
10
14
  from agno.session import Session
@@ -17,21 +21,52 @@ class SessionType(str, Enum):
17
21
 
18
22
 
19
23
  class BaseDb(ABC):
24
+ """Base abstract class for all our Database implementations."""
25
+
26
+ # We assume the database to be up to date with the 2.0.0 release
27
+ default_schema_version = "2.0.0"
28
+
20
29
  def __init__(
21
30
  self,
22
31
  session_table: Optional[str] = None,
32
+ culture_table: Optional[str] = None,
23
33
  memory_table: Optional[str] = None,
24
34
  metrics_table: Optional[str] = None,
25
35
  eval_table: Optional[str] = None,
26
36
  knowledge_table: Optional[str] = None,
37
+ traces_table: Optional[str] = None,
38
+ spans_table: Optional[str] = None,
39
+ versions_table: Optional[str] = None,
27
40
  id: Optional[str] = None,
28
41
  ):
29
42
  self.id = id or str(uuid4())
30
43
  self.session_table_name = session_table or "agno_sessions"
44
+ self.culture_table_name = culture_table or "agno_culture"
31
45
  self.memory_table_name = memory_table or "agno_memories"
32
46
  self.metrics_table_name = metrics_table or "agno_metrics"
33
47
  self.eval_table_name = eval_table or "agno_eval_runs"
34
48
  self.knowledge_table_name = knowledge_table or "agno_knowledge"
49
+ self.trace_table_name = traces_table or "agno_traces"
50
+ self.span_table_name = spans_table or "agno_spans"
51
+ self.versions_table_name = versions_table or "agno_schema_versions"
52
+
53
+ @abstractmethod
54
+ def table_exists(self, table_name: str) -> bool:
55
+ raise NotImplementedError
56
+
57
+ def _create_all_tables(self) -> None:
58
+ """Create all tables for this database."""
59
+ pass
60
+
61
+ # --- Schema Version ---
62
+ @abstractmethod
63
+ def get_latest_schema_version(self, table_name: str):
64
+ raise NotImplementedError
65
+
66
+ @abstractmethod
67
+ def upsert_schema_version(self, table_name: str, version: str):
68
+ """Upsert the schema version into the database."""
69
+ raise NotImplementedError
35
70
 
36
71
  # --- Sessions ---
37
72
  @abstractmethod
@@ -71,7 +106,11 @@ class BaseDb(ABC):
71
106
 
72
107
  @abstractmethod
73
108
  def rename_session(
74
- self, session_id: str, session_type: SessionType, session_name: str, deserialize: Optional[bool] = True
109
+ self,
110
+ session_id: str,
111
+ session_type: SessionType,
112
+ session_name: str,
113
+ deserialize: Optional[bool] = True,
75
114
  ) -> Optional[Union[Session, Dict[str, Any]]]:
76
115
  raise NotImplementedError
77
116
 
@@ -83,13 +122,15 @@ class BaseDb(ABC):
83
122
 
84
123
  @abstractmethod
85
124
  def upsert_sessions(
86
- self, sessions: List[Session], deserialize: Optional[bool] = True
125
+ self,
126
+ sessions: List[Session],
127
+ deserialize: Optional[bool] = True,
128
+ preserve_updated_at: bool = False,
87
129
  ) -> List[Union[Session, Dict[str, Any]]]:
88
130
  """Bulk upsert multiple sessions for improved performance on large datasets."""
89
131
  raise NotImplementedError
90
132
 
91
133
  # --- Memory ---
92
-
93
134
  @abstractmethod
94
135
  def clear_memories(self) -> None:
95
136
  raise NotImplementedError
@@ -148,7 +189,10 @@ class BaseDb(ABC):
148
189
 
149
190
  @abstractmethod
150
191
  def upsert_memories(
151
- self, memories: List[UserMemory], deserialize: Optional[bool] = True
192
+ self,
193
+ memories: List[UserMemory],
194
+ deserialize: Optional[bool] = True,
195
+ preserve_updated_at: bool = False,
152
196
  ) -> List[Union[UserMemory, Dict[str, Any]]]:
153
197
  """Bulk upsert multiple memories for improved performance on large datasets."""
154
198
  raise NotImplementedError
@@ -261,3 +305,642 @@ class BaseDb(ABC):
261
305
  self, eval_run_id: str, name: str, deserialize: Optional[bool] = True
262
306
  ) -> Optional[Union[EvalRunRecord, Dict[str, Any]]]:
263
307
  raise NotImplementedError
308
+
309
+ # --- Traces ---
310
+ @abstractmethod
311
+ def upsert_trace(self, trace: "Trace") -> None:
312
+ """Create or update a single trace record in the database.
313
+
314
+ Args:
315
+ trace: The Trace object to store (one per trace_id).
316
+ """
317
+ raise NotImplementedError
318
+
319
+ @abstractmethod
320
+ def get_trace(
321
+ self,
322
+ trace_id: Optional[str] = None,
323
+ run_id: Optional[str] = None,
324
+ session_id: Optional[str] = None,
325
+ user_id: Optional[str] = None,
326
+ agent_id: Optional[str] = None,
327
+ ):
328
+ """Get a single trace by trace_id or other filters.
329
+
330
+ Args:
331
+ trace_id: The unique trace identifier.
332
+ run_id: Filter by run ID (returns first match).
333
+ session_id: Filter by session ID (returns first match).
334
+ user_id: Filter by user ID (returns first match).
335
+ agent_id: Filter by agent ID (returns first match).
336
+
337
+ Returns:
338
+ Optional[Trace]: The trace if found, None otherwise.
339
+
340
+ Note:
341
+ If multiple filters are provided, trace_id takes precedence.
342
+ For other filters, the most recent trace is returned.
343
+ """
344
+ raise NotImplementedError
345
+
346
+ @abstractmethod
347
+ def get_traces(
348
+ self,
349
+ run_id: Optional[str] = None,
350
+ session_id: Optional[str] = None,
351
+ user_id: Optional[str] = None,
352
+ agent_id: Optional[str] = None,
353
+ team_id: Optional[str] = None,
354
+ workflow_id: Optional[str] = None,
355
+ status: Optional[str] = None,
356
+ start_time: Optional[datetime] = None,
357
+ end_time: Optional[datetime] = None,
358
+ limit: Optional[int] = 20,
359
+ page: Optional[int] = 1,
360
+ ) -> tuple[List, int]:
361
+ """Get traces matching the provided filters with pagination.
362
+
363
+ Args:
364
+ run_id: Filter by run ID.
365
+ session_id: Filter by session ID.
366
+ user_id: Filter by user ID.
367
+ agent_id: Filter by agent ID.
368
+ team_id: Filter by team ID.
369
+ workflow_id: Filter by workflow ID.
370
+ status: Filter by status (OK, ERROR).
371
+ start_time: Filter traces starting after this datetime.
372
+ end_time: Filter traces ending before this datetime.
373
+ limit: Maximum number of traces to return per page.
374
+ page: Page number (1-indexed).
375
+
376
+ Returns:
377
+ tuple[List[Trace], int]: Tuple of (list of matching traces with datetime fields, total count).
378
+ """
379
+ raise NotImplementedError
380
+
381
+ @abstractmethod
382
+ def get_trace_stats(
383
+ self,
384
+ user_id: Optional[str] = None,
385
+ agent_id: Optional[str] = None,
386
+ team_id: Optional[str] = None,
387
+ workflow_id: Optional[str] = None,
388
+ start_time: Optional[datetime] = None,
389
+ end_time: Optional[datetime] = None,
390
+ limit: Optional[int] = 20,
391
+ page: Optional[int] = 1,
392
+ ) -> tuple[List[Dict[str, Any]], int]:
393
+ """Get trace statistics grouped by session.
394
+
395
+ Args:
396
+ user_id: Filter by user ID.
397
+ agent_id: Filter by agent ID.
398
+ team_id: Filter by team ID.
399
+ workflow_id: Filter by workflow ID.
400
+ start_time: Filter sessions with traces created after this datetime.
401
+ end_time: Filter sessions with traces created before this datetime.
402
+ limit: Maximum number of sessions to return per page.
403
+ page: Page number (1-indexed).
404
+
405
+ Returns:
406
+ tuple[List[Dict], int]: Tuple of (list of session stats dicts, total count).
407
+ Each dict contains: session_id, user_id, agent_id, team_id, total_traces,
408
+ first_trace_at (datetime), last_trace_at (datetime).
409
+ """
410
+ raise NotImplementedError
411
+
412
+ # --- Spans ---
413
+ @abstractmethod
414
+ def create_span(self, span: "Span") -> None:
415
+ """Create a single span in the database.
416
+
417
+ Args:
418
+ span: The Span object to store.
419
+ """
420
+ raise NotImplementedError
421
+
422
+ @abstractmethod
423
+ def create_spans(self, spans: List) -> None:
424
+ """Create multiple spans in the database as a batch.
425
+
426
+ Args:
427
+ spans: List of Span objects to store.
428
+ """
429
+ raise NotImplementedError
430
+
431
+ @abstractmethod
432
+ def get_span(self, span_id: str):
433
+ """Get a single span by its span_id.
434
+
435
+ Args:
436
+ span_id: The unique span identifier.
437
+
438
+ Returns:
439
+ Optional[Span]: The span if found, None otherwise.
440
+ """
441
+ raise NotImplementedError
442
+
443
+ @abstractmethod
444
+ def get_spans(
445
+ self,
446
+ trace_id: Optional[str] = None,
447
+ parent_span_id: Optional[str] = None,
448
+ limit: Optional[int] = 1000,
449
+ ) -> List:
450
+ """Get spans matching the provided filters.
451
+
452
+ Args:
453
+ trace_id: Filter by trace ID.
454
+ parent_span_id: Filter by parent span ID.
455
+ limit: Maximum number of spans to return.
456
+
457
+ Returns:
458
+ List[Span]: List of matching spans.
459
+ """
460
+ raise NotImplementedError
461
+
462
+ # --- Cultural Knowledge ---
463
+ @abstractmethod
464
+ def clear_cultural_knowledge(self) -> None:
465
+ raise NotImplementedError
466
+
467
+ @abstractmethod
468
+ def delete_cultural_knowledge(self, id: str) -> None:
469
+ raise NotImplementedError
470
+
471
+ @abstractmethod
472
+ def get_cultural_knowledge(self, id: str) -> Optional[CulturalKnowledge]:
473
+ raise NotImplementedError
474
+
475
+ @abstractmethod
476
+ def get_all_cultural_knowledge(
477
+ self,
478
+ name: Optional[str] = None,
479
+ limit: Optional[int] = None,
480
+ page: Optional[int] = None,
481
+ sort_by: Optional[str] = None,
482
+ sort_order: Optional[str] = None,
483
+ agent_id: Optional[str] = None,
484
+ team_id: Optional[str] = None,
485
+ ) -> Optional[List[CulturalKnowledge]]:
486
+ raise NotImplementedError
487
+
488
+ @abstractmethod
489
+ def upsert_cultural_knowledge(self, cultural_knowledge: CulturalKnowledge) -> Optional[CulturalKnowledge]:
490
+ raise NotImplementedError
491
+
492
+
493
+ class AsyncBaseDb(ABC):
494
+ """Base abstract class for all our async database implementations."""
495
+
496
+ def __init__(
497
+ self,
498
+ id: Optional[str] = None,
499
+ session_table: Optional[str] = None,
500
+ memory_table: Optional[str] = None,
501
+ metrics_table: Optional[str] = None,
502
+ eval_table: Optional[str] = None,
503
+ knowledge_table: Optional[str] = None,
504
+ traces_table: Optional[str] = None,
505
+ spans_table: Optional[str] = None,
506
+ culture_table: Optional[str] = None,
507
+ versions_table: Optional[str] = None,
508
+ ):
509
+ self.id = id or str(uuid4())
510
+ self.session_table_name = session_table or "agno_sessions"
511
+ self.memory_table_name = memory_table or "agno_memories"
512
+ self.metrics_table_name = metrics_table or "agno_metrics"
513
+ self.eval_table_name = eval_table or "agno_eval_runs"
514
+ self.knowledge_table_name = knowledge_table or "agno_knowledge"
515
+ self.trace_table_name = traces_table or "agno_traces"
516
+ self.span_table_name = spans_table or "agno_spans"
517
+ self.culture_table_name = culture_table or "agno_culture"
518
+ self.versions_table_name = versions_table or "agno_schema_versions"
519
+
520
+ @abstractmethod
521
+ async def table_exists(self, table_name: str) -> bool:
522
+ """Check if a table with the given name exists in this database.
523
+
524
+ Default implementation returns True if the table name is configured.
525
+ Subclasses should override this to perform actual existence checks.
526
+
527
+ Args:
528
+ table_name: Name of the table to check
529
+
530
+ Returns:
531
+ bool: True if the table exists, False otherwise
532
+ """
533
+ raise NotImplementedError
534
+
535
+ @abstractmethod
536
+ async def get_latest_schema_version(self, table_name: str) -> str:
537
+ raise NotImplementedError
538
+
539
+ @abstractmethod
540
+ async def upsert_schema_version(self, table_name: str, version: str):
541
+ """Upsert the schema version into the database."""
542
+ raise NotImplementedError
543
+
544
+ # --- Sessions ---
545
+ @abstractmethod
546
+ async def delete_session(self, session_id: str) -> bool:
547
+ raise NotImplementedError
548
+
549
+ @abstractmethod
550
+ async def delete_sessions(self, session_ids: List[str]) -> None:
551
+ raise NotImplementedError
552
+
553
+ @abstractmethod
554
+ async def get_session(
555
+ self,
556
+ session_id: str,
557
+ session_type: SessionType,
558
+ user_id: Optional[str] = None,
559
+ deserialize: Optional[bool] = True,
560
+ ) -> Optional[Union[Session, Dict[str, Any]]]:
561
+ raise NotImplementedError
562
+
563
+ @abstractmethod
564
+ async def get_sessions(
565
+ self,
566
+ session_type: Optional[SessionType] = None,
567
+ user_id: Optional[str] = None,
568
+ component_id: Optional[str] = None,
569
+ session_name: Optional[str] = None,
570
+ start_timestamp: Optional[int] = None,
571
+ end_timestamp: Optional[int] = None,
572
+ limit: Optional[int] = None,
573
+ page: Optional[int] = None,
574
+ sort_by: Optional[str] = None,
575
+ sort_order: Optional[str] = None,
576
+ deserialize: Optional[bool] = True,
577
+ ) -> Union[List[Session], Tuple[List[Dict[str, Any]], int]]:
578
+ raise NotImplementedError
579
+
580
+ @abstractmethod
581
+ async def rename_session(
582
+ self,
583
+ session_id: str,
584
+ session_type: SessionType,
585
+ session_name: str,
586
+ deserialize: Optional[bool] = True,
587
+ ) -> Optional[Union[Session, Dict[str, Any]]]:
588
+ raise NotImplementedError
589
+
590
+ @abstractmethod
591
+ async def upsert_session(
592
+ self, session: Session, deserialize: Optional[bool] = True
593
+ ) -> Optional[Union[Session, Dict[str, Any]]]:
594
+ raise NotImplementedError
595
+
596
+ # --- Memory ---
597
+ @abstractmethod
598
+ async def clear_memories(self) -> None:
599
+ raise NotImplementedError
600
+
601
+ @abstractmethod
602
+ async def delete_user_memory(self, memory_id: str, user_id: Optional[str] = None) -> None:
603
+ raise NotImplementedError
604
+
605
+ @abstractmethod
606
+ async def delete_user_memories(self, memory_ids: List[str], user_id: Optional[str] = None) -> None:
607
+ raise NotImplementedError
608
+
609
+ @abstractmethod
610
+ async def get_all_memory_topics(self, user_id: Optional[str] = None) -> List[str]:
611
+ raise NotImplementedError
612
+
613
+ @abstractmethod
614
+ async def get_user_memory(
615
+ self,
616
+ memory_id: str,
617
+ deserialize: Optional[bool] = True,
618
+ user_id: Optional[str] = None,
619
+ ) -> Optional[Union[UserMemory, Dict[str, Any]]]:
620
+ raise NotImplementedError
621
+
622
+ @abstractmethod
623
+ async def get_user_memories(
624
+ self,
625
+ user_id: Optional[str] = None,
626
+ agent_id: Optional[str] = None,
627
+ team_id: Optional[str] = None,
628
+ topics: Optional[List[str]] = None,
629
+ search_content: Optional[str] = None,
630
+ limit: Optional[int] = None,
631
+ page: Optional[int] = None,
632
+ sort_by: Optional[str] = None,
633
+ sort_order: Optional[str] = None,
634
+ deserialize: Optional[bool] = True,
635
+ ) -> Union[List[UserMemory], Tuple[List[Dict[str, Any]], int]]:
636
+ raise NotImplementedError
637
+
638
+ @abstractmethod
639
+ async def get_user_memory_stats(
640
+ self,
641
+ limit: Optional[int] = None,
642
+ page: Optional[int] = None,
643
+ user_id: Optional[str] = None,
644
+ ) -> Tuple[List[Dict[str, Any]], int]:
645
+ raise NotImplementedError
646
+
647
+ @abstractmethod
648
+ async def upsert_user_memory(
649
+ self, memory: UserMemory, deserialize: Optional[bool] = True
650
+ ) -> Optional[Union[UserMemory, Dict[str, Any]]]:
651
+ raise NotImplementedError
652
+
653
+ # --- Metrics ---
654
+ @abstractmethod
655
+ async def get_metrics(
656
+ self, starting_date: Optional[date] = None, ending_date: Optional[date] = None
657
+ ) -> Tuple[List[Dict[str, Any]], Optional[int]]:
658
+ raise NotImplementedError
659
+
660
+ @abstractmethod
661
+ async def calculate_metrics(self) -> Optional[Any]:
662
+ raise NotImplementedError
663
+
664
+ # --- Knowledge ---
665
+ @abstractmethod
666
+ async def delete_knowledge_content(self, id: str):
667
+ """Delete a knowledge row from the database.
668
+
669
+ Args:
670
+ id (str): The ID of the knowledge row to delete.
671
+ """
672
+ raise NotImplementedError
673
+
674
+ @abstractmethod
675
+ async def get_knowledge_content(self, id: str) -> Optional[KnowledgeRow]:
676
+ """Get a knowledge row from the database.
677
+
678
+ Args:
679
+ id (str): The ID of the knowledge row to get.
680
+
681
+ Returns:
682
+ Optional[KnowledgeRow]: The knowledge row, or None if it doesn't exist.
683
+ """
684
+ raise NotImplementedError
685
+
686
+ @abstractmethod
687
+ async def get_knowledge_contents(
688
+ self,
689
+ limit: Optional[int] = None,
690
+ page: Optional[int] = None,
691
+ sort_by: Optional[str] = None,
692
+ sort_order: Optional[str] = None,
693
+ ) -> Tuple[List[KnowledgeRow], int]:
694
+ """Get all knowledge contents from the database.
695
+
696
+ Args:
697
+ limit (Optional[int]): The maximum number of knowledge contents to return.
698
+ page (Optional[int]): The page number.
699
+ sort_by (Optional[str]): The column to sort by.
700
+ sort_order (Optional[str]): The order to sort by.
701
+
702
+ Returns:
703
+ Tuple[List[KnowledgeRow], int]: The knowledge contents and total count.
704
+
705
+ Raises:
706
+ Exception: If an error occurs during retrieval.
707
+ """
708
+ raise NotImplementedError
709
+
710
+ @abstractmethod
711
+ async def upsert_knowledge_content(self, knowledge_row: KnowledgeRow):
712
+ """Upsert knowledge content in the database.
713
+
714
+ Args:
715
+ knowledge_row (KnowledgeRow): The knowledge row to upsert.
716
+
717
+ Returns:
718
+ Optional[KnowledgeRow]: The upserted knowledge row, or None if the operation fails.
719
+ """
720
+ raise NotImplementedError
721
+
722
+ # --- Evals ---
723
+ @abstractmethod
724
+ async def create_eval_run(self, eval_run: EvalRunRecord) -> Optional[EvalRunRecord]:
725
+ raise NotImplementedError
726
+
727
+ @abstractmethod
728
+ async def delete_eval_runs(self, eval_run_ids: List[str]) -> None:
729
+ raise NotImplementedError
730
+
731
+ @abstractmethod
732
+ async def get_eval_run(
733
+ self, eval_run_id: str, deserialize: Optional[bool] = True
734
+ ) -> Optional[Union[EvalRunRecord, Dict[str, Any]]]:
735
+ raise NotImplementedError
736
+
737
+ @abstractmethod
738
+ async def get_eval_runs(
739
+ self,
740
+ limit: Optional[int] = None,
741
+ page: Optional[int] = None,
742
+ sort_by: Optional[str] = None,
743
+ sort_order: Optional[str] = None,
744
+ agent_id: Optional[str] = None,
745
+ team_id: Optional[str] = None,
746
+ workflow_id: Optional[str] = None,
747
+ model_id: Optional[str] = None,
748
+ filter_type: Optional[EvalFilterType] = None,
749
+ eval_type: Optional[List[EvalType]] = None,
750
+ deserialize: Optional[bool] = True,
751
+ ) -> Union[List[EvalRunRecord], Tuple[List[Dict[str, Any]], int]]:
752
+ raise NotImplementedError
753
+
754
+ @abstractmethod
755
+ async def rename_eval_run(
756
+ self, eval_run_id: str, name: str, deserialize: Optional[bool] = True
757
+ ) -> Optional[Union[EvalRunRecord, Dict[str, Any]]]:
758
+ raise NotImplementedError
759
+
760
+ # --- Traces ---
761
+ @abstractmethod
762
+ async def upsert_trace(self, trace) -> None:
763
+ """Create or update a single trace record in the database.
764
+
765
+ Args:
766
+ trace: The Trace object to update (one per trace_id).
767
+ """
768
+ raise NotImplementedError
769
+
770
+ @abstractmethod
771
+ async def get_trace(
772
+ self,
773
+ trace_id: Optional[str] = None,
774
+ run_id: Optional[str] = None,
775
+ session_id: Optional[str] = None,
776
+ user_id: Optional[str] = None,
777
+ agent_id: Optional[str] = None,
778
+ ):
779
+ """Get a single trace by trace_id or other filters.
780
+
781
+ Args:
782
+ trace_id: The unique trace identifier.
783
+ run_id: Filter by run ID (returns first match).
784
+ session_id: Filter by session ID (returns first match).
785
+ user_id: Filter by user ID (returns first match).
786
+ agent_id: Filter by agent ID (returns first match).
787
+
788
+ Returns:
789
+ Optional[Trace]: The trace if found, None otherwise.
790
+
791
+ Note:
792
+ If multiple filters are provided, trace_id takes precedence.
793
+ For other filters, the most recent trace is returned.
794
+ """
795
+ raise NotImplementedError
796
+
797
+ @abstractmethod
798
+ async def get_traces(
799
+ self,
800
+ run_id: Optional[str] = None,
801
+ session_id: Optional[str] = None,
802
+ user_id: Optional[str] = None,
803
+ agent_id: Optional[str] = None,
804
+ team_id: Optional[str] = None,
805
+ workflow_id: Optional[str] = None,
806
+ status: Optional[str] = None,
807
+ start_time: Optional[datetime] = None,
808
+ end_time: Optional[datetime] = None,
809
+ limit: Optional[int] = 20,
810
+ page: Optional[int] = 1,
811
+ ) -> tuple[List, int]:
812
+ """Get traces matching the provided filters with pagination.
813
+
814
+ Args:
815
+ run_id: Filter by run ID.
816
+ session_id: Filter by session ID.
817
+ user_id: Filter by user ID.
818
+ agent_id: Filter by agent ID.
819
+ team_id: Filter by team ID.
820
+ workflow_id: Filter by workflow ID.
821
+ status: Filter by status (OK, ERROR).
822
+ start_time: Filter traces starting after this datetime.
823
+ end_time: Filter traces ending before this datetime.
824
+ limit: Maximum number of traces to return per page.
825
+ page: Page number (1-indexed).
826
+
827
+ Returns:
828
+ tuple[List[Trace], int]: Tuple of (list of matching traces with datetime fields, total count).
829
+ """
830
+ raise NotImplementedError
831
+
832
+ @abstractmethod
833
+ async def get_trace_stats(
834
+ self,
835
+ user_id: Optional[str] = None,
836
+ agent_id: Optional[str] = None,
837
+ team_id: Optional[str] = None,
838
+ workflow_id: Optional[str] = None,
839
+ start_time: Optional[datetime] = None,
840
+ end_time: Optional[datetime] = None,
841
+ limit: Optional[int] = 20,
842
+ page: Optional[int] = 1,
843
+ ) -> tuple[List[Dict[str, Any]], int]:
844
+ """Get trace statistics grouped by session.
845
+
846
+ Args:
847
+ user_id: Filter by user ID.
848
+ agent_id: Filter by agent ID.
849
+ team_id: Filter by team ID.
850
+ workflow_id: Filter by workflow ID.
851
+ start_time: Filter sessions with traces created after this datetime.
852
+ end_time: Filter sessions with traces created before this datetime.
853
+ limit: Maximum number of sessions to return per page.
854
+ page: Page number (1-indexed).
855
+
856
+ Returns:
857
+ tuple[List[Dict], int]: Tuple of (list of session stats dicts, total count).
858
+ Each dict contains: session_id, user_id, agent_id, team_id, total_traces,
859
+ first_trace_at (datetime), last_trace_at (datetime).
860
+ """
861
+ raise NotImplementedError
862
+
863
+ # --- Spans ---
864
+ @abstractmethod
865
+ async def create_span(self, span) -> None:
866
+ """Create a single span in the database.
867
+
868
+ Args:
869
+ span: The Span object to store.
870
+ """
871
+ raise NotImplementedError
872
+
873
+ @abstractmethod
874
+ async def create_spans(self, spans: List) -> None:
875
+ """Create multiple spans in the database as a batch.
876
+
877
+ Args:
878
+ spans: List of Span objects to store.
879
+ """
880
+ raise NotImplementedError
881
+
882
+ @abstractmethod
883
+ async def get_span(self, span_id: str):
884
+ """Get a single span by its span_id.
885
+
886
+ Args:
887
+ span_id: The unique span identifier.
888
+
889
+ Returns:
890
+ Optional[Span]: The span if found, None otherwise.
891
+ """
892
+ raise NotImplementedError
893
+
894
+ @abstractmethod
895
+ async def get_spans(
896
+ self,
897
+ trace_id: Optional[str] = None,
898
+ parent_span_id: Optional[str] = None,
899
+ limit: Optional[int] = 1000,
900
+ ) -> List:
901
+ """Get spans matching the provided filters.
902
+
903
+ Args:
904
+ trace_id: Filter by trace ID.
905
+ parent_span_id: Filter by parent span ID.
906
+ limit: Maximum number of spans to return.
907
+
908
+ Returns:
909
+ List[Span]: List of matching spans.
910
+ """
911
+ raise NotImplementedError
912
+
913
+ # --- Cultural Notions ---
914
+ @abstractmethod
915
+ async def clear_cultural_knowledge(self) -> None:
916
+ raise NotImplementedError
917
+
918
+ @abstractmethod
919
+ async def delete_cultural_knowledge(self, id: str) -> None:
920
+ raise NotImplementedError
921
+
922
+ @abstractmethod
923
+ async def get_cultural_knowledge(
924
+ self, id: str, deserialize: Optional[bool] = True
925
+ ) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
926
+ raise NotImplementedError
927
+
928
+ @abstractmethod
929
+ async def get_all_cultural_knowledge(
930
+ self,
931
+ agent_id: Optional[str] = None,
932
+ team_id: Optional[str] = None,
933
+ name: Optional[str] = None,
934
+ limit: Optional[int] = None,
935
+ page: Optional[int] = None,
936
+ sort_by: Optional[str] = None,
937
+ sort_order: Optional[str] = None,
938
+ deserialize: Optional[bool] = True,
939
+ ) -> Union[List[CulturalKnowledge], Tuple[List[Dict[str, Any]], int]]:
940
+ raise NotImplementedError
941
+
942
+ @abstractmethod
943
+ async def upsert_cultural_knowledge(
944
+ self, cultural_knowledge: CulturalKnowledge, deserialize: Optional[bool] = True
945
+ ) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
946
+ raise NotImplementedError