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/utils/log.py
CHANGED
|
@@ -1,19 +1,92 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from functools import lru_cache
|
|
2
3
|
from os import getenv
|
|
4
|
+
from typing import Any, Literal, Optional
|
|
3
5
|
|
|
4
6
|
from rich.logging import RichHandler
|
|
7
|
+
from rich.text import Text
|
|
5
8
|
|
|
6
9
|
LOGGER_NAME = "agno"
|
|
10
|
+
TEAM_LOGGER_NAME = f"{LOGGER_NAME}-team"
|
|
11
|
+
WORKFLOW_LOGGER_NAME = f"{LOGGER_NAME}-workflow"
|
|
7
12
|
|
|
13
|
+
# Define custom styles for different log sources
|
|
14
|
+
LOG_STYLES = {
|
|
15
|
+
"agent": {
|
|
16
|
+
"debug": "green",
|
|
17
|
+
"info": "blue",
|
|
18
|
+
},
|
|
19
|
+
"team": {
|
|
20
|
+
"debug": "magenta",
|
|
21
|
+
"info": "steel_blue1",
|
|
22
|
+
},
|
|
23
|
+
"workflow": {
|
|
24
|
+
"debug": "sandy_brown",
|
|
25
|
+
"info": "orange3",
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ColoredRichHandler(RichHandler):
|
|
31
|
+
def __init__(self, *args, source_type: Optional[str] = None, **kwargs):
|
|
32
|
+
super().__init__(*args, **kwargs)
|
|
33
|
+
self.source_type = source_type
|
|
34
|
+
|
|
35
|
+
def get_level_text(self, record: logging.LogRecord) -> Text:
|
|
36
|
+
# Return empty Text if message is empty
|
|
37
|
+
if not record.msg:
|
|
38
|
+
return Text("")
|
|
39
|
+
|
|
40
|
+
level_name = record.levelname.lower()
|
|
41
|
+
if self.source_type and self.source_type in LOG_STYLES:
|
|
42
|
+
if level_name in LOG_STYLES[self.source_type]:
|
|
43
|
+
color = LOG_STYLES[self.source_type][level_name]
|
|
44
|
+
return Text(record.levelname, style=color)
|
|
45
|
+
else:
|
|
46
|
+
if level_name in LOG_STYLES["agent"]:
|
|
47
|
+
color = LOG_STYLES["agent"][level_name]
|
|
48
|
+
return Text(record.levelname, style=color)
|
|
49
|
+
return super().get_level_text(record)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class AgnoLogger(logging.Logger):
|
|
53
|
+
def __init__(self, name: str, level: int = logging.NOTSET):
|
|
54
|
+
super().__init__(name, level)
|
|
55
|
+
|
|
56
|
+
def debug(self, msg: str, center: bool = False, symbol: str = "*", *args, **kwargs): # type: ignore
|
|
57
|
+
if center:
|
|
58
|
+
msg = center_header(str(msg), symbol)
|
|
59
|
+
super().debug(msg, *args, **kwargs)
|
|
60
|
+
|
|
61
|
+
def info(self, msg: str, center: bool = False, symbol: str = "*", *args, **kwargs): # type: ignore
|
|
62
|
+
if center:
|
|
63
|
+
msg = center_header(str(msg), symbol)
|
|
64
|
+
super().info(msg, *args, **kwargs)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def build_logger(logger_name: str, source_type: Optional[str] = None) -> Any:
|
|
68
|
+
# If a logger with the name "agno.{source_type}" is already set, we want to use that one
|
|
69
|
+
_logger = logging.getLogger(f"agno.{logger_name}")
|
|
70
|
+
if _logger.handlers or _logger.level != logging.NOTSET:
|
|
71
|
+
return _logger
|
|
72
|
+
|
|
73
|
+
# Set the custom logger class as the default for this logger
|
|
74
|
+
logging.setLoggerClass(AgnoLogger)
|
|
75
|
+
|
|
76
|
+
# Create logger with custom class
|
|
77
|
+
_logger = logging.getLogger(logger_name)
|
|
78
|
+
|
|
79
|
+
# Reset logger class to default to avoid affecting other loggers
|
|
80
|
+
logging.setLoggerClass(logging.Logger)
|
|
8
81
|
|
|
9
|
-
def get_logger(logger_name: str) -> logging.Logger:
|
|
10
82
|
# https://rich.readthedocs.io/en/latest/reference/logging.html#rich.logging.RichHandler
|
|
11
83
|
# https://rich.readthedocs.io/en/latest/logging.html#handle-exceptions
|
|
12
|
-
rich_handler =
|
|
84
|
+
rich_handler = ColoredRichHandler(
|
|
13
85
|
show_time=False,
|
|
14
86
|
rich_tracebacks=False,
|
|
15
87
|
show_path=True if getenv("AGNO_API_RUNTIME") == "dev" else False,
|
|
16
88
|
tracebacks_show_locals=False,
|
|
89
|
+
source_type=source_type or "agent",
|
|
17
90
|
)
|
|
18
91
|
rich_handler.setFormatter(
|
|
19
92
|
logging.Formatter(
|
|
@@ -22,21 +95,161 @@ def get_logger(logger_name: str) -> logging.Logger:
|
|
|
22
95
|
)
|
|
23
96
|
)
|
|
24
97
|
|
|
25
|
-
_logger = logging.getLogger(logger_name)
|
|
26
98
|
_logger.addHandler(rich_handler)
|
|
27
99
|
_logger.setLevel(logging.INFO)
|
|
28
100
|
_logger.propagate = False
|
|
29
101
|
return _logger
|
|
30
102
|
|
|
31
103
|
|
|
32
|
-
|
|
104
|
+
agent_logger: AgnoLogger = build_logger(LOGGER_NAME, source_type="agent")
|
|
105
|
+
team_logger: AgnoLogger = build_logger(TEAM_LOGGER_NAME, source_type="team")
|
|
106
|
+
workflow_logger: AgnoLogger = build_logger(WORKFLOW_LOGGER_NAME, source_type="workflow")
|
|
33
107
|
|
|
108
|
+
# Set the default logger to the agent logger
|
|
109
|
+
logger: AgnoLogger = agent_logger
|
|
34
110
|
|
|
35
|
-
|
|
36
|
-
|
|
111
|
+
|
|
112
|
+
debug_on: bool = False
|
|
113
|
+
debug_level: Literal[1, 2] = 1
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def set_log_level_to_debug(source_type: Optional[str] = None, level: Literal[1, 2] = 1):
|
|
117
|
+
if source_type is None:
|
|
118
|
+
use_agent_logger()
|
|
119
|
+
|
|
120
|
+
_logger = logging.getLogger(LOGGER_NAME if source_type is None else f"{LOGGER_NAME}-{source_type}")
|
|
37
121
|
_logger.setLevel(logging.DEBUG)
|
|
38
122
|
|
|
123
|
+
global debug_on
|
|
124
|
+
debug_on = True
|
|
39
125
|
|
|
40
|
-
|
|
41
|
-
|
|
126
|
+
global debug_level
|
|
127
|
+
debug_level = level
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def set_log_level_to_info(source_type: Optional[str] = None):
|
|
131
|
+
_logger = logging.getLogger(LOGGER_NAME if source_type is None else f"{LOGGER_NAME}-{source_type}")
|
|
42
132
|
_logger.setLevel(logging.INFO)
|
|
133
|
+
|
|
134
|
+
global debug_on
|
|
135
|
+
debug_on = False
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def set_log_level_to_warning(source_type: Optional[str] = None):
|
|
139
|
+
_logger = logging.getLogger(LOGGER_NAME if source_type is None else f"{LOGGER_NAME}-{source_type}")
|
|
140
|
+
_logger.setLevel(logging.WARNING)
|
|
141
|
+
|
|
142
|
+
global debug_on
|
|
143
|
+
debug_on = False
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def set_log_level_to_error(source_type: Optional[str] = None):
|
|
147
|
+
_logger = logging.getLogger(LOGGER_NAME if source_type is None else f"{LOGGER_NAME}-{source_type}")
|
|
148
|
+
_logger.setLevel(logging.ERROR)
|
|
149
|
+
|
|
150
|
+
global debug_on
|
|
151
|
+
debug_on = False
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def center_header(message: str, symbol: str = "*") -> str:
|
|
155
|
+
try:
|
|
156
|
+
import shutil
|
|
157
|
+
|
|
158
|
+
terminal_width = shutil.get_terminal_size().columns
|
|
159
|
+
except Exception:
|
|
160
|
+
terminal_width = 80 # fallback width
|
|
161
|
+
|
|
162
|
+
header = f" {message} "
|
|
163
|
+
return f"{header.center(terminal_width - 20, symbol)}"
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def use_team_logger():
|
|
167
|
+
"""Switch the default logger to use team_logger"""
|
|
168
|
+
global logger
|
|
169
|
+
logger = team_logger
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def use_agent_logger():
|
|
173
|
+
"""Switch the default logger to use the default agent logger"""
|
|
174
|
+
global logger
|
|
175
|
+
logger = agent_logger
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def use_workflow_logger():
|
|
179
|
+
"""Switch the default logger to use workflow_logger"""
|
|
180
|
+
global logger
|
|
181
|
+
logger = workflow_logger
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@lru_cache(maxsize=128)
|
|
185
|
+
def _using_default_logger(logger_instance: Any) -> bool:
|
|
186
|
+
"""Return True if the currently active logger is our default AgnoLogger"""
|
|
187
|
+
return isinstance(logger_instance, AgnoLogger)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def log_debug(msg, center: bool = False, symbol: str = "*", log_level: Literal[1, 2] = 1, *args, **kwargs):
|
|
191
|
+
global logger
|
|
192
|
+
global debug_on
|
|
193
|
+
global debug_level
|
|
194
|
+
|
|
195
|
+
if debug_on:
|
|
196
|
+
if debug_level >= log_level:
|
|
197
|
+
if _using_default_logger(logger):
|
|
198
|
+
logger.debug(msg, center, symbol, *args, **kwargs)
|
|
199
|
+
else:
|
|
200
|
+
logger.debug(msg, *args, **kwargs)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def log_info(msg, center: bool = False, symbol: str = "*", *args, **kwargs):
|
|
204
|
+
global logger
|
|
205
|
+
if _using_default_logger(logger):
|
|
206
|
+
logger.info(msg, center, symbol, *args, **kwargs)
|
|
207
|
+
else:
|
|
208
|
+
logger.info(msg, *args, **kwargs)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def log_warning(msg, *args, **kwargs):
|
|
212
|
+
global logger
|
|
213
|
+
logger.warning(msg, *args, **kwargs)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def log_error(msg, *args, **kwargs):
|
|
217
|
+
global logger
|
|
218
|
+
logger.error(msg, *args, **kwargs)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def log_exception(msg, *args, **kwargs):
|
|
222
|
+
global logger
|
|
223
|
+
logger.exception(msg, *args, **kwargs)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def configure_agno_logging(
|
|
227
|
+
custom_default_logger: Optional[Any] = None,
|
|
228
|
+
custom_agent_logger: Optional[Any] = None,
|
|
229
|
+
custom_team_logger: Optional[Any] = None,
|
|
230
|
+
custom_workflow_logger: Optional[Any] = None,
|
|
231
|
+
) -> None:
|
|
232
|
+
"""
|
|
233
|
+
Util to set custom loggers. These will be used everywhere across the Agno library.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
custom_default_logger: Default logger to use (overrides agent_logger for default)
|
|
237
|
+
custom_agent_logger: Custom logger for agent operations
|
|
238
|
+
custom_team_logger: Custom logger for team operations
|
|
239
|
+
custom_workflow_logger: Custom logger for workflow operations
|
|
240
|
+
"""
|
|
241
|
+
if custom_default_logger is not None:
|
|
242
|
+
global logger
|
|
243
|
+
logger = custom_default_logger
|
|
244
|
+
|
|
245
|
+
if custom_agent_logger is not None:
|
|
246
|
+
global agent_logger
|
|
247
|
+
agent_logger = custom_agent_logger
|
|
248
|
+
|
|
249
|
+
if custom_team_logger is not None:
|
|
250
|
+
global team_logger
|
|
251
|
+
team_logger = custom_team_logger
|
|
252
|
+
|
|
253
|
+
if custom_workflow_logger is not None:
|
|
254
|
+
global workflow_logger
|
|
255
|
+
workflow_logger = custom_workflow_logger
|
agno/utils/mcp.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from functools import partial
|
|
3
|
+
from uuid import uuid4
|
|
4
|
+
|
|
5
|
+
from agno.utils.log import log_debug, log_exception
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from mcp import ClientSession
|
|
9
|
+
from mcp.types import CallToolResult, EmbeddedResource, ImageContent, TextContent
|
|
10
|
+
from mcp.types import Tool as MCPTool
|
|
11
|
+
except (ImportError, ModuleNotFoundError):
|
|
12
|
+
raise ImportError("`mcp` not installed. Please install using `pip install mcp`")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
from agno.media import Image
|
|
16
|
+
from agno.tools.function import ToolResult
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_entrypoint_for_tool(tool: MCPTool, session: ClientSession):
|
|
20
|
+
"""
|
|
21
|
+
Return an entrypoint for an MCP tool.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
tool: The MCP tool to create an entrypoint for
|
|
25
|
+
session: The session to use
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Callable: The entrypoint function for the tool
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
async def call_tool(tool_name: str, **kwargs) -> ToolResult:
|
|
32
|
+
try:
|
|
33
|
+
await session.send_ping()
|
|
34
|
+
except Exception as e:
|
|
35
|
+
log_exception(e)
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
log_debug(f"Calling MCP Tool '{tool_name}' with args: {kwargs}")
|
|
39
|
+
result: CallToolResult = await session.call_tool(tool_name, kwargs) # type: ignore
|
|
40
|
+
|
|
41
|
+
# Return an error if the tool call failed
|
|
42
|
+
if result.isError:
|
|
43
|
+
return ToolResult(content=f"Error from MCP tool '{tool_name}': {result.content}")
|
|
44
|
+
|
|
45
|
+
# Process the result content
|
|
46
|
+
response_str = ""
|
|
47
|
+
images = []
|
|
48
|
+
|
|
49
|
+
for content_item in result.content:
|
|
50
|
+
if isinstance(content_item, TextContent):
|
|
51
|
+
text_content = content_item.text
|
|
52
|
+
|
|
53
|
+
# Parse as JSON to check for custom image format
|
|
54
|
+
try:
|
|
55
|
+
parsed_json = json.loads(text_content)
|
|
56
|
+
if (
|
|
57
|
+
isinstance(parsed_json, dict)
|
|
58
|
+
and parsed_json.get("type") == "image"
|
|
59
|
+
and "data" in parsed_json
|
|
60
|
+
):
|
|
61
|
+
log_debug("Found custom JSON image format in TextContent")
|
|
62
|
+
|
|
63
|
+
# Extract image data
|
|
64
|
+
image_data = parsed_json.get("data")
|
|
65
|
+
mime_type = parsed_json.get("mimeType", "image/png")
|
|
66
|
+
|
|
67
|
+
if image_data and isinstance(image_data, str):
|
|
68
|
+
import base64
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
image_bytes = base64.b64decode(image_data)
|
|
72
|
+
except Exception as e:
|
|
73
|
+
log_debug(f"Failed to decode base64 image data: {e}")
|
|
74
|
+
image_bytes = None
|
|
75
|
+
|
|
76
|
+
if image_bytes:
|
|
77
|
+
img_artifact = Image(
|
|
78
|
+
id=str(uuid4()),
|
|
79
|
+
url=None,
|
|
80
|
+
content=image_bytes,
|
|
81
|
+
mime_type=mime_type,
|
|
82
|
+
)
|
|
83
|
+
images.append(img_artifact)
|
|
84
|
+
response_str += "Image has been generated and added to the response.\n"
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
except (json.JSONDecodeError, TypeError):
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
response_str += text_content + "\n"
|
|
91
|
+
|
|
92
|
+
elif isinstance(content_item, ImageContent):
|
|
93
|
+
# Handle standard MCP ImageContent
|
|
94
|
+
image_data = getattr(content_item, "data", None)
|
|
95
|
+
|
|
96
|
+
if image_data and isinstance(image_data, str):
|
|
97
|
+
import base64
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
image_data = base64.b64decode(image_data)
|
|
101
|
+
except Exception as e:
|
|
102
|
+
log_debug(f"Failed to decode base64 image data: {e}")
|
|
103
|
+
image_data = None
|
|
104
|
+
|
|
105
|
+
img_artifact = Image(
|
|
106
|
+
id=str(uuid4()),
|
|
107
|
+
url=getattr(content_item, "url", None),
|
|
108
|
+
content=image_data,
|
|
109
|
+
mime_type=getattr(content_item, "mimeType", "image/png"),
|
|
110
|
+
)
|
|
111
|
+
images.append(img_artifact)
|
|
112
|
+
response_str += "Image has been generated and added to the response.\n"
|
|
113
|
+
elif isinstance(content_item, EmbeddedResource):
|
|
114
|
+
# Handle embedded resources
|
|
115
|
+
response_str += f"[Embedded resource: {content_item.resource.model_dump_json()}]\n"
|
|
116
|
+
else:
|
|
117
|
+
# Handle other content types
|
|
118
|
+
response_str += f"[Unsupported content type: {content_item.type}]\n"
|
|
119
|
+
|
|
120
|
+
return ToolResult(
|
|
121
|
+
content=response_str.strip(),
|
|
122
|
+
images=images if images else None,
|
|
123
|
+
)
|
|
124
|
+
except Exception as e:
|
|
125
|
+
log_exception(f"Failed to call MCP tool '{tool_name}': {e}")
|
|
126
|
+
return ToolResult(content=f"Error: {e}")
|
|
127
|
+
|
|
128
|
+
return partial(call_tool, tool_name=tool.name)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def prepare_command(command: str) -> list[str]:
|
|
132
|
+
"""Sanitize a command and split it into parts before using it to run a MCP server."""
|
|
133
|
+
import os
|
|
134
|
+
import shutil
|
|
135
|
+
from shlex import split
|
|
136
|
+
|
|
137
|
+
# Block dangerous characters
|
|
138
|
+
if any(char in command for char in ["&", "|", ";", "`", "$", "(", ")"]):
|
|
139
|
+
raise ValueError("MCP command can't contain shell metacharacters")
|
|
140
|
+
|
|
141
|
+
parts = split(command)
|
|
142
|
+
if not parts:
|
|
143
|
+
raise ValueError("MCP command can't be empty")
|
|
144
|
+
|
|
145
|
+
# Only allow specific executables
|
|
146
|
+
ALLOWED_COMMANDS = {
|
|
147
|
+
# Python
|
|
148
|
+
"python",
|
|
149
|
+
"python3",
|
|
150
|
+
"uv",
|
|
151
|
+
"uvx",
|
|
152
|
+
"pipx",
|
|
153
|
+
# Node
|
|
154
|
+
"node",
|
|
155
|
+
"npm",
|
|
156
|
+
"npx",
|
|
157
|
+
"yarn",
|
|
158
|
+
"pnpm",
|
|
159
|
+
"bun",
|
|
160
|
+
# Other runtimes
|
|
161
|
+
"deno",
|
|
162
|
+
"java",
|
|
163
|
+
"ruby",
|
|
164
|
+
"docker",
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
executable = parts[0].split("/")[-1]
|
|
168
|
+
|
|
169
|
+
# Check if it's a relative path starting with ./ or ../
|
|
170
|
+
if executable.startswith("./") or executable.startswith("../"):
|
|
171
|
+
# Allow relative paths to binaries
|
|
172
|
+
return parts
|
|
173
|
+
|
|
174
|
+
# Check if it's an absolute path to a binary
|
|
175
|
+
if executable.startswith("/") and os.path.isfile(executable):
|
|
176
|
+
# Allow absolute paths to existing files
|
|
177
|
+
return parts
|
|
178
|
+
|
|
179
|
+
# Check if it's a binary in current directory without ./
|
|
180
|
+
if "/" not in executable and os.path.isfile(executable):
|
|
181
|
+
# Allow binaries in current directory
|
|
182
|
+
return parts
|
|
183
|
+
|
|
184
|
+
# Check if it's a binary in PATH
|
|
185
|
+
if shutil.which(executable):
|
|
186
|
+
return parts
|
|
187
|
+
|
|
188
|
+
if executable not in ALLOWED_COMMANDS:
|
|
189
|
+
raise ValueError(f"MCP command needs to use one of the following executables: {ALLOWED_COMMANDS}")
|
|
190
|
+
|
|
191
|
+
first_part = parts[0]
|
|
192
|
+
executable = first_part.split("/")[-1]
|
|
193
|
+
|
|
194
|
+
# Allow known commands
|
|
195
|
+
if executable in ALLOWED_COMMANDS:
|
|
196
|
+
return parts
|
|
197
|
+
|
|
198
|
+
# Allow relative paths to custom binaries
|
|
199
|
+
if first_part.startswith(("./", "../")):
|
|
200
|
+
return parts
|
|
201
|
+
|
|
202
|
+
# Allow absolute paths to existing files
|
|
203
|
+
if first_part.startswith("/") and os.path.isfile(first_part):
|
|
204
|
+
return parts
|
|
205
|
+
|
|
206
|
+
# Allow binaries in current directory without ./
|
|
207
|
+
if "/" not in first_part and os.path.isfile(first_part):
|
|
208
|
+
return parts
|
|
209
|
+
|
|
210
|
+
# Allow binaries in PATH
|
|
211
|
+
if shutil.which(first_part):
|
|
212
|
+
return parts
|
|
213
|
+
|
|
214
|
+
raise ValueError(f"MCP command needs to use one of the following executables: {ALLOWED_COMMANDS}")
|