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/run/agent.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from dataclasses import asdict, dataclass, field
|
|
2
2
|
from enum import Enum
|
|
3
3
|
from time import time
|
|
4
|
-
from typing import Any, Dict, List, Optional, Sequence, Union
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Union
|
|
5
5
|
|
|
6
6
|
from pydantic import BaseModel
|
|
7
7
|
|
|
@@ -12,6 +12,122 @@ from agno.models.response import ToolExecution
|
|
|
12
12
|
from agno.reasoning.step import ReasoningStep
|
|
13
13
|
from agno.run.base import BaseRunOutputEvent, MessageReferences, RunStatus
|
|
14
14
|
from agno.utils.log import logger
|
|
15
|
+
from agno.utils.media import (
|
|
16
|
+
reconstruct_audio_list,
|
|
17
|
+
reconstruct_files,
|
|
18
|
+
reconstruct_images,
|
|
19
|
+
reconstruct_response_audio,
|
|
20
|
+
reconstruct_videos,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from agno.session.summary import SessionSummary
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class RunInput:
|
|
29
|
+
"""Container for the raw input data passed to Agent.run().
|
|
30
|
+
|
|
31
|
+
This captures the original input exactly as provided by the user,
|
|
32
|
+
separate from the processed messages that go to the model.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
input_content: The literal input message/content passed to run()
|
|
36
|
+
images: Images directly passed to run()
|
|
37
|
+
videos: Videos directly passed to run()
|
|
38
|
+
audios: Audio files directly passed to run()
|
|
39
|
+
files: Files directly passed to run()
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
input_content: Union[str, List, Dict, Message, BaseModel, List[Message]]
|
|
43
|
+
images: Optional[Sequence[Image]] = None
|
|
44
|
+
videos: Optional[Sequence[Video]] = None
|
|
45
|
+
audios: Optional[Sequence[Audio]] = None
|
|
46
|
+
files: Optional[Sequence[File]] = None
|
|
47
|
+
|
|
48
|
+
def input_content_string(self) -> str:
|
|
49
|
+
import json
|
|
50
|
+
|
|
51
|
+
if isinstance(self.input_content, (str)):
|
|
52
|
+
return self.input_content
|
|
53
|
+
elif isinstance(self.input_content, BaseModel):
|
|
54
|
+
return self.input_content.model_dump_json(exclude_none=True)
|
|
55
|
+
elif isinstance(self.input_content, Message):
|
|
56
|
+
return json.dumps(self.input_content.to_dict())
|
|
57
|
+
elif isinstance(self.input_content, list) and self.input_content and isinstance(self.input_content[0], Message):
|
|
58
|
+
return json.dumps([m.to_dict() for m in self.input_content])
|
|
59
|
+
else:
|
|
60
|
+
return str(self.input_content)
|
|
61
|
+
|
|
62
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
63
|
+
"""Convert to dictionary representation"""
|
|
64
|
+
result: Dict[str, Any] = {}
|
|
65
|
+
|
|
66
|
+
if self.input_content is not None:
|
|
67
|
+
if isinstance(self.input_content, (str)):
|
|
68
|
+
result["input_content"] = self.input_content
|
|
69
|
+
elif isinstance(self.input_content, BaseModel):
|
|
70
|
+
result["input_content"] = self.input_content.model_dump(exclude_none=True)
|
|
71
|
+
elif isinstance(self.input_content, Message):
|
|
72
|
+
result["input_content"] = self.input_content.to_dict()
|
|
73
|
+
|
|
74
|
+
# Handle input_content provided as a list of Message objects
|
|
75
|
+
elif (
|
|
76
|
+
isinstance(self.input_content, list)
|
|
77
|
+
and self.input_content
|
|
78
|
+
and isinstance(self.input_content[0], Message)
|
|
79
|
+
):
|
|
80
|
+
result["input_content"] = [m.to_dict() for m in self.input_content]
|
|
81
|
+
|
|
82
|
+
# Handle input_content provided as a list of dicts
|
|
83
|
+
elif (
|
|
84
|
+
isinstance(self.input_content, list) and self.input_content and isinstance(self.input_content[0], dict)
|
|
85
|
+
):
|
|
86
|
+
for content in self.input_content:
|
|
87
|
+
# Handle media input
|
|
88
|
+
if isinstance(content, dict):
|
|
89
|
+
if content.get("images"):
|
|
90
|
+
content["images"] = [
|
|
91
|
+
img.to_dict() if isinstance(img, Image) else img for img in content["images"]
|
|
92
|
+
]
|
|
93
|
+
if content.get("videos"):
|
|
94
|
+
content["videos"] = [
|
|
95
|
+
vid.to_dict() if isinstance(vid, Video) else vid for vid in content["videos"]
|
|
96
|
+
]
|
|
97
|
+
if content.get("audios"):
|
|
98
|
+
content["audios"] = [
|
|
99
|
+
aud.to_dict() if isinstance(aud, Audio) else aud for aud in content["audios"]
|
|
100
|
+
]
|
|
101
|
+
if content.get("files"):
|
|
102
|
+
content["files"] = [
|
|
103
|
+
file.to_dict() if isinstance(file, File) else file for file in content["files"]
|
|
104
|
+
]
|
|
105
|
+
result["input_content"] = self.input_content
|
|
106
|
+
else:
|
|
107
|
+
result["input_content"] = self.input_content
|
|
108
|
+
|
|
109
|
+
if self.images:
|
|
110
|
+
result["images"] = [img.to_dict() for img in self.images]
|
|
111
|
+
if self.videos:
|
|
112
|
+
result["videos"] = [vid.to_dict() for vid in self.videos]
|
|
113
|
+
if self.audios:
|
|
114
|
+
result["audios"] = [aud.to_dict() for aud in self.audios]
|
|
115
|
+
if self.files:
|
|
116
|
+
result["files"] = [file.to_dict() for file in self.files]
|
|
117
|
+
|
|
118
|
+
return result
|
|
119
|
+
|
|
120
|
+
@classmethod
|
|
121
|
+
def from_dict(cls, data: Dict[str, Any]) -> "RunInput":
|
|
122
|
+
"""Create RunInput from dictionary"""
|
|
123
|
+
images = reconstruct_images(data.get("images"))
|
|
124
|
+
videos = reconstruct_videos(data.get("videos"))
|
|
125
|
+
audios = reconstruct_audio_list(data.get("audios"))
|
|
126
|
+
files = reconstruct_files(data.get("files"))
|
|
127
|
+
|
|
128
|
+
return cls(
|
|
129
|
+
input_content=data.get("input_content", ""), images=images, videos=videos, audios=audios, files=files
|
|
130
|
+
)
|
|
15
131
|
|
|
16
132
|
|
|
17
133
|
class RunEvent(str, Enum):
|
|
@@ -19,6 +135,7 @@ class RunEvent(str, Enum):
|
|
|
19
135
|
|
|
20
136
|
run_started = "RunStarted"
|
|
21
137
|
run_content = "RunContent"
|
|
138
|
+
run_content_completed = "RunContentCompleted"
|
|
22
139
|
run_intermediate_content = "RunIntermediateContent"
|
|
23
140
|
run_completed = "RunCompleted"
|
|
24
141
|
run_error = "RunError"
|
|
@@ -27,6 +144,12 @@ class RunEvent(str, Enum):
|
|
|
27
144
|
run_paused = "RunPaused"
|
|
28
145
|
run_continued = "RunContinued"
|
|
29
146
|
|
|
147
|
+
pre_hook_started = "PreHookStarted"
|
|
148
|
+
pre_hook_completed = "PreHookCompleted"
|
|
149
|
+
|
|
150
|
+
post_hook_started = "PostHookStarted"
|
|
151
|
+
post_hook_completed = "PostHookCompleted"
|
|
152
|
+
|
|
30
153
|
tool_call_started = "ToolCallStarted"
|
|
31
154
|
tool_call_completed = "ToolCallCompleted"
|
|
32
155
|
|
|
@@ -37,6 +160,9 @@ class RunEvent(str, Enum):
|
|
|
37
160
|
memory_update_started = "MemoryUpdateStarted"
|
|
38
161
|
memory_update_completed = "MemoryUpdateCompleted"
|
|
39
162
|
|
|
163
|
+
session_summary_started = "SessionSummaryStarted"
|
|
164
|
+
session_summary_completed = "SessionSummaryCompleted"
|
|
165
|
+
|
|
40
166
|
parser_model_response_started = "ParserModelResponseStarted"
|
|
41
167
|
parser_model_response_completed = "ParserModelResponseCompleted"
|
|
42
168
|
|
|
@@ -53,6 +179,7 @@ class BaseAgentRunEvent(BaseRunOutputEvent):
|
|
|
53
179
|
agent_id: str = ""
|
|
54
180
|
agent_name: str = ""
|
|
55
181
|
run_id: Optional[str] = None
|
|
182
|
+
parent_run_id: Optional[str] = None
|
|
56
183
|
session_id: Optional[str] = None
|
|
57
184
|
|
|
58
185
|
# Step context for workflow execution
|
|
@@ -94,8 +221,12 @@ class RunContentEvent(BaseAgentRunEvent):
|
|
|
94
221
|
|
|
95
222
|
event: str = RunEvent.run_content.value
|
|
96
223
|
content: Optional[Any] = None
|
|
224
|
+
workflow_agent: bool = (
|
|
225
|
+
False # Used by consumers of the events to distinguish between workflow agent and regular agent
|
|
226
|
+
)
|
|
97
227
|
content_type: str = "str"
|
|
98
228
|
reasoning_content: Optional[str] = None
|
|
229
|
+
model_provider_data: Optional[Dict[str, Any]] = None
|
|
99
230
|
citations: Optional[Citations] = None
|
|
100
231
|
response_audio: Optional[Audio] = None # Model audio response
|
|
101
232
|
image: Optional[Image] = None # Image attached to the response
|
|
@@ -105,6 +236,11 @@ class RunContentEvent(BaseAgentRunEvent):
|
|
|
105
236
|
reasoning_messages: Optional[List[Message]] = None
|
|
106
237
|
|
|
107
238
|
|
|
239
|
+
@dataclass
|
|
240
|
+
class RunContentCompletedEvent(BaseAgentRunEvent):
|
|
241
|
+
event: str = RunEvent.run_content_completed.value
|
|
242
|
+
|
|
243
|
+
|
|
108
244
|
@dataclass
|
|
109
245
|
class IntermediateRunContentEvent(BaseAgentRunEvent):
|
|
110
246
|
event: str = RunEvent.run_intermediate_content.value
|
|
@@ -119,6 +255,7 @@ class RunCompletedEvent(BaseAgentRunEvent):
|
|
|
119
255
|
content_type: str = "str"
|
|
120
256
|
reasoning_content: Optional[str] = None
|
|
121
257
|
citations: Optional[Citations] = None
|
|
258
|
+
model_provider_data: Optional[Dict[str, Any]] = None
|
|
122
259
|
images: Optional[List[Image]] = None # Images attached to the response
|
|
123
260
|
videos: Optional[List[Video]] = None # Videos attached to the response
|
|
124
261
|
audio: Optional[List[Audio]] = None # Audio attached to the response
|
|
@@ -129,6 +266,7 @@ class RunCompletedEvent(BaseAgentRunEvent):
|
|
|
129
266
|
reasoning_messages: Optional[List[Message]] = None
|
|
130
267
|
metadata: Optional[Dict[str, Any]] = None
|
|
131
268
|
metrics: Optional[Metrics] = None
|
|
269
|
+
session_state: Optional[Dict[str, Any]] = None
|
|
132
270
|
|
|
133
271
|
|
|
134
272
|
@dataclass
|
|
@@ -151,6 +289,11 @@ class RunErrorEvent(BaseAgentRunEvent):
|
|
|
151
289
|
event: str = RunEvent.run_error.value
|
|
152
290
|
content: Optional[str] = None
|
|
153
291
|
|
|
292
|
+
# From exceptions
|
|
293
|
+
error_type: Optional[str] = None
|
|
294
|
+
error_id: Optional[str] = None
|
|
295
|
+
additional_data: Optional[Dict[str, Any]] = None
|
|
296
|
+
|
|
154
297
|
|
|
155
298
|
@dataclass
|
|
156
299
|
class RunCancelledEvent(BaseAgentRunEvent):
|
|
@@ -162,6 +305,32 @@ class RunCancelledEvent(BaseAgentRunEvent):
|
|
|
162
305
|
return True
|
|
163
306
|
|
|
164
307
|
|
|
308
|
+
@dataclass
|
|
309
|
+
class PreHookStartedEvent(BaseAgentRunEvent):
|
|
310
|
+
event: str = RunEvent.pre_hook_started.value
|
|
311
|
+
pre_hook_name: Optional[str] = None
|
|
312
|
+
run_input: Optional[RunInput] = None
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
@dataclass
|
|
316
|
+
class PreHookCompletedEvent(BaseAgentRunEvent):
|
|
317
|
+
event: str = RunEvent.pre_hook_completed.value
|
|
318
|
+
pre_hook_name: Optional[str] = None
|
|
319
|
+
run_input: Optional[RunInput] = None
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
@dataclass
|
|
323
|
+
class PostHookStartedEvent(BaseAgentRunEvent):
|
|
324
|
+
event: str = RunEvent.post_hook_started.value
|
|
325
|
+
post_hook_name: Optional[str] = None
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
@dataclass
|
|
329
|
+
class PostHookCompletedEvent(BaseAgentRunEvent):
|
|
330
|
+
event: str = RunEvent.post_hook_completed.value
|
|
331
|
+
post_hook_name: Optional[str] = None
|
|
332
|
+
|
|
333
|
+
|
|
165
334
|
@dataclass
|
|
166
335
|
class MemoryUpdateStartedEvent(BaseAgentRunEvent):
|
|
167
336
|
event: str = RunEvent.memory_update_started.value
|
|
@@ -172,6 +341,17 @@ class MemoryUpdateCompletedEvent(BaseAgentRunEvent):
|
|
|
172
341
|
event: str = RunEvent.memory_update_completed.value
|
|
173
342
|
|
|
174
343
|
|
|
344
|
+
@dataclass
|
|
345
|
+
class SessionSummaryStartedEvent(BaseAgentRunEvent):
|
|
346
|
+
event: str = RunEvent.session_summary_started.value
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
@dataclass
|
|
350
|
+
class SessionSummaryCompletedEvent(BaseAgentRunEvent):
|
|
351
|
+
event: str = RunEvent.session_summary_completed.value
|
|
352
|
+
session_summary: Optional["SessionSummary"] = None
|
|
353
|
+
|
|
354
|
+
|
|
175
355
|
@dataclass
|
|
176
356
|
class ReasoningStartedEvent(BaseAgentRunEvent):
|
|
177
357
|
event: str = RunEvent.reasoning_started.value
|
|
@@ -232,21 +412,33 @@ class OutputModelResponseCompletedEvent(BaseAgentRunEvent):
|
|
|
232
412
|
class CustomEvent(BaseAgentRunEvent):
|
|
233
413
|
event: str = RunEvent.custom_event.value
|
|
234
414
|
|
|
415
|
+
def __init__(self, **kwargs):
|
|
416
|
+
# Store arbitrary attributes directly on the instance
|
|
417
|
+
for key, value in kwargs.items():
|
|
418
|
+
setattr(self, key, value)
|
|
419
|
+
|
|
235
420
|
|
|
236
421
|
RunOutputEvent = Union[
|
|
237
422
|
RunStartedEvent,
|
|
238
423
|
RunContentEvent,
|
|
239
424
|
IntermediateRunContentEvent,
|
|
425
|
+
RunContentCompletedEvent,
|
|
240
426
|
RunCompletedEvent,
|
|
241
427
|
RunErrorEvent,
|
|
242
428
|
RunCancelledEvent,
|
|
243
429
|
RunPausedEvent,
|
|
244
430
|
RunContinuedEvent,
|
|
431
|
+
PreHookStartedEvent,
|
|
432
|
+
PreHookCompletedEvent,
|
|
433
|
+
PostHookStartedEvent,
|
|
434
|
+
PostHookCompletedEvent,
|
|
245
435
|
ReasoningStartedEvent,
|
|
246
436
|
ReasoningStepEvent,
|
|
247
437
|
ReasoningCompletedEvent,
|
|
248
438
|
MemoryUpdateStartedEvent,
|
|
249
439
|
MemoryUpdateCompletedEvent,
|
|
440
|
+
SessionSummaryStartedEvent,
|
|
441
|
+
SessionSummaryCompletedEvent,
|
|
250
442
|
ToolCallStartedEvent,
|
|
251
443
|
ToolCallCompletedEvent,
|
|
252
444
|
ParserModelResponseStartedEvent,
|
|
@@ -261,17 +453,24 @@ RunOutputEvent = Union[
|
|
|
261
453
|
RUN_EVENT_TYPE_REGISTRY = {
|
|
262
454
|
RunEvent.run_started.value: RunStartedEvent,
|
|
263
455
|
RunEvent.run_content.value: RunContentEvent,
|
|
456
|
+
RunEvent.run_content_completed.value: RunContentCompletedEvent,
|
|
264
457
|
RunEvent.run_intermediate_content.value: IntermediateRunContentEvent,
|
|
265
458
|
RunEvent.run_completed.value: RunCompletedEvent,
|
|
266
459
|
RunEvent.run_error.value: RunErrorEvent,
|
|
267
460
|
RunEvent.run_cancelled.value: RunCancelledEvent,
|
|
268
461
|
RunEvent.run_paused.value: RunPausedEvent,
|
|
269
462
|
RunEvent.run_continued.value: RunContinuedEvent,
|
|
463
|
+
RunEvent.pre_hook_started.value: PreHookStartedEvent,
|
|
464
|
+
RunEvent.pre_hook_completed.value: PreHookCompletedEvent,
|
|
465
|
+
RunEvent.post_hook_started.value: PostHookStartedEvent,
|
|
466
|
+
RunEvent.post_hook_completed.value: PostHookCompletedEvent,
|
|
270
467
|
RunEvent.reasoning_started.value: ReasoningStartedEvent,
|
|
271
468
|
RunEvent.reasoning_step.value: ReasoningStepEvent,
|
|
272
469
|
RunEvent.reasoning_completed.value: ReasoningCompletedEvent,
|
|
273
470
|
RunEvent.memory_update_started.value: MemoryUpdateStartedEvent,
|
|
274
471
|
RunEvent.memory_update_completed.value: MemoryUpdateCompletedEvent,
|
|
472
|
+
RunEvent.session_summary_started.value: SessionSummaryStartedEvent,
|
|
473
|
+
RunEvent.session_summary_completed.value: SessionSummaryCompletedEvent,
|
|
275
474
|
RunEvent.tool_call_started.value: ToolCallStartedEvent,
|
|
276
475
|
RunEvent.tool_call_completed.value: ToolCallCompletedEvent,
|
|
277
476
|
RunEvent.parser_model_response_started.value: ParserModelResponseStartedEvent,
|
|
@@ -290,78 +489,6 @@ def run_output_event_from_dict(data: dict) -> BaseRunOutputEvent:
|
|
|
290
489
|
return cls.from_dict(data) # type: ignore
|
|
291
490
|
|
|
292
491
|
|
|
293
|
-
@dataclass
|
|
294
|
-
class RunInput:
|
|
295
|
-
"""Container for the raw input data passed to Agent.run().
|
|
296
|
-
|
|
297
|
-
This captures the original input exactly as provided by the user,
|
|
298
|
-
separate from the processed messages that go to the model.
|
|
299
|
-
|
|
300
|
-
Attributes:
|
|
301
|
-
input_content: The literal input message/content passed to run()
|
|
302
|
-
images: Images directly passed to run()
|
|
303
|
-
videos: Videos directly passed to run()
|
|
304
|
-
audios: Audio files directly passed to run()
|
|
305
|
-
files: Files directly passed to run()
|
|
306
|
-
"""
|
|
307
|
-
|
|
308
|
-
input_content: Optional[Union[str, List, Dict, Message, BaseModel, List[Message]]] = None
|
|
309
|
-
images: Optional[Sequence[Image]] = None
|
|
310
|
-
videos: Optional[Sequence[Video]] = None
|
|
311
|
-
audios: Optional[Sequence[Audio]] = None
|
|
312
|
-
files: Optional[Sequence[File]] = None
|
|
313
|
-
|
|
314
|
-
def to_dict(self) -> Dict[str, Any]:
|
|
315
|
-
"""Convert to dictionary representation"""
|
|
316
|
-
result: Dict[str, Any] = {}
|
|
317
|
-
|
|
318
|
-
if self.input_content is not None:
|
|
319
|
-
if isinstance(self.input_content, (str)):
|
|
320
|
-
result["input_content"] = self.input_content
|
|
321
|
-
elif isinstance(self.input_content, BaseModel):
|
|
322
|
-
result["input_content"] = self.input_content.model_dump(exclude_none=True)
|
|
323
|
-
elif isinstance(self.input_content, Message):
|
|
324
|
-
result["input_content"] = self.input_content.to_dict()
|
|
325
|
-
elif (
|
|
326
|
-
isinstance(self.input_content, list)
|
|
327
|
-
and self.input_content
|
|
328
|
-
and isinstance(self.input_content[0], Message)
|
|
329
|
-
):
|
|
330
|
-
result["input_content"] = [m.to_dict() for m in self.input_content]
|
|
331
|
-
else:
|
|
332
|
-
result["input_content"] = self.input_content
|
|
333
|
-
|
|
334
|
-
if self.images:
|
|
335
|
-
result["images"] = [img.to_dict() for img in self.images]
|
|
336
|
-
if self.videos:
|
|
337
|
-
result["videos"] = [vid.to_dict() for vid in self.videos]
|
|
338
|
-
if self.audios:
|
|
339
|
-
result["audios"] = [aud.to_dict() for aud in self.audios]
|
|
340
|
-
|
|
341
|
-
return result
|
|
342
|
-
|
|
343
|
-
@classmethod
|
|
344
|
-
def from_dict(cls, data: Dict[str, Any]) -> "RunInput":
|
|
345
|
-
"""Create RunInput from dictionary"""
|
|
346
|
-
images = None
|
|
347
|
-
if data.get("images"):
|
|
348
|
-
images = [Image.model_validate(img_data) for img_data in data["images"]]
|
|
349
|
-
|
|
350
|
-
videos = None
|
|
351
|
-
if data.get("videos"):
|
|
352
|
-
videos = [Video.model_validate(vid_data) for vid_data in data["videos"]]
|
|
353
|
-
|
|
354
|
-
audios = None
|
|
355
|
-
if data.get("audios"):
|
|
356
|
-
audios = [Audio.model_validate(aud_data) for aud_data in data["audios"]]
|
|
357
|
-
|
|
358
|
-
files = None
|
|
359
|
-
if data.get("files"):
|
|
360
|
-
files = [File.model_validate(file_data) for file_data in data["files"]]
|
|
361
|
-
|
|
362
|
-
return cls(input_content=data.get("input_content"), images=images, videos=videos, audios=audios, files=files)
|
|
363
|
-
|
|
364
|
-
|
|
365
492
|
@dataclass
|
|
366
493
|
class RunOutput:
|
|
367
494
|
"""Response returned by Agent.run() or Workflow.run() functions"""
|
|
@@ -374,6 +501,9 @@ class RunOutput:
|
|
|
374
501
|
workflow_id: Optional[str] = None
|
|
375
502
|
user_id: Optional[str] = None
|
|
376
503
|
|
|
504
|
+
# Input media and messages from user
|
|
505
|
+
input: Optional[RunInput] = None
|
|
506
|
+
|
|
377
507
|
content: Optional[Any] = None
|
|
378
508
|
content_type: str = "str"
|
|
379
509
|
|
|
@@ -381,6 +511,8 @@ class RunOutput:
|
|
|
381
511
|
reasoning_steps: Optional[List[ReasoningStep]] = None
|
|
382
512
|
reasoning_messages: Optional[List[Message]] = None
|
|
383
513
|
|
|
514
|
+
model_provider_data: Optional[Dict[str, Any]] = None
|
|
515
|
+
|
|
384
516
|
model: Optional[str] = None
|
|
385
517
|
model_provider: Optional[str] = None
|
|
386
518
|
messages: Optional[List[Message]] = None
|
|
@@ -392,15 +524,14 @@ class RunOutput:
|
|
|
392
524
|
images: Optional[List[Image]] = None # Images attached to the response
|
|
393
525
|
videos: Optional[List[Video]] = None # Videos attached to the response
|
|
394
526
|
audio: Optional[List[Audio]] = None # Audio attached to the response
|
|
527
|
+
files: Optional[List[File]] = None # Files attached to the response
|
|
395
528
|
response_audio: Optional[Audio] = None # Model audio response
|
|
396
529
|
|
|
397
|
-
# Input media and messages from user
|
|
398
|
-
input: Optional[RunInput] = None
|
|
399
|
-
|
|
400
530
|
citations: Optional[Citations] = None
|
|
401
531
|
references: Optional[List[MessageReferences]] = None
|
|
402
532
|
|
|
403
533
|
metadata: Optional[Dict[str, Any]] = None
|
|
534
|
+
session_state: Optional[Dict[str, Any]] = None
|
|
404
535
|
|
|
405
536
|
created_at: int = field(default_factory=lambda: int(time()))
|
|
406
537
|
|
|
@@ -446,6 +577,7 @@ class RunOutput:
|
|
|
446
577
|
"images",
|
|
447
578
|
"videos",
|
|
448
579
|
"audio",
|
|
580
|
+
"files",
|
|
449
581
|
"response_audio",
|
|
450
582
|
"input",
|
|
451
583
|
"citations",
|
|
@@ -508,6 +640,14 @@ class RunOutput:
|
|
|
508
640
|
else:
|
|
509
641
|
_dict["audio"].append(aud)
|
|
510
642
|
|
|
643
|
+
if self.files is not None:
|
|
644
|
+
_dict["files"] = []
|
|
645
|
+
for file in self.files:
|
|
646
|
+
if isinstance(file, File):
|
|
647
|
+
_dict["files"].append(file.to_dict())
|
|
648
|
+
else:
|
|
649
|
+
_dict["files"].append(file)
|
|
650
|
+
|
|
511
651
|
if self.response_audio is not None:
|
|
512
652
|
if isinstance(self.response_audio, Audio):
|
|
513
653
|
_dict["response_audio"] = self.response_audio.to_dict()
|
|
@@ -536,7 +676,7 @@ class RunOutput:
|
|
|
536
676
|
|
|
537
677
|
return _dict
|
|
538
678
|
|
|
539
|
-
def to_json(self) -> str:
|
|
679
|
+
def to_json(self, separators=(", ", ": "), indent: Optional[int] = 2) -> str:
|
|
540
680
|
import json
|
|
541
681
|
|
|
542
682
|
try:
|
|
@@ -545,7 +685,10 @@ class RunOutput:
|
|
|
545
685
|
logger.error("Failed to convert response to json", exc_info=True)
|
|
546
686
|
raise
|
|
547
687
|
|
|
548
|
-
|
|
688
|
+
if indent is None:
|
|
689
|
+
return json.dumps(_dict, separators=separators)
|
|
690
|
+
else:
|
|
691
|
+
return json.dumps(_dict, indent=indent, separators=separators)
|
|
549
692
|
|
|
550
693
|
@classmethod
|
|
551
694
|
def from_dict(cls, data: Dict[str, Any]) -> "RunOutput":
|
|
@@ -553,10 +696,20 @@ class RunOutput:
|
|
|
553
696
|
data = data.pop("run")
|
|
554
697
|
|
|
555
698
|
events = data.pop("events", None)
|
|
556
|
-
|
|
699
|
+
final_events = []
|
|
700
|
+
for event in events or []:
|
|
701
|
+
if "agent_id" in event:
|
|
702
|
+
event = run_output_event_from_dict(event)
|
|
703
|
+
else:
|
|
704
|
+
# Use the factory from response.py for agent events
|
|
705
|
+
from agno.run.team import team_run_output_event_from_dict
|
|
706
|
+
|
|
707
|
+
event = team_run_output_event_from_dict(event)
|
|
708
|
+
final_events.append(event)
|
|
709
|
+
events = final_events
|
|
557
710
|
|
|
558
711
|
messages = data.pop("messages", None)
|
|
559
|
-
messages = [Message.
|
|
712
|
+
messages = [Message.from_dict(message) for message in messages] if messages else None
|
|
560
713
|
|
|
561
714
|
citations = data.pop("citations", None)
|
|
562
715
|
citations = Citations.model_validate(citations) if citations else None
|
|
@@ -564,17 +717,11 @@ class RunOutput:
|
|
|
564
717
|
tools = data.pop("tools", [])
|
|
565
718
|
tools = [ToolExecution.from_dict(tool) for tool in tools] if tools else None
|
|
566
719
|
|
|
567
|
-
images = data.pop("images", [])
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
audio = data.pop("audio", [])
|
|
574
|
-
audio = [Audio.model_validate(audio) for audio in audio] if audio else None
|
|
575
|
-
|
|
576
|
-
response_audio = data.pop("response_audio", None)
|
|
577
|
-
response_audio = Audio.model_validate(response_audio) if response_audio else None
|
|
720
|
+
images = reconstruct_images(data.pop("images", []))
|
|
721
|
+
videos = reconstruct_videos(data.pop("videos", []))
|
|
722
|
+
audio = reconstruct_audio_list(data.pop("audio", []))
|
|
723
|
+
files = reconstruct_files(data.pop("files", []))
|
|
724
|
+
response_audio = reconstruct_response_audio(data.pop("response_audio", None))
|
|
578
725
|
|
|
579
726
|
input_data = data.pop("input", None)
|
|
580
727
|
input_obj = None
|
|
@@ -588,7 +735,7 @@ class RunOutput:
|
|
|
588
735
|
additional_input = data.pop("additional_input", None)
|
|
589
736
|
|
|
590
737
|
if additional_input is not None:
|
|
591
|
-
additional_input = [Message.
|
|
738
|
+
additional_input = [Message.from_dict(message) for message in additional_input]
|
|
592
739
|
|
|
593
740
|
reasoning_steps = data.pop("reasoning_steps", None)
|
|
594
741
|
if reasoning_steps is not None:
|
|
@@ -596,12 +743,18 @@ class RunOutput:
|
|
|
596
743
|
|
|
597
744
|
reasoning_messages = data.pop("reasoning_messages", None)
|
|
598
745
|
if reasoning_messages is not None:
|
|
599
|
-
reasoning_messages = [Message.
|
|
746
|
+
reasoning_messages = [Message.from_dict(message) for message in reasoning_messages]
|
|
600
747
|
|
|
601
748
|
references = data.pop("references", None)
|
|
602
749
|
if references is not None:
|
|
603
750
|
references = [MessageReferences.model_validate(reference) for reference in references]
|
|
604
751
|
|
|
752
|
+
# Filter data to only include fields that are actually defined in the RunOutput dataclass
|
|
753
|
+
from dataclasses import fields
|
|
754
|
+
|
|
755
|
+
supported_fields = {f.name for f in fields(cls)}
|
|
756
|
+
filtered_data = {k: v for k, v in data.items() if k in supported_fields}
|
|
757
|
+
|
|
605
758
|
return cls(
|
|
606
759
|
messages=messages,
|
|
607
760
|
metrics=metrics,
|
|
@@ -610,6 +763,7 @@ class RunOutput:
|
|
|
610
763
|
images=images,
|
|
611
764
|
audio=audio,
|
|
612
765
|
videos=videos,
|
|
766
|
+
files=files,
|
|
613
767
|
response_audio=response_audio,
|
|
614
768
|
input=input_obj,
|
|
615
769
|
events=events,
|
|
@@ -617,7 +771,7 @@ class RunOutput:
|
|
|
617
771
|
reasoning_steps=reasoning_steps,
|
|
618
772
|
reasoning_messages=reasoning_messages,
|
|
619
773
|
references=references,
|
|
620
|
-
**
|
|
774
|
+
**filtered_data,
|
|
621
775
|
)
|
|
622
776
|
|
|
623
777
|
def get_content_as_string(self, **kwargs) -> str:
|
agno/run/base.py
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
from dataclasses import asdict, dataclass
|
|
2
2
|
from enum import Enum
|
|
3
|
-
from typing import Any, Dict
|
|
3
|
+
from typing import Any, Dict, List, Optional, Union
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
|
|
7
|
+
from agno.filters import FilterExpr
|
|
7
8
|
from agno.media import Audio, Image, Video
|
|
8
9
|
from agno.models.message import Citations, Message, MessageReferences
|
|
9
10
|
from agno.models.metrics import Metrics
|
|
10
|
-
from agno.models.response import ToolExecution
|
|
11
11
|
from agno.reasoning.step import ReasoningStep
|
|
12
12
|
from agno.utils.log import log_error
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
@dataclass
|
|
16
|
+
class RunContext:
|
|
17
|
+
run_id: str
|
|
18
|
+
session_id: str
|
|
19
|
+
user_id: Optional[str] = None
|
|
20
|
+
|
|
21
|
+
dependencies: Optional[Dict[str, Any]] = None
|
|
22
|
+
knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
23
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
24
|
+
session_state: Optional[Dict[str, Any]] = None
|
|
25
|
+
|
|
26
|
+
|
|
15
27
|
@dataclass
|
|
16
28
|
class BaseRunOutputEvent:
|
|
17
29
|
def to_dict(self) -> Dict[str, Any]:
|
|
@@ -35,6 +47,7 @@ class BaseRunOutputEvent:
|
|
|
35
47
|
"reasoning_steps",
|
|
36
48
|
"references",
|
|
37
49
|
"additional_input",
|
|
50
|
+
"session_summary",
|
|
38
51
|
"metrics",
|
|
39
52
|
]
|
|
40
53
|
}
|
|
@@ -97,6 +110,8 @@ class BaseRunOutputEvent:
|
|
|
97
110
|
_dict["content"] = self.content.model_dump(exclude_none=True)
|
|
98
111
|
|
|
99
112
|
if hasattr(self, "tools") and self.tools is not None:
|
|
113
|
+
from agno.models.response import ToolExecution
|
|
114
|
+
|
|
100
115
|
_dict["tools"] = []
|
|
101
116
|
for tool in self.tools:
|
|
102
117
|
if isinstance(tool, ToolExecution):
|
|
@@ -105,6 +120,8 @@ class BaseRunOutputEvent:
|
|
|
105
120
|
_dict["tools"].append(tool)
|
|
106
121
|
|
|
107
122
|
if hasattr(self, "tool") and self.tool is not None:
|
|
123
|
+
from agno.models.response import ToolExecution
|
|
124
|
+
|
|
108
125
|
if isinstance(self.tool, ToolExecution):
|
|
109
126
|
_dict["tool"] = self.tool.to_dict()
|
|
110
127
|
else:
|
|
@@ -113,23 +130,33 @@ class BaseRunOutputEvent:
|
|
|
113
130
|
if hasattr(self, "metrics") and self.metrics is not None:
|
|
114
131
|
_dict["metrics"] = self.metrics.to_dict()
|
|
115
132
|
|
|
133
|
+
if hasattr(self, "session_summary") and self.session_summary is not None:
|
|
134
|
+
_dict["session_summary"] = self.session_summary.to_dict()
|
|
135
|
+
|
|
116
136
|
return _dict
|
|
117
137
|
|
|
118
|
-
def to_json(self) -> str:
|
|
138
|
+
def to_json(self, separators=(", ", ": "), indent: Optional[int] = 2) -> str:
|
|
119
139
|
import json
|
|
120
140
|
|
|
141
|
+
from agno.utils.serialize import json_serializer
|
|
142
|
+
|
|
121
143
|
try:
|
|
122
144
|
_dict = self.to_dict()
|
|
123
145
|
except Exception:
|
|
124
146
|
log_error("Failed to convert response event to json", exc_info=True)
|
|
125
147
|
raise
|
|
126
148
|
|
|
127
|
-
|
|
149
|
+
if indent is None:
|
|
150
|
+
return json.dumps(_dict, separators=separators, default=json_serializer, ensure_ascii=False)
|
|
151
|
+
else:
|
|
152
|
+
return json.dumps(_dict, indent=indent, separators=separators, default=json_serializer, ensure_ascii=False)
|
|
128
153
|
|
|
129
154
|
@classmethod
|
|
130
155
|
def from_dict(cls, data: Dict[str, Any]):
|
|
131
156
|
tool = data.pop("tool", None)
|
|
132
157
|
if tool:
|
|
158
|
+
from agno.models.response import ToolExecution
|
|
159
|
+
|
|
133
160
|
data["tool"] = ToolExecution.from_dict(tool)
|
|
134
161
|
|
|
135
162
|
images = data.pop("images", None)
|
|
@@ -168,7 +195,19 @@ class BaseRunOutputEvent:
|
|
|
168
195
|
if metrics:
|
|
169
196
|
data["metrics"] = Metrics(**metrics)
|
|
170
197
|
|
|
171
|
-
|
|
198
|
+
session_summary = data.pop("session_summary", None)
|
|
199
|
+
if session_summary:
|
|
200
|
+
from agno.session.summary import SessionSummary
|
|
201
|
+
|
|
202
|
+
data["session_summary"] = SessionSummary.from_dict(session_summary)
|
|
203
|
+
|
|
204
|
+
# Filter data to only include fields that are actually defined in the target class
|
|
205
|
+
from dataclasses import fields
|
|
206
|
+
|
|
207
|
+
supported_fields = {f.name for f in fields(cls)}
|
|
208
|
+
filtered_data = {k: v for k, v in data.items() if k in supported_fields}
|
|
209
|
+
|
|
210
|
+
return cls(**filtered_data)
|
|
172
211
|
|
|
173
212
|
@property
|
|
174
213
|
def is_paused(self):
|