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
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
|
|
5
|
+
from agno.models.base import Model
|
|
6
|
+
from agno.models.message import Message
|
|
7
|
+
from agno.utils.log import logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_anthropic_reasoning_model(reasoning_model: Model) -> bool:
|
|
11
|
+
"""Check if the model is an Anthropic Claude model with thinking support."""
|
|
12
|
+
is_claude = reasoning_model.__class__.__name__ == "Claude"
|
|
13
|
+
if not is_claude:
|
|
14
|
+
return False
|
|
15
|
+
|
|
16
|
+
# Check if provider is Anthropic (not VertexAI)
|
|
17
|
+
is_anthropic_provider = hasattr(reasoning_model, "provider") and reasoning_model.provider == "Anthropic"
|
|
18
|
+
|
|
19
|
+
# Check if thinking parameter is set
|
|
20
|
+
has_thinking = hasattr(reasoning_model, "thinking") and reasoning_model.thinking is not None
|
|
21
|
+
|
|
22
|
+
return is_claude and is_anthropic_provider and has_thinking
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_anthropic_reasoning(reasoning_agent: "Agent", messages: List[Message]) -> Optional[Message]: # type: ignore # noqa: F821
|
|
26
|
+
"""Get reasoning from an Anthropic Claude model."""
|
|
27
|
+
from agno.run.agent import RunOutput
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
reasoning_agent_response: RunOutput = reasoning_agent.run(input=messages)
|
|
31
|
+
except Exception as e:
|
|
32
|
+
logger.warning(f"Reasoning error: {e}")
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
reasoning_content: str = ""
|
|
36
|
+
redacted_reasoning_content: Optional[str] = None
|
|
37
|
+
|
|
38
|
+
if reasoning_agent_response.messages is not None:
|
|
39
|
+
for msg in reasoning_agent_response.messages:
|
|
40
|
+
if msg.reasoning_content is not None:
|
|
41
|
+
reasoning_content = msg.reasoning_content
|
|
42
|
+
if hasattr(msg, "redacted_reasoning_content") and msg.redacted_reasoning_content is not None:
|
|
43
|
+
redacted_reasoning_content = msg.redacted_reasoning_content
|
|
44
|
+
break
|
|
45
|
+
|
|
46
|
+
return Message(
|
|
47
|
+
role="assistant",
|
|
48
|
+
content=f"<thinking>\n{reasoning_content}\n</thinking>",
|
|
49
|
+
reasoning_content=reasoning_content,
|
|
50
|
+
redacted_reasoning_content=redacted_reasoning_content,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def aget_anthropic_reasoning(reasoning_agent: "Agent", messages: List[Message]) -> Optional[Message]: # type: ignore # noqa: F821
|
|
55
|
+
"""Get reasoning from an Anthropic Claude model asynchronously."""
|
|
56
|
+
from agno.run.agent import RunOutput
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
reasoning_agent_response: RunOutput = await reasoning_agent.arun(input=messages)
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.warning(f"Reasoning error: {e}")
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
reasoning_content: str = ""
|
|
65
|
+
redacted_reasoning_content: Optional[str] = None
|
|
66
|
+
|
|
67
|
+
if reasoning_agent_response.messages is not None:
|
|
68
|
+
for msg in reasoning_agent_response.messages:
|
|
69
|
+
if msg.reasoning_content is not None:
|
|
70
|
+
reasoning_content = msg.reasoning_content
|
|
71
|
+
if hasattr(msg, "redacted_reasoning_content") and msg.redacted_reasoning_content is not None:
|
|
72
|
+
redacted_reasoning_content = msg.redacted_reasoning_content
|
|
73
|
+
break
|
|
74
|
+
|
|
75
|
+
return Message(
|
|
76
|
+
role="assistant",
|
|
77
|
+
content=f"<thinking>\n{reasoning_content}\n</thinking>",
|
|
78
|
+
reasoning_content=reasoning_content,
|
|
79
|
+
redacted_reasoning_content=redacted_reasoning_content,
|
|
80
|
+
)
|
agno/reasoning/gemini.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
|
|
5
|
+
from agno.models.base import Model
|
|
6
|
+
from agno.models.message import Message
|
|
7
|
+
from agno.utils.log import logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_gemini_reasoning_model(reasoning_model: Model) -> bool:
|
|
11
|
+
"""Check if the model is a Gemini model with thinking support."""
|
|
12
|
+
is_gemini_class = reasoning_model.__class__.__name__ == "Gemini"
|
|
13
|
+
if not is_gemini_class:
|
|
14
|
+
return False
|
|
15
|
+
|
|
16
|
+
# Check if it's a Gemini 2.5+ model (supports thinking)
|
|
17
|
+
model_id = reasoning_model.id.lower()
|
|
18
|
+
has_thinking_support = "2.5" in model_id
|
|
19
|
+
|
|
20
|
+
# Also check if thinking parameters are set
|
|
21
|
+
# Note: thinking_budget=0 explicitly disables thinking mode per Google's API docs
|
|
22
|
+
has_thinking_budget = (
|
|
23
|
+
hasattr(reasoning_model, "thinking_budget")
|
|
24
|
+
and reasoning_model.thinking_budget is not None
|
|
25
|
+
and reasoning_model.thinking_budget > 0
|
|
26
|
+
)
|
|
27
|
+
has_include_thoughts = hasattr(reasoning_model, "include_thoughts") and reasoning_model.include_thoughts is not None
|
|
28
|
+
|
|
29
|
+
return is_gemini_class and (has_thinking_support or has_thinking_budget or has_include_thoughts)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_gemini_reasoning(reasoning_agent: "Agent", messages: List[Message]) -> Optional[Message]: # type: ignore # noqa: F821
|
|
33
|
+
"""Get reasoning from a Gemini model."""
|
|
34
|
+
from agno.run.agent import RunOutput
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
reasoning_agent_response: RunOutput = reasoning_agent.run(input=messages)
|
|
38
|
+
except Exception as e:
|
|
39
|
+
logger.warning(f"Reasoning error: {e}")
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
reasoning_content: str = ""
|
|
43
|
+
if reasoning_agent_response.messages is not None:
|
|
44
|
+
for msg in reasoning_agent_response.messages:
|
|
45
|
+
if msg.reasoning_content is not None:
|
|
46
|
+
reasoning_content = msg.reasoning_content
|
|
47
|
+
break
|
|
48
|
+
|
|
49
|
+
return Message(
|
|
50
|
+
role="assistant", content=f"<thinking>\n{reasoning_content}\n</thinking>", reasoning_content=reasoning_content
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def aget_gemini_reasoning(reasoning_agent: "Agent", messages: List[Message]) -> Optional[Message]: # type: ignore # noqa: F821
|
|
55
|
+
"""Get reasoning from a Gemini model asynchronously."""
|
|
56
|
+
from agno.run.agent import RunOutput
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
reasoning_agent_response: RunOutput = await reasoning_agent.arun(input=messages)
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.warning(f"Reasoning error: {e}")
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
reasoning_content: str = ""
|
|
65
|
+
if reasoning_agent_response.messages is not None:
|
|
66
|
+
for msg in reasoning_agent_response.messages:
|
|
67
|
+
if msg.reasoning_content is not None:
|
|
68
|
+
reasoning_content = msg.reasoning_content
|
|
69
|
+
break
|
|
70
|
+
|
|
71
|
+
return Message(
|
|
72
|
+
role="assistant", content=f"<thinking>\n{reasoning_content}\n</thinking>", reasoning_content=reasoning_content
|
|
73
|
+
)
|
agno/reasoning/openai.py
CHANGED
|
@@ -28,6 +28,11 @@ def is_openai_reasoning_model(reasoning_model: Model) -> bool:
|
|
|
28
28
|
def get_openai_reasoning(reasoning_agent: "Agent", messages: List[Message]) -> Optional[Message]: # type: ignore # noqa: F821
|
|
29
29
|
from agno.run.agent import RunOutput
|
|
30
30
|
|
|
31
|
+
# Update system message role to "system"
|
|
32
|
+
for message in messages:
|
|
33
|
+
if message.role == "developer":
|
|
34
|
+
message.role = "system"
|
|
35
|
+
|
|
31
36
|
try:
|
|
32
37
|
reasoning_agent_response: RunOutput = reasoning_agent.run(input=messages)
|
|
33
38
|
except Exception as e:
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
|
|
5
|
+
from agno.models.base import Model
|
|
6
|
+
from agno.models.message import Message
|
|
7
|
+
from agno.utils.log import logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_vertexai_reasoning_model(reasoning_model: Model) -> bool:
|
|
11
|
+
"""Check if the model is a VertexAI model with thinking support."""
|
|
12
|
+
# Check if provider is VertexAI
|
|
13
|
+
is_vertexai_provider = hasattr(reasoning_model, "provider") and reasoning_model.provider == "VertexAI"
|
|
14
|
+
|
|
15
|
+
# Check if thinking parameter is set
|
|
16
|
+
has_thinking = hasattr(reasoning_model, "thinking") and reasoning_model.thinking is not None
|
|
17
|
+
|
|
18
|
+
return is_vertexai_provider and has_thinking
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_vertexai_reasoning(reasoning_agent: "Agent", messages: List[Message]) -> Optional[Message]: # type: ignore # noqa: F821
|
|
22
|
+
"""Get reasoning from a VertexAI Claude model."""
|
|
23
|
+
from agno.run.agent import RunOutput
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
reasoning_agent_response: RunOutput = reasoning_agent.run(input=messages)
|
|
27
|
+
except Exception as e:
|
|
28
|
+
logger.warning(f"Reasoning error: {e}")
|
|
29
|
+
return None
|
|
30
|
+
|
|
31
|
+
reasoning_content: str = ""
|
|
32
|
+
redacted_reasoning_content: Optional[str] = None
|
|
33
|
+
|
|
34
|
+
if reasoning_agent_response.messages is not None:
|
|
35
|
+
for msg in reasoning_agent_response.messages:
|
|
36
|
+
if msg.reasoning_content is not None:
|
|
37
|
+
reasoning_content = msg.reasoning_content
|
|
38
|
+
if hasattr(msg, "redacted_reasoning_content") and msg.redacted_reasoning_content is not None:
|
|
39
|
+
redacted_reasoning_content = msg.redacted_reasoning_content
|
|
40
|
+
break
|
|
41
|
+
|
|
42
|
+
return Message(
|
|
43
|
+
role="assistant",
|
|
44
|
+
content=f"<thinking>\n{reasoning_content}\n</thinking>",
|
|
45
|
+
reasoning_content=reasoning_content,
|
|
46
|
+
redacted_reasoning_content=redacted_reasoning_content,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def aget_vertexai_reasoning(reasoning_agent: "Agent", messages: List[Message]) -> Optional[Message]: # type: ignore # noqa: F821
|
|
51
|
+
"""Get reasoning from a VertexAI Claude model asynchronously."""
|
|
52
|
+
from agno.run.agent import RunOutput
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
reasoning_agent_response: RunOutput = await reasoning_agent.arun(input=messages)
|
|
56
|
+
except Exception as e:
|
|
57
|
+
logger.warning(f"Reasoning error: {e}")
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
reasoning_content: str = ""
|
|
61
|
+
redacted_reasoning_content: Optional[str] = None
|
|
62
|
+
|
|
63
|
+
if reasoning_agent_response.messages is not None:
|
|
64
|
+
for msg in reasoning_agent_response.messages:
|
|
65
|
+
if msg.reasoning_content is not None:
|
|
66
|
+
reasoning_content = msg.reasoning_content
|
|
67
|
+
if hasattr(msg, "redacted_reasoning_content") and msg.redacted_reasoning_content is not None:
|
|
68
|
+
redacted_reasoning_content = msg.redacted_reasoning_content
|
|
69
|
+
break
|
|
70
|
+
|
|
71
|
+
return Message(
|
|
72
|
+
role="assistant",
|
|
73
|
+
content=f"<thinking>\n{reasoning_content}\n</thinking>",
|
|
74
|
+
reasoning_content=reasoning_content,
|
|
75
|
+
redacted_reasoning_content=redacted_reasoning_content,
|
|
76
|
+
)
|
agno/run/__init__.py
CHANGED
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
|
|
|
@@ -11,7 +11,18 @@ from agno.models.metrics import Metrics
|
|
|
11
11
|
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
|
+
from agno.run.requirement import RunRequirement
|
|
14
15
|
from agno.utils.log import logger
|
|
16
|
+
from agno.utils.media import (
|
|
17
|
+
reconstruct_audio_list,
|
|
18
|
+
reconstruct_files,
|
|
19
|
+
reconstruct_images,
|
|
20
|
+
reconstruct_response_audio,
|
|
21
|
+
reconstruct_videos,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from agno.session.summary import SessionSummary
|
|
15
26
|
|
|
16
27
|
|
|
17
28
|
@dataclass
|
|
@@ -60,12 +71,39 @@ class RunInput:
|
|
|
60
71
|
result["input_content"] = self.input_content.model_dump(exclude_none=True)
|
|
61
72
|
elif isinstance(self.input_content, Message):
|
|
62
73
|
result["input_content"] = self.input_content.to_dict()
|
|
74
|
+
|
|
75
|
+
# Handle input_content provided as a list of Message objects
|
|
63
76
|
elif (
|
|
64
77
|
isinstance(self.input_content, list)
|
|
65
78
|
and self.input_content
|
|
66
79
|
and isinstance(self.input_content[0], Message)
|
|
67
80
|
):
|
|
68
81
|
result["input_content"] = [m.to_dict() for m in self.input_content]
|
|
82
|
+
|
|
83
|
+
# Handle input_content provided as a list of dicts
|
|
84
|
+
elif (
|
|
85
|
+
isinstance(self.input_content, list) and self.input_content and isinstance(self.input_content[0], dict)
|
|
86
|
+
):
|
|
87
|
+
for content in self.input_content:
|
|
88
|
+
# Handle media input
|
|
89
|
+
if isinstance(content, dict):
|
|
90
|
+
if content.get("images"):
|
|
91
|
+
content["images"] = [
|
|
92
|
+
img.to_dict() if isinstance(img, Image) else img for img in content["images"]
|
|
93
|
+
]
|
|
94
|
+
if content.get("videos"):
|
|
95
|
+
content["videos"] = [
|
|
96
|
+
vid.to_dict() if isinstance(vid, Video) else vid for vid in content["videos"]
|
|
97
|
+
]
|
|
98
|
+
if content.get("audios"):
|
|
99
|
+
content["audios"] = [
|
|
100
|
+
aud.to_dict() if isinstance(aud, Audio) else aud for aud in content["audios"]
|
|
101
|
+
]
|
|
102
|
+
if content.get("files"):
|
|
103
|
+
content["files"] = [
|
|
104
|
+
file.to_dict() if isinstance(file, File) else file for file in content["files"]
|
|
105
|
+
]
|
|
106
|
+
result["input_content"] = self.input_content
|
|
69
107
|
else:
|
|
70
108
|
result["input_content"] = self.input_content
|
|
71
109
|
|
|
@@ -83,21 +121,10 @@ class RunInput:
|
|
|
83
121
|
@classmethod
|
|
84
122
|
def from_dict(cls, data: Dict[str, Any]) -> "RunInput":
|
|
85
123
|
"""Create RunInput from dictionary"""
|
|
86
|
-
images =
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
videos = None
|
|
91
|
-
if data.get("videos"):
|
|
92
|
-
videos = [Video.model_validate(vid_data) for vid_data in data["videos"]]
|
|
93
|
-
|
|
94
|
-
audios = None
|
|
95
|
-
if data.get("audios"):
|
|
96
|
-
audios = [Audio.model_validate(aud_data) for aud_data in data["audios"]]
|
|
97
|
-
|
|
98
|
-
files = None
|
|
99
|
-
if data.get("files"):
|
|
100
|
-
files = [File.model_validate(file_data) for file_data in data["files"]]
|
|
124
|
+
images = reconstruct_images(data.get("images"))
|
|
125
|
+
videos = reconstruct_videos(data.get("videos"))
|
|
126
|
+
audios = reconstruct_audio_list(data.get("audios"))
|
|
127
|
+
files = reconstruct_files(data.get("files"))
|
|
101
128
|
|
|
102
129
|
return cls(
|
|
103
130
|
input_content=data.get("input_content", ""), images=images, videos=videos, audios=audios, files=files
|
|
@@ -109,6 +136,7 @@ class RunEvent(str, Enum):
|
|
|
109
136
|
|
|
110
137
|
run_started = "RunStarted"
|
|
111
138
|
run_content = "RunContent"
|
|
139
|
+
run_content_completed = "RunContentCompleted"
|
|
112
140
|
run_intermediate_content = "RunIntermediateContent"
|
|
113
141
|
run_completed = "RunCompleted"
|
|
114
142
|
run_error = "RunError"
|
|
@@ -120,6 +148,9 @@ class RunEvent(str, Enum):
|
|
|
120
148
|
pre_hook_started = "PreHookStarted"
|
|
121
149
|
pre_hook_completed = "PreHookCompleted"
|
|
122
150
|
|
|
151
|
+
post_hook_started = "PostHookStarted"
|
|
152
|
+
post_hook_completed = "PostHookCompleted"
|
|
153
|
+
|
|
123
154
|
tool_call_started = "ToolCallStarted"
|
|
124
155
|
tool_call_completed = "ToolCallCompleted"
|
|
125
156
|
|
|
@@ -130,6 +161,9 @@ class RunEvent(str, Enum):
|
|
|
130
161
|
memory_update_started = "MemoryUpdateStarted"
|
|
131
162
|
memory_update_completed = "MemoryUpdateCompleted"
|
|
132
163
|
|
|
164
|
+
session_summary_started = "SessionSummaryStarted"
|
|
165
|
+
session_summary_completed = "SessionSummaryCompleted"
|
|
166
|
+
|
|
133
167
|
parser_model_response_started = "ParserModelResponseStarted"
|
|
134
168
|
parser_model_response_completed = "ParserModelResponseCompleted"
|
|
135
169
|
|
|
@@ -188,6 +222,9 @@ class RunContentEvent(BaseAgentRunEvent):
|
|
|
188
222
|
|
|
189
223
|
event: str = RunEvent.run_content.value
|
|
190
224
|
content: Optional[Any] = None
|
|
225
|
+
workflow_agent: bool = (
|
|
226
|
+
False # Used by consumers of the events to distinguish between workflow agent and regular agent
|
|
227
|
+
)
|
|
191
228
|
content_type: str = "str"
|
|
192
229
|
reasoning_content: Optional[str] = None
|
|
193
230
|
model_provider_data: Optional[Dict[str, Any]] = None
|
|
@@ -200,6 +237,11 @@ class RunContentEvent(BaseAgentRunEvent):
|
|
|
200
237
|
reasoning_messages: Optional[List[Message]] = None
|
|
201
238
|
|
|
202
239
|
|
|
240
|
+
@dataclass
|
|
241
|
+
class RunContentCompletedEvent(BaseAgentRunEvent):
|
|
242
|
+
event: str = RunEvent.run_content_completed.value
|
|
243
|
+
|
|
244
|
+
|
|
203
245
|
@dataclass
|
|
204
246
|
class IntermediateRunContentEvent(BaseAgentRunEvent):
|
|
205
247
|
event: str = RunEvent.run_intermediate_content.value
|
|
@@ -225,17 +267,25 @@ class RunCompletedEvent(BaseAgentRunEvent):
|
|
|
225
267
|
reasoning_messages: Optional[List[Message]] = None
|
|
226
268
|
metadata: Optional[Dict[str, Any]] = None
|
|
227
269
|
metrics: Optional[Metrics] = None
|
|
270
|
+
session_state: Optional[Dict[str, Any]] = None
|
|
228
271
|
|
|
229
272
|
|
|
230
273
|
@dataclass
|
|
231
274
|
class RunPausedEvent(BaseAgentRunEvent):
|
|
232
275
|
event: str = RunEvent.run_paused.value
|
|
233
276
|
tools: Optional[List[ToolExecution]] = None
|
|
277
|
+
requirements: Optional[List[RunRequirement]] = None
|
|
234
278
|
|
|
235
279
|
@property
|
|
236
280
|
def is_paused(self):
|
|
237
281
|
return True
|
|
238
282
|
|
|
283
|
+
@property
|
|
284
|
+
def active_requirements(self) -> List[RunRequirement]:
|
|
285
|
+
if not self.requirements:
|
|
286
|
+
return []
|
|
287
|
+
return [requirement for requirement in self.requirements if not requirement.is_resolved()]
|
|
288
|
+
|
|
239
289
|
|
|
240
290
|
@dataclass
|
|
241
291
|
class RunContinuedEvent(BaseAgentRunEvent):
|
|
@@ -277,6 +327,18 @@ class PreHookCompletedEvent(BaseAgentRunEvent):
|
|
|
277
327
|
run_input: Optional[RunInput] = None
|
|
278
328
|
|
|
279
329
|
|
|
330
|
+
@dataclass
|
|
331
|
+
class PostHookStartedEvent(BaseAgentRunEvent):
|
|
332
|
+
event: str = RunEvent.post_hook_started.value
|
|
333
|
+
post_hook_name: Optional[str] = None
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
@dataclass
|
|
337
|
+
class PostHookCompletedEvent(BaseAgentRunEvent):
|
|
338
|
+
event: str = RunEvent.post_hook_completed.value
|
|
339
|
+
post_hook_name: Optional[str] = None
|
|
340
|
+
|
|
341
|
+
|
|
280
342
|
@dataclass
|
|
281
343
|
class MemoryUpdateStartedEvent(BaseAgentRunEvent):
|
|
282
344
|
event: str = RunEvent.memory_update_started.value
|
|
@@ -287,6 +349,17 @@ class MemoryUpdateCompletedEvent(BaseAgentRunEvent):
|
|
|
287
349
|
event: str = RunEvent.memory_update_completed.value
|
|
288
350
|
|
|
289
351
|
|
|
352
|
+
@dataclass
|
|
353
|
+
class SessionSummaryStartedEvent(BaseAgentRunEvent):
|
|
354
|
+
event: str = RunEvent.session_summary_started.value
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
@dataclass
|
|
358
|
+
class SessionSummaryCompletedEvent(BaseAgentRunEvent):
|
|
359
|
+
event: str = RunEvent.session_summary_completed.value
|
|
360
|
+
session_summary: Optional["SessionSummary"] = None
|
|
361
|
+
|
|
362
|
+
|
|
290
363
|
@dataclass
|
|
291
364
|
class ReasoningStartedEvent(BaseAgentRunEvent):
|
|
292
365
|
event: str = RunEvent.reasoning_started.value
|
|
@@ -347,11 +420,17 @@ class OutputModelResponseCompletedEvent(BaseAgentRunEvent):
|
|
|
347
420
|
class CustomEvent(BaseAgentRunEvent):
|
|
348
421
|
event: str = RunEvent.custom_event.value
|
|
349
422
|
|
|
423
|
+
def __init__(self, **kwargs):
|
|
424
|
+
# Store arbitrary attributes directly on the instance
|
|
425
|
+
for key, value in kwargs.items():
|
|
426
|
+
setattr(self, key, value)
|
|
427
|
+
|
|
350
428
|
|
|
351
429
|
RunOutputEvent = Union[
|
|
352
430
|
RunStartedEvent,
|
|
353
431
|
RunContentEvent,
|
|
354
432
|
IntermediateRunContentEvent,
|
|
433
|
+
RunContentCompletedEvent,
|
|
355
434
|
RunCompletedEvent,
|
|
356
435
|
RunErrorEvent,
|
|
357
436
|
RunCancelledEvent,
|
|
@@ -359,11 +438,15 @@ RunOutputEvent = Union[
|
|
|
359
438
|
RunContinuedEvent,
|
|
360
439
|
PreHookStartedEvent,
|
|
361
440
|
PreHookCompletedEvent,
|
|
441
|
+
PostHookStartedEvent,
|
|
442
|
+
PostHookCompletedEvent,
|
|
362
443
|
ReasoningStartedEvent,
|
|
363
444
|
ReasoningStepEvent,
|
|
364
445
|
ReasoningCompletedEvent,
|
|
365
446
|
MemoryUpdateStartedEvent,
|
|
366
447
|
MemoryUpdateCompletedEvent,
|
|
448
|
+
SessionSummaryStartedEvent,
|
|
449
|
+
SessionSummaryCompletedEvent,
|
|
367
450
|
ToolCallStartedEvent,
|
|
368
451
|
ToolCallCompletedEvent,
|
|
369
452
|
ParserModelResponseStartedEvent,
|
|
@@ -378,6 +461,7 @@ RunOutputEvent = Union[
|
|
|
378
461
|
RUN_EVENT_TYPE_REGISTRY = {
|
|
379
462
|
RunEvent.run_started.value: RunStartedEvent,
|
|
380
463
|
RunEvent.run_content.value: RunContentEvent,
|
|
464
|
+
RunEvent.run_content_completed.value: RunContentCompletedEvent,
|
|
381
465
|
RunEvent.run_intermediate_content.value: IntermediateRunContentEvent,
|
|
382
466
|
RunEvent.run_completed.value: RunCompletedEvent,
|
|
383
467
|
RunEvent.run_error.value: RunErrorEvent,
|
|
@@ -386,11 +470,15 @@ RUN_EVENT_TYPE_REGISTRY = {
|
|
|
386
470
|
RunEvent.run_continued.value: RunContinuedEvent,
|
|
387
471
|
RunEvent.pre_hook_started.value: PreHookStartedEvent,
|
|
388
472
|
RunEvent.pre_hook_completed.value: PreHookCompletedEvent,
|
|
473
|
+
RunEvent.post_hook_started.value: PostHookStartedEvent,
|
|
474
|
+
RunEvent.post_hook_completed.value: PostHookCompletedEvent,
|
|
389
475
|
RunEvent.reasoning_started.value: ReasoningStartedEvent,
|
|
390
476
|
RunEvent.reasoning_step.value: ReasoningStepEvent,
|
|
391
477
|
RunEvent.reasoning_completed.value: ReasoningCompletedEvent,
|
|
392
478
|
RunEvent.memory_update_started.value: MemoryUpdateStartedEvent,
|
|
393
479
|
RunEvent.memory_update_completed.value: MemoryUpdateCompletedEvent,
|
|
480
|
+
RunEvent.session_summary_started.value: SessionSummaryStartedEvent,
|
|
481
|
+
RunEvent.session_summary_completed.value: SessionSummaryCompletedEvent,
|
|
394
482
|
RunEvent.tool_call_started.value: ToolCallStartedEvent,
|
|
395
483
|
RunEvent.tool_call_completed.value: ToolCallCompletedEvent,
|
|
396
484
|
RunEvent.parser_model_response_started.value: ParserModelResponseStartedEvent,
|
|
@@ -451,6 +539,7 @@ class RunOutput:
|
|
|
451
539
|
references: Optional[List[MessageReferences]] = None
|
|
452
540
|
|
|
453
541
|
metadata: Optional[Dict[str, Any]] = None
|
|
542
|
+
session_state: Optional[Dict[str, Any]] = None
|
|
454
543
|
|
|
455
544
|
created_at: int = field(default_factory=lambda: int(time()))
|
|
456
545
|
|
|
@@ -458,11 +547,20 @@ class RunOutput:
|
|
|
458
547
|
|
|
459
548
|
status: RunStatus = RunStatus.running
|
|
460
549
|
|
|
550
|
+
# User control flow (HITL) requirements to continue a run when paused, in order of arrival
|
|
551
|
+
requirements: Optional[list[RunRequirement]] = None
|
|
552
|
+
|
|
461
553
|
# === FOREIGN KEY RELATIONSHIPS ===
|
|
462
554
|
# These fields establish relationships to parent workflow/step structures
|
|
463
555
|
# and should be treated as foreign keys for data integrity
|
|
464
556
|
workflow_step_id: Optional[str] = None # FK: Points to StepOutput.step_id
|
|
465
557
|
|
|
558
|
+
@property
|
|
559
|
+
def active_requirements(self) -> list[RunRequirement]:
|
|
560
|
+
if not self.requirements:
|
|
561
|
+
return []
|
|
562
|
+
return [requirement for requirement in self.requirements if not requirement.is_resolved()]
|
|
563
|
+
|
|
466
564
|
@property
|
|
467
565
|
def is_paused(self):
|
|
468
566
|
return self.status == RunStatus.paused
|
|
@@ -491,6 +589,7 @@ class RunOutput:
|
|
|
491
589
|
and k
|
|
492
590
|
not in [
|
|
493
591
|
"messages",
|
|
592
|
+
"metrics",
|
|
494
593
|
"tools",
|
|
495
594
|
"metadata",
|
|
496
595
|
"images",
|
|
@@ -505,6 +604,7 @@ class RunOutput:
|
|
|
505
604
|
"reasoning_steps",
|
|
506
605
|
"reasoning_messages",
|
|
507
606
|
"references",
|
|
607
|
+
"requirements",
|
|
508
608
|
]
|
|
509
609
|
}
|
|
510
610
|
|
|
@@ -590,6 +690,9 @@ class RunOutput:
|
|
|
590
690
|
else:
|
|
591
691
|
_dict["tools"].append(tool)
|
|
592
692
|
|
|
693
|
+
if self.requirements is not None:
|
|
694
|
+
_dict["requirements"] = [req.to_dict() if hasattr(req, "to_dict") else req for req in self.requirements]
|
|
695
|
+
|
|
593
696
|
if self.input is not None:
|
|
594
697
|
_dict["input"] = self.input.to_dict()
|
|
595
698
|
|
|
@@ -615,7 +718,17 @@ class RunOutput:
|
|
|
615
718
|
data = data.pop("run")
|
|
616
719
|
|
|
617
720
|
events = data.pop("events", None)
|
|
618
|
-
|
|
721
|
+
final_events = []
|
|
722
|
+
for event in events or []:
|
|
723
|
+
if "agent_id" in event:
|
|
724
|
+
event = run_output_event_from_dict(event)
|
|
725
|
+
else:
|
|
726
|
+
# Use the factory from response.py for agent events
|
|
727
|
+
from agno.run.team import team_run_output_event_from_dict
|
|
728
|
+
|
|
729
|
+
event = team_run_output_event_from_dict(event)
|
|
730
|
+
final_events.append(event)
|
|
731
|
+
events = final_events
|
|
619
732
|
|
|
620
733
|
messages = data.pop("messages", None)
|
|
621
734
|
messages = [Message.from_dict(message) for message in messages] if messages else None
|
|
@@ -626,20 +739,23 @@ class RunOutput:
|
|
|
626
739
|
tools = data.pop("tools", [])
|
|
627
740
|
tools = [ToolExecution.from_dict(tool) for tool in tools] if tools else None
|
|
628
741
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
742
|
+
# Handle requirements
|
|
743
|
+
requirements_data = data.pop("requirements", None)
|
|
744
|
+
requirements: Optional[List[RunRequirement]] = None
|
|
745
|
+
if requirements_data is not None:
|
|
746
|
+
requirements_list: List[RunRequirement] = []
|
|
747
|
+
for item in requirements_data:
|
|
748
|
+
if isinstance(item, RunRequirement):
|
|
749
|
+
requirements_list.append(item)
|
|
750
|
+
elif isinstance(item, dict):
|
|
751
|
+
requirements_list.append(RunRequirement.from_dict(item))
|
|
752
|
+
requirements = requirements_list if requirements_list else None
|
|
753
|
+
|
|
754
|
+
images = reconstruct_images(data.pop("images", []))
|
|
755
|
+
videos = reconstruct_videos(data.pop("videos", []))
|
|
756
|
+
audio = reconstruct_audio_list(data.pop("audio", []))
|
|
757
|
+
files = reconstruct_files(data.pop("files", []))
|
|
758
|
+
response_audio = reconstruct_response_audio(data.pop("response_audio", None))
|
|
643
759
|
|
|
644
760
|
input_data = data.pop("input", None)
|
|
645
761
|
input_obj = None
|
|
@@ -667,6 +783,12 @@ class RunOutput:
|
|
|
667
783
|
if references is not None:
|
|
668
784
|
references = [MessageReferences.model_validate(reference) for reference in references]
|
|
669
785
|
|
|
786
|
+
# Filter data to only include fields that are actually defined in the RunOutput dataclass
|
|
787
|
+
from dataclasses import fields
|
|
788
|
+
|
|
789
|
+
supported_fields = {f.name for f in fields(cls)}
|
|
790
|
+
filtered_data = {k: v for k, v in data.items() if k in supported_fields}
|
|
791
|
+
|
|
670
792
|
return cls(
|
|
671
793
|
messages=messages,
|
|
672
794
|
metrics=metrics,
|
|
@@ -683,7 +805,8 @@ class RunOutput:
|
|
|
683
805
|
reasoning_steps=reasoning_steps,
|
|
684
806
|
reasoning_messages=reasoning_messages,
|
|
685
807
|
references=references,
|
|
686
|
-
|
|
808
|
+
requirements=requirements,
|
|
809
|
+
**filtered_data,
|
|
687
810
|
)
|
|
688
811
|
|
|
689
812
|
def get_content_as_string(self, **kwargs) -> str:
|