agno 0.1.2__py3-none-any.whl → 2.3.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/__init__.py +8 -0
- agno/agent/__init__.py +44 -5
- agno/agent/agent.py +10531 -2975
- agno/api/agent.py +14 -53
- agno/api/api.py +7 -46
- agno/api/evals.py +22 -0
- agno/api/os.py +17 -0
- agno/api/routes.py +6 -25
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +6 -9
- agno/api/schemas/evals.py +16 -0
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/team.py +10 -10
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +16 -0
- agno/api/settings.py +53 -0
- agno/api/team.py +22 -26
- agno/api/workflow.py +28 -0
- agno/cloud/aws/base.py +214 -0
- agno/cloud/aws/s3/__init__.py +2 -0
- agno/cloud/aws/s3/api_client.py +43 -0
- agno/cloud/aws/s3/bucket.py +195 -0
- agno/cloud/aws/s3/object.py +57 -0
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/__init__.py +24 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +946 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +2781 -0
- agno/db/dynamo/schemas.py +442 -0
- agno/db/dynamo/utils.py +743 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +2379 -0
- agno/db/firestore/schemas.py +181 -0
- agno/db/firestore/utils.py +376 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1791 -0
- agno/db/gcs_json/utils.py +228 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +1312 -0
- agno/db/in_memory/utils.py +230 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1777 -0
- agno/db/json/utils.py +230 -0
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +635 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +17 -0
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +2597 -0
- agno/db/mongo/schemas.py +119 -0
- agno/db/mongo/utils.py +276 -0
- agno/db/mysql/__init__.py +4 -0
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +2923 -0
- agno/db/mysql/schemas.py +186 -0
- agno/db/mysql/utils.py +488 -0
- agno/db/postgres/__init__.py +4 -0
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +2870 -0
- agno/db/postgres/schemas.py +187 -0
- agno/db/postgres/utils.py +442 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +2141 -0
- agno/db/redis/schemas.py +159 -0
- agno/db/redis/utils.py +346 -0
- agno/db/schemas/__init__.py +4 -0
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +34 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +61 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +179 -0
- agno/db/singlestore/singlestore.py +2877 -0
- agno/db/singlestore/utils.py +384 -0
- agno/db/sqlite/__init__.py +4 -0
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +181 -0
- agno/db/sqlite/sqlite.py +2908 -0
- agno/db/sqlite/utils.py +429 -0
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +334 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1908 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +118 -0
- agno/eval/__init__.py +24 -0
- agno/eval/accuracy.py +666 -276
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +779 -0
- agno/eval/reliability.py +241 -62
- agno/eval/utils.py +120 -0
- agno/exceptions.py +143 -1
- agno/filters.py +354 -0
- agno/guardrails/__init__.py +6 -0
- agno/guardrails/base.py +19 -0
- agno/guardrails/openai.py +144 -0
- agno/guardrails/pii.py +94 -0
- agno/guardrails/prompt_injection.py +52 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/__init__.py +3 -0
- agno/integrations/discord/client.py +203 -0
- agno/knowledge/__init__.py +5 -1
- agno/{document → knowledge}/chunking/agentic.py +22 -14
- agno/{document → knowledge}/chunking/document.py +2 -2
- agno/{document → knowledge}/chunking/fixed.py +7 -6
- agno/knowledge/chunking/markdown.py +151 -0
- agno/{document → knowledge}/chunking/recursive.py +15 -3
- agno/knowledge/chunking/row.py +39 -0
- agno/knowledge/chunking/semantic.py +91 -0
- agno/knowledge/chunking/strategy.py +165 -0
- agno/knowledge/content.py +74 -0
- agno/knowledge/document/__init__.py +5 -0
- agno/{document → knowledge/document}/base.py +12 -2
- agno/knowledge/embedder/__init__.py +5 -0
- agno/knowledge/embedder/aws_bedrock.py +343 -0
- agno/knowledge/embedder/azure_openai.py +210 -0
- agno/{embedder → knowledge/embedder}/base.py +8 -0
- agno/knowledge/embedder/cohere.py +323 -0
- agno/knowledge/embedder/fastembed.py +62 -0
- agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
- agno/knowledge/embedder/google.py +258 -0
- agno/knowledge/embedder/huggingface.py +94 -0
- agno/knowledge/embedder/jina.py +182 -0
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +206 -0
- agno/knowledge/embedder/nebius.py +13 -0
- agno/knowledge/embedder/ollama.py +154 -0
- agno/knowledge/embedder/openai.py +195 -0
- agno/knowledge/embedder/sentence_transformer.py +63 -0
- agno/{embedder → knowledge/embedder}/together.py +1 -1
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +165 -0
- agno/knowledge/knowledge.py +3006 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/knowledge/reader/arxiv_reader.py +81 -0
- agno/knowledge/reader/base.py +95 -0
- agno/knowledge/reader/csv_reader.py +164 -0
- agno/knowledge/reader/docx_reader.py +82 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
- agno/knowledge/reader/firecrawl_reader.py +201 -0
- agno/knowledge/reader/json_reader.py +88 -0
- agno/knowledge/reader/markdown_reader.py +137 -0
- agno/knowledge/reader/pdf_reader.py +431 -0
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +313 -0
- agno/knowledge/reader/s3_reader.py +89 -0
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +127 -0
- agno/knowledge/reader/web_search_reader.py +325 -0
- agno/knowledge/reader/website_reader.py +455 -0
- agno/knowledge/reader/wikipedia_reader.py +91 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/knowledge/reranker/__init__.py +3 -0
- agno/{reranker → knowledge/reranker}/base.py +1 -1
- agno/{reranker → knowledge/reranker}/cohere.py +2 -2
- agno/knowledge/reranker/infinity.py +195 -0
- agno/knowledge/reranker/sentence_transformer.py +54 -0
- agno/knowledge/types.py +39 -0
- agno/knowledge/utils.py +234 -0
- agno/media.py +439 -95
- agno/memory/__init__.py +16 -3
- agno/memory/manager.py +1474 -123
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +66 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/aimlapi/__init__.py +5 -0
- agno/models/aimlapi/aimlapi.py +62 -0
- agno/models/anthropic/__init__.py +4 -0
- agno/models/anthropic/claude.py +960 -496
- agno/models/aws/__init__.py +15 -0
- agno/models/aws/bedrock.py +686 -451
- agno/models/aws/claude.py +190 -183
- agno/models/azure/__init__.py +18 -1
- agno/models/azure/ai_foundry.py +489 -0
- agno/models/azure/openai_chat.py +89 -40
- agno/models/base.py +2477 -550
- agno/models/cerebras/__init__.py +12 -0
- agno/models/cerebras/cerebras.py +565 -0
- agno/models/cerebras/cerebras_openai.py +131 -0
- agno/models/cohere/__init__.py +4 -0
- agno/models/cohere/chat.py +306 -492
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +74 -0
- agno/models/dashscope/__init__.py +5 -0
- agno/models/dashscope/dashscope.py +90 -0
- agno/models/deepinfra/__init__.py +5 -0
- agno/models/deepinfra/deepinfra.py +45 -0
- agno/models/deepseek/__init__.py +4 -0
- agno/models/deepseek/deepseek.py +110 -9
- agno/models/fireworks/__init__.py +4 -0
- agno/models/fireworks/fireworks.py +19 -22
- agno/models/google/__init__.py +3 -7
- agno/models/google/gemini.py +1717 -662
- agno/models/google/utils.py +22 -0
- agno/models/groq/__init__.py +4 -0
- agno/models/groq/groq.py +391 -666
- agno/models/huggingface/__init__.py +4 -0
- agno/models/huggingface/huggingface.py +266 -538
- agno/models/ibm/__init__.py +5 -0
- agno/models/ibm/watsonx.py +432 -0
- agno/models/internlm/__init__.py +3 -0
- agno/models/internlm/internlm.py +20 -3
- agno/models/langdb/__init__.py +1 -0
- agno/models/langdb/langdb.py +60 -0
- agno/models/litellm/__init__.py +14 -0
- agno/models/litellm/chat.py +503 -0
- agno/models/litellm/litellm_openai.py +42 -0
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/lmstudio/__init__.py +5 -0
- agno/models/lmstudio/lmstudio.py +25 -0
- agno/models/message.py +361 -39
- agno/models/meta/__init__.py +12 -0
- agno/models/meta/llama.py +502 -0
- agno/models/meta/llama_openai.py +79 -0
- agno/models/metrics.py +120 -0
- agno/models/mistral/__init__.py +4 -0
- agno/models/mistral/mistral.py +293 -393
- agno/models/nebius/__init__.py +3 -0
- agno/models/nebius/nebius.py +53 -0
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/__init__.py +4 -0
- agno/models/nvidia/nvidia.py +22 -3
- agno/models/ollama/__init__.py +4 -2
- agno/models/ollama/chat.py +257 -492
- agno/models/openai/__init__.py +7 -0
- agno/models/openai/chat.py +725 -770
- agno/models/openai/like.py +16 -2
- agno/models/openai/responses.py +1121 -0
- agno/models/openrouter/__init__.py +4 -0
- agno/models/openrouter/openrouter.py +62 -5
- agno/models/perplexity/__init__.py +5 -0
- agno/models/perplexity/perplexity.py +203 -0
- agno/models/portkey/__init__.py +3 -0
- agno/models/portkey/portkey.py +82 -0
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +69 -0
- agno/models/response.py +177 -7
- agno/models/sambanova/__init__.py +4 -0
- agno/models/sambanova/sambanova.py +23 -4
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +42 -0
- agno/models/together/__init__.py +4 -0
- agno/models/together/together.py +21 -164
- agno/models/utils.py +266 -0
- agno/models/vercel/__init__.py +3 -0
- agno/models/vercel/v0.py +43 -0
- agno/models/vertexai/__init__.py +0 -1
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/__init__.py +3 -0
- agno/models/vllm/vllm.py +83 -0
- agno/models/xai/__init__.py +2 -0
- agno/models/xai/xai.py +111 -7
- agno/os/__init__.py +3 -0
- agno/os/app.py +1027 -0
- agno/os/auth.py +244 -0
- agno/os/config.py +126 -0
- agno/os/interfaces/__init__.py +1 -0
- agno/os/interfaces/a2a/__init__.py +3 -0
- agno/os/interfaces/a2a/a2a.py +42 -0
- agno/os/interfaces/a2a/router.py +249 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +47 -0
- agno/os/interfaces/agui/router.py +147 -0
- agno/os/interfaces/agui/utils.py +574 -0
- agno/os/interfaces/base.py +25 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/os/interfaces/slack/router.py +148 -0
- agno/os/interfaces/slack/security.py +30 -0
- agno/os/interfaces/slack/slack.py +47 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/os/interfaces/whatsapp/router.py +210 -0
- agno/os/interfaces/whatsapp/security.py +55 -0
- agno/os/interfaces/whatsapp/whatsapp.py +36 -0
- agno/os/mcp.py +293 -0
- agno/os/middleware/__init__.py +9 -0
- agno/os/middleware/jwt.py +797 -0
- agno/os/router.py +258 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +599 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +450 -0
- agno/os/routers/evals/schemas.py +174 -0
- agno/os/routers/evals/utils.py +231 -0
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +1008 -0
- agno/os/routers/knowledge/schemas.py +178 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +661 -0
- agno/os/routers/memory/schemas.py +88 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +190 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +997 -0
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +512 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +499 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +624 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +534 -0
- agno/os/scopes.py +469 -0
- agno/{playground → os}/settings.py +7 -15
- agno/os/utils.py +973 -0
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +67 -0
- agno/reasoning/deepseek.py +63 -0
- agno/reasoning/default.py +97 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +71 -0
- agno/reasoning/helpers.py +24 -1
- agno/reasoning/ollama.py +67 -0
- agno/reasoning/openai.py +86 -0
- agno/reasoning/step.py +2 -1
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +822 -0
- agno/run/base.py +247 -0
- agno/run/cancel.py +81 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +767 -0
- agno/run/workflow.py +708 -0
- agno/session/__init__.py +10 -0
- agno/session/agent.py +260 -0
- agno/session/summary.py +265 -0
- agno/session/team.py +342 -0
- agno/session/workflow.py +501 -0
- agno/table.py +10 -0
- agno/team/__init__.py +37 -0
- agno/team/team.py +9536 -0
- agno/tools/__init__.py +7 -0
- agno/tools/agentql.py +120 -0
- agno/tools/airflow.py +22 -12
- agno/tools/api.py +122 -0
- agno/tools/apify.py +276 -83
- agno/tools/{arxiv_toolkit.py → arxiv.py} +20 -12
- agno/tools/aws_lambda.py +28 -7
- agno/tools/aws_ses.py +66 -0
- agno/tools/baidusearch.py +11 -4
- agno/tools/bitbucket.py +292 -0
- agno/tools/brandfetch.py +213 -0
- agno/tools/bravesearch.py +106 -0
- agno/tools/brightdata.py +367 -0
- agno/tools/browserbase.py +209 -0
- agno/tools/calcom.py +32 -23
- agno/tools/calculator.py +24 -37
- agno/tools/cartesia.py +187 -0
- agno/tools/{clickup_tool.py → clickup.py} +17 -28
- agno/tools/confluence.py +91 -26
- agno/tools/crawl4ai.py +139 -43
- agno/tools/csv_toolkit.py +28 -22
- agno/tools/dalle.py +36 -22
- agno/tools/daytona.py +475 -0
- agno/tools/decorator.py +169 -14
- agno/tools/desi_vocal.py +23 -11
- agno/tools/discord.py +32 -29
- agno/tools/docker.py +716 -0
- agno/tools/duckdb.py +76 -81
- agno/tools/duckduckgo.py +43 -40
- agno/tools/e2b.py +703 -0
- agno/tools/eleven_labs.py +65 -54
- agno/tools/email.py +13 -5
- agno/tools/evm.py +129 -0
- agno/tools/exa.py +324 -42
- agno/tools/fal.py +39 -35
- agno/tools/file.py +196 -30
- agno/tools/file_generation.py +356 -0
- agno/tools/financial_datasets.py +288 -0
- agno/tools/firecrawl.py +108 -33
- agno/tools/function.py +960 -122
- agno/tools/giphy.py +34 -12
- agno/tools/github.py +1294 -97
- agno/tools/gmail.py +922 -0
- agno/tools/google_bigquery.py +117 -0
- agno/tools/google_drive.py +271 -0
- agno/tools/google_maps.py +253 -0
- agno/tools/googlecalendar.py +607 -107
- agno/tools/googlesheets.py +377 -0
- agno/tools/hackernews.py +20 -12
- agno/tools/jina.py +24 -14
- agno/tools/jira.py +48 -19
- agno/tools/knowledge.py +218 -0
- agno/tools/linear.py +82 -43
- agno/tools/linkup.py +58 -0
- agno/tools/local_file_system.py +15 -7
- agno/tools/lumalab.py +41 -26
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +284 -0
- agno/tools/mem0.py +193 -0
- agno/tools/memory.py +419 -0
- agno/tools/mlx_transcribe.py +11 -9
- agno/tools/models/azure_openai.py +190 -0
- agno/tools/models/gemini.py +203 -0
- agno/tools/models/groq.py +158 -0
- agno/tools/models/morph.py +186 -0
- agno/tools/models/nebius.py +124 -0
- agno/tools/models_labs.py +163 -82
- agno/tools/moviepy_video.py +18 -13
- agno/tools/nano_banana.py +151 -0
- agno/tools/neo4j.py +134 -0
- agno/tools/newspaper.py +15 -4
- agno/tools/newspaper4k.py +19 -6
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +181 -17
- agno/tools/openbb.py +27 -20
- agno/tools/opencv.py +321 -0
- agno/tools/openweather.py +233 -0
- agno/tools/oxylabs.py +385 -0
- agno/tools/pandas.py +25 -15
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +238 -185
- agno/tools/pubmed.py +125 -13
- agno/tools/python.py +48 -35
- agno/tools/reasoning.py +283 -0
- agno/tools/reddit.py +207 -29
- agno/tools/redshift.py +406 -0
- agno/tools/replicate.py +69 -26
- agno/tools/resend.py +11 -6
- agno/tools/scrapegraph.py +179 -19
- agno/tools/searxng.py +23 -31
- agno/tools/serpapi.py +15 -10
- agno/tools/serper.py +255 -0
- agno/tools/shell.py +23 -12
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +56 -14
- agno/tools/sleep.py +8 -6
- agno/tools/spider.py +35 -11
- agno/tools/spotify.py +919 -0
- agno/tools/sql.py +34 -19
- agno/tools/tavily.py +158 -8
- agno/tools/telegram.py +18 -8
- agno/tools/todoist.py +218 -0
- agno/tools/toolkit.py +134 -9
- agno/tools/trafilatura.py +388 -0
- agno/tools/trello.py +25 -28
- agno/tools/twilio.py +18 -9
- agno/tools/user_control_flow.py +78 -0
- agno/tools/valyu.py +228 -0
- agno/tools/visualization.py +467 -0
- agno/tools/webbrowser.py +28 -0
- agno/tools/webex.py +76 -0
- agno/tools/website.py +23 -19
- agno/tools/webtools.py +45 -0
- agno/tools/whatsapp.py +286 -0
- agno/tools/wikipedia.py +28 -19
- agno/tools/workflow.py +285 -0
- agno/tools/{twitter.py → x.py} +142 -46
- agno/tools/yfinance.py +41 -39
- agno/tools/youtube.py +34 -17
- agno/tools/zendesk.py +15 -5
- agno/tools/zep.py +454 -0
- agno/tools/zoom.py +86 -37
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +157 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +111 -0
- agno/utils/agent.py +938 -0
- agno/utils/audio.py +37 -1
- agno/utils/certs.py +27 -0
- agno/utils/code_execution.py +11 -0
- agno/utils/common.py +103 -20
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +700 -0
- agno/utils/functions.py +107 -37
- agno/utils/gemini.py +426 -0
- agno/utils/hooks.py +171 -0
- agno/utils/http.py +185 -0
- agno/utils/json_schema.py +159 -37
- agno/utils/knowledge.py +36 -0
- agno/utils/location.py +19 -0
- agno/utils/log.py +221 -8
- agno/utils/mcp.py +214 -0
- agno/utils/media.py +335 -14
- agno/utils/merge_dict.py +22 -1
- agno/utils/message.py +77 -2
- agno/utils/models/ai_foundry.py +50 -0
- agno/utils/models/claude.py +373 -0
- agno/utils/models/cohere.py +94 -0
- agno/utils/models/llama.py +85 -0
- agno/utils/models/mistral.py +100 -0
- agno/utils/models/openai_responses.py +140 -0
- agno/utils/models/schema_utils.py +153 -0
- agno/utils/models/watsonx.py +41 -0
- agno/utils/openai.py +257 -0
- agno/utils/pickle.py +1 -1
- agno/utils/pprint.py +124 -8
- agno/utils/print_response/agent.py +930 -0
- agno/utils/print_response/team.py +1914 -0
- agno/utils/print_response/workflow.py +1668 -0
- agno/utils/prompts.py +111 -0
- agno/utils/reasoning.py +108 -0
- agno/utils/response.py +163 -0
- agno/utils/serialize.py +32 -0
- agno/utils/shell.py +4 -4
- agno/utils/streamlit.py +487 -0
- agno/utils/string.py +204 -51
- agno/utils/team.py +139 -0
- agno/utils/timer.py +9 -2
- agno/utils/tokens.py +657 -0
- agno/utils/tools.py +19 -1
- agno/utils/whatsapp.py +305 -0
- agno/utils/yaml_io.py +3 -3
- agno/vectordb/__init__.py +2 -0
- agno/vectordb/base.py +87 -9
- agno/vectordb/cassandra/__init__.py +5 -1
- agno/vectordb/cassandra/cassandra.py +383 -27
- agno/vectordb/chroma/__init__.py +4 -0
- agno/vectordb/chroma/chromadb.py +748 -83
- agno/vectordb/clickhouse/__init__.py +7 -1
- agno/vectordb/clickhouse/clickhousedb.py +554 -53
- agno/vectordb/couchbase/__init__.py +3 -0
- agno/vectordb/couchbase/couchbase.py +1446 -0
- agno/vectordb/lancedb/__init__.py +5 -0
- agno/vectordb/lancedb/lance_db.py +730 -98
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +163 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +388 -0
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +166 -0
- agno/vectordb/milvus/__init__.py +3 -0
- agno/vectordb/milvus/milvus.py +966 -78
- agno/vectordb/mongodb/__init__.py +9 -1
- agno/vectordb/mongodb/mongodb.py +1175 -172
- agno/vectordb/pgvector/__init__.py +8 -0
- agno/vectordb/pgvector/pgvector.py +599 -115
- agno/vectordb/pineconedb/__init__.py +5 -1
- agno/vectordb/pineconedb/pineconedb.py +406 -43
- agno/vectordb/qdrant/__init__.py +4 -0
- agno/vectordb/qdrant/qdrant.py +914 -61
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/__init__.py +8 -1
- agno/vectordb/singlestore/singlestore.py +771 -0
- agno/vectordb/surrealdb/__init__.py +3 -0
- agno/vectordb/surrealdb/surrealdb.py +663 -0
- agno/vectordb/upstashdb/__init__.py +5 -0
- agno/vectordb/upstashdb/upstashdb.py +718 -0
- agno/vectordb/weaviate/__init__.py +8 -0
- agno/vectordb/weaviate/index.py +15 -0
- agno/vectordb/weaviate/weaviate.py +1009 -0
- agno/workflow/__init__.py +23 -1
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +759 -0
- agno/workflow/loop.py +756 -0
- agno/workflow/parallel.py +853 -0
- agno/workflow/router.py +723 -0
- agno/workflow/step.py +1564 -0
- agno/workflow/steps.py +613 -0
- agno/workflow/types.py +556 -0
- agno/workflow/workflow.py +4327 -514
- agno-2.3.13.dist-info/METADATA +639 -0
- agno-2.3.13.dist-info/RECORD +613 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +1 -1
- agno-2.3.13.dist-info/licenses/LICENSE +201 -0
- agno/api/playground.py +0 -91
- agno/api/schemas/playground.py +0 -22
- agno/api/schemas/user.py +0 -22
- agno/api/schemas/workspace.py +0 -46
- agno/api/user.py +0 -160
- agno/api/workspace.py +0 -151
- agno/cli/auth_server.py +0 -118
- agno/cli/config.py +0 -275
- agno/cli/console.py +0 -88
- agno/cli/credentials.py +0 -23
- agno/cli/entrypoint.py +0 -571
- agno/cli/operator.py +0 -355
- agno/cli/settings.py +0 -85
- agno/cli/ws/ws_cli.py +0 -817
- agno/constants.py +0 -13
- agno/document/__init__.py +0 -1
- agno/document/chunking/semantic.py +0 -47
- agno/document/chunking/strategy.py +0 -31
- agno/document/reader/__init__.py +0 -1
- agno/document/reader/arxiv_reader.py +0 -41
- agno/document/reader/base.py +0 -22
- agno/document/reader/csv_reader.py +0 -84
- agno/document/reader/docx_reader.py +0 -46
- agno/document/reader/firecrawl_reader.py +0 -99
- agno/document/reader/json_reader.py +0 -43
- agno/document/reader/pdf_reader.py +0 -219
- agno/document/reader/s3/pdf_reader.py +0 -46
- agno/document/reader/s3/text_reader.py +0 -51
- agno/document/reader/text_reader.py +0 -41
- agno/document/reader/website_reader.py +0 -175
- agno/document/reader/youtube_reader.py +0 -50
- agno/embedder/__init__.py +0 -1
- agno/embedder/azure_openai.py +0 -86
- agno/embedder/cohere.py +0 -72
- agno/embedder/fastembed.py +0 -37
- agno/embedder/google.py +0 -73
- agno/embedder/huggingface.py +0 -54
- agno/embedder/mistral.py +0 -80
- agno/embedder/ollama.py +0 -57
- agno/embedder/openai.py +0 -74
- agno/embedder/sentence_transformer.py +0 -38
- agno/embedder/voyageai.py +0 -64
- agno/eval/perf.py +0 -201
- agno/file/__init__.py +0 -1
- agno/file/file.py +0 -16
- agno/file/local/csv.py +0 -32
- agno/file/local/txt.py +0 -19
- agno/infra/app.py +0 -240
- agno/infra/base.py +0 -144
- agno/infra/context.py +0 -20
- agno/infra/db_app.py +0 -52
- agno/infra/resource.py +0 -205
- agno/infra/resources.py +0 -55
- agno/knowledge/agent.py +0 -230
- agno/knowledge/arxiv.py +0 -22
- agno/knowledge/combined.py +0 -22
- agno/knowledge/csv.py +0 -28
- agno/knowledge/csv_url.py +0 -19
- agno/knowledge/document.py +0 -20
- agno/knowledge/docx.py +0 -30
- agno/knowledge/json.py +0 -28
- agno/knowledge/langchain.py +0 -71
- agno/knowledge/llamaindex.py +0 -66
- agno/knowledge/pdf.py +0 -28
- agno/knowledge/pdf_url.py +0 -26
- agno/knowledge/s3/base.py +0 -60
- agno/knowledge/s3/pdf.py +0 -21
- agno/knowledge/s3/text.py +0 -23
- agno/knowledge/text.py +0 -30
- agno/knowledge/website.py +0 -88
- agno/knowledge/wikipedia.py +0 -31
- agno/knowledge/youtube.py +0 -22
- agno/memory/agent.py +0 -392
- agno/memory/classifier.py +0 -104
- agno/memory/db/__init__.py +0 -1
- agno/memory/db/base.py +0 -42
- agno/memory/db/mongodb.py +0 -189
- agno/memory/db/postgres.py +0 -203
- agno/memory/db/sqlite.py +0 -193
- agno/memory/memory.py +0 -15
- agno/memory/row.py +0 -36
- agno/memory/summarizer.py +0 -192
- agno/memory/summary.py +0 -19
- agno/memory/workflow.py +0 -38
- agno/models/google/gemini_openai.py +0 -26
- agno/models/ollama/hermes.py +0 -221
- agno/models/ollama/tools.py +0 -362
- agno/models/vertexai/gemini.py +0 -595
- agno/playground/__init__.py +0 -3
- agno/playground/async_router.py +0 -421
- agno/playground/deploy.py +0 -249
- agno/playground/operator.py +0 -92
- agno/playground/playground.py +0 -91
- agno/playground/schemas.py +0 -76
- agno/playground/serve.py +0 -55
- agno/playground/sync_router.py +0 -405
- agno/reasoning/agent.py +0 -68
- agno/run/response.py +0 -112
- agno/storage/agent/__init__.py +0 -0
- agno/storage/agent/base.py +0 -38
- agno/storage/agent/dynamodb.py +0 -350
- agno/storage/agent/json.py +0 -92
- agno/storage/agent/mongodb.py +0 -228
- agno/storage/agent/postgres.py +0 -367
- agno/storage/agent/session.py +0 -79
- agno/storage/agent/singlestore.py +0 -303
- agno/storage/agent/sqlite.py +0 -357
- agno/storage/agent/yaml.py +0 -93
- agno/storage/workflow/__init__.py +0 -0
- agno/storage/workflow/base.py +0 -40
- agno/storage/workflow/mongodb.py +0 -233
- agno/storage/workflow/postgres.py +0 -366
- agno/storage/workflow/session.py +0 -60
- agno/storage/workflow/sqlite.py +0 -359
- agno/tools/googlesearch.py +0 -88
- agno/utils/defaults.py +0 -57
- agno/utils/filesystem.py +0 -39
- agno/utils/git.py +0 -52
- agno/utils/json_io.py +0 -30
- agno/utils/load_env.py +0 -19
- agno/utils/py_io.py +0 -19
- agno/utils/pyproject.py +0 -18
- agno/utils/resource_filter.py +0 -31
- agno/vectordb/singlestore/s2vectordb.py +0 -390
- agno/vectordb/singlestore/s2vectordb2.py +0 -355
- agno/workspace/__init__.py +0 -0
- agno/workspace/config.py +0 -325
- agno/workspace/enums.py +0 -6
- agno/workspace/helpers.py +0 -48
- agno/workspace/operator.py +0 -758
- agno/workspace/settings.py +0 -63
- agno-0.1.2.dist-info/LICENSE +0 -375
- agno-0.1.2.dist-info/METADATA +0 -502
- agno-0.1.2.dist-info/RECORD +0 -352
- agno-0.1.2.dist-info/entry_points.txt +0 -3
- /agno/{cli → db/migrations}/__init__.py +0 -0
- /agno/{cli/ws → db/migrations/versions}/__init__.py +0 -0
- /agno/{document/chunking/__init__.py → db/schemas/metrics.py} +0 -0
- /agno/{document/reader/s3 → integrations}/__init__.py +0 -0
- /agno/{file/local → knowledge/chunking}/__init__.py +0 -0
- /agno/{infra → knowledge/remote_content}/__init__.py +0 -0
- /agno/{knowledge/s3 → tools/models}/__init__.py +0 -0
- /agno/{reranker → utils/models}/__init__.py +0 -0
- /agno/{storage → utils/print_response}/__init__.py +0 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
agno/tools/whatsapp.py
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
from os import getenv
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
from agno.tools import Toolkit
|
|
7
|
+
from agno.utils.log import logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class WhatsAppTools(Toolkit):
|
|
11
|
+
"""WhatsApp Business API toolkit for sending messages."""
|
|
12
|
+
|
|
13
|
+
base_url = "https://graph.facebook.com"
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
access_token: Optional[str] = None,
|
|
18
|
+
phone_number_id: Optional[str] = None,
|
|
19
|
+
version: Optional[str] = None,
|
|
20
|
+
recipient_waid: Optional[str] = None,
|
|
21
|
+
async_mode: bool = False,
|
|
22
|
+
):
|
|
23
|
+
"""Initialize WhatsApp toolkit.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
access_token: WhatsApp Business API access token
|
|
27
|
+
phone_number_id: WhatsApp Business Account phone number ID
|
|
28
|
+
version: API version to use
|
|
29
|
+
recipient_waid: Default recipient WhatsApp ID (optional)
|
|
30
|
+
async_mode: Whether to use async methods (default: False)
|
|
31
|
+
"""
|
|
32
|
+
# Core credentials
|
|
33
|
+
self.access_token = access_token or getenv("WHATSAPP_ACCESS_TOKEN")
|
|
34
|
+
if not self.access_token:
|
|
35
|
+
logger.error("WHATSAPP_ACCESS_TOKEN not set. Please set the WHATSAPP_ACCESS_TOKEN environment variable.")
|
|
36
|
+
|
|
37
|
+
self.phone_number_id = phone_number_id or getenv("WHATSAPP_PHONE_NUMBER_ID")
|
|
38
|
+
if not self.phone_number_id:
|
|
39
|
+
logger.error(
|
|
40
|
+
"WHATSAPP_PHONE_NUMBER_ID not set. Please set the WHATSAPP_PHONE_NUMBER_ID environment variable."
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Optional default recipient
|
|
44
|
+
self.default_recipient = recipient_waid or getenv("WHATSAPP_RECIPIENT_WAID")
|
|
45
|
+
|
|
46
|
+
# API version and mode
|
|
47
|
+
self.version = version or getenv("WHATSAPP_VERSION", "v22.0")
|
|
48
|
+
self.async_mode = async_mode
|
|
49
|
+
|
|
50
|
+
tools: List[Any] = []
|
|
51
|
+
if self.async_mode:
|
|
52
|
+
tools.append(self.send_text_message_async)
|
|
53
|
+
tools.append(self.send_template_message_async)
|
|
54
|
+
else:
|
|
55
|
+
tools.append(self.send_text_message_sync)
|
|
56
|
+
tools.append(self.send_template_message_sync)
|
|
57
|
+
|
|
58
|
+
super().__init__(name="whatsapp", tools=tools)
|
|
59
|
+
|
|
60
|
+
def _get_headers(self) -> Dict[str, str]:
|
|
61
|
+
"""Get headers for API requests."""
|
|
62
|
+
return {"Authorization": f"Bearer {self.access_token}", "Content-Type": "application/json"}
|
|
63
|
+
|
|
64
|
+
def _get_messages_url(self) -> str:
|
|
65
|
+
"""Get the messages endpoint URL."""
|
|
66
|
+
return f"{self.base_url}/{self.version}/{self.phone_number_id}/messages"
|
|
67
|
+
|
|
68
|
+
async def _send_message_async(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
69
|
+
"""Send a message asynchronously using the WhatsApp API.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
data: Message data to send
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
API response as dictionary
|
|
76
|
+
"""
|
|
77
|
+
url = self._get_messages_url()
|
|
78
|
+
headers = self._get_headers()
|
|
79
|
+
|
|
80
|
+
logger.debug(f"Sending WhatsApp request to URL: {url}")
|
|
81
|
+
|
|
82
|
+
async with httpx.AsyncClient() as client:
|
|
83
|
+
response = await client.post(url, headers=headers, json=data)
|
|
84
|
+
|
|
85
|
+
response.raise_for_status()
|
|
86
|
+
return response.json()
|
|
87
|
+
|
|
88
|
+
def _send_message_sync(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
89
|
+
"""Send a message synchronously using the WhatsApp API.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
data: Message data to send
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
API response as dictionary
|
|
96
|
+
"""
|
|
97
|
+
url = self._get_messages_url()
|
|
98
|
+
headers = self._get_headers()
|
|
99
|
+
|
|
100
|
+
logger.debug(f"Sending WhatsApp request to URL: {url}")
|
|
101
|
+
response = httpx.post(url, headers=headers, json=data)
|
|
102
|
+
|
|
103
|
+
response.raise_for_status()
|
|
104
|
+
return response.json()
|
|
105
|
+
|
|
106
|
+
def send_text_message_sync(
|
|
107
|
+
self,
|
|
108
|
+
text: str = "",
|
|
109
|
+
recipient: Optional[str] = None,
|
|
110
|
+
preview_url: bool = False,
|
|
111
|
+
recipient_type: str = "individual",
|
|
112
|
+
) -> str:
|
|
113
|
+
"""Send a text message to a WhatsApp user (synchronous version).
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
text: The text message to send
|
|
117
|
+
recipient: Recipient's WhatsApp ID or phone number (e.g., "+1234567890"). If not provided, uses default_recipient
|
|
118
|
+
preview_url: Whether to generate previews for links in the message
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Success message with message ID
|
|
122
|
+
"""
|
|
123
|
+
# Use default recipient if none provided
|
|
124
|
+
if recipient is None:
|
|
125
|
+
if not self.default_recipient:
|
|
126
|
+
raise ValueError("No recipient provided and no default recipient set")
|
|
127
|
+
recipient = self.default_recipient
|
|
128
|
+
logger.debug(f"Using default recipient: {recipient}")
|
|
129
|
+
|
|
130
|
+
logger.debug(f"Sending WhatsApp message to {recipient}: {text}")
|
|
131
|
+
logger.debug(f"Current config - Phone Number ID: {self.phone_number_id}, Version: {self.version}")
|
|
132
|
+
|
|
133
|
+
data = {
|
|
134
|
+
"messaging_product": "whatsapp",
|
|
135
|
+
"recipient_type": recipient_type,
|
|
136
|
+
"to": recipient,
|
|
137
|
+
"type": "text",
|
|
138
|
+
"text": {"preview_url": preview_url, "body": text},
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
response = self._send_message_sync(data)
|
|
143
|
+
message_id = response.get("messages", [{}])[0].get("id", "unknown")
|
|
144
|
+
return f"Message sent successfully! Message ID: {message_id}"
|
|
145
|
+
except httpx.HTTPStatusError as e:
|
|
146
|
+
logger.error(f"Failed to send WhatsApp message: {e}")
|
|
147
|
+
logger.error(f"Error response: {e.response.text if hasattr(e, 'response') else 'No response text'}")
|
|
148
|
+
raise
|
|
149
|
+
except Exception as e:
|
|
150
|
+
logger.error(f"Unexpected error sending WhatsApp message: {str(e)}")
|
|
151
|
+
raise
|
|
152
|
+
|
|
153
|
+
def send_template_message_sync(
|
|
154
|
+
self,
|
|
155
|
+
recipient: Optional[str] = None,
|
|
156
|
+
template_name: str = "",
|
|
157
|
+
language_code: str = "en_US",
|
|
158
|
+
components: Optional[List[Dict[str, Any]]] = None,
|
|
159
|
+
) -> str:
|
|
160
|
+
"""Send a template message to a WhatsApp user (synchronous version).
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
recipient: Recipient's WhatsApp ID or phone number (e.g., "+1234567890"). If not provided, uses default_recipient
|
|
164
|
+
template_name: Name of the template to use
|
|
165
|
+
language_code: Language code for the template (e.g., "en_US")
|
|
166
|
+
components: Optional list of template components (header, body, buttons)
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Success message with message ID
|
|
170
|
+
"""
|
|
171
|
+
# Use default recipient if none provided
|
|
172
|
+
if recipient is None:
|
|
173
|
+
if not self.default_recipient:
|
|
174
|
+
raise ValueError("No recipient provided and no default recipient set")
|
|
175
|
+
recipient = self.default_recipient
|
|
176
|
+
|
|
177
|
+
logger.debug(f"Sending WhatsApp template message to {recipient}: {template_name}")
|
|
178
|
+
|
|
179
|
+
data = {
|
|
180
|
+
"messaging_product": "whatsapp",
|
|
181
|
+
"to": recipient,
|
|
182
|
+
"type": "template",
|
|
183
|
+
"template": {"name": template_name, "language": {"code": language_code}},
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if components:
|
|
187
|
+
data["template"]["components"] = components # type: ignore[index]
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
response = self._send_message_sync(data)
|
|
191
|
+
message_id = response.get("messages", [{}])[0].get("id", "unknown")
|
|
192
|
+
return f"Template message sent successfully! Message ID: {message_id}"
|
|
193
|
+
except httpx.HTTPStatusError as e:
|
|
194
|
+
logger.error(f"Failed to send WhatsApp template message: {e}")
|
|
195
|
+
raise
|
|
196
|
+
|
|
197
|
+
async def send_text_message_async(
|
|
198
|
+
self,
|
|
199
|
+
text: str = "",
|
|
200
|
+
recipient: Optional[str] = None,
|
|
201
|
+
preview_url: bool = False,
|
|
202
|
+
recipient_type: str = "individual",
|
|
203
|
+
) -> str:
|
|
204
|
+
"""Send a text message to a WhatsApp user (asynchronous version).
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
text: The text message to send
|
|
208
|
+
recipient: Recipient's WhatsApp ID or phone number (e.g., "+1234567890"). If not provided, uses default_recipient
|
|
209
|
+
preview_url: Whether to generate previews for links in the message
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
Success message with message ID
|
|
213
|
+
"""
|
|
214
|
+
# Use default recipient if none provided
|
|
215
|
+
if recipient is None:
|
|
216
|
+
if not self.default_recipient:
|
|
217
|
+
raise ValueError("No recipient provided and no default recipient set")
|
|
218
|
+
recipient = self.default_recipient
|
|
219
|
+
logger.debug(f"Using default recipient: {recipient}")
|
|
220
|
+
|
|
221
|
+
logger.debug(f"Sending WhatsApp message to {recipient}: {text}")
|
|
222
|
+
logger.debug(f"Current config - Phone Number ID: {self.phone_number_id}, Version: {self.version}")
|
|
223
|
+
|
|
224
|
+
data = {
|
|
225
|
+
"messaging_product": "whatsapp",
|
|
226
|
+
"recipient_type": recipient_type,
|
|
227
|
+
"to": recipient,
|
|
228
|
+
"type": "text",
|
|
229
|
+
"text": {"preview_url": preview_url, "body": text},
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
response = await self._send_message_async(data)
|
|
234
|
+
message_id = response.get("messages", [{}])[0].get("id", "unknown")
|
|
235
|
+
return f"Message sent successfully! Message ID: {message_id}"
|
|
236
|
+
except httpx.HTTPStatusError as e:
|
|
237
|
+
logger.error(f"Failed to send WhatsApp message: {e}")
|
|
238
|
+
logger.error(f"Error response: {e.response.text if hasattr(e, 'response') else 'No response text'}")
|
|
239
|
+
raise
|
|
240
|
+
except Exception as e:
|
|
241
|
+
logger.error(f"Unexpected error sending WhatsApp message: {str(e)}")
|
|
242
|
+
raise
|
|
243
|
+
|
|
244
|
+
async def send_template_message_async(
|
|
245
|
+
self,
|
|
246
|
+
recipient: Optional[str] = None,
|
|
247
|
+
template_name: str = "",
|
|
248
|
+
language_code: str = "en_US",
|
|
249
|
+
components: Optional[List[Dict[str, Any]]] = None,
|
|
250
|
+
) -> str:
|
|
251
|
+
"""Send a template message to a WhatsApp user (asynchronous version).
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
recipient: Recipient's WhatsApp ID or phone number (e.g., "+1234567890"). If not provided, uses default_recipient
|
|
255
|
+
template_name: Name of the template to use
|
|
256
|
+
language_code: Language code for the template (e.g., "en_US")
|
|
257
|
+
components: Optional list of template components (header, body, buttons)
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
Success message with message ID
|
|
261
|
+
"""
|
|
262
|
+
# Use default recipient if none provided
|
|
263
|
+
if recipient is None:
|
|
264
|
+
if not self.default_recipient:
|
|
265
|
+
raise ValueError("No recipient provided and no default recipient set")
|
|
266
|
+
recipient = self.default_recipient
|
|
267
|
+
|
|
268
|
+
logger.debug(f"Sending WhatsApp template message to {recipient}: {template_name}")
|
|
269
|
+
|
|
270
|
+
data = {
|
|
271
|
+
"messaging_product": "whatsapp",
|
|
272
|
+
"to": recipient,
|
|
273
|
+
"type": "template",
|
|
274
|
+
"template": {"name": template_name, "language": {"code": language_code}},
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if components:
|
|
278
|
+
data["template"]["components"] = components # type: ignore[index]
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
response = await self._send_message_async(data)
|
|
282
|
+
message_id = response.get("messages", [{}])[0].get("id", "unknown")
|
|
283
|
+
return f"Template message sent successfully! Message ID: {message_id}"
|
|
284
|
+
except httpx.HTTPStatusError as e:
|
|
285
|
+
logger.error(f"Failed to send WhatsApp template message: {e}")
|
|
286
|
+
raise
|
agno/tools/wikipedia.py
CHANGED
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from typing import List, Optional
|
|
3
3
|
|
|
4
|
-
from agno.document import Document
|
|
5
|
-
from agno.knowledge.
|
|
4
|
+
from agno.knowledge.document import Document
|
|
5
|
+
from agno.knowledge.knowledge import Knowledge
|
|
6
|
+
from agno.knowledge.reader.wikipedia_reader import WikipediaReader
|
|
6
7
|
from agno.tools import Toolkit
|
|
7
|
-
from agno.utils.log import
|
|
8
|
+
from agno.utils.log import log_debug, log_info
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class WikipediaTools(Toolkit):
|
|
11
|
-
def __init__(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
knowledge: Optional[Knowledge] = None,
|
|
15
|
+
all: bool = False,
|
|
16
|
+
**kwargs,
|
|
17
|
+
):
|
|
18
|
+
tools = []
|
|
19
|
+
|
|
20
|
+
self.knowledge: Optional[Knowledge] = knowledge
|
|
21
|
+
if self.knowledge is not None and isinstance(self.knowledge, Knowledge):
|
|
22
|
+
tools.append(self.search_wikipedia_and_update_knowledge_base)
|
|
17
23
|
else:
|
|
18
|
-
|
|
24
|
+
tools.append(self.search_wikipedia) # type: ignore
|
|
25
|
+
|
|
26
|
+
super().__init__(name="wikipedia_tools", tools=tools, **kwargs)
|
|
19
27
|
|
|
20
28
|
def search_wikipedia_and_update_knowledge_base(self, topic: str) -> str:
|
|
21
29
|
"""This function searches wikipedia for a topic, adds the results to the knowledge base and returns them.
|
|
@@ -26,15 +34,16 @@ class WikipediaTools(Toolkit):
|
|
|
26
34
|
:return: Relevant documents from Wikipedia knowledge base.
|
|
27
35
|
"""
|
|
28
36
|
|
|
29
|
-
if self.
|
|
30
|
-
return "Knowledge
|
|
37
|
+
if self.knowledge is None:
|
|
38
|
+
return "Knowledge not provided"
|
|
31
39
|
|
|
32
|
-
|
|
33
|
-
self.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
log_debug(f"Adding to knowledge: {topic}")
|
|
41
|
+
self.knowledge.add_content(
|
|
42
|
+
topics=[topic],
|
|
43
|
+
reader=WikipediaReader(),
|
|
44
|
+
)
|
|
45
|
+
log_debug(f"Searching knowledge: {topic}")
|
|
46
|
+
relevant_docs: List[Document] = self.knowledge.search(query=topic)
|
|
38
47
|
return json.dumps([doc.to_dict() for doc in relevant_docs])
|
|
39
48
|
|
|
40
49
|
def search_wikipedia(self, query: str) -> str:
|
|
@@ -50,5 +59,5 @@ class WikipediaTools(Toolkit):
|
|
|
50
59
|
"The `wikipedia` package is not installed. Please install it via `pip install wikipedia`."
|
|
51
60
|
)
|
|
52
61
|
|
|
53
|
-
|
|
62
|
+
log_info(f"Searching wikipedia for: {query}")
|
|
54
63
|
return json.dumps(Document(name=query, content=wikipedia.summary(query)).to_dict())
|
agno/tools/workflow.py
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from textwrap import dedent
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from agno.tools import Toolkit
|
|
8
|
+
from agno.utils.log import log_debug, log_error
|
|
9
|
+
from agno.workflow.workflow import Workflow, WorkflowRunOutput
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RunWorkflowInput(BaseModel):
|
|
13
|
+
input_data: str = Field(..., description="The input data for the workflow.")
|
|
14
|
+
additional_data: Optional[Dict[str, Any]] = Field(default=None, description="The additional data for the workflow.")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class WorkflowTools(Toolkit):
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
workflow: Workflow,
|
|
21
|
+
enable_run_workflow: bool = True,
|
|
22
|
+
enable_think: bool = False,
|
|
23
|
+
enable_analyze: bool = False,
|
|
24
|
+
all: bool = False,
|
|
25
|
+
instructions: Optional[str] = None,
|
|
26
|
+
add_instructions: bool = True,
|
|
27
|
+
add_few_shot: bool = False,
|
|
28
|
+
few_shot_examples: Optional[str] = None,
|
|
29
|
+
async_mode: bool = False,
|
|
30
|
+
**kwargs,
|
|
31
|
+
):
|
|
32
|
+
# Add instructions for using this toolkit
|
|
33
|
+
if instructions is None:
|
|
34
|
+
self.instructions = self.DEFAULT_INSTRUCTIONS
|
|
35
|
+
if add_few_shot:
|
|
36
|
+
if few_shot_examples is not None:
|
|
37
|
+
self.instructions += "\n" + few_shot_examples
|
|
38
|
+
else:
|
|
39
|
+
self.instructions = instructions
|
|
40
|
+
|
|
41
|
+
# The workflow to execute
|
|
42
|
+
self.workflow: Workflow = workflow
|
|
43
|
+
|
|
44
|
+
super().__init__(
|
|
45
|
+
name="workflow_tools",
|
|
46
|
+
instructions=self.instructions,
|
|
47
|
+
add_instructions=add_instructions,
|
|
48
|
+
auto_register=False,
|
|
49
|
+
**kwargs,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if enable_think or all:
|
|
53
|
+
if async_mode:
|
|
54
|
+
self.register(self.async_think, name="think")
|
|
55
|
+
else:
|
|
56
|
+
self.register(self.think, name="think")
|
|
57
|
+
if enable_run_workflow or all:
|
|
58
|
+
if async_mode:
|
|
59
|
+
self.register(self.async_run_workflow, name="run_workflow")
|
|
60
|
+
else:
|
|
61
|
+
self.register(self.run_workflow, name="run_workflow")
|
|
62
|
+
if enable_analyze or all:
|
|
63
|
+
if async_mode:
|
|
64
|
+
self.register(self.async_analyze, name="analyze")
|
|
65
|
+
else:
|
|
66
|
+
self.register(self.analyze, name="analyze")
|
|
67
|
+
|
|
68
|
+
def think(self, session_state: Dict[str, Any], thought: str) -> str:
|
|
69
|
+
"""Use this tool as a scratchpad to reason about the workflow execution, refine your approach, brainstorm workflow inputs, or revise your plan.
|
|
70
|
+
Call `Think` whenever you need to figure out what to do next, analyze the user's requirements, plan workflow inputs, or decide on execution strategy.
|
|
71
|
+
You should use this tool as frequently as needed.
|
|
72
|
+
Args:
|
|
73
|
+
thought: Your thought process and reasoning about workflow execution.
|
|
74
|
+
"""
|
|
75
|
+
try:
|
|
76
|
+
log_debug(f"Workflow Thought: {thought}")
|
|
77
|
+
|
|
78
|
+
# Add the thought to the session state
|
|
79
|
+
if session_state is None:
|
|
80
|
+
session_state = {}
|
|
81
|
+
if "workflow_thoughts" not in session_state:
|
|
82
|
+
session_state["workflow_thoughts"] = []
|
|
83
|
+
session_state["workflow_thoughts"].append(thought)
|
|
84
|
+
|
|
85
|
+
# Return the full log of thoughts and the new thought
|
|
86
|
+
thoughts = "\n".join([f"- {t}" for t in session_state["workflow_thoughts"]])
|
|
87
|
+
formatted_thoughts = dedent(
|
|
88
|
+
f"""Workflow Thoughts:
|
|
89
|
+
{thoughts}
|
|
90
|
+
"""
|
|
91
|
+
).strip()
|
|
92
|
+
return formatted_thoughts
|
|
93
|
+
except Exception as e:
|
|
94
|
+
log_error(f"Error recording workflow thought: {e}")
|
|
95
|
+
return f"Error recording workflow thought: {e}"
|
|
96
|
+
|
|
97
|
+
async def async_think(self, session_state: Dict[str, Any], thought: str) -> str:
|
|
98
|
+
"""Use this tool as a scratchpad to reason about the workflow execution, refine your approach, brainstorm workflow inputs, or revise your plan.
|
|
99
|
+
Call `Think` whenever you need to figure out what to do next, analyze the user's requirements, plan workflow inputs, or decide on execution strategy.
|
|
100
|
+
You should use this tool as frequently as needed.
|
|
101
|
+
Args:
|
|
102
|
+
thought: Your thought process and reasoning about workflow execution.
|
|
103
|
+
"""
|
|
104
|
+
try:
|
|
105
|
+
log_debug(f"Workflow Thought: {thought}")
|
|
106
|
+
|
|
107
|
+
# Add the thought to the session state
|
|
108
|
+
if session_state is None:
|
|
109
|
+
session_state = {}
|
|
110
|
+
if "workflow_thoughts" not in session_state:
|
|
111
|
+
session_state["workflow_thoughts"] = []
|
|
112
|
+
session_state["workflow_thoughts"].append(thought)
|
|
113
|
+
|
|
114
|
+
# Return the full log of thoughts and the new thought
|
|
115
|
+
thoughts = "\n".join([f"- {t}" for t in session_state["workflow_thoughts"]])
|
|
116
|
+
formatted_thoughts = dedent(
|
|
117
|
+
f"""Workflow Thoughts:
|
|
118
|
+
{thoughts}
|
|
119
|
+
"""
|
|
120
|
+
).strip()
|
|
121
|
+
return formatted_thoughts
|
|
122
|
+
except Exception as e:
|
|
123
|
+
log_error(f"Error recording workflow thought: {e}")
|
|
124
|
+
return f"Error recording workflow thought: {e}"
|
|
125
|
+
|
|
126
|
+
def run_workflow(
|
|
127
|
+
self,
|
|
128
|
+
session_state: Dict[str, Any],
|
|
129
|
+
input: RunWorkflowInput,
|
|
130
|
+
) -> str:
|
|
131
|
+
"""Use this tool to execute the workflow with the specified inputs and parameters.
|
|
132
|
+
After thinking through the requirements, use this tool to run the workflow with appropriate inputs.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
input: The input data for the workflow.
|
|
136
|
+
"""
|
|
137
|
+
if isinstance(input, dict):
|
|
138
|
+
input = RunWorkflowInput.model_validate(input)
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
log_debug(f"Running workflow with input: {input.input_data}")
|
|
142
|
+
|
|
143
|
+
user_id = session_state.get("current_user_id")
|
|
144
|
+
session_id = session_state.get("current_session_id")
|
|
145
|
+
|
|
146
|
+
# Execute the workflow
|
|
147
|
+
result: WorkflowRunOutput = self.workflow.run(
|
|
148
|
+
input=input.input_data,
|
|
149
|
+
user_id=user_id,
|
|
150
|
+
session_id=session_id,
|
|
151
|
+
session_state=session_state,
|
|
152
|
+
additional_data=input.additional_data,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
if "workflow_results" not in session_state:
|
|
156
|
+
session_state["workflow_results"] = []
|
|
157
|
+
|
|
158
|
+
session_state["workflow_results"].append(result.to_dict())
|
|
159
|
+
|
|
160
|
+
return json.dumps(result.to_dict(), indent=2)
|
|
161
|
+
|
|
162
|
+
except Exception as e:
|
|
163
|
+
log_error(f"Error running workflow: {e}")
|
|
164
|
+
return f"Error running workflow: {e}"
|
|
165
|
+
|
|
166
|
+
async def async_run_workflow(
|
|
167
|
+
self,
|
|
168
|
+
session_state: Dict[str, Any],
|
|
169
|
+
input: RunWorkflowInput,
|
|
170
|
+
) -> str:
|
|
171
|
+
"""Use this tool to execute the workflow with the specified inputs and parameters.
|
|
172
|
+
After thinking through the requirements, use this tool to run the workflow with appropriate inputs.
|
|
173
|
+
Args:
|
|
174
|
+
input_data: The input data for the workflow (use a `str` for a simple input)
|
|
175
|
+
additional_data: The additional data for the workflow. This is a dictionary of key-value pairs that will be passed to the workflow. E.g. {"topic": "food", "style": "Humour"}
|
|
176
|
+
"""
|
|
177
|
+
if isinstance(input, dict):
|
|
178
|
+
input = RunWorkflowInput.model_validate(input)
|
|
179
|
+
|
|
180
|
+
try:
|
|
181
|
+
log_debug(f"Running workflow with input: {input.input_data}")
|
|
182
|
+
|
|
183
|
+
user_id = session_state.get("current_user_id")
|
|
184
|
+
session_id = session_state.get("current_session_id")
|
|
185
|
+
|
|
186
|
+
# Execute the workflow
|
|
187
|
+
result: WorkflowRunOutput = await self.workflow.arun(
|
|
188
|
+
input=input.input_data,
|
|
189
|
+
user_id=user_id,
|
|
190
|
+
session_id=session_id,
|
|
191
|
+
session_state=session_state,
|
|
192
|
+
additional_data=input.additional_data,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
if "workflow_results" not in session_state:
|
|
196
|
+
session_state["workflow_results"] = []
|
|
197
|
+
|
|
198
|
+
session_state["workflow_results"].append(result.to_dict())
|
|
199
|
+
|
|
200
|
+
return json.dumps(result.to_dict(), indent=2)
|
|
201
|
+
|
|
202
|
+
except Exception as e:
|
|
203
|
+
log_error(f"Error running workflow: {e}")
|
|
204
|
+
return f"Error running workflow: {e}"
|
|
205
|
+
|
|
206
|
+
def analyze(self, session_state: Dict[str, Any], analysis: str) -> str:
|
|
207
|
+
"""Use this tool to evaluate whether the workflow execution results are correct and sufficient.
|
|
208
|
+
If not, go back to "Think" or "Run" with refined inputs or parameters.
|
|
209
|
+
Args:
|
|
210
|
+
analysis: Your analysis of the workflow execution results.
|
|
211
|
+
"""
|
|
212
|
+
try:
|
|
213
|
+
log_debug(f"Workflow Analysis: {analysis}")
|
|
214
|
+
|
|
215
|
+
# Add the analysis to the session state
|
|
216
|
+
if session_state is None:
|
|
217
|
+
session_state = {}
|
|
218
|
+
if "workflow_analysis" not in session_state:
|
|
219
|
+
session_state["workflow_analysis"] = []
|
|
220
|
+
session_state["workflow_analysis"].append(analysis)
|
|
221
|
+
|
|
222
|
+
# Return the full log of analysis and the new analysis
|
|
223
|
+
analysis_log = "\n".join([f"- {a}" for a in session_state["workflow_analysis"]])
|
|
224
|
+
formatted_analysis = dedent(
|
|
225
|
+
f"""Workflow Analysis:
|
|
226
|
+
{analysis_log}
|
|
227
|
+
"""
|
|
228
|
+
).strip()
|
|
229
|
+
return formatted_analysis
|
|
230
|
+
except Exception as e:
|
|
231
|
+
log_error(f"Error recording workflow analysis: {e}")
|
|
232
|
+
return f"Error recording workflow analysis: {e}"
|
|
233
|
+
|
|
234
|
+
async def async_analyze(self, session_state: Dict[str, Any], analysis: str) -> str:
|
|
235
|
+
"""Use this tool to evaluate whether the workflow execution results are correct and sufficient.
|
|
236
|
+
If not, go back to "Think" or "Run" with refined inputs or parameters.
|
|
237
|
+
Args:
|
|
238
|
+
analysis: Your analysis of the workflow execution results.
|
|
239
|
+
"""
|
|
240
|
+
try:
|
|
241
|
+
log_debug(f"Workflow Analysis: {analysis}")
|
|
242
|
+
|
|
243
|
+
# Add the analysis to the session state
|
|
244
|
+
if session_state is None:
|
|
245
|
+
session_state = {}
|
|
246
|
+
if "workflow_analysis" not in session_state:
|
|
247
|
+
session_state["workflow_analysis"] = []
|
|
248
|
+
session_state["workflow_analysis"].append(analysis)
|
|
249
|
+
|
|
250
|
+
# Return the full log of analysis and the new analysis
|
|
251
|
+
analysis_log = "\n".join([f"- {a}" for a in session_state["workflow_analysis"]])
|
|
252
|
+
formatted_analysis = dedent(
|
|
253
|
+
f"""Workflow Analysis:
|
|
254
|
+
{analysis_log}
|
|
255
|
+
"""
|
|
256
|
+
).strip()
|
|
257
|
+
return formatted_analysis
|
|
258
|
+
except Exception as e:
|
|
259
|
+
log_error(f"Error recording workflow analysis: {e}")
|
|
260
|
+
return f"Error recording workflow analysis: {e}"
|
|
261
|
+
|
|
262
|
+
DEFAULT_INSTRUCTIONS = dedent("""\
|
|
263
|
+
You have access to the Think, Run Workflow, and Analyze tools that will help you execute workflows and analyze their results. Use these tools as frequently as needed to successfully complete workflow-based tasks.
|
|
264
|
+
## How to use the Think, Run Workflow, and Analyze tools:
|
|
265
|
+
|
|
266
|
+
1. **Think**
|
|
267
|
+
- Purpose: A scratchpad for planning workflow execution, brainstorming inputs, and refining your approach. You never reveal your "Think" content to the user.
|
|
268
|
+
- Usage: Call `think` whenever you need to figure out what workflow inputs to use, analyze requirements, or decide on execution strategy before (or after) you run the workflow.
|
|
269
|
+
2. **Run Workflow**
|
|
270
|
+
- Purpose: Executes the workflow with specified inputs and parameters.
|
|
271
|
+
- Usage: Call `run_workflow` with appropriate input data whenever you want to execute the workflow.
|
|
272
|
+
- For all workflows, start with simple inputs and gradually increase complexity
|
|
273
|
+
3. **Analyze**
|
|
274
|
+
- Purpose: Evaluate whether the workflow execution results are correct and sufficient. If not, go back to "Think" or "Run Workflow" with refined inputs.
|
|
275
|
+
- Usage: Call `analyze` after getting workflow results to verify the quality and correctness of the execution. Consider:
|
|
276
|
+
- Completeness: Did the workflow complete all expected steps?
|
|
277
|
+
- Quality: Are the results accurate and meet the requirements?
|
|
278
|
+
- Errors: Were there any failures or unexpected behaviors?
|
|
279
|
+
**Important Guidelines**:
|
|
280
|
+
- Do not include your internal chain-of-thought in direct user responses.
|
|
281
|
+
- Use "Think" to reason internally. These notes are never exposed to the user.
|
|
282
|
+
- When you provide a final answer to the user, be clear, concise, and based on the workflow results.
|
|
283
|
+
- If workflow execution fails or produces unexpected results, acknowledge limitations and explain what went wrong.
|
|
284
|
+
- Synthesize information from multiple workflow runs if you execute the workflow several times with different inputs.\
|
|
285
|
+
""")
|