agno 0.1.2__py3-none-any.whl → 2.3.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/__init__.py +8 -0
- agno/agent/__init__.py +44 -5
- agno/agent/agent.py +10531 -2975
- agno/api/agent.py +14 -53
- agno/api/api.py +7 -46
- agno/api/evals.py +22 -0
- agno/api/os.py +17 -0
- agno/api/routes.py +6 -25
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +6 -9
- agno/api/schemas/evals.py +16 -0
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/team.py +10 -10
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +16 -0
- agno/api/settings.py +53 -0
- agno/api/team.py +22 -26
- agno/api/workflow.py +28 -0
- agno/cloud/aws/base.py +214 -0
- agno/cloud/aws/s3/__init__.py +2 -0
- agno/cloud/aws/s3/api_client.py +43 -0
- agno/cloud/aws/s3/bucket.py +195 -0
- agno/cloud/aws/s3/object.py +57 -0
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/__init__.py +24 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +946 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +2781 -0
- agno/db/dynamo/schemas.py +442 -0
- agno/db/dynamo/utils.py +743 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +2379 -0
- agno/db/firestore/schemas.py +181 -0
- agno/db/firestore/utils.py +376 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1791 -0
- agno/db/gcs_json/utils.py +228 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +1312 -0
- agno/db/in_memory/utils.py +230 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1777 -0
- agno/db/json/utils.py +230 -0
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +635 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +17 -0
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +2597 -0
- agno/db/mongo/schemas.py +119 -0
- agno/db/mongo/utils.py +276 -0
- agno/db/mysql/__init__.py +4 -0
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +2923 -0
- agno/db/mysql/schemas.py +186 -0
- agno/db/mysql/utils.py +488 -0
- agno/db/postgres/__init__.py +4 -0
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +2870 -0
- agno/db/postgres/schemas.py +187 -0
- agno/db/postgres/utils.py +442 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +2141 -0
- agno/db/redis/schemas.py +159 -0
- agno/db/redis/utils.py +346 -0
- agno/db/schemas/__init__.py +4 -0
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +34 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +61 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +179 -0
- agno/db/singlestore/singlestore.py +2877 -0
- agno/db/singlestore/utils.py +384 -0
- agno/db/sqlite/__init__.py +4 -0
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +181 -0
- agno/db/sqlite/sqlite.py +2908 -0
- agno/db/sqlite/utils.py +429 -0
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +334 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1908 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +118 -0
- agno/eval/__init__.py +24 -0
- agno/eval/accuracy.py +666 -276
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +779 -0
- agno/eval/reliability.py +241 -62
- agno/eval/utils.py +120 -0
- agno/exceptions.py +143 -1
- agno/filters.py +354 -0
- agno/guardrails/__init__.py +6 -0
- agno/guardrails/base.py +19 -0
- agno/guardrails/openai.py +144 -0
- agno/guardrails/pii.py +94 -0
- agno/guardrails/prompt_injection.py +52 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/__init__.py +3 -0
- agno/integrations/discord/client.py +203 -0
- agno/knowledge/__init__.py +5 -1
- agno/{document → knowledge}/chunking/agentic.py +22 -14
- agno/{document → knowledge}/chunking/document.py +2 -2
- agno/{document → knowledge}/chunking/fixed.py +7 -6
- agno/knowledge/chunking/markdown.py +151 -0
- agno/{document → knowledge}/chunking/recursive.py +15 -3
- agno/knowledge/chunking/row.py +39 -0
- agno/knowledge/chunking/semantic.py +91 -0
- agno/knowledge/chunking/strategy.py +165 -0
- agno/knowledge/content.py +74 -0
- agno/knowledge/document/__init__.py +5 -0
- agno/{document → knowledge/document}/base.py +12 -2
- agno/knowledge/embedder/__init__.py +5 -0
- agno/knowledge/embedder/aws_bedrock.py +343 -0
- agno/knowledge/embedder/azure_openai.py +210 -0
- agno/{embedder → knowledge/embedder}/base.py +8 -0
- agno/knowledge/embedder/cohere.py +323 -0
- agno/knowledge/embedder/fastembed.py +62 -0
- agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
- agno/knowledge/embedder/google.py +258 -0
- agno/knowledge/embedder/huggingface.py +94 -0
- agno/knowledge/embedder/jina.py +182 -0
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +206 -0
- agno/knowledge/embedder/nebius.py +13 -0
- agno/knowledge/embedder/ollama.py +154 -0
- agno/knowledge/embedder/openai.py +195 -0
- agno/knowledge/embedder/sentence_transformer.py +63 -0
- agno/{embedder → knowledge/embedder}/together.py +1 -1
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +165 -0
- agno/knowledge/knowledge.py +3006 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/knowledge/reader/arxiv_reader.py +81 -0
- agno/knowledge/reader/base.py +95 -0
- agno/knowledge/reader/csv_reader.py +164 -0
- agno/knowledge/reader/docx_reader.py +82 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
- agno/knowledge/reader/firecrawl_reader.py +201 -0
- agno/knowledge/reader/json_reader.py +88 -0
- agno/knowledge/reader/markdown_reader.py +137 -0
- agno/knowledge/reader/pdf_reader.py +431 -0
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +313 -0
- agno/knowledge/reader/s3_reader.py +89 -0
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +127 -0
- agno/knowledge/reader/web_search_reader.py +325 -0
- agno/knowledge/reader/website_reader.py +455 -0
- agno/knowledge/reader/wikipedia_reader.py +91 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/knowledge/reranker/__init__.py +3 -0
- agno/{reranker → knowledge/reranker}/base.py +1 -1
- agno/{reranker → knowledge/reranker}/cohere.py +2 -2
- agno/knowledge/reranker/infinity.py +195 -0
- agno/knowledge/reranker/sentence_transformer.py +54 -0
- agno/knowledge/types.py +39 -0
- agno/knowledge/utils.py +234 -0
- agno/media.py +439 -95
- agno/memory/__init__.py +16 -3
- agno/memory/manager.py +1474 -123
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +66 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/aimlapi/__init__.py +5 -0
- agno/models/aimlapi/aimlapi.py +62 -0
- agno/models/anthropic/__init__.py +4 -0
- agno/models/anthropic/claude.py +960 -496
- agno/models/aws/__init__.py +15 -0
- agno/models/aws/bedrock.py +686 -451
- agno/models/aws/claude.py +190 -183
- agno/models/azure/__init__.py +18 -1
- agno/models/azure/ai_foundry.py +489 -0
- agno/models/azure/openai_chat.py +89 -40
- agno/models/base.py +2477 -550
- agno/models/cerebras/__init__.py +12 -0
- agno/models/cerebras/cerebras.py +565 -0
- agno/models/cerebras/cerebras_openai.py +131 -0
- agno/models/cohere/__init__.py +4 -0
- agno/models/cohere/chat.py +306 -492
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +74 -0
- agno/models/dashscope/__init__.py +5 -0
- agno/models/dashscope/dashscope.py +90 -0
- agno/models/deepinfra/__init__.py +5 -0
- agno/models/deepinfra/deepinfra.py +45 -0
- agno/models/deepseek/__init__.py +4 -0
- agno/models/deepseek/deepseek.py +110 -9
- agno/models/fireworks/__init__.py +4 -0
- agno/models/fireworks/fireworks.py +19 -22
- agno/models/google/__init__.py +3 -7
- agno/models/google/gemini.py +1717 -662
- agno/models/google/utils.py +22 -0
- agno/models/groq/__init__.py +4 -0
- agno/models/groq/groq.py +391 -666
- agno/models/huggingface/__init__.py +4 -0
- agno/models/huggingface/huggingface.py +266 -538
- agno/models/ibm/__init__.py +5 -0
- agno/models/ibm/watsonx.py +432 -0
- agno/models/internlm/__init__.py +3 -0
- agno/models/internlm/internlm.py +20 -3
- agno/models/langdb/__init__.py +1 -0
- agno/models/langdb/langdb.py +60 -0
- agno/models/litellm/__init__.py +14 -0
- agno/models/litellm/chat.py +503 -0
- agno/models/litellm/litellm_openai.py +42 -0
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/lmstudio/__init__.py +5 -0
- agno/models/lmstudio/lmstudio.py +25 -0
- agno/models/message.py +361 -39
- agno/models/meta/__init__.py +12 -0
- agno/models/meta/llama.py +502 -0
- agno/models/meta/llama_openai.py +79 -0
- agno/models/metrics.py +120 -0
- agno/models/mistral/__init__.py +4 -0
- agno/models/mistral/mistral.py +293 -393
- agno/models/nebius/__init__.py +3 -0
- agno/models/nebius/nebius.py +53 -0
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/__init__.py +4 -0
- agno/models/nvidia/nvidia.py +22 -3
- agno/models/ollama/__init__.py +4 -2
- agno/models/ollama/chat.py +257 -492
- agno/models/openai/__init__.py +7 -0
- agno/models/openai/chat.py +725 -770
- agno/models/openai/like.py +16 -2
- agno/models/openai/responses.py +1121 -0
- agno/models/openrouter/__init__.py +4 -0
- agno/models/openrouter/openrouter.py +62 -5
- agno/models/perplexity/__init__.py +5 -0
- agno/models/perplexity/perplexity.py +203 -0
- agno/models/portkey/__init__.py +3 -0
- agno/models/portkey/portkey.py +82 -0
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +69 -0
- agno/models/response.py +177 -7
- agno/models/sambanova/__init__.py +4 -0
- agno/models/sambanova/sambanova.py +23 -4
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +42 -0
- agno/models/together/__init__.py +4 -0
- agno/models/together/together.py +21 -164
- agno/models/utils.py +266 -0
- agno/models/vercel/__init__.py +3 -0
- agno/models/vercel/v0.py +43 -0
- agno/models/vertexai/__init__.py +0 -1
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/__init__.py +3 -0
- agno/models/vllm/vllm.py +83 -0
- agno/models/xai/__init__.py +2 -0
- agno/models/xai/xai.py +111 -7
- agno/os/__init__.py +3 -0
- agno/os/app.py +1027 -0
- agno/os/auth.py +244 -0
- agno/os/config.py +126 -0
- agno/os/interfaces/__init__.py +1 -0
- agno/os/interfaces/a2a/__init__.py +3 -0
- agno/os/interfaces/a2a/a2a.py +42 -0
- agno/os/interfaces/a2a/router.py +249 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +47 -0
- agno/os/interfaces/agui/router.py +147 -0
- agno/os/interfaces/agui/utils.py +574 -0
- agno/os/interfaces/base.py +25 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/os/interfaces/slack/router.py +148 -0
- agno/os/interfaces/slack/security.py +30 -0
- agno/os/interfaces/slack/slack.py +47 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/os/interfaces/whatsapp/router.py +210 -0
- agno/os/interfaces/whatsapp/security.py +55 -0
- agno/os/interfaces/whatsapp/whatsapp.py +36 -0
- agno/os/mcp.py +293 -0
- agno/os/middleware/__init__.py +9 -0
- agno/os/middleware/jwt.py +797 -0
- agno/os/router.py +258 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +599 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +450 -0
- agno/os/routers/evals/schemas.py +174 -0
- agno/os/routers/evals/utils.py +231 -0
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +1008 -0
- agno/os/routers/knowledge/schemas.py +178 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +661 -0
- agno/os/routers/memory/schemas.py +88 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +190 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +997 -0
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +512 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +499 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +624 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +534 -0
- agno/os/scopes.py +469 -0
- agno/{playground → os}/settings.py +7 -15
- agno/os/utils.py +973 -0
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +67 -0
- agno/reasoning/deepseek.py +63 -0
- agno/reasoning/default.py +97 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +71 -0
- agno/reasoning/helpers.py +24 -1
- agno/reasoning/ollama.py +67 -0
- agno/reasoning/openai.py +86 -0
- agno/reasoning/step.py +2 -1
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +822 -0
- agno/run/base.py +247 -0
- agno/run/cancel.py +81 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +767 -0
- agno/run/workflow.py +708 -0
- agno/session/__init__.py +10 -0
- agno/session/agent.py +260 -0
- agno/session/summary.py +265 -0
- agno/session/team.py +342 -0
- agno/session/workflow.py +501 -0
- agno/table.py +10 -0
- agno/team/__init__.py +37 -0
- agno/team/team.py +9536 -0
- agno/tools/__init__.py +7 -0
- agno/tools/agentql.py +120 -0
- agno/tools/airflow.py +22 -12
- agno/tools/api.py +122 -0
- agno/tools/apify.py +276 -83
- agno/tools/{arxiv_toolkit.py → arxiv.py} +20 -12
- agno/tools/aws_lambda.py +28 -7
- agno/tools/aws_ses.py +66 -0
- agno/tools/baidusearch.py +11 -4
- agno/tools/bitbucket.py +292 -0
- agno/tools/brandfetch.py +213 -0
- agno/tools/bravesearch.py +106 -0
- agno/tools/brightdata.py +367 -0
- agno/tools/browserbase.py +209 -0
- agno/tools/calcom.py +32 -23
- agno/tools/calculator.py +24 -37
- agno/tools/cartesia.py +187 -0
- agno/tools/{clickup_tool.py → clickup.py} +17 -28
- agno/tools/confluence.py +91 -26
- agno/tools/crawl4ai.py +139 -43
- agno/tools/csv_toolkit.py +28 -22
- agno/tools/dalle.py +36 -22
- agno/tools/daytona.py +475 -0
- agno/tools/decorator.py +169 -14
- agno/tools/desi_vocal.py +23 -11
- agno/tools/discord.py +32 -29
- agno/tools/docker.py +716 -0
- agno/tools/duckdb.py +76 -81
- agno/tools/duckduckgo.py +43 -40
- agno/tools/e2b.py +703 -0
- agno/tools/eleven_labs.py +65 -54
- agno/tools/email.py +13 -5
- agno/tools/evm.py +129 -0
- agno/tools/exa.py +324 -42
- agno/tools/fal.py +39 -35
- agno/tools/file.py +196 -30
- agno/tools/file_generation.py +356 -0
- agno/tools/financial_datasets.py +288 -0
- agno/tools/firecrawl.py +108 -33
- agno/tools/function.py +960 -122
- agno/tools/giphy.py +34 -12
- agno/tools/github.py +1294 -97
- agno/tools/gmail.py +922 -0
- agno/tools/google_bigquery.py +117 -0
- agno/tools/google_drive.py +271 -0
- agno/tools/google_maps.py +253 -0
- agno/tools/googlecalendar.py +607 -107
- agno/tools/googlesheets.py +377 -0
- agno/tools/hackernews.py +20 -12
- agno/tools/jina.py +24 -14
- agno/tools/jira.py +48 -19
- agno/tools/knowledge.py +218 -0
- agno/tools/linear.py +82 -43
- agno/tools/linkup.py +58 -0
- agno/tools/local_file_system.py +15 -7
- agno/tools/lumalab.py +41 -26
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +284 -0
- agno/tools/mem0.py +193 -0
- agno/tools/memory.py +419 -0
- agno/tools/mlx_transcribe.py +11 -9
- agno/tools/models/azure_openai.py +190 -0
- agno/tools/models/gemini.py +203 -0
- agno/tools/models/groq.py +158 -0
- agno/tools/models/morph.py +186 -0
- agno/tools/models/nebius.py +124 -0
- agno/tools/models_labs.py +163 -82
- agno/tools/moviepy_video.py +18 -13
- agno/tools/nano_banana.py +151 -0
- agno/tools/neo4j.py +134 -0
- agno/tools/newspaper.py +15 -4
- agno/tools/newspaper4k.py +19 -6
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +181 -17
- agno/tools/openbb.py +27 -20
- agno/tools/opencv.py +321 -0
- agno/tools/openweather.py +233 -0
- agno/tools/oxylabs.py +385 -0
- agno/tools/pandas.py +25 -15
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +238 -185
- agno/tools/pubmed.py +125 -13
- agno/tools/python.py +48 -35
- agno/tools/reasoning.py +283 -0
- agno/tools/reddit.py +207 -29
- agno/tools/redshift.py +406 -0
- agno/tools/replicate.py +69 -26
- agno/tools/resend.py +11 -6
- agno/tools/scrapegraph.py +179 -19
- agno/tools/searxng.py +23 -31
- agno/tools/serpapi.py +15 -10
- agno/tools/serper.py +255 -0
- agno/tools/shell.py +23 -12
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +56 -14
- agno/tools/sleep.py +8 -6
- agno/tools/spider.py +35 -11
- agno/tools/spotify.py +919 -0
- agno/tools/sql.py +34 -19
- agno/tools/tavily.py +158 -8
- agno/tools/telegram.py +18 -8
- agno/tools/todoist.py +218 -0
- agno/tools/toolkit.py +134 -9
- agno/tools/trafilatura.py +388 -0
- agno/tools/trello.py +25 -28
- agno/tools/twilio.py +18 -9
- agno/tools/user_control_flow.py +78 -0
- agno/tools/valyu.py +228 -0
- agno/tools/visualization.py +467 -0
- agno/tools/webbrowser.py +28 -0
- agno/tools/webex.py +76 -0
- agno/tools/website.py +23 -19
- agno/tools/webtools.py +45 -0
- agno/tools/whatsapp.py +286 -0
- agno/tools/wikipedia.py +28 -19
- agno/tools/workflow.py +285 -0
- agno/tools/{twitter.py → x.py} +142 -46
- agno/tools/yfinance.py +41 -39
- agno/tools/youtube.py +34 -17
- agno/tools/zendesk.py +15 -5
- agno/tools/zep.py +454 -0
- agno/tools/zoom.py +86 -37
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +157 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +111 -0
- agno/utils/agent.py +938 -0
- agno/utils/audio.py +37 -1
- agno/utils/certs.py +27 -0
- agno/utils/code_execution.py +11 -0
- agno/utils/common.py +103 -20
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +700 -0
- agno/utils/functions.py +107 -37
- agno/utils/gemini.py +426 -0
- agno/utils/hooks.py +171 -0
- agno/utils/http.py +185 -0
- agno/utils/json_schema.py +159 -37
- agno/utils/knowledge.py +36 -0
- agno/utils/location.py +19 -0
- agno/utils/log.py +221 -8
- agno/utils/mcp.py +214 -0
- agno/utils/media.py +335 -14
- agno/utils/merge_dict.py +22 -1
- agno/utils/message.py +77 -2
- agno/utils/models/ai_foundry.py +50 -0
- agno/utils/models/claude.py +373 -0
- agno/utils/models/cohere.py +94 -0
- agno/utils/models/llama.py +85 -0
- agno/utils/models/mistral.py +100 -0
- agno/utils/models/openai_responses.py +140 -0
- agno/utils/models/schema_utils.py +153 -0
- agno/utils/models/watsonx.py +41 -0
- agno/utils/openai.py +257 -0
- agno/utils/pickle.py +1 -1
- agno/utils/pprint.py +124 -8
- agno/utils/print_response/agent.py +930 -0
- agno/utils/print_response/team.py +1914 -0
- agno/utils/print_response/workflow.py +1668 -0
- agno/utils/prompts.py +111 -0
- agno/utils/reasoning.py +108 -0
- agno/utils/response.py +163 -0
- agno/utils/serialize.py +32 -0
- agno/utils/shell.py +4 -4
- agno/utils/streamlit.py +487 -0
- agno/utils/string.py +204 -51
- agno/utils/team.py +139 -0
- agno/utils/timer.py +9 -2
- agno/utils/tokens.py +657 -0
- agno/utils/tools.py +19 -1
- agno/utils/whatsapp.py +305 -0
- agno/utils/yaml_io.py +3 -3
- agno/vectordb/__init__.py +2 -0
- agno/vectordb/base.py +87 -9
- agno/vectordb/cassandra/__init__.py +5 -1
- agno/vectordb/cassandra/cassandra.py +383 -27
- agno/vectordb/chroma/__init__.py +4 -0
- agno/vectordb/chroma/chromadb.py +748 -83
- agno/vectordb/clickhouse/__init__.py +7 -1
- agno/vectordb/clickhouse/clickhousedb.py +554 -53
- agno/vectordb/couchbase/__init__.py +3 -0
- agno/vectordb/couchbase/couchbase.py +1446 -0
- agno/vectordb/lancedb/__init__.py +5 -0
- agno/vectordb/lancedb/lance_db.py +730 -98
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +163 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +388 -0
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +166 -0
- agno/vectordb/milvus/__init__.py +3 -0
- agno/vectordb/milvus/milvus.py +966 -78
- agno/vectordb/mongodb/__init__.py +9 -1
- agno/vectordb/mongodb/mongodb.py +1175 -172
- agno/vectordb/pgvector/__init__.py +8 -0
- agno/vectordb/pgvector/pgvector.py +599 -115
- agno/vectordb/pineconedb/__init__.py +5 -1
- agno/vectordb/pineconedb/pineconedb.py +406 -43
- agno/vectordb/qdrant/__init__.py +4 -0
- agno/vectordb/qdrant/qdrant.py +914 -61
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/__init__.py +8 -1
- agno/vectordb/singlestore/singlestore.py +771 -0
- agno/vectordb/surrealdb/__init__.py +3 -0
- agno/vectordb/surrealdb/surrealdb.py +663 -0
- agno/vectordb/upstashdb/__init__.py +5 -0
- agno/vectordb/upstashdb/upstashdb.py +718 -0
- agno/vectordb/weaviate/__init__.py +8 -0
- agno/vectordb/weaviate/index.py +15 -0
- agno/vectordb/weaviate/weaviate.py +1009 -0
- agno/workflow/__init__.py +23 -1
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +759 -0
- agno/workflow/loop.py +756 -0
- agno/workflow/parallel.py +853 -0
- agno/workflow/router.py +723 -0
- agno/workflow/step.py +1564 -0
- agno/workflow/steps.py +613 -0
- agno/workflow/types.py +556 -0
- agno/workflow/workflow.py +4327 -514
- agno-2.3.13.dist-info/METADATA +639 -0
- agno-2.3.13.dist-info/RECORD +613 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +1 -1
- agno-2.3.13.dist-info/licenses/LICENSE +201 -0
- agno/api/playground.py +0 -91
- agno/api/schemas/playground.py +0 -22
- agno/api/schemas/user.py +0 -22
- agno/api/schemas/workspace.py +0 -46
- agno/api/user.py +0 -160
- agno/api/workspace.py +0 -151
- agno/cli/auth_server.py +0 -118
- agno/cli/config.py +0 -275
- agno/cli/console.py +0 -88
- agno/cli/credentials.py +0 -23
- agno/cli/entrypoint.py +0 -571
- agno/cli/operator.py +0 -355
- agno/cli/settings.py +0 -85
- agno/cli/ws/ws_cli.py +0 -817
- agno/constants.py +0 -13
- agno/document/__init__.py +0 -1
- agno/document/chunking/semantic.py +0 -47
- agno/document/chunking/strategy.py +0 -31
- agno/document/reader/__init__.py +0 -1
- agno/document/reader/arxiv_reader.py +0 -41
- agno/document/reader/base.py +0 -22
- agno/document/reader/csv_reader.py +0 -84
- agno/document/reader/docx_reader.py +0 -46
- agno/document/reader/firecrawl_reader.py +0 -99
- agno/document/reader/json_reader.py +0 -43
- agno/document/reader/pdf_reader.py +0 -219
- agno/document/reader/s3/pdf_reader.py +0 -46
- agno/document/reader/s3/text_reader.py +0 -51
- agno/document/reader/text_reader.py +0 -41
- agno/document/reader/website_reader.py +0 -175
- agno/document/reader/youtube_reader.py +0 -50
- agno/embedder/__init__.py +0 -1
- agno/embedder/azure_openai.py +0 -86
- agno/embedder/cohere.py +0 -72
- agno/embedder/fastembed.py +0 -37
- agno/embedder/google.py +0 -73
- agno/embedder/huggingface.py +0 -54
- agno/embedder/mistral.py +0 -80
- agno/embedder/ollama.py +0 -57
- agno/embedder/openai.py +0 -74
- agno/embedder/sentence_transformer.py +0 -38
- agno/embedder/voyageai.py +0 -64
- agno/eval/perf.py +0 -201
- agno/file/__init__.py +0 -1
- agno/file/file.py +0 -16
- agno/file/local/csv.py +0 -32
- agno/file/local/txt.py +0 -19
- agno/infra/app.py +0 -240
- agno/infra/base.py +0 -144
- agno/infra/context.py +0 -20
- agno/infra/db_app.py +0 -52
- agno/infra/resource.py +0 -205
- agno/infra/resources.py +0 -55
- agno/knowledge/agent.py +0 -230
- agno/knowledge/arxiv.py +0 -22
- agno/knowledge/combined.py +0 -22
- agno/knowledge/csv.py +0 -28
- agno/knowledge/csv_url.py +0 -19
- agno/knowledge/document.py +0 -20
- agno/knowledge/docx.py +0 -30
- agno/knowledge/json.py +0 -28
- agno/knowledge/langchain.py +0 -71
- agno/knowledge/llamaindex.py +0 -66
- agno/knowledge/pdf.py +0 -28
- agno/knowledge/pdf_url.py +0 -26
- agno/knowledge/s3/base.py +0 -60
- agno/knowledge/s3/pdf.py +0 -21
- agno/knowledge/s3/text.py +0 -23
- agno/knowledge/text.py +0 -30
- agno/knowledge/website.py +0 -88
- agno/knowledge/wikipedia.py +0 -31
- agno/knowledge/youtube.py +0 -22
- agno/memory/agent.py +0 -392
- agno/memory/classifier.py +0 -104
- agno/memory/db/__init__.py +0 -1
- agno/memory/db/base.py +0 -42
- agno/memory/db/mongodb.py +0 -189
- agno/memory/db/postgres.py +0 -203
- agno/memory/db/sqlite.py +0 -193
- agno/memory/memory.py +0 -15
- agno/memory/row.py +0 -36
- agno/memory/summarizer.py +0 -192
- agno/memory/summary.py +0 -19
- agno/memory/workflow.py +0 -38
- agno/models/google/gemini_openai.py +0 -26
- agno/models/ollama/hermes.py +0 -221
- agno/models/ollama/tools.py +0 -362
- agno/models/vertexai/gemini.py +0 -595
- agno/playground/__init__.py +0 -3
- agno/playground/async_router.py +0 -421
- agno/playground/deploy.py +0 -249
- agno/playground/operator.py +0 -92
- agno/playground/playground.py +0 -91
- agno/playground/schemas.py +0 -76
- agno/playground/serve.py +0 -55
- agno/playground/sync_router.py +0 -405
- agno/reasoning/agent.py +0 -68
- agno/run/response.py +0 -112
- agno/storage/agent/__init__.py +0 -0
- agno/storage/agent/base.py +0 -38
- agno/storage/agent/dynamodb.py +0 -350
- agno/storage/agent/json.py +0 -92
- agno/storage/agent/mongodb.py +0 -228
- agno/storage/agent/postgres.py +0 -367
- agno/storage/agent/session.py +0 -79
- agno/storage/agent/singlestore.py +0 -303
- agno/storage/agent/sqlite.py +0 -357
- agno/storage/agent/yaml.py +0 -93
- agno/storage/workflow/__init__.py +0 -0
- agno/storage/workflow/base.py +0 -40
- agno/storage/workflow/mongodb.py +0 -233
- agno/storage/workflow/postgres.py +0 -366
- agno/storage/workflow/session.py +0 -60
- agno/storage/workflow/sqlite.py +0 -359
- agno/tools/googlesearch.py +0 -88
- agno/utils/defaults.py +0 -57
- agno/utils/filesystem.py +0 -39
- agno/utils/git.py +0 -52
- agno/utils/json_io.py +0 -30
- agno/utils/load_env.py +0 -19
- agno/utils/py_io.py +0 -19
- agno/utils/pyproject.py +0 -18
- agno/utils/resource_filter.py +0 -31
- agno/vectordb/singlestore/s2vectordb.py +0 -390
- agno/vectordb/singlestore/s2vectordb2.py +0 -355
- agno/workspace/__init__.py +0 -0
- agno/workspace/config.py +0 -325
- agno/workspace/enums.py +0 -6
- agno/workspace/helpers.py +0 -48
- agno/workspace/operator.py +0 -758
- agno/workspace/settings.py +0 -63
- agno-0.1.2.dist-info/LICENSE +0 -375
- agno-0.1.2.dist-info/METADATA +0 -502
- agno-0.1.2.dist-info/RECORD +0 -352
- agno-0.1.2.dist-info/entry_points.txt +0 -3
- /agno/{cli → db/migrations}/__init__.py +0 -0
- /agno/{cli/ws → db/migrations/versions}/__init__.py +0 -0
- /agno/{document/chunking/__init__.py → db/schemas/metrics.py} +0 -0
- /agno/{document/reader/s3 → integrations}/__init__.py +0 -0
- /agno/{file/local → knowledge/chunking}/__init__.py +0 -0
- /agno/{infra → knowledge/remote_content}/__init__.py +0 -0
- /agno/{knowledge/s3 → tools/models}/__init__.py +0 -0
- /agno/{reranker → utils/models}/__init__.py +0 -0
- /agno/{storage → utils/print_response}/__init__.py +0 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
agno/models/aws/bedrock.py
CHANGED
|
@@ -1,22 +1,38 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from
|
|
3
|
+
from os import getenv
|
|
4
|
+
from typing import Any, AsyncIterator, Dict, Iterator, List, Optional, Tuple, Type, Union
|
|
4
5
|
|
|
5
|
-
from
|
|
6
|
-
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
from agno.exceptions import ModelProviderError
|
|
9
|
+
from agno.models.base import Model
|
|
7
10
|
from agno.models.message import Message
|
|
8
|
-
from agno.models.
|
|
9
|
-
from agno.
|
|
10
|
-
from agno.
|
|
11
|
-
from agno.utils.
|
|
12
|
-
|
|
13
|
-
)
|
|
11
|
+
from agno.models.metrics import Metrics
|
|
12
|
+
from agno.models.response import ModelResponse
|
|
13
|
+
from agno.run.agent import RunOutput
|
|
14
|
+
from agno.utils.log import log_debug, log_error, log_warning
|
|
15
|
+
from agno.utils.tokens import count_schema_tokens
|
|
14
16
|
|
|
15
17
|
try:
|
|
16
|
-
from boto3 import
|
|
18
|
+
from boto3 import client as AwsClient
|
|
19
|
+
from boto3.session import Session
|
|
20
|
+
from botocore.exceptions import ClientError
|
|
17
21
|
except ImportError:
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
raise ImportError("`boto3` not installed. Please install using `pip install boto3`")
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
import aioboto3
|
|
26
|
+
|
|
27
|
+
AIOBOTO3_AVAILABLE = True
|
|
28
|
+
except ImportError:
|
|
29
|
+
aioboto3 = None
|
|
30
|
+
AIOBOTO3_AVAILABLE = False
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
BEDROCK_SUPPORTED_IMAGE_FORMATS = ["png", "jpeg", "webp", "gif"]
|
|
34
|
+
BEDROCK_SUPPORTED_VIDEO_FORMATS = ["mp4", "mov", "mkv", "webm", "flv", "mpeg", "mpg", "wmv", "three_gp"]
|
|
35
|
+
BEDROCK_SUPPORTED_FILE_FORMATS = ["pdf", "csv", "doc", "docx", "xls", "xlsx", "html", "txt", "md"]
|
|
20
36
|
|
|
21
37
|
|
|
22
38
|
@dataclass
|
|
@@ -24,524 +40,743 @@ class AwsBedrock(Model):
|
|
|
24
40
|
"""
|
|
25
41
|
AWS Bedrock model.
|
|
26
42
|
|
|
43
|
+
To use this model, you need to either:
|
|
44
|
+
1. Set the following environment variables:
|
|
45
|
+
- AWS_ACCESS_KEY_ID
|
|
46
|
+
- AWS_SECRET_ACCESS_KEY
|
|
47
|
+
- AWS_REGION
|
|
48
|
+
2. Or provide a boto3 Session object
|
|
49
|
+
|
|
50
|
+
For async support, you also need aioboto3 installed:
|
|
51
|
+
pip install aioboto3
|
|
52
|
+
|
|
53
|
+
Not all Bedrock models support all features. See this documentation for more information: https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html
|
|
54
|
+
|
|
27
55
|
Args:
|
|
28
56
|
aws_region (Optional[str]): The AWS region to use.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
57
|
+
aws_access_key_id (Optional[str]): The AWS access key ID to use.
|
|
58
|
+
aws_secret_access_key (Optional[str]): The AWS secret access key to use.
|
|
59
|
+
aws_sso_auth (Optional[str]): Removes the need for an access and secret access key by leveraging the current profile's authentication
|
|
60
|
+
session (Optional[Session]): A boto3 Session object to use for authentication.
|
|
33
61
|
"""
|
|
34
62
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
63
|
+
id: str = "mistral.mistral-small-2402-v1:0"
|
|
64
|
+
name: str = "AwsBedrock"
|
|
65
|
+
provider: str = "AwsBedrock"
|
|
38
66
|
|
|
39
|
-
|
|
40
|
-
|
|
67
|
+
aws_sso_auth: Optional[bool] = False
|
|
68
|
+
aws_region: Optional[str] = None
|
|
69
|
+
aws_access_key_id: Optional[str] = None
|
|
70
|
+
aws_secret_access_key: Optional[str] = None
|
|
71
|
+
session: Optional[Session] = None
|
|
72
|
+
|
|
73
|
+
# Request parameters
|
|
74
|
+
max_tokens: Optional[int] = None
|
|
75
|
+
temperature: Optional[float] = None
|
|
76
|
+
top_p: Optional[float] = None
|
|
77
|
+
stop_sequences: Optional[List[str]] = None
|
|
78
|
+
request_params: Optional[Dict[str, Any]] = None
|
|
79
|
+
|
|
80
|
+
client: Optional[AwsClient] = None
|
|
81
|
+
async_client: Optional[Any] = None
|
|
82
|
+
async_session: Optional[Any] = None
|
|
83
|
+
|
|
84
|
+
def get_client(self) -> AwsClient:
|
|
85
|
+
"""
|
|
86
|
+
Get the Bedrock client.
|
|
41
87
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
88
|
+
Returns:
|
|
89
|
+
AwsClient: The Bedrock client.
|
|
90
|
+
"""
|
|
91
|
+
if self.client is not None:
|
|
92
|
+
return self.client
|
|
93
|
+
|
|
94
|
+
if self.session:
|
|
95
|
+
self.client = self.session.client("bedrock-runtime")
|
|
96
|
+
return self.client
|
|
97
|
+
|
|
98
|
+
self.aws_access_key_id = self.aws_access_key_id or getenv("AWS_ACCESS_KEY_ID")
|
|
99
|
+
self.aws_secret_access_key = self.aws_secret_access_key or getenv("AWS_SECRET_ACCESS_KEY")
|
|
100
|
+
self.aws_region = self.aws_region or getenv("AWS_REGION")
|
|
101
|
+
|
|
102
|
+
if self.aws_sso_auth:
|
|
103
|
+
self.client = AwsClient(service_name="bedrock-runtime", region_name=self.aws_region)
|
|
104
|
+
else:
|
|
105
|
+
if not self.aws_access_key_id or not self.aws_secret_access_key:
|
|
106
|
+
log_error(
|
|
107
|
+
"AWS credentials not found. Please set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables or provide a boto3 session."
|
|
108
|
+
)
|
|
46
109
|
|
|
47
|
-
|
|
48
|
-
|
|
110
|
+
self.client = AwsClient(
|
|
111
|
+
service_name="bedrock-runtime",
|
|
112
|
+
region_name=self.aws_region,
|
|
113
|
+
aws_access_key_id=self.aws_access_key_id,
|
|
114
|
+
aws_secret_access_key=self.aws_secret_access_key,
|
|
115
|
+
)
|
|
116
|
+
return self.client
|
|
49
117
|
|
|
50
|
-
|
|
118
|
+
def get_async_client(self):
|
|
119
|
+
"""
|
|
120
|
+
Get the async Bedrock client context manager.
|
|
51
121
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
122
|
+
Returns:
|
|
123
|
+
The async Bedrock client context manager.
|
|
124
|
+
"""
|
|
125
|
+
if not AIOBOTO3_AVAILABLE:
|
|
126
|
+
raise ImportError(
|
|
127
|
+
"`aioboto3` not installed. Please install using `pip install aioboto3` for async support."
|
|
128
|
+
)
|
|
56
129
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
130
|
+
if self.async_session is None:
|
|
131
|
+
self.aws_access_key_id = self.aws_access_key_id or getenv("AWS_ACCESS_KEY_ID")
|
|
132
|
+
self.aws_secret_access_key = self.aws_secret_access_key or getenv("AWS_SECRET_ACCESS_KEY")
|
|
133
|
+
self.aws_region = self.aws_region or getenv("AWS_REGION")
|
|
61
134
|
|
|
62
|
-
|
|
63
|
-
from os import getenv
|
|
135
|
+
self.async_session = aioboto3.Session()
|
|
64
136
|
|
|
65
|
-
|
|
137
|
+
client_kwargs = {
|
|
138
|
+
"service_name": "bedrock-runtime",
|
|
139
|
+
"region_name": self.aws_region,
|
|
140
|
+
}
|
|
66
141
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
142
|
+
if self.aws_sso_auth:
|
|
143
|
+
pass
|
|
144
|
+
else:
|
|
145
|
+
if not self.aws_access_key_id or not self.aws_secret_access_key:
|
|
146
|
+
import os
|
|
147
|
+
|
|
148
|
+
env_access_key = os.environ.get("AWS_ACCESS_KEY_ID")
|
|
149
|
+
env_secret_key = os.environ.get("AWS_SECRET_ACCESS_KEY")
|
|
150
|
+
env_region = os.environ.get("AWS_REGION")
|
|
151
|
+
|
|
152
|
+
if env_access_key and env_secret_key:
|
|
153
|
+
self.aws_access_key_id = env_access_key
|
|
154
|
+
self.aws_secret_access_key = env_secret_key
|
|
155
|
+
if env_region:
|
|
156
|
+
self.aws_region = env_region
|
|
157
|
+
client_kwargs["region_name"] = self.aws_region
|
|
158
|
+
|
|
159
|
+
if self.aws_access_key_id and self.aws_secret_access_key:
|
|
160
|
+
client_kwargs.update(
|
|
161
|
+
{
|
|
162
|
+
"aws_access_key_id": self.aws_access_key_id,
|
|
163
|
+
"aws_secret_access_key": self.aws_secret_access_key,
|
|
164
|
+
}
|
|
165
|
+
)
|
|
71
166
|
|
|
72
|
-
|
|
73
|
-
if self.aws_client is not None:
|
|
74
|
-
return self.aws_client
|
|
167
|
+
return self.async_session.client(**client_kwargs)
|
|
75
168
|
|
|
76
|
-
|
|
77
|
-
|
|
169
|
+
def _format_tools_for_request(self, tools: Optional[List[Dict[str, Any]]]) -> List[Dict[str, Any]]:
|
|
170
|
+
"""
|
|
171
|
+
Format the tools for the request.
|
|
78
172
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
173
|
+
Returns:
|
|
174
|
+
List[Dict[str, Any]]: The formatted tools.
|
|
175
|
+
"""
|
|
176
|
+
parsed_tools = []
|
|
177
|
+
if tools is not None:
|
|
178
|
+
for tool_def in tools:
|
|
179
|
+
func_def = tool_def.get("function", {})
|
|
180
|
+
properties = {}
|
|
181
|
+
required = []
|
|
182
|
+
|
|
183
|
+
for param_name, param_info in func_def.get("parameters", {}).get("properties", {}).items():
|
|
184
|
+
properties[param_name] = param_info.copy()
|
|
185
|
+
|
|
186
|
+
if "description" not in properties[param_name]:
|
|
187
|
+
properties[param_name]["description"] = ""
|
|
188
|
+
|
|
189
|
+
if "null" not in (
|
|
190
|
+
param_info.get("type") if isinstance(param_info.get("type"), list) else [param_info.get("type")]
|
|
191
|
+
):
|
|
192
|
+
required.append(param_name)
|
|
193
|
+
|
|
194
|
+
parsed_tools.append(
|
|
195
|
+
{
|
|
196
|
+
"toolSpec": {
|
|
197
|
+
"name": func_def.get("name") or "",
|
|
198
|
+
"description": func_def.get("description") or "",
|
|
199
|
+
"inputSchema": {"json": {"type": "object", "properties": properties, "required": required}},
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
)
|
|
83
203
|
|
|
84
|
-
|
|
85
|
-
self._bedrock_runtime_client = boto3_session.client(service_name="bedrock-runtime")
|
|
86
|
-
return self._bedrock_runtime_client
|
|
204
|
+
return parsed_tools
|
|
87
205
|
|
|
88
|
-
def
|
|
206
|
+
def _get_inference_config(self) -> Dict[str, Any]:
|
|
89
207
|
"""
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
Args:
|
|
93
|
-
body (Dict[str, Any]): The request body.
|
|
208
|
+
Get the inference config.
|
|
94
209
|
|
|
95
210
|
Returns:
|
|
96
|
-
Dict[str, Any]: The
|
|
211
|
+
Dict[str, Any]: The inference config.
|
|
97
212
|
"""
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
213
|
+
request_kwargs = {
|
|
214
|
+
"maxTokens": self.max_tokens,
|
|
215
|
+
"temperature": self.temperature,
|
|
216
|
+
"topP": self.top_p,
|
|
217
|
+
"stopSequences": self.stop_sequences,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return {k: v for k, v in request_kwargs.items() if v is not None}
|
|
221
|
+
|
|
222
|
+
def _format_messages(
|
|
223
|
+
self, messages: List[Message], compress_tool_results: bool = False
|
|
224
|
+
) -> Tuple[List[Dict[str, Any]], Optional[List[Dict[str, Any]]]]:
|
|
101
225
|
"""
|
|
102
|
-
|
|
226
|
+
Format the messages for the request.
|
|
103
227
|
|
|
104
228
|
Args:
|
|
105
|
-
|
|
229
|
+
messages: List of messages to format
|
|
230
|
+
compress_tool_results: Whether to compress tool results
|
|
106
231
|
|
|
107
232
|
Returns:
|
|
108
|
-
|
|
233
|
+
Tuple[List[Dict[str, Any]], Optional[List[Dict[str, Any]]]]: The formatted messages.
|
|
109
234
|
"""
|
|
110
|
-
response = self.bedrock_runtime_client.converse_stream(**body)
|
|
111
|
-
stream = response.get("stream")
|
|
112
|
-
if stream:
|
|
113
|
-
for event in stream:
|
|
114
|
-
yield event
|
|
115
|
-
|
|
116
|
-
def create_assistant_message(self, request_body: Dict[str, Any]) -> Message:
|
|
117
|
-
raise NotImplementedError("Please use a subclass of AwsBedrock")
|
|
118
|
-
|
|
119
|
-
def get_request_body(self, messages: List[Message]) -> Dict[str, Any]:
|
|
120
|
-
raise NotImplementedError("Please use a subclass of AwsBedrock")
|
|
121
|
-
|
|
122
|
-
def parse_response_message(self, response: Dict[str, Any]) -> Dict[str, Any]:
|
|
123
|
-
raise NotImplementedError("Please use a subclass of AwsBedrock")
|
|
124
|
-
|
|
125
|
-
def _create_tool_calls(
|
|
126
|
-
self, stop_reason: str, parsed_response: Dict[str, Any]
|
|
127
|
-
) -> Tuple[List[str], List[Dict[str, Any]]]:
|
|
128
|
-
tool_ids: List[str] = []
|
|
129
|
-
tool_calls: List[Dict[str, Any]] = []
|
|
130
|
-
|
|
131
|
-
if stop_reason == "tool_use":
|
|
132
|
-
tool_requests = parsed_response.get("tool_requests")
|
|
133
|
-
if tool_requests:
|
|
134
|
-
for tool in tool_requests:
|
|
135
|
-
if "toolUse" in tool:
|
|
136
|
-
tool_use = tool["toolUse"]
|
|
137
|
-
tool_id = tool_use["toolUseId"]
|
|
138
|
-
tool_name = tool_use["name"]
|
|
139
|
-
tool_args = tool_use["input"]
|
|
140
235
|
|
|
141
|
-
|
|
142
|
-
|
|
236
|
+
formatted_messages: List[Dict[str, Any]] = []
|
|
237
|
+
system_message = None
|
|
238
|
+
for message in messages:
|
|
239
|
+
if message.role == "system":
|
|
240
|
+
system_message = [{"text": message.content}]
|
|
241
|
+
elif message.role == "tool":
|
|
242
|
+
content = message.get_content(use_compressed_content=compress_tool_results)
|
|
243
|
+
tool_result = {
|
|
244
|
+
"toolUseId": message.tool_call_id,
|
|
245
|
+
"content": [{"json": {"result": content}}],
|
|
246
|
+
}
|
|
247
|
+
formatted_message: Dict[str, Any] = {"role": "user", "content": [{"toolResult": tool_result}]}
|
|
248
|
+
formatted_messages.append(formatted_message)
|
|
249
|
+
else:
|
|
250
|
+
formatted_message = {"role": message.role, "content": []}
|
|
251
|
+
if isinstance(message.content, list):
|
|
252
|
+
formatted_message["content"].extend(message.content)
|
|
253
|
+
elif message.tool_calls:
|
|
254
|
+
tool_use_content = []
|
|
255
|
+
for tool_call in message.tool_calls:
|
|
256
|
+
try:
|
|
257
|
+
# Parse arguments with error handling for empty or invalid JSON
|
|
258
|
+
arguments = tool_call["function"]["arguments"]
|
|
259
|
+
if not arguments or arguments.strip() == "":
|
|
260
|
+
tool_input = {}
|
|
261
|
+
else:
|
|
262
|
+
tool_input = json.loads(arguments)
|
|
263
|
+
except (json.JSONDecodeError, KeyError) as e:
|
|
264
|
+
log_warning(f"Failed to parse tool call arguments: {e}")
|
|
265
|
+
tool_input = {}
|
|
266
|
+
|
|
267
|
+
tool_use_content.append(
|
|
143
268
|
{
|
|
144
|
-
"
|
|
145
|
-
|
|
146
|
-
"name":
|
|
147
|
-
"
|
|
148
|
-
}
|
|
269
|
+
"toolUse": {
|
|
270
|
+
"toolUseId": tool_call["id"],
|
|
271
|
+
"name": tool_call["function"]["name"],
|
|
272
|
+
"input": tool_input,
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
)
|
|
276
|
+
formatted_message["content"].extend(tool_use_content)
|
|
277
|
+
else:
|
|
278
|
+
formatted_message["content"].append({"text": message.content})
|
|
279
|
+
|
|
280
|
+
if message.images:
|
|
281
|
+
for image in message.images:
|
|
282
|
+
if not image.content:
|
|
283
|
+
raise ValueError("Image content is required for AWS Bedrock.")
|
|
284
|
+
if not image.format:
|
|
285
|
+
raise ValueError("Image format is required for AWS Bedrock.")
|
|
286
|
+
|
|
287
|
+
if image.format not in BEDROCK_SUPPORTED_IMAGE_FORMATS:
|
|
288
|
+
raise ValueError(
|
|
289
|
+
f"Unsupported image format: {image.format}. "
|
|
290
|
+
f"Supported formats: {BEDROCK_SUPPORTED_IMAGE_FORMATS}"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
formatted_message["content"].append(
|
|
294
|
+
{
|
|
295
|
+
"image": {
|
|
296
|
+
"format": image.format,
|
|
297
|
+
"source": {
|
|
298
|
+
"bytes": image.content,
|
|
299
|
+
},
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
)
|
|
303
|
+
if message.audio:
|
|
304
|
+
log_warning("Audio input is currently unsupported.")
|
|
305
|
+
|
|
306
|
+
if message.videos:
|
|
307
|
+
for video in message.videos:
|
|
308
|
+
if not video.content:
|
|
309
|
+
raise ValueError("Video content is required for AWS Bedrock.")
|
|
310
|
+
if not video.format:
|
|
311
|
+
raise ValueError("Video format is required for AWS Bedrock.")
|
|
312
|
+
|
|
313
|
+
if video.format not in BEDROCK_SUPPORTED_VIDEO_FORMATS:
|
|
314
|
+
raise ValueError(
|
|
315
|
+
f"Unsupported video format: {video.format}. "
|
|
316
|
+
f"Supported formats: {BEDROCK_SUPPORTED_VIDEO_FORMATS}"
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
formatted_message["content"].append(
|
|
320
|
+
{
|
|
321
|
+
"video": {
|
|
322
|
+
"format": video.format,
|
|
323
|
+
"source": {
|
|
324
|
+
"bytes": video.content,
|
|
325
|
+
},
|
|
326
|
+
}
|
|
149
327
|
}
|
|
150
328
|
)
|
|
151
329
|
|
|
152
|
-
|
|
330
|
+
if message.files:
|
|
331
|
+
for file in message.files:
|
|
332
|
+
if not file.content:
|
|
333
|
+
raise ValueError("File content is required for AWS Bedrock document input.")
|
|
334
|
+
if not file.format:
|
|
335
|
+
raise ValueError("File format is required for AWS Bedrock document input.")
|
|
336
|
+
if not file.name:
|
|
337
|
+
raise ValueError("File name is required for AWS Bedrock document input.")
|
|
338
|
+
|
|
339
|
+
if file.format not in BEDROCK_SUPPORTED_FILE_FORMATS:
|
|
340
|
+
raise ValueError(
|
|
341
|
+
f"Unsupported file format: {file.format}. "
|
|
342
|
+
f"Supported formats: {BEDROCK_SUPPORTED_FILE_FORMATS}"
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
formatted_message["content"].append(
|
|
346
|
+
{
|
|
347
|
+
"document": {
|
|
348
|
+
"format": file.format,
|
|
349
|
+
"name": file.name,
|
|
350
|
+
"source": {
|
|
351
|
+
"bytes": file.content,
|
|
352
|
+
},
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
)
|
|
153
356
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
357
|
+
formatted_messages.append(formatted_message)
|
|
358
|
+
# TODO: Add caching: https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-call.html
|
|
359
|
+
return formatted_messages, system_message
|
|
360
|
+
|
|
361
|
+
def count_tokens(
|
|
362
|
+
self,
|
|
363
|
+
messages: List[Message],
|
|
364
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
365
|
+
output_schema: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
366
|
+
) -> int:
|
|
367
|
+
try:
|
|
368
|
+
formatted_messages, system_message = self._format_messages(messages, compress_tool_results=True)
|
|
369
|
+
converse_input: Dict[str, Any] = {"messages": formatted_messages}
|
|
370
|
+
if system_message:
|
|
371
|
+
converse_input["system"] = system_message
|
|
372
|
+
|
|
373
|
+
response = self.get_client().count_tokens(modelId=self.id, input={"converse": converse_input})
|
|
374
|
+
tokens = response.get("inputTokens", 0)
|
|
375
|
+
|
|
376
|
+
# Count tool tokens
|
|
377
|
+
if tools:
|
|
378
|
+
from agno.utils.tokens import count_tool_tokens
|
|
379
|
+
|
|
380
|
+
tokens += count_tool_tokens(tools, self.id)
|
|
381
|
+
|
|
382
|
+
# Count schema tokens
|
|
383
|
+
tokens += count_schema_tokens(output_schema, self.id)
|
|
384
|
+
|
|
385
|
+
return tokens
|
|
386
|
+
except Exception as e:
|
|
387
|
+
log_warning(f"Failed to count tokens via Bedrock API: {e}")
|
|
388
|
+
return super().count_tokens(messages, tools, output_schema)
|
|
389
|
+
|
|
390
|
+
async def acount_tokens(
|
|
391
|
+
self,
|
|
392
|
+
messages: List[Message],
|
|
393
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
394
|
+
output_schema: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
395
|
+
) -> int:
|
|
396
|
+
try:
|
|
397
|
+
formatted_messages, system_message = self._format_messages(messages, compress_tool_results=True)
|
|
398
|
+
converse_input: Dict[str, Any] = {"messages": formatted_messages}
|
|
399
|
+
if system_message:
|
|
400
|
+
converse_input["system"] = system_message
|
|
401
|
+
|
|
402
|
+
async with self.get_async_client() as client:
|
|
403
|
+
response = await client.count_tokens(modelId=self.id, input={"converse": converse_input})
|
|
404
|
+
tokens = response.get("inputTokens", 0)
|
|
405
|
+
|
|
406
|
+
# Count tool tokens
|
|
407
|
+
if tools:
|
|
408
|
+
from agno.utils.tokens import count_tool_tokens
|
|
409
|
+
|
|
410
|
+
tokens += count_tool_tokens(tools, self.id)
|
|
411
|
+
|
|
412
|
+
# Count schema tokens
|
|
413
|
+
tokens += count_schema_tokens(output_schema, self.id)
|
|
414
|
+
|
|
415
|
+
return tokens
|
|
416
|
+
except Exception as e:
|
|
417
|
+
log_warning(f"Failed to count tokens via Bedrock API: {e}")
|
|
418
|
+
return await super().acount_tokens(messages, tools, output_schema)
|
|
419
|
+
|
|
420
|
+
def invoke(
|
|
421
|
+
self,
|
|
422
|
+
messages: List[Message],
|
|
423
|
+
assistant_message: Message,
|
|
424
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
425
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
426
|
+
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
427
|
+
run_response: Optional[RunOutput] = None,
|
|
428
|
+
compress_tool_results: bool = False,
|
|
429
|
+
) -> ModelResponse:
|
|
430
|
+
"""
|
|
431
|
+
Invoke the Bedrock API.
|
|
157
432
|
"""
|
|
158
|
-
|
|
433
|
+
try:
|
|
434
|
+
formatted_messages, system_message = self._format_messages(messages, compress_tool_results)
|
|
159
435
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
model_response (ModelResponse): The model response.
|
|
436
|
+
tool_config = None
|
|
437
|
+
if tools:
|
|
438
|
+
tool_config = {"tools": self._format_tools_for_request(tools)}
|
|
164
439
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
model_response.tool_calls = []
|
|
440
|
+
body = {
|
|
441
|
+
"system": system_message,
|
|
442
|
+
"toolConfig": tool_config,
|
|
443
|
+
"inferenceConfig": self._get_inference_config(),
|
|
444
|
+
}
|
|
445
|
+
body = {k: v for k, v in body.items() if v is not None}
|
|
172
446
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
function_calls_to_run: List[Any] = []
|
|
177
|
-
function_call_results: List[Message] = []
|
|
178
|
-
for tool_call in assistant_message.tool_calls:
|
|
179
|
-
_tool_call_id = tool_call.get("id")
|
|
180
|
-
_function_call = get_function_call_for_tool_call(tool_call, self._functions)
|
|
181
|
-
if _function_call is None:
|
|
182
|
-
messages.append(
|
|
183
|
-
Message(
|
|
184
|
-
role="tool",
|
|
185
|
-
tool_call_id=_tool_call_id,
|
|
186
|
-
content="Could not find function to call.",
|
|
187
|
-
)
|
|
188
|
-
)
|
|
189
|
-
continue
|
|
190
|
-
if _function_call.error is not None:
|
|
191
|
-
messages.append(
|
|
192
|
-
Message(
|
|
193
|
-
role="tool",
|
|
194
|
-
tool_call_id=_tool_call_id,
|
|
195
|
-
content=_function_call.error,
|
|
196
|
-
)
|
|
197
|
-
)
|
|
198
|
-
continue
|
|
199
|
-
function_calls_to_run.append(_function_call)
|
|
200
|
-
|
|
201
|
-
if self.show_tool_calls:
|
|
202
|
-
model_response.content += "\nRunning:"
|
|
203
|
-
for _f in function_calls_to_run:
|
|
204
|
-
model_response.content += f"\n - {_f.get_call_str()}"
|
|
205
|
-
model_response.content += "\n\n"
|
|
206
|
-
|
|
207
|
-
for function_call_response in self.run_function_calls(
|
|
208
|
-
function_calls=function_calls_to_run, function_call_results=function_call_results, tool_role=tool_role
|
|
209
|
-
):
|
|
210
|
-
if (
|
|
211
|
-
function_call_response.event == ModelResponseEvent.tool_call_completed.value
|
|
212
|
-
and function_call_response.tool_calls is not None
|
|
213
|
-
):
|
|
214
|
-
model_response.tool_calls.extend(function_call_response.tool_calls)
|
|
215
|
-
|
|
216
|
-
if len(function_call_results) > 0:
|
|
217
|
-
fc_responses: List = []
|
|
218
|
-
|
|
219
|
-
for _fc_message_index, _fc_message in enumerate(function_call_results):
|
|
220
|
-
tool_result = {
|
|
221
|
-
"toolUseId": tool_ids[_fc_message_index],
|
|
222
|
-
"content": [{"json": json.dumps(_fc_message.content)}],
|
|
223
|
-
}
|
|
224
|
-
tool_result_message = {"role": "user", "content": json.dumps([{"toolResult": tool_result}])}
|
|
225
|
-
fc_responses.append(tool_result_message)
|
|
447
|
+
if self.request_params:
|
|
448
|
+
log_debug(f"Calling {self.provider} with request parameters: {self.request_params}", log_level=2)
|
|
449
|
+
body.update(**self.request_params)
|
|
226
450
|
|
|
227
|
-
|
|
228
|
-
|
|
451
|
+
if run_response and run_response.metrics:
|
|
452
|
+
run_response.metrics.set_time_to_first_token()
|
|
453
|
+
|
|
454
|
+
assistant_message.metrics.start_timer()
|
|
455
|
+
response = self.get_client().converse(modelId=self.id, messages=formatted_messages, **body)
|
|
456
|
+
assistant_message.metrics.stop_timer()
|
|
457
|
+
|
|
458
|
+
model_response = self._parse_provider_response(response, response_format=response_format)
|
|
229
459
|
|
|
230
460
|
return model_response
|
|
231
|
-
return None
|
|
232
461
|
|
|
233
|
-
|
|
462
|
+
except ClientError as e:
|
|
463
|
+
log_error(f"Unexpected error calling Bedrock API: {str(e)}")
|
|
464
|
+
raise ModelProviderError(message=str(e.response), model_name=self.name, model_id=self.id) from e
|
|
465
|
+
except Exception as e:
|
|
466
|
+
log_error(f"Unexpected error calling Bedrock API: {str(e)}")
|
|
467
|
+
raise ModelProviderError(message=str(e), model_name=self.name, model_id=self.id) from e
|
|
468
|
+
|
|
469
|
+
def invoke_stream(
|
|
470
|
+
self,
|
|
471
|
+
messages: List[Message],
|
|
472
|
+
assistant_message: Message,
|
|
473
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
474
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
475
|
+
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
476
|
+
run_response: Optional[RunOutput] = None,
|
|
477
|
+
compress_tool_results: bool = False,
|
|
478
|
+
) -> Iterator[ModelResponse]:
|
|
234
479
|
"""
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
Args:
|
|
238
|
-
assistant_message: The assistant's message object where individual metrics are stored.
|
|
239
|
-
parsed_response: The parsed response containing usage metrics.
|
|
240
|
-
response_timer: Timer object that has the elapsed time of the response.
|
|
480
|
+
Invoke the Bedrock API with streaming.
|
|
241
481
|
"""
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
482
|
+
try:
|
|
483
|
+
formatted_messages, system_message = self._format_messages(messages, compress_tool_results)
|
|
484
|
+
|
|
485
|
+
tool_config = None
|
|
486
|
+
if tools:
|
|
487
|
+
tool_config = {"tools": self._format_tools_for_request(tools)}
|
|
488
|
+
|
|
489
|
+
body = {
|
|
490
|
+
"system": system_message,
|
|
491
|
+
"toolConfig": tool_config,
|
|
492
|
+
"inferenceConfig": self._get_inference_config(),
|
|
493
|
+
}
|
|
494
|
+
body = {k: v for k, v in body.items() if v is not None}
|
|
495
|
+
|
|
496
|
+
if self.request_params:
|
|
497
|
+
body.update(**self.request_params)
|
|
498
|
+
|
|
499
|
+
if run_response and run_response.metrics:
|
|
500
|
+
run_response.metrics.set_time_to_first_token()
|
|
501
|
+
|
|
502
|
+
assistant_message.metrics.start_timer()
|
|
503
|
+
|
|
504
|
+
# Track current tool being built across chunks
|
|
505
|
+
current_tool: Dict[str, Any] = {}
|
|
506
|
+
|
|
507
|
+
for chunk in self.get_client().converse_stream(modelId=self.id, messages=formatted_messages, **body)[
|
|
508
|
+
"stream"
|
|
509
|
+
]:
|
|
510
|
+
model_response, current_tool = self._parse_provider_response_delta(chunk, current_tool)
|
|
511
|
+
yield model_response
|
|
512
|
+
|
|
513
|
+
assistant_message.metrics.stop_timer()
|
|
514
|
+
|
|
515
|
+
except ClientError as e:
|
|
516
|
+
log_error(f"Unexpected error calling Bedrock API: {str(e)}")
|
|
517
|
+
raise ModelProviderError(message=str(e.response), model_name=self.name, model_id=self.id) from e
|
|
518
|
+
except Exception as e:
|
|
519
|
+
log_error(f"Unexpected error calling Bedrock API: {str(e)}")
|
|
520
|
+
raise ModelProviderError(message=str(e), model_name=self.name, model_id=self.id) from e
|
|
521
|
+
|
|
522
|
+
async def ainvoke(
|
|
523
|
+
self,
|
|
524
|
+
messages: List[Message],
|
|
525
|
+
assistant_message: Message,
|
|
526
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
527
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
528
|
+
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
529
|
+
run_response: Optional[RunOutput] = None,
|
|
530
|
+
compress_tool_results: bool = False,
|
|
531
|
+
) -> ModelResponse:
|
|
267
532
|
"""
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
Args:
|
|
271
|
-
messages (List[Message]): The messages to include in the request.
|
|
272
|
-
|
|
273
|
-
Returns:
|
|
274
|
-
ModelResponse: The response from the Bedrock API.
|
|
533
|
+
Async invoke the Bedrock API.
|
|
275
534
|
"""
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
model_response = ModelResponse()
|
|
535
|
+
try:
|
|
536
|
+
formatted_messages, system_message = self._format_messages(messages, compress_tool_results)
|
|
279
537
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
body = self.get_request_body(messages)
|
|
284
|
-
response: Dict[str, Any] = self.invoke(body=body)
|
|
285
|
-
response_timer.stop()
|
|
286
|
-
|
|
287
|
-
# Parse response
|
|
288
|
-
parsed_response = self.parse_response_message(response)
|
|
289
|
-
logger.debug(f"Parsed response: {parsed_response}")
|
|
290
|
-
stop_reason = parsed_response["stop_reason"]
|
|
291
|
-
|
|
292
|
-
# Create assistant message
|
|
293
|
-
assistant_message = self.create_assistant_message(parsed_response)
|
|
294
|
-
|
|
295
|
-
# Update usage metrics using the new function
|
|
296
|
-
self._update_metrics(assistant_message, parsed_response, response_timer)
|
|
297
|
-
|
|
298
|
-
# Add assistant message to messages
|
|
299
|
-
messages.append(assistant_message)
|
|
300
|
-
assistant_message.log()
|
|
301
|
-
|
|
302
|
-
# Create tool calls if needed
|
|
303
|
-
tool_ids, tool_calls = self._create_tool_calls(stop_reason, parsed_response)
|
|
304
|
-
|
|
305
|
-
# Handle tool calls
|
|
306
|
-
if stop_reason == "tool_use" and tool_calls:
|
|
307
|
-
assistant_message.content = parsed_response["tool_requests"][0]["text"]
|
|
308
|
-
assistant_message.tool_calls = tool_calls
|
|
309
|
-
|
|
310
|
-
# Run tool calls
|
|
311
|
-
if self._handle_tool_calls(assistant_message, messages, model_response, tool_ids):
|
|
312
|
-
response_after_tool_calls = self.response(messages=messages)
|
|
313
|
-
if response_after_tool_calls.content is not None:
|
|
314
|
-
if model_response.content is None:
|
|
315
|
-
model_response.content = ""
|
|
316
|
-
model_response.content += response_after_tool_calls.content
|
|
317
|
-
return model_response
|
|
538
|
+
tool_config = None
|
|
539
|
+
if tools:
|
|
540
|
+
tool_config = {"tools": self._format_tools_for_request(tools)}
|
|
318
541
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
542
|
+
body = {
|
|
543
|
+
"system": system_message,
|
|
544
|
+
"toolConfig": tool_config,
|
|
545
|
+
"inferenceConfig": self._get_inference_config(),
|
|
546
|
+
}
|
|
547
|
+
body = {k: v for k, v in body.items() if v is not None}
|
|
322
548
|
|
|
323
|
-
|
|
324
|
-
|
|
549
|
+
if self.request_params:
|
|
550
|
+
log_debug(f"Calling {self.provider} with request parameters: {self.request_params}", log_level=2)
|
|
551
|
+
body.update(**self.request_params)
|
|
325
552
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
Handle tool calls in the assistant message.
|
|
553
|
+
if run_response and run_response.metrics:
|
|
554
|
+
run_response.metrics.set_time_to_first_token()
|
|
329
555
|
|
|
330
|
-
|
|
331
|
-
assistant_message (Message): The assistant message.
|
|
332
|
-
messages (List[Message]): The list of messages.
|
|
333
|
-
tool_ids (List[str]): The list of tool IDs.
|
|
334
|
-
"""
|
|
335
|
-
tool_role: str = "tool"
|
|
336
|
-
function_calls_to_run: List[Any] = []
|
|
337
|
-
function_call_results: List[Message] = []
|
|
338
|
-
for tool_call in assistant_message.tool_calls or []:
|
|
339
|
-
_tool_call_id = tool_call.get("id")
|
|
340
|
-
_function_call = get_function_call_for_tool_call(tool_call, self._functions)
|
|
341
|
-
if _function_call is None:
|
|
342
|
-
messages.append(
|
|
343
|
-
Message(
|
|
344
|
-
role="tool",
|
|
345
|
-
tool_call_id=_tool_call_id,
|
|
346
|
-
content="Could not find function to call.",
|
|
347
|
-
)
|
|
348
|
-
)
|
|
349
|
-
continue
|
|
350
|
-
if _function_call.error is not None:
|
|
351
|
-
messages.append(
|
|
352
|
-
Message(
|
|
353
|
-
role="tool",
|
|
354
|
-
tool_call_id=_tool_call_id,
|
|
355
|
-
content=_function_call.error,
|
|
356
|
-
)
|
|
357
|
-
)
|
|
358
|
-
continue
|
|
359
|
-
function_calls_to_run.append(_function_call)
|
|
360
|
-
|
|
361
|
-
if self.show_tool_calls:
|
|
362
|
-
yield ModelResponse(content="\nRunning:")
|
|
363
|
-
for _f in function_calls_to_run:
|
|
364
|
-
yield ModelResponse(content=f"\n - {_f.get_call_str()}")
|
|
365
|
-
yield ModelResponse(content="\n\n")
|
|
366
|
-
|
|
367
|
-
for _ in self.run_function_calls(
|
|
368
|
-
function_calls=function_calls_to_run, function_call_results=function_call_results, tool_role=tool_role
|
|
369
|
-
):
|
|
370
|
-
pass
|
|
556
|
+
assistant_message.metrics.start_timer()
|
|
371
557
|
|
|
372
|
-
|
|
373
|
-
|
|
558
|
+
async with self.get_async_client() as client:
|
|
559
|
+
response = await client.converse(modelId=self.id, messages=formatted_messages, **body)
|
|
374
560
|
|
|
375
|
-
|
|
376
|
-
tool_result = {
|
|
377
|
-
"toolUseId": tool_ids[_fc_message_index],
|
|
378
|
-
"content": [{"json": json.dumps(_fc_message.content)}],
|
|
379
|
-
}
|
|
380
|
-
tool_result_message = {"role": "user", "content": json.dumps([{"toolResult": tool_result}])}
|
|
381
|
-
fc_responses.append(tool_result_message)
|
|
561
|
+
assistant_message.metrics.stop_timer()
|
|
382
562
|
|
|
383
|
-
|
|
384
|
-
messages.append(Message(role="user", content=json.dumps(fc_responses)))
|
|
563
|
+
model_response = self._parse_provider_response(response, response_format=response_format)
|
|
385
564
|
|
|
386
|
-
|
|
565
|
+
return model_response
|
|
566
|
+
|
|
567
|
+
except ClientError as e:
|
|
568
|
+
log_error(f"Unexpected error calling Bedrock API: {str(e)}")
|
|
569
|
+
raise ModelProviderError(message=str(e.response), model_name=self.name, model_id=self.id) from e
|
|
570
|
+
except Exception as e:
|
|
571
|
+
log_error(f"Unexpected error calling Bedrock API: {str(e)}")
|
|
572
|
+
raise ModelProviderError(message=str(e), model_name=self.name, model_id=self.id) from e
|
|
573
|
+
|
|
574
|
+
async def ainvoke_stream(
|
|
575
|
+
self,
|
|
576
|
+
messages: List[Message],
|
|
577
|
+
assistant_message: Message,
|
|
578
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
579
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
580
|
+
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
581
|
+
run_response: Optional[RunOutput] = None,
|
|
582
|
+
compress_tool_results: bool = False,
|
|
583
|
+
) -> AsyncIterator[ModelResponse]:
|
|
584
|
+
"""
|
|
585
|
+
Async invoke the Bedrock API with streaming.
|
|
387
586
|
"""
|
|
388
|
-
|
|
587
|
+
try:
|
|
588
|
+
formatted_messages, system_message = self._format_messages(messages, compress_tool_results)
|
|
589
|
+
|
|
590
|
+
tool_config = None
|
|
591
|
+
if tools:
|
|
592
|
+
tool_config = {"tools": self._format_tools_for_request(tools)}
|
|
593
|
+
|
|
594
|
+
body = {
|
|
595
|
+
"system": system_message,
|
|
596
|
+
"toolConfig": tool_config,
|
|
597
|
+
"inferenceConfig": self._get_inference_config(),
|
|
598
|
+
}
|
|
599
|
+
body = {k: v for k, v in body.items() if v is not None}
|
|
600
|
+
|
|
601
|
+
if self.request_params:
|
|
602
|
+
body.update(**self.request_params)
|
|
603
|
+
|
|
604
|
+
if run_response and run_response.metrics:
|
|
605
|
+
run_response.metrics.set_time_to_first_token()
|
|
606
|
+
|
|
607
|
+
assistant_message.metrics.start_timer()
|
|
608
|
+
|
|
609
|
+
# Track current tool being built across chunks
|
|
610
|
+
current_tool: Dict[str, Any] = {}
|
|
611
|
+
|
|
612
|
+
async with self.get_async_client() as client:
|
|
613
|
+
response = await client.converse_stream(modelId=self.id, messages=formatted_messages, **body)
|
|
614
|
+
async for chunk in response["stream"]:
|
|
615
|
+
model_response, current_tool = self._parse_provider_response_delta(chunk, current_tool)
|
|
616
|
+
yield model_response
|
|
617
|
+
|
|
618
|
+
assistant_message.metrics.stop_timer()
|
|
619
|
+
|
|
620
|
+
except ClientError as e:
|
|
621
|
+
log_error(f"Unexpected error calling Bedrock API: {str(e)}")
|
|
622
|
+
raise ModelProviderError(message=str(e.response), model_name=self.name, model_id=self.id) from e
|
|
623
|
+
except Exception as e:
|
|
624
|
+
log_error(f"Unexpected error calling Bedrock API: {str(e)}")
|
|
625
|
+
raise ModelProviderError(message=str(e), model_name=self.name, model_id=self.id) from e
|
|
626
|
+
|
|
627
|
+
# Overwrite the default from the base model
|
|
628
|
+
def format_function_call_results(
|
|
629
|
+
self,
|
|
630
|
+
messages: List[Message],
|
|
631
|
+
function_call_results: List[Message],
|
|
632
|
+
compress_tool_results: bool = False,
|
|
633
|
+
**kwargs,
|
|
634
|
+
) -> None:
|
|
635
|
+
"""
|
|
636
|
+
Handle the results of function calls for Bedrock.
|
|
637
|
+
Uses compressed_content if compress_tool_results is True.
|
|
389
638
|
|
|
390
639
|
Args:
|
|
391
|
-
|
|
392
|
-
|
|
640
|
+
messages (List[Message]): The list of conversation messages.
|
|
641
|
+
function_call_results (List[Message]): The results of the function calls.
|
|
642
|
+
compress_tool_results: Whether to compress tool results.
|
|
643
|
+
**kwargs: Additional arguments including tool_ids.
|
|
393
644
|
"""
|
|
394
|
-
assistant_message.metrics["time"] = stream_data.response_timer.elapsed
|
|
395
|
-
if stream_data.time_to_first_token is not None:
|
|
396
|
-
assistant_message.metrics["time_to_first_token"] = stream_data.time_to_first_token
|
|
397
|
-
|
|
398
|
-
if "response_times" not in self.metrics:
|
|
399
|
-
self.metrics["response_times"] = []
|
|
400
|
-
self.metrics["response_times"].append(stream_data.response_timer.elapsed)
|
|
401
|
-
if stream_data.time_to_first_token is not None:
|
|
402
|
-
if "time_to_first_token" not in self.metrics:
|
|
403
|
-
self.metrics["time_to_first_token"] = []
|
|
404
|
-
self.metrics["time_to_first_token"].append(stream_data.time_to_first_token)
|
|
405
|
-
if stream_data.completion_tokens > 0:
|
|
406
|
-
if "tokens_per_second" not in self.metrics:
|
|
407
|
-
self.metrics["tokens_per_second"] = []
|
|
408
|
-
self.metrics["tokens_per_second"].append(
|
|
409
|
-
f"{stream_data.completion_tokens / stream_data.response_timer.elapsed:.4f}"
|
|
410
|
-
)
|
|
411
645
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
self.metrics["prompt_tokens"] = self.metrics.get("prompt_tokens", 0) + stream_data.response_prompt_tokens
|
|
415
|
-
self.metrics["input_tokens"] = self.metrics.get("input_tokens", 0) + stream_data.response_prompt_tokens
|
|
646
|
+
if function_call_results:
|
|
647
|
+
tool_ids = kwargs.get("tool_ids", [])
|
|
416
648
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
self.metrics["output_tokens"] = self.metrics.get("output_tokens", 0) + stream_data.response_completion_tokens
|
|
649
|
+
for _fc_message_index, _fc_message in enumerate(function_call_results):
|
|
650
|
+
# Use tool_call_id from message if tool_ids list is insufficient
|
|
651
|
+
tool_id = tool_ids[_fc_message_index] if _fc_message_index < len(tool_ids) else _fc_message.tool_call_id
|
|
652
|
+
if not _fc_message.tool_call_id:
|
|
653
|
+
_fc_message.tool_call_id = tool_id
|
|
423
654
|
|
|
424
|
-
|
|
425
|
-
|
|
655
|
+
# Append as standard role="tool" message
|
|
656
|
+
messages.append(_fc_message)
|
|
426
657
|
|
|
427
|
-
def
|
|
658
|
+
def _parse_provider_response(self, response: Dict[str, Any], **kwargs) -> ModelResponse:
|
|
428
659
|
"""
|
|
429
|
-
|
|
660
|
+
Parse the provider response.
|
|
430
661
|
|
|
431
662
|
Args:
|
|
432
|
-
|
|
663
|
+
response (Dict[str, Any]): The response from the provider.
|
|
433
664
|
|
|
434
665
|
Returns:
|
|
435
|
-
|
|
666
|
+
ModelResponse: The parsed response.
|
|
436
667
|
"""
|
|
437
|
-
|
|
438
|
-
self._log_messages(messages)
|
|
439
|
-
|
|
440
|
-
stream_data: StreamData = StreamData()
|
|
441
|
-
stream_data.response_timer.start()
|
|
442
|
-
|
|
443
|
-
tool_use: Dict[str, Any] = {}
|
|
444
|
-
tool_ids: List[str] = []
|
|
445
|
-
tool_calls: List[Dict[str, Any]] = []
|
|
446
|
-
stop_reason: Optional[str] = None
|
|
447
|
-
content: List[Dict[str, Any]] = []
|
|
448
|
-
|
|
449
|
-
request_body = self.get_request_body(messages)
|
|
450
|
-
response = self.invoke_stream(body=request_body)
|
|
451
|
-
|
|
452
|
-
# Process the streaming response
|
|
453
|
-
for chunk in response:
|
|
454
|
-
if "contentBlockStart" in chunk:
|
|
455
|
-
tool = chunk["contentBlockStart"]["start"].get("toolUse")
|
|
456
|
-
if tool:
|
|
457
|
-
tool_use["toolUseId"] = tool["toolUseId"]
|
|
458
|
-
tool_use["name"] = tool["name"]
|
|
459
|
-
|
|
460
|
-
elif "contentBlockDelta" in chunk:
|
|
461
|
-
delta = chunk["contentBlockDelta"]["delta"]
|
|
462
|
-
if "toolUse" in delta:
|
|
463
|
-
if "input" not in tool_use:
|
|
464
|
-
tool_use["input"] = ""
|
|
465
|
-
tool_use["input"] += delta["toolUse"]["input"]
|
|
466
|
-
elif "text" in delta:
|
|
467
|
-
stream_data.response_content += delta["text"]
|
|
468
|
-
stream_data.completion_tokens += 1
|
|
469
|
-
if stream_data.completion_tokens == 1:
|
|
470
|
-
stream_data.time_to_first_token = stream_data.response_timer.elapsed
|
|
471
|
-
logger.debug(f"Time to first token: {stream_data.time_to_first_token:.4f}s")
|
|
472
|
-
yield ModelResponse(content=delta["text"]) # Yield text content as it's received
|
|
473
|
-
|
|
474
|
-
elif "contentBlockStop" in chunk:
|
|
475
|
-
if "input" in tool_use:
|
|
476
|
-
# Finish collecting tool use input
|
|
477
|
-
try:
|
|
478
|
-
tool_use["input"] = json.loads(tool_use["input"])
|
|
479
|
-
except json.JSONDecodeError as e:
|
|
480
|
-
logger.error(f"Failed to parse tool input as JSON: {e}")
|
|
481
|
-
tool_use["input"] = {}
|
|
482
|
-
content.append({"toolUse": tool_use})
|
|
483
|
-
tool_ids.append(tool_use["toolUseId"])
|
|
484
|
-
# Prepare the tool call
|
|
485
|
-
tool_call = {
|
|
486
|
-
"type": "function",
|
|
487
|
-
"function": {
|
|
488
|
-
"name": tool_use["name"],
|
|
489
|
-
"arguments": json.dumps(tool_use["input"]),
|
|
490
|
-
},
|
|
491
|
-
}
|
|
492
|
-
tool_calls.append(tool_call)
|
|
493
|
-
tool_use = {}
|
|
494
|
-
else:
|
|
495
|
-
# Finish collecting text content
|
|
496
|
-
content.append({"text": stream_data.response_content})
|
|
668
|
+
model_response = ModelResponse()
|
|
497
669
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
670
|
+
if "output" in response and "message" in response["output"]:
|
|
671
|
+
message = response["output"]["message"]
|
|
672
|
+
# Set the role of the message
|
|
673
|
+
model_response.role = message["role"]
|
|
501
674
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
if "usage" in metadata:
|
|
505
|
-
stream_data.response_prompt_tokens = metadata["usage"]["inputTokens"]
|
|
506
|
-
stream_data.response_total_tokens = metadata["usage"]["totalTokens"]
|
|
507
|
-
stream_data.completion_tokens = metadata["usage"]["outputTokens"]
|
|
675
|
+
# Get the content of the message
|
|
676
|
+
content = message["content"]
|
|
508
677
|
|
|
509
|
-
|
|
678
|
+
# Tools
|
|
679
|
+
if "stopReason" in response and response["stopReason"] == "tool_use":
|
|
680
|
+
model_response.tool_calls = []
|
|
681
|
+
model_response.extra = model_response.extra or {}
|
|
682
|
+
model_response.extra["tool_ids"] = []
|
|
683
|
+
for tool in content:
|
|
684
|
+
if "toolUse" in tool:
|
|
685
|
+
model_response.extra["tool_ids"].append(tool["toolUse"]["toolUseId"])
|
|
686
|
+
model_response.tool_calls.append(
|
|
687
|
+
{
|
|
688
|
+
"id": tool["toolUse"]["toolUseId"],
|
|
689
|
+
"type": "function",
|
|
690
|
+
"function": {
|
|
691
|
+
"name": tool["toolUse"]["name"],
|
|
692
|
+
"arguments": json.dumps(tool["toolUse"]["input"]),
|
|
693
|
+
},
|
|
694
|
+
}
|
|
695
|
+
)
|
|
510
696
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
697
|
+
# Extract text content if it's a list of dictionaries
|
|
698
|
+
if isinstance(content, list) and content and isinstance(content[0], dict):
|
|
699
|
+
content = [item.get("text", "") for item in content if "text" in item]
|
|
700
|
+
content = "\n".join(content) # Join multiple text items if present
|
|
514
701
|
|
|
515
|
-
|
|
516
|
-
logger.debug(
|
|
517
|
-
f"Time per output token: {stream_data.response_timer.elapsed / stream_data.completion_tokens:.4f}s"
|
|
518
|
-
)
|
|
519
|
-
logger.debug(
|
|
520
|
-
f"Throughput: {stream_data.completion_tokens / stream_data.response_timer.elapsed:.4f} tokens/s"
|
|
521
|
-
)
|
|
702
|
+
model_response.content = content
|
|
522
703
|
|
|
523
|
-
|
|
524
|
-
|
|
704
|
+
if "usage" in response:
|
|
705
|
+
model_response.response_usage = self._get_metrics(response["usage"])
|
|
525
706
|
|
|
526
|
-
|
|
527
|
-
messages.append(assistant_message)
|
|
528
|
-
assistant_message.log()
|
|
707
|
+
return model_response
|
|
529
708
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
709
|
+
def _parse_provider_response_delta(
|
|
710
|
+
self, response_delta: Dict[str, Any], current_tool: Dict[str, Any]
|
|
711
|
+
) -> Tuple[ModelResponse, Dict[str, Any]]:
|
|
712
|
+
"""Parse the provider response delta for streaming.
|
|
534
713
|
|
|
535
|
-
|
|
714
|
+
Args:
|
|
715
|
+
response_delta: The streaming response delta from AWS Bedrock
|
|
716
|
+
current_tool: The current tool being built across chunks
|
|
536
717
|
|
|
537
|
-
|
|
538
|
-
|
|
718
|
+
Returns:
|
|
719
|
+
Tuple[ModelResponse, Dict[str, Any]]: The parsed model response delta and updated current_tool
|
|
720
|
+
"""
|
|
721
|
+
model_response = ModelResponse(role="assistant")
|
|
722
|
+
|
|
723
|
+
# Handle contentBlockStart - tool use start
|
|
724
|
+
if "contentBlockStart" in response_delta:
|
|
725
|
+
start = response_delta["contentBlockStart"]["start"]
|
|
726
|
+
if "toolUse" in start:
|
|
727
|
+
# Start a new tool
|
|
728
|
+
tool_use_data = start["toolUse"]
|
|
729
|
+
current_tool = {
|
|
730
|
+
"id": tool_use_data.get("toolUseId", ""),
|
|
731
|
+
"type": "function",
|
|
732
|
+
"function": {
|
|
733
|
+
"name": tool_use_data.get("name", ""),
|
|
734
|
+
"arguments": "", # Will be filled in subsequent deltas
|
|
735
|
+
},
|
|
736
|
+
}
|
|
539
737
|
|
|
540
|
-
|
|
541
|
-
|
|
738
|
+
# Handle contentBlockDelta - text content or tool input
|
|
739
|
+
elif "contentBlockDelta" in response_delta:
|
|
740
|
+
delta = response_delta["contentBlockDelta"]["delta"]
|
|
741
|
+
if "text" in delta:
|
|
742
|
+
model_response.content = delta["text"]
|
|
743
|
+
elif "toolUse" in delta and current_tool:
|
|
744
|
+
# Accumulate tool input
|
|
745
|
+
tool_input = delta["toolUse"].get("input", "")
|
|
746
|
+
if tool_input:
|
|
747
|
+
current_tool["function"]["arguments"] += tool_input
|
|
748
|
+
|
|
749
|
+
# Handle contentBlockStop - tool use complete
|
|
750
|
+
elif "contentBlockStop" in response_delta and current_tool:
|
|
751
|
+
# Tool is complete, add it to model response
|
|
752
|
+
model_response.tool_calls = [current_tool]
|
|
753
|
+
# Track tool_id in extra for format_function_call_results
|
|
754
|
+
model_response.extra = {"tool_ids": [current_tool["id"]]}
|
|
755
|
+
# Reset current_tool for next tool
|
|
756
|
+
current_tool = {}
|
|
757
|
+
|
|
758
|
+
# Handle metadata/usage information
|
|
759
|
+
elif "metadata" in response_delta or "messageStop" in response_delta:
|
|
760
|
+
body = response_delta.get("metadata") or response_delta.get("messageStop") or {}
|
|
761
|
+
if "usage" in body:
|
|
762
|
+
model_response.response_usage = self._get_metrics(body["usage"])
|
|
763
|
+
|
|
764
|
+
return model_response, current_tool
|
|
765
|
+
|
|
766
|
+
def _get_metrics(self, response_usage: Dict[str, Any]) -> Metrics:
|
|
767
|
+
"""
|
|
768
|
+
Parse the given AWS Bedrock usage into an Agno Metrics object.
|
|
769
|
+
|
|
770
|
+
Args:
|
|
771
|
+
response_usage: Usage data from AWS Bedrock
|
|
772
|
+
|
|
773
|
+
Returns:
|
|
774
|
+
Metrics: Parsed metrics data
|
|
775
|
+
"""
|
|
776
|
+
metrics = Metrics()
|
|
542
777
|
|
|
543
|
-
|
|
544
|
-
|
|
778
|
+
metrics.input_tokens = response_usage.get("inputTokens", 0) or 0
|
|
779
|
+
metrics.output_tokens = response_usage.get("outputTokens", 0) or 0
|
|
780
|
+
metrics.total_tokens = metrics.input_tokens + metrics.output_tokens
|
|
545
781
|
|
|
546
|
-
|
|
547
|
-
raise NotImplementedError(f"Async not supported on {self.name}.")
|
|
782
|
+
return metrics
|