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.
Files changed (92) hide show
  1. agno/agent/agent.py +197 -110
  2. agno/api/api.py +2 -0
  3. agno/db/base.py +26 -0
  4. agno/db/dynamo/dynamo.py +8 -0
  5. agno/db/dynamo/schemas.py +1 -0
  6. agno/db/firestore/firestore.py +8 -0
  7. agno/db/firestore/schemas.py +1 -0
  8. agno/db/gcs_json/gcs_json_db.py +8 -0
  9. agno/db/in_memory/in_memory_db.py +8 -1
  10. agno/db/json/json_db.py +8 -0
  11. agno/db/migrations/manager.py +199 -0
  12. agno/db/migrations/versions/__init__.py +0 -0
  13. agno/db/migrations/versions/v2_3_0.py +938 -0
  14. agno/db/mongo/async_mongo.py +16 -6
  15. agno/db/mongo/mongo.py +11 -0
  16. agno/db/mongo/schemas.py +3 -0
  17. agno/db/mongo/utils.py +17 -0
  18. agno/db/mysql/mysql.py +76 -3
  19. agno/db/mysql/schemas.py +20 -10
  20. agno/db/postgres/async_postgres.py +99 -25
  21. agno/db/postgres/postgres.py +75 -6
  22. agno/db/postgres/schemas.py +30 -20
  23. agno/db/redis/redis.py +15 -2
  24. agno/db/redis/schemas.py +4 -0
  25. agno/db/schemas/memory.py +13 -0
  26. agno/db/singlestore/schemas.py +11 -0
  27. agno/db/singlestore/singlestore.py +79 -5
  28. agno/db/sqlite/async_sqlite.py +97 -19
  29. agno/db/sqlite/schemas.py +10 -0
  30. agno/db/sqlite/sqlite.py +79 -2
  31. agno/db/surrealdb/surrealdb.py +8 -0
  32. agno/knowledge/chunking/semantic.py +7 -2
  33. agno/knowledge/embedder/nebius.py +1 -1
  34. agno/knowledge/knowledge.py +57 -86
  35. agno/knowledge/reader/csv_reader.py +7 -9
  36. agno/knowledge/reader/docx_reader.py +5 -5
  37. agno/knowledge/reader/field_labeled_csv_reader.py +16 -18
  38. agno/knowledge/reader/json_reader.py +5 -4
  39. agno/knowledge/reader/markdown_reader.py +8 -8
  40. agno/knowledge/reader/pdf_reader.py +11 -11
  41. agno/knowledge/reader/pptx_reader.py +5 -5
  42. agno/knowledge/reader/s3_reader.py +3 -3
  43. agno/knowledge/reader/text_reader.py +8 -8
  44. agno/knowledge/reader/web_search_reader.py +1 -48
  45. agno/knowledge/reader/website_reader.py +10 -10
  46. agno/models/anthropic/claude.py +319 -28
  47. agno/models/aws/claude.py +32 -0
  48. agno/models/azure/openai_chat.py +19 -10
  49. agno/models/base.py +612 -545
  50. agno/models/cerebras/cerebras.py +8 -11
  51. agno/models/cohere/chat.py +27 -1
  52. agno/models/google/gemini.py +39 -7
  53. agno/models/groq/groq.py +25 -11
  54. agno/models/meta/llama.py +20 -9
  55. agno/models/meta/llama_openai.py +3 -19
  56. agno/models/nebius/nebius.py +4 -4
  57. agno/models/openai/chat.py +30 -14
  58. agno/models/openai/responses.py +10 -13
  59. agno/models/response.py +1 -0
  60. agno/models/vertexai/claude.py +26 -0
  61. agno/os/app.py +8 -19
  62. agno/os/router.py +54 -0
  63. agno/os/routers/knowledge/knowledge.py +2 -2
  64. agno/os/schema.py +2 -2
  65. agno/session/agent.py +57 -92
  66. agno/session/summary.py +1 -1
  67. agno/session/team.py +62 -112
  68. agno/session/workflow.py +353 -57
  69. agno/team/team.py +227 -125
  70. agno/tools/models/nebius.py +5 -5
  71. agno/tools/models_labs.py +20 -10
  72. agno/tools/nano_banana.py +151 -0
  73. agno/tools/yfinance.py +12 -11
  74. agno/utils/http.py +111 -0
  75. agno/utils/media.py +11 -0
  76. agno/utils/models/claude.py +8 -0
  77. agno/utils/print_response/agent.py +33 -12
  78. agno/utils/print_response/team.py +22 -12
  79. agno/vectordb/couchbase/couchbase.py +6 -2
  80. agno/workflow/condition.py +13 -0
  81. agno/workflow/loop.py +13 -0
  82. agno/workflow/parallel.py +13 -0
  83. agno/workflow/router.py +13 -0
  84. agno/workflow/step.py +120 -20
  85. agno/workflow/steps.py +13 -0
  86. agno/workflow/workflow.py +76 -63
  87. {agno-2.2.13.dist-info → agno-2.3.1.dist-info}/METADATA +6 -2
  88. {agno-2.2.13.dist-info → agno-2.3.1.dist-info}/RECORD +91 -88
  89. agno/tools/googlesearch.py +0 -98
  90. {agno-2.2.13.dist-info → agno-2.3.1.dist-info}/WHEEL +0 -0
  91. {agno-2.2.13.dist-info → agno-2.3.1.dist-info}/licenses/LICENSE +0 -0
  92. {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 Knowledge Configuration",
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.get_filters(),
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
- "read_team_history": False,
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
- "read_team_history": team.read_team_history,
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 _should_skip_message(
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
- last_n: Optional[int] = None,
133
- last_n_messages: Optional[int] = None,
134
- skip_role: Optional[str] = None,
135
- skip_status: Optional[List[RunStatus]] = None,
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 from the last_n runs, excluding previously tagged history 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
- last_n: The number of runs to return from the end of the conversation. Defaults to all runs.
143
- last_n_messages: The number of messages to return from the end of the conversation. Defaults to all messages.
144
- skip_role: Skip messages with this role.
145
- skip_status: Skip messages with this status.
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 from the specified runs, excluding history 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 skip_status is None:
154
- skip_status = [RunStatus.paused, RunStatus.cancelled, RunStatus.error]
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
- session_runs = [run for run in session_runs if hasattr(run, "agent_id") and run.agent_id == agent_id] # type: ignore
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
- session_runs = [run for run in session_runs if hasattr(run, "team_id") and run.team_id == team_id] # type: ignore
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
- session_runs = [run for run in session_runs if run.parent_run_id is None] # type: ignore
169
+ runs = [run for run in runs if run.parent_run_id is None] # type: ignore
165
170
 
166
171
  # Filter by status
167
- session_runs = [run for run in session_runs if hasattr(run, "status") and run.status not in skip_status] # type: ignore
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
- # Filter by last_n_messages
173
- if last_n_messages is not None:
174
- for run_response in session_runs:
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 self._should_skip_message(message, skip_role, skip_history_messages):
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
- -(last_n_messages - 1) :
196
+ -(limit - 1) :
192
197
  ] # Grab one less message then add the system message
193
198
  else:
194
- messages_from_history = messages_from_history[-last_n_messages:]
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
- # Filter by last_n runs
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 self._should_skip_message(message, skip_role, skip_history_messages):
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.get_messages_for_session(), # type: ignore
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 _should_skip_message(
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
- last_n: Optional[int] = None,
134
- last_n_messages: Optional[int] = None,
135
- skip_role: Optional[str] = None,
136
- skip_status: Optional[List[RunStatus]] = None,
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
- member_runs: bool = False,
125
+ skip_member_messages: bool = True,
139
126
  ) -> List[Message]:
140
- """Returns the messages from the last_n runs, excluding previously tagged history messages.
141
- Args:
127
+ """Returns the messages belonging to the session that fit the given criteria.
142
128
 
143
- agent_id: The id of the agent to get the messages from.
129
+ Args:
144
130
  team_id: The id of the team to get the messages from.
145
- last_n: The number of runs to return from the end of the conversation. Defaults to all runs.
146
- last_n_messages: The number of messages to return from the end of the conversation. Defaults to all messages.
147
- skip_role: Skip messages with this role.
148
- skip_status: Skip messages with this status.
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 from the specified runs, excluding history 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 skip_status is None:
157
- skip_status = [RunStatus.paused, RunStatus.cancelled, RunStatus.error]
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 agent_id and team_id
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 not member_runs:
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 skip_status] # type: ignore
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
- # Filter by last_n_messages
177
- if last_n_messages is not None:
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 self._should_skip_message(message, skip_role, skip_history_messages):
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
- -(last_n_messages - 1) :
203
+ -(limit - 1) :
196
204
  ] # Grab one less message then add the system message
197
205
  else:
198
- messages_from_history = messages_from_history[-last_n_messages:]
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[-last_n:] if last_n is not None else 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 self._should_skip_message(message, skip_role, skip_history_messages):
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