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
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from typing import Any, Dict, Optional, Sequence, TypeVar, Union, cast
|
|
3
|
+
|
|
4
|
+
from surrealdb import BlockingHttpSurrealConnection, BlockingWsSurrealConnection, Surreal
|
|
5
|
+
|
|
6
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
7
|
+
from agno.utils.log import logger
|
|
8
|
+
|
|
9
|
+
RecordType = TypeVar("RecordType")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def build_client(
|
|
13
|
+
url: str, creds: dict[str, str], ns: str, db: str
|
|
14
|
+
) -> Union[BlockingWsSurrealConnection, BlockingHttpSurrealConnection]:
|
|
15
|
+
client = Surreal(url=url)
|
|
16
|
+
client.signin(creds)
|
|
17
|
+
client.use(namespace=ns, database=db)
|
|
18
|
+
return client
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _query_aux(
|
|
22
|
+
client: Union[BlockingWsSurrealConnection, BlockingHttpSurrealConnection],
|
|
23
|
+
query: str,
|
|
24
|
+
vars: dict[str, Any],
|
|
25
|
+
) -> Union[list, dict, str, int]:
|
|
26
|
+
try:
|
|
27
|
+
response = client.query(query, vars)
|
|
28
|
+
except Exception as e:
|
|
29
|
+
msg = f"!! Query execution error: {query} with {vars}, Error: {e}"
|
|
30
|
+
logger.error(msg)
|
|
31
|
+
raise RuntimeError(msg)
|
|
32
|
+
return response
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def query(
|
|
36
|
+
client: Union[BlockingWsSurrealConnection, BlockingHttpSurrealConnection],
|
|
37
|
+
query: str,
|
|
38
|
+
vars: dict[str, Any],
|
|
39
|
+
record_type: type[RecordType],
|
|
40
|
+
) -> Sequence[RecordType]:
|
|
41
|
+
response = _query_aux(client, query, vars)
|
|
42
|
+
if isinstance(response, list):
|
|
43
|
+
if dataclasses.is_dataclass(record_type) and hasattr(record_type, "from_dict"):
|
|
44
|
+
return [getattr(record_type, "from_dict").__call__(x) for x in response]
|
|
45
|
+
else:
|
|
46
|
+
result: list[RecordType] = []
|
|
47
|
+
for x in response:
|
|
48
|
+
if isinstance(x, dict):
|
|
49
|
+
result.append(record_type(**x))
|
|
50
|
+
else:
|
|
51
|
+
result.append(record_type.__call__(x))
|
|
52
|
+
return result
|
|
53
|
+
else:
|
|
54
|
+
raise ValueError(f"Unexpected response type: {type(response)}")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def query_one(
|
|
58
|
+
client: Union[BlockingWsSurrealConnection, BlockingHttpSurrealConnection],
|
|
59
|
+
query: str,
|
|
60
|
+
vars: dict[str, Any],
|
|
61
|
+
record_type: type[RecordType],
|
|
62
|
+
) -> Optional[RecordType]:
|
|
63
|
+
response = _query_aux(client, query, vars)
|
|
64
|
+
if response is None:
|
|
65
|
+
return None
|
|
66
|
+
elif not isinstance(response, list):
|
|
67
|
+
if dataclasses.is_dataclass(record_type) and hasattr(record_type, "from_dict"):
|
|
68
|
+
return getattr(record_type, "from_dict").__call__(response)
|
|
69
|
+
elif isinstance(response, dict):
|
|
70
|
+
return record_type(**response)
|
|
71
|
+
else:
|
|
72
|
+
return record_type.__call__(response)
|
|
73
|
+
elif isinstance(response, list):
|
|
74
|
+
# Handle list responses - SurrealDB might return a list with a single element
|
|
75
|
+
if len(response) == 1 and isinstance(response[0], dict):
|
|
76
|
+
result = response[0]
|
|
77
|
+
if dataclasses.is_dataclass(record_type) and hasattr(record_type, "from_dict"):
|
|
78
|
+
return getattr(record_type, "from_dict").__call__(result)
|
|
79
|
+
elif record_type is dict:
|
|
80
|
+
return cast(RecordType, result)
|
|
81
|
+
else:
|
|
82
|
+
return record_type(**result)
|
|
83
|
+
elif len(response) == 0:
|
|
84
|
+
return None
|
|
85
|
+
else:
|
|
86
|
+
raise ValueError(f"Expected single record, got {len(response)} records: {response}")
|
|
87
|
+
else:
|
|
88
|
+
raise ValueError(f"Unexpected response type: {type(response)}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# -- Cultural Knowledge util methods --
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def serialize_cultural_knowledge_for_db(cultural_knowledge: CulturalKnowledge) -> Dict[str, Any]:
|
|
95
|
+
"""Serialize a CulturalKnowledge object for database storage.
|
|
96
|
+
|
|
97
|
+
Converts the model's separate content, categories, and notes fields
|
|
98
|
+
into a single dict for the database content field.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
cultural_knowledge (CulturalKnowledge): The cultural knowledge object to serialize.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Dict[str, Any]: A dictionary with content, categories, and notes.
|
|
105
|
+
"""
|
|
106
|
+
content_dict: Dict[str, Any] = {}
|
|
107
|
+
if cultural_knowledge.content is not None:
|
|
108
|
+
content_dict["content"] = cultural_knowledge.content
|
|
109
|
+
if cultural_knowledge.categories is not None:
|
|
110
|
+
content_dict["categories"] = cultural_knowledge.categories
|
|
111
|
+
if cultural_knowledge.notes is not None:
|
|
112
|
+
content_dict["notes"] = cultural_knowledge.notes
|
|
113
|
+
|
|
114
|
+
return content_dict if content_dict else {}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def deserialize_cultural_knowledge_from_db(db_row: Dict[str, Any]) -> CulturalKnowledge:
|
|
118
|
+
"""Deserialize a database row to a CulturalKnowledge object.
|
|
119
|
+
|
|
120
|
+
The database stores content as a dict containing content, categories, and notes.
|
|
121
|
+
This method extracts those fields and converts them back to the model format.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
db_row (Dict[str, Any]): The database row as a dictionary.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
CulturalKnowledge: The cultural knowledge object.
|
|
128
|
+
"""
|
|
129
|
+
# Extract content, categories, and notes from the content field
|
|
130
|
+
content_json = db_row.get("content", {}) or {}
|
|
131
|
+
|
|
132
|
+
return CulturalKnowledge.from_dict(
|
|
133
|
+
{
|
|
134
|
+
"id": db_row.get("id"),
|
|
135
|
+
"name": db_row.get("name"),
|
|
136
|
+
"summary": db_row.get("summary"),
|
|
137
|
+
"content": content_json.get("content"),
|
|
138
|
+
"categories": content_json.get("categories"),
|
|
139
|
+
"notes": content_json.get("notes"),
|
|
140
|
+
"metadata": db_row.get("metadata"),
|
|
141
|
+
"input": db_row.get("input"),
|
|
142
|
+
"created_at": db_row.get("created_at"),
|
|
143
|
+
"updated_at": db_row.get("updated_at"),
|
|
144
|
+
"agent_id": db_row.get("agent_id"),
|
|
145
|
+
"team_id": db_row.get("team_id"),
|
|
146
|
+
}
|
|
147
|
+
)
|
agno/db/utils.py
CHANGED
|
@@ -4,7 +4,6 @@ import json
|
|
|
4
4
|
from datetime import date, datetime
|
|
5
5
|
from uuid import UUID
|
|
6
6
|
|
|
7
|
-
from agno.db.base import SessionType
|
|
8
7
|
from agno.models.message import Message
|
|
9
8
|
from agno.models.metrics import Metrics
|
|
10
9
|
|
|
@@ -55,34 +54,63 @@ def serialize_session_json_fields(session: dict) -> dict:
|
|
|
55
54
|
|
|
56
55
|
|
|
57
56
|
def deserialize_session_json_fields(session: dict) -> dict:
|
|
58
|
-
"""Deserialize
|
|
57
|
+
"""Deserialize JSON fields in the given Session dictionary.
|
|
59
58
|
|
|
60
59
|
Args:
|
|
61
60
|
session (dict): The dictionary to deserialize.
|
|
62
61
|
|
|
63
62
|
Returns:
|
|
64
|
-
dict: The dictionary with JSON fields deserialized.
|
|
63
|
+
dict: The dictionary with JSON string fields deserialized to objects.
|
|
65
64
|
"""
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if session.get("
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if session.get("
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
from agno.utils.log import log_warning
|
|
66
|
+
|
|
67
|
+
if session.get("agent_data") is not None and isinstance(session["agent_data"], str):
|
|
68
|
+
try:
|
|
69
|
+
session["agent_data"] = json.loads(session["agent_data"])
|
|
70
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
71
|
+
log_warning(f"Warning: Could not parse agent_data as JSON, keeping as string: {e}")
|
|
72
|
+
|
|
73
|
+
if session.get("team_data") is not None and isinstance(session["team_data"], str):
|
|
74
|
+
try:
|
|
75
|
+
session["team_data"] = json.loads(session["team_data"])
|
|
76
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
77
|
+
log_warning(f"Warning: Could not parse team_data as JSON, keeping as string: {e}")
|
|
78
|
+
|
|
79
|
+
if session.get("workflow_data") is not None and isinstance(session["workflow_data"], str):
|
|
80
|
+
try:
|
|
81
|
+
session["workflow_data"] = json.loads(session["workflow_data"])
|
|
82
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
83
|
+
log_warning(f"Warning: Could not parse workflow_data as JSON, keeping as string: {e}")
|
|
84
|
+
|
|
85
|
+
if session.get("metadata") is not None and isinstance(session["metadata"], str):
|
|
86
|
+
try:
|
|
87
|
+
session["metadata"] = json.loads(session["metadata"])
|
|
88
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
89
|
+
log_warning(f"Warning: Could not parse metadata as JSON, keeping as string: {e}")
|
|
90
|
+
|
|
91
|
+
if session.get("chat_history") is not None and isinstance(session["chat_history"], str):
|
|
92
|
+
try:
|
|
93
|
+
session["chat_history"] = json.loads(session["chat_history"])
|
|
94
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
95
|
+
log_warning(f"Warning: Could not parse chat_history as JSON, keeping as string: {e}")
|
|
96
|
+
|
|
97
|
+
if session.get("summary") is not None and isinstance(session["summary"], str):
|
|
98
|
+
try:
|
|
99
|
+
session["summary"] = json.loads(session["summary"])
|
|
100
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
101
|
+
log_warning(f"Warning: Could not parse summary as JSON, keeping as string: {e}")
|
|
102
|
+
|
|
78
103
|
if session.get("session_data") is not None and isinstance(session["session_data"], str):
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
104
|
+
try:
|
|
105
|
+
session["session_data"] = json.loads(session["session_data"])
|
|
106
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
107
|
+
log_warning(f"Warning: Could not parse session_data as JSON, keeping as string: {e}")
|
|
108
|
+
|
|
109
|
+
# Handle runs field with session type checking
|
|
110
|
+
if session.get("runs") is not None and isinstance(session["runs"], str):
|
|
111
|
+
try:
|
|
86
112
|
session["runs"] = json.loads(session["runs"])
|
|
113
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
114
|
+
log_warning(f"Warning: Could not parse runs as JSON, keeping as string: {e}")
|
|
87
115
|
|
|
88
116
|
return session
|
agno/eval/accuracy.py
CHANGED
|
@@ -7,13 +7,13 @@ from uuid import uuid4
|
|
|
7
7
|
from pydantic import BaseModel, Field
|
|
8
8
|
|
|
9
9
|
from agno.agent import Agent
|
|
10
|
-
from agno.db.base import BaseDb
|
|
10
|
+
from agno.db.base import AsyncBaseDb, BaseDb
|
|
11
11
|
from agno.db.schemas.evals import EvalType
|
|
12
12
|
from agno.eval.utils import async_log_eval, log_eval_run, store_result_in_file
|
|
13
13
|
from agno.exceptions import EvalError
|
|
14
14
|
from agno.models.base import Model
|
|
15
15
|
from agno.team.team import Team
|
|
16
|
-
from agno.utils.log import logger, set_log_level_to_debug, set_log_level_to_info
|
|
16
|
+
from agno.utils.log import log_error, logger, set_log_level_to_debug, set_log_level_to_info
|
|
17
17
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
19
|
from rich.console import Console
|
|
@@ -176,7 +176,7 @@ class AccuracyEval:
|
|
|
176
176
|
# Enable debug logs
|
|
177
177
|
debug_mode: bool = getenv("AGNO_DEBUG", "false").lower() == "true"
|
|
178
178
|
# The database to store Evaluation results
|
|
179
|
-
db: Optional[BaseDb] = None
|
|
179
|
+
db: Optional[Union[BaseDb, AsyncBaseDb]] = None
|
|
180
180
|
|
|
181
181
|
# Telemetry settings
|
|
182
182
|
# telemetry=True logs minimal telemetry for analytics
|
|
@@ -327,6 +327,9 @@ Remember: You must only compare the agent_output to the expected_output. The exp
|
|
|
327
327
|
print_summary: bool = True,
|
|
328
328
|
print_results: bool = True,
|
|
329
329
|
) -> Optional[AccuracyResult]:
|
|
330
|
+
if isinstance(self.db, AsyncBaseDb):
|
|
331
|
+
raise ValueError("run() is not supported with an async DB. Please use arun() instead.")
|
|
332
|
+
|
|
330
333
|
if self.agent is None and self.team is None:
|
|
331
334
|
logger.error("You need to provide one of 'agent' or 'team' to run the evaluation.")
|
|
332
335
|
return None
|
|
@@ -661,47 +664,51 @@ Remember: You must only compare the agent_output to the expected_output. The exp
|
|
|
661
664
|
)
|
|
662
665
|
# Log results to the Agno DB if requested
|
|
663
666
|
if self.db:
|
|
664
|
-
if self.
|
|
665
|
-
|
|
666
|
-
team_id = None
|
|
667
|
-
model_id = self.agent.model.id if self.agent.model is not None else None
|
|
668
|
-
model_provider = self.agent.model.provider if self.agent.model is not None else None
|
|
669
|
-
evaluated_component_name = self.agent.name
|
|
670
|
-
elif self.team is not None:
|
|
671
|
-
agent_id = None
|
|
672
|
-
team_id = self.team.id
|
|
673
|
-
model_id = self.team.model.id if self.team.model is not None else None
|
|
674
|
-
model_provider = self.team.model.provider if self.team.model is not None else None
|
|
675
|
-
evaluated_component_name = self.team.name
|
|
676
|
-
else:
|
|
677
|
-
agent_id = None
|
|
678
|
-
team_id = None
|
|
679
|
-
model_id = None
|
|
680
|
-
model_provider = None
|
|
681
|
-
evaluated_component_name = None
|
|
667
|
+
if isinstance(self.db, AsyncBaseDb):
|
|
668
|
+
log_error("You are using an async DB in a non-async method. The evaluation won't be stored in the DB.")
|
|
682
669
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
670
|
+
else:
|
|
671
|
+
if self.agent is not None:
|
|
672
|
+
agent_id = self.agent.id
|
|
673
|
+
team_id = None
|
|
674
|
+
model_id = self.agent.model.id if self.agent.model is not None else None
|
|
675
|
+
model_provider = self.agent.model.provider if self.agent.model is not None else None
|
|
676
|
+
evaluated_component_name = self.agent.name
|
|
677
|
+
elif self.team is not None:
|
|
678
|
+
agent_id = None
|
|
679
|
+
team_id = self.team.id
|
|
680
|
+
model_id = self.team.model.id if self.team.model is not None else None
|
|
681
|
+
model_provider = self.team.model.provider if self.team.model is not None else None
|
|
682
|
+
evaluated_component_name = self.team.name
|
|
683
|
+
else:
|
|
684
|
+
agent_id = None
|
|
685
|
+
team_id = None
|
|
686
|
+
model_id = None
|
|
687
|
+
model_provider = None
|
|
688
|
+
evaluated_component_name = None
|
|
689
|
+
|
|
690
|
+
log_eval_input = {
|
|
691
|
+
"additional_guidelines": self.additional_guidelines,
|
|
692
|
+
"additional_context": self.additional_context,
|
|
693
|
+
"num_iterations": self.num_iterations,
|
|
694
|
+
"expected_output": self.expected_output,
|
|
695
|
+
"input": self.input,
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
log_eval_run(
|
|
699
|
+
db=self.db,
|
|
700
|
+
run_id=self.eval_id, # type: ignore
|
|
701
|
+
run_data=asdict(self.result),
|
|
702
|
+
eval_type=EvalType.ACCURACY,
|
|
703
|
+
name=self.name if self.name is not None else None,
|
|
704
|
+
agent_id=agent_id,
|
|
705
|
+
team_id=team_id,
|
|
706
|
+
model_id=model_id,
|
|
707
|
+
model_provider=model_provider,
|
|
708
|
+
evaluated_component_name=evaluated_component_name,
|
|
709
|
+
workflow_id=None,
|
|
710
|
+
eval_input=log_eval_input,
|
|
711
|
+
)
|
|
705
712
|
|
|
706
713
|
if self.telemetry:
|
|
707
714
|
from agno.api.evals import EvalRunCreate, create_eval_run_telemetry
|
agno/eval/performance.py
CHANGED
|
@@ -3,10 +3,10 @@ import gc
|
|
|
3
3
|
import tracemalloc
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
5
|
from os import getenv
|
|
6
|
-
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union
|
|
7
7
|
from uuid import uuid4
|
|
8
8
|
|
|
9
|
-
from agno.db.base import BaseDb
|
|
9
|
+
from agno.db.base import AsyncBaseDb, BaseDb
|
|
10
10
|
from agno.db.schemas.evals import EvalType
|
|
11
11
|
from agno.eval.utils import async_log_eval, log_eval_run, store_result_in_file
|
|
12
12
|
from agno.utils.log import log_debug, set_log_level_to_debug, set_log_level_to_info
|
|
@@ -222,7 +222,7 @@ class PerformanceEval:
|
|
|
222
222
|
# Enable debug logs
|
|
223
223
|
debug_mode: bool = getenv("AGNO_DEBUG", "false").lower() == "true"
|
|
224
224
|
# The database to store Evaluation results
|
|
225
|
-
db: Optional[BaseDb] = None
|
|
225
|
+
db: Optional[Union[BaseDb, AsyncBaseDb]] = None
|
|
226
226
|
|
|
227
227
|
# Telemetry settings
|
|
228
228
|
# telemetry=True logs minimal telemetry for analytics
|
|
@@ -491,6 +491,9 @@ class PerformanceEval:
|
|
|
491
491
|
6. Print results as requested
|
|
492
492
|
7. Log results to the Agno platform if requested
|
|
493
493
|
"""
|
|
494
|
+
if isinstance(self.db, AsyncBaseDb):
|
|
495
|
+
raise ValueError("run() is not supported with an async DB. Please use arun() instead.")
|
|
496
|
+
|
|
494
497
|
from rich.console import Console
|
|
495
498
|
from rich.live import Live
|
|
496
499
|
from rich.status import Status
|
agno/eval/reliability.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from dataclasses import asdict, dataclass, field
|
|
2
2
|
from os import getenv
|
|
3
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
|
4
4
|
from uuid import uuid4
|
|
5
5
|
|
|
6
|
-
from agno.db.base import BaseDb
|
|
6
|
+
from agno.db.base import AsyncBaseDb, BaseDb
|
|
7
7
|
|
|
8
8
|
if TYPE_CHECKING:
|
|
9
9
|
from rich.console import Console
|
|
@@ -63,7 +63,7 @@ class ReliabilityEval:
|
|
|
63
63
|
# Enable debug logs
|
|
64
64
|
debug_mode: bool = getenv("AGNO_DEBUG", "false").lower() == "true"
|
|
65
65
|
# The database to store Evaluation results
|
|
66
|
-
db: Optional[BaseDb] = None
|
|
66
|
+
db: Optional[Union[BaseDb, AsyncBaseDb]] = None
|
|
67
67
|
|
|
68
68
|
# Telemetry settings
|
|
69
69
|
# telemetry=True logs minimal telemetry for analytics
|
|
@@ -71,6 +71,9 @@ class ReliabilityEval:
|
|
|
71
71
|
telemetry: bool = True
|
|
72
72
|
|
|
73
73
|
def run(self, *, print_results: bool = False) -> Optional[ReliabilityResult]:
|
|
74
|
+
if isinstance(self.db, AsyncBaseDb):
|
|
75
|
+
raise ValueError("run() is not supported with an async DB. Please use arun() instead.")
|
|
76
|
+
|
|
74
77
|
if self.agent_response is None and self.team_response is None:
|
|
75
78
|
raise ValueError("You need to provide 'agent_response' or 'team_response' to run the evaluation.")
|
|
76
79
|
|
agno/eval/utils.py
CHANGED
|
@@ -2,7 +2,7 @@ from dataclasses import asdict
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from typing import TYPE_CHECKING, Optional, Union
|
|
4
4
|
|
|
5
|
-
from agno.db.base import BaseDb
|
|
5
|
+
from agno.db.base import AsyncBaseDb, BaseDb
|
|
6
6
|
from agno.db.schemas.evals import EvalRunRecord, EvalType
|
|
7
7
|
from agno.utils.log import log_debug, logger
|
|
8
8
|
|
|
@@ -49,7 +49,7 @@ def log_eval_run(
|
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
async def async_log_eval(
|
|
52
|
-
db: BaseDb,
|
|
52
|
+
db: Union[BaseDb, AsyncBaseDb],
|
|
53
53
|
run_id: str,
|
|
54
54
|
run_data: dict,
|
|
55
55
|
eval_type: EvalType,
|
|
@@ -65,21 +65,38 @@ async def async_log_eval(
|
|
|
65
65
|
"""Call the API to create an evaluation run."""
|
|
66
66
|
|
|
67
67
|
try:
|
|
68
|
-
db
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
68
|
+
if isinstance(db, AsyncBaseDb):
|
|
69
|
+
await db.create_eval_run(
|
|
70
|
+
EvalRunRecord(
|
|
71
|
+
run_id=run_id,
|
|
72
|
+
eval_type=eval_type,
|
|
73
|
+
eval_data=run_data,
|
|
74
|
+
eval_input=eval_input,
|
|
75
|
+
agent_id=agent_id,
|
|
76
|
+
model_id=model_id,
|
|
77
|
+
model_provider=model_provider,
|
|
78
|
+
name=name,
|
|
79
|
+
evaluated_component_name=evaluated_component_name,
|
|
80
|
+
team_id=team_id,
|
|
81
|
+
workflow_id=workflow_id,
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
else:
|
|
85
|
+
db.create_eval_run(
|
|
86
|
+
EvalRunRecord(
|
|
87
|
+
run_id=run_id,
|
|
88
|
+
eval_type=eval_type,
|
|
89
|
+
eval_data=run_data,
|
|
90
|
+
eval_input=eval_input,
|
|
91
|
+
agent_id=agent_id,
|
|
92
|
+
model_id=model_id,
|
|
93
|
+
model_provider=model_provider,
|
|
94
|
+
name=name,
|
|
95
|
+
evaluated_component_name=evaluated_component_name,
|
|
96
|
+
team_id=team_id,
|
|
97
|
+
workflow_id=workflow_id,
|
|
98
|
+
)
|
|
81
99
|
)
|
|
82
|
-
)
|
|
83
100
|
except Exception as e:
|
|
84
101
|
log_debug(f"Could not create agent event: {e}")
|
|
85
102
|
|
agno/exceptions.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Any, Dict, List, Optional, Union
|
|
2
3
|
|
|
3
4
|
from agno.models.message import Message
|
|
4
5
|
|
|
@@ -17,6 +18,8 @@ class AgentRunException(Exception):
|
|
|
17
18
|
self.agent_message = agent_message
|
|
18
19
|
self.messages = messages
|
|
19
20
|
self.stop_execution = stop_execution
|
|
21
|
+
self.type = "agent_run_error"
|
|
22
|
+
self.error_id = "agent_run_error"
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
class RetryAgentRun(AgentRunException):
|
|
@@ -32,6 +35,7 @@ class RetryAgentRun(AgentRunException):
|
|
|
32
35
|
super().__init__(
|
|
33
36
|
exc, user_message=user_message, agent_message=agent_message, messages=messages, stop_execution=False
|
|
34
37
|
)
|
|
38
|
+
self.error_id = "retry_agent_run_error"
|
|
35
39
|
|
|
36
40
|
|
|
37
41
|
class StopAgentRun(AgentRunException):
|
|
@@ -47,6 +51,7 @@ class StopAgentRun(AgentRunException):
|
|
|
47
51
|
super().__init__(
|
|
48
52
|
exc, user_message=user_message, agent_message=agent_message, messages=messages, stop_execution=True
|
|
49
53
|
)
|
|
54
|
+
self.error_id = "stop_agent_run_error"
|
|
50
55
|
|
|
51
56
|
|
|
52
57
|
class RunCancelledException(Exception):
|
|
@@ -54,6 +59,8 @@ class RunCancelledException(Exception):
|
|
|
54
59
|
|
|
55
60
|
def __init__(self, message: str = "Operation cancelled by user"):
|
|
56
61
|
super().__init__(message)
|
|
62
|
+
self.type = "run_cancelled_error"
|
|
63
|
+
self.error_id = "run_cancelled_error"
|
|
57
64
|
|
|
58
65
|
|
|
59
66
|
class AgnoError(Exception):
|
|
@@ -63,6 +70,8 @@ class AgnoError(Exception):
|
|
|
63
70
|
super().__init__(message)
|
|
64
71
|
self.message = message
|
|
65
72
|
self.status_code = status_code
|
|
73
|
+
self.type = "agno_error"
|
|
74
|
+
self.error_id = "agno_error"
|
|
66
75
|
|
|
67
76
|
def __str__(self) -> str:
|
|
68
77
|
return str(self.message)
|
|
@@ -78,6 +87,9 @@ class ModelProviderError(AgnoError):
|
|
|
78
87
|
self.model_name = model_name
|
|
79
88
|
self.model_id = model_id
|
|
80
89
|
|
|
90
|
+
self.type = "model_provider_error"
|
|
91
|
+
self.error_id = "model_provider_error"
|
|
92
|
+
|
|
81
93
|
|
|
82
94
|
class ModelRateLimitError(ModelProviderError):
|
|
83
95
|
"""Exception raised when a model provider returns a rate limit error."""
|
|
@@ -86,9 +98,64 @@ class ModelRateLimitError(ModelProviderError):
|
|
|
86
98
|
self, message: str, status_code: int = 429, model_name: Optional[str] = None, model_id: Optional[str] = None
|
|
87
99
|
):
|
|
88
100
|
super().__init__(message, status_code, model_name, model_id)
|
|
101
|
+
self.error_id = "model_rate_limit_error"
|
|
89
102
|
|
|
90
103
|
|
|
91
104
|
class EvalError(Exception):
|
|
92
105
|
"""Exception raised when an evaluation fails."""
|
|
93
106
|
|
|
94
107
|
pass
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class CheckTrigger(Enum):
|
|
111
|
+
"""Enum for guardrail triggers."""
|
|
112
|
+
|
|
113
|
+
OFF_TOPIC = "off_topic"
|
|
114
|
+
INPUT_NOT_ALLOWED = "input_not_allowed"
|
|
115
|
+
OUTPUT_NOT_ALLOWED = "output_not_allowed"
|
|
116
|
+
VALIDATION_FAILED = "validation_failed"
|
|
117
|
+
|
|
118
|
+
PROMPT_INJECTION = "prompt_injection"
|
|
119
|
+
PII_DETECTED = "pii_detected"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class InputCheckError(Exception):
|
|
123
|
+
"""Exception raised when an input check fails."""
|
|
124
|
+
|
|
125
|
+
def __init__(
|
|
126
|
+
self,
|
|
127
|
+
message: str,
|
|
128
|
+
check_trigger: CheckTrigger = CheckTrigger.INPUT_NOT_ALLOWED,
|
|
129
|
+
additional_data: Optional[Dict[str, Any]] = None,
|
|
130
|
+
):
|
|
131
|
+
super().__init__(message)
|
|
132
|
+
self.type = "input_check_error"
|
|
133
|
+
if isinstance(check_trigger, CheckTrigger):
|
|
134
|
+
self.error_id = check_trigger.value
|
|
135
|
+
else:
|
|
136
|
+
self.error_id = str(check_trigger)
|
|
137
|
+
|
|
138
|
+
self.message = message
|
|
139
|
+
self.check_trigger = check_trigger
|
|
140
|
+
self.additional_data = additional_data
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class OutputCheckError(Exception):
|
|
144
|
+
"""Exception raised when an output check fails."""
|
|
145
|
+
|
|
146
|
+
def __init__(
|
|
147
|
+
self,
|
|
148
|
+
message: str,
|
|
149
|
+
check_trigger: CheckTrigger = CheckTrigger.OUTPUT_NOT_ALLOWED,
|
|
150
|
+
additional_data: Optional[Dict[str, Any]] = None,
|
|
151
|
+
):
|
|
152
|
+
super().__init__(message)
|
|
153
|
+
self.type = "output_check_error"
|
|
154
|
+
if isinstance(check_trigger, CheckTrigger):
|
|
155
|
+
self.error_id = check_trigger.value
|
|
156
|
+
else:
|
|
157
|
+
self.error_id = str(check_trigger)
|
|
158
|
+
|
|
159
|
+
self.message = message
|
|
160
|
+
self.check_trigger = check_trigger
|
|
161
|
+
self.additional_data = additional_data
|