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,503 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from os import getenv
|
|
4
|
+
from typing import Any, AsyncIterator, Dict, Iterator, List, Optional, Type, Union
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
from agno.models.base import Model
|
|
9
|
+
from agno.models.message import Message
|
|
10
|
+
from agno.models.metrics import Metrics
|
|
11
|
+
from agno.models.response import ModelResponse
|
|
12
|
+
from agno.run.agent import RunOutput
|
|
13
|
+
from agno.tools.function import Function
|
|
14
|
+
from agno.utils.log import log_debug, log_error, log_warning
|
|
15
|
+
from agno.utils.openai import _format_file_for_message, audio_to_message, images_to_message
|
|
16
|
+
from agno.utils.tokens import count_schema_tokens
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
import litellm
|
|
20
|
+
from litellm import validate_environment
|
|
21
|
+
except ImportError:
|
|
22
|
+
raise ImportError("`litellm` not installed. Please install it via `pip install litellm`")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class LiteLLM(Model):
|
|
27
|
+
"""
|
|
28
|
+
A class for interacting with LiteLLM Python SDK.
|
|
29
|
+
|
|
30
|
+
LiteLLM allows you to use a unified interface for various LLM providers.
|
|
31
|
+
For more information, see: https://docs.litellm.ai/docs/
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
id: str = "gpt-4o"
|
|
35
|
+
name: str = "LiteLLM"
|
|
36
|
+
provider: str = "LiteLLM"
|
|
37
|
+
|
|
38
|
+
api_key: Optional[str] = None
|
|
39
|
+
api_base: Optional[str] = None
|
|
40
|
+
max_tokens: Optional[int] = None
|
|
41
|
+
temperature: float = 0.7
|
|
42
|
+
top_p: float = 1.0
|
|
43
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
44
|
+
extra_headers: Optional[Dict[str, Any]] = None
|
|
45
|
+
extra_query: Optional[Dict[str, Any]] = None
|
|
46
|
+
extra_body: Optional[Dict[str, Any]] = None
|
|
47
|
+
request_params: Optional[Dict[str, Any]] = None
|
|
48
|
+
|
|
49
|
+
client: Optional[Any] = None
|
|
50
|
+
|
|
51
|
+
def __post_init__(self):
|
|
52
|
+
"""Initialize the model after the dataclass initialization."""
|
|
53
|
+
super().__post_init__()
|
|
54
|
+
|
|
55
|
+
# Set up API key from environment variable if not already set
|
|
56
|
+
if not self.client and not self.api_key:
|
|
57
|
+
self.api_key = getenv("LITELLM_API_KEY")
|
|
58
|
+
if not self.api_key:
|
|
59
|
+
# Check for other present valid keys, e.g. OPENAI_API_KEY if self.id is an OpenAI model
|
|
60
|
+
env_validation = validate_environment(model=self.id, api_base=self.api_base)
|
|
61
|
+
if not env_validation.get("keys_in_environment"):
|
|
62
|
+
log_error(
|
|
63
|
+
"LITELLM_API_KEY not set. Please set the LITELLM_API_KEY or other valid environment variables."
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def get_client(self) -> Any:
|
|
67
|
+
"""
|
|
68
|
+
Returns a LiteLLM client.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Any: An instance of the LiteLLM client.
|
|
72
|
+
"""
|
|
73
|
+
if self.client is not None:
|
|
74
|
+
return self.client
|
|
75
|
+
|
|
76
|
+
self.client = litellm
|
|
77
|
+
return self.client
|
|
78
|
+
|
|
79
|
+
def _format_messages(self, messages: List[Message], compress_tool_results: bool = False) -> List[Dict[str, Any]]:
|
|
80
|
+
"""Format messages for LiteLLM API."""
|
|
81
|
+
formatted_messages = []
|
|
82
|
+
for m in messages:
|
|
83
|
+
# Use compressed content for tool messages if compression is active
|
|
84
|
+
if m.role == "tool":
|
|
85
|
+
content = m.get_content(use_compressed_content=compress_tool_results)
|
|
86
|
+
else:
|
|
87
|
+
content = m.content if m.content is not None else ""
|
|
88
|
+
|
|
89
|
+
msg = {"role": m.role, "content": content}
|
|
90
|
+
|
|
91
|
+
# Handle media
|
|
92
|
+
if (m.images is not None and len(m.images) > 0) or (m.audio is not None and len(m.audio) > 0):
|
|
93
|
+
if isinstance(m.content, str):
|
|
94
|
+
content_list = [{"type": "text", "text": m.content}]
|
|
95
|
+
if m.images is not None:
|
|
96
|
+
content_list.extend(images_to_message(images=m.images))
|
|
97
|
+
if m.audio is not None:
|
|
98
|
+
content_list.extend(audio_to_message(audio=m.audio))
|
|
99
|
+
msg["content"] = content_list
|
|
100
|
+
|
|
101
|
+
if m.videos is not None and len(m.videos) > 0:
|
|
102
|
+
log_warning("Video input is currently unsupported by LLM providers.")
|
|
103
|
+
|
|
104
|
+
# Handle files
|
|
105
|
+
if m.files is not None:
|
|
106
|
+
if isinstance(msg["content"], str):
|
|
107
|
+
content_list = [{"type": "text", "text": msg["content"]}]
|
|
108
|
+
else:
|
|
109
|
+
content_list = msg["content"] if isinstance(msg["content"], list) else []
|
|
110
|
+
for file in m.files:
|
|
111
|
+
file_part = _format_file_for_message(file)
|
|
112
|
+
if file_part:
|
|
113
|
+
content_list.append(file_part)
|
|
114
|
+
msg["content"] = content_list
|
|
115
|
+
|
|
116
|
+
# Handle tool calls in assistant messages
|
|
117
|
+
if m.role == "assistant" and m.tool_calls:
|
|
118
|
+
msg["tool_calls"] = [
|
|
119
|
+
{
|
|
120
|
+
"id": tc.get("id", f"call_{i}"),
|
|
121
|
+
"type": "function",
|
|
122
|
+
"function": {"name": tc["function"]["name"], "arguments": tc["function"]["arguments"]},
|
|
123
|
+
}
|
|
124
|
+
for i, tc in enumerate(m.tool_calls)
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
# Handle tool responses
|
|
128
|
+
if m.role == "tool":
|
|
129
|
+
msg["tool_call_id"] = m.tool_call_id or ""
|
|
130
|
+
msg["name"] = m.name or ""
|
|
131
|
+
|
|
132
|
+
if m.audio is not None and len(m.audio) > 0:
|
|
133
|
+
log_warning("Audio input is currently unsupported.")
|
|
134
|
+
|
|
135
|
+
if m.images is not None and len(m.images) > 0:
|
|
136
|
+
log_warning("Image input is currently unsupported.")
|
|
137
|
+
|
|
138
|
+
if m.videos is not None and len(m.videos) > 0:
|
|
139
|
+
log_warning("Video input is currently unsupported.")
|
|
140
|
+
formatted_messages.append(msg)
|
|
141
|
+
|
|
142
|
+
return formatted_messages
|
|
143
|
+
|
|
144
|
+
def get_request_params(self, tools: Optional[List[Dict[str, Any]]] = None) -> Dict[str, Any]:
|
|
145
|
+
"""
|
|
146
|
+
Returns keyword arguments for API requests.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Dict[str, Any]: The API kwargs for the model.
|
|
150
|
+
"""
|
|
151
|
+
base_params: Dict[str, Any] = {
|
|
152
|
+
"model": self.id,
|
|
153
|
+
"temperature": self.temperature,
|
|
154
|
+
"top_p": self.top_p,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if self.max_tokens:
|
|
158
|
+
base_params["max_tokens"] = self.max_tokens
|
|
159
|
+
if self.api_key:
|
|
160
|
+
base_params["api_key"] = self.api_key
|
|
161
|
+
if self.api_base:
|
|
162
|
+
base_params["api_base"] = self.api_base
|
|
163
|
+
if self.extra_headers:
|
|
164
|
+
base_params["extra_headers"] = self.extra_headers
|
|
165
|
+
if self.extra_query:
|
|
166
|
+
base_params["extra_query"] = self.extra_query
|
|
167
|
+
if tools:
|
|
168
|
+
base_params["tools"] = tools
|
|
169
|
+
base_params["tool_choice"] = "auto"
|
|
170
|
+
|
|
171
|
+
# Handle metadata via extra_body as per LiteLLM docs
|
|
172
|
+
if self.metadata:
|
|
173
|
+
if self.extra_body:
|
|
174
|
+
base_params["extra_body"] = {**self.extra_body, "metadata": self.metadata}
|
|
175
|
+
else:
|
|
176
|
+
base_params["extra_body"] = {"metadata": self.metadata}
|
|
177
|
+
elif self.extra_body:
|
|
178
|
+
base_params["extra_body"] = self.extra_body
|
|
179
|
+
|
|
180
|
+
# Add additional request params if provided
|
|
181
|
+
request_params: Dict[str, Any] = {k: v for k, v in base_params.items() if v is not None}
|
|
182
|
+
if self.request_params:
|
|
183
|
+
request_params.update(self.request_params)
|
|
184
|
+
|
|
185
|
+
if request_params:
|
|
186
|
+
log_debug(f"Calling {self.provider} with request parameters: {request_params}", log_level=2)
|
|
187
|
+
return request_params
|
|
188
|
+
|
|
189
|
+
def invoke(
|
|
190
|
+
self,
|
|
191
|
+
messages: List[Message],
|
|
192
|
+
assistant_message: Message,
|
|
193
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
194
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
195
|
+
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
196
|
+
run_response: Optional[RunOutput] = None,
|
|
197
|
+
compress_tool_results: bool = False,
|
|
198
|
+
) -> ModelResponse:
|
|
199
|
+
"""Sends a chat completion request to the LiteLLM API."""
|
|
200
|
+
completion_kwargs = self.get_request_params(tools=tools)
|
|
201
|
+
completion_kwargs["messages"] = self._format_messages(messages, compress_tool_results)
|
|
202
|
+
|
|
203
|
+
if run_response and run_response.metrics:
|
|
204
|
+
run_response.metrics.set_time_to_first_token()
|
|
205
|
+
|
|
206
|
+
assistant_message.metrics.start_timer()
|
|
207
|
+
|
|
208
|
+
provider_response = self.get_client().completion(**completion_kwargs)
|
|
209
|
+
|
|
210
|
+
assistant_message.metrics.stop_timer()
|
|
211
|
+
|
|
212
|
+
model_response = self._parse_provider_response(provider_response, response_format=response_format)
|
|
213
|
+
return model_response
|
|
214
|
+
|
|
215
|
+
def invoke_stream(
|
|
216
|
+
self,
|
|
217
|
+
messages: List[Message],
|
|
218
|
+
assistant_message: Message,
|
|
219
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
220
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
221
|
+
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
222
|
+
run_response: Optional[RunOutput] = None,
|
|
223
|
+
compress_tool_results: bool = False,
|
|
224
|
+
) -> Iterator[ModelResponse]:
|
|
225
|
+
"""Sends a streaming chat completion request to the LiteLLM API."""
|
|
226
|
+
completion_kwargs = self.get_request_params(tools=tools)
|
|
227
|
+
completion_kwargs["messages"] = self._format_messages(messages, compress_tool_results)
|
|
228
|
+
completion_kwargs["stream"] = True
|
|
229
|
+
completion_kwargs["stream_options"] = {"include_usage": True}
|
|
230
|
+
|
|
231
|
+
if run_response and run_response.metrics:
|
|
232
|
+
run_response.metrics.set_time_to_first_token()
|
|
233
|
+
|
|
234
|
+
assistant_message.metrics.start_timer()
|
|
235
|
+
|
|
236
|
+
for chunk in self.get_client().completion(**completion_kwargs):
|
|
237
|
+
yield self._parse_provider_response_delta(chunk)
|
|
238
|
+
|
|
239
|
+
assistant_message.metrics.stop_timer()
|
|
240
|
+
|
|
241
|
+
async def ainvoke(
|
|
242
|
+
self,
|
|
243
|
+
messages: List[Message],
|
|
244
|
+
assistant_message: Message,
|
|
245
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
246
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
247
|
+
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
248
|
+
run_response: Optional[RunOutput] = None,
|
|
249
|
+
compress_tool_results: bool = False,
|
|
250
|
+
) -> ModelResponse:
|
|
251
|
+
"""Sends an asynchronous chat completion request to the LiteLLM API."""
|
|
252
|
+
completion_kwargs = self.get_request_params(tools=tools)
|
|
253
|
+
completion_kwargs["messages"] = self._format_messages(messages, compress_tool_results)
|
|
254
|
+
|
|
255
|
+
if run_response and run_response.metrics:
|
|
256
|
+
run_response.metrics.set_time_to_first_token()
|
|
257
|
+
|
|
258
|
+
assistant_message.metrics.start_timer()
|
|
259
|
+
|
|
260
|
+
provider_response = await self.get_client().acompletion(**completion_kwargs)
|
|
261
|
+
|
|
262
|
+
assistant_message.metrics.stop_timer()
|
|
263
|
+
|
|
264
|
+
model_response = self._parse_provider_response(provider_response, response_format=response_format)
|
|
265
|
+
return model_response
|
|
266
|
+
|
|
267
|
+
async def ainvoke_stream(
|
|
268
|
+
self,
|
|
269
|
+
messages: List[Message],
|
|
270
|
+
assistant_message: Message,
|
|
271
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
272
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
273
|
+
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
274
|
+
run_response: Optional[RunOutput] = None,
|
|
275
|
+
compress_tool_results: bool = False,
|
|
276
|
+
) -> AsyncIterator[ModelResponse]:
|
|
277
|
+
"""Sends an asynchronous streaming chat request to the LiteLLM API."""
|
|
278
|
+
completion_kwargs = self.get_request_params(tools=tools)
|
|
279
|
+
completion_kwargs["messages"] = self._format_messages(messages, compress_tool_results)
|
|
280
|
+
completion_kwargs["stream"] = True
|
|
281
|
+
completion_kwargs["stream_options"] = {"include_usage": True}
|
|
282
|
+
|
|
283
|
+
if run_response and run_response.metrics:
|
|
284
|
+
run_response.metrics.set_time_to_first_token()
|
|
285
|
+
|
|
286
|
+
assistant_message.metrics.start_timer()
|
|
287
|
+
|
|
288
|
+
try:
|
|
289
|
+
# litellm.acompletion returns a coroutine that resolves to an async iterator
|
|
290
|
+
# We need to await it first to get the actual async iterator
|
|
291
|
+
async_stream = await self.get_client().acompletion(**completion_kwargs)
|
|
292
|
+
async for chunk in async_stream:
|
|
293
|
+
yield self._parse_provider_response_delta(chunk)
|
|
294
|
+
|
|
295
|
+
assistant_message.metrics.stop_timer()
|
|
296
|
+
|
|
297
|
+
except Exception as e:
|
|
298
|
+
log_error(f"Error in streaming response: {e}")
|
|
299
|
+
raise
|
|
300
|
+
|
|
301
|
+
def _parse_provider_response(self, response: Any, **kwargs) -> ModelResponse:
|
|
302
|
+
"""Parse the provider response."""
|
|
303
|
+
model_response = ModelResponse()
|
|
304
|
+
|
|
305
|
+
response_message = response.choices[0].message
|
|
306
|
+
|
|
307
|
+
if response_message.content is not None:
|
|
308
|
+
model_response.content = response_message.content
|
|
309
|
+
|
|
310
|
+
if hasattr(response_message, "tool_calls") and response_message.tool_calls:
|
|
311
|
+
model_response.tool_calls = []
|
|
312
|
+
for tool_call in response_message.tool_calls:
|
|
313
|
+
model_response.tool_calls.append(
|
|
314
|
+
{
|
|
315
|
+
"id": tool_call.id,
|
|
316
|
+
"type": "function",
|
|
317
|
+
"function": {"name": tool_call.function.name, "arguments": tool_call.function.arguments},
|
|
318
|
+
}
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
if response.usage is not None:
|
|
322
|
+
model_response.response_usage = self._get_metrics(response.usage)
|
|
323
|
+
|
|
324
|
+
return model_response
|
|
325
|
+
|
|
326
|
+
def _parse_provider_response_delta(self, response_delta: Any) -> ModelResponse:
|
|
327
|
+
"""Parse the provider response delta for streaming responses."""
|
|
328
|
+
model_response = ModelResponse()
|
|
329
|
+
|
|
330
|
+
if hasattr(response_delta, "choices") and len(response_delta.choices) > 0:
|
|
331
|
+
choice_delta = response_delta.choices[0].delta
|
|
332
|
+
|
|
333
|
+
if choice_delta:
|
|
334
|
+
if hasattr(choice_delta, "content") and choice_delta.content is not None:
|
|
335
|
+
model_response.content = choice_delta.content
|
|
336
|
+
|
|
337
|
+
if hasattr(choice_delta, "tool_calls") and choice_delta.tool_calls:
|
|
338
|
+
processed_tool_calls = []
|
|
339
|
+
for tool_call in choice_delta.tool_calls:
|
|
340
|
+
# Get the actual index from the tool call, defaulting to 0 if not available
|
|
341
|
+
actual_index = getattr(tool_call, "index", 0) if hasattr(tool_call, "index") else 0
|
|
342
|
+
|
|
343
|
+
# Create a basic structure with the correct index
|
|
344
|
+
tool_call_dict = {"index": actual_index, "type": "function"}
|
|
345
|
+
|
|
346
|
+
# Extract ID if available
|
|
347
|
+
if hasattr(tool_call, "id") and tool_call.id is not None:
|
|
348
|
+
tool_call_dict["id"] = tool_call.id
|
|
349
|
+
|
|
350
|
+
# Extract function data
|
|
351
|
+
function_data = {}
|
|
352
|
+
if hasattr(tool_call, "function"):
|
|
353
|
+
if hasattr(tool_call.function, "name") and tool_call.function.name is not None:
|
|
354
|
+
function_data["name"] = tool_call.function.name
|
|
355
|
+
if hasattr(tool_call.function, "arguments") and tool_call.function.arguments is not None:
|
|
356
|
+
function_data["arguments"] = tool_call.function.arguments
|
|
357
|
+
|
|
358
|
+
tool_call_dict["function"] = function_data
|
|
359
|
+
processed_tool_calls.append(tool_call_dict)
|
|
360
|
+
|
|
361
|
+
model_response.tool_calls = processed_tool_calls
|
|
362
|
+
|
|
363
|
+
# Add usage metrics if present in streaming response
|
|
364
|
+
if hasattr(response_delta, "usage") and response_delta.usage is not None:
|
|
365
|
+
model_response.response_usage = self._get_metrics(response_delta.usage)
|
|
366
|
+
|
|
367
|
+
return model_response
|
|
368
|
+
|
|
369
|
+
@staticmethod
|
|
370
|
+
def parse_tool_calls(tool_calls_data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
371
|
+
"""
|
|
372
|
+
Build tool calls from streamed tool call data.
|
|
373
|
+
|
|
374
|
+
Args:
|
|
375
|
+
tool_calls_data (List[Dict[str, Any]]): The tool call data to build from.
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
List[Dict[str, Any]]: The built tool calls.
|
|
379
|
+
"""
|
|
380
|
+
# Early return for empty list
|
|
381
|
+
if not tool_calls_data:
|
|
382
|
+
return []
|
|
383
|
+
|
|
384
|
+
# Group tool calls by index
|
|
385
|
+
tool_calls_by_index: Dict[int, Dict[str, Any]] = {}
|
|
386
|
+
|
|
387
|
+
for tc in tool_calls_data:
|
|
388
|
+
# Get index (default to 0)
|
|
389
|
+
index = tc.get("index", 0)
|
|
390
|
+
if not isinstance(index, int):
|
|
391
|
+
index = 0
|
|
392
|
+
|
|
393
|
+
# Initialize if first time seeing this index
|
|
394
|
+
if index not in tool_calls_by_index:
|
|
395
|
+
tool_calls_by_index[index] = {"id": None, "type": "function", "function": {"name": "", "arguments": ""}}
|
|
396
|
+
|
|
397
|
+
# Update with new information
|
|
398
|
+
if tc.get("id") is not None:
|
|
399
|
+
tool_calls_by_index[index]["id"] = tc["id"]
|
|
400
|
+
|
|
401
|
+
if tc.get("type") is not None:
|
|
402
|
+
tool_calls_by_index[index]["type"] = tc["type"]
|
|
403
|
+
|
|
404
|
+
# Update function information
|
|
405
|
+
function_data = tc.get("function", {})
|
|
406
|
+
if not isinstance(function_data, dict):
|
|
407
|
+
function_data = {}
|
|
408
|
+
|
|
409
|
+
# Update function name if provided
|
|
410
|
+
if function_data.get("name") is not None:
|
|
411
|
+
name = function_data.get("name", "")
|
|
412
|
+
if isinstance(tool_calls_by_index[index]["function"], dict):
|
|
413
|
+
# type: ignore
|
|
414
|
+
tool_calls_by_index[index]["function"]["name"] = name
|
|
415
|
+
|
|
416
|
+
# Update function arguments if provided
|
|
417
|
+
if function_data.get("arguments") is not None:
|
|
418
|
+
args = function_data.get("arguments", "")
|
|
419
|
+
if isinstance(tool_calls_by_index[index]["function"], dict):
|
|
420
|
+
current_args = tool_calls_by_index[index]["function"].get("arguments", "") # type: ignore
|
|
421
|
+
if isinstance(current_args, str) and isinstance(args, str):
|
|
422
|
+
# type: ignore
|
|
423
|
+
tool_calls_by_index[index]["function"]["arguments"] = current_args + args
|
|
424
|
+
|
|
425
|
+
# Process arguments - Ensure they're valid JSON for the Message.log() method
|
|
426
|
+
result = []
|
|
427
|
+
for tc in tool_calls_by_index.values():
|
|
428
|
+
# Make a safe copy to avoid modifying the original
|
|
429
|
+
tc_copy = {
|
|
430
|
+
"id": tc.get("id"),
|
|
431
|
+
"type": tc.get("type", "function"),
|
|
432
|
+
"function": {"name": "", "arguments": ""},
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
# Safely copy function data
|
|
436
|
+
if isinstance(tc.get("function"), dict):
|
|
437
|
+
func_dict = tc.get("function", {})
|
|
438
|
+
tc_copy["function"]["name"] = func_dict.get("name", "")
|
|
439
|
+
|
|
440
|
+
# Process arguments
|
|
441
|
+
args = func_dict.get("arguments", "")
|
|
442
|
+
if args and isinstance(args, str):
|
|
443
|
+
try:
|
|
444
|
+
# Check if arguments are already valid JSON
|
|
445
|
+
parsed = json.loads(args)
|
|
446
|
+
# If it's not a dict, convert to a JSON string of a dict
|
|
447
|
+
if not isinstance(parsed, dict):
|
|
448
|
+
tc_copy["function"]["arguments"] = json.dumps({"value": parsed})
|
|
449
|
+
else:
|
|
450
|
+
tc_copy["function"]["arguments"] = args
|
|
451
|
+
except json.JSONDecodeError:
|
|
452
|
+
# If not valid JSON, make it a JSON dict
|
|
453
|
+
tc_copy["function"]["arguments"] = json.dumps({"text": args})
|
|
454
|
+
|
|
455
|
+
result.append(tc_copy)
|
|
456
|
+
|
|
457
|
+
return result
|
|
458
|
+
|
|
459
|
+
def _get_metrics(self, response_usage: Any) -> Metrics:
|
|
460
|
+
"""
|
|
461
|
+
Parse the given LiteLLM usage into an Agno Metrics object.
|
|
462
|
+
|
|
463
|
+
Args:
|
|
464
|
+
response_usage: Usage data from LiteLLM
|
|
465
|
+
|
|
466
|
+
Returns:
|
|
467
|
+
Metrics: Parsed metrics data
|
|
468
|
+
"""
|
|
469
|
+
metrics = Metrics()
|
|
470
|
+
|
|
471
|
+
if isinstance(response_usage, dict):
|
|
472
|
+
metrics.input_tokens = response_usage.get("prompt_tokens") or 0
|
|
473
|
+
metrics.output_tokens = response_usage.get("completion_tokens") or 0
|
|
474
|
+
else:
|
|
475
|
+
metrics.input_tokens = response_usage.prompt_tokens or 0
|
|
476
|
+
metrics.output_tokens = response_usage.completion_tokens or 0
|
|
477
|
+
|
|
478
|
+
metrics.total_tokens = metrics.input_tokens + metrics.output_tokens
|
|
479
|
+
|
|
480
|
+
return metrics
|
|
481
|
+
|
|
482
|
+
def count_tokens(
|
|
483
|
+
self,
|
|
484
|
+
messages: List[Message],
|
|
485
|
+
tools: Optional[List[Union[Function, Dict[str, Any]]]] = None,
|
|
486
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
487
|
+
) -> int:
|
|
488
|
+
formatted_messages = self._format_messages(messages, compress_tool_results=True)
|
|
489
|
+
formatted_tools = self._format_tools(tools) if tools else None
|
|
490
|
+
tokens = litellm.token_counter(
|
|
491
|
+
model=self.id,
|
|
492
|
+
messages=formatted_messages,
|
|
493
|
+
tools=formatted_tools, # type: ignore
|
|
494
|
+
)
|
|
495
|
+
return tokens + count_schema_tokens(response_format, self.id)
|
|
496
|
+
|
|
497
|
+
async def acount_tokens(
|
|
498
|
+
self,
|
|
499
|
+
messages: List[Message],
|
|
500
|
+
tools: Optional[List[Union[Function, Dict[str, Any]]]] = None,
|
|
501
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
502
|
+
) -> int:
|
|
503
|
+
return self.count_tokens(messages, tools, response_format)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from os import getenv
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
from agno.exceptions import ModelAuthenticationError
|
|
6
|
+
from agno.models.openai.like import OpenAILike
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class LiteLLMOpenAI(OpenAILike):
|
|
11
|
+
"""
|
|
12
|
+
A class for interacting with LiteLLM.
|
|
13
|
+
|
|
14
|
+
Attributes:
|
|
15
|
+
id (str): The id of the LiteLLM model. Default is "gpt-4o".
|
|
16
|
+
name (str): The name of this chat model instance. Default is "LiteLLM".
|
|
17
|
+
provider (str): The provider of the model. Default is "LiteLLM".
|
|
18
|
+
base_url (str): The base url to which the requests are sent.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
id: str = "gpt-4o"
|
|
22
|
+
name: str = "LiteLLM"
|
|
23
|
+
provider: str = "LiteLLM"
|
|
24
|
+
|
|
25
|
+
api_key: Optional[str] = field(default_factory=lambda: getenv("LITELLM_API_KEY"))
|
|
26
|
+
base_url: str = "http://0.0.0.0:4000"
|
|
27
|
+
|
|
28
|
+
def _get_client_params(self) -> Dict[str, Any]:
|
|
29
|
+
"""
|
|
30
|
+
Returns client parameters for API requests, checking for LITELLM_API_KEY.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Dict[str, Any]: A dictionary of client parameters for API requests.
|
|
34
|
+
"""
|
|
35
|
+
if not self.api_key:
|
|
36
|
+
self.api_key = getenv("LITELLM_API_KEY")
|
|
37
|
+
if not self.api_key:
|
|
38
|
+
raise ModelAuthenticationError(
|
|
39
|
+
message="LITELLM_API_KEY not set. Please set the LITELLM_API_KEY environment variable.",
|
|
40
|
+
model_name=self.name,
|
|
41
|
+
)
|
|
42
|
+
return super()._get_client_params()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from agno.models.openai.like import OpenAILike
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class LlamaCpp(OpenAILike):
|
|
8
|
+
"""
|
|
9
|
+
A class for interacting with LLMs using Llama CPP.
|
|
10
|
+
|
|
11
|
+
Attributes:
|
|
12
|
+
id (str): The id of the Llama CPP model. Default is "ggml-org/gpt-oss-20b-GGUF".
|
|
13
|
+
name (str): The name of this chat model instance. Default is "LlamaCpp".
|
|
14
|
+
provider (str): The provider of the model. Default is "LlamaCpp".
|
|
15
|
+
base_url (str): The base url to which the requests are sent.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
id: str = "ggml-org/gpt-oss-20b-GGUF"
|
|
19
|
+
name: str = "LlamaCpp"
|
|
20
|
+
provider: str = "LlamaCpp"
|
|
21
|
+
|
|
22
|
+
base_url: str = "http://127.0.0.1:8080/v1"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from agno.models.openai.like import OpenAILike
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class LMStudio(OpenAILike):
|
|
8
|
+
"""
|
|
9
|
+
A class for interacting with LM Studio.
|
|
10
|
+
|
|
11
|
+
Attributes:
|
|
12
|
+
id (str): The id of the LM Studio model. Default is "qwen2.5-7b-instruct-1m".
|
|
13
|
+
name (str): The name of this chat model instance. Default is "LMStudio".
|
|
14
|
+
provider (str): The provider of the model. Default is "LMStudio".
|
|
15
|
+
base_url (str): The base url to which the requests are sent.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
id: str = "qwen2.5-7b-instruct-1m"
|
|
19
|
+
name: str = "LMStudio"
|
|
20
|
+
provider: str = "LMStudio"
|
|
21
|
+
|
|
22
|
+
base_url: str = "http://127.0.0.1:1234/v1"
|
|
23
|
+
|
|
24
|
+
supports_native_structured_outputs: bool = False
|
|
25
|
+
supports_json_schema_outputs: bool = True
|