Skip to content

Commit 759a8dd

Browse files
authored
chore: Release 1.7.11 (#4217)
# Changelog ## New Features: - **In Memory Storage**: Added `InMemoryStorage` for a simpler storage option useful in rapid prototyping. - **Trafilatura Tools**: Added `TrafilaturaTools` SDK for web scraping and text extraction using the Trafilatura library, with 15 comprehensive cookbook examples. - **Qwen and Dashscope Native Models**: Use Qwen models via `DashScope` integration. ## Improvements: - **Async Support for Milvus Hybrid Search** - **Bedrock Files Support**: Now supporting `File` for compatible AWS Bedrock models. ## Bug Fixes: - **MarkdownKnowledgeBase Custom Chunking Strategy**: Fixed issues with setting a custom chunking strategy.
1 parent 509e20b commit 759a8dd

File tree

7 files changed

+59
-26
lines changed

7 files changed

+59
-26
lines changed

cookbook/agent_concepts/user_control_flows/external_tool_execution_async_responses.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@ def execute_shell_command(command: str) -> str:
2727
Returns:
2828
str: The output of the shell command
2929
"""
30-
if command.startswith("ls ") or command == "ls" or command.startswith("cat ") or command.startswith("head "):
30+
if (
31+
command.startswith("ls ")
32+
or command == "ls"
33+
or command.startswith("cat ")
34+
or command.startswith("head ")
35+
):
3136
return subprocess.check_output(command, shell=True).decode("utf-8")
3237
raise Exception(f"Unsupported command: {command}")
3338

@@ -41,7 +46,9 @@ def execute_shell_command(command: str) -> str:
4146
run_response = asyncio.run(agent.arun("What files do I have in my current directory?"))
4247

4348
# Keep executing externally-required tools until the run completes
44-
while run_response.is_paused and len(run_response.tools_awaiting_external_execution) > 0:
49+
while (
50+
run_response.is_paused and len(run_response.tools_awaiting_external_execution) > 0
51+
):
4552
for tool in run_response.tools_awaiting_external_execution:
4653
if tool.tool_name == execute_shell_command.name:
4754
print(f"Executing {tool.tool_name} with args {tool.tool_args} externally")
@@ -57,5 +64,3 @@ def execute_shell_command(command: str) -> str:
5764

5865
# Or for simple debug flow
5966
# agent.print_response("What files do I have in my current directory?")
60-
61-

libs/agno/agno/models/aws/bedrock.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
BEDROCK_SUPPORTED_VIDEO_FORMATS = ["mp4", "mov", "mkv", "webm", "flv", "mpeg", "mpg", "wmv", "three_gp"]
3232
BEDROCK_SUPPORTED_FILE_FORMATS = ["pdf", "csv", "doc", "docx", "xls", "xlsx", "html", "txt", "md"]
3333

34+
3435
@dataclass
3536
class AwsBedrock(Model):
3637
"""

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.7.10"
3+
version = "1.7.11"
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/models/anthropic/test_tool_use.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,5 +255,5 @@ def test_tool_call_list_parameters():
255255
tool_calls.extend(msg.tool_calls)
256256
for call in tool_calls:
257257
if call.get("type", "") == "function":
258-
assert call["function"]["name"] in ["get_contents", "exa_answer", "search_exa"]
258+
assert call["function"]["name"] in ["get_contents", "exa_answer", "search_exa", "find_similar"]
259259
assert response.content is not None

libs/agno/tests/integration/models/openai/responses/test_tool_use.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
from agno.agent import Agent, RunResponse # noqa
77
from agno.models.openai import OpenAIResponses
8-
from agno.tools.duckduckgo import DuckDuckGoTools
98
from agno.tools.exa import ExaTools
109
from agno.tools.yfinance import YFinanceTools
1110

@@ -153,22 +152,29 @@ def test_parallel_tool_calls():
153152

154153
def test_multiple_tool_calls():
155154
"""Test multiple different tool types with the responses API."""
155+
156+
def get_the_weather(city: str):
157+
return f"It is currently 70 degrees and cloudy in {city}"
158+
159+
def get_favourite_city():
160+
return "Tokyo"
161+
156162
agent = Agent(
157163
model=OpenAIResponses(id="gpt-4.1-mini"),
158-
tools=[YFinanceTools(cache_results=True), DuckDuckGoTools(cache_results=True)],
164+
tools=[get_the_weather, get_favourite_city],
159165
markdown=True,
160166
telemetry=False,
161167
monitoring=False,
162168
)
163169

164-
response = agent.run("Find the latest price of TSLA. Then, based on the price, find the latest news about it.")
170+
response = agent.run("Find my favourite city. Then, get the weather in that city.")
165171

166172
# Verify tool usage
167173
tool_calls = [msg.tool_calls for msg in response.messages if msg.tool_calls]
168-
assert len(tool_calls) >= 2 # At least two messages have tool calls
169-
assert sum(len(calls) for calls in tool_calls) >= 2 # Total of 2 tool calls made
174+
assert len(tool_calls) >= 2 # At least one message has tool calls
175+
assert sum(len(calls) for calls in tool_calls) >= 1 # Total of 1 tool calls made
170176
assert response.content is not None
171-
assert "TSLA" in response.content and "latest news" in response.content.lower()
177+
assert "Tokyo" in response.content and "70" in response.content
172178

173179

174180
def test_tool_call_custom_tool_no_parameters():

libs/agno/tests/integration/models/xai/test_basic.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,15 @@ def test_basic():
2929

3030

3131
def test_basic_stream():
32-
agent = Agent(model=xAI(id="grok-3-mini-fast"), markdown=True, telemetry=False, monitoring=False)
32+
agent = Agent(model=xAI(id="grok-3-mini"), markdown=True, telemetry=False, monitoring=False)
3333

3434
response_stream = agent.run("Share a 2 sentence horror story", stream=True)
3535

3636
# Verify it's an iterator
3737
assert hasattr(response_stream, "__iter__")
3838

39-
responses = list(response_stream)
40-
assert len(responses) > 0
41-
for response in responses:
42-
assert response.content is not None
39+
for response in response_stream:
40+
assert response.content is not None or response.reasoning_content is not None
4341

4442
_assert_metrics(agent.run_response)
4543

@@ -63,7 +61,7 @@ async def test_async_basic_stream():
6361
response_stream = await agent.arun("Share a 2 sentence horror story", stream=True)
6462

6563
async for response in response_stream:
66-
assert response.content is not None
64+
assert response.content is not None or response.reasoning_content is not None
6765

6866
_assert_metrics(agent.run_response)
6967

libs/agno/tests/unit/models/openai/test_openai_responses_id_handling.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from typing import Any, Dict, List, Optional
22

3+
from agno.models.base import MessageData
34
from agno.models.message import Message
45
from agno.models.openai.responses import OpenAIResponses
56
from agno.models.response import ModelResponse
6-
from agno.models.base import MessageData
77

88

99
class _FakeError:
@@ -47,7 +47,15 @@ def __init__(self, *, _id: str, call_id: Optional[str], name: str, arguments: st
4747

4848

4949
class _FakeStreamEvent:
50-
def __init__(self, *, type: str, item: Optional[_FakeStreamItem] = None, delta: str = "", response: Any = None, annotation: Any = None):
50+
def __init__(
51+
self,
52+
*,
53+
type: str,
54+
item: Optional[_FakeStreamItem] = None,
55+
delta: str = "",
56+
response: Any = None,
57+
annotation: Any = None,
58+
):
5159
self.type = type
5260
self.item = item
5361
self.delta = delta
@@ -66,15 +74,22 @@ def test_format_messages_maps_tool_output_fc_to_call_id():
6674
"id": "fc_abc123",
6775
"call_id": "call_def456",
6876
"type": "function",
69-
"function": {"name": "execute_shell_command", "arguments": "{\"command\": \"ls -la\"}"},
77+
"function": {"name": "execute_shell_command", "arguments": '{"command": "ls -la"}'},
7078
}
7179
],
7280
)
7381

7482
# Tool output referring to the fc_* id should be normalized to call_*
7583
tool_output = Message(role="tool", tool_call_id="fc_abc123", content="ok")
7684

77-
fm = model._format_messages(messages=[Message(role="system", content="s"), Message(role="user", content="u"), assistant_with_tool_call, tool_output])
85+
fm = model._format_messages(
86+
messages=[
87+
Message(role="system", content="s"),
88+
Message(role="user", content="u"),
89+
assistant_with_tool_call,
90+
tool_output,
91+
]
92+
)
7893

7994
# Expect one function_call and one function_call_output normalized
8095
fc_items = [x for x in fm if x.get("type") == "function_call"]
@@ -114,7 +129,10 @@ def test_process_stream_response_builds_tool_calls():
114129
stream_data = MessageData()
115130

116131
# Simulate function_call added and then completed
117-
added = _FakeStreamEvent(type="response.output_item.added", item=_FakeStreamItem(_id="fc_abc123", call_id="call_def456", name="execute", arguments="{}"))
132+
added = _FakeStreamEvent(
133+
type="response.output_item.added",
134+
item=_FakeStreamItem(_id="fc_abc123", call_id="call_def456", name="execute", arguments="{}"),
135+
)
118136
mr, tool_use = model._process_stream_response(added, assistant_message, stream_data, {})
119137
assert mr is None
120138

@@ -155,9 +173,14 @@ def test_reasoning_previous_response_skips_prior_function_call_items(monkeypatch
155173
],
156174
)
157175

158-
fm = model._format_messages(messages=[Message(role="system", content="s"), Message(role="user", content="u"), assistant_with_prev, assistant_with_tool_call])
176+
fm = model._format_messages(
177+
messages=[
178+
Message(role="system", content="s"),
179+
Message(role="user", content="u"),
180+
assistant_with_prev,
181+
assistant_with_tool_call,
182+
]
183+
)
159184

160185
# Expect no re-sent function_call when previous_response_id is present for reasoning models
161186
assert all(x.get("type") != "function_call" for x in fm)
162-
163-

0 commit comments

Comments
 (0)