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/utils/http.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from time import sleep
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
DEFAULT_MAX_RETRIES = 3
|
|
11
|
+
DEFAULT_BACKOFF_FACTOR = 2 # Exponential backoff: 1, 2, 4, 8...
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def fetch_with_retry(
|
|
15
|
+
url: str,
|
|
16
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
17
|
+
backoff_factor: int = DEFAULT_BACKOFF_FACTOR,
|
|
18
|
+
proxy: Optional[str] = None,
|
|
19
|
+
) -> httpx.Response:
|
|
20
|
+
"""Synchronous HTTP GET with retry logic."""
|
|
21
|
+
|
|
22
|
+
for attempt in range(max_retries):
|
|
23
|
+
try:
|
|
24
|
+
response = httpx.get(url, proxy=proxy) if proxy else httpx.get(url)
|
|
25
|
+
response.raise_for_status()
|
|
26
|
+
return response
|
|
27
|
+
except httpx.RequestError as e:
|
|
28
|
+
if attempt == max_retries - 1:
|
|
29
|
+
logger.error(f"Failed to fetch {url} after {max_retries} attempts: {e}")
|
|
30
|
+
raise
|
|
31
|
+
wait_time = backoff_factor**attempt
|
|
32
|
+
logger.warning(f"Request failed (attempt {attempt + 1}), retrying in {wait_time} seconds...")
|
|
33
|
+
sleep(wait_time)
|
|
34
|
+
except httpx.HTTPStatusError as e:
|
|
35
|
+
logger.error(f"HTTP error for {url}: {e.response.status_code} - {e.response.text}")
|
|
36
|
+
raise
|
|
37
|
+
|
|
38
|
+
raise httpx.RequestError(f"Failed to fetch {url} after {max_retries} attempts")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
async def async_fetch_with_retry(
|
|
42
|
+
url: str,
|
|
43
|
+
client: Optional[httpx.AsyncClient] = None,
|
|
44
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
45
|
+
backoff_factor: int = DEFAULT_BACKOFF_FACTOR,
|
|
46
|
+
proxy: Optional[str] = None,
|
|
47
|
+
) -> httpx.Response:
|
|
48
|
+
"""Asynchronous HTTP GET with retry logic."""
|
|
49
|
+
|
|
50
|
+
async def _fetch():
|
|
51
|
+
if client is None:
|
|
52
|
+
client_args = {"proxy": proxy} if proxy else {}
|
|
53
|
+
async with httpx.AsyncClient(**client_args) as local_client: # type: ignore
|
|
54
|
+
return await local_client.get(url)
|
|
55
|
+
else:
|
|
56
|
+
return await client.get(url)
|
|
57
|
+
|
|
58
|
+
for attempt in range(max_retries):
|
|
59
|
+
try:
|
|
60
|
+
response = await _fetch()
|
|
61
|
+
response.raise_for_status()
|
|
62
|
+
return response
|
|
63
|
+
except httpx.RequestError as e:
|
|
64
|
+
if attempt == max_retries - 1:
|
|
65
|
+
logger.error(f"Failed to fetch {url} after {max_retries} attempts: {e}")
|
|
66
|
+
raise
|
|
67
|
+
wait_time = backoff_factor**attempt
|
|
68
|
+
logger.warning(f"Request failed (attempt {attempt + 1}), retrying in {wait_time} seconds...")
|
|
69
|
+
await asyncio.sleep(wait_time)
|
|
70
|
+
except httpx.HTTPStatusError as e:
|
|
71
|
+
logger.error(f"HTTP error for {url}: {e.response.status_code} - {e.response.text}")
|
|
72
|
+
raise
|
|
73
|
+
|
|
74
|
+
raise httpx.RequestError(f"Failed to fetch {url} after {max_retries} attempts")
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Any, Dict, Optional, Union, get_args, get_origin
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
from agno.utils.log import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def is_origin_union_type(origin: Any) -> bool:
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
if sys.version_info.minor >= 10:
|
|
13
|
+
from types import UnionType # type: ignore
|
|
14
|
+
|
|
15
|
+
return origin in [Union, UnionType]
|
|
16
|
+
|
|
17
|
+
return origin is Union
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_json_type_for_py_type(arg: str) -> str:
|
|
21
|
+
"""
|
|
22
|
+
Get the JSON schema type for a given type.
|
|
23
|
+
:param arg: The type to get the JSON schema type for.
|
|
24
|
+
:return: The JSON schema type.
|
|
25
|
+
"""
|
|
26
|
+
# log_info(f"Getting JSON type for: {arg}")
|
|
27
|
+
if arg in ("int", "float", "complex", "Decimal"):
|
|
28
|
+
return "number"
|
|
29
|
+
elif arg in ("str", "string"):
|
|
30
|
+
return "string"
|
|
31
|
+
elif arg in ("bool", "boolean"):
|
|
32
|
+
return "boolean"
|
|
33
|
+
elif arg in ("NoneType", "None"):
|
|
34
|
+
return "null"
|
|
35
|
+
elif arg in ("list", "tuple", "set", "frozenset"):
|
|
36
|
+
return "array"
|
|
37
|
+
elif arg in ("dict", "mapping"):
|
|
38
|
+
return "object"
|
|
39
|
+
|
|
40
|
+
# If the type is not recognized, return "object"
|
|
41
|
+
return "object"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def inline_pydantic_schema(schema: Dict[str, Any]) -> Dict[str, Any]:
|
|
45
|
+
"""
|
|
46
|
+
Recursively inline Pydantic model schemas by replacing $ref with actual schema.
|
|
47
|
+
"""
|
|
48
|
+
if not isinstance(schema, dict):
|
|
49
|
+
return schema
|
|
50
|
+
|
|
51
|
+
def resolve_ref(ref: str, defs: Dict[str, Any]) -> Dict[str, Any]:
|
|
52
|
+
"""Resolve a $ref to its actual schema."""
|
|
53
|
+
if not ref.startswith("#/$defs/"):
|
|
54
|
+
return {"type": "object"} # Fallback for external refs
|
|
55
|
+
|
|
56
|
+
def_name = ref.split("/")[-1]
|
|
57
|
+
if def_name in defs:
|
|
58
|
+
return defs[def_name]
|
|
59
|
+
return {"type": "object"} # Fallback if definition not found
|
|
60
|
+
|
|
61
|
+
def process_schema(s: Dict[str, Any], defs: Dict[str, Any]) -> Dict[str, Any]:
|
|
62
|
+
"""Process a schema dictionary, resolving all references."""
|
|
63
|
+
if not isinstance(s, dict):
|
|
64
|
+
return s
|
|
65
|
+
|
|
66
|
+
# Handle $ref
|
|
67
|
+
if "$ref" in s:
|
|
68
|
+
return resolve_ref(s["$ref"], defs)
|
|
69
|
+
|
|
70
|
+
# Create a new dict to avoid modifying the input
|
|
71
|
+
result = s.copy()
|
|
72
|
+
|
|
73
|
+
# Handle arrays
|
|
74
|
+
if "items" in result:
|
|
75
|
+
result["items"] = process_schema(result["items"], defs)
|
|
76
|
+
|
|
77
|
+
# Handle object properties
|
|
78
|
+
if "properties" in result:
|
|
79
|
+
for prop_name, prop_schema in result["properties"].items():
|
|
80
|
+
result["properties"][prop_name] = process_schema(prop_schema, defs)
|
|
81
|
+
|
|
82
|
+
# Handle anyOf (for Union types)
|
|
83
|
+
if "anyOf" in result:
|
|
84
|
+
result["anyOf"] = [process_schema(sub_schema, defs) for sub_schema in result["anyOf"]]
|
|
85
|
+
|
|
86
|
+
# Handle allOf (for inheritance)
|
|
87
|
+
if "allOf" in result:
|
|
88
|
+
result["allOf"] = [process_schema(sub_schema, defs) for sub_schema in result["allOf"]]
|
|
89
|
+
|
|
90
|
+
# Handle additionalProperties
|
|
91
|
+
if "additionalProperties" in result:
|
|
92
|
+
result["additionalProperties"] = process_schema(result["additionalProperties"], defs)
|
|
93
|
+
|
|
94
|
+
# Handle propertyNames
|
|
95
|
+
if "propertyNames" in result:
|
|
96
|
+
result["propertyNames"] = process_schema(result["propertyNames"], defs)
|
|
97
|
+
|
|
98
|
+
return result
|
|
99
|
+
|
|
100
|
+
# Store definitions for later use
|
|
101
|
+
definitions = schema.pop("$defs", {})
|
|
102
|
+
|
|
103
|
+
# First, resolve any nested references in definitions
|
|
104
|
+
resolved_definitions = {}
|
|
105
|
+
for def_name, def_schema in definitions.items():
|
|
106
|
+
resolved_definitions[def_name] = process_schema(def_schema, definitions)
|
|
107
|
+
|
|
108
|
+
# Process the main schema with resolved definitions
|
|
109
|
+
result = process_schema(schema, resolved_definitions)
|
|
110
|
+
|
|
111
|
+
# Remove any remaining definitions
|
|
112
|
+
if "$defs" in result:
|
|
113
|
+
del result["$defs"]
|
|
114
|
+
|
|
115
|
+
return result
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def get_json_schema_for_arg(type_hint: Any) -> Optional[Dict[str, Any]]:
|
|
119
|
+
# log_info(f"Getting JSON schema for arg: {t}")
|
|
120
|
+
type_args = get_args(type_hint)
|
|
121
|
+
# log_info(f"Type args: {type_args}")
|
|
122
|
+
type_origin = get_origin(type_hint)
|
|
123
|
+
# log_info(f"Type origin: {type_origin}")
|
|
124
|
+
if type_origin is not None:
|
|
125
|
+
if type_origin in (list, tuple, set, frozenset):
|
|
126
|
+
json_schema_for_items = get_json_schema_for_arg(type_args[0]) if type_args else {"type": "string"}
|
|
127
|
+
return {"type": "array", "items": json_schema_for_items}
|
|
128
|
+
elif type_origin is dict:
|
|
129
|
+
# Handle both key and value types for dictionaries
|
|
130
|
+
key_schema = get_json_schema_for_arg(type_args[0]) if type_args else {"type": "string"}
|
|
131
|
+
value_schema = get_json_schema_for_arg(type_args[1]) if len(type_args) > 1 else {"type": "string"}
|
|
132
|
+
return {"type": "object", "propertyNames": key_schema, "additionalProperties": value_schema}
|
|
133
|
+
elif is_origin_union_type(type_origin):
|
|
134
|
+
types = []
|
|
135
|
+
for arg in type_args:
|
|
136
|
+
try:
|
|
137
|
+
schema = get_json_schema_for_arg(arg)
|
|
138
|
+
if schema:
|
|
139
|
+
types.append(schema)
|
|
140
|
+
except Exception:
|
|
141
|
+
continue
|
|
142
|
+
return {"anyOf": types} if types else None
|
|
143
|
+
|
|
144
|
+
if isinstance(type_hint, type) and issubclass(type_hint, Enum):
|
|
145
|
+
enum_values = [member.value for member in type_hint]
|
|
146
|
+
return {"type": "string", "enum": enum_values}
|
|
147
|
+
|
|
148
|
+
if isinstance(type_hint, type) and issubclass(type_hint, BaseModel):
|
|
149
|
+
# Get the schema and inline it
|
|
150
|
+
schema = type_hint.model_json_schema()
|
|
151
|
+
return inline_pydantic_schema(schema) # type: ignore
|
|
152
|
+
|
|
153
|
+
if hasattr(type_hint, "__dataclass_fields__"):
|
|
154
|
+
# Convert dataclass to JSON schema
|
|
155
|
+
properties = {}
|
|
156
|
+
required = []
|
|
157
|
+
|
|
158
|
+
for field_name, field in type_hint.__dataclass_fields__.items():
|
|
159
|
+
field_type = field.type
|
|
160
|
+
field_schema = get_json_schema_for_arg(field_type)
|
|
161
|
+
|
|
162
|
+
if (
|
|
163
|
+
field_schema
|
|
164
|
+
and "anyOf" in field_schema
|
|
165
|
+
and any(schema["type"] == "null" for schema in field_schema["anyOf"])
|
|
166
|
+
):
|
|
167
|
+
field_schema["type"] = next(
|
|
168
|
+
schema["type"] for schema in field_schema["anyOf"] if schema["type"] != "null"
|
|
169
|
+
)
|
|
170
|
+
field_schema.pop("anyOf")
|
|
171
|
+
else:
|
|
172
|
+
required.append(field_name)
|
|
173
|
+
|
|
174
|
+
if field_schema:
|
|
175
|
+
properties[field_name] = field_schema
|
|
176
|
+
|
|
177
|
+
arg_json_schema = {"type": "object", "properties": properties, "additionalProperties": False}
|
|
178
|
+
|
|
179
|
+
if required:
|
|
180
|
+
arg_json_schema["required"] = required
|
|
181
|
+
return arg_json_schema
|
|
182
|
+
|
|
183
|
+
json_schema: Dict[str, Any] = {"type": get_json_type_for_py_type(type_hint.__name__)}
|
|
184
|
+
if json_schema["type"] == "object":
|
|
185
|
+
json_schema["properties"] = {}
|
|
186
|
+
json_schema["additionalProperties"] = False
|
|
187
|
+
return json_schema
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def get_json_schema(
|
|
191
|
+
type_hints: Dict[str, Any], param_descriptions: Optional[Dict[str, str]] = None, strict: bool = False
|
|
192
|
+
) -> Dict[str, Any]:
|
|
193
|
+
json_schema: Dict[str, Any] = {
|
|
194
|
+
"type": "object",
|
|
195
|
+
"properties": {},
|
|
196
|
+
}
|
|
197
|
+
if strict:
|
|
198
|
+
json_schema["additionalProperties"] = False
|
|
199
|
+
|
|
200
|
+
# We only include the fields in the type_hints dict
|
|
201
|
+
for parameter_name, type_hint in type_hints.items():
|
|
202
|
+
# log_info(f"Parsing arg: {k} | {v}")
|
|
203
|
+
if parameter_name == "return":
|
|
204
|
+
continue
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
# Check if type is Optional (Union with NoneType)
|
|
208
|
+
type_origin = get_origin(type_hint)
|
|
209
|
+
type_args = get_args(type_hint)
|
|
210
|
+
is_optional = type_origin is Union and len(type_args) == 2 and any(arg is type(None) for arg in type_args)
|
|
211
|
+
|
|
212
|
+
# Get the actual type if it's Optional
|
|
213
|
+
if is_optional:
|
|
214
|
+
type_hint = next(arg for arg in type_args if arg is not type(None))
|
|
215
|
+
|
|
216
|
+
if type_hint:
|
|
217
|
+
arg_json_schema = get_json_schema_for_arg(type_hint)
|
|
218
|
+
else:
|
|
219
|
+
arg_json_schema = {}
|
|
220
|
+
|
|
221
|
+
if arg_json_schema is not None:
|
|
222
|
+
# Add description
|
|
223
|
+
if param_descriptions and parameter_name in param_descriptions and param_descriptions[parameter_name]:
|
|
224
|
+
arg_json_schema["description"] = param_descriptions[parameter_name]
|
|
225
|
+
|
|
226
|
+
json_schema["properties"][parameter_name] = arg_json_schema
|
|
227
|
+
|
|
228
|
+
else:
|
|
229
|
+
logger.warning(f"Could not parse argument {parameter_name} of type {type_hint}")
|
|
230
|
+
except Exception as e:
|
|
231
|
+
logger.error(f"Error processing argument {parameter_name}: {str(e)}")
|
|
232
|
+
continue
|
|
233
|
+
|
|
234
|
+
return json_schema
|
agno/utils/knowledge.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Optional, Union
|
|
2
|
+
|
|
3
|
+
from agno.filters import FilterExpr
|
|
4
|
+
from agno.utils.log import log_info
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_agentic_or_user_search_filters(
|
|
8
|
+
filters: Optional[Dict[str, Any]], effective_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]]
|
|
9
|
+
) -> Dict[str, Any]:
|
|
10
|
+
"""Helper function to determine the final filters to use for the search.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
filters: Filters passed by the agent.
|
|
14
|
+
effective_filters: Filters passed by user.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Dict[str, Any]: The final filters to use for the search.
|
|
18
|
+
"""
|
|
19
|
+
search_filters = None
|
|
20
|
+
|
|
21
|
+
# If agentic filters exist and manual filters (passed by user) do not, use agentic filters
|
|
22
|
+
if filters and not effective_filters:
|
|
23
|
+
search_filters = filters
|
|
24
|
+
|
|
25
|
+
# If both agentic filters exist and manual filters (passed by user) exist, use manual filters (give priority to user and override)
|
|
26
|
+
if filters and effective_filters:
|
|
27
|
+
if isinstance(effective_filters, dict):
|
|
28
|
+
search_filters = effective_filters
|
|
29
|
+
elif isinstance(effective_filters, list):
|
|
30
|
+
# If effective_filters is a list (likely List[FilterExpr]), convert both filters and effective_filters to a dict if possible, otherwise raise
|
|
31
|
+
raise ValueError(
|
|
32
|
+
"Merging dict and list of filters is not supported; effective_filters should be a dict for search compatibility."
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
log_info(f"Filters used by Agent: {search_filters}")
|
|
36
|
+
return search_filters or {}
|
agno/utils/location.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from agno.utils.log import log_warning
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_location() -> Dict[str, Any]:
|
|
9
|
+
"""Get approximate location using IP geolocation."""
|
|
10
|
+
try:
|
|
11
|
+
response = requests.get("https://api.ipify.org?format=json", timeout=5)
|
|
12
|
+
ip = response.json()["ip"]
|
|
13
|
+
response = requests.get(f"http://ip-api.com/json/{ip}", timeout=5)
|
|
14
|
+
if response.status_code == 200:
|
|
15
|
+
data = response.json()
|
|
16
|
+
return {"city": data.get("city"), "region": data.get("region"), "country": data.get("country")}
|
|
17
|
+
except Exception as e:
|
|
18
|
+
log_warning(f"Failed to get location: {e}")
|
|
19
|
+
return {}
|
agno/utils/log.py
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from functools import lru_cache
|
|
3
|
+
from os import getenv
|
|
4
|
+
from typing import Any, Literal, Optional
|
|
5
|
+
|
|
6
|
+
from rich.logging import RichHandler
|
|
7
|
+
from rich.text import Text
|
|
8
|
+
|
|
9
|
+
LOGGER_NAME = "agno"
|
|
10
|
+
TEAM_LOGGER_NAME = f"{LOGGER_NAME}-team"
|
|
11
|
+
WORKFLOW_LOGGER_NAME = f"{LOGGER_NAME}-workflow"
|
|
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)
|
|
81
|
+
|
|
82
|
+
# https://rich.readthedocs.io/en/latest/reference/logging.html#rich.logging.RichHandler
|
|
83
|
+
# https://rich.readthedocs.io/en/latest/logging.html#handle-exceptions
|
|
84
|
+
rich_handler = ColoredRichHandler(
|
|
85
|
+
show_time=False,
|
|
86
|
+
rich_tracebacks=False,
|
|
87
|
+
show_path=True if getenv("AGNO_API_RUNTIME") == "dev" else False,
|
|
88
|
+
tracebacks_show_locals=False,
|
|
89
|
+
source_type=source_type or "agent",
|
|
90
|
+
)
|
|
91
|
+
rich_handler.setFormatter(
|
|
92
|
+
logging.Formatter(
|
|
93
|
+
fmt="%(message)s",
|
|
94
|
+
datefmt="[%X]",
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
_logger.addHandler(rich_handler)
|
|
99
|
+
_logger.setLevel(logging.INFO)
|
|
100
|
+
_logger.propagate = False
|
|
101
|
+
return _logger
|
|
102
|
+
|
|
103
|
+
|
|
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")
|
|
107
|
+
|
|
108
|
+
# Set the default logger to the agent logger
|
|
109
|
+
logger: AgnoLogger = agent_logger
|
|
110
|
+
|
|
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}")
|
|
121
|
+
_logger.setLevel(logging.DEBUG)
|
|
122
|
+
|
|
123
|
+
global debug_on
|
|
124
|
+
debug_on = True
|
|
125
|
+
|
|
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}")
|
|
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
|