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/gcs_json/utils.py CHANGED
@@ -6,6 +6,7 @@ from typing import Any, Dict, List, Optional
6
6
  from uuid import uuid4
7
7
 
8
8
  from agno.db.schemas.culture import CulturalKnowledge
9
+ from agno.db.utils import get_sort_value
9
10
  from agno.utils.log import log_debug
10
11
 
11
12
 
@@ -21,6 +22,9 @@ def apply_sorting(
21
22
 
22
23
  Returns:
23
24
  The sorted list
25
+
26
+ Note:
27
+ If sorting by "updated_at", will fallback to "created_at" in case of None.
24
28
  """
25
29
  if sort_by is None or not data:
26
30
  return data
@@ -31,8 +35,16 @@ def apply_sorting(
31
35
  return data
32
36
 
33
37
  try:
34
- reverse_order = sort_order != "asc" if sort_order else True
35
- return sorted(data, key=lambda x: x.get(sort_by, 0), reverse=reverse_order)
38
+ is_descending = sort_order != "asc" if sort_order else True
39
+
40
+ # Sort using the helper function that handles updated_at -> created_at fallback
41
+ sorted_records = sorted(
42
+ data,
43
+ key=lambda x: (get_sort_value(x, sort_by) is None, get_sort_value(x, sort_by)),
44
+ reverse=is_descending,
45
+ )
46
+
47
+ return sorted_records
36
48
  except Exception as e:
37
49
  log_debug(f"Error sorting data by '{sort_by}': {e}")
38
50
  return data
@@ -1,7 +1,7 @@
1
1
  import time
2
2
  from copy import deepcopy
3
3
  from datetime import date, datetime, timedelta, timezone
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
7
  from agno.db.base import BaseDb, SessionType
@@ -20,6 +20,9 @@ from agno.db.schemas.memory import UserMemory
20
20
  from agno.session import AgentSession, Session, TeamSession, WorkflowSession
21
21
  from agno.utils.log import log_debug, log_error, log_info, log_warning
22
22
 
23
+ if TYPE_CHECKING:
24
+ from agno.tracing.schemas import Span, Trace
25
+
23
26
 
24
27
  class InMemoryDb(BaseDb):
25
28
  def __init__(self):
@@ -38,8 +41,15 @@ class InMemoryDb(BaseDb):
38
41
  """In-memory implementation, always returns True."""
39
42
  return True
40
43
 
41
- # -- Session methods --
44
+ def get_latest_schema_version(self):
45
+ """Get the latest version of the database schema."""
46
+ pass
47
+
48
+ def upsert_schema_version(self, version: str) -> None:
49
+ """Upsert the schema version into the database."""
50
+ pass
42
51
 
52
+ # -- Session methods --
43
53
  def delete_session(self, session_id: str) -> bool:
44
54
  """Delete a session from in-memory storage.
45
55
 
@@ -513,13 +523,14 @@ class InMemoryDb(BaseDb):
513
523
  raise e
514
524
 
515
525
  def get_user_memory_stats(
516
- self, limit: Optional[int] = None, page: Optional[int] = None
526
+ self, limit: Optional[int] = None, page: Optional[int] = None, user_id: Optional[str] = None
517
527
  ) -> Tuple[List[Dict[str, Any]], int]:
518
528
  """Get user memory statistics.
519
529
 
520
530
  Args:
521
531
  limit (Optional[int]): Maximum number of stats to return.
522
532
  page (Optional[int]): Page number for pagination.
533
+ user_id (Optional[str]): User ID for filtering.
523
534
 
524
535
  Returns:
525
536
  Tuple[List[Dict[str, Any]], int]: List of user memory statistics and total count.
@@ -532,7 +543,9 @@ class InMemoryDb(BaseDb):
532
543
 
533
544
  for memory in self._memories:
534
545
  memory_user_id = memory.get("user_id")
535
-
546
+ # filter by user_id if provided
547
+ if user_id is not None and memory_user_id != user_id:
548
+ continue
536
549
  if memory_user_id:
537
550
  if memory_user_id not in user_stats:
538
551
  user_stats[memory_user_id] = {
@@ -1158,3 +1171,189 @@ class InMemoryDb(BaseDb):
1158
1171
  except Exception as e:
1159
1172
  log_error(f"Error upserting cultural knowledge: {e}")
1160
1173
  raise e
1174
+
1175
+ # --- Traces ---
1176
+ def upsert_trace(self, trace: "Trace") -> None:
1177
+ """Create or update a single trace record in the database.
1178
+
1179
+ Args:
1180
+ trace: The Trace object to store (one per trace_id).
1181
+ """
1182
+ raise NotImplementedError
1183
+
1184
+ def get_trace(
1185
+ self,
1186
+ trace_id: Optional[str] = None,
1187
+ run_id: Optional[str] = None,
1188
+ ):
1189
+ """Get a single trace by trace_id or other filters.
1190
+
1191
+ Args:
1192
+ trace_id: The unique trace identifier.
1193
+ run_id: Filter by run ID (returns first match).
1194
+
1195
+ Returns:
1196
+ Optional[Trace]: The trace if found, None otherwise.
1197
+
1198
+ Note:
1199
+ If multiple filters are provided, trace_id takes precedence.
1200
+ For other filters, the most recent trace is returned.
1201
+ """
1202
+ raise NotImplementedError
1203
+
1204
+ def get_traces(
1205
+ self,
1206
+ run_id: Optional[str] = None,
1207
+ session_id: Optional[str] = None,
1208
+ user_id: Optional[str] = None,
1209
+ agent_id: Optional[str] = None,
1210
+ team_id: Optional[str] = None,
1211
+ workflow_id: Optional[str] = None,
1212
+ status: Optional[str] = None,
1213
+ start_time: Optional[datetime] = None,
1214
+ end_time: Optional[datetime] = None,
1215
+ limit: Optional[int] = 20,
1216
+ page: Optional[int] = 1,
1217
+ ) -> tuple[List, int]:
1218
+ """Get traces matching the provided filters.
1219
+
1220
+ Args:
1221
+ run_id: Filter by run ID.
1222
+ session_id: Filter by session ID.
1223
+ user_id: Filter by user ID.
1224
+ agent_id: Filter by agent ID.
1225
+ team_id: Filter by team ID.
1226
+ workflow_id: Filter by workflow ID.
1227
+ status: Filter by status (OK, ERROR, UNSET).
1228
+ start_time: Filter traces starting after this datetime.
1229
+ end_time: Filter traces ending before this datetime.
1230
+ limit: Maximum number of traces to return per page.
1231
+ page: Page number (1-indexed).
1232
+
1233
+ Returns:
1234
+ tuple[List[Trace], int]: Tuple of (list of matching traces, total count).
1235
+ """
1236
+ raise NotImplementedError
1237
+
1238
+ def get_trace_stats(
1239
+ self,
1240
+ user_id: Optional[str] = None,
1241
+ agent_id: Optional[str] = None,
1242
+ team_id: Optional[str] = None,
1243
+ workflow_id: Optional[str] = None,
1244
+ start_time: Optional[datetime] = None,
1245
+ end_time: Optional[datetime] = None,
1246
+ limit: Optional[int] = 20,
1247
+ page: Optional[int] = 1,
1248
+ ) -> tuple[List[Dict[str, Any]], int]:
1249
+ """Get trace statistics grouped by session.
1250
+
1251
+ Args:
1252
+ user_id: Filter by user ID.
1253
+ agent_id: Filter by agent ID.
1254
+ team_id: Filter by team ID.
1255
+ workflow_id: Filter by workflow ID.
1256
+ start_time: Filter sessions with traces created after this datetime.
1257
+ end_time: Filter sessions with traces created before this datetime.
1258
+ limit: Maximum number of sessions to return per page.
1259
+ page: Page number (1-indexed).
1260
+
1261
+ Returns:
1262
+ tuple[List[Dict], int]: Tuple of (list of session stats dicts, total count).
1263
+ Each dict contains: session_id, user_id, agent_id, team_id, workflow_id, total_traces,
1264
+ first_trace_at, last_trace_at.
1265
+ """
1266
+ raise NotImplementedError
1267
+
1268
+ # --- Spans ---
1269
+ def create_span(self, span: "Span") -> None:
1270
+ """Create a single span in the database.
1271
+
1272
+ Args:
1273
+ span: The Span object to store.
1274
+ """
1275
+ raise NotImplementedError
1276
+
1277
+ def create_spans(self, spans: List) -> None:
1278
+ """Create multiple spans in the database as a batch.
1279
+
1280
+ Args:
1281
+ spans: List of Span objects to store.
1282
+ """
1283
+ raise NotImplementedError
1284
+
1285
+ def get_span(self, span_id: str):
1286
+ """Get a single span by its span_id.
1287
+
1288
+ Args:
1289
+ span_id: The unique span identifier.
1290
+
1291
+ Returns:
1292
+ Optional[Span]: The span if found, None otherwise.
1293
+ """
1294
+ raise NotImplementedError
1295
+
1296
+ def get_spans(
1297
+ self,
1298
+ trace_id: Optional[str] = None,
1299
+ parent_span_id: Optional[str] = None,
1300
+ limit: Optional[int] = 1000,
1301
+ ) -> List:
1302
+ """Get spans matching the provided filters.
1303
+
1304
+ Args:
1305
+ trace_id: Filter by trace ID.
1306
+ parent_span_id: Filter by parent span ID.
1307
+ limit: Maximum number of spans to return.
1308
+
1309
+ Returns:
1310
+ List[Span]: List of matching spans.
1311
+ """
1312
+ raise NotImplementedError
1313
+
1314
+ # -- Learning methods (stubs) --
1315
+ def get_learning(
1316
+ self,
1317
+ learning_type: str,
1318
+ user_id: Optional[str] = None,
1319
+ agent_id: Optional[str] = None,
1320
+ team_id: Optional[str] = None,
1321
+ session_id: Optional[str] = None,
1322
+ namespace: Optional[str] = None,
1323
+ entity_id: Optional[str] = None,
1324
+ entity_type: Optional[str] = None,
1325
+ ) -> Optional[Dict[str, Any]]:
1326
+ raise NotImplementedError("Learning methods not yet implemented for InMemoryDb")
1327
+
1328
+ def upsert_learning(
1329
+ self,
1330
+ id: str,
1331
+ learning_type: str,
1332
+ content: Dict[str, Any],
1333
+ user_id: Optional[str] = None,
1334
+ agent_id: Optional[str] = None,
1335
+ team_id: Optional[str] = None,
1336
+ session_id: Optional[str] = None,
1337
+ namespace: Optional[str] = None,
1338
+ entity_id: Optional[str] = None,
1339
+ entity_type: Optional[str] = None,
1340
+ metadata: Optional[Dict[str, Any]] = None,
1341
+ ) -> None:
1342
+ raise NotImplementedError("Learning methods not yet implemented for InMemoryDb")
1343
+
1344
+ def delete_learning(self, id: str) -> bool:
1345
+ raise NotImplementedError("Learning methods not yet implemented for InMemoryDb")
1346
+
1347
+ def get_learnings(
1348
+ self,
1349
+ learning_type: Optional[str] = None,
1350
+ user_id: Optional[str] = None,
1351
+ agent_id: Optional[str] = None,
1352
+ team_id: Optional[str] = None,
1353
+ session_id: Optional[str] = None,
1354
+ namespace: Optional[str] = None,
1355
+ entity_id: Optional[str] = None,
1356
+ entity_type: Optional[str] = None,
1357
+ limit: Optional[int] = None,
1358
+ ) -> List[Dict[str, Any]]:
1359
+ raise NotImplementedError("Learning methods not yet implemented for InMemoryDb")
@@ -6,6 +6,7 @@ from typing import Any, Dict, List, Optional
6
6
  from uuid import uuid4
7
7
 
8
8
  from agno.db.schemas.culture import CulturalKnowledge
9
+ from agno.db.utils import get_sort_value
9
10
  from agno.utils.log import log_debug
10
11
 
11
12
 
@@ -21,6 +22,9 @@ def apply_sorting(
21
22
 
22
23
  Returns:
23
24
  The sorted list
25
+
26
+ Note:
27
+ If sorting by "updated_at", will fallback to "created_at" in case of None.
24
28
  """
25
29
  if sort_by is None or not data:
26
30
  return data
@@ -31,8 +35,16 @@ def apply_sorting(
31
35
  return data
32
36
 
33
37
  try:
34
- reverse_order = sort_order != "asc" if sort_order else True
35
- return sorted(data, key=lambda x: x.get(sort_by, 0), reverse=reverse_order)
38
+ is_descending = sort_order != "asc" if sort_order else True
39
+
40
+ # Sort using the helper function that handles updated_at -> created_at fallback
41
+ sorted_records = sorted(
42
+ data,
43
+ key=lambda x: (get_sort_value(x, sort_by) is None, get_sort_value(x, sort_by)),
44
+ reverse=is_descending,
45
+ )
46
+
47
+ return sorted_records
36
48
  except Exception as e:
37
49
  log_debug(f"Error sorting data by '{sort_by}': {e}")
38
50
  return data