agno 2.1.2__py3-none-any.whl → 2.3.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/agent/agent.py +5540 -2273
- agno/api/api.py +2 -0
- agno/api/os.py +1 -1
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +689 -6
- agno/db/dynamo/dynamo.py +933 -37
- agno/db/dynamo/schemas.py +174 -10
- agno/db/dynamo/utils.py +63 -4
- agno/db/firestore/firestore.py +831 -9
- agno/db/firestore/schemas.py +51 -0
- agno/db/firestore/utils.py +102 -4
- agno/db/gcs_json/gcs_json_db.py +660 -12
- agno/db/gcs_json/utils.py +60 -26
- agno/db/in_memory/in_memory_db.py +287 -14
- agno/db/in_memory/utils.py +60 -2
- agno/db/json/json_db.py +590 -14
- agno/db/json/utils.py +60 -26
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +43 -13
- 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 +2760 -0
- agno/db/mongo/mongo.py +879 -11
- agno/db/mongo/schemas.py +42 -0
- agno/db/mongo/utils.py +80 -8
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +946 -68
- agno/db/mysql/schemas.py +72 -10
- agno/db/mysql/utils.py +198 -7
- agno/db/postgres/__init__.py +2 -1
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +942 -57
- agno/db/postgres/schemas.py +81 -18
- agno/db/postgres/utils.py +164 -2
- agno/db/redis/redis.py +671 -7
- agno/db/redis/schemas.py +50 -0
- agno/db/redis/utils.py +65 -7
- agno/db/schemas/__init__.py +2 -1
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +1 -0
- agno/db/schemas/memory.py +17 -2
- agno/db/singlestore/schemas.py +63 -0
- agno/db/singlestore/singlestore.py +949 -83
- agno/db/singlestore/utils.py +60 -2
- agno/db/sqlite/__init__.py +2 -1
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +62 -0
- agno/db/sqlite/sqlite.py +965 -46
- agno/db/sqlite/utils.py +169 -8
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +334 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1908 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +2 -0
- agno/eval/__init__.py +10 -0
- agno/eval/accuracy.py +75 -55
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +16 -7
- agno/eval/reliability.py +28 -16
- agno/eval/utils.py +35 -17
- agno/exceptions.py +27 -2
- agno/filters.py +354 -0
- agno/guardrails/prompt_injection.py +1 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/client.py +1 -1
- agno/knowledge/chunking/agentic.py +13 -10
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/chunking/semantic.py +9 -4
- agno/knowledge/chunking/strategy.py +59 -15
- agno/knowledge/embedder/fastembed.py +1 -1
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/ollama.py +8 -0
- agno/knowledge/embedder/openai.py +8 -8
- agno/knowledge/embedder/sentence_transformer.py +6 -2
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/knowledge.py +1618 -318
- agno/knowledge/reader/base.py +6 -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 +16 -20
- agno/knowledge/reader/json_reader.py +5 -4
- agno/knowledge/reader/markdown_reader.py +8 -8
- agno/knowledge/reader/pdf_reader.py +17 -19
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +32 -3
- agno/knowledge/reader/s3_reader.py +3 -3
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +22 -10
- agno/knowledge/reader/web_search_reader.py +1 -48
- agno/knowledge/reader/website_reader.py +10 -10
- agno/knowledge/reader/wikipedia_reader.py +33 -1
- agno/knowledge/types.py +1 -0
- agno/knowledge/utils.py +72 -7
- agno/media.py +22 -6
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +544 -83
- 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 +515 -40
- agno/models/aws/bedrock.py +102 -21
- agno/models/aws/claude.py +131 -274
- agno/models/azure/ai_foundry.py +41 -19
- agno/models/azure/openai_chat.py +39 -8
- agno/models/base.py +1249 -525
- agno/models/cerebras/cerebras.py +91 -21
- agno/models/cerebras/cerebras_openai.py +21 -2
- agno/models/cohere/chat.py +40 -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 +877 -80
- agno/models/google/utils.py +22 -0
- agno/models/groq/groq.py +51 -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 +44 -9
- agno/models/litellm/litellm_openai.py +18 -1
- agno/models/message.py +28 -5
- agno/models/meta/llama.py +47 -14
- agno/models/meta/llama_openai.py +22 -17
- agno/models/mistral/mistral.py +8 -4
- agno/models/nebius/nebius.py +6 -7
- agno/models/nvidia/nvidia.py +20 -3
- agno/models/ollama/chat.py +24 -8
- agno/models/openai/chat.py +104 -29
- agno/models/openai/responses.py +101 -81
- agno/models/openrouter/openrouter.py +60 -3
- agno/models/perplexity/perplexity.py +17 -1
- agno/models/portkey/portkey.py +7 -6
- agno/models/requesty/requesty.py +24 -4
- agno/models/response.py +73 -2
- agno/models/sambanova/sambanova.py +20 -3
- agno/models/siliconflow/siliconflow.py +19 -2
- agno/models/together/together.py +20 -3
- agno/models/utils.py +254 -8
- agno/models/vercel/v0.py +20 -3
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/vllm.py +19 -14
- agno/models/xai/xai.py +19 -2
- agno/os/app.py +549 -152
- agno/os/auth.py +190 -3
- agno/os/config.py +23 -0
- agno/os/interfaces/a2a/router.py +8 -11
- agno/os/interfaces/a2a/utils.py +1 -1
- agno/os/interfaces/agui/router.py +18 -3
- agno/os/interfaces/agui/utils.py +152 -39
- agno/os/interfaces/slack/router.py +55 -37
- agno/os/interfaces/slack/slack.py +9 -1
- agno/os/interfaces/whatsapp/router.py +0 -1
- agno/os/interfaces/whatsapp/security.py +3 -1
- agno/os/mcp.py +110 -52
- agno/os/middleware/__init__.py +2 -0
- agno/os/middleware/jwt.py +676 -112
- agno/os/router.py +40 -1478
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +599 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/evals.py +96 -39
- agno/os/routers/evals/schemas.py +65 -33
- agno/os/routers/evals/utils.py +80 -10
- agno/os/routers/health.py +10 -4
- agno/os/routers/knowledge/knowledge.py +196 -38
- agno/os/routers/knowledge/schemas.py +82 -22
- agno/os/routers/memory/memory.py +279 -52
- agno/os/routers/memory/schemas.py +46 -17
- agno/os/routers/metrics/metrics.py +20 -8
- agno/os/routers/metrics/schemas.py +16 -16
- agno/os/routers/session/session.py +462 -34
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +512 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +499 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +624 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +256 -693
- agno/os/scopes.py +469 -0
- agno/os/utils.py +514 -36
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/openai.py +5 -0
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +155 -32
- agno/run/base.py +55 -3
- agno/run/requirement.py +181 -0
- agno/run/team.py +125 -38
- agno/run/workflow.py +72 -18
- agno/session/agent.py +102 -89
- agno/session/summary.py +56 -15
- agno/session/team.py +164 -90
- agno/session/workflow.py +405 -40
- agno/table.py +10 -0
- agno/team/team.py +3974 -1903
- agno/tools/dalle.py +2 -4
- agno/tools/eleven_labs.py +23 -25
- agno/tools/exa.py +21 -16
- agno/tools/file.py +153 -23
- agno/tools/file_generation.py +16 -10
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +193 -38
- agno/tools/gmail.py +238 -14
- agno/tools/google_drive.py +271 -0
- agno/tools/googlecalendar.py +36 -8
- agno/tools/googlesheets.py +20 -5
- agno/tools/jira.py +20 -0
- 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 +3 -3
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/notion.py +204 -0
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +76 -36
- agno/tools/redshift.py +406 -0
- agno/tools/scrapegraph.py +1 -1
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +18 -3
- agno/tools/spotify.py +919 -0
- agno/tools/tavily.py +146 -0
- agno/tools/toolkit.py +25 -0
- agno/tools/workflow.py +8 -1
- agno/tools/yfinance.py +12 -11
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +157 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +111 -0
- agno/utils/agent.py +938 -0
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +151 -3
- agno/utils/gemini.py +15 -5
- agno/utils/hooks.py +118 -4
- agno/utils/http.py +113 -2
- agno/utils/knowledge.py +12 -5
- agno/utils/log.py +1 -0
- agno/utils/mcp.py +92 -2
- agno/utils/media.py +187 -1
- agno/utils/merge_dict.py +3 -3
- agno/utils/message.py +60 -0
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +49 -14
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/print_response/agent.py +109 -16
- agno/utils/print_response/team.py +223 -30
- agno/utils/print_response/workflow.py +251 -34
- agno/utils/streamlit.py +1 -1
- agno/utils/team.py +98 -9
- agno/utils/tokens.py +657 -0
- agno/vectordb/base.py +39 -7
- agno/vectordb/cassandra/cassandra.py +21 -5
- agno/vectordb/chroma/chromadb.py +43 -12
- agno/vectordb/clickhouse/clickhousedb.py +21 -5
- agno/vectordb/couchbase/couchbase.py +29 -5
- agno/vectordb/lancedb/lance_db.py +92 -181
- agno/vectordb/langchaindb/langchaindb.py +24 -4
- agno/vectordb/lightrag/lightrag.py +17 -3
- agno/vectordb/llamaindex/llamaindexdb.py +25 -5
- agno/vectordb/milvus/milvus.py +50 -37
- agno/vectordb/mongodb/__init__.py +7 -1
- agno/vectordb/mongodb/mongodb.py +36 -30
- agno/vectordb/pgvector/pgvector.py +201 -77
- agno/vectordb/pineconedb/pineconedb.py +41 -23
- agno/vectordb/qdrant/qdrant.py +67 -54
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/singlestore.py +50 -29
- agno/vectordb/surrealdb/surrealdb.py +31 -41
- agno/vectordb/upstashdb/upstashdb.py +34 -6
- agno/vectordb/weaviate/weaviate.py +53 -14
- agno/workflow/__init__.py +2 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +120 -18
- agno/workflow/loop.py +77 -10
- agno/workflow/parallel.py +231 -143
- agno/workflow/router.py +118 -17
- agno/workflow/step.py +609 -170
- agno/workflow/steps.py +73 -6
- agno/workflow/types.py +96 -21
- agno/workflow/workflow.py +2039 -262
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/METADATA +201 -66
- agno-2.3.13.dist-info/RECORD +613 -0
- agno/tools/googlesearch.py +0 -98
- agno/tools/mcp.py +0 -679
- agno/tools/memori.py +0 -339
- agno-2.1.2.dist-info/RECORD +0 -543
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +0 -0
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/licenses/LICENSE +0 -0
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
agno/db/redis/schemas.py
CHANGED
|
@@ -27,6 +27,9 @@ USER_MEMORY_SCHEMA = {
|
|
|
27
27
|
"team_id": {"type": "string"},
|
|
28
28
|
"user_id": {"type": "string"},
|
|
29
29
|
"topics": {"type": "json"},
|
|
30
|
+
"input": {"type": "string"},
|
|
31
|
+
"feedback": {"type": "string"},
|
|
32
|
+
"created_at": {"type": "integer"},
|
|
30
33
|
"updated_at": {"type": "integer"},
|
|
31
34
|
}
|
|
32
35
|
|
|
@@ -81,6 +84,50 @@ KNOWLEDGE_SCHEMA = {
|
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
|
|
87
|
+
CULTURAL_KNOWLEDGE_SCHEMA = {
|
|
88
|
+
"id": {"type": "string", "primary_key": True},
|
|
89
|
+
"name": {"type": "string"},
|
|
90
|
+
"summary": {"type": "string"},
|
|
91
|
+
"content": {"type": "json"},
|
|
92
|
+
"metadata": {"type": "json"},
|
|
93
|
+
"input": {"type": "string"},
|
|
94
|
+
"created_at": {"type": "integer"},
|
|
95
|
+
"updated_at": {"type": "integer"},
|
|
96
|
+
"agent_id": {"type": "string"},
|
|
97
|
+
"team_id": {"type": "string"},
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
TRACE_SCHEMA = {
|
|
101
|
+
"trace_id": {"type": "string", "primary_key": True},
|
|
102
|
+
"name": {"type": "string"},
|
|
103
|
+
"status": {"type": "string"},
|
|
104
|
+
"duration_ms": {"type": "integer"},
|
|
105
|
+
"run_id": {"type": "string"},
|
|
106
|
+
"session_id": {"type": "string"},
|
|
107
|
+
"user_id": {"type": "string"},
|
|
108
|
+
"agent_id": {"type": "string"},
|
|
109
|
+
"team_id": {"type": "string"},
|
|
110
|
+
"workflow_id": {"type": "string"},
|
|
111
|
+
"start_time": {"type": "string"},
|
|
112
|
+
"end_time": {"type": "string"},
|
|
113
|
+
"created_at": {"type": "string"},
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
SPAN_SCHEMA = {
|
|
117
|
+
"span_id": {"type": "string", "primary_key": True},
|
|
118
|
+
"trace_id": {"type": "string"},
|
|
119
|
+
"parent_span_id": {"type": "string"},
|
|
120
|
+
"name": {"type": "string"},
|
|
121
|
+
"span_kind": {"type": "string"},
|
|
122
|
+
"status_code": {"type": "string"},
|
|
123
|
+
"status_message": {"type": "string"},
|
|
124
|
+
"start_time": {"type": "string"},
|
|
125
|
+
"end_time": {"type": "string"},
|
|
126
|
+
"attributes": {"type": "json"},
|
|
127
|
+
"created_at": {"type": "string"},
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
84
131
|
def get_table_schema_definition(table_type: str) -> dict[str, Any]:
|
|
85
132
|
"""
|
|
86
133
|
Get the expected schema definition for the given table.
|
|
@@ -100,6 +147,9 @@ def get_table_schema_definition(table_type: str) -> dict[str, Any]:
|
|
|
100
147
|
"metrics": METRICS_SCHEMA,
|
|
101
148
|
"evals": EVAL_SCHEMA,
|
|
102
149
|
"knowledge": KNOWLEDGE_SCHEMA,
|
|
150
|
+
"culture": CULTURAL_KNOWLEDGE_SCHEMA,
|
|
151
|
+
"traces": TRACE_SCHEMA,
|
|
152
|
+
"spans": SPAN_SCHEMA,
|
|
103
153
|
}
|
|
104
154
|
|
|
105
155
|
schema = schemas.get(table_type, {})
|
agno/db/redis/utils.py
CHANGED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
import json
|
|
4
4
|
import time
|
|
5
5
|
from datetime import date, datetime, timedelta, timezone
|
|
6
|
-
from typing import Any, Dict, List, Optional
|
|
6
|
+
from typing import Any, Dict, List, Optional, Union
|
|
7
7
|
from uuid import UUID
|
|
8
8
|
|
|
9
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
9
10
|
from agno.utils.log import log_warning
|
|
10
11
|
|
|
11
12
|
try:
|
|
12
|
-
from redis import Redis
|
|
13
|
+
from redis import Redis, RedisCluster
|
|
13
14
|
except ImportError:
|
|
14
15
|
raise ImportError("`redis` not installed. Please install it using `pip install redis`")
|
|
15
16
|
|
|
@@ -50,7 +51,7 @@ def generate_index_key(prefix: str, table_type: str, index_field: str, index_val
|
|
|
50
51
|
return f"{prefix}:{table_type}:index:{index_field}:{index_value}"
|
|
51
52
|
|
|
52
53
|
|
|
53
|
-
def get_all_keys_for_table(redis_client: Redis, prefix: str, table_type: str) -> List[str]:
|
|
54
|
+
def get_all_keys_for_table(redis_client: Union[Redis, RedisCluster], prefix: str, table_type: str) -> List[str]:
|
|
54
55
|
"""Get all relevant keys for the given table type.
|
|
55
56
|
|
|
56
57
|
Args:
|
|
@@ -129,7 +130,7 @@ def apply_filters(records: List[Dict[str, Any]], conditions: Dict[str, Any]) ->
|
|
|
129
130
|
|
|
130
131
|
|
|
131
132
|
def create_index_entries(
|
|
132
|
-
redis_client: Redis,
|
|
133
|
+
redis_client: Union[Redis, RedisCluster],
|
|
133
134
|
prefix: str,
|
|
134
135
|
table_type: str,
|
|
135
136
|
record_id: str,
|
|
@@ -143,7 +144,7 @@ def create_index_entries(
|
|
|
143
144
|
|
|
144
145
|
|
|
145
146
|
def remove_index_entries(
|
|
146
|
-
redis_client: Redis,
|
|
147
|
+
redis_client: Union[Redis, RedisCluster],
|
|
147
148
|
prefix: str,
|
|
148
149
|
table_type: str,
|
|
149
150
|
record_id: str,
|
|
@@ -199,7 +200,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
|
199
200
|
all_user_ids = set()
|
|
200
201
|
|
|
201
202
|
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
202
|
-
sessions = sessions_data.get(session_type, [])
|
|
203
|
+
sessions = sessions_data.get(session_type, []) or []
|
|
203
204
|
metrics[sessions_count_key] = len(sessions)
|
|
204
205
|
|
|
205
206
|
for session in sessions:
|
|
@@ -220,7 +221,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
|
220
221
|
|
|
221
222
|
model_metrics = []
|
|
222
223
|
for model, count in model_counts.items():
|
|
223
|
-
model_id, model_provider = model.
|
|
224
|
+
model_id, model_provider = model.rsplit(":", 1)
|
|
224
225
|
model_metrics.append({"model_id": model_id, "model_provider": model_provider, "count": count})
|
|
225
226
|
|
|
226
227
|
metrics["users_count"] = len(all_user_ids)
|
|
@@ -286,3 +287,60 @@ def get_dates_to_calculate_metrics_for(starting_date: date) -> list[date]:
|
|
|
286
287
|
if days_diff <= 0:
|
|
287
288
|
return []
|
|
288
289
|
return [starting_date + timedelta(days=x) for x in range(days_diff)]
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
# -- Cultural Knowledge util methods --
|
|
293
|
+
def serialize_cultural_knowledge_for_db(cultural_knowledge: CulturalKnowledge) -> Dict[str, Any]:
|
|
294
|
+
"""Serialize a CulturalKnowledge object for database storage.
|
|
295
|
+
|
|
296
|
+
Converts the model's separate content, categories, and notes fields
|
|
297
|
+
into a single dict for the database content column.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
cultural_knowledge (CulturalKnowledge): The cultural knowledge object to serialize.
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
Dict[str, Any]: A dictionary with the content field as a dict containing content, categories, and notes.
|
|
304
|
+
"""
|
|
305
|
+
content_dict: Dict[str, Any] = {}
|
|
306
|
+
if cultural_knowledge.content is not None:
|
|
307
|
+
content_dict["content"] = cultural_knowledge.content
|
|
308
|
+
if cultural_knowledge.categories is not None:
|
|
309
|
+
content_dict["categories"] = cultural_knowledge.categories
|
|
310
|
+
if cultural_knowledge.notes is not None:
|
|
311
|
+
content_dict["notes"] = cultural_knowledge.notes
|
|
312
|
+
|
|
313
|
+
return content_dict if content_dict else {}
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def deserialize_cultural_knowledge_from_db(db_row: Dict[str, Any]) -> CulturalKnowledge:
|
|
317
|
+
"""Deserialize a database row to a CulturalKnowledge object.
|
|
318
|
+
|
|
319
|
+
The database stores content as a dict containing content, categories, and notes.
|
|
320
|
+
This method extracts those fields and converts them back to the model format.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
db_row (Dict[str, Any]): The database row as a dictionary.
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
CulturalKnowledge: The cultural knowledge object.
|
|
327
|
+
"""
|
|
328
|
+
# Extract content, categories, and notes from the content field
|
|
329
|
+
content_json = db_row.get("content", {}) or {}
|
|
330
|
+
|
|
331
|
+
return CulturalKnowledge.from_dict(
|
|
332
|
+
{
|
|
333
|
+
"id": db_row.get("id"),
|
|
334
|
+
"name": db_row.get("name"),
|
|
335
|
+
"summary": db_row.get("summary"),
|
|
336
|
+
"content": content_json.get("content"),
|
|
337
|
+
"categories": content_json.get("categories"),
|
|
338
|
+
"notes": content_json.get("notes"),
|
|
339
|
+
"metadata": db_row.get("metadata"),
|
|
340
|
+
"input": db_row.get("input"),
|
|
341
|
+
"created_at": db_row.get("created_at"),
|
|
342
|
+
"updated_at": db_row.get("updated_at"),
|
|
343
|
+
"agent_id": db_row.get("agent_id"),
|
|
344
|
+
"team_id": db_row.get("team_id"),
|
|
345
|
+
}
|
|
346
|
+
)
|
agno/db/schemas/__init__.py
CHANGED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
|
+
from typing import Any, Dict, Optional, Union
|
|
4
|
+
|
|
5
|
+
from typing_extensions import List
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class CulturalKnowledge:
|
|
10
|
+
"""Model for Cultural Knowledge
|
|
11
|
+
|
|
12
|
+
Notice: Culture is an experimental feature and is subject to change.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
# The id of the cultural knowledge, auto-generated if not provided
|
|
16
|
+
id: Optional[str] = None
|
|
17
|
+
name: Optional[str] = None
|
|
18
|
+
|
|
19
|
+
content: Optional[str] = None
|
|
20
|
+
categories: Optional[List[str]] = None
|
|
21
|
+
notes: Optional[List[str]] = None
|
|
22
|
+
|
|
23
|
+
summary: Optional[str] = None
|
|
24
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
25
|
+
input: Optional[str] = None
|
|
26
|
+
|
|
27
|
+
created_at: Optional[int] = field(default=None)
|
|
28
|
+
updated_at: Optional[int] = field(default=None)
|
|
29
|
+
|
|
30
|
+
agent_id: Optional[str] = None
|
|
31
|
+
team_id: Optional[str] = None
|
|
32
|
+
|
|
33
|
+
def __post_init__(self):
|
|
34
|
+
if self.name is not None and not self.name.strip():
|
|
35
|
+
raise ValueError("name must be a non-empty string")
|
|
36
|
+
self.created_at = _now_epoch_s() if self.created_at is None else _to_epoch_s(self.created_at)
|
|
37
|
+
self.updated_at = self.created_at if self.updated_at is None else _to_epoch_s(self.updated_at)
|
|
38
|
+
|
|
39
|
+
def bump_updated_at(self) -> None:
|
|
40
|
+
"""Bump updated_at to now (UTC)."""
|
|
41
|
+
self.updated_at = _now_epoch_s()
|
|
42
|
+
|
|
43
|
+
def preview(self) -> Dict[str, Any]:
|
|
44
|
+
"""Return a preview of the cultural knowledge"""
|
|
45
|
+
_preview: Dict[str, Any] = {
|
|
46
|
+
"name": self.name,
|
|
47
|
+
}
|
|
48
|
+
if self.categories is not None:
|
|
49
|
+
_preview["categories"] = self.categories
|
|
50
|
+
if self.summary is not None:
|
|
51
|
+
_preview["summary"] = self.summary[:100] + "..." if len(self.summary) > 100 else self.summary
|
|
52
|
+
if self.content is not None:
|
|
53
|
+
_preview["content"] = self.content[:100] + "..." if len(self.content) > 100 else self.content
|
|
54
|
+
if self.notes is not None:
|
|
55
|
+
_preview["notes"] = [note[:100] + "..." if len(note) > 100 else note for note in self.notes]
|
|
56
|
+
return _preview
|
|
57
|
+
|
|
58
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
59
|
+
_dict = {
|
|
60
|
+
"id": self.id,
|
|
61
|
+
"name": self.name,
|
|
62
|
+
"summary": self.summary,
|
|
63
|
+
"content": self.content,
|
|
64
|
+
"categories": self.categories,
|
|
65
|
+
"metadata": self.metadata,
|
|
66
|
+
"notes": self.notes,
|
|
67
|
+
"input": self.input,
|
|
68
|
+
"created_at": (_epoch_to_rfc3339_z(self.created_at) if self.created_at is not None else None),
|
|
69
|
+
"updated_at": (_epoch_to_rfc3339_z(self.updated_at) if self.updated_at is not None else None),
|
|
70
|
+
"agent_id": self.agent_id,
|
|
71
|
+
"team_id": self.team_id,
|
|
72
|
+
}
|
|
73
|
+
return {k: v for k, v in _dict.items() if v is not None}
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def from_dict(cls, data: Dict[str, Any]) -> "CulturalKnowledge":
|
|
77
|
+
d = dict(data)
|
|
78
|
+
|
|
79
|
+
# Preserve 0 and None explicitly; only process if key exists
|
|
80
|
+
if "created_at" in d and d["created_at"] is not None:
|
|
81
|
+
d["created_at"] = _to_epoch_s(d["created_at"])
|
|
82
|
+
if "updated_at" in d and d["updated_at"] is not None:
|
|
83
|
+
d["updated_at"] = _to_epoch_s(d["updated_at"])
|
|
84
|
+
|
|
85
|
+
return cls(**d)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _now_epoch_s() -> int:
|
|
89
|
+
return int(datetime.now(timezone.utc).timestamp())
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _to_epoch_s(value: Union[int, float, str, datetime]) -> int:
|
|
93
|
+
"""Normalize various datetime representations to epoch seconds (UTC)."""
|
|
94
|
+
if isinstance(value, (int, float)):
|
|
95
|
+
# assume value is already in seconds
|
|
96
|
+
return int(value)
|
|
97
|
+
|
|
98
|
+
if isinstance(value, datetime):
|
|
99
|
+
dt = value
|
|
100
|
+
if dt.tzinfo is None:
|
|
101
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
|
102
|
+
return int(dt.timestamp())
|
|
103
|
+
|
|
104
|
+
if isinstance(value, str):
|
|
105
|
+
s = value.strip()
|
|
106
|
+
if s.endswith("Z"):
|
|
107
|
+
s = s[:-1] + "+00:00"
|
|
108
|
+
try:
|
|
109
|
+
dt = datetime.fromisoformat(s)
|
|
110
|
+
except ValueError as e:
|
|
111
|
+
raise ValueError(f"Unsupported datetime string: {value!r}") from e
|
|
112
|
+
if dt.tzinfo is None:
|
|
113
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
|
114
|
+
return int(dt.timestamp())
|
|
115
|
+
|
|
116
|
+
raise TypeError(f"Unsupported datetime value: {type(value)}")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _epoch_to_rfc3339_z(ts: Union[int, float]) -> str:
|
|
120
|
+
return datetime.fromtimestamp(float(ts), tz=timezone.utc).isoformat().replace("+00:00", "Z")
|
agno/db/schemas/evals.py
CHANGED
agno/db/schemas/memory.py
CHANGED
|
@@ -2,6 +2,8 @@ from dataclasses import dataclass
|
|
|
2
2
|
from datetime import datetime, timezone
|
|
3
3
|
from typing import Any, Dict, List, Optional
|
|
4
4
|
|
|
5
|
+
from agno.utils.dttm import now_epoch_s
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
@dataclass
|
|
7
9
|
class UserMemory:
|
|
@@ -12,18 +14,25 @@ class UserMemory:
|
|
|
12
14
|
topics: Optional[List[str]] = None
|
|
13
15
|
user_id: Optional[str] = None
|
|
14
16
|
input: Optional[str] = None
|
|
15
|
-
|
|
17
|
+
created_at: Optional[int] = None
|
|
18
|
+
updated_at: Optional[int] = None
|
|
16
19
|
feedback: Optional[str] = None
|
|
17
20
|
|
|
18
21
|
agent_id: Optional[str] = None
|
|
19
22
|
team_id: Optional[str] = None
|
|
20
23
|
|
|
24
|
+
def __post_init__(self) -> None:
|
|
25
|
+
"""Automatically set created_at if not provided."""
|
|
26
|
+
if self.created_at is None:
|
|
27
|
+
self.created_at = now_epoch_s()
|
|
28
|
+
|
|
21
29
|
def to_dict(self) -> Dict[str, Any]:
|
|
22
30
|
_dict = {
|
|
23
31
|
"memory_id": self.memory_id,
|
|
24
32
|
"memory": self.memory,
|
|
25
33
|
"topics": self.topics,
|
|
26
|
-
"
|
|
34
|
+
"created_at": datetime.fromtimestamp(self.created_at).isoformat() if self.created_at else None,
|
|
35
|
+
"updated_at": datetime.fromtimestamp(self.updated_at).isoformat() if self.updated_at else None,
|
|
27
36
|
"input": self.input,
|
|
28
37
|
"user_id": self.user_id,
|
|
29
38
|
"agent_id": self.agent_id,
|
|
@@ -36,6 +45,12 @@ class UserMemory:
|
|
|
36
45
|
def from_dict(cls, data: Dict[str, Any]) -> "UserMemory":
|
|
37
46
|
data = dict(data)
|
|
38
47
|
|
|
48
|
+
if created_at := data.get("created_at"):
|
|
49
|
+
if isinstance(created_at, (int, float)):
|
|
50
|
+
data["created_at"] = datetime.fromtimestamp(created_at, tz=timezone.utc)
|
|
51
|
+
else:
|
|
52
|
+
data["created_at"] = datetime.fromisoformat(created_at)
|
|
53
|
+
|
|
39
54
|
# Convert updated_at to datetime
|
|
40
55
|
if updated_at := data.get("updated_at"):
|
|
41
56
|
if isinstance(updated_at, (int, float)):
|
agno/db/singlestore/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
|
|
|
@@ -92,6 +94,63 @@ METRICS_TABLE_SCHEMA = {
|
|
|
92
94
|
"completed": {"type": Boolean, "nullable": False, "default": False},
|
|
93
95
|
}
|
|
94
96
|
|
|
97
|
+
CULTURAL_KNOWLEDGE_TABLE_SCHEMA = {
|
|
98
|
+
"id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
|
|
99
|
+
"name": {"type": lambda: String(255), "nullable": False, "index": True},
|
|
100
|
+
"summary": {"type": Text, "nullable": True},
|
|
101
|
+
"content": {"type": JSON, "nullable": True},
|
|
102
|
+
"metadata": {"type": JSON, "nullable": True},
|
|
103
|
+
"input": {"type": Text, "nullable": True},
|
|
104
|
+
"created_at": {"type": BigInteger, "nullable": True},
|
|
105
|
+
"updated_at": {"type": BigInteger, "nullable": True},
|
|
106
|
+
"agent_id": {"type": lambda: String(128), "nullable": True},
|
|
107
|
+
"team_id": {"type": lambda: String(128), "nullable": True},
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
VERSIONS_TABLE_SCHEMA = {
|
|
112
|
+
"table_name": {"type": lambda: String(128), "nullable": False, "primary_key": True},
|
|
113
|
+
"version": {"type": lambda: String(10), "nullable": False},
|
|
114
|
+
"created_at": {"type": lambda: String(128), "nullable": False, "index": True},
|
|
115
|
+
"updated_at": {"type": lambda: String(128), "nullable": True},
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
TRACE_TABLE_SCHEMA = {
|
|
119
|
+
"trace_id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
|
|
120
|
+
"name": {"type": lambda: String(512), "nullable": False},
|
|
121
|
+
"status": {"type": lambda: String(20), "nullable": False, "index": True},
|
|
122
|
+
"start_time": {"type": lambda: String(64), "nullable": False, "index": True}, # ISO 8601 datetime string
|
|
123
|
+
"end_time": {"type": lambda: String(64), "nullable": False}, # ISO 8601 datetime string
|
|
124
|
+
"duration_ms": {"type": BigInteger, "nullable": False},
|
|
125
|
+
"run_id": {"type": lambda: String(128), "nullable": True, "index": True},
|
|
126
|
+
"session_id": {"type": lambda: String(128), "nullable": True, "index": True},
|
|
127
|
+
"user_id": {"type": lambda: String(128), "nullable": True, "index": True},
|
|
128
|
+
"agent_id": {"type": lambda: String(128), "nullable": True, "index": True},
|
|
129
|
+
"team_id": {"type": lambda: String(128), "nullable": True, "index": True},
|
|
130
|
+
"workflow_id": {"type": lambda: String(128), "nullable": True, "index": True},
|
|
131
|
+
"created_at": {"type": lambda: String(64), "nullable": False, "index": True}, # ISO 8601 datetime string
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
SPAN_TABLE_SCHEMA = {
|
|
135
|
+
"span_id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
|
|
136
|
+
"trace_id": {
|
|
137
|
+
"type": lambda: String(128),
|
|
138
|
+
"nullable": False,
|
|
139
|
+
"index": True,
|
|
140
|
+
"foreign_key": "agno_traces.trace_id", # Foreign key to traces table
|
|
141
|
+
},
|
|
142
|
+
"parent_span_id": {"type": lambda: String(128), "nullable": True, "index": True},
|
|
143
|
+
"name": {"type": lambda: String(512), "nullable": False},
|
|
144
|
+
"span_kind": {"type": lambda: String(50), "nullable": False},
|
|
145
|
+
"status_code": {"type": lambda: String(20), "nullable": False},
|
|
146
|
+
"status_message": {"type": Text, "nullable": True},
|
|
147
|
+
"start_time": {"type": lambda: String(64), "nullable": False, "index": True}, # ISO 8601 datetime string
|
|
148
|
+
"end_time": {"type": lambda: String(64), "nullable": False}, # ISO 8601 datetime string
|
|
149
|
+
"duration_ms": {"type": BigInteger, "nullable": False},
|
|
150
|
+
"attributes": {"type": JSON, "nullable": True},
|
|
151
|
+
"created_at": {"type": lambda: String(64), "nullable": False, "index": True}, # ISO 8601 datetime string
|
|
152
|
+
}
|
|
153
|
+
|
|
95
154
|
|
|
96
155
|
def get_table_schema_definition(table_type: str) -> dict[str, Any]:
|
|
97
156
|
"""
|
|
@@ -107,6 +166,10 @@ def get_table_schema_definition(table_type: str) -> dict[str, Any]:
|
|
|
107
166
|
"metrics": METRICS_TABLE_SCHEMA,
|
|
108
167
|
"memories": USER_MEMORY_TABLE_SCHEMA,
|
|
109
168
|
"knowledge": KNOWLEDGE_TABLE_SCHEMA,
|
|
169
|
+
"culture": CULTURAL_KNOWLEDGE_TABLE_SCHEMA,
|
|
170
|
+
"versions": VERSIONS_TABLE_SCHEMA,
|
|
171
|
+
"traces": TRACE_TABLE_SCHEMA,
|
|
172
|
+
"spans": SPAN_TABLE_SCHEMA,
|
|
110
173
|
}
|
|
111
174
|
schema = schemas.get(table_type, {})
|
|
112
175
|
|