agno 2.2.13__py3-none-any.whl → 2.4.3__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/agent/__init__.py +6 -0
- agno/agent/agent.py +5252 -3145
- agno/agent/remote.py +525 -0
- agno/api/api.py +2 -0
- agno/client/__init__.py +3 -0
- agno/client/a2a/__init__.py +10 -0
- agno/client/a2a/client.py +554 -0
- agno/client/a2a/schemas.py +112 -0
- agno/client/a2a/utils.py +369 -0
- agno/client/os.py +2669 -0
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/manager.py +2 -2
- agno/db/base.py +927 -6
- agno/db/dynamo/dynamo.py +788 -2
- agno/db/dynamo/schemas.py +128 -0
- agno/db/dynamo/utils.py +26 -3
- agno/db/firestore/firestore.py +674 -50
- agno/db/firestore/schemas.py +41 -0
- agno/db/firestore/utils.py +25 -10
- agno/db/gcs_json/gcs_json_db.py +506 -3
- agno/db/gcs_json/utils.py +14 -2
- agno/db/in_memory/in_memory_db.py +203 -4
- agno/db/in_memory/utils.py +14 -2
- agno/db/json/json_db.py +498 -2
- agno/db/json/utils.py +14 -2
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/utils.py +19 -0
- agno/db/migrations/v1_to_v2.py +54 -16
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +977 -0
- agno/db/mongo/async_mongo.py +1013 -39
- agno/db/mongo/mongo.py +684 -4
- agno/db/mongo/schemas.py +48 -0
- agno/db/mongo/utils.py +17 -0
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2958 -0
- agno/db/mysql/mysql.py +722 -53
- agno/db/mysql/schemas.py +77 -11
- agno/db/mysql/utils.py +151 -8
- agno/db/postgres/async_postgres.py +1254 -137
- agno/db/postgres/postgres.py +2316 -93
- agno/db/postgres/schemas.py +153 -21
- agno/db/postgres/utils.py +22 -7
- agno/db/redis/redis.py +531 -3
- agno/db/redis/schemas.py +36 -0
- agno/db/redis/utils.py +31 -15
- agno/db/schemas/evals.py +1 -0
- agno/db/schemas/memory.py +20 -9
- agno/db/singlestore/schemas.py +70 -1
- agno/db/singlestore/singlestore.py +737 -74
- agno/db/singlestore/utils.py +13 -3
- agno/db/sqlite/async_sqlite.py +1069 -89
- agno/db/sqlite/schemas.py +133 -1
- agno/db/sqlite/sqlite.py +2203 -165
- agno/db/sqlite/utils.py +21 -11
- agno/db/surrealdb/models.py +25 -0
- agno/db/surrealdb/surrealdb.py +603 -1
- agno/db/utils.py +60 -0
- agno/eval/__init__.py +26 -3
- agno/eval/accuracy.py +25 -12
- agno/eval/agent_as_judge.py +871 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +10 -4
- agno/eval/reliability.py +22 -13
- agno/eval/utils.py +2 -1
- agno/exceptions.py +42 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/client.py +13 -2
- agno/knowledge/__init__.py +4 -0
- agno/knowledge/chunking/code.py +90 -0
- agno/knowledge/chunking/document.py +65 -4
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/chunking/markdown.py +102 -11
- agno/knowledge/chunking/recursive.py +2 -2
- agno/knowledge/chunking/semantic.py +130 -48
- agno/knowledge/chunking/strategy.py +18 -0
- agno/knowledge/embedder/azure_openai.py +0 -1
- agno/knowledge/embedder/google.py +1 -1
- agno/knowledge/embedder/mistral.py +1 -1
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/openai.py +16 -12
- agno/knowledge/filesystem.py +412 -0
- agno/knowledge/knowledge.py +4261 -1199
- agno/knowledge/protocol.py +134 -0
- agno/knowledge/reader/arxiv_reader.py +3 -2
- agno/knowledge/reader/base.py +9 -7
- agno/knowledge/reader/csv_reader.py +91 -42
- agno/knowledge/reader/docx_reader.py +9 -10
- agno/knowledge/reader/excel_reader.py +225 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +38 -48
- agno/knowledge/reader/firecrawl_reader.py +3 -2
- agno/knowledge/reader/json_reader.py +16 -22
- agno/knowledge/reader/markdown_reader.py +15 -14
- agno/knowledge/reader/pdf_reader.py +33 -28
- agno/knowledge/reader/pptx_reader.py +9 -10
- agno/knowledge/reader/reader_factory.py +135 -1
- agno/knowledge/reader/s3_reader.py +8 -16
- agno/knowledge/reader/tavily_reader.py +3 -3
- agno/knowledge/reader/text_reader.py +15 -14
- agno/knowledge/reader/utils/__init__.py +17 -0
- agno/knowledge/reader/utils/spreadsheet.py +114 -0
- agno/knowledge/reader/web_search_reader.py +8 -65
- agno/knowledge/reader/website_reader.py +16 -13
- agno/knowledge/reader/wikipedia_reader.py +36 -3
- agno/knowledge/reader/youtube_reader.py +3 -2
- agno/knowledge/remote_content/__init__.py +33 -0
- agno/knowledge/remote_content/config.py +266 -0
- agno/knowledge/remote_content/remote_content.py +105 -17
- agno/knowledge/utils.py +76 -22
- agno/learn/__init__.py +71 -0
- agno/learn/config.py +463 -0
- agno/learn/curate.py +185 -0
- agno/learn/machine.py +725 -0
- agno/learn/schemas.py +1114 -0
- agno/learn/stores/__init__.py +38 -0
- agno/learn/stores/decision_log.py +1156 -0
- agno/learn/stores/entity_memory.py +3275 -0
- agno/learn/stores/learned_knowledge.py +1583 -0
- agno/learn/stores/protocol.py +117 -0
- agno/learn/stores/session_context.py +1217 -0
- agno/learn/stores/user_memory.py +1495 -0
- agno/learn/stores/user_profile.py +1220 -0
- agno/learn/utils.py +209 -0
- agno/media.py +22 -6
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +223 -8
- 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/aimlapi.py +17 -0
- agno/models/anthropic/claude.py +434 -59
- agno/models/aws/bedrock.py +121 -20
- agno/models/aws/claude.py +131 -274
- agno/models/azure/ai_foundry.py +10 -6
- agno/models/azure/openai_chat.py +33 -10
- agno/models/base.py +1162 -561
- agno/models/cerebras/cerebras.py +120 -24
- agno/models/cerebras/cerebras_openai.py +21 -2
- agno/models/cohere/chat.py +65 -6
- agno/models/cometapi/cometapi.py +18 -1
- agno/models/dashscope/dashscope.py +2 -3
- agno/models/deepinfra/deepinfra.py +18 -1
- agno/models/deepseek/deepseek.py +69 -3
- agno/models/fireworks/fireworks.py +18 -1
- agno/models/google/gemini.py +959 -89
- agno/models/google/utils.py +22 -0
- agno/models/groq/groq.py +48 -18
- agno/models/huggingface/huggingface.py +17 -6
- agno/models/ibm/watsonx.py +16 -6
- agno/models/internlm/internlm.py +18 -1
- agno/models/langdb/langdb.py +13 -1
- agno/models/litellm/chat.py +88 -9
- agno/models/litellm/litellm_openai.py +18 -1
- agno/models/message.py +24 -5
- agno/models/meta/llama.py +40 -13
- agno/models/meta/llama_openai.py +22 -21
- agno/models/metrics.py +12 -0
- agno/models/mistral/mistral.py +8 -4
- agno/models/n1n/__init__.py +3 -0
- agno/models/n1n/n1n.py +57 -0
- agno/models/nebius/nebius.py +6 -7
- agno/models/nvidia/nvidia.py +20 -3
- agno/models/ollama/__init__.py +2 -0
- agno/models/ollama/chat.py +17 -6
- agno/models/ollama/responses.py +100 -0
- agno/models/openai/__init__.py +2 -0
- agno/models/openai/chat.py +117 -26
- agno/models/openai/open_responses.py +46 -0
- agno/models/openai/responses.py +110 -32
- agno/models/openrouter/__init__.py +2 -0
- agno/models/openrouter/openrouter.py +67 -2
- agno/models/openrouter/responses.py +146 -0
- agno/models/perplexity/perplexity.py +19 -1
- agno/models/portkey/portkey.py +7 -6
- agno/models/requesty/requesty.py +19 -2
- agno/models/response.py +20 -2
- agno/models/sambanova/sambanova.py +20 -3
- agno/models/siliconflow/siliconflow.py +19 -2
- agno/models/together/together.py +20 -3
- agno/models/vercel/v0.py +20 -3
- agno/models/vertexai/claude.py +124 -4
- agno/models/vllm/vllm.py +19 -14
- agno/models/xai/xai.py +19 -2
- agno/os/app.py +467 -137
- agno/os/auth.py +253 -5
- agno/os/config.py +22 -0
- agno/os/interfaces/a2a/a2a.py +7 -6
- agno/os/interfaces/a2a/router.py +635 -26
- agno/os/interfaces/a2a/utils.py +32 -33
- agno/os/interfaces/agui/agui.py +5 -3
- agno/os/interfaces/agui/router.py +26 -16
- agno/os/interfaces/agui/utils.py +97 -57
- agno/os/interfaces/base.py +7 -7
- agno/os/interfaces/slack/router.py +16 -7
- agno/os/interfaces/slack/slack.py +7 -7
- agno/os/interfaces/whatsapp/router.py +35 -7
- agno/os/interfaces/whatsapp/security.py +3 -1
- agno/os/interfaces/whatsapp/whatsapp.py +11 -8
- agno/os/managers.py +326 -0
- agno/os/mcp.py +652 -79
- agno/os/middleware/__init__.py +4 -0
- agno/os/middleware/jwt.py +718 -115
- agno/os/middleware/trailing_slash.py +27 -0
- agno/os/router.py +105 -1558
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +655 -0
- agno/os/routers/agents/schema.py +288 -0
- agno/os/routers/components/__init__.py +3 -0
- agno/os/routers/components/components.py +475 -0
- agno/os/routers/database.py +155 -0
- agno/os/routers/evals/evals.py +111 -18
- agno/os/routers/evals/schemas.py +38 -5
- agno/os/routers/evals/utils.py +80 -11
- agno/os/routers/health.py +3 -3
- agno/os/routers/knowledge/knowledge.py +284 -35
- agno/os/routers/knowledge/schemas.py +14 -2
- agno/os/routers/memory/memory.py +274 -11
- agno/os/routers/memory/schemas.py +44 -3
- agno/os/routers/metrics/metrics.py +30 -15
- agno/os/routers/metrics/schemas.py +10 -6
- agno/os/routers/registry/__init__.py +3 -0
- agno/os/routers/registry/registry.py +337 -0
- agno/os/routers/session/session.py +143 -14
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +550 -0
- agno/os/routers/teams/schema.py +280 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +549 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +757 -0
- agno/os/routers/workflows/schema.py +139 -0
- agno/os/schema.py +157 -584
- agno/os/scopes.py +469 -0
- agno/os/settings.py +3 -0
- agno/os/utils.py +574 -185
- agno/reasoning/anthropic.py +85 -1
- agno/reasoning/azure_ai_foundry.py +93 -1
- agno/reasoning/deepseek.py +102 -2
- agno/reasoning/default.py +6 -7
- agno/reasoning/gemini.py +87 -3
- agno/reasoning/groq.py +109 -2
- agno/reasoning/helpers.py +6 -7
- agno/reasoning/manager.py +1238 -0
- agno/reasoning/ollama.py +93 -1
- agno/reasoning/openai.py +115 -1
- agno/reasoning/vertexai.py +85 -1
- agno/registry/__init__.py +3 -0
- agno/registry/registry.py +68 -0
- agno/remote/__init__.py +3 -0
- agno/remote/base.py +581 -0
- agno/run/__init__.py +2 -4
- agno/run/agent.py +134 -19
- agno/run/base.py +49 -1
- agno/run/cancel.py +65 -52
- agno/run/cancellation_management/__init__.py +9 -0
- agno/run/cancellation_management/base.py +78 -0
- agno/run/cancellation_management/in_memory_cancellation_manager.py +100 -0
- agno/run/cancellation_management/redis_cancellation_manager.py +236 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +111 -19
- agno/run/workflow.py +2 -1
- agno/session/agent.py +57 -92
- agno/session/summary.py +1 -1
- agno/session/team.py +62 -115
- agno/session/workflow.py +353 -57
- agno/skills/__init__.py +17 -0
- agno/skills/agent_skills.py +377 -0
- agno/skills/errors.py +32 -0
- agno/skills/loaders/__init__.py +4 -0
- agno/skills/loaders/base.py +27 -0
- agno/skills/loaders/local.py +216 -0
- agno/skills/skill.py +65 -0
- agno/skills/utils.py +107 -0
- agno/skills/validator.py +277 -0
- agno/table.py +10 -0
- agno/team/__init__.py +5 -1
- agno/team/remote.py +447 -0
- agno/team/team.py +3769 -2202
- agno/tools/brandfetch.py +27 -18
- agno/tools/browserbase.py +225 -16
- agno/tools/crawl4ai.py +3 -0
- agno/tools/duckduckgo.py +25 -71
- agno/tools/exa.py +0 -21
- agno/tools/file.py +14 -13
- agno/tools/file_generation.py +12 -6
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +94 -113
- agno/tools/google_bigquery.py +11 -2
- agno/tools/google_drive.py +4 -3
- agno/tools/knowledge.py +9 -4
- agno/tools/mcp/mcp.py +301 -18
- agno/tools/mcp/multi_mcp.py +269 -14
- agno/tools/mem0.py +11 -10
- agno/tools/memory.py +47 -46
- agno/tools/mlx_transcribe.py +10 -7
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/parallel.py +0 -7
- agno/tools/postgres.py +76 -36
- agno/tools/python.py +14 -6
- agno/tools/reasoning.py +30 -23
- agno/tools/redshift.py +406 -0
- agno/tools/shopify.py +1519 -0
- agno/tools/spotify.py +919 -0
- agno/tools/tavily.py +4 -1
- agno/tools/toolkit.py +253 -18
- agno/tools/websearch.py +93 -0
- agno/tools/website.py +1 -1
- agno/tools/wikipedia.py +1 -1
- agno/tools/workflow.py +56 -48
- agno/tools/yfinance.py +12 -11
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +161 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +112 -0
- agno/utils/agent.py +251 -10
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +264 -7
- agno/utils/hooks.py +111 -3
- agno/utils/http.py +161 -2
- agno/utils/mcp.py +49 -8
- agno/utils/media.py +22 -1
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +20 -5
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/os.py +0 -0
- agno/utils/print_response/agent.py +99 -16
- agno/utils/print_response/team.py +223 -24
- agno/utils/print_response/workflow.py +0 -2
- agno/utils/prompts.py +8 -6
- agno/utils/remote.py +23 -0
- agno/utils/response.py +1 -13
- agno/utils/string.py +91 -2
- agno/utils/team.py +62 -12
- agno/utils/tokens.py +657 -0
- agno/vectordb/base.py +15 -2
- agno/vectordb/cassandra/cassandra.py +1 -1
- agno/vectordb/chroma/__init__.py +2 -1
- agno/vectordb/chroma/chromadb.py +468 -23
- agno/vectordb/clickhouse/clickhousedb.py +1 -1
- agno/vectordb/couchbase/couchbase.py +6 -2
- agno/vectordb/lancedb/lance_db.py +7 -38
- agno/vectordb/lightrag/lightrag.py +7 -6
- agno/vectordb/milvus/milvus.py +118 -84
- agno/vectordb/mongodb/__init__.py +2 -1
- agno/vectordb/mongodb/mongodb.py +14 -31
- agno/vectordb/pgvector/pgvector.py +120 -66
- agno/vectordb/pineconedb/pineconedb.py +2 -19
- agno/vectordb/qdrant/__init__.py +2 -1
- agno/vectordb/qdrant/qdrant.py +33 -56
- agno/vectordb/redis/__init__.py +2 -1
- agno/vectordb/redis/redisdb.py +19 -31
- agno/vectordb/singlestore/singlestore.py +17 -9
- agno/vectordb/surrealdb/surrealdb.py +2 -38
- agno/vectordb/weaviate/__init__.py +2 -1
- agno/vectordb/weaviate/weaviate.py +7 -3
- agno/workflow/__init__.py +5 -1
- agno/workflow/agent.py +2 -2
- agno/workflow/condition.py +12 -10
- agno/workflow/loop.py +28 -9
- agno/workflow/parallel.py +21 -13
- agno/workflow/remote.py +362 -0
- agno/workflow/router.py +12 -9
- agno/workflow/step.py +261 -36
- agno/workflow/steps.py +12 -8
- agno/workflow/types.py +40 -77
- agno/workflow/workflow.py +939 -213
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/METADATA +134 -181
- agno-2.4.3.dist-info/RECORD +677 -0
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/WHEEL +1 -1
- agno/tools/googlesearch.py +0 -98
- agno/tools/memori.py +0 -339
- agno-2.2.13.dist-info/RECORD +0 -575
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/licenses/LICENSE +0 -0
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/top_level.txt +0 -0
agno/db/mysql/schemas.py
CHANGED
|
@@ -39,6 +39,8 @@ USER_MEMORY_TABLE_SCHEMA = {
|
|
|
39
39
|
"team_id": {"type": lambda: String(128), "nullable": True},
|
|
40
40
|
"user_id": {"type": lambda: String(128), "nullable": True, "index": True},
|
|
41
41
|
"topics": {"type": JSON, "nullable": True},
|
|
42
|
+
"feedback": {"type": Text, "nullable": True},
|
|
43
|
+
"created_at": {"type": BigInteger, "nullable": False, "index": True},
|
|
42
44
|
"updated_at": {"type": BigInteger, "nullable": True, "index": True},
|
|
43
45
|
}
|
|
44
46
|
|
|
@@ -76,20 +78,20 @@ KNOWLEDGE_TABLE_SCHEMA = {
|
|
|
76
78
|
|
|
77
79
|
METRICS_TABLE_SCHEMA = {
|
|
78
80
|
"id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
|
|
79
|
-
"agent_runs_count": {"type": BigInteger, "nullable": False},
|
|
80
|
-
"team_runs_count": {"type": BigInteger, "nullable": False},
|
|
81
|
-
"workflow_runs_count": {"type": BigInteger, "nullable": False},
|
|
82
|
-
"agent_sessions_count": {"type": BigInteger, "nullable": False},
|
|
83
|
-
"team_sessions_count": {"type": BigInteger, "nullable": False},
|
|
84
|
-
"workflow_sessions_count": {"type": BigInteger, "nullable": False},
|
|
85
|
-
"users_count": {"type": BigInteger, "nullable": False},
|
|
86
|
-
"token_metrics": {"type": JSON, "nullable": False},
|
|
87
|
-
"model_metrics": {"type": JSON, "nullable": False},
|
|
81
|
+
"agent_runs_count": {"type": BigInteger, "nullable": False, "default": 0},
|
|
82
|
+
"team_runs_count": {"type": BigInteger, "nullable": False, "default": 0},
|
|
83
|
+
"workflow_runs_count": {"type": BigInteger, "nullable": False, "default": 0},
|
|
84
|
+
"agent_sessions_count": {"type": BigInteger, "nullable": False, "default": 0},
|
|
85
|
+
"team_sessions_count": {"type": BigInteger, "nullable": False, "default": 0},
|
|
86
|
+
"workflow_sessions_count": {"type": BigInteger, "nullable": False, "default": 0},
|
|
87
|
+
"users_count": {"type": BigInteger, "nullable": False, "default": 0},
|
|
88
|
+
"token_metrics": {"type": JSON, "nullable": False, "default": {}},
|
|
89
|
+
"model_metrics": {"type": JSON, "nullable": False, "default": {}},
|
|
88
90
|
"date": {"type": Date, "nullable": False, "index": True},
|
|
89
91
|
"aggregation_period": {"type": lambda: String(20), "nullable": False},
|
|
90
92
|
"created_at": {"type": BigInteger, "nullable": False},
|
|
91
93
|
"updated_at": {"type": BigInteger, "nullable": True},
|
|
92
|
-
"completed": {"type": Boolean, "nullable": False},
|
|
94
|
+
"completed": {"type": Boolean, "nullable": False, "default": False},
|
|
93
95
|
"_unique_constraints": [
|
|
94
96
|
{
|
|
95
97
|
"name": "uq_metrics_date_period",
|
|
@@ -111,17 +113,79 @@ CULTURAL_KNOWLEDGE_TABLE_SCHEMA = {
|
|
|
111
113
|
"team_id": {"type": lambda: String(128), "nullable": True},
|
|
112
114
|
}
|
|
113
115
|
|
|
116
|
+
VERSIONS_TABLE_SCHEMA = {
|
|
117
|
+
"table_name": {"type": lambda: String(128), "nullable": False, "primary_key": True},
|
|
118
|
+
"version": {"type": lambda: String(10), "nullable": False},
|
|
119
|
+
"created_at": {"type": lambda: String(128), "nullable": False, "index": True},
|
|
120
|
+
"updated_at": {"type": lambda: String(128), "nullable": True},
|
|
121
|
+
}
|
|
114
122
|
|
|
115
|
-
|
|
123
|
+
TRACE_TABLE_SCHEMA = {
|
|
124
|
+
"trace_id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
|
|
125
|
+
"name": {"type": lambda: String(255), "nullable": False},
|
|
126
|
+
"status": {"type": lambda: String(50), "nullable": False, "index": True},
|
|
127
|
+
"start_time": {"type": lambda: String(128), "nullable": False, "index": True}, # ISO 8601 datetime string
|
|
128
|
+
"end_time": {"type": lambda: String(128), "nullable": False}, # ISO 8601 datetime string
|
|
129
|
+
"duration_ms": {"type": BigInteger, "nullable": False},
|
|
130
|
+
"run_id": {"type": lambda: String(128), "nullable": True, "index": True},
|
|
131
|
+
"session_id": {"type": lambda: String(128), "nullable": True, "index": True},
|
|
132
|
+
"user_id": {"type": lambda: String(128), "nullable": True, "index": True},
|
|
133
|
+
"agent_id": {"type": lambda: String(128), "nullable": True, "index": True},
|
|
134
|
+
"team_id": {"type": lambda: String(128), "nullable": True, "index": True},
|
|
135
|
+
"workflow_id": {"type": lambda: String(128), "nullable": True, "index": True},
|
|
136
|
+
"created_at": {"type": lambda: String(128), "nullable": False, "index": True}, # ISO 8601 datetime string
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _get_span_table_schema(traces_table_name: str = "agno_traces", db_schema: str = "agno") -> dict[str, Any]:
|
|
141
|
+
"""Get the span table schema with the correct foreign key reference.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
traces_table_name: The name of the traces table to reference in the foreign key.
|
|
145
|
+
db_schema: The database schema name.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
The span table schema dictionary.
|
|
149
|
+
"""
|
|
150
|
+
return {
|
|
151
|
+
"span_id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
|
|
152
|
+
"trace_id": {
|
|
153
|
+
"type": lambda: String(128),
|
|
154
|
+
"nullable": False,
|
|
155
|
+
"index": True,
|
|
156
|
+
"foreign_key": f"{db_schema}.{traces_table_name}.trace_id",
|
|
157
|
+
},
|
|
158
|
+
"parent_span_id": {"type": lambda: String(128), "nullable": True, "index": True},
|
|
159
|
+
"name": {"type": lambda: String(255), "nullable": False},
|
|
160
|
+
"span_kind": {"type": lambda: String(50), "nullable": False},
|
|
161
|
+
"status_code": {"type": lambda: String(50), "nullable": False},
|
|
162
|
+
"status_message": {"type": Text, "nullable": True},
|
|
163
|
+
"start_time": {"type": lambda: String(128), "nullable": False, "index": True}, # ISO 8601 datetime string
|
|
164
|
+
"end_time": {"type": lambda: String(128), "nullable": False}, # ISO 8601 datetime string
|
|
165
|
+
"duration_ms": {"type": BigInteger, "nullable": False},
|
|
166
|
+
"attributes": {"type": JSON, "nullable": True},
|
|
167
|
+
"created_at": {"type": lambda: String(128), "nullable": False, "index": True}, # ISO 8601 datetime string
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def get_table_schema_definition(
|
|
172
|
+
table_type: str, traces_table_name: str = "agno_traces", db_schema: str = "agno"
|
|
173
|
+
) -> dict[str, Any]:
|
|
116
174
|
"""
|
|
117
175
|
Get the expected schema definition for the given table.
|
|
118
176
|
|
|
119
177
|
Args:
|
|
120
178
|
table_type (str): The type of table to get the schema for.
|
|
179
|
+
traces_table_name (str): The name of the traces table (used for spans foreign key).
|
|
180
|
+
db_schema (str): The database schema name (used for spans foreign key).
|
|
121
181
|
|
|
122
182
|
Returns:
|
|
123
183
|
Dict[str, Any]: Dictionary containing column definitions for the table
|
|
124
184
|
"""
|
|
185
|
+
# Handle spans table specially to resolve the foreign key reference
|
|
186
|
+
if table_type == "spans":
|
|
187
|
+
return _get_span_table_schema(traces_table_name, db_schema)
|
|
188
|
+
|
|
125
189
|
schemas = {
|
|
126
190
|
"sessions": SESSION_TABLE_SCHEMA,
|
|
127
191
|
"evals": EVAL_TABLE_SCHEMA,
|
|
@@ -129,6 +193,8 @@ def get_table_schema_definition(table_type: str) -> dict[str, Any]:
|
|
|
129
193
|
"memories": USER_MEMORY_TABLE_SCHEMA,
|
|
130
194
|
"knowledge": KNOWLEDGE_TABLE_SCHEMA,
|
|
131
195
|
"culture": CULTURAL_KNOWLEDGE_TABLE_SCHEMA,
|
|
196
|
+
"versions": VERSIONS_TABLE_SCHEMA,
|
|
197
|
+
"traces": TRACE_TABLE_SCHEMA,
|
|
132
198
|
}
|
|
133
199
|
|
|
134
200
|
schema = schemas.get(table_type, {})
|
agno/db/mysql/utils.py
CHANGED
|
@@ -5,15 +5,14 @@ from datetime import date, datetime, timedelta, timezone
|
|
|
5
5
|
from typing import Any, Dict, List, Optional
|
|
6
6
|
from uuid import uuid4
|
|
7
7
|
|
|
8
|
-
from sqlalchemy import Engine
|
|
9
|
-
|
|
10
8
|
from agno.db.mysql.schemas import get_table_schema_definition
|
|
11
9
|
from agno.db.schemas.culture import CulturalKnowledge
|
|
12
10
|
from agno.utils.log import log_debug, log_error, log_warning
|
|
13
11
|
|
|
14
12
|
try:
|
|
15
|
-
from sqlalchemy import Table
|
|
13
|
+
from sqlalchemy import Engine, Table, func
|
|
16
14
|
from sqlalchemy.dialects import mysql
|
|
15
|
+
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
|
|
17
16
|
from sqlalchemy.inspection import inspect
|
|
18
17
|
from sqlalchemy.orm import Session
|
|
19
18
|
from sqlalchemy.sql.expression import text
|
|
@@ -33,6 +32,11 @@ def apply_sorting(stmt, table: Table, sort_by: Optional[str] = None, sort_order:
|
|
|
33
32
|
|
|
34
33
|
Returns:
|
|
35
34
|
The modified statement with sorting applied
|
|
35
|
+
|
|
36
|
+
Note:
|
|
37
|
+
For 'updated_at' sorting, uses COALESCE(updated_at, created_at) to fall back
|
|
38
|
+
to created_at when updated_at is NULL. This ensures pre-2.0 records (which may
|
|
39
|
+
have NULL updated_at) are sorted correctly by their creation time.
|
|
36
40
|
"""
|
|
37
41
|
if sort_by is None:
|
|
38
42
|
return stmt
|
|
@@ -41,8 +45,13 @@ def apply_sorting(stmt, table: Table, sort_by: Optional[str] = None, sort_order:
|
|
|
41
45
|
log_debug(f"Invalid sort field: '{sort_by}'. Will not apply any sorting.")
|
|
42
46
|
return stmt
|
|
43
47
|
|
|
44
|
-
#
|
|
45
|
-
|
|
48
|
+
# For updated_at, use COALESCE to fall back to created_at if updated_at is NULL
|
|
49
|
+
# This handles pre-2.0 records that may have NULL updated_at values
|
|
50
|
+
if sort_by == "updated_at" and hasattr(table.c, "created_at"):
|
|
51
|
+
sort_column = func.coalesce(table.c.updated_at, table.c.created_at)
|
|
52
|
+
else:
|
|
53
|
+
sort_column = getattr(table.c, sort_by)
|
|
54
|
+
|
|
46
55
|
if sort_order and sort_order == "asc":
|
|
47
56
|
return stmt.order_by(sort_column.asc())
|
|
48
57
|
else:
|
|
@@ -91,8 +100,10 @@ def is_valid_table(db_engine: Engine, table_name: str, table_type: str, db_schem
|
|
|
91
100
|
Check if the existing table has the expected column names.
|
|
92
101
|
|
|
93
102
|
Args:
|
|
103
|
+
db_engine: Database engine
|
|
94
104
|
table_name (str): Name of the table to validate
|
|
95
|
-
|
|
105
|
+
table_type (str): Type of table (for schema lookup)
|
|
106
|
+
db_schema (str): Database schema name
|
|
96
107
|
|
|
97
108
|
Returns:
|
|
98
109
|
bool: True if table has all expected columns, False otherwise
|
|
@@ -123,6 +134,7 @@ def bulk_upsert_metrics(session: Session, table: Table, metrics_records: list[di
|
|
|
123
134
|
"""Bulk upsert metrics into the database.
|
|
124
135
|
|
|
125
136
|
Args:
|
|
137
|
+
session (Session): The SQLAlchemy session
|
|
126
138
|
table (Table): The table to upsert into.
|
|
127
139
|
metrics_records (list[dict]): The metrics records to upsert.
|
|
128
140
|
|
|
@@ -156,7 +168,10 @@ def bulk_upsert_metrics(session: Session, table: Table, metrics_records: list[di
|
|
|
156
168
|
|
|
157
169
|
for record in metrics_records:
|
|
158
170
|
select_stmt = select(table).where(
|
|
159
|
-
and_(
|
|
171
|
+
and_(
|
|
172
|
+
table.c.date == record["date"],
|
|
173
|
+
table.c.aggregation_period == record["aggregation_period"],
|
|
174
|
+
)
|
|
160
175
|
)
|
|
161
176
|
result = session.execute(select_stmt).fetchone()
|
|
162
177
|
if result:
|
|
@@ -165,6 +180,55 @@ def bulk_upsert_metrics(session: Session, table: Table, metrics_records: list[di
|
|
|
165
180
|
return results # type: ignore
|
|
166
181
|
|
|
167
182
|
|
|
183
|
+
async def abulk_upsert_metrics(session: AsyncSession, table: Table, metrics_records: list[dict]) -> list[dict]:
|
|
184
|
+
"""Async bulk upsert metrics into the database.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
session (AsyncSession): The async SQLAlchemy session
|
|
188
|
+
table (Table): The table to upsert into.
|
|
189
|
+
metrics_records (list[dict]): The metrics records to upsert.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
list[dict]: The upserted metrics records.
|
|
193
|
+
"""
|
|
194
|
+
if not metrics_records:
|
|
195
|
+
return []
|
|
196
|
+
|
|
197
|
+
results = []
|
|
198
|
+
|
|
199
|
+
# MySQL doesn't support returning in the same way as PostgreSQL
|
|
200
|
+
# We'll need to insert/update and then fetch the records
|
|
201
|
+
for record in metrics_records:
|
|
202
|
+
stmt = mysql.insert(table).values(record)
|
|
203
|
+
|
|
204
|
+
# Columns to update in case of conflict
|
|
205
|
+
update_dict = {
|
|
206
|
+
col.name: record.get(col.name)
|
|
207
|
+
for col in table.columns
|
|
208
|
+
if col.name not in ["id", "date", "created_at", "aggregation_period"] and col.name in record
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
stmt = stmt.on_duplicate_key_update(**update_dict)
|
|
212
|
+
await session.execute(stmt)
|
|
213
|
+
|
|
214
|
+
# Fetch the updated records
|
|
215
|
+
from sqlalchemy import and_, select
|
|
216
|
+
|
|
217
|
+
for record in metrics_records:
|
|
218
|
+
select_stmt = select(table).where(
|
|
219
|
+
and_(
|
|
220
|
+
table.c.date == record["date"],
|
|
221
|
+
table.c.aggregation_period == record["aggregation_period"],
|
|
222
|
+
)
|
|
223
|
+
)
|
|
224
|
+
result = await session.execute(select_stmt)
|
|
225
|
+
fetched_row = result.fetchone()
|
|
226
|
+
if fetched_row:
|
|
227
|
+
results.append(dict(fetched_row._mapping))
|
|
228
|
+
|
|
229
|
+
return results
|
|
230
|
+
|
|
231
|
+
|
|
168
232
|
def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
169
233
|
"""Calculate metrics for the given single date.
|
|
170
234
|
|
|
@@ -299,7 +363,9 @@ def get_dates_to_calculate_metrics_for(starting_date: date) -> list[date]:
|
|
|
299
363
|
|
|
300
364
|
|
|
301
365
|
# -- Cultural Knowledge util methods --
|
|
302
|
-
def serialize_cultural_knowledge_for_db(
|
|
366
|
+
def serialize_cultural_knowledge_for_db(
|
|
367
|
+
cultural_knowledge: CulturalKnowledge,
|
|
368
|
+
) -> Dict[str, Any]:
|
|
303
369
|
"""Serialize a CulturalKnowledge object for database storage.
|
|
304
370
|
|
|
305
371
|
Converts the model's separate content, categories, and notes fields
|
|
@@ -353,3 +419,80 @@ def deserialize_cultural_knowledge_from_db(db_row: Dict[str, Any]) -> CulturalKn
|
|
|
353
419
|
"team_id": db_row.get("team_id"),
|
|
354
420
|
}
|
|
355
421
|
)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
# -- Async DB util methods --
|
|
425
|
+
async def acreate_schema(session: AsyncSession, db_schema: str) -> None:
|
|
426
|
+
"""Async version: Create the database schema if it doesn't exist.
|
|
427
|
+
|
|
428
|
+
Args:
|
|
429
|
+
session: The async SQLAlchemy session to use
|
|
430
|
+
db_schema (str): The definition of the database schema to create
|
|
431
|
+
"""
|
|
432
|
+
try:
|
|
433
|
+
log_debug(f"Creating database if not exists: {db_schema}")
|
|
434
|
+
# MySQL uses CREATE DATABASE instead of CREATE SCHEMA
|
|
435
|
+
await session.execute(text(f"CREATE DATABASE IF NOT EXISTS `{db_schema}`;"))
|
|
436
|
+
except Exception as e:
|
|
437
|
+
log_warning(f"Could not create database {db_schema}: {e}")
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
async def ais_table_available(session: AsyncSession, table_name: str, db_schema: str) -> bool:
|
|
441
|
+
"""Async version: Check if a table with the given name exists in the given schema.
|
|
442
|
+
|
|
443
|
+
Returns:
|
|
444
|
+
bool: True if the table exists, False otherwise.
|
|
445
|
+
"""
|
|
446
|
+
try:
|
|
447
|
+
exists_query = text(
|
|
448
|
+
"SELECT 1 FROM information_schema.tables WHERE table_schema = :schema AND table_name = :table"
|
|
449
|
+
)
|
|
450
|
+
result = await session.execute(exists_query, {"schema": db_schema, "table": table_name})
|
|
451
|
+
exists = result.scalar() is not None
|
|
452
|
+
if not exists:
|
|
453
|
+
log_debug(f"Table {db_schema}.{table_name} {'exists' if exists else 'does not exist'}")
|
|
454
|
+
|
|
455
|
+
return exists
|
|
456
|
+
|
|
457
|
+
except Exception as e:
|
|
458
|
+
log_error(f"Error checking if table exists: {e}")
|
|
459
|
+
return False
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
async def ais_valid_table(db_engine: AsyncEngine, table_name: str, table_type: str, db_schema: str) -> bool:
|
|
463
|
+
"""Async version: Check if the existing table has the expected column names.
|
|
464
|
+
|
|
465
|
+
Args:
|
|
466
|
+
db_engine: Async database engine
|
|
467
|
+
table_name (str): Name of the table to validate
|
|
468
|
+
table_type (str): Type of table (for schema lookup)
|
|
469
|
+
db_schema (str): Database schema name
|
|
470
|
+
|
|
471
|
+
Returns:
|
|
472
|
+
bool: True if table has all expected columns, False otherwise
|
|
473
|
+
"""
|
|
474
|
+
try:
|
|
475
|
+
expected_table_schema = get_table_schema_definition(table_type)
|
|
476
|
+
expected_columns = {col_name for col_name in expected_table_schema.keys() if not col_name.startswith("_")}
|
|
477
|
+
|
|
478
|
+
# Get existing columns from the async engine
|
|
479
|
+
async with db_engine.connect() as conn:
|
|
480
|
+
existing_columns = await conn.run_sync(_get_table_columns, table_name, db_schema)
|
|
481
|
+
|
|
482
|
+
# Check if all expected columns exist
|
|
483
|
+
missing_columns = expected_columns - existing_columns
|
|
484
|
+
if missing_columns:
|
|
485
|
+
log_warning(f"Missing columns {missing_columns} in table {db_schema}.{table_name}")
|
|
486
|
+
return False
|
|
487
|
+
|
|
488
|
+
return True
|
|
489
|
+
except Exception as e:
|
|
490
|
+
log_error(f"Error validating table schema for {db_schema}.{table_name}: {e}")
|
|
491
|
+
return False
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def _get_table_columns(connection, table_name: str, db_schema: str) -> set[str]:
|
|
495
|
+
"""Helper function to get table columns using sync inspector."""
|
|
496
|
+
inspector = inspect(connection)
|
|
497
|
+
columns_info = inspector.get_columns(table_name, schema=db_schema)
|
|
498
|
+
return {col["name"] for col in columns_info}
|