Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from haystack.components.generators.utils import print_streaming_chunk
from haystack.components.tools import ToolInvoker
from haystack.dataclasses import ChatMessage, ChatRole, StreamingChunk, ToolCall
from haystack.tools import Tool
from haystack.tools import Tool, Toolset
from haystack.utils.auth import Secret
from openai import OpenAIError
from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletionMessage
Expand Down Expand Up @@ -605,6 +605,67 @@ def test_live_run_with_response_format_pydantic_model(self, calendar_event_model
assert isinstance(msg["event_date"], str)
assert isinstance(msg["event_location"], str)

@pytest.mark.skipif(
not os.environ.get("OPENROUTER_API_KEY", None),
reason="Export an env var called OPENROUTER_API_KEY containing the OpenRouter API key to run this test.",
)
@pytest.mark.integration
def test_integration_mixing_tools_and_toolset(self):
"""Test mixing Tool list and Toolset at runtime."""

def weather_function(city: str) -> str:
"""Get weather information for a city."""
return f"Weather in {city}: 22°C, sunny"

def time_function(city: str) -> str:
"""Get current time in a city."""
return f"Current time in {city}: 14:30"

def echo_function(text: str) -> str:
"""Echo a text."""
return text

# Create tools
weather_tool = Tool(
name="weather",
description="Get weather information for a city",
parameters={"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
function=weather_function,
)

time_tool = Tool(
name="time",
description="Get current time in a city",
parameters={"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
function=time_function,
)

echo_tool = Tool(
name="echo",
description="Echo a text",
parameters={"type": "object", "properties": {"text": {"type": "string"}}, "required": ["text"]},
function=echo_function,
)

# Create Toolset with weather and time tools
toolset = Toolset([weather_tool, time_tool])

# Initialize with toolset
component = OpenRouterChatGenerator(tools=toolset)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vblagoje In the issue it says we are allowed to pass tool and toolsets in the same list.

# Pass echo_tool as a list at runtime - runtime tools should take precedence
messages = [ChatMessage.from_user("Echo this: Hello World")]
results = component.run(messages, tools=[echo_tool])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vblagoje we need to update this to test passing mixed list of tools and toolsets instead of overriding init tools.


assert len(results["replies"]) == 1
message = results["replies"][0]

# Should use echo_tool since it was passed at runtime
assert message.tool_calls is not None
tool_call = message.tool_calls[0]
assert tool_call.tool_name == "echo"
assert tool_call.arguments == {"text": "Hello World"}


class TestChatCompletionChunkConversion:
def test_handle_stream_response(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
ChatRole,
StreamingChunk,
)
from haystack.tools import Tool
from haystack.tools import Tool, Toolset
from openai import AsyncOpenAI
from openai.types.chat import ChatCompletion, ChatCompletionMessage
from openai.types.chat.chat_completion import Choice
Expand Down Expand Up @@ -262,3 +262,65 @@ async def callback(chunk: StreamingChunk):
assert tool_call.tool_name == "weather"
assert tool_call.arguments == {"city": "Paris"}
assert tool_message.meta["finish_reason"] == "tool_calls"

@pytest.mark.skipif(
not os.environ.get("OPENROUTER_API_KEY", None),
reason="Export an env var called OPENROUTER_API_KEY containing the OpenRouter API key to run this test.",
)
@pytest.mark.integration
@pytest.mark.asyncio
async def test_integration_mixing_tools_and_toolset_async(self):
"""Test mixing Tool list and Toolset at runtime in async mode."""

def weather_function(city: str) -> str:
"""Get weather information for a city."""
return f"Weather in {city}: 22°C, sunny"

def time_function(city: str) -> str:
"""Get current time in a city."""
return f"Current time in {city}: 14:30"

def echo_function(text: str) -> str:
"""Echo a text."""
return text

# Create tools
weather_tool = Tool(
name="weather",
description="Get weather information for a city",
parameters={"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
function=weather_function,
)

time_tool = Tool(
name="time",
description="Get current time in a city",
parameters={"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
function=time_function,
)

echo_tool = Tool(
name="echo",
description="Echo a text",
parameters={"type": "object", "properties": {"text": {"type": "string"}}, "required": ["text"]},
function=echo_function,
)

# Create Toolset with weather and time tools
toolset = Toolset([weather_tool, time_tool])

# Initialize with toolset
component = OpenRouterChatGenerator(tools=toolset)

# Pass echo_tool as a list at runtime - runtime tools should take precedence
messages = [ChatMessage.from_user("Echo this: Hello World")]
results = await component.run_async(messages, tools=[echo_tool])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vblagoje please update this as well like the above test

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Amnah199 - done


assert len(results["replies"]) == 1
message = results["replies"][0]

# Should use echo_tool since it was passed at runtime
assert message.tool_calls is not None
tool_call = message.tool_calls[0]
assert tool_call.tool_name == "echo"
assert tool_call.arguments == {"text": "Hello World"}
Loading