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.
- agno/agent/agent.py +5540 -2273
- agno/api/api.py +2 -0
- agno/api/os.py +1 -1
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +689 -6
- agno/db/dynamo/dynamo.py +933 -37
- agno/db/dynamo/schemas.py +174 -10
- agno/db/dynamo/utils.py +63 -4
- agno/db/firestore/firestore.py +831 -9
- agno/db/firestore/schemas.py +51 -0
- agno/db/firestore/utils.py +102 -4
- agno/db/gcs_json/gcs_json_db.py +660 -12
- agno/db/gcs_json/utils.py +60 -26
- agno/db/in_memory/in_memory_db.py +287 -14
- agno/db/in_memory/utils.py +60 -2
- agno/db/json/json_db.py +590 -14
- agno/db/json/utils.py +60 -26
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +43 -13
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +15 -1
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +879 -11
- agno/db/mongo/schemas.py +42 -0
- agno/db/mongo/utils.py +80 -8
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +946 -68
- agno/db/mysql/schemas.py +72 -10
- agno/db/mysql/utils.py +198 -7
- agno/db/postgres/__init__.py +2 -1
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +942 -57
- agno/db/postgres/schemas.py +81 -18
- agno/db/postgres/utils.py +164 -2
- agno/db/redis/redis.py +671 -7
- agno/db/redis/schemas.py +50 -0
- agno/db/redis/utils.py +65 -7
- agno/db/schemas/__init__.py +2 -1
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +1 -0
- agno/db/schemas/memory.py +17 -2
- agno/db/singlestore/schemas.py +63 -0
- agno/db/singlestore/singlestore.py +949 -83
- agno/db/singlestore/utils.py +60 -2
- agno/db/sqlite/__init__.py +2 -1
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +62 -0
- agno/db/sqlite/sqlite.py +965 -46
- agno/db/sqlite/utils.py +169 -8
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +334 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1908 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +2 -0
- agno/eval/__init__.py +10 -0
- agno/eval/accuracy.py +75 -55
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +16 -7
- agno/eval/reliability.py +28 -16
- agno/eval/utils.py +35 -17
- agno/exceptions.py +27 -2
- agno/filters.py +354 -0
- agno/guardrails/prompt_injection.py +1 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/client.py +1 -1
- agno/knowledge/chunking/agentic.py +13 -10
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/chunking/semantic.py +9 -4
- agno/knowledge/chunking/strategy.py +59 -15
- agno/knowledge/embedder/fastembed.py +1 -1
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/ollama.py +8 -0
- agno/knowledge/embedder/openai.py +8 -8
- agno/knowledge/embedder/sentence_transformer.py +6 -2
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/knowledge.py +1618 -318
- agno/knowledge/reader/base.py +6 -2
- agno/knowledge/reader/csv_reader.py +8 -10
- agno/knowledge/reader/docx_reader.py +5 -6
- agno/knowledge/reader/field_labeled_csv_reader.py +16 -20
- agno/knowledge/reader/json_reader.py +5 -4
- agno/knowledge/reader/markdown_reader.py +8 -8
- agno/knowledge/reader/pdf_reader.py +17 -19
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +32 -3
- agno/knowledge/reader/s3_reader.py +3 -3
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +22 -10
- agno/knowledge/reader/web_search_reader.py +1 -48
- agno/knowledge/reader/website_reader.py +10 -10
- agno/knowledge/reader/wikipedia_reader.py +33 -1
- agno/knowledge/types.py +1 -0
- agno/knowledge/utils.py +72 -7
- agno/media.py +22 -6
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +544 -83
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +66 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/aimlapi/aimlapi.py +17 -0
- agno/models/anthropic/claude.py +515 -40
- agno/models/aws/bedrock.py +102 -21
- agno/models/aws/claude.py +131 -274
- agno/models/azure/ai_foundry.py +41 -19
- agno/models/azure/openai_chat.py +39 -8
- agno/models/base.py +1249 -525
- agno/models/cerebras/cerebras.py +91 -21
- agno/models/cerebras/cerebras_openai.py +21 -2
- agno/models/cohere/chat.py +40 -6
- agno/models/cometapi/cometapi.py +18 -1
- agno/models/dashscope/dashscope.py +2 -3
- agno/models/deepinfra/deepinfra.py +18 -1
- agno/models/deepseek/deepseek.py +69 -3
- agno/models/fireworks/fireworks.py +18 -1
- agno/models/google/gemini.py +877 -80
- agno/models/google/utils.py +22 -0
- agno/models/groq/groq.py +51 -18
- agno/models/huggingface/huggingface.py +17 -6
- agno/models/ibm/watsonx.py +16 -6
- agno/models/internlm/internlm.py +18 -1
- agno/models/langdb/langdb.py +13 -1
- agno/models/litellm/chat.py +44 -9
- agno/models/litellm/litellm_openai.py +18 -1
- agno/models/message.py +28 -5
- agno/models/meta/llama.py +47 -14
- agno/models/meta/llama_openai.py +22 -17
- agno/models/mistral/mistral.py +8 -4
- agno/models/nebius/nebius.py +6 -7
- agno/models/nvidia/nvidia.py +20 -3
- agno/models/ollama/chat.py +24 -8
- agno/models/openai/chat.py +104 -29
- agno/models/openai/responses.py +101 -81
- agno/models/openrouter/openrouter.py +60 -3
- agno/models/perplexity/perplexity.py +17 -1
- agno/models/portkey/portkey.py +7 -6
- agno/models/requesty/requesty.py +24 -4
- agno/models/response.py +73 -2
- agno/models/sambanova/sambanova.py +20 -3
- agno/models/siliconflow/siliconflow.py +19 -2
- agno/models/together/together.py +20 -3
- agno/models/utils.py +254 -8
- agno/models/vercel/v0.py +20 -3
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/vllm.py +19 -14
- agno/models/xai/xai.py +19 -2
- agno/os/app.py +549 -152
- agno/os/auth.py +190 -3
- agno/os/config.py +23 -0
- agno/os/interfaces/a2a/router.py +8 -11
- agno/os/interfaces/a2a/utils.py +1 -1
- agno/os/interfaces/agui/router.py +18 -3
- agno/os/interfaces/agui/utils.py +152 -39
- agno/os/interfaces/slack/router.py +55 -37
- agno/os/interfaces/slack/slack.py +9 -1
- agno/os/interfaces/whatsapp/router.py +0 -1
- agno/os/interfaces/whatsapp/security.py +3 -1
- agno/os/mcp.py +110 -52
- agno/os/middleware/__init__.py +2 -0
- agno/os/middleware/jwt.py +676 -112
- agno/os/router.py +40 -1478
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +599 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/evals.py +96 -39
- agno/os/routers/evals/schemas.py +65 -33
- agno/os/routers/evals/utils.py +80 -10
- agno/os/routers/health.py +10 -4
- agno/os/routers/knowledge/knowledge.py +196 -38
- agno/os/routers/knowledge/schemas.py +82 -22
- agno/os/routers/memory/memory.py +279 -52
- agno/os/routers/memory/schemas.py +46 -17
- agno/os/routers/metrics/metrics.py +20 -8
- agno/os/routers/metrics/schemas.py +16 -16
- agno/os/routers/session/session.py +462 -34
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +512 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +499 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +624 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +256 -693
- agno/os/scopes.py +469 -0
- agno/os/utils.py +514 -36
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/openai.py +5 -0
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +155 -32
- agno/run/base.py +55 -3
- agno/run/requirement.py +181 -0
- agno/run/team.py +125 -38
- agno/run/workflow.py +72 -18
- agno/session/agent.py +102 -89
- agno/session/summary.py +56 -15
- agno/session/team.py +164 -90
- agno/session/workflow.py +405 -40
- agno/table.py +10 -0
- agno/team/team.py +3974 -1903
- agno/tools/dalle.py +2 -4
- agno/tools/eleven_labs.py +23 -25
- agno/tools/exa.py +21 -16
- agno/tools/file.py +153 -23
- agno/tools/file_generation.py +16 -10
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +193 -38
- agno/tools/gmail.py +238 -14
- agno/tools/google_drive.py +271 -0
- agno/tools/googlecalendar.py +36 -8
- agno/tools/googlesheets.py +20 -5
- agno/tools/jira.py +20 -0
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +3 -3
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/notion.py +204 -0
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +76 -36
- agno/tools/redshift.py +406 -0
- agno/tools/scrapegraph.py +1 -1
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +18 -3
- agno/tools/spotify.py +919 -0
- agno/tools/tavily.py +146 -0
- agno/tools/toolkit.py +25 -0
- agno/tools/workflow.py +8 -1
- agno/tools/yfinance.py +12 -11
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +157 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +111 -0
- agno/utils/agent.py +938 -0
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +151 -3
- agno/utils/gemini.py +15 -5
- agno/utils/hooks.py +118 -4
- agno/utils/http.py +113 -2
- agno/utils/knowledge.py +12 -5
- agno/utils/log.py +1 -0
- agno/utils/mcp.py +92 -2
- agno/utils/media.py +187 -1
- agno/utils/merge_dict.py +3 -3
- agno/utils/message.py +60 -0
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +49 -14
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/print_response/agent.py +109 -16
- agno/utils/print_response/team.py +223 -30
- agno/utils/print_response/workflow.py +251 -34
- agno/utils/streamlit.py +1 -1
- agno/utils/team.py +98 -9
- agno/utils/tokens.py +657 -0
- agno/vectordb/base.py +39 -7
- agno/vectordb/cassandra/cassandra.py +21 -5
- agno/vectordb/chroma/chromadb.py +43 -12
- agno/vectordb/clickhouse/clickhousedb.py +21 -5
- agno/vectordb/couchbase/couchbase.py +29 -5
- agno/vectordb/lancedb/lance_db.py +92 -181
- agno/vectordb/langchaindb/langchaindb.py +24 -4
- agno/vectordb/lightrag/lightrag.py +17 -3
- agno/vectordb/llamaindex/llamaindexdb.py +25 -5
- agno/vectordb/milvus/milvus.py +50 -37
- agno/vectordb/mongodb/__init__.py +7 -1
- agno/vectordb/mongodb/mongodb.py +36 -30
- agno/vectordb/pgvector/pgvector.py +201 -77
- agno/vectordb/pineconedb/pineconedb.py +41 -23
- agno/vectordb/qdrant/qdrant.py +67 -54
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/singlestore.py +50 -29
- agno/vectordb/surrealdb/surrealdb.py +31 -41
- agno/vectordb/upstashdb/upstashdb.py +34 -6
- agno/vectordb/weaviate/weaviate.py +53 -14
- agno/workflow/__init__.py +2 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +120 -18
- agno/workflow/loop.py +77 -10
- agno/workflow/parallel.py +231 -143
- agno/workflow/router.py +118 -17
- agno/workflow/step.py +609 -170
- agno/workflow/steps.py +73 -6
- agno/workflow/types.py +96 -21
- agno/workflow/workflow.py +2039 -262
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/METADATA +201 -66
- agno-2.3.13.dist-info/RECORD +613 -0
- agno/tools/googlesearch.py +0 -98
- agno/tools/mcp.py +0 -679
- agno/tools/memori.py +0 -339
- agno-2.1.2.dist-info/RECORD +0 -543
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +0 -0
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/licenses/LICENSE +0 -0
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
agno/culture/manager.py
ADDED
|
@@ -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) # type: ignore
|
|
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) # type: ignore
|
|
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
|