Skip to content

Commit 6f79346

Browse files
dirkbrndkausmeows
andauthored
chore: Release 1.6.3 (#3582)
# Changelog ## New Features: - **User Control Flows on Playground**: The Agno Platform now support user control flows on the playground. - **Team & Agent Events on RunResponse:** Added `store_events` parameter to optionally add all events that happened during the course of an agent/team run on the `RunResponse`/`TeamRunResponse`. - **Team Member Responses on Playground**: The Agno Platform now shows member responses during team runs. - **Behind-the-scenes on Playground**: The Agno Platform now shows what is happening during runs, for both agents and teams. - **Metadata filtering support for `csv` and `csv_url` knowledge bases:** Add knowledge filters support for these knowledge base types. ## Bug Fixes: - **AG-UI Fix**: Fixed issue related to missing messages when using the Agno AG-UI app. - **Chat History Fix**: Fixed issue related to history not available when `agent_id` not set. - **MongoDB ObjectId serialization issue when using with agent:** Fixed issue while using mongodb vectordb with ObjectId present in the metadata it throws Object of type `ObjectId` is not JSON serializable --------- Co-authored-by: Kaustubh <shuklakaustubh84@gmail.com>
1 parent 518d7fc commit 6f79346

19 files changed

+82
-53
lines changed

cookbook/teams/team_events.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@
88
from agno.models.openai.chat import OpenAIChat
99
from agno.team import Team, TeamRunEvent
1010
from agno.tools.duckduckgo import DuckDuckGoTools
11-
from agno.tools.wikipedia import WikipediaTools
11+
from agno.tools.hackernews import HackerNewsTools
1212

1313
wikipedia_agent = Agent(
14-
agent_id="wikipedia-agent",
15-
name="Wikipedia Agent",
16-
role="Search wikipedia for information",
14+
agent_id="hacker-news-agent",
15+
name="Hacker News Agent",
16+
role="Search Hacker News for information",
1717
model=MistralChat(id="mistral-large-latest"),
18-
tools=[WikipediaTools()],
18+
tools=[HackerNewsTools()],
1919
instructions=[
20-
"Find information about the company in the wikipedia",
20+
"Find articles about the company in the Hacker News",
2121
],
2222
)
2323

libs/agno/agno/agent/agent.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ class Agent:
279279

280280
# Persist the events on the run response
281281
store_events: bool = False
282-
events_to_skip: Optional[List[str]] = None
282+
events_to_skip: Optional[List[RunEvent]] = None
283283

284284
# --- Agent Team ---
285285
# The team of agents that this agent can transfer tasks to.
@@ -399,7 +399,7 @@ def __init__(
399399
stream: Optional[bool] = None,
400400
stream_intermediate_steps: bool = False,
401401
store_events: bool = False,
402-
events_to_skip: Optional[List[str]] = None,
402+
events_to_skip: Optional[List[RunEvent]] = None,
403403
team: Optional[List[Agent]] = None,
404404
team_data: Optional[Dict[str, Any]] = None,
405405
role: Optional[str] = None,
@@ -504,7 +504,9 @@ def __init__(
504504

505505
self.store_events = store_events
506506
# By default, we skip the run response content event
507-
self.events_to_skip = events_to_skip or [RunEvent.run_response_content.value]
507+
self.events_to_skip = events_to_skip
508+
if self.events_to_skip is None:
509+
self.events_to_skip = [RunEvent.run_response_content]
508510

509511
self.team = team
510512

@@ -633,7 +635,8 @@ def initialize_agent(self) -> None:
633635
from copy import deepcopy
634636

635637
# We store a copy of memory to ensure different team instances reference unique memory copy
636-
self.memory = deepcopy(self.memory)
638+
if isinstance(self.memory, Memory):
639+
self.memory = deepcopy(self.memory)
637640
self._memory_deepcopy_done = True
638641

639642
# Default to the agent's model if no model is provided
@@ -2482,13 +2485,6 @@ def _handle_agent_run_paused_stream(
24822485
if not run_response.content:
24832486
run_response.content = self._get_paused_content(run_response)
24842487

2485-
# Save session to storage
2486-
self.write_to_storage(user_id=user_id, session_id=session_id)
2487-
# Log Agent Run
2488-
self._log_agent_run(user_id=user_id, session_id=session_id)
2489-
2490-
log_debug(f"Agent Run Paused: {run_response.run_id}", center=True, symbol="*")
2491-
24922488
# Save output to file if save_response_to_file is set
24932489
self.save_run_response_to_file(message=message, session_id=session_id)
24942490

@@ -2501,6 +2497,13 @@ def _handle_agent_run_paused_stream(
25012497
run_response,
25022498
)
25032499

2500+
# Save session to storage
2501+
self.write_to_storage(user_id=user_id, session_id=session_id)
2502+
# Log Agent Run
2503+
self._log_agent_run(user_id=user_id, session_id=session_id)
2504+
2505+
log_debug(f"Agent Run Paused: {run_response.run_id}", center=True, symbol="*")
2506+
25042507
def _convert_response_to_structured_format(self, run_response: RunResponse):
25052508
# Convert the response to the structured format if needed
25062509
if self.response_model is not None and not isinstance(run_response.content, self.response_model):
@@ -6192,7 +6195,8 @@ async def areason(self, run_messages: RunMessages) -> Any:
61926195

61936196
def _handle_event(self, event: RunResponseEvent, run_response: RunResponse):
61946197
# We only store events that are not run_response_content events
6195-
if self.store_events and event.event not in (self.events_to_skip or []):
6198+
events_to_skip = [event.value for event in self.events_to_skip] if self.events_to_skip else []
6199+
if self.store_events and event.event not in events_to_skip:
61966200
if run_response.events is None:
61976201
run_response.events = []
61986202
run_response.events.append(event)

libs/agno/agno/team/team.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ class Team:
267267
# Store the events from the Team
268268
store_events: bool = False
269269
# List of events to skip from the Team
270-
events_to_skip: Optional[List[str]] = None
270+
events_to_skip: Optional[List[Union[RunEvent, TeamRunEvent]]] = None
271271

272272
# Optional app ID. Indicates this team is part of an app.
273273
app_id: Optional[str] = None
@@ -347,7 +347,7 @@ def __init__(
347347
stream: Optional[bool] = None,
348348
stream_intermediate_steps: bool = False,
349349
store_events: bool = False,
350-
events_to_skip: Optional[List[str]] = None,
350+
events_to_skip: Optional[List[Union[RunEvent, TeamRunEvent]]] = None,
351351
stream_member_events: bool = True,
352352
debug_mode: bool = False,
353353
show_members_responses: bool = False,
@@ -433,10 +433,13 @@ def __init__(
433433
self.stream = stream
434434
self.stream_intermediate_steps = stream_intermediate_steps
435435
self.store_events = store_events
436-
self.events_to_skip = events_to_skip or [
437-
RunEvent.run_response_content.value,
438-
TeamRunEvent.run_response_content.value,
439-
]
436+
437+
self.events_to_skip = events_to_skip
438+
if self.events_to_skip is None:
439+
self.events_to_skip = [
440+
RunEvent.run_response_content,
441+
TeamRunEvent.run_response_content,
442+
]
440443
self.stream_member_events = stream_member_events
441444

442445
self.debug_mode = debug_mode
@@ -598,7 +601,8 @@ def initialize_team(self, session_id: Optional[str] = None) -> None:
598601
self.memory = Memory()
599602
elif not self._memory_deepcopy_done:
600603
# We store a copy of memory to ensure different team instances reference unique memory copy
601-
self.memory = deepcopy(self.memory)
604+
if isinstance(self.memory, Memory):
605+
self.memory = deepcopy(self.memory)
602606
self._memory_deepcopy_done = True
603607

604608
# Default to the team's model if no model is provided
@@ -2118,7 +2122,8 @@ def _get_response_format(self) -> Optional[Union[Dict, Type[BaseModel]]]:
21182122

21192123
def _handle_event(self, event: Union[RunResponseEvent, TeamRunResponseEvent], run_response: TeamRunResponse):
21202124
# We only store events that are not run_response_content events
2121-
if self.store_events and event.event not in (self.events_to_skip or []):
2125+
events_to_skip = [event.value for event in self.events_to_skip] if self.events_to_skip else []
2126+
if self.store_events and event.event not in events_to_skip:
21222127
if run_response.events is None:
21232128
run_response.events = []
21242129
run_response.events.append(event)

libs/agno/agno/vectordb/mongodb/mongodb.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import asyncio
22
import time
33
from typing import Any, Dict, List, Optional
4+
45
from bson import ObjectId
56

67
from agno.document import Document
@@ -808,7 +809,7 @@ def hybrid_search(
808809

809810
try:
810811
results = list(collection.aggregate(pipeline))
811-
812+
812813
docs = []
813814
for doc in results:
814815
# Convert ObjectIds to strings before creating Document
@@ -817,11 +818,10 @@ def hybrid_search(
817818
id=str(clean_doc["_id"]),
818819
name=clean_doc.get("name"),
819820
content=clean_doc["content"],
820-
meta_data={
821-
**clean_doc.get("meta_data", {}), "score": clean_doc.get("score", 0.0)},
821+
meta_data={**clean_doc.get("meta_data", {}), "score": clean_doc.get("score", 0.0)},
822822
)
823823
docs.append(document)
824-
824+
825825
log_info(f"Hybrid search completed. Found {len(docs)} documents.")
826826
return docs
827827
except errors.OperationFailure as e:

libs/agno/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "agno"
3-
version = "1.6.2"
3+
version = "1.6.3"
44
description = "Agno: a lightweight library for building Multi-Agent Systems"
55
requires-python = ">=3.7,<4"
66
readme = "README.md"

libs/agno/tests/integration/agent/test_event_streaming.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ def test_intermediate_steps_with_reasoning():
227227
assert events[RunEvent.reasoning_step][0].reasoning_content is not None
228228

229229

230-
def test_intermediate_steps_with_user_confirmation():
230+
def test_intermediate_steps_with_user_confirmation(agent_storage):
231231
"""Test that the agent streams events."""
232232

233233
@tool(requires_confirmation=True)
@@ -237,6 +237,8 @@ def get_the_weather(city: str):
237237
agent = Agent(
238238
model=OpenAIChat(id="gpt-4o-mini"),
239239
tools=[get_the_weather],
240+
storage=agent_storage,
241+
store_events=True,
240242
telemetry=False,
241243
monitoring=False,
242244
)
@@ -265,6 +267,13 @@ def get_the_weather(city: str):
265267
run_id = agent.run_response.run_id
266268
updated_tools[0].confirmed = True
267269

270+
# Check stored events
271+
stored_session = agent_storage.get_all_sessions()[0]
272+
assert stored_session.memory["runs"][0]["events"] is not None
273+
assert len(stored_session.memory["runs"][0]["events"]) == 2
274+
assert stored_session.memory["runs"][0]["events"][0]["event"] == RunEvent.run_started
275+
assert stored_session.memory["runs"][0]["events"][1]["event"] == RunEvent.run_paused
276+
268277
# Then we continue the run
269278
response_generator = agent.continue_run(
270279
run_id=run_id, updated_tools=updated_tools, stream=True, stream_intermediate_steps=True
@@ -297,6 +306,17 @@ def get_the_weather(city: str):
297306

298307
assert agent.run_response.is_paused is False
299308

309+
# Check stored events
310+
stored_session = agent_storage.get_all_sessions()[0]
311+
assert stored_session.memory["runs"][0]["events"] is not None
312+
assert len(stored_session.memory["runs"][0]["events"]) == 6
313+
assert stored_session.memory["runs"][0]["events"][0]["event"] == RunEvent.run_started
314+
assert stored_session.memory["runs"][0]["events"][1]["event"] == RunEvent.run_paused
315+
assert stored_session.memory["runs"][0]["events"][2]["event"] == RunEvent.run_continued
316+
assert stored_session.memory["runs"][0]["events"][3]["event"] == RunEvent.tool_call_started
317+
assert stored_session.memory["runs"][0]["events"][4]["event"] == RunEvent.tool_call_completed
318+
assert stored_session.memory["runs"][0]["events"][5]["event"] == RunEvent.run_completed
319+
300320

301321
def test_intermediate_steps_with_memory(agent_storage, memory):
302322
"""Test that the agent streams events."""

libs/agno/tests/integration/knowledge/test_arxiv_knowledge_base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ async def test_arxiv_knowledge_base_async_integration(setup_vector_db):
9797
search_knowledge=True,
9898
instructions=[
9999
"You are a helpful assistant that can answer questions.",
100-
"You can use the asearch_knowledge_base tool to search the knowledge base of journal articles for information.",
100+
"You can use the search_knowledge_base tool to search the knowledge base of journal articles for information.",
101101
],
102102
)
103103
response = await agent.arun("What are the key capabilities of GPT-3?", markdown=True)
@@ -108,7 +108,7 @@ async def test_arxiv_knowledge_base_async_integration(setup_vector_db):
108108
tool_calls.extend(msg.tool_calls)
109109

110110
function_calls = [call for call in tool_calls if call.get("type") == "function"]
111-
assert any(call["function"]["name"] == "asearch_knowledge_base" for call in function_calls)
111+
assert any(call["function"]["name"] == "search_knowledge_base" for call in function_calls)
112112

113113

114114
def test_arxiv_knowledge_base_empty_query_integration(setup_vector_db):

libs/agno/tests/integration/knowledge/test_csv_url_knowledge_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ async def test_csv_url_knowledge_base_async():
8181
search_knowledge=True,
8282
instructions=[
8383
"You are a helpful assistant that can answer questions.",
84-
"You can use the asearch_knowledge_base tool to search the knowledge base of CSVs for information.",
84+
"You can use the search_knowledge_base tool to search the knowledge base of CSVs for information.",
8585
],
8686
)
8787
response = await agent.arun("Which employees have salaries above 50000?", markdown=True)

libs/agno/tests/integration/knowledge/test_docx_knowledge_base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ async def test_docx_knowledge_base_async_directory(setup_vector_db):
112112
tool_calls.extend(msg.tool_calls)
113113

114114
function_calls = [call for call in tool_calls if call.get("type") == "function"]
115-
# For async operations, we use asearch_knowledge_base
116-
assert any(call["function"]["name"] == "asearch_knowledge_base" for call in function_calls)
115+
# For async operations, we use search_knowledge_base
116+
assert any(call["function"]["name"] == "search_knowledge_base" for call in function_calls)
117117

118118

119119
# for the one with new knowledge filter DX- filters at initialization

libs/agno/tests/integration/knowledge/test_firecrawl_knowledge_base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ async def test_firecrawl_knowledge_base_async_directory(setup_vector_db):
8787
tool_calls.extend(msg.tool_calls)
8888

8989
function_calls = [call for call in tool_calls if call.get("type") == "function"]
90-
assert any(call["function"]["name"] == "asearch_knowledge_base" for call in function_calls)
90+
assert any(call["function"]["name"] == "search_knowledge_base" for call in function_calls)
9191

9292

9393
@pytest.mark.skip(reason="Skipping firecrawl knowledge base tests")
@@ -109,7 +109,7 @@ async def test_firecrawl_knowledge_base_async_single_url(setup_vector_db):
109109
tool_calls.extend(msg.tool_calls)
110110

111111
function_calls = [call for call in tool_calls if call.get("type") == "function"]
112-
assert any(call["function"]["name"] == "asearch_knowledge_base" for call in function_calls)
112+
assert any(call["function"]["name"] == "search_knowledge_base" for call in function_calls)
113113

114114

115115
@pytest.mark.skip(reason="Skipping firecrawl knowledge base tests")

0 commit comments

Comments
 (0)