agno 2.2.13__py3-none-any.whl → 2.4.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/agent/__init__.py +6 -0
- agno/agent/agent.py +5252 -3145
- agno/agent/remote.py +525 -0
- agno/api/api.py +2 -0
- agno/client/__init__.py +3 -0
- agno/client/a2a/__init__.py +10 -0
- agno/client/a2a/client.py +554 -0
- agno/client/a2a/schemas.py +112 -0
- agno/client/a2a/utils.py +369 -0
- agno/client/os.py +2669 -0
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/manager.py +2 -2
- agno/db/base.py +927 -6
- agno/db/dynamo/dynamo.py +788 -2
- agno/db/dynamo/schemas.py +128 -0
- agno/db/dynamo/utils.py +26 -3
- agno/db/firestore/firestore.py +674 -50
- agno/db/firestore/schemas.py +41 -0
- agno/db/firestore/utils.py +25 -10
- agno/db/gcs_json/gcs_json_db.py +506 -3
- agno/db/gcs_json/utils.py +14 -2
- agno/db/in_memory/in_memory_db.py +203 -4
- agno/db/in_memory/utils.py +14 -2
- agno/db/json/json_db.py +498 -2
- agno/db/json/utils.py +14 -2
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/utils.py +19 -0
- agno/db/migrations/v1_to_v2.py +54 -16
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +977 -0
- agno/db/mongo/async_mongo.py +1013 -39
- agno/db/mongo/mongo.py +684 -4
- agno/db/mongo/schemas.py +48 -0
- agno/db/mongo/utils.py +17 -0
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2958 -0
- agno/db/mysql/mysql.py +722 -53
- agno/db/mysql/schemas.py +77 -11
- agno/db/mysql/utils.py +151 -8
- agno/db/postgres/async_postgres.py +1254 -137
- agno/db/postgres/postgres.py +2316 -93
- agno/db/postgres/schemas.py +153 -21
- agno/db/postgres/utils.py +22 -7
- agno/db/redis/redis.py +531 -3
- agno/db/redis/schemas.py +36 -0
- agno/db/redis/utils.py +31 -15
- agno/db/schemas/evals.py +1 -0
- agno/db/schemas/memory.py +20 -9
- agno/db/singlestore/schemas.py +70 -1
- agno/db/singlestore/singlestore.py +737 -74
- agno/db/singlestore/utils.py +13 -3
- agno/db/sqlite/async_sqlite.py +1069 -89
- agno/db/sqlite/schemas.py +133 -1
- agno/db/sqlite/sqlite.py +2203 -165
- agno/db/sqlite/utils.py +21 -11
- agno/db/surrealdb/models.py +25 -0
- agno/db/surrealdb/surrealdb.py +603 -1
- agno/db/utils.py +60 -0
- agno/eval/__init__.py +26 -3
- agno/eval/accuracy.py +25 -12
- agno/eval/agent_as_judge.py +871 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +10 -4
- agno/eval/reliability.py +22 -13
- agno/eval/utils.py +2 -1
- agno/exceptions.py +42 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/client.py +13 -2
- agno/knowledge/__init__.py +4 -0
- agno/knowledge/chunking/code.py +90 -0
- agno/knowledge/chunking/document.py +65 -4
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/chunking/markdown.py +102 -11
- agno/knowledge/chunking/recursive.py +2 -2
- agno/knowledge/chunking/semantic.py +130 -48
- agno/knowledge/chunking/strategy.py +18 -0
- agno/knowledge/embedder/azure_openai.py +0 -1
- agno/knowledge/embedder/google.py +1 -1
- agno/knowledge/embedder/mistral.py +1 -1
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/openai.py +16 -12
- agno/knowledge/filesystem.py +412 -0
- agno/knowledge/knowledge.py +4261 -1199
- agno/knowledge/protocol.py +134 -0
- agno/knowledge/reader/arxiv_reader.py +3 -2
- agno/knowledge/reader/base.py +9 -7
- agno/knowledge/reader/csv_reader.py +91 -42
- agno/knowledge/reader/docx_reader.py +9 -10
- agno/knowledge/reader/excel_reader.py +225 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +38 -48
- agno/knowledge/reader/firecrawl_reader.py +3 -2
- agno/knowledge/reader/json_reader.py +16 -22
- agno/knowledge/reader/markdown_reader.py +15 -14
- agno/knowledge/reader/pdf_reader.py +33 -28
- agno/knowledge/reader/pptx_reader.py +9 -10
- agno/knowledge/reader/reader_factory.py +135 -1
- agno/knowledge/reader/s3_reader.py +8 -16
- agno/knowledge/reader/tavily_reader.py +3 -3
- agno/knowledge/reader/text_reader.py +15 -14
- agno/knowledge/reader/utils/__init__.py +17 -0
- agno/knowledge/reader/utils/spreadsheet.py +114 -0
- agno/knowledge/reader/web_search_reader.py +8 -65
- agno/knowledge/reader/website_reader.py +16 -13
- agno/knowledge/reader/wikipedia_reader.py +36 -3
- agno/knowledge/reader/youtube_reader.py +3 -2
- agno/knowledge/remote_content/__init__.py +33 -0
- agno/knowledge/remote_content/config.py +266 -0
- agno/knowledge/remote_content/remote_content.py +105 -17
- agno/knowledge/utils.py +76 -22
- agno/learn/__init__.py +71 -0
- agno/learn/config.py +463 -0
- agno/learn/curate.py +185 -0
- agno/learn/machine.py +725 -0
- agno/learn/schemas.py +1114 -0
- agno/learn/stores/__init__.py +38 -0
- agno/learn/stores/decision_log.py +1156 -0
- agno/learn/stores/entity_memory.py +3275 -0
- agno/learn/stores/learned_knowledge.py +1583 -0
- agno/learn/stores/protocol.py +117 -0
- agno/learn/stores/session_context.py +1217 -0
- agno/learn/stores/user_memory.py +1495 -0
- agno/learn/stores/user_profile.py +1220 -0
- agno/learn/utils.py +209 -0
- agno/media.py +22 -6
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +223 -8
- 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 +434 -59
- agno/models/aws/bedrock.py +121 -20
- agno/models/aws/claude.py +131 -274
- agno/models/azure/ai_foundry.py +10 -6
- agno/models/azure/openai_chat.py +33 -10
- agno/models/base.py +1162 -561
- agno/models/cerebras/cerebras.py +120 -24
- agno/models/cerebras/cerebras_openai.py +21 -2
- agno/models/cohere/chat.py +65 -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 +959 -89
- agno/models/google/utils.py +22 -0
- agno/models/groq/groq.py +48 -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 +88 -9
- agno/models/litellm/litellm_openai.py +18 -1
- agno/models/message.py +24 -5
- agno/models/meta/llama.py +40 -13
- agno/models/meta/llama_openai.py +22 -21
- agno/models/metrics.py +12 -0
- agno/models/mistral/mistral.py +8 -4
- agno/models/n1n/__init__.py +3 -0
- agno/models/n1n/n1n.py +57 -0
- agno/models/nebius/nebius.py +6 -7
- agno/models/nvidia/nvidia.py +20 -3
- agno/models/ollama/__init__.py +2 -0
- agno/models/ollama/chat.py +17 -6
- agno/models/ollama/responses.py +100 -0
- agno/models/openai/__init__.py +2 -0
- agno/models/openai/chat.py +117 -26
- agno/models/openai/open_responses.py +46 -0
- agno/models/openai/responses.py +110 -32
- agno/models/openrouter/__init__.py +2 -0
- agno/models/openrouter/openrouter.py +67 -2
- agno/models/openrouter/responses.py +146 -0
- agno/models/perplexity/perplexity.py +19 -1
- agno/models/portkey/portkey.py +7 -6
- agno/models/requesty/requesty.py +19 -2
- agno/models/response.py +20 -2
- agno/models/sambanova/sambanova.py +20 -3
- agno/models/siliconflow/siliconflow.py +19 -2
- agno/models/together/together.py +20 -3
- agno/models/vercel/v0.py +20 -3
- agno/models/vertexai/claude.py +124 -4
- agno/models/vllm/vllm.py +19 -14
- agno/models/xai/xai.py +19 -2
- agno/os/app.py +467 -137
- agno/os/auth.py +253 -5
- agno/os/config.py +22 -0
- agno/os/interfaces/a2a/a2a.py +7 -6
- agno/os/interfaces/a2a/router.py +635 -26
- agno/os/interfaces/a2a/utils.py +32 -33
- agno/os/interfaces/agui/agui.py +5 -3
- agno/os/interfaces/agui/router.py +26 -16
- agno/os/interfaces/agui/utils.py +97 -57
- agno/os/interfaces/base.py +7 -7
- agno/os/interfaces/slack/router.py +16 -7
- agno/os/interfaces/slack/slack.py +7 -7
- agno/os/interfaces/whatsapp/router.py +35 -7
- agno/os/interfaces/whatsapp/security.py +3 -1
- agno/os/interfaces/whatsapp/whatsapp.py +11 -8
- agno/os/managers.py +326 -0
- agno/os/mcp.py +652 -79
- agno/os/middleware/__init__.py +4 -0
- agno/os/middleware/jwt.py +718 -115
- agno/os/middleware/trailing_slash.py +27 -0
- agno/os/router.py +105 -1558
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +655 -0
- agno/os/routers/agents/schema.py +288 -0
- agno/os/routers/components/__init__.py +3 -0
- agno/os/routers/components/components.py +475 -0
- agno/os/routers/database.py +155 -0
- agno/os/routers/evals/evals.py +111 -18
- agno/os/routers/evals/schemas.py +38 -5
- agno/os/routers/evals/utils.py +80 -11
- agno/os/routers/health.py +3 -3
- agno/os/routers/knowledge/knowledge.py +284 -35
- agno/os/routers/knowledge/schemas.py +14 -2
- agno/os/routers/memory/memory.py +274 -11
- agno/os/routers/memory/schemas.py +44 -3
- agno/os/routers/metrics/metrics.py +30 -15
- agno/os/routers/metrics/schemas.py +10 -6
- agno/os/routers/registry/__init__.py +3 -0
- agno/os/routers/registry/registry.py +337 -0
- agno/os/routers/session/session.py +143 -14
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +550 -0
- agno/os/routers/teams/schema.py +280 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +549 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +757 -0
- agno/os/routers/workflows/schema.py +139 -0
- agno/os/schema.py +157 -584
- agno/os/scopes.py +469 -0
- agno/os/settings.py +3 -0
- agno/os/utils.py +574 -185
- agno/reasoning/anthropic.py +85 -1
- agno/reasoning/azure_ai_foundry.py +93 -1
- agno/reasoning/deepseek.py +102 -2
- agno/reasoning/default.py +6 -7
- agno/reasoning/gemini.py +87 -3
- agno/reasoning/groq.py +109 -2
- agno/reasoning/helpers.py +6 -7
- agno/reasoning/manager.py +1238 -0
- agno/reasoning/ollama.py +93 -1
- agno/reasoning/openai.py +115 -1
- agno/reasoning/vertexai.py +85 -1
- agno/registry/__init__.py +3 -0
- agno/registry/registry.py +68 -0
- agno/remote/__init__.py +3 -0
- agno/remote/base.py +581 -0
- agno/run/__init__.py +2 -4
- agno/run/agent.py +134 -19
- agno/run/base.py +49 -1
- agno/run/cancel.py +65 -52
- agno/run/cancellation_management/__init__.py +9 -0
- agno/run/cancellation_management/base.py +78 -0
- agno/run/cancellation_management/in_memory_cancellation_manager.py +100 -0
- agno/run/cancellation_management/redis_cancellation_manager.py +236 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +111 -19
- agno/run/workflow.py +2 -1
- agno/session/agent.py +57 -92
- agno/session/summary.py +1 -1
- agno/session/team.py +62 -115
- agno/session/workflow.py +353 -57
- agno/skills/__init__.py +17 -0
- agno/skills/agent_skills.py +377 -0
- agno/skills/errors.py +32 -0
- agno/skills/loaders/__init__.py +4 -0
- agno/skills/loaders/base.py +27 -0
- agno/skills/loaders/local.py +216 -0
- agno/skills/skill.py +65 -0
- agno/skills/utils.py +107 -0
- agno/skills/validator.py +277 -0
- agno/table.py +10 -0
- agno/team/__init__.py +5 -1
- agno/team/remote.py +447 -0
- agno/team/team.py +3769 -2202
- agno/tools/brandfetch.py +27 -18
- agno/tools/browserbase.py +225 -16
- agno/tools/crawl4ai.py +3 -0
- agno/tools/duckduckgo.py +25 -71
- agno/tools/exa.py +0 -21
- agno/tools/file.py +14 -13
- agno/tools/file_generation.py +12 -6
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +94 -113
- agno/tools/google_bigquery.py +11 -2
- agno/tools/google_drive.py +4 -3
- agno/tools/knowledge.py +9 -4
- agno/tools/mcp/mcp.py +301 -18
- agno/tools/mcp/multi_mcp.py +269 -14
- agno/tools/mem0.py +11 -10
- agno/tools/memory.py +47 -46
- agno/tools/mlx_transcribe.py +10 -7
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/parallel.py +0 -7
- agno/tools/postgres.py +76 -36
- agno/tools/python.py +14 -6
- agno/tools/reasoning.py +30 -23
- agno/tools/redshift.py +406 -0
- agno/tools/shopify.py +1519 -0
- agno/tools/spotify.py +919 -0
- agno/tools/tavily.py +4 -1
- agno/tools/toolkit.py +253 -18
- agno/tools/websearch.py +93 -0
- agno/tools/website.py +1 -1
- agno/tools/wikipedia.py +1 -1
- agno/tools/workflow.py +56 -48
- agno/tools/yfinance.py +12 -11
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +161 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +112 -0
- agno/utils/agent.py +251 -10
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +264 -7
- agno/utils/hooks.py +111 -3
- agno/utils/http.py +161 -2
- agno/utils/mcp.py +49 -8
- agno/utils/media.py +22 -1
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +20 -5
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/os.py +0 -0
- agno/utils/print_response/agent.py +99 -16
- agno/utils/print_response/team.py +223 -24
- agno/utils/print_response/workflow.py +0 -2
- agno/utils/prompts.py +8 -6
- agno/utils/remote.py +23 -0
- agno/utils/response.py +1 -13
- agno/utils/string.py +91 -2
- agno/utils/team.py +62 -12
- agno/utils/tokens.py +657 -0
- agno/vectordb/base.py +15 -2
- agno/vectordb/cassandra/cassandra.py +1 -1
- agno/vectordb/chroma/__init__.py +2 -1
- agno/vectordb/chroma/chromadb.py +468 -23
- agno/vectordb/clickhouse/clickhousedb.py +1 -1
- agno/vectordb/couchbase/couchbase.py +6 -2
- agno/vectordb/lancedb/lance_db.py +7 -38
- agno/vectordb/lightrag/lightrag.py +7 -6
- agno/vectordb/milvus/milvus.py +118 -84
- agno/vectordb/mongodb/__init__.py +2 -1
- agno/vectordb/mongodb/mongodb.py +14 -31
- agno/vectordb/pgvector/pgvector.py +120 -66
- agno/vectordb/pineconedb/pineconedb.py +2 -19
- agno/vectordb/qdrant/__init__.py +2 -1
- agno/vectordb/qdrant/qdrant.py +33 -56
- agno/vectordb/redis/__init__.py +2 -1
- agno/vectordb/redis/redisdb.py +19 -31
- agno/vectordb/singlestore/singlestore.py +17 -9
- agno/vectordb/surrealdb/surrealdb.py +2 -38
- agno/vectordb/weaviate/__init__.py +2 -1
- agno/vectordb/weaviate/weaviate.py +7 -3
- agno/workflow/__init__.py +5 -1
- agno/workflow/agent.py +2 -2
- agno/workflow/condition.py +12 -10
- agno/workflow/loop.py +28 -9
- agno/workflow/parallel.py +21 -13
- agno/workflow/remote.py +362 -0
- agno/workflow/router.py +12 -9
- agno/workflow/step.py +261 -36
- agno/workflow/steps.py +12 -8
- agno/workflow/types.py +40 -77
- agno/workflow/workflow.py +939 -213
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/METADATA +134 -181
- agno-2.4.3.dist-info/RECORD +677 -0
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/WHEEL +1 -1
- agno/tools/googlesearch.py +0 -98
- agno/tools/memori.py +0 -339
- agno-2.2.13.dist-info/RECORD +0 -575
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/licenses/LICENSE +0 -0
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/top_level.txt +0 -0
agno/tools/postgres.py
CHANGED
|
@@ -14,6 +14,21 @@ from agno.utils.log import log_debug, log_error
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class PostgresTools(Toolkit):
|
|
17
|
+
"""
|
|
18
|
+
A toolkit for interacting with PostgreSQL databases.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
connection (Optional[PgConnection[DictRow]]): Existing database connection to reuse.
|
|
22
|
+
db_name (Optional[str]): Database name to connect to.
|
|
23
|
+
user (Optional[str]): Username for authentication.
|
|
24
|
+
password (Optional[str]): Password for authentication.
|
|
25
|
+
host (Optional[str]): PostgreSQL server hostname.
|
|
26
|
+
port (Optional[int]): PostgreSQL server port number.
|
|
27
|
+
table_schema (str): Default schema for table operations. Default is "public".
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
_requires_connect: bool = True
|
|
31
|
+
|
|
17
32
|
def __init__(
|
|
18
33
|
self,
|
|
19
34
|
connection: Optional[PgConnection[DictRow]] = None,
|
|
@@ -44,50 +59,71 @@ class PostgresTools(Toolkit):
|
|
|
44
59
|
|
|
45
60
|
super().__init__(name="postgres_tools", tools=tools, **kwargs)
|
|
46
61
|
|
|
47
|
-
|
|
48
|
-
def connection(self) -> PgConnection[DictRow]:
|
|
49
|
-
"""
|
|
50
|
-
Returns the Postgres psycopg connection.
|
|
51
|
-
:return psycopg.connection.Connection: psycopg connection
|
|
62
|
+
def connect(self) -> PgConnection[DictRow]:
|
|
52
63
|
"""
|
|
53
|
-
|
|
54
|
-
log_debug("Establishing new PostgreSQL connection.")
|
|
55
|
-
connection_kwargs: Dict[str, Any] = {"row_factory": dict_row}
|
|
56
|
-
if self.db_name:
|
|
57
|
-
connection_kwargs["dbname"] = self.db_name
|
|
58
|
-
if self.user:
|
|
59
|
-
connection_kwargs["user"] = self.user
|
|
60
|
-
if self.password:
|
|
61
|
-
connection_kwargs["password"] = self.password
|
|
62
|
-
if self.host:
|
|
63
|
-
connection_kwargs["host"] = self.host
|
|
64
|
-
if self.port:
|
|
65
|
-
connection_kwargs["port"] = self.port
|
|
66
|
-
|
|
67
|
-
connection_kwargs["options"] = f"-c search_path={self.table_schema}"
|
|
68
|
-
|
|
69
|
-
self._connection = psycopg.connect(**connection_kwargs)
|
|
70
|
-
self._connection.read_only = True
|
|
64
|
+
Establish a connection to the PostgreSQL database.
|
|
71
65
|
|
|
66
|
+
Returns:
|
|
67
|
+
The database connection object.
|
|
68
|
+
"""
|
|
69
|
+
if self._connection is not None and not self._connection.closed:
|
|
70
|
+
log_debug("Connection already established, reusing existing connection")
|
|
71
|
+
return self._connection
|
|
72
|
+
|
|
73
|
+
log_debug("Establishing new PostgreSQL connection.")
|
|
74
|
+
connection_kwargs: Dict[str, Any] = {"row_factory": dict_row}
|
|
75
|
+
if self.db_name:
|
|
76
|
+
connection_kwargs["dbname"] = self.db_name
|
|
77
|
+
if self.user:
|
|
78
|
+
connection_kwargs["user"] = self.user
|
|
79
|
+
if self.password:
|
|
80
|
+
connection_kwargs["password"] = self.password
|
|
81
|
+
if self.host:
|
|
82
|
+
connection_kwargs["host"] = self.host
|
|
83
|
+
if self.port:
|
|
84
|
+
connection_kwargs["port"] = self.port
|
|
85
|
+
|
|
86
|
+
connection_kwargs["options"] = f"-c search_path={self.table_schema}"
|
|
87
|
+
|
|
88
|
+
self._connection = psycopg.connect(**connection_kwargs)
|
|
89
|
+
self._connection.read_only = True
|
|
72
90
|
return self._connection
|
|
73
91
|
|
|
74
|
-
def
|
|
75
|
-
return self
|
|
76
|
-
|
|
77
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
78
|
-
self.close()
|
|
79
|
-
|
|
80
|
-
def close(self):
|
|
92
|
+
def close(self) -> None:
|
|
81
93
|
"""Closes the database connection if it's open."""
|
|
82
94
|
if self._connection and not self._connection.closed:
|
|
83
95
|
log_debug("Closing PostgreSQL connection.")
|
|
84
96
|
self._connection.close()
|
|
85
97
|
self._connection = None
|
|
86
98
|
|
|
99
|
+
@property
|
|
100
|
+
def is_connected(self) -> bool:
|
|
101
|
+
"""Check if a connection is currently established."""
|
|
102
|
+
return self._connection is not None and not self._connection.closed
|
|
103
|
+
|
|
104
|
+
def _ensure_connection(self) -> PgConnection[DictRow]:
|
|
105
|
+
"""
|
|
106
|
+
Ensure a connection exists, creating one if necessary.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
The database connection object.
|
|
110
|
+
"""
|
|
111
|
+
if not self.is_connected:
|
|
112
|
+
return self.connect()
|
|
113
|
+
return self._connection # type: ignore
|
|
114
|
+
|
|
115
|
+
def __enter__(self):
|
|
116
|
+
return self.connect()
|
|
117
|
+
|
|
118
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
119
|
+
if self.is_connected:
|
|
120
|
+
self.close()
|
|
121
|
+
|
|
87
122
|
def _execute_query(self, query: str, params: Optional[tuple] = None) -> str:
|
|
88
123
|
try:
|
|
89
|
-
|
|
90
|
-
|
|
124
|
+
connection = self._ensure_connection()
|
|
125
|
+
with connection.cursor() as cursor:
|
|
126
|
+
log_debug("Running PostgreSQL query")
|
|
91
127
|
cursor.execute(query, params)
|
|
92
128
|
|
|
93
129
|
if cursor.description is None:
|
|
@@ -105,8 +141,8 @@ class PostgresTools(Toolkit):
|
|
|
105
141
|
|
|
106
142
|
except psycopg.Error as e:
|
|
107
143
|
log_error(f"Database error: {e}")
|
|
108
|
-
if self.
|
|
109
|
-
self.
|
|
144
|
+
if self._connection and not self._connection.closed:
|
|
145
|
+
self._connection.rollback()
|
|
110
146
|
return f"Error executing query: {e}"
|
|
111
147
|
except Exception as e:
|
|
112
148
|
log_error(f"An unexpected error occurred: {e}")
|
|
@@ -146,7 +182,8 @@ class PostgresTools(Toolkit):
|
|
|
146
182
|
A string containing a summary of the table.
|
|
147
183
|
"""
|
|
148
184
|
try:
|
|
149
|
-
|
|
185
|
+
connection = self._ensure_connection()
|
|
186
|
+
with connection.cursor() as cursor:
|
|
150
187
|
# First, get column information using a parameterized query
|
|
151
188
|
schema_query = """
|
|
152
189
|
SELECT column_name, data_type
|
|
@@ -230,7 +267,8 @@ class PostgresTools(Toolkit):
|
|
|
230
267
|
stmt = sql.SQL("SELECT * FROM {tbl};").format(tbl=table_identifier)
|
|
231
268
|
|
|
232
269
|
try:
|
|
233
|
-
|
|
270
|
+
connection = self._ensure_connection()
|
|
271
|
+
with connection.cursor() as cursor:
|
|
234
272
|
cursor.execute(stmt)
|
|
235
273
|
|
|
236
274
|
if cursor.description is None:
|
|
@@ -245,6 +283,8 @@ class PostgresTools(Toolkit):
|
|
|
245
283
|
|
|
246
284
|
return f"Successfully exported table '{table}' to '{path}'."
|
|
247
285
|
except (psycopg.Error, IOError) as e:
|
|
286
|
+
if self._connection and not self._connection.closed:
|
|
287
|
+
self._connection.rollback()
|
|
248
288
|
return f"Error exporting table: {e}"
|
|
249
289
|
|
|
250
290
|
def run_query(self, query: str) -> str:
|
agno/tools/python.py
CHANGED
|
@@ -4,7 +4,7 @@ from pathlib import Path
|
|
|
4
4
|
from typing import Any, List, Optional
|
|
5
5
|
|
|
6
6
|
from agno.tools import Toolkit
|
|
7
|
-
from agno.utils.log import log_debug, log_info, logger
|
|
7
|
+
from agno.utils.log import log_debug, log_error, log_info, logger
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
@functools.lru_cache(maxsize=None)
|
|
@@ -18,9 +18,11 @@ class PythonTools(Toolkit):
|
|
|
18
18
|
base_dir: Optional[Path] = None,
|
|
19
19
|
safe_globals: Optional[dict] = None,
|
|
20
20
|
safe_locals: Optional[dict] = None,
|
|
21
|
+
restrict_to_base_dir: bool = True,
|
|
21
22
|
**kwargs,
|
|
22
23
|
):
|
|
23
|
-
self.base_dir: Path = base_dir or Path.cwd()
|
|
24
|
+
self.base_dir: Path = (base_dir or Path.cwd()).resolve()
|
|
25
|
+
self.restrict_to_base_dir = restrict_to_base_dir
|
|
24
26
|
|
|
25
27
|
# Restricted global and local scope
|
|
26
28
|
self.safe_globals: dict = safe_globals or globals()
|
|
@@ -55,7 +57,9 @@ class PythonTools(Toolkit):
|
|
|
55
57
|
"""
|
|
56
58
|
try:
|
|
57
59
|
warn()
|
|
58
|
-
file_path = self.base_dir.
|
|
60
|
+
safe, file_path = self._check_path(file_name, self.base_dir, self.restrict_to_base_dir)
|
|
61
|
+
if not safe:
|
|
62
|
+
return f"Error: Path '{file_name}' is outside the allowed base directory"
|
|
59
63
|
log_debug(f"Saving code to {file_path}")
|
|
60
64
|
if not file_path.parent.exists():
|
|
61
65
|
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -89,8 +93,9 @@ class PythonTools(Toolkit):
|
|
|
89
93
|
"""
|
|
90
94
|
try:
|
|
91
95
|
warn()
|
|
92
|
-
file_path = self.base_dir.
|
|
93
|
-
|
|
96
|
+
safe, file_path = self._check_path(file_name, self.base_dir, self.restrict_to_base_dir)
|
|
97
|
+
if not safe:
|
|
98
|
+
return f"Error: Path '{file_name}' is outside the allowed base directory"
|
|
94
99
|
log_info(f"Running {file_path}")
|
|
95
100
|
globals_after_run = runpy.run_path(str(file_path), init_globals=self.safe_globals, run_name="__main__")
|
|
96
101
|
if variable_to_return:
|
|
@@ -113,7 +118,10 @@ class PythonTools(Toolkit):
|
|
|
113
118
|
"""
|
|
114
119
|
try:
|
|
115
120
|
log_info(f"Reading file: {file_name}")
|
|
116
|
-
file_path = self.base_dir.
|
|
121
|
+
safe, file_path = self._check_path(file_name, self.base_dir, self.restrict_to_base_dir)
|
|
122
|
+
if not safe:
|
|
123
|
+
log_error(f"Attempted to read file outside base directory: {file_name}")
|
|
124
|
+
return "Error reading file: path outside allowed directory"
|
|
117
125
|
contents = file_path.read_text(encoding="utf-8")
|
|
118
126
|
return str(contents)
|
|
119
127
|
except Exception as e:
|
agno/tools/reasoning.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from textwrap import dedent
|
|
2
|
-
from typing import Any,
|
|
2
|
+
from typing import Any, List, Optional
|
|
3
3
|
|
|
4
4
|
from agno.reasoning.step import NextAction, ReasoningStep
|
|
5
|
+
from agno.run import RunContext
|
|
5
6
|
from agno.tools import Toolkit
|
|
6
7
|
from agno.utils.log import log_debug, log_error
|
|
7
8
|
|
|
@@ -49,7 +50,7 @@ class ReasoningTools(Toolkit):
|
|
|
49
50
|
|
|
50
51
|
def think(
|
|
51
52
|
self,
|
|
52
|
-
|
|
53
|
+
run_context: RunContext,
|
|
53
54
|
title: str,
|
|
54
55
|
thought: str,
|
|
55
56
|
action: Optional[str] = None,
|
|
@@ -80,21 +81,24 @@ class ReasoningTools(Toolkit):
|
|
|
80
81
|
confidence=confidence,
|
|
81
82
|
)
|
|
82
83
|
|
|
83
|
-
current_run_id =
|
|
84
|
+
current_run_id = run_context.run_id
|
|
84
85
|
|
|
85
86
|
# Add this step to the Agent's session state
|
|
86
|
-
if session_state is None:
|
|
87
|
-
session_state = {}
|
|
88
|
-
if "reasoning_steps" not in session_state:
|
|
89
|
-
session_state["reasoning_steps"] = {}
|
|
90
|
-
if current_run_id not in session_state["reasoning_steps"]:
|
|
91
|
-
session_state["reasoning_steps"][current_run_id] = []
|
|
92
|
-
session_state["reasoning_steps"][current_run_id].append(reasoning_step.model_dump_json())
|
|
87
|
+
if run_context.session_state is None:
|
|
88
|
+
run_context.session_state = {}
|
|
89
|
+
if "reasoning_steps" not in run_context.session_state:
|
|
90
|
+
run_context.session_state["reasoning_steps"] = {}
|
|
91
|
+
if current_run_id not in run_context.session_state["reasoning_steps"]:
|
|
92
|
+
run_context.session_state["reasoning_steps"][current_run_id] = []
|
|
93
|
+
run_context.session_state["reasoning_steps"][current_run_id].append(reasoning_step.model_dump_json())
|
|
93
94
|
|
|
94
95
|
# Return all previous reasoning_steps and the new reasoning_step
|
|
95
|
-
if
|
|
96
|
+
if (
|
|
97
|
+
"reasoning_steps" in run_context.session_state
|
|
98
|
+
and current_run_id in run_context.session_state["reasoning_steps"]
|
|
99
|
+
):
|
|
96
100
|
formatted_reasoning_steps = ""
|
|
97
|
-
for i, step in enumerate(session_state["reasoning_steps"][current_run_id], 1):
|
|
101
|
+
for i, step in enumerate(run_context.session_state["reasoning_steps"][current_run_id], 1):
|
|
98
102
|
step_parsed = ReasoningStep.model_validate_json(step)
|
|
99
103
|
step_str = dedent(f"""\
|
|
100
104
|
Step {i}:
|
|
@@ -112,7 +116,7 @@ Confidence: {step_parsed.confidence}
|
|
|
112
116
|
|
|
113
117
|
def analyze(
|
|
114
118
|
self,
|
|
115
|
-
|
|
119
|
+
run_context: RunContext,
|
|
116
120
|
title: str,
|
|
117
121
|
result: str,
|
|
118
122
|
analysis: str,
|
|
@@ -150,20 +154,23 @@ Confidence: {step_parsed.confidence}
|
|
|
150
154
|
confidence=confidence,
|
|
151
155
|
)
|
|
152
156
|
|
|
153
|
-
current_run_id =
|
|
157
|
+
current_run_id = run_context.run_id
|
|
154
158
|
# Add this step to the Agent's session state
|
|
155
|
-
if session_state is None:
|
|
156
|
-
session_state = {}
|
|
157
|
-
if "reasoning_steps" not in session_state:
|
|
158
|
-
session_state["reasoning_steps"] = {}
|
|
159
|
-
if current_run_id not in session_state["reasoning_steps"]:
|
|
160
|
-
session_state["reasoning_steps"][current_run_id] = []
|
|
161
|
-
session_state["reasoning_steps"][current_run_id].append(reasoning_step.model_dump_json())
|
|
159
|
+
if run_context.session_state is None:
|
|
160
|
+
run_context.session_state = {}
|
|
161
|
+
if "reasoning_steps" not in run_context.session_state:
|
|
162
|
+
run_context.session_state["reasoning_steps"] = {}
|
|
163
|
+
if current_run_id not in run_context.session_state["reasoning_steps"]:
|
|
164
|
+
run_context.session_state["reasoning_steps"][current_run_id] = []
|
|
165
|
+
run_context.session_state["reasoning_steps"][current_run_id].append(reasoning_step.model_dump_json())
|
|
162
166
|
|
|
163
167
|
# Return all previous reasoning_steps and the new reasoning_step
|
|
164
|
-
if
|
|
168
|
+
if (
|
|
169
|
+
"reasoning_steps" in run_context.session_state
|
|
170
|
+
and current_run_id in run_context.session_state["reasoning_steps"]
|
|
171
|
+
):
|
|
165
172
|
formatted_reasoning_steps = ""
|
|
166
|
-
for i, step in enumerate(session_state["reasoning_steps"][current_run_id], 1):
|
|
173
|
+
for i, step in enumerate(run_context.session_state["reasoning_steps"][current_run_id], 1):
|
|
167
174
|
step_parsed = ReasoningStep.model_validate_json(step)
|
|
168
175
|
step_str = dedent(f"""\
|
|
169
176
|
Step {i}:
|