agno 2.0.1__py3-none-any.whl → 2.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/agent/agent.py +6015 -2823
- agno/api/api.py +2 -0
- agno/api/os.py +1 -1
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +385 -6
- agno/db/dynamo/dynamo.py +388 -81
- agno/db/dynamo/schemas.py +47 -10
- agno/db/dynamo/utils.py +63 -4
- agno/db/firestore/firestore.py +435 -64
- agno/db/firestore/schemas.py +11 -0
- agno/db/firestore/utils.py +102 -4
- agno/db/gcs_json/gcs_json_db.py +384 -42
- agno/db/gcs_json/utils.py +60 -26
- agno/db/in_memory/in_memory_db.py +351 -66
- agno/db/in_memory/utils.py +60 -2
- agno/db/json/json_db.py +339 -48
- agno/db/json/utils.py +60 -26
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +510 -37
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +15 -1
- agno/db/mongo/async_mongo.py +2036 -0
- agno/db/mongo/mongo.py +653 -76
- agno/db/mongo/schemas.py +13 -0
- agno/db/mongo/utils.py +80 -8
- agno/db/mysql/mysql.py +687 -25
- agno/db/mysql/schemas.py +61 -37
- agno/db/mysql/utils.py +60 -2
- agno/db/postgres/__init__.py +2 -1
- agno/db/postgres/async_postgres.py +2001 -0
- agno/db/postgres/postgres.py +676 -57
- agno/db/postgres/schemas.py +43 -18
- agno/db/postgres/utils.py +164 -2
- agno/db/redis/redis.py +344 -38
- agno/db/redis/schemas.py +18 -0
- agno/db/redis/utils.py +60 -2
- agno/db/schemas/__init__.py +2 -1
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/memory.py +13 -0
- agno/db/singlestore/schemas.py +26 -1
- agno/db/singlestore/singlestore.py +687 -53
- agno/db/singlestore/utils.py +60 -2
- agno/db/sqlite/__init__.py +2 -1
- agno/db/sqlite/async_sqlite.py +2371 -0
- agno/db/sqlite/schemas.py +24 -0
- agno/db/sqlite/sqlite.py +774 -85
- agno/db/sqlite/utils.py +168 -5
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +309 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1361 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +50 -22
- agno/eval/accuracy.py +50 -43
- agno/eval/performance.py +6 -3
- agno/eval/reliability.py +6 -3
- agno/eval/utils.py +33 -16
- agno/exceptions.py +68 -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/integrations/discord/client.py +1 -0
- agno/knowledge/chunking/agentic.py +13 -10
- agno/knowledge/chunking/fixed.py +1 -1
- agno/knowledge/chunking/semantic.py +40 -8
- agno/knowledge/chunking/strategy.py +59 -15
- agno/knowledge/embedder/aws_bedrock.py +9 -4
- agno/knowledge/embedder/azure_openai.py +54 -0
- agno/knowledge/embedder/base.py +2 -0
- agno/knowledge/embedder/cohere.py +184 -5
- agno/knowledge/embedder/fastembed.py +1 -1
- agno/knowledge/embedder/google.py +79 -1
- agno/knowledge/embedder/huggingface.py +9 -4
- agno/knowledge/embedder/jina.py +63 -0
- agno/knowledge/embedder/mistral.py +78 -11
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/ollama.py +13 -0
- agno/knowledge/embedder/openai.py +37 -65
- agno/knowledge/embedder/sentence_transformer.py +8 -4
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +69 -16
- agno/knowledge/knowledge.py +594 -186
- agno/knowledge/reader/base.py +9 -2
- agno/knowledge/reader/csv_reader.py +8 -10
- agno/knowledge/reader/docx_reader.py +5 -6
- agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
- agno/knowledge/reader/json_reader.py +6 -5
- agno/knowledge/reader/markdown_reader.py +13 -13
- agno/knowledge/reader/pdf_reader.py +43 -68
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +51 -6
- agno/knowledge/reader/s3_reader.py +3 -15
- agno/knowledge/reader/tavily_reader.py +194 -0
- agno/knowledge/reader/text_reader.py +13 -13
- agno/knowledge/reader/web_search_reader.py +2 -43
- agno/knowledge/reader/website_reader.py +43 -25
- agno/knowledge/reranker/__init__.py +2 -8
- agno/knowledge/types.py +9 -0
- agno/knowledge/utils.py +20 -0
- agno/media.py +72 -0
- agno/memory/manager.py +336 -82
- agno/models/aimlapi/aimlapi.py +2 -2
- agno/models/anthropic/claude.py +183 -37
- agno/models/aws/bedrock.py +52 -112
- agno/models/aws/claude.py +33 -1
- agno/models/azure/ai_foundry.py +33 -15
- agno/models/azure/openai_chat.py +25 -8
- agno/models/base.py +999 -519
- agno/models/cerebras/cerebras.py +19 -13
- agno/models/cerebras/cerebras_openai.py +8 -5
- agno/models/cohere/chat.py +27 -1
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +57 -0
- agno/models/dashscope/dashscope.py +1 -0
- agno/models/deepinfra/deepinfra.py +2 -2
- agno/models/deepseek/deepseek.py +2 -2
- agno/models/fireworks/fireworks.py +2 -2
- agno/models/google/gemini.py +103 -31
- agno/models/groq/groq.py +28 -11
- agno/models/huggingface/huggingface.py +2 -1
- agno/models/internlm/internlm.py +2 -2
- agno/models/langdb/langdb.py +4 -4
- agno/models/litellm/chat.py +18 -1
- agno/models/litellm/litellm_openai.py +2 -2
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/message.py +139 -0
- agno/models/meta/llama.py +27 -10
- agno/models/meta/llama_openai.py +5 -17
- agno/models/nebius/nebius.py +6 -6
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/nvidia.py +2 -2
- agno/models/ollama/chat.py +59 -5
- agno/models/openai/chat.py +69 -29
- agno/models/openai/responses.py +103 -106
- agno/models/openrouter/openrouter.py +41 -3
- agno/models/perplexity/perplexity.py +4 -5
- agno/models/portkey/portkey.py +3 -3
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +52 -0
- agno/models/response.py +77 -1
- agno/models/sambanova/sambanova.py +2 -2
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +25 -0
- agno/models/together/together.py +2 -2
- agno/models/utils.py +254 -8
- agno/models/vercel/v0.py +2 -2
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +96 -0
- agno/models/vllm/vllm.py +1 -0
- agno/models/xai/xai.py +3 -2
- agno/os/app.py +543 -178
- agno/os/auth.py +24 -14
- agno/os/config.py +1 -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 +250 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/agui.py +23 -7
- agno/os/interfaces/agui/router.py +27 -3
- agno/os/interfaces/agui/utils.py +242 -142
- agno/os/interfaces/base.py +6 -2
- agno/os/interfaces/slack/router.py +81 -23
- agno/os/interfaces/slack/slack.py +29 -14
- agno/os/interfaces/whatsapp/router.py +11 -4
- agno/os/interfaces/whatsapp/whatsapp.py +14 -7
- agno/os/mcp.py +111 -54
- agno/os/middleware/__init__.py +7 -0
- agno/os/middleware/jwt.py +233 -0
- agno/os/router.py +556 -139
- agno/os/routers/evals/evals.py +71 -34
- agno/os/routers/evals/schemas.py +31 -31
- agno/os/routers/evals/utils.py +6 -5
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/knowledge.py +185 -38
- agno/os/routers/knowledge/schemas.py +82 -22
- agno/os/routers/memory/memory.py +158 -53
- agno/os/routers/memory/schemas.py +20 -16
- agno/os/routers/metrics/metrics.py +20 -8
- agno/os/routers/metrics/schemas.py +16 -16
- agno/os/routers/session/session.py +499 -38
- agno/os/schema.py +308 -198
- agno/os/utils.py +401 -41
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +2 -2
- agno/reasoning/deepseek.py +2 -2
- agno/reasoning/default.py +3 -1
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +2 -2
- agno/reasoning/ollama.py +2 -2
- agno/reasoning/openai.py +7 -2
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +248 -94
- agno/run/base.py +44 -5
- agno/run/team.py +238 -97
- agno/run/workflow.py +144 -33
- agno/session/agent.py +105 -89
- agno/session/summary.py +65 -25
- agno/session/team.py +176 -96
- agno/session/workflow.py +406 -40
- agno/team/team.py +3854 -1610
- agno/tools/dalle.py +2 -4
- agno/tools/decorator.py +4 -2
- agno/tools/duckduckgo.py +15 -11
- agno/tools/e2b.py +14 -7
- agno/tools/eleven_labs.py +23 -25
- agno/tools/exa.py +21 -16
- agno/tools/file.py +153 -23
- agno/tools/file_generation.py +350 -0
- agno/tools/firecrawl.py +4 -4
- agno/tools/function.py +250 -30
- agno/tools/gmail.py +238 -14
- agno/tools/google_drive.py +270 -0
- agno/tools/googlecalendar.py +36 -8
- agno/tools/googlesheets.py +20 -5
- agno/tools/jira.py +20 -0
- agno/tools/knowledge.py +3 -3
- 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 +11 -17
- agno/tools/memori.py +1 -53
- agno/tools/memory.py +419 -0
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/notion.py +204 -0
- agno/tools/parallel.py +314 -0
- agno/tools/scrapegraph.py +58 -31
- agno/tools/searxng.py +2 -2
- agno/tools/serper.py +2 -2
- agno/tools/slack.py +18 -3
- agno/tools/spider.py +2 -2
- agno/tools/tavily.py +146 -0
- agno/tools/whatsapp.py +1 -1
- agno/tools/workflow.py +278 -0
- agno/tools/yfinance.py +12 -11
- agno/utils/agent.py +820 -0
- agno/utils/audio.py +27 -0
- agno/utils/common.py +90 -1
- agno/utils/events.py +217 -2
- agno/utils/gemini.py +180 -22
- agno/utils/hooks.py +57 -0
- agno/utils/http.py +111 -0
- agno/utils/knowledge.py +12 -5
- agno/utils/log.py +1 -0
- agno/utils/mcp.py +92 -2
- agno/utils/media.py +188 -10
- agno/utils/merge_dict.py +22 -1
- agno/utils/message.py +60 -0
- agno/utils/models/claude.py +40 -11
- agno/utils/print_response/agent.py +105 -21
- agno/utils/print_response/team.py +103 -38
- agno/utils/print_response/workflow.py +251 -34
- agno/utils/reasoning.py +22 -1
- agno/utils/serialize.py +32 -0
- agno/utils/streamlit.py +16 -10
- agno/utils/string.py +41 -0
- agno/utils/team.py +98 -9
- agno/utils/tools.py +1 -1
- agno/vectordb/base.py +23 -4
- agno/vectordb/cassandra/cassandra.py +65 -9
- agno/vectordb/chroma/chromadb.py +182 -38
- agno/vectordb/clickhouse/clickhousedb.py +64 -11
- agno/vectordb/couchbase/couchbase.py +105 -10
- agno/vectordb/lancedb/lance_db.py +124 -133
- agno/vectordb/langchaindb/langchaindb.py +25 -7
- agno/vectordb/lightrag/lightrag.py +17 -3
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +46 -7
- agno/vectordb/milvus/milvus.py +126 -9
- agno/vectordb/mongodb/__init__.py +7 -1
- agno/vectordb/mongodb/mongodb.py +112 -7
- agno/vectordb/pgvector/pgvector.py +142 -21
- agno/vectordb/pineconedb/pineconedb.py +80 -8
- agno/vectordb/qdrant/qdrant.py +125 -39
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +694 -0
- agno/vectordb/singlestore/singlestore.py +111 -25
- agno/vectordb/surrealdb/surrealdb.py +31 -5
- agno/vectordb/upstashdb/upstashdb.py +76 -8
- agno/vectordb/weaviate/weaviate.py +86 -15
- agno/workflow/__init__.py +2 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +112 -18
- agno/workflow/loop.py +69 -10
- agno/workflow/parallel.py +266 -118
- agno/workflow/router.py +110 -17
- agno/workflow/step.py +638 -129
- agno/workflow/steps.py +65 -6
- agno/workflow/types.py +61 -23
- agno/workflow/workflow.py +2085 -272
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/METADATA +182 -58
- agno-2.3.0.dist-info/RECORD +577 -0
- agno/knowledge/reader/url_reader.py +0 -128
- agno/tools/googlesearch.py +0 -98
- agno/tools/mcp.py +0 -610
- agno/utils/models/aws_claude.py +0 -170
- agno-2.0.1.dist-info/RECORD +0 -515
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/WHEEL +0 -0
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/licenses/LICENSE +0 -0
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/top_level.txt +0 -0
agno/db/postgres/schemas.py
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
5
|
try:
|
|
6
|
-
from sqlalchemy.
|
|
6
|
+
from sqlalchemy.dialects.postgresql import JSONB
|
|
7
|
+
from sqlalchemy.types import BigInteger, Boolean, Date, String, Text
|
|
7
8
|
except ImportError:
|
|
8
9
|
raise ImportError("`sqlalchemy` not installed. Please install it using `pip install sqlalchemy`")
|
|
9
10
|
|
|
@@ -14,13 +15,13 @@ SESSION_TABLE_SCHEMA = {
|
|
|
14
15
|
"team_id": {"type": String, "nullable": True},
|
|
15
16
|
"workflow_id": {"type": String, "nullable": True},
|
|
16
17
|
"user_id": {"type": String, "nullable": True},
|
|
17
|
-
"session_data": {"type":
|
|
18
|
-
"agent_data": {"type":
|
|
19
|
-
"team_data": {"type":
|
|
20
|
-
"workflow_data": {"type":
|
|
21
|
-
"metadata": {"type":
|
|
22
|
-
"runs": {"type":
|
|
23
|
-
"summary": {"type":
|
|
18
|
+
"session_data": {"type": JSONB, "nullable": True},
|
|
19
|
+
"agent_data": {"type": JSONB, "nullable": True},
|
|
20
|
+
"team_data": {"type": JSONB, "nullable": True},
|
|
21
|
+
"workflow_data": {"type": JSONB, "nullable": True},
|
|
22
|
+
"metadata": {"type": JSONB, "nullable": True},
|
|
23
|
+
"runs": {"type": JSONB, "nullable": True},
|
|
24
|
+
"summary": {"type": JSONB, "nullable": True},
|
|
24
25
|
"created_at": {"type": BigInteger, "nullable": False, "index": True},
|
|
25
26
|
"updated_at": {"type": BigInteger, "nullable": True},
|
|
26
27
|
"_unique_constraints": [
|
|
@@ -33,20 +34,22 @@ SESSION_TABLE_SCHEMA = {
|
|
|
33
34
|
|
|
34
35
|
MEMORY_TABLE_SCHEMA = {
|
|
35
36
|
"memory_id": {"type": String, "primary_key": True, "nullable": False},
|
|
36
|
-
"memory": {"type":
|
|
37
|
-
"
|
|
37
|
+
"memory": {"type": JSONB, "nullable": False},
|
|
38
|
+
"feedback": {"type": Text, "nullable": True},
|
|
39
|
+
"input": {"type": Text, "nullable": True},
|
|
38
40
|
"agent_id": {"type": String, "nullable": True},
|
|
39
41
|
"team_id": {"type": String, "nullable": True},
|
|
40
42
|
"user_id": {"type": String, "nullable": True, "index": True},
|
|
41
|
-
"topics": {"type":
|
|
43
|
+
"topics": {"type": JSONB, "nullable": True},
|
|
44
|
+
"created_at": {"type": BigInteger, "nullable": False, "index": True},
|
|
42
45
|
"updated_at": {"type": BigInteger, "nullable": True, "index": True},
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
EVAL_TABLE_SCHEMA = {
|
|
46
49
|
"run_id": {"type": String, "primary_key": True, "nullable": False},
|
|
47
50
|
"eval_type": {"type": String, "nullable": False},
|
|
48
|
-
"eval_data": {"type":
|
|
49
|
-
"eval_input": {"type":
|
|
51
|
+
"eval_data": {"type": JSONB, "nullable": False},
|
|
52
|
+
"eval_input": {"type": JSONB, "nullable": False},
|
|
50
53
|
"name": {"type": String, "nullable": True},
|
|
51
54
|
"agent_id": {"type": String, "nullable": True},
|
|
52
55
|
"team_id": {"type": String, "nullable": True},
|
|
@@ -61,14 +64,14 @@ EVAL_TABLE_SCHEMA = {
|
|
|
61
64
|
KNOWLEDGE_TABLE_SCHEMA = {
|
|
62
65
|
"id": {"type": String, "primary_key": True, "nullable": False},
|
|
63
66
|
"name": {"type": String, "nullable": False},
|
|
64
|
-
"description": {"type":
|
|
65
|
-
"metadata": {"type":
|
|
67
|
+
"description": {"type": Text, "nullable": False},
|
|
68
|
+
"metadata": {"type": JSONB, "nullable": True},
|
|
66
69
|
"type": {"type": String, "nullable": True},
|
|
67
70
|
"size": {"type": BigInteger, "nullable": True},
|
|
68
71
|
"linked_to": {"type": String, "nullable": True},
|
|
69
72
|
"access_count": {"type": BigInteger, "nullable": True},
|
|
70
73
|
"status": {"type": String, "nullable": True},
|
|
71
|
-
"status_message": {"type":
|
|
74
|
+
"status_message": {"type": Text, "nullable": True},
|
|
72
75
|
"created_at": {"type": BigInteger, "nullable": True},
|
|
73
76
|
"updated_at": {"type": BigInteger, "nullable": True},
|
|
74
77
|
"external_id": {"type": String, "nullable": True},
|
|
@@ -83,8 +86,8 @@ METRICS_TABLE_SCHEMA = {
|
|
|
83
86
|
"team_sessions_count": {"type": BigInteger, "nullable": False, "default": 0},
|
|
84
87
|
"workflow_sessions_count": {"type": BigInteger, "nullable": False, "default": 0},
|
|
85
88
|
"users_count": {"type": BigInteger, "nullable": False, "default": 0},
|
|
86
|
-
"token_metrics": {"type":
|
|
87
|
-
"model_metrics": {"type":
|
|
89
|
+
"token_metrics": {"type": JSONB, "nullable": False, "default": {}},
|
|
90
|
+
"model_metrics": {"type": JSONB, "nullable": False, "default": {}},
|
|
88
91
|
"date": {"type": Date, "nullable": False, "index": True},
|
|
89
92
|
"aggregation_period": {"type": String, "nullable": False},
|
|
90
93
|
"created_at": {"type": BigInteger, "nullable": False},
|
|
@@ -98,6 +101,26 @@ METRICS_TABLE_SCHEMA = {
|
|
|
98
101
|
],
|
|
99
102
|
}
|
|
100
103
|
|
|
104
|
+
CULTURAL_KNOWLEDGE_TABLE_SCHEMA = {
|
|
105
|
+
"id": {"type": String, "primary_key": True, "nullable": False},
|
|
106
|
+
"name": {"type": String, "nullable": False, "index": True},
|
|
107
|
+
"summary": {"type": Text, "nullable": True},
|
|
108
|
+
"content": {"type": JSONB, "nullable": True},
|
|
109
|
+
"metadata": {"type": JSONB, "nullable": True},
|
|
110
|
+
"input": {"type": Text, "nullable": True},
|
|
111
|
+
"created_at": {"type": BigInteger, "nullable": True},
|
|
112
|
+
"updated_at": {"type": BigInteger, "nullable": True},
|
|
113
|
+
"agent_id": {"type": String, "nullable": True},
|
|
114
|
+
"team_id": {"type": String, "nullable": True},
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
VERSIONS_TABLE_SCHEMA = {
|
|
118
|
+
"table_name": {"type": String, "nullable": False, "primary_key": True},
|
|
119
|
+
"version": {"type": String, "nullable": False},
|
|
120
|
+
"created_at": {"type": String, "nullable": False, "index": True},
|
|
121
|
+
"updated_at": {"type": String, "nullable": True},
|
|
122
|
+
}
|
|
123
|
+
|
|
101
124
|
|
|
102
125
|
def get_table_schema_definition(table_type: str) -> dict[str, Any]:
|
|
103
126
|
"""
|
|
@@ -115,6 +138,8 @@ def get_table_schema_definition(table_type: str) -> dict[str, Any]:
|
|
|
115
138
|
"metrics": METRICS_TABLE_SCHEMA,
|
|
116
139
|
"memories": MEMORY_TABLE_SCHEMA,
|
|
117
140
|
"knowledge": KNOWLEDGE_TABLE_SCHEMA,
|
|
141
|
+
"culture": CULTURAL_KNOWLEDGE_TABLE_SCHEMA,
|
|
142
|
+
"versions": VERSIONS_TABLE_SCHEMA,
|
|
118
143
|
}
|
|
119
144
|
|
|
120
145
|
schema = schemas.get(table_type, {})
|
agno/db/postgres/utils.py
CHANGED
|
@@ -6,8 +6,10 @@ from typing import Any, Dict, List, Optional
|
|
|
6
6
|
from uuid import uuid4
|
|
7
7
|
|
|
8
8
|
from sqlalchemy import Engine
|
|
9
|
+
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
|
|
9
10
|
|
|
10
11
|
from agno.db.postgres.schemas import get_table_schema_definition
|
|
12
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
11
13
|
from agno.utils.log import log_debug, log_error, log_warning
|
|
12
14
|
|
|
13
15
|
try:
|
|
@@ -62,6 +64,20 @@ def create_schema(session: Session, db_schema: str) -> None:
|
|
|
62
64
|
log_warning(f"Could not create schema {db_schema}: {e}")
|
|
63
65
|
|
|
64
66
|
|
|
67
|
+
async def acreate_schema(session: AsyncSession, db_schema: str) -> None:
|
|
68
|
+
"""Create the database schema if it doesn't exist.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
session: The SQLAlchemy session to use
|
|
72
|
+
db_schema (str): The definition of the database schema to create
|
|
73
|
+
"""
|
|
74
|
+
try:
|
|
75
|
+
log_debug(f"Creating schema if not exists: {db_schema}")
|
|
76
|
+
await session.execute(text(f"CREATE SCHEMA IF NOT EXISTS {db_schema};"))
|
|
77
|
+
except Exception as e:
|
|
78
|
+
log_warning(f"Could not create schema {db_schema}: {e}")
|
|
79
|
+
|
|
80
|
+
|
|
65
81
|
def is_table_available(session: Session, table_name: str, db_schema: str) -> bool:
|
|
66
82
|
"""
|
|
67
83
|
Check if a table with the given name exists in the given schema.
|
|
@@ -81,6 +97,24 @@ def is_table_available(session: Session, table_name: str, db_schema: str) -> boo
|
|
|
81
97
|
return False
|
|
82
98
|
|
|
83
99
|
|
|
100
|
+
async def ais_table_available(session: AsyncSession, table_name: str, db_schema: str) -> bool:
|
|
101
|
+
"""
|
|
102
|
+
Check if a table with the given name exists in the given schema.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
bool: True if the table exists, False otherwise.
|
|
106
|
+
"""
|
|
107
|
+
try:
|
|
108
|
+
exists_query = text(
|
|
109
|
+
"SELECT 1 FROM information_schema.tables WHERE table_schema = :schema AND table_name = :table"
|
|
110
|
+
)
|
|
111
|
+
exists = (await session.execute(exists_query, {"schema": db_schema, "table": table_name})).scalar() is not None
|
|
112
|
+
return exists
|
|
113
|
+
except Exception as e:
|
|
114
|
+
log_error(f"Error checking if table exists: {e}")
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
|
|
84
118
|
def is_valid_table(db_engine: Engine, table_name: str, table_type: str, db_schema: str) -> bool:
|
|
85
119
|
"""
|
|
86
120
|
Check if the existing table has the expected column names.
|
|
@@ -113,6 +147,44 @@ def is_valid_table(db_engine: Engine, table_name: str, table_type: str, db_schem
|
|
|
113
147
|
return False
|
|
114
148
|
|
|
115
149
|
|
|
150
|
+
async def ais_valid_table(db_engine: AsyncEngine, table_name: str, table_type: str, db_schema: str) -> bool:
|
|
151
|
+
"""
|
|
152
|
+
Check if the existing table has the expected column names.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
table_name (str): Name of the table to validate
|
|
156
|
+
schema (str): Database schema name
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
bool: True if table has all expected columns, False otherwise
|
|
160
|
+
"""
|
|
161
|
+
try:
|
|
162
|
+
expected_table_schema = get_table_schema_definition(table_type)
|
|
163
|
+
expected_columns = {col_name for col_name in expected_table_schema.keys() if not col_name.startswith("_")}
|
|
164
|
+
|
|
165
|
+
# Get existing columns from the async engine
|
|
166
|
+
async with db_engine.connect() as conn:
|
|
167
|
+
existing_columns = await conn.run_sync(_get_table_columns, table_name, db_schema)
|
|
168
|
+
|
|
169
|
+
# Check if all expected columns exist
|
|
170
|
+
missing_columns = expected_columns - existing_columns
|
|
171
|
+
if missing_columns:
|
|
172
|
+
log_warning(f"Missing columns {missing_columns} in table {db_schema}.{table_name}")
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
return True
|
|
176
|
+
except Exception as e:
|
|
177
|
+
log_error(f"Error validating table schema for {db_schema}.{table_name}: {e}")
|
|
178
|
+
return False
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _get_table_columns(conn, table_name: str, db_schema: str) -> set[str]:
|
|
182
|
+
"""Helper function to get table columns using sync inspector."""
|
|
183
|
+
inspector = inspect(conn)
|
|
184
|
+
columns_info = inspector.get_columns(table_name, schema=db_schema)
|
|
185
|
+
return {col["name"] for col in columns_info}
|
|
186
|
+
|
|
187
|
+
|
|
116
188
|
# -- Metrics util methods --
|
|
117
189
|
def bulk_upsert_metrics(session: Session, table: Table, metrics_records: list[dict]) -> list[dict]:
|
|
118
190
|
"""Bulk upsert metrics into the database.
|
|
@@ -147,6 +219,39 @@ def bulk_upsert_metrics(session: Session, table: Table, metrics_records: list[di
|
|
|
147
219
|
return results # type: ignore
|
|
148
220
|
|
|
149
221
|
|
|
222
|
+
async def abulk_upsert_metrics(session: AsyncSession, table: Table, metrics_records: list[dict]) -> list[dict]:
|
|
223
|
+
"""Bulk upsert metrics into the database.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
table (Table): The table to upsert into.
|
|
227
|
+
metrics_records (list[dict]): The metrics records to upsert.
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
list[dict]: The upserted metrics records.
|
|
231
|
+
"""
|
|
232
|
+
if not metrics_records:
|
|
233
|
+
return []
|
|
234
|
+
|
|
235
|
+
results = []
|
|
236
|
+
stmt = postgresql.insert(table)
|
|
237
|
+
|
|
238
|
+
# Columns to update in case of conflict
|
|
239
|
+
update_columns = {
|
|
240
|
+
col.name: stmt.excluded[col.name]
|
|
241
|
+
for col in table.columns
|
|
242
|
+
if col.name not in ["id", "date", "created_at", "aggregation_period"]
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
stmt = stmt.on_conflict_do_update(index_elements=["date", "aggregation_period"], set_=update_columns).returning( # type: ignore
|
|
246
|
+
table
|
|
247
|
+
)
|
|
248
|
+
result = await session.execute(stmt, metrics_records)
|
|
249
|
+
results = [row._mapping for row in result.fetchall()]
|
|
250
|
+
await session.commit()
|
|
251
|
+
|
|
252
|
+
return results # type: ignore
|
|
253
|
+
|
|
254
|
+
|
|
150
255
|
def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
151
256
|
"""Calculate metrics for the given single date.
|
|
152
257
|
|
|
@@ -187,7 +292,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
|
187
292
|
all_user_ids = set()
|
|
188
293
|
|
|
189
294
|
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
190
|
-
sessions = sessions_data.get(session_type, [])
|
|
295
|
+
sessions = sessions_data.get(session_type, []) or []
|
|
191
296
|
metrics[sessions_count_key] = len(sessions)
|
|
192
297
|
|
|
193
298
|
for session in sessions:
|
|
@@ -208,7 +313,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
|
208
313
|
|
|
209
314
|
model_metrics = []
|
|
210
315
|
for model, count in model_counts.items():
|
|
211
|
-
model_id, model_provider = model.
|
|
316
|
+
model_id, model_provider = model.rsplit(":", 1)
|
|
212
317
|
model_metrics.append({"model_id": model_id, "model_provider": model_provider, "count": count})
|
|
213
318
|
|
|
214
319
|
metrics["users_count"] = len(all_user_ids)
|
|
@@ -278,3 +383,60 @@ def get_dates_to_calculate_metrics_for(starting_date: date) -> list[date]:
|
|
|
278
383
|
if days_diff <= 0:
|
|
279
384
|
return []
|
|
280
385
|
return [starting_date + timedelta(days=x) for x in range(days_diff)]
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
# -- Cultural Knowledge util methods --
|
|
389
|
+
def serialize_cultural_knowledge(cultural_knowledge: CulturalKnowledge) -> Dict[str, Any]:
|
|
390
|
+
"""Serialize a CulturalKnowledge object for database storage.
|
|
391
|
+
|
|
392
|
+
Converts the model's separate content, categories, and notes fields
|
|
393
|
+
into a single JSON dict for the database content column.
|
|
394
|
+
|
|
395
|
+
Args:
|
|
396
|
+
cultural_knowledge (CulturalKnowledge): The cultural knowledge object to serialize.
|
|
397
|
+
|
|
398
|
+
Returns:
|
|
399
|
+
Dict[str, Any]: A dictionary with the content field as JSON containing content, categories, and notes.
|
|
400
|
+
"""
|
|
401
|
+
content_dict: Dict[str, Any] = {}
|
|
402
|
+
if cultural_knowledge.content is not None:
|
|
403
|
+
content_dict["content"] = cultural_knowledge.content
|
|
404
|
+
if cultural_knowledge.categories is not None:
|
|
405
|
+
content_dict["categories"] = cultural_knowledge.categories
|
|
406
|
+
if cultural_knowledge.notes is not None:
|
|
407
|
+
content_dict["notes"] = cultural_knowledge.notes
|
|
408
|
+
|
|
409
|
+
return content_dict if content_dict else {}
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def deserialize_cultural_knowledge(db_row: Dict[str, Any]) -> CulturalKnowledge:
|
|
413
|
+
"""Deserialize a database row to a CulturalKnowledge object.
|
|
414
|
+
|
|
415
|
+
The database stores content as a JSON dict containing content, categories, and notes.
|
|
416
|
+
This method extracts those fields and converts them back to the model format.
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
db_row (Dict[str, Any]): The database row as a dictionary.
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
CulturalKnowledge: The cultural knowledge object.
|
|
423
|
+
"""
|
|
424
|
+
# Extract content, categories, and notes from the JSON content field
|
|
425
|
+
content_json = db_row.get("content", {}) or {}
|
|
426
|
+
|
|
427
|
+
return CulturalKnowledge.from_dict(
|
|
428
|
+
{
|
|
429
|
+
"id": db_row.get("id"),
|
|
430
|
+
"name": db_row.get("name"),
|
|
431
|
+
"summary": db_row.get("summary"),
|
|
432
|
+
"content": content_json.get("content"),
|
|
433
|
+
"categories": content_json.get("categories"),
|
|
434
|
+
"notes": content_json.get("notes"),
|
|
435
|
+
"metadata": db_row.get("metadata"),
|
|
436
|
+
"input": db_row.get("input"),
|
|
437
|
+
"created_at": db_row.get("created_at"),
|
|
438
|
+
"updated_at": db_row.get("updated_at"),
|
|
439
|
+
"agent_id": db_row.get("agent_id"),
|
|
440
|
+
"team_id": db_row.get("team_id"),
|
|
441
|
+
}
|
|
442
|
+
)
|