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,195 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
3
|
+
|
|
4
|
+
from typing_extensions import Literal
|
|
5
|
+
|
|
6
|
+
from agno.knowledge.embedder.base import Embedder
|
|
7
|
+
from agno.utils.log import log_info, log_warning
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from openai import AsyncOpenAI
|
|
11
|
+
from openai import OpenAI as OpenAIClient
|
|
12
|
+
from openai.types.create_embedding_response import CreateEmbeddingResponse
|
|
13
|
+
except ImportError:
|
|
14
|
+
raise ImportError("`openai` not installed")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class OpenAIEmbedder(Embedder):
|
|
19
|
+
id: str = "text-embedding-3-small"
|
|
20
|
+
dimensions: Optional[int] = None
|
|
21
|
+
encoding_format: Literal["float", "base64"] = "float"
|
|
22
|
+
user: Optional[str] = None
|
|
23
|
+
api_key: Optional[str] = None
|
|
24
|
+
organization: Optional[str] = None
|
|
25
|
+
base_url: Optional[str] = None
|
|
26
|
+
request_params: Optional[Dict[str, Any]] = None
|
|
27
|
+
client_params: Optional[Dict[str, Any]] = None
|
|
28
|
+
openai_client: Optional[OpenAIClient] = None
|
|
29
|
+
async_client: Optional[AsyncOpenAI] = None
|
|
30
|
+
|
|
31
|
+
def __post_init__(self):
|
|
32
|
+
if self.dimensions is None:
|
|
33
|
+
self.dimensions = 3072 if self.id == "text-embedding-3-large" else 1536
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def client(self) -> OpenAIClient:
|
|
37
|
+
if self.openai_client:
|
|
38
|
+
return self.openai_client
|
|
39
|
+
|
|
40
|
+
_client_params: Dict[str, Any] = {
|
|
41
|
+
"api_key": self.api_key,
|
|
42
|
+
"organization": self.organization,
|
|
43
|
+
"base_url": self.base_url,
|
|
44
|
+
}
|
|
45
|
+
_client_params = {k: v for k, v in _client_params.items() if v is not None}
|
|
46
|
+
if self.client_params:
|
|
47
|
+
_client_params.update(self.client_params)
|
|
48
|
+
self.openai_client = OpenAIClient(**_client_params)
|
|
49
|
+
return self.openai_client
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def aclient(self) -> AsyncOpenAI:
|
|
53
|
+
if self.async_client:
|
|
54
|
+
return self.async_client
|
|
55
|
+
params = {
|
|
56
|
+
"api_key": self.api_key,
|
|
57
|
+
"organization": self.organization,
|
|
58
|
+
"base_url": self.base_url,
|
|
59
|
+
}
|
|
60
|
+
filtered_params: Dict[str, Any] = {k: v for k, v in params.items() if v is not None}
|
|
61
|
+
if self.client_params:
|
|
62
|
+
filtered_params.update(self.client_params)
|
|
63
|
+
self.async_client = AsyncOpenAI(**filtered_params)
|
|
64
|
+
return self.async_client
|
|
65
|
+
|
|
66
|
+
def response(self, text: str) -> CreateEmbeddingResponse:
|
|
67
|
+
_request_params: Dict[str, Any] = {
|
|
68
|
+
"input": text,
|
|
69
|
+
"model": self.id,
|
|
70
|
+
"encoding_format": self.encoding_format,
|
|
71
|
+
}
|
|
72
|
+
if self.user is not None:
|
|
73
|
+
_request_params["user"] = self.user
|
|
74
|
+
if self.id.startswith("text-embedding-3"):
|
|
75
|
+
_request_params["dimensions"] = self.dimensions
|
|
76
|
+
if self.request_params:
|
|
77
|
+
_request_params.update(self.request_params)
|
|
78
|
+
return self.client.embeddings.create(**_request_params)
|
|
79
|
+
|
|
80
|
+
def get_embedding(self, text: str) -> List[float]:
|
|
81
|
+
try:
|
|
82
|
+
response: CreateEmbeddingResponse = self.response(text=text)
|
|
83
|
+
return response.data[0].embedding
|
|
84
|
+
except Exception as e:
|
|
85
|
+
log_warning(e)
|
|
86
|
+
return []
|
|
87
|
+
|
|
88
|
+
def get_embedding_and_usage(self, text: str) -> Tuple[List[float], Optional[Dict]]:
|
|
89
|
+
try:
|
|
90
|
+
response: CreateEmbeddingResponse = self.response(text=text)
|
|
91
|
+
|
|
92
|
+
embedding = response.data[0].embedding
|
|
93
|
+
usage = response.usage
|
|
94
|
+
if usage:
|
|
95
|
+
return embedding, usage.model_dump()
|
|
96
|
+
return embedding, None
|
|
97
|
+
except Exception as e:
|
|
98
|
+
log_warning(e)
|
|
99
|
+
return [], None
|
|
100
|
+
|
|
101
|
+
async def async_get_embedding(self, text: str) -> List[float]:
|
|
102
|
+
req: Dict[str, Any] = {
|
|
103
|
+
"input": text,
|
|
104
|
+
"model": self.id,
|
|
105
|
+
"encoding_format": self.encoding_format,
|
|
106
|
+
}
|
|
107
|
+
if self.user is not None:
|
|
108
|
+
req["user"] = self.user
|
|
109
|
+
if self.id.startswith("text-embedding-3"):
|
|
110
|
+
req["dimensions"] = self.dimensions
|
|
111
|
+
if self.request_params:
|
|
112
|
+
req.update(self.request_params)
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
response: CreateEmbeddingResponse = await self.aclient.embeddings.create(**req)
|
|
116
|
+
return response.data[0].embedding
|
|
117
|
+
except Exception as e:
|
|
118
|
+
log_warning(e)
|
|
119
|
+
return []
|
|
120
|
+
|
|
121
|
+
async def async_get_embedding_and_usage(self, text: str):
|
|
122
|
+
req: Dict[str, Any] = {
|
|
123
|
+
"input": text,
|
|
124
|
+
"model": self.id,
|
|
125
|
+
"encoding_format": self.encoding_format,
|
|
126
|
+
}
|
|
127
|
+
if self.user is not None:
|
|
128
|
+
req["user"] = self.user
|
|
129
|
+
if self.id.startswith("text-embedding-3"):
|
|
130
|
+
req["dimensions"] = self.dimensions
|
|
131
|
+
if self.request_params:
|
|
132
|
+
req.update(self.request_params)
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
response = await self.aclient.embeddings.create(**req)
|
|
136
|
+
embedding = response.data[0].embedding
|
|
137
|
+
usage = response.usage
|
|
138
|
+
return embedding, usage.model_dump() if usage else None
|
|
139
|
+
except Exception as e:
|
|
140
|
+
log_warning(f"Error getting embedding: {e}")
|
|
141
|
+
return [], None
|
|
142
|
+
|
|
143
|
+
async def async_get_embeddings_batch_and_usage(
|
|
144
|
+
self, texts: List[str]
|
|
145
|
+
) -> Tuple[List[List[float]], List[Optional[Dict]]]:
|
|
146
|
+
"""
|
|
147
|
+
Get embeddings and usage for multiple texts in batches (async version).
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
texts: List of text strings to embed
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Tuple of (List of embedding vectors, List of usage dictionaries)
|
|
154
|
+
"""
|
|
155
|
+
all_embeddings = []
|
|
156
|
+
all_usage = []
|
|
157
|
+
log_info(f"Getting embeddings and usage for {len(texts)} texts in batches of {self.batch_size} (async)")
|
|
158
|
+
|
|
159
|
+
for i in range(0, len(texts), self.batch_size):
|
|
160
|
+
batch_texts = texts[i : i + self.batch_size]
|
|
161
|
+
|
|
162
|
+
req: Dict[str, Any] = {
|
|
163
|
+
"input": batch_texts,
|
|
164
|
+
"model": self.id,
|
|
165
|
+
"encoding_format": self.encoding_format,
|
|
166
|
+
}
|
|
167
|
+
if self.user is not None:
|
|
168
|
+
req["user"] = self.user
|
|
169
|
+
if self.id.startswith("text-embedding-3"):
|
|
170
|
+
req["dimensions"] = self.dimensions
|
|
171
|
+
if self.request_params:
|
|
172
|
+
req.update(self.request_params)
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
response: CreateEmbeddingResponse = await self.aclient.embeddings.create(**req)
|
|
176
|
+
batch_embeddings = [data.embedding for data in response.data]
|
|
177
|
+
all_embeddings.extend(batch_embeddings)
|
|
178
|
+
|
|
179
|
+
# For each embedding in the batch, add the same usage information
|
|
180
|
+
usage_dict = response.usage.model_dump() if response.usage else None
|
|
181
|
+
all_usage.extend([usage_dict] * len(batch_embeddings))
|
|
182
|
+
except Exception as e:
|
|
183
|
+
log_warning(f"Error in async batch embedding: {e}")
|
|
184
|
+
# Fallback to individual calls for this batch
|
|
185
|
+
for text in batch_texts:
|
|
186
|
+
try:
|
|
187
|
+
embedding, usage = await self.async_get_embedding_and_usage(text)
|
|
188
|
+
all_embeddings.append(embedding)
|
|
189
|
+
all_usage.append(usage)
|
|
190
|
+
except Exception as e2:
|
|
191
|
+
log_warning(f"Error in individual async embedding fallback: {e2}")
|
|
192
|
+
all_embeddings.append([])
|
|
193
|
+
all_usage.append(None)
|
|
194
|
+
|
|
195
|
+
return all_embeddings, all_usage
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Dict, List, Optional, Tuple, Union
|
|
3
|
+
|
|
4
|
+
from agno.knowledge.embedder.base import Embedder
|
|
5
|
+
from agno.utils.log import logger
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from sentence_transformers import SentenceTransformer
|
|
9
|
+
|
|
10
|
+
except ImportError:
|
|
11
|
+
raise ImportError("`sentence-transformers` not installed, please run `pip install sentence-transformers`")
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
except ImportError:
|
|
17
|
+
raise ImportError("numpy not installed, use `pip install numpy`")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class SentenceTransformerEmbedder(Embedder):
|
|
22
|
+
id: str = "sentence-transformers/all-MiniLM-L6-v2"
|
|
23
|
+
dimensions: int = 384
|
|
24
|
+
sentence_transformer_client: Optional[SentenceTransformer] = None
|
|
25
|
+
prompt: Optional[str] = None
|
|
26
|
+
normalize_embeddings: bool = False
|
|
27
|
+
|
|
28
|
+
def __post_init__(self):
|
|
29
|
+
# Initialize the SentenceTransformer model eagerly to avoid race conditions in async contexts
|
|
30
|
+
if self.sentence_transformer_client is None:
|
|
31
|
+
self.sentence_transformer_client = SentenceTransformer(model_name_or_path=self.id)
|
|
32
|
+
|
|
33
|
+
def get_embedding(self, text: Union[str, List[str]]) -> List[float]:
|
|
34
|
+
if self.sentence_transformer_client is None:
|
|
35
|
+
raise RuntimeError("SentenceTransformer model not initialized")
|
|
36
|
+
model = self.sentence_transformer_client
|
|
37
|
+
embedding = model.encode(text, prompt=self.prompt, normalize_embeddings=self.normalize_embeddings)
|
|
38
|
+
try:
|
|
39
|
+
if isinstance(embedding, np.ndarray):
|
|
40
|
+
return embedding.tolist()
|
|
41
|
+
|
|
42
|
+
return embedding # type: ignore
|
|
43
|
+
except Exception as e:
|
|
44
|
+
logger.warning(e)
|
|
45
|
+
return []
|
|
46
|
+
|
|
47
|
+
def get_embedding_and_usage(self, text: str) -> Tuple[List[float], Optional[Dict]]:
|
|
48
|
+
return self.get_embedding(text=text), None
|
|
49
|
+
|
|
50
|
+
async def async_get_embedding(self, text: Union[str, List[str]]) -> List[float]:
|
|
51
|
+
"""Async version using thread executor for CPU-bound operations."""
|
|
52
|
+
import asyncio
|
|
53
|
+
|
|
54
|
+
loop = asyncio.get_event_loop()
|
|
55
|
+
# Run the CPU-bound operation in a thread executor
|
|
56
|
+
return await loop.run_in_executor(None, self.get_embedding, text)
|
|
57
|
+
|
|
58
|
+
async def async_get_embedding_and_usage(self, text: str) -> Tuple[List[float], Optional[Dict]]:
|
|
59
|
+
"""Async version using thread executor for CPU-bound operations."""
|
|
60
|
+
import asyncio
|
|
61
|
+
|
|
62
|
+
loop = asyncio.get_event_loop()
|
|
63
|
+
return await loop.run_in_executor(None, self.get_embedding_and_usage, text)
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from os import getenv
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
from agno.knowledge.embedder.base import Embedder
|
|
7
|
+
from agno.utils.log import logger
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from vllm import LLM # type: ignore
|
|
11
|
+
from vllm.outputs import EmbeddingRequestOutput # type: ignore
|
|
12
|
+
except ImportError:
|
|
13
|
+
raise ImportError("`vllm` not installed. Please install using `pip install vllm`.")
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from openai import AsyncOpenAI
|
|
17
|
+
from openai import OpenAI as OpenAIClient
|
|
18
|
+
from openai.types.create_embedding_response import CreateEmbeddingResponse
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class VLLMEmbedder(Embedder):
|
|
23
|
+
"""
|
|
24
|
+
VLLM Embedder supporting both local and remote deployment modes.
|
|
25
|
+
|
|
26
|
+
Local Mode (default):
|
|
27
|
+
- Loads model locally and runs inference on your GPU/CPU
|
|
28
|
+
- No API key required
|
|
29
|
+
- Example: VLLMEmbedder(id="intfloat/e5-mistral-7b-instruct")
|
|
30
|
+
|
|
31
|
+
Remote Mode:
|
|
32
|
+
- Connects to a remote vLLM server via OpenAI-compatible API
|
|
33
|
+
- Uses OpenAI SDK to communicate with vLLM's OpenAI-compatible endpoint
|
|
34
|
+
- Requires base_url and optionally api_key
|
|
35
|
+
- Example: VLLMEmbedder(base_url="http://localhost:8000/v1", api_key="your-key")
|
|
36
|
+
- Ref: https://docs.vllm.ai/en/latest/serving/openai_compatible_server.html
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
id: str = "sentence-transformers/all-MiniLM-L6-v2"
|
|
40
|
+
dimensions: int = 4096
|
|
41
|
+
# Local mode parameters
|
|
42
|
+
enforce_eager: bool = True
|
|
43
|
+
vllm_kwargs: Optional[Dict[str, Any]] = None
|
|
44
|
+
vllm_client: Optional[LLM] = None
|
|
45
|
+
# Remote mode parameters
|
|
46
|
+
api_key: Optional[str] = getenv("VLLM_API_KEY")
|
|
47
|
+
base_url: Optional[str] = None
|
|
48
|
+
request_params: Optional[Dict[str, Any]] = None
|
|
49
|
+
client_params: Optional[Dict[str, Any]] = None
|
|
50
|
+
remote_client: Optional["OpenAIClient"] = None # OpenAI-compatible client for vLLM server
|
|
51
|
+
async_remote_client: Optional["AsyncOpenAI"] = None # Async OpenAI-compatible client for vLLM server
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def is_remote(self) -> bool:
|
|
55
|
+
"""Determine if we should use remote mode."""
|
|
56
|
+
return self.base_url is not None
|
|
57
|
+
|
|
58
|
+
def _get_vllm_client(self) -> LLM:
|
|
59
|
+
"""Get local VLLM client."""
|
|
60
|
+
if self.vllm_client:
|
|
61
|
+
return self.vllm_client
|
|
62
|
+
|
|
63
|
+
_vllm_params: Dict[str, Any] = {
|
|
64
|
+
"model": self.id,
|
|
65
|
+
"task": "embed",
|
|
66
|
+
"enforce_eager": self.enforce_eager,
|
|
67
|
+
}
|
|
68
|
+
if self.vllm_kwargs:
|
|
69
|
+
_vllm_params.update(self.vllm_kwargs)
|
|
70
|
+
self.vllm_client = LLM(**_vllm_params)
|
|
71
|
+
return self.vllm_client
|
|
72
|
+
|
|
73
|
+
def _get_remote_client(self) -> "OpenAIClient":
|
|
74
|
+
"""Get OpenAI-compatible client for remote vLLM server."""
|
|
75
|
+
if self.remote_client:
|
|
76
|
+
return self.remote_client
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
from openai import OpenAI as OpenAIClient
|
|
80
|
+
except ImportError:
|
|
81
|
+
raise ImportError("`openai` package required for remote vLLM mode. ")
|
|
82
|
+
|
|
83
|
+
_client_params: Dict[str, Any] = {
|
|
84
|
+
"api_key": self.api_key or "EMPTY", # VLLM can run without API key
|
|
85
|
+
"base_url": self.base_url,
|
|
86
|
+
}
|
|
87
|
+
if self.client_params:
|
|
88
|
+
_client_params.update(self.client_params)
|
|
89
|
+
self.remote_client = OpenAIClient(**_client_params)
|
|
90
|
+
return self.remote_client
|
|
91
|
+
|
|
92
|
+
def _get_async_remote_client(self) -> "AsyncOpenAI":
|
|
93
|
+
"""Get async OpenAI-compatible client for remote vLLM server."""
|
|
94
|
+
if self.async_remote_client:
|
|
95
|
+
return self.async_remote_client
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
from openai import AsyncOpenAI
|
|
99
|
+
except ImportError:
|
|
100
|
+
raise ImportError("`openai` package required for remote vLLM mode. ")
|
|
101
|
+
|
|
102
|
+
_client_params: Dict[str, Any] = {
|
|
103
|
+
"api_key": self.api_key or "EMPTY",
|
|
104
|
+
"base_url": self.base_url,
|
|
105
|
+
}
|
|
106
|
+
if self.client_params:
|
|
107
|
+
_client_params.update(self.client_params)
|
|
108
|
+
self.async_remote_client = AsyncOpenAI(**_client_params)
|
|
109
|
+
return self.async_remote_client
|
|
110
|
+
|
|
111
|
+
def _create_embedding_local(self, text: str) -> Optional[EmbeddingRequestOutput]:
|
|
112
|
+
"""Create embedding using local VLLM."""
|
|
113
|
+
try:
|
|
114
|
+
outputs = self._get_vllm_client().embed([text])
|
|
115
|
+
return outputs[0] if outputs else None
|
|
116
|
+
except Exception as e:
|
|
117
|
+
logger.warning(f"Error creating local embedding: {e}")
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
def _create_embedding_remote(self, text: str) -> "CreateEmbeddingResponse":
|
|
121
|
+
"""Create embedding using remote vLLM server."""
|
|
122
|
+
_request_params: Dict[str, Any] = {
|
|
123
|
+
"input": text,
|
|
124
|
+
"model": self.id,
|
|
125
|
+
}
|
|
126
|
+
if self.request_params:
|
|
127
|
+
_request_params.update(self.request_params)
|
|
128
|
+
return self._get_remote_client().embeddings.create(**_request_params)
|
|
129
|
+
|
|
130
|
+
def get_embedding(self, text: str) -> List[float]:
|
|
131
|
+
try:
|
|
132
|
+
if self.is_remote:
|
|
133
|
+
# Remote mode: OpenAI-compatible API
|
|
134
|
+
response: "CreateEmbeddingResponse" = self._create_embedding_remote(text=text)
|
|
135
|
+
return response.data[0].embedding
|
|
136
|
+
else:
|
|
137
|
+
# Local mode: Direct VLLM
|
|
138
|
+
output = self._create_embedding_local(text=text)
|
|
139
|
+
if output and hasattr(output, "outputs") and hasattr(output.outputs, "embedding"):
|
|
140
|
+
embedding = output.outputs.embedding
|
|
141
|
+
if len(embedding) != self.dimensions:
|
|
142
|
+
logger.warning(f"Expected embedding dimension {self.dimensions}, but got {len(embedding)}")
|
|
143
|
+
return embedding
|
|
144
|
+
return []
|
|
145
|
+
except Exception as e:
|
|
146
|
+
logger.warning(f"Error extracting embedding: {e}")
|
|
147
|
+
return []
|
|
148
|
+
|
|
149
|
+
def get_embedding_and_usage(self, text: str) -> Tuple[List[float], Optional[Dict]]:
|
|
150
|
+
if self.is_remote:
|
|
151
|
+
try:
|
|
152
|
+
response: "CreateEmbeddingResponse" = self._create_embedding_remote(text=text)
|
|
153
|
+
embedding = response.data[0].embedding
|
|
154
|
+
usage = response.usage
|
|
155
|
+
if usage:
|
|
156
|
+
return embedding, usage.model_dump()
|
|
157
|
+
return embedding, None
|
|
158
|
+
except Exception as e:
|
|
159
|
+
logger.warning(f"Error in remote embedding: {e}")
|
|
160
|
+
return [], None
|
|
161
|
+
else:
|
|
162
|
+
embedding = self.get_embedding(text=text)
|
|
163
|
+
# Local VLLM doesn't provide usage information
|
|
164
|
+
return embedding, None
|
|
165
|
+
|
|
166
|
+
async def async_get_embedding(self, text: str) -> List[float]:
|
|
167
|
+
"""Async version of get_embedding using thread executor for local mode."""
|
|
168
|
+
if self.is_remote:
|
|
169
|
+
# Remote mode: async client for vLLM server
|
|
170
|
+
try:
|
|
171
|
+
req: Dict[str, Any] = {
|
|
172
|
+
"input": text,
|
|
173
|
+
"model": self.id,
|
|
174
|
+
}
|
|
175
|
+
if self.request_params:
|
|
176
|
+
req.update(self.request_params)
|
|
177
|
+
response: "CreateEmbeddingResponse" = await self._get_async_remote_client().embeddings.create(**req)
|
|
178
|
+
return response.data[0].embedding
|
|
179
|
+
except Exception as e:
|
|
180
|
+
logger.warning(f"Error in async remote embedding: {e}")
|
|
181
|
+
return []
|
|
182
|
+
else:
|
|
183
|
+
# Local mode: use thread executor for CPU-bound operations
|
|
184
|
+
loop = asyncio.get_event_loop()
|
|
185
|
+
return await loop.run_in_executor(None, self.get_embedding, text)
|
|
186
|
+
|
|
187
|
+
async def async_get_embedding_and_usage(self, text: str) -> Tuple[List[float], Optional[Dict]]:
|
|
188
|
+
"""Async version of get_embedding_and_usage using thread executor for local mode."""
|
|
189
|
+
if self.is_remote:
|
|
190
|
+
try:
|
|
191
|
+
req: Dict[str, Any] = {
|
|
192
|
+
"input": text,
|
|
193
|
+
"model": self.id,
|
|
194
|
+
}
|
|
195
|
+
if self.request_params:
|
|
196
|
+
req.update(self.request_params)
|
|
197
|
+
response: "CreateEmbeddingResponse" = await self._get_async_remote_client().embeddings.create(**req)
|
|
198
|
+
embedding = response.data[0].embedding
|
|
199
|
+
usage = response.usage
|
|
200
|
+
return embedding, usage.model_dump() if usage else None
|
|
201
|
+
except Exception as e:
|
|
202
|
+
logger.warning(f"Error in async remote embedding: {e}")
|
|
203
|
+
return [], None
|
|
204
|
+
else:
|
|
205
|
+
# Local mode: use thread executor for CPU-bound operations
|
|
206
|
+
try:
|
|
207
|
+
loop = asyncio.get_event_loop()
|
|
208
|
+
return await loop.run_in_executor(None, self.get_embedding_and_usage, text)
|
|
209
|
+
except Exception as e:
|
|
210
|
+
logger.warning(f"Error in async local embedding: {e}")
|
|
211
|
+
return [], None
|
|
212
|
+
|
|
213
|
+
async def async_get_embeddings_batch_and_usage(
|
|
214
|
+
self, texts: List[str]
|
|
215
|
+
) -> Tuple[List[List[float]], List[Optional[Dict]]]:
|
|
216
|
+
"""
|
|
217
|
+
Get embeddings and usage for multiple texts in batches (async version).
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
texts: List of text strings to embed
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
Tuple of (List of embedding vectors, List of usage dictionaries)
|
|
224
|
+
"""
|
|
225
|
+
all_embeddings = []
|
|
226
|
+
all_usage = []
|
|
227
|
+
logger.info(f"Getting embeddings for {len(texts)} texts in batches of {self.batch_size} (async)")
|
|
228
|
+
|
|
229
|
+
for i in range(0, len(texts), self.batch_size):
|
|
230
|
+
batch_texts = texts[i : i + self.batch_size]
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
if self.is_remote:
|
|
234
|
+
# Remote mode: use batch API
|
|
235
|
+
req: Dict[str, Any] = {
|
|
236
|
+
"input": batch_texts,
|
|
237
|
+
"model": self.id,
|
|
238
|
+
}
|
|
239
|
+
if self.request_params:
|
|
240
|
+
req.update(self.request_params)
|
|
241
|
+
response: "CreateEmbeddingResponse" = await self._get_async_remote_client().embeddings.create(**req)
|
|
242
|
+
batch_embeddings = [data.embedding for data in response.data]
|
|
243
|
+
all_embeddings.extend(batch_embeddings)
|
|
244
|
+
|
|
245
|
+
# For each embedding in the batch, add the same usage information
|
|
246
|
+
usage_dict = response.usage.model_dump() if response.usage else None
|
|
247
|
+
all_usage.extend([usage_dict] * len(batch_embeddings))
|
|
248
|
+
else:
|
|
249
|
+
# Local mode: process individually using thread executor
|
|
250
|
+
for text in batch_texts:
|
|
251
|
+
embedding, usage = await self.async_get_embedding_and_usage(text)
|
|
252
|
+
all_embeddings.append(embedding)
|
|
253
|
+
all_usage.append(usage)
|
|
254
|
+
|
|
255
|
+
except Exception as e:
|
|
256
|
+
logger.warning(f"Error in async batch embedding: {e}")
|
|
257
|
+
# Fallback: add empty results for failed batch
|
|
258
|
+
for _ in batch_texts:
|
|
259
|
+
all_embeddings.append([])
|
|
260
|
+
all_usage.append(None)
|
|
261
|
+
|
|
262
|
+
return all_embeddings, all_usage
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
3
|
+
|
|
4
|
+
from agno.knowledge.embedder.base import Embedder
|
|
5
|
+
from agno.utils.log import logger
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from voyageai import AsyncClient as AsyncVoyageClient
|
|
9
|
+
from voyageai import Client as VoyageClient
|
|
10
|
+
from voyageai.object import EmbeddingsObject
|
|
11
|
+
except ImportError:
|
|
12
|
+
raise ImportError("`voyageai` not installed. Please install using `pip install voyageai`")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class VoyageAIEmbedder(Embedder):
|
|
17
|
+
id: str = "voyage-2"
|
|
18
|
+
dimensions: int = 1024
|
|
19
|
+
request_params: Optional[Dict[str, Any]] = None
|
|
20
|
+
api_key: Optional[str] = None
|
|
21
|
+
base_url: str = "https://api.voyageai.com/v1/embeddings"
|
|
22
|
+
max_retries: Optional[int] = None
|
|
23
|
+
timeout: Optional[float] = None
|
|
24
|
+
client_params: Optional[Dict[str, Any]] = None
|
|
25
|
+
voyage_client: Optional[VoyageClient] = None
|
|
26
|
+
async_client: Optional[AsyncVoyageClient] = None
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def client(self) -> VoyageClient:
|
|
30
|
+
if self.voyage_client:
|
|
31
|
+
return self.voyage_client
|
|
32
|
+
|
|
33
|
+
_client_params: Dict[str, Any] = {}
|
|
34
|
+
if self.api_key is not None:
|
|
35
|
+
_client_params["api_key"] = self.api_key
|
|
36
|
+
if self.max_retries is not None:
|
|
37
|
+
_client_params["max_retries"] = self.max_retries
|
|
38
|
+
if self.timeout is not None:
|
|
39
|
+
_client_params["timeout"] = self.timeout
|
|
40
|
+
if self.client_params:
|
|
41
|
+
_client_params.update(self.client_params)
|
|
42
|
+
self.voyage_client = VoyageClient(**_client_params)
|
|
43
|
+
return self.voyage_client
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def aclient(self) -> AsyncVoyageClient:
|
|
47
|
+
if self.async_client:
|
|
48
|
+
return self.async_client
|
|
49
|
+
|
|
50
|
+
_client_params: Dict[str, Any] = {}
|
|
51
|
+
if self.api_key is not None:
|
|
52
|
+
_client_params["api_key"] = self.api_key
|
|
53
|
+
if self.max_retries is not None:
|
|
54
|
+
_client_params["max_retries"] = self.max_retries
|
|
55
|
+
if self.timeout is not None:
|
|
56
|
+
_client_params["timeout"] = self.timeout
|
|
57
|
+
if self.client_params:
|
|
58
|
+
_client_params.update(self.client_params)
|
|
59
|
+
self.async_client = AsyncVoyageClient(**_client_params)
|
|
60
|
+
return self.async_client
|
|
61
|
+
|
|
62
|
+
def _response(self, text: str) -> EmbeddingsObject:
|
|
63
|
+
_request_params: Dict[str, Any] = {
|
|
64
|
+
"texts": [text],
|
|
65
|
+
"model": self.id,
|
|
66
|
+
}
|
|
67
|
+
if self.request_params:
|
|
68
|
+
_request_params.update(self.request_params)
|
|
69
|
+
return self.client.embed(**_request_params)
|
|
70
|
+
|
|
71
|
+
def get_embedding(self, text: str) -> List[float]:
|
|
72
|
+
response: EmbeddingsObject = self._response(text=text)
|
|
73
|
+
try:
|
|
74
|
+
embedding = response.embeddings[0]
|
|
75
|
+
return [float(x) for x in embedding] # Ensure all values are float
|
|
76
|
+
except Exception as e:
|
|
77
|
+
logger.warning(e)
|
|
78
|
+
return []
|
|
79
|
+
|
|
80
|
+
def get_embedding_and_usage(self, text: str) -> Tuple[List[float], Optional[Dict]]:
|
|
81
|
+
response: EmbeddingsObject = self._response(text=text)
|
|
82
|
+
|
|
83
|
+
embedding = response.embeddings[0]
|
|
84
|
+
usage = {"total_tokens": response.total_tokens}
|
|
85
|
+
return [float(x) for x in embedding], usage
|
|
86
|
+
|
|
87
|
+
async def _async_response(self, text: str) -> EmbeddingsObject:
|
|
88
|
+
"""Async version of _response using AsyncVoyageClient."""
|
|
89
|
+
_request_params: Dict[str, Any] = {
|
|
90
|
+
"texts": [text],
|
|
91
|
+
"model": self.id,
|
|
92
|
+
}
|
|
93
|
+
if self.request_params:
|
|
94
|
+
_request_params.update(self.request_params)
|
|
95
|
+
return await self.aclient.embed(**_request_params)
|
|
96
|
+
|
|
97
|
+
async def async_get_embedding(self, text: str) -> List[float]:
|
|
98
|
+
"""Async version of get_embedding."""
|
|
99
|
+
try:
|
|
100
|
+
response: EmbeddingsObject = await self._async_response(text=text)
|
|
101
|
+
embedding = response.embeddings[0]
|
|
102
|
+
return [float(x) for x in embedding] # Ensure all values are float
|
|
103
|
+
except Exception as e:
|
|
104
|
+
logger.warning(f"Error getting embedding: {e}")
|
|
105
|
+
return []
|
|
106
|
+
|
|
107
|
+
async def async_get_embedding_and_usage(self, text: str) -> Tuple[List[float], Optional[Dict]]:
|
|
108
|
+
"""Async version of get_embedding_and_usage."""
|
|
109
|
+
try:
|
|
110
|
+
response: EmbeddingsObject = await self._async_response(text=text)
|
|
111
|
+
embedding = response.embeddings[0]
|
|
112
|
+
usage = {"total_tokens": response.total_tokens}
|
|
113
|
+
return [float(x) for x in embedding], usage
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logger.warning(f"Error getting embedding and usage: {e}")
|
|
116
|
+
return [], None
|
|
117
|
+
|
|
118
|
+
async def async_get_embeddings_batch_and_usage(
|
|
119
|
+
self, texts: List[str]
|
|
120
|
+
) -> Tuple[List[List[float]], List[Optional[Dict]]]:
|
|
121
|
+
"""
|
|
122
|
+
Get embeddings and usage for multiple texts in batches.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
texts: List of text strings to embed
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Tuple of (List of embedding vectors, List of usage dictionaries)
|
|
129
|
+
"""
|
|
130
|
+
all_embeddings: List[List[float]] = []
|
|
131
|
+
all_usage: List[Optional[Dict]] = []
|
|
132
|
+
logger.info(f"Getting embeddings and usage for {len(texts)} texts in batches of {self.batch_size}")
|
|
133
|
+
|
|
134
|
+
for i in range(0, len(texts), self.batch_size):
|
|
135
|
+
batch_texts = texts[i : i + self.batch_size]
|
|
136
|
+
|
|
137
|
+
req: Dict[str, Any] = {
|
|
138
|
+
"texts": batch_texts,
|
|
139
|
+
"model": self.id,
|
|
140
|
+
}
|
|
141
|
+
if self.request_params:
|
|
142
|
+
req.update(self.request_params)
|
|
143
|
+
|
|
144
|
+
try:
|
|
145
|
+
response: EmbeddingsObject = await self.aclient.embed(**req)
|
|
146
|
+
batch_embeddings = [[float(x) for x in emb] for emb in response.embeddings]
|
|
147
|
+
all_embeddings.extend(batch_embeddings)
|
|
148
|
+
|
|
149
|
+
# For each embedding in the batch, add the same usage information
|
|
150
|
+
usage_dict = {"total_tokens": response.total_tokens}
|
|
151
|
+
all_usage.extend([usage_dict] * len(batch_embeddings))
|
|
152
|
+
except Exception as e:
|
|
153
|
+
logger.warning(f"Error in async batch embedding: {e}")
|
|
154
|
+
# Fallback to individual calls for this batch
|
|
155
|
+
for text in batch_texts:
|
|
156
|
+
try:
|
|
157
|
+
embedding, usage = await self.async_get_embedding_and_usage(text)
|
|
158
|
+
all_embeddings.append(embedding)
|
|
159
|
+
all_usage.append(usage)
|
|
160
|
+
except Exception as e2:
|
|
161
|
+
logger.warning(f"Error in individual async embedding fallback: {e2}")
|
|
162
|
+
all_embeddings.append([])
|
|
163
|
+
all_usage.append(None)
|
|
164
|
+
|
|
165
|
+
return all_embeddings, all_usage
|