-
Notifications
You must be signed in to change notification settings - Fork 199
feat: OpenRouterChatGenerator add integration tests for mixing Tool/Toolset
#2421
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
|
@@ -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) | ||
|
|
||
| # 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]) | ||
|
||
|
|
||
| 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): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
|
@@ -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]) | ||
|
||
|
|
||
| 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"} | ||
There was a problem hiding this comment.
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.