agno 2.0.1__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 (314) hide show
  1. agno/agent/agent.py +6015 -2823
  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 +594 -186
  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 +2 -8
  105. agno/knowledge/types.py +9 -0
  106. agno/knowledge/utils.py +20 -0
  107. agno/media.py +72 -0
  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 +999 -519
  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 +103 -31
  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 +139 -0
  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 +59 -5
  142. agno/models/openai/chat.py +69 -29
  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 +77 -1
  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 -178
  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 +248 -94
  205. agno/run/base.py +44 -5
  206. agno/run/team.py +238 -97
  207. agno/run/workflow.py +144 -33
  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 -1610
  213. agno/tools/dalle.py +2 -4
  214. agno/tools/decorator.py +4 -2
  215. agno/tools/duckduckgo.py +15 -11
  216. agno/tools/e2b.py +14 -7
  217. agno/tools/eleven_labs.py +23 -25
  218. agno/tools/exa.py +21 -16
  219. agno/tools/file.py +153 -23
  220. agno/tools/file_generation.py +350 -0
  221. agno/tools/firecrawl.py +4 -4
  222. agno/tools/function.py +250 -30
  223. agno/tools/gmail.py +238 -14
  224. agno/tools/google_drive.py +270 -0
  225. agno/tools/googlecalendar.py +36 -8
  226. agno/tools/googlesheets.py +20 -5
  227. agno/tools/jira.py +20 -0
  228. agno/tools/knowledge.py +3 -3
  229. agno/tools/mcp/__init__.py +10 -0
  230. agno/tools/mcp/mcp.py +331 -0
  231. agno/tools/mcp/multi_mcp.py +347 -0
  232. agno/tools/mcp/params.py +24 -0
  233. agno/tools/mcp_toolbox.py +284 -0
  234. agno/tools/mem0.py +11 -17
  235. agno/tools/memori.py +1 -53
  236. agno/tools/memory.py +419 -0
  237. agno/tools/models/nebius.py +5 -5
  238. agno/tools/models_labs.py +20 -10
  239. agno/tools/notion.py +204 -0
  240. agno/tools/parallel.py +314 -0
  241. agno/tools/scrapegraph.py +58 -31
  242. agno/tools/searxng.py +2 -2
  243. agno/tools/serper.py +2 -2
  244. agno/tools/slack.py +18 -3
  245. agno/tools/spider.py +2 -2
  246. agno/tools/tavily.py +146 -0
  247. agno/tools/whatsapp.py +1 -1
  248. agno/tools/workflow.py +278 -0
  249. agno/tools/yfinance.py +12 -11
  250. agno/utils/agent.py +820 -0
  251. agno/utils/audio.py +27 -0
  252. agno/utils/common.py +90 -1
  253. agno/utils/events.py +217 -2
  254. agno/utils/gemini.py +180 -22
  255. agno/utils/hooks.py +57 -0
  256. agno/utils/http.py +111 -0
  257. agno/utils/knowledge.py +12 -5
  258. agno/utils/log.py +1 -0
  259. agno/utils/mcp.py +92 -2
  260. agno/utils/media.py +188 -10
  261. agno/utils/merge_dict.py +22 -1
  262. agno/utils/message.py +60 -0
  263. agno/utils/models/claude.py +40 -11
  264. agno/utils/print_response/agent.py +105 -21
  265. agno/utils/print_response/team.py +103 -38
  266. agno/utils/print_response/workflow.py +251 -34
  267. agno/utils/reasoning.py +22 -1
  268. agno/utils/serialize.py +32 -0
  269. agno/utils/streamlit.py +16 -10
  270. agno/utils/string.py +41 -0
  271. agno/utils/team.py +98 -9
  272. agno/utils/tools.py +1 -1
  273. agno/vectordb/base.py +23 -4
  274. agno/vectordb/cassandra/cassandra.py +65 -9
  275. agno/vectordb/chroma/chromadb.py +182 -38
  276. agno/vectordb/clickhouse/clickhousedb.py +64 -11
  277. agno/vectordb/couchbase/couchbase.py +105 -10
  278. agno/vectordb/lancedb/lance_db.py +124 -133
  279. agno/vectordb/langchaindb/langchaindb.py +25 -7
  280. agno/vectordb/lightrag/lightrag.py +17 -3
  281. agno/vectordb/llamaindex/__init__.py +3 -0
  282. agno/vectordb/llamaindex/llamaindexdb.py +46 -7
  283. agno/vectordb/milvus/milvus.py +126 -9
  284. agno/vectordb/mongodb/__init__.py +7 -1
  285. agno/vectordb/mongodb/mongodb.py +112 -7
  286. agno/vectordb/pgvector/pgvector.py +142 -21
  287. agno/vectordb/pineconedb/pineconedb.py +80 -8
  288. agno/vectordb/qdrant/qdrant.py +125 -39
  289. agno/vectordb/redis/__init__.py +9 -0
  290. agno/vectordb/redis/redisdb.py +694 -0
  291. agno/vectordb/singlestore/singlestore.py +111 -25
  292. agno/vectordb/surrealdb/surrealdb.py +31 -5
  293. agno/vectordb/upstashdb/upstashdb.py +76 -8
  294. agno/vectordb/weaviate/weaviate.py +86 -15
  295. agno/workflow/__init__.py +2 -0
  296. agno/workflow/agent.py +299 -0
  297. agno/workflow/condition.py +112 -18
  298. agno/workflow/loop.py +69 -10
  299. agno/workflow/parallel.py +266 -118
  300. agno/workflow/router.py +110 -17
  301. agno/workflow/step.py +638 -129
  302. agno/workflow/steps.py +65 -6
  303. agno/workflow/types.py +61 -23
  304. agno/workflow/workflow.py +2085 -272
  305. {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/METADATA +182 -58
  306. agno-2.3.0.dist-info/RECORD +577 -0
  307. agno/knowledge/reader/url_reader.py +0 -128
  308. agno/tools/googlesearch.py +0 -98
  309. agno/tools/mcp.py +0 -610
  310. agno/utils/models/aws_claude.py +0 -170
  311. agno-2.0.1.dist-info/RECORD +0 -515
  312. {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/WHEEL +0 -0
  313. {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/licenses/LICENSE +0 -0
  314. {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,956 @@
1
+ from copy import deepcopy
2
+ from dataclasses import dataclass
3
+ from os import getenv
4
+ from textwrap import dedent
5
+ from typing import Any, Callable, Dict, List, Optional, Union, cast
6
+
7
+ from agno.db.base import AsyncBaseDb, BaseDb
8
+ from agno.db.schemas.culture import CulturalKnowledge
9
+ from agno.models.base import Model
10
+ from agno.models.message import Message
11
+ from agno.models.utils import get_model
12
+ from agno.tools.function import Function
13
+ from agno.utils.log import (
14
+ log_debug,
15
+ log_error,
16
+ log_warning,
17
+ set_log_level_to_debug,
18
+ set_log_level_to_info,
19
+ )
20
+
21
+
22
+ @dataclass
23
+ class CultureManager:
24
+ """Culture Manager
25
+
26
+ Notice: Culture is an experimental feature and is subject to change.
27
+ """
28
+
29
+ # Model used for culture management
30
+ model: Optional[Model] = None
31
+
32
+ # Provide the system message for the manager as a string. If not provided, the default system message will be used.
33
+ system_message: Optional[str] = None
34
+ # Provide the cultural knowledge capture instructions for the manager as a string. If not provided, the default cultural knowledge capture instructions will be used.
35
+ culture_capture_instructions: Optional[str] = None
36
+ # Additional instructions for the manager. These instructions are appended to the default system message.
37
+ additional_instructions: Optional[str] = None
38
+
39
+ # The database to store cultural knowledge
40
+ db: Optional[Union[AsyncBaseDb, BaseDb]] = None
41
+
42
+ # ----- Db tools ---------
43
+ # If the Culture Manager can add cultural knowledge
44
+ add_knowledge: bool = True
45
+ # If the Culture Manager can update cultural knowledge
46
+ update_knowledge: bool = True
47
+ # If the Culture Manager can delete cultural knowledge
48
+ delete_knowledge: bool = True
49
+ # If the Culture Manager can clear cultural knowledge
50
+ clear_knowledge: bool = True
51
+
52
+ # ----- Internal settings ---------
53
+ # Whether cultural knowledge were updated in the last run of the CultureManager
54
+ knowledge_updated: bool = False
55
+ debug_mode: bool = False
56
+
57
+ def __init__(
58
+ self,
59
+ model: Optional[Union[Model, str]] = None,
60
+ db: Optional[Union[BaseDb, AsyncBaseDb]] = None,
61
+ system_message: Optional[str] = None,
62
+ culture_capture_instructions: Optional[str] = None,
63
+ additional_instructions: Optional[str] = None,
64
+ add_knowledge: bool = True,
65
+ update_knowledge: bool = True,
66
+ delete_knowledge: bool = False,
67
+ clear_knowledge: bool = True,
68
+ debug_mode: bool = False,
69
+ ):
70
+ self.model = get_model(model)
71
+ self.db = db
72
+ self.system_message = system_message
73
+ self.culture_capture_instructions = culture_capture_instructions
74
+ self.additional_instructions = additional_instructions
75
+ self.add_knowledge = add_knowledge
76
+ self.update_knowledge = update_knowledge
77
+ self.delete_knowledge = delete_knowledge
78
+ self.clear_knowledge = clear_knowledge
79
+ self.debug_mode = debug_mode
80
+
81
+ def get_model(self) -> Model:
82
+ if self.model is None:
83
+ try:
84
+ from agno.models.openai import OpenAIChat
85
+ except ModuleNotFoundError as e:
86
+ log_error(e)
87
+ log_error(
88
+ "Agno uses `openai` as the default model provider. Please provide a `model` or install `openai`."
89
+ )
90
+ exit(1)
91
+ self.model = OpenAIChat(id="gpt-4o")
92
+ return self.model
93
+
94
+ def set_log_level(self):
95
+ if self.debug_mode or getenv("AGNO_DEBUG", "false").lower() == "true":
96
+ self.debug_mode = True
97
+ set_log_level_to_debug()
98
+ else:
99
+ set_log_level_to_info()
100
+
101
+ def initialize(self):
102
+ self.set_log_level()
103
+
104
+ # -*- Public functions
105
+ def get_knowledge(self, id: str) -> Optional[CulturalKnowledge]:
106
+ """Get the cultural knowledge by id"""
107
+ if not self.db:
108
+ return None
109
+
110
+ self.db = cast(BaseDb, self.db)
111
+
112
+ return self.db.get_cultural_knowledge(id=id)
113
+
114
+ async def aget_knowledge(self, id: str) -> Optional[CulturalKnowledge]:
115
+ """Get the cultural knowledge by id"""
116
+ if not self.db:
117
+ return None
118
+
119
+ self.db = cast(AsyncBaseDb, self.db)
120
+
121
+ return await self.db.get_cultural_knowledge(id=id)
122
+
123
+ def get_all_knowledge(self, name: Optional[str] = None) -> Optional[List[CulturalKnowledge]]:
124
+ """Get all cultural knowledge in the database"""
125
+ if not self.db:
126
+ return None
127
+
128
+ self.db = cast(BaseDb, self.db)
129
+
130
+ return self.db.get_all_cultural_knowledge(name=name)
131
+
132
+ async def aget_all_knowledge(self, name: Optional[str] = None) -> Optional[List[CulturalKnowledge]]:
133
+ """Get all cultural knowledge in the database"""
134
+ if not self.db:
135
+ return None
136
+
137
+ if isinstance(self.db, AsyncBaseDb):
138
+ return await self.db.get_all_cultural_knowledge(name=name)
139
+ else:
140
+ return self.db.get_all_cultural_knowledge(name=name)
141
+
142
+ def add_cultural_knowledge(
143
+ self,
144
+ knowledge: CulturalKnowledge,
145
+ ) -> Optional[str]:
146
+ """Add a cultural knowledge
147
+ Args:
148
+ knowledge (CulturalKnowledge): The knowledge to add
149
+ Returns:
150
+ str: The id of the knowledge
151
+ """
152
+ if self.db:
153
+ if knowledge.id is None:
154
+ from uuid import uuid4
155
+
156
+ knowledge_id = knowledge.id or str(uuid4())
157
+ knowledge.id = knowledge_id
158
+
159
+ if not knowledge.updated_at:
160
+ knowledge.bump_updated_at()
161
+
162
+ self._upsert_db_knowledge(knowledge=knowledge)
163
+ return knowledge.id
164
+
165
+ else:
166
+ log_warning("Cultural knowledge database not provided.")
167
+ return None
168
+
169
+ def clear_all_knowledge(self) -> None:
170
+ """Clears all cultural knowledge."""
171
+ if self.db:
172
+ self.db.clear_cultural_knowledge()
173
+
174
+ # -*- Agent Functions -*-
175
+ def create_cultural_knowledge(
176
+ self,
177
+ message: Optional[str] = None,
178
+ messages: Optional[List[Message]] = None,
179
+ ) -> str:
180
+ """Creates a cultural knowledge from a message or a list of messages"""
181
+ self.set_log_level()
182
+
183
+ if self.db is None:
184
+ log_warning("CultureDb not provided.")
185
+ return "Please provide a db to store cultural knowledge"
186
+
187
+ if not messages and not message:
188
+ raise ValueError("You must provide either a message or a list of messages")
189
+
190
+ if message:
191
+ messages = [Message(role="user", content=message)]
192
+
193
+ if not messages or not isinstance(messages, list):
194
+ raise ValueError("Invalid messages list")
195
+
196
+ cultural_knowledge = self.get_all_knowledge()
197
+ if cultural_knowledge is None:
198
+ cultural_knowledge = []
199
+
200
+ existing_knowledge = [cultural_knowledge.to_dict() for cultural_knowledge in cultural_knowledge]
201
+
202
+ self.db = cast(BaseDb, self.db)
203
+ response = self.create_or_update_cultural_knowledge(
204
+ messages=messages,
205
+ existing_knowledge=existing_knowledge,
206
+ db=self.db,
207
+ update_knowledge=self.update_knowledge,
208
+ add_knowledge=self.add_knowledge,
209
+ )
210
+
211
+ return response
212
+
213
+ async def acreate_cultural_knowledge(
214
+ self,
215
+ message: Optional[str] = None,
216
+ messages: Optional[List[Message]] = None,
217
+ ) -> str:
218
+ """Creates a cultural knowledge from a message or a list of messages"""
219
+ self.set_log_level()
220
+
221
+ if self.db is None:
222
+ log_warning("CultureDb not provided.")
223
+ return "Please provide a db to store cultural knowledge"
224
+
225
+ if not messages and not message:
226
+ raise ValueError("You must provide either a message or a list of messages")
227
+
228
+ if message:
229
+ messages = [Message(role="user", content=message)]
230
+
231
+ if not messages or not isinstance(messages, list):
232
+ raise ValueError("Invalid messages list")
233
+
234
+ if isinstance(self.db, AsyncBaseDb):
235
+ knowledge = await self.aget_all_knowledge()
236
+ else:
237
+ knowledge = self.get_all_knowledge()
238
+
239
+ if knowledge is None:
240
+ knowledge = []
241
+
242
+ existing_knowledge = [knowledge.preview() for knowledge in knowledge]
243
+
244
+ self.db = cast(AsyncBaseDb, self.db)
245
+ response = await self.acreate_or_update_cultural_knowledge(
246
+ messages=messages,
247
+ existing_knowledge=existing_knowledge,
248
+ db=self.db,
249
+ update_knowledge=self.update_knowledge,
250
+ add_knowledge=self.add_knowledge,
251
+ )
252
+
253
+ return response
254
+
255
+ def update_culture_task(self, task: str) -> str:
256
+ """Updates the culture with a task"""
257
+
258
+ if not self.db:
259
+ log_warning("CultureDb not provided.")
260
+ return "Please provide a db to store cultural knowledge"
261
+
262
+ if not isinstance(self.db, BaseDb):
263
+ raise ValueError(
264
+ "update_culture_task() is not supported with an async DB. Please use aupdate_culture_task() instead."
265
+ )
266
+
267
+ knowledge = self.get_all_knowledge()
268
+ if knowledge is None:
269
+ knowledge = []
270
+
271
+ existing_knowledge = [knowledge.preview() for knowledge in knowledge]
272
+
273
+ self.db = cast(BaseDb, self.db)
274
+ response = self.run_cultural_knowledge_task(
275
+ task=task,
276
+ existing_knowledge=existing_knowledge,
277
+ db=self.db,
278
+ delete_knowledge=self.delete_knowledge,
279
+ update_knowledge=self.update_knowledge,
280
+ add_knowledge=self.add_knowledge,
281
+ clear_knowledge=self.clear_knowledge,
282
+ )
283
+
284
+ return response
285
+
286
+ async def aupdate_culture_task(
287
+ self,
288
+ task: str,
289
+ ) -> str:
290
+ """Updates the culture with a task asynchronously"""
291
+
292
+ if not self.db:
293
+ log_warning("CultureDb not provided.")
294
+ return "Please provide a db to store cultural knowledge"
295
+
296
+ if not isinstance(self.db, AsyncBaseDb):
297
+ raise ValueError(
298
+ "aupdate_culture_task() is not supported with a sync DB. Please use update_culture_task() instead."
299
+ )
300
+
301
+ knowledge = await self.aget_all_knowledge()
302
+ if knowledge is None:
303
+ knowledge = []
304
+
305
+ existing_knowledge = [_knowledge.preview() for _knowledge in knowledge]
306
+
307
+ self.db = cast(AsyncBaseDb, self.db)
308
+ response = await self.arun_cultural_knowledge_task(
309
+ task=task,
310
+ existing_knowledge=existing_knowledge,
311
+ db=self.db,
312
+ delete_knowledge=self.delete_knowledge,
313
+ update_knowledge=self.update_knowledge,
314
+ add_knowledge=self.add_knowledge,
315
+ clear_knowledge=self.clear_knowledge,
316
+ )
317
+
318
+ return response
319
+
320
+ # -*- Utility Functions -*-
321
+ def _determine_tools_for_model(self, tools: List[Callable]) -> List[Union[Function, dict]]:
322
+ # Have to reset each time, because of different user IDs
323
+
324
+ _function_names = []
325
+ _functions: List[Union[Function, dict]] = []
326
+
327
+ for tool in tools:
328
+ try:
329
+ function_name = tool.__name__
330
+ if function_name in _function_names:
331
+ continue
332
+ _function_names.append(function_name)
333
+ func = Function.from_callable(tool, strict=True) # type: ignore
334
+ func.strict = True
335
+ _functions.append(func)
336
+ log_debug(f"Added function {func.name}")
337
+ except Exception as e:
338
+ log_warning(f"Could not add function {tool}: {e}")
339
+
340
+ return _functions
341
+
342
+ def get_system_message(
343
+ self,
344
+ existing_knowledge: Optional[List[Dict[str, Any]]] = None,
345
+ enable_delete_knowledge: bool = True,
346
+ enable_clear_knowledge: bool = True,
347
+ enable_update_knowledge: bool = True,
348
+ enable_add_knowledge: bool = True,
349
+ ) -> Message:
350
+ """Build the system prompt that instructs the model how to maintain cultural knowledge."""
351
+
352
+ if self.system_message is not None:
353
+ return Message(role="system", content=self.system_message)
354
+
355
+ # Default capture instructions
356
+ culture_capture_instructions = self.culture_capture_instructions or dedent(
357
+ """
358
+ Cultural knowledge should capture shared knowledge, insights, and practices that can improve performance across agents:
359
+ - Best practices and successful approaches discovered in previous interactions
360
+ - Common patterns in user behavior, team workflows, or recurring issues
361
+ - Processes, design principles, or rules of operation
362
+ - Guardrails, decision rationales, or ethical guidelines
363
+ - Domain-specific lessons that generalize beyond one case
364
+ - Communication styles or collaboration methods that lead to better outcomes
365
+ - Any other valuable insight that should persist across agents and time
366
+ """
367
+ )
368
+
369
+ system_prompt_lines: List[str] = [
370
+ "You are the **Cultural Knowledge Manager**, responsible for maintaining, evolving, and safeguarding "
371
+ "the shared cultural knowledge for Agents and Multi-Agent Teams. ",
372
+ "",
373
+ "Given a user message, your task is to distill, organize, and extract collective intelligence from it, including insights, lessons, "
374
+ "rules, principles, and narratives that guide future behavior across agents and teams.",
375
+ "",
376
+ "You will be provided with criteria for cultural knowledge to capture in the <knowledge_to_capture> section, "
377
+ "and the existing cultural knowledge in the <existing_knowledge> section.",
378
+ "",
379
+ "## When to add or update cultural knowledge",
380
+ "- Decide if knowledge should be **added, updated, deleted**, or if **no changes are needed**.",
381
+ "- If new insights meet the criteria in <knowledge_to_capture> and are not already captured in the <existing_knowledge> section, add them.",
382
+ "- If existing practices evolve, update relevant entries (while preserving historical context if useful).",
383
+ "- If nothing new or valuable emerged, respond with exactly: `No changes needed`.",
384
+ "",
385
+ "## How to add or update cultural knowledge",
386
+ "- Write entries that are **clear, specific, and actionable** (avoid vague abstractions).",
387
+ "- Each entry should capture one coherent idea or rule — use multiple entries if necessary.",
388
+ "- Do **not** duplicate information; update similar entries instead.",
389
+ "- When updating, append new insights rather than overwriting useful context.",
390
+ "- Use short Markdown lists, examples, or code blocks to increase clarity.",
391
+ "",
392
+ "## Criteria for creating cultural knowledge",
393
+ "<knowledge_to_capture>" + culture_capture_instructions + "</knowledge_to_capture>",
394
+ "",
395
+ "## Metadata & structure (use these fields when creating/updating)",
396
+ "- `name`: short, specific title (required).",
397
+ "- `summary`: one-line purpose or takeaway.",
398
+ "- `content`: reusable insight, rule, or guideline (required).",
399
+ "- `categories`: list of tags (e.g., ['guardrails', 'rules', 'principles', 'practices', 'patterns', 'behaviors', 'stories']).",
400
+ "- `notes`: list of contextual notes, rationale, or examples.",
401
+ "- `metadata`: optional structured info (e.g., source, author, version).",
402
+ "",
403
+ "## De-duplication, lineage, and precedence",
404
+ "- Search <existing_knowledge> by name/category before adding new entries.",
405
+ "- If a similar entry exists, **update** it instead of creating a duplicate.",
406
+ "- Preserve lineage via `notes` when revising entries.",
407
+ "- When entries conflict, prefer the entry with higher `confidence`.",
408
+ "",
409
+ "## Safety & privacy",
410
+ "- Never include secrets, credentials, personal data, or proprietary information.",
411
+ "",
412
+ "## Tool usage",
413
+ "You can call multiple tools in a single response. Use them only when valuable cultural knowledge emerges.",
414
+ ]
415
+
416
+ # Tool permissions (based on flags)
417
+ tool_lines: List[str] = []
418
+ if enable_add_knowledge:
419
+ tool_lines.append("- Add new entries using the `add_knowledge` tool.")
420
+ if enable_update_knowledge:
421
+ tool_lines.append("- Update existing entries using the `update_knowledge` tool.")
422
+ if enable_delete_knowledge:
423
+ tool_lines.append("- Delete entries using the `delete_knowledge` tool (use sparingly; prefer deprecate).")
424
+ if enable_clear_knowledge:
425
+ tool_lines.append("- Clear all entries using the `clear_knowledge` tool (only when explicitly instructed).")
426
+ if tool_lines:
427
+ system_prompt_lines += [""] + tool_lines
428
+
429
+ if existing_knowledge and len(existing_knowledge) > 0:
430
+ system_prompt_lines.append("\n<existing_knowledge>")
431
+ for _existing_knowledge in existing_knowledge: # type: ignore
432
+ system_prompt_lines.append("--------------------------------")
433
+ system_prompt_lines.append(f"Knowledge ID: {_existing_knowledge.get('id')}")
434
+ system_prompt_lines.append(f"Name: {_existing_knowledge.get('name')}")
435
+ system_prompt_lines.append(f"Summary: {_existing_knowledge.get('summary')}")
436
+ system_prompt_lines.append(f"Categories: {_existing_knowledge.get('categories')}")
437
+ system_prompt_lines.append(f"Content: {_existing_knowledge.get('content')}")
438
+ system_prompt_lines.append("</existing_knowledge>")
439
+
440
+ # Final guardrail for no-op
441
+ system_prompt_lines += [
442
+ "",
443
+ "## When no changes are needed",
444
+ "If no valuable cultural knowledge emerges, or everything is already captured, respond with exactly:",
445
+ "`No changes needed`",
446
+ ]
447
+
448
+ if self.additional_instructions:
449
+ system_prompt_lines.append(self.additional_instructions)
450
+
451
+ return Message(role="system", content="\n".join(system_prompt_lines))
452
+
453
+ def create_or_update_cultural_knowledge(
454
+ self,
455
+ messages: List[Message],
456
+ existing_knowledge: List[Dict[str, Any]],
457
+ db: BaseDb,
458
+ update_knowledge: bool = True,
459
+ add_knowledge: bool = True,
460
+ ) -> str:
461
+ if self.model is None:
462
+ log_error("No model provided for culture manager")
463
+ return "No model provided for culture manager"
464
+
465
+ log_debug("CultureManager Start", center=True)
466
+
467
+ model_copy = deepcopy(self.model)
468
+ # Update the Model (set defaults, add logit etc.)
469
+ _tools = self._determine_tools_for_model(
470
+ self._get_db_tools(
471
+ db,
472
+ enable_add_knowledge=add_knowledge,
473
+ enable_update_knowledge=update_knowledge,
474
+ enable_delete_knowledge=False,
475
+ enable_clear_knowledge=False,
476
+ ),
477
+ )
478
+
479
+ # Prepare the List of messages to send to the Model
480
+ messages_for_model: List[Message] = [
481
+ self.get_system_message(
482
+ existing_knowledge=existing_knowledge,
483
+ enable_update_knowledge=update_knowledge,
484
+ enable_add_knowledge=add_knowledge,
485
+ enable_delete_knowledge=False,
486
+ enable_clear_knowledge=False,
487
+ ),
488
+ *messages,
489
+ ]
490
+
491
+ # Generate a response from the Model (includes running function calls)
492
+ response = model_copy.response(
493
+ messages=messages_for_model,
494
+ tools=_tools,
495
+ )
496
+
497
+ if response.tool_calls is not None and len(response.tool_calls) > 0:
498
+ self.knowledge_updated = True
499
+
500
+ log_debug("Culture Manager End", center=True)
501
+
502
+ return response.content or "No response from model"
503
+
504
+ async def acreate_or_update_cultural_knowledge(
505
+ self,
506
+ messages: List[Message],
507
+ existing_knowledge: List[Dict[str, Any]],
508
+ db: AsyncBaseDb,
509
+ update_knowledge: bool = True,
510
+ add_knowledge: bool = True,
511
+ ) -> str:
512
+ if self.model is None:
513
+ log_error("No model provided for cultural manager")
514
+ return "No model provided for cultural manager"
515
+
516
+ log_debug("Cultural Manager Start", center=True)
517
+
518
+ model_copy = deepcopy(self.model)
519
+ db = cast(AsyncBaseDb, db)
520
+
521
+ _tools = self._determine_tools_for_model(
522
+ await self._aget_db_tools(
523
+ db,
524
+ enable_update_knowledge=update_knowledge,
525
+ enable_add_knowledge=add_knowledge,
526
+ ),
527
+ )
528
+
529
+ # Prepare the List of messages to send to the Model
530
+ messages_for_model: List[Message] = [
531
+ self.get_system_message(
532
+ existing_knowledge=existing_knowledge,
533
+ enable_update_knowledge=update_knowledge,
534
+ enable_add_knowledge=add_knowledge,
535
+ ),
536
+ # For models that require a non-system message
537
+ *messages,
538
+ ]
539
+
540
+ # Generate a response from the Model (includes running function calls)
541
+ response = await model_copy.aresponse(
542
+ messages=messages_for_model,
543
+ tools=_tools,
544
+ )
545
+
546
+ if response.tool_calls is not None and len(response.tool_calls) > 0:
547
+ self.knowledge_updated = True
548
+
549
+ log_debug("Cultural Knowledge Manager End", center=True)
550
+
551
+ return response.content or "No response from model"
552
+
553
+ def run_cultural_knowledge_task(
554
+ self,
555
+ task: str,
556
+ existing_knowledge: List[Dict[str, Any]],
557
+ db: BaseDb,
558
+ delete_knowledge: bool = True,
559
+ update_knowledge: bool = True,
560
+ add_knowledge: bool = True,
561
+ clear_knowledge: bool = True,
562
+ ) -> str:
563
+ if self.model is None:
564
+ log_error("No model provided for cultural manager")
565
+ return "No model provided for cultural manager"
566
+
567
+ log_debug("Cultural Knowledge Manager Start", center=True)
568
+
569
+ model_copy = deepcopy(self.model)
570
+ # Update the Model (set defaults, add logit etc.)
571
+ _tools = self._determine_tools_for_model(
572
+ self._get_db_tools(
573
+ db,
574
+ enable_delete_knowledge=delete_knowledge,
575
+ enable_clear_knowledge=clear_knowledge,
576
+ enable_update_knowledge=update_knowledge,
577
+ enable_add_knowledge=add_knowledge,
578
+ ),
579
+ )
580
+
581
+ # Prepare the List of messages to send to the Model
582
+ messages_for_model: List[Message] = [
583
+ self.get_system_message(
584
+ existing_knowledge,
585
+ enable_delete_knowledge=delete_knowledge,
586
+ enable_clear_knowledge=clear_knowledge,
587
+ enable_update_knowledge=update_knowledge,
588
+ enable_add_knowledge=add_knowledge,
589
+ ),
590
+ # For models that require a non-system message
591
+ Message(role="user", content=task),
592
+ ]
593
+
594
+ # Generate a response from the Model (includes running function calls)
595
+ response = model_copy.response(
596
+ messages=messages_for_model,
597
+ tools=_tools,
598
+ )
599
+
600
+ if response.tool_calls is not None and len(response.tool_calls) > 0:
601
+ self.knowledge_updated = True
602
+
603
+ log_debug("Cultural Knowledge Manager End", center=True)
604
+
605
+ return response.content or "No response from model"
606
+
607
+ async def arun_cultural_knowledge_task(
608
+ self,
609
+ task: str,
610
+ existing_knowledge: List[Dict[str, Any]],
611
+ db: Union[BaseDb, AsyncBaseDb],
612
+ delete_knowledge: bool = True,
613
+ clear_knowledge: bool = True,
614
+ update_knowledge: bool = True,
615
+ add_knowledge: bool = True,
616
+ ) -> str:
617
+ if self.model is None:
618
+ log_error("No model provided for cultural manager")
619
+ return "No model provided for cultural manager"
620
+
621
+ log_debug("Cultural Manager Start", center=True)
622
+
623
+ model_copy = deepcopy(self.model)
624
+ # Update the Model (set defaults, add logit etc.)
625
+ if isinstance(db, AsyncBaseDb):
626
+ _tools = self._determine_tools_for_model(
627
+ await self._aget_db_tools(
628
+ db,
629
+ enable_delete_knowledge=delete_knowledge,
630
+ enable_clear_knowledge=clear_knowledge,
631
+ enable_update_knowledge=update_knowledge,
632
+ enable_add_knowledge=add_knowledge,
633
+ ),
634
+ )
635
+ else:
636
+ _tools = self._determine_tools_for_model(
637
+ self._get_db_tools(
638
+ db,
639
+ enable_delete_knowledge=delete_knowledge,
640
+ enable_clear_knowledge=clear_knowledge,
641
+ enable_update_knowledge=update_knowledge,
642
+ enable_add_knowledge=add_knowledge,
643
+ ),
644
+ )
645
+
646
+ # Prepare the List of messages to send to the Model
647
+ messages_for_model: List[Message] = [
648
+ self.get_system_message(
649
+ existing_knowledge,
650
+ enable_delete_knowledge=delete_knowledge,
651
+ enable_clear_knowledge=clear_knowledge,
652
+ enable_update_knowledge=update_knowledge,
653
+ enable_add_knowledge=add_knowledge,
654
+ ),
655
+ # For models that require a non-system message
656
+ Message(role="user", content=task),
657
+ ]
658
+
659
+ # Generate a response from the Model (includes running function calls)
660
+ response = await model_copy.aresponse(
661
+ messages=messages_for_model,
662
+ tools=_tools,
663
+ )
664
+
665
+ if response.tool_calls is not None and len(response.tool_calls) > 0:
666
+ self.knowledge_updated = True
667
+
668
+ log_debug("Cultural Manager End", center=True)
669
+
670
+ return response.content or "No response from model"
671
+
672
+ # -*- DB Functions -*-
673
+ def _clear_db_knowledge(self) -> str:
674
+ """Use this function to clear all cultural knowledge from the database."""
675
+ try:
676
+ if not self.db:
677
+ raise ValueError("Culture db not initialized")
678
+ self.db = cast(BaseDb, self.db)
679
+ self.db.clear_cultural_knowledge()
680
+ return "Cultural knowledge cleared successfully"
681
+ except Exception as e:
682
+ log_warning(f"Error clearing cultural knowledge in db: {e}")
683
+ return f"Error clearing cultural knowledge: {e}"
684
+
685
+ async def _aclear_db_knowledge(self) -> str:
686
+ """Use this function to clear all cultural knowledge from the database."""
687
+ try:
688
+ if not self.db:
689
+ raise ValueError("Culture db not initialized")
690
+ self.db = cast(AsyncBaseDb, self.db)
691
+ await self.db.clear_cultural_knowledge()
692
+ return "Cultural knowledge cleared successfully"
693
+ except Exception as e:
694
+ log_warning(f"Error clearing cultural knowledge in db: {e}")
695
+ return f"Error clearing cultural knowledge: {e}"
696
+
697
+ def _delete_db_knowledge(self, knowledge_id: str) -> str:
698
+ """Use this function to delete a cultural knowledge from the database."""
699
+ try:
700
+ if not self.db:
701
+ raise ValueError("Culture db not initialized")
702
+ self.db = cast(BaseDb, self.db)
703
+ self.db.delete_cultural_knowledge(id=knowledge_id)
704
+ return "Cultural knowledge deleted successfully"
705
+ except Exception as e:
706
+ log_warning(f"Error deleting cultural knowledge in db: {e}")
707
+ return f"Error deleting cultural knowledge: {e}"
708
+
709
+ async def _adelete_db_knowledge(self, knowledge_id: str) -> str:
710
+ """Use this function to delete a cultural knowledge from the database."""
711
+ try:
712
+ if not self.db:
713
+ raise ValueError("Culture db not initialized")
714
+ self.db = cast(AsyncBaseDb, self.db)
715
+ await self.db.delete_cultural_knowledge(id=knowledge_id)
716
+ return "Cultural knowledge deleted successfully"
717
+ except Exception as e:
718
+ log_warning(f"Error deleting cultural knowledge in db: {e}")
719
+ return f"Error deleting cultural knowledge: {e}"
720
+
721
+ def _upsert_db_knowledge(self, knowledge: CulturalKnowledge) -> str:
722
+ """Use this function to add a cultural knowledge to the database."""
723
+ try:
724
+ if not self.db:
725
+ raise ValueError("Culture db not initialized")
726
+ self.db = cast(BaseDb, self.db)
727
+ self.db.upsert_cultural_knowledge(cultural_knowledge=knowledge)
728
+ return "Cultural knowledge added successfully"
729
+ except Exception as e:
730
+ log_warning(f"Error storing cultural knowledge in db: {e}")
731
+ return f"Error adding cultural knowledge: {e}"
732
+
733
+ # -* Get DB Tools -*-
734
+ def _get_db_tools(
735
+ self,
736
+ db: Union[BaseDb, AsyncBaseDb],
737
+ enable_add_knowledge: bool = True,
738
+ enable_update_knowledge: bool = True,
739
+ enable_delete_knowledge: bool = True,
740
+ enable_clear_knowledge: bool = True,
741
+ ) -> List[Callable]:
742
+ def add_cultural_knowledge(
743
+ name: str,
744
+ summary: Optional[str] = None,
745
+ content: Optional[str] = None,
746
+ categories: Optional[List[str]] = None,
747
+ ) -> str:
748
+ """Use this function to add a cultural knowledge to the database.
749
+ Args:
750
+ name (str): The name of the cultural knowledge. Short, specific title.
751
+ summary (Optional[str]): The summary of the cultural knowledge. One-line purpose or takeaway.
752
+ content (Optional[str]): The content of the cultural knowledge. Reusable insight, rule, or guideline.
753
+ categories (Optional[List[str]]): The categories of the cultural knowledge. List of tags (e.g. ["guardrails", "rules", "principles", "practices", "patterns", "behaviors", "stories"]).
754
+ Returns:
755
+ str: A message indicating if the cultural knowledge was added successfully or not.
756
+ """
757
+ from uuid import uuid4
758
+
759
+ try:
760
+ knowledge_id = str(uuid4())
761
+ db.upsert_cultural_knowledge(
762
+ CulturalKnowledge(
763
+ id=knowledge_id,
764
+ name=name,
765
+ summary=summary,
766
+ content=content,
767
+ categories=categories,
768
+ )
769
+ )
770
+ log_debug(f"Cultural knowledge added: {knowledge_id}")
771
+ return "Cultural knowledge added successfully"
772
+ except Exception as e:
773
+ log_warning(f"Error storing cultural knowledge in db: {e}")
774
+ return f"Error adding cultural knowledge: {e}"
775
+
776
+ def update_cultural_knowledge(
777
+ knowledge_id: str,
778
+ name: str,
779
+ summary: Optional[str] = None,
780
+ content: Optional[str] = None,
781
+ categories: Optional[List[str]] = None,
782
+ ) -> str:
783
+ """Use this function to update an existing cultural knowledge in the database.
784
+ Args:
785
+ knowledge_id (str): The id of the cultural knowledge to be updated.
786
+ name (str): The name of the cultural knowledge. Short, specific title.
787
+ summary (Optional[str]): The summary of the cultural knowledge. One-line purpose or takeaway.
788
+ content (Optional[str]): The content of the cultural knowledge. Reusable insight, rule, or guideline.
789
+ categories (Optional[List[str]]): The categories of the cultural knowledge. List of tags (e.g. ["guardrails", "rules", "principles", "practices", "patterns", "behaviors", "stories"]).
790
+ Returns:
791
+ str: A message indicating if the cultural knowledge was updated successfully or not.
792
+ """
793
+ from agno.db.base import CulturalKnowledge
794
+
795
+ try:
796
+ db.upsert_cultural_knowledge(
797
+ CulturalKnowledge(
798
+ id=knowledge_id,
799
+ name=name,
800
+ summary=summary,
801
+ content=content,
802
+ categories=categories,
803
+ )
804
+ )
805
+ log_debug("Cultural knowledge updated")
806
+ return "Cultural knowledge updated successfully"
807
+ except Exception as e:
808
+ log_warning(f"Error storing cultural knowledge in db: {e}")
809
+ return f"Error adding cultural knowledge: {e}"
810
+
811
+ def delete_cultural_knowledge(knowledge_id: str) -> str:
812
+ """Use this function to delete a single cultural knowledge from the database.
813
+ Args:
814
+ knowledge_id (str): The id of the cultural knowledge to be deleted.
815
+ Returns:
816
+ str: A message indicating if the cultural knowledge was deleted successfully or not.
817
+ """
818
+ try:
819
+ db.delete_cultural_knowledge(id=knowledge_id)
820
+ log_debug("Cultural knowledge deleted")
821
+ return "Cultural knowledge deleted successfully"
822
+ except Exception as e:
823
+ log_warning(f"Error deleting cultural knowledge in db: {e}")
824
+ return f"Error deleting cultural knowledge: {e}"
825
+
826
+ def clear_cultural_knowledge() -> str:
827
+ """Use this function to remove all (or clear all) cultural knowledge from the database.
828
+ Returns:
829
+ str: A message indicating if the cultural knowledge was cleared successfully or not.
830
+ """
831
+ db.clear_cultural_knowledge()
832
+ log_debug("Cultural knowledge cleared")
833
+ return "Cultural knowledge cleared successfully"
834
+
835
+ functions: List[Callable] = []
836
+ if enable_add_knowledge:
837
+ functions.append(add_cultural_knowledge)
838
+ if enable_update_knowledge:
839
+ functions.append(update_cultural_knowledge)
840
+ if enable_delete_knowledge:
841
+ functions.append(delete_cultural_knowledge)
842
+ if enable_clear_knowledge:
843
+ functions.append(clear_cultural_knowledge)
844
+ return functions
845
+
846
+ async def _aget_db_tools(
847
+ self,
848
+ db: AsyncBaseDb,
849
+ enable_add_knowledge: bool = True,
850
+ enable_update_knowledge: bool = True,
851
+ enable_delete_knowledge: bool = True,
852
+ enable_clear_knowledge: bool = True,
853
+ ) -> List[Callable]:
854
+ async def add_cultural_knowledge(
855
+ name: str,
856
+ summary: Optional[str] = None,
857
+ content: Optional[str] = None,
858
+ categories: Optional[List[str]] = None,
859
+ ) -> str:
860
+ """Use this function to add a cultural knowledge to the database.
861
+ Args:
862
+ name (str): The name of the cultural knowledge.
863
+ summary (Optional[str]): The summary of the cultural knowledge.
864
+ content (Optional[str]): The content of the cultural knowledge.
865
+ categories (Optional[List[str]]): The categories of the cultural knowledge (e.g. ["name", "hobbies", "location"]).
866
+ Returns:
867
+ str: A message indicating if the cultural knowledge was added successfully or not.
868
+ """
869
+ from uuid import uuid4
870
+
871
+ try:
872
+ knowledge_id = str(uuid4())
873
+ await db.upsert_cultural_knowledge(
874
+ CulturalKnowledge(
875
+ id=knowledge_id,
876
+ name=name,
877
+ summary=summary,
878
+ content=content,
879
+ categories=categories,
880
+ )
881
+ )
882
+ log_debug(f"Cultural knowledge added: {knowledge_id}")
883
+ return "Cultural knowledge added successfully"
884
+ except Exception as e:
885
+ log_warning(f"Error storing cultural knowledge in db: {e}")
886
+ return f"Error adding cultural knowledge: {e}"
887
+
888
+ async def update_cultural_knowledge(
889
+ knowledge_id: str,
890
+ name: str,
891
+ summary: Optional[str] = None,
892
+ content: Optional[str] = None,
893
+ categories: Optional[List[str]] = None,
894
+ ) -> str:
895
+ """Use this function to update an existing cultural knowledge in the database.
896
+ Args:
897
+ knowledge_id (str): The id of the cultural knowledge to be updated.
898
+ name (str): The name of the cultural knowledge.
899
+ summary (Optional[str]): The summary of the cultural knowledge.
900
+ content (Optional[str]): The content of the cultural knowledge.
901
+ categories (Optional[List[str]]): The categories of the cultural knowledge (e.g. ["name", "hobbies", "location"]).
902
+ Returns:
903
+ str: A message indicating if the cultural knowledge was updated successfully or not.
904
+ """
905
+ from agno.db.base import CulturalKnowledge
906
+
907
+ try:
908
+ await db.upsert_cultural_knowledge(
909
+ CulturalKnowledge(
910
+ id=knowledge_id,
911
+ name=name,
912
+ summary=summary,
913
+ content=content,
914
+ categories=categories,
915
+ )
916
+ )
917
+ log_debug("Cultural knowledge updated")
918
+ return "Cultural knowledge updated successfully"
919
+ except Exception as e:
920
+ log_warning(f"Error storing cultural knowledge in db: {e}")
921
+ return f"Error updating cultural knowledge: {e}"
922
+
923
+ async def delete_cultural_knowledge(knowledge_id: str) -> str:
924
+ """Use this function to delete a single cultural knowledge from the database.
925
+ Args:
926
+ knowledge_id (str): The id of the cultural knowledge to be deleted.
927
+ Returns:
928
+ str: A message indicating if the cultural knowledge was deleted successfully or not.
929
+ """
930
+ try:
931
+ await db.delete_cultural_knowledge(id=knowledge_id)
932
+ log_debug("Cultural knowledge deleted")
933
+ return "Cultural knowledge deleted successfully"
934
+ except Exception as e:
935
+ log_warning(f"Error deleting cultural knowledge in db: {e}")
936
+ return f"Error deleting cultural knowledge: {e}"
937
+
938
+ async def clear_cultural_knowledge() -> str:
939
+ """Use this function to remove all (or clear all) cultural knowledge from the database.
940
+ Returns:
941
+ str: A message indicating if the cultural knowledge was cleared successfully or not.
942
+ """
943
+ await db.clear_cultural_knowledge()
944
+ log_debug("Cultural knowledge cleared")
945
+ return "Cultural knowledge cleared successfully"
946
+
947
+ functions: List[Callable] = []
948
+ if enable_add_knowledge:
949
+ functions.append(add_cultural_knowledge)
950
+ if enable_update_knowledge:
951
+ functions.append(update_cultural_knowledge)
952
+ if enable_delete_knowledge:
953
+ functions.append(delete_cultural_knowledge)
954
+ if enable_clear_knowledge:
955
+ functions.append(clear_cultural_knowledge)
956
+ return functions