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/redis/schemas.py CHANGED
@@ -27,6 +27,9 @@ USER_MEMORY_SCHEMA = {
27
27
  "team_id": {"type": "string"},
28
28
  "user_id": {"type": "string"},
29
29
  "topics": {"type": "json"},
30
+ "input": {"type": "string"},
31
+ "feedback": {"type": "string"},
32
+ "created_at": {"type": "integer"},
30
33
  "updated_at": {"type": "integer"},
31
34
  }
32
35
 
@@ -81,6 +84,50 @@ KNOWLEDGE_SCHEMA = {
81
84
  }
82
85
 
83
86
 
87
+ CULTURAL_KNOWLEDGE_SCHEMA = {
88
+ "id": {"type": "string", "primary_key": True},
89
+ "name": {"type": "string"},
90
+ "summary": {"type": "string"},
91
+ "content": {"type": "json"},
92
+ "metadata": {"type": "json"},
93
+ "input": {"type": "string"},
94
+ "created_at": {"type": "integer"},
95
+ "updated_at": {"type": "integer"},
96
+ "agent_id": {"type": "string"},
97
+ "team_id": {"type": "string"},
98
+ }
99
+
100
+ TRACE_SCHEMA = {
101
+ "trace_id": {"type": "string", "primary_key": True},
102
+ "name": {"type": "string"},
103
+ "status": {"type": "string"},
104
+ "duration_ms": {"type": "integer"},
105
+ "run_id": {"type": "string"},
106
+ "session_id": {"type": "string"},
107
+ "user_id": {"type": "string"},
108
+ "agent_id": {"type": "string"},
109
+ "team_id": {"type": "string"},
110
+ "workflow_id": {"type": "string"},
111
+ "start_time": {"type": "string"},
112
+ "end_time": {"type": "string"},
113
+ "created_at": {"type": "string"},
114
+ }
115
+
116
+ SPAN_SCHEMA = {
117
+ "span_id": {"type": "string", "primary_key": True},
118
+ "trace_id": {"type": "string"},
119
+ "parent_span_id": {"type": "string"},
120
+ "name": {"type": "string"},
121
+ "span_kind": {"type": "string"},
122
+ "status_code": {"type": "string"},
123
+ "status_message": {"type": "string"},
124
+ "start_time": {"type": "string"},
125
+ "end_time": {"type": "string"},
126
+ "attributes": {"type": "json"},
127
+ "created_at": {"type": "string"},
128
+ }
129
+
130
+
84
131
  def get_table_schema_definition(table_type: str) -> dict[str, Any]:
85
132
  """
86
133
  Get the expected schema definition for the given table.
@@ -100,6 +147,9 @@ def get_table_schema_definition(table_type: str) -> dict[str, Any]:
100
147
  "metrics": METRICS_SCHEMA,
101
148
  "evals": EVAL_SCHEMA,
102
149
  "knowledge": KNOWLEDGE_SCHEMA,
150
+ "culture": CULTURAL_KNOWLEDGE_SCHEMA,
151
+ "traces": TRACE_SCHEMA,
152
+ "spans": SPAN_SCHEMA,
103
153
  }
104
154
 
105
155
  schema = schemas.get(table_type, {})
agno/db/redis/utils.py CHANGED
@@ -3,13 +3,14 @@
3
3
  import json
4
4
  import time
5
5
  from datetime import date, datetime, timedelta, timezone
6
- from typing import Any, Dict, List, Optional
6
+ from typing import Any, Dict, List, Optional, Union
7
7
  from uuid import UUID
8
8
 
9
+ from agno.db.schemas.culture import CulturalKnowledge
9
10
  from agno.utils.log import log_warning
10
11
 
11
12
  try:
12
- from redis import Redis
13
+ from redis import Redis, RedisCluster
13
14
  except ImportError:
14
15
  raise ImportError("`redis` not installed. Please install it using `pip install redis`")
15
16
 
@@ -50,7 +51,7 @@ def generate_index_key(prefix: str, table_type: str, index_field: str, index_val
50
51
  return f"{prefix}:{table_type}:index:{index_field}:{index_value}"
51
52
 
52
53
 
53
- def get_all_keys_for_table(redis_client: Redis, prefix: str, table_type: str) -> List[str]:
54
+ def get_all_keys_for_table(redis_client: Union[Redis, RedisCluster], prefix: str, table_type: str) -> List[str]:
54
55
  """Get all relevant keys for the given table type.
55
56
 
56
57
  Args:
@@ -129,7 +130,7 @@ def apply_filters(records: List[Dict[str, Any]], conditions: Dict[str, Any]) ->
129
130
 
130
131
 
131
132
  def create_index_entries(
132
- redis_client: Redis,
133
+ redis_client: Union[Redis, RedisCluster],
133
134
  prefix: str,
134
135
  table_type: str,
135
136
  record_id: str,
@@ -143,7 +144,7 @@ def create_index_entries(
143
144
 
144
145
 
145
146
  def remove_index_entries(
146
- redis_client: Redis,
147
+ redis_client: Union[Redis, RedisCluster],
147
148
  prefix: str,
148
149
  table_type: str,
149
150
  record_id: str,
@@ -199,7 +200,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
199
200
  all_user_ids = set()
200
201
 
201
202
  for session_type, sessions_count_key, runs_count_key in session_types:
202
- sessions = sessions_data.get(session_type, [])
203
+ sessions = sessions_data.get(session_type, []) or []
203
204
  metrics[sessions_count_key] = len(sessions)
204
205
 
205
206
  for session in sessions:
@@ -220,7 +221,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
220
221
 
221
222
  model_metrics = []
222
223
  for model, count in model_counts.items():
223
- model_id, model_provider = model.split(":")
224
+ model_id, model_provider = model.rsplit(":", 1)
224
225
  model_metrics.append({"model_id": model_id, "model_provider": model_provider, "count": count})
225
226
 
226
227
  metrics["users_count"] = len(all_user_ids)
@@ -286,3 +287,60 @@ def get_dates_to_calculate_metrics_for(starting_date: date) -> list[date]:
286
287
  if days_diff <= 0:
287
288
  return []
288
289
  return [starting_date + timedelta(days=x) for x in range(days_diff)]
290
+
291
+
292
+ # -- Cultural Knowledge util methods --
293
+ def serialize_cultural_knowledge_for_db(cultural_knowledge: CulturalKnowledge) -> Dict[str, Any]:
294
+ """Serialize a CulturalKnowledge object for database storage.
295
+
296
+ Converts the model's separate content, categories, and notes fields
297
+ into a single dict for the database content column.
298
+
299
+ Args:
300
+ cultural_knowledge (CulturalKnowledge): The cultural knowledge object to serialize.
301
+
302
+ Returns:
303
+ Dict[str, Any]: A dictionary with the content field as a dict containing content, categories, and notes.
304
+ """
305
+ content_dict: Dict[str, Any] = {}
306
+ if cultural_knowledge.content is not None:
307
+ content_dict["content"] = cultural_knowledge.content
308
+ if cultural_knowledge.categories is not None:
309
+ content_dict["categories"] = cultural_knowledge.categories
310
+ if cultural_knowledge.notes is not None:
311
+ content_dict["notes"] = cultural_knowledge.notes
312
+
313
+ return content_dict if content_dict else {}
314
+
315
+
316
+ def deserialize_cultural_knowledge_from_db(db_row: Dict[str, Any]) -> CulturalKnowledge:
317
+ """Deserialize a database row to a CulturalKnowledge object.
318
+
319
+ The database stores content as a dict containing content, categories, and notes.
320
+ This method extracts those fields and converts them back to the model format.
321
+
322
+ Args:
323
+ db_row (Dict[str, Any]): The database row as a dictionary.
324
+
325
+ Returns:
326
+ CulturalKnowledge: The cultural knowledge object.
327
+ """
328
+ # Extract content, categories, and notes from the content field
329
+ content_json = db_row.get("content", {}) or {}
330
+
331
+ return CulturalKnowledge.from_dict(
332
+ {
333
+ "id": db_row.get("id"),
334
+ "name": db_row.get("name"),
335
+ "summary": db_row.get("summary"),
336
+ "content": content_json.get("content"),
337
+ "categories": content_json.get("categories"),
338
+ "notes": content_json.get("notes"),
339
+ "metadata": db_row.get("metadata"),
340
+ "input": db_row.get("input"),
341
+ "created_at": db_row.get("created_at"),
342
+ "updated_at": db_row.get("updated_at"),
343
+ "agent_id": db_row.get("agent_id"),
344
+ "team_id": db_row.get("team_id"),
345
+ }
346
+ )
@@ -1,3 +1,4 @@
1
+ from agno.db.schemas.culture import CulturalKnowledge
1
2
  from agno.db.schemas.memory import UserMemory
2
3
 
3
- __all__ = ["UserMemory"]
4
+ __all__ = ["CulturalKnowledge", "UserMemory"]
@@ -0,0 +1,120 @@
1
+ from dataclasses import dataclass, field
2
+ from datetime import datetime, timezone
3
+ from typing import Any, Dict, Optional, Union
4
+
5
+ from typing_extensions import List
6
+
7
+
8
+ @dataclass
9
+ class CulturalKnowledge:
10
+ """Model for Cultural Knowledge
11
+
12
+ Notice: Culture is an experimental feature and is subject to change.
13
+ """
14
+
15
+ # The id of the cultural knowledge, auto-generated if not provided
16
+ id: Optional[str] = None
17
+ name: Optional[str] = None
18
+
19
+ content: Optional[str] = None
20
+ categories: Optional[List[str]] = None
21
+ notes: Optional[List[str]] = None
22
+
23
+ summary: Optional[str] = None
24
+ metadata: Optional[Dict[str, Any]] = None
25
+ input: Optional[str] = None
26
+
27
+ created_at: Optional[int] = field(default=None)
28
+ updated_at: Optional[int] = field(default=None)
29
+
30
+ agent_id: Optional[str] = None
31
+ team_id: Optional[str] = None
32
+
33
+ def __post_init__(self):
34
+ if self.name is not None and not self.name.strip():
35
+ raise ValueError("name must be a non-empty string")
36
+ self.created_at = _now_epoch_s() if self.created_at is None else _to_epoch_s(self.created_at)
37
+ self.updated_at = self.created_at if self.updated_at is None else _to_epoch_s(self.updated_at)
38
+
39
+ def bump_updated_at(self) -> None:
40
+ """Bump updated_at to now (UTC)."""
41
+ self.updated_at = _now_epoch_s()
42
+
43
+ def preview(self) -> Dict[str, Any]:
44
+ """Return a preview of the cultural knowledge"""
45
+ _preview: Dict[str, Any] = {
46
+ "name": self.name,
47
+ }
48
+ if self.categories is not None:
49
+ _preview["categories"] = self.categories
50
+ if self.summary is not None:
51
+ _preview["summary"] = self.summary[:100] + "..." if len(self.summary) > 100 else self.summary
52
+ if self.content is not None:
53
+ _preview["content"] = self.content[:100] + "..." if len(self.content) > 100 else self.content
54
+ if self.notes is not None:
55
+ _preview["notes"] = [note[:100] + "..." if len(note) > 100 else note for note in self.notes]
56
+ return _preview
57
+
58
+ def to_dict(self) -> Dict[str, Any]:
59
+ _dict = {
60
+ "id": self.id,
61
+ "name": self.name,
62
+ "summary": self.summary,
63
+ "content": self.content,
64
+ "categories": self.categories,
65
+ "metadata": self.metadata,
66
+ "notes": self.notes,
67
+ "input": self.input,
68
+ "created_at": (_epoch_to_rfc3339_z(self.created_at) if self.created_at is not None else None),
69
+ "updated_at": (_epoch_to_rfc3339_z(self.updated_at) if self.updated_at is not None else None),
70
+ "agent_id": self.agent_id,
71
+ "team_id": self.team_id,
72
+ }
73
+ return {k: v for k, v in _dict.items() if v is not None}
74
+
75
+ @classmethod
76
+ def from_dict(cls, data: Dict[str, Any]) -> "CulturalKnowledge":
77
+ d = dict(data)
78
+
79
+ # Preserve 0 and None explicitly; only process if key exists
80
+ if "created_at" in d and d["created_at"] is not None:
81
+ d["created_at"] = _to_epoch_s(d["created_at"])
82
+ if "updated_at" in d and d["updated_at"] is not None:
83
+ d["updated_at"] = _to_epoch_s(d["updated_at"])
84
+
85
+ return cls(**d)
86
+
87
+
88
+ def _now_epoch_s() -> int:
89
+ return int(datetime.now(timezone.utc).timestamp())
90
+
91
+
92
+ def _to_epoch_s(value: Union[int, float, str, datetime]) -> int:
93
+ """Normalize various datetime representations to epoch seconds (UTC)."""
94
+ if isinstance(value, (int, float)):
95
+ # assume value is already in seconds
96
+ return int(value)
97
+
98
+ if isinstance(value, datetime):
99
+ dt = value
100
+ if dt.tzinfo is None:
101
+ dt = dt.replace(tzinfo=timezone.utc)
102
+ return int(dt.timestamp())
103
+
104
+ if isinstance(value, str):
105
+ s = value.strip()
106
+ if s.endswith("Z"):
107
+ s = s[:-1] + "+00:00"
108
+ try:
109
+ dt = datetime.fromisoformat(s)
110
+ except ValueError as e:
111
+ raise ValueError(f"Unsupported datetime string: {value!r}") from e
112
+ if dt.tzinfo is None:
113
+ dt = dt.replace(tzinfo=timezone.utc)
114
+ return int(dt.timestamp())
115
+
116
+ raise TypeError(f"Unsupported datetime value: {type(value)}")
117
+
118
+
119
+ def _epoch_to_rfc3339_z(ts: Union[int, float]) -> str:
120
+ return datetime.fromtimestamp(float(ts), tz=timezone.utc).isoformat().replace("+00:00", "Z")
agno/db/schemas/evals.py CHANGED
@@ -6,6 +6,7 @@ from pydantic import BaseModel
6
6
 
7
7
  class EvalType(str, Enum):
8
8
  ACCURACY = "accuracy"
9
+ AGENT_AS_JUDGE = "agent_as_judge"
9
10
  PERFORMANCE = "performance"
10
11
  RELIABILITY = "reliability"
11
12
 
agno/db/schemas/memory.py CHANGED
@@ -2,6 +2,8 @@ from dataclasses import dataclass
2
2
  from datetime import datetime, timezone
3
3
  from typing import Any, Dict, List, Optional
4
4
 
5
+ from agno.utils.dttm import now_epoch_s
6
+
5
7
 
6
8
  @dataclass
7
9
  class UserMemory:
@@ -12,18 +14,25 @@ class UserMemory:
12
14
  topics: Optional[List[str]] = None
13
15
  user_id: Optional[str] = None
14
16
  input: Optional[str] = None
15
- updated_at: Optional[datetime] = None
17
+ created_at: Optional[int] = None
18
+ updated_at: Optional[int] = None
16
19
  feedback: Optional[str] = None
17
20
 
18
21
  agent_id: Optional[str] = None
19
22
  team_id: Optional[str] = None
20
23
 
24
+ def __post_init__(self) -> None:
25
+ """Automatically set created_at if not provided."""
26
+ if self.created_at is None:
27
+ self.created_at = now_epoch_s()
28
+
21
29
  def to_dict(self) -> Dict[str, Any]:
22
30
  _dict = {
23
31
  "memory_id": self.memory_id,
24
32
  "memory": self.memory,
25
33
  "topics": self.topics,
26
- "updated_at": self.updated_at.isoformat() if self.updated_at else None,
34
+ "created_at": datetime.fromtimestamp(self.created_at).isoformat() if self.created_at else None,
35
+ "updated_at": datetime.fromtimestamp(self.updated_at).isoformat() if self.updated_at else None,
27
36
  "input": self.input,
28
37
  "user_id": self.user_id,
29
38
  "agent_id": self.agent_id,
@@ -36,6 +45,12 @@ class UserMemory:
36
45
  def from_dict(cls, data: Dict[str, Any]) -> "UserMemory":
37
46
  data = dict(data)
38
47
 
48
+ if created_at := data.get("created_at"):
49
+ if isinstance(created_at, (int, float)):
50
+ data["created_at"] = datetime.fromtimestamp(created_at, tz=timezone.utc)
51
+ else:
52
+ data["created_at"] = datetime.fromisoformat(created_at)
53
+
39
54
  # Convert updated_at to datetime
40
55
  if updated_at := data.get("updated_at"):
41
56
  if isinstance(updated_at, (int, float)):
@@ -39,6 +39,8 @@ USER_MEMORY_TABLE_SCHEMA = {
39
39
  "team_id": {"type": lambda: String(128), "nullable": True},
40
40
  "user_id": {"type": lambda: String(128), "nullable": True, "index": True},
41
41
  "topics": {"type": JSON, "nullable": True},
42
+ "feedback": {"type": Text, "nullable": True},
43
+ "created_at": {"type": BigInteger, "nullable": False, "index": True},
42
44
  "updated_at": {"type": BigInteger, "nullable": True, "index": True},
43
45
  }
44
46
 
@@ -92,6 +94,63 @@ METRICS_TABLE_SCHEMA = {
92
94
  "completed": {"type": Boolean, "nullable": False, "default": False},
93
95
  }
94
96
 
97
+ CULTURAL_KNOWLEDGE_TABLE_SCHEMA = {
98
+ "id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
99
+ "name": {"type": lambda: String(255), "nullable": False, "index": True},
100
+ "summary": {"type": Text, "nullable": True},
101
+ "content": {"type": JSON, "nullable": True},
102
+ "metadata": {"type": JSON, "nullable": True},
103
+ "input": {"type": Text, "nullable": True},
104
+ "created_at": {"type": BigInteger, "nullable": True},
105
+ "updated_at": {"type": BigInteger, "nullable": True},
106
+ "agent_id": {"type": lambda: String(128), "nullable": True},
107
+ "team_id": {"type": lambda: String(128), "nullable": True},
108
+ }
109
+
110
+
111
+ VERSIONS_TABLE_SCHEMA = {
112
+ "table_name": {"type": lambda: String(128), "nullable": False, "primary_key": True},
113
+ "version": {"type": lambda: String(10), "nullable": False},
114
+ "created_at": {"type": lambda: String(128), "nullable": False, "index": True},
115
+ "updated_at": {"type": lambda: String(128), "nullable": True},
116
+ }
117
+
118
+ TRACE_TABLE_SCHEMA = {
119
+ "trace_id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
120
+ "name": {"type": lambda: String(512), "nullable": False},
121
+ "status": {"type": lambda: String(20), "nullable": False, "index": True},
122
+ "start_time": {"type": lambda: String(64), "nullable": False, "index": True}, # ISO 8601 datetime string
123
+ "end_time": {"type": lambda: String(64), "nullable": False}, # ISO 8601 datetime string
124
+ "duration_ms": {"type": BigInteger, "nullable": False},
125
+ "run_id": {"type": lambda: String(128), "nullable": True, "index": True},
126
+ "session_id": {"type": lambda: String(128), "nullable": True, "index": True},
127
+ "user_id": {"type": lambda: String(128), "nullable": True, "index": True},
128
+ "agent_id": {"type": lambda: String(128), "nullable": True, "index": True},
129
+ "team_id": {"type": lambda: String(128), "nullable": True, "index": True},
130
+ "workflow_id": {"type": lambda: String(128), "nullable": True, "index": True},
131
+ "created_at": {"type": lambda: String(64), "nullable": False, "index": True}, # ISO 8601 datetime string
132
+ }
133
+
134
+ SPAN_TABLE_SCHEMA = {
135
+ "span_id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
136
+ "trace_id": {
137
+ "type": lambda: String(128),
138
+ "nullable": False,
139
+ "index": True,
140
+ "foreign_key": "agno_traces.trace_id", # Foreign key to traces table
141
+ },
142
+ "parent_span_id": {"type": lambda: String(128), "nullable": True, "index": True},
143
+ "name": {"type": lambda: String(512), "nullable": False},
144
+ "span_kind": {"type": lambda: String(50), "nullable": False},
145
+ "status_code": {"type": lambda: String(20), "nullable": False},
146
+ "status_message": {"type": Text, "nullable": True},
147
+ "start_time": {"type": lambda: String(64), "nullable": False, "index": True}, # ISO 8601 datetime string
148
+ "end_time": {"type": lambda: String(64), "nullable": False}, # ISO 8601 datetime string
149
+ "duration_ms": {"type": BigInteger, "nullable": False},
150
+ "attributes": {"type": JSON, "nullable": True},
151
+ "created_at": {"type": lambda: String(64), "nullable": False, "index": True}, # ISO 8601 datetime string
152
+ }
153
+
95
154
 
96
155
  def get_table_schema_definition(table_type: str) -> dict[str, Any]:
97
156
  """
@@ -107,6 +166,10 @@ def get_table_schema_definition(table_type: str) -> dict[str, Any]:
107
166
  "metrics": METRICS_TABLE_SCHEMA,
108
167
  "memories": USER_MEMORY_TABLE_SCHEMA,
109
168
  "knowledge": KNOWLEDGE_TABLE_SCHEMA,
169
+ "culture": CULTURAL_KNOWLEDGE_TABLE_SCHEMA,
170
+ "versions": VERSIONS_TABLE_SCHEMA,
171
+ "traces": TRACE_TABLE_SCHEMA,
172
+ "spans": SPAN_TABLE_SCHEMA,
110
173
  }
111
174
  schema = schemas.get(table_type, {})
112
175