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,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
CHANGED
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
import json
|
|
2
|
+
from typing import Any, List
|
|
2
3
|
|
|
3
4
|
import httpx
|
|
4
5
|
|
|
5
6
|
from agno.tools import Toolkit
|
|
6
|
-
from agno.utils.log import logger
|
|
7
|
+
from agno.utils.log import log_debug, logger
|
|
7
8
|
|
|
8
9
|
|
|
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
|
+
|
|
10
20
|
def __init__(
|
|
11
|
-
self,
|
|
12
|
-
get_top_stories: bool = True,
|
|
13
|
-
get_user_details: bool = True,
|
|
21
|
+
self, enable_get_top_stories: bool = True, enable_get_user_details: bool = True, all: bool = False, **kwargs
|
|
14
22
|
):
|
|
15
|
-
|
|
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)
|
|
16
28
|
|
|
17
|
-
|
|
18
|
-
if get_top_stories:
|
|
19
|
-
self.register(self.get_top_hackernews_stories)
|
|
20
|
-
if get_user_details:
|
|
21
|
-
self.register(self.get_user_details)
|
|
29
|
+
super().__init__(name="hackers_news", tools=tools, **kwargs)
|
|
22
30
|
|
|
23
31
|
def get_top_hackernews_stories(self, num_stories: int = 10) -> str:
|
|
24
32
|
"""Use this function to get top stories from Hacker News.
|
|
@@ -30,7 +38,7 @@ class HackerNewsTools(Toolkit):
|
|
|
30
38
|
str: JSON string of top stories.
|
|
31
39
|
"""
|
|
32
40
|
|
|
33
|
-
|
|
41
|
+
log_debug(f"Getting top {num_stories} stories from Hacker News")
|
|
34
42
|
# Fetch top story IDs
|
|
35
43
|
response = httpx.get("https://hacker-news.firebaseio.com/v0/topstories.json")
|
|
36
44
|
story_ids = response.json()
|
|
@@ -55,7 +63,7 @@ class HackerNewsTools(Toolkit):
|
|
|
55
63
|
"""
|
|
56
64
|
|
|
57
65
|
try:
|
|
58
|
-
|
|
66
|
+
log_debug(f"Getting details for user: {username}")
|
|
59
67
|
user = httpx.get(f"https://hacker-news.firebaseio.com/v0/user/{username}.json").json()
|
|
60
68
|
user_details = {
|
|
61
69
|
"id": user.get("user_id"),
|
agno/tools/jina.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from os import getenv
|
|
2
|
-
from typing import Dict, Optional
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
3
|
|
|
4
4
|
import httpx
|
|
5
5
|
from pydantic import BaseModel, Field, HttpUrl
|
|
@@ -14,6 +14,7 @@ class JinaReaderToolsConfig(BaseModel):
|
|
|
14
14
|
search_url: HttpUrl = Field("https://s.jina.ai/", description="Search URL for Jina Reader API") # type: ignore
|
|
15
15
|
max_content_length: int = Field(10000, description="Maximum content length in characters")
|
|
16
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")
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
class JinaReaderTools(Toolkit):
|
|
@@ -24,28 +25,33 @@ class JinaReaderTools(Toolkit):
|
|
|
24
25
|
search_url: str = "https://s.jina.ai/",
|
|
25
26
|
max_content_length: int = 10000,
|
|
26
27
|
timeout: Optional[int] = None,
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
search_query_content: bool = True,
|
|
29
|
+
enable_read_url: bool = True,
|
|
30
|
+
enable_search_query: bool = False,
|
|
31
|
+
all: bool = False,
|
|
32
|
+
**kwargs,
|
|
29
33
|
):
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
self.api_key = api_key or getenv("JINA_API_KEY")
|
|
32
35
|
self.config: JinaReaderToolsConfig = JinaReaderToolsConfig(
|
|
33
|
-
api_key=api_key,
|
|
36
|
+
api_key=self.api_key,
|
|
34
37
|
base_url=base_url,
|
|
35
38
|
search_url=search_url,
|
|
36
39
|
max_content_length=max_content_length,
|
|
37
40
|
timeout=timeout,
|
|
41
|
+
search_query_content=search_query_content,
|
|
38
42
|
)
|
|
39
43
|
|
|
40
|
-
|
|
41
|
-
|
|
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)
|
|
44
51
|
|
|
45
52
|
def read_url(self, url: str) -> str:
|
|
46
53
|
"""Reads a URL and returns the truncated content using Jina Reader API."""
|
|
47
54
|
full_url = f"{self.config.base_url}{url}"
|
|
48
|
-
logger.info(f"Reading URL: {full_url}")
|
|
49
55
|
try:
|
|
50
56
|
response = httpx.get(full_url, headers=self._get_headers())
|
|
51
57
|
response.raise_for_status()
|
|
@@ -58,10 +64,14 @@ class JinaReaderTools(Toolkit):
|
|
|
58
64
|
|
|
59
65
|
def search_query(self, query: str) -> str:
|
|
60
66
|
"""Performs a web search using Jina Reader API and returns the truncated results."""
|
|
61
|
-
full_url = f"{self.config.search_url}
|
|
62
|
-
|
|
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}
|
|
63
73
|
try:
|
|
64
|
-
response = httpx.
|
|
74
|
+
response = httpx.post(full_url, headers=headers, json=body)
|
|
65
75
|
response.raise_for_status()
|
|
66
76
|
content = response.json()
|
|
67
77
|
return self._truncate_content(str(content))
|
agno/tools/jira.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import json
|
|
2
|
-
import
|
|
3
|
-
from typing import Optional, cast
|
|
2
|
+
from os import getenv
|
|
3
|
+
from typing import Any, List, Optional, cast
|
|
4
4
|
|
|
5
5
|
from agno.tools import Toolkit
|
|
6
|
-
from agno.utils.log import logger
|
|
6
|
+
from agno.utils.log import log_debug, logger
|
|
7
7
|
|
|
8
8
|
try:
|
|
9
9
|
from jira import JIRA, Issue
|
|
@@ -18,13 +18,18 @@ class JiraTools(Toolkit):
|
|
|
18
18
|
username: Optional[str] = None,
|
|
19
19
|
password: Optional[str] = None,
|
|
20
20
|
token: Optional[str] = None,
|
|
21
|
+
enable_get_issue: bool = True,
|
|
22
|
+
enable_create_issue: bool = True,
|
|
23
|
+
enable_search_issues: bool = True,
|
|
24
|
+
enable_add_comment: bool = True,
|
|
25
|
+
enable_add_worklog: bool = True,
|
|
26
|
+
all: bool = False,
|
|
27
|
+
**kwargs,
|
|
21
28
|
):
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
self.
|
|
25
|
-
self.
|
|
26
|
-
self.password = password or os.getenv("JIRA_PASSWORD")
|
|
27
|
-
self.token = token or os.getenv("JIRA_TOKEN")
|
|
29
|
+
self.server_url = server_url or getenv("JIRA_SERVER_URL")
|
|
30
|
+
self.username = username or getenv("JIRA_USERNAME")
|
|
31
|
+
self.password = password or getenv("JIRA_PASSWORD")
|
|
32
|
+
self.token = token or getenv("JIRA_TOKEN")
|
|
28
33
|
|
|
29
34
|
if not self.server_url:
|
|
30
35
|
raise ValueError("JIRA server URL not provided.")
|
|
@@ -42,12 +47,19 @@ class JiraTools(Toolkit):
|
|
|
42
47
|
else:
|
|
43
48
|
self.jira = JIRA(server=self.server_url)
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
tools: List[Any] = []
|
|
51
|
+
if enable_get_issue or all:
|
|
52
|
+
tools.append(self.get_issue)
|
|
53
|
+
if enable_create_issue or all:
|
|
54
|
+
tools.append(self.create_issue)
|
|
55
|
+
if enable_search_issues or all:
|
|
56
|
+
tools.append(self.search_issues)
|
|
57
|
+
if enable_add_comment or all:
|
|
58
|
+
tools.append(self.add_comment)
|
|
59
|
+
if enable_add_worklog or all:
|
|
60
|
+
tools.append(self.add_worklog)
|
|
61
|
+
|
|
62
|
+
super().__init__(name="jira_tools", tools=tools, **kwargs)
|
|
51
63
|
|
|
52
64
|
def get_issue(self, issue_key: str) -> str:
|
|
53
65
|
"""
|
|
@@ -67,7 +79,7 @@ class JiraTools(Toolkit):
|
|
|
67
79
|
"summary": issue.fields.summary,
|
|
68
80
|
"description": issue.fields.description or "",
|
|
69
81
|
}
|
|
70
|
-
|
|
82
|
+
log_debug(f"Issue details retrieved for {issue_key}: {issue_details}")
|
|
71
83
|
return json.dumps(issue_details)
|
|
72
84
|
except Exception as e:
|
|
73
85
|
logger.error(f"Error retrieving issue {issue_key}: {e}")
|
|
@@ -92,7 +104,7 @@ class JiraTools(Toolkit):
|
|
|
92
104
|
}
|
|
93
105
|
new_issue = self.jira.create_issue(fields=issue_dict)
|
|
94
106
|
issue_url = f"{self.server_url}/browse/{new_issue.key}"
|
|
95
|
-
|
|
107
|
+
log_debug(f"Issue created with key: {new_issue.key}")
|
|
96
108
|
return json.dumps({"key": new_issue.key, "url": issue_url})
|
|
97
109
|
except Exception as e:
|
|
98
110
|
logger.error(f"Error creating issue in project {project_key}: {e}")
|
|
@@ -118,7 +130,7 @@ class JiraTools(Toolkit):
|
|
|
118
130
|
"assignee": issue.fields.assignee.displayName if issue.fields.assignee else "Unassigned",
|
|
119
131
|
}
|
|
120
132
|
results.append(issue_details)
|
|
121
|
-
|
|
133
|
+
log_debug(f"Found {len(results)} issues for JQL '{jql_str}'")
|
|
122
134
|
return json.dumps(results)
|
|
123
135
|
except Exception as e:
|
|
124
136
|
logger.error(f"Error searching issues with JQL '{jql_str}': {e}")
|
|
@@ -134,8 +146,25 @@ class JiraTools(Toolkit):
|
|
|
134
146
|
"""
|
|
135
147
|
try:
|
|
136
148
|
self.jira.add_comment(issue_key, comment)
|
|
137
|
-
|
|
149
|
+
log_debug(f"Comment added to issue {issue_key}")
|
|
138
150
|
return json.dumps({"status": "success", "issue_key": issue_key})
|
|
139
151
|
except Exception as e:
|
|
140
152
|
logger.error(f"Error adding comment to issue {issue_key}: {e}")
|
|
141
153
|
return json.dumps({"error": str(e)})
|
|
154
|
+
|
|
155
|
+
def add_worklog(self, issue_key: str, time_spent: str, comment: Optional[str] = None) -> str:
|
|
156
|
+
"""
|
|
157
|
+
Adds a worklog entry to log time spent on a specific Jira issue.
|
|
158
|
+
|
|
159
|
+
:param issue_key: The key of the issue to log work against (e.g., 'PROJ-123').
|
|
160
|
+
:param time_spent: The amount of time spent. Use Jira's format, e.g., '2h', '30m', '1d 4h'.
|
|
161
|
+
:param comment: An optional comment describing the work done.
|
|
162
|
+
:return: A JSON string indicating success or containing an error message.
|
|
163
|
+
"""
|
|
164
|
+
try:
|
|
165
|
+
self.jira.add_worklog(issue=issue_key, timeSpent=time_spent, comment=comment)
|
|
166
|
+
log_debug(f"Worklog of '{time_spent}' added to issue {issue_key}")
|
|
167
|
+
return json.dumps({"status": "success", "issue_key": issue_key, "time_spent": time_spent})
|
|
168
|
+
except Exception as e:
|
|
169
|
+
logger.error(f"Error adding worklog to issue {issue_key}: {e}")
|
|
170
|
+
return json.dumps({"error": str(e)})
|