fix: HuggingFaceAPIChatGenerator - make tool conversion compatible with huggingface_hub>=0.31.0 (#9354)

* fix: HuggingFaceAPIChatGenerator - make tool conversion compatible with huggingface_hub>=0.31.0

* relnote
This commit is contained in:
Stefano Fiorucci 2025-05-07 18:37:05 +02:00 committed by GitHub
parent de5c7ea3d2
commit 4b4b0f0041
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 83 additions and 28 deletions

View File

@ -80,6 +80,26 @@ def _convert_hfapi_tool_calls(hfapi_tool_calls: Optional[List["ChatCompletionOut
return tool_calls
def _convert_tools_to_hfapi_tools(
tools: Optional[Union[List[Tool], Toolset]],
) -> Optional[List["ChatCompletionInputTool"]]:
if not tools:
return None
# huggingface_hub<0.31.0 uses "arguments", huggingface_hub>=0.31.0 uses "parameters"
parameters_name = "arguments" if hasattr(ChatCompletionInputFunctionDefinition, "arguments") else "parameters"
hf_tools = []
for tool in tools:
hf_tools_args = {"name": tool.name, "description": tool.description, parameters_name: tool.parameters}
hf_tools.append(
ChatCompletionInputTool(function=ChatCompletionInputFunctionDefinition(**hf_tools_args), type="function")
)
return hf_tools
@component
class HuggingFaceAPIChatGenerator:
"""
@ -313,19 +333,11 @@ class HuggingFaceAPIChatGenerator:
if streaming_callback:
return self._run_streaming(formatted_messages, generation_kwargs, streaming_callback)
hf_tools = None
if tools:
if isinstance(tools, Toolset):
tools = list(tools)
hf_tools = [
ChatCompletionInputTool(
function=ChatCompletionInputFunctionDefinition(
name=tool.name, description=tool.description, arguments=tool.parameters
),
type="function",
)
for tool in tools
]
if tools and isinstance(tools, Toolset):
tools = list(tools)
hf_tools = _convert_tools_to_hfapi_tools(tools)
return self._run_non_streaming(formatted_messages, generation_kwargs, hf_tools)
@component.output_types(replies=List[ChatMessage])
@ -373,19 +385,11 @@ class HuggingFaceAPIChatGenerator:
if streaming_callback:
return await self._run_streaming_async(formatted_messages, generation_kwargs, streaming_callback)
hf_tools = None
if tools:
if isinstance(tools, Toolset):
tools = list(tools)
hf_tools = [
ChatCompletionInputTool(
function=ChatCompletionInputFunctionDefinition(
name=tool.name, description=tool.description, arguments=tool.parameters
),
type="function",
)
for tool in tools
]
if tools and isinstance(tools, Toolset):
tools = list(tools)
hf_tools = _convert_tools_to_hfapi_tools(tools)
return await self._run_non_streaming_async(formatted_messages, generation_kwargs, hf_tools)
def _run_streaming(

View File

@ -0,0 +1,7 @@
---
fixes:
- |
Make internal tool conversion in the HuggingFaceAPICompatibleChatGenerator compatible with huggingface_hub>=0.31.0.
In the huggingface_hub library, `arguments` attribute of `ChatCompletionInputFunctionDefinition` has been renamed to
`parameters`.
Our implementation is compatible with both the legacy version and the new one.

View File

@ -10,6 +10,7 @@ from haystack import Pipeline
from haystack.dataclasses import StreamingChunk
from haystack.utils.auth import Secret
from haystack.utils.hf import HFGenerationAPIType
from huggingface_hub import (
ChatCompletionOutput,
ChatCompletionOutputComplete,
@ -21,9 +22,14 @@ from huggingface_hub import (
ChatCompletionStreamOutputChoice,
ChatCompletionStreamOutputDelta,
)
from huggingface_hub.utils import RepositoryNotFoundError
from huggingface_hub.errors import RepositoryNotFoundError
from haystack.components.generators.chat.hugging_face_api import (
HuggingFaceAPIChatGenerator,
_convert_hfapi_tool_calls,
_convert_tools_to_hfapi_tools,
)
from haystack.components.generators.chat.hugging_face_api import HuggingFaceAPIChatGenerator, _convert_hfapi_tool_calls
from haystack.tools import Tool
from haystack.dataclasses import ChatMessage, ToolCall
from haystack.tools.toolset import Toolset
@ -980,3 +986,41 @@ class TestHuggingFaceAPIChatGenerator:
},
}
assert data["init_parameters"]["tools"] == expected_tools_data
def test_convert_tools_to_hfapi_tools(self):
assert _convert_tools_to_hfapi_tools(None) is None
assert _convert_tools_to_hfapi_tools([]) is None
tool = Tool(
name="weather",
description="useful to determine the weather in a given location",
parameters={"city": {"type": "string"}},
function=get_weather,
)
hf_tools = _convert_tools_to_hfapi_tools([tool])
assert len(hf_tools) == 1
assert hf_tools[0].type == "function"
assert hf_tools[0].function.name == "weather"
assert hf_tools[0].function.description == "useful to determine the weather in a given location"
assert hf_tools[0].function.parameters == {"city": {"type": "string"}}
def test_convert_tools_to_hfapi_tools_legacy(self):
# this satisfies the check hasattr(ChatCompletionInputFunctionDefinition, "arguments")
mock_class = MagicMock()
with patch(
"haystack.components.generators.chat.hugging_face_api.ChatCompletionInputFunctionDefinition", mock_class
):
tool = Tool(
name="weather",
description="useful to determine the weather in a given location",
parameters={"city": {"type": "string"}},
function=get_weather,
)
_convert_tools_to_hfapi_tools([tool])
mock_class.assert_called_once_with(
name="weather",
arguments={"city": {"type": "string"}},
description="useful to determine the weather in a given location",
)