agno 0.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/__init__.py +8 -0
- agno/agent/__init__.py +44 -5
- agno/agent/agent.py +10531 -2975
- agno/api/agent.py +14 -53
- agno/api/api.py +7 -46
- agno/api/evals.py +22 -0
- agno/api/os.py +17 -0
- agno/api/routes.py +6 -25
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +6 -9
- agno/api/schemas/evals.py +16 -0
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/team.py +10 -10
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +16 -0
- agno/api/settings.py +53 -0
- agno/api/team.py +22 -26
- agno/api/workflow.py +28 -0
- agno/cloud/aws/base.py +214 -0
- agno/cloud/aws/s3/__init__.py +2 -0
- agno/cloud/aws/s3/api_client.py +43 -0
- agno/cloud/aws/s3/bucket.py +195 -0
- agno/cloud/aws/s3/object.py +57 -0
- 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/__init__.py +24 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +946 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +2781 -0
- agno/db/dynamo/schemas.py +442 -0
- agno/db/dynamo/utils.py +743 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +2379 -0
- agno/db/firestore/schemas.py +181 -0
- agno/db/firestore/utils.py +376 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1791 -0
- agno/db/gcs_json/utils.py +228 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +1312 -0
- agno/db/in_memory/utils.py +230 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1777 -0
- agno/db/json/utils.py +230 -0
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +635 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +17 -0
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +2597 -0
- agno/db/mongo/schemas.py +119 -0
- agno/db/mongo/utils.py +276 -0
- agno/db/mysql/__init__.py +4 -0
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +2923 -0
- agno/db/mysql/schemas.py +186 -0
- agno/db/mysql/utils.py +488 -0
- agno/db/postgres/__init__.py +4 -0
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +2870 -0
- agno/db/postgres/schemas.py +187 -0
- agno/db/postgres/utils.py +442 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +2141 -0
- agno/db/redis/schemas.py +159 -0
- agno/db/redis/utils.py +346 -0
- agno/db/schemas/__init__.py +4 -0
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +34 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +61 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +179 -0
- agno/db/singlestore/singlestore.py +2877 -0
- agno/db/singlestore/utils.py +384 -0
- agno/db/sqlite/__init__.py +4 -0
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +181 -0
- agno/db/sqlite/sqlite.py +2908 -0
- agno/db/sqlite/utils.py +429 -0
- 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 +118 -0
- agno/eval/__init__.py +24 -0
- agno/eval/accuracy.py +666 -276
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +779 -0
- agno/eval/reliability.py +241 -62
- agno/eval/utils.py +120 -0
- agno/exceptions.py +143 -1
- agno/filters.py +354 -0
- agno/guardrails/__init__.py +6 -0
- agno/guardrails/base.py +19 -0
- agno/guardrails/openai.py +144 -0
- agno/guardrails/pii.py +94 -0
- agno/guardrails/prompt_injection.py +52 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/__init__.py +3 -0
- agno/integrations/discord/client.py +203 -0
- agno/knowledge/__init__.py +5 -1
- agno/{document → knowledge}/chunking/agentic.py +22 -14
- agno/{document → knowledge}/chunking/document.py +2 -2
- agno/{document → knowledge}/chunking/fixed.py +7 -6
- agno/knowledge/chunking/markdown.py +151 -0
- agno/{document → knowledge}/chunking/recursive.py +15 -3
- agno/knowledge/chunking/row.py +39 -0
- agno/knowledge/chunking/semantic.py +91 -0
- agno/knowledge/chunking/strategy.py +165 -0
- agno/knowledge/content.py +74 -0
- agno/knowledge/document/__init__.py +5 -0
- agno/{document → knowledge/document}/base.py +12 -2
- agno/knowledge/embedder/__init__.py +5 -0
- agno/knowledge/embedder/aws_bedrock.py +343 -0
- agno/knowledge/embedder/azure_openai.py +210 -0
- agno/{embedder → knowledge/embedder}/base.py +8 -0
- agno/knowledge/embedder/cohere.py +323 -0
- agno/knowledge/embedder/fastembed.py +62 -0
- agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
- agno/knowledge/embedder/google.py +258 -0
- agno/knowledge/embedder/huggingface.py +94 -0
- agno/knowledge/embedder/jina.py +182 -0
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +206 -0
- agno/knowledge/embedder/nebius.py +13 -0
- agno/knowledge/embedder/ollama.py +154 -0
- agno/knowledge/embedder/openai.py +195 -0
- agno/knowledge/embedder/sentence_transformer.py +63 -0
- agno/{embedder → knowledge/embedder}/together.py +1 -1
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +165 -0
- agno/knowledge/knowledge.py +3006 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/knowledge/reader/arxiv_reader.py +81 -0
- agno/knowledge/reader/base.py +95 -0
- agno/knowledge/reader/csv_reader.py +164 -0
- agno/knowledge/reader/docx_reader.py +82 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
- agno/knowledge/reader/firecrawl_reader.py +201 -0
- agno/knowledge/reader/json_reader.py +88 -0
- agno/knowledge/reader/markdown_reader.py +137 -0
- agno/knowledge/reader/pdf_reader.py +431 -0
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +313 -0
- agno/knowledge/reader/s3_reader.py +89 -0
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +127 -0
- agno/knowledge/reader/web_search_reader.py +325 -0
- agno/knowledge/reader/website_reader.py +455 -0
- agno/knowledge/reader/wikipedia_reader.py +91 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/knowledge/reranker/__init__.py +3 -0
- agno/{reranker → knowledge/reranker}/base.py +1 -1
- agno/{reranker → knowledge/reranker}/cohere.py +2 -2
- agno/knowledge/reranker/infinity.py +195 -0
- agno/knowledge/reranker/sentence_transformer.py +54 -0
- agno/knowledge/types.py +39 -0
- agno/knowledge/utils.py +234 -0
- agno/media.py +439 -95
- agno/memory/__init__.py +16 -3
- agno/memory/manager.py +1474 -123
- 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/__init__.py +5 -0
- agno/models/aimlapi/aimlapi.py +62 -0
- agno/models/anthropic/__init__.py +4 -0
- agno/models/anthropic/claude.py +960 -496
- agno/models/aws/__init__.py +15 -0
- agno/models/aws/bedrock.py +686 -451
- agno/models/aws/claude.py +190 -183
- agno/models/azure/__init__.py +18 -1
- agno/models/azure/ai_foundry.py +489 -0
- agno/models/azure/openai_chat.py +89 -40
- agno/models/base.py +2477 -550
- agno/models/cerebras/__init__.py +12 -0
- agno/models/cerebras/cerebras.py +565 -0
- agno/models/cerebras/cerebras_openai.py +131 -0
- agno/models/cohere/__init__.py +4 -0
- agno/models/cohere/chat.py +306 -492
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +74 -0
- agno/models/dashscope/__init__.py +5 -0
- agno/models/dashscope/dashscope.py +90 -0
- agno/models/deepinfra/__init__.py +5 -0
- agno/models/deepinfra/deepinfra.py +45 -0
- agno/models/deepseek/__init__.py +4 -0
- agno/models/deepseek/deepseek.py +110 -9
- agno/models/fireworks/__init__.py +4 -0
- agno/models/fireworks/fireworks.py +19 -22
- agno/models/google/__init__.py +3 -7
- agno/models/google/gemini.py +1717 -662
- agno/models/google/utils.py +22 -0
- agno/models/groq/__init__.py +4 -0
- agno/models/groq/groq.py +391 -666
- agno/models/huggingface/__init__.py +4 -0
- agno/models/huggingface/huggingface.py +266 -538
- agno/models/ibm/__init__.py +5 -0
- agno/models/ibm/watsonx.py +432 -0
- agno/models/internlm/__init__.py +3 -0
- agno/models/internlm/internlm.py +20 -3
- agno/models/langdb/__init__.py +1 -0
- agno/models/langdb/langdb.py +60 -0
- agno/models/litellm/__init__.py +14 -0
- agno/models/litellm/chat.py +503 -0
- agno/models/litellm/litellm_openai.py +42 -0
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/lmstudio/__init__.py +5 -0
- agno/models/lmstudio/lmstudio.py +25 -0
- agno/models/message.py +361 -39
- agno/models/meta/__init__.py +12 -0
- agno/models/meta/llama.py +502 -0
- agno/models/meta/llama_openai.py +79 -0
- agno/models/metrics.py +120 -0
- agno/models/mistral/__init__.py +4 -0
- agno/models/mistral/mistral.py +293 -393
- agno/models/nebius/__init__.py +3 -0
- agno/models/nebius/nebius.py +53 -0
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/__init__.py +4 -0
- agno/models/nvidia/nvidia.py +22 -3
- agno/models/ollama/__init__.py +4 -2
- agno/models/ollama/chat.py +257 -492
- agno/models/openai/__init__.py +7 -0
- agno/models/openai/chat.py +725 -770
- agno/models/openai/like.py +16 -2
- agno/models/openai/responses.py +1121 -0
- agno/models/openrouter/__init__.py +4 -0
- agno/models/openrouter/openrouter.py +62 -5
- agno/models/perplexity/__init__.py +5 -0
- agno/models/perplexity/perplexity.py +203 -0
- agno/models/portkey/__init__.py +3 -0
- agno/models/portkey/portkey.py +82 -0
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +69 -0
- agno/models/response.py +177 -7
- agno/models/sambanova/__init__.py +4 -0
- agno/models/sambanova/sambanova.py +23 -4
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +42 -0
- agno/models/together/__init__.py +4 -0
- agno/models/together/together.py +21 -164
- agno/models/utils.py +266 -0
- agno/models/vercel/__init__.py +3 -0
- agno/models/vercel/v0.py +43 -0
- agno/models/vertexai/__init__.py +0 -1
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/__init__.py +3 -0
- agno/models/vllm/vllm.py +83 -0
- agno/models/xai/__init__.py +2 -0
- agno/models/xai/xai.py +111 -7
- agno/os/__init__.py +3 -0
- agno/os/app.py +1027 -0
- agno/os/auth.py +244 -0
- agno/os/config.py +126 -0
- agno/os/interfaces/__init__.py +1 -0
- agno/os/interfaces/a2a/__init__.py +3 -0
- agno/os/interfaces/a2a/a2a.py +42 -0
- agno/os/interfaces/a2a/router.py +249 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +47 -0
- agno/os/interfaces/agui/router.py +147 -0
- agno/os/interfaces/agui/utils.py +574 -0
- agno/os/interfaces/base.py +25 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/os/interfaces/slack/router.py +148 -0
- agno/os/interfaces/slack/security.py +30 -0
- agno/os/interfaces/slack/slack.py +47 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/os/interfaces/whatsapp/router.py +210 -0
- agno/os/interfaces/whatsapp/security.py +55 -0
- agno/os/interfaces/whatsapp/whatsapp.py +36 -0
- agno/os/mcp.py +293 -0
- agno/os/middleware/__init__.py +9 -0
- agno/os/middleware/jwt.py +797 -0
- agno/os/router.py +258 -0
- agno/os/routers/__init__.py +3 -0
- 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/__init__.py +3 -0
- agno/os/routers/evals/evals.py +450 -0
- agno/os/routers/evals/schemas.py +174 -0
- agno/os/routers/evals/utils.py +231 -0
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +1008 -0
- agno/os/routers/knowledge/schemas.py +178 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +661 -0
- agno/os/routers/memory/schemas.py +88 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +190 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +997 -0
- 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 +534 -0
- agno/os/scopes.py +469 -0
- agno/{playground → os}/settings.py +7 -15
- agno/os/utils.py +973 -0
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +67 -0
- agno/reasoning/deepseek.py +63 -0
- agno/reasoning/default.py +97 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +71 -0
- agno/reasoning/helpers.py +24 -1
- agno/reasoning/ollama.py +67 -0
- agno/reasoning/openai.py +86 -0
- agno/reasoning/step.py +2 -1
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +822 -0
- agno/run/base.py +247 -0
- agno/run/cancel.py +81 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +767 -0
- agno/run/workflow.py +708 -0
- agno/session/__init__.py +10 -0
- agno/session/agent.py +260 -0
- agno/session/summary.py +265 -0
- agno/session/team.py +342 -0
- agno/session/workflow.py +501 -0
- agno/table.py +10 -0
- agno/team/__init__.py +37 -0
- agno/team/team.py +9536 -0
- agno/tools/__init__.py +7 -0
- agno/tools/agentql.py +120 -0
- agno/tools/airflow.py +22 -12
- agno/tools/api.py +122 -0
- agno/tools/apify.py +276 -83
- agno/tools/{arxiv_toolkit.py → arxiv.py} +20 -12
- agno/tools/aws_lambda.py +28 -7
- agno/tools/aws_ses.py +66 -0
- agno/tools/baidusearch.py +11 -4
- agno/tools/bitbucket.py +292 -0
- agno/tools/brandfetch.py +213 -0
- agno/tools/bravesearch.py +106 -0
- agno/tools/brightdata.py +367 -0
- agno/tools/browserbase.py +209 -0
- agno/tools/calcom.py +32 -23
- agno/tools/calculator.py +24 -37
- agno/tools/cartesia.py +187 -0
- agno/tools/{clickup_tool.py → clickup.py} +17 -28
- agno/tools/confluence.py +91 -26
- agno/tools/crawl4ai.py +139 -43
- agno/tools/csv_toolkit.py +28 -22
- agno/tools/dalle.py +36 -22
- agno/tools/daytona.py +475 -0
- agno/tools/decorator.py +169 -14
- agno/tools/desi_vocal.py +23 -11
- agno/tools/discord.py +32 -29
- agno/tools/docker.py +716 -0
- agno/tools/duckdb.py +76 -81
- agno/tools/duckduckgo.py +43 -40
- agno/tools/e2b.py +703 -0
- agno/tools/eleven_labs.py +65 -54
- agno/tools/email.py +13 -5
- agno/tools/evm.py +129 -0
- agno/tools/exa.py +324 -42
- agno/tools/fal.py +39 -35
- agno/tools/file.py +196 -30
- agno/tools/file_generation.py +356 -0
- agno/tools/financial_datasets.py +288 -0
- agno/tools/firecrawl.py +108 -33
- agno/tools/function.py +960 -122
- agno/tools/giphy.py +34 -12
- agno/tools/github.py +1294 -97
- agno/tools/gmail.py +922 -0
- agno/tools/google_bigquery.py +117 -0
- agno/tools/google_drive.py +271 -0
- agno/tools/google_maps.py +253 -0
- agno/tools/googlecalendar.py +607 -107
- agno/tools/googlesheets.py +377 -0
- agno/tools/hackernews.py +20 -12
- agno/tools/jina.py +24 -14
- agno/tools/jira.py +48 -19
- agno/tools/knowledge.py +218 -0
- agno/tools/linear.py +82 -43
- agno/tools/linkup.py +58 -0
- agno/tools/local_file_system.py +15 -7
- agno/tools/lumalab.py +41 -26
- 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 +284 -0
- agno/tools/mem0.py +193 -0
- agno/tools/memory.py +419 -0
- agno/tools/mlx_transcribe.py +11 -9
- agno/tools/models/azure_openai.py +190 -0
- agno/tools/models/gemini.py +203 -0
- agno/tools/models/groq.py +158 -0
- agno/tools/models/morph.py +186 -0
- agno/tools/models/nebius.py +124 -0
- agno/tools/models_labs.py +163 -82
- agno/tools/moviepy_video.py +18 -13
- agno/tools/nano_banana.py +151 -0
- agno/tools/neo4j.py +134 -0
- agno/tools/newspaper.py +15 -4
- agno/tools/newspaper4k.py +19 -6
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +181 -17
- agno/tools/openbb.py +27 -20
- agno/tools/opencv.py +321 -0
- agno/tools/openweather.py +233 -0
- agno/tools/oxylabs.py +385 -0
- agno/tools/pandas.py +25 -15
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +238 -185
- agno/tools/pubmed.py +125 -13
- agno/tools/python.py +48 -35
- agno/tools/reasoning.py +283 -0
- agno/tools/reddit.py +207 -29
- agno/tools/redshift.py +406 -0
- agno/tools/replicate.py +69 -26
- agno/tools/resend.py +11 -6
- agno/tools/scrapegraph.py +179 -19
- agno/tools/searxng.py +23 -31
- agno/tools/serpapi.py +15 -10
- agno/tools/serper.py +255 -0
- agno/tools/shell.py +23 -12
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +56 -14
- agno/tools/sleep.py +8 -6
- agno/tools/spider.py +35 -11
- agno/tools/spotify.py +919 -0
- agno/tools/sql.py +34 -19
- agno/tools/tavily.py +158 -8
- agno/tools/telegram.py +18 -8
- agno/tools/todoist.py +218 -0
- agno/tools/toolkit.py +134 -9
- agno/tools/trafilatura.py +388 -0
- agno/tools/trello.py +25 -28
- agno/tools/twilio.py +18 -9
- agno/tools/user_control_flow.py +78 -0
- agno/tools/valyu.py +228 -0
- agno/tools/visualization.py +467 -0
- agno/tools/webbrowser.py +28 -0
- agno/tools/webex.py +76 -0
- agno/tools/website.py +23 -19
- agno/tools/webtools.py +45 -0
- agno/tools/whatsapp.py +286 -0
- agno/tools/wikipedia.py +28 -19
- agno/tools/workflow.py +285 -0
- agno/tools/{twitter.py → x.py} +142 -46
- agno/tools/yfinance.py +41 -39
- agno/tools/youtube.py +34 -17
- agno/tools/zendesk.py +15 -5
- agno/tools/zep.py +454 -0
- agno/tools/zoom.py +86 -37
- 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/audio.py +37 -1
- agno/utils/certs.py +27 -0
- agno/utils/code_execution.py +11 -0
- agno/utils/common.py +103 -20
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +700 -0
- agno/utils/functions.py +107 -37
- agno/utils/gemini.py +426 -0
- agno/utils/hooks.py +171 -0
- agno/utils/http.py +185 -0
- agno/utils/json_schema.py +159 -37
- agno/utils/knowledge.py +36 -0
- agno/utils/location.py +19 -0
- agno/utils/log.py +221 -8
- agno/utils/mcp.py +214 -0
- agno/utils/media.py +335 -14
- agno/utils/merge_dict.py +22 -1
- agno/utils/message.py +77 -2
- agno/utils/models/ai_foundry.py +50 -0
- agno/utils/models/claude.py +373 -0
- agno/utils/models/cohere.py +94 -0
- agno/utils/models/llama.py +85 -0
- agno/utils/models/mistral.py +100 -0
- agno/utils/models/openai_responses.py +140 -0
- agno/utils/models/schema_utils.py +153 -0
- agno/utils/models/watsonx.py +41 -0
- agno/utils/openai.py +257 -0
- agno/utils/pickle.py +1 -1
- agno/utils/pprint.py +124 -8
- agno/utils/print_response/agent.py +930 -0
- agno/utils/print_response/team.py +1914 -0
- agno/utils/print_response/workflow.py +1668 -0
- agno/utils/prompts.py +111 -0
- agno/utils/reasoning.py +108 -0
- agno/utils/response.py +163 -0
- agno/utils/serialize.py +32 -0
- agno/utils/shell.py +4 -4
- agno/utils/streamlit.py +487 -0
- agno/utils/string.py +204 -51
- agno/utils/team.py +139 -0
- agno/utils/timer.py +9 -2
- agno/utils/tokens.py +657 -0
- agno/utils/tools.py +19 -1
- agno/utils/whatsapp.py +305 -0
- agno/utils/yaml_io.py +3 -3
- agno/vectordb/__init__.py +2 -0
- agno/vectordb/base.py +87 -9
- agno/vectordb/cassandra/__init__.py +5 -1
- agno/vectordb/cassandra/cassandra.py +383 -27
- agno/vectordb/chroma/__init__.py +4 -0
- agno/vectordb/chroma/chromadb.py +748 -83
- agno/vectordb/clickhouse/__init__.py +7 -1
- agno/vectordb/clickhouse/clickhousedb.py +554 -53
- agno/vectordb/couchbase/__init__.py +3 -0
- agno/vectordb/couchbase/couchbase.py +1446 -0
- agno/vectordb/lancedb/__init__.py +5 -0
- agno/vectordb/lancedb/lance_db.py +730 -98
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +163 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +388 -0
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +166 -0
- agno/vectordb/milvus/__init__.py +3 -0
- agno/vectordb/milvus/milvus.py +966 -78
- agno/vectordb/mongodb/__init__.py +9 -1
- agno/vectordb/mongodb/mongodb.py +1175 -172
- agno/vectordb/pgvector/__init__.py +8 -0
- agno/vectordb/pgvector/pgvector.py +599 -115
- agno/vectordb/pineconedb/__init__.py +5 -1
- agno/vectordb/pineconedb/pineconedb.py +406 -43
- agno/vectordb/qdrant/__init__.py +4 -0
- agno/vectordb/qdrant/qdrant.py +914 -61
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/__init__.py +8 -1
- agno/vectordb/singlestore/singlestore.py +771 -0
- agno/vectordb/surrealdb/__init__.py +3 -0
- agno/vectordb/surrealdb/surrealdb.py +663 -0
- agno/vectordb/upstashdb/__init__.py +5 -0
- agno/vectordb/upstashdb/upstashdb.py +718 -0
- agno/vectordb/weaviate/__init__.py +8 -0
- agno/vectordb/weaviate/index.py +15 -0
- agno/vectordb/weaviate/weaviate.py +1009 -0
- agno/workflow/__init__.py +23 -1
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +759 -0
- agno/workflow/loop.py +756 -0
- agno/workflow/parallel.py +853 -0
- agno/workflow/router.py +723 -0
- agno/workflow/step.py +1564 -0
- agno/workflow/steps.py +613 -0
- agno/workflow/types.py +556 -0
- agno/workflow/workflow.py +4327 -514
- agno-2.3.13.dist-info/METADATA +639 -0
- agno-2.3.13.dist-info/RECORD +613 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +1 -1
- agno-2.3.13.dist-info/licenses/LICENSE +201 -0
- agno/api/playground.py +0 -91
- agno/api/schemas/playground.py +0 -22
- agno/api/schemas/user.py +0 -22
- agno/api/schemas/workspace.py +0 -46
- agno/api/user.py +0 -160
- agno/api/workspace.py +0 -151
- agno/cli/auth_server.py +0 -118
- agno/cli/config.py +0 -275
- agno/cli/console.py +0 -88
- agno/cli/credentials.py +0 -23
- agno/cli/entrypoint.py +0 -571
- agno/cli/operator.py +0 -355
- agno/cli/settings.py +0 -85
- agno/cli/ws/ws_cli.py +0 -817
- agno/constants.py +0 -13
- agno/document/__init__.py +0 -1
- agno/document/chunking/semantic.py +0 -47
- agno/document/chunking/strategy.py +0 -31
- agno/document/reader/__init__.py +0 -1
- agno/document/reader/arxiv_reader.py +0 -41
- agno/document/reader/base.py +0 -22
- agno/document/reader/csv_reader.py +0 -84
- agno/document/reader/docx_reader.py +0 -46
- agno/document/reader/firecrawl_reader.py +0 -99
- agno/document/reader/json_reader.py +0 -43
- agno/document/reader/pdf_reader.py +0 -219
- agno/document/reader/s3/pdf_reader.py +0 -46
- agno/document/reader/s3/text_reader.py +0 -51
- agno/document/reader/text_reader.py +0 -41
- agno/document/reader/website_reader.py +0 -175
- agno/document/reader/youtube_reader.py +0 -50
- agno/embedder/__init__.py +0 -1
- agno/embedder/azure_openai.py +0 -86
- agno/embedder/cohere.py +0 -72
- agno/embedder/fastembed.py +0 -37
- agno/embedder/google.py +0 -73
- agno/embedder/huggingface.py +0 -54
- agno/embedder/mistral.py +0 -80
- agno/embedder/ollama.py +0 -57
- agno/embedder/openai.py +0 -74
- agno/embedder/sentence_transformer.py +0 -38
- agno/embedder/voyageai.py +0 -64
- agno/eval/perf.py +0 -201
- agno/file/__init__.py +0 -1
- agno/file/file.py +0 -16
- agno/file/local/csv.py +0 -32
- agno/file/local/txt.py +0 -19
- agno/infra/app.py +0 -240
- agno/infra/base.py +0 -144
- agno/infra/context.py +0 -20
- agno/infra/db_app.py +0 -52
- agno/infra/resource.py +0 -205
- agno/infra/resources.py +0 -55
- agno/knowledge/agent.py +0 -230
- agno/knowledge/arxiv.py +0 -22
- agno/knowledge/combined.py +0 -22
- agno/knowledge/csv.py +0 -28
- agno/knowledge/csv_url.py +0 -19
- agno/knowledge/document.py +0 -20
- agno/knowledge/docx.py +0 -30
- agno/knowledge/json.py +0 -28
- agno/knowledge/langchain.py +0 -71
- agno/knowledge/llamaindex.py +0 -66
- agno/knowledge/pdf.py +0 -28
- agno/knowledge/pdf_url.py +0 -26
- agno/knowledge/s3/base.py +0 -60
- agno/knowledge/s3/pdf.py +0 -21
- agno/knowledge/s3/text.py +0 -23
- agno/knowledge/text.py +0 -30
- agno/knowledge/website.py +0 -88
- agno/knowledge/wikipedia.py +0 -31
- agno/knowledge/youtube.py +0 -22
- agno/memory/agent.py +0 -392
- agno/memory/classifier.py +0 -104
- agno/memory/db/__init__.py +0 -1
- agno/memory/db/base.py +0 -42
- agno/memory/db/mongodb.py +0 -189
- agno/memory/db/postgres.py +0 -203
- agno/memory/db/sqlite.py +0 -193
- agno/memory/memory.py +0 -15
- agno/memory/row.py +0 -36
- agno/memory/summarizer.py +0 -192
- agno/memory/summary.py +0 -19
- agno/memory/workflow.py +0 -38
- agno/models/google/gemini_openai.py +0 -26
- agno/models/ollama/hermes.py +0 -221
- agno/models/ollama/tools.py +0 -362
- agno/models/vertexai/gemini.py +0 -595
- agno/playground/__init__.py +0 -3
- agno/playground/async_router.py +0 -421
- agno/playground/deploy.py +0 -249
- agno/playground/operator.py +0 -92
- agno/playground/playground.py +0 -91
- agno/playground/schemas.py +0 -76
- agno/playground/serve.py +0 -55
- agno/playground/sync_router.py +0 -405
- agno/reasoning/agent.py +0 -68
- agno/run/response.py +0 -112
- agno/storage/agent/__init__.py +0 -0
- agno/storage/agent/base.py +0 -38
- agno/storage/agent/dynamodb.py +0 -350
- agno/storage/agent/json.py +0 -92
- agno/storage/agent/mongodb.py +0 -228
- agno/storage/agent/postgres.py +0 -367
- agno/storage/agent/session.py +0 -79
- agno/storage/agent/singlestore.py +0 -303
- agno/storage/agent/sqlite.py +0 -357
- agno/storage/agent/yaml.py +0 -93
- agno/storage/workflow/__init__.py +0 -0
- agno/storage/workflow/base.py +0 -40
- agno/storage/workflow/mongodb.py +0 -233
- agno/storage/workflow/postgres.py +0 -366
- agno/storage/workflow/session.py +0 -60
- agno/storage/workflow/sqlite.py +0 -359
- agno/tools/googlesearch.py +0 -88
- agno/utils/defaults.py +0 -57
- agno/utils/filesystem.py +0 -39
- agno/utils/git.py +0 -52
- agno/utils/json_io.py +0 -30
- agno/utils/load_env.py +0 -19
- agno/utils/py_io.py +0 -19
- agno/utils/pyproject.py +0 -18
- agno/utils/resource_filter.py +0 -31
- agno/vectordb/singlestore/s2vectordb.py +0 -390
- agno/vectordb/singlestore/s2vectordb2.py +0 -355
- agno/workspace/__init__.py +0 -0
- agno/workspace/config.py +0 -325
- agno/workspace/enums.py +0 -6
- agno/workspace/helpers.py +0 -48
- agno/workspace/operator.py +0 -758
- agno/workspace/settings.py +0 -63
- agno-0.1.2.dist-info/LICENSE +0 -375
- agno-0.1.2.dist-info/METADATA +0 -502
- agno-0.1.2.dist-info/RECORD +0 -352
- agno-0.1.2.dist-info/entry_points.txt +0 -3
- /agno/{cli → db/migrations}/__init__.py +0 -0
- /agno/{cli/ws → db/migrations/versions}/__init__.py +0 -0
- /agno/{document/chunking/__init__.py → db/schemas/metrics.py} +0 -0
- /agno/{document/reader/s3 → integrations}/__init__.py +0 -0
- /agno/{file/local → knowledge/chunking}/__init__.py +0 -0
- /agno/{infra → knowledge/remote_content}/__init__.py +0 -0
- /agno/{knowledge/s3 → tools/models}/__init__.py +0 -0
- /agno/{reranker → utils/models}/__init__.py +0 -0
- /agno/{storage → utils/print_response}/__init__.py +0 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
agno/tools/postgres.py
CHANGED
|
@@ -1,35 +1,46 @@
|
|
|
1
|
-
|
|
1
|
+
import csv
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
2
3
|
|
|
3
4
|
try:
|
|
4
|
-
import
|
|
5
|
+
import psycopg
|
|
6
|
+
from psycopg import sql
|
|
7
|
+
from psycopg.connection import Connection as PgConnection
|
|
8
|
+
from psycopg.rows import DictRow, dict_row
|
|
5
9
|
except ImportError:
|
|
6
|
-
raise ImportError(
|
|
7
|
-
"`psycopg2` not installed. Please install using `pip install psycopg2`. If you face issues, try `pip install psycopg2-binary`."
|
|
8
|
-
)
|
|
10
|
+
raise ImportError("`psycopg` not installed. Please install using `pip install 'psycopg-binary'`.")
|
|
9
11
|
|
|
10
12
|
from agno.tools import Toolkit
|
|
11
|
-
from agno.utils.log import
|
|
13
|
+
from agno.utils.log import log_debug, log_error
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
class PostgresTools(Toolkit):
|
|
15
|
-
"""
|
|
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
|
|
16
31
|
|
|
17
32
|
def __init__(
|
|
18
33
|
self,
|
|
19
|
-
connection: Optional[
|
|
34
|
+
connection: Optional[PgConnection[DictRow]] = None,
|
|
20
35
|
db_name: Optional[str] = None,
|
|
21
36
|
user: Optional[str] = None,
|
|
22
37
|
password: Optional[str] = None,
|
|
23
38
|
host: Optional[str] = None,
|
|
24
39
|
port: Optional[int] = None,
|
|
25
|
-
run_queries: bool = True,
|
|
26
|
-
inspect_queries: bool = False,
|
|
27
|
-
summarize_tables: bool = True,
|
|
28
|
-
export_tables: bool = False,
|
|
29
40
|
table_schema: str = "public",
|
|
41
|
+
**kwargs,
|
|
30
42
|
):
|
|
31
|
-
|
|
32
|
-
self._connection: Optional[psycopg2.extensions.connection] = connection
|
|
43
|
+
self._connection: Optional[PgConnection[DictRow]] = connection
|
|
33
44
|
self.db_name: Optional[str] = db_name
|
|
34
45
|
self.user: Optional[str] = user
|
|
35
46
|
self.password: Optional[str] = password
|
|
@@ -37,208 +48,250 @@ class PostgresTools(Toolkit):
|
|
|
37
48
|
self.port: Optional[int] = port
|
|
38
49
|
self.table_schema: str = table_schema
|
|
39
50
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
self.
|
|
44
|
-
|
|
45
|
-
self.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
tools: List[Any] = [
|
|
52
|
+
self.show_tables,
|
|
53
|
+
self.describe_table,
|
|
54
|
+
self.summarize_table,
|
|
55
|
+
self.inspect_query,
|
|
56
|
+
self.run_query,
|
|
57
|
+
self.export_table_to_path,
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
super().__init__(name="postgres_tools", tools=tools, **kwargs)
|
|
61
|
+
|
|
62
|
+
def connect(self) -> PgConnection[DictRow]:
|
|
63
|
+
"""
|
|
64
|
+
Establish a connection to the PostgreSQL database.
|
|
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
|
|
90
|
+
return self._connection
|
|
91
|
+
|
|
92
|
+
def close(self) -> None:
|
|
93
|
+
"""Closes the database connection if it's open."""
|
|
94
|
+
if self._connection and not self._connection.closed:
|
|
95
|
+
log_debug("Closing PostgreSQL connection.")
|
|
96
|
+
self._connection.close()
|
|
97
|
+
self._connection = None
|
|
50
98
|
|
|
51
99
|
@property
|
|
52
|
-
def
|
|
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]:
|
|
53
105
|
"""
|
|
54
|
-
|
|
106
|
+
Ensure a connection exists, creating one if necessary.
|
|
55
107
|
|
|
56
|
-
:
|
|
108
|
+
Returns:
|
|
109
|
+
The database connection object.
|
|
57
110
|
"""
|
|
58
|
-
if self.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
connection_kwargs["database"] = self.db_name
|
|
62
|
-
if self.user is not None:
|
|
63
|
-
connection_kwargs["user"] = self.user
|
|
64
|
-
if self.password is not None:
|
|
65
|
-
connection_kwargs["password"] = self.password
|
|
66
|
-
if self.host is not None:
|
|
67
|
-
connection_kwargs["host"] = self.host
|
|
68
|
-
if self.port is not None:
|
|
69
|
-
connection_kwargs["port"] = self.port
|
|
70
|
-
if self.table_schema is not None:
|
|
71
|
-
connection_kwargs["options"] = f"-c search_path={self.table_schema}"
|
|
72
|
-
|
|
73
|
-
self._connection = psycopg2.connect(**connection_kwargs)
|
|
74
|
-
self._connection.set_session(readonly=True)
|
|
111
|
+
if not self.is_connected:
|
|
112
|
+
return self.connect()
|
|
113
|
+
return self._connection # type: ignore
|
|
75
114
|
|
|
76
|
-
|
|
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
|
+
|
|
122
|
+
def _execute_query(self, query: str, params: Optional[tuple] = None) -> str:
|
|
123
|
+
try:
|
|
124
|
+
connection = self._ensure_connection()
|
|
125
|
+
with connection.cursor() as cursor:
|
|
126
|
+
log_debug("Running PostgreSQL query")
|
|
127
|
+
cursor.execute(query, params)
|
|
128
|
+
|
|
129
|
+
if cursor.description is None:
|
|
130
|
+
return cursor.statusmessage or "Query executed successfully with no output."
|
|
131
|
+
|
|
132
|
+
columns = [desc[0] for desc in cursor.description]
|
|
133
|
+
rows = cursor.fetchall()
|
|
134
|
+
|
|
135
|
+
if not rows:
|
|
136
|
+
return f"Query returned no results.\nColumns: {', '.join(columns)}"
|
|
137
|
+
|
|
138
|
+
header = ",".join(columns)
|
|
139
|
+
data_rows = [",".join(map(str, row.values())) for row in rows]
|
|
140
|
+
return f"{header}\n" + "\n".join(data_rows)
|
|
141
|
+
|
|
142
|
+
except psycopg.Error as e:
|
|
143
|
+
log_error(f"Database error: {e}")
|
|
144
|
+
if self._connection and not self._connection.closed:
|
|
145
|
+
self._connection.rollback()
|
|
146
|
+
return f"Error executing query: {e}"
|
|
147
|
+
except Exception as e:
|
|
148
|
+
log_error(f"An unexpected error occurred: {e}")
|
|
149
|
+
return f"An unexpected error occurred: {e}"
|
|
77
150
|
|
|
78
151
|
def show_tables(self) -> str:
|
|
79
|
-
"""
|
|
152
|
+
"""Lists all tables in the configured schema."""
|
|
80
153
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
stmt = f"SELECT table_name FROM information_schema.tables WHERE table_schema = '{self.table_schema}';"
|
|
84
|
-
tables = self.run_query(stmt)
|
|
85
|
-
logger.debug(f"Tables: {tables}")
|
|
86
|
-
return tables
|
|
154
|
+
stmt = "SELECT table_name FROM information_schema.tables WHERE table_schema = %s;"
|
|
155
|
+
return self._execute_query(stmt, (self.table_schema,))
|
|
87
156
|
|
|
88
157
|
def describe_table(self, table: str) -> str:
|
|
89
|
-
"""Function to describe a table
|
|
90
|
-
|
|
91
|
-
:param table: Table to describe
|
|
92
|
-
:return: Description of the table
|
|
93
158
|
"""
|
|
94
|
-
|
|
95
|
-
table_description = self.run_query(stmt)
|
|
159
|
+
Provides the schema (column name, data type, is nullable) for a given table.
|
|
96
160
|
|
|
97
|
-
|
|
98
|
-
|
|
161
|
+
Args:
|
|
162
|
+
table: The name of the table to describe.
|
|
99
163
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
The function launches a query that computes a number of aggregates over all columns,
|
|
103
|
-
including min, max, avg, std and approx_unique.
|
|
104
|
-
|
|
105
|
-
:param table: Table to summarize
|
|
106
|
-
:return: Summary of the table
|
|
164
|
+
Returns:
|
|
165
|
+
A string describing the table's columns and data types.
|
|
107
166
|
"""
|
|
108
|
-
stmt =
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
FROM
|
|
113
|
-
information_schema.columns
|
|
114
|
-
WHERE
|
|
115
|
-
table_name = '{table}'
|
|
116
|
-
AND table_schema = '{self.table_schema}'
|
|
117
|
-
)
|
|
118
|
-
SELECT
|
|
119
|
-
column_name,
|
|
120
|
-
data_type,
|
|
121
|
-
COUNT(COALESCE(column_name::text, '')) AS non_null_count,
|
|
122
|
-
COUNT(*) - COUNT(COALESCE(column_name::text, '')) AS null_count,
|
|
123
|
-
SUM(COALESCE(column_name::numeric, 0)) AS sum,
|
|
124
|
-
AVG(COALESCE(column_name::numeric, 0)) AS mean,
|
|
125
|
-
MIN(column_name::numeric) AS min,
|
|
126
|
-
MAX(column_name::numeric) AS max,
|
|
127
|
-
STDDEV(COALESCE(column_name::numeric, 0)) AS stddev
|
|
128
|
-
FROM
|
|
129
|
-
column_stats,
|
|
130
|
-
LATERAL (
|
|
131
|
-
SELECT
|
|
132
|
-
*
|
|
133
|
-
FROM
|
|
134
|
-
{table}
|
|
135
|
-
) AS tbl
|
|
136
|
-
WHERE
|
|
137
|
-
data_type IN ('integer', 'numeric', 'real', 'double precision')
|
|
138
|
-
GROUP BY
|
|
139
|
-
column_name, data_type
|
|
140
|
-
UNION ALL
|
|
141
|
-
SELECT
|
|
142
|
-
column_name,
|
|
143
|
-
data_type,
|
|
144
|
-
COUNT(COALESCE(column_name::text, '')) AS non_null_count,
|
|
145
|
-
COUNT(*) - COUNT(COALESCE(column_name::text, '')) AS null_count,
|
|
146
|
-
NULL AS sum,
|
|
147
|
-
NULL AS mean,
|
|
148
|
-
NULL AS min,
|
|
149
|
-
NULL AS max,
|
|
150
|
-
NULL AS stddev
|
|
151
|
-
FROM
|
|
152
|
-
column_stats,
|
|
153
|
-
LATERAL (
|
|
154
|
-
SELECT
|
|
155
|
-
*
|
|
156
|
-
FROM
|
|
157
|
-
{table}
|
|
158
|
-
) AS tbl
|
|
159
|
-
WHERE
|
|
160
|
-
data_type NOT IN ('integer', 'numeric', 'real', 'double precision')
|
|
161
|
-
GROUP BY
|
|
162
|
-
column_name, data_type;
|
|
167
|
+
stmt = """
|
|
168
|
+
SELECT column_name, data_type, is_nullable
|
|
169
|
+
FROM information_schema.columns
|
|
170
|
+
WHERE table_schema = %s AND table_name = %s;
|
|
163
171
|
"""
|
|
164
|
-
|
|
172
|
+
return self._execute_query(stmt, (self.table_schema, table))
|
|
165
173
|
|
|
166
|
-
|
|
167
|
-
|
|
174
|
+
def summarize_table(self, table: str) -> str:
|
|
175
|
+
"""
|
|
176
|
+
Computes and returns key summary statistics for a table's columns.
|
|
168
177
|
|
|
169
|
-
|
|
170
|
-
|
|
178
|
+
Args:
|
|
179
|
+
table: The name of the table to summarize.
|
|
171
180
|
|
|
172
|
-
:
|
|
173
|
-
|
|
181
|
+
Returns:
|
|
182
|
+
A string containing a summary of the table.
|
|
174
183
|
"""
|
|
175
|
-
|
|
176
|
-
|
|
184
|
+
try:
|
|
185
|
+
connection = self._ensure_connection()
|
|
186
|
+
with connection.cursor() as cursor:
|
|
187
|
+
# First, get column information using a parameterized query
|
|
188
|
+
schema_query = """
|
|
189
|
+
SELECT column_name, data_type
|
|
190
|
+
FROM information_schema.columns
|
|
191
|
+
WHERE table_schema = %s AND table_name = %s;
|
|
192
|
+
"""
|
|
193
|
+
cursor.execute(schema_query, (self.table_schema, table))
|
|
194
|
+
columns = cursor.fetchall()
|
|
195
|
+
if not columns:
|
|
196
|
+
return f"Error: Table '{table}' not found in schema '{self.table_schema}'."
|
|
197
|
+
|
|
198
|
+
summary_parts = [f"Summary for table: {table}\n"]
|
|
199
|
+
table_identifier = sql.Identifier(self.table_schema, table)
|
|
200
|
+
|
|
201
|
+
for col in columns:
|
|
202
|
+
col_name, data_type = col["column_name"], col["data_type"]
|
|
203
|
+
col_identifier = sql.Identifier(col_name)
|
|
204
|
+
|
|
205
|
+
query = None
|
|
206
|
+
if any(
|
|
207
|
+
t in data_type for t in ["integer", "numeric", "real", "double precision", "bigint", "smallint"]
|
|
208
|
+
):
|
|
209
|
+
query = sql.SQL("""
|
|
210
|
+
SELECT
|
|
211
|
+
COUNT(*) AS total_rows,
|
|
212
|
+
COUNT({col}) AS non_null_rows,
|
|
213
|
+
MIN({col}) AS min,
|
|
214
|
+
MAX({col}) AS max,
|
|
215
|
+
AVG({col}) AS average,
|
|
216
|
+
STDDEV({col}) AS std_deviation
|
|
217
|
+
FROM {tbl};
|
|
218
|
+
""").format(col=col_identifier, tbl=table_identifier)
|
|
219
|
+
elif any(t in data_type for t in ["char", "text", "uuid"]):
|
|
220
|
+
query = sql.SQL("""
|
|
221
|
+
SELECT
|
|
222
|
+
COUNT(*) AS total_rows,
|
|
223
|
+
COUNT({col}) AS non_null_rows,
|
|
224
|
+
COUNT(DISTINCT {col}) AS unique_values,
|
|
225
|
+
AVG(LENGTH({col}::text)) as avg_length
|
|
226
|
+
FROM {tbl};
|
|
227
|
+
""").format(col=col_identifier, tbl=table_identifier)
|
|
228
|
+
|
|
229
|
+
if query:
|
|
230
|
+
cursor.execute(query)
|
|
231
|
+
stats = cursor.fetchone()
|
|
232
|
+
summary_parts.append(f"\n--- Column: {col_name} (Type: {data_type}) ---")
|
|
233
|
+
if stats is not None:
|
|
234
|
+
for key, value in stats.items():
|
|
235
|
+
val_str = (
|
|
236
|
+
f"{value:.2f}" if isinstance(value, float) and value is not None else str(value)
|
|
237
|
+
)
|
|
238
|
+
summary_parts.append(f" {key}: {val_str}")
|
|
239
|
+
else:
|
|
240
|
+
summary_parts.append(" No statistics available")
|
|
177
241
|
|
|
178
|
-
|
|
179
|
-
return explain_plan
|
|
242
|
+
return "\n".join(summary_parts)
|
|
180
243
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
If the path is provided, the table will be saved under that path.
|
|
184
|
-
Eg: If path is /tmp, the table will be saved as /tmp/table.csv
|
|
185
|
-
Otherwise it will be saved in the current directory
|
|
244
|
+
except psycopg.Error as e:
|
|
245
|
+
return f"Error summarizing table: {e}"
|
|
186
246
|
|
|
187
|
-
|
|
188
|
-
:param path: Path to export to
|
|
189
|
-
:return: None
|
|
247
|
+
def inspect_query(self, query: str) -> str:
|
|
190
248
|
"""
|
|
249
|
+
Shows the execution plan for a SQL query (using EXPLAIN).
|
|
191
250
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
path = f"{path}/{table}.csv"
|
|
197
|
-
|
|
198
|
-
export_statement = f"COPY {self.table_schema}.{table} TO '{path}' DELIMITER ',' CSV HEADER;"
|
|
199
|
-
result = self.run_query(export_statement)
|
|
200
|
-
logger.debug(f"Exported {table} to {path}/{table}")
|
|
201
|
-
|
|
202
|
-
return result
|
|
251
|
+
:param query: The SQL query to inspect.
|
|
252
|
+
:return: The query's execution plan.
|
|
253
|
+
"""
|
|
254
|
+
return self._execute_query(f"EXPLAIN {query}")
|
|
203
255
|
|
|
204
|
-
def
|
|
205
|
-
"""
|
|
256
|
+
def export_table_to_path(self, table: str, path: str) -> str:
|
|
257
|
+
"""
|
|
258
|
+
Exports a table's data to a local CSV file.
|
|
206
259
|
|
|
207
|
-
:param
|
|
208
|
-
:
|
|
260
|
+
:param table: The name of the table to export.
|
|
261
|
+
:param path: The local file path to save the file.
|
|
262
|
+
:return: A confirmation message with the file path.
|
|
209
263
|
"""
|
|
264
|
+
log_debug(f"Exporting Table {table} as CSV to local path {path}")
|
|
210
265
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
formatted_sql = query.replace("`", "")
|
|
214
|
-
# If there are multiple statements, only run the first one
|
|
215
|
-
formatted_sql = formatted_sql.split(";")[0]
|
|
266
|
+
table_identifier = sql.Identifier(self.table_schema, table)
|
|
267
|
+
stmt = sql.SQL("SELECT * FROM {tbl};").format(tbl=table_identifier)
|
|
216
268
|
|
|
217
269
|
try:
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
cursor.execute(query)
|
|
222
|
-
query_result = cursor.fetchall()
|
|
223
|
-
|
|
224
|
-
result_output = "No output"
|
|
225
|
-
if query_result is not None:
|
|
226
|
-
try:
|
|
227
|
-
results_as_python_objects = query_result
|
|
228
|
-
result_rows = []
|
|
229
|
-
for row in results_as_python_objects:
|
|
230
|
-
if len(row) == 1:
|
|
231
|
-
result_rows.append(str(row[0]))
|
|
232
|
-
else:
|
|
233
|
-
result_rows.append(",".join(str(x) for x in row))
|
|
270
|
+
connection = self._ensure_connection()
|
|
271
|
+
with connection.cursor() as cursor:
|
|
272
|
+
cursor.execute(stmt)
|
|
234
273
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
except AttributeError:
|
|
238
|
-
result_output = str(query_result)
|
|
274
|
+
if cursor.description is None:
|
|
275
|
+
return f"Error: Query returned no description for table '{table}'."
|
|
239
276
|
|
|
240
|
-
|
|
277
|
+
columns = [desc[0] for desc in cursor.description]
|
|
241
278
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
279
|
+
with open(path, "w", newline="", encoding="utf-8") as f:
|
|
280
|
+
writer = csv.writer(f)
|
|
281
|
+
writer.writerow(columns)
|
|
282
|
+
writer.writerows(row.values() for row in cursor)
|
|
283
|
+
|
|
284
|
+
return f"Successfully exported table '{table}' to '{path}'."
|
|
285
|
+
except (psycopg.Error, IOError) as e:
|
|
286
|
+
if self._connection and not self._connection.closed:
|
|
287
|
+
self._connection.rollback()
|
|
288
|
+
return f"Error exporting table: {e}"
|
|
289
|
+
|
|
290
|
+
def run_query(self, query: str) -> str:
|
|
291
|
+
"""
|
|
292
|
+
Runs a read-only SQL query and returns the result.
|
|
293
|
+
|
|
294
|
+
:param query: The SQL query to run.
|
|
295
|
+
:return: The query result as a formatted string.
|
|
296
|
+
"""
|
|
297
|
+
return self._execute_query(query)
|
agno/tools/pubmed.py
CHANGED
|
@@ -5,6 +5,7 @@ from xml.etree import ElementTree
|
|
|
5
5
|
import httpx
|
|
6
6
|
|
|
7
7
|
from agno.tools import Toolkit
|
|
8
|
+
from agno.utils.log import log_debug
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class PubmedTools(Toolkit):
|
|
@@ -12,12 +13,20 @@ class PubmedTools(Toolkit):
|
|
|
12
13
|
self,
|
|
13
14
|
email: str = "your_email@example.com",
|
|
14
15
|
max_results: Optional[int] = None,
|
|
16
|
+
results_expanded: bool = False,
|
|
17
|
+
enable_search_pubmed: bool = True,
|
|
18
|
+
all: bool = False,
|
|
19
|
+
**kwargs,
|
|
15
20
|
):
|
|
16
|
-
super().__init__(name="pubmed")
|
|
17
21
|
self.max_results: Optional[int] = max_results
|
|
18
22
|
self.email: str = email
|
|
23
|
+
self.results_expanded: bool = results_expanded
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
tools: List[Any] = []
|
|
26
|
+
if enable_search_pubmed or all:
|
|
27
|
+
tools.append(self.search_pubmed)
|
|
28
|
+
|
|
29
|
+
super().__init__(name="pubmed", tools=tools, **kwargs)
|
|
21
30
|
|
|
22
31
|
def fetch_pubmed_ids(self, query: str, max_results: int, email: str) -> List[str]:
|
|
23
32
|
url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi"
|
|
@@ -41,36 +50,139 @@ class PubmedTools(Toolkit):
|
|
|
41
50
|
def parse_details(self, xml_root: ElementTree.Element) -> List[Dict[str, Any]]:
|
|
42
51
|
articles = []
|
|
43
52
|
for article in xml_root.findall(".//PubmedArticle"):
|
|
53
|
+
# Get existing fields
|
|
44
54
|
pub_date = article.find(".//PubDate/Year")
|
|
45
55
|
title = article.find(".//ArticleTitle")
|
|
46
|
-
|
|
56
|
+
|
|
57
|
+
# Handle abstract sections with labels (methods, results, etc.)
|
|
58
|
+
abstract_sections = article.findall(".//AbstractText")
|
|
59
|
+
abstract_text = ""
|
|
60
|
+
if abstract_sections:
|
|
61
|
+
for section in abstract_sections:
|
|
62
|
+
label = section.get("Label", "")
|
|
63
|
+
if label:
|
|
64
|
+
abstract_text += f"{label}: {section.text}\n\n"
|
|
65
|
+
else:
|
|
66
|
+
abstract_text += f"{section.text}\n\n"
|
|
67
|
+
abstract_text = abstract_text.strip()
|
|
68
|
+
else:
|
|
69
|
+
abstract_text = "No abstract available"
|
|
70
|
+
|
|
71
|
+
# Get first author
|
|
72
|
+
first_author_elem = article.find(".//AuthorList/Author[1]")
|
|
73
|
+
first_author = "Unknown"
|
|
74
|
+
if first_author_elem is not None:
|
|
75
|
+
last_name = first_author_elem.find("LastName")
|
|
76
|
+
fore_name = first_author_elem.find("ForeName")
|
|
77
|
+
if last_name is not None and fore_name is not None:
|
|
78
|
+
first_author = f"{last_name.text}, {fore_name.text}"
|
|
79
|
+
elif last_name is not None and last_name.text:
|
|
80
|
+
first_author = last_name.text
|
|
81
|
+
|
|
82
|
+
# Get DOI
|
|
83
|
+
doi_elem = article.find(".//ArticleIdList/ArticleId[@IdType='doi']")
|
|
84
|
+
doi = doi_elem.text if doi_elem is not None else "No DOI available"
|
|
85
|
+
|
|
86
|
+
# Get PMID for URL construction
|
|
87
|
+
pmid_elem = article.find(".//PMID")
|
|
88
|
+
pmid = pmid_elem.text if pmid_elem is not None else ""
|
|
89
|
+
pubmed_url = f"https://pubmed.ncbi.nlm.nih.gov/{pmid}/" if pmid else "No URL available"
|
|
90
|
+
|
|
91
|
+
# Check if full text is available via PMC
|
|
92
|
+
pmc_elem = article.find(".//ArticleIdList/ArticleId[@IdType='pmc']")
|
|
93
|
+
full_text_url = "Not available"
|
|
94
|
+
if pmc_elem is not None:
|
|
95
|
+
full_text_url = f"https://www.ncbi.nlm.nih.gov/pmc/articles/{pmc_elem.text}/"
|
|
96
|
+
elif doi_elem is not None:
|
|
97
|
+
full_text_url = f"https://doi.org/{doi}"
|
|
98
|
+
|
|
99
|
+
# Get keywords
|
|
100
|
+
keywords = []
|
|
101
|
+
for keyword in article.findall(".//KeywordList/Keyword"):
|
|
102
|
+
if keyword.text:
|
|
103
|
+
keywords.append(keyword.text)
|
|
104
|
+
|
|
105
|
+
# Get MeSH terms (useful for understanding medical context)
|
|
106
|
+
mesh_terms = []
|
|
107
|
+
for mesh in article.findall(".//MeshHeading/DescriptorName"):
|
|
108
|
+
if mesh.text:
|
|
109
|
+
mesh_terms.append(mesh.text)
|
|
110
|
+
|
|
111
|
+
# Get journal info
|
|
112
|
+
journal_elem = article.find(".//Journal/Title")
|
|
113
|
+
journal = journal_elem.text if journal_elem is not None else "Unknown Journal"
|
|
114
|
+
|
|
115
|
+
# Publication type (research article, review, etc.)
|
|
116
|
+
pub_types = []
|
|
117
|
+
for pub_type in article.findall(".//PublicationTypeList/PublicationType"):
|
|
118
|
+
if pub_type.text:
|
|
119
|
+
pub_types.append(pub_type.text)
|
|
120
|
+
|
|
47
121
|
articles.append(
|
|
48
122
|
{
|
|
49
|
-
"Published":
|
|
123
|
+
"Published": pub_date.text if pub_date is not None else "No date available",
|
|
50
124
|
"Title": title.text if title is not None else "No title available",
|
|
51
|
-
"Summary":
|
|
125
|
+
"Summary": abstract_text,
|
|
126
|
+
"First_Author": first_author,
|
|
127
|
+
"DOI": doi,
|
|
128
|
+
"PubMed_URL": pubmed_url,
|
|
129
|
+
"Full_Text_URL": full_text_url,
|
|
130
|
+
"Keywords": ", ".join(keywords) if keywords else "No keywords available",
|
|
131
|
+
"MeSH_Terms": ", ".join(mesh_terms) if mesh_terms else "No MeSH terms available",
|
|
132
|
+
"Journal": journal,
|
|
133
|
+
"Publication_Type": ", ".join(pub_types) if pub_types else "Not specified",
|
|
52
134
|
}
|
|
53
135
|
)
|
|
136
|
+
|
|
54
137
|
return articles
|
|
55
138
|
|
|
56
|
-
def search_pubmed(self, query: str, max_results: int = 10) -> str:
|
|
139
|
+
def search_pubmed(self, query: str, max_results: Optional[int] = 10) -> str:
|
|
57
140
|
"""Use this function to search PubMed for articles.
|
|
58
141
|
|
|
59
142
|
Args:
|
|
60
143
|
query (str): The search query.
|
|
61
|
-
max_results (int): The maximum number of results to return.
|
|
144
|
+
max_results (int): The maximum number of results to return (default 10).
|
|
62
145
|
|
|
63
146
|
Returns:
|
|
64
147
|
str: A JSON string containing the search results.
|
|
65
148
|
"""
|
|
66
149
|
try:
|
|
67
|
-
|
|
150
|
+
log_debug(f"Searching PubMed for: {query}")
|
|
151
|
+
max_results = max_results or self.max_results or 10
|
|
152
|
+
ids = self.fetch_pubmed_ids(query, max_results, self.email)
|
|
68
153
|
details_root = self.fetch_details(ids)
|
|
69
154
|
articles = self.parse_details(details_root)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
155
|
+
|
|
156
|
+
# Create result strings based on configured detail level
|
|
157
|
+
results = []
|
|
158
|
+
for article in articles:
|
|
159
|
+
if self.results_expanded:
|
|
160
|
+
# Comprehensive format with all metadata
|
|
161
|
+
article_text = (
|
|
162
|
+
f"Published: {article.get('Published')}\n"
|
|
163
|
+
f"Title: {article.get('Title')}\n"
|
|
164
|
+
f"First Author: {article.get('First_Author')}\n"
|
|
165
|
+
f"Journal: {article.get('Journal')}\n"
|
|
166
|
+
f"Publication Type: {article.get('Publication_Type')}\n"
|
|
167
|
+
f"DOI: {article.get('DOI')}\n"
|
|
168
|
+
f"PubMed URL: {article.get('PubMed_URL')}\n"
|
|
169
|
+
f"Full Text URL: {article.get('Full_Text_URL')}\n"
|
|
170
|
+
f"Keywords: {article.get('Keywords')}\n"
|
|
171
|
+
f"MeSH Terms: {article.get('MeSH_Terms')}\n"
|
|
172
|
+
f"Summary:\n{article.get('Summary')}"
|
|
173
|
+
)
|
|
174
|
+
else:
|
|
175
|
+
# Concise format with just essential information
|
|
176
|
+
summary = article.get("Summary", "")
|
|
177
|
+
article_text = (
|
|
178
|
+
f"Title: {article.get('Title')}\n"
|
|
179
|
+
f"Published: {article.get('Published')}\n"
|
|
180
|
+
f"Summary: {summary[:200]}..."
|
|
181
|
+
if len(summary) > 200
|
|
182
|
+
else f"Summary: {summary}"
|
|
183
|
+
)
|
|
184
|
+
results.append(article_text)
|
|
185
|
+
|
|
74
186
|
return json.dumps(results)
|
|
75
187
|
except Exception as e:
|
|
76
|
-
return f"
|
|
188
|
+
return f"Could not fetch articles. Error: {e}"
|