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/hooks.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from agno.eval.base import BaseEval
|
|
6
|
+
|
|
7
|
+
from agno.guardrails.base import BaseGuardrail
|
|
8
|
+
from agno.hooks.decorator import HOOK_RUN_IN_BACKGROUND_ATTR
|
|
9
|
+
from agno.utils.log import log_warning
|
|
10
|
+
|
|
11
|
+
# Keys that should be deep copied for background hooks to prevent race conditions
|
|
12
|
+
BACKGROUND_HOOK_COPY_KEYS = frozenset(
|
|
13
|
+
{"run_input", "run_context", "run_output", "session_state", "dependencies", "metadata"}
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def copy_args_for_background(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
18
|
+
"""
|
|
19
|
+
Create a copy of hook arguments for background execution.
|
|
20
|
+
|
|
21
|
+
This deep copies run_input, run_context, run_output, session_state, dependencies,
|
|
22
|
+
and metadata to prevent race conditions when hooks run in the background.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
args: The original arguments dictionary
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
A new dictionary with copied values for sensitive keys
|
|
29
|
+
"""
|
|
30
|
+
copied_args = {}
|
|
31
|
+
for key, value in args.items():
|
|
32
|
+
if key in BACKGROUND_HOOK_COPY_KEYS and value is not None:
|
|
33
|
+
try:
|
|
34
|
+
copied_args[key] = deepcopy(value)
|
|
35
|
+
except Exception:
|
|
36
|
+
# If deepcopy fails (e.g., for non-copyable objects), use the original
|
|
37
|
+
log_warning(f"Could not deepcopy {key} for background hook, using original reference")
|
|
38
|
+
copied_args[key] = value
|
|
39
|
+
else:
|
|
40
|
+
copied_args[key] = value
|
|
41
|
+
return copied_args
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def should_run_hook_in_background(hook: Callable[..., Any]) -> bool:
|
|
45
|
+
"""
|
|
46
|
+
Check if a hook function should run in background.
|
|
47
|
+
|
|
48
|
+
This checks for the _agno_run_in_background attribute set by the @hook decorator.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
hook: The hook function to check
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
True if the hook is decorated with @hook(run_in_background=True)
|
|
55
|
+
"""
|
|
56
|
+
return getattr(hook, HOOK_RUN_IN_BACKGROUND_ATTR, False)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def normalize_pre_hooks(
|
|
60
|
+
hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, "BaseEval"]]],
|
|
61
|
+
async_mode: bool = False,
|
|
62
|
+
) -> Optional[List[Callable[..., Any]]]:
|
|
63
|
+
"""Normalize pre-hooks to a list format.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
hooks: List of hook functions, guardrails, or eval instances
|
|
67
|
+
async_mode: Whether to use async versions of methods
|
|
68
|
+
"""
|
|
69
|
+
from agno.eval.base import BaseEval
|
|
70
|
+
|
|
71
|
+
result_hooks: List[Callable[..., Any]] = []
|
|
72
|
+
|
|
73
|
+
if hooks is not None:
|
|
74
|
+
for hook in hooks:
|
|
75
|
+
if isinstance(hook, BaseGuardrail):
|
|
76
|
+
if async_mode:
|
|
77
|
+
result_hooks.append(hook.async_check)
|
|
78
|
+
else:
|
|
79
|
+
result_hooks.append(hook.check)
|
|
80
|
+
elif isinstance(hook, BaseEval):
|
|
81
|
+
# Extract pre_check method
|
|
82
|
+
method = hook.async_pre_check if async_mode else hook.pre_check
|
|
83
|
+
|
|
84
|
+
from functools import partial
|
|
85
|
+
|
|
86
|
+
wrapped = partial(method)
|
|
87
|
+
wrapped.__name__ = method.__name__ # type: ignore
|
|
88
|
+
setattr(wrapped, HOOK_RUN_IN_BACKGROUND_ATTR, getattr(hook, "run_in_background", False))
|
|
89
|
+
result_hooks.append(wrapped)
|
|
90
|
+
else:
|
|
91
|
+
# Check if the hook is async and used within sync methods
|
|
92
|
+
if not async_mode:
|
|
93
|
+
import asyncio
|
|
94
|
+
|
|
95
|
+
if asyncio.iscoroutinefunction(hook):
|
|
96
|
+
raise ValueError(
|
|
97
|
+
f"Cannot use {hook.__name__} (an async hook) with `run()`. Use `arun()` instead."
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
result_hooks.append(hook)
|
|
101
|
+
return result_hooks if result_hooks else None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def normalize_post_hooks(
|
|
105
|
+
hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, "BaseEval"]]],
|
|
106
|
+
async_mode: bool = False,
|
|
107
|
+
) -> Optional[List[Callable[..., Any]]]:
|
|
108
|
+
"""Normalize post-hooks to a list format.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
hooks: List of hook functions, guardrails, or eval instances
|
|
112
|
+
async_mode: Whether to use async versions of methods
|
|
113
|
+
"""
|
|
114
|
+
from agno.eval.base import BaseEval
|
|
115
|
+
|
|
116
|
+
result_hooks: List[Callable[..., Any]] = []
|
|
117
|
+
|
|
118
|
+
if hooks is not None:
|
|
119
|
+
for hook in hooks:
|
|
120
|
+
if isinstance(hook, BaseGuardrail):
|
|
121
|
+
if async_mode:
|
|
122
|
+
result_hooks.append(hook.async_check)
|
|
123
|
+
else:
|
|
124
|
+
result_hooks.append(hook.check)
|
|
125
|
+
elif isinstance(hook, BaseEval):
|
|
126
|
+
# Extract post_check method
|
|
127
|
+
method = hook.async_post_check if async_mode else hook.post_check # type: ignore[assignment]
|
|
128
|
+
|
|
129
|
+
from functools import partial
|
|
130
|
+
|
|
131
|
+
wrapped = partial(method)
|
|
132
|
+
wrapped.__name__ = method.__name__ # type: ignore
|
|
133
|
+
setattr(wrapped, HOOK_RUN_IN_BACKGROUND_ATTR, getattr(hook, "run_in_background", False))
|
|
134
|
+
result_hooks.append(wrapped)
|
|
135
|
+
else:
|
|
136
|
+
# Check if the hook is async and used within sync methods
|
|
137
|
+
if not async_mode:
|
|
138
|
+
import asyncio
|
|
139
|
+
|
|
140
|
+
if asyncio.iscoroutinefunction(hook):
|
|
141
|
+
raise ValueError(
|
|
142
|
+
f"Cannot use {hook.__name__} (an async hook) with `run()`. Use `arun()` instead."
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
result_hooks.append(hook)
|
|
146
|
+
return result_hooks if result_hooks else None
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def filter_hook_args(hook: Callable[..., Any], all_args: Dict[str, Any]) -> Dict[str, Any]:
|
|
150
|
+
"""Filter arguments to only include those that the hook function accepts."""
|
|
151
|
+
import inspect
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
sig = inspect.signature(hook)
|
|
155
|
+
accepted_params = set(sig.parameters.keys())
|
|
156
|
+
|
|
157
|
+
has_var_keyword = any(param.kind == inspect.Parameter.VAR_KEYWORD for param in sig.parameters.values())
|
|
158
|
+
|
|
159
|
+
# If the function has **kwargs, pass all arguments
|
|
160
|
+
if has_var_keyword:
|
|
161
|
+
return all_args
|
|
162
|
+
|
|
163
|
+
# Otherwise, filter to only include accepted parameters
|
|
164
|
+
filtered_args = {key: value for key, value in all_args.items() if key in accepted_params}
|
|
165
|
+
|
|
166
|
+
return filtered_args
|
|
167
|
+
|
|
168
|
+
except Exception as e:
|
|
169
|
+
log_warning(f"Could not inspect hook signature, passing all arguments: {e}")
|
|
170
|
+
# If signature inspection fails, pass all arguments as fallback
|
|
171
|
+
return all_args
|
agno/utils/http.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
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
|
+
# Global httpx clients for resource efficiency
|
|
14
|
+
# These are shared across all models to reuse connection pools and avoid resource leaks.
|
|
15
|
+
# Consumers can override these at application startup using set_default_sync_client()
|
|
16
|
+
# and set_default_async_client() to customize limits, timeouts, proxies, etc.
|
|
17
|
+
_global_sync_client: Optional[httpx.Client] = None
|
|
18
|
+
_global_async_client: Optional[httpx.AsyncClient] = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_default_sync_client() -> httpx.Client:
|
|
22
|
+
"""Get or create the global synchronous httpx client.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
A singleton httpx.Client instance with default limits.
|
|
26
|
+
"""
|
|
27
|
+
global _global_sync_client
|
|
28
|
+
if _global_sync_client is None or _global_sync_client.is_closed:
|
|
29
|
+
_global_sync_client = httpx.Client(
|
|
30
|
+
limits=httpx.Limits(max_connections=1000, max_keepalive_connections=200), http2=True, follow_redirects=True
|
|
31
|
+
)
|
|
32
|
+
return _global_sync_client
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_default_async_client() -> httpx.AsyncClient:
|
|
36
|
+
"""Get or create the global asynchronous httpx client.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
A singleton httpx.AsyncClient instance with default limits.
|
|
40
|
+
"""
|
|
41
|
+
global _global_async_client
|
|
42
|
+
if _global_async_client is None or _global_async_client.is_closed:
|
|
43
|
+
_global_async_client = httpx.AsyncClient(
|
|
44
|
+
limits=httpx.Limits(max_connections=1000, max_keepalive_connections=200), http2=True, follow_redirects=True
|
|
45
|
+
)
|
|
46
|
+
return _global_async_client
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def close_sync_client() -> None:
|
|
50
|
+
"""Closes the global sync httpx client.
|
|
51
|
+
|
|
52
|
+
Should be called during application shutdown.
|
|
53
|
+
"""
|
|
54
|
+
global _global_sync_client
|
|
55
|
+
if _global_sync_client is not None and not _global_sync_client.is_closed:
|
|
56
|
+
_global_sync_client.close()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
async def aclose_default_clients() -> None:
|
|
60
|
+
"""Asynchronously close the global httpx clients.
|
|
61
|
+
|
|
62
|
+
Should be called during application shutdown in async contexts.
|
|
63
|
+
"""
|
|
64
|
+
global _global_sync_client, _global_async_client
|
|
65
|
+
if _global_sync_client is not None and not _global_sync_client.is_closed:
|
|
66
|
+
_global_sync_client.close()
|
|
67
|
+
if _global_async_client is not None and not _global_async_client.is_closed:
|
|
68
|
+
await _global_async_client.aclose()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def set_default_sync_client(client: httpx.Client) -> None:
|
|
72
|
+
"""Set the global synchronous httpx client.
|
|
73
|
+
|
|
74
|
+
IMPORTANT: Call before creating any model instances. Models cache clients on first use.
|
|
75
|
+
|
|
76
|
+
Allows consumers to override the default httpx client with custom configuration
|
|
77
|
+
(e.g., custom limits, timeouts, proxies, SSL verification, etc.).
|
|
78
|
+
This is useful at application startup to customize how all models connect.
|
|
79
|
+
|
|
80
|
+
Example:
|
|
81
|
+
>>> import httpx
|
|
82
|
+
>>> from agno.utils.http import set_default_sync_client
|
|
83
|
+
>>> custom_client = httpx.Client(
|
|
84
|
+
... limits=httpx.Limits(max_connections=500),
|
|
85
|
+
... timeout=httpx.Timeout(30.0),
|
|
86
|
+
... verify=False # for dev environments
|
|
87
|
+
... )
|
|
88
|
+
>>> set_default_sync_client(custom_client)
|
|
89
|
+
>>> # All models will now use this custom client
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
client: An httpx.Client instance to use as the global sync client.
|
|
93
|
+
"""
|
|
94
|
+
global _global_sync_client
|
|
95
|
+
_global_sync_client = client
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def set_default_async_client(client: httpx.AsyncClient) -> None:
|
|
99
|
+
"""Set the global asynchronous httpx client.
|
|
100
|
+
|
|
101
|
+
IMPORTANT: Call before creating any model instances. Models cache clients on first use.
|
|
102
|
+
|
|
103
|
+
Allows consumers to override the default async httpx client with custom configuration
|
|
104
|
+
(e.g., custom limits, timeouts, proxies, SSL verification, etc.).
|
|
105
|
+
This is useful at application startup to customize how all models connect.
|
|
106
|
+
|
|
107
|
+
Example:
|
|
108
|
+
>>> import httpx
|
|
109
|
+
>>> from agno.utils.http import set_default_async_client
|
|
110
|
+
>>> custom_client = httpx.AsyncClient(
|
|
111
|
+
... limits=httpx.Limits(max_connections=500),
|
|
112
|
+
... timeout=httpx.Timeout(30.0),
|
|
113
|
+
... verify=False # for dev environments
|
|
114
|
+
... )
|
|
115
|
+
>>> set_default_async_client(custom_client)
|
|
116
|
+
>>> # All models will now use this custom client
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
client: An httpx.AsyncClient instance to use as the global async client.
|
|
120
|
+
"""
|
|
121
|
+
global _global_async_client
|
|
122
|
+
_global_async_client = client
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def fetch_with_retry(
|
|
126
|
+
url: str,
|
|
127
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
128
|
+
backoff_factor: int = DEFAULT_BACKOFF_FACTOR,
|
|
129
|
+
proxy: Optional[str] = None,
|
|
130
|
+
) -> httpx.Response:
|
|
131
|
+
"""Synchronous HTTP GET with retry logic."""
|
|
132
|
+
|
|
133
|
+
for attempt in range(max_retries):
|
|
134
|
+
try:
|
|
135
|
+
response = httpx.get(url, proxy=proxy) if proxy else httpx.get(url)
|
|
136
|
+
response.raise_for_status()
|
|
137
|
+
return response
|
|
138
|
+
except httpx.RequestError as e:
|
|
139
|
+
if attempt == max_retries - 1:
|
|
140
|
+
logger.error(f"Failed to fetch {url} after {max_retries} attempts: {e}")
|
|
141
|
+
raise
|
|
142
|
+
wait_time = backoff_factor**attempt
|
|
143
|
+
logger.warning("Connection error.")
|
|
144
|
+
sleep(wait_time)
|
|
145
|
+
except httpx.HTTPStatusError as e:
|
|
146
|
+
logger.error(f"HTTP error for {url}: {e.response.status_code} - {e.response.text}")
|
|
147
|
+
raise
|
|
148
|
+
|
|
149
|
+
raise httpx.RequestError(f"Failed to fetch {url} after {max_retries} attempts")
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
async def async_fetch_with_retry(
|
|
153
|
+
url: str,
|
|
154
|
+
client: Optional[httpx.AsyncClient] = None,
|
|
155
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
156
|
+
backoff_factor: int = DEFAULT_BACKOFF_FACTOR,
|
|
157
|
+
proxy: Optional[str] = None,
|
|
158
|
+
) -> httpx.Response:
|
|
159
|
+
"""Asynchronous HTTP GET with retry logic."""
|
|
160
|
+
|
|
161
|
+
async def _fetch():
|
|
162
|
+
if client is None:
|
|
163
|
+
client_args = {"proxy": proxy} if proxy else {}
|
|
164
|
+
async with httpx.AsyncClient(**client_args) as local_client: # type: ignore
|
|
165
|
+
return await local_client.get(url)
|
|
166
|
+
else:
|
|
167
|
+
return await client.get(url)
|
|
168
|
+
|
|
169
|
+
for attempt in range(max_retries):
|
|
170
|
+
try:
|
|
171
|
+
response = await _fetch()
|
|
172
|
+
response.raise_for_status()
|
|
173
|
+
return response
|
|
174
|
+
except httpx.RequestError as e:
|
|
175
|
+
if attempt == max_retries - 1:
|
|
176
|
+
logger.error(f"Failed to fetch {url} after {max_retries} attempts: {e}")
|
|
177
|
+
raise
|
|
178
|
+
wait_time = backoff_factor**attempt
|
|
179
|
+
logger.warning("Connection error.")
|
|
180
|
+
await asyncio.sleep(wait_time)
|
|
181
|
+
except httpx.HTTPStatusError as e:
|
|
182
|
+
logger.error(f"HTTP error for {url}: {e.response.status_code} - {e.response.text}")
|
|
183
|
+
raise
|
|
184
|
+
|
|
185
|
+
raise httpx.RequestError(f"Failed to fetch {url} after {max_retries} attempts")
|
agno/utils/json_schema.py
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
|
+
from enum import Enum
|
|
1
2
|
from typing import Any, Dict, Optional, Union, get_args, get_origin
|
|
2
3
|
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
3
6
|
from agno.utils.log import logger
|
|
4
7
|
|
|
5
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
|
+
|
|
6
20
|
def get_json_type_for_py_type(arg: str) -> str:
|
|
7
21
|
"""
|
|
8
22
|
Get the JSON schema type for a given type.
|
|
9
23
|
:param arg: The type to get the JSON schema type for.
|
|
10
24
|
:return: The JSON schema type.
|
|
11
25
|
"""
|
|
12
|
-
#
|
|
26
|
+
# log_info(f"Getting JSON type for: {arg}")
|
|
13
27
|
if arg in ("int", "float", "complex", "Decimal"):
|
|
14
28
|
return "number"
|
|
15
29
|
elif arg in ("str", "string"):
|
|
@@ -27,13 +41,86 @@ def get_json_type_for_py_type(arg: str) -> str:
|
|
|
27
41
|
return "object"
|
|
28
42
|
|
|
29
43
|
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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"]]
|
|
36
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}")
|
|
37
124
|
if type_origin is not None:
|
|
38
125
|
if type_origin in (list, tuple, set, frozenset):
|
|
39
126
|
json_schema_for_items = get_json_schema_for_arg(type_args[0]) if type_args else {"type": "string"}
|
|
@@ -43,19 +130,61 @@ def get_json_schema_for_arg(t: Any) -> Optional[Dict[str, Any]]:
|
|
|
43
130
|
key_schema = get_json_schema_for_arg(type_args[0]) if type_args else {"type": "string"}
|
|
44
131
|
value_schema = get_json_schema_for_arg(type_args[1]) if len(type_args) > 1 else {"type": "string"}
|
|
45
132
|
return {"type": "object", "propertyNames": key_schema, "additionalProperties": value_schema}
|
|
46
|
-
elif type_origin
|
|
133
|
+
elif is_origin_union_type(type_origin):
|
|
47
134
|
types = []
|
|
48
135
|
for arg in type_args:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
continue
|
|
136
|
+
try:
|
|
137
|
+
schema = get_json_schema_for_arg(arg)
|
|
138
|
+
if schema:
|
|
139
|
+
types.append(schema)
|
|
140
|
+
except Exception:
|
|
141
|
+
continue
|
|
56
142
|
return {"anyOf": types} if types else None
|
|
57
143
|
|
|
58
|
-
|
|
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
|
|
59
188
|
|
|
60
189
|
|
|
61
190
|
def get_json_schema(
|
|
@@ -68,45 +197,38 @@ def get_json_schema(
|
|
|
68
197
|
if strict:
|
|
69
198
|
json_schema["additionalProperties"] = False
|
|
70
199
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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":
|
|
74
204
|
continue
|
|
75
205
|
|
|
76
206
|
try:
|
|
77
207
|
# Check if type is Optional (Union with NoneType)
|
|
78
|
-
type_origin = get_origin(
|
|
79
|
-
type_args = get_args(
|
|
208
|
+
type_origin = get_origin(type_hint)
|
|
209
|
+
type_args = get_args(type_hint)
|
|
80
210
|
is_optional = type_origin is Union and len(type_args) == 2 and any(arg is type(None) for arg in type_args)
|
|
81
211
|
|
|
82
212
|
# Get the actual type if it's Optional
|
|
83
213
|
if is_optional:
|
|
84
|
-
|
|
214
|
+
type_hint = next(arg for arg in type_args if arg is not type(None))
|
|
85
215
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
arg_json_schema = get_json_schema_for_arg(v)
|
|
216
|
+
if type_hint:
|
|
217
|
+
arg_json_schema = get_json_schema_for_arg(type_hint)
|
|
89
218
|
else:
|
|
90
219
|
arg_json_schema = {}
|
|
91
220
|
|
|
92
221
|
if arg_json_schema is not None:
|
|
93
|
-
if is_optional:
|
|
94
|
-
# Handle null type for optional fields
|
|
95
|
-
if isinstance(arg_json_schema["type"], list):
|
|
96
|
-
arg_json_schema["type"].append("null")
|
|
97
|
-
else:
|
|
98
|
-
arg_json_schema["type"] = [arg_json_schema["type"], "null"]
|
|
99
|
-
|
|
100
222
|
# Add description
|
|
101
|
-
if param_descriptions and
|
|
102
|
-
arg_json_schema["description"] = param_descriptions[
|
|
223
|
+
if param_descriptions and parameter_name in param_descriptions and param_descriptions[parameter_name]:
|
|
224
|
+
arg_json_schema["description"] = param_descriptions[parameter_name]
|
|
103
225
|
|
|
104
|
-
json_schema["properties"][
|
|
226
|
+
json_schema["properties"][parameter_name] = arg_json_schema
|
|
105
227
|
|
|
106
228
|
else:
|
|
107
|
-
logger.warning(f"Could not parse argument {
|
|
229
|
+
logger.warning(f"Could not parse argument {parameter_name} of type {type_hint}")
|
|
108
230
|
except Exception as e:
|
|
109
|
-
logger.error(f"Error processing argument {
|
|
231
|
+
logger.error(f"Error processing argument {parameter_name}: {str(e)}")
|
|
110
232
|
continue
|
|
111
233
|
|
|
112
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 {}
|