agno 1.8.0__py3-none-any.whl → 2.0.0a1__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 +2781 -4126
- agno/api/agent.py +9 -65
- agno/api/api.py +5 -46
- agno/api/evals.py +6 -17
- 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 +9 -64
- 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 +1749 -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 +1438 -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 +888 -0
- agno/db/in_memory/utils.py +172 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1051 -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 +1417 -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 +298 -0
- agno/db/postgres/__init__.py +3 -0
- agno/db/postgres/postgres.py +1720 -0
- agno/db/postgres/schemas.py +124 -0
- agno/db/postgres/utils.py +281 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +1371 -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 +1722 -0
- agno/db/singlestore/utils.py +327 -0
- agno/db/sqlite/__init__.py +3 -0
- agno/db/sqlite/schemas.py +119 -0
- agno/db/sqlite/sqlite.py +1680 -0
- agno/db/sqlite/utils.py +269 -0
- agno/db/utils.py +88 -0
- agno/eval/__init__.py +14 -0
- agno/eval/accuracy.py +142 -43
- 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 +10 -10
- 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 +1515 -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 +68 -15
- agno/knowledge/reader/docx_reader.py +83 -0
- agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
- agno/knowledge/reader/gcs_reader.py +67 -0
- agno/{document → knowledge}/reader/json_reader.py +30 -9
- agno/{document → knowledge}/reader/markdown_reader.py +36 -9
- agno/{document → knowledge}/reader/pdf_reader.py +79 -21
- agno/knowledge/reader/reader_factory.py +275 -0
- agno/knowledge/reader/s3_reader.py +171 -0
- agno/{document → knowledge}/reader/text_reader.py +31 -10
- agno/knowledge/reader/url_reader.py +84 -0
- agno/knowledge/reader/web_search_reader.py +389 -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 +2 -2
- 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 +129 -82
- agno/models/aws/bedrock.py +107 -175
- agno/models/aws/claude.py +64 -18
- agno/models/azure/ai_foundry.py +73 -23
- agno/models/base.py +347 -287
- agno/models/cerebras/cerebras.py +84 -27
- agno/models/cohere/chat.py +106 -98
- agno/models/dashscope/dashscope.py +14 -5
- agno/models/google/gemini.py +123 -53
- 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 +38 -144
- 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 +84 -46
- agno/models/openai/chat.py +135 -27
- agno/models/openai/responses.py +233 -115
- agno/models/perplexity/perplexity.py +26 -2
- agno/models/portkey/portkey.py +0 -7
- agno/models/response.py +14 -8
- 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 +393 -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 +65 -28
- 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 +33 -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 +30 -0
- agno/os/router.py +843 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +204 -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 +413 -0
- agno/os/routers/knowledge/schemas.py +118 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +179 -0
- agno/os/routers/memory/schemas.py +58 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +58 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +163 -0
- agno/os/schema.py +892 -0
- agno/{app/playground → os}/settings.py +8 -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/{response.py → agent.py} +144 -72
- agno/run/base.py +44 -58
- agno/run/cancel.py +83 -0
- agno/run/team.py +133 -77
- agno/run/workflow.py +537 -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 +2967 -4243
- 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 +42 -22
- agno/tools/browserbase.py +13 -4
- agno/tools/calcom.py +12 -10
- agno/tools/calculator.py +10 -27
- agno/tools/cartesia.py +18 -13
- agno/tools/{clickup_tool.py → clickup.py} +12 -25
- agno/tools/confluence.py +71 -18
- agno/tools/crawl4ai.py +7 -1
- agno/tools/csv_toolkit.py +9 -8
- agno/tools/dalle.py +18 -11
- agno/tools/daytona.py +13 -16
- agno/tools/decorator.py +6 -3
- agno/tools/desi_vocal.py +16 -7
- 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 +35 -28
- agno/tools/email.py +4 -1
- agno/tools/evm.py +7 -1
- agno/tools/exa.py +19 -14
- agno/tools/fal.py +29 -29
- agno/tools/file.py +9 -8
- agno/tools/financial_datasets.py +25 -44
- agno/tools/firecrawl.py +22 -22
- agno/tools/function.py +68 -17
- agno/tools/giphy.py +22 -10
- agno/tools/github.py +48 -126
- agno/tools/gmail.py +46 -62
- 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 +18 -33
- agno/tools/linkup.py +5 -1
- agno/tools/local_file_system.py +8 -5
- agno/tools/lumalab.py +31 -19
- 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 +32 -14
- agno/tools/models/gemini.py +58 -31
- agno/tools/models/groq.py +29 -20
- agno/tools/models/nebius.py +27 -11
- agno/tools/models_labs.py +39 -15
- agno/tools/moviepy_video.py +7 -6
- agno/tools/neo4j.py +134 -0
- agno/tools/newspaper.py +7 -2
- agno/tools/newspaper4k.py +8 -3
- agno/tools/openai.py +57 -26
- agno/tools/openbb.py +12 -11
- agno/tools/opencv.py +62 -46
- 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 +54 -41
- 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 +95 -118
- agno/utils/knowledge.py +29 -0
- agno/utils/location.py +2 -2
- agno/utils/log.py +2 -2
- agno/utils/mcp.py +11 -5
- agno/utils/media.py +39 -0
- agno/utils/message.py +12 -1
- agno/utils/models/claude.py +6 -4
- agno/utils/models/mistral.py +8 -7
- agno/utils/models/schema_utils.py +3 -3
- agno/utils/pprint.py +33 -32
- agno/utils/print_response/agent.py +779 -0
- agno/utils/print_response/team.py +1565 -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/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 +356 -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} +200 -42
- agno/workflow/{v2/steps.py → steps.py} +147 -66
- agno/workflow/types.py +482 -0
- agno/workflow/workflow.py +2394 -696
- agno-2.0.0a1.dist-info/METADATA +355 -0
- agno-2.0.0a1.dist-info/RECORD +514 -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 -698
- 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/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.0.dist-info/METADATA +0 -979
- agno-1.8.0.dist-info/RECORD +0 -565
- agno-1.8.0.dist-info/entry_points.txt +0 -3
- /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.0.dist-info → agno-2.0.0a1.dist-info}/WHEEL +0 -0
- {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/licenses/LICENSE +0 -0
- {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/top_level.txt +0 -0
agno/db/sqlite/utils.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import time
|
|
3
|
+
from datetime import date, datetime, timedelta, timezone
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
from uuid import uuid4
|
|
6
|
+
|
|
7
|
+
from agno.db.sqlite.schemas import get_table_schema_definition
|
|
8
|
+
from agno.utils.log import log_debug, log_error, log_warning
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
from sqlalchemy import Table
|
|
12
|
+
from sqlalchemy.dialects import sqlite
|
|
13
|
+
from sqlalchemy.engine import Engine
|
|
14
|
+
from sqlalchemy.inspection import inspect
|
|
15
|
+
from sqlalchemy.orm import Session
|
|
16
|
+
from sqlalchemy.sql.expression import text
|
|
17
|
+
except ImportError:
|
|
18
|
+
raise ImportError("`sqlalchemy` not installed. Please install it using `pip install sqlalchemy`")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# -- DB util methods --
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def apply_sorting(stmt, table: Table, sort_by: Optional[str] = None, sort_order: Optional[str] = None):
|
|
25
|
+
"""Apply sorting to the given SQLAlchemy statement.
|
|
26
|
+
Args:
|
|
27
|
+
stmt: The SQLAlchemy statement to modify
|
|
28
|
+
table: The table being queried
|
|
29
|
+
sort_by: The field to sort by
|
|
30
|
+
sort_order: The sort order ('asc' or 'desc')
|
|
31
|
+
Returns:
|
|
32
|
+
The modified statement with sorting applied
|
|
33
|
+
"""
|
|
34
|
+
if sort_by is None:
|
|
35
|
+
return stmt
|
|
36
|
+
if not hasattr(table.c, sort_by):
|
|
37
|
+
log_debug(f"Invalid sort field: '{sort_by}'. Will not apply any sorting.")
|
|
38
|
+
return stmt
|
|
39
|
+
|
|
40
|
+
# Apply the given sorting
|
|
41
|
+
sort_column = getattr(table.c, sort_by)
|
|
42
|
+
if sort_order and sort_order == "asc":
|
|
43
|
+
return stmt.order_by(sort_column.asc())
|
|
44
|
+
else:
|
|
45
|
+
return stmt.order_by(sort_column.desc())
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def is_table_available(session: Session, table_name: str, db_schema: Optional[str] = None) -> bool:
|
|
49
|
+
"""
|
|
50
|
+
Check if a table with the given name exists.
|
|
51
|
+
Note: db_schema parameter is ignored in SQLite but kept for API compatibility.
|
|
52
|
+
Returns:
|
|
53
|
+
bool: True if the table exists, False otherwise.
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
# SQLite uses sqlite_master instead of information_schema
|
|
57
|
+
exists_query = text("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = :table")
|
|
58
|
+
exists = session.execute(exists_query, {"table": table_name}).scalar() is not None
|
|
59
|
+
if not exists:
|
|
60
|
+
log_debug(f"Table {table_name} {'exists' if exists else 'does not exist'}")
|
|
61
|
+
return exists
|
|
62
|
+
except Exception as e:
|
|
63
|
+
log_error(f"Error checking if table exists: {e}")
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def is_valid_table(db_engine: Engine, table_name: str, table_type: str, db_schema: Optional[str] = None) -> bool:
|
|
68
|
+
"""
|
|
69
|
+
Check if the existing table has the expected column names.
|
|
70
|
+
Note: db_schema parameter is ignored in SQLite but kept for API compatibility.
|
|
71
|
+
Args:
|
|
72
|
+
db_engine (Engine): Database engine
|
|
73
|
+
table_name (str): Name of the table to validate
|
|
74
|
+
table_type (str): Type of table to get expected schema
|
|
75
|
+
db_schema (Optional[str]): Database schema name (ignored in SQLite)
|
|
76
|
+
Returns:
|
|
77
|
+
bool: True if table has all expected columns, False otherwise
|
|
78
|
+
"""
|
|
79
|
+
try:
|
|
80
|
+
expected_table_schema = get_table_schema_definition(table_type)
|
|
81
|
+
expected_columns = {col_name for col_name in expected_table_schema.keys() if not col_name.startswith("_")}
|
|
82
|
+
|
|
83
|
+
# Get existing columns (no schema parameter for SQLite)
|
|
84
|
+
inspector = inspect(db_engine)
|
|
85
|
+
existing_columns_info = inspector.get_columns(table_name) # No schema parameter
|
|
86
|
+
existing_columns = set(col["name"] for col in existing_columns_info)
|
|
87
|
+
|
|
88
|
+
# Check if all expected columns exist
|
|
89
|
+
missing_columns = expected_columns - existing_columns
|
|
90
|
+
if missing_columns:
|
|
91
|
+
log_warning(f"Missing columns {missing_columns} in table {table_name}")
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
log_debug(f"Table {table_name} has all expected columns")
|
|
95
|
+
return True
|
|
96
|
+
except Exception as e:
|
|
97
|
+
log_error(f"Error validating table schema for {table_name}: {e}")
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# -- Metrics util methods --
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def bulk_upsert_metrics(session: Session, table: Table, metrics_records: list[dict]) -> list[dict]:
|
|
105
|
+
"""Bulk upsert metrics into the database.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
table (Table): The table to upsert into.
|
|
109
|
+
metrics_records (list[dict]): The metrics records to upsert.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
list[dict]: The upserted metrics records.
|
|
113
|
+
"""
|
|
114
|
+
if not metrics_records:
|
|
115
|
+
return []
|
|
116
|
+
|
|
117
|
+
results = []
|
|
118
|
+
stmt = sqlite.insert(table)
|
|
119
|
+
|
|
120
|
+
# Columns to update in case of conflict
|
|
121
|
+
update_columns = {
|
|
122
|
+
col.name: stmt.excluded[col.name]
|
|
123
|
+
for col in table.columns
|
|
124
|
+
if col.name not in ["id", "date", "created_at", "aggregation_period"]
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
stmt = stmt.on_conflict_do_update(index_elements=["date", "aggregation_period"], set_=update_columns).returning( # type: ignore
|
|
128
|
+
table
|
|
129
|
+
)
|
|
130
|
+
result = session.execute(stmt, metrics_records)
|
|
131
|
+
results = [row._mapping for row in result.fetchall()]
|
|
132
|
+
session.commit()
|
|
133
|
+
|
|
134
|
+
return results # type: ignore
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
138
|
+
"""Calculate metrics for the given single date.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
date_to_process (date): The date to calculate metrics for.
|
|
142
|
+
sessions_data (dict): The sessions data to calculate metrics for.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
dict: The calculated metrics.
|
|
146
|
+
"""
|
|
147
|
+
metrics = {
|
|
148
|
+
"users_count": 0,
|
|
149
|
+
"agent_sessions_count": 0,
|
|
150
|
+
"team_sessions_count": 0,
|
|
151
|
+
"workflow_sessions_count": 0,
|
|
152
|
+
"agent_runs_count": 0,
|
|
153
|
+
"team_runs_count": 0,
|
|
154
|
+
"workflow_runs_count": 0,
|
|
155
|
+
}
|
|
156
|
+
token_metrics = {
|
|
157
|
+
"input_tokens": 0,
|
|
158
|
+
"output_tokens": 0,
|
|
159
|
+
"total_tokens": 0,
|
|
160
|
+
"audio_total_tokens": 0,
|
|
161
|
+
"audio_input_tokens": 0,
|
|
162
|
+
"audio_output_tokens": 0,
|
|
163
|
+
"cache_read_tokens": 0,
|
|
164
|
+
"cache_write_tokens": 0,
|
|
165
|
+
"reasoning_tokens": 0,
|
|
166
|
+
}
|
|
167
|
+
model_counts: Dict[str, int] = {}
|
|
168
|
+
|
|
169
|
+
session_types = [
|
|
170
|
+
("agent", "agent_sessions_count", "agent_runs_count"),
|
|
171
|
+
("team", "team_sessions_count", "team_runs_count"),
|
|
172
|
+
("workflow", "workflow_sessions_count", "workflow_runs_count"),
|
|
173
|
+
]
|
|
174
|
+
all_user_ids = set()
|
|
175
|
+
|
|
176
|
+
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
177
|
+
sessions = sessions_data.get(session_type, [])
|
|
178
|
+
metrics[sessions_count_key] = len(sessions)
|
|
179
|
+
|
|
180
|
+
for session in sessions:
|
|
181
|
+
if session.get("user_id"):
|
|
182
|
+
all_user_ids.add(session["user_id"])
|
|
183
|
+
metrics[runs_count_key] += len(session.get("runs", []))
|
|
184
|
+
if runs := session.get("runs", []):
|
|
185
|
+
runs = json.loads(runs)
|
|
186
|
+
for run in runs:
|
|
187
|
+
if model_id := run.get("model"):
|
|
188
|
+
model_provider = run.get("model_provider", "")
|
|
189
|
+
model_counts[f"{model_id}:{model_provider}"] = (
|
|
190
|
+
model_counts.get(f"{model_id}:{model_provider}", 0) + 1
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
session_data = json.loads(session.get("session_data", {}))
|
|
194
|
+
session_metrics = session_data.get("session_metrics", {})
|
|
195
|
+
for field in token_metrics:
|
|
196
|
+
token_metrics[field] += session_metrics.get(field, 0)
|
|
197
|
+
|
|
198
|
+
model_metrics = []
|
|
199
|
+
for model, count in model_counts.items():
|
|
200
|
+
model_id, model_provider = model.split(":")
|
|
201
|
+
model_metrics.append({"model_id": model_id, "model_provider": model_provider, "count": count})
|
|
202
|
+
|
|
203
|
+
metrics["users_count"] = len(all_user_ids)
|
|
204
|
+
current_time = int(time.time())
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
"id": str(uuid4()),
|
|
208
|
+
"date": date_to_process,
|
|
209
|
+
"completed": date_to_process < datetime.now(timezone.utc).date(),
|
|
210
|
+
"token_metrics": token_metrics,
|
|
211
|
+
"model_metrics": model_metrics,
|
|
212
|
+
"created_at": current_time,
|
|
213
|
+
"updated_at": current_time,
|
|
214
|
+
"aggregation_period": "daily",
|
|
215
|
+
**metrics,
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def fetch_all_sessions_data(
|
|
220
|
+
sessions: List[Dict[str, Any]], dates_to_process: list[date], start_timestamp: int
|
|
221
|
+
) -> Optional[dict]:
|
|
222
|
+
"""Return all session data for the given dates, for all session types.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
dates_to_process (list[date]): The dates to fetch session data for.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
dict: A dictionary with dates as keys and session data as values, for all session types.
|
|
229
|
+
|
|
230
|
+
Example:
|
|
231
|
+
{
|
|
232
|
+
"2000-01-01": {
|
|
233
|
+
"agent": [<session1>, <session2>, ...],
|
|
234
|
+
"team": [...],
|
|
235
|
+
"workflow": [...],
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
"""
|
|
239
|
+
if not dates_to_process:
|
|
240
|
+
return None
|
|
241
|
+
|
|
242
|
+
all_sessions_data: Dict[str, Dict[str, List[Dict[str, Any]]]] = {
|
|
243
|
+
date_to_process.isoformat(): {"agent": [], "team": [], "workflow": []} for date_to_process in dates_to_process
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
for session in sessions:
|
|
247
|
+
session_date = (
|
|
248
|
+
datetime.fromtimestamp(session.get("created_at", start_timestamp), tz=timezone.utc).date().isoformat()
|
|
249
|
+
)
|
|
250
|
+
if session_date in all_sessions_data:
|
|
251
|
+
all_sessions_data[session_date][session["session_type"]].append(session)
|
|
252
|
+
|
|
253
|
+
return all_sessions_data
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def get_dates_to_calculate_metrics_for(starting_date: date) -> list[date]:
|
|
257
|
+
"""Return the list of dates to calculate metrics for.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
starting_date (date): The starting date to calculate metrics for.
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
list[date]: The list of dates to calculate metrics for.
|
|
264
|
+
"""
|
|
265
|
+
today = datetime.now(timezone.utc).date()
|
|
266
|
+
days_diff = (today - starting_date).days + 1
|
|
267
|
+
if days_diff <= 0:
|
|
268
|
+
return []
|
|
269
|
+
return [starting_date + timedelta(days=x) for x in range(days_diff)]
|
agno/db/utils.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Logic shared across different database implementations"""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from datetime import date, datetime
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from agno.db.base import SessionType
|
|
8
|
+
from agno.models.message import Message
|
|
9
|
+
from agno.models.metrics import Metrics
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CustomJSONEncoder(json.JSONEncoder):
|
|
13
|
+
"""Custom encoder to handle non JSON serializable types."""
|
|
14
|
+
|
|
15
|
+
def default(self, obj):
|
|
16
|
+
if isinstance(obj, UUID):
|
|
17
|
+
return str(obj)
|
|
18
|
+
elif isinstance(obj, (date, datetime)):
|
|
19
|
+
return obj.isoformat()
|
|
20
|
+
elif isinstance(obj, Message):
|
|
21
|
+
return obj.to_dict()
|
|
22
|
+
elif isinstance(obj, Metrics):
|
|
23
|
+
return obj.to_dict()
|
|
24
|
+
|
|
25
|
+
return super().default(obj)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def serialize_session_json_fields(session: dict) -> dict:
|
|
29
|
+
"""Serialize all JSON fields in the given Session dictionary.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
data (dict): The dictionary to serialize JSON fields in.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
dict: The dictionary with JSON fields serialized.
|
|
36
|
+
"""
|
|
37
|
+
if session.get("session_data") is not None:
|
|
38
|
+
session["session_data"] = json.dumps(session["session_data"])
|
|
39
|
+
if session.get("agent_data") is not None:
|
|
40
|
+
session["agent_data"] = json.dumps(session["agent_data"])
|
|
41
|
+
if session.get("team_data") is not None:
|
|
42
|
+
session["team_data"] = json.dumps(session["team_data"])
|
|
43
|
+
if session.get("workflow_data") is not None:
|
|
44
|
+
session["workflow_data"] = json.dumps(session["workflow_data"])
|
|
45
|
+
if session.get("metadata") is not None:
|
|
46
|
+
session["metadata"] = json.dumps(session["metadata"])
|
|
47
|
+
if session.get("chat_history") is not None:
|
|
48
|
+
session["chat_history"] = json.dumps(session["chat_history"])
|
|
49
|
+
if session.get("summary") is not None:
|
|
50
|
+
session["summary"] = json.dumps(session["summary"], cls=CustomJSONEncoder)
|
|
51
|
+
if session.get("runs") is not None:
|
|
52
|
+
session["runs"] = json.dumps(session["runs"], cls=CustomJSONEncoder)
|
|
53
|
+
|
|
54
|
+
return session
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def deserialize_session_json_fields(session: dict) -> dict:
|
|
58
|
+
"""Deserialize all JSON fields in the given Session dictionary.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
session (dict): The dictionary to deserialize.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
dict: The dictionary with JSON fields deserialized.
|
|
65
|
+
"""
|
|
66
|
+
if session.get("agent_data") is not None:
|
|
67
|
+
session["agent_data"] = json.loads(session["agent_data"])
|
|
68
|
+
if session.get("team_data") is not None:
|
|
69
|
+
session["team_data"] = json.loads(session["team_data"])
|
|
70
|
+
if session.get("workflow_data") is not None:
|
|
71
|
+
session["workflow_data"] = json.loads(session["workflow_data"])
|
|
72
|
+
if session.get("metadata") is not None:
|
|
73
|
+
session["metadata"] = json.loads(session["metadata"])
|
|
74
|
+
if session.get("chat_history") is not None:
|
|
75
|
+
session["chat_history"] = json.loads(session["chat_history"])
|
|
76
|
+
if session.get("summary") is not None:
|
|
77
|
+
session["summary"] = json.loads(session["summary"])
|
|
78
|
+
if session.get("session_data") is not None and isinstance(session["session_data"], str):
|
|
79
|
+
session["session_data"] = json.loads(session["session_data"])
|
|
80
|
+
if session.get("runs") is not None:
|
|
81
|
+
if session["session_type"] == SessionType.AGENT.value:
|
|
82
|
+
session["runs"] = json.loads(session["runs"])
|
|
83
|
+
if session["session_type"] == SessionType.TEAM.value:
|
|
84
|
+
session["runs"] = json.loads(session["runs"])
|
|
85
|
+
if session["session_type"] == SessionType.WORKFLOW.value:
|
|
86
|
+
session["runs"] = json.loads(session["runs"])
|
|
87
|
+
|
|
88
|
+
return session
|
agno/eval/__init__.py
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from agno.eval.accuracy import AccuracyAgentResponse, AccuracyEval, AccuracyEvaluation, AccuracyResult
|
|
2
|
+
from agno.eval.performance import PerformanceEval, PerformanceResult
|
|
3
|
+
from agno.eval.reliability import ReliabilityEval, ReliabilityResult
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"AccuracyAgentResponse",
|
|
7
|
+
"AccuracyEvaluation",
|
|
8
|
+
"AccuracyResult",
|
|
9
|
+
"AccuracyEval",
|
|
10
|
+
"PerformanceEval",
|
|
11
|
+
"PerformanceResult",
|
|
12
|
+
"ReliabilityEval",
|
|
13
|
+
"ReliabilityResult",
|
|
14
|
+
]
|
agno/eval/accuracy.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
from dataclasses import asdict, dataclass, field
|
|
2
2
|
from os import getenv
|
|
3
3
|
from textwrap import dedent
|
|
4
|
-
from typing import TYPE_CHECKING, Callable, List, Optional, Union
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
|
|
7
7
|
from pydantic import BaseModel, Field
|
|
8
8
|
|
|
9
9
|
from agno.agent import Agent
|
|
10
|
-
from agno.
|
|
11
|
-
from agno.
|
|
10
|
+
from agno.db.base import BaseDb
|
|
11
|
+
from agno.db.schemas.evals import EvalType
|
|
12
|
+
from agno.eval.utils import async_log_eval, log_eval_run, store_result_in_file
|
|
12
13
|
from agno.exceptions import EvalError
|
|
13
14
|
from agno.models.base import Model
|
|
14
15
|
from agno.team.team import Team
|
|
@@ -167,8 +168,13 @@ class AccuracyEval:
|
|
|
167
168
|
file_path_to_save_results: Optional[str] = None
|
|
168
169
|
# Enable debug logs
|
|
169
170
|
debug_mode: bool = getenv("AGNO_DEBUG", "false").lower() == "true"
|
|
170
|
-
#
|
|
171
|
-
|
|
171
|
+
# The database to store Evaluation results
|
|
172
|
+
db: Optional[BaseDb] = None
|
|
173
|
+
|
|
174
|
+
# Telemetry settings
|
|
175
|
+
# telemetry=True logs minimal telemetry for analytics
|
|
176
|
+
# This helps us improve our Evals and provide better support
|
|
177
|
+
telemetry: bool = True
|
|
172
178
|
|
|
173
179
|
def get_evaluator_agent(self) -> Agent:
|
|
174
180
|
"""Return the evaluator agent. If not provided, build it based on the evaluator fields and default instructions."""
|
|
@@ -235,7 +241,7 @@ You are an expert judge tasked with comparing the quality of an AI Agent’s out
|
|
|
235
241
|
{additional_guidelines}{additional_context}
|
|
236
242
|
Remember: You must only compare the agent_output to the expected_output. The expected_output is correct as it was provided by the user.
|
|
237
243
|
""",
|
|
238
|
-
|
|
244
|
+
output_schema=AccuracyAgentResponse,
|
|
239
245
|
structured_outputs=True,
|
|
240
246
|
)
|
|
241
247
|
|
|
@@ -344,9 +350,9 @@ Remember: You must only compare the agent_output to the expected_output. The exp
|
|
|
344
350
|
live_log.update(status)
|
|
345
351
|
|
|
346
352
|
if self.agent is not None:
|
|
347
|
-
output = self.agent.run(
|
|
353
|
+
output = self.agent.run(input=eval_input).content
|
|
348
354
|
elif self.team is not None:
|
|
349
|
-
output = self.team.run(
|
|
355
|
+
output = self.team.run(input=eval_input).content
|
|
350
356
|
|
|
351
357
|
if not output:
|
|
352
358
|
logger.error(f"Failed to generate a valid answer on iteration {i + 1}: {output}")
|
|
@@ -398,22 +404,31 @@ Remember: You must only compare the agent_output to the expected_output. The exp
|
|
|
398
404
|
if self.print_summary or print_summary:
|
|
399
405
|
self.result.print_summary(console)
|
|
400
406
|
|
|
401
|
-
# Log results to the Agno
|
|
407
|
+
# Log results to the Agno DB if requested
|
|
402
408
|
if self.agent is not None:
|
|
403
|
-
agent_id = self.agent.
|
|
409
|
+
agent_id = self.agent.id
|
|
404
410
|
team_id = None
|
|
405
411
|
model_id = self.agent.model.id if self.agent.model is not None else None
|
|
406
412
|
model_provider = self.agent.model.provider if self.agent.model is not None else None
|
|
407
|
-
|
|
413
|
+
evaluated_component_name = self.agent.name
|
|
408
414
|
elif self.team is not None:
|
|
409
415
|
agent_id = None
|
|
410
|
-
team_id = self.team.
|
|
416
|
+
team_id = self.team.id
|
|
411
417
|
model_id = self.team.model.id if self.team.model is not None else None
|
|
412
418
|
model_provider = self.team.model.provider if self.team.model is not None else None
|
|
413
|
-
|
|
419
|
+
evaluated_component_name = self.team.name
|
|
420
|
+
|
|
421
|
+
if self.db:
|
|
422
|
+
log_eval_input = {
|
|
423
|
+
"additional_guidelines": self.additional_guidelines,
|
|
424
|
+
"additional_context": self.additional_context,
|
|
425
|
+
"num_iterations": self.num_iterations,
|
|
426
|
+
"expected_output": self.expected_output,
|
|
427
|
+
"input": self.input,
|
|
428
|
+
}
|
|
414
429
|
|
|
415
|
-
if self.monitoring:
|
|
416
430
|
log_eval_run(
|
|
431
|
+
db=self.db,
|
|
417
432
|
run_id=self.eval_id, # type: ignore
|
|
418
433
|
run_data=asdict(self.result),
|
|
419
434
|
eval_type=EvalType.ACCURACY,
|
|
@@ -422,7 +437,19 @@ Remember: You must only compare the agent_output to the expected_output. The exp
|
|
|
422
437
|
model_id=model_id,
|
|
423
438
|
model_provider=model_provider,
|
|
424
439
|
name=self.name if self.name is not None else None,
|
|
425
|
-
|
|
440
|
+
evaluated_component_name=evaluated_component_name,
|
|
441
|
+
eval_input=log_eval_input,
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
if self.telemetry:
|
|
445
|
+
from agno.api.evals import EvalRunCreate, create_eval_run_telemetry
|
|
446
|
+
|
|
447
|
+
create_eval_run_telemetry(
|
|
448
|
+
eval_run=EvalRunCreate(
|
|
449
|
+
run_id=self.eval_id,
|
|
450
|
+
eval_type=EvalType.ACCURACY,
|
|
451
|
+
data=self._get_telemetry_data(),
|
|
452
|
+
),
|
|
426
453
|
)
|
|
427
454
|
|
|
428
455
|
logger.debug(f"*********** Evaluation {self.eval_id} Finished ***********")
|
|
@@ -464,10 +491,10 @@ Remember: You must only compare the agent_output to the expected_output. The exp
|
|
|
464
491
|
live_log.update(status)
|
|
465
492
|
|
|
466
493
|
if self.agent is not None:
|
|
467
|
-
response = await self.agent.arun(
|
|
494
|
+
response = await self.agent.arun(input=eval_input)
|
|
468
495
|
output = response.content
|
|
469
496
|
elif self.team is not None:
|
|
470
|
-
response = await self.team.arun(
|
|
497
|
+
response = await self.team.arun(input=eval_input) # type: ignore
|
|
471
498
|
output = response.content
|
|
472
499
|
|
|
473
500
|
if not output:
|
|
@@ -520,19 +547,48 @@ Remember: You must only compare the agent_output to the expected_output. The exp
|
|
|
520
547
|
if self.print_summary or print_summary:
|
|
521
548
|
self.result.print_summary(console)
|
|
522
549
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
550
|
+
if self.agent is not None:
|
|
551
|
+
agent_id = self.agent.id
|
|
552
|
+
team_id = None
|
|
553
|
+
model_id = self.agent.model.id if self.agent.model is not None else None
|
|
554
|
+
model_provider = self.agent.model.provider if self.agent.model is not None else None
|
|
555
|
+
evaluated_component_name = self.agent.name
|
|
556
|
+
elif self.team is not None:
|
|
557
|
+
agent_id = None
|
|
558
|
+
team_id = self.team.id
|
|
559
|
+
model_id = self.team.model.id if self.team.model is not None else None
|
|
560
|
+
model_provider = self.team.model.provider if self.team.model is not None else None
|
|
561
|
+
evaluated_component_name = self.team.name
|
|
562
|
+
|
|
563
|
+
# Log results to the Agno DB if requested
|
|
564
|
+
if self.db:
|
|
565
|
+
log_eval_input = {
|
|
566
|
+
"additional_guidelines": self.additional_guidelines,
|
|
567
|
+
"additional_context": self.additional_context,
|
|
568
|
+
"num_iterations": self.num_iterations,
|
|
569
|
+
"expected_output": self.expected_output,
|
|
570
|
+
"input": self.input,
|
|
571
|
+
}
|
|
572
|
+
await async_log_eval(
|
|
573
|
+
db=self.db,
|
|
526
574
|
run_id=self.eval_id, # type: ignore
|
|
527
575
|
run_data=asdict(self.result),
|
|
528
576
|
eval_type=EvalType.ACCURACY,
|
|
529
|
-
agent_id=
|
|
530
|
-
model_id=
|
|
531
|
-
model_provider=
|
|
532
|
-
if self.agent is not None and self.agent.model is not None
|
|
533
|
-
else None,
|
|
577
|
+
agent_id=agent_id,
|
|
578
|
+
model_id=model_id,
|
|
579
|
+
model_provider=model_provider,
|
|
534
580
|
name=self.name if self.name is not None else None,
|
|
535
|
-
|
|
581
|
+
evaluated_component_name=evaluated_component_name,
|
|
582
|
+
team_id=team_id,
|
|
583
|
+
workflow_id=None,
|
|
584
|
+
eval_input=log_eval_input,
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
if self.telemetry:
|
|
588
|
+
from agno.api.evals import EvalRunCreate, async_create_eval_run_telemetry
|
|
589
|
+
|
|
590
|
+
await async_create_eval_run_telemetry(
|
|
591
|
+
eval_run=EvalRunCreate(run_id=self.eval_id, eval_type=EvalType.ACCURACY),
|
|
536
592
|
)
|
|
537
593
|
|
|
538
594
|
logger.debug(f"*********** Evaluation {self.eval_id} Finished ***********")
|
|
@@ -596,28 +652,37 @@ Remember: You must only compare the agent_output to the expected_output. The exp
|
|
|
596
652
|
eval_id=self.eval_id,
|
|
597
653
|
result=self.result,
|
|
598
654
|
)
|
|
599
|
-
# Log results to the Agno
|
|
600
|
-
if self.
|
|
655
|
+
# Log results to the Agno DB if requested
|
|
656
|
+
if self.db:
|
|
601
657
|
if self.agent is not None:
|
|
602
|
-
agent_id = self.agent.
|
|
658
|
+
agent_id = self.agent.id
|
|
603
659
|
team_id = None
|
|
604
660
|
model_id = self.agent.model.id if self.agent.model is not None else None
|
|
605
661
|
model_provider = self.agent.model.provider if self.agent.model is not None else None
|
|
606
|
-
|
|
662
|
+
evaluated_component_name = self.agent.name
|
|
607
663
|
elif self.team is not None:
|
|
608
664
|
agent_id = None
|
|
609
|
-
team_id = self.team.
|
|
665
|
+
team_id = self.team.id
|
|
610
666
|
model_id = self.team.model.id if self.team.model is not None else None
|
|
611
667
|
model_provider = self.team.model.provider if self.team.model is not None else None
|
|
612
|
-
|
|
668
|
+
evaluated_component_name = self.team.name
|
|
613
669
|
else:
|
|
614
670
|
agent_id = None
|
|
615
671
|
team_id = None
|
|
616
672
|
model_id = None
|
|
617
673
|
model_provider = None
|
|
618
|
-
|
|
674
|
+
evaluated_component_name = None
|
|
675
|
+
|
|
676
|
+
log_eval_input = {
|
|
677
|
+
"additional_guidelines": self.additional_guidelines,
|
|
678
|
+
"additional_context": self.additional_context,
|
|
679
|
+
"num_iterations": self.num_iterations,
|
|
680
|
+
"expected_output": self.expected_output,
|
|
681
|
+
"input": self.input,
|
|
682
|
+
}
|
|
619
683
|
|
|
620
684
|
log_eval_run(
|
|
685
|
+
db=self.db,
|
|
621
686
|
run_id=self.eval_id, # type: ignore
|
|
622
687
|
run_data=asdict(self.result),
|
|
623
688
|
eval_type=EvalType.ACCURACY,
|
|
@@ -626,7 +691,20 @@ Remember: You must only compare the agent_output to the expected_output. The exp
|
|
|
626
691
|
team_id=team_id,
|
|
627
692
|
model_id=model_id,
|
|
628
693
|
model_provider=model_provider,
|
|
629
|
-
|
|
694
|
+
evaluated_component_name=evaluated_component_name,
|
|
695
|
+
workflow_id=None,
|
|
696
|
+
eval_input=log_eval_input,
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
if self.telemetry:
|
|
700
|
+
from agno.api.evals import EvalRunCreate, create_eval_run_telemetry
|
|
701
|
+
|
|
702
|
+
create_eval_run_telemetry(
|
|
703
|
+
eval_run=EvalRunCreate(
|
|
704
|
+
run_id=self.eval_id,
|
|
705
|
+
eval_type=EvalType.ACCURACY,
|
|
706
|
+
data=self._get_telemetry_data(),
|
|
707
|
+
),
|
|
630
708
|
)
|
|
631
709
|
|
|
632
710
|
logger.debug(f"*********** Evaluation End: {self.eval_id} ***********")
|
|
@@ -690,22 +768,31 @@ Remember: You must only compare the agent_output to the expected_output. The exp
|
|
|
690
768
|
eval_id=self.eval_id,
|
|
691
769
|
result=self.result,
|
|
692
770
|
)
|
|
693
|
-
# Log results to the Agno
|
|
694
|
-
if self.
|
|
771
|
+
# Log results to the Agno DB if requested
|
|
772
|
+
if self.db:
|
|
695
773
|
if self.agent is not None:
|
|
696
|
-
agent_id = self.agent.
|
|
774
|
+
agent_id = self.agent.id
|
|
697
775
|
team_id = None
|
|
698
776
|
model_id = self.agent.model.id if self.agent.model is not None else None
|
|
699
777
|
model_provider = self.agent.model.provider if self.agent.model is not None else None
|
|
700
|
-
|
|
778
|
+
evaluated_component_name = self.agent.name
|
|
701
779
|
elif self.team is not None:
|
|
702
780
|
agent_id = None
|
|
703
|
-
team_id = self.team.
|
|
781
|
+
team_id = self.team.id
|
|
704
782
|
model_id = self.team.model.id if self.team.model is not None else None
|
|
705
783
|
model_provider = self.team.model.provider if self.team.model is not None else None
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
784
|
+
evaluated_component_name = self.team.name
|
|
785
|
+
|
|
786
|
+
log_eval_input = {
|
|
787
|
+
"additional_guidelines": self.additional_guidelines,
|
|
788
|
+
"additional_context": self.additional_context,
|
|
789
|
+
"num_iterations": self.num_iterations,
|
|
790
|
+
"expected_output": self.expected_output,
|
|
791
|
+
"input": self.input,
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
await async_log_eval(
|
|
795
|
+
db=self.db,
|
|
709
796
|
run_id=self.eval_id, # type: ignore
|
|
710
797
|
run_data=asdict(self.result),
|
|
711
798
|
eval_type=EvalType.ACCURACY,
|
|
@@ -714,8 +801,20 @@ Remember: You must only compare the agent_output to the expected_output. The exp
|
|
|
714
801
|
team_id=team_id,
|
|
715
802
|
model_id=model_id,
|
|
716
803
|
model_provider=model_provider,
|
|
717
|
-
|
|
804
|
+
evaluated_component_name=evaluated_component_name,
|
|
805
|
+
workflow_id=None,
|
|
806
|
+
eval_input=log_eval_input,
|
|
718
807
|
)
|
|
719
808
|
|
|
720
809
|
logger.debug(f"*********** Evaluation End: {self.eval_id} ***********")
|
|
721
810
|
return self.result
|
|
811
|
+
|
|
812
|
+
def _get_telemetry_data(self) -> Dict[str, Any]:
|
|
813
|
+
"""Get the telemetry data for the evaluation"""
|
|
814
|
+
return {
|
|
815
|
+
"agent_id": self.agent.id if self.agent else None,
|
|
816
|
+
"team_id": self.team.id if self.team else None,
|
|
817
|
+
"model_id": self.agent.model.id if self.agent and self.agent.model else None,
|
|
818
|
+
"model_provider": self.agent.model.provider if self.agent and self.agent.model else None,
|
|
819
|
+
"num_iterations": self.num_iterations,
|
|
820
|
+
}
|