agno 2.2.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 +51 -0
- agno/agent/agent.py +10405 -0
- agno/api/__init__.py +0 -0
- agno/api/agent.py +28 -0
- agno/api/api.py +40 -0
- agno/api/evals.py +22 -0
- agno/api/os.py +17 -0
- agno/api/routes.py +13 -0
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +16 -0
- agno/api/schemas/evals.py +16 -0
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/response.py +6 -0
- agno/api/schemas/team.py +16 -0
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +16 -0
- agno/api/settings.py +53 -0
- agno/api/team.py +30 -0
- 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/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 +598 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +2042 -0
- agno/db/dynamo/schemas.py +314 -0
- agno/db/dynamo/utils.py +743 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +1795 -0
- agno/db/firestore/schemas.py +140 -0
- agno/db/firestore/utils.py +376 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1335 -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 +1160 -0
- agno/db/in_memory/utils.py +230 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1328 -0
- agno/db/json/utils.py +230 -0
- agno/db/migrations/__init__.py +0 -0
- agno/db/migrations/v1_to_v2.py +635 -0
- agno/db/mongo/__init__.py +17 -0
- agno/db/mongo/async_mongo.py +2026 -0
- agno/db/mongo/mongo.py +1982 -0
- agno/db/mongo/schemas.py +87 -0
- agno/db/mongo/utils.py +259 -0
- agno/db/mysql/__init__.py +3 -0
- agno/db/mysql/mysql.py +2308 -0
- agno/db/mysql/schemas.py +138 -0
- agno/db/mysql/utils.py +355 -0
- agno/db/postgres/__init__.py +4 -0
- agno/db/postgres/async_postgres.py +1927 -0
- agno/db/postgres/postgres.py +2260 -0
- agno/db/postgres/schemas.py +139 -0
- agno/db/postgres/utils.py +442 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +1660 -0
- agno/db/redis/schemas.py +123 -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 +33 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +46 -0
- agno/db/schemas/metrics.py +0 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +130 -0
- agno/db/singlestore/singlestore.py +2272 -0
- agno/db/singlestore/utils.py +384 -0
- agno/db/sqlite/__init__.py +4 -0
- agno/db/sqlite/async_sqlite.py +2293 -0
- agno/db/sqlite/schemas.py +133 -0
- agno/db/sqlite/sqlite.py +2288 -0
- agno/db/sqlite/utils.py +431 -0
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +309 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1353 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +116 -0
- agno/debug.py +18 -0
- agno/eval/__init__.py +14 -0
- agno/eval/accuracy.py +834 -0
- agno/eval/performance.py +773 -0
- agno/eval/reliability.py +306 -0
- agno/eval/utils.py +119 -0
- agno/exceptions.py +161 -0
- 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/integrations/__init__.py +0 -0
- agno/integrations/discord/__init__.py +3 -0
- agno/integrations/discord/client.py +203 -0
- agno/knowledge/__init__.py +5 -0
- agno/knowledge/chunking/__init__.py +0 -0
- agno/knowledge/chunking/agentic.py +79 -0
- agno/knowledge/chunking/document.py +91 -0
- agno/knowledge/chunking/fixed.py +57 -0
- agno/knowledge/chunking/markdown.py +151 -0
- agno/knowledge/chunking/recursive.py +63 -0
- agno/knowledge/chunking/row.py +39 -0
- agno/knowledge/chunking/semantic.py +86 -0
- agno/knowledge/chunking/strategy.py +165 -0
- agno/knowledge/content.py +74 -0
- agno/knowledge/document/__init__.py +5 -0
- agno/knowledge/document/base.py +58 -0
- agno/knowledge/embedder/__init__.py +5 -0
- agno/knowledge/embedder/aws_bedrock.py +343 -0
- agno/knowledge/embedder/azure_openai.py +210 -0
- agno/knowledge/embedder/base.py +23 -0
- agno/knowledge/embedder/cohere.py +323 -0
- agno/knowledge/embedder/fastembed.py +62 -0
- agno/knowledge/embedder/fireworks.py +13 -0
- 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/knowledge/embedder/together.py +13 -0
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +165 -0
- agno/knowledge/knowledge.py +1988 -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 +166 -0
- agno/knowledge/reader/docx_reader.py +82 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +292 -0
- agno/knowledge/reader/firecrawl_reader.py +201 -0
- agno/knowledge/reader/json_reader.py +87 -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 +194 -0
- agno/knowledge/reader/text_reader.py +115 -0
- agno/knowledge/reader/web_search_reader.py +372 -0
- agno/knowledge/reader/website_reader.py +455 -0
- agno/knowledge/reader/wikipedia_reader.py +59 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/__init__.py +0 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/knowledge/reranker/__init__.py +3 -0
- agno/knowledge/reranker/base.py +14 -0
- agno/knowledge/reranker/cohere.py +64 -0
- 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 +189 -0
- agno/media.py +462 -0
- agno/memory/__init__.py +3 -0
- agno/memory/manager.py +1327 -0
- agno/models/__init__.py +0 -0
- agno/models/aimlapi/__init__.py +5 -0
- agno/models/aimlapi/aimlapi.py +45 -0
- agno/models/anthropic/__init__.py +5 -0
- agno/models/anthropic/claude.py +757 -0
- agno/models/aws/__init__.py +15 -0
- agno/models/aws/bedrock.py +701 -0
- agno/models/aws/claude.py +378 -0
- agno/models/azure/__init__.py +18 -0
- agno/models/azure/ai_foundry.py +485 -0
- agno/models/azure/openai_chat.py +131 -0
- agno/models/base.py +2175 -0
- agno/models/cerebras/__init__.py +12 -0
- agno/models/cerebras/cerebras.py +501 -0
- agno/models/cerebras/cerebras_openai.py +112 -0
- agno/models/cohere/__init__.py +5 -0
- agno/models/cohere/chat.py +389 -0
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +57 -0
- agno/models/dashscope/__init__.py +5 -0
- agno/models/dashscope/dashscope.py +91 -0
- agno/models/deepinfra/__init__.py +5 -0
- agno/models/deepinfra/deepinfra.py +28 -0
- agno/models/deepseek/__init__.py +5 -0
- agno/models/deepseek/deepseek.py +61 -0
- agno/models/defaults.py +1 -0
- agno/models/fireworks/__init__.py +5 -0
- agno/models/fireworks/fireworks.py +26 -0
- agno/models/google/__init__.py +5 -0
- agno/models/google/gemini.py +1085 -0
- agno/models/groq/__init__.py +5 -0
- agno/models/groq/groq.py +556 -0
- agno/models/huggingface/__init__.py +5 -0
- agno/models/huggingface/huggingface.py +491 -0
- agno/models/ibm/__init__.py +5 -0
- agno/models/ibm/watsonx.py +422 -0
- agno/models/internlm/__init__.py +3 -0
- agno/models/internlm/internlm.py +26 -0
- agno/models/langdb/__init__.py +1 -0
- agno/models/langdb/langdb.py +48 -0
- agno/models/litellm/__init__.py +14 -0
- agno/models/litellm/chat.py +468 -0
- agno/models/litellm/litellm_openai.py +25 -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 +434 -0
- agno/models/meta/__init__.py +12 -0
- agno/models/meta/llama.py +475 -0
- agno/models/meta/llama_openai.py +78 -0
- agno/models/metrics.py +120 -0
- agno/models/mistral/__init__.py +5 -0
- agno/models/mistral/mistral.py +432 -0
- agno/models/nebius/__init__.py +3 -0
- agno/models/nebius/nebius.py +54 -0
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/__init__.py +5 -0
- agno/models/nvidia/nvidia.py +28 -0
- agno/models/ollama/__init__.py +5 -0
- agno/models/ollama/chat.py +441 -0
- agno/models/openai/__init__.py +9 -0
- agno/models/openai/chat.py +883 -0
- agno/models/openai/like.py +27 -0
- agno/models/openai/responses.py +1050 -0
- agno/models/openrouter/__init__.py +5 -0
- agno/models/openrouter/openrouter.py +66 -0
- agno/models/perplexity/__init__.py +5 -0
- agno/models/perplexity/perplexity.py +187 -0
- agno/models/portkey/__init__.py +3 -0
- agno/models/portkey/portkey.py +81 -0
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +52 -0
- agno/models/response.py +199 -0
- agno/models/sambanova/__init__.py +5 -0
- agno/models/sambanova/sambanova.py +28 -0
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +25 -0
- agno/models/together/__init__.py +5 -0
- agno/models/together/together.py +25 -0
- agno/models/utils.py +266 -0
- agno/models/vercel/__init__.py +3 -0
- agno/models/vercel/v0.py +26 -0
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +70 -0
- agno/models/vllm/__init__.py +3 -0
- agno/models/vllm/vllm.py +78 -0
- agno/models/xai/__init__.py +3 -0
- agno/models/xai/xai.py +113 -0
- agno/os/__init__.py +3 -0
- agno/os/app.py +876 -0
- agno/os/auth.py +57 -0
- agno/os/config.py +104 -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 +250 -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 +144 -0
- agno/os/interfaces/agui/utils.py +534 -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 +211 -0
- agno/os/interfaces/whatsapp/security.py +53 -0
- agno/os/interfaces/whatsapp/whatsapp.py +36 -0
- agno/os/mcp.py +292 -0
- agno/os/middleware/__init__.py +7 -0
- agno/os/middleware/jwt.py +233 -0
- agno/os/router.py +1763 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +430 -0
- agno/os/routers/evals/schemas.py +142 -0
- agno/os/routers/evals/utils.py +162 -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 +997 -0
- agno/os/routers/knowledge/schemas.py +178 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +515 -0
- agno/os/routers/memory/schemas.py +62 -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/schema.py +1055 -0
- agno/os/settings.py +43 -0
- agno/os/utils.py +630 -0
- agno/py.typed +0 -0
- agno/reasoning/__init__.py +0 -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 +63 -0
- agno/reasoning/ollama.py +67 -0
- agno/reasoning/openai.py +86 -0
- agno/reasoning/step.py +31 -0
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +787 -0
- agno/run/base.py +229 -0
- agno/run/cancel.py +81 -0
- agno/run/messages.py +32 -0
- agno/run/team.py +753 -0
- agno/run/workflow.py +708 -0
- agno/session/__init__.py +10 -0
- agno/session/agent.py +295 -0
- agno/session/summary.py +265 -0
- agno/session/team.py +392 -0
- agno/session/workflow.py +205 -0
- agno/team/__init__.py +37 -0
- agno/team/team.py +8793 -0
- agno/tools/__init__.py +10 -0
- agno/tools/agentql.py +120 -0
- agno/tools/airflow.py +69 -0
- agno/tools/api.py +122 -0
- agno/tools/apify.py +314 -0
- agno/tools/arxiv.py +127 -0
- agno/tools/aws_lambda.py +53 -0
- agno/tools/aws_ses.py +66 -0
- agno/tools/baidusearch.py +89 -0
- 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 +255 -0
- agno/tools/calculator.py +151 -0
- agno/tools/cartesia.py +187 -0
- agno/tools/clickup.py +244 -0
- agno/tools/confluence.py +240 -0
- agno/tools/crawl4ai.py +158 -0
- agno/tools/csv_toolkit.py +185 -0
- agno/tools/dalle.py +110 -0
- agno/tools/daytona.py +475 -0
- agno/tools/decorator.py +262 -0
- agno/tools/desi_vocal.py +108 -0
- agno/tools/discord.py +161 -0
- agno/tools/docker.py +716 -0
- agno/tools/duckdb.py +379 -0
- agno/tools/duckduckgo.py +91 -0
- agno/tools/e2b.py +703 -0
- agno/tools/eleven_labs.py +196 -0
- agno/tools/email.py +67 -0
- agno/tools/evm.py +129 -0
- agno/tools/exa.py +396 -0
- agno/tools/fal.py +127 -0
- agno/tools/file.py +240 -0
- agno/tools/file_generation.py +350 -0
- agno/tools/financial_datasets.py +288 -0
- agno/tools/firecrawl.py +143 -0
- agno/tools/function.py +1187 -0
- agno/tools/giphy.py +93 -0
- agno/tools/github.py +1760 -0
- agno/tools/gmail.py +922 -0
- agno/tools/google_bigquery.py +117 -0
- agno/tools/google_drive.py +270 -0
- agno/tools/google_maps.py +253 -0
- agno/tools/googlecalendar.py +674 -0
- agno/tools/googlesearch.py +98 -0
- agno/tools/googlesheets.py +377 -0
- agno/tools/hackernews.py +77 -0
- agno/tools/jina.py +101 -0
- agno/tools/jira.py +170 -0
- agno/tools/knowledge.py +218 -0
- agno/tools/linear.py +426 -0
- agno/tools/linkup.py +58 -0
- agno/tools/local_file_system.py +90 -0
- agno/tools/lumalab.py +183 -0
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +284 -0
- agno/tools/mem0.py +193 -0
- agno/tools/memori.py +339 -0
- agno/tools/memory.py +419 -0
- agno/tools/mlx_transcribe.py +139 -0
- agno/tools/models/__init__.py +0 -0
- 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 +195 -0
- agno/tools/moviepy_video.py +349 -0
- agno/tools/neo4j.py +134 -0
- agno/tools/newspaper.py +46 -0
- agno/tools/newspaper4k.py +93 -0
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +202 -0
- agno/tools/openbb.py +160 -0
- agno/tools/opencv.py +321 -0
- agno/tools/openweather.py +233 -0
- agno/tools/oxylabs.py +385 -0
- agno/tools/pandas.py +102 -0
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +257 -0
- agno/tools/pubmed.py +188 -0
- agno/tools/python.py +205 -0
- agno/tools/reasoning.py +283 -0
- agno/tools/reddit.py +467 -0
- agno/tools/replicate.py +117 -0
- agno/tools/resend.py +62 -0
- agno/tools/scrapegraph.py +222 -0
- agno/tools/searxng.py +152 -0
- agno/tools/serpapi.py +116 -0
- agno/tools/serper.py +255 -0
- agno/tools/shell.py +53 -0
- agno/tools/slack.py +136 -0
- agno/tools/sleep.py +20 -0
- agno/tools/spider.py +116 -0
- agno/tools/sql.py +154 -0
- agno/tools/streamlit/__init__.py +0 -0
- agno/tools/streamlit/components.py +113 -0
- agno/tools/tavily.py +254 -0
- agno/tools/telegram.py +48 -0
- agno/tools/todoist.py +218 -0
- agno/tools/tool_registry.py +1 -0
- agno/tools/toolkit.py +146 -0
- agno/tools/trafilatura.py +388 -0
- agno/tools/trello.py +274 -0
- agno/tools/twilio.py +186 -0
- 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 +54 -0
- agno/tools/webtools.py +45 -0
- agno/tools/whatsapp.py +286 -0
- agno/tools/wikipedia.py +63 -0
- agno/tools/workflow.py +278 -0
- agno/tools/x.py +335 -0
- agno/tools/yfinance.py +257 -0
- agno/tools/youtube.py +184 -0
- agno/tools/zendesk.py +82 -0
- agno/tools/zep.py +454 -0
- agno/tools/zoom.py +382 -0
- agno/utils/__init__.py +0 -0
- agno/utils/agent.py +820 -0
- agno/utils/audio.py +49 -0
- agno/utils/certs.py +27 -0
- agno/utils/code_execution.py +11 -0
- agno/utils/common.py +132 -0
- agno/utils/dttm.py +13 -0
- agno/utils/enum.py +22 -0
- agno/utils/env.py +11 -0
- agno/utils/events.py +696 -0
- agno/utils/format_str.py +16 -0
- agno/utils/functions.py +166 -0
- agno/utils/gemini.py +426 -0
- agno/utils/hooks.py +57 -0
- agno/utils/http.py +74 -0
- agno/utils/json_schema.py +234 -0
- agno/utils/knowledge.py +36 -0
- agno/utils/location.py +19 -0
- agno/utils/log.py +255 -0
- agno/utils/mcp.py +214 -0
- agno/utils/media.py +352 -0
- agno/utils/merge_dict.py +41 -0
- agno/utils/message.py +118 -0
- agno/utils/models/__init__.py +0 -0
- agno/utils/models/ai_foundry.py +43 -0
- agno/utils/models/claude.py +358 -0
- agno/utils/models/cohere.py +87 -0
- agno/utils/models/llama.py +78 -0
- agno/utils/models/mistral.py +98 -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 +32 -0
- agno/utils/pprint.py +178 -0
- agno/utils/print_response/__init__.py +0 -0
- agno/utils/print_response/agent.py +842 -0
- agno/utils/print_response/team.py +1724 -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/response_iterator.py +17 -0
- agno/utils/safe_formatter.py +24 -0
- agno/utils/serialize.py +32 -0
- agno/utils/shell.py +22 -0
- agno/utils/streamlit.py +487 -0
- agno/utils/string.py +231 -0
- agno/utils/team.py +139 -0
- agno/utils/timer.py +41 -0
- agno/utils/tools.py +102 -0
- agno/utils/web.py +23 -0
- agno/utils/whatsapp.py +305 -0
- agno/utils/yaml_io.py +25 -0
- agno/vectordb/__init__.py +3 -0
- agno/vectordb/base.py +127 -0
- agno/vectordb/cassandra/__init__.py +5 -0
- agno/vectordb/cassandra/cassandra.py +501 -0
- agno/vectordb/cassandra/extra_param_mixin.py +11 -0
- agno/vectordb/cassandra/index.py +13 -0
- agno/vectordb/chroma/__init__.py +5 -0
- agno/vectordb/chroma/chromadb.py +929 -0
- agno/vectordb/clickhouse/__init__.py +9 -0
- agno/vectordb/clickhouse/clickhousedb.py +835 -0
- agno/vectordb/clickhouse/index.py +9 -0
- agno/vectordb/couchbase/__init__.py +3 -0
- agno/vectordb/couchbase/couchbase.py +1442 -0
- agno/vectordb/distance.py +7 -0
- agno/vectordb/lancedb/__init__.py +6 -0
- agno/vectordb/lancedb/lance_db.py +995 -0
- 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 +4 -0
- agno/vectordb/milvus/milvus.py +1182 -0
- agno/vectordb/mongodb/__init__.py +9 -0
- agno/vectordb/mongodb/mongodb.py +1417 -0
- agno/vectordb/pgvector/__init__.py +12 -0
- agno/vectordb/pgvector/index.py +23 -0
- agno/vectordb/pgvector/pgvector.py +1462 -0
- agno/vectordb/pineconedb/__init__.py +5 -0
- agno/vectordb/pineconedb/pineconedb.py +747 -0
- agno/vectordb/qdrant/__init__.py +5 -0
- agno/vectordb/qdrant/qdrant.py +1134 -0
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +694 -0
- agno/vectordb/search.py +7 -0
- agno/vectordb/singlestore/__init__.py +10 -0
- agno/vectordb/singlestore/index.py +41 -0
- agno/vectordb/singlestore/singlestore.py +763 -0
- agno/vectordb/surrealdb/__init__.py +3 -0
- agno/vectordb/surrealdb/surrealdb.py +699 -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 +1005 -0
- agno/workflow/__init__.py +23 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +738 -0
- agno/workflow/loop.py +735 -0
- agno/workflow/parallel.py +824 -0
- agno/workflow/router.py +702 -0
- agno/workflow/step.py +1432 -0
- agno/workflow/steps.py +592 -0
- agno/workflow/types.py +520 -0
- agno/workflow/workflow.py +4321 -0
- agno-2.2.13.dist-info/METADATA +614 -0
- agno-2.2.13.dist-info/RECORD +575 -0
- agno-2.2.13.dist-info/WHEEL +5 -0
- agno-2.2.13.dist-info/licenses/LICENSE +201 -0
- agno-2.2.13.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
from agno.tools import Toolkit
|
|
5
|
+
from agno.utils.log import log_debug
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from googlesearch import search
|
|
9
|
+
except ImportError:
|
|
10
|
+
raise ImportError("`googlesearch-python` not installed. Please install using `pip install googlesearch-python`")
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from pycountry import pycountry
|
|
14
|
+
except ImportError:
|
|
15
|
+
raise ImportError("`pycountry` not installed. Please install using `pip install pycountry`")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class GoogleSearchTools(Toolkit):
|
|
19
|
+
"""
|
|
20
|
+
GoogleSearch is a Python library for searching Google easily.
|
|
21
|
+
It uses requests and BeautifulSoup4 to scrape Google.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
fixed_max_results (Optional[int]): A fixed number of maximum results.
|
|
25
|
+
fixed_language (Optional[str]): Language of the search results.
|
|
26
|
+
headers (Optional[Any]): Custom headers for the request.
|
|
27
|
+
proxy (Optional[str]): Proxy settings for the request.
|
|
28
|
+
timeout (Optional[int]): Timeout for the request, default is 10 seconds.
|
|
29
|
+
cache_results (bool): Enable caching of search results.
|
|
30
|
+
cache_ttl (int): Time-to-live for cached results in seconds.
|
|
31
|
+
cache_dir (Optional[str]): Directory to store cache files.
|
|
32
|
+
enable_google_search (bool): Enable Google search.
|
|
33
|
+
all (bool): Enable all tools.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
fixed_max_results: Optional[int] = None,
|
|
39
|
+
fixed_language: Optional[str] = None,
|
|
40
|
+
headers: Optional[Any] = None,
|
|
41
|
+
proxy: Optional[str] = None,
|
|
42
|
+
timeout: Optional[int] = 10,
|
|
43
|
+
enable_google_search: bool = True,
|
|
44
|
+
all: bool = False,
|
|
45
|
+
**kwargs,
|
|
46
|
+
):
|
|
47
|
+
self.fixed_max_results: Optional[int] = fixed_max_results
|
|
48
|
+
self.fixed_language: Optional[str] = fixed_language
|
|
49
|
+
self.headers: Optional[Any] = headers
|
|
50
|
+
self.proxy: Optional[str] = proxy
|
|
51
|
+
self.timeout: Optional[int] = timeout
|
|
52
|
+
|
|
53
|
+
tools = []
|
|
54
|
+
if all or enable_google_search:
|
|
55
|
+
tools.append(self.google_search)
|
|
56
|
+
|
|
57
|
+
super().__init__(name="google_search_tools", tools=tools, **kwargs)
|
|
58
|
+
|
|
59
|
+
def google_search(self, query: str, max_results: int = 5, language: str = "en") -> str:
|
|
60
|
+
"""
|
|
61
|
+
Use this function to search Google for a specified query.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
query (str): The query to search for.
|
|
65
|
+
max_results (int, optional): The maximum number of results to return. Default is 5.
|
|
66
|
+
language (str, optional): The language of the search results. Default is "en".
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
str: A JSON formatted string containing the search results.
|
|
70
|
+
"""
|
|
71
|
+
max_results = self.fixed_max_results or max_results
|
|
72
|
+
language = self.fixed_language or language
|
|
73
|
+
|
|
74
|
+
# Resolve language to ISO 639-1 code if needed
|
|
75
|
+
if len(language) != 2:
|
|
76
|
+
_language = pycountry.languages.lookup(language)
|
|
77
|
+
if _language:
|
|
78
|
+
language = _language.alpha_2
|
|
79
|
+
else:
|
|
80
|
+
language = "en"
|
|
81
|
+
|
|
82
|
+
log_debug(f"Searching Google [{language}] for: {query}")
|
|
83
|
+
|
|
84
|
+
# Perform Google search using the googlesearch-python package
|
|
85
|
+
results = list(search(query, num_results=max_results, lang=language))
|
|
86
|
+
|
|
87
|
+
# Collect the search results
|
|
88
|
+
res: List[Dict[str, str]] = []
|
|
89
|
+
for result in results:
|
|
90
|
+
res.append(
|
|
91
|
+
{
|
|
92
|
+
"title": result.title,
|
|
93
|
+
"url": result.url,
|
|
94
|
+
"description": result.description,
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return json.dumps(res, indent=2)
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Google Sheets Toolset for interacting with Sheets API
|
|
3
|
+
|
|
4
|
+
Required Environment Variables:
|
|
5
|
+
-----------------------------
|
|
6
|
+
- GOOGLE_CLIENT_ID: Google OAuth client ID
|
|
7
|
+
- GOOGLE_CLIENT_SECRET: Google OAuth client secret
|
|
8
|
+
- GOOGLE_PROJECT_ID: Google Cloud project ID
|
|
9
|
+
- GOOGLE_REDIRECT_URI: Google OAuth redirect URI (default: http://localhost)
|
|
10
|
+
|
|
11
|
+
How to Get These Credentials:
|
|
12
|
+
---------------------------
|
|
13
|
+
1. Go to Google Cloud Console (https://console.cloud.google.com)
|
|
14
|
+
2. Create a new project or select an existing one
|
|
15
|
+
3. Enable the Google Sheets API:
|
|
16
|
+
- Go to "APIs & Services" > "Enable APIs and Services"
|
|
17
|
+
- Search for "Google Sheets API"
|
|
18
|
+
- Click "Enable"
|
|
19
|
+
|
|
20
|
+
4. Create OAuth 2.0 credentials:
|
|
21
|
+
- Go to "APIs & Services" > "Credentials"
|
|
22
|
+
- Click "Create Credentials" > "OAuth client ID"
|
|
23
|
+
- Go through the OAuth consent screen setup
|
|
24
|
+
- Give it a name and click "Create"
|
|
25
|
+
- You'll receive:
|
|
26
|
+
* Client ID (GOOGLE_CLIENT_ID)
|
|
27
|
+
* Client Secret (GOOGLE_CLIENT_SECRET)
|
|
28
|
+
- The Project ID (GOOGLE_PROJECT_ID) is visible in the project dropdown at the top of the page
|
|
29
|
+
|
|
30
|
+
5. Set up environment variables:
|
|
31
|
+
Create a .envrc file in your project root with:
|
|
32
|
+
```
|
|
33
|
+
export GOOGLE_CLIENT_ID=your_client_id_here
|
|
34
|
+
export GOOGLE_CLIENT_SECRET=your_client_secret_here
|
|
35
|
+
export GOOGLE_PROJECT_ID=your_project_id_here
|
|
36
|
+
export GOOGLE_REDIRECT_URI=http://localhost # Default value
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Alternatively, follow the instructions in the Google Sheets API Quickstart guide:
|
|
40
|
+
1: Steps: https://developers.google.com/sheets/api/quickstart/python
|
|
41
|
+
2: Save the credentials.json file to the root of the project or update the path in the GoogleSheetsTools class
|
|
42
|
+
|
|
43
|
+
Note: The first time you run the application, it will open a browser window for OAuth authentication.
|
|
44
|
+
A token.json file will be created to store the authentication credentials for future use.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
import json
|
|
48
|
+
from functools import wraps
|
|
49
|
+
from os import getenv
|
|
50
|
+
from pathlib import Path
|
|
51
|
+
from typing import Any, List, Optional, Union
|
|
52
|
+
|
|
53
|
+
from agno.tools import Toolkit
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
from google.auth.transport.requests import Request
|
|
57
|
+
from google.oauth2.credentials import Credentials
|
|
58
|
+
from google.oauth2.service_account import Credentials as ServiceAccountCredentials
|
|
59
|
+
from google_auth_oauthlib.flow import InstalledAppFlow
|
|
60
|
+
from googleapiclient.discovery import Resource, build
|
|
61
|
+
except ImportError:
|
|
62
|
+
raise ImportError(
|
|
63
|
+
"`google-api-python-client` `google-auth-httplib2` `google-auth-oauthlib` not installed. Please install using `pip install google-api-python-client google-auth-httplib2 google-auth-oauthlib`"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def authenticate(func):
|
|
68
|
+
"""Decorator to ensure authentication before executing a function."""
|
|
69
|
+
|
|
70
|
+
@wraps(func)
|
|
71
|
+
def wrapper(self, *args, **kwargs):
|
|
72
|
+
if not self.creds or not self.creds.valid:
|
|
73
|
+
self._auth()
|
|
74
|
+
if not self.service:
|
|
75
|
+
self.service = build("sheets", "v4", credentials=self.creds)
|
|
76
|
+
return func(self, *args, **kwargs)
|
|
77
|
+
|
|
78
|
+
return wrapper
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class GoogleSheetsTools(Toolkit):
|
|
82
|
+
# Default scopes for Google Sheets API access
|
|
83
|
+
DEFAULT_SCOPES = {
|
|
84
|
+
"read": "https://www.googleapis.com/auth/spreadsheets.readonly",
|
|
85
|
+
"write": "https://www.googleapis.com/auth/spreadsheets",
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
service: Optional[Resource]
|
|
89
|
+
|
|
90
|
+
def __init__(
|
|
91
|
+
self,
|
|
92
|
+
scopes: Optional[List[str]] = None,
|
|
93
|
+
spreadsheet_id: Optional[str] = None,
|
|
94
|
+
spreadsheet_range: Optional[str] = None,
|
|
95
|
+
creds: Optional[Union[Credentials, ServiceAccountCredentials]] = None,
|
|
96
|
+
creds_path: Optional[str] = None,
|
|
97
|
+
token_path: Optional[str] = None,
|
|
98
|
+
service_account_path: Optional[str] = None,
|
|
99
|
+
oauth_port: int = 0,
|
|
100
|
+
enable_read_sheet: bool = True,
|
|
101
|
+
enable_create_sheet: bool = False,
|
|
102
|
+
enable_update_sheet: bool = False,
|
|
103
|
+
enable_create_duplicate_sheet: bool = False,
|
|
104
|
+
all: bool = False,
|
|
105
|
+
**kwargs,
|
|
106
|
+
):
|
|
107
|
+
"""Initialize GoogleSheetsTools with the specified configuration.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
scopes (Optional[List[str]]): Custom OAuth scopes. If None, uses write scope by default.
|
|
111
|
+
spreadsheet_id (Optional[str]): ID of the target spreadsheet.
|
|
112
|
+
spreadsheet_range (Optional[str]): Range within the spreadsheet.
|
|
113
|
+
creds (Optional[Credentials | ServiceAccountCredentials]): Pre-existing credentials.
|
|
114
|
+
creds_path (Optional[str]): Path to credentials file.
|
|
115
|
+
token_path (Optional[str]): Path to token file.
|
|
116
|
+
service_account_path (Optional[str]): Path to a service account file.
|
|
117
|
+
oauth_port (int): Port to use for OAuth authentication. Defaults to 0.
|
|
118
|
+
enable_read_sheet (bool): Enable reading from a sheet.
|
|
119
|
+
enable_create_sheet (bool): Enable creating a sheet.
|
|
120
|
+
enable_update_sheet (bool): Enable updating a sheet.
|
|
121
|
+
enable_create_duplicate_sheet (bool): Enable creating a duplicate sheet.
|
|
122
|
+
all (bool): Enable all tools.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
self.spreadsheet_id = spreadsheet_id
|
|
126
|
+
self.spreadsheet_range = spreadsheet_range
|
|
127
|
+
self.creds = creds
|
|
128
|
+
self.credentials_path = creds_path
|
|
129
|
+
self.token_path = token_path
|
|
130
|
+
self.oauth_port = oauth_port
|
|
131
|
+
self.service: Optional[Resource] = None
|
|
132
|
+
self.service_account_path = service_account_path
|
|
133
|
+
|
|
134
|
+
# Determine required scopes based on operations if no custom scopes provided
|
|
135
|
+
if scopes is None:
|
|
136
|
+
self.scopes = []
|
|
137
|
+
if enable_read_sheet:
|
|
138
|
+
self.scopes.append(self.DEFAULT_SCOPES["read"])
|
|
139
|
+
if enable_create_sheet or enable_update_sheet or enable_create_duplicate_sheet:
|
|
140
|
+
self.scopes.append(self.DEFAULT_SCOPES["write"])
|
|
141
|
+
# Remove duplicates while preserving order
|
|
142
|
+
self.scopes = list(dict.fromkeys(self.scopes))
|
|
143
|
+
else:
|
|
144
|
+
self.scopes = scopes
|
|
145
|
+
# Validate that required scopes are present for requested operations
|
|
146
|
+
if (enable_create_sheet or enable_update_sheet or enable_create_duplicate_sheet) and self.DEFAULT_SCOPES[
|
|
147
|
+
"write"
|
|
148
|
+
] not in self.scopes:
|
|
149
|
+
raise ValueError(f"The scope {self.DEFAULT_SCOPES['write']} is required for write operations")
|
|
150
|
+
if (
|
|
151
|
+
enable_read_sheet
|
|
152
|
+
and self.DEFAULT_SCOPES["read"] not in self.scopes
|
|
153
|
+
and self.DEFAULT_SCOPES["write"] not in self.scopes
|
|
154
|
+
):
|
|
155
|
+
raise ValueError(
|
|
156
|
+
f"Either {self.DEFAULT_SCOPES['read']} or {self.DEFAULT_SCOPES['write']} is required for read operations"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
tools: List[Any] = []
|
|
160
|
+
if all or enable_read_sheet:
|
|
161
|
+
tools.append(self.read_sheet)
|
|
162
|
+
if all or enable_create_sheet:
|
|
163
|
+
tools.append(self.create_sheet)
|
|
164
|
+
if all or enable_update_sheet:
|
|
165
|
+
tools.append(self.update_sheet)
|
|
166
|
+
if all or enable_create_duplicate_sheet:
|
|
167
|
+
tools.append(self.create_duplicate_sheet)
|
|
168
|
+
|
|
169
|
+
super().__init__(name="google_sheets_tools", tools=tools, **kwargs)
|
|
170
|
+
|
|
171
|
+
def _auth(self) -> None:
|
|
172
|
+
"""
|
|
173
|
+
Authenticate with Google Sheets API
|
|
174
|
+
"""
|
|
175
|
+
if self.creds and self.creds.valid:
|
|
176
|
+
return
|
|
177
|
+
|
|
178
|
+
service_account_path = self.service_account_path or getenv("GOOGLE_SERVICE_ACCOUNT_FILE")
|
|
179
|
+
|
|
180
|
+
if service_account_path:
|
|
181
|
+
self.creds = ServiceAccountCredentials.from_service_account_file(
|
|
182
|
+
service_account_path,
|
|
183
|
+
scopes=self.scopes,
|
|
184
|
+
)
|
|
185
|
+
if self.creds and self.creds.expired:
|
|
186
|
+
self.creds.refresh(Request())
|
|
187
|
+
return
|
|
188
|
+
|
|
189
|
+
token_file = Path(self.token_path or "token.json")
|
|
190
|
+
creds_file = Path(self.credentials_path or "credentials.json")
|
|
191
|
+
|
|
192
|
+
if token_file.exists():
|
|
193
|
+
self.creds = Credentials.from_authorized_user_file(str(token_file), self.scopes)
|
|
194
|
+
|
|
195
|
+
if not self.creds or not self.creds.valid:
|
|
196
|
+
if self.creds and self.creds.expired and self.creds.refresh_token: # type: ignore
|
|
197
|
+
self.creds.refresh(Request())
|
|
198
|
+
else:
|
|
199
|
+
client_config = {
|
|
200
|
+
"installed": {
|
|
201
|
+
"client_id": getenv("GOOGLE_CLIENT_ID"),
|
|
202
|
+
"client_secret": getenv("GOOGLE_CLIENT_SECRET"),
|
|
203
|
+
"project_id": getenv("GOOGLE_PROJECT_ID"),
|
|
204
|
+
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
|
205
|
+
"token_uri": "https://oauth2.googleapis.com/token",
|
|
206
|
+
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
|
207
|
+
"redirect_uris": [getenv("GOOGLE_REDIRECT_URI", "http://localhost")],
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
# File based authentication
|
|
211
|
+
if creds_file.exists():
|
|
212
|
+
flow = InstalledAppFlow.from_client_secrets_file(str(creds_file), self.scopes)
|
|
213
|
+
else:
|
|
214
|
+
flow = InstalledAppFlow.from_client_config(client_config, self.scopes)
|
|
215
|
+
# Opens up a browser window for OAuth authentication
|
|
216
|
+
self.creds = flow.run_local_server(port=self.oauth_port)
|
|
217
|
+
token_file.write_text(self.creds.to_json()) if self.creds else None # type: ignore
|
|
218
|
+
|
|
219
|
+
@authenticate
|
|
220
|
+
def read_sheet(self, spreadsheet_id: Optional[str] = None, spreadsheet_range: Optional[str] = None) -> str:
|
|
221
|
+
"""
|
|
222
|
+
Read values from a Google Sheet. Prioritizes instance attributes over method parameters.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
spreadsheet_id: Fallback spreadsheet ID if instance attribute is None
|
|
226
|
+
spreadsheet_range: Fallback range if instance attribute is None
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
JSON of list of rows, where each row is a list of values
|
|
230
|
+
"""
|
|
231
|
+
if not self.creds:
|
|
232
|
+
return "Not authenticated. Call auth() first."
|
|
233
|
+
|
|
234
|
+
# Prioritize instance attributes
|
|
235
|
+
sheet_id = self.spreadsheet_id or spreadsheet_id
|
|
236
|
+
sheet_range = self.spreadsheet_range or spreadsheet_range
|
|
237
|
+
|
|
238
|
+
if not sheet_id or not sheet_range:
|
|
239
|
+
return "Spreadsheet ID and range must be provided either in constructor or method call"
|
|
240
|
+
|
|
241
|
+
try:
|
|
242
|
+
result = self.service.spreadsheets().values().get(spreadsheetId=sheet_id, range=sheet_range).execute() # type: ignore
|
|
243
|
+
return json.dumps(result.get("values", []))
|
|
244
|
+
|
|
245
|
+
except Exception as e:
|
|
246
|
+
return f"Error reading Google Sheet: {e}"
|
|
247
|
+
|
|
248
|
+
@authenticate
|
|
249
|
+
def create_sheet(self, title: str) -> str:
|
|
250
|
+
"""
|
|
251
|
+
Create a Google Sheet with a given title.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
title: The title of the Google Sheet
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
The ID of the created Google Sheet
|
|
258
|
+
"""
|
|
259
|
+
if not self.creds:
|
|
260
|
+
return "Not authenticated. Call auth() first."
|
|
261
|
+
|
|
262
|
+
try:
|
|
263
|
+
spreadsheet = {"properties": {"title": title}}
|
|
264
|
+
|
|
265
|
+
spreadsheet = self.service.spreadsheets().create(body=spreadsheet, fields="spreadsheetId").execute() # type: ignore
|
|
266
|
+
spreadsheet_id = spreadsheet.get("spreadsheetId")
|
|
267
|
+
|
|
268
|
+
return f"Spreadsheet created: https://docs.google.com/spreadsheets/d/{spreadsheet_id}"
|
|
269
|
+
|
|
270
|
+
except Exception as e:
|
|
271
|
+
return f"Error creating Google Sheet: {e}"
|
|
272
|
+
|
|
273
|
+
@authenticate
|
|
274
|
+
def update_sheet(
|
|
275
|
+
self, data: List[List[Any]], spreadsheet_id: Optional[str] = None, range_name: Optional[str] = None
|
|
276
|
+
) -> str:
|
|
277
|
+
"""Updates a Google Sheet with the provided data.
|
|
278
|
+
|
|
279
|
+
Note: This function can overwrite existing data in the sheet.
|
|
280
|
+
User needs to ensure that the provided range correctly matches the data that needs to be updated.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
data: The data to update the sheet with
|
|
284
|
+
spreadsheet_id: The ID of the Google Sheet
|
|
285
|
+
range_name: The range of the Google Sheet to update
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
A message indicating the success or failure of the operation
|
|
289
|
+
"""
|
|
290
|
+
if not self.creds:
|
|
291
|
+
return "Not authenticated. Call auth() first."
|
|
292
|
+
|
|
293
|
+
try:
|
|
294
|
+
# Define the request body
|
|
295
|
+
body = {"values": data}
|
|
296
|
+
|
|
297
|
+
# Update the sheet
|
|
298
|
+
self.service.spreadsheets().values().update( # type: ignore
|
|
299
|
+
spreadsheetId=spreadsheet_id,
|
|
300
|
+
range=range_name,
|
|
301
|
+
valueInputOption="RAW",
|
|
302
|
+
body=body,
|
|
303
|
+
).execute()
|
|
304
|
+
|
|
305
|
+
return f"Sheet updated successfully: {spreadsheet_id}"
|
|
306
|
+
|
|
307
|
+
except Exception as e:
|
|
308
|
+
return f"Error updating Google Sheet: {e}"
|
|
309
|
+
|
|
310
|
+
@authenticate
|
|
311
|
+
def create_duplicate_sheet(
|
|
312
|
+
self, source_id: str, new_title: Optional[str] = None, copy_permissions: bool = True
|
|
313
|
+
) -> str:
|
|
314
|
+
"""Duplicate a Google Spreadsheet using the Google Drive API's copy feature.
|
|
315
|
+
This ensures an exact duplicate including formatting and data.
|
|
316
|
+
|
|
317
|
+
Note: Make sure your credentials include the drive scope 'https://www.googleapis.com/auth/drive'
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
source_id: The ID of the source spreadsheet.
|
|
321
|
+
new_title: Optional new title for the duplicated spreadsheet. If not provided, the source title will be used.
|
|
322
|
+
copy_permissions: Whether to copy the permissions from the source spreadsheet. Defaults to True.
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
A link to the duplicated spreadsheet.
|
|
326
|
+
"""
|
|
327
|
+
if not self.creds:
|
|
328
|
+
return "Not authenticated. Call auth() first."
|
|
329
|
+
|
|
330
|
+
if not self.service:
|
|
331
|
+
return "Service not initialized"
|
|
332
|
+
|
|
333
|
+
try:
|
|
334
|
+
# Ensure the drive scope is included
|
|
335
|
+
if "https://www.googleapis.com/auth/drive" not in self.scopes:
|
|
336
|
+
self.scopes.append("https://www.googleapis.com/auth/drive")
|
|
337
|
+
self._auth() # Re-authenticate with updated scopes
|
|
338
|
+
|
|
339
|
+
drive_service = build("drive", "v3", credentials=self.creds)
|
|
340
|
+
|
|
341
|
+
# Use new_title if provided, otherwise fetch the title from the source spreadsheet
|
|
342
|
+
if not new_title:
|
|
343
|
+
source_sheet = self.service.spreadsheets().get(spreadsheetId=source_id).execute()
|
|
344
|
+
new_title = source_sheet["properties"]["title"]
|
|
345
|
+
|
|
346
|
+
body = {"name": new_title}
|
|
347
|
+
new_file = drive_service.files().copy(fileId=source_id, body=body).execute()
|
|
348
|
+
new_spreadsheet_id = new_file.get("id")
|
|
349
|
+
|
|
350
|
+
# Copy permissions if requested
|
|
351
|
+
if copy_permissions:
|
|
352
|
+
# Get permissions from source file
|
|
353
|
+
source_permissions = (
|
|
354
|
+
drive_service.permissions()
|
|
355
|
+
.list(fileId=source_id, fields="permissions(emailAddress,role,type)")
|
|
356
|
+
.execute()
|
|
357
|
+
.get("permissions", [])
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
# Apply each permission to the new file
|
|
361
|
+
for permission in source_permissions:
|
|
362
|
+
# Skip the owner permission as it can't be transferred
|
|
363
|
+
if permission.get("role") == "owner":
|
|
364
|
+
continue
|
|
365
|
+
|
|
366
|
+
drive_service.permissions().create(
|
|
367
|
+
fileId=new_spreadsheet_id,
|
|
368
|
+
body={
|
|
369
|
+
"role": permission.get("role"),
|
|
370
|
+
"type": permission.get("type"),
|
|
371
|
+
"emailAddress": permission.get("emailAddress"),
|
|
372
|
+
},
|
|
373
|
+
).execute()
|
|
374
|
+
|
|
375
|
+
return f"Spreadsheet duplicated successfully: https://docs.google.com/spreadsheets/d/{new_spreadsheet_id}"
|
|
376
|
+
except Exception as e:
|
|
377
|
+
return f"Error duplicating spreadsheet via Drive API: {e}"
|
agno/tools/hackernews.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any, List
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
from agno.tools import Toolkit
|
|
7
|
+
from agno.utils.log import log_debug, logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class HackerNewsTools(Toolkit):
|
|
11
|
+
"""
|
|
12
|
+
HackerNews is a tool for getting top stories from Hacker News.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
enable_get_top_stories (bool): Enable getting top stories from Hacker News. Default is True.
|
|
16
|
+
enable_get_user_details (bool): Enable getting user details from Hacker News. Default is True.
|
|
17
|
+
all (bool): Enable all tools. Overrides individual flags when True. Default is False.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self, enable_get_top_stories: bool = True, enable_get_user_details: bool = True, all: bool = False, **kwargs
|
|
22
|
+
):
|
|
23
|
+
tools: List[Any] = []
|
|
24
|
+
if all or enable_get_top_stories:
|
|
25
|
+
tools.append(self.get_top_hackernews_stories)
|
|
26
|
+
if all or enable_get_user_details:
|
|
27
|
+
tools.append(self.get_user_details)
|
|
28
|
+
|
|
29
|
+
super().__init__(name="hackers_news", tools=tools, **kwargs)
|
|
30
|
+
|
|
31
|
+
def get_top_hackernews_stories(self, num_stories: int = 10) -> str:
|
|
32
|
+
"""Use this function to get top stories from Hacker News.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
num_stories (int): Number of stories to return. Defaults to 10.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
str: JSON string of top stories.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
log_debug(f"Getting top {num_stories} stories from Hacker News")
|
|
42
|
+
# Fetch top story IDs
|
|
43
|
+
response = httpx.get("https://hacker-news.firebaseio.com/v0/topstories.json")
|
|
44
|
+
story_ids = response.json()
|
|
45
|
+
|
|
46
|
+
# Fetch story details
|
|
47
|
+
stories = []
|
|
48
|
+
for story_id in story_ids[:num_stories]:
|
|
49
|
+
story_response = httpx.get(f"https://hacker-news.firebaseio.com/v0/item/{story_id}.json")
|
|
50
|
+
story = story_response.json()
|
|
51
|
+
story["username"] = story["by"]
|
|
52
|
+
stories.append(story)
|
|
53
|
+
return json.dumps(stories)
|
|
54
|
+
|
|
55
|
+
def get_user_details(self, username: str) -> str:
|
|
56
|
+
"""Use this function to get the details of a Hacker News user using their username.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
username (str): Username of the user to get details for.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
str: JSON string of the user details.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
log_debug(f"Getting details for user: {username}")
|
|
67
|
+
user = httpx.get(f"https://hacker-news.firebaseio.com/v0/user/{username}.json").json()
|
|
68
|
+
user_details = {
|
|
69
|
+
"id": user.get("user_id"),
|
|
70
|
+
"karma": user.get("karma"),
|
|
71
|
+
"about": user.get("about"),
|
|
72
|
+
"total_items_submitted": len(user.get("submitted", [])),
|
|
73
|
+
}
|
|
74
|
+
return json.dumps(user_details)
|
|
75
|
+
except Exception as e:
|
|
76
|
+
logger.exception(e)
|
|
77
|
+
return f"Error getting user details: {e}"
|
agno/tools/jina.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from os import getenv
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
from pydantic import BaseModel, Field, HttpUrl
|
|
6
|
+
|
|
7
|
+
from agno.tools import Toolkit
|
|
8
|
+
from agno.utils.log import logger
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class JinaReaderToolsConfig(BaseModel):
|
|
12
|
+
api_key: Optional[str] = Field(None, description="API key for Jina Reader")
|
|
13
|
+
base_url: HttpUrl = Field("https://r.jina.ai/", description="Base URL for Jina Reader API") # type: ignore
|
|
14
|
+
search_url: HttpUrl = Field("https://s.jina.ai/", description="Search URL for Jina Reader API") # type: ignore
|
|
15
|
+
max_content_length: int = Field(10000, description="Maximum content length in characters")
|
|
16
|
+
timeout: Optional[int] = Field(None, description="Timeout for Jina Reader API requests")
|
|
17
|
+
search_query_content: Optional[bool] = Field(False, description="Toggle full URL content in query search result")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class JinaReaderTools(Toolkit):
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
api_key: Optional[str] = getenv("JINA_API_KEY"),
|
|
24
|
+
base_url: str = "https://r.jina.ai/",
|
|
25
|
+
search_url: str = "https://s.jina.ai/",
|
|
26
|
+
max_content_length: int = 10000,
|
|
27
|
+
timeout: Optional[int] = None,
|
|
28
|
+
search_query_content: bool = True,
|
|
29
|
+
enable_read_url: bool = True,
|
|
30
|
+
enable_search_query: bool = False,
|
|
31
|
+
all: bool = False,
|
|
32
|
+
**kwargs,
|
|
33
|
+
):
|
|
34
|
+
self.api_key = api_key or getenv("JINA_API_KEY")
|
|
35
|
+
self.config: JinaReaderToolsConfig = JinaReaderToolsConfig(
|
|
36
|
+
api_key=self.api_key,
|
|
37
|
+
base_url=base_url,
|
|
38
|
+
search_url=search_url,
|
|
39
|
+
max_content_length=max_content_length,
|
|
40
|
+
timeout=timeout,
|
|
41
|
+
search_query_content=search_query_content,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
tools: List[Any] = []
|
|
45
|
+
if all or enable_read_url:
|
|
46
|
+
tools.append(self.read_url)
|
|
47
|
+
if all or enable_search_query:
|
|
48
|
+
tools.append(self.search_query)
|
|
49
|
+
|
|
50
|
+
super().__init__(name="jina_reader_tools", tools=tools, **kwargs)
|
|
51
|
+
|
|
52
|
+
def read_url(self, url: str) -> str:
|
|
53
|
+
"""Reads a URL and returns the truncated content using Jina Reader API."""
|
|
54
|
+
full_url = f"{self.config.base_url}{url}"
|
|
55
|
+
try:
|
|
56
|
+
response = httpx.get(full_url, headers=self._get_headers())
|
|
57
|
+
response.raise_for_status()
|
|
58
|
+
content = response.json()
|
|
59
|
+
return self._truncate_content(str(content))
|
|
60
|
+
except Exception as e:
|
|
61
|
+
error_msg = f"Error reading URL: {str(e)}"
|
|
62
|
+
logger.error(error_msg)
|
|
63
|
+
return error_msg
|
|
64
|
+
|
|
65
|
+
def search_query(self, query: str) -> str:
|
|
66
|
+
"""Performs a web search using Jina Reader API and returns the truncated results."""
|
|
67
|
+
full_url = f"{self.config.search_url}"
|
|
68
|
+
headers = self._get_headers()
|
|
69
|
+
if not self.config.search_query_content:
|
|
70
|
+
headers["X-Respond-With"] = "no-content" # to avoid returning full content in search results
|
|
71
|
+
|
|
72
|
+
body = {"q": query}
|
|
73
|
+
try:
|
|
74
|
+
response = httpx.post(full_url, headers=headers, json=body)
|
|
75
|
+
response.raise_for_status()
|
|
76
|
+
content = response.json()
|
|
77
|
+
return self._truncate_content(str(content))
|
|
78
|
+
except Exception as e:
|
|
79
|
+
error_msg = f"Error performing search: {str(e)}"
|
|
80
|
+
logger.error(error_msg)
|
|
81
|
+
return error_msg
|
|
82
|
+
|
|
83
|
+
def _get_headers(self) -> Dict[str, str]:
|
|
84
|
+
headers = {
|
|
85
|
+
"Accept": "application/json",
|
|
86
|
+
"X-With-Links-Summary": "true",
|
|
87
|
+
"X-With-Images-Summary": "true",
|
|
88
|
+
}
|
|
89
|
+
if self.config.api_key:
|
|
90
|
+
headers["Authorization"] = f"Bearer {self.config.api_key}"
|
|
91
|
+
if self.config.timeout:
|
|
92
|
+
headers["X-Timeout"] = str(self.config.timeout)
|
|
93
|
+
|
|
94
|
+
return headers
|
|
95
|
+
|
|
96
|
+
def _truncate_content(self, content: str) -> str:
|
|
97
|
+
"""Truncate content to the maximum allowed length."""
|
|
98
|
+
if len(content) > self.config.max_content_length:
|
|
99
|
+
truncated = content[: self.config.max_content_length]
|
|
100
|
+
return truncated + "... (content truncated)"
|
|
101
|
+
return content
|