agno 2.2.13__py3-none-any.whl → 2.3.1__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 +197 -110
- agno/api/api.py +2 -0
- agno/db/base.py +26 -0
- agno/db/dynamo/dynamo.py +8 -0
- agno/db/dynamo/schemas.py +1 -0
- agno/db/firestore/firestore.py +8 -0
- agno/db/firestore/schemas.py +1 -0
- agno/db/gcs_json/gcs_json_db.py +8 -0
- agno/db/in_memory/in_memory_db.py +8 -1
- agno/db/json/json_db.py +8 -0
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/async_mongo.py +16 -6
- agno/db/mongo/mongo.py +11 -0
- agno/db/mongo/schemas.py +3 -0
- agno/db/mongo/utils.py +17 -0
- agno/db/mysql/mysql.py +76 -3
- agno/db/mysql/schemas.py +20 -10
- agno/db/postgres/async_postgres.py +99 -25
- agno/db/postgres/postgres.py +75 -6
- agno/db/postgres/schemas.py +30 -20
- agno/db/redis/redis.py +15 -2
- agno/db/redis/schemas.py +4 -0
- agno/db/schemas/memory.py +13 -0
- agno/db/singlestore/schemas.py +11 -0
- agno/db/singlestore/singlestore.py +79 -5
- agno/db/sqlite/async_sqlite.py +97 -19
- agno/db/sqlite/schemas.py +10 -0
- agno/db/sqlite/sqlite.py +79 -2
- agno/db/surrealdb/surrealdb.py +8 -0
- agno/knowledge/chunking/semantic.py +7 -2
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/knowledge.py +57 -86
- agno/knowledge/reader/csv_reader.py +7 -9
- agno/knowledge/reader/docx_reader.py +5 -5
- agno/knowledge/reader/field_labeled_csv_reader.py +16 -18
- agno/knowledge/reader/json_reader.py +5 -4
- agno/knowledge/reader/markdown_reader.py +8 -8
- agno/knowledge/reader/pdf_reader.py +11 -11
- agno/knowledge/reader/pptx_reader.py +5 -5
- agno/knowledge/reader/s3_reader.py +3 -3
- agno/knowledge/reader/text_reader.py +8 -8
- agno/knowledge/reader/web_search_reader.py +1 -48
- agno/knowledge/reader/website_reader.py +10 -10
- agno/models/anthropic/claude.py +319 -28
- agno/models/aws/claude.py +32 -0
- agno/models/azure/openai_chat.py +19 -10
- agno/models/base.py +612 -545
- agno/models/cerebras/cerebras.py +8 -11
- agno/models/cohere/chat.py +27 -1
- agno/models/google/gemini.py +39 -7
- agno/models/groq/groq.py +25 -11
- agno/models/meta/llama.py +20 -9
- agno/models/meta/llama_openai.py +3 -19
- agno/models/nebius/nebius.py +4 -4
- agno/models/openai/chat.py +30 -14
- agno/models/openai/responses.py +10 -13
- agno/models/response.py +1 -0
- agno/models/vertexai/claude.py +26 -0
- agno/os/app.py +8 -19
- agno/os/router.py +54 -0
- agno/os/routers/knowledge/knowledge.py +2 -2
- agno/os/schema.py +2 -2
- agno/session/agent.py +57 -92
- agno/session/summary.py +1 -1
- agno/session/team.py +62 -112
- agno/session/workflow.py +353 -57
- agno/team/team.py +227 -125
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/yfinance.py +12 -11
- agno/utils/http.py +111 -0
- agno/utils/media.py +11 -0
- agno/utils/models/claude.py +8 -0
- agno/utils/print_response/agent.py +33 -12
- agno/utils/print_response/team.py +22 -12
- agno/vectordb/couchbase/couchbase.py +6 -2
- agno/workflow/condition.py +13 -0
- agno/workflow/loop.py +13 -0
- agno/workflow/parallel.py +13 -0
- agno/workflow/router.py +13 -0
- agno/workflow/step.py +120 -20
- agno/workflow/steps.py +13 -0
- agno/workflow/workflow.py +76 -63
- {agno-2.2.13.dist-info → agno-2.3.1.dist-info}/METADATA +6 -2
- {agno-2.2.13.dist-info → agno-2.3.1.dist-info}/RECORD +91 -88
- agno/tools/googlesearch.py +0 -98
- {agno-2.2.13.dist-info → agno-2.3.1.dist-info}/WHEEL +0 -0
- {agno-2.2.13.dist-info → agno-2.3.1.dist-info}/licenses/LICENSE +0 -0
- {agno-2.2.13.dist-info → agno-2.3.1.dist-info}/top_level.txt +0 -0
agno/os/router.py
CHANGED
|
@@ -13,9 +13,12 @@ from fastapi import (
|
|
|
13
13
|
WebSocket,
|
|
14
14
|
)
|
|
15
15
|
from fastapi.responses import JSONResponse, StreamingResponse
|
|
16
|
+
from packaging import version
|
|
16
17
|
from pydantic import BaseModel
|
|
17
18
|
|
|
18
19
|
from agno.agent.agent import Agent
|
|
20
|
+
from agno.db.base import AsyncBaseDb
|
|
21
|
+
from agno.db.migrations.manager import MigrationManager
|
|
19
22
|
from agno.exceptions import InputCheckError, OutputCheckError
|
|
20
23
|
from agno.media import Audio, Image, Video
|
|
21
24
|
from agno.media import File as FileMedia
|
|
@@ -39,6 +42,7 @@ from agno.os.schema import (
|
|
|
39
42
|
from agno.os.settings import AgnoAPISettings
|
|
40
43
|
from agno.os.utils import (
|
|
41
44
|
get_agent_by_id,
|
|
45
|
+
get_db,
|
|
42
46
|
get_team_by_id,
|
|
43
47
|
get_workflow_by_id,
|
|
44
48
|
process_audio,
|
|
@@ -1760,4 +1764,54 @@ def get_base_router(
|
|
|
1760
1764
|
|
|
1761
1765
|
return JSONResponse(content={}, status_code=200)
|
|
1762
1766
|
|
|
1767
|
+
# -- Database Migration routes ---
|
|
1768
|
+
|
|
1769
|
+
@router.post(
|
|
1770
|
+
"/databases/{db_id}/migrate",
|
|
1771
|
+
tags=["Database"],
|
|
1772
|
+
operation_id="migrate_database",
|
|
1773
|
+
summary="Migrate Database",
|
|
1774
|
+
description=(
|
|
1775
|
+
"Migrate the given database schema to the given target version. "
|
|
1776
|
+
"If a target version is not provided, the database will be migrated to the latest version. "
|
|
1777
|
+
),
|
|
1778
|
+
responses={
|
|
1779
|
+
200: {
|
|
1780
|
+
"description": "Database migrated successfully",
|
|
1781
|
+
"content": {
|
|
1782
|
+
"application/json": {
|
|
1783
|
+
"example": {"message": "Database migrated successfully to version 3.0.0"},
|
|
1784
|
+
}
|
|
1785
|
+
},
|
|
1786
|
+
},
|
|
1787
|
+
404: {"description": "Database not found", "model": NotFoundResponse},
|
|
1788
|
+
500: {"description": "Failed to migrate database", "model": InternalServerErrorResponse},
|
|
1789
|
+
},
|
|
1790
|
+
)
|
|
1791
|
+
async def migrate_database(db_id: str, target_version: Optional[str] = None):
|
|
1792
|
+
db = await get_db(os.dbs, db_id)
|
|
1793
|
+
if not db:
|
|
1794
|
+
raise HTTPException(status_code=404, detail="Database not found")
|
|
1795
|
+
|
|
1796
|
+
if target_version:
|
|
1797
|
+
|
|
1798
|
+
# Use the session table as proxy for the database schema version
|
|
1799
|
+
if isinstance(db, AsyncBaseDb):
|
|
1800
|
+
current_version = await db.get_latest_schema_version(db.session_table_name)
|
|
1801
|
+
else:
|
|
1802
|
+
current_version = db.get_latest_schema_version(db.session_table_name)
|
|
1803
|
+
|
|
1804
|
+
if version.parse(target_version) > version.parse(current_version): # type: ignore
|
|
1805
|
+
MigrationManager(db).up(target_version) # type: ignore
|
|
1806
|
+
else:
|
|
1807
|
+
MigrationManager(db).down(target_version) # type: ignore
|
|
1808
|
+
|
|
1809
|
+
# If the target version is not provided, migrate to the latest version
|
|
1810
|
+
else:
|
|
1811
|
+
MigrationManager(db).up() # type: ignore
|
|
1812
|
+
|
|
1813
|
+
return JSONResponse(
|
|
1814
|
+
content={"message": f"Database migrated successfully to version {target_version}"}, status_code=200
|
|
1815
|
+
)
|
|
1816
|
+
|
|
1763
1817
|
return router
|
|
@@ -617,7 +617,7 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
|
|
|
617
617
|
"/knowledge/config",
|
|
618
618
|
status_code=200,
|
|
619
619
|
operation_id="get_knowledge_config",
|
|
620
|
-
summary="Get
|
|
620
|
+
summary="Get Config",
|
|
621
621
|
description=(
|
|
622
622
|
"Retrieve available readers, chunkers, and configuration options for content processing. "
|
|
623
623
|
"This endpoint provides metadata about supported file types, processing strategies, and filters."
|
|
@@ -942,7 +942,7 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
|
|
|
942
942
|
vector_dbs=vector_dbs,
|
|
943
943
|
readersForType=types_of_readers,
|
|
944
944
|
chunkers=chunkers_dict,
|
|
945
|
-
filters=knowledge.
|
|
945
|
+
filters=knowledge.get_valid_filters(),
|
|
946
946
|
)
|
|
947
947
|
|
|
948
948
|
return router
|
agno/os/schema.py
CHANGED
|
@@ -458,7 +458,7 @@ class TeamResponse(BaseModel):
|
|
|
458
458
|
"reasoning_max_steps": 10,
|
|
459
459
|
# Default tools defaults
|
|
460
460
|
"search_knowledge": True,
|
|
461
|
-
"
|
|
461
|
+
"read_chat_history": False,
|
|
462
462
|
"get_member_information_tool": False,
|
|
463
463
|
# System message defaults
|
|
464
464
|
"system_message_role": "system",
|
|
@@ -556,7 +556,7 @@ class TeamResponse(BaseModel):
|
|
|
556
556
|
|
|
557
557
|
default_tools_info = {
|
|
558
558
|
"search_knowledge": team.search_knowledge,
|
|
559
|
-
"
|
|
559
|
+
"read_chat_history": team.read_chat_history,
|
|
560
560
|
"get_member_information_tool": team.get_member_information_tool,
|
|
561
561
|
}
|
|
562
562
|
|
agno/session/agent.py
CHANGED
|
@@ -112,71 +112,76 @@ class AgentSession:
|
|
|
112
112
|
return run
|
|
113
113
|
return None
|
|
114
114
|
|
|
115
|
-
def
|
|
116
|
-
self, message: Message, skip_role: Optional[str] = None, skip_history_messages: bool = True
|
|
117
|
-
) -> bool:
|
|
118
|
-
"""Processes a message for history"""
|
|
119
|
-
# Skip messages that were tagged as history in previous runs
|
|
120
|
-
if hasattr(message, "from_history") and message.from_history and skip_history_messages:
|
|
121
|
-
return True
|
|
122
|
-
|
|
123
|
-
# Skip messages with specified role
|
|
124
|
-
if skip_role and message.role == skip_role:
|
|
125
|
-
return True
|
|
126
|
-
return False
|
|
127
|
-
|
|
128
|
-
def get_messages_from_last_n_runs(
|
|
115
|
+
def get_messages(
|
|
129
116
|
self,
|
|
130
117
|
agent_id: Optional[str] = None,
|
|
131
118
|
team_id: Optional[str] = None,
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
119
|
+
last_n_runs: Optional[int] = None,
|
|
120
|
+
limit: Optional[int] = None,
|
|
121
|
+
skip_roles: Optional[List[str]] = None,
|
|
122
|
+
skip_statuses: Optional[List[RunStatus]] = None,
|
|
136
123
|
skip_history_messages: bool = True,
|
|
137
124
|
) -> List[Message]:
|
|
138
|
-
"""Returns the messages
|
|
125
|
+
"""Returns the messages belonging to the session that fit the given criteria.
|
|
126
|
+
|
|
139
127
|
Args:
|
|
140
128
|
agent_id: The id of the agent to get the messages from.
|
|
141
129
|
team_id: The id of the team to get the messages from.
|
|
142
|
-
|
|
143
|
-
last_n_messages: The number of messages to return from the
|
|
144
|
-
|
|
145
|
-
|
|
130
|
+
last_n_runs: The number of runs to return messages from, counting from the latest. Defaults to all runs.
|
|
131
|
+
last_n_messages: The number of messages to return, counting from the latest. Defaults to all messages.
|
|
132
|
+
skip_roles: Skip messages with these roles.
|
|
133
|
+
skip_statuses: Skip messages with these statuses.
|
|
146
134
|
skip_history_messages: Skip messages that were tagged as history in previous runs.
|
|
135
|
+
|
|
147
136
|
Returns:
|
|
148
|
-
A list of Messages
|
|
137
|
+
A list of Messages belonging to the session.
|
|
149
138
|
"""
|
|
139
|
+
|
|
140
|
+
def _should_skip_message(
|
|
141
|
+
message: Message, skip_roles: Optional[List[str]] = None, skip_history_messages: bool = True
|
|
142
|
+
) -> bool:
|
|
143
|
+
"""Logic to determine if a message should be skipped"""
|
|
144
|
+
# Skip messages that were tagged as history in previous runs
|
|
145
|
+
if hasattr(message, "from_history") and message.from_history and skip_history_messages:
|
|
146
|
+
return True
|
|
147
|
+
|
|
148
|
+
# Skip messages with specified role
|
|
149
|
+
if skip_roles and message.role in skip_roles:
|
|
150
|
+
return True
|
|
151
|
+
|
|
152
|
+
return False
|
|
153
|
+
|
|
150
154
|
if not self.runs:
|
|
151
155
|
return []
|
|
152
156
|
|
|
153
|
-
if
|
|
154
|
-
|
|
157
|
+
if skip_statuses is None:
|
|
158
|
+
skip_statuses = [RunStatus.paused, RunStatus.cancelled, RunStatus.error]
|
|
159
|
+
|
|
160
|
+
runs = self.runs
|
|
155
161
|
|
|
156
|
-
session_runs = self.runs
|
|
157
162
|
# Filter by agent_id and team_id
|
|
158
163
|
if agent_id:
|
|
159
|
-
|
|
164
|
+
runs = [run for run in runs if hasattr(run, "agent_id") and run.agent_id == agent_id] # type: ignore
|
|
160
165
|
if team_id:
|
|
161
|
-
|
|
166
|
+
runs = [run for run in runs if hasattr(run, "team_id") and run.team_id == team_id] # type: ignore
|
|
162
167
|
|
|
163
168
|
# Skip any messages that might be part of members of teams (for session re-use)
|
|
164
|
-
|
|
169
|
+
runs = [run for run in runs if run.parent_run_id is None] # type: ignore
|
|
165
170
|
|
|
166
171
|
# Filter by status
|
|
167
|
-
|
|
172
|
+
runs = [run for run in runs if hasattr(run, "status") and run.status not in skip_statuses] # type: ignore
|
|
168
173
|
|
|
169
174
|
messages_from_history = []
|
|
170
175
|
system_message = None
|
|
171
176
|
|
|
172
|
-
#
|
|
173
|
-
if
|
|
174
|
-
for run_response in
|
|
177
|
+
# Limit the number of messages returned if limit is set
|
|
178
|
+
if limit is not None:
|
|
179
|
+
for run_response in runs:
|
|
175
180
|
if not run_response or not run_response.messages:
|
|
176
181
|
continue
|
|
177
182
|
|
|
178
183
|
for message in run_response.messages or []:
|
|
179
|
-
if
|
|
184
|
+
if _should_skip_message(message, skip_roles, skip_history_messages):
|
|
180
185
|
continue
|
|
181
186
|
|
|
182
187
|
if message.role == "system":
|
|
@@ -188,24 +193,24 @@ class AgentSession:
|
|
|
188
193
|
|
|
189
194
|
if system_message:
|
|
190
195
|
messages_from_history = [system_message] + messages_from_history[
|
|
191
|
-
-(
|
|
196
|
+
-(limit - 1) :
|
|
192
197
|
] # Grab one less message then add the system message
|
|
193
198
|
else:
|
|
194
|
-
messages_from_history = messages_from_history[-
|
|
199
|
+
messages_from_history = messages_from_history[-limit:]
|
|
195
200
|
|
|
196
201
|
# Remove tool result messages that don't have an associated assistant message with tool calls
|
|
197
202
|
while len(messages_from_history) > 0 and messages_from_history[0].role == "tool":
|
|
198
203
|
messages_from_history.pop(0)
|
|
199
204
|
|
|
205
|
+
# If limit is not set, return all messages
|
|
200
206
|
else:
|
|
201
|
-
|
|
202
|
-
runs_to_process = session_runs[-last_n:] if last_n is not None else session_runs
|
|
207
|
+
runs_to_process = runs[-last_n_runs:] if last_n_runs is not None else runs
|
|
203
208
|
for run_response in runs_to_process:
|
|
204
209
|
if not run_response or not run_response.messages:
|
|
205
210
|
continue
|
|
206
211
|
|
|
207
212
|
for message in run_response.messages or []:
|
|
208
|
-
if
|
|
213
|
+
if _should_skip_message(message, skip_roles, skip_history_messages):
|
|
209
214
|
continue
|
|
210
215
|
|
|
211
216
|
if message.role == "system":
|
|
@@ -219,6 +224,18 @@ class AgentSession:
|
|
|
219
224
|
log_debug(f"Getting messages from previous runs: {len(messages_from_history)}")
|
|
220
225
|
return messages_from_history
|
|
221
226
|
|
|
227
|
+
def get_chat_history(self, last_n_runs: Optional[int] = None) -> List[Message]:
|
|
228
|
+
"""Return the chat history (user and assistant messages) for the session.
|
|
229
|
+
Use get_messages() for more filtering options.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
last_n_runs: Number of recent runs to include. If None, all runs will be considered.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
A list of user and assistant Messages belonging to the session.
|
|
236
|
+
"""
|
|
237
|
+
return self.get_messages(skip_roles=["system", "tool"], last_n_runs=last_n_runs)
|
|
238
|
+
|
|
222
239
|
def get_tool_calls(self, num_calls: Optional[int] = None) -> List[Dict[str, Any]]:
|
|
223
240
|
"""Returns a list of tool calls from the messages"""
|
|
224
241
|
|
|
@@ -235,61 +252,9 @@ class AgentSession:
|
|
|
235
252
|
return tool_calls
|
|
236
253
|
return tool_calls
|
|
237
254
|
|
|
238
|
-
def get_messages_for_session(
|
|
239
|
-
self,
|
|
240
|
-
user_role: str = "user",
|
|
241
|
-
assistant_role: Optional[List[str]] = None,
|
|
242
|
-
skip_history_messages: bool = True,
|
|
243
|
-
) -> List[Message]:
|
|
244
|
-
"""Returns a list of messages for the session that iterate through user message and assistant response."""
|
|
245
|
-
|
|
246
|
-
if assistant_role is None:
|
|
247
|
-
# TODO: Check if we still need CHATBOT as a role
|
|
248
|
-
assistant_role = ["assistant", "model", "CHATBOT"]
|
|
249
|
-
|
|
250
|
-
final_messages: List[Message] = []
|
|
251
|
-
session_runs = self.runs
|
|
252
|
-
if not session_runs:
|
|
253
|
-
return []
|
|
254
|
-
|
|
255
|
-
for run_response in session_runs:
|
|
256
|
-
if run_response and run_response.messages:
|
|
257
|
-
user_message_from_run = None
|
|
258
|
-
assistant_message_from_run = None
|
|
259
|
-
|
|
260
|
-
# Start from the beginning to look for the user message
|
|
261
|
-
for message in run_response.messages or []:
|
|
262
|
-
if hasattr(message, "from_history") and message.from_history and skip_history_messages:
|
|
263
|
-
continue
|
|
264
|
-
if message.role == user_role:
|
|
265
|
-
user_message_from_run = message
|
|
266
|
-
break
|
|
267
|
-
|
|
268
|
-
# Start from the end to look for the assistant response
|
|
269
|
-
for message in run_response.messages[::-1]:
|
|
270
|
-
if hasattr(message, "from_history") and message.from_history and skip_history_messages:
|
|
271
|
-
continue
|
|
272
|
-
if message.role in assistant_role:
|
|
273
|
-
assistant_message_from_run = message
|
|
274
|
-
break
|
|
275
|
-
|
|
276
|
-
if user_message_from_run and assistant_message_from_run:
|
|
277
|
-
final_messages.append(user_message_from_run)
|
|
278
|
-
final_messages.append(assistant_message_from_run)
|
|
279
|
-
return final_messages
|
|
280
|
-
|
|
281
255
|
def get_session_summary(self) -> Optional[SessionSummary]:
|
|
282
256
|
"""Get the session summary for the session"""
|
|
283
257
|
|
|
284
258
|
if self.summary is None:
|
|
285
259
|
return None
|
|
286
260
|
return self.summary
|
|
287
|
-
|
|
288
|
-
# Chat History functions
|
|
289
|
-
def get_chat_history(self) -> List[Message]:
|
|
290
|
-
"""Get the chat history for the session"""
|
|
291
|
-
|
|
292
|
-
messages = []
|
|
293
|
-
for run in self.runs or []:
|
|
294
|
-
messages.extend([msg for msg in run.messages or [] if not msg.from_history])
|
|
295
|
-
return messages
|
agno/session/summary.py
CHANGED
|
@@ -150,7 +150,7 @@ class SessionSummaryManager:
|
|
|
150
150
|
response_format = self.get_response_format(self.model)
|
|
151
151
|
|
|
152
152
|
system_message = self.get_system_message(
|
|
153
|
-
conversation=session.
|
|
153
|
+
conversation=session.get_messages(), # type: ignore
|
|
154
154
|
response_format=response_format,
|
|
155
155
|
)
|
|
156
156
|
|
agno/session/team.py
CHANGED
|
@@ -113,74 +113,82 @@ class TeamSession:
|
|
|
113
113
|
|
|
114
114
|
log_debug("Added RunOutput to Team Session")
|
|
115
115
|
|
|
116
|
-
def
|
|
117
|
-
self, message: Message, skip_role: Optional[str] = None, skip_history_messages: bool = True
|
|
118
|
-
) -> bool:
|
|
119
|
-
"""Processes a message for history"""
|
|
120
|
-
# Skip messages that were tagged as history in previous runs
|
|
121
|
-
if hasattr(message, "from_history") and message.from_history and skip_history_messages:
|
|
122
|
-
return True
|
|
123
|
-
|
|
124
|
-
# Skip messages with specified role
|
|
125
|
-
if skip_role and message.role == skip_role:
|
|
126
|
-
return True
|
|
127
|
-
return False
|
|
128
|
-
|
|
129
|
-
def get_messages_from_last_n_runs(
|
|
116
|
+
def get_messages(
|
|
130
117
|
self,
|
|
131
|
-
agent_id: Optional[str] = None,
|
|
132
118
|
team_id: Optional[str] = None,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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,
|
|
137
124
|
skip_history_messages: bool = True,
|
|
138
|
-
|
|
125
|
+
skip_member_messages: bool = True,
|
|
139
126
|
) -> List[Message]:
|
|
140
|
-
"""Returns the messages
|
|
141
|
-
Args:
|
|
127
|
+
"""Returns the messages belonging to the session that fit the given criteria.
|
|
142
128
|
|
|
143
|
-
|
|
129
|
+
Args:
|
|
144
130
|
team_id: The id of the team to get the messages from.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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.
|
|
149
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
|
+
|
|
150
139
|
Returns:
|
|
151
|
-
A list of Messages
|
|
140
|
+
A list of Messages belonging to the session.
|
|
152
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
|
+
|
|
153
160
|
if not self.runs:
|
|
154
161
|
return []
|
|
155
162
|
|
|
156
|
-
if
|
|
157
|
-
|
|
163
|
+
if skip_statuses is None:
|
|
164
|
+
skip_statuses = [RunStatus.paused, RunStatus.cancelled, RunStatus.error]
|
|
158
165
|
|
|
159
166
|
session_runs = self.runs
|
|
160
167
|
|
|
161
|
-
# Filter by
|
|
162
|
-
if agent_id:
|
|
163
|
-
session_runs = [run for run in session_runs if hasattr(run, "agent_id") and run.agent_id == agent_id] # type: ignore
|
|
168
|
+
# Filter by team_id and member_ids
|
|
164
169
|
if team_id:
|
|
165
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
|
|
166
173
|
|
|
167
|
-
if
|
|
174
|
+
if skip_member_messages:
|
|
168
175
|
# Filter for the top-level runs (main team runs or agent runs when sharing session)
|
|
169
176
|
session_runs = [run for run in session_runs if run.parent_run_id is None] # type: ignore
|
|
177
|
+
|
|
170
178
|
# Filter by status
|
|
171
|
-
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
|
|
172
180
|
|
|
173
181
|
messages_from_history = []
|
|
174
182
|
system_message = None
|
|
175
183
|
|
|
176
|
-
#
|
|
177
|
-
if
|
|
184
|
+
# Limit the number of messages returned if limit is set
|
|
185
|
+
if limit is not None:
|
|
178
186
|
for run_response in session_runs:
|
|
179
187
|
if not run_response or not run_response.messages:
|
|
180
188
|
continue
|
|
181
189
|
|
|
182
190
|
for message in run_response.messages or []:
|
|
183
|
-
if
|
|
191
|
+
if _should_skip_message(message, skip_roles, skip_history_messages):
|
|
184
192
|
continue
|
|
185
193
|
|
|
186
194
|
if message.role == "system":
|
|
@@ -192,24 +200,24 @@ class TeamSession:
|
|
|
192
200
|
|
|
193
201
|
if system_message:
|
|
194
202
|
messages_from_history = [system_message] + messages_from_history[
|
|
195
|
-
-(
|
|
203
|
+
-(limit - 1) :
|
|
196
204
|
] # Grab one less message then add the system message
|
|
197
205
|
else:
|
|
198
|
-
messages_from_history = messages_from_history[-
|
|
206
|
+
messages_from_history = messages_from_history[-limit:]
|
|
199
207
|
|
|
200
208
|
# Remove tool result messages that don't have an associated assistant message with tool calls
|
|
201
209
|
while len(messages_from_history) > 0 and messages_from_history[0].role == "tool":
|
|
202
210
|
messages_from_history.pop(0)
|
|
203
211
|
else:
|
|
204
212
|
# Filter by last_n runs
|
|
205
|
-
runs_to_process = session_runs[-
|
|
213
|
+
runs_to_process = session_runs[-last_n_runs:] if last_n_runs is not None else session_runs
|
|
206
214
|
|
|
207
215
|
for run_response in runs_to_process:
|
|
208
216
|
if not (run_response and run_response.messages):
|
|
209
217
|
continue
|
|
210
218
|
|
|
211
219
|
for message in run_response.messages or []:
|
|
212
|
-
if
|
|
220
|
+
if _should_skip_message(message, skip_roles, skip_history_messages):
|
|
213
221
|
continue
|
|
214
222
|
|
|
215
223
|
if message.role == "system":
|
|
@@ -223,6 +231,18 @@ class TeamSession:
|
|
|
223
231
|
log_debug(f"Getting messages from previous runs: {len(messages_from_history)}")
|
|
224
232
|
return messages_from_history
|
|
225
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
|
+
|
|
226
246
|
def get_tool_calls(self, num_calls: Optional[int] = None) -> List[Dict[str, Any]]:
|
|
227
247
|
"""Returns a list of tool calls from the messages"""
|
|
228
248
|
|
|
@@ -241,49 +261,6 @@ class TeamSession:
|
|
|
241
261
|
return tool_calls
|
|
242
262
|
return tool_calls
|
|
243
263
|
|
|
244
|
-
def get_messages_for_session(
|
|
245
|
-
self,
|
|
246
|
-
user_role: str = "user",
|
|
247
|
-
assistant_role: Optional[List[str]] = None,
|
|
248
|
-
skip_history_messages: bool = True,
|
|
249
|
-
) -> List[Message]:
|
|
250
|
-
"""Returns a list of messages for the session that iterate through user message and assistant response."""
|
|
251
|
-
|
|
252
|
-
if assistant_role is None:
|
|
253
|
-
# TODO: Check if we still need CHATBOT as a role
|
|
254
|
-
assistant_role = ["assistant", "model", "CHATBOT"]
|
|
255
|
-
|
|
256
|
-
final_messages: List[Message] = []
|
|
257
|
-
session_runs = self.runs
|
|
258
|
-
if session_runs is None:
|
|
259
|
-
return []
|
|
260
|
-
|
|
261
|
-
for run_response in session_runs:
|
|
262
|
-
if run_response and run_response.messages:
|
|
263
|
-
user_message_from_run = None
|
|
264
|
-
assistant_message_from_run = None
|
|
265
|
-
|
|
266
|
-
# Start from the beginning to look for the user message
|
|
267
|
-
for message in run_response.messages or []:
|
|
268
|
-
if hasattr(message, "from_history") and message.from_history and skip_history_messages:
|
|
269
|
-
continue
|
|
270
|
-
if message.role == user_role:
|
|
271
|
-
user_message_from_run = message
|
|
272
|
-
break
|
|
273
|
-
|
|
274
|
-
# Start from the end to look for the assistant response
|
|
275
|
-
for message in run_response.messages[::-1]:
|
|
276
|
-
if hasattr(message, "from_history") and message.from_history and skip_history_messages:
|
|
277
|
-
continue
|
|
278
|
-
if message.role in assistant_role:
|
|
279
|
-
assistant_message_from_run = message
|
|
280
|
-
break
|
|
281
|
-
|
|
282
|
-
if user_message_from_run and assistant_message_from_run:
|
|
283
|
-
final_messages.append(user_message_from_run)
|
|
284
|
-
final_messages.append(assistant_message_from_run)
|
|
285
|
-
return final_messages
|
|
286
|
-
|
|
287
264
|
def get_team_history(self, num_runs: Optional[int] = None) -> List[Tuple[str, str]]:
|
|
288
265
|
"""Get team history as structured data (input, response pairs) -> This is the history of the team leader, not the members.
|
|
289
266
|
|
|
@@ -363,30 +340,3 @@ class TeamSession:
|
|
|
363
340
|
return None
|
|
364
341
|
|
|
365
342
|
return self.summary # type: ignore
|
|
366
|
-
|
|
367
|
-
# Chat History functions
|
|
368
|
-
def get_chat_history(
|
|
369
|
-
self, skip_history_messages: bool = True, skip_roles: Optional[List[str]] = None
|
|
370
|
-
) -> List[Message]:
|
|
371
|
-
"""
|
|
372
|
-
Get the chat history for the session.
|
|
373
|
-
This is all messages across all runs for the team leader.
|
|
374
|
-
"""
|
|
375
|
-
|
|
376
|
-
messages = []
|
|
377
|
-
if self.runs is None:
|
|
378
|
-
return []
|
|
379
|
-
|
|
380
|
-
for run in self.runs or []:
|
|
381
|
-
if run.parent_run_id is not None:
|
|
382
|
-
continue
|
|
383
|
-
|
|
384
|
-
if run.messages is not None:
|
|
385
|
-
for msg in run.messages or []:
|
|
386
|
-
if skip_history_messages and msg.from_history:
|
|
387
|
-
continue
|
|
388
|
-
if skip_roles and msg.role in skip_roles:
|
|
389
|
-
continue
|
|
390
|
-
messages.append(msg)
|
|
391
|
-
|
|
392
|
-
return messages
|