From 6ad016bdde84ff8f9c4909b0ea265e3524875bcf Mon Sep 17 00:00:00 2001 From: Dirk Brand Date: Thu, 20 Mar 2025 13:15:57 +0200 Subject: [PATCH 1/5] Bump version --- libs/agno/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/agno/pyproject.toml b/libs/agno/pyproject.toml index af909244f12..f8887a02b5b 100644 --- a/libs/agno/pyproject.toml +++ b/libs/agno/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "agno" -version = "1.1.13" +version = "1.1.14" description = "Agno: a lightweight framework for building multi-modal Agents" requires-python = ">=3.7,<4" readme = "README.md" From fc8fd14f6e1011906010a517acc315b4f6a38cb8 Mon Sep 17 00:00:00 2001 From: Dirk Brand Date: Thu, 20 Mar 2025 13:31:59 +0200 Subject: [PATCH 2/5] Update --- .github/workflows/test_on_release.yml | 1 + libs/agno/agno/models/deepinfra/deepinfra.py | 2 ++ libs/agno/agno/models/deepseek/deepseek.py | 3 +++ libs/agno/agno/models/openai/chat.py | 2 +- .../models/google/test_tool_use.py | 25 ------------------- .../integration/teams/test_collaborate.py | 3 ++- 6 files changed, 9 insertions(+), 27 deletions(-) diff --git a/.github/workflows/test_on_release.yml b/.github/workflows/test_on_release.yml index 649924ef009..bc1dc5c1959 100644 --- a/.github/workflows/test_on_release.yml +++ b/.github/workflows/test_on_release.yml @@ -493,6 +493,7 @@ jobs: python -m pytest ./libs/agno/tests/integration/models/xai test-ibm-watsonx: + if: false # Our account is not working runs-on: ubuntu-latest strategy: matrix: diff --git a/libs/agno/agno/models/deepinfra/deepinfra.py b/libs/agno/agno/models/deepinfra/deepinfra.py index 63ceb63140d..426cf290681 100644 --- a/libs/agno/agno/models/deepinfra/deepinfra.py +++ b/libs/agno/agno/models/deepinfra/deepinfra.py @@ -26,3 +26,5 @@ class DeepInfra(OpenAILike): api_key: Optional[str] = getenv("DEEPINFRA_API_KEY", None) base_url: str = "https://api.deepinfra.com/v1/openai" + + supports_native_structured_outputs: bool = False diff --git a/libs/agno/agno/models/deepseek/deepseek.py b/libs/agno/agno/models/deepseek/deepseek.py index a6bd9bf6f1c..b706bcb9d32 100644 --- a/libs/agno/agno/models/deepseek/deepseek.py +++ b/libs/agno/agno/models/deepseek/deepseek.py @@ -19,3 +19,6 @@ class DeepSeek(OpenAILike): api_key: Optional[str] = getenv("DEEPSEEK_API_KEY", None) base_url: str = "https://api.deepseek.com" + + # Their support for structured outputs is currently broken + supports_native_structured_outputs: bool = False diff --git a/libs/agno/agno/models/openai/chat.py b/libs/agno/agno/models/openai/chat.py index dbb3abb4a7c..149b6e71181 100644 --- a/libs/agno/agno/models/openai/chat.py +++ b/libs/agno/agno/models/openai/chat.py @@ -317,7 +317,7 @@ def invoke(self, messages: List[Message]) -> Union[ChatCompletion, ParsedChatCom ) else: raise ValueError("response_format must be a subclass of BaseModel if structured_outputs=True") - + return self.get_client().chat.completions.create( model=self.id, messages=[self._format_message(m) for m in messages], # type: ignore diff --git a/libs/agno/tests/integration/models/google/test_tool_use.py b/libs/agno/tests/integration/models/google/test_tool_use.py index 605f2108ac7..9dbbb16c017 100644 --- a/libs/agno/tests/integration/models/google/test_tool_use.py +++ b/libs/agno/tests/integration/models/google/test_tool_use.py @@ -117,31 +117,6 @@ class StockPrice(BaseModel): tools=[YFinanceTools()], show_tool_calls=True, markdown=True, - exponential_backoff=True, - response_model=StockPrice, - structured_outputs=True, - telemetry=False, - monitoring=False, - ) - # Gemini does not support structured outputs for tool calls at this time - response = agent.run("What is the current price of TSLA?") - assert isinstance(response.content, StockPrice) - assert response.content is not None - assert response.content.price is not None - assert response.content.currency is not None - - -def test_tool_use_with_response_model(): - class StockPrice(BaseModel): - price: float = Field(..., description="The price of the stock") - currency: str = Field(..., description="The currency of the stock") - - agent = Agent( - model=Gemini(id="gemini-2.0-flash-exp"), - tools=[YFinanceTools()], - show_tool_calls=True, - markdown=True, - exponential_backoff=True, response_model=StockPrice, telemetry=False, monitoring=False, diff --git a/libs/agno/tests/integration/teams/test_collaborate.py b/libs/agno/tests/integration/teams/test_collaborate.py index e89d423cb8a..646da6d3561 100644 --- a/libs/agno/tests/integration/teams/test_collaborate.py +++ b/libs/agno/tests/integration/teams/test_collaborate.py @@ -35,7 +35,8 @@ def test_collaborate_team_basic(): assert response.content is not None assert isinstance(response.content, str) assert len(response.content) > 0 - assert len(response.tools) == 1 + tools = response.tools + assert len(tools) == 1 member_responses = response.member_responses assert len(member_responses) == 2 From 540002ded1f10e5322f7c796f2a15d74766fb798 Mon Sep 17 00:00:00 2001 From: Dirk Brand Date: Thu, 20 Mar 2025 14:29:19 +0200 Subject: [PATCH 3/5] Update --- .../openai/chat/reasoning/reasoning_effort.py | 1 + cookbook/models/openai/responses/__init__.py | 0 .../integration/models/groq/test_tool_use.py | 23 +++++++++++++++++++ .../models/openai/chat/test_tool_use.py | 3 ++- .../models/openai/responses/test_tool_use.py | 1 - 5 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 cookbook/models/openai/responses/__init__.py diff --git a/cookbook/models/openai/chat/reasoning/reasoning_effort.py b/cookbook/models/openai/chat/reasoning/reasoning_effort.py index 972ab41fbed..6c394d1f124 100644 --- a/cookbook/models/openai/chat/reasoning/reasoning_effort.py +++ b/cookbook/models/openai/chat/reasoning/reasoning_effort.py @@ -7,6 +7,7 @@ tools=[YFinanceTools(enable_all=True)], show_tool_calls=True, markdown=True, + debug_mode=True ) # Print the response in the terminal diff --git a/cookbook/models/openai/responses/__init__.py b/cookbook/models/openai/responses/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libs/agno/tests/integration/models/groq/test_tool_use.py b/libs/agno/tests/integration/models/groq/test_tool_use.py index dc6ac564007..1211fc2f7b9 100644 --- a/libs/agno/tests/integration/models/groq/test_tool_use.py +++ b/libs/agno/tests/integration/models/groq/test_tool_use.py @@ -1,6 +1,7 @@ from typing import Optional import pytest +from pydantic import BaseModel, Field from agno.agent import Agent, RunResponse # noqa from agno.models.groq import Groq @@ -126,6 +127,28 @@ def test_parallel_tool_calls(): assert "TSLA" in response.content and "AAPL" in response.content +@pytest.mark.skip(reason="Groq does not support native structured outputs for tool calls at this time.") +def test_tool_use_with_native_structured_outputs(): + class StockPrice(BaseModel): + price: float = Field(..., description="The price of the stock") + currency: str = Field(..., description="The currency of the stock") + + agent = Agent( + model=Groq(id="llama-3.3-70b-versatile"), + tools=[YFinanceTools()], + show_tool_calls=True, + markdown=True, + response_model=StockPrice, + telemetry=False, + monitoring=False, + ) + response = agent.run("What is the current price of TSLA?") + assert isinstance(response.content, StockPrice) + assert response.content is not None + assert response.content.price is not None + assert response.content.currency is not None + + def test_multiple_tool_calls(): agent = Agent( model=Groq(id="llama-3.3-70b-versatile"), diff --git a/libs/agno/tests/integration/models/openai/chat/test_tool_use.py b/libs/agno/tests/integration/models/openai/chat/test_tool_use.py index e3934fa4199..6fc1112871c 100644 --- a/libs/agno/tests/integration/models/openai/chat/test_tool_use.py +++ b/libs/agno/tests/integration/models/openai/chat/test_tool_use.py @@ -111,7 +111,8 @@ class StockPrice(BaseModel): show_tool_calls=True, markdown=True, response_model=StockPrice, - structured_outputs=True, + telemetry=False, + monitoring=False, ) response = agent.run("What is the current price of TSLA?") assert isinstance(response.content, StockPrice) diff --git a/libs/agno/tests/integration/models/openai/responses/test_tool_use.py b/libs/agno/tests/integration/models/openai/responses/test_tool_use.py index c90bb5ba981..0a149c1ac85 100644 --- a/libs/agno/tests/integration/models/openai/responses/test_tool_use.py +++ b/libs/agno/tests/integration/models/openai/responses/test_tool_use.py @@ -117,7 +117,6 @@ class StockPrice(BaseModel): show_tool_calls=True, markdown=True, response_model=StockPrice, - structured_outputs=True, telemetry=False, monitoring=False, ) From c695fe6b5aa39626ea81336dcdaa69f488f3cfaa Mon Sep 17 00:00:00 2001 From: Dirk Brand Date: Thu, 20 Mar 2025 15:36:24 +0200 Subject: [PATCH 4/5] Update --- cookbook/models/openai/__init__.py | 0 libs/agno/tests/integration/models/anthropic/test_tool_use.py | 2 +- libs/agno/tests/integration/models/litellm_openai/__init__.py | 0 libs/agno/tests/integration/tools/__init__.py | 0 4 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 cookbook/models/openai/__init__.py create mode 100644 libs/agno/tests/integration/models/litellm_openai/__init__.py create mode 100644 libs/agno/tests/integration/tools/__init__.py diff --git a/cookbook/models/openai/__init__.py b/cookbook/models/openai/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libs/agno/tests/integration/models/anthropic/test_tool_use.py b/libs/agno/tests/integration/models/anthropic/test_tool_use.py index f042be6e55c..23d6da54360 100644 --- a/libs/agno/tests/integration/models/anthropic/test_tool_use.py +++ b/libs/agno/tests/integration/models/anthropic/test_tool_use.py @@ -185,7 +185,7 @@ def get_the_weather_in_tokyo(): # Verify tool usage assert any(msg.tool_calls for msg in response.messages) assert response.content is not None - assert "70" in response.content + assert "Tokyo" in response.content def test_tool_call_custom_tool_optional_parameters(): diff --git a/libs/agno/tests/integration/models/litellm_openai/__init__.py b/libs/agno/tests/integration/models/litellm_openai/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libs/agno/tests/integration/tools/__init__.py b/libs/agno/tests/integration/tools/__init__.py new file mode 100644 index 00000000000..e69de29bb2d From 5813e4ae15d784f07470f8cac588f5aa28898a2c Mon Sep 17 00:00:00 2001 From: Dirk Brand Date: Thu, 20 Mar 2025 15:54:39 +0200 Subject: [PATCH 5/5] update --- cookbook/models/openai/chat/reasoning/reasoning_effort.py | 2 +- libs/agno/agno/models/openai/chat.py | 2 +- libs/agno/agno/tools/website.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cookbook/models/openai/chat/reasoning/reasoning_effort.py b/cookbook/models/openai/chat/reasoning/reasoning_effort.py index 6c394d1f124..0979194c73d 100644 --- a/cookbook/models/openai/chat/reasoning/reasoning_effort.py +++ b/cookbook/models/openai/chat/reasoning/reasoning_effort.py @@ -7,7 +7,7 @@ tools=[YFinanceTools(enable_all=True)], show_tool_calls=True, markdown=True, - debug_mode=True + debug_mode=True, ) # Print the response in the terminal diff --git a/libs/agno/agno/models/openai/chat.py b/libs/agno/agno/models/openai/chat.py index 149b6e71181..dbb3abb4a7c 100644 --- a/libs/agno/agno/models/openai/chat.py +++ b/libs/agno/agno/models/openai/chat.py @@ -317,7 +317,7 @@ def invoke(self, messages: List[Message]) -> Union[ChatCompletion, ParsedChatCom ) else: raise ValueError("response_format must be a subclass of BaseModel if structured_outputs=True") - + return self.get_client().chat.completions.create( model=self.id, messages=[self._format_message(m) for m in messages], # type: ignore diff --git a/libs/agno/agno/tools/website.py b/libs/agno/agno/tools/website.py index ced1a3e4307..747d1e723f8 100644 --- a/libs/agno/agno/tools/website.py +++ b/libs/agno/agno/tools/website.py @@ -1,5 +1,5 @@ import json -from typing import List, Optional, cast +from typing import List, Optional, cast, Union from agno.document import Document from agno.knowledge.combined import CombinedKnowledgeBase @@ -9,9 +9,9 @@ class WebsiteTools(Toolkit): - def __init__(self, knowledge_base: Optional[WebsiteKnowledgeBase | CombinedKnowledgeBase] = None): + def __init__(self, knowledge_base: Optional[Union[WebsiteKnowledgeBase, CombinedKnowledgeBase]] = None): super().__init__(name="website_tools") - self.knowledge_base: Optional[WebsiteKnowledgeBase | CombinedKnowledgeBase] = knowledge_base + self.knowledge_base: Optional[Union[WebsiteKnowledgeBase, CombinedKnowledgeBase]] = knowledge_base if self.knowledge_base is not None: if isinstance(self.knowledge_base, WebsiteKnowledgeBase):