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/api/api.py CHANGED
@@ -19,6 +19,7 @@ class Api:
19
19
  base_url=agno_api_settings.api_url,
20
20
  headers=self.headers,
21
21
  timeout=60,
22
+ http2=True,
22
23
  )
23
24
 
24
25
  def AsyncClient(self) -> HttpxAsyncClient:
@@ -26,6 +27,7 @@ class Api:
26
27
  base_url=agno_api_settings.api_url,
27
28
  headers=self.headers,
28
29
  timeout=60,
30
+ http2=True,
29
31
  )
30
32
 
31
33
 
agno/api/os.py CHANGED
@@ -14,4 +14,4 @@ def log_os_telemetry(launch: OSLaunch) -> None:
14
14
  )
15
15
  response.raise_for_status()
16
16
  except Exception as e:
17
- log_debug(f"Could not create OS launch: {e}")
17
+ log_debug(f"Could not register OS launch for telemetry: {e}")
@@ -0,0 +1,3 @@
1
+ from agno.compression.manager import CompressionManager
2
+
3
+ __all__ = ["CompressionManager"]
@@ -0,0 +1,247 @@
1
+ import asyncio
2
+ from dataclasses import dataclass, field
3
+ from textwrap import dedent
4
+ from typing import Any, Dict, List, Optional, Type, Union
5
+
6
+ from pydantic import BaseModel
7
+
8
+ from agno.models.base import Model
9
+ from agno.models.message import Message
10
+ from agno.models.utils import get_model
11
+ from agno.utils.log import log_error, log_info, log_warning
12
+
13
+ DEFAULT_COMPRESSION_PROMPT = dedent("""\
14
+ You are compressing tool call results to save context space while preserving critical information.
15
+
16
+ Your goal: Extract only the essential information from the tool output.
17
+
18
+ ALWAYS PRESERVE:
19
+ • Specific facts: numbers, statistics, amounts, prices, quantities, metrics
20
+ • Temporal data: dates, times, timestamps (use short format: "Oct 21 2025")
21
+ • Entities: people, companies, products, locations, organizations
22
+ • Identifiers: URLs, IDs, codes, technical identifiers, versions
23
+ • Key quotes, citations, sources (if relevant to agent's task)
24
+
25
+ COMPRESS TO ESSENTIALS:
26
+ • Descriptions: keep only key attributes
27
+ • Explanations: distill to core insight
28
+ • Lists: focus on most relevant items based on agent context
29
+ • Background: minimal context only if critical
30
+
31
+ REMOVE ENTIRELY:
32
+ • Introductions, conclusions, transitions
33
+ • Hedging language ("might", "possibly", "appears to")
34
+ • Meta-commentary ("According to", "The results show")
35
+ • Formatting artifacts (markdown, HTML, JSON structure)
36
+ • Redundant or repetitive information
37
+ • Generic background not relevant to agent's task
38
+ • Promotional language, filler words
39
+
40
+ EXAMPLE:
41
+ Input: "According to recent market analysis and industry reports, OpenAI has made several significant announcements in the technology sector. The company revealed ChatGPT Atlas on October 21, 2025, which represents a new AI-powered browser application that has been specifically designed for macOS users. This browser is strategically positioned to compete with traditional search engines in the market. Additionally, on October 6, 2025, OpenAI launched Apps in ChatGPT, which includes a comprehensive software development kit (SDK) for developers. The company has also announced several initial strategic partners who will be integrating with this new feature, including well-known companies such as Spotify, the popular music streaming service, Zillow, which is a real estate marketplace platform, and Canva, a graphic design platform."
42
+
43
+ Output: "OpenAI - Oct 21 2025: ChatGPT Atlas (AI browser, macOS, search competitor); Oct 6 2025: Apps in ChatGPT + SDK; Partners: Spotify, Zillow, Canva"
44
+
45
+ Be concise while retaining all critical facts.
46
+ """)
47
+
48
+
49
+ @dataclass
50
+ class CompressionManager:
51
+ model: Optional[Model] = None # model used for compression
52
+ compress_tool_results: bool = True
53
+ compress_tool_results_limit: Optional[int] = None
54
+ compress_token_limit: Optional[int] = None
55
+ compress_tool_call_instructions: Optional[str] = None
56
+
57
+ stats: Dict[str, Any] = field(default_factory=dict)
58
+
59
+ def __post_init__(self):
60
+ if self.compress_tool_results_limit is None and self.compress_token_limit is None:
61
+ self.compress_tool_results_limit = 3
62
+
63
+ def _is_tool_result_message(self, msg: Message) -> bool:
64
+ return msg.role == "tool"
65
+
66
+ def should_compress(
67
+ self,
68
+ messages: List[Message],
69
+ tools: Optional[List] = None,
70
+ model: Optional[Model] = None,
71
+ response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
72
+ ) -> bool:
73
+ """Check if tool results should be compressed.
74
+
75
+ Args:
76
+ messages: List of messages to check.
77
+ tools: List of tools for token counting.
78
+ model: The Agent / Team model.
79
+ response_format: Output schema for accurate token counting.
80
+ """
81
+ if not self.compress_tool_results:
82
+ return False
83
+
84
+ # Token-based threshold check
85
+ if self.compress_token_limit is not None and model is not None:
86
+ tokens = model.count_tokens(messages, tools, response_format)
87
+ if tokens >= self.compress_token_limit:
88
+ log_info(f"Token limit hit: {tokens} >= {self.compress_token_limit}")
89
+ return True
90
+
91
+ # Count-based threshold check
92
+ if self.compress_tool_results_limit is not None:
93
+ uncompressed_tools_count = len(
94
+ [m for m in messages if self._is_tool_result_message(m) and m.compressed_content is None]
95
+ )
96
+ if uncompressed_tools_count >= self.compress_tool_results_limit:
97
+ log_info(f"Tool count limit hit: {uncompressed_tools_count} >= {self.compress_tool_results_limit}")
98
+ return True
99
+
100
+ return False
101
+
102
+ def _compress_tool_result(self, tool_result: Message) -> Optional[str]:
103
+ if not tool_result:
104
+ return None
105
+
106
+ tool_content = f"Tool: {tool_result.tool_name or 'unknown'}\n{tool_result.content}"
107
+
108
+ self.model = get_model(self.model)
109
+ if not self.model:
110
+ log_warning("No compression model available")
111
+ return None
112
+
113
+ compression_prompt = self.compress_tool_call_instructions or DEFAULT_COMPRESSION_PROMPT
114
+ compression_message = "Tool Results to Compress: " + tool_content + "\n"
115
+
116
+ try:
117
+ response = self.model.response(
118
+ messages=[
119
+ Message(role="system", content=compression_prompt),
120
+ Message(role="user", content=compression_message),
121
+ ]
122
+ )
123
+ return response.content
124
+ except Exception as e:
125
+ log_error(f"Error compressing tool result: {e}")
126
+ return tool_content
127
+
128
+ def compress(self, messages: List[Message]) -> None:
129
+ """Compress uncompressed tool results"""
130
+ if not self.compress_tool_results:
131
+ return
132
+
133
+ uncompressed_tools = [msg for msg in messages if msg.role == "tool" and msg.compressed_content is None]
134
+
135
+ if not uncompressed_tools:
136
+ return
137
+
138
+ # Compress uncompressed tool results
139
+ for tool_msg in uncompressed_tools:
140
+ original_len = len(str(tool_msg.content)) if tool_msg.content else 0
141
+ compressed = self._compress_tool_result(tool_msg)
142
+ if compressed:
143
+ tool_msg.compressed_content = compressed
144
+ # Count actual tool results (Gemini combines multiple in one message)
145
+ tool_results_count = len(tool_msg.tool_calls) if tool_msg.tool_calls else 1
146
+ self.stats["tool_results_compressed"] = (
147
+ self.stats.get("tool_results_compressed", 0) + tool_results_count
148
+ )
149
+ self.stats["original_size"] = self.stats.get("original_size", 0) + original_len
150
+ self.stats["compressed_size"] = self.stats.get("compressed_size", 0) + len(compressed)
151
+ else:
152
+ log_warning(f"Compression failed for {tool_msg.tool_name}")
153
+
154
+ # * Async methods *#
155
+ async def ashould_compress(
156
+ self,
157
+ messages: List[Message],
158
+ tools: Optional[List] = None,
159
+ model: Optional[Model] = None,
160
+ response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
161
+ ) -> bool:
162
+ """Async check if tool results should be compressed.
163
+
164
+ Args:
165
+ messages: List of messages to check.
166
+ tools: List of tools for token counting.
167
+ model: The Agent / Team model.
168
+ response_format: Output schema for accurate token counting.
169
+ """
170
+ if not self.compress_tool_results:
171
+ return False
172
+
173
+ # Token-based threshold check
174
+ if self.compress_token_limit is not None and model is not None:
175
+ tokens = await model.acount_tokens(messages, tools, response_format)
176
+ if tokens >= self.compress_token_limit:
177
+ log_info(f"Token limit hit: {tokens} >= {self.compress_token_limit}")
178
+ return True
179
+
180
+ # Count-based threshold check
181
+ if self.compress_tool_results_limit is not None:
182
+ uncompressed_tools_count = len(
183
+ [m for m in messages if self._is_tool_result_message(m) and m.compressed_content is None]
184
+ )
185
+ if uncompressed_tools_count >= self.compress_tool_results_limit:
186
+ log_info(f"Tool count limit hit: {uncompressed_tools_count} >= {self.compress_tool_results_limit}")
187
+ return True
188
+
189
+ return False
190
+
191
+ async def _acompress_tool_result(self, tool_result: Message) -> Optional[str]:
192
+ """Async compress a single tool result"""
193
+ if not tool_result:
194
+ return None
195
+
196
+ tool_content = f"Tool: {tool_result.tool_name or 'unknown'}\n{tool_result.content}"
197
+
198
+ self.model = get_model(self.model)
199
+ if not self.model:
200
+ log_warning("No compression model available")
201
+ return None
202
+
203
+ compression_prompt = self.compress_tool_call_instructions or DEFAULT_COMPRESSION_PROMPT
204
+ compression_message = "Tool Results to Compress: " + tool_content + "\n"
205
+
206
+ try:
207
+ response = await self.model.aresponse(
208
+ messages=[
209
+ Message(role="system", content=compression_prompt),
210
+ Message(role="user", content=compression_message),
211
+ ]
212
+ )
213
+ return response.content
214
+ except Exception as e:
215
+ log_error(f"Error compressing tool result: {e}")
216
+ return tool_content
217
+
218
+ async def acompress(self, messages: List[Message]) -> None:
219
+ """Async compress uncompressed tool results"""
220
+ if not self.compress_tool_results:
221
+ return
222
+
223
+ uncompressed_tools = [msg for msg in messages if msg.role == "tool" and msg.compressed_content is None]
224
+
225
+ if not uncompressed_tools:
226
+ return
227
+
228
+ # Track original sizes before compression
229
+ original_sizes = [len(str(msg.content)) if msg.content else 0 for msg in uncompressed_tools]
230
+
231
+ # Parallel compression using asyncio.gather
232
+ tasks = [self._acompress_tool_result(msg) for msg in uncompressed_tools]
233
+ results = await asyncio.gather(*tasks)
234
+
235
+ # Apply results and track stats
236
+ for msg, compressed, original_len in zip(uncompressed_tools, results, original_sizes):
237
+ if compressed:
238
+ msg.compressed_content = compressed
239
+ # Count actual tool results (Gemini combines multiple in one message)
240
+ tool_results_count = len(msg.tool_calls) if msg.tool_calls else 1
241
+ self.stats["tool_results_compressed"] = (
242
+ self.stats.get("tool_results_compressed", 0) + tool_results_count
243
+ )
244
+ self.stats["original_size"] = self.stats.get("original_size", 0) + original_len
245
+ self.stats["compressed_size"] = self.stats.get("compressed_size", 0) + len(compressed)
246
+ else:
247
+ log_warning(f"Compression failed for {msg.tool_name}")
@@ -0,0 +1,3 @@
1
+ from agno.culture.manager import CulturalKnowledge, CultureManager
2
+
3
+ __all__ = ["CultureManager", "CulturalKnowledge"]