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
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
4
|
+
|
|
5
|
+
from agno.media import File, Image
|
|
6
|
+
from agno.models.message import Message
|
|
7
|
+
from agno.utils.log import log_error, log_warning
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from anthropic.types import (
|
|
11
|
+
TextBlock,
|
|
12
|
+
ToolUseBlock,
|
|
13
|
+
)
|
|
14
|
+
except ImportError:
|
|
15
|
+
raise ImportError("`anthropic` not installed. Please install using `pip install anthropic`")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class MCPToolConfiguration:
|
|
20
|
+
enabled: bool = True
|
|
21
|
+
allowed_tools: List[str] = field(default_factory=list)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class MCPServerConfiguration:
|
|
26
|
+
type: str
|
|
27
|
+
url: str
|
|
28
|
+
name: str
|
|
29
|
+
tool_configuration: Optional[MCPToolConfiguration] = None
|
|
30
|
+
authorization_token: Optional[str] = None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
ROLE_MAP = {
|
|
34
|
+
"system": "system",
|
|
35
|
+
"developer": "system",
|
|
36
|
+
"user": "user",
|
|
37
|
+
"assistant": "assistant",
|
|
38
|
+
"tool": "user",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _format_image_for_message(image: Image) -> Optional[Dict[str, Any]]:
|
|
43
|
+
"""
|
|
44
|
+
Add an image to a message by converting it to base64 encoded format.
|
|
45
|
+
"""
|
|
46
|
+
using_filetype = False
|
|
47
|
+
|
|
48
|
+
import base64
|
|
49
|
+
|
|
50
|
+
# 'imghdr' was deprecated in Python 3.11: https://docs.python.org/3/library/imghdr.html
|
|
51
|
+
# 'filetype' used as a fallback
|
|
52
|
+
try:
|
|
53
|
+
import imghdr
|
|
54
|
+
except ImportError:
|
|
55
|
+
try:
|
|
56
|
+
import filetype
|
|
57
|
+
|
|
58
|
+
using_filetype = True
|
|
59
|
+
except ImportError:
|
|
60
|
+
raise ImportError("`filetype` not installed. Please install using `pip install filetype`")
|
|
61
|
+
|
|
62
|
+
type_mapping = {
|
|
63
|
+
"jpeg": "image/jpeg",
|
|
64
|
+
"jpg": "image/jpeg",
|
|
65
|
+
"png": "image/png",
|
|
66
|
+
"gif": "image/gif",
|
|
67
|
+
"webp": "image/webp",
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
img_type = None
|
|
72
|
+
|
|
73
|
+
# Case 0: Image is an Anthropic uploaded file
|
|
74
|
+
if image.content is not None and hasattr(image.content, "id"):
|
|
75
|
+
content_bytes = image.content
|
|
76
|
+
|
|
77
|
+
# Case 1: Image is a URL
|
|
78
|
+
if image.url is not None:
|
|
79
|
+
content_bytes = image.get_content_bytes() # type: ignore
|
|
80
|
+
|
|
81
|
+
# If image URL has a suffix, use it as the type (without dot)
|
|
82
|
+
import os
|
|
83
|
+
from urllib.parse import urlparse
|
|
84
|
+
|
|
85
|
+
if image.url:
|
|
86
|
+
parsed_url = urlparse(image.url)
|
|
87
|
+
_, ext = os.path.splitext(parsed_url.path)
|
|
88
|
+
if ext:
|
|
89
|
+
img_type = ext.lstrip(".").lower()
|
|
90
|
+
|
|
91
|
+
# Case 2: Image is a local file path
|
|
92
|
+
elif image.filepath is not None:
|
|
93
|
+
from pathlib import Path
|
|
94
|
+
|
|
95
|
+
path = Path(image.filepath) if isinstance(image.filepath, str) else image.filepath
|
|
96
|
+
if path.exists() and path.is_file():
|
|
97
|
+
with open(image.filepath, "rb") as f:
|
|
98
|
+
content_bytes = f.read()
|
|
99
|
+
|
|
100
|
+
# If image file path has a suffix, use it as the type (without dot)
|
|
101
|
+
path_ext = path.suffix.lstrip(".")
|
|
102
|
+
if path_ext:
|
|
103
|
+
img_type = path_ext.lower()
|
|
104
|
+
else:
|
|
105
|
+
log_error(f"Image file not found: {image}")
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
# Case 3: Image is a bytes object
|
|
109
|
+
elif image.content is not None:
|
|
110
|
+
content_bytes = image.content
|
|
111
|
+
|
|
112
|
+
else:
|
|
113
|
+
log_error(f"Unsupported image type: {type(image)}")
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
if not img_type:
|
|
117
|
+
if using_filetype:
|
|
118
|
+
kind = filetype.guess(content_bytes)
|
|
119
|
+
if not kind:
|
|
120
|
+
log_error("Unable to determine image type")
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
img_type = kind.extension
|
|
124
|
+
else:
|
|
125
|
+
img_type = imghdr.what(None, h=content_bytes) # type: ignore
|
|
126
|
+
|
|
127
|
+
if not img_type:
|
|
128
|
+
log_error("Unable to determine image type")
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
media_type = type_mapping.get(img_type)
|
|
132
|
+
if not media_type:
|
|
133
|
+
log_error(f"Unsupported image type: {img_type}")
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
"type": "image",
|
|
138
|
+
"source": {
|
|
139
|
+
"type": "base64",
|
|
140
|
+
"media_type": media_type,
|
|
141
|
+
"data": base64.b64encode(content_bytes).decode("utf-8"), # type: ignore
|
|
142
|
+
},
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
except Exception as e:
|
|
146
|
+
log_error(f"Error processing image: {e}")
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _format_file_for_message(file: File) -> Optional[Dict[str, Any]]:
|
|
151
|
+
"""
|
|
152
|
+
Add a document url or base64 encoded content to a message.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
mime_mapping = {
|
|
156
|
+
"application/pdf": "base64",
|
|
157
|
+
"text/plain": "text",
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
# Case 0: File is an Anthropic uploaded file
|
|
161
|
+
if file.external is not None and hasattr(file.external, "id"):
|
|
162
|
+
return {
|
|
163
|
+
"type": "document",
|
|
164
|
+
"source": {
|
|
165
|
+
"type": "file",
|
|
166
|
+
"file_id": file.external.id,
|
|
167
|
+
},
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
# Case 1: Document is a URL
|
|
171
|
+
if file.url is not None:
|
|
172
|
+
return {
|
|
173
|
+
"type": "document",
|
|
174
|
+
"source": {
|
|
175
|
+
"type": "url",
|
|
176
|
+
"url": file.url,
|
|
177
|
+
},
|
|
178
|
+
"citations": {"enabled": True},
|
|
179
|
+
}
|
|
180
|
+
# Case 2: Document is a local file path
|
|
181
|
+
elif file.filepath is not None:
|
|
182
|
+
import base64
|
|
183
|
+
from pathlib import Path
|
|
184
|
+
|
|
185
|
+
path = Path(file.filepath) if isinstance(file.filepath, str) else file.filepath
|
|
186
|
+
if path.exists() and path.is_file():
|
|
187
|
+
file_data = base64.standard_b64encode(path.read_bytes()).decode("utf-8")
|
|
188
|
+
|
|
189
|
+
# Determine media type
|
|
190
|
+
media_type = file.mime_type
|
|
191
|
+
if media_type is None:
|
|
192
|
+
import mimetypes
|
|
193
|
+
|
|
194
|
+
media_type = mimetypes.guess_type(file.filepath)[0] or "application/pdf"
|
|
195
|
+
|
|
196
|
+
# Map media type to type, default to "base64" if no mapping exists
|
|
197
|
+
type = mime_mapping.get(media_type, "base64")
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
"type": "document",
|
|
201
|
+
"source": {
|
|
202
|
+
"type": type,
|
|
203
|
+
"media_type": media_type,
|
|
204
|
+
"data": file_data,
|
|
205
|
+
},
|
|
206
|
+
"citations": {"enabled": True},
|
|
207
|
+
}
|
|
208
|
+
else:
|
|
209
|
+
log_error(f"Document file not found: {file}")
|
|
210
|
+
return None
|
|
211
|
+
# Case 3: Document is bytes content
|
|
212
|
+
elif file.content is not None:
|
|
213
|
+
import base64
|
|
214
|
+
|
|
215
|
+
file_data = base64.standard_b64encode(file.content).decode("utf-8")
|
|
216
|
+
return {
|
|
217
|
+
"type": "document",
|
|
218
|
+
"source": {"type": "base64", "media_type": file.mime_type or "application/pdf", "data": file_data},
|
|
219
|
+
"citations": {"enabled": True},
|
|
220
|
+
}
|
|
221
|
+
return None
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def format_messages(
|
|
225
|
+
messages: List[Message], compress_tool_results: bool = False
|
|
226
|
+
) -> Tuple[List[Dict[str, Union[str, list]]], str]:
|
|
227
|
+
"""
|
|
228
|
+
Process the list of messages and separate them into API messages and system messages.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
messages (List[Message]): The list of messages to process.
|
|
232
|
+
compress_tool_results: Whether to compress tool results.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
Tuple[List[Dict[str, Union[str, list]]], str]: A tuple containing the list of API messages and the concatenated system messages.
|
|
236
|
+
"""
|
|
237
|
+
chat_messages: List[Dict[str, Union[str, list]]] = []
|
|
238
|
+
system_messages: List[str] = []
|
|
239
|
+
|
|
240
|
+
for message in messages:
|
|
241
|
+
content = message.content or ""
|
|
242
|
+
# Both "system" and "developer" roles should be extracted as system messages
|
|
243
|
+
if message.role in ("system", "developer"):
|
|
244
|
+
if content is not None:
|
|
245
|
+
system_messages.append(content) # type: ignore
|
|
246
|
+
continue
|
|
247
|
+
elif message.role == "user":
|
|
248
|
+
if isinstance(content, str):
|
|
249
|
+
content = [{"type": "text", "text": content}]
|
|
250
|
+
|
|
251
|
+
if message.images is not None:
|
|
252
|
+
for image in message.images:
|
|
253
|
+
image_content = _format_image_for_message(image)
|
|
254
|
+
if image_content:
|
|
255
|
+
content.append(image_content)
|
|
256
|
+
|
|
257
|
+
if message.files is not None:
|
|
258
|
+
for file in message.files:
|
|
259
|
+
file_content = _format_file_for_message(file)
|
|
260
|
+
if file_content:
|
|
261
|
+
content.append(file_content)
|
|
262
|
+
|
|
263
|
+
if message.audio is not None and len(message.audio) > 0:
|
|
264
|
+
log_warning("Audio input is currently unsupported.")
|
|
265
|
+
|
|
266
|
+
if message.videos is not None and len(message.videos) > 0:
|
|
267
|
+
log_warning("Video input is currently unsupported.")
|
|
268
|
+
|
|
269
|
+
elif message.role == "assistant":
|
|
270
|
+
content = []
|
|
271
|
+
|
|
272
|
+
if message.reasoning_content is not None and message.provider_data is not None:
|
|
273
|
+
from anthropic.types import RedactedThinkingBlock, ThinkingBlock
|
|
274
|
+
|
|
275
|
+
content.append(
|
|
276
|
+
ThinkingBlock(
|
|
277
|
+
thinking=message.reasoning_content,
|
|
278
|
+
signature=message.provider_data.get("signature"),
|
|
279
|
+
type="thinking",
|
|
280
|
+
)
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
if message.redacted_reasoning_content is not None:
|
|
284
|
+
from anthropic.types import RedactedThinkingBlock
|
|
285
|
+
|
|
286
|
+
content.append(
|
|
287
|
+
RedactedThinkingBlock(data=message.redacted_reasoning_content, type="redacted_reasoning_content")
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
if isinstance(message.content, str) and message.content and len(message.content.strip()) > 0:
|
|
291
|
+
content.append(TextBlock(text=message.content, type="text"))
|
|
292
|
+
|
|
293
|
+
if message.tool_calls:
|
|
294
|
+
for tool_call in message.tool_calls:
|
|
295
|
+
content.append(
|
|
296
|
+
ToolUseBlock(
|
|
297
|
+
id=tool_call["id"],
|
|
298
|
+
input=json.loads(tool_call["function"]["arguments"])
|
|
299
|
+
if "arguments" in tool_call["function"]
|
|
300
|
+
else {},
|
|
301
|
+
name=tool_call["function"]["name"],
|
|
302
|
+
type="tool_use",
|
|
303
|
+
)
|
|
304
|
+
)
|
|
305
|
+
elif message.role == "tool":
|
|
306
|
+
content = []
|
|
307
|
+
|
|
308
|
+
# Use compressed content for tool messages if compression is active
|
|
309
|
+
tool_result = message.get_content(use_compressed_content=compress_tool_results)
|
|
310
|
+
|
|
311
|
+
content.append(
|
|
312
|
+
{
|
|
313
|
+
"type": "tool_result",
|
|
314
|
+
"tool_use_id": message.tool_call_id,
|
|
315
|
+
"content": str(tool_result),
|
|
316
|
+
}
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# Skip empty assistant responses
|
|
320
|
+
if message.role == "assistant" and not content:
|
|
321
|
+
continue
|
|
322
|
+
|
|
323
|
+
chat_messages.append({"role": ROLE_MAP[message.role], "content": content}) # type: ignore
|
|
324
|
+
return chat_messages, " ".join(system_messages)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def format_tools_for_model(tools: Optional[List[Dict[str, Any]]] = None) -> Optional[List[Dict[str, Any]]]:
|
|
328
|
+
"""
|
|
329
|
+
Transforms function definitions into a format accepted by the Anthropic API.
|
|
330
|
+
Now supports strict mode for structured outputs.
|
|
331
|
+
"""
|
|
332
|
+
if not tools:
|
|
333
|
+
return None
|
|
334
|
+
|
|
335
|
+
parsed_tools: List[Dict[str, Any]] = []
|
|
336
|
+
for tool_def in tools:
|
|
337
|
+
if tool_def.get("type", "") != "function":
|
|
338
|
+
parsed_tools.append(tool_def)
|
|
339
|
+
continue
|
|
340
|
+
|
|
341
|
+
func_def = tool_def.get("function", {})
|
|
342
|
+
parameters: Dict[str, Any] = func_def.get("parameters", {})
|
|
343
|
+
properties: Dict[str, Any] = parameters.get("properties", {})
|
|
344
|
+
required: List[str] = parameters.get("required", [])
|
|
345
|
+
required_params: List[str] = required
|
|
346
|
+
|
|
347
|
+
input_properties: Dict[str, Any] = {}
|
|
348
|
+
for param_name, param_info in properties.items():
|
|
349
|
+
# Preserve the complete schema structure for complex types
|
|
350
|
+
input_properties[param_name] = param_info.copy()
|
|
351
|
+
|
|
352
|
+
# Ensure description is present (default to empty if missing)
|
|
353
|
+
if "description" not in input_properties[param_name]:
|
|
354
|
+
input_properties[param_name]["description"] = ""
|
|
355
|
+
|
|
356
|
+
tool = {
|
|
357
|
+
"name": func_def.get("name") or "",
|
|
358
|
+
"description": func_def.get("description") or "",
|
|
359
|
+
"input_schema": {
|
|
360
|
+
"type": parameters.get("type", "object"),
|
|
361
|
+
"properties": input_properties,
|
|
362
|
+
"required": required_params,
|
|
363
|
+
"additionalProperties": False,
|
|
364
|
+
},
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
# Add strict mode if specified (check both function dict and tool_def top level)
|
|
368
|
+
strict_mode = func_def.get("strict") or tool_def.get("strict")
|
|
369
|
+
if strict_mode is True:
|
|
370
|
+
tool["strict"] = True
|
|
371
|
+
|
|
372
|
+
parsed_tools.append(tool)
|
|
373
|
+
return parsed_tools
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, Dict, List, Sequence
|
|
3
|
+
|
|
4
|
+
from agno.media import Image
|
|
5
|
+
from agno.models.message import Message
|
|
6
|
+
from agno.utils.log import log_error, log_warning
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _format_images_for_message(message: Message, images: Sequence[Image]) -> List[Dict[str, Any]]:
|
|
10
|
+
"""
|
|
11
|
+
Format an image into the format expected by WatsonX.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
# Create a default message content with text
|
|
15
|
+
message_content_with_image: List[Dict[str, Any]] = [{"type": "text", "text": message.content}]
|
|
16
|
+
|
|
17
|
+
# Add images to the message content
|
|
18
|
+
for image in images:
|
|
19
|
+
try:
|
|
20
|
+
if image.content is not None:
|
|
21
|
+
image_content = image.content
|
|
22
|
+
elif image.url is not None:
|
|
23
|
+
image_content = image.get_content_bytes() # type: ignore
|
|
24
|
+
elif image.filepath is not None:
|
|
25
|
+
if isinstance(image.filepath, Path):
|
|
26
|
+
image_content = image.filepath.read_bytes()
|
|
27
|
+
else:
|
|
28
|
+
with open(image.filepath, "rb") as f:
|
|
29
|
+
image_content = f.read()
|
|
30
|
+
else:
|
|
31
|
+
log_warning(f"Unsupported image format: {image}")
|
|
32
|
+
continue
|
|
33
|
+
|
|
34
|
+
if image_content is not None:
|
|
35
|
+
import base64
|
|
36
|
+
|
|
37
|
+
base64_image = base64.b64encode(image_content).decode("utf-8")
|
|
38
|
+
image_url = f"data:image/jpeg;base64,{base64_image}"
|
|
39
|
+
image_payload = {"type": "image_url", "image_url": {"url": image_url}}
|
|
40
|
+
message_content_with_image.append(image_payload)
|
|
41
|
+
|
|
42
|
+
except Exception as e:
|
|
43
|
+
log_error(f"Failed to process image: {str(e)}")
|
|
44
|
+
|
|
45
|
+
# Update the message content with the images
|
|
46
|
+
return message_content_with_image
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def format_messages(messages: List[Message], compress_tool_results: bool = False) -> List[Dict[str, Any]]:
|
|
50
|
+
"""
|
|
51
|
+
Format messages for the Cohere API.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
messages (List[Message]): The list of messages.
|
|
55
|
+
compress_tool_results: Whether to compress tool results.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
List[Dict[str, Any]]: The formatted messages.
|
|
59
|
+
"""
|
|
60
|
+
formatted_messages = []
|
|
61
|
+
for message in messages:
|
|
62
|
+
# Use compressed content for tool messages if compression is active
|
|
63
|
+
content = message.content
|
|
64
|
+
|
|
65
|
+
if message.role == "tool":
|
|
66
|
+
content = message.get_content(use_compressed_content=compress_tool_results)
|
|
67
|
+
|
|
68
|
+
message_dict = {
|
|
69
|
+
"role": message.role,
|
|
70
|
+
"content": content,
|
|
71
|
+
"name": message.name,
|
|
72
|
+
"tool_call_id": message.tool_call_id,
|
|
73
|
+
"tool_calls": message.tool_calls,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if message.images is not None and len(message.images) > 0:
|
|
77
|
+
# Ignore non-string message content
|
|
78
|
+
if isinstance(message.content, str):
|
|
79
|
+
message_content_with_image = _format_images_for_message(message=message, images=message.images)
|
|
80
|
+
if len(message_content_with_image) > 1:
|
|
81
|
+
message_dict["content"] = message_content_with_image
|
|
82
|
+
|
|
83
|
+
if message.videos is not None and len(message.videos) > 0:
|
|
84
|
+
log_warning("Video input is currently unsupported.")
|
|
85
|
+
|
|
86
|
+
if message.audio is not None and len(message.audio) > 0:
|
|
87
|
+
log_warning("Audio input is currently unsupported.")
|
|
88
|
+
|
|
89
|
+
if message.files is not None and len(message.files) > 0:
|
|
90
|
+
log_warning("File input is currently unsupported.")
|
|
91
|
+
|
|
92
|
+
message_dict = {k: v for k, v in message_dict.items() if v is not None}
|
|
93
|
+
formatted_messages.append(message_dict)
|
|
94
|
+
return formatted_messages
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
|
|
3
|
+
from agno.agent import Message
|
|
4
|
+
from agno.utils.log import log_warning
|
|
5
|
+
from agno.utils.openai import process_image
|
|
6
|
+
|
|
7
|
+
ROLE_MAP = {
|
|
8
|
+
"user": "user",
|
|
9
|
+
"assistant": "assistant",
|
|
10
|
+
"system": "system",
|
|
11
|
+
"tool": "tool",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
TOOL_CALL_ROLE_MAP = {
|
|
15
|
+
"user": "user",
|
|
16
|
+
"assistant": "assistant",
|
|
17
|
+
"system": "user",
|
|
18
|
+
"tool": "tool",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def format_message(
|
|
23
|
+
message: Message, openai_like: bool = False, tool_calls: bool = False, compress_tool_results: bool = False
|
|
24
|
+
) -> Dict[str, Any]:
|
|
25
|
+
"""
|
|
26
|
+
Format a message into the format expected by Llama API.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
message (Message): The message to format.
|
|
30
|
+
openai_like (bool): Whether to format the message as an OpenAI-like message.
|
|
31
|
+
tool_calls (bool): Whether tool calls are present.
|
|
32
|
+
compress_tool_results: Whether to compress tool results.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Dict[str, Any]: The formatted message.
|
|
36
|
+
"""
|
|
37
|
+
message_dict: Dict[str, Any] = {
|
|
38
|
+
"role": ROLE_MAP[message.role] if not tool_calls else TOOL_CALL_ROLE_MAP[message.role],
|
|
39
|
+
"content": [{"type": "text", "text": message.content or " "}],
|
|
40
|
+
"name": message.name,
|
|
41
|
+
"tool_call_id": message.tool_call_id,
|
|
42
|
+
"tool_calls": message.tool_calls,
|
|
43
|
+
}
|
|
44
|
+
message_dict = {k: v for k, v in message_dict.items() if v is not None}
|
|
45
|
+
|
|
46
|
+
if message.images is not None and len(message.images) > 0:
|
|
47
|
+
for image in message.images:
|
|
48
|
+
image_payload = process_image(image)
|
|
49
|
+
if image_payload:
|
|
50
|
+
message_dict["content"].append(image_payload)
|
|
51
|
+
|
|
52
|
+
if message.videos is not None and len(message.videos) > 0:
|
|
53
|
+
log_warning("Video input is currently unsupported.")
|
|
54
|
+
|
|
55
|
+
if message.audio is not None and len(message.audio) > 0:
|
|
56
|
+
log_warning("Audio input is currently unsupported.")
|
|
57
|
+
|
|
58
|
+
if message.role == "tool":
|
|
59
|
+
# Use compressed content if compression is active
|
|
60
|
+
content = message.get_content(use_compressed_content=compress_tool_results)
|
|
61
|
+
|
|
62
|
+
message_dict = {
|
|
63
|
+
"role": "tool",
|
|
64
|
+
"tool_call_id": message.tool_call_id,
|
|
65
|
+
"content": content,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if message.role == "assistant":
|
|
69
|
+
text_content = {"type": "text", "text": message.content or " "}
|
|
70
|
+
|
|
71
|
+
if message.tool_calls is not None and len(message.tool_calls) > 0:
|
|
72
|
+
message_dict = {
|
|
73
|
+
"content": [text_content] if openai_like else text_content,
|
|
74
|
+
"role": "assistant",
|
|
75
|
+
"tool_calls": message.tool_calls,
|
|
76
|
+
"stop_reason": "tool_calls",
|
|
77
|
+
}
|
|
78
|
+
else:
|
|
79
|
+
message_dict = {
|
|
80
|
+
"role": "assistant",
|
|
81
|
+
"content": [text_content] if openai_like else text_content,
|
|
82
|
+
"stop_reason": "stop",
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return message_dict
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from typing import Any, List, Optional, Union
|
|
2
|
+
|
|
3
|
+
from agno.media import Image
|
|
4
|
+
from agno.models.message import Message
|
|
5
|
+
from agno.utils.log import log_error, log_warning
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
# TODO: Adapt these imports to the new Mistral SDK versions
|
|
9
|
+
from mistralai.models import ( # type: ignore
|
|
10
|
+
AssistantMessage, # type: ignore
|
|
11
|
+
ImageURLChunk, # type: ignore
|
|
12
|
+
SystemMessage, # type: ignore
|
|
13
|
+
TextChunk, # type: ignore
|
|
14
|
+
ToolMessage, # type: ignore
|
|
15
|
+
UserMessage, # type: ignore
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
MistralMessage = Union[UserMessage, AssistantMessage, SystemMessage, ToolMessage]
|
|
19
|
+
|
|
20
|
+
except ImportError:
|
|
21
|
+
raise ImportError("`mistralai` not installed. Please install using `pip install mistralai`")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _format_image_for_message(image: Image) -> Optional[ImageURLChunk]:
|
|
25
|
+
# Case 1: Image is a URL
|
|
26
|
+
if image.url is not None:
|
|
27
|
+
return ImageURLChunk(image_url=image.url)
|
|
28
|
+
# Case 2: Image is a local file path
|
|
29
|
+
elif image.filepath is not None:
|
|
30
|
+
import base64
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
|
|
33
|
+
path = Path(image.filepath) if isinstance(image.filepath, str) else image.filepath
|
|
34
|
+
if not path.exists() or not path.is_file():
|
|
35
|
+
log_error(f"Image file not found: {image}")
|
|
36
|
+
raise FileNotFoundError(f"Image file not found: {image}")
|
|
37
|
+
|
|
38
|
+
with open(image.filepath, "rb") as image_file:
|
|
39
|
+
base64_image = base64.b64encode(image_file.read()).decode("utf-8")
|
|
40
|
+
return ImageURLChunk(image_url=f"data:image/jpeg;base64,{base64_image}")
|
|
41
|
+
|
|
42
|
+
# Case 3: Image is a bytes object
|
|
43
|
+
elif image.content is not None:
|
|
44
|
+
import base64
|
|
45
|
+
|
|
46
|
+
base64_image = base64.b64encode(image.content).decode("utf-8")
|
|
47
|
+
return ImageURLChunk(image_url=f"data:image/jpeg;base64,{base64_image}")
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def format_messages(messages: List[Message], compress_tool_results: bool = False) -> List[MistralMessage]:
|
|
52
|
+
mistral_messages: List[MistralMessage] = []
|
|
53
|
+
|
|
54
|
+
for message in messages:
|
|
55
|
+
mistral_message: MistralMessage
|
|
56
|
+
if message.role == "user":
|
|
57
|
+
if message.audio is not None and len(message.audio) > 0:
|
|
58
|
+
log_warning("Audio input is currently unsupported.")
|
|
59
|
+
|
|
60
|
+
if message.files is not None and len(message.files) > 0:
|
|
61
|
+
log_warning("File input is currently unsupported.")
|
|
62
|
+
|
|
63
|
+
if message.videos is not None and len(message.videos) > 0:
|
|
64
|
+
log_warning("Video input is currently unsupported.")
|
|
65
|
+
|
|
66
|
+
if message.images is not None:
|
|
67
|
+
content: List[Any] = [TextChunk(type="text", text=message.content)]
|
|
68
|
+
for image in message.images:
|
|
69
|
+
image_content = _format_image_for_message(image)
|
|
70
|
+
if image_content:
|
|
71
|
+
content.append(image_content)
|
|
72
|
+
mistral_message = UserMessage(role="user", content=content)
|
|
73
|
+
else:
|
|
74
|
+
mistral_message = UserMessage(role="user", content=message.content)
|
|
75
|
+
elif message.role == "assistant":
|
|
76
|
+
if message.reasoning_content is not None:
|
|
77
|
+
mistral_message = UserMessage(role="user", content=message.content)
|
|
78
|
+
elif message.tool_calls is not None:
|
|
79
|
+
mistral_message = AssistantMessage(
|
|
80
|
+
role="assistant", content=message.content, tool_calls=message.tool_calls
|
|
81
|
+
)
|
|
82
|
+
else:
|
|
83
|
+
mistral_message = AssistantMessage(role=message.role, content=message.content)
|
|
84
|
+
elif message.role == "system":
|
|
85
|
+
mistral_message = SystemMessage(role="system", content=message.content)
|
|
86
|
+
elif message.role == "tool":
|
|
87
|
+
# Get compressed content if compression is active
|
|
88
|
+
tool_content = message.get_content(use_compressed_content=compress_tool_results)
|
|
89
|
+
mistral_message = ToolMessage(name="tool", content=tool_content, tool_call_id=message.tool_call_id)
|
|
90
|
+
else:
|
|
91
|
+
raise ValueError(f"Unknown role: {message.role}")
|
|
92
|
+
|
|
93
|
+
mistral_messages.append(mistral_message)
|
|
94
|
+
|
|
95
|
+
# Check if the last message is an assistant message
|
|
96
|
+
if mistral_messages and hasattr(mistral_messages[-1], "role") and mistral_messages[-1].role == "assistant":
|
|
97
|
+
# Set prefix=True for the last assistant message to allow it as the last message
|
|
98
|
+
mistral_messages[-1].prefix = True
|
|
99
|
+
|
|
100
|
+
return mistral_messages
|