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,323 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
4
|
+
|
|
5
|
+
from agno.knowledge.embedder.base import Embedder
|
|
6
|
+
from agno.utils.log import log_debug, log_error, log_info, log_warning
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from cohere import AsyncClient as AsyncCohereClient
|
|
10
|
+
from cohere import Client as CohereClient
|
|
11
|
+
from cohere.types.embed_response import EmbeddingsByTypeEmbedResponse, EmbeddingsFloatsEmbedResponse
|
|
12
|
+
except ImportError:
|
|
13
|
+
raise ImportError("`cohere` not installed. Please install using `pip install cohere`.")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class CohereEmbedder(Embedder):
|
|
18
|
+
id: str = "embed-english-v3.0"
|
|
19
|
+
input_type: str = "search_query"
|
|
20
|
+
embedding_types: Optional[List[str]] = None
|
|
21
|
+
api_key: Optional[str] = None
|
|
22
|
+
request_params: Optional[Dict[str, Any]] = None
|
|
23
|
+
client_params: Optional[Dict[str, Any]] = None
|
|
24
|
+
cohere_client: Optional[CohereClient] = None
|
|
25
|
+
async_client: Optional[AsyncCohereClient] = None
|
|
26
|
+
exponential_backoff: bool = False # Enable exponential backoff on rate limits
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def client(self) -> CohereClient:
|
|
30
|
+
if self.cohere_client:
|
|
31
|
+
return self.cohere_client
|
|
32
|
+
client_params: Dict[str, Any] = {}
|
|
33
|
+
if self.api_key:
|
|
34
|
+
client_params["api_key"] = self.api_key
|
|
35
|
+
if self.client_params:
|
|
36
|
+
client_params.update(self.client_params)
|
|
37
|
+
self.cohere_client = CohereClient(**client_params)
|
|
38
|
+
return self.cohere_client
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def aclient(self) -> AsyncCohereClient:
|
|
42
|
+
"""Lazy init for Cohere async client."""
|
|
43
|
+
if self.async_client:
|
|
44
|
+
return self.async_client
|
|
45
|
+
params: Dict[str, Any] = {}
|
|
46
|
+
if self.api_key:
|
|
47
|
+
params["api_key"] = self.api_key
|
|
48
|
+
if self.client_params:
|
|
49
|
+
params.update(self.client_params)
|
|
50
|
+
self.async_client = AsyncCohereClient(**params)
|
|
51
|
+
return self.async_client
|
|
52
|
+
|
|
53
|
+
def response(self, text: str) -> Union[EmbeddingsFloatsEmbedResponse, EmbeddingsByTypeEmbedResponse]:
|
|
54
|
+
request_params: Dict[str, Any] = {}
|
|
55
|
+
|
|
56
|
+
if self.id:
|
|
57
|
+
request_params["model"] = self.id
|
|
58
|
+
if self.input_type:
|
|
59
|
+
request_params["input_type"] = self.input_type
|
|
60
|
+
if self.embedding_types:
|
|
61
|
+
request_params["embedding_types"] = self.embedding_types
|
|
62
|
+
if self.request_params:
|
|
63
|
+
request_params.update(self.request_params)
|
|
64
|
+
return self.client.embed(texts=[text], **request_params)
|
|
65
|
+
|
|
66
|
+
def _get_batch_request_params(self) -> Dict[str, Any]:
|
|
67
|
+
"""Get request parameters for batch embedding calls."""
|
|
68
|
+
request_params: Dict[str, Any] = {}
|
|
69
|
+
|
|
70
|
+
if self.id:
|
|
71
|
+
request_params["model"] = self.id
|
|
72
|
+
if self.input_type:
|
|
73
|
+
request_params["input_type"] = self.input_type
|
|
74
|
+
if self.embedding_types:
|
|
75
|
+
request_params["embedding_types"] = self.embedding_types
|
|
76
|
+
if self.request_params:
|
|
77
|
+
request_params.update(self.request_params)
|
|
78
|
+
|
|
79
|
+
return request_params
|
|
80
|
+
|
|
81
|
+
def _is_rate_limit_error(self, error: Exception) -> bool:
|
|
82
|
+
"""Check if the error is a rate limiting error."""
|
|
83
|
+
if hasattr(error, "status_code") and error.status_code == 429:
|
|
84
|
+
return True
|
|
85
|
+
error_str = str(error).lower()
|
|
86
|
+
return any(
|
|
87
|
+
phrase in error_str
|
|
88
|
+
for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def _exponential_backoff_sleep(self, attempt: int, base_delay: float = 1.0) -> None:
|
|
92
|
+
"""Sleep with exponential backoff."""
|
|
93
|
+
delay = base_delay * (2**attempt) + (time.time() % 1) # Add jitter
|
|
94
|
+
log_debug(f"Rate limited, waiting {delay:.2f} seconds before retry (attempt {attempt + 1})")
|
|
95
|
+
time.sleep(delay)
|
|
96
|
+
|
|
97
|
+
async def _async_rate_limit_backoff_sleep(self, attempt: int) -> None:
|
|
98
|
+
"""Async version of rate-limit-aware backoff for APIs with per-minute limits."""
|
|
99
|
+
import asyncio
|
|
100
|
+
|
|
101
|
+
# For 40 req/min APIs like Cohere Trial, we need longer waits
|
|
102
|
+
if attempt == 0:
|
|
103
|
+
delay = 15.0 # Wait 15 seconds (1/4 of minute window)
|
|
104
|
+
elif attempt == 1:
|
|
105
|
+
delay = 30.0 # Wait 30 seconds (1/2 of minute window)
|
|
106
|
+
else:
|
|
107
|
+
delay = 60.0 # Wait full minute for window reset
|
|
108
|
+
|
|
109
|
+
# Add small jitter
|
|
110
|
+
delay += time.time() % 3
|
|
111
|
+
|
|
112
|
+
log_debug(
|
|
113
|
+
f"Async rate limit backoff, waiting {delay:.1f} seconds for rate limit window reset (attempt {attempt + 1})"
|
|
114
|
+
)
|
|
115
|
+
await asyncio.sleep(delay)
|
|
116
|
+
|
|
117
|
+
async def _async_batch_with_retry(
|
|
118
|
+
self, texts: List[str], max_retries: int = 3
|
|
119
|
+
) -> Tuple[List[List[float]], List[Optional[Dict]]]:
|
|
120
|
+
"""Execute async batch embedding with rate-limit-aware backoff for rate limiting."""
|
|
121
|
+
|
|
122
|
+
log_debug(f"Starting async batch retry for {len(texts)} texts with max_retries={max_retries}")
|
|
123
|
+
|
|
124
|
+
for attempt in range(max_retries + 1):
|
|
125
|
+
try:
|
|
126
|
+
request_params = self._get_batch_request_params()
|
|
127
|
+
response: Union[
|
|
128
|
+
EmbeddingsFloatsEmbedResponse, EmbeddingsByTypeEmbedResponse
|
|
129
|
+
] = await self.aclient.embed(texts=texts, **request_params)
|
|
130
|
+
|
|
131
|
+
# Extract embeddings from response
|
|
132
|
+
if isinstance(response, EmbeddingsFloatsEmbedResponse):
|
|
133
|
+
batch_embeddings = response.embeddings
|
|
134
|
+
elif isinstance(response, EmbeddingsByTypeEmbedResponse):
|
|
135
|
+
batch_embeddings = response.embeddings.float_ if response.embeddings.float_ else []
|
|
136
|
+
else:
|
|
137
|
+
log_warning("No embeddings found in response")
|
|
138
|
+
batch_embeddings = []
|
|
139
|
+
|
|
140
|
+
# Extract usage information
|
|
141
|
+
usage = response.meta.billed_units if response.meta else None
|
|
142
|
+
usage_dict = usage.model_dump() if usage else None
|
|
143
|
+
all_usage = [usage_dict] * len(batch_embeddings)
|
|
144
|
+
|
|
145
|
+
log_debug(f"Async batch embedding succeeded on attempt {attempt + 1}")
|
|
146
|
+
return batch_embeddings, all_usage
|
|
147
|
+
|
|
148
|
+
except Exception as e:
|
|
149
|
+
if self._is_rate_limit_error(e):
|
|
150
|
+
if not self.exponential_backoff:
|
|
151
|
+
log_warning(
|
|
152
|
+
"Rate limit detected. To enable automatic backoff retry, set enable_backoff=True when creating the embedder."
|
|
153
|
+
)
|
|
154
|
+
raise e
|
|
155
|
+
|
|
156
|
+
log_info(f"Async rate limit detected on attempt {attempt + 1}")
|
|
157
|
+
if attempt < max_retries:
|
|
158
|
+
await self._async_rate_limit_backoff_sleep(attempt)
|
|
159
|
+
continue
|
|
160
|
+
else:
|
|
161
|
+
log_warning(f"Async max retries ({max_retries}) reached for rate limiting")
|
|
162
|
+
raise e
|
|
163
|
+
else:
|
|
164
|
+
log_debug(f"Async non-rate-limit error on attempt {attempt + 1}: {e}")
|
|
165
|
+
raise e
|
|
166
|
+
|
|
167
|
+
# This should never be reached, but just in case
|
|
168
|
+
log_error("Could not create embeddings. End of retry loop reached.")
|
|
169
|
+
return [], []
|
|
170
|
+
|
|
171
|
+
def get_embedding(self, text: str) -> List[float]:
|
|
172
|
+
response: Union[EmbeddingsFloatsEmbedResponse, EmbeddingsByTypeEmbedResponse] = self.response(text=text)
|
|
173
|
+
try:
|
|
174
|
+
if isinstance(response, EmbeddingsFloatsEmbedResponse):
|
|
175
|
+
return response.embeddings[0]
|
|
176
|
+
elif isinstance(response, EmbeddingsByTypeEmbedResponse):
|
|
177
|
+
return response.embeddings.float_[0] if response.embeddings.float_ else []
|
|
178
|
+
else:
|
|
179
|
+
log_warning("No embeddings found")
|
|
180
|
+
return []
|
|
181
|
+
except Exception as e:
|
|
182
|
+
log_warning(e)
|
|
183
|
+
return []
|
|
184
|
+
|
|
185
|
+
def get_embedding_and_usage(self, text: str) -> Tuple[List[float], Optional[Dict[str, Any]]]:
|
|
186
|
+
response: Union[EmbeddingsFloatsEmbedResponse, EmbeddingsByTypeEmbedResponse] = self.response(text=text)
|
|
187
|
+
|
|
188
|
+
embedding: List[float] = []
|
|
189
|
+
if isinstance(response, EmbeddingsFloatsEmbedResponse):
|
|
190
|
+
embedding = response.embeddings[0]
|
|
191
|
+
elif isinstance(response, EmbeddingsByTypeEmbedResponse):
|
|
192
|
+
embedding = response.embeddings.float_[0] if response.embeddings.float_ else []
|
|
193
|
+
|
|
194
|
+
usage = response.meta.billed_units if response.meta else None
|
|
195
|
+
if usage:
|
|
196
|
+
return embedding, usage.model_dump()
|
|
197
|
+
return embedding, None
|
|
198
|
+
|
|
199
|
+
async def async_get_embedding(self, text: str) -> List[float]:
|
|
200
|
+
request_params: Dict[str, Any] = {}
|
|
201
|
+
|
|
202
|
+
if self.id:
|
|
203
|
+
request_params["model"] = self.id
|
|
204
|
+
if self.input_type:
|
|
205
|
+
request_params["input_type"] = self.input_type
|
|
206
|
+
if self.embedding_types:
|
|
207
|
+
request_params["embedding_types"] = self.embedding_types
|
|
208
|
+
if self.request_params:
|
|
209
|
+
request_params.update(self.request_params)
|
|
210
|
+
|
|
211
|
+
try:
|
|
212
|
+
response: Union[EmbeddingsFloatsEmbedResponse, EmbeddingsByTypeEmbedResponse] = await self.aclient.embed(
|
|
213
|
+
texts=[text], **request_params
|
|
214
|
+
)
|
|
215
|
+
if isinstance(response, EmbeddingsFloatsEmbedResponse):
|
|
216
|
+
return response.embeddings[0]
|
|
217
|
+
elif isinstance(response, EmbeddingsByTypeEmbedResponse):
|
|
218
|
+
return response.embeddings.float_[0] if response.embeddings.float_ else []
|
|
219
|
+
else:
|
|
220
|
+
log_warning("No embeddings found")
|
|
221
|
+
return []
|
|
222
|
+
except Exception as e:
|
|
223
|
+
log_warning(e)
|
|
224
|
+
return []
|
|
225
|
+
|
|
226
|
+
async def async_get_embedding_and_usage(self, text: str) -> Tuple[List[float], Optional[Dict[str, Any]]]:
|
|
227
|
+
request_params: Dict[str, Any] = {}
|
|
228
|
+
|
|
229
|
+
if self.id:
|
|
230
|
+
request_params["model"] = self.id
|
|
231
|
+
if self.input_type:
|
|
232
|
+
request_params["input_type"] = self.input_type
|
|
233
|
+
if self.embedding_types:
|
|
234
|
+
request_params["embedding_types"] = self.embedding_types
|
|
235
|
+
if self.request_params:
|
|
236
|
+
request_params.update(self.request_params)
|
|
237
|
+
|
|
238
|
+
response: Union[EmbeddingsFloatsEmbedResponse, EmbeddingsByTypeEmbedResponse] = await self.aclient.embed(
|
|
239
|
+
texts=[text], **request_params
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
embedding: List[float] = []
|
|
243
|
+
if isinstance(response, EmbeddingsFloatsEmbedResponse):
|
|
244
|
+
embedding = response.embeddings[0]
|
|
245
|
+
elif isinstance(response, EmbeddingsByTypeEmbedResponse):
|
|
246
|
+
embedding = response.embeddings.float_[0] if response.embeddings.float_ else []
|
|
247
|
+
|
|
248
|
+
usage = response.meta.billed_units if response.meta else None
|
|
249
|
+
if usage:
|
|
250
|
+
return embedding, usage.model_dump()
|
|
251
|
+
return embedding, None
|
|
252
|
+
|
|
253
|
+
async def async_get_embeddings_batch_and_usage(
|
|
254
|
+
self, texts: List[str]
|
|
255
|
+
) -> Tuple[List[List[float]], List[Optional[Dict]]]:
|
|
256
|
+
"""
|
|
257
|
+
Get embeddings and usage for multiple texts in batches (async version).
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
texts: List of text strings to embed
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
s, List of usage dictionaries)
|
|
264
|
+
"""
|
|
265
|
+
all_embeddings = []
|
|
266
|
+
all_usage = []
|
|
267
|
+
log_info(f"Getting embeddings and usage for {len(texts)} texts in batches of {self.batch_size} (async)")
|
|
268
|
+
|
|
269
|
+
for i in range(0, len(texts), self.batch_size):
|
|
270
|
+
batch_texts = texts[i : i + self.batch_size]
|
|
271
|
+
|
|
272
|
+
try:
|
|
273
|
+
# Use retry logic for batch processing
|
|
274
|
+
batch_embeddings, batch_usage = await self._async_batch_with_retry(batch_texts)
|
|
275
|
+
all_embeddings.extend(batch_embeddings)
|
|
276
|
+
all_usage.extend(batch_usage)
|
|
277
|
+
|
|
278
|
+
except Exception as e:
|
|
279
|
+
log_warning(f"Async batch embedding failed after retries: {e}")
|
|
280
|
+
|
|
281
|
+
# Check if this is a rate limit error and backoff is disabled
|
|
282
|
+
if self._is_rate_limit_error(e) and not self.exponential_backoff:
|
|
283
|
+
log_warning("Rate limit hit and backoff is disabled. Failing immediately.")
|
|
284
|
+
raise e
|
|
285
|
+
|
|
286
|
+
# Only fall back to individual calls for non-rate-limit errors
|
|
287
|
+
# For rate limit errors, we should reduce batch size instead
|
|
288
|
+
if self._is_rate_limit_error(e):
|
|
289
|
+
log_warning("Rate limit hit even after retries. Consider reducing batch_size or upgrading API key.")
|
|
290
|
+
# Try with smaller batch size
|
|
291
|
+
if len(batch_texts) > 1:
|
|
292
|
+
smaller_batch_size = max(1, len(batch_texts) // 2)
|
|
293
|
+
log_info(f"Retrying with smaller batch size: {smaller_batch_size}")
|
|
294
|
+
for j in range(0, len(batch_texts), smaller_batch_size):
|
|
295
|
+
small_batch = batch_texts[j : j + smaller_batch_size]
|
|
296
|
+
try:
|
|
297
|
+
small_embeddings, small_usage = await self._async_batch_with_retry(small_batch)
|
|
298
|
+
all_embeddings.extend(small_embeddings)
|
|
299
|
+
all_usage.extend(small_usage)
|
|
300
|
+
except Exception as e3:
|
|
301
|
+
log_error(f"Failed even with reduced batch size: {e3}")
|
|
302
|
+
# Fall back to empty results for this batch
|
|
303
|
+
all_embeddings.extend([[] for _ in small_batch])
|
|
304
|
+
all_usage.extend([None for _ in small_batch])
|
|
305
|
+
else:
|
|
306
|
+
# Single item already failed, add empty result
|
|
307
|
+
log_debug("Single item failed, adding empty result")
|
|
308
|
+
all_embeddings.append([])
|
|
309
|
+
all_usage.append(None)
|
|
310
|
+
else:
|
|
311
|
+
# For non-rate-limit errors, fall back to individual calls
|
|
312
|
+
log_debug("Non-rate-limit error, falling back to individual calls")
|
|
313
|
+
for text in batch_texts:
|
|
314
|
+
try:
|
|
315
|
+
embedding, usage = await self.async_get_embedding_and_usage(text)
|
|
316
|
+
all_embeddings.append(embedding)
|
|
317
|
+
all_usage.append(usage)
|
|
318
|
+
except Exception as e2:
|
|
319
|
+
log_warning(f"Error in individual async embedding fallback: {e2}")
|
|
320
|
+
all_embeddings.append([])
|
|
321
|
+
all_usage.append(None)
|
|
322
|
+
|
|
323
|
+
return all_embeddings, all_usage
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Dict, List, Optional, Tuple
|
|
3
|
+
|
|
4
|
+
from agno.knowledge.embedder.base import Embedder
|
|
5
|
+
from agno.utils.log import logger
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
except ImportError:
|
|
11
|
+
raise ImportError("numpy not installed, use `pip install numpy`")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
from fastembed import TextEmbedding # type: ignore
|
|
16
|
+
|
|
17
|
+
except ImportError:
|
|
18
|
+
raise ImportError("fastembed not installed, use pip install fastembed")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class FastEmbedEmbedder(Embedder):
|
|
23
|
+
"""Using BAAI/bge-small-en-v1.5 model, more models available: https://qdrant.github.io/fastembed/examples/Supported_Models/"""
|
|
24
|
+
|
|
25
|
+
id: str = "BAAI/bge-small-en-v1.5"
|
|
26
|
+
dimensions: Optional[int] = 384
|
|
27
|
+
|
|
28
|
+
def get_embedding(self, text: str) -> List[float]:
|
|
29
|
+
model = TextEmbedding(model_name=self.id)
|
|
30
|
+
embeddings = model.embed(text)
|
|
31
|
+
embedding_list = list(embeddings)[0]
|
|
32
|
+
if isinstance(embedding_list, np.ndarray):
|
|
33
|
+
return embedding_list.tolist()
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
return list(embedding_list)
|
|
37
|
+
except Exception as e:
|
|
38
|
+
logger.warning(e)
|
|
39
|
+
return []
|
|
40
|
+
|
|
41
|
+
def get_embedding_and_usage(self, text: str) -> Tuple[List[float], Optional[Dict]]:
|
|
42
|
+
embedding = self.get_embedding(text=text)
|
|
43
|
+
# Currently, FastEmbed does not provide usage information
|
|
44
|
+
usage = None
|
|
45
|
+
|
|
46
|
+
return embedding, usage
|
|
47
|
+
|
|
48
|
+
async def async_get_embedding(self, text: str) -> List[float]:
|
|
49
|
+
"""Async version using thread executor for CPU-bound operations."""
|
|
50
|
+
import asyncio
|
|
51
|
+
|
|
52
|
+
loop = asyncio.get_event_loop()
|
|
53
|
+
# Run the CPU-bound operation in a thread executor
|
|
54
|
+
return await loop.run_in_executor(None, self.get_embedding, text)
|
|
55
|
+
|
|
56
|
+
async def async_get_embedding_and_usage(self, text: str) -> Tuple[List[float], Optional[Dict]]:
|
|
57
|
+
"""Async version using thread executor for CPU-bound operations."""
|
|
58
|
+
import asyncio
|
|
59
|
+
|
|
60
|
+
loop = asyncio.get_event_loop()
|
|
61
|
+
# Run the CPU-bound operation in a thread executor
|
|
62
|
+
return await loop.run_in_executor(None, self.get_embedding_and_usage, text)
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from os import getenv
|
|
3
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
4
|
+
|
|
5
|
+
from agno.knowledge.embedder.base import Embedder
|
|
6
|
+
from agno.utils.log import log_error, log_info, log_warning
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from google import genai
|
|
10
|
+
from google.genai import Client as GeminiClient
|
|
11
|
+
from google.genai.types import EmbedContentResponse
|
|
12
|
+
except ImportError:
|
|
13
|
+
raise ImportError("`google-genai` not installed. Please install it using `pip install google-genai`")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class GeminiEmbedder(Embedder):
|
|
18
|
+
id: str = "gemini-embedding-exp-03-07"
|
|
19
|
+
task_type: str = "RETRIEVAL_QUERY"
|
|
20
|
+
title: Optional[str] = None
|
|
21
|
+
dimensions: Optional[int] = 1536
|
|
22
|
+
api_key: Optional[str] = None
|
|
23
|
+
request_params: Optional[Dict[str, Any]] = None
|
|
24
|
+
client_params: Optional[Dict[str, Any]] = None
|
|
25
|
+
gemini_client: Optional[GeminiClient] = None
|
|
26
|
+
# Vertex AI parameters
|
|
27
|
+
vertexai: bool = False
|
|
28
|
+
project_id: Optional[str] = None
|
|
29
|
+
location: Optional[str] = None
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def client(self):
|
|
33
|
+
if self.gemini_client:
|
|
34
|
+
return self.gemini_client
|
|
35
|
+
|
|
36
|
+
_client_params: Dict[str, Any] = {}
|
|
37
|
+
vertexai = self.vertexai or getenv("GOOGLE_GENAI_USE_VERTEXAI", "false").lower() == "true"
|
|
38
|
+
|
|
39
|
+
if not vertexai:
|
|
40
|
+
self.api_key = self.api_key or getenv("GOOGLE_API_KEY")
|
|
41
|
+
if not self.api_key:
|
|
42
|
+
log_error("GOOGLE_API_KEY not set. Please set the GOOGLE_API_KEY environment variable.")
|
|
43
|
+
_client_params["api_key"] = self.api_key
|
|
44
|
+
else:
|
|
45
|
+
log_info("Using Vertex AI API for embeddings")
|
|
46
|
+
_client_params["vertexai"] = True
|
|
47
|
+
_client_params["project"] = self.project_id or getenv("GOOGLE_CLOUD_PROJECT")
|
|
48
|
+
_client_params["location"] = self.location or getenv("GOOGLE_CLOUD_LOCATION")
|
|
49
|
+
|
|
50
|
+
_client_params = {k: v for k, v in _client_params.items() if v is not None}
|
|
51
|
+
|
|
52
|
+
if self.client_params:
|
|
53
|
+
_client_params.update(self.client_params)
|
|
54
|
+
|
|
55
|
+
self.gemini_client = genai.Client(**_client_params)
|
|
56
|
+
|
|
57
|
+
return self.gemini_client
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def aclient(self) -> GeminiClient:
|
|
61
|
+
"""Returns the same client instance since Google GenAI Client supports both sync and async operations."""
|
|
62
|
+
return self.client
|
|
63
|
+
|
|
64
|
+
def _response(self, text: str) -> EmbedContentResponse:
|
|
65
|
+
# If a user provides a model id with the `models/` prefix, we need to remove it
|
|
66
|
+
_id = self.id
|
|
67
|
+
if _id.startswith("models/"):
|
|
68
|
+
_id = _id.split("/")[-1]
|
|
69
|
+
|
|
70
|
+
_request_params: Dict[str, Any] = {"contents": text, "model": _id, "config": {}}
|
|
71
|
+
if self.dimensions:
|
|
72
|
+
_request_params["config"]["output_dimensionality"] = self.dimensions
|
|
73
|
+
if self.task_type:
|
|
74
|
+
_request_params["config"]["task_type"] = self.task_type
|
|
75
|
+
if self.title:
|
|
76
|
+
_request_params["config"]["title"] = self.title
|
|
77
|
+
if not _request_params["config"]:
|
|
78
|
+
del _request_params["config"]
|
|
79
|
+
|
|
80
|
+
if self.request_params:
|
|
81
|
+
_request_params.update(self.request_params)
|
|
82
|
+
return self.client.models.embed_content(**_request_params)
|
|
83
|
+
|
|
84
|
+
def get_embedding(self, text: str) -> List[float]:
|
|
85
|
+
response = self._response(text=text)
|
|
86
|
+
try:
|
|
87
|
+
if response.embeddings and len(response.embeddings) > 0:
|
|
88
|
+
values = response.embeddings[0].values
|
|
89
|
+
if values is not None:
|
|
90
|
+
return values
|
|
91
|
+
log_info("No embeddings found in response")
|
|
92
|
+
return []
|
|
93
|
+
except Exception as e:
|
|
94
|
+
log_error(f"Error extracting embeddings: {e}")
|
|
95
|
+
return []
|
|
96
|
+
|
|
97
|
+
def get_embedding_and_usage(self, text: str) -> Tuple[List[float], Optional[Dict[str, Any]]]:
|
|
98
|
+
response = self._response(text=text)
|
|
99
|
+
usage = None
|
|
100
|
+
if response.metadata and hasattr(response.metadata, "billable_character_count"):
|
|
101
|
+
usage = {"billable_character_count": response.metadata.billable_character_count}
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
if response.embeddings and len(response.embeddings) > 0:
|
|
105
|
+
values = response.embeddings[0].values
|
|
106
|
+
if values is not None:
|
|
107
|
+
return values, usage
|
|
108
|
+
log_info("No embeddings found in response")
|
|
109
|
+
return [], usage
|
|
110
|
+
except Exception as e:
|
|
111
|
+
log_error(f"Error extracting embeddings: {e}")
|
|
112
|
+
return [], usage
|
|
113
|
+
|
|
114
|
+
async def async_get_embedding(self, text: str) -> List[float]:
|
|
115
|
+
"""Async version of get_embedding using client.aio."""
|
|
116
|
+
# If a user provides a model id with the `models/` prefix, we need to remove it
|
|
117
|
+
_id = self.id
|
|
118
|
+
if _id.startswith("models/"):
|
|
119
|
+
_id = _id.split("/")[-1]
|
|
120
|
+
|
|
121
|
+
_request_params: Dict[str, Any] = {"contents": text, "model": _id, "config": {}}
|
|
122
|
+
if self.dimensions:
|
|
123
|
+
_request_params["config"]["output_dimensionality"] = self.dimensions
|
|
124
|
+
if self.task_type:
|
|
125
|
+
_request_params["config"]["task_type"] = self.task_type
|
|
126
|
+
if self.title:
|
|
127
|
+
_request_params["config"]["title"] = self.title
|
|
128
|
+
if not _request_params["config"]:
|
|
129
|
+
del _request_params["config"]
|
|
130
|
+
|
|
131
|
+
if self.request_params:
|
|
132
|
+
_request_params.update(self.request_params)
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
response = await self.aclient.aio.models.embed_content(**_request_params)
|
|
136
|
+
if response.embeddings and len(response.embeddings) > 0:
|
|
137
|
+
values = response.embeddings[0].values
|
|
138
|
+
if values is not None:
|
|
139
|
+
return values
|
|
140
|
+
log_info("No embeddings found in response")
|
|
141
|
+
return []
|
|
142
|
+
except Exception as e:
|
|
143
|
+
log_error(f"Error extracting embeddings: {e}")
|
|
144
|
+
return []
|
|
145
|
+
|
|
146
|
+
async def async_get_embedding_and_usage(self, text: str) -> Tuple[List[float], Optional[Dict[str, Any]]]:
|
|
147
|
+
"""Async version of get_embedding_and_usage using client.aio."""
|
|
148
|
+
# If a user provides a model id with the `models/` prefix, we need to remove it
|
|
149
|
+
_id = self.id
|
|
150
|
+
if _id.startswith("models/"):
|
|
151
|
+
_id = _id.split("/")[-1]
|
|
152
|
+
|
|
153
|
+
_request_params: Dict[str, Any] = {"contents": text, "model": _id, "config": {}}
|
|
154
|
+
if self.dimensions:
|
|
155
|
+
_request_params["config"]["output_dimensionality"] = self.dimensions
|
|
156
|
+
if self.task_type:
|
|
157
|
+
_request_params["config"]["task_type"] = self.task_type
|
|
158
|
+
if self.title:
|
|
159
|
+
_request_params["config"]["title"] = self.title
|
|
160
|
+
if not _request_params["config"]:
|
|
161
|
+
del _request_params["config"]
|
|
162
|
+
|
|
163
|
+
if self.request_params:
|
|
164
|
+
_request_params.update(self.request_params)
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
response = await self.aclient.aio.models.embed_content(**_request_params)
|
|
168
|
+
usage = None
|
|
169
|
+
if response.metadata and hasattr(response.metadata, "billable_character_count"):
|
|
170
|
+
usage = {"billable_character_count": response.metadata.billable_character_count}
|
|
171
|
+
|
|
172
|
+
if response.embeddings and len(response.embeddings) > 0:
|
|
173
|
+
values = response.embeddings[0].values
|
|
174
|
+
if values is not None:
|
|
175
|
+
return values, usage
|
|
176
|
+
log_info("No embeddings found in response")
|
|
177
|
+
return [], usage
|
|
178
|
+
except Exception as e:
|
|
179
|
+
log_error(f"Error extracting embeddings: {e}")
|
|
180
|
+
return [], usage
|
|
181
|
+
|
|
182
|
+
async def async_get_embeddings_batch_and_usage(
|
|
183
|
+
self, texts: List[str]
|
|
184
|
+
) -> Tuple[List[List[float]], List[Optional[Dict[str, Any]]]]:
|
|
185
|
+
"""
|
|
186
|
+
Get embeddings and usage for multiple texts in batches.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
texts: List of text strings to embed
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Tuple of (List of embedding vectors, List of usage dictionaries)
|
|
193
|
+
"""
|
|
194
|
+
all_embeddings: List[List[float]] = []
|
|
195
|
+
all_usage: List[Optional[Dict[str, Any]]] = []
|
|
196
|
+
log_info(f"Getting embeddings and usage for {len(texts)} texts in batches of {self.batch_size}")
|
|
197
|
+
|
|
198
|
+
for i in range(0, len(texts), self.batch_size):
|
|
199
|
+
batch_texts = texts[i : i + self.batch_size]
|
|
200
|
+
|
|
201
|
+
# If a user provides a model id with the `models/` prefix, we need to remove it
|
|
202
|
+
_id = self.id
|
|
203
|
+
if _id.startswith("models/"):
|
|
204
|
+
_id = _id.split("/")[-1]
|
|
205
|
+
|
|
206
|
+
_request_params: Dict[str, Any] = {"contents": batch_texts, "model": _id, "config": {}}
|
|
207
|
+
if self.dimensions:
|
|
208
|
+
_request_params["config"]["output_dimensionality"] = self.dimensions
|
|
209
|
+
if self.task_type:
|
|
210
|
+
_request_params["config"]["task_type"] = self.task_type
|
|
211
|
+
if self.title:
|
|
212
|
+
_request_params["config"]["title"] = self.title
|
|
213
|
+
if not _request_params["config"]:
|
|
214
|
+
del _request_params["config"]
|
|
215
|
+
|
|
216
|
+
if self.request_params:
|
|
217
|
+
_request_params.update(self.request_params)
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
response = await self.aclient.aio.models.embed_content(**_request_params)
|
|
221
|
+
|
|
222
|
+
# Extract embeddings from batch response
|
|
223
|
+
if response.embeddings:
|
|
224
|
+
batch_embeddings = []
|
|
225
|
+
for embedding in response.embeddings:
|
|
226
|
+
if embedding.values is not None:
|
|
227
|
+
batch_embeddings.append(embedding.values)
|
|
228
|
+
else:
|
|
229
|
+
batch_embeddings.append([])
|
|
230
|
+
all_embeddings.extend(batch_embeddings)
|
|
231
|
+
else:
|
|
232
|
+
# If no embeddings, add empty lists for each text in batch
|
|
233
|
+
all_embeddings.extend([[] for _ in batch_texts])
|
|
234
|
+
|
|
235
|
+
# Extract usage information
|
|
236
|
+
usage_dict = None
|
|
237
|
+
if response.metadata and hasattr(response.metadata, "billable_character_count"):
|
|
238
|
+
usage_dict = {"billable_character_count": response.metadata.billable_character_count}
|
|
239
|
+
|
|
240
|
+
# Add same usage info for each embedding in the batch
|
|
241
|
+
all_usage.extend([usage_dict] * len(batch_texts))
|
|
242
|
+
|
|
243
|
+
except Exception as e:
|
|
244
|
+
log_warning(f"Error in async batch embedding: {e}")
|
|
245
|
+
# Fallback to individual calls for this batch
|
|
246
|
+
for text in batch_texts:
|
|
247
|
+
try:
|
|
248
|
+
text_embedding: List[float]
|
|
249
|
+
text_usage: Optional[Dict[str, Any]]
|
|
250
|
+
text_embedding, text_usage = await self.async_get_embedding_and_usage(text)
|
|
251
|
+
all_embeddings.append(text_embedding)
|
|
252
|
+
all_usage.append(text_usage)
|
|
253
|
+
except Exception as e2:
|
|
254
|
+
log_warning(f"Error in individual async embedding fallback: {e2}")
|
|
255
|
+
all_embeddings.append([])
|
|
256
|
+
all_usage.append(None)
|
|
257
|
+
|
|
258
|
+
return all_embeddings, all_usage
|