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/functions.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from typing import Any, Dict, Optional
|
|
2
|
+
from typing import Any, Callable, Dict, Optional, TypeVar
|
|
3
3
|
|
|
4
4
|
from agno.tools.function import Function, FunctionCall
|
|
5
|
-
from agno.utils.log import
|
|
5
|
+
from agno.utils.log import log_debug, log_error
|
|
6
|
+
|
|
7
|
+
T = TypeVar("T")
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
def get_function_call(
|
|
@@ -11,7 +13,6 @@ def get_function_call(
|
|
|
11
13
|
call_id: Optional[str] = None,
|
|
12
14
|
functions: Optional[Dict[str, Function]] = None,
|
|
13
15
|
) -> Optional[FunctionCall]:
|
|
14
|
-
logger.debug(f"Getting function {name}")
|
|
15
16
|
if functions is None:
|
|
16
17
|
return None
|
|
17
18
|
|
|
@@ -19,7 +20,7 @@ def get_function_call(
|
|
|
19
20
|
if name in functions:
|
|
20
21
|
function_to_call = functions[name]
|
|
21
22
|
if function_to_call is None:
|
|
22
|
-
|
|
23
|
+
log_error(f"Function {name} not found")
|
|
23
24
|
return None
|
|
24
25
|
|
|
25
26
|
function_call = FunctionCall(function=function_to_call)
|
|
@@ -27,16 +28,14 @@ def get_function_call(
|
|
|
27
28
|
function_call.call_id = call_id
|
|
28
29
|
if arguments is not None and arguments != "":
|
|
29
30
|
try:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
arguments = arguments.replace("False", "false")
|
|
37
|
-
_arguments = json.loads(arguments)
|
|
31
|
+
try:
|
|
32
|
+
_arguments = json.loads(arguments)
|
|
33
|
+
except Exception:
|
|
34
|
+
import ast
|
|
35
|
+
|
|
36
|
+
_arguments = ast.literal_eval(arguments)
|
|
38
37
|
except Exception as e:
|
|
39
|
-
|
|
38
|
+
log_error(f"Unable to decode function arguments:\n{arguments}\nError: {e}")
|
|
40
39
|
function_call.error = (
|
|
41
40
|
f"Error while decoding function arguments: {e}\n\n"
|
|
42
41
|
f"Please make sure we can json.loads() the arguments and retry."
|
|
@@ -44,7 +43,7 @@ def get_function_call(
|
|
|
44
43
|
return function_call
|
|
45
44
|
|
|
46
45
|
if not isinstance(_arguments, dict):
|
|
47
|
-
|
|
46
|
+
log_error(f"Function arguments are not a valid JSON object: {arguments}")
|
|
48
47
|
function_call.error = "Function arguments are not a valid JSON object.\n\n Please fix and retry."
|
|
49
48
|
return function_call
|
|
50
49
|
|
|
@@ -66,31 +65,102 @@ def get_function_call(
|
|
|
66
65
|
|
|
67
66
|
function_call.arguments = clean_arguments
|
|
68
67
|
except Exception as e:
|
|
69
|
-
|
|
68
|
+
log_error(f"Unable to parsing function arguments:\n{arguments}\nError: {e}")
|
|
70
69
|
function_call.error = f"Error while parsing function arguments: {e}\n\n Please fix and retry."
|
|
71
70
|
return function_call
|
|
72
71
|
return function_call
|
|
73
72
|
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
74
|
+
def cache_result(enable_cache: bool = True, cache_dir: Optional[str] = None, cache_ttl: int = 3600):
|
|
75
|
+
"""
|
|
76
|
+
Decorator factory that creates a file-based caching decorator for function results.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
enable_cache (bool): Enable caching of function results.
|
|
80
|
+
cache_dir (Optional[str]): Directory to store cache files. Defaults to system temp dir.
|
|
81
|
+
cache_ttl (int): Time-to-live for cached results in seconds.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
A decorator function that caches results on the filesystem.
|
|
85
|
+
"""
|
|
86
|
+
import functools
|
|
87
|
+
import hashlib
|
|
88
|
+
import json
|
|
89
|
+
import os
|
|
90
|
+
import tempfile
|
|
91
|
+
import time
|
|
92
|
+
|
|
93
|
+
def decorator(func: Callable[..., T]) -> Callable[..., T]:
|
|
94
|
+
@functools.wraps(func)
|
|
95
|
+
def wrapper(*args, **kwargs):
|
|
96
|
+
# First argument might be 'self' but we don't need to handle it specially
|
|
97
|
+
instance = args[0] if args else None
|
|
98
|
+
|
|
99
|
+
# Skip caching if cache_results is False (only for class methods)
|
|
100
|
+
if instance and hasattr(instance, "cache_results") and not instance.cache_results:
|
|
101
|
+
return func(*args, **kwargs)
|
|
102
|
+
|
|
103
|
+
if not enable_cache:
|
|
104
|
+
return func(*args, **kwargs)
|
|
105
|
+
|
|
106
|
+
# Get cache directory
|
|
107
|
+
instance_cache_dir = (
|
|
108
|
+
getattr(instance, "cache_dir", cache_dir) if hasattr(instance, "cache_dir") else cache_dir
|
|
109
|
+
)
|
|
110
|
+
base_cache_dir = instance_cache_dir or os.path.join(tempfile.gettempdir(), "agno_cache")
|
|
111
|
+
|
|
112
|
+
# Create cache directory if it doesn't exist
|
|
113
|
+
func_cache_dir = os.path.join(base_cache_dir, func.__module__, func.__qualname__)
|
|
114
|
+
os.makedirs(func_cache_dir, exist_ok=True)
|
|
115
|
+
|
|
116
|
+
# Create a cache key using all arguments
|
|
117
|
+
# Convert args and kwargs to strings and join them
|
|
118
|
+
args_str = str(args)
|
|
119
|
+
kwargs_str = str(sorted(kwargs.items()))
|
|
120
|
+
|
|
121
|
+
# Create a hash for potentially large input
|
|
122
|
+
key_str = f"{func.__module__}.{func.__qualname__}:{args_str}:{kwargs_str}"
|
|
123
|
+
cache_key = hashlib.md5(key_str.encode()).hexdigest()
|
|
124
|
+
|
|
125
|
+
# Define cache file path
|
|
126
|
+
cache_file = os.path.join(func_cache_dir, f"{cache_key}.json")
|
|
127
|
+
|
|
128
|
+
# Check for cached result
|
|
129
|
+
if os.path.exists(cache_file):
|
|
130
|
+
try:
|
|
131
|
+
with open(cache_file, "r") as f:
|
|
132
|
+
cache_data = json.load(f)
|
|
133
|
+
|
|
134
|
+
timestamp = cache_data.get("timestamp", 0)
|
|
135
|
+
result = cache_data.get("result")
|
|
136
|
+
|
|
137
|
+
# Use instance ttl if available, otherwise use decorator ttl
|
|
138
|
+
effective_ttl = (
|
|
139
|
+
getattr(instance, "cache_ttl", cache_ttl) if hasattr(instance, "cache_ttl") else cache_ttl
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
if time.time() - timestamp <= effective_ttl:
|
|
143
|
+
log_debug(f"Cache hit for: {func.__name__}")
|
|
144
|
+
return result
|
|
145
|
+
|
|
146
|
+
# Remove expired entry
|
|
147
|
+
os.remove(cache_file)
|
|
148
|
+
except Exception as e:
|
|
149
|
+
log_error(f"Error reading cache: {e}")
|
|
150
|
+
# Continue with function execution if cache read fails
|
|
151
|
+
|
|
152
|
+
# Execute the function and cache the result
|
|
153
|
+
result = func(*args, **kwargs)
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
with open(cache_file, "w") as f:
|
|
157
|
+
json.dump({"timestamp": time.time(), "result": result}, f)
|
|
158
|
+
except Exception as e:
|
|
159
|
+
log_error(f"Error writing cache: {e}")
|
|
160
|
+
# Continue even if cache write fails
|
|
161
|
+
|
|
162
|
+
return result
|
|
163
|
+
|
|
164
|
+
return wrapper
|
|
165
|
+
|
|
166
|
+
return decorator
|
agno/utils/gemini.py
ADDED
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, Dict, List, Optional, Type, Union
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
from agno.media import Image
|
|
7
|
+
from agno.utils.log import log_error, log_warning
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from google.genai.types import (
|
|
11
|
+
FunctionDeclaration,
|
|
12
|
+
Schema,
|
|
13
|
+
Tool,
|
|
14
|
+
)
|
|
15
|
+
from google.genai.types import (
|
|
16
|
+
Type as GeminiType,
|
|
17
|
+
)
|
|
18
|
+
except ImportError:
|
|
19
|
+
raise ImportError("`google-genai` not installed. Please install it using `pip install google-genai`")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def prepare_response_schema(pydantic_model: Type[BaseModel]) -> Union[Type[BaseModel], Schema]:
|
|
23
|
+
"""
|
|
24
|
+
Prepare a Pydantic model for use as Gemini response schema.
|
|
25
|
+
|
|
26
|
+
Returns the model directly if Gemini can handle it natively,
|
|
27
|
+
otherwise converts to Gemini's Schema format.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
pydantic_model: A Pydantic model class
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Either the original Pydantic model or a converted Schema object
|
|
34
|
+
"""
|
|
35
|
+
schema_dict = pydantic_model.model_json_schema()
|
|
36
|
+
|
|
37
|
+
# Convert to Gemini Schema if the model has problematic patterns
|
|
38
|
+
if needs_conversion(schema_dict):
|
|
39
|
+
try:
|
|
40
|
+
converted = convert_schema(schema_dict)
|
|
41
|
+
except Exception as e:
|
|
42
|
+
log_warning(f"Failed to convert schema for {pydantic_model}: {e}")
|
|
43
|
+
converted = None
|
|
44
|
+
|
|
45
|
+
if converted is None:
|
|
46
|
+
# If conversion fails, let Gemini handle it directly
|
|
47
|
+
return pydantic_model
|
|
48
|
+
return converted
|
|
49
|
+
|
|
50
|
+
# Gemini can handle this model directly
|
|
51
|
+
return pydantic_model
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def needs_conversion(schema_dict: Dict[str, Any]) -> bool:
|
|
55
|
+
"""
|
|
56
|
+
Check if a schema needs conversion for Gemini.
|
|
57
|
+
|
|
58
|
+
Returns True if the schema has:
|
|
59
|
+
- Self-references or circular references
|
|
60
|
+
- Dict fields (additionalProperties) that Gemini doesn't handle well
|
|
61
|
+
- Empty object definitions that Gemini rejects
|
|
62
|
+
"""
|
|
63
|
+
# Check for dict fields (additionalProperties) anywhere in the schema
|
|
64
|
+
if has_additional_properties(schema_dict):
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
# Check if schema has $defs with circular references
|
|
68
|
+
if "$defs" in schema_dict:
|
|
69
|
+
defs = schema_dict["$defs"]
|
|
70
|
+
for def_name, def_schema in defs.items():
|
|
71
|
+
ref_path = f"#/$defs/{def_name}"
|
|
72
|
+
if has_self_reference(def_schema, ref_path):
|
|
73
|
+
return True
|
|
74
|
+
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def has_additional_properties(schema: Any) -> bool:
|
|
79
|
+
"""Check if schema has additionalProperties (Dict fields)"""
|
|
80
|
+
if isinstance(schema, dict):
|
|
81
|
+
# Direct check
|
|
82
|
+
if "additionalProperties" in schema:
|
|
83
|
+
return True
|
|
84
|
+
|
|
85
|
+
# Check properties recursively
|
|
86
|
+
if "properties" in schema:
|
|
87
|
+
for prop_schema in schema["properties"].values():
|
|
88
|
+
if has_additional_properties(prop_schema):
|
|
89
|
+
return True
|
|
90
|
+
|
|
91
|
+
# Check array items
|
|
92
|
+
if "items" in schema:
|
|
93
|
+
if has_additional_properties(schema["items"]):
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def has_self_reference(schema: Dict, target_ref: str) -> bool:
|
|
100
|
+
"""Check if a schema references itself (directly or indirectly)"""
|
|
101
|
+
if isinstance(schema, dict):
|
|
102
|
+
# Direct self-reference
|
|
103
|
+
if schema.get("$ref") == target_ref:
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
# Check properties
|
|
107
|
+
if "properties" in schema:
|
|
108
|
+
for prop_schema in schema["properties"].values():
|
|
109
|
+
if has_self_reference(prop_schema, target_ref):
|
|
110
|
+
return True
|
|
111
|
+
|
|
112
|
+
# Check array items
|
|
113
|
+
if "items" in schema:
|
|
114
|
+
if has_self_reference(schema["items"], target_ref):
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
# Check anyOf/oneOf/allOf
|
|
118
|
+
for key in ["anyOf", "oneOf", "allOf"]:
|
|
119
|
+
if key in schema:
|
|
120
|
+
for sub_schema in schema[key]:
|
|
121
|
+
if has_self_reference(sub_schema, target_ref):
|
|
122
|
+
return True
|
|
123
|
+
|
|
124
|
+
return False
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def format_image_for_message(image: Image) -> Optional[Dict[str, Any]]:
|
|
128
|
+
# Case 1: Image is a URL
|
|
129
|
+
# Download the image from the URL and add it as base64 encoded data
|
|
130
|
+
if image.url is not None:
|
|
131
|
+
content_bytes = image.get_content_bytes() # type: ignore
|
|
132
|
+
if content_bytes is not None:
|
|
133
|
+
try:
|
|
134
|
+
import base64
|
|
135
|
+
|
|
136
|
+
image_data = {
|
|
137
|
+
"mime_type": "image/jpeg",
|
|
138
|
+
"data": base64.b64encode(content_bytes).decode("utf-8"),
|
|
139
|
+
}
|
|
140
|
+
return image_data
|
|
141
|
+
except Exception as e:
|
|
142
|
+
log_warning(f"Failed to download image from {image}: {e}")
|
|
143
|
+
return None
|
|
144
|
+
else:
|
|
145
|
+
log_warning(f"Unsupported image format: {image}")
|
|
146
|
+
return None
|
|
147
|
+
|
|
148
|
+
# Case 2: Image is a local path
|
|
149
|
+
elif image.filepath is not None:
|
|
150
|
+
try:
|
|
151
|
+
image_path = Path(image.filepath)
|
|
152
|
+
if image_path.exists() and image_path.is_file():
|
|
153
|
+
with open(image_path, "rb") as f:
|
|
154
|
+
content_bytes = f.read()
|
|
155
|
+
else:
|
|
156
|
+
log_error(f"Image file {image_path} does not exist.")
|
|
157
|
+
raise
|
|
158
|
+
return {
|
|
159
|
+
"mime_type": "image/jpeg",
|
|
160
|
+
"data": content_bytes,
|
|
161
|
+
}
|
|
162
|
+
except Exception as e:
|
|
163
|
+
log_warning(f"Failed to load image from {image.filepath}: {e}")
|
|
164
|
+
return None
|
|
165
|
+
|
|
166
|
+
# Case 3: Image is a bytes object
|
|
167
|
+
# Add it as base64 encoded data
|
|
168
|
+
elif image.content is not None and isinstance(image.content, bytes):
|
|
169
|
+
import base64
|
|
170
|
+
|
|
171
|
+
image_data = {"mime_type": "image/jpeg", "data": base64.b64encode(image.content).decode("utf-8")}
|
|
172
|
+
return image_data
|
|
173
|
+
else:
|
|
174
|
+
log_warning(f"Unknown image type: {type(image)}")
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def convert_schema(
|
|
179
|
+
schema_dict: Dict[str, Any], root_schema: Optional[Dict[str, Any]] = None, visited_refs: Optional[set] = None
|
|
180
|
+
) -> Optional[Schema]:
|
|
181
|
+
"""
|
|
182
|
+
Recursively convert a JSON-like schema dictionary to a types.Schema object.
|
|
183
|
+
|
|
184
|
+
Parameters:
|
|
185
|
+
schema_dict (dict): The JSON schema dictionary with keys like "type", "description",
|
|
186
|
+
"properties", and "required".
|
|
187
|
+
root_schema (dict, optional): The root schema containing $defs for resolving $ref
|
|
188
|
+
visited_refs (set, optional): Set of visited $ref paths to detect circular references
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
types.Schema: The converted schema.
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
# If this is the initial call, set root_schema to self and initialize visited_refs
|
|
195
|
+
if root_schema is None:
|
|
196
|
+
root_schema = schema_dict
|
|
197
|
+
if visited_refs is None:
|
|
198
|
+
visited_refs = set()
|
|
199
|
+
|
|
200
|
+
# Handle $ref references with cycle detection
|
|
201
|
+
if "$ref" in schema_dict:
|
|
202
|
+
ref_path = schema_dict["$ref"]
|
|
203
|
+
|
|
204
|
+
# Check for circular reference
|
|
205
|
+
if ref_path in visited_refs:
|
|
206
|
+
# Return a basic object schema to break the cycle
|
|
207
|
+
return Schema(
|
|
208
|
+
type=GeminiType.OBJECT,
|
|
209
|
+
description=f"Circular reference to {ref_path}",
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
if ref_path.startswith("#/$defs/"):
|
|
213
|
+
def_name = ref_path.split("/")[-1]
|
|
214
|
+
if "$defs" in root_schema and def_name in root_schema["$defs"]:
|
|
215
|
+
# Add to visited set before recursing
|
|
216
|
+
new_visited = visited_refs.copy()
|
|
217
|
+
new_visited.add(ref_path)
|
|
218
|
+
|
|
219
|
+
referenced_schema = root_schema["$defs"][def_name]
|
|
220
|
+
return convert_schema(referenced_schema, root_schema, new_visited)
|
|
221
|
+
# If we can't resolve the reference, return None
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
schema_type = schema_dict.get("type", "")
|
|
225
|
+
if schema_type is None or schema_type == "null":
|
|
226
|
+
return None
|
|
227
|
+
description = schema_dict.get("description", None)
|
|
228
|
+
title = schema_dict.get("title", None)
|
|
229
|
+
default = schema_dict.get("default", None)
|
|
230
|
+
|
|
231
|
+
# Handle enum types
|
|
232
|
+
if "enum" in schema_dict:
|
|
233
|
+
enum_values = schema_dict["enum"]
|
|
234
|
+
return Schema(type=GeminiType.STRING, enum=enum_values, description=description, default=default, title=title)
|
|
235
|
+
|
|
236
|
+
if schema_type == "object":
|
|
237
|
+
# Handle regular objects with properties
|
|
238
|
+
if "properties" in schema_dict:
|
|
239
|
+
properties = {}
|
|
240
|
+
for key, prop_def in schema_dict["properties"].items():
|
|
241
|
+
# Process nullable types
|
|
242
|
+
prop_type = prop_def.get("type", "")
|
|
243
|
+
is_nullable = False
|
|
244
|
+
if isinstance(prop_type, list) and "null" in prop_type:
|
|
245
|
+
prop_def["type"] = prop_type[0]
|
|
246
|
+
is_nullable = True
|
|
247
|
+
|
|
248
|
+
# Process property schema (pass root_schema and visited_refs for $ref resolution)
|
|
249
|
+
converted_schema = convert_schema(prop_def, root_schema, visited_refs)
|
|
250
|
+
if converted_schema is not None:
|
|
251
|
+
if is_nullable:
|
|
252
|
+
converted_schema.nullable = True
|
|
253
|
+
properties[key] = converted_schema
|
|
254
|
+
else:
|
|
255
|
+
properties[key] = Schema(
|
|
256
|
+
title=prop_def.get("title", None), description=prop_def.get("description", None)
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
required = schema_dict.get("required", [])
|
|
260
|
+
|
|
261
|
+
if properties:
|
|
262
|
+
return Schema(
|
|
263
|
+
type=GeminiType.OBJECT,
|
|
264
|
+
properties=properties,
|
|
265
|
+
required=required,
|
|
266
|
+
description=description,
|
|
267
|
+
default=default,
|
|
268
|
+
title=title,
|
|
269
|
+
)
|
|
270
|
+
else:
|
|
271
|
+
return Schema(type=GeminiType.OBJECT, description=description, default=default, title=title)
|
|
272
|
+
|
|
273
|
+
# Handle Dict types (objects with additionalProperties but no properties)
|
|
274
|
+
elif "additionalProperties" in schema_dict:
|
|
275
|
+
additional_props = schema_dict["additionalProperties"]
|
|
276
|
+
|
|
277
|
+
# If additionalProperties is a schema object (Dict[str, T] case)
|
|
278
|
+
if isinstance(additional_props, dict) and "type" in additional_props:
|
|
279
|
+
# For Gemini, we need to represent Dict[str, T] as an object with at least one property
|
|
280
|
+
# to avoid the "properties should be non-empty" error.
|
|
281
|
+
# We'll create a generic property that represents the dictionary structure
|
|
282
|
+
|
|
283
|
+
# Handle both single types and union types (arrays) from Zod schemas
|
|
284
|
+
type_value = additional_props.get("type", "string")
|
|
285
|
+
if isinstance(type_value, list):
|
|
286
|
+
value_type = type_value[0].upper() if type_value else "STRING"
|
|
287
|
+
union_types = ", ".join(type_value)
|
|
288
|
+
type_description_suffix = f" (supports union types: {union_types})"
|
|
289
|
+
else:
|
|
290
|
+
# Single type
|
|
291
|
+
value_type = type_value.upper()
|
|
292
|
+
type_description_suffix = ""
|
|
293
|
+
|
|
294
|
+
# Create a placeholder property to satisfy Gemini's requirements
|
|
295
|
+
# This is a workaround since Gemini doesn't support additionalProperties directly
|
|
296
|
+
placeholder_properties = {
|
|
297
|
+
"example_key": Schema(
|
|
298
|
+
type=value_type,
|
|
299
|
+
description=f"Example key-value pair. This object can contain any number of keys with {value_type.lower()} values{type_description_suffix}.",
|
|
300
|
+
)
|
|
301
|
+
}
|
|
302
|
+
if value_type == "ARRAY":
|
|
303
|
+
placeholder_properties["example_key"].items = {} # type: ignore
|
|
304
|
+
|
|
305
|
+
return Schema(
|
|
306
|
+
type=GeminiType.OBJECT,
|
|
307
|
+
properties=placeholder_properties,
|
|
308
|
+
description=description
|
|
309
|
+
or f"Dictionary with {value_type.lower()} values{type_description_suffix}. Can contain any number of key-value pairs.",
|
|
310
|
+
default=default,
|
|
311
|
+
)
|
|
312
|
+
else:
|
|
313
|
+
# additionalProperties is false or true
|
|
314
|
+
return Schema(type=GeminiType.OBJECT, description=description, default=default, title=title)
|
|
315
|
+
|
|
316
|
+
# Handle empty objects
|
|
317
|
+
else:
|
|
318
|
+
return Schema(type=GeminiType.OBJECT, description=description, default=default, title=title)
|
|
319
|
+
|
|
320
|
+
elif schema_type == "array" and "items" in schema_dict:
|
|
321
|
+
if not schema_dict["items"]: # Handle empty {}
|
|
322
|
+
items = Schema(type=GeminiType.STRING)
|
|
323
|
+
else:
|
|
324
|
+
converted_items = convert_schema(schema_dict["items"], root_schema, visited_refs)
|
|
325
|
+
items = converted_items if converted_items is not None else Schema(type=GeminiType.STRING)
|
|
326
|
+
min_items = schema_dict.get("minItems")
|
|
327
|
+
max_items = schema_dict.get("maxItems")
|
|
328
|
+
return Schema(
|
|
329
|
+
type=GeminiType.ARRAY,
|
|
330
|
+
description=description,
|
|
331
|
+
items=items,
|
|
332
|
+
min_items=min_items,
|
|
333
|
+
max_items=max_items,
|
|
334
|
+
title=title,
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
elif schema_type == "string":
|
|
338
|
+
schema_kwargs = {
|
|
339
|
+
"type": GeminiType.STRING,
|
|
340
|
+
"description": description,
|
|
341
|
+
"default": default,
|
|
342
|
+
"title": title,
|
|
343
|
+
}
|
|
344
|
+
if "format" in schema_dict:
|
|
345
|
+
schema_kwargs["format"] = schema_dict["format"]
|
|
346
|
+
return Schema(**schema_kwargs)
|
|
347
|
+
|
|
348
|
+
elif schema_type in ("integer", "number"):
|
|
349
|
+
schema_kwargs = {
|
|
350
|
+
"type": schema_type.upper(),
|
|
351
|
+
"description": description,
|
|
352
|
+
"default": default,
|
|
353
|
+
"title": title,
|
|
354
|
+
}
|
|
355
|
+
if "maximum" in schema_dict:
|
|
356
|
+
schema_kwargs["maximum"] = schema_dict["maximum"]
|
|
357
|
+
if "minimum" in schema_dict:
|
|
358
|
+
schema_kwargs["minimum"] = schema_dict["minimum"]
|
|
359
|
+
return Schema(**schema_kwargs)
|
|
360
|
+
|
|
361
|
+
elif schema_type == "" and "anyOf" in schema_dict:
|
|
362
|
+
any_of = []
|
|
363
|
+
for sub_schema in schema_dict["anyOf"]:
|
|
364
|
+
sub_schema_converted = convert_schema(sub_schema, root_schema, visited_refs)
|
|
365
|
+
any_of.append(sub_schema_converted)
|
|
366
|
+
|
|
367
|
+
is_nullable = False
|
|
368
|
+
filtered_any_of = []
|
|
369
|
+
|
|
370
|
+
for schema in any_of:
|
|
371
|
+
if schema is None:
|
|
372
|
+
is_nullable = True
|
|
373
|
+
else:
|
|
374
|
+
filtered_any_of.append(schema)
|
|
375
|
+
|
|
376
|
+
any_of = filtered_any_of # type: ignore
|
|
377
|
+
if len(any_of) == 1 and any_of[0] is not None:
|
|
378
|
+
any_of[0].nullable = is_nullable
|
|
379
|
+
return any_of[0]
|
|
380
|
+
else:
|
|
381
|
+
return Schema(
|
|
382
|
+
any_of=any_of,
|
|
383
|
+
description=description,
|
|
384
|
+
default=default,
|
|
385
|
+
title=title,
|
|
386
|
+
)
|
|
387
|
+
else:
|
|
388
|
+
if isinstance(schema_type, list):
|
|
389
|
+
non_null_types = [t for t in schema_type if t != "null"]
|
|
390
|
+
if non_null_types:
|
|
391
|
+
schema_type = non_null_types[0]
|
|
392
|
+
else:
|
|
393
|
+
schema_type = ""
|
|
394
|
+
# Only convert to uppercase if schema_type is not empty
|
|
395
|
+
if schema_type:
|
|
396
|
+
schema_type = schema_type.upper()
|
|
397
|
+
return Schema(type=schema_type, description=description, default=default, title=title)
|
|
398
|
+
else:
|
|
399
|
+
# If we get here with an empty type and no other handlers matched,
|
|
400
|
+
# something is wrong with the schema
|
|
401
|
+
return None
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def format_function_definitions(tools_list: List[Dict[str, Any]]) -> Optional[Tool]:
|
|
405
|
+
function_declarations = []
|
|
406
|
+
|
|
407
|
+
for tool in tools_list:
|
|
408
|
+
if tool.get("type") == "function":
|
|
409
|
+
func_info = tool.get("function", {})
|
|
410
|
+
name = func_info.get("name")
|
|
411
|
+
description = func_info.get("description", "")
|
|
412
|
+
parameters_dict = func_info.get("parameters", {})
|
|
413
|
+
|
|
414
|
+
parameters_schema = convert_schema(parameters_dict)
|
|
415
|
+
# Create a FunctionDeclaration instance
|
|
416
|
+
function_decl = FunctionDeclaration(
|
|
417
|
+
name=name,
|
|
418
|
+
description=description,
|
|
419
|
+
parameters=parameters_schema,
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
function_declarations.append(function_decl)
|
|
423
|
+
if function_declarations:
|
|
424
|
+
return Tool(function_declarations=function_declarations)
|
|
425
|
+
else:
|
|
426
|
+
return None
|