agno 2.0.0rc2__py3-none-any.whl → 2.3.0__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 (331) hide show
  1. agno/agent/agent.py +6009 -2874
  2. agno/api/api.py +2 -0
  3. agno/api/os.py +1 -1
  4. agno/culture/__init__.py +3 -0
  5. agno/culture/manager.py +956 -0
  6. agno/db/async_postgres/__init__.py +3 -0
  7. agno/db/base.py +385 -6
  8. agno/db/dynamo/dynamo.py +388 -81
  9. agno/db/dynamo/schemas.py +47 -10
  10. agno/db/dynamo/utils.py +63 -4
  11. agno/db/firestore/firestore.py +435 -64
  12. agno/db/firestore/schemas.py +11 -0
  13. agno/db/firestore/utils.py +102 -4
  14. agno/db/gcs_json/gcs_json_db.py +384 -42
  15. agno/db/gcs_json/utils.py +60 -26
  16. agno/db/in_memory/in_memory_db.py +351 -66
  17. agno/db/in_memory/utils.py +60 -2
  18. agno/db/json/json_db.py +339 -48
  19. agno/db/json/utils.py +60 -26
  20. agno/db/migrations/manager.py +199 -0
  21. agno/db/migrations/v1_to_v2.py +510 -37
  22. agno/db/migrations/versions/__init__.py +0 -0
  23. agno/db/migrations/versions/v2_3_0.py +938 -0
  24. agno/db/mongo/__init__.py +15 -1
  25. agno/db/mongo/async_mongo.py +2036 -0
  26. agno/db/mongo/mongo.py +653 -76
  27. agno/db/mongo/schemas.py +13 -0
  28. agno/db/mongo/utils.py +80 -8
  29. agno/db/mysql/mysql.py +687 -25
  30. agno/db/mysql/schemas.py +61 -37
  31. agno/db/mysql/utils.py +60 -2
  32. agno/db/postgres/__init__.py +2 -1
  33. agno/db/postgres/async_postgres.py +2001 -0
  34. agno/db/postgres/postgres.py +676 -57
  35. agno/db/postgres/schemas.py +43 -18
  36. agno/db/postgres/utils.py +164 -2
  37. agno/db/redis/redis.py +344 -38
  38. agno/db/redis/schemas.py +18 -0
  39. agno/db/redis/utils.py +60 -2
  40. agno/db/schemas/__init__.py +2 -1
  41. agno/db/schemas/culture.py +120 -0
  42. agno/db/schemas/memory.py +13 -0
  43. agno/db/singlestore/schemas.py +26 -1
  44. agno/db/singlestore/singlestore.py +687 -53
  45. agno/db/singlestore/utils.py +60 -2
  46. agno/db/sqlite/__init__.py +2 -1
  47. agno/db/sqlite/async_sqlite.py +2371 -0
  48. agno/db/sqlite/schemas.py +24 -0
  49. agno/db/sqlite/sqlite.py +774 -85
  50. agno/db/sqlite/utils.py +168 -5
  51. agno/db/surrealdb/__init__.py +3 -0
  52. agno/db/surrealdb/metrics.py +292 -0
  53. agno/db/surrealdb/models.py +309 -0
  54. agno/db/surrealdb/queries.py +71 -0
  55. agno/db/surrealdb/surrealdb.py +1361 -0
  56. agno/db/surrealdb/utils.py +147 -0
  57. agno/db/utils.py +50 -22
  58. agno/eval/accuracy.py +50 -43
  59. agno/eval/performance.py +6 -3
  60. agno/eval/reliability.py +6 -3
  61. agno/eval/utils.py +33 -16
  62. agno/exceptions.py +68 -1
  63. agno/filters.py +354 -0
  64. agno/guardrails/__init__.py +6 -0
  65. agno/guardrails/base.py +19 -0
  66. agno/guardrails/openai.py +144 -0
  67. agno/guardrails/pii.py +94 -0
  68. agno/guardrails/prompt_injection.py +52 -0
  69. agno/integrations/discord/client.py +1 -0
  70. agno/knowledge/chunking/agentic.py +13 -10
  71. agno/knowledge/chunking/fixed.py +1 -1
  72. agno/knowledge/chunking/semantic.py +40 -8
  73. agno/knowledge/chunking/strategy.py +59 -15
  74. agno/knowledge/embedder/aws_bedrock.py +9 -4
  75. agno/knowledge/embedder/azure_openai.py +54 -0
  76. agno/knowledge/embedder/base.py +2 -0
  77. agno/knowledge/embedder/cohere.py +184 -5
  78. agno/knowledge/embedder/fastembed.py +1 -1
  79. agno/knowledge/embedder/google.py +79 -1
  80. agno/knowledge/embedder/huggingface.py +9 -4
  81. agno/knowledge/embedder/jina.py +63 -0
  82. agno/knowledge/embedder/mistral.py +78 -11
  83. agno/knowledge/embedder/nebius.py +1 -1
  84. agno/knowledge/embedder/ollama.py +13 -0
  85. agno/knowledge/embedder/openai.py +37 -65
  86. agno/knowledge/embedder/sentence_transformer.py +8 -4
  87. agno/knowledge/embedder/vllm.py +262 -0
  88. agno/knowledge/embedder/voyageai.py +69 -16
  89. agno/knowledge/knowledge.py +595 -187
  90. agno/knowledge/reader/base.py +9 -2
  91. agno/knowledge/reader/csv_reader.py +8 -10
  92. agno/knowledge/reader/docx_reader.py +5 -6
  93. agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
  94. agno/knowledge/reader/json_reader.py +6 -5
  95. agno/knowledge/reader/markdown_reader.py +13 -13
  96. agno/knowledge/reader/pdf_reader.py +43 -68
  97. agno/knowledge/reader/pptx_reader.py +101 -0
  98. agno/knowledge/reader/reader_factory.py +51 -6
  99. agno/knowledge/reader/s3_reader.py +3 -15
  100. agno/knowledge/reader/tavily_reader.py +194 -0
  101. agno/knowledge/reader/text_reader.py +13 -13
  102. agno/knowledge/reader/web_search_reader.py +2 -43
  103. agno/knowledge/reader/website_reader.py +43 -25
  104. agno/knowledge/reranker/__init__.py +3 -0
  105. agno/knowledge/types.py +9 -0
  106. agno/knowledge/utils.py +20 -0
  107. agno/media.py +339 -266
  108. agno/memory/manager.py +336 -82
  109. agno/models/aimlapi/aimlapi.py +2 -2
  110. agno/models/anthropic/claude.py +183 -37
  111. agno/models/aws/bedrock.py +52 -112
  112. agno/models/aws/claude.py +33 -1
  113. agno/models/azure/ai_foundry.py +33 -15
  114. agno/models/azure/openai_chat.py +25 -8
  115. agno/models/base.py +1011 -566
  116. agno/models/cerebras/cerebras.py +19 -13
  117. agno/models/cerebras/cerebras_openai.py +8 -5
  118. agno/models/cohere/chat.py +27 -1
  119. agno/models/cometapi/__init__.py +5 -0
  120. agno/models/cometapi/cometapi.py +57 -0
  121. agno/models/dashscope/dashscope.py +1 -0
  122. agno/models/deepinfra/deepinfra.py +2 -2
  123. agno/models/deepseek/deepseek.py +2 -2
  124. agno/models/fireworks/fireworks.py +2 -2
  125. agno/models/google/gemini.py +110 -37
  126. agno/models/groq/groq.py +28 -11
  127. agno/models/huggingface/huggingface.py +2 -1
  128. agno/models/internlm/internlm.py +2 -2
  129. agno/models/langdb/langdb.py +4 -4
  130. agno/models/litellm/chat.py +18 -1
  131. agno/models/litellm/litellm_openai.py +2 -2
  132. agno/models/llama_cpp/__init__.py +5 -0
  133. agno/models/llama_cpp/llama_cpp.py +22 -0
  134. agno/models/message.py +143 -4
  135. agno/models/meta/llama.py +27 -10
  136. agno/models/meta/llama_openai.py +5 -17
  137. agno/models/nebius/nebius.py +6 -6
  138. agno/models/nexus/__init__.py +3 -0
  139. agno/models/nexus/nexus.py +22 -0
  140. agno/models/nvidia/nvidia.py +2 -2
  141. agno/models/ollama/chat.py +60 -6
  142. agno/models/openai/chat.py +102 -43
  143. agno/models/openai/responses.py +103 -106
  144. agno/models/openrouter/openrouter.py +41 -3
  145. agno/models/perplexity/perplexity.py +4 -5
  146. agno/models/portkey/portkey.py +3 -3
  147. agno/models/requesty/__init__.py +5 -0
  148. agno/models/requesty/requesty.py +52 -0
  149. agno/models/response.py +81 -5
  150. agno/models/sambanova/sambanova.py +2 -2
  151. agno/models/siliconflow/__init__.py +5 -0
  152. agno/models/siliconflow/siliconflow.py +25 -0
  153. agno/models/together/together.py +2 -2
  154. agno/models/utils.py +254 -8
  155. agno/models/vercel/v0.py +2 -2
  156. agno/models/vertexai/__init__.py +0 -0
  157. agno/models/vertexai/claude.py +96 -0
  158. agno/models/vllm/vllm.py +1 -0
  159. agno/models/xai/xai.py +3 -2
  160. agno/os/app.py +543 -175
  161. agno/os/auth.py +24 -14
  162. agno/os/config.py +1 -0
  163. agno/os/interfaces/__init__.py +1 -0
  164. agno/os/interfaces/a2a/__init__.py +3 -0
  165. agno/os/interfaces/a2a/a2a.py +42 -0
  166. agno/os/interfaces/a2a/router.py +250 -0
  167. agno/os/interfaces/a2a/utils.py +924 -0
  168. agno/os/interfaces/agui/agui.py +23 -7
  169. agno/os/interfaces/agui/router.py +27 -3
  170. agno/os/interfaces/agui/utils.py +242 -142
  171. agno/os/interfaces/base.py +6 -2
  172. agno/os/interfaces/slack/router.py +81 -23
  173. agno/os/interfaces/slack/slack.py +29 -14
  174. agno/os/interfaces/whatsapp/router.py +11 -4
  175. agno/os/interfaces/whatsapp/whatsapp.py +14 -7
  176. agno/os/mcp.py +111 -54
  177. agno/os/middleware/__init__.py +7 -0
  178. agno/os/middleware/jwt.py +233 -0
  179. agno/os/router.py +556 -139
  180. agno/os/routers/evals/evals.py +71 -34
  181. agno/os/routers/evals/schemas.py +31 -31
  182. agno/os/routers/evals/utils.py +6 -5
  183. agno/os/routers/health.py +31 -0
  184. agno/os/routers/home.py +52 -0
  185. agno/os/routers/knowledge/knowledge.py +185 -38
  186. agno/os/routers/knowledge/schemas.py +82 -22
  187. agno/os/routers/memory/memory.py +158 -53
  188. agno/os/routers/memory/schemas.py +20 -16
  189. agno/os/routers/metrics/metrics.py +20 -8
  190. agno/os/routers/metrics/schemas.py +16 -16
  191. agno/os/routers/session/session.py +499 -38
  192. agno/os/schema.py +308 -198
  193. agno/os/utils.py +401 -41
  194. agno/reasoning/anthropic.py +80 -0
  195. agno/reasoning/azure_ai_foundry.py +2 -2
  196. agno/reasoning/deepseek.py +2 -2
  197. agno/reasoning/default.py +3 -1
  198. agno/reasoning/gemini.py +73 -0
  199. agno/reasoning/groq.py +2 -2
  200. agno/reasoning/ollama.py +2 -2
  201. agno/reasoning/openai.py +7 -2
  202. agno/reasoning/vertexai.py +76 -0
  203. agno/run/__init__.py +6 -0
  204. agno/run/agent.py +266 -112
  205. agno/run/base.py +53 -24
  206. agno/run/team.py +252 -111
  207. agno/run/workflow.py +156 -45
  208. agno/session/agent.py +105 -89
  209. agno/session/summary.py +65 -25
  210. agno/session/team.py +176 -96
  211. agno/session/workflow.py +406 -40
  212. agno/team/team.py +3854 -1692
  213. agno/tools/brightdata.py +3 -3
  214. agno/tools/cartesia.py +3 -5
  215. agno/tools/dalle.py +9 -8
  216. agno/tools/decorator.py +4 -2
  217. agno/tools/desi_vocal.py +2 -2
  218. agno/tools/duckduckgo.py +15 -11
  219. agno/tools/e2b.py +20 -13
  220. agno/tools/eleven_labs.py +26 -28
  221. agno/tools/exa.py +21 -16
  222. agno/tools/fal.py +4 -4
  223. agno/tools/file.py +153 -23
  224. agno/tools/file_generation.py +350 -0
  225. agno/tools/firecrawl.py +4 -4
  226. agno/tools/function.py +257 -37
  227. agno/tools/giphy.py +2 -2
  228. agno/tools/gmail.py +238 -14
  229. agno/tools/google_drive.py +270 -0
  230. agno/tools/googlecalendar.py +36 -8
  231. agno/tools/googlesheets.py +20 -5
  232. agno/tools/jira.py +20 -0
  233. agno/tools/knowledge.py +3 -3
  234. agno/tools/lumalab.py +3 -3
  235. agno/tools/mcp/__init__.py +10 -0
  236. agno/tools/mcp/mcp.py +331 -0
  237. agno/tools/mcp/multi_mcp.py +347 -0
  238. agno/tools/mcp/params.py +24 -0
  239. agno/tools/mcp_toolbox.py +284 -0
  240. agno/tools/mem0.py +11 -17
  241. agno/tools/memori.py +1 -53
  242. agno/tools/memory.py +419 -0
  243. agno/tools/models/azure_openai.py +2 -2
  244. agno/tools/models/gemini.py +3 -3
  245. agno/tools/models/groq.py +3 -5
  246. agno/tools/models/nebius.py +7 -7
  247. agno/tools/models_labs.py +25 -15
  248. agno/tools/notion.py +204 -0
  249. agno/tools/openai.py +4 -9
  250. agno/tools/opencv.py +3 -3
  251. agno/tools/parallel.py +314 -0
  252. agno/tools/replicate.py +7 -7
  253. agno/tools/scrapegraph.py +58 -31
  254. agno/tools/searxng.py +2 -2
  255. agno/tools/serper.py +2 -2
  256. agno/tools/slack.py +18 -3
  257. agno/tools/spider.py +2 -2
  258. agno/tools/tavily.py +146 -0
  259. agno/tools/whatsapp.py +1 -1
  260. agno/tools/workflow.py +278 -0
  261. agno/tools/yfinance.py +12 -11
  262. agno/utils/agent.py +820 -0
  263. agno/utils/audio.py +27 -0
  264. agno/utils/common.py +90 -1
  265. agno/utils/events.py +222 -7
  266. agno/utils/gemini.py +181 -23
  267. agno/utils/hooks.py +57 -0
  268. agno/utils/http.py +111 -0
  269. agno/utils/knowledge.py +12 -5
  270. agno/utils/log.py +1 -0
  271. agno/utils/mcp.py +95 -5
  272. agno/utils/media.py +188 -10
  273. agno/utils/merge_dict.py +22 -1
  274. agno/utils/message.py +60 -0
  275. agno/utils/models/claude.py +40 -11
  276. agno/utils/models/cohere.py +1 -1
  277. agno/utils/models/watsonx.py +1 -1
  278. agno/utils/openai.py +1 -1
  279. agno/utils/print_response/agent.py +105 -21
  280. agno/utils/print_response/team.py +103 -38
  281. agno/utils/print_response/workflow.py +251 -34
  282. agno/utils/reasoning.py +22 -1
  283. agno/utils/serialize.py +32 -0
  284. agno/utils/streamlit.py +16 -10
  285. agno/utils/string.py +41 -0
  286. agno/utils/team.py +98 -9
  287. agno/utils/tools.py +1 -1
  288. agno/vectordb/base.py +23 -4
  289. agno/vectordb/cassandra/cassandra.py +65 -9
  290. agno/vectordb/chroma/chromadb.py +182 -38
  291. agno/vectordb/clickhouse/clickhousedb.py +64 -11
  292. agno/vectordb/couchbase/couchbase.py +105 -10
  293. agno/vectordb/lancedb/lance_db.py +183 -135
  294. agno/vectordb/langchaindb/langchaindb.py +25 -7
  295. agno/vectordb/lightrag/lightrag.py +17 -3
  296. agno/vectordb/llamaindex/__init__.py +3 -0
  297. agno/vectordb/llamaindex/llamaindexdb.py +46 -7
  298. agno/vectordb/milvus/milvus.py +126 -9
  299. agno/vectordb/mongodb/__init__.py +7 -1
  300. agno/vectordb/mongodb/mongodb.py +112 -7
  301. agno/vectordb/pgvector/pgvector.py +142 -21
  302. agno/vectordb/pineconedb/pineconedb.py +80 -8
  303. agno/vectordb/qdrant/qdrant.py +125 -39
  304. agno/vectordb/redis/__init__.py +9 -0
  305. agno/vectordb/redis/redisdb.py +694 -0
  306. agno/vectordb/singlestore/singlestore.py +111 -25
  307. agno/vectordb/surrealdb/surrealdb.py +31 -5
  308. agno/vectordb/upstashdb/upstashdb.py +76 -8
  309. agno/vectordb/weaviate/weaviate.py +86 -15
  310. agno/workflow/__init__.py +2 -0
  311. agno/workflow/agent.py +299 -0
  312. agno/workflow/condition.py +112 -18
  313. agno/workflow/loop.py +69 -10
  314. agno/workflow/parallel.py +266 -118
  315. agno/workflow/router.py +110 -17
  316. agno/workflow/step.py +645 -136
  317. agno/workflow/steps.py +65 -6
  318. agno/workflow/types.py +71 -33
  319. agno/workflow/workflow.py +2113 -300
  320. agno-2.3.0.dist-info/METADATA +618 -0
  321. agno-2.3.0.dist-info/RECORD +577 -0
  322. agno-2.3.0.dist-info/licenses/LICENSE +201 -0
  323. agno/knowledge/reader/url_reader.py +0 -128
  324. agno/tools/googlesearch.py +0 -98
  325. agno/tools/mcp.py +0 -610
  326. agno/utils/models/aws_claude.py +0 -170
  327. agno-2.0.0rc2.dist-info/METADATA +0 -355
  328. agno-2.0.0rc2.dist-info/RECORD +0 -515
  329. agno-2.0.0rc2.dist-info/licenses/LICENSE +0 -375
  330. {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/WHEEL +0 -0
  331. {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,32 @@
1
+ """JSON serialization utilities for handling datetime and enum objects."""
2
+
3
+ from datetime import date, datetime, time
4
+ from enum import Enum
5
+ from typing import Any
6
+
7
+
8
+ def json_serializer(obj: Any) -> Any:
9
+ """Custom JSON serializer for objects not serializable by default json module.
10
+
11
+ Handles:
12
+ - datetime, date, time objects -> ISO format strings
13
+ - Enum objects -> their values (or names if values are not JSON-serializable)
14
+ - All other objects -> string representation
15
+
16
+ Args:
17
+ obj: Object to serialize
18
+
19
+ Returns:
20
+ JSON-serializable representation of the object
21
+ """
22
+ # Datetime like
23
+ if isinstance(obj, (datetime, date, time)):
24
+ return obj.isoformat()
25
+
26
+ # Enums
27
+ if isinstance(obj, Enum):
28
+ v = obj.value
29
+ return v if isinstance(v, (str, int, float, bool, type(None))) else obj.name
30
+
31
+ # Fallback to string
32
+ return str(obj)
agno/utils/streamlit.py CHANGED
@@ -1,14 +1,20 @@
1
1
  from datetime import datetime
2
2
  from typing import Any, Callable, Dict, List, Optional
3
3
 
4
- import streamlit as st
5
-
6
- from agno.agent import Agent
7
- from agno.db.base import SessionType
8
- from agno.models.anthropic import Claude
9
- from agno.models.google import Gemini
10
- from agno.models.openai import OpenAIChat
11
- from agno.utils.log import logger
4
+ try:
5
+ from agno.agent import Agent
6
+ from agno.db.base import SessionType
7
+ from agno.models.anthropic import Claude
8
+ from agno.models.google import Gemini
9
+ from agno.models.openai import OpenAIChat
10
+ from agno.utils.log import logger
11
+ except ImportError:
12
+ raise ImportError("`agno` not installed. Please install using `pip install agno`")
13
+
14
+ try:
15
+ import streamlit as st
16
+ except ImportError:
17
+ raise ImportError("`streamlit` not installed. Please install using `pip install streamlit`")
12
18
 
13
19
 
14
20
  def add_message(role: str, content: str, tool_calls: Optional[List[Dict[str, Any]]] = None) -> None:
@@ -74,7 +80,7 @@ def session_selector_widget(agent: Agent, model_id: str, agent_creation_callback
74
80
  session_options = []
75
81
  session_dict = {}
76
82
 
77
- for session in sessions:
83
+ for session in sessions: # type: ignore
78
84
  if not hasattr(session, "session_id") or not session.session_id:
79
85
  continue
80
86
 
@@ -446,7 +452,7 @@ MODELS = [
446
452
  "gpt-4o",
447
453
  "o3-mini",
448
454
  "gpt-5",
449
- "claude-4-sonnet",
455
+ "claude-sonnet-4-5-20250929",
450
456
  "gemini-2.5-pro",
451
457
  ]
452
458
 
agno/utils/string.py CHANGED
@@ -1,7 +1,9 @@
1
1
  import hashlib
2
2
  import json
3
3
  import re
4
+ import uuid
4
5
  from typing import Optional, Type
6
+ from uuid import uuid4
5
7
 
6
8
  from pydantic import BaseModel, ValidationError
7
9
 
@@ -156,6 +158,15 @@ def _parse_individual_json(content: str, output_schema: Type[BaseModel]) -> Opti
156
158
  def parse_response_model_str(content: str, output_schema: Type[BaseModel]) -> Optional[BaseModel]:
157
159
  structured_output = None
158
160
 
161
+ # Extract thinking content first to prevent <think> tags from corrupting JSON
162
+ from agno.utils.reasoning import extract_thinking_content
163
+
164
+ # handle thinking content b/w <think> tags
165
+ if "</think>" in content:
166
+ reasoning_content, output_content = extract_thinking_content(content)
167
+ if reasoning_content:
168
+ content = output_content
169
+
159
170
  # Clean content first to simplify all parsing attempts
160
171
  cleaned_content = _clean_json_content(content)
161
172
 
@@ -188,3 +199,33 @@ def parse_response_model_str(content: str, output_schema: Type[BaseModel]) -> Op
188
199
  logger.warning("All parsing attempts failed.")
189
200
 
190
201
  return structured_output
202
+
203
+
204
+ def generate_id(seed: Optional[str] = None) -> str:
205
+ """
206
+ Generate a deterministic UUID5 based on a seed string.
207
+ If no seed is provided, generate a random UUID4.
208
+
209
+ Args:
210
+ seed (str): The seed string to generate the UUID from.
211
+
212
+ Returns:
213
+ str: A deterministic UUID5 string.
214
+ """
215
+ if seed is None:
216
+ return str(uuid4())
217
+ return str(uuid.uuid5(uuid.NAMESPACE_DNS, seed))
218
+
219
+
220
+ def generate_id_from_name(name: Optional[str] = None) -> str:
221
+ """
222
+ Generate a deterministic ID from a name string.
223
+ If no name is provided, generate a random UUID4.
224
+
225
+ Args:
226
+ name (str): The name string to generate the ID from.
227
+ """
228
+ if name:
229
+ return name.lower().replace(" ", "-").replace("_", "-")
230
+ else:
231
+ return str(uuid4())
agno/utils/team.py CHANGED
@@ -1,6 +1,10 @@
1
- from typing import TYPE_CHECKING, Optional, Union
1
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
2
2
 
3
3
  from agno.agent import Agent
4
+ from agno.media import Audio, File, Image, Video
5
+ from agno.run.agent import RunOutput
6
+ from agno.run.team import TeamRunOutput
7
+ from agno.utils.log import log_debug
4
8
  from agno.utils.string import is_valid_uuid, url_safe_string
5
9
 
6
10
  if TYPE_CHECKING:
@@ -9,19 +13,20 @@ if TYPE_CHECKING:
9
13
 
10
14
  def format_member_agent_task(
11
15
  task_description: str,
12
- expected_output: Optional[str] = None,
13
16
  team_member_interactions_str: Optional[str] = None,
17
+ team_history_str: Optional[str] = None,
14
18
  ) -> str:
15
- member_agent_task = "You are a member of a team of agents. Your goal is to complete the following task:"
16
- member_agent_task += f"\n\n<task>\n{task_description}\n</task>"
17
-
18
- if expected_output is not None:
19
- member_agent_task += f"\n\n<expected_output>\n{expected_output}\n</expected_output>"
19
+ member_task_str = ""
20
20
 
21
21
  if team_member_interactions_str:
22
- member_agent_task += f"\n\n{team_member_interactions_str}"
22
+ member_task_str += f"{team_member_interactions_str}\n\n"
23
+
24
+ if team_history_str:
25
+ member_task_str += f"{team_history_str}\n\n"
23
26
 
24
- return member_agent_task
27
+ member_task_str += f"{task_description}"
28
+
29
+ return member_task_str
25
30
 
26
31
 
27
32
  def get_member_id(member: Union[Agent, "Team"]) -> str:
@@ -48,3 +53,87 @@ def get_member_id(member: Union[Agent, "Team"]) -> str:
48
53
  else:
49
54
  url_safe_member_id = None
50
55
  return url_safe_member_id
56
+
57
+
58
+ def add_interaction_to_team_run_context(
59
+ team_run_context: Dict[str, Any],
60
+ member_name: str,
61
+ task: str,
62
+ run_response: Union[RunOutput, TeamRunOutput],
63
+ ) -> None:
64
+ if "member_responses" not in team_run_context:
65
+ team_run_context["member_responses"] = []
66
+ team_run_context["member_responses"].append(
67
+ {
68
+ "member_name": member_name,
69
+ "task": task,
70
+ "run_response": run_response,
71
+ }
72
+ )
73
+ log_debug(f"Updated team run context with member name: {member_name}")
74
+
75
+
76
+ def get_team_member_interactions_str(team_run_context: Dict[str, Any]) -> str:
77
+ if not team_run_context:
78
+ return ""
79
+ team_member_interactions_str = ""
80
+ if "member_responses" in team_run_context:
81
+ team_member_interactions_str += "<member_interaction_context>\nSee below interactions wit other team members.\n"
82
+
83
+ for interaction in team_run_context["member_responses"]:
84
+ response_dict = interaction["run_response"].to_dict()
85
+ response_content = (
86
+ response_dict.get("content")
87
+ or ",".join([tool.get("content", "") for tool in response_dict.get("tools", [])])
88
+ or ""
89
+ )
90
+ team_member_interactions_str += f"Member: {interaction['member_name']}\n"
91
+ team_member_interactions_str += f"Task: {interaction['task']}\n"
92
+ team_member_interactions_str += f"Response: {response_content}\n"
93
+ team_member_interactions_str += "\n"
94
+ team_member_interactions_str += "</member_interaction_context>\n"
95
+ return team_member_interactions_str
96
+
97
+
98
+ def get_team_run_context_images(team_run_context: Dict[str, Any]) -> List[Image]:
99
+ if not team_run_context:
100
+ return []
101
+ images = []
102
+ if "member_responses" in team_run_context:
103
+ for interaction in team_run_context["member_responses"]:
104
+ if interaction["run_response"].images:
105
+ images.extend(interaction["run_response"].images)
106
+ return images
107
+
108
+
109
+ def get_team_run_context_videos(team_run_context: Dict[str, Any]) -> List[Video]:
110
+ if not team_run_context:
111
+ return []
112
+ videos = []
113
+ if "member_responses" in team_run_context:
114
+ for interaction in team_run_context["member_responses"]:
115
+ if interaction["run_response"].videos:
116
+ videos.extend(interaction["run_response"].videos)
117
+ return videos
118
+
119
+
120
+ def get_team_run_context_audio(team_run_context: Dict[str, Any]) -> List[Audio]:
121
+ if not team_run_context:
122
+ return []
123
+ audio = []
124
+ if "member_responses" in team_run_context:
125
+ for interaction in team_run_context["member_responses"]:
126
+ if interaction["run_response"].audio:
127
+ audio.extend(interaction["run_response"].audio)
128
+ return audio
129
+
130
+
131
+ def get_team_run_context_files(team_run_context: Dict[str, Any]) -> List[File]:
132
+ if not team_run_context:
133
+ return []
134
+ files = []
135
+ if "member_responses" in team_run_context:
136
+ for interaction in team_run_context["member_responses"]:
137
+ if interaction["run_response"].files:
138
+ files.extend(interaction["run_response"].files)
139
+ return files
agno/utils/tools.py CHANGED
@@ -13,7 +13,7 @@ def get_function_call_for_tool_call(
13
13
  _tool_call_function = tool_call.get("function")
14
14
  if _tool_call_function is not None:
15
15
  _tool_call_function_name = _tool_call_function.get("name")
16
- _tool_call_function_arguments_str = _tool_call_function.get("arguments")
16
+ _tool_call_function_arguments_str = _tool_call_function.get("arguments") or "{}"
17
17
  if _tool_call_function_name is not None:
18
18
  return get_function_call(
19
19
  name=_tool_call_function_name,
agno/vectordb/base.py CHANGED
@@ -2,11 +2,28 @@ from abc import ABC, abstractmethod
2
2
  from typing import Any, Dict, List, Optional
3
3
 
4
4
  from agno.knowledge.document import Document
5
+ from agno.utils.string import generate_id
5
6
 
6
7
 
7
8
  class VectorDb(ABC):
8
9
  """Base class for Vector Databases"""
9
10
 
11
+ def __init__(self, *, id: Optional[str] = None, name: Optional[str] = None, description: Optional[str] = None):
12
+ """Initialize base VectorDb.
13
+
14
+ Args:
15
+ id: Optional custom ID. If not provided, an id will be generated.
16
+ name: Optional name for the vector database.
17
+ description: Optional description for the vector database.
18
+ """
19
+ if name is None:
20
+ name = self.__class__.__name__
21
+
22
+ self.name = name
23
+ self.description = description
24
+ # Last resort fallback to generate id from name if ID not specified
25
+ self.id = id if id else generate_id(name)
26
+
10
27
  @abstractmethod
11
28
  def create(self) -> None:
12
29
  raise NotImplementedError
@@ -55,13 +72,11 @@ class VectorDb(ABC):
55
72
  raise NotImplementedError
56
73
 
57
74
  @abstractmethod
58
- def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
75
+ def search(self, query: str, limit: int = 5, filters: Optional[Any] = None) -> List[Document]:
59
76
  raise NotImplementedError
60
77
 
61
78
  @abstractmethod
62
- async def async_search(
63
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
64
- ) -> List[Document]:
79
+ async def async_search(self, query: str, limit: int = 5, filters: Optional[Any] = None) -> List[Document]:
65
80
  raise NotImplementedError
66
81
 
67
82
  @abstractmethod
@@ -106,3 +121,7 @@ class VectorDb(ABC):
106
121
  @abstractmethod
107
122
  def delete_by_content_id(self, content_id: str) -> bool:
108
123
  raise NotImplementedError
124
+
125
+ @abstractmethod
126
+ def get_supported_search_types(self) -> List[str]:
127
+ raise NotImplementedError
@@ -1,9 +1,10 @@
1
1
  import asyncio
2
- from typing import Any, Dict, Iterable, List, Optional
2
+ from typing import Any, Dict, Iterable, List, Optional, Union
3
3
 
4
+ from agno.filters import FilterExpr
4
5
  from agno.knowledge.document import Document
5
6
  from agno.knowledge.embedder import Embedder
6
- from agno.utils.log import log_debug, log_error, log_info
7
+ from agno.utils.log import log_debug, log_error, log_info, log_warning
7
8
  from agno.vectordb.base import VectorDb
8
9
  from agno.vectordb.cassandra.index import AgnoMetadataVectorCassandraTable
9
10
 
@@ -15,6 +16,8 @@ class Cassandra(VectorDb):
15
16
  keyspace: str,
16
17
  embedder: Optional[Embedder] = None,
17
18
  session=None,
19
+ name: Optional[str] = None,
20
+ description: Optional[str] = None,
18
21
  ) -> None:
19
22
  if not table_name:
20
23
  raise ValueError("Table name must be provided.")
@@ -30,6 +33,9 @@ class Cassandra(VectorDb):
30
33
 
31
34
  embedder = OpenAIEmbedder()
32
35
  log_info("Embedder not provided, using OpenAIEmbedder as default.")
36
+ # Initialize base class with name and description
37
+ super().__init__(name=name, description=description)
38
+
33
39
  self.table_name: str = table_name
34
40
  self.embedder: Embedder = embedder
35
41
  self.session = session
@@ -119,12 +125,52 @@ class Cassandra(VectorDb):
119
125
  """Insert documents asynchronously by running in a thread."""
120
126
  log_info(f"Cassandra VectorDB : Inserting Documents to the table {self.table_name}")
121
127
 
122
- for doc in documents:
128
+ if self.embedder.enable_batch and hasattr(self.embedder, "async_get_embeddings_batch_and_usage"):
129
+ # Use batch embedding when enabled and supported
123
130
  try:
124
- embed_tasks = [doc.async_embed(embedder=self.embedder)]
125
- await asyncio.gather(*embed_tasks, return_exceptions=True)
131
+ # Extract content from all documents
132
+ doc_contents = [doc.content for doc in documents]
133
+
134
+ # Get batch embeddings and usage
135
+ embeddings, usages = await self.embedder.async_get_embeddings_batch_and_usage(doc_contents)
136
+
137
+ # Process documents with pre-computed embeddings
138
+ for j, doc in enumerate(documents):
139
+ try:
140
+ if j < len(embeddings):
141
+ doc.embedding = embeddings[j]
142
+ doc.usage = usages[j] if j < len(usages) else None
143
+ except Exception as e:
144
+ log_error(f"Error assigning batch embedding to document '{doc.name}': {e}")
145
+
126
146
  except Exception as e:
127
- log_error(f"Error processing document '{doc.name}': {e}")
147
+ # Check if this is a rate limit error - don't fall back as it would make things worse
148
+ error_str = str(e).lower()
149
+ is_rate_limit = any(
150
+ phrase in error_str
151
+ for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
152
+ )
153
+
154
+ if is_rate_limit:
155
+ log_error(f"Rate limit detected during batch embedding. {e}")
156
+ raise e
157
+ else:
158
+ log_error(f"Async batch embedding failed, falling back to individual embeddings: {e}")
159
+ # Fall back to individual embedding
160
+ for doc in documents:
161
+ try:
162
+ embed_tasks = [doc.async_embed(embedder=self.embedder)]
163
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
164
+ except Exception as e:
165
+ log_error(f"Error processing document '{doc.name}': {e}")
166
+ else:
167
+ # Use individual embedding (original behavior)
168
+ for doc in documents:
169
+ try:
170
+ embed_tasks = [doc.async_embed(embedder=self.embedder)]
171
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
172
+ except Exception as e:
173
+ log_error(f"Error processing document '{doc.name}': {e}")
128
174
 
129
175
  futures = []
130
176
  for doc in documents:
@@ -159,13 +205,17 @@ class Cassandra(VectorDb):
159
205
  self.delete_by_content_hash(content_hash)
160
206
  await self.async_insert(content_hash, documents, filters)
161
207
 
162
- def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
208
+ def search(
209
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
210
+ ) -> List[Document]:
163
211
  """Keyword-based search on document metadata."""
164
212
  log_debug(f"Cassandra VectorDB : Performing Vector Search on {self.table_name} with query {query}")
213
+ if filters is not None:
214
+ log_warning("Filters are not yet supported in Cassandra. No filters will be applied.")
165
215
  return self.vector_search(query=query, limit=limit)
166
216
 
167
217
  async def async_search(
168
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
218
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
169
219
  ) -> List[Document]:
170
220
  """Search asynchronously by running in a thread."""
171
221
  return await asyncio.to_thread(self.search, query, limit, filters)
@@ -176,7 +226,9 @@ class Cassandra(VectorDb):
176
226
  ) -> List[Document]:
177
227
  return [self._row_to_document(row=hit) for hit in hits]
178
228
 
179
- def vector_search(self, query: str, limit: int = 5) -> List[Document]:
229
+ def vector_search(
230
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
231
+ ) -> List[Document]:
180
232
  """Vector similarity search implementation."""
181
233
  query_embedding = self.embedder.get_embedding(query)
182
234
  hits = list(
@@ -443,3 +495,7 @@ class Cassandra(VectorDb):
443
495
  except Exception as e:
444
496
  log_error(f"Error updating metadata for content_id {content_id}: {e}")
445
497
  raise
498
+
499
+ def get_supported_search_types(self) -> List[str]:
500
+ """Get the supported search types for this vector database."""
501
+ return [] # Cassandra doesn't use SearchType enum