agno 2.2.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/__init__.py +8 -0
- agno/agent/__init__.py +51 -0
- agno/agent/agent.py +10405 -0
- agno/api/__init__.py +0 -0
- agno/api/agent.py +28 -0
- agno/api/api.py +40 -0
- agno/api/evals.py +22 -0
- agno/api/os.py +17 -0
- agno/api/routes.py +13 -0
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +16 -0
- agno/api/schemas/evals.py +16 -0
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/response.py +6 -0
- agno/api/schemas/team.py +16 -0
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +16 -0
- agno/api/settings.py +53 -0
- agno/api/team.py +30 -0
- agno/api/workflow.py +28 -0
- agno/cloud/aws/base.py +214 -0
- agno/cloud/aws/s3/__init__.py +2 -0
- agno/cloud/aws/s3/api_client.py +43 -0
- agno/cloud/aws/s3/bucket.py +195 -0
- agno/cloud/aws/s3/object.py +57 -0
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/__init__.py +24 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +598 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +2042 -0
- agno/db/dynamo/schemas.py +314 -0
- agno/db/dynamo/utils.py +743 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +1795 -0
- agno/db/firestore/schemas.py +140 -0
- agno/db/firestore/utils.py +376 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1335 -0
- agno/db/gcs_json/utils.py +228 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +1160 -0
- agno/db/in_memory/utils.py +230 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1328 -0
- agno/db/json/utils.py +230 -0
- agno/db/migrations/__init__.py +0 -0
- agno/db/migrations/v1_to_v2.py +635 -0
- agno/db/mongo/__init__.py +17 -0
- agno/db/mongo/async_mongo.py +2026 -0
- agno/db/mongo/mongo.py +1982 -0
- agno/db/mongo/schemas.py +87 -0
- agno/db/mongo/utils.py +259 -0
- agno/db/mysql/__init__.py +3 -0
- agno/db/mysql/mysql.py +2308 -0
- agno/db/mysql/schemas.py +138 -0
- agno/db/mysql/utils.py +355 -0
- agno/db/postgres/__init__.py +4 -0
- agno/db/postgres/async_postgres.py +1927 -0
- agno/db/postgres/postgres.py +2260 -0
- agno/db/postgres/schemas.py +139 -0
- agno/db/postgres/utils.py +442 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +1660 -0
- agno/db/redis/schemas.py +123 -0
- agno/db/redis/utils.py +346 -0
- agno/db/schemas/__init__.py +4 -0
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +33 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +46 -0
- agno/db/schemas/metrics.py +0 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +130 -0
- agno/db/singlestore/singlestore.py +2272 -0
- agno/db/singlestore/utils.py +384 -0
- agno/db/sqlite/__init__.py +4 -0
- agno/db/sqlite/async_sqlite.py +2293 -0
- agno/db/sqlite/schemas.py +133 -0
- agno/db/sqlite/sqlite.py +2288 -0
- agno/db/sqlite/utils.py +431 -0
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +309 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1353 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +116 -0
- agno/debug.py +18 -0
- agno/eval/__init__.py +14 -0
- agno/eval/accuracy.py +834 -0
- agno/eval/performance.py +773 -0
- agno/eval/reliability.py +306 -0
- agno/eval/utils.py +119 -0
- agno/exceptions.py +161 -0
- agno/filters.py +354 -0
- agno/guardrails/__init__.py +6 -0
- agno/guardrails/base.py +19 -0
- agno/guardrails/openai.py +144 -0
- agno/guardrails/pii.py +94 -0
- agno/guardrails/prompt_injection.py +52 -0
- agno/integrations/__init__.py +0 -0
- agno/integrations/discord/__init__.py +3 -0
- agno/integrations/discord/client.py +203 -0
- agno/knowledge/__init__.py +5 -0
- agno/knowledge/chunking/__init__.py +0 -0
- agno/knowledge/chunking/agentic.py +79 -0
- agno/knowledge/chunking/document.py +91 -0
- agno/knowledge/chunking/fixed.py +57 -0
- agno/knowledge/chunking/markdown.py +151 -0
- agno/knowledge/chunking/recursive.py +63 -0
- agno/knowledge/chunking/row.py +39 -0
- agno/knowledge/chunking/semantic.py +86 -0
- agno/knowledge/chunking/strategy.py +165 -0
- agno/knowledge/content.py +74 -0
- agno/knowledge/document/__init__.py +5 -0
- agno/knowledge/document/base.py +58 -0
- agno/knowledge/embedder/__init__.py +5 -0
- agno/knowledge/embedder/aws_bedrock.py +343 -0
- agno/knowledge/embedder/azure_openai.py +210 -0
- agno/knowledge/embedder/base.py +23 -0
- agno/knowledge/embedder/cohere.py +323 -0
- agno/knowledge/embedder/fastembed.py +62 -0
- agno/knowledge/embedder/fireworks.py +13 -0
- agno/knowledge/embedder/google.py +258 -0
- agno/knowledge/embedder/huggingface.py +94 -0
- agno/knowledge/embedder/jina.py +182 -0
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +206 -0
- agno/knowledge/embedder/nebius.py +13 -0
- agno/knowledge/embedder/ollama.py +154 -0
- agno/knowledge/embedder/openai.py +195 -0
- agno/knowledge/embedder/sentence_transformer.py +63 -0
- agno/knowledge/embedder/together.py +13 -0
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +165 -0
- agno/knowledge/knowledge.py +1988 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/knowledge/reader/arxiv_reader.py +81 -0
- agno/knowledge/reader/base.py +95 -0
- agno/knowledge/reader/csv_reader.py +166 -0
- agno/knowledge/reader/docx_reader.py +82 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +292 -0
- agno/knowledge/reader/firecrawl_reader.py +201 -0
- agno/knowledge/reader/json_reader.py +87 -0
- agno/knowledge/reader/markdown_reader.py +137 -0
- agno/knowledge/reader/pdf_reader.py +431 -0
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +313 -0
- agno/knowledge/reader/s3_reader.py +89 -0
- agno/knowledge/reader/tavily_reader.py +194 -0
- agno/knowledge/reader/text_reader.py +115 -0
- agno/knowledge/reader/web_search_reader.py +372 -0
- agno/knowledge/reader/website_reader.py +455 -0
- agno/knowledge/reader/wikipedia_reader.py +59 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/__init__.py +0 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/knowledge/reranker/__init__.py +3 -0
- agno/knowledge/reranker/base.py +14 -0
- agno/knowledge/reranker/cohere.py +64 -0
- agno/knowledge/reranker/infinity.py +195 -0
- agno/knowledge/reranker/sentence_transformer.py +54 -0
- agno/knowledge/types.py +39 -0
- agno/knowledge/utils.py +189 -0
- agno/media.py +462 -0
- agno/memory/__init__.py +3 -0
- agno/memory/manager.py +1327 -0
- agno/models/__init__.py +0 -0
- agno/models/aimlapi/__init__.py +5 -0
- agno/models/aimlapi/aimlapi.py +45 -0
- agno/models/anthropic/__init__.py +5 -0
- agno/models/anthropic/claude.py +757 -0
- agno/models/aws/__init__.py +15 -0
- agno/models/aws/bedrock.py +701 -0
- agno/models/aws/claude.py +378 -0
- agno/models/azure/__init__.py +18 -0
- agno/models/azure/ai_foundry.py +485 -0
- agno/models/azure/openai_chat.py +131 -0
- agno/models/base.py +2175 -0
- agno/models/cerebras/__init__.py +12 -0
- agno/models/cerebras/cerebras.py +501 -0
- agno/models/cerebras/cerebras_openai.py +112 -0
- agno/models/cohere/__init__.py +5 -0
- agno/models/cohere/chat.py +389 -0
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +57 -0
- agno/models/dashscope/__init__.py +5 -0
- agno/models/dashscope/dashscope.py +91 -0
- agno/models/deepinfra/__init__.py +5 -0
- agno/models/deepinfra/deepinfra.py +28 -0
- agno/models/deepseek/__init__.py +5 -0
- agno/models/deepseek/deepseek.py +61 -0
- agno/models/defaults.py +1 -0
- agno/models/fireworks/__init__.py +5 -0
- agno/models/fireworks/fireworks.py +26 -0
- agno/models/google/__init__.py +5 -0
- agno/models/google/gemini.py +1085 -0
- agno/models/groq/__init__.py +5 -0
- agno/models/groq/groq.py +556 -0
- agno/models/huggingface/__init__.py +5 -0
- agno/models/huggingface/huggingface.py +491 -0
- agno/models/ibm/__init__.py +5 -0
- agno/models/ibm/watsonx.py +422 -0
- agno/models/internlm/__init__.py +3 -0
- agno/models/internlm/internlm.py +26 -0
- agno/models/langdb/__init__.py +1 -0
- agno/models/langdb/langdb.py +48 -0
- agno/models/litellm/__init__.py +14 -0
- agno/models/litellm/chat.py +468 -0
- agno/models/litellm/litellm_openai.py +25 -0
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/lmstudio/__init__.py +5 -0
- agno/models/lmstudio/lmstudio.py +25 -0
- agno/models/message.py +434 -0
- agno/models/meta/__init__.py +12 -0
- agno/models/meta/llama.py +475 -0
- agno/models/meta/llama_openai.py +78 -0
- agno/models/metrics.py +120 -0
- agno/models/mistral/__init__.py +5 -0
- agno/models/mistral/mistral.py +432 -0
- agno/models/nebius/__init__.py +3 -0
- agno/models/nebius/nebius.py +54 -0
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/__init__.py +5 -0
- agno/models/nvidia/nvidia.py +28 -0
- agno/models/ollama/__init__.py +5 -0
- agno/models/ollama/chat.py +441 -0
- agno/models/openai/__init__.py +9 -0
- agno/models/openai/chat.py +883 -0
- agno/models/openai/like.py +27 -0
- agno/models/openai/responses.py +1050 -0
- agno/models/openrouter/__init__.py +5 -0
- agno/models/openrouter/openrouter.py +66 -0
- agno/models/perplexity/__init__.py +5 -0
- agno/models/perplexity/perplexity.py +187 -0
- agno/models/portkey/__init__.py +3 -0
- agno/models/portkey/portkey.py +81 -0
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +52 -0
- agno/models/response.py +199 -0
- agno/models/sambanova/__init__.py +5 -0
- agno/models/sambanova/sambanova.py +28 -0
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +25 -0
- agno/models/together/__init__.py +5 -0
- agno/models/together/together.py +25 -0
- agno/models/utils.py +266 -0
- agno/models/vercel/__init__.py +3 -0
- agno/models/vercel/v0.py +26 -0
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +70 -0
- agno/models/vllm/__init__.py +3 -0
- agno/models/vllm/vllm.py +78 -0
- agno/models/xai/__init__.py +3 -0
- agno/models/xai/xai.py +113 -0
- agno/os/__init__.py +3 -0
- agno/os/app.py +876 -0
- agno/os/auth.py +57 -0
- agno/os/config.py +104 -0
- agno/os/interfaces/__init__.py +1 -0
- agno/os/interfaces/a2a/__init__.py +3 -0
- agno/os/interfaces/a2a/a2a.py +42 -0
- agno/os/interfaces/a2a/router.py +250 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +47 -0
- agno/os/interfaces/agui/router.py +144 -0
- agno/os/interfaces/agui/utils.py +534 -0
- agno/os/interfaces/base.py +25 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/os/interfaces/slack/router.py +148 -0
- agno/os/interfaces/slack/security.py +30 -0
- agno/os/interfaces/slack/slack.py +47 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/os/interfaces/whatsapp/router.py +211 -0
- agno/os/interfaces/whatsapp/security.py +53 -0
- agno/os/interfaces/whatsapp/whatsapp.py +36 -0
- agno/os/mcp.py +292 -0
- agno/os/middleware/__init__.py +7 -0
- agno/os/middleware/jwt.py +233 -0
- agno/os/router.py +1763 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +430 -0
- agno/os/routers/evals/schemas.py +142 -0
- agno/os/routers/evals/utils.py +162 -0
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +997 -0
- agno/os/routers/knowledge/schemas.py +178 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +515 -0
- agno/os/routers/memory/schemas.py +62 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +190 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +997 -0
- agno/os/schema.py +1055 -0
- agno/os/settings.py +43 -0
- agno/os/utils.py +630 -0
- agno/py.typed +0 -0
- agno/reasoning/__init__.py +0 -0
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +67 -0
- agno/reasoning/deepseek.py +63 -0
- agno/reasoning/default.py +97 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +71 -0
- agno/reasoning/helpers.py +63 -0
- agno/reasoning/ollama.py +67 -0
- agno/reasoning/openai.py +86 -0
- agno/reasoning/step.py +31 -0
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +787 -0
- agno/run/base.py +229 -0
- agno/run/cancel.py +81 -0
- agno/run/messages.py +32 -0
- agno/run/team.py +753 -0
- agno/run/workflow.py +708 -0
- agno/session/__init__.py +10 -0
- agno/session/agent.py +295 -0
- agno/session/summary.py +265 -0
- agno/session/team.py +392 -0
- agno/session/workflow.py +205 -0
- agno/team/__init__.py +37 -0
- agno/team/team.py +8793 -0
- agno/tools/__init__.py +10 -0
- agno/tools/agentql.py +120 -0
- agno/tools/airflow.py +69 -0
- agno/tools/api.py +122 -0
- agno/tools/apify.py +314 -0
- agno/tools/arxiv.py +127 -0
- agno/tools/aws_lambda.py +53 -0
- agno/tools/aws_ses.py +66 -0
- agno/tools/baidusearch.py +89 -0
- agno/tools/bitbucket.py +292 -0
- agno/tools/brandfetch.py +213 -0
- agno/tools/bravesearch.py +106 -0
- agno/tools/brightdata.py +367 -0
- agno/tools/browserbase.py +209 -0
- agno/tools/calcom.py +255 -0
- agno/tools/calculator.py +151 -0
- agno/tools/cartesia.py +187 -0
- agno/tools/clickup.py +244 -0
- agno/tools/confluence.py +240 -0
- agno/tools/crawl4ai.py +158 -0
- agno/tools/csv_toolkit.py +185 -0
- agno/tools/dalle.py +110 -0
- agno/tools/daytona.py +475 -0
- agno/tools/decorator.py +262 -0
- agno/tools/desi_vocal.py +108 -0
- agno/tools/discord.py +161 -0
- agno/tools/docker.py +716 -0
- agno/tools/duckdb.py +379 -0
- agno/tools/duckduckgo.py +91 -0
- agno/tools/e2b.py +703 -0
- agno/tools/eleven_labs.py +196 -0
- agno/tools/email.py +67 -0
- agno/tools/evm.py +129 -0
- agno/tools/exa.py +396 -0
- agno/tools/fal.py +127 -0
- agno/tools/file.py +240 -0
- agno/tools/file_generation.py +350 -0
- agno/tools/financial_datasets.py +288 -0
- agno/tools/firecrawl.py +143 -0
- agno/tools/function.py +1187 -0
- agno/tools/giphy.py +93 -0
- agno/tools/github.py +1760 -0
- agno/tools/gmail.py +922 -0
- agno/tools/google_bigquery.py +117 -0
- agno/tools/google_drive.py +270 -0
- agno/tools/google_maps.py +253 -0
- agno/tools/googlecalendar.py +674 -0
- agno/tools/googlesearch.py +98 -0
- agno/tools/googlesheets.py +377 -0
- agno/tools/hackernews.py +77 -0
- agno/tools/jina.py +101 -0
- agno/tools/jira.py +170 -0
- agno/tools/knowledge.py +218 -0
- agno/tools/linear.py +426 -0
- agno/tools/linkup.py +58 -0
- agno/tools/local_file_system.py +90 -0
- agno/tools/lumalab.py +183 -0
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +284 -0
- agno/tools/mem0.py +193 -0
- agno/tools/memori.py +339 -0
- agno/tools/memory.py +419 -0
- agno/tools/mlx_transcribe.py +139 -0
- agno/tools/models/__init__.py +0 -0
- agno/tools/models/azure_openai.py +190 -0
- agno/tools/models/gemini.py +203 -0
- agno/tools/models/groq.py +158 -0
- agno/tools/models/morph.py +186 -0
- agno/tools/models/nebius.py +124 -0
- agno/tools/models_labs.py +195 -0
- agno/tools/moviepy_video.py +349 -0
- agno/tools/neo4j.py +134 -0
- agno/tools/newspaper.py +46 -0
- agno/tools/newspaper4k.py +93 -0
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +202 -0
- agno/tools/openbb.py +160 -0
- agno/tools/opencv.py +321 -0
- agno/tools/openweather.py +233 -0
- agno/tools/oxylabs.py +385 -0
- agno/tools/pandas.py +102 -0
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +257 -0
- agno/tools/pubmed.py +188 -0
- agno/tools/python.py +205 -0
- agno/tools/reasoning.py +283 -0
- agno/tools/reddit.py +467 -0
- agno/tools/replicate.py +117 -0
- agno/tools/resend.py +62 -0
- agno/tools/scrapegraph.py +222 -0
- agno/tools/searxng.py +152 -0
- agno/tools/serpapi.py +116 -0
- agno/tools/serper.py +255 -0
- agno/tools/shell.py +53 -0
- agno/tools/slack.py +136 -0
- agno/tools/sleep.py +20 -0
- agno/tools/spider.py +116 -0
- agno/tools/sql.py +154 -0
- agno/tools/streamlit/__init__.py +0 -0
- agno/tools/streamlit/components.py +113 -0
- agno/tools/tavily.py +254 -0
- agno/tools/telegram.py +48 -0
- agno/tools/todoist.py +218 -0
- agno/tools/tool_registry.py +1 -0
- agno/tools/toolkit.py +146 -0
- agno/tools/trafilatura.py +388 -0
- agno/tools/trello.py +274 -0
- agno/tools/twilio.py +186 -0
- agno/tools/user_control_flow.py +78 -0
- agno/tools/valyu.py +228 -0
- agno/tools/visualization.py +467 -0
- agno/tools/webbrowser.py +28 -0
- agno/tools/webex.py +76 -0
- agno/tools/website.py +54 -0
- agno/tools/webtools.py +45 -0
- agno/tools/whatsapp.py +286 -0
- agno/tools/wikipedia.py +63 -0
- agno/tools/workflow.py +278 -0
- agno/tools/x.py +335 -0
- agno/tools/yfinance.py +257 -0
- agno/tools/youtube.py +184 -0
- agno/tools/zendesk.py +82 -0
- agno/tools/zep.py +454 -0
- agno/tools/zoom.py +382 -0
- agno/utils/__init__.py +0 -0
- agno/utils/agent.py +820 -0
- agno/utils/audio.py +49 -0
- agno/utils/certs.py +27 -0
- agno/utils/code_execution.py +11 -0
- agno/utils/common.py +132 -0
- agno/utils/dttm.py +13 -0
- agno/utils/enum.py +22 -0
- agno/utils/env.py +11 -0
- agno/utils/events.py +696 -0
- agno/utils/format_str.py +16 -0
- agno/utils/functions.py +166 -0
- agno/utils/gemini.py +426 -0
- agno/utils/hooks.py +57 -0
- agno/utils/http.py +74 -0
- agno/utils/json_schema.py +234 -0
- agno/utils/knowledge.py +36 -0
- agno/utils/location.py +19 -0
- agno/utils/log.py +255 -0
- agno/utils/mcp.py +214 -0
- agno/utils/media.py +352 -0
- agno/utils/merge_dict.py +41 -0
- agno/utils/message.py +118 -0
- agno/utils/models/__init__.py +0 -0
- agno/utils/models/ai_foundry.py +43 -0
- agno/utils/models/claude.py +358 -0
- agno/utils/models/cohere.py +87 -0
- agno/utils/models/llama.py +78 -0
- agno/utils/models/mistral.py +98 -0
- agno/utils/models/openai_responses.py +140 -0
- agno/utils/models/schema_utils.py +153 -0
- agno/utils/models/watsonx.py +41 -0
- agno/utils/openai.py +257 -0
- agno/utils/pickle.py +32 -0
- agno/utils/pprint.py +178 -0
- agno/utils/print_response/__init__.py +0 -0
- agno/utils/print_response/agent.py +842 -0
- agno/utils/print_response/team.py +1724 -0
- agno/utils/print_response/workflow.py +1668 -0
- agno/utils/prompts.py +111 -0
- agno/utils/reasoning.py +108 -0
- agno/utils/response.py +163 -0
- agno/utils/response_iterator.py +17 -0
- agno/utils/safe_formatter.py +24 -0
- agno/utils/serialize.py +32 -0
- agno/utils/shell.py +22 -0
- agno/utils/streamlit.py +487 -0
- agno/utils/string.py +231 -0
- agno/utils/team.py +139 -0
- agno/utils/timer.py +41 -0
- agno/utils/tools.py +102 -0
- agno/utils/web.py +23 -0
- agno/utils/whatsapp.py +305 -0
- agno/utils/yaml_io.py +25 -0
- agno/vectordb/__init__.py +3 -0
- agno/vectordb/base.py +127 -0
- agno/vectordb/cassandra/__init__.py +5 -0
- agno/vectordb/cassandra/cassandra.py +501 -0
- agno/vectordb/cassandra/extra_param_mixin.py +11 -0
- agno/vectordb/cassandra/index.py +13 -0
- agno/vectordb/chroma/__init__.py +5 -0
- agno/vectordb/chroma/chromadb.py +929 -0
- agno/vectordb/clickhouse/__init__.py +9 -0
- agno/vectordb/clickhouse/clickhousedb.py +835 -0
- agno/vectordb/clickhouse/index.py +9 -0
- agno/vectordb/couchbase/__init__.py +3 -0
- agno/vectordb/couchbase/couchbase.py +1442 -0
- agno/vectordb/distance.py +7 -0
- agno/vectordb/lancedb/__init__.py +6 -0
- agno/vectordb/lancedb/lance_db.py +995 -0
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +163 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +388 -0
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +166 -0
- agno/vectordb/milvus/__init__.py +4 -0
- agno/vectordb/milvus/milvus.py +1182 -0
- agno/vectordb/mongodb/__init__.py +9 -0
- agno/vectordb/mongodb/mongodb.py +1417 -0
- agno/vectordb/pgvector/__init__.py +12 -0
- agno/vectordb/pgvector/index.py +23 -0
- agno/vectordb/pgvector/pgvector.py +1462 -0
- agno/vectordb/pineconedb/__init__.py +5 -0
- agno/vectordb/pineconedb/pineconedb.py +747 -0
- agno/vectordb/qdrant/__init__.py +5 -0
- agno/vectordb/qdrant/qdrant.py +1134 -0
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +694 -0
- agno/vectordb/search.py +7 -0
- agno/vectordb/singlestore/__init__.py +10 -0
- agno/vectordb/singlestore/index.py +41 -0
- agno/vectordb/singlestore/singlestore.py +763 -0
- agno/vectordb/surrealdb/__init__.py +3 -0
- agno/vectordb/surrealdb/surrealdb.py +699 -0
- agno/vectordb/upstashdb/__init__.py +5 -0
- agno/vectordb/upstashdb/upstashdb.py +718 -0
- agno/vectordb/weaviate/__init__.py +8 -0
- agno/vectordb/weaviate/index.py +15 -0
- agno/vectordb/weaviate/weaviate.py +1005 -0
- agno/workflow/__init__.py +23 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +738 -0
- agno/workflow/loop.py +735 -0
- agno/workflow/parallel.py +824 -0
- agno/workflow/router.py +702 -0
- agno/workflow/step.py +1432 -0
- agno/workflow/steps.py +592 -0
- agno/workflow/types.py +520 -0
- agno/workflow/workflow.py +4321 -0
- agno-2.2.13.dist-info/METADATA +614 -0
- agno-2.2.13.dist-info/RECORD +575 -0
- agno-2.2.13.dist-info/WHEEL +5 -0
- agno-2.2.13.dist-info/licenses/LICENSE +201 -0
- agno-2.2.13.dist-info/top_level.txt +1 -0
agno/tools/pubmed.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
|
+
from xml.etree import ElementTree
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from agno.tools import Toolkit
|
|
8
|
+
from agno.utils.log import log_debug
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PubmedTools(Toolkit):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
email: str = "your_email@example.com",
|
|
15
|
+
max_results: Optional[int] = None,
|
|
16
|
+
results_expanded: bool = False,
|
|
17
|
+
enable_search_pubmed: bool = True,
|
|
18
|
+
all: bool = False,
|
|
19
|
+
**kwargs,
|
|
20
|
+
):
|
|
21
|
+
self.max_results: Optional[int] = max_results
|
|
22
|
+
self.email: str = email
|
|
23
|
+
self.results_expanded: bool = results_expanded
|
|
24
|
+
|
|
25
|
+
tools: List[Any] = []
|
|
26
|
+
if enable_search_pubmed or all:
|
|
27
|
+
tools.append(self.search_pubmed)
|
|
28
|
+
|
|
29
|
+
super().__init__(name="pubmed", tools=tools, **kwargs)
|
|
30
|
+
|
|
31
|
+
def fetch_pubmed_ids(self, query: str, max_results: int, email: str) -> List[str]:
|
|
32
|
+
url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi"
|
|
33
|
+
params = {
|
|
34
|
+
"db": "pubmed",
|
|
35
|
+
"term": query,
|
|
36
|
+
"retmax": max_results,
|
|
37
|
+
"email": email,
|
|
38
|
+
"usehistory": "y",
|
|
39
|
+
}
|
|
40
|
+
response = httpx.get(url, params=params) # type: ignore
|
|
41
|
+
root = ElementTree.fromstring(response.content)
|
|
42
|
+
return [id_elem.text for id_elem in root.findall(".//Id") if id_elem.text is not None]
|
|
43
|
+
|
|
44
|
+
def fetch_details(self, pubmed_ids: List[str]) -> ElementTree.Element:
|
|
45
|
+
url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi"
|
|
46
|
+
params = {"db": "pubmed", "id": ",".join(pubmed_ids), "retmode": "xml"}
|
|
47
|
+
response = httpx.get(url, params=params)
|
|
48
|
+
return ElementTree.fromstring(response.content)
|
|
49
|
+
|
|
50
|
+
def parse_details(self, xml_root: ElementTree.Element) -> List[Dict[str, Any]]:
|
|
51
|
+
articles = []
|
|
52
|
+
for article in xml_root.findall(".//PubmedArticle"):
|
|
53
|
+
# Get existing fields
|
|
54
|
+
pub_date = article.find(".//PubDate/Year")
|
|
55
|
+
title = article.find(".//ArticleTitle")
|
|
56
|
+
|
|
57
|
+
# Handle abstract sections with labels (methods, results, etc.)
|
|
58
|
+
abstract_sections = article.findall(".//AbstractText")
|
|
59
|
+
abstract_text = ""
|
|
60
|
+
if abstract_sections:
|
|
61
|
+
for section in abstract_sections:
|
|
62
|
+
label = section.get("Label", "")
|
|
63
|
+
if label:
|
|
64
|
+
abstract_text += f"{label}: {section.text}\n\n"
|
|
65
|
+
else:
|
|
66
|
+
abstract_text += f"{section.text}\n\n"
|
|
67
|
+
abstract_text = abstract_text.strip()
|
|
68
|
+
else:
|
|
69
|
+
abstract_text = "No abstract available"
|
|
70
|
+
|
|
71
|
+
# Get first author
|
|
72
|
+
first_author_elem = article.find(".//AuthorList/Author[1]")
|
|
73
|
+
first_author = "Unknown"
|
|
74
|
+
if first_author_elem is not None:
|
|
75
|
+
last_name = first_author_elem.find("LastName")
|
|
76
|
+
fore_name = first_author_elem.find("ForeName")
|
|
77
|
+
if last_name is not None and fore_name is not None:
|
|
78
|
+
first_author = f"{last_name.text}, {fore_name.text}"
|
|
79
|
+
elif last_name is not None and last_name.text:
|
|
80
|
+
first_author = last_name.text
|
|
81
|
+
|
|
82
|
+
# Get DOI
|
|
83
|
+
doi_elem = article.find(".//ArticleIdList/ArticleId[@IdType='doi']")
|
|
84
|
+
doi = doi_elem.text if doi_elem is not None else "No DOI available"
|
|
85
|
+
|
|
86
|
+
# Get PMID for URL construction
|
|
87
|
+
pmid_elem = article.find(".//PMID")
|
|
88
|
+
pmid = pmid_elem.text if pmid_elem is not None else ""
|
|
89
|
+
pubmed_url = f"https://pubmed.ncbi.nlm.nih.gov/{pmid}/" if pmid else "No URL available"
|
|
90
|
+
|
|
91
|
+
# Check if full text is available via PMC
|
|
92
|
+
pmc_elem = article.find(".//ArticleIdList/ArticleId[@IdType='pmc']")
|
|
93
|
+
full_text_url = "Not available"
|
|
94
|
+
if pmc_elem is not None:
|
|
95
|
+
full_text_url = f"https://www.ncbi.nlm.nih.gov/pmc/articles/{pmc_elem.text}/"
|
|
96
|
+
elif doi_elem is not None:
|
|
97
|
+
full_text_url = f"https://doi.org/{doi}"
|
|
98
|
+
|
|
99
|
+
# Get keywords
|
|
100
|
+
keywords = []
|
|
101
|
+
for keyword in article.findall(".//KeywordList/Keyword"):
|
|
102
|
+
if keyword.text:
|
|
103
|
+
keywords.append(keyword.text)
|
|
104
|
+
|
|
105
|
+
# Get MeSH terms (useful for understanding medical context)
|
|
106
|
+
mesh_terms = []
|
|
107
|
+
for mesh in article.findall(".//MeshHeading/DescriptorName"):
|
|
108
|
+
if mesh.text:
|
|
109
|
+
mesh_terms.append(mesh.text)
|
|
110
|
+
|
|
111
|
+
# Get journal info
|
|
112
|
+
journal_elem = article.find(".//Journal/Title")
|
|
113
|
+
journal = journal_elem.text if journal_elem is not None else "Unknown Journal"
|
|
114
|
+
|
|
115
|
+
# Publication type (research article, review, etc.)
|
|
116
|
+
pub_types = []
|
|
117
|
+
for pub_type in article.findall(".//PublicationTypeList/PublicationType"):
|
|
118
|
+
if pub_type.text:
|
|
119
|
+
pub_types.append(pub_type.text)
|
|
120
|
+
|
|
121
|
+
articles.append(
|
|
122
|
+
{
|
|
123
|
+
"Published": pub_date.text if pub_date is not None else "No date available",
|
|
124
|
+
"Title": title.text if title is not None else "No title available",
|
|
125
|
+
"Summary": abstract_text,
|
|
126
|
+
"First_Author": first_author,
|
|
127
|
+
"DOI": doi,
|
|
128
|
+
"PubMed_URL": pubmed_url,
|
|
129
|
+
"Full_Text_URL": full_text_url,
|
|
130
|
+
"Keywords": ", ".join(keywords) if keywords else "No keywords available",
|
|
131
|
+
"MeSH_Terms": ", ".join(mesh_terms) if mesh_terms else "No MeSH terms available",
|
|
132
|
+
"Journal": journal,
|
|
133
|
+
"Publication_Type": ", ".join(pub_types) if pub_types else "Not specified",
|
|
134
|
+
}
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
return articles
|
|
138
|
+
|
|
139
|
+
def search_pubmed(self, query: str, max_results: Optional[int] = 10) -> str:
|
|
140
|
+
"""Use this function to search PubMed for articles.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
query (str): The search query.
|
|
144
|
+
max_results (int): The maximum number of results to return (default 10).
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
str: A JSON string containing the search results.
|
|
148
|
+
"""
|
|
149
|
+
try:
|
|
150
|
+
log_debug(f"Searching PubMed for: {query}")
|
|
151
|
+
max_results = max_results or self.max_results or 10
|
|
152
|
+
ids = self.fetch_pubmed_ids(query, max_results, self.email)
|
|
153
|
+
details_root = self.fetch_details(ids)
|
|
154
|
+
articles = self.parse_details(details_root)
|
|
155
|
+
|
|
156
|
+
# Create result strings based on configured detail level
|
|
157
|
+
results = []
|
|
158
|
+
for article in articles:
|
|
159
|
+
if self.results_expanded:
|
|
160
|
+
# Comprehensive format with all metadata
|
|
161
|
+
article_text = (
|
|
162
|
+
f"Published: {article.get('Published')}\n"
|
|
163
|
+
f"Title: {article.get('Title')}\n"
|
|
164
|
+
f"First Author: {article.get('First_Author')}\n"
|
|
165
|
+
f"Journal: {article.get('Journal')}\n"
|
|
166
|
+
f"Publication Type: {article.get('Publication_Type')}\n"
|
|
167
|
+
f"DOI: {article.get('DOI')}\n"
|
|
168
|
+
f"PubMed URL: {article.get('PubMed_URL')}\n"
|
|
169
|
+
f"Full Text URL: {article.get('Full_Text_URL')}\n"
|
|
170
|
+
f"Keywords: {article.get('Keywords')}\n"
|
|
171
|
+
f"MeSH Terms: {article.get('MeSH_Terms')}\n"
|
|
172
|
+
f"Summary:\n{article.get('Summary')}"
|
|
173
|
+
)
|
|
174
|
+
else:
|
|
175
|
+
# Concise format with just essential information
|
|
176
|
+
summary = article.get("Summary", "")
|
|
177
|
+
article_text = (
|
|
178
|
+
f"Title: {article.get('Title')}\n"
|
|
179
|
+
f"Published: {article.get('Published')}\n"
|
|
180
|
+
f"Summary: {summary[:200]}..."
|
|
181
|
+
if len(summary) > 200
|
|
182
|
+
else f"Summary: {summary}"
|
|
183
|
+
)
|
|
184
|
+
results.append(article_text)
|
|
185
|
+
|
|
186
|
+
return json.dumps(results)
|
|
187
|
+
except Exception as e:
|
|
188
|
+
return f"Could not fetch articles. Error: {e}"
|
agno/tools/python.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import runpy
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, List, Optional
|
|
5
|
+
|
|
6
|
+
from agno.tools import Toolkit
|
|
7
|
+
from agno.utils.log import log_debug, log_info, logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@functools.lru_cache(maxsize=None)
|
|
11
|
+
def warn() -> None:
|
|
12
|
+
logger.warning("PythonTools can run arbitrary code, please provide human supervision.")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PythonTools(Toolkit):
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
base_dir: Optional[Path] = None,
|
|
19
|
+
safe_globals: Optional[dict] = None,
|
|
20
|
+
safe_locals: Optional[dict] = None,
|
|
21
|
+
**kwargs,
|
|
22
|
+
):
|
|
23
|
+
self.base_dir: Path = base_dir or Path.cwd()
|
|
24
|
+
|
|
25
|
+
# Restricted global and local scope
|
|
26
|
+
self.safe_globals: dict = safe_globals or globals()
|
|
27
|
+
self.safe_locals: dict = safe_locals or locals()
|
|
28
|
+
|
|
29
|
+
tools: List[Any] = [
|
|
30
|
+
self.save_to_file_and_run,
|
|
31
|
+
self.run_python_code,
|
|
32
|
+
self.pip_install_package,
|
|
33
|
+
self.uv_pip_install_package,
|
|
34
|
+
self.run_python_file_return_variable,
|
|
35
|
+
self.read_file,
|
|
36
|
+
self.list_files,
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
super().__init__(name="python_tools", tools=tools, **kwargs)
|
|
40
|
+
|
|
41
|
+
def save_to_file_and_run(
|
|
42
|
+
self, file_name: str, code: str, variable_to_return: Optional[str] = None, overwrite: bool = True
|
|
43
|
+
) -> str:
|
|
44
|
+
"""This function saves Python code to a file called `file_name` and then runs it.
|
|
45
|
+
If successful, returns the value of `variable_to_return` if provided otherwise returns a success message.
|
|
46
|
+
If failed, returns an error message.
|
|
47
|
+
|
|
48
|
+
Make sure the file_name ends with `.py`
|
|
49
|
+
|
|
50
|
+
:param file_name: The name of the file the code will be saved to.
|
|
51
|
+
:param code: The code to save and run.
|
|
52
|
+
:param variable_to_return: The variable to return.
|
|
53
|
+
:param overwrite: Overwrite the file if it already exists.
|
|
54
|
+
:return: if run is successful, the value of `variable_to_return` if provided else file name.
|
|
55
|
+
"""
|
|
56
|
+
try:
|
|
57
|
+
warn()
|
|
58
|
+
file_path = self.base_dir.joinpath(file_name)
|
|
59
|
+
log_debug(f"Saving code to {file_path}")
|
|
60
|
+
if not file_path.parent.exists():
|
|
61
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
62
|
+
if file_path.exists() and not overwrite:
|
|
63
|
+
return f"File {file_name} already exists"
|
|
64
|
+
file_path.write_text(code, encoding="utf-8")
|
|
65
|
+
log_info(f"Saved: {file_path}")
|
|
66
|
+
log_info(f"Running {file_path}")
|
|
67
|
+
globals_after_run = runpy.run_path(str(file_path), init_globals=self.safe_globals, run_name="__main__")
|
|
68
|
+
|
|
69
|
+
if variable_to_return:
|
|
70
|
+
variable_value = globals_after_run.get(variable_to_return)
|
|
71
|
+
if variable_value is None:
|
|
72
|
+
return f"Variable {variable_to_return} not found"
|
|
73
|
+
log_debug(f"Variable {variable_to_return} value: {variable_value}")
|
|
74
|
+
return str(variable_value)
|
|
75
|
+
else:
|
|
76
|
+
return f"successfully ran {str(file_path)}"
|
|
77
|
+
except Exception as e:
|
|
78
|
+
logger.error(f"Error saving and running code: {e}")
|
|
79
|
+
return f"Error saving and running code: {e}"
|
|
80
|
+
|
|
81
|
+
def run_python_file_return_variable(self, file_name: str, variable_to_return: Optional[str] = None) -> str:
|
|
82
|
+
"""This function runs code in a Python file.
|
|
83
|
+
If successful, returns the value of `variable_to_return` if provided otherwise returns a success message.
|
|
84
|
+
If failed, returns an error message.
|
|
85
|
+
|
|
86
|
+
:param file_name: The name of the file to run.
|
|
87
|
+
:param variable_to_return: The variable to return.
|
|
88
|
+
:return: if run is successful, the value of `variable_to_return` if provided else file name.
|
|
89
|
+
"""
|
|
90
|
+
try:
|
|
91
|
+
warn()
|
|
92
|
+
file_path = self.base_dir.joinpath(file_name)
|
|
93
|
+
|
|
94
|
+
log_info(f"Running {file_path}")
|
|
95
|
+
globals_after_run = runpy.run_path(str(file_path), init_globals=self.safe_globals, run_name="__main__")
|
|
96
|
+
if variable_to_return:
|
|
97
|
+
variable_value = globals_after_run.get(variable_to_return)
|
|
98
|
+
if variable_value is None:
|
|
99
|
+
return f"Variable {variable_to_return} not found"
|
|
100
|
+
log_debug(f"Variable {variable_to_return} value: {variable_value}")
|
|
101
|
+
return str(variable_value)
|
|
102
|
+
else:
|
|
103
|
+
return f"successfully ran {str(file_path)}"
|
|
104
|
+
except Exception as e:
|
|
105
|
+
logger.error(f"Error running file: {e}")
|
|
106
|
+
return f"Error running file: {e}"
|
|
107
|
+
|
|
108
|
+
def read_file(self, file_name: str) -> str:
|
|
109
|
+
"""Reads the contents of the file `file_name` and returns the contents if successful.
|
|
110
|
+
|
|
111
|
+
:param file_name: The name of the file to read.
|
|
112
|
+
:return: The contents of the file if successful, otherwise returns an error message.
|
|
113
|
+
"""
|
|
114
|
+
try:
|
|
115
|
+
log_info(f"Reading file: {file_name}")
|
|
116
|
+
file_path = self.base_dir.joinpath(file_name)
|
|
117
|
+
contents = file_path.read_text(encoding="utf-8")
|
|
118
|
+
return str(contents)
|
|
119
|
+
except Exception as e:
|
|
120
|
+
logger.error(f"Error reading file: {e}")
|
|
121
|
+
return f"Error reading file: {e}"
|
|
122
|
+
|
|
123
|
+
def list_files(self) -> str:
|
|
124
|
+
"""Returns a list of files in the base directory
|
|
125
|
+
|
|
126
|
+
:return: Comma separated list of files in the base directory.
|
|
127
|
+
"""
|
|
128
|
+
try:
|
|
129
|
+
log_info(f"Reading files in : {self.base_dir}")
|
|
130
|
+
files = [str(file_path.name) for file_path in self.base_dir.iterdir()]
|
|
131
|
+
return ", ".join(files)
|
|
132
|
+
except Exception as e:
|
|
133
|
+
logger.error(f"Error reading files: {e}")
|
|
134
|
+
return f"Error reading files: {e}"
|
|
135
|
+
|
|
136
|
+
def run_python_code(self, code: str, variable_to_return: Optional[str] = None) -> str:
|
|
137
|
+
"""This function to runs Python code in the current environment.
|
|
138
|
+
If successful, returns the value of `variable_to_return` if provided otherwise returns a success message.
|
|
139
|
+
If failed, returns an error message.
|
|
140
|
+
|
|
141
|
+
Returns the value of `variable_to_return` if successful, otherwise returns an error message.
|
|
142
|
+
|
|
143
|
+
:param code: The code to run.
|
|
144
|
+
:param variable_to_return: The variable to return.
|
|
145
|
+
:return: value of `variable_to_return` if successful, otherwise returns an error message.
|
|
146
|
+
"""
|
|
147
|
+
try:
|
|
148
|
+
warn()
|
|
149
|
+
|
|
150
|
+
log_debug(f"Running code:\n\n{code}\n\n")
|
|
151
|
+
exec(code, self.safe_globals, self.safe_locals)
|
|
152
|
+
|
|
153
|
+
if variable_to_return:
|
|
154
|
+
variable_value = self.safe_locals.get(variable_to_return)
|
|
155
|
+
if variable_value is None:
|
|
156
|
+
return f"Variable {variable_to_return} not found"
|
|
157
|
+
log_debug(f"Variable {variable_to_return} value: {variable_value}")
|
|
158
|
+
return str(variable_value)
|
|
159
|
+
else:
|
|
160
|
+
return "successfully ran python code"
|
|
161
|
+
except Exception as e:
|
|
162
|
+
logger.error(f"Error running python code: {e}")
|
|
163
|
+
return f"Error running python code: {e}"
|
|
164
|
+
|
|
165
|
+
def pip_install_package(self, package_name: str) -> str:
|
|
166
|
+
"""This function installs a package using pip in the current environment.
|
|
167
|
+
If successful, returns a success message.
|
|
168
|
+
If failed, returns an error message.
|
|
169
|
+
|
|
170
|
+
:param package_name: The name of the package to install.
|
|
171
|
+
:return: success message if successful, otherwise returns an error message.
|
|
172
|
+
"""
|
|
173
|
+
try:
|
|
174
|
+
warn()
|
|
175
|
+
|
|
176
|
+
log_debug(f"Installing package {package_name}")
|
|
177
|
+
import subprocess
|
|
178
|
+
import sys
|
|
179
|
+
|
|
180
|
+
subprocess.check_call([sys.executable, "-m", "pip", "install", package_name])
|
|
181
|
+
return f"successfully installed package {package_name}"
|
|
182
|
+
except Exception as e:
|
|
183
|
+
logger.error(f"Error installing package {package_name}: {e}")
|
|
184
|
+
return f"Error installing package {package_name}: {e}"
|
|
185
|
+
|
|
186
|
+
def uv_pip_install_package(self, package_name: str) -> str:
|
|
187
|
+
"""This function installs a package using uv and pip in the current environment.
|
|
188
|
+
If successful, returns a success message.
|
|
189
|
+
If failed, returns an error message.
|
|
190
|
+
|
|
191
|
+
:param package_name: The name of the package to install.
|
|
192
|
+
:return: success message if successful, otherwise returns an error message.
|
|
193
|
+
"""
|
|
194
|
+
try:
|
|
195
|
+
warn()
|
|
196
|
+
|
|
197
|
+
log_debug(f"Installing package {package_name}")
|
|
198
|
+
import subprocess
|
|
199
|
+
import sys
|
|
200
|
+
|
|
201
|
+
subprocess.check_call([sys.executable, "-m", "uv", "pip", "install", package_name])
|
|
202
|
+
return f"successfully installed package {package_name}"
|
|
203
|
+
except Exception as e:
|
|
204
|
+
logger.error(f"Error installing package {package_name}: {e}")
|
|
205
|
+
return f"Error installing package {package_name}: {e}"
|
agno/tools/reasoning.py
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
from textwrap import dedent
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
from agno.reasoning.step import NextAction, ReasoningStep
|
|
5
|
+
from agno.tools import Toolkit
|
|
6
|
+
from agno.utils.log import log_debug, log_error
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ReasoningTools(Toolkit):
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
enable_think: bool = True,
|
|
13
|
+
enable_analyze: bool = True,
|
|
14
|
+
all: bool = False,
|
|
15
|
+
instructions: Optional[str] = None,
|
|
16
|
+
add_instructions: bool = False,
|
|
17
|
+
add_few_shot: bool = False,
|
|
18
|
+
few_shot_examples: Optional[str] = None,
|
|
19
|
+
**kwargs,
|
|
20
|
+
):
|
|
21
|
+
"""A toolkit that provides step-by-step reasoning tools: Think and Analyze."""
|
|
22
|
+
|
|
23
|
+
# Add instructions for using this toolkit
|
|
24
|
+
if instructions is None:
|
|
25
|
+
self.instructions = "<reasoning_instructions>\n" + self.DEFAULT_INSTRUCTIONS
|
|
26
|
+
if add_few_shot:
|
|
27
|
+
if few_shot_examples is not None:
|
|
28
|
+
self.instructions += "\n" + few_shot_examples
|
|
29
|
+
else:
|
|
30
|
+
self.instructions += "\n" + self.FEW_SHOT_EXAMPLES
|
|
31
|
+
self.instructions += "\n</reasoning_instructions>\n"
|
|
32
|
+
else:
|
|
33
|
+
self.instructions = instructions
|
|
34
|
+
|
|
35
|
+
tools: List[Any] = []
|
|
36
|
+
# Prefer new flags; fallback to legacy ones
|
|
37
|
+
if all or enable_think:
|
|
38
|
+
tools.append(self.think)
|
|
39
|
+
if all or enable_analyze:
|
|
40
|
+
tools.append(self.analyze)
|
|
41
|
+
|
|
42
|
+
super().__init__(
|
|
43
|
+
name="reasoning_tools",
|
|
44
|
+
instructions=self.instructions,
|
|
45
|
+
add_instructions=add_instructions,
|
|
46
|
+
tools=tools,
|
|
47
|
+
**kwargs,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def think(
|
|
51
|
+
self,
|
|
52
|
+
session_state: Dict[str, Any],
|
|
53
|
+
title: str,
|
|
54
|
+
thought: str,
|
|
55
|
+
action: Optional[str] = None,
|
|
56
|
+
confidence: float = 0.8,
|
|
57
|
+
) -> str:
|
|
58
|
+
"""Use this tool as a scratchpad to reason about the question and work through it step-by-step.
|
|
59
|
+
This tool will help you break down complex problems into logical steps and track the reasoning process.
|
|
60
|
+
You can call it as many times as needed. These internal thoughts are never revealed to the user.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
title: A concise title for this step
|
|
64
|
+
thought: Your detailed thought for this step
|
|
65
|
+
action: What you'll do based on this thought
|
|
66
|
+
confidence: How confident you are about this thought (0.0 to 1.0)
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
A list of previous thoughts and the new thought
|
|
70
|
+
"""
|
|
71
|
+
try:
|
|
72
|
+
log_debug(f"Thought about {title}")
|
|
73
|
+
|
|
74
|
+
# Create a reasoning step
|
|
75
|
+
reasoning_step = ReasoningStep(
|
|
76
|
+
title=title,
|
|
77
|
+
reasoning=thought,
|
|
78
|
+
action=action,
|
|
79
|
+
next_action=NextAction.CONTINUE,
|
|
80
|
+
confidence=confidence,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
current_run_id = session_state.get("current_run_id", None)
|
|
84
|
+
|
|
85
|
+
# Add this step to the Agent's session state
|
|
86
|
+
if session_state is None:
|
|
87
|
+
session_state = {}
|
|
88
|
+
if "reasoning_steps" not in session_state:
|
|
89
|
+
session_state["reasoning_steps"] = {}
|
|
90
|
+
if current_run_id not in session_state["reasoning_steps"]:
|
|
91
|
+
session_state["reasoning_steps"][current_run_id] = []
|
|
92
|
+
session_state["reasoning_steps"][current_run_id].append(reasoning_step.model_dump_json())
|
|
93
|
+
|
|
94
|
+
# Return all previous reasoning_steps and the new reasoning_step
|
|
95
|
+
if "reasoning_steps" in session_state and current_run_id in session_state["reasoning_steps"]:
|
|
96
|
+
formatted_reasoning_steps = ""
|
|
97
|
+
for i, step in enumerate(session_state["reasoning_steps"][current_run_id], 1):
|
|
98
|
+
step_parsed = ReasoningStep.model_validate_json(step)
|
|
99
|
+
step_str = dedent(f"""\
|
|
100
|
+
Step {i}:
|
|
101
|
+
Title: {step_parsed.title}
|
|
102
|
+
Reasoning: {step_parsed.reasoning}
|
|
103
|
+
Action: {step_parsed.action}
|
|
104
|
+
Confidence: {step_parsed.confidence}
|
|
105
|
+
""")
|
|
106
|
+
formatted_reasoning_steps += step_str + "\n"
|
|
107
|
+
return formatted_reasoning_steps.strip()
|
|
108
|
+
return reasoning_step.model_dump_json()
|
|
109
|
+
except Exception as e:
|
|
110
|
+
log_error(f"Error recording thought: {e}")
|
|
111
|
+
return f"Error recording thought: {e}"
|
|
112
|
+
|
|
113
|
+
def analyze(
|
|
114
|
+
self,
|
|
115
|
+
session_state: Dict[str, Any],
|
|
116
|
+
title: str,
|
|
117
|
+
result: str,
|
|
118
|
+
analysis: str,
|
|
119
|
+
next_action: str = "continue",
|
|
120
|
+
confidence: float = 0.8,
|
|
121
|
+
) -> str:
|
|
122
|
+
"""Use this tool to analyze results from a reasoning step and determine next actions.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
title: A concise title for this analysis step
|
|
126
|
+
result: The outcome of the previous action
|
|
127
|
+
analysis: Your analysis of the results
|
|
128
|
+
next_action: What to do next ("continue", "validate", or "final_answer")
|
|
129
|
+
confidence: How confident you are in this analysis (0.0 to 1.0)
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
A list of previous thoughts and the new analysis
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
log_debug(f"Analyzed {title}")
|
|
136
|
+
|
|
137
|
+
# Map string next_action to enum
|
|
138
|
+
next_action_enum = NextAction.CONTINUE
|
|
139
|
+
if next_action.lower() == "validate":
|
|
140
|
+
next_action_enum = NextAction.VALIDATE
|
|
141
|
+
elif next_action.lower() in ["final", "final_answer", "finalize"]:
|
|
142
|
+
next_action_enum = NextAction.FINAL_ANSWER
|
|
143
|
+
|
|
144
|
+
# Create a reasoning step for the analysis
|
|
145
|
+
reasoning_step = ReasoningStep(
|
|
146
|
+
title=title,
|
|
147
|
+
result=result,
|
|
148
|
+
reasoning=analysis,
|
|
149
|
+
next_action=next_action_enum,
|
|
150
|
+
confidence=confidence,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
current_run_id = session_state.get("current_run_id", None)
|
|
154
|
+
# Add this step to the Agent's session state
|
|
155
|
+
if session_state is None:
|
|
156
|
+
session_state = {}
|
|
157
|
+
if "reasoning_steps" not in session_state:
|
|
158
|
+
session_state["reasoning_steps"] = {}
|
|
159
|
+
if current_run_id not in session_state["reasoning_steps"]:
|
|
160
|
+
session_state["reasoning_steps"][current_run_id] = []
|
|
161
|
+
session_state["reasoning_steps"][current_run_id].append(reasoning_step.model_dump_json())
|
|
162
|
+
|
|
163
|
+
# Return all previous reasoning_steps and the new reasoning_step
|
|
164
|
+
if "reasoning_steps" in session_state and current_run_id in session_state["reasoning_steps"]:
|
|
165
|
+
formatted_reasoning_steps = ""
|
|
166
|
+
for i, step in enumerate(session_state["reasoning_steps"][current_run_id], 1):
|
|
167
|
+
step_parsed = ReasoningStep.model_validate_json(step)
|
|
168
|
+
step_str = dedent(f"""\
|
|
169
|
+
Step {i}:
|
|
170
|
+
Title: {step_parsed.title}
|
|
171
|
+
Reasoning: {step_parsed.reasoning}
|
|
172
|
+
Action: {step_parsed.action}
|
|
173
|
+
Confidence: {step_parsed.confidence}
|
|
174
|
+
""")
|
|
175
|
+
formatted_reasoning_steps += step_str + "\n"
|
|
176
|
+
return formatted_reasoning_steps.strip()
|
|
177
|
+
return reasoning_step.model_dump_json()
|
|
178
|
+
except Exception as e:
|
|
179
|
+
log_error(f"Error recording analysis: {e}")
|
|
180
|
+
return f"Error recording analysis: {e}"
|
|
181
|
+
|
|
182
|
+
# --------------------------------------------------------------------------------
|
|
183
|
+
# Default instructions and few-shot examples
|
|
184
|
+
# --------------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
DEFAULT_INSTRUCTIONS = dedent(
|
|
187
|
+
"""\
|
|
188
|
+
You have access to the `think` and `analyze` tools to work through problems step-by-step and structure your thought process. You must ALWAYS `think` before making tool calls or generating a response.
|
|
189
|
+
|
|
190
|
+
1. **Think** (scratchpad):
|
|
191
|
+
- Purpose: Use the `think` tool as a scratchpad to break down complex problems, outline steps, and decide on immediate actions within your reasoning flow. Use this to structure your internal monologue.
|
|
192
|
+
- Usage: Call `think` before making tool calls or generating a response. Explain your reasoning and specify the intended action (e.g., "make a tool call", "perform calculation", "ask clarifying question").
|
|
193
|
+
|
|
194
|
+
2. **Analyze** (evaluation):
|
|
195
|
+
- Purpose: Evaluate the result of a think step or a set of tool calls. Assess if the result is expected, sufficient, or requires further investigation.
|
|
196
|
+
- Usage: Call `analyze` after a set of tool calls. Determine the `next_action` based on your analysis: `continue` (more reasoning needed), `validate` (seek external confirmation/validation if possible), or `final_answer` (ready to conclude).
|
|
197
|
+
- Explain your reasoning highlighting whether the result is correct/sufficient.
|
|
198
|
+
|
|
199
|
+
## IMPORTANT GUIDELINES
|
|
200
|
+
- **Always Think First:** You MUST use the `think` tool before making tool calls or generating a response.
|
|
201
|
+
- **Iterate to Solve:** Use the `think` and `analyze` tools iteratively to build a clear reasoning path. The typical flow is `Think` -> [`Tool Calls` if needed] -> [`Analyze` if needed] -> ... -> `final_answer`. Repeat this cycle until you reach a satisfactory conclusion.
|
|
202
|
+
- **Make multiple tool calls in parallel:** After a `think` step, you can make multiple tool calls in parallel.
|
|
203
|
+
- **Keep Thoughts Internal:** The reasoning steps (thoughts and analyses) are for your internal process only. Do not share them directly with the user.
|
|
204
|
+
- **Conclude Clearly:** When your analysis determines the `next_action` is `final_answer`, provide a concise and accurate final answer to the user."""
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
FEW_SHOT_EXAMPLES = dedent(
|
|
208
|
+
"""
|
|
209
|
+
Below are examples demonstrating how to use the `think` and `analyze` tools.
|
|
210
|
+
|
|
211
|
+
### Examples
|
|
212
|
+
|
|
213
|
+
**Example 1: Simple Fact Retrieval**
|
|
214
|
+
|
|
215
|
+
*User Request:* How many continents are there on Earth?
|
|
216
|
+
|
|
217
|
+
*Agent's Internal Process:*
|
|
218
|
+
|
|
219
|
+
```tool_call
|
|
220
|
+
think(
|
|
221
|
+
title="Understand Request",
|
|
222
|
+
thought="The user wants to know the standard number of continents on Earth. This is a common piece of knowledge.",
|
|
223
|
+
action="Recall or verify the number of continents.",
|
|
224
|
+
confidence=0.95
|
|
225
|
+
)
|
|
226
|
+
```
|
|
227
|
+
*--(Agent internally recalls the fact)--*
|
|
228
|
+
```tool_call
|
|
229
|
+
analyze(
|
|
230
|
+
title="Evaluate Fact",
|
|
231
|
+
result="Standard geographical models list 7 continents: Africa, Antarctica, Asia, Australia, Europe, North America, South America.",
|
|
232
|
+
analysis="The recalled information directly answers the user's question accurately.",
|
|
233
|
+
next_action="final_answer",
|
|
234
|
+
confidence=1.0
|
|
235
|
+
)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
*Agent's Final Answer to User:*
|
|
239
|
+
There are 7 continents on Earth: Africa, Antarctica, Asia, Australia, Europe, North America, and South America.
|
|
240
|
+
|
|
241
|
+
**Example 2: Multi-Step Information Gathering**
|
|
242
|
+
|
|
243
|
+
*User Request:* What is the capital of France and its current population?
|
|
244
|
+
|
|
245
|
+
*Agent's Internal Process:*
|
|
246
|
+
|
|
247
|
+
```tool_call
|
|
248
|
+
think(
|
|
249
|
+
title="Plan Information Retrieval",
|
|
250
|
+
thought="The user needs two pieces of information: the capital of France and its current population. I should use external tools (like search) to find the most up-to-date and accurate information.",
|
|
251
|
+
action="First, search for the capital of France.",
|
|
252
|
+
confidence=0.95
|
|
253
|
+
)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
*Perform multiple tool calls in parallel*
|
|
257
|
+
*--(Tool call 1: search(query="capital of France"))--*
|
|
258
|
+
*--(Tool call 2: search(query="population of Paris current"))--*
|
|
259
|
+
*--(Tool Result 1: "Paris")--*
|
|
260
|
+
*--(Tool Result 2: "Approximately 2.1 million (city proper, estimate for early 2024)")--*
|
|
261
|
+
|
|
262
|
+
```tool_call
|
|
263
|
+
analyze(
|
|
264
|
+
title="Analyze Capital Search Result",
|
|
265
|
+
result="The search result indicates Paris is the capital of France.",
|
|
266
|
+
analysis="This provides the first piece of requested information. Now I need to find the population of Paris.",
|
|
267
|
+
next_action="continue",
|
|
268
|
+
confidence=1.0
|
|
269
|
+
)
|
|
270
|
+
```
|
|
271
|
+
```tool_call
|
|
272
|
+
analyze(
|
|
273
|
+
title="Analyze Population Search Result",
|
|
274
|
+
result="The search provided an estimated population figure for Paris.",
|
|
275
|
+
analysis="I now have both the capital and its estimated population. I can provide the final answer.",
|
|
276
|
+
next_action="final_answer",
|
|
277
|
+
confidence=0.9
|
|
278
|
+
)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
*Agent's Final Answer to User:*
|
|
282
|
+
The capital of France is Paris. Its estimated population (city proper) is approximately 2.1 million as of early 2024."""
|
|
283
|
+
)
|