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/media.py
CHANGED
|
@@ -1,52 +1,373 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import time
|
|
3
|
+
from enum import Enum
|
|
1
4
|
from pathlib import Path
|
|
5
|
+
from typing import List, Optional
|
|
2
6
|
|
|
3
|
-
import
|
|
7
|
+
import httpx
|
|
4
8
|
|
|
9
|
+
from agno.media import Audio, File, Image, Video
|
|
10
|
+
from agno.utils.log import log_info, log_warning
|
|
5
11
|
|
|
6
|
-
|
|
12
|
+
|
|
13
|
+
class SampleDataFileExtension(str, Enum):
|
|
14
|
+
DOCX = "docx"
|
|
15
|
+
PDF = "pdf"
|
|
16
|
+
TXT = "txt"
|
|
17
|
+
JSON = "json"
|
|
18
|
+
CSV = "csv"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def download_image(url: str, output_path: str) -> bool:
|
|
7
22
|
"""
|
|
8
23
|
Downloads an image from the specified URL and saves it to the given local path.
|
|
9
24
|
Parameters:
|
|
10
25
|
- url (str): URL of the image to download.
|
|
11
|
-
-
|
|
26
|
+
- output_path (str): Local filesystem path to save the image
|
|
12
27
|
"""
|
|
13
28
|
try:
|
|
14
29
|
# Send HTTP GET request to the image URL
|
|
15
|
-
response =
|
|
30
|
+
response = httpx.get(url)
|
|
16
31
|
response.raise_for_status() # Raise an exception for HTTP errors
|
|
17
32
|
|
|
18
33
|
# Check if the response contains image content
|
|
19
34
|
content_type = response.headers.get("Content-Type")
|
|
20
35
|
if not content_type or not content_type.startswith("image"):
|
|
21
|
-
|
|
36
|
+
log_warning(f"URL does not point to an image. Content-Type: {content_type}")
|
|
22
37
|
return False
|
|
23
38
|
|
|
24
|
-
path = Path(
|
|
39
|
+
path = Path(output_path)
|
|
25
40
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
26
41
|
|
|
27
42
|
# Write the image to the local file in binary mode
|
|
28
|
-
with open(
|
|
29
|
-
for chunk in response.
|
|
43
|
+
with open(output_path, "wb") as file:
|
|
44
|
+
for chunk in response.iter_bytes(chunk_size=8192):
|
|
30
45
|
if chunk:
|
|
31
46
|
file.write(chunk)
|
|
32
47
|
|
|
33
|
-
|
|
48
|
+
log_info(f"Image successfully downloaded and saved to '{output_path}'.")
|
|
34
49
|
return True
|
|
35
50
|
|
|
36
|
-
except
|
|
37
|
-
|
|
51
|
+
except httpx.HTTPError as e:
|
|
52
|
+
log_warning(f"Error downloading the image: {e}")
|
|
38
53
|
return False
|
|
39
54
|
except IOError as e:
|
|
40
|
-
|
|
55
|
+
log_warning(f"Error saving the image to '{output_path}': {e}")
|
|
41
56
|
return False
|
|
42
57
|
|
|
43
58
|
|
|
59
|
+
def download_audio(url: str, output_path: str) -> str:
|
|
60
|
+
"""Download audio from URL"""
|
|
61
|
+
response = httpx.get(url)
|
|
62
|
+
response.raise_for_status()
|
|
63
|
+
|
|
64
|
+
with open(output_path, "wb") as f:
|
|
65
|
+
for chunk in response.iter_bytes(chunk_size=8192):
|
|
66
|
+
f.write(chunk)
|
|
67
|
+
return output_path
|
|
68
|
+
|
|
69
|
+
|
|
44
70
|
def download_video(url: str, output_path: str) -> str:
|
|
45
71
|
"""Download video from URL"""
|
|
46
|
-
response =
|
|
72
|
+
response = httpx.get(url)
|
|
47
73
|
response.raise_for_status()
|
|
48
74
|
|
|
49
75
|
with open(output_path, "wb") as f:
|
|
50
|
-
for chunk in response.
|
|
76
|
+
for chunk in response.iter_bytes(chunk_size=8192):
|
|
51
77
|
f.write(chunk)
|
|
52
78
|
return output_path
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def download_file(url: str, output_path: str) -> None:
|
|
82
|
+
"""
|
|
83
|
+
Download a file from a given URL and save it to the specified path.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
url (str): The URL of the file to download
|
|
87
|
+
output_path (str): The local path where the file should be saved
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
httpx.HTTPError: If the download fails
|
|
91
|
+
"""
|
|
92
|
+
try:
|
|
93
|
+
response = httpx.get(url)
|
|
94
|
+
response.raise_for_status()
|
|
95
|
+
|
|
96
|
+
output_file = Path(output_path)
|
|
97
|
+
output_file.parent.mkdir(parents=True, exist_ok=True)
|
|
98
|
+
|
|
99
|
+
with open(output_file, "wb") as f:
|
|
100
|
+
for chunk in response.iter_bytes(chunk_size=8192):
|
|
101
|
+
if chunk:
|
|
102
|
+
f.write(chunk)
|
|
103
|
+
|
|
104
|
+
except httpx.HTTPError as e:
|
|
105
|
+
raise Exception(f"Failed to download file from {url}: {str(e)}")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def save_base64_data(base64_data: str, output_path: str) -> bool:
|
|
109
|
+
"""
|
|
110
|
+
Saves base64 string to the specified path as bytes.
|
|
111
|
+
"""
|
|
112
|
+
try:
|
|
113
|
+
# Decode the base64 string into bytes
|
|
114
|
+
decoded_data = base64.b64decode(base64_data)
|
|
115
|
+
except Exception as e:
|
|
116
|
+
raise Exception(f"An unexpected error occurred during base64 decoding: {e}")
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
path = Path(output_path)
|
|
120
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
121
|
+
|
|
122
|
+
# Write the bytes to the local file in binary mode
|
|
123
|
+
with open(path, "wb") as file:
|
|
124
|
+
file.write(decoded_data)
|
|
125
|
+
|
|
126
|
+
log_info(f"Data successfully saved to '{path}'.")
|
|
127
|
+
return True
|
|
128
|
+
except Exception as e:
|
|
129
|
+
raise Exception(f"An unexpected error occurred while saving data to '{output_path}': {e}")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def wait_for_media_ready(url: str, timeout: int = 120, interval: int = 5, verbose: bool = True) -> bool:
|
|
133
|
+
"""
|
|
134
|
+
Wait for media to be ready at URL by polling with HEAD requests.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
url (str): The URL to check for media availability
|
|
138
|
+
timeout (int): Maximum time to wait in seconds (default: 120)
|
|
139
|
+
interval (int): Seconds between each check (default: 5)
|
|
140
|
+
verbose (bool): Whether to print progress messages (default: True)
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
bool: True if media is ready, False if timeout reached
|
|
144
|
+
"""
|
|
145
|
+
max_attempts = timeout // interval
|
|
146
|
+
|
|
147
|
+
if verbose:
|
|
148
|
+
log_info("Media generated! Waiting for upload to complete...")
|
|
149
|
+
|
|
150
|
+
for attempt in range(max_attempts):
|
|
151
|
+
try:
|
|
152
|
+
response = httpx.head(url, timeout=10)
|
|
153
|
+
response.raise_for_status()
|
|
154
|
+
if verbose:
|
|
155
|
+
log_info(f"Media ready: {url}")
|
|
156
|
+
return True
|
|
157
|
+
except httpx.HTTPError:
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
if verbose and (attempt + 1) % 3 == 0:
|
|
161
|
+
log_info(f"Still processing... ({(attempt + 1) * interval}s elapsed)")
|
|
162
|
+
|
|
163
|
+
time.sleep(interval)
|
|
164
|
+
|
|
165
|
+
if verbose:
|
|
166
|
+
log_warning(f"Timeout waiting for media. Try this URL later: {url}")
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def download_knowledge_filters_sample_data(
|
|
171
|
+
num_files: int = 5, file_extension: SampleDataFileExtension = SampleDataFileExtension.DOCX
|
|
172
|
+
) -> List[str]:
|
|
173
|
+
"""
|
|
174
|
+
Download sample data files with configurable file extension.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
num_files (int): Number of files to download
|
|
178
|
+
file_extension (SampleDataFileExtension): File extension type (DOCX, PDF, TXT, JSON)
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
List[str]: List of paths to downloaded files
|
|
182
|
+
"""
|
|
183
|
+
file_paths = []
|
|
184
|
+
root_path = Path.cwd()
|
|
185
|
+
|
|
186
|
+
for i in range(1, num_files + 1):
|
|
187
|
+
if file_extension == SampleDataFileExtension.CSV:
|
|
188
|
+
filename = f"filters_{i}.csv"
|
|
189
|
+
else:
|
|
190
|
+
filename = f"cv_{i}.{file_extension.value}"
|
|
191
|
+
|
|
192
|
+
download_path = root_path / "cookbook" / "data" / filename
|
|
193
|
+
download_path.parent.mkdir(parents=True, exist_ok=True)
|
|
194
|
+
|
|
195
|
+
download_file(
|
|
196
|
+
f"https://agno-public.s3.us-east-1.amazonaws.com/demo_data/filters/{filename}", str(download_path)
|
|
197
|
+
)
|
|
198
|
+
file_paths.append(str(download_path))
|
|
199
|
+
return file_paths
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def reconstruct_image_from_dict(img_data):
|
|
203
|
+
"""
|
|
204
|
+
Reconstruct an Image object from dictionary data.
|
|
205
|
+
|
|
206
|
+
Handles both base64-encoded content (from database) and regular image data (url/filepath).
|
|
207
|
+
"""
|
|
208
|
+
try:
|
|
209
|
+
if isinstance(img_data, dict):
|
|
210
|
+
# If content is base64 string, decode it back to bytes
|
|
211
|
+
if "content" in img_data and isinstance(img_data["content"], str):
|
|
212
|
+
return Image.from_base64(
|
|
213
|
+
img_data["content"],
|
|
214
|
+
id=img_data.get("id"),
|
|
215
|
+
mime_type=img_data.get("mime_type"),
|
|
216
|
+
format=img_data.get("format"),
|
|
217
|
+
detail=img_data.get("detail"),
|
|
218
|
+
original_prompt=img_data.get("original_prompt"),
|
|
219
|
+
revised_prompt=img_data.get("revised_prompt"),
|
|
220
|
+
alt_text=img_data.get("alt_text"),
|
|
221
|
+
)
|
|
222
|
+
else:
|
|
223
|
+
# Regular image (filepath/url)
|
|
224
|
+
return Image(**img_data)
|
|
225
|
+
return img_data
|
|
226
|
+
except Exception as e:
|
|
227
|
+
log_warning(f"Failed to reconstruct image from dict: {e}")
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def reconstruct_video_from_dict(vid_data):
|
|
232
|
+
"""
|
|
233
|
+
Reconstruct a Video object from dictionary data.
|
|
234
|
+
|
|
235
|
+
Handles both base64-encoded content (from database) and regular video data (url/filepath).
|
|
236
|
+
"""
|
|
237
|
+
try:
|
|
238
|
+
if isinstance(vid_data, dict):
|
|
239
|
+
# If content is base64 string, decode it back to bytes
|
|
240
|
+
if "content" in vid_data and isinstance(vid_data["content"], str):
|
|
241
|
+
return Video.from_base64(
|
|
242
|
+
vid_data["content"],
|
|
243
|
+
id=vid_data.get("id"),
|
|
244
|
+
mime_type=vid_data.get("mime_type"),
|
|
245
|
+
format=vid_data.get("format"),
|
|
246
|
+
)
|
|
247
|
+
else:
|
|
248
|
+
# Regular video (filepath/url)
|
|
249
|
+
return Video(**vid_data)
|
|
250
|
+
return vid_data
|
|
251
|
+
except Exception as e:
|
|
252
|
+
log_warning(f"Failed to reconstruct video from dict: {e}")
|
|
253
|
+
return None
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def reconstruct_audio_from_dict(aud_data):
|
|
257
|
+
"""
|
|
258
|
+
Reconstruct an Audio object from dictionary data.
|
|
259
|
+
|
|
260
|
+
Handles both base64-encoded content (from database) and regular audio data (url/filepath).
|
|
261
|
+
"""
|
|
262
|
+
try:
|
|
263
|
+
if isinstance(aud_data, dict):
|
|
264
|
+
# If content is base64 string, decode it back to bytes
|
|
265
|
+
if "content" in aud_data and isinstance(aud_data["content"], str):
|
|
266
|
+
return Audio.from_base64(
|
|
267
|
+
aud_data["content"],
|
|
268
|
+
id=aud_data.get("id"),
|
|
269
|
+
mime_type=aud_data.get("mime_type"),
|
|
270
|
+
transcript=aud_data.get("transcript"),
|
|
271
|
+
expires_at=aud_data.get("expires_at"),
|
|
272
|
+
sample_rate=aud_data.get("sample_rate", 24000),
|
|
273
|
+
channels=aud_data.get("channels", 1),
|
|
274
|
+
)
|
|
275
|
+
else:
|
|
276
|
+
# Regular audio (filepath/url)
|
|
277
|
+
return Audio(**aud_data)
|
|
278
|
+
return aud_data
|
|
279
|
+
except Exception as e:
|
|
280
|
+
log_warning(f"Failed to reconstruct audio from dict: {e}")
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def reconstruct_file_from_dict(file_data):
|
|
285
|
+
"""
|
|
286
|
+
Reconstruct a File object from dictionary data.
|
|
287
|
+
|
|
288
|
+
Handles both base64-encoded content (from database) and regular file data (url/filepath).
|
|
289
|
+
"""
|
|
290
|
+
try:
|
|
291
|
+
if isinstance(file_data, dict):
|
|
292
|
+
# If content is base64 string, decode it back to bytes
|
|
293
|
+
if "content" in file_data and isinstance(file_data["content"], str):
|
|
294
|
+
file_obj = File.from_base64(
|
|
295
|
+
file_data["content"],
|
|
296
|
+
id=file_data.get("id"),
|
|
297
|
+
mime_type=file_data.get("mime_type"),
|
|
298
|
+
filename=file_data.get("filename"),
|
|
299
|
+
name=file_data.get("name"),
|
|
300
|
+
format=file_data.get("format"),
|
|
301
|
+
)
|
|
302
|
+
# Preserve additional fields that from_base64 doesn't handle
|
|
303
|
+
if file_data.get("size") is not None:
|
|
304
|
+
file_obj.size = file_data.get("size")
|
|
305
|
+
if file_data.get("file_type") is not None:
|
|
306
|
+
file_obj.file_type = file_data.get("file_type")
|
|
307
|
+
if file_data.get("filepath") is not None:
|
|
308
|
+
file_obj.filepath = file_data.get("filepath")
|
|
309
|
+
if file_data.get("url") is not None:
|
|
310
|
+
file_obj.url = file_data.get("url")
|
|
311
|
+
return file_obj
|
|
312
|
+
else:
|
|
313
|
+
# Regular file (filepath/url)
|
|
314
|
+
return File(**file_data)
|
|
315
|
+
return file_data
|
|
316
|
+
except Exception as e:
|
|
317
|
+
log_warning(f"Failed to reconstruct file from dict: {e}")
|
|
318
|
+
return None
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def reconstruct_images(images: Optional[List[dict]]) -> Optional[List[Image]]:
|
|
322
|
+
"""Reconstruct a list of Image objects from list of dictionaries.
|
|
323
|
+
|
|
324
|
+
Failed reconstructions are skipped with a warning logged.
|
|
325
|
+
"""
|
|
326
|
+
if not images:
|
|
327
|
+
return None
|
|
328
|
+
reconstructed = [reconstruct_image_from_dict(img_data) for img_data in images]
|
|
329
|
+
valid_images = [img for img in reconstructed if img is not None]
|
|
330
|
+
return valid_images if valid_images else None
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def reconstruct_videos(videos: Optional[List[dict]]) -> Optional[List[Video]]:
|
|
334
|
+
"""Reconstruct a list of Video objects from list of dictionaries.
|
|
335
|
+
|
|
336
|
+
Failed reconstructions are skipped with a warning logged.
|
|
337
|
+
"""
|
|
338
|
+
if not videos:
|
|
339
|
+
return None
|
|
340
|
+
reconstructed = [reconstruct_video_from_dict(vid_data) for vid_data in videos]
|
|
341
|
+
valid_videos = [vid for vid in reconstructed if vid is not None]
|
|
342
|
+
return valid_videos if valid_videos else None
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def reconstruct_audio_list(audio: Optional[List[dict]]) -> Optional[List[Audio]]:
|
|
346
|
+
"""Reconstruct a list of Audio objects from list of dictionaries.
|
|
347
|
+
|
|
348
|
+
Failed reconstructions are skipped with a warning logged.
|
|
349
|
+
"""
|
|
350
|
+
if not audio:
|
|
351
|
+
return None
|
|
352
|
+
reconstructed = [reconstruct_audio_from_dict(aud_data) for aud_data in audio]
|
|
353
|
+
valid_audio = [aud for aud in reconstructed if aud is not None]
|
|
354
|
+
return valid_audio if valid_audio else None
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def reconstruct_files(files: Optional[List[dict]]) -> Optional[List[File]]:
|
|
358
|
+
"""Reconstruct a list of File objects from list of dictionaries.
|
|
359
|
+
|
|
360
|
+
Failed reconstructions are skipped with a warning logged.
|
|
361
|
+
"""
|
|
362
|
+
if not files:
|
|
363
|
+
return None
|
|
364
|
+
reconstructed = [reconstruct_file_from_dict(file_data) for file_data in files]
|
|
365
|
+
valid_files = [f for f in reconstructed if f is not None]
|
|
366
|
+
return valid_files if valid_files else None
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def reconstruct_response_audio(audio: Optional[dict]) -> Optional[Audio]:
|
|
370
|
+
"""Reconstruct a single Audio object for response audio."""
|
|
371
|
+
if not audio:
|
|
372
|
+
return None
|
|
373
|
+
return reconstruct_audio_from_dict(audio)
|
agno/utils/merge_dict.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Dict
|
|
1
|
+
from typing import Any, Dict, List
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
def merge_dictionaries(a: Dict[str, Any], b: Dict[str, Any]) -> None:
|
|
@@ -18,3 +18,24 @@ def merge_dictionaries(a: Dict[str, Any], b: Dict[str, Any]) -> None:
|
|
|
18
18
|
merge_dictionaries(a[key], b[key])
|
|
19
19
|
else:
|
|
20
20
|
a[key] = b[key]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def merge_parallel_session_states(original_state: Dict[str, Any], modified_states: List[Dict[str, Any]]) -> None:
|
|
24
|
+
"""
|
|
25
|
+
Smart merge for parallel session states that only applies actual changes.
|
|
26
|
+
This prevents parallel steps from overwriting each other's changes.
|
|
27
|
+
"""
|
|
28
|
+
if not original_state or not modified_states:
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
# Collect all actual changes (keys where value differs from original)
|
|
32
|
+
all_changes = {}
|
|
33
|
+
for modified_state in modified_states:
|
|
34
|
+
if modified_state:
|
|
35
|
+
for key, value in modified_state.items():
|
|
36
|
+
if key not in original_state or original_state[key] != value:
|
|
37
|
+
all_changes[key] = value
|
|
38
|
+
|
|
39
|
+
# Apply all collected changes to the original state
|
|
40
|
+
for key, value in all_changes.items():
|
|
41
|
+
original_state[key] = value
|
agno/utils/message.py
CHANGED
|
@@ -1,19 +1,92 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
1
2
|
from typing import Dict, List, Union
|
|
2
3
|
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
3
6
|
from agno.models.message import Message
|
|
7
|
+
from agno.utils.log import log_debug
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def filter_tool_calls(messages: List[Message], max_tool_calls: int) -> None:
|
|
11
|
+
"""
|
|
12
|
+
Filter messages (in-place) to keep only the most recent N tool calls.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
messages: List of messages to filter (modified in-place)
|
|
16
|
+
max_tool_calls: Number of recent tool calls to keep
|
|
17
|
+
"""
|
|
18
|
+
# Count total tool calls
|
|
19
|
+
tool_call_count = sum(1 for m in messages if m.role == "tool")
|
|
20
|
+
|
|
21
|
+
# No filtering needed
|
|
22
|
+
if tool_call_count <= max_tool_calls:
|
|
23
|
+
return
|
|
24
|
+
|
|
25
|
+
# Collect tool_call_ids to keep (most recent N)
|
|
26
|
+
tool_call_ids_list: List[str] = []
|
|
27
|
+
for msg in reversed(messages):
|
|
28
|
+
if msg.role == "tool" and len(tool_call_ids_list) < max_tool_calls:
|
|
29
|
+
if msg.tool_call_id:
|
|
30
|
+
tool_call_ids_list.append(msg.tool_call_id)
|
|
4
31
|
|
|
32
|
+
tool_call_ids_to_keep: set[str] = set(tool_call_ids_list)
|
|
5
33
|
|
|
6
|
-
|
|
34
|
+
# Filter messages in-place
|
|
35
|
+
filtered_messages = []
|
|
36
|
+
for msg in messages:
|
|
37
|
+
if msg.role == "tool":
|
|
38
|
+
# Keep only tool results in our window
|
|
39
|
+
if msg.tool_call_id in tool_call_ids_to_keep:
|
|
40
|
+
filtered_messages.append(msg)
|
|
41
|
+
elif msg.role == "assistant" and msg.tool_calls:
|
|
42
|
+
# Filter tool_calls within the assistant message
|
|
43
|
+
# Use deepcopy to ensure complete isolation of the filtered message
|
|
44
|
+
filtered_msg = deepcopy(msg)
|
|
45
|
+
# Filter tool_calls
|
|
46
|
+
if filtered_msg.tool_calls is not None:
|
|
47
|
+
filtered_msg.tool_calls = [
|
|
48
|
+
tc for tc in filtered_msg.tool_calls if tc.get("id") in tool_call_ids_to_keep
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
if filtered_msg.tool_calls:
|
|
52
|
+
# Has tool_calls remaining, keep it
|
|
53
|
+
filtered_messages.append(filtered_msg)
|
|
54
|
+
# skip empty messages
|
|
55
|
+
elif filtered_msg.content:
|
|
56
|
+
filtered_msg.tool_calls = None
|
|
57
|
+
filtered_messages.append(filtered_msg)
|
|
58
|
+
else:
|
|
59
|
+
filtered_messages.append(msg)
|
|
60
|
+
|
|
61
|
+
messages[:] = filtered_messages
|
|
62
|
+
|
|
63
|
+
# Log filtering information
|
|
64
|
+
num_filtered = tool_call_count - len(tool_call_ids_to_keep)
|
|
65
|
+
log_debug(f"Filtered {num_filtered} tool calls, kept {len(tool_call_ids_to_keep)}")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def get_text_from_message(message: Union[List, Dict, str, Message, BaseModel]) -> str:
|
|
7
69
|
"""Return the user texts from the message"""
|
|
70
|
+
import json
|
|
8
71
|
|
|
9
72
|
if isinstance(message, str):
|
|
10
73
|
return message
|
|
74
|
+
if isinstance(message, BaseModel):
|
|
75
|
+
return message.model_dump_json(indent=2, exclude_none=True)
|
|
11
76
|
if isinstance(message, list):
|
|
12
77
|
text_messages = []
|
|
13
78
|
if len(message) == 0:
|
|
14
79
|
return ""
|
|
15
80
|
|
|
16
|
-
if
|
|
81
|
+
# Check if it's a list of Message objects
|
|
82
|
+
if isinstance(message[0], Message):
|
|
83
|
+
for m in message:
|
|
84
|
+
if isinstance(m, Message) and m.role == "user" and m.content is not None:
|
|
85
|
+
# Recursively extract text from the message content
|
|
86
|
+
content_text = get_text_from_message(m.content)
|
|
87
|
+
if content_text:
|
|
88
|
+
text_messages.append(content_text)
|
|
89
|
+
elif "type" in message[0]:
|
|
17
90
|
for m in message:
|
|
18
91
|
m_type = m.get("type")
|
|
19
92
|
if m_type is not None and isinstance(m_type, str):
|
|
@@ -38,6 +111,8 @@ def get_text_from_message(message: Union[List, Dict, str, Message]) -> str:
|
|
|
38
111
|
if isinstance(message, dict):
|
|
39
112
|
if "content" in message:
|
|
40
113
|
return get_text_from_message(message["content"])
|
|
114
|
+
else:
|
|
115
|
+
return json.dumps(message, indent=2)
|
|
41
116
|
if isinstance(message, Message) and message.content is not None:
|
|
42
117
|
return get_text_from_message(message.content)
|
|
43
118
|
return ""
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
|
|
3
|
+
from agno.models.message import Message
|
|
4
|
+
from agno.utils.log import log_warning
|
|
5
|
+
from agno.utils.openai import images_to_message
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def format_message(message: Message, compress_tool_results: bool = False) -> Dict[str, Any]:
|
|
9
|
+
"""
|
|
10
|
+
Format a message into the format expected by OpenAI.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
message (Message): The message to format.
|
|
14
|
+
compress_tool_results: Whether to compress tool results.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Dict[str, Any]: The formatted message.
|
|
18
|
+
"""
|
|
19
|
+
# Use compressed content for tool messages if compression is active
|
|
20
|
+
content = message.content
|
|
21
|
+
|
|
22
|
+
if message.role == "tool":
|
|
23
|
+
content = message.get_content(use_compressed_content=compress_tool_results)
|
|
24
|
+
|
|
25
|
+
message_dict: Dict[str, Any] = {
|
|
26
|
+
"role": message.role,
|
|
27
|
+
"content": content,
|
|
28
|
+
"name": message.name,
|
|
29
|
+
"tool_call_id": message.tool_call_id,
|
|
30
|
+
"tool_calls": message.tool_calls,
|
|
31
|
+
}
|
|
32
|
+
message_dict = {k: v for k, v in message_dict.items() if v is not None}
|
|
33
|
+
|
|
34
|
+
if message.images is not None and len(message.images) > 0:
|
|
35
|
+
# Ignore non-string message content
|
|
36
|
+
# because we assume that the images/audio are already added to the message
|
|
37
|
+
if isinstance(message.content, str):
|
|
38
|
+
message_dict["content"] = [{"type": "text", "text": message.content}]
|
|
39
|
+
message_dict["content"].extend(images_to_message(images=message.images))
|
|
40
|
+
|
|
41
|
+
if message.audio is not None and len(message.audio) > 0:
|
|
42
|
+
log_warning("Audio input is currently unsupported.")
|
|
43
|
+
|
|
44
|
+
if message.files is not None and len(message.files) > 0:
|
|
45
|
+
log_warning("File input is currently unsupported.")
|
|
46
|
+
|
|
47
|
+
if message.videos is not None and len(message.videos) > 0:
|
|
48
|
+
log_warning("Video input is currently unsupported.")
|
|
49
|
+
|
|
50
|
+
return message_dict
|