agno 1.8.1__py3-none-any.whl → 2.0.0__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 +19 -27
- agno/agent/agent.py +3143 -4170
- agno/api/agent.py +11 -67
- agno/api/api.py +5 -46
- agno/api/evals.py +8 -19
- agno/api/os.py +17 -0
- agno/api/routes.py +6 -41
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +5 -21
- agno/api/schemas/evals.py +7 -16
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/team.py +5 -21
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +11 -7
- agno/api/settings.py +53 -0
- agno/api/team.py +11 -66
- 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/db/__init__.py +24 -0
- agno/db/base.py +245 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +1743 -0
- agno/db/dynamo/schemas.py +278 -0
- agno/db/dynamo/utils.py +684 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +1432 -0
- agno/db/firestore/schemas.py +130 -0
- agno/db/firestore/utils.py +278 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1001 -0
- agno/db/gcs_json/utils.py +194 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +882 -0
- agno/db/in_memory/utils.py +172 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1045 -0
- agno/db/json/utils.py +196 -0
- agno/db/migrations/v1_to_v2.py +162 -0
- agno/db/mongo/__init__.py +3 -0
- agno/db/mongo/mongo.py +1416 -0
- agno/db/mongo/schemas.py +77 -0
- agno/db/mongo/utils.py +204 -0
- agno/db/mysql/__init__.py +3 -0
- agno/db/mysql/mysql.py +1719 -0
- agno/db/mysql/schemas.py +124 -0
- agno/db/mysql/utils.py +297 -0
- agno/db/postgres/__init__.py +3 -0
- agno/db/postgres/postgres.py +1710 -0
- agno/db/postgres/schemas.py +124 -0
- agno/db/postgres/utils.py +280 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +1367 -0
- agno/db/redis/schemas.py +109 -0
- agno/db/redis/utils.py +288 -0
- agno/db/schemas/__init__.py +3 -0
- agno/db/schemas/evals.py +33 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +46 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +116 -0
- agno/db/singlestore/singlestore.py +1712 -0
- agno/db/singlestore/utils.py +326 -0
- agno/db/sqlite/__init__.py +3 -0
- agno/db/sqlite/schemas.py +119 -0
- agno/db/sqlite/sqlite.py +1676 -0
- agno/db/sqlite/utils.py +268 -0
- agno/db/utils.py +88 -0
- agno/eval/__init__.py +14 -0
- agno/eval/accuracy.py +154 -48
- agno/eval/performance.py +88 -23
- agno/eval/reliability.py +73 -20
- agno/eval/utils.py +23 -13
- agno/integrations/discord/__init__.py +3 -0
- agno/{app → integrations}/discord/client.py +15 -11
- agno/knowledge/__init__.py +2 -2
- agno/{document → knowledge}/chunking/agentic.py +2 -2
- agno/{document → knowledge}/chunking/document.py +2 -2
- agno/{document → knowledge}/chunking/fixed.py +3 -3
- agno/{document → knowledge}/chunking/markdown.py +2 -2
- agno/{document → knowledge}/chunking/recursive.py +2 -2
- agno/{document → knowledge}/chunking/row.py +2 -2
- agno/knowledge/chunking/semantic.py +59 -0
- agno/knowledge/chunking/strategy.py +121 -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/{embedder → knowledge/embedder}/aws_bedrock.py +127 -1
- agno/{embedder → knowledge/embedder}/azure_openai.py +65 -1
- agno/{embedder → knowledge/embedder}/base.py +6 -0
- agno/{embedder → knowledge/embedder}/cohere.py +72 -1
- agno/{embedder → knowledge/embedder}/fastembed.py +17 -1
- agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
- agno/{embedder → knowledge/embedder}/google.py +74 -1
- agno/{embedder → knowledge/embedder}/huggingface.py +36 -2
- agno/{embedder → knowledge/embedder}/jina.py +48 -2
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +139 -0
- agno/{embedder → knowledge/embedder}/nebius.py +1 -1
- agno/{embedder → knowledge/embedder}/ollama.py +54 -3
- agno/knowledge/embedder/openai.py +223 -0
- agno/{embedder → knowledge/embedder}/sentence_transformer.py +16 -1
- agno/{embedder → knowledge/embedder}/together.py +1 -1
- agno/{embedder → knowledge/embedder}/voyageai.py +49 -1
- agno/knowledge/knowledge.py +1551 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/{document → knowledge}/reader/arxiv_reader.py +32 -4
- agno/knowledge/reader/base.py +88 -0
- agno/{document → knowledge}/reader/csv_reader.py +47 -65
- agno/knowledge/reader/docx_reader.py +83 -0
- agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
- agno/{document → knowledge}/reader/json_reader.py +30 -9
- agno/{document → knowledge}/reader/markdown_reader.py +58 -9
- agno/{document → knowledge}/reader/pdf_reader.py +71 -126
- agno/knowledge/reader/reader_factory.py +268 -0
- agno/knowledge/reader/s3_reader.py +101 -0
- agno/{document → knowledge}/reader/text_reader.py +31 -10
- agno/knowledge/reader/url_reader.py +128 -0
- agno/knowledge/reader/web_search_reader.py +366 -0
- agno/{document → knowledge}/reader/website_reader.py +37 -10
- agno/knowledge/reader/wikipedia_reader.py +59 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/{reranker → knowledge/reranker}/base.py +1 -1
- agno/{reranker → knowledge/reranker}/cohere.py +2 -2
- agno/{reranker → knowledge/reranker}/infinity.py +2 -2
- agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
- agno/knowledge/types.py +30 -0
- agno/knowledge/utils.py +169 -0
- agno/media.py +269 -268
- agno/memory/__init__.py +2 -10
- agno/memory/manager.py +1003 -148
- agno/models/aimlapi/__init__.py +2 -2
- agno/models/aimlapi/aimlapi.py +6 -6
- agno/models/anthropic/claude.py +131 -131
- agno/models/aws/bedrock.py +110 -182
- agno/models/aws/claude.py +64 -18
- agno/models/azure/ai_foundry.py +73 -23
- agno/models/base.py +346 -290
- agno/models/cerebras/cerebras.py +84 -27
- agno/models/cohere/chat.py +106 -98
- agno/models/google/gemini.py +105 -46
- agno/models/groq/groq.py +97 -35
- agno/models/huggingface/huggingface.py +92 -27
- agno/models/ibm/watsonx.py +72 -13
- agno/models/litellm/chat.py +85 -13
- agno/models/message.py +46 -151
- agno/models/meta/llama.py +85 -49
- agno/models/metrics.py +120 -0
- agno/models/mistral/mistral.py +90 -21
- agno/models/ollama/__init__.py +0 -2
- agno/models/ollama/chat.py +85 -47
- agno/models/openai/chat.py +154 -37
- agno/models/openai/responses.py +178 -105
- agno/models/perplexity/perplexity.py +26 -2
- agno/models/portkey/portkey.py +0 -7
- agno/models/response.py +15 -9
- agno/models/utils.py +20 -0
- agno/models/vercel/__init__.py +2 -2
- agno/models/vercel/v0.py +1 -1
- agno/models/vllm/__init__.py +2 -2
- agno/models/vllm/vllm.py +3 -3
- agno/models/xai/xai.py +10 -10
- agno/os/__init__.py +3 -0
- agno/os/app.py +497 -0
- agno/os/auth.py +47 -0
- agno/os/config.py +103 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +31 -0
- agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
- agno/{app → os/interfaces}/agui/utils.py +77 -33
- agno/os/interfaces/base.py +21 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
- agno/os/interfaces/slack/slack.py +32 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
- agno/os/interfaces/whatsapp/whatsapp.py +29 -0
- agno/os/mcp.py +235 -0
- agno/os/router.py +1400 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +393 -0
- agno/os/routers/evals/schemas.py +142 -0
- agno/os/routers/evals/utils.py +161 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +850 -0
- agno/os/routers/knowledge/schemas.py +118 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +410 -0
- agno/os/routers/memory/schemas.py +58 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +178 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +536 -0
- agno/os/schema.py +945 -0
- agno/{app/playground → os}/settings.py +7 -15
- agno/os/utils.py +270 -0
- agno/reasoning/azure_ai_foundry.py +4 -4
- agno/reasoning/deepseek.py +4 -4
- agno/reasoning/default.py +6 -11
- agno/reasoning/groq.py +4 -4
- agno/reasoning/helpers.py +4 -6
- agno/reasoning/ollama.py +4 -4
- agno/reasoning/openai.py +4 -4
- agno/run/agent.py +633 -0
- agno/run/base.py +53 -77
- agno/run/cancel.py +81 -0
- agno/run/team.py +243 -96
- agno/run/workflow.py +550 -12
- agno/session/__init__.py +10 -0
- agno/session/agent.py +244 -0
- agno/session/summary.py +225 -0
- agno/session/team.py +262 -0
- agno/{storage/session/v2 → session}/workflow.py +47 -24
- agno/team/__init__.py +15 -16
- agno/team/team.py +3260 -4824
- agno/tools/agentql.py +14 -5
- agno/tools/airflow.py +9 -4
- agno/tools/api.py +7 -3
- agno/tools/apify.py +2 -46
- agno/tools/arxiv.py +8 -3
- agno/tools/aws_lambda.py +7 -5
- agno/tools/aws_ses.py +7 -1
- agno/tools/baidusearch.py +4 -1
- agno/tools/bitbucket.py +4 -4
- agno/tools/brandfetch.py +14 -11
- agno/tools/bravesearch.py +4 -1
- agno/tools/brightdata.py +43 -23
- agno/tools/browserbase.py +13 -4
- agno/tools/calcom.py +12 -10
- agno/tools/calculator.py +10 -27
- agno/tools/cartesia.py +20 -17
- agno/tools/{clickup_tool.py → clickup.py} +12 -25
- agno/tools/confluence.py +8 -8
- agno/tools/crawl4ai.py +7 -1
- agno/tools/csv_toolkit.py +9 -8
- agno/tools/dalle.py +22 -12
- agno/tools/daytona.py +13 -16
- agno/tools/decorator.py +6 -3
- agno/tools/desi_vocal.py +17 -8
- agno/tools/discord.py +11 -8
- agno/tools/docker.py +30 -42
- agno/tools/duckdb.py +34 -53
- agno/tools/duckduckgo.py +8 -7
- agno/tools/e2b.py +62 -62
- agno/tools/eleven_labs.py +36 -29
- agno/tools/email.py +4 -1
- agno/tools/evm.py +7 -1
- agno/tools/exa.py +19 -14
- agno/tools/fal.py +30 -30
- agno/tools/file.py +9 -8
- agno/tools/financial_datasets.py +25 -44
- agno/tools/firecrawl.py +22 -22
- agno/tools/function.py +127 -18
- agno/tools/giphy.py +23 -11
- agno/tools/github.py +48 -126
- agno/tools/gmail.py +45 -61
- agno/tools/google_bigquery.py +7 -6
- agno/tools/google_maps.py +11 -26
- agno/tools/googlesearch.py +7 -2
- agno/tools/googlesheets.py +21 -17
- agno/tools/hackernews.py +9 -5
- agno/tools/jina.py +5 -4
- agno/tools/jira.py +18 -9
- agno/tools/knowledge.py +31 -32
- agno/tools/linear.py +19 -34
- agno/tools/linkup.py +5 -1
- agno/tools/local_file_system.py +8 -5
- agno/tools/lumalab.py +32 -20
- agno/tools/mcp.py +1 -2
- agno/tools/mem0.py +18 -12
- agno/tools/memori.py +14 -10
- agno/tools/mlx_transcribe.py +3 -2
- agno/tools/models/azure_openai.py +33 -15
- agno/tools/models/gemini.py +59 -32
- agno/tools/models/groq.py +30 -23
- agno/tools/models/nebius.py +28 -12
- agno/tools/models_labs.py +40 -16
- agno/tools/moviepy_video.py +7 -6
- agno/tools/neo4j.py +10 -8
- agno/tools/newspaper.py +7 -2
- agno/tools/newspaper4k.py +8 -3
- agno/tools/openai.py +58 -32
- agno/tools/openbb.py +12 -11
- agno/tools/opencv.py +63 -47
- agno/tools/openweather.py +14 -12
- agno/tools/pandas.py +11 -3
- agno/tools/postgres.py +4 -12
- agno/tools/pubmed.py +4 -1
- agno/tools/python.py +9 -22
- agno/tools/reasoning.py +35 -27
- agno/tools/reddit.py +11 -26
- agno/tools/replicate.py +55 -42
- agno/tools/resend.py +4 -1
- agno/tools/scrapegraph.py +15 -14
- agno/tools/searxng.py +10 -23
- agno/tools/serpapi.py +6 -3
- agno/tools/serper.py +13 -4
- agno/tools/shell.py +9 -2
- agno/tools/slack.py +12 -11
- agno/tools/sleep.py +3 -2
- agno/tools/spider.py +24 -4
- agno/tools/sql.py +7 -6
- agno/tools/tavily.py +6 -4
- agno/tools/telegram.py +12 -4
- agno/tools/todoist.py +11 -31
- agno/tools/toolkit.py +1 -1
- agno/tools/trafilatura.py +22 -6
- agno/tools/trello.py +9 -22
- agno/tools/twilio.py +10 -3
- agno/tools/user_control_flow.py +6 -1
- agno/tools/valyu.py +34 -5
- agno/tools/visualization.py +19 -28
- agno/tools/webbrowser.py +4 -3
- agno/tools/webex.py +11 -7
- agno/tools/website.py +15 -46
- agno/tools/webtools.py +12 -4
- agno/tools/whatsapp.py +5 -9
- agno/tools/wikipedia.py +20 -13
- agno/tools/x.py +14 -13
- agno/tools/yfinance.py +13 -40
- agno/tools/youtube.py +26 -20
- agno/tools/zendesk.py +7 -2
- agno/tools/zep.py +10 -7
- agno/tools/zoom.py +10 -9
- agno/utils/common.py +1 -19
- agno/utils/events.py +100 -123
- agno/utils/gemini.py +32 -2
- agno/utils/knowledge.py +29 -0
- agno/utils/log.py +54 -4
- agno/utils/mcp.py +68 -10
- agno/utils/media.py +39 -0
- agno/utils/message.py +12 -1
- agno/utils/models/aws_claude.py +1 -1
- agno/utils/models/claude.py +47 -4
- agno/utils/models/cohere.py +1 -1
- agno/utils/models/mistral.py +8 -7
- agno/utils/models/schema_utils.py +3 -3
- agno/utils/models/watsonx.py +1 -1
- agno/utils/openai.py +1 -1
- agno/utils/pprint.py +33 -32
- agno/utils/print_response/agent.py +779 -0
- agno/utils/print_response/team.py +1669 -0
- agno/utils/print_response/workflow.py +1451 -0
- agno/utils/prompts.py +14 -14
- agno/utils/reasoning.py +87 -0
- agno/utils/response.py +42 -42
- agno/utils/streamlit.py +481 -0
- agno/utils/string.py +8 -22
- agno/utils/team.py +50 -0
- agno/utils/timer.py +2 -2
- agno/vectordb/base.py +33 -21
- agno/vectordb/cassandra/cassandra.py +287 -23
- agno/vectordb/chroma/chromadb.py +482 -59
- agno/vectordb/clickhouse/clickhousedb.py +270 -63
- agno/vectordb/couchbase/couchbase.py +309 -29
- agno/vectordb/lancedb/lance_db.py +360 -21
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +145 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +374 -0
- agno/vectordb/llamaindex/llamaindexdb.py +127 -0
- agno/vectordb/milvus/milvus.py +242 -32
- agno/vectordb/mongodb/mongodb.py +200 -24
- agno/vectordb/pgvector/pgvector.py +319 -37
- agno/vectordb/pineconedb/pineconedb.py +221 -27
- agno/vectordb/qdrant/qdrant.py +334 -14
- agno/vectordb/singlestore/singlestore.py +286 -29
- agno/vectordb/surrealdb/surrealdb.py +187 -7
- agno/vectordb/upstashdb/upstashdb.py +342 -26
- agno/vectordb/weaviate/weaviate.py +227 -165
- agno/workflow/__init__.py +17 -13
- agno/workflow/{v2/condition.py → condition.py} +135 -32
- agno/workflow/{v2/loop.py → loop.py} +115 -28
- agno/workflow/{v2/parallel.py → parallel.py} +138 -108
- agno/workflow/{v2/router.py → router.py} +133 -32
- agno/workflow/{v2/step.py → step.py} +207 -49
- agno/workflow/{v2/steps.py → steps.py} +147 -66
- agno/workflow/types.py +482 -0
- agno/workflow/workflow.py +2410 -696
- agno-2.0.0.dist-info/METADATA +494 -0
- agno-2.0.0.dist-info/RECORD +515 -0
- agno-2.0.0.dist-info/licenses/LICENSE +201 -0
- agno/agent/metrics.py +0 -107
- agno/api/app.py +0 -35
- agno/api/playground.py +0 -92
- agno/api/schemas/app.py +0 -12
- agno/api/schemas/playground.py +0 -22
- agno/api/schemas/user.py +0 -35
- agno/api/schemas/workspace.py +0 -46
- agno/api/user.py +0 -160
- agno/api/workflows.py +0 -33
- agno/api/workspace.py +0 -175
- agno/app/agui/__init__.py +0 -3
- agno/app/agui/app.py +0 -17
- agno/app/agui/sync_router.py +0 -120
- agno/app/base.py +0 -186
- agno/app/discord/__init__.py +0 -3
- agno/app/fastapi/__init__.py +0 -3
- agno/app/fastapi/app.py +0 -107
- agno/app/fastapi/async_router.py +0 -457
- agno/app/fastapi/sync_router.py +0 -448
- agno/app/playground/app.py +0 -228
- agno/app/playground/async_router.py +0 -1050
- agno/app/playground/deploy.py +0 -249
- agno/app/playground/operator.py +0 -183
- agno/app/playground/schemas.py +0 -220
- agno/app/playground/serve.py +0 -55
- agno/app/playground/sync_router.py +0 -1042
- agno/app/playground/utils.py +0 -46
- agno/app/settings.py +0 -15
- agno/app/slack/__init__.py +0 -3
- agno/app/slack/app.py +0 -19
- agno/app/slack/sync_router.py +0 -92
- agno/app/utils.py +0 -54
- agno/app/whatsapp/__init__.py +0 -3
- agno/app/whatsapp/app.py +0 -15
- agno/app/whatsapp/sync_router.py +0 -197
- agno/cli/auth_server.py +0 -249
- agno/cli/config.py +0 -274
- agno/cli/console.py +0 -88
- agno/cli/credentials.py +0 -23
- agno/cli/entrypoint.py +0 -571
- agno/cli/operator.py +0 -357
- agno/cli/settings.py +0 -96
- agno/cli/ws/ws_cli.py +0 -817
- agno/constants.py +0 -13
- agno/document/__init__.py +0 -5
- agno/document/chunking/semantic.py +0 -45
- agno/document/chunking/strategy.py +0 -31
- agno/document/reader/__init__.py +0 -5
- agno/document/reader/base.py +0 -47
- agno/document/reader/docx_reader.py +0 -60
- agno/document/reader/gcs/pdf_reader.py +0 -44
- agno/document/reader/s3/pdf_reader.py +0 -59
- agno/document/reader/s3/text_reader.py +0 -63
- agno/document/reader/url_reader.py +0 -59
- agno/document/reader/youtube_reader.py +0 -58
- agno/embedder/__init__.py +0 -5
- agno/embedder/langdb.py +0 -80
- agno/embedder/mistral.py +0 -82
- agno/embedder/openai.py +0 -78
- agno/file/__init__.py +0 -5
- 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 -702
- agno/knowledge/arxiv.py +0 -33
- agno/knowledge/combined.py +0 -36
- agno/knowledge/csv.py +0 -144
- agno/knowledge/csv_url.py +0 -124
- agno/knowledge/document.py +0 -223
- agno/knowledge/docx.py +0 -137
- agno/knowledge/firecrawl.py +0 -34
- agno/knowledge/gcs/__init__.py +0 -0
- agno/knowledge/gcs/base.py +0 -39
- agno/knowledge/gcs/pdf.py +0 -125
- agno/knowledge/json.py +0 -137
- agno/knowledge/langchain.py +0 -71
- agno/knowledge/light_rag.py +0 -273
- agno/knowledge/llamaindex.py +0 -66
- agno/knowledge/markdown.py +0 -154
- agno/knowledge/pdf.py +0 -164
- agno/knowledge/pdf_bytes.py +0 -42
- agno/knowledge/pdf_url.py +0 -148
- agno/knowledge/s3/__init__.py +0 -0
- agno/knowledge/s3/base.py +0 -64
- agno/knowledge/s3/pdf.py +0 -33
- agno/knowledge/s3/text.py +0 -34
- agno/knowledge/text.py +0 -141
- agno/knowledge/url.py +0 -46
- agno/knowledge/website.py +0 -179
- agno/knowledge/wikipedia.py +0 -32
- agno/knowledge/youtube.py +0 -35
- agno/memory/agent.py +0 -423
- agno/memory/classifier.py +0 -104
- agno/memory/db/__init__.py +0 -5
- 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 -22
- agno/memory/row.py +0 -36
- agno/memory/summarizer.py +0 -201
- agno/memory/summary.py +0 -19
- agno/memory/team.py +0 -415
- agno/memory/v2/__init__.py +0 -2
- agno/memory/v2/db/__init__.py +0 -1
- agno/memory/v2/db/base.py +0 -42
- agno/memory/v2/db/firestore.py +0 -339
- agno/memory/v2/db/mongodb.py +0 -196
- agno/memory/v2/db/postgres.py +0 -214
- agno/memory/v2/db/redis.py +0 -187
- agno/memory/v2/db/schema.py +0 -54
- agno/memory/v2/db/sqlite.py +0 -209
- agno/memory/v2/manager.py +0 -437
- agno/memory/v2/memory.py +0 -1097
- agno/memory/v2/schema.py +0 -55
- agno/memory/v2/summarizer.py +0 -215
- agno/memory/workflow.py +0 -38
- agno/models/ollama/tools.py +0 -430
- agno/models/qwen/__init__.py +0 -5
- agno/playground/__init__.py +0 -10
- agno/playground/deploy.py +0 -3
- agno/playground/playground.py +0 -3
- agno/playground/serve.py +0 -3
- agno/playground/settings.py +0 -3
- agno/reranker/__init__.py +0 -0
- agno/run/response.py +0 -467
- agno/run/v2/__init__.py +0 -0
- agno/run/v2/workflow.py +0 -567
- agno/storage/__init__.py +0 -0
- agno/storage/agent/__init__.py +0 -0
- agno/storage/agent/dynamodb.py +0 -1
- agno/storage/agent/json.py +0 -1
- agno/storage/agent/mongodb.py +0 -1
- agno/storage/agent/postgres.py +0 -1
- agno/storage/agent/singlestore.py +0 -1
- agno/storage/agent/sqlite.py +0 -1
- agno/storage/agent/yaml.py +0 -1
- agno/storage/base.py +0 -60
- agno/storage/dynamodb.py +0 -673
- agno/storage/firestore.py +0 -297
- agno/storage/gcs_json.py +0 -261
- agno/storage/in_memory.py +0 -234
- agno/storage/json.py +0 -237
- agno/storage/mongodb.py +0 -328
- agno/storage/mysql.py +0 -685
- agno/storage/postgres.py +0 -682
- agno/storage/redis.py +0 -336
- agno/storage/session/__init__.py +0 -16
- agno/storage/session/agent.py +0 -64
- agno/storage/session/team.py +0 -63
- agno/storage/session/v2/__init__.py +0 -5
- agno/storage/session/workflow.py +0 -61
- agno/storage/singlestore.py +0 -606
- agno/storage/sqlite.py +0 -646
- agno/storage/workflow/__init__.py +0 -0
- agno/storage/workflow/mongodb.py +0 -1
- agno/storage/workflow/postgres.py +0 -1
- agno/storage/workflow/sqlite.py +0 -1
- agno/storage/yaml.py +0 -241
- agno/tools/thinking.py +0 -73
- 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/workflow/v2/__init__.py +0 -21
- agno/workflow/v2/types.py +0 -357
- agno/workflow/v2/workflow.py +0 -3312
- agno/workspace/__init__.py +0 -0
- agno/workspace/config.py +0 -325
- agno/workspace/enums.py +0 -6
- agno/workspace/helpers.py +0 -52
- agno/workspace/operator.py +0 -757
- agno/workspace/settings.py +0 -158
- agno-1.8.1.dist-info/METADATA +0 -982
- agno-1.8.1.dist-info/RECORD +0 -566
- agno-1.8.1.dist-info/entry_points.txt +0 -3
- agno-1.8.1.dist-info/licenses/LICENSE +0 -375
- /agno/{app → db/migrations}/__init__.py +0 -0
- /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
- /agno/{cli → integrations}/__init__.py +0 -0
- /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
- /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
- /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
- /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
- /agno/{app → os/interfaces}/slack/security.py +0 -0
- /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
- /agno/{file/local → utils/print_response}/__init__.py +0 -0
- /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0.dist-info}/WHEEL +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0.dist-info}/top_level.txt +0 -0
agno/models/message.py
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from dataclasses import asdict, dataclass
|
|
3
2
|
from time import time
|
|
4
3
|
from typing import Any, Dict, List, Optional, Sequence, Union
|
|
5
4
|
|
|
6
5
|
from pydantic import BaseModel, ConfigDict, Field
|
|
7
6
|
|
|
8
|
-
from agno.media import Audio,
|
|
7
|
+
from agno.media import Audio, File, Image, Video
|
|
8
|
+
from agno.models.metrics import Metrics
|
|
9
9
|
from agno.utils.log import log_debug, log_error, log_info, log_warning
|
|
10
|
-
from agno.utils.timer import Timer
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
class MessageReferences(BaseModel):
|
|
@@ -49,118 +48,6 @@ class Citations(BaseModel):
|
|
|
49
48
|
documents: Optional[List[DocumentCitation]] = None
|
|
50
49
|
|
|
51
50
|
|
|
52
|
-
@dataclass
|
|
53
|
-
class MessageMetrics:
|
|
54
|
-
input_tokens: int = 0
|
|
55
|
-
output_tokens: int = 0
|
|
56
|
-
total_tokens: int = 0
|
|
57
|
-
|
|
58
|
-
audio_tokens: int = 0
|
|
59
|
-
input_audio_tokens: int = 0
|
|
60
|
-
output_audio_tokens: int = 0
|
|
61
|
-
cached_tokens: int = 0
|
|
62
|
-
cache_write_tokens: int = 0
|
|
63
|
-
reasoning_tokens: int = 0
|
|
64
|
-
prompt_tokens: int = 0
|
|
65
|
-
completion_tokens: int = 0
|
|
66
|
-
prompt_tokens_details: Optional[dict] = None
|
|
67
|
-
completion_tokens_details: Optional[dict] = None
|
|
68
|
-
|
|
69
|
-
additional_metrics: Optional[dict] = None
|
|
70
|
-
|
|
71
|
-
time: Optional[float] = None
|
|
72
|
-
time_to_first_token: Optional[float] = None
|
|
73
|
-
|
|
74
|
-
timer: Optional[Timer] = None
|
|
75
|
-
|
|
76
|
-
def to_dict(self) -> Dict[str, Any]:
|
|
77
|
-
metrics_dict = asdict(self)
|
|
78
|
-
metrics_dict.pop("timer")
|
|
79
|
-
metrics_dict = {
|
|
80
|
-
k: v
|
|
81
|
-
for k, v in metrics_dict.items()
|
|
82
|
-
if v is not None and (not isinstance(v, (int, float)) or v != 0) and (not isinstance(v, dict) or len(v) > 0)
|
|
83
|
-
}
|
|
84
|
-
return metrics_dict
|
|
85
|
-
|
|
86
|
-
def start_timer(self):
|
|
87
|
-
if self.timer is None:
|
|
88
|
-
self.timer = Timer()
|
|
89
|
-
self.timer.start()
|
|
90
|
-
|
|
91
|
-
def stop_timer(self, set_time: bool = True):
|
|
92
|
-
if self.timer is not None:
|
|
93
|
-
self.timer.stop()
|
|
94
|
-
if set_time:
|
|
95
|
-
self.time = self.timer.elapsed
|
|
96
|
-
|
|
97
|
-
def set_time_to_first_token(self):
|
|
98
|
-
if self.timer is not None:
|
|
99
|
-
self.time_to_first_token = self.timer.elapsed
|
|
100
|
-
|
|
101
|
-
def __add__(self, other: "MessageMetrics") -> "MessageMetrics":
|
|
102
|
-
# Create new instance with summed basic metrics
|
|
103
|
-
result = MessageMetrics(
|
|
104
|
-
input_tokens=self.input_tokens + other.input_tokens,
|
|
105
|
-
output_tokens=self.output_tokens + other.output_tokens,
|
|
106
|
-
total_tokens=self.total_tokens + other.total_tokens,
|
|
107
|
-
prompt_tokens=self.prompt_tokens + other.prompt_tokens,
|
|
108
|
-
completion_tokens=self.completion_tokens + other.completion_tokens,
|
|
109
|
-
audio_tokens=self.audio_tokens + other.audio_tokens,
|
|
110
|
-
input_audio_tokens=self.input_audio_tokens + other.input_audio_tokens,
|
|
111
|
-
output_audio_tokens=self.output_audio_tokens + other.output_audio_tokens,
|
|
112
|
-
cached_tokens=self.cached_tokens + other.cached_tokens,
|
|
113
|
-
cache_write_tokens=self.cache_write_tokens + other.cache_write_tokens,
|
|
114
|
-
reasoning_tokens=self.reasoning_tokens + other.reasoning_tokens,
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
# Handle prompt_tokens_details
|
|
118
|
-
if self.prompt_tokens_details or other.prompt_tokens_details:
|
|
119
|
-
result.prompt_tokens_details = {}
|
|
120
|
-
# Merge from self
|
|
121
|
-
if self.prompt_tokens_details:
|
|
122
|
-
result.prompt_tokens_details.update(self.prompt_tokens_details)
|
|
123
|
-
# Add values from other
|
|
124
|
-
if other.prompt_tokens_details:
|
|
125
|
-
for key, value in other.prompt_tokens_details.items():
|
|
126
|
-
result.prompt_tokens_details[key] = result.prompt_tokens_details.get(key, 0) + value
|
|
127
|
-
|
|
128
|
-
# Handle completion_tokens_details similarly
|
|
129
|
-
if self.completion_tokens_details or other.completion_tokens_details:
|
|
130
|
-
result.completion_tokens_details = {}
|
|
131
|
-
if self.completion_tokens_details:
|
|
132
|
-
result.completion_tokens_details.update(self.completion_tokens_details)
|
|
133
|
-
if other.completion_tokens_details:
|
|
134
|
-
for key, value in other.completion_tokens_details.items():
|
|
135
|
-
result.completion_tokens_details[key] = result.completion_tokens_details.get(key, 0) + value
|
|
136
|
-
|
|
137
|
-
# Handle additional metrics
|
|
138
|
-
if self.additional_metrics or other.additional_metrics:
|
|
139
|
-
result.additional_metrics = {}
|
|
140
|
-
if self.additional_metrics:
|
|
141
|
-
result.additional_metrics.update(self.additional_metrics)
|
|
142
|
-
if other.additional_metrics:
|
|
143
|
-
result.additional_metrics.update(other.additional_metrics)
|
|
144
|
-
|
|
145
|
-
# Sum times if both exist
|
|
146
|
-
if self.time is not None and other.time is not None:
|
|
147
|
-
result.time = self.time + other.time
|
|
148
|
-
elif self.time is not None:
|
|
149
|
-
result.time = self.time
|
|
150
|
-
elif other.time is not None:
|
|
151
|
-
result.time = other.time
|
|
152
|
-
|
|
153
|
-
# Handle time_to_first_token (take the first non-None value)
|
|
154
|
-
result.time_to_first_token = self.time_to_first_token or other.time_to_first_token
|
|
155
|
-
|
|
156
|
-
return result
|
|
157
|
-
|
|
158
|
-
def __radd__(self, other: "MessageMetrics") -> "MessageMetrics":
|
|
159
|
-
if other == 0: # Handle sum() starting value
|
|
160
|
-
return self
|
|
161
|
-
return self + other
|
|
162
|
-
|
|
163
|
-
|
|
164
51
|
class Message(BaseModel):
|
|
165
52
|
"""Message sent to the Model"""
|
|
166
53
|
|
|
@@ -184,12 +71,12 @@ class Message(BaseModel):
|
|
|
184
71
|
files: Optional[Sequence[File]] = None
|
|
185
72
|
|
|
186
73
|
# Output from the models
|
|
187
|
-
audio_output: Optional[
|
|
188
|
-
image_output: Optional[
|
|
74
|
+
audio_output: Optional[Audio] = None
|
|
75
|
+
image_output: Optional[Image] = None
|
|
76
|
+
video_output: Optional[Video] = None
|
|
189
77
|
|
|
190
78
|
# The thinking content from the model
|
|
191
|
-
|
|
192
|
-
redacted_thinking: Optional[str] = None
|
|
79
|
+
redacted_reasoning_content: Optional[str] = None
|
|
193
80
|
|
|
194
81
|
# Data from the provider we might need on subsequent messages
|
|
195
82
|
provider_data: Optional[Dict[str, Any]] = None
|
|
@@ -213,7 +100,7 @@ class Message(BaseModel):
|
|
|
213
100
|
# This flag is enabled when a message is fetched from the agent's memory.
|
|
214
101
|
from_history: bool = False
|
|
215
102
|
# Metrics for the message.
|
|
216
|
-
metrics:
|
|
103
|
+
metrics: Metrics = Field(default_factory=Metrics)
|
|
217
104
|
# The references added to the message for RAG
|
|
218
105
|
references: Optional[MessageReferences] = None
|
|
219
106
|
# The Unix timestamp the message was created.
|
|
@@ -232,6 +119,10 @@ class Message(BaseModel):
|
|
|
232
119
|
return json.dumps(self.content)
|
|
233
120
|
return ""
|
|
234
121
|
|
|
122
|
+
@classmethod
|
|
123
|
+
def from_dict(cls, data: Dict[str, Any]) -> "Message":
|
|
124
|
+
return cls(**data)
|
|
125
|
+
|
|
235
126
|
def to_dict(self) -> Dict[str, Any]:
|
|
236
127
|
"""Returns the message as a dictionary."""
|
|
237
128
|
message_dict = {
|
|
@@ -246,8 +137,7 @@ class Message(BaseModel):
|
|
|
246
137
|
"tool_args": self.tool_args,
|
|
247
138
|
"tool_call_error": self.tool_call_error,
|
|
248
139
|
"tool_calls": self.tool_calls,
|
|
249
|
-
"
|
|
250
|
-
"redacted_thinking": self.redacted_thinking,
|
|
140
|
+
"redacted_reasoning_content": self.redacted_reasoning_content,
|
|
251
141
|
"provider_data": self.provider_data,
|
|
252
142
|
}
|
|
253
143
|
# Filter out None and empty collections
|
|
@@ -316,8 +206,8 @@ class Message(BaseModel):
|
|
|
316
206
|
_logger(f"Name: {self.name}")
|
|
317
207
|
if self.tool_call_id:
|
|
318
208
|
_logger(f"Tool call Id: {self.tool_call_id}")
|
|
319
|
-
if self.
|
|
320
|
-
_logger(f"<
|
|
209
|
+
if self.reasoning_content:
|
|
210
|
+
_logger(f"<reasoning>\n{self.reasoning_content}\n</reasoning>")
|
|
321
211
|
if self.content:
|
|
322
212
|
if isinstance(self.content, str) or isinstance(self.content, list):
|
|
323
213
|
_logger(self.content)
|
|
@@ -338,12 +228,13 @@ class Message(BaseModel):
|
|
|
338
228
|
if isinstance(tool_call_arguments, dict)
|
|
339
229
|
else json.loads(tool_call_arguments)
|
|
340
230
|
)
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
231
|
+
if tool_call_args:
|
|
232
|
+
# Ensure tool_call_args is a dictionary before calling .items()
|
|
233
|
+
if isinstance(tool_call_args, dict):
|
|
234
|
+
arguments = ", ".join(f"{k}: {v}" for k, v in tool_call_args.items())
|
|
235
|
+
tool_calls_list.append(f" Arguments: '{arguments}'")
|
|
236
|
+
else:
|
|
237
|
+
tool_calls_list.append(f" Arguments: '{tool_call_args}'")
|
|
347
238
|
except json.JSONDecodeError:
|
|
348
239
|
tool_calls_list.append(" Arguments: 'Invalid JSON format'")
|
|
349
240
|
tool_calls_str = "\n".join(tool_calls_list)
|
|
@@ -359,42 +250,46 @@ class Message(BaseModel):
|
|
|
359
250
|
_logger(f"Files added: {len(self.files)}")
|
|
360
251
|
|
|
361
252
|
metrics_header = " TOOL METRICS " if self.role == "tool" else " METRICS "
|
|
362
|
-
if metrics and self.metrics is not None and self.metrics !=
|
|
253
|
+
if metrics and self.metrics is not None and self.metrics != Metrics():
|
|
363
254
|
_logger(metrics_header, center=True, symbol="*")
|
|
364
255
|
|
|
365
|
-
#
|
|
256
|
+
# Token metrics
|
|
366
257
|
token_metrics = []
|
|
367
|
-
if self.metrics.input_tokens:
|
|
258
|
+
if self.metrics.input_tokens and self.metrics.input_tokens > 0:
|
|
368
259
|
token_metrics.append(f"input={self.metrics.input_tokens}")
|
|
369
|
-
if self.metrics.output_tokens:
|
|
260
|
+
if self.metrics.output_tokens and self.metrics.output_tokens > 0:
|
|
370
261
|
token_metrics.append(f"output={self.metrics.output_tokens}")
|
|
371
|
-
if self.metrics.total_tokens:
|
|
262
|
+
if self.metrics.total_tokens and self.metrics.total_tokens > 0:
|
|
372
263
|
token_metrics.append(f"total={self.metrics.total_tokens}")
|
|
373
|
-
if self.metrics.
|
|
374
|
-
token_metrics.append(f"cached={self.metrics.
|
|
375
|
-
if self.metrics.cache_write_tokens:
|
|
264
|
+
if self.metrics.cache_read_tokens and self.metrics.cache_read_tokens > 0:
|
|
265
|
+
token_metrics.append(f"cached={self.metrics.cache_read_tokens}")
|
|
266
|
+
if self.metrics.cache_write_tokens and self.metrics.cache_write_tokens > 0:
|
|
376
267
|
token_metrics.append(f"cache_write_tokens={self.metrics.cache_write_tokens}")
|
|
377
|
-
if self.metrics.reasoning_tokens:
|
|
268
|
+
if self.metrics.reasoning_tokens and self.metrics.reasoning_tokens > 0:
|
|
378
269
|
token_metrics.append(f"reasoning={self.metrics.reasoning_tokens}")
|
|
379
|
-
if self.metrics.
|
|
380
|
-
token_metrics.append(f"audio={self.metrics.
|
|
270
|
+
if self.metrics.audio_total_tokens and self.metrics.audio_total_tokens > 0:
|
|
271
|
+
token_metrics.append(f"audio={self.metrics.audio_total_tokens}")
|
|
381
272
|
if token_metrics:
|
|
382
273
|
_logger(f"* Tokens: {', '.join(token_metrics)}")
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
if self.metrics.
|
|
386
|
-
_logger(f"*
|
|
387
|
-
if self.metrics.
|
|
388
|
-
_logger(
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
if self.metrics.time_to_first_token is not None:
|
|
274
|
+
|
|
275
|
+
# Time related metrics
|
|
276
|
+
if self.metrics.duration is not None and self.metrics.duration > 0:
|
|
277
|
+
_logger(f"* Duration: {self.metrics.duration:.4f}s")
|
|
278
|
+
if self.metrics.output_tokens and self.metrics.duration and self.metrics.duration > 0:
|
|
279
|
+
_logger(
|
|
280
|
+
f"* Tokens per second: {self.metrics.output_tokens / self.metrics.duration:.4f} tokens/s"
|
|
281
|
+
)
|
|
282
|
+
if self.metrics.time_to_first_token is not None and self.metrics.time_to_first_token > 0:
|
|
392
283
|
_logger(f"* Time to first token: {self.metrics.time_to_first_token:.4f}s")
|
|
284
|
+
|
|
285
|
+
# Non-generic metrics
|
|
286
|
+
if self.metrics.provider_metrics:
|
|
287
|
+
_logger(f"* Provider metrics: {self.metrics.provider_metrics}")
|
|
393
288
|
if self.metrics.additional_metrics:
|
|
394
289
|
_logger(f"* Additional metrics: {self.metrics.additional_metrics}")
|
|
290
|
+
|
|
395
291
|
_logger(metrics_header, center=True, symbol="*")
|
|
396
292
|
|
|
397
293
|
def content_is_valid(self) -> bool:
|
|
398
294
|
"""Check if the message content is valid."""
|
|
399
|
-
|
|
400
295
|
return self.content is not None and len(self.content) > 0
|
agno/models/meta/llama.py
CHANGED
|
@@ -9,18 +9,21 @@ from pydantic import BaseModel
|
|
|
9
9
|
from agno.exceptions import ModelProviderError
|
|
10
10
|
from agno.models.base import Model
|
|
11
11
|
from agno.models.message import Message
|
|
12
|
+
from agno.models.metrics import Metrics
|
|
12
13
|
from agno.models.response import ModelResponse
|
|
14
|
+
from agno.run.agent import RunOutput
|
|
13
15
|
from agno.utils.log import log_debug, log_error, log_warning
|
|
14
16
|
from agno.utils.models.llama import format_message
|
|
15
17
|
|
|
16
18
|
try:
|
|
17
19
|
from llama_api_client import AsyncLlamaAPIClient, LlamaAPIClient
|
|
18
|
-
from llama_api_client.types.create_chat_completion_response import CreateChatCompletionResponse
|
|
20
|
+
from llama_api_client.types.create_chat_completion_response import CreateChatCompletionResponse, Metric
|
|
19
21
|
from llama_api_client.types.create_chat_completion_response_stream_chunk import (
|
|
20
22
|
CreateChatCompletionResponseStreamChunk,
|
|
21
23
|
EventDeltaTextDelta,
|
|
22
24
|
EventDeltaToolCallDelta,
|
|
23
25
|
EventDeltaToolCallDeltaFunction,
|
|
26
|
+
EventMetric,
|
|
24
27
|
)
|
|
25
28
|
from llama_api_client.types.message_text_content_item import MessageTextContentItem
|
|
26
29
|
except ImportError:
|
|
@@ -192,54 +195,84 @@ class Llama(Model):
|
|
|
192
195
|
def invoke(
|
|
193
196
|
self,
|
|
194
197
|
messages: List[Message],
|
|
198
|
+
assistant_message: Message,
|
|
195
199
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
196
200
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
197
201
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
198
|
-
|
|
202
|
+
run_response: Optional[RunOutput] = None,
|
|
203
|
+
) -> ModelResponse:
|
|
199
204
|
"""
|
|
200
205
|
Send a chat completion request to the Llama API.
|
|
201
206
|
"""
|
|
202
|
-
|
|
207
|
+
assistant_message.metrics.start_timer()
|
|
208
|
+
|
|
209
|
+
provider_response = self.get_client().chat.completions.create(
|
|
203
210
|
model=self.id,
|
|
204
211
|
messages=[format_message(m, tool_calls=bool(tools)) for m in messages], # type: ignore
|
|
205
212
|
**self.get_request_params(tools=tools, response_format=response_format),
|
|
206
213
|
)
|
|
207
214
|
|
|
215
|
+
assistant_message.metrics.stop_timer()
|
|
216
|
+
|
|
217
|
+
model_response = self._parse_provider_response(provider_response, response_format=response_format)
|
|
218
|
+
return model_response
|
|
219
|
+
|
|
208
220
|
async def ainvoke(
|
|
209
221
|
self,
|
|
210
222
|
messages: List[Message],
|
|
223
|
+
assistant_message: Message,
|
|
211
224
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
212
225
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
213
226
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
214
|
-
|
|
227
|
+
run_response: Optional[RunOutput] = None,
|
|
228
|
+
) -> ModelResponse:
|
|
215
229
|
"""
|
|
216
230
|
Sends an asynchronous chat completion request to the Llama API.
|
|
217
231
|
"""
|
|
232
|
+
if run_response and run_response.metrics:
|
|
233
|
+
run_response.metrics.set_time_to_first_token()
|
|
218
234
|
|
|
219
|
-
|
|
235
|
+
assistant_message.metrics.start_timer()
|
|
236
|
+
|
|
237
|
+
provider_response = await self.get_async_client().chat.completions.create(
|
|
220
238
|
model=self.id,
|
|
221
239
|
messages=[format_message(m, tool_calls=bool(tools)) for m in messages], # type: ignore
|
|
222
240
|
**self.get_request_params(tools=tools, response_format=response_format),
|
|
223
241
|
)
|
|
224
242
|
|
|
243
|
+
assistant_message.metrics.stop_timer()
|
|
244
|
+
|
|
245
|
+
model_response = self._parse_provider_response(provider_response, response_format=response_format)
|
|
246
|
+
return model_response
|
|
247
|
+
|
|
225
248
|
def invoke_stream(
|
|
226
249
|
self,
|
|
227
250
|
messages: List[Message],
|
|
251
|
+
assistant_message: Message,
|
|
228
252
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
229
253
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
230
254
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
231
|
-
|
|
255
|
+
run_response: Optional[RunOutput] = None,
|
|
256
|
+
) -> Iterator[ModelResponse]:
|
|
232
257
|
"""
|
|
233
258
|
Send a streaming chat completion request to the Llama API.
|
|
234
259
|
"""
|
|
260
|
+
if run_response and run_response.metrics:
|
|
261
|
+
run_response.metrics.set_time_to_first_token()
|
|
235
262
|
|
|
236
263
|
try:
|
|
237
|
-
|
|
264
|
+
assistant_message.metrics.start_timer()
|
|
265
|
+
|
|
266
|
+
for chunk in self.get_client().chat.completions.create(
|
|
238
267
|
model=self.id,
|
|
239
268
|
messages=[format_message(m, tool_calls=bool(tools)) for m in messages], # type: ignore
|
|
240
269
|
stream=True,
|
|
241
270
|
**self.get_request_params(tools=tools, response_format=response_format),
|
|
242
|
-
)
|
|
271
|
+
):
|
|
272
|
+
yield self._parse_provider_response_delta(chunk) # type: ignore
|
|
273
|
+
|
|
274
|
+
assistant_message.metrics.stop_timer()
|
|
275
|
+
|
|
243
276
|
except Exception as e:
|
|
244
277
|
log_error(f"Error from Llama API: {e}")
|
|
245
278
|
raise ModelProviderError(message=str(e), model_name=self.name, model_id=self.id) from e
|
|
@@ -247,29 +280,36 @@ class Llama(Model):
|
|
|
247
280
|
async def ainvoke_stream(
|
|
248
281
|
self,
|
|
249
282
|
messages: List[Message],
|
|
283
|
+
assistant_message: Message,
|
|
250
284
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
251
285
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
252
286
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
253
|
-
|
|
287
|
+
run_response: Optional[RunOutput] = None,
|
|
288
|
+
) -> AsyncIterator[ModelResponse]:
|
|
254
289
|
"""
|
|
255
290
|
Sends an asynchronous streaming chat completion request to the Llama API.
|
|
256
291
|
"""
|
|
292
|
+
if run_response and run_response.metrics:
|
|
293
|
+
run_response.metrics.set_time_to_first_token()
|
|
294
|
+
|
|
295
|
+
assistant_message.metrics.start_timer()
|
|
257
296
|
|
|
258
297
|
try:
|
|
259
|
-
|
|
298
|
+
async for chunk in await self.get_async_client().chat.completions.create(
|
|
260
299
|
model=self.id,
|
|
261
300
|
messages=[format_message(m, tool_calls=bool(tools)) for m in messages], # type: ignore
|
|
262
301
|
stream=True,
|
|
263
302
|
**self.get_request_params(tools=tools, response_format=response_format),
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
|
|
303
|
+
):
|
|
304
|
+
yield self._parse_provider_response_delta(chunk) # type: ignore
|
|
305
|
+
|
|
306
|
+
assistant_message.metrics.stop_timer()
|
|
307
|
+
|
|
267
308
|
except Exception as e:
|
|
268
309
|
log_error(f"Error from Llama API: {e}")
|
|
269
310
|
raise ModelProviderError(message=str(e), model_name=self.name, model_id=self.id) from e
|
|
270
311
|
|
|
271
|
-
|
|
272
|
-
def parse_tool_calls(tool_calls_data: List[EventDeltaToolCallDeltaFunction]) -> List[Dict[str, Any]]:
|
|
312
|
+
def parse_tool_calls(self, tool_calls_data: List[EventDeltaToolCallDeltaFunction]) -> List[Dict[str, Any]]:
|
|
273
313
|
"""
|
|
274
314
|
Parse the tool calls from the Llama API.
|
|
275
315
|
|
|
@@ -321,7 +361,7 @@ class Llama(Model):
|
|
|
321
361
|
|
|
322
362
|
return tool_calls
|
|
323
363
|
|
|
324
|
-
def
|
|
364
|
+
def _parse_provider_response(self, response: CreateChatCompletionResponse, **kwargs) -> ModelResponse:
|
|
325
365
|
"""
|
|
326
366
|
Parse the Llama response into a ModelResponse.
|
|
327
367
|
|
|
@@ -371,26 +411,12 @@ class Llama(Model):
|
|
|
371
411
|
|
|
372
412
|
# Add metrics from the metrics list
|
|
373
413
|
if hasattr(response, "metrics") and response.metrics is not None:
|
|
374
|
-
|
|
375
|
-
metric_map = {
|
|
376
|
-
"num_prompt_tokens": "input_tokens",
|
|
377
|
-
"num_completion_tokens": "output_tokens",
|
|
378
|
-
"num_total_tokens": "total_tokens",
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
for metric in response.metrics:
|
|
382
|
-
key = metric_map.get(metric.metric)
|
|
383
|
-
if key:
|
|
384
|
-
value = int(metric.value)
|
|
385
|
-
usage_data[key] = value
|
|
386
|
-
|
|
387
|
-
if usage_data:
|
|
388
|
-
model_response.response_usage = usage_data
|
|
414
|
+
model_response.response_usage = self._get_metrics(response.metrics)
|
|
389
415
|
|
|
390
416
|
return model_response
|
|
391
417
|
|
|
392
|
-
def
|
|
393
|
-
self,
|
|
418
|
+
def _parse_provider_response_delta(
|
|
419
|
+
self, response: CreateChatCompletionResponseStreamChunk, **kwargs
|
|
394
420
|
) -> ModelResponse:
|
|
395
421
|
"""
|
|
396
422
|
Parse the Llama streaming response into a ModelResponse.
|
|
@@ -403,25 +429,12 @@ class Llama(Model):
|
|
|
403
429
|
"""
|
|
404
430
|
model_response = ModelResponse()
|
|
405
431
|
|
|
406
|
-
if
|
|
407
|
-
delta =
|
|
432
|
+
if response is not None:
|
|
433
|
+
delta = response.event
|
|
408
434
|
|
|
409
435
|
# Capture metrics event
|
|
410
436
|
if delta.event_type == "metrics" and delta.metrics is not None:
|
|
411
|
-
|
|
412
|
-
metric_map = {
|
|
413
|
-
"num_prompt_tokens": "input_tokens",
|
|
414
|
-
"num_completion_tokens": "output_tokens",
|
|
415
|
-
"num_total_tokens": "total_tokens",
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
for metric in delta.metrics:
|
|
419
|
-
key = metric_map.get(metric.metric)
|
|
420
|
-
if key:
|
|
421
|
-
usage_data[key] = int(metric.value)
|
|
422
|
-
|
|
423
|
-
if usage_data:
|
|
424
|
-
model_response.response_usage = usage_data
|
|
437
|
+
model_response.response_usage = self._get_metrics(delta.metrics)
|
|
425
438
|
|
|
426
439
|
if isinstance(delta.delta, EventDeltaTextDelta):
|
|
427
440
|
model_response.content = delta.delta.text
|
|
@@ -431,3 +444,26 @@ class Llama(Model):
|
|
|
431
444
|
model_response.tool_calls = delta.delta # type: ignore
|
|
432
445
|
|
|
433
446
|
return model_response
|
|
447
|
+
|
|
448
|
+
def _get_metrics(self, response_usage: Union[List[Metric], List[EventMetric]]) -> Metrics:
|
|
449
|
+
"""
|
|
450
|
+
Parse the given Llama usage into an Agno Metrics object.
|
|
451
|
+
|
|
452
|
+
Args:
|
|
453
|
+
response_usage: Usage data from Llama
|
|
454
|
+
|
|
455
|
+
Returns:
|
|
456
|
+
Metrics: Parsed metrics data
|
|
457
|
+
"""
|
|
458
|
+
metrics = Metrics()
|
|
459
|
+
|
|
460
|
+
for metric in response_usage:
|
|
461
|
+
metrics_field = metric.metric
|
|
462
|
+
if metrics_field == "num_prompt_tokens":
|
|
463
|
+
metrics.input_tokens = int(metric.value)
|
|
464
|
+
elif metrics_field == "num_completion_tokens":
|
|
465
|
+
metrics.output_tokens = int(metric.value)
|
|
466
|
+
|
|
467
|
+
metrics.total_tokens = metrics.input_tokens + metrics.output_tokens
|
|
468
|
+
|
|
469
|
+
return metrics
|
agno/models/metrics.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from dataclasses import asdict, dataclass
|
|
2
|
+
from typing import Any, Dict, Optional
|
|
3
|
+
|
|
4
|
+
from agno.utils.timer import Timer
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class Metrics:
|
|
9
|
+
"""All relevant metrics for a session, run or message."""
|
|
10
|
+
|
|
11
|
+
# Main token consumption values
|
|
12
|
+
input_tokens: int = 0
|
|
13
|
+
output_tokens: int = 0
|
|
14
|
+
total_tokens: int = 0
|
|
15
|
+
|
|
16
|
+
# Audio token usage
|
|
17
|
+
audio_input_tokens: int = 0
|
|
18
|
+
audio_output_tokens: int = 0
|
|
19
|
+
audio_total_tokens: int = 0
|
|
20
|
+
|
|
21
|
+
# Cache token usage
|
|
22
|
+
cache_read_tokens: int = 0
|
|
23
|
+
cache_write_tokens: int = 0
|
|
24
|
+
|
|
25
|
+
# Tokens employed in reasoning
|
|
26
|
+
reasoning_tokens: int = 0
|
|
27
|
+
|
|
28
|
+
# Time metrics
|
|
29
|
+
# Internal timer utility for tracking execution time
|
|
30
|
+
timer: Optional[Timer] = None
|
|
31
|
+
# Time from run start to first token generation, in seconds
|
|
32
|
+
time_to_first_token: Optional[float] = None
|
|
33
|
+
# Total run time, in seconds
|
|
34
|
+
duration: Optional[float] = None
|
|
35
|
+
|
|
36
|
+
# Provider-specific metrics
|
|
37
|
+
provider_metrics: Optional[dict] = None
|
|
38
|
+
|
|
39
|
+
# Any additional metrics
|
|
40
|
+
additional_metrics: Optional[dict] = None
|
|
41
|
+
|
|
42
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
43
|
+
metrics_dict = asdict(self)
|
|
44
|
+
# Remove the timer util if present
|
|
45
|
+
metrics_dict.pop("timer", None)
|
|
46
|
+
metrics_dict = {
|
|
47
|
+
k: v
|
|
48
|
+
for k, v in metrics_dict.items()
|
|
49
|
+
if v is not None and (not isinstance(v, (int, float)) or v != 0) and (not isinstance(v, dict) or len(v) > 0)
|
|
50
|
+
}
|
|
51
|
+
return metrics_dict
|
|
52
|
+
|
|
53
|
+
def __add__(self, other: "Metrics") -> "Metrics":
|
|
54
|
+
# Create new instance of the same type as self
|
|
55
|
+
result_class = type(self)
|
|
56
|
+
result = result_class(
|
|
57
|
+
input_tokens=self.input_tokens + other.input_tokens,
|
|
58
|
+
output_tokens=self.output_tokens + other.output_tokens,
|
|
59
|
+
total_tokens=self.total_tokens + other.total_tokens,
|
|
60
|
+
audio_total_tokens=self.audio_total_tokens + other.audio_total_tokens,
|
|
61
|
+
audio_input_tokens=self.audio_input_tokens + other.audio_input_tokens,
|
|
62
|
+
audio_output_tokens=self.audio_output_tokens + other.audio_output_tokens,
|
|
63
|
+
cache_read_tokens=self.cache_read_tokens + other.cache_read_tokens,
|
|
64
|
+
cache_write_tokens=self.cache_write_tokens + other.cache_write_tokens,
|
|
65
|
+
reasoning_tokens=self.reasoning_tokens + other.reasoning_tokens,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Handle provider_metrics
|
|
69
|
+
if self.provider_metrics or other.provider_metrics:
|
|
70
|
+
result.provider_metrics = {}
|
|
71
|
+
if self.provider_metrics:
|
|
72
|
+
result.provider_metrics.update(self.provider_metrics)
|
|
73
|
+
if other.provider_metrics:
|
|
74
|
+
result.provider_metrics.update(other.provider_metrics)
|
|
75
|
+
|
|
76
|
+
# Handle additional metrics
|
|
77
|
+
if self.additional_metrics or other.additional_metrics:
|
|
78
|
+
result.additional_metrics = {}
|
|
79
|
+
if self.additional_metrics:
|
|
80
|
+
result.additional_metrics.update(self.additional_metrics)
|
|
81
|
+
if other.additional_metrics:
|
|
82
|
+
result.additional_metrics.update(other.additional_metrics)
|
|
83
|
+
|
|
84
|
+
# Sum durations if both exist
|
|
85
|
+
if self.duration is not None and other.duration is not None:
|
|
86
|
+
result.duration = self.duration + other.duration
|
|
87
|
+
elif self.duration is not None:
|
|
88
|
+
result.duration = self.duration
|
|
89
|
+
elif other.duration is not None:
|
|
90
|
+
result.duration = other.duration
|
|
91
|
+
|
|
92
|
+
# Sum time to first token if both exist
|
|
93
|
+
if self.time_to_first_token is not None and other.time_to_first_token is not None:
|
|
94
|
+
result.time_to_first_token = self.time_to_first_token + other.time_to_first_token
|
|
95
|
+
elif self.time_to_first_token is not None:
|
|
96
|
+
result.time_to_first_token = self.time_to_first_token
|
|
97
|
+
elif other.time_to_first_token is not None:
|
|
98
|
+
result.time_to_first_token = other.time_to_first_token
|
|
99
|
+
|
|
100
|
+
return result
|
|
101
|
+
|
|
102
|
+
def __radd__(self, other: "Metrics") -> "Metrics":
|
|
103
|
+
if other == 0: # Handle sum() starting value
|
|
104
|
+
return self
|
|
105
|
+
return self + other
|
|
106
|
+
|
|
107
|
+
def start_timer(self):
|
|
108
|
+
if self.timer is None:
|
|
109
|
+
self.timer = Timer()
|
|
110
|
+
self.timer.start()
|
|
111
|
+
|
|
112
|
+
def stop_timer(self, set_duration: bool = True):
|
|
113
|
+
if self.timer is not None:
|
|
114
|
+
self.timer.stop()
|
|
115
|
+
if set_duration:
|
|
116
|
+
self.duration = self.timer.elapsed
|
|
117
|
+
|
|
118
|
+
def set_time_to_first_token(self):
|
|
119
|
+
if self.timer is not None:
|
|
120
|
+
self.time_to_first_token = self.timer.elapsed
|