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/eval/performance.py
ADDED
|
@@ -0,0 +1,779 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import gc
|
|
3
|
+
import tracemalloc
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from os import getenv
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union
|
|
7
|
+
from uuid import uuid4
|
|
8
|
+
|
|
9
|
+
from agno.db.base import AsyncBaseDb, BaseDb
|
|
10
|
+
from agno.db.schemas.evals import EvalType
|
|
11
|
+
from agno.eval.utils import async_log_eval, log_eval_run, store_result_in_file
|
|
12
|
+
from agno.utils.log import log_debug, set_log_level_to_debug, set_log_level_to_info
|
|
13
|
+
from agno.utils.timer import Timer
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from rich.console import Console
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class PerformanceResult:
|
|
21
|
+
"""
|
|
22
|
+
Holds run-time and memory-usage statistics.
|
|
23
|
+
In addition to average, min, max, std dev, we add median and percentile metrics
|
|
24
|
+
for a more robust understanding.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
# Run time performance in seconds
|
|
28
|
+
run_times: List[float] = field(default_factory=list)
|
|
29
|
+
avg_run_time: float = field(init=False)
|
|
30
|
+
min_run_time: float = field(init=False)
|
|
31
|
+
max_run_time: float = field(init=False)
|
|
32
|
+
std_dev_run_time: float = field(init=False)
|
|
33
|
+
median_run_time: float = field(init=False)
|
|
34
|
+
p95_run_time: float = field(init=False)
|
|
35
|
+
|
|
36
|
+
# Memory performance in MiB
|
|
37
|
+
memory_usages: List[float] = field(default_factory=list)
|
|
38
|
+
avg_memory_usage: float = field(init=False)
|
|
39
|
+
min_memory_usage: float = field(init=False)
|
|
40
|
+
max_memory_usage: float = field(init=False)
|
|
41
|
+
std_dev_memory_usage: float = field(init=False)
|
|
42
|
+
median_memory_usage: float = field(init=False)
|
|
43
|
+
p95_memory_usage: float = field(init=False)
|
|
44
|
+
|
|
45
|
+
def __post_init__(self):
|
|
46
|
+
self.compute_stats()
|
|
47
|
+
|
|
48
|
+
def compute_stats(self):
|
|
49
|
+
"""Compute a variety of statistics for both runtime and memory usage."""
|
|
50
|
+
import statistics
|
|
51
|
+
|
|
52
|
+
def safe_stats(data: List[float]):
|
|
53
|
+
"""Compute stats for a non-empty list of floats."""
|
|
54
|
+
data_sorted = sorted(data) # ensure data is sorted for correct percentile
|
|
55
|
+
avg = statistics.mean(data_sorted)
|
|
56
|
+
mn = data_sorted[0]
|
|
57
|
+
mx = data_sorted[-1]
|
|
58
|
+
std = statistics.stdev(data_sorted) if len(data_sorted) > 1 else 0
|
|
59
|
+
med = statistics.median(data_sorted)
|
|
60
|
+
# For 95th percentile, use statistics.quantiles
|
|
61
|
+
p95 = statistics.quantiles(data_sorted, n=100)[94] if len(data_sorted) > 1 else 0
|
|
62
|
+
return avg, mn, mx, std, med, p95
|
|
63
|
+
|
|
64
|
+
# Populate runtime stats
|
|
65
|
+
if self.run_times:
|
|
66
|
+
(
|
|
67
|
+
self.avg_run_time,
|
|
68
|
+
self.min_run_time,
|
|
69
|
+
self.max_run_time,
|
|
70
|
+
self.std_dev_run_time,
|
|
71
|
+
self.median_run_time,
|
|
72
|
+
self.p95_run_time,
|
|
73
|
+
) = safe_stats(self.run_times)
|
|
74
|
+
else:
|
|
75
|
+
self.avg_run_time = 0
|
|
76
|
+
self.min_run_time = 0
|
|
77
|
+
self.max_run_time = 0
|
|
78
|
+
self.std_dev_run_time = 0
|
|
79
|
+
self.median_run_time = 0
|
|
80
|
+
self.p95_run_time = 0
|
|
81
|
+
|
|
82
|
+
# Populate memory stats
|
|
83
|
+
if self.memory_usages:
|
|
84
|
+
(
|
|
85
|
+
self.avg_memory_usage,
|
|
86
|
+
self.min_memory_usage,
|
|
87
|
+
self.max_memory_usage,
|
|
88
|
+
self.std_dev_memory_usage,
|
|
89
|
+
self.median_memory_usage,
|
|
90
|
+
self.p95_memory_usage,
|
|
91
|
+
) = safe_stats(self.memory_usages)
|
|
92
|
+
else:
|
|
93
|
+
self.avg_memory_usage = 0
|
|
94
|
+
self.min_memory_usage = 0
|
|
95
|
+
self.max_memory_usage = 0
|
|
96
|
+
self.std_dev_memory_usage = 0
|
|
97
|
+
self.median_memory_usage = 0
|
|
98
|
+
self.p95_memory_usage = 0
|
|
99
|
+
|
|
100
|
+
def print_summary(
|
|
101
|
+
self, console: Optional["Console"] = None, measure_memory: bool = True, measure_runtime: bool = True
|
|
102
|
+
):
|
|
103
|
+
"""
|
|
104
|
+
Prints a summary table of the computed stats.
|
|
105
|
+
"""
|
|
106
|
+
from rich.console import Console
|
|
107
|
+
from rich.table import Table
|
|
108
|
+
|
|
109
|
+
if console is None:
|
|
110
|
+
console = Console()
|
|
111
|
+
|
|
112
|
+
# Create performance table
|
|
113
|
+
perf_table = Table(title="Performance Summary", show_header=True, header_style="bold magenta")
|
|
114
|
+
perf_table.add_column("Metric", style="cyan")
|
|
115
|
+
if measure_runtime:
|
|
116
|
+
perf_table.add_column("Time (seconds)", style="green")
|
|
117
|
+
if measure_memory:
|
|
118
|
+
perf_table.add_column("Memory (MiB)", style="yellow")
|
|
119
|
+
|
|
120
|
+
# Add rows
|
|
121
|
+
if measure_runtime and measure_memory:
|
|
122
|
+
perf_table.add_row("Average", f"{self.avg_run_time:.6f}", f"{self.avg_memory_usage:.6f}")
|
|
123
|
+
perf_table.add_row("Minimum", f"{self.min_run_time:.6f}", f"{self.min_memory_usage:.6f}")
|
|
124
|
+
perf_table.add_row("Maximum", f"{self.max_run_time:.6f}", f"{self.max_memory_usage:.6f}")
|
|
125
|
+
perf_table.add_row("Std Dev", f"{self.std_dev_run_time:.6f}", f"{self.std_dev_memory_usage:.6f}")
|
|
126
|
+
perf_table.add_row("Median", f"{self.median_run_time:.6f}", f"{self.median_memory_usage:.6f}")
|
|
127
|
+
perf_table.add_row("95th %ile", f"{self.p95_run_time:.6f}", f"{self.p95_memory_usage:.6f}")
|
|
128
|
+
elif measure_runtime:
|
|
129
|
+
perf_table.add_row("Average", f"{self.avg_run_time:.6f}")
|
|
130
|
+
perf_table.add_row("Minimum", f"{self.min_run_time:.6f}")
|
|
131
|
+
perf_table.add_row("Maximum", f"{self.max_run_time:.6f}")
|
|
132
|
+
perf_table.add_row("Std Dev", f"{self.std_dev_run_time:.6f}")
|
|
133
|
+
perf_table.add_row("Median", f"{self.median_run_time:.6f}")
|
|
134
|
+
perf_table.add_row("95th %ile", f"{self.p95_run_time:.6f}")
|
|
135
|
+
elif measure_memory:
|
|
136
|
+
perf_table.add_row("Average", f"{self.avg_memory_usage:.6f}")
|
|
137
|
+
perf_table.add_row("Minimum", f"{self.min_memory_usage:.6f}")
|
|
138
|
+
perf_table.add_row("Maximum", f"{self.max_memory_usage:.6f}")
|
|
139
|
+
perf_table.add_row("Std Dev", f"{self.std_dev_memory_usage:.6f}")
|
|
140
|
+
perf_table.add_row("Median", f"{self.median_memory_usage:.6f}")
|
|
141
|
+
perf_table.add_row("95th %ile", f"{self.p95_memory_usage:.6f}")
|
|
142
|
+
|
|
143
|
+
console.print(perf_table)
|
|
144
|
+
|
|
145
|
+
def print_results(
|
|
146
|
+
self, console: Optional["Console"] = None, measure_memory: bool = True, measure_runtime: bool = True
|
|
147
|
+
):
|
|
148
|
+
"""
|
|
149
|
+
Prints individual run results in tabular form.
|
|
150
|
+
"""
|
|
151
|
+
from rich.console import Console
|
|
152
|
+
from rich.table import Table
|
|
153
|
+
|
|
154
|
+
if console is None:
|
|
155
|
+
console = Console()
|
|
156
|
+
|
|
157
|
+
# Create runs table
|
|
158
|
+
results_table = Table(title="Individual Runs", show_header=True, header_style="bold magenta")
|
|
159
|
+
results_table.add_column("Run #", style="cyan")
|
|
160
|
+
if measure_runtime:
|
|
161
|
+
results_table.add_column("Time (seconds)", style="green")
|
|
162
|
+
if measure_memory:
|
|
163
|
+
results_table.add_column("Memory (MiB)", style="yellow")
|
|
164
|
+
|
|
165
|
+
# Add rows
|
|
166
|
+
for i in range(len(self.run_times) or len(self.memory_usages)):
|
|
167
|
+
if measure_runtime and measure_memory:
|
|
168
|
+
results_table.add_row(str(i + 1), f"{self.run_times[i]:.6f}", f"{self.memory_usages[i]:.6f}")
|
|
169
|
+
elif measure_runtime:
|
|
170
|
+
results_table.add_row(str(i + 1), f"{self.run_times[i]:.6f}")
|
|
171
|
+
elif measure_memory:
|
|
172
|
+
results_table.add_row(str(i + 1), f"{self.memory_usages[i]:.6f}")
|
|
173
|
+
else:
|
|
174
|
+
results_table.add_row(str(i + 1))
|
|
175
|
+
|
|
176
|
+
console.print(results_table)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@dataclass
|
|
180
|
+
class PerformanceEval:
|
|
181
|
+
"""
|
|
182
|
+
Evaluate the performance of a function by measuring run time and peak memory usage.
|
|
183
|
+
|
|
184
|
+
- Warm-up runs are included to avoid measuring overhead on the first execution(s).
|
|
185
|
+
- Debug mode can show top memory allocations using tracemalloc snapshots.
|
|
186
|
+
- Optionally, you can enable cProfile for CPU profiling stats.
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
# Function to evaluate
|
|
190
|
+
func: Callable
|
|
191
|
+
measure_runtime: bool = True
|
|
192
|
+
measure_memory: bool = True
|
|
193
|
+
|
|
194
|
+
# Evaluation name
|
|
195
|
+
name: Optional[str] = None
|
|
196
|
+
# Evaluation UUID
|
|
197
|
+
eval_id: str = field(default_factory=lambda: str(uuid4()))
|
|
198
|
+
# Number of warm-up runs (not included in final stats)
|
|
199
|
+
warmup_runs: Optional[int] = 10
|
|
200
|
+
# Number of measured iterations
|
|
201
|
+
num_iterations: int = 50
|
|
202
|
+
# Result of the evaluation
|
|
203
|
+
result: Optional[PerformanceResult] = None
|
|
204
|
+
|
|
205
|
+
# Print summary of results
|
|
206
|
+
print_summary: bool = False
|
|
207
|
+
# Print detailed results
|
|
208
|
+
print_results: bool = False
|
|
209
|
+
# Print detailed memory growth analysis
|
|
210
|
+
memory_growth_tracking: bool = False
|
|
211
|
+
# Number of memory allocations to track
|
|
212
|
+
top_n_memory_allocations: int = 5
|
|
213
|
+
|
|
214
|
+
# Agent and Team information
|
|
215
|
+
agent_id: Optional[str] = None
|
|
216
|
+
team_id: Optional[str] = None
|
|
217
|
+
model_id: Optional[str] = None
|
|
218
|
+
model_provider: Optional[str] = None
|
|
219
|
+
|
|
220
|
+
# If set, results will be saved in the given file path
|
|
221
|
+
file_path_to_save_results: Optional[str] = None
|
|
222
|
+
# Enable debug logs
|
|
223
|
+
debug_mode: bool = getenv("AGNO_DEBUG", "false").lower() == "true"
|
|
224
|
+
# The database to store Evaluation results
|
|
225
|
+
db: Optional[Union[BaseDb, AsyncBaseDb]] = None
|
|
226
|
+
|
|
227
|
+
# Telemetry settings
|
|
228
|
+
# telemetry=True logs minimal telemetry for analytics
|
|
229
|
+
# This helps us improve our Evals and provide better support
|
|
230
|
+
telemetry: bool = True
|
|
231
|
+
|
|
232
|
+
def _measure_time(self) -> float:
|
|
233
|
+
"""Measure execution time for a single run."""
|
|
234
|
+
timer = Timer()
|
|
235
|
+
|
|
236
|
+
timer.start()
|
|
237
|
+
self.func()
|
|
238
|
+
timer.stop()
|
|
239
|
+
self._set_log_level() # Set log level incase function changed it
|
|
240
|
+
|
|
241
|
+
return timer.elapsed
|
|
242
|
+
|
|
243
|
+
async def _async_measure_time(self) -> float:
|
|
244
|
+
"""Measure execution time for a single run of the contextual async function."""
|
|
245
|
+
timer = Timer()
|
|
246
|
+
|
|
247
|
+
timer.start()
|
|
248
|
+
await self.func()
|
|
249
|
+
timer.stop()
|
|
250
|
+
self._set_log_level() # Set log level incase function changed it
|
|
251
|
+
|
|
252
|
+
return timer.elapsed
|
|
253
|
+
|
|
254
|
+
def _measure_memory(self, baseline: float) -> float:
|
|
255
|
+
"""
|
|
256
|
+
Measures peak memory usage using tracemalloc.
|
|
257
|
+
Subtracts the provided 'baseline' to compute an adjusted usage.
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
# Clear memory before measurement
|
|
261
|
+
gc.collect()
|
|
262
|
+
# Start tracing memory
|
|
263
|
+
tracemalloc.start()
|
|
264
|
+
|
|
265
|
+
self.func()
|
|
266
|
+
|
|
267
|
+
# Get peak memory usage
|
|
268
|
+
current, peak = tracemalloc.get_traced_memory()
|
|
269
|
+
|
|
270
|
+
# Stop tracing memory
|
|
271
|
+
tracemalloc.stop()
|
|
272
|
+
|
|
273
|
+
self._set_log_level() # Set log level incase function changed it
|
|
274
|
+
|
|
275
|
+
# Convert to MiB and subtract baseline
|
|
276
|
+
peak_mib = peak / 1024 / 1024
|
|
277
|
+
adjusted_usage = max(0, peak_mib - baseline)
|
|
278
|
+
|
|
279
|
+
if self.debug_mode:
|
|
280
|
+
log_debug(f"[DEBUG] Raw peak usage: {peak_mib:.6f} MiB, Adjusted: {adjusted_usage:.6f} MiB")
|
|
281
|
+
|
|
282
|
+
return adjusted_usage
|
|
283
|
+
|
|
284
|
+
def _parse_eval_run_data(self) -> dict:
|
|
285
|
+
"""Parse the evaluation result into a dictionary with the data we want for monitoring."""
|
|
286
|
+
if self.result is None:
|
|
287
|
+
return {}
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
"result": {
|
|
291
|
+
"avg_run_time": self.result.avg_run_time,
|
|
292
|
+
"min_run_time": self.result.min_run_time,
|
|
293
|
+
"max_run_time": self.result.max_run_time,
|
|
294
|
+
"std_dev_run_time": self.result.std_dev_run_time,
|
|
295
|
+
"median_run_time": self.result.median_run_time,
|
|
296
|
+
"p95_run_time": self.result.p95_run_time,
|
|
297
|
+
"avg_memory_usage": self.result.avg_memory_usage,
|
|
298
|
+
"min_memory_usage": self.result.min_memory_usage,
|
|
299
|
+
"max_memory_usage": self.result.max_memory_usage,
|
|
300
|
+
"std_dev_memory_usage": self.result.std_dev_memory_usage,
|
|
301
|
+
"median_memory_usage": self.result.median_memory_usage,
|
|
302
|
+
"p95_memory_usage": self.result.p95_memory_usage,
|
|
303
|
+
},
|
|
304
|
+
"runs": [
|
|
305
|
+
{"runtime": runtime, "memory": memory_usage}
|
|
306
|
+
for runtime, memory_usage in zip(self.result.run_times, self.result.memory_usages)
|
|
307
|
+
],
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async def _async_measure_memory(self, baseline: float) -> float:
|
|
311
|
+
"""
|
|
312
|
+
Measures peak memory usage using tracemalloc for async functions.
|
|
313
|
+
Subtracts the provided 'baseline' to compute an adjusted usage.
|
|
314
|
+
"""
|
|
315
|
+
|
|
316
|
+
# Clear memory before measurement
|
|
317
|
+
gc.collect()
|
|
318
|
+
# Start tracing memory
|
|
319
|
+
tracemalloc.start()
|
|
320
|
+
|
|
321
|
+
await self.func()
|
|
322
|
+
|
|
323
|
+
# Get peak memory usage
|
|
324
|
+
current, peak = tracemalloc.get_traced_memory()
|
|
325
|
+
|
|
326
|
+
# Stop tracing memory
|
|
327
|
+
tracemalloc.stop()
|
|
328
|
+
self._set_log_level() # Set log level incase function changed it
|
|
329
|
+
|
|
330
|
+
# Convert to MiB and subtract baseline
|
|
331
|
+
peak_mib = peak / 1024 / 1024
|
|
332
|
+
adjusted_usage = max(0, peak_mib - baseline)
|
|
333
|
+
|
|
334
|
+
if self.debug_mode:
|
|
335
|
+
log_debug(f"[DEBUG] Raw peak usage: {peak_mib:.6f} MiB, Adjusted: {adjusted_usage:.6f} MiB")
|
|
336
|
+
|
|
337
|
+
return adjusted_usage
|
|
338
|
+
|
|
339
|
+
def _compute_tracemalloc_baseline(self, samples: int = 3) -> float:
|
|
340
|
+
"""
|
|
341
|
+
Runs tracemalloc multiple times with an empty function to establish
|
|
342
|
+
a stable average baseline for memory usage in MiB.
|
|
343
|
+
"""
|
|
344
|
+
|
|
345
|
+
def empty_func():
|
|
346
|
+
return
|
|
347
|
+
|
|
348
|
+
results = []
|
|
349
|
+
for _ in range(samples):
|
|
350
|
+
gc.collect()
|
|
351
|
+
tracemalloc.start()
|
|
352
|
+
empty_func()
|
|
353
|
+
_, peak = tracemalloc.get_traced_memory()
|
|
354
|
+
tracemalloc.stop()
|
|
355
|
+
results.append(peak / 1024 / 1024)
|
|
356
|
+
|
|
357
|
+
return sum(results) / len(results) if results else 0
|
|
358
|
+
|
|
359
|
+
def _set_log_level(self):
|
|
360
|
+
if self.debug_mode:
|
|
361
|
+
set_log_level_to_debug()
|
|
362
|
+
else:
|
|
363
|
+
set_log_level_to_info()
|
|
364
|
+
|
|
365
|
+
def _compare_memory_snapshots(self, snapshot1, snapshot2, top_n: int):
|
|
366
|
+
"""
|
|
367
|
+
Compare two memory snapshots to identify what's causing memory growth.
|
|
368
|
+
"""
|
|
369
|
+
if self.debug_mode:
|
|
370
|
+
log_debug("[DEBUG] Memory growth analysis:")
|
|
371
|
+
|
|
372
|
+
# Compare snapshots to find new allocations
|
|
373
|
+
stats = snapshot2.compare_to(snapshot1, "lineno")
|
|
374
|
+
|
|
375
|
+
log_debug(f"[DEBUG] Top {top_n} memory growth sources:")
|
|
376
|
+
growth_found = False
|
|
377
|
+
for stat in stats[:top_n]:
|
|
378
|
+
if stat.size_diff > 0: # Only show growth
|
|
379
|
+
growth_found = True
|
|
380
|
+
log_debug(f" +{stat.size_diff / 1024 / 1024:.1f} MiB: {stat.count_diff} new blocks")
|
|
381
|
+
log_debug(f" {stat.traceback.format()}")
|
|
382
|
+
|
|
383
|
+
if not growth_found:
|
|
384
|
+
log_debug(" No significant memory growth detected between snapshots")
|
|
385
|
+
|
|
386
|
+
# Show total growth
|
|
387
|
+
total_growth = sum(stat.size_diff for stat in stats if stat.size_diff > 0)
|
|
388
|
+
total_shrinkage = sum(abs(stat.size_diff) for stat in stats if stat.size_diff < 0)
|
|
389
|
+
log_debug(f"[DEBUG] Total memory growth: {total_growth / 1024 / 1024:.1f} MiB")
|
|
390
|
+
log_debug(f"[DEBUG] Total memory freed: {total_shrinkage / 1024 / 1024:.1f} MiB")
|
|
391
|
+
log_debug(f"[DEBUG] Net memory change: {(total_growth - total_shrinkage) / 1024 / 1024:.1f} MiB")
|
|
392
|
+
|
|
393
|
+
def _measure_memory_with_growth_tracking(
|
|
394
|
+
self, baseline: float, previous_snapshot=None
|
|
395
|
+
) -> tuple[float, tracemalloc.Snapshot]:
|
|
396
|
+
"""
|
|
397
|
+
Enhanced memory measurement that tracks growth between runs.
|
|
398
|
+
Returns (adjusted_usage, current_snapshot)
|
|
399
|
+
"""
|
|
400
|
+
# Clear memory before measurement
|
|
401
|
+
gc.collect()
|
|
402
|
+
# Start tracing memory
|
|
403
|
+
tracemalloc.start()
|
|
404
|
+
|
|
405
|
+
self.func()
|
|
406
|
+
|
|
407
|
+
# Get peak memory usage
|
|
408
|
+
current, peak = tracemalloc.get_traced_memory()
|
|
409
|
+
# Take snapshot before stopping
|
|
410
|
+
current_snapshot = tracemalloc.take_snapshot()
|
|
411
|
+
# Stop tracing memory
|
|
412
|
+
tracemalloc.stop()
|
|
413
|
+
|
|
414
|
+
self._set_log_level() # Set log level incase function changed it
|
|
415
|
+
|
|
416
|
+
# Convert to MiB and subtract baseline
|
|
417
|
+
peak_mib = peak / 1024 / 1024
|
|
418
|
+
adjusted_usage = max(0, peak_mib - baseline)
|
|
419
|
+
|
|
420
|
+
if self.debug_mode and current_snapshot:
|
|
421
|
+
log_debug(f"[DEBUG] Raw peak usage: {peak_mib:.6f} MiB, Adjusted: {adjusted_usage:.6f} MiB")
|
|
422
|
+
|
|
423
|
+
# Compare with previous snapshot if available
|
|
424
|
+
if previous_snapshot is not None:
|
|
425
|
+
self._compare_memory_snapshots(previous_snapshot, current_snapshot, self.top_n_memory_allocations)
|
|
426
|
+
else:
|
|
427
|
+
# Get detailed memory allocation statistics
|
|
428
|
+
top_stats = current_snapshot.statistics("lineno")
|
|
429
|
+
|
|
430
|
+
log_debug(f"[DEBUG] Top {self.top_n_memory_allocations} memory allocations:")
|
|
431
|
+
for stat in top_stats[: self.top_n_memory_allocations]:
|
|
432
|
+
log_debug(f" {stat.count} blocks: {stat.size / 1024 / 1024:.1f} MiB")
|
|
433
|
+
log_debug(f" {stat.traceback.format()}")
|
|
434
|
+
|
|
435
|
+
return adjusted_usage, current_snapshot
|
|
436
|
+
|
|
437
|
+
async def _async_measure_memory_with_growth_tracking(
|
|
438
|
+
self, baseline: float, previous_snapshot=None
|
|
439
|
+
) -> tuple[float, tracemalloc.Snapshot]:
|
|
440
|
+
"""
|
|
441
|
+
Enhanced async memory measurement that tracks growth between runs.
|
|
442
|
+
Returns (adjusted_usage, current_snapshot)
|
|
443
|
+
"""
|
|
444
|
+
# Clear memory before measurement
|
|
445
|
+
gc.collect()
|
|
446
|
+
# Start tracing memory
|
|
447
|
+
tracemalloc.start()
|
|
448
|
+
|
|
449
|
+
await self.func()
|
|
450
|
+
|
|
451
|
+
# Get peak memory usage
|
|
452
|
+
current, peak = tracemalloc.get_traced_memory()
|
|
453
|
+
# Take snapshot before stopping
|
|
454
|
+
current_snapshot = tracemalloc.take_snapshot()
|
|
455
|
+
# Stop tracing memory
|
|
456
|
+
tracemalloc.stop()
|
|
457
|
+
|
|
458
|
+
self._set_log_level() # Set log level incase function changed it
|
|
459
|
+
|
|
460
|
+
# Convert to MiB and subtract baseline
|
|
461
|
+
peak_mib = peak / 1024 / 1024
|
|
462
|
+
adjusted_usage = max(0, peak_mib - baseline)
|
|
463
|
+
|
|
464
|
+
if self.debug_mode and current_snapshot:
|
|
465
|
+
log_debug(f"[DEBUG] Raw peak usage: {peak_mib:.6f} MiB, Adjusted: {adjusted_usage:.6f} MiB")
|
|
466
|
+
|
|
467
|
+
# Compare with previous snapshot if available
|
|
468
|
+
if previous_snapshot is not None:
|
|
469
|
+
self._compare_memory_snapshots(previous_snapshot, current_snapshot, self.top_n_memory_allocations)
|
|
470
|
+
else:
|
|
471
|
+
# Get detailed memory allocation statistics
|
|
472
|
+
top_stats = current_snapshot.statistics("lineno")
|
|
473
|
+
|
|
474
|
+
log_debug(f"[DEBUG] Top {self.top_n_memory_allocations} memory allocations:")
|
|
475
|
+
for stat in top_stats[: self.top_n_memory_allocations]:
|
|
476
|
+
log_debug(f" {stat.count} blocks: {stat.size / 1024 / 1024:.1f} MiB")
|
|
477
|
+
log_debug(f" {stat.traceback.format()}")
|
|
478
|
+
|
|
479
|
+
return adjusted_usage, current_snapshot
|
|
480
|
+
|
|
481
|
+
def run(
|
|
482
|
+
self, *, print_summary: bool = False, print_results: bool = False, memory_growth_tracking: bool = False
|
|
483
|
+
) -> PerformanceResult:
|
|
484
|
+
"""
|
|
485
|
+
Main method to run the performance evaluation.
|
|
486
|
+
1. Do optional warm-up runs.
|
|
487
|
+
2. Measure runtime
|
|
488
|
+
3. Measure memory
|
|
489
|
+
4. Collect results
|
|
490
|
+
5. Save results if requested
|
|
491
|
+
6. Print results as requested
|
|
492
|
+
7. Log results to the Agno platform if requested
|
|
493
|
+
"""
|
|
494
|
+
if isinstance(self.db, AsyncBaseDb):
|
|
495
|
+
raise ValueError("run() is not supported with an async DB. Please use arun() instead.")
|
|
496
|
+
|
|
497
|
+
from rich.console import Console
|
|
498
|
+
from rich.live import Live
|
|
499
|
+
from rich.status import Status
|
|
500
|
+
|
|
501
|
+
# Generate unique run_id for this execution (don't modify self.eval_id due to concurrency)
|
|
502
|
+
run_id = str(uuid4())
|
|
503
|
+
|
|
504
|
+
run_times = []
|
|
505
|
+
memory_usages = []
|
|
506
|
+
previous_snapshot = None
|
|
507
|
+
|
|
508
|
+
self._set_log_level()
|
|
509
|
+
|
|
510
|
+
log_debug(f"************ Evaluation Start: {run_id} ************")
|
|
511
|
+
|
|
512
|
+
# Add a spinner while running the evaluations
|
|
513
|
+
console = Console()
|
|
514
|
+
with Live(console=console, transient=True) as live_log:
|
|
515
|
+
# 1. Do optional warm-up runs.
|
|
516
|
+
if self.warmup_runs is not None:
|
|
517
|
+
for i in range(self.warmup_runs):
|
|
518
|
+
status = Status(f"Warm-up run {i + 1}/{self.warmup_runs}...", spinner="dots", speed=1.0)
|
|
519
|
+
live_log.update(status)
|
|
520
|
+
self.func() # Simply run the function without measuring
|
|
521
|
+
self._set_log_level() # Set log level incase function changed it
|
|
522
|
+
status.stop()
|
|
523
|
+
|
|
524
|
+
# 2. Measure runtime
|
|
525
|
+
if self.measure_runtime:
|
|
526
|
+
for i in range(self.num_iterations):
|
|
527
|
+
status = Status(
|
|
528
|
+
f"Runtime measurement {i + 1}/{self.num_iterations}...",
|
|
529
|
+
spinner="dots",
|
|
530
|
+
speed=1.0,
|
|
531
|
+
refresh_per_second=10,
|
|
532
|
+
)
|
|
533
|
+
live_log.update(status)
|
|
534
|
+
# Measure runtime
|
|
535
|
+
elapsed_time = self._measure_time()
|
|
536
|
+
run_times.append(elapsed_time)
|
|
537
|
+
|
|
538
|
+
log_debug(f"Run {i + 1} - Time taken: {elapsed_time:.6f} seconds")
|
|
539
|
+
status.stop()
|
|
540
|
+
|
|
541
|
+
# 3. Measure memory
|
|
542
|
+
if self.measure_memory:
|
|
543
|
+
# 3.1 Compute memory baseline
|
|
544
|
+
memory_baseline = 0.0
|
|
545
|
+
memory_baseline = self._compute_tracemalloc_baseline()
|
|
546
|
+
log_debug(f"Computed memory baseline: {memory_baseline:.6f} MiB")
|
|
547
|
+
|
|
548
|
+
for i in range(self.num_iterations):
|
|
549
|
+
status = Status(
|
|
550
|
+
f"Memory measurement {i + 1}/{self.num_iterations}...",
|
|
551
|
+
spinner="dots",
|
|
552
|
+
speed=1.0,
|
|
553
|
+
refresh_per_second=10,
|
|
554
|
+
)
|
|
555
|
+
live_log.update(status)
|
|
556
|
+
|
|
557
|
+
# Measure memory
|
|
558
|
+
if self.memory_growth_tracking or memory_growth_tracking:
|
|
559
|
+
usage, current_snapshot = self._measure_memory_with_growth_tracking(
|
|
560
|
+
memory_baseline, previous_snapshot
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
# Update previous snapshot for next iteration
|
|
564
|
+
previous_snapshot = current_snapshot
|
|
565
|
+
|
|
566
|
+
else:
|
|
567
|
+
usage = self._measure_memory(memory_baseline)
|
|
568
|
+
memory_usages.append(usage)
|
|
569
|
+
log_debug(f"Run {i + 1} - Memory usage: {usage:.6f} MiB (adjusted)")
|
|
570
|
+
|
|
571
|
+
status.stop()
|
|
572
|
+
|
|
573
|
+
# 4. Collect results
|
|
574
|
+
self.result = PerformanceResult(run_times=run_times, memory_usages=memory_usages)
|
|
575
|
+
|
|
576
|
+
# 5. Save result to file if requested
|
|
577
|
+
if self.file_path_to_save_results is not None and self.result is not None:
|
|
578
|
+
store_result_in_file(
|
|
579
|
+
file_path=self.file_path_to_save_results,
|
|
580
|
+
name=self.name,
|
|
581
|
+
eval_id=self.eval_id,
|
|
582
|
+
result=self.result,
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
# 6. Print results if requested
|
|
586
|
+
if self.print_results or print_results:
|
|
587
|
+
self.result.print_results(console, measure_memory=self.measure_memory, measure_runtime=self.measure_runtime)
|
|
588
|
+
if self.print_summary or print_summary:
|
|
589
|
+
self.result.print_summary(console, measure_memory=self.measure_memory, measure_runtime=self.measure_runtime)
|
|
590
|
+
|
|
591
|
+
# 7. Log results to the Agno platform if requested
|
|
592
|
+
if self.db:
|
|
593
|
+
eval_input = {
|
|
594
|
+
"num_iterations": self.num_iterations,
|
|
595
|
+
"warmup_runs": self.warmup_runs,
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
log_eval_run(
|
|
599
|
+
db=self.db,
|
|
600
|
+
run_id=self.eval_id, # type: ignore
|
|
601
|
+
run_data=self._parse_eval_run_data(),
|
|
602
|
+
eval_type=EvalType.PERFORMANCE,
|
|
603
|
+
name=self.name if self.name is not None else None,
|
|
604
|
+
evaluated_component_name=self.func.__name__,
|
|
605
|
+
agent_id=self.agent_id,
|
|
606
|
+
team_id=self.team_id,
|
|
607
|
+
model_id=self.model_id,
|
|
608
|
+
model_provider=self.model_provider,
|
|
609
|
+
eval_input=eval_input,
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
if self.telemetry:
|
|
613
|
+
from agno.api.evals import EvalRunCreate, create_eval_run_telemetry
|
|
614
|
+
|
|
615
|
+
create_eval_run_telemetry(
|
|
616
|
+
eval_run=EvalRunCreate(
|
|
617
|
+
run_id=self.eval_id, eval_type=EvalType.PERFORMANCE, data=self._get_telemetry_data()
|
|
618
|
+
),
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
log_debug(f"*********** Evaluation End: {run_id} ***********")
|
|
622
|
+
return self.result
|
|
623
|
+
|
|
624
|
+
async def arun(
|
|
625
|
+
self, *, print_summary: bool = False, print_results: bool = False, memory_growth_tracking: bool = False
|
|
626
|
+
) -> PerformanceResult:
|
|
627
|
+
"""
|
|
628
|
+
Async method to run the performance evaluation of async functions.
|
|
629
|
+
1. Do optional warm-up runs.
|
|
630
|
+
2. Measure runtime
|
|
631
|
+
3. Measure memory
|
|
632
|
+
4. Collect results
|
|
633
|
+
5. Save results if requested
|
|
634
|
+
6. Print results as requested
|
|
635
|
+
7. Log results to the Agno platform if requested
|
|
636
|
+
"""
|
|
637
|
+
# Validate the function to evaluate is async.
|
|
638
|
+
if not asyncio.iscoroutinefunction(self.func):
|
|
639
|
+
raise ValueError(
|
|
640
|
+
f"The provided function ({self.func.__name__}) is not async. Use the run() method for sync functions."
|
|
641
|
+
)
|
|
642
|
+
|
|
643
|
+
from rich.console import Console
|
|
644
|
+
from rich.live import Live
|
|
645
|
+
from rich.status import Status
|
|
646
|
+
|
|
647
|
+
# Generate unique run_id for this execution (don't modify self.eval_id due to concurrency)
|
|
648
|
+
run_id = str(uuid4())
|
|
649
|
+
|
|
650
|
+
run_times = []
|
|
651
|
+
memory_usages = []
|
|
652
|
+
previous_snapshot = None
|
|
653
|
+
|
|
654
|
+
self._set_log_level()
|
|
655
|
+
|
|
656
|
+
log_debug(f"************ Evaluation Start: {run_id} ************")
|
|
657
|
+
|
|
658
|
+
# Add a spinner while running the evaluations
|
|
659
|
+
console = Console()
|
|
660
|
+
with Live(console=console, transient=True) as live_log:
|
|
661
|
+
# 1. Do optional warm-up runs.
|
|
662
|
+
if self.warmup_runs is not None:
|
|
663
|
+
for i in range(self.warmup_runs):
|
|
664
|
+
status = Status(f"Warm-up run {i + 1}/{self.warmup_runs}...", spinner="dots", speed=1.0)
|
|
665
|
+
live_log.update(status)
|
|
666
|
+
await self.func() # Simply run the function without measuring
|
|
667
|
+
self._set_log_level() # Set log level incase function changed it
|
|
668
|
+
status.stop()
|
|
669
|
+
|
|
670
|
+
# 2. Measure runtime
|
|
671
|
+
if self.measure_runtime:
|
|
672
|
+
for i in range(self.num_iterations):
|
|
673
|
+
status = Status(
|
|
674
|
+
f"Runtime measurement {i + 1}/{self.num_iterations}...",
|
|
675
|
+
spinner="dots",
|
|
676
|
+
speed=1.0,
|
|
677
|
+
refresh_per_second=10,
|
|
678
|
+
)
|
|
679
|
+
live_log.update(status)
|
|
680
|
+
# Measure runtime
|
|
681
|
+
elapsed_time = await self._async_measure_time()
|
|
682
|
+
run_times.append(elapsed_time)
|
|
683
|
+
|
|
684
|
+
log_debug(f"Run {i + 1} - Time taken: {elapsed_time:.6f} seconds")
|
|
685
|
+
status.stop()
|
|
686
|
+
|
|
687
|
+
# 3. Measure memory
|
|
688
|
+
if self.measure_memory:
|
|
689
|
+
# 3.1 Compute memory baseline
|
|
690
|
+
memory_baseline = 0.0
|
|
691
|
+
memory_baseline = self._compute_tracemalloc_baseline()
|
|
692
|
+
log_debug(f"Computed memory baseline: {memory_baseline:.6f} MiB")
|
|
693
|
+
|
|
694
|
+
for i in range(self.num_iterations):
|
|
695
|
+
status = Status(
|
|
696
|
+
f"Memory measurement {i + 1}/{self.num_iterations}...",
|
|
697
|
+
spinner="dots",
|
|
698
|
+
speed=1.0,
|
|
699
|
+
refresh_per_second=10,
|
|
700
|
+
)
|
|
701
|
+
live_log.update(status)
|
|
702
|
+
|
|
703
|
+
# Measure memory
|
|
704
|
+
if self.memory_growth_tracking or memory_growth_tracking:
|
|
705
|
+
usage, current_snapshot = await self._async_measure_memory_with_growth_tracking(
|
|
706
|
+
memory_baseline, previous_snapshot
|
|
707
|
+
)
|
|
708
|
+
|
|
709
|
+
# Update previous snapshot for next iteration
|
|
710
|
+
previous_snapshot = current_snapshot
|
|
711
|
+
|
|
712
|
+
else:
|
|
713
|
+
usage = await self._async_measure_memory(memory_baseline)
|
|
714
|
+
memory_usages.append(usage)
|
|
715
|
+
log_debug(f"Run {i + 1} - Memory usage: {usage:.6f} MiB (adjusted)")
|
|
716
|
+
|
|
717
|
+
status.stop()
|
|
718
|
+
|
|
719
|
+
# 4. Collect results
|
|
720
|
+
self.result = PerformanceResult(run_times=run_times, memory_usages=memory_usages)
|
|
721
|
+
|
|
722
|
+
# 5. Save result to file if requested
|
|
723
|
+
if self.file_path_to_save_results is not None and self.result is not None:
|
|
724
|
+
store_result_in_file(
|
|
725
|
+
file_path=self.file_path_to_save_results,
|
|
726
|
+
name=self.name,
|
|
727
|
+
eval_id=self.eval_id,
|
|
728
|
+
result=self.result,
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
# 6. Print results if requested
|
|
732
|
+
if self.print_results or print_results:
|
|
733
|
+
self.result.print_results(console, measure_memory=self.measure_memory, measure_runtime=self.measure_runtime)
|
|
734
|
+
if self.print_summary or print_summary:
|
|
735
|
+
self.result.print_summary(console, measure_memory=self.measure_memory, measure_runtime=self.measure_runtime)
|
|
736
|
+
|
|
737
|
+
# 7. Log results to the Agno platform if requested
|
|
738
|
+
if self.db:
|
|
739
|
+
eval_input = {
|
|
740
|
+
"num_iterations": self.num_iterations,
|
|
741
|
+
"warmup_runs": self.warmup_runs,
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
await async_log_eval(
|
|
745
|
+
db=self.db,
|
|
746
|
+
run_id=self.eval_id, # type: ignore
|
|
747
|
+
run_data=self._parse_eval_run_data(),
|
|
748
|
+
eval_type=EvalType.PERFORMANCE,
|
|
749
|
+
name=self.name if self.name is not None else None,
|
|
750
|
+
evaluated_component_name=self.func.__name__,
|
|
751
|
+
agent_id=self.agent_id,
|
|
752
|
+
team_id=self.team_id,
|
|
753
|
+
model_id=self.model_id,
|
|
754
|
+
model_provider=self.model_provider,
|
|
755
|
+
eval_input=eval_input,
|
|
756
|
+
)
|
|
757
|
+
|
|
758
|
+
if self.telemetry:
|
|
759
|
+
from agno.api.evals import EvalRunCreate, async_create_eval_run_telemetry
|
|
760
|
+
|
|
761
|
+
await async_create_eval_run_telemetry(
|
|
762
|
+
eval_run=EvalRunCreate(
|
|
763
|
+
run_id=self.eval_id, eval_type=EvalType.PERFORMANCE, data=self._get_telemetry_data()
|
|
764
|
+
),
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
log_debug(f"*********** Evaluation End: {run_id} ***********")
|
|
768
|
+
return self.result
|
|
769
|
+
|
|
770
|
+
def _get_telemetry_data(self) -> Dict[str, Any]:
|
|
771
|
+
"""Get the telemetry data for the evaluation"""
|
|
772
|
+
return {
|
|
773
|
+
"model_id": self.model_id,
|
|
774
|
+
"model_provider": self.model_provider,
|
|
775
|
+
"num_iterations": self.num_iterations,
|
|
776
|
+
"warmup_runs": self.warmup_runs,
|
|
777
|
+
"measure_memory": self.measure_memory,
|
|
778
|
+
"measure_runtime": self.measure_runtime,
|
|
779
|
+
}
|