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
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from os import getenv
|
|
3
|
+
from typing import Any, List, Optional
|
|
4
|
+
|
|
5
|
+
from agno.tools import Toolkit
|
|
6
|
+
from agno.utils.log import log_debug, logger
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from google.cloud import bigquery
|
|
10
|
+
except ImportError:
|
|
11
|
+
raise ImportError("`bigquery` not installed. Please install using `pip install google-cloud-bigquery`")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GoogleBigQueryTools(Toolkit):
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
dataset: str,
|
|
18
|
+
project: Optional[str] = None,
|
|
19
|
+
location: Optional[str] = None,
|
|
20
|
+
credentials: Optional[Any] = None,
|
|
21
|
+
enable_list_tables: bool = True,
|
|
22
|
+
enable_describe_table: bool = True,
|
|
23
|
+
enable_run_sql_query: bool = True,
|
|
24
|
+
all: bool = False,
|
|
25
|
+
**kwargs,
|
|
26
|
+
):
|
|
27
|
+
self.project = project or getenv("GOOGLE_CLOUD_PROJECT")
|
|
28
|
+
self.location = location or getenv("GOOGLE_CLOUD_LOCATION")
|
|
29
|
+
|
|
30
|
+
if not self.project:
|
|
31
|
+
raise ValueError("project is required")
|
|
32
|
+
if not self.location:
|
|
33
|
+
raise ValueError("location is required")
|
|
34
|
+
|
|
35
|
+
self.dataset = dataset
|
|
36
|
+
|
|
37
|
+
# Initialize the BQ CLient
|
|
38
|
+
self.client = bigquery.Client(project=self.project, credentials=credentials)
|
|
39
|
+
|
|
40
|
+
tools: List[Any] = []
|
|
41
|
+
if all or enable_list_tables:
|
|
42
|
+
tools.append(self.list_tables)
|
|
43
|
+
if all or enable_describe_table:
|
|
44
|
+
tools.append(self.describe_table)
|
|
45
|
+
if all or enable_run_sql_query:
|
|
46
|
+
tools.append(self.run_sql_query)
|
|
47
|
+
|
|
48
|
+
super().__init__(name="google_bigquery_tools", tools=tools, **kwargs)
|
|
49
|
+
|
|
50
|
+
def list_tables(self) -> str:
|
|
51
|
+
"""Use this function to get a list of table names in the dataset.
|
|
52
|
+
Returns:
|
|
53
|
+
str: list of tables in the dataset.
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
log_debug("listing tables in the database")
|
|
57
|
+
tables = self.client.list_tables(self.dataset)
|
|
58
|
+
tables_str = str([table.table_id for table in tables])
|
|
59
|
+
log_debug(f"table_names: {tables_str}")
|
|
60
|
+
return tables_str
|
|
61
|
+
except Exception as e:
|
|
62
|
+
logger.error(f"Error getting tables: {e}")
|
|
63
|
+
return f"Error getting tables: {e}"
|
|
64
|
+
|
|
65
|
+
def describe_table(self, table_id: str) -> str:
|
|
66
|
+
"""Use this function to describe a table.
|
|
67
|
+
Args:
|
|
68
|
+
table_name (str): The name of the table to get the schema for.
|
|
69
|
+
Returns:
|
|
70
|
+
str: schema of a table
|
|
71
|
+
"""
|
|
72
|
+
try:
|
|
73
|
+
table_id = f"{self.project}.{self.dataset}.{table_id}"
|
|
74
|
+
log_debug(f"Describing table: {table_id}")
|
|
75
|
+
api_response = self.client.get_table(table_id)
|
|
76
|
+
table_api_repr = api_response.to_api_repr()
|
|
77
|
+
desc = str(table_api_repr.get("description", ""))
|
|
78
|
+
col_names = str([column["name"] for column in table_api_repr["schema"]["fields"]]) # Columns in a table
|
|
79
|
+
result = json.dumps({"table_description": desc, "columns": col_names})
|
|
80
|
+
return result
|
|
81
|
+
except Exception as e:
|
|
82
|
+
logger.error(f"Error getting table schema: {e}")
|
|
83
|
+
return f"Error getting table schema: {e}"
|
|
84
|
+
|
|
85
|
+
def run_sql_query(self, query: str) -> str:
|
|
86
|
+
"""Use this function to run a BigQuery SQL query and return the result.
|
|
87
|
+
Args:
|
|
88
|
+
query (str): The query to run.
|
|
89
|
+
Returns:
|
|
90
|
+
str: Result of the Google BigQuery SQL query.
|
|
91
|
+
Notes:
|
|
92
|
+
- The result may be empty if the query does not return any data.
|
|
93
|
+
"""
|
|
94
|
+
try:
|
|
95
|
+
return json.dumps(self._run_sql(sql=query), default=str)
|
|
96
|
+
except Exception as e:
|
|
97
|
+
logger.error(f"Error running query: {e}")
|
|
98
|
+
return f"Error running query: {e}"
|
|
99
|
+
|
|
100
|
+
def _run_sql(self, sql: str) -> str:
|
|
101
|
+
"""Internal function to run a sql query.
|
|
102
|
+
Args:
|
|
103
|
+
sql (str): The sql query to run.
|
|
104
|
+
Returns:
|
|
105
|
+
results (str): The result of the query.
|
|
106
|
+
"""
|
|
107
|
+
try:
|
|
108
|
+
log_debug(f"Running Google SQL |\n{sql}")
|
|
109
|
+
cleaned_query = sql.replace("\\n", " ").replace("\n", "").replace("\\", "")
|
|
110
|
+
job_config = bigquery.QueryJobConfig(default_dataset=f"{self.project}.{self.dataset}")
|
|
111
|
+
query_job = self.client.query(cleaned_query, job_config)
|
|
112
|
+
results = query_job.result()
|
|
113
|
+
results_str = str([dict(row) for row in results])
|
|
114
|
+
return results_str.replace("\\", "").replace("\n", "")
|
|
115
|
+
except Exception as e:
|
|
116
|
+
logger.error(f"Error while executing SQL: {e}")
|
|
117
|
+
return ""
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Google Drive API integration for file management and sharing.
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
This module provides functions to interact with Google Drive, including listing,
|
|
6
|
+
uploading, and downloading files.
|
|
7
|
+
It uses the Google Drive API and handles authentication via OAuth2.
|
|
8
|
+
|
|
9
|
+
Required Environment Variables:
|
|
10
|
+
-----------------------------
|
|
11
|
+
- GOOGLE_CLIENT_ID: Google OAuth client ID
|
|
12
|
+
- GOOGLE_CLIENT_SECRET: Google OAuth client secret
|
|
13
|
+
- GOOGLE_PROJECT_ID: Google Cloud project ID
|
|
14
|
+
- GOOGLE_REDIRECT_URI: Google OAuth redirect URI (default: http://localhost)
|
|
15
|
+
- GOOGLE_CLOUD_QUOTA_PROJECT_ID: Google Cloud quota project ID
|
|
16
|
+
|
|
17
|
+
How to Get These Credentials:
|
|
18
|
+
---------------------------
|
|
19
|
+
1. Go to Google Cloud Console (https://console.cloud.google.com)
|
|
20
|
+
2. Create a new project or select an existing one
|
|
21
|
+
3. Enable the Google Drive API:
|
|
22
|
+
- Go to "APIs & Services" > "Enable APIs and Services"
|
|
23
|
+
- Search for "Google Drive API"
|
|
24
|
+
- Click "Enable"
|
|
25
|
+
|
|
26
|
+
4. Create OAuth 2.0 credentials:
|
|
27
|
+
- Go to "APIs & Services" > "Credentials"
|
|
28
|
+
- Click "Create Credentials" > "OAuth client ID"
|
|
29
|
+
- Enable the OAuth Consent Screen if you haven't already
|
|
30
|
+
- After enabling the Consent Screen, click on "Create Credentials" > "OAuth client ID"
|
|
31
|
+
- You'll receive:
|
|
32
|
+
* Client ID (GOOGLE_CLIENT_ID)
|
|
33
|
+
* Client Secret (GOOGLE_CLIENT_SECRET)
|
|
34
|
+
- The Project ID (GOOGLE_PROJECT_ID) is visible in the project dropdown at the top of the page
|
|
35
|
+
|
|
36
|
+
5. Add auth redirect URI:
|
|
37
|
+
- Go to https://console.cloud.google.com/auth/clients
|
|
38
|
+
- Add `http://localhost:5050` as a recognized redirect URI OR with http://localhost:{PORT_NUMBER}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
6. Set up environment variables:
|
|
42
|
+
Create a .envrc file in your project root with:
|
|
43
|
+
``
|
|
44
|
+
export GOOGLE_CLIENT_ID=your_client_id_here
|
|
45
|
+
export GOOGLE_CLIENT_SECRET=your_client_secret_here
|
|
46
|
+
export GOOGLE_PROJECT_ID=your_project_id_here
|
|
47
|
+
export GOOGLE_REDIRECT_URI=http://localhost/ # Default value
|
|
48
|
+
export GOOGLE_AUTHENTICATION_PORT=5050 # Port for OAuth redirect
|
|
49
|
+
export GOOGLE_CLOUD_QUOTA_PROJECT_ID=your_quota_project_id_here
|
|
50
|
+
``
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
Remember to install the dependencies using `pip install google google-auth-oauthlib`
|
|
55
|
+
|
|
56
|
+
Important Points to Note :
|
|
57
|
+
1. The first time you run the application, it will open a browser window for OAuth authentication.
|
|
58
|
+
2. A token.json file will be created to store the authentication credentials for future use.
|
|
59
|
+
|
|
60
|
+
You can customize the authentication port by setting the `GOOGLE_AUTHENTICATION_PORT` environment variable.
|
|
61
|
+
This will be used in the `run_local_server` method for OAuth authentication.
|
|
62
|
+
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
import mimetypes
|
|
66
|
+
from functools import wraps
|
|
67
|
+
from os import getenv
|
|
68
|
+
from pathlib import Path
|
|
69
|
+
from typing import Any, List, Optional, Union
|
|
70
|
+
|
|
71
|
+
from agno.tools import Toolkit
|
|
72
|
+
from agno.utils.log import log_error
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
from google.auth.transport.requests import Request
|
|
76
|
+
from google.oauth2.credentials import Credentials
|
|
77
|
+
from google_auth_oauthlib.flow import InstalledAppFlow
|
|
78
|
+
from googleapiclient.discovery import Resource, build
|
|
79
|
+
from googleapiclient.http import MediaFileUpload, MediaIoBaseDownload
|
|
80
|
+
except ImportError:
|
|
81
|
+
raise ImportError(
|
|
82
|
+
"Google client library for Python not found , install it using `pip install google-api-python-client google-auth-httplib2 google-auth-oauthlib`"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def authenticate(func):
|
|
87
|
+
"""Decorator to ensure authentication before executing a function."""
|
|
88
|
+
|
|
89
|
+
@wraps(func)
|
|
90
|
+
def wrapper(self, *args, **kwargs):
|
|
91
|
+
if not self.creds or not self.creds.valid:
|
|
92
|
+
self._auth()
|
|
93
|
+
if not self.service:
|
|
94
|
+
# Set quota project on credentials if available
|
|
95
|
+
creds_to_use = self.creds
|
|
96
|
+
if hasattr(self, "quota_project_id") and self.quota_project_id:
|
|
97
|
+
creds_to_use = self.creds.with_quota_project(self.quota_project_id)
|
|
98
|
+
self.service = build("drive", "v3", credentials=creds_to_use)
|
|
99
|
+
return func(self, *args, **kwargs)
|
|
100
|
+
|
|
101
|
+
return wrapper
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class GoogleDriveTools(Toolkit):
|
|
105
|
+
# Default scopes for Google Drive API access
|
|
106
|
+
DEFAULT_SCOPES = ["https://www.googleapis.com/auth/drive.file", "https://www.googleapis.com/auth/drive.readonly"]
|
|
107
|
+
|
|
108
|
+
def __init__(
|
|
109
|
+
self,
|
|
110
|
+
auth_port: Optional[int] = 5050,
|
|
111
|
+
creds: Optional[Credentials] = None,
|
|
112
|
+
scopes: Optional[List[str]] = None,
|
|
113
|
+
creds_path: Optional[str] = None,
|
|
114
|
+
token_path: Optional[str] = None,
|
|
115
|
+
quota_project_id: Optional[str] = None,
|
|
116
|
+
**kwargs,
|
|
117
|
+
):
|
|
118
|
+
self.creds: Optional[Credentials] = creds
|
|
119
|
+
self.service: Optional[Resource] = None
|
|
120
|
+
self.credentials_path = creds_path
|
|
121
|
+
self.token_path = token_path
|
|
122
|
+
self.scopes = scopes or []
|
|
123
|
+
self.scopes.extend(self.DEFAULT_SCOPES)
|
|
124
|
+
|
|
125
|
+
self.quota_project_id = quota_project_id or getenv("GOOGLE_CLOUD_QUOTA_PROJECT_ID")
|
|
126
|
+
if not self.quota_project_id:
|
|
127
|
+
raise ValueError("GOOGLE_CLOUD_QUOTA_PROJECT_ID is not set")
|
|
128
|
+
|
|
129
|
+
self.auth_port: int = int(getenv("GOOGLE_AUTH_PORT", str(auth_port)))
|
|
130
|
+
if not self.auth_port:
|
|
131
|
+
raise ValueError("GOOGLE_AUTH_PORT is not set")
|
|
132
|
+
|
|
133
|
+
tools: List[Any] = [
|
|
134
|
+
self.list_files,
|
|
135
|
+
]
|
|
136
|
+
super().__init__(name="google_drive_tools", tools=tools, **kwargs)
|
|
137
|
+
if not self.scopes:
|
|
138
|
+
# Add read permission by default
|
|
139
|
+
self.scopes.append(self.DEFAULT_SCOPES[1]) # 'drive.readonly'
|
|
140
|
+
# Add write permission if allow_update is True
|
|
141
|
+
if getattr(self, "allow_update", False):
|
|
142
|
+
self.scopes.append(self.DEFAULT_SCOPES[0]) # 'drive.file'
|
|
143
|
+
|
|
144
|
+
def _auth(self):
|
|
145
|
+
"""
|
|
146
|
+
Authenticate and set up the Google Drive API client.
|
|
147
|
+
This method checks if credentials are valid and refreshes or requests them if needed.
|
|
148
|
+
"""
|
|
149
|
+
if self.creds and self.creds.valid:
|
|
150
|
+
# Already authenticated
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
token_file = Path(self.token_path or "token.json")
|
|
154
|
+
creds_file = Path(self.credentials_path or "credentials.json")
|
|
155
|
+
|
|
156
|
+
if token_file.exists():
|
|
157
|
+
self.creds = Credentials.from_authorized_user_file(str(token_file), self.scopes)
|
|
158
|
+
if not self.creds or not self.creds.valid:
|
|
159
|
+
if self.creds and self.creds.expired and self.creds.refresh_token:
|
|
160
|
+
self.creds.refresh(Request())
|
|
161
|
+
else:
|
|
162
|
+
client_config = {
|
|
163
|
+
"installed": {
|
|
164
|
+
"client_id": getenv("GOOGLE_CLIENT_ID"),
|
|
165
|
+
"client_secret": getenv("GOOGLE_CLIENT_SECRET"),
|
|
166
|
+
"project_id": getenv("GOOGLE_PROJECT_ID"),
|
|
167
|
+
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
|
168
|
+
"token_uri": "https://oauth2.googleapis.com/token",
|
|
169
|
+
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
|
170
|
+
"redirect_uris": [getenv("GOOGLE_REDIRECT_URI", "http://localhost")],
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
# File based authentication
|
|
174
|
+
if creds_file.exists():
|
|
175
|
+
flow = InstalledAppFlow.from_client_secrets_file(str(creds_file), self.scopes)
|
|
176
|
+
else:
|
|
177
|
+
flow = InstalledAppFlow.from_client_config(client_config, self.scopes)
|
|
178
|
+
# Opens up a browser window for OAuth authentication
|
|
179
|
+
self.creds = flow.run_local_server(port=self.auth_port) # type: ignore
|
|
180
|
+
|
|
181
|
+
token_file.write_text(self.creds.to_json()) if self.creds else None
|
|
182
|
+
|
|
183
|
+
@authenticate
|
|
184
|
+
def list_files(self, query: Optional[str] = None, page_size: int = 10) -> List[dict]:
|
|
185
|
+
"""
|
|
186
|
+
List files in your Google Drive.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
query (Optional[str]): Optional search query to filter files (see Google Drive API docs).
|
|
190
|
+
page_size (int): Maximum number of files to return.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
List[dict]: List of file metadata dictionaries.
|
|
194
|
+
"""
|
|
195
|
+
if not self.service:
|
|
196
|
+
raise ValueError("Google Drive service is not initialized. Please authenticate first.")
|
|
197
|
+
try:
|
|
198
|
+
results = (
|
|
199
|
+
self.service.files() # type: ignore
|
|
200
|
+
.list(q=query, pageSize=page_size, fields="nextPageToken, files(id, name, mimeType, modifiedTime)")
|
|
201
|
+
.execute()
|
|
202
|
+
)
|
|
203
|
+
items = results.get("files", [])
|
|
204
|
+
return items
|
|
205
|
+
except Exception as error:
|
|
206
|
+
log_error(f"Could not list files: {error}")
|
|
207
|
+
return []
|
|
208
|
+
|
|
209
|
+
@authenticate
|
|
210
|
+
def upload_file(self, file_path: Union[str, Path], mime_type: Optional[str] = None) -> Optional[dict]:
|
|
211
|
+
"""
|
|
212
|
+
Upload a file to your Google Drive.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
file_path (Union[str, Path]): Path to the file you want to upload.
|
|
216
|
+
mime_type (Optional[str]): MIME type of the file. If not provided, it will be guessed.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
Optional[dict]: Metadata of the uploaded file, or None if upload failed.
|
|
220
|
+
"""
|
|
221
|
+
if not self.service:
|
|
222
|
+
raise ValueError("Google Drive service is not initialized. Please authenticate first.")
|
|
223
|
+
file_path = Path(file_path)
|
|
224
|
+
if not file_path.exists() or not file_path.is_file():
|
|
225
|
+
raise ValueError(f"The file '{file_path}' does not exist or is not a file.")
|
|
226
|
+
if mime_type is None:
|
|
227
|
+
mime_type, _ = mimetypes.guess_type(file_path.as_posix())
|
|
228
|
+
if mime_type is None:
|
|
229
|
+
mime_type = "application/octet-stream" # Default MIME type
|
|
230
|
+
|
|
231
|
+
file_metadata = {"name": file_path.name}
|
|
232
|
+
media = MediaFileUpload(file_path.as_posix(), mimetype=mime_type)
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
uploaded_file = (
|
|
236
|
+
self.service.files() # type: ignore
|
|
237
|
+
.create(body=file_metadata, media_body=media, fields="id, name, mimeType, modifiedTime")
|
|
238
|
+
.execute()
|
|
239
|
+
)
|
|
240
|
+
return uploaded_file
|
|
241
|
+
except Exception as error:
|
|
242
|
+
log_error(f"Could not upload file '{file_path}': {error}")
|
|
243
|
+
return None
|
|
244
|
+
|
|
245
|
+
@authenticate
|
|
246
|
+
def download_file(self, file_id: str, dest_path: Union[str, Path]) -> Optional[Path]:
|
|
247
|
+
"""
|
|
248
|
+
Download a file from your Google Drive.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
file_id (str): The ID of the file you want to download.
|
|
252
|
+
dest_path (Union[str, Path]): Where to save the downloaded file.
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
Optional[Path]: The path to the downloaded file, or None if download failed.
|
|
256
|
+
"""
|
|
257
|
+
if not self.service:
|
|
258
|
+
raise ValueError("Google Drive service is not initialized. Please authenticate first.")
|
|
259
|
+
dest_path = Path(dest_path)
|
|
260
|
+
try:
|
|
261
|
+
request = self.service.files().get_media(fileId=file_id) # type: ignore
|
|
262
|
+
with open(dest_path, "wb") as fh:
|
|
263
|
+
downloader = MediaIoBaseDownload(fh, request)
|
|
264
|
+
done = False
|
|
265
|
+
while not done:
|
|
266
|
+
status, done = downloader.next_chunk()
|
|
267
|
+
print(f"Download progress: {int(status.progress() * 100)}%.")
|
|
268
|
+
return dest_path
|
|
269
|
+
except Exception as error:
|
|
270
|
+
log_error(f"Could not download file '{file_id}': {error}")
|
|
271
|
+
return None
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides tools for searching business information using the Google Maps API.
|
|
3
|
+
|
|
4
|
+
Prerequisites:
|
|
5
|
+
- Set the environment variable `GOOGLE_MAPS_API_KEY` with your Google Maps API key.
|
|
6
|
+
You can obtain the API key from the Google Cloud Console:
|
|
7
|
+
https://console.cloud.google.com/projectselector2/google/maps-apis/credentials
|
|
8
|
+
|
|
9
|
+
- You also need to activate the Address Validation API for your project.
|
|
10
|
+
https://console.developers.google.com/apis/api/addressvalidation.googleapis.com
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from os import getenv
|
|
17
|
+
from typing import Any, List, Optional
|
|
18
|
+
|
|
19
|
+
from agno.tools import Toolkit
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
import googlemaps
|
|
23
|
+
from google.maps import places_v1
|
|
24
|
+
except ImportError:
|
|
25
|
+
print("Error importing googlemaps. Please install the package using `pip install googlemaps google-maps-places`.")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class GoogleMapTools(Toolkit):
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
key: Optional[str] = None,
|
|
32
|
+
**kwargs,
|
|
33
|
+
):
|
|
34
|
+
self.api_key = key or getenv("GOOGLE_MAPS_API_KEY")
|
|
35
|
+
if not self.api_key:
|
|
36
|
+
raise ValueError("GOOGLE_MAPS_API_KEY is not set in the environment variables.")
|
|
37
|
+
self.client = googlemaps.Client(key=self.api_key)
|
|
38
|
+
|
|
39
|
+
self.places_client = places_v1.PlacesClient()
|
|
40
|
+
|
|
41
|
+
tools: List[Any] = [
|
|
42
|
+
self.search_places,
|
|
43
|
+
self.get_directions,
|
|
44
|
+
self.validate_address,
|
|
45
|
+
self.geocode_address,
|
|
46
|
+
self.reverse_geocode,
|
|
47
|
+
self.get_distance_matrix,
|
|
48
|
+
self.get_elevation,
|
|
49
|
+
self.get_timezone,
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
super().__init__(name="google_maps", tools=tools, **kwargs)
|
|
53
|
+
|
|
54
|
+
def search_places(self, query: str) -> str:
|
|
55
|
+
"""
|
|
56
|
+
Search for places using Google Maps Places API.
|
|
57
|
+
This tool takes a search query and returns detailed place information.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
query (str): The query string to search for using Google Maps Search API. (e.g., "dental clinics in Noida")
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Stringified list of dictionaries containing business information like name, address, phone, website, rating, and reviews etc.
|
|
64
|
+
"""
|
|
65
|
+
try:
|
|
66
|
+
# Perform places search
|
|
67
|
+
request = places_v1.SearchTextRequest(
|
|
68
|
+
text_query=query,
|
|
69
|
+
)
|
|
70
|
+
response = self.places_client.search_text(request=request, metadata=[("x-goog-fieldmask", "*")])
|
|
71
|
+
|
|
72
|
+
places = []
|
|
73
|
+
for place in response.places:
|
|
74
|
+
place_info = {
|
|
75
|
+
"name": place.display_name.text,
|
|
76
|
+
"address": place.formatted_address,
|
|
77
|
+
"rating": place.rating,
|
|
78
|
+
"reviews": [{"text": review.text.text, "rating": review.rating} for review in place.reviews],
|
|
79
|
+
"place_id": place.id,
|
|
80
|
+
"phone": place.international_phone_number,
|
|
81
|
+
"website": place.website_uri,
|
|
82
|
+
"hours": [description for description in place.regular_opening_hours.weekday_descriptions],
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
places.append(place_info)
|
|
86
|
+
|
|
87
|
+
return json.dumps(places)
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
print(f"Error searching Google Maps: {str(e)}")
|
|
91
|
+
return str([])
|
|
92
|
+
|
|
93
|
+
def get_directions(
|
|
94
|
+
self,
|
|
95
|
+
origin: str,
|
|
96
|
+
destination: str,
|
|
97
|
+
mode: str = "driving",
|
|
98
|
+
departure_time: Optional[datetime] = None,
|
|
99
|
+
avoid: Optional[List[str]] = None,
|
|
100
|
+
) -> str:
|
|
101
|
+
"""
|
|
102
|
+
Get directions between two locations using Google Maps Directions API.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
origin (str): Starting point address or coordinates
|
|
106
|
+
destination (str): Destination address or coordinates
|
|
107
|
+
mode (str, optional): Travel mode. Options: "driving", "walking", "bicycling", "transit". Defaults to "driving"
|
|
108
|
+
departure_time (datetime, optional): Desired departure time for transit directions
|
|
109
|
+
avoid (List[str], optional): Features to avoid: "tolls", "highways", "ferries"
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
str: Stringified dictionary containing route information including steps, distance, duration, etc.
|
|
113
|
+
"""
|
|
114
|
+
try:
|
|
115
|
+
result = self.client.directions(origin, destination, mode=mode, departure_time=departure_time, avoid=avoid)
|
|
116
|
+
return str(result)
|
|
117
|
+
except Exception as e:
|
|
118
|
+
print(f"Error getting directions: {str(e)}")
|
|
119
|
+
return str([])
|
|
120
|
+
|
|
121
|
+
def validate_address(
|
|
122
|
+
self, address: str, region_code: str = "US", locality: Optional[str] = None, enable_usps_cass: bool = False
|
|
123
|
+
) -> str:
|
|
124
|
+
"""
|
|
125
|
+
Validate an address using Google Maps Address Validation API.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
address (str): The address to validate
|
|
129
|
+
region_code (str): The region code (e.g., "US" for United States)
|
|
130
|
+
locality (str, optional): The locality (city) to help with validation
|
|
131
|
+
enable_usps_cass (bool): Whether to enable USPS CASS validation for US addresses
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
str: Stringified dictionary containing address validation results
|
|
135
|
+
"""
|
|
136
|
+
try:
|
|
137
|
+
result = self.client.addressvalidation(
|
|
138
|
+
[address], regionCode=region_code, locality=locality, enableUspsCass=enable_usps_cass
|
|
139
|
+
)
|
|
140
|
+
return str(result)
|
|
141
|
+
except Exception as e:
|
|
142
|
+
print(f"Error validating address: {str(e)}")
|
|
143
|
+
return str({})
|
|
144
|
+
|
|
145
|
+
def geocode_address(self, address: str, region: Optional[str] = None) -> str:
|
|
146
|
+
"""
|
|
147
|
+
Convert an address into geographic coordinates using Google Maps Geocoding API.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
address (str): The address to geocode
|
|
151
|
+
region (str, optional): The region code to bias results
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
str: Stringified list of dictionaries containing location information
|
|
155
|
+
"""
|
|
156
|
+
try:
|
|
157
|
+
result = self.client.geocode(address, region=region)
|
|
158
|
+
return str(result)
|
|
159
|
+
except Exception as e:
|
|
160
|
+
print(f"Error geocoding address: {str(e)}")
|
|
161
|
+
return str([])
|
|
162
|
+
|
|
163
|
+
def reverse_geocode(
|
|
164
|
+
self, lat: float, lng: float, result_type: Optional[List[str]] = None, location_type: Optional[List[str]] = None
|
|
165
|
+
) -> str:
|
|
166
|
+
"""
|
|
167
|
+
Convert geographic coordinates into an address using Google Maps Reverse Geocoding API.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
lat (float): Latitude
|
|
171
|
+
lng (float): Longitude
|
|
172
|
+
result_type (List[str], optional): Array of address types to filter results
|
|
173
|
+
location_type (List[str], optional): Array of location types to filter results
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
str: Stringified list of dictionaries containing address information
|
|
177
|
+
"""
|
|
178
|
+
try:
|
|
179
|
+
result = self.client.reverse_geocode((lat, lng), result_type=result_type, location_type=location_type)
|
|
180
|
+
return str(result)
|
|
181
|
+
except Exception as e:
|
|
182
|
+
print(f"Error reverse geocoding: {str(e)}")
|
|
183
|
+
return str([])
|
|
184
|
+
|
|
185
|
+
def get_distance_matrix(
|
|
186
|
+
self,
|
|
187
|
+
origins: List[str],
|
|
188
|
+
destinations: List[str],
|
|
189
|
+
mode: str = "driving",
|
|
190
|
+
departure_time: Optional[datetime] = None,
|
|
191
|
+
avoid: Optional[List[str]] = None,
|
|
192
|
+
) -> str:
|
|
193
|
+
"""
|
|
194
|
+
Calculate distance and time for a matrix of origins and destinations.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
origins (List[str]): List of addresses or coordinates
|
|
198
|
+
destinations (List[str]): List of addresses or coordinates
|
|
199
|
+
mode (str, optional): Travel mode. Options: "driving", "walking", "bicycling", "transit"
|
|
200
|
+
departure_time (datetime, optional): Desired departure time
|
|
201
|
+
avoid (List[str], optional): Features to avoid: "tolls", "highways", "ferries"
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
str: Stringified dictionary containing distance and duration information
|
|
205
|
+
"""
|
|
206
|
+
try:
|
|
207
|
+
result = self.client.distance_matrix(
|
|
208
|
+
origins, destinations, mode=mode, departure_time=departure_time, avoid=avoid
|
|
209
|
+
)
|
|
210
|
+
return str(result)
|
|
211
|
+
except Exception as e:
|
|
212
|
+
print(f"Error getting distance matrix: {str(e)}")
|
|
213
|
+
return str({})
|
|
214
|
+
|
|
215
|
+
def get_elevation(self, lat: float, lng: float) -> str:
|
|
216
|
+
"""
|
|
217
|
+
Get the elevation for a specific location using Google Maps Elevation API.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
lat (float): Latitude
|
|
221
|
+
lng (float): Longitude
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
str: Stringified dictionary containing elevation data
|
|
225
|
+
"""
|
|
226
|
+
try:
|
|
227
|
+
result = self.client.elevation((lat, lng))
|
|
228
|
+
return str(result)
|
|
229
|
+
except Exception as e:
|
|
230
|
+
print(f"Error getting elevation: {str(e)}")
|
|
231
|
+
return str([])
|
|
232
|
+
|
|
233
|
+
def get_timezone(self, lat: float, lng: float, timestamp: Optional[datetime] = None) -> str:
|
|
234
|
+
"""
|
|
235
|
+
Get timezone information for a location using Google Maps Time Zone API.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
lat (float): Latitude
|
|
239
|
+
lng (float): Longitude
|
|
240
|
+
timestamp (datetime, optional): The timestamp to use for timezone calculation
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
str: Stringified dictionary containing timezone information
|
|
244
|
+
"""
|
|
245
|
+
try:
|
|
246
|
+
if timestamp is None:
|
|
247
|
+
timestamp = datetime.now()
|
|
248
|
+
|
|
249
|
+
result = self.client.timezone(location=(lat, lng), timestamp=timestamp)
|
|
250
|
+
return str(result)
|
|
251
|
+
except Exception as e:
|
|
252
|
+
print(f"Error getting timezone: {str(e)}")
|
|
253
|
+
return str({})
|