agno 2.0.0rc2__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 +6009 -2874
- 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 +595 -187
- 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 +3 -0
- agno/knowledge/types.py +9 -0
- agno/knowledge/utils.py +20 -0
- agno/media.py +339 -266
- 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 +1011 -566
- 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 +110 -37
- 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 +143 -4
- 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 +60 -6
- agno/models/openai/chat.py +102 -43
- 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 +81 -5
- 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 -175
- 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 +266 -112
- agno/run/base.py +53 -24
- agno/run/team.py +252 -111
- agno/run/workflow.py +156 -45
- 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 -1692
- agno/tools/brightdata.py +3 -3
- agno/tools/cartesia.py +3 -5
- agno/tools/dalle.py +9 -8
- agno/tools/decorator.py +4 -2
- agno/tools/desi_vocal.py +2 -2
- agno/tools/duckduckgo.py +15 -11
- agno/tools/e2b.py +20 -13
- agno/tools/eleven_labs.py +26 -28
- agno/tools/exa.py +21 -16
- agno/tools/fal.py +4 -4
- agno/tools/file.py +153 -23
- agno/tools/file_generation.py +350 -0
- agno/tools/firecrawl.py +4 -4
- agno/tools/function.py +257 -37
- agno/tools/giphy.py +2 -2
- 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/lumalab.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/azure_openai.py +2 -2
- agno/tools/models/gemini.py +3 -3
- agno/tools/models/groq.py +3 -5
- agno/tools/models/nebius.py +7 -7
- agno/tools/models_labs.py +25 -15
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +4 -9
- agno/tools/opencv.py +3 -3
- agno/tools/parallel.py +314 -0
- agno/tools/replicate.py +7 -7
- 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 +222 -7
- agno/utils/gemini.py +181 -23
- 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 +95 -5
- 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/models/cohere.py +1 -1
- agno/utils/models/watsonx.py +1 -1
- agno/utils/openai.py +1 -1
- 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 +183 -135
- 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 +645 -136
- agno/workflow/steps.py +65 -6
- agno/workflow/types.py +71 -33
- agno/workflow/workflow.py +2113 -300
- agno-2.3.0.dist-info/METADATA +618 -0
- agno-2.3.0.dist-info/RECORD +577 -0
- agno-2.3.0.dist-info/licenses/LICENSE +201 -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.0rc2.dist-info/METADATA +0 -355
- agno-2.0.0rc2.dist-info/RECORD +0 -515
- agno-2.0.0rc2.dist-info/licenses/LICENSE +0 -375
- {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/WHEEL +0 -0
- {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/top_level.txt +0 -0
agno/session/summary.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
from textwrap import dedent
|
|
4
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, Union
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, Union
|
|
5
5
|
|
|
6
6
|
from pydantic import BaseModel, Field
|
|
7
7
|
|
|
8
8
|
from agno.models.base import Model
|
|
9
|
+
from agno.models.utils import get_model
|
|
9
10
|
from agno.run.agent import Message
|
|
10
11
|
from agno.utils.log import log_debug, log_warning
|
|
11
12
|
|
|
@@ -66,6 +67,9 @@ class SessionSummaryManager:
|
|
|
66
67
|
# Prompt used for session summary generation
|
|
67
68
|
session_summary_prompt: Optional[str] = None
|
|
68
69
|
|
|
70
|
+
# User message prompt for requesting the summary
|
|
71
|
+
summary_request_message: str = "Provide the summary of the conversation."
|
|
72
|
+
|
|
69
73
|
# Whether session summaries were created in the last run
|
|
70
74
|
summaries_updated: bool = False
|
|
71
75
|
|
|
@@ -90,20 +94,35 @@ class SessionSummaryManager:
|
|
|
90
94
|
response_format: Union[Dict[str, Any], Type[BaseModel]],
|
|
91
95
|
) -> Message:
|
|
92
96
|
if self.session_summary_prompt is not None:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
<conversation>
|
|
102
|
-
""")
|
|
97
|
+
system_prompt = self.session_summary_prompt
|
|
98
|
+
else:
|
|
99
|
+
system_prompt = dedent("""\
|
|
100
|
+
Analyze the following conversation between a user and an assistant, and extract the following details:
|
|
101
|
+
- Summary (str): Provide a concise summary of the session, focusing on important information that would be helpful for future interactions.
|
|
102
|
+
- Topics (Optional[List[str]]): List the topics discussed in the session.
|
|
103
|
+
Keep the summary concise and to the point. Only include relevant information.
|
|
104
|
+
""")
|
|
103
105
|
conversation_messages = []
|
|
106
|
+
system_prompt += "<conversation>"
|
|
104
107
|
for message in conversation:
|
|
105
108
|
if message.role == "user":
|
|
106
|
-
|
|
109
|
+
# Handle empty user messages with media - note what media was provided
|
|
110
|
+
if not message.content or (isinstance(message.content, str) and message.content.strip() == ""):
|
|
111
|
+
media_types = []
|
|
112
|
+
if hasattr(message, "images") and message.images:
|
|
113
|
+
media_types.append(f"{len(message.images)} image(s)")
|
|
114
|
+
if hasattr(message, "videos") and message.videos:
|
|
115
|
+
media_types.append(f"{len(message.videos)} video(s)")
|
|
116
|
+
if hasattr(message, "audio") and message.audio:
|
|
117
|
+
media_types.append(f"{len(message.audio)} audio file(s)")
|
|
118
|
+
if hasattr(message, "files") and message.files:
|
|
119
|
+
media_types.append(f"{len(message.files)} file(s)")
|
|
120
|
+
|
|
121
|
+
if media_types:
|
|
122
|
+
conversation_messages.append(f"User: [Provided {', '.join(media_types)}]")
|
|
123
|
+
# Skip empty messages with no media
|
|
124
|
+
else:
|
|
125
|
+
conversation_messages.append(f"User: {message.content}")
|
|
107
126
|
elif message.role in ["assistant", "model"]:
|
|
108
127
|
conversation_messages.append(f"Assistant: {message.content}\n")
|
|
109
128
|
system_prompt += "\n".join(conversation_messages)
|
|
@@ -119,23 +138,30 @@ class SessionSummaryManager:
|
|
|
119
138
|
def _prepare_summary_messages(
|
|
120
139
|
self,
|
|
121
140
|
session: Optional["Session"] = None,
|
|
122
|
-
) -> List[Message]:
|
|
123
|
-
"""Prepare messages for session summary generation"""
|
|
124
|
-
|
|
141
|
+
) -> Optional[List[Message]]:
|
|
142
|
+
"""Prepare messages for session summary generation. Returns None if no meaningful messages to summarize."""
|
|
143
|
+
if not session:
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
self.model = get_model(self.model)
|
|
147
|
+
if self.model is None:
|
|
148
|
+
return None
|
|
149
|
+
|
|
125
150
|
response_format = self.get_response_format(self.model)
|
|
126
151
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
conversation=session.get_messages_for_session(), # type: ignore
|
|
131
|
-
response_format=response_format,
|
|
132
|
-
),
|
|
133
|
-
Message(role="user", content="Provide the summary of the conversation."),
|
|
134
|
-
]
|
|
135
|
-
if session
|
|
136
|
-
else []
|
|
152
|
+
system_message = self.get_system_message(
|
|
153
|
+
conversation=session.get_messages(), # type: ignore
|
|
154
|
+
response_format=response_format,
|
|
137
155
|
)
|
|
138
156
|
|
|
157
|
+
if system_message is None:
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
return [
|
|
161
|
+
system_message,
|
|
162
|
+
Message(role="user", content=self.summary_request_message),
|
|
163
|
+
]
|
|
164
|
+
|
|
139
165
|
def _process_summary_response(self, summary_response, session_summary_model: "Model") -> Optional[SessionSummary]: # type: ignore
|
|
140
166
|
"""Process the model response into a SessionSummary"""
|
|
141
167
|
from datetime import datetime
|
|
@@ -188,10 +214,17 @@ class SessionSummaryManager:
|
|
|
188
214
|
) -> Optional[SessionSummary]:
|
|
189
215
|
"""Creates a summary of the session"""
|
|
190
216
|
log_debug("Creating session summary", center=True)
|
|
217
|
+
self.model = get_model(self.model)
|
|
191
218
|
if self.model is None:
|
|
192
219
|
return None
|
|
193
220
|
|
|
194
221
|
messages = self._prepare_summary_messages(session)
|
|
222
|
+
|
|
223
|
+
# Skip summary generation if there are no meaningful messages
|
|
224
|
+
if messages is None:
|
|
225
|
+
log_debug("No meaningful messages to summarize, skipping session summary")
|
|
226
|
+
return None
|
|
227
|
+
|
|
195
228
|
response_format = self.get_response_format(self.model)
|
|
196
229
|
|
|
197
230
|
summary_response = self.model.response(messages=messages, response_format=response_format)
|
|
@@ -209,10 +242,17 @@ class SessionSummaryManager:
|
|
|
209
242
|
) -> Optional[SessionSummary]:
|
|
210
243
|
"""Creates a summary of the session"""
|
|
211
244
|
log_debug("Creating session summary", center=True)
|
|
245
|
+
self.model = get_model(self.model)
|
|
212
246
|
if self.model is None:
|
|
213
247
|
return None
|
|
214
248
|
|
|
215
249
|
messages = self._prepare_summary_messages(session)
|
|
250
|
+
|
|
251
|
+
# Skip summary generation if there are no meaningful messages
|
|
252
|
+
if messages is None:
|
|
253
|
+
log_debug("No meaningful messages to summarize, skipping session summary")
|
|
254
|
+
return None
|
|
255
|
+
|
|
216
256
|
response_format = self.get_response_format(self.model)
|
|
217
257
|
|
|
218
258
|
summary_response = await self.model.aresponse(messages=messages, response_format=response_format)
|
agno/session/team.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import asdict, dataclass
|
|
4
|
-
from typing import Any, Dict, List, Mapping, Optional, Union
|
|
4
|
+
from typing import Any, Dict, List, Mapping, Optional, Tuple, Union
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel
|
|
5
7
|
|
|
6
8
|
from agno.models.message import Message
|
|
7
9
|
from agno.run.agent import RunOutput, RunStatus
|
|
@@ -54,16 +56,18 @@ class TeamSession:
|
|
|
54
56
|
log_warning("TeamSession is missing session_id")
|
|
55
57
|
return None
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
summary = data.get("summary")
|
|
60
|
+
if summary is not None and isinstance(summary, dict):
|
|
58
61
|
data["summary"] = SessionSummary.from_dict(data["summary"]) # type: ignore
|
|
59
62
|
|
|
60
|
-
runs = data.get("runs"
|
|
63
|
+
runs = data.get("runs")
|
|
61
64
|
serialized_runs: List[Union[TeamRunOutput, RunOutput]] = []
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
if runs is not None and isinstance(runs[0], dict):
|
|
66
|
+
for run in runs:
|
|
67
|
+
if "agent_id" in run:
|
|
68
|
+
serialized_runs.append(RunOutput.from_dict(run))
|
|
69
|
+
elif "team_id" in run:
|
|
70
|
+
serialized_runs.append(TeamRunOutput.from_dict(run))
|
|
67
71
|
|
|
68
72
|
return cls(
|
|
69
73
|
session_id=data.get("session_id"), # type: ignore
|
|
@@ -92,6 +96,7 @@ class TeamSession:
|
|
|
92
96
|
if messages is None:
|
|
93
97
|
return
|
|
94
98
|
|
|
99
|
+
# Make message duration None
|
|
95
100
|
for m in messages or []:
|
|
96
101
|
if m.metrics is not None:
|
|
97
102
|
m.metrics.duration = None
|
|
@@ -108,74 +113,136 @@ class TeamSession:
|
|
|
108
113
|
|
|
109
114
|
log_debug("Added RunOutput to Team Session")
|
|
110
115
|
|
|
111
|
-
def
|
|
116
|
+
def get_messages(
|
|
112
117
|
self,
|
|
113
|
-
agent_id: Optional[str] = None,
|
|
114
118
|
team_id: Optional[str] = None,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
member_ids: Optional[List[str]] = None,
|
|
120
|
+
last_n_runs: Optional[int] = None,
|
|
121
|
+
limit: Optional[int] = None,
|
|
122
|
+
skip_roles: Optional[List[str]] = None,
|
|
123
|
+
skip_statuses: Optional[List[RunStatus]] = None,
|
|
118
124
|
skip_history_messages: bool = True,
|
|
119
|
-
|
|
125
|
+
skip_member_messages: bool = True,
|
|
120
126
|
) -> List[Message]:
|
|
121
|
-
"""Returns the messages
|
|
122
|
-
Args:
|
|
127
|
+
"""Returns the messages belonging to the session that fit the given criteria.
|
|
123
128
|
|
|
124
|
-
|
|
129
|
+
Args:
|
|
125
130
|
team_id: The id of the team to get the messages from.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
131
|
+
member_ids: The ids of the members to get the messages from.
|
|
132
|
+
last_n_runs: The number of runs to return messages from, counting from the latest. Defaults to all runs.
|
|
133
|
+
limit: The number of messages to return, counting from the latest. Defaults to all messages.
|
|
134
|
+
skip_roles: Skip messages with these roles.
|
|
135
|
+
skip_statuses: Skip messages with these statuses.
|
|
129
136
|
skip_history_messages: Skip messages that were tagged as history in previous runs.
|
|
137
|
+
skip_member_messages: Skip messages created by members of the team.
|
|
138
|
+
|
|
130
139
|
Returns:
|
|
131
|
-
A list of Messages
|
|
140
|
+
A list of Messages belonging to the session.
|
|
132
141
|
"""
|
|
142
|
+
|
|
143
|
+
def _should_skip_message(
|
|
144
|
+
message: Message, skip_roles: Optional[List[str]] = None, skip_history_messages: bool = True
|
|
145
|
+
) -> bool:
|
|
146
|
+
"""Processes a message for history"""
|
|
147
|
+
# Skip messages that were tagged as history in previous runs
|
|
148
|
+
if hasattr(message, "from_history") and message.from_history and skip_history_messages:
|
|
149
|
+
return True
|
|
150
|
+
|
|
151
|
+
# Skip messages with specified role
|
|
152
|
+
if skip_roles and message.role in skip_roles:
|
|
153
|
+
return True
|
|
154
|
+
return False
|
|
155
|
+
|
|
156
|
+
if member_ids is not None and skip_member_messages:
|
|
157
|
+
log_debug("Member IDs to filter by were provided. The skip_member_messages flag will be ignored.")
|
|
158
|
+
skip_member_messages = False
|
|
159
|
+
|
|
133
160
|
if not self.runs:
|
|
134
161
|
return []
|
|
135
162
|
|
|
136
|
-
if
|
|
137
|
-
|
|
163
|
+
if skip_statuses is None:
|
|
164
|
+
skip_statuses = [RunStatus.paused, RunStatus.cancelled, RunStatus.error]
|
|
138
165
|
|
|
139
166
|
session_runs = self.runs
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
session_runs = [run for run in session_runs if hasattr(run, "agent_id") and run.agent_id == agent_id] # type: ignore
|
|
167
|
+
|
|
168
|
+
# Filter by team_id and member_ids
|
|
143
169
|
if team_id:
|
|
144
170
|
session_runs = [run for run in session_runs if hasattr(run, "team_id") and run.team_id == team_id] # type: ignore
|
|
171
|
+
if member_ids:
|
|
172
|
+
session_runs = [run for run in session_runs if hasattr(run, "agent_id") and run.agent_id in member_ids] # type: ignore
|
|
145
173
|
|
|
146
|
-
if
|
|
147
|
-
# Filter for the main team runs
|
|
174
|
+
if skip_member_messages:
|
|
175
|
+
# Filter for the top-level runs (main team runs or agent runs when sharing session)
|
|
148
176
|
session_runs = [run for run in session_runs if run.parent_run_id is None] # type: ignore
|
|
149
177
|
|
|
150
178
|
# Filter by status
|
|
151
|
-
session_runs = [run for run in session_runs if hasattr(run, "status") and run.status not in
|
|
179
|
+
session_runs = [run for run in session_runs if hasattr(run, "status") and run.status not in skip_statuses] # type: ignore
|
|
152
180
|
|
|
153
|
-
# Filter by last_n
|
|
154
|
-
runs_to_process = session_runs[-last_n:] if last_n is not None else session_runs
|
|
155
181
|
messages_from_history = []
|
|
156
182
|
system_message = None
|
|
157
|
-
for run_response in runs_to_process:
|
|
158
|
-
if not (run_response and run_response.messages):
|
|
159
|
-
continue
|
|
160
183
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
184
|
+
# Limit the number of messages returned if limit is set
|
|
185
|
+
if limit is not None:
|
|
186
|
+
for run_response in session_runs:
|
|
187
|
+
if not run_response or not run_response.messages:
|
|
164
188
|
continue
|
|
165
|
-
|
|
166
|
-
|
|
189
|
+
|
|
190
|
+
for message in run_response.messages or []:
|
|
191
|
+
if _should_skip_message(message, skip_roles, skip_history_messages):
|
|
192
|
+
continue
|
|
193
|
+
|
|
194
|
+
if message.role == "system":
|
|
195
|
+
# Only add the system message once
|
|
196
|
+
if system_message is None:
|
|
197
|
+
system_message = message
|
|
198
|
+
else:
|
|
199
|
+
messages_from_history.append(message)
|
|
200
|
+
|
|
201
|
+
if system_message:
|
|
202
|
+
messages_from_history = [system_message] + messages_from_history[
|
|
203
|
+
-(limit - 1) :
|
|
204
|
+
] # Grab one less message then add the system message
|
|
205
|
+
else:
|
|
206
|
+
messages_from_history = messages_from_history[-limit:]
|
|
207
|
+
|
|
208
|
+
# Remove tool result messages that don't have an associated assistant message with tool calls
|
|
209
|
+
while len(messages_from_history) > 0 and messages_from_history[0].role == "tool":
|
|
210
|
+
messages_from_history.pop(0)
|
|
211
|
+
else:
|
|
212
|
+
# Filter by last_n runs
|
|
213
|
+
runs_to_process = session_runs[-last_n_runs:] if last_n_runs is not None else session_runs
|
|
214
|
+
|
|
215
|
+
for run_response in runs_to_process:
|
|
216
|
+
if not (run_response and run_response.messages):
|
|
167
217
|
continue
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
218
|
+
|
|
219
|
+
for message in run_response.messages or []:
|
|
220
|
+
if _should_skip_message(message, skip_roles, skip_history_messages):
|
|
221
|
+
continue
|
|
222
|
+
|
|
223
|
+
if message.role == "system":
|
|
224
|
+
# Only add the system message once
|
|
225
|
+
if system_message is None:
|
|
226
|
+
system_message = message
|
|
227
|
+
messages_from_history.append(system_message)
|
|
228
|
+
else:
|
|
229
|
+
messages_from_history.append(message)
|
|
175
230
|
|
|
176
231
|
log_debug(f"Getting messages from previous runs: {len(messages_from_history)}")
|
|
177
232
|
return messages_from_history
|
|
178
233
|
|
|
234
|
+
def get_chat_history(self, last_n_runs: Optional[int] = None) -> List[Message]:
|
|
235
|
+
"""Return the chat history (user and assistant messages) for the session.
|
|
236
|
+
Use get_messages() for more filtering options.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
last_n_runs: Number of recent runs to include. If None, all runs will be considered.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
A list of user and assistant Messages belonging to the session.
|
|
243
|
+
"""
|
|
244
|
+
return self.get_messages(skip_roles=["system", "tool"], skip_member_messages=True, last_n_runs=last_n_runs)
|
|
245
|
+
|
|
179
246
|
def get_tool_calls(self, num_calls: Optional[int] = None) -> List[Dict[str, Any]]:
|
|
180
247
|
"""Returns a list of tool calls from the messages"""
|
|
181
248
|
|
|
@@ -194,69 +261,82 @@ class TeamSession:
|
|
|
194
261
|
return tool_calls
|
|
195
262
|
return tool_calls
|
|
196
263
|
|
|
197
|
-
def
|
|
198
|
-
|
|
199
|
-
user_role: str = "user",
|
|
200
|
-
assistant_role: Optional[List[str]] = None,
|
|
201
|
-
skip_history_messages: bool = True,
|
|
202
|
-
) -> List[Message]:
|
|
203
|
-
"""Returns a list of messages for the session that iterate through user message and assistant response."""
|
|
264
|
+
def get_team_history(self, num_runs: Optional[int] = None) -> List[Tuple[str, str]]:
|
|
265
|
+
"""Get team history as structured data (input, response pairs) -> This is the history of the team leader, not the members.
|
|
204
266
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
267
|
+
Args:
|
|
268
|
+
num_runs: Number of recent runs to include. If None, returns all available history.
|
|
269
|
+
"""
|
|
270
|
+
if not self.runs:
|
|
271
|
+
return []
|
|
208
272
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
273
|
+
from agno.run.base import RunStatus
|
|
274
|
+
|
|
275
|
+
# Get completed runs only (exclude current/pending run)
|
|
276
|
+
completed_runs = [run for run in self.runs if run.status == RunStatus.completed and run.parent_run_id is None]
|
|
277
|
+
|
|
278
|
+
if num_runs is not None and len(completed_runs) > num_runs:
|
|
279
|
+
recent_runs = completed_runs[-num_runs:]
|
|
280
|
+
else:
|
|
281
|
+
recent_runs = completed_runs
|
|
282
|
+
|
|
283
|
+
if not recent_runs:
|
|
212
284
|
return []
|
|
213
285
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
286
|
+
# Return structured data as list of (input, response) tuples
|
|
287
|
+
history_data = []
|
|
288
|
+
for run in recent_runs:
|
|
289
|
+
# Get input
|
|
290
|
+
input_str = ""
|
|
291
|
+
if run.input:
|
|
292
|
+
input_str = run.input.input_content_string()
|
|
218
293
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
294
|
+
# Get response
|
|
295
|
+
response_str = ""
|
|
296
|
+
if run.content:
|
|
297
|
+
response_str = (
|
|
298
|
+
run.content.model_dump_json(indent=2, exclude_none=True)
|
|
299
|
+
if isinstance(run.content, BaseModel)
|
|
300
|
+
else str(run.content)
|
|
301
|
+
)
|
|
226
302
|
|
|
227
|
-
|
|
228
|
-
for message in run_response.messages[::-1]:
|
|
229
|
-
if hasattr(message, "from_history") and message.from_history and skip_history_messages:
|
|
230
|
-
continue
|
|
231
|
-
if message.role in assistant_role:
|
|
232
|
-
assistant_message_from_run = message
|
|
233
|
-
break
|
|
303
|
+
history_data.append((input_str, response_str))
|
|
234
304
|
|
|
235
|
-
|
|
236
|
-
final_messages.append(user_message_from_run)
|
|
237
|
-
final_messages.append(assistant_message_from_run)
|
|
238
|
-
return final_messages
|
|
305
|
+
return history_data
|
|
239
306
|
|
|
240
|
-
def
|
|
241
|
-
"""Get
|
|
307
|
+
def get_team_history_context(self, num_runs: Optional[int] = None) -> Optional[str]:
|
|
308
|
+
"""Get formatted team history context for steps
|
|
242
309
|
|
|
243
|
-
|
|
310
|
+
Args:
|
|
311
|
+
num_runs: Number of recent runs to include. If None, returns all available history.
|
|
312
|
+
"""
|
|
313
|
+
history_data = self.get_team_history(num_runs)
|
|
314
|
+
|
|
315
|
+
if not history_data:
|
|
244
316
|
return None
|
|
245
317
|
|
|
246
|
-
|
|
318
|
+
# Format as team history context using the structured data
|
|
319
|
+
context_parts = ["<team_history_context>"]
|
|
247
320
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
"""Get the chat history for the session"""
|
|
321
|
+
for i, (input_str, response_str) in enumerate(history_data, 1):
|
|
322
|
+
context_parts.append(f"[run-{i}]")
|
|
251
323
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
324
|
+
if input_str:
|
|
325
|
+
context_parts.append(f"input: {input_str}")
|
|
326
|
+
if response_str:
|
|
327
|
+
context_parts.append(f"response: {response_str}")
|
|
255
328
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
329
|
+
context_parts.append("") # Empty line between runs
|
|
330
|
+
|
|
331
|
+
context_parts.append("</team_history_context>")
|
|
332
|
+
context_parts.append("") # Empty line before current input
|
|
259
333
|
|
|
260
|
-
|
|
334
|
+
return "\n".join(context_parts)
|
|
261
335
|
|
|
262
|
-
|
|
336
|
+
def get_session_summary(self) -> Optional[SessionSummary]:
|
|
337
|
+
"""Get the session summary for the session"""
|
|
338
|
+
|
|
339
|
+
if self.summary is None:
|
|
340
|
+
return None
|
|
341
|
+
|
|
342
|
+
return self.summary # type: ignore
|