huggingface-api-haystack 0.2.0__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. huggingface_api_haystack-0.4.0/CHANGELOG.md +35 -0
  2. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/PKG-INFO +1 -1
  3. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/pyproject.toml +1 -1
  4. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/src/haystack_integrations/components/embedders/huggingface_api/document_embedder.py +1 -1
  5. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/src/haystack_integrations/components/embedders/huggingface_api/text_embedder.py +1 -1
  6. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/src/haystack_integrations/components/generators/huggingface_api/chat/chat_generator.py +9 -6
  7. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/src/haystack_integrations/components/rankers/huggingface_api/ranker.py +9 -0
  8. huggingface_api_haystack-0.4.0/tests/conftest.py +39 -0
  9. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/tests/test_chat_generator.py +14 -53
  10. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/tests/test_document_embedder.py +1 -1
  11. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/tests/test_ranker.py +29 -15
  12. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/tests/test_text_embedder.py +1 -1
  13. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/tests/test_utils.py +4 -4
  14. huggingface_api_haystack-0.2.0/CHANGELOG.md +0 -9
  15. huggingface_api_haystack-0.2.0/tests/conftest.py +0 -27
  16. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/.gitignore +0 -0
  17. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/LICENSE.txt +0 -0
  18. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/README.md +0 -0
  19. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/pydoc/config_docusaurus.yml +0 -0
  20. {huggingface_api_haystack-0.2.0/src/haystack_integrations/components → huggingface_api_haystack-0.4.0/src/haystack_integrations}/common/huggingface_api/__init__.py +0 -0
  21. {huggingface_api_haystack-0.2.0/src/haystack_integrations/components → huggingface_api_haystack-0.4.0/src/haystack_integrations}/common/huggingface_api/utils.py +0 -0
  22. {huggingface_api_haystack-0.2.0/src/haystack_integrations/components → huggingface_api_haystack-0.4.0/src/haystack_integrations}/common/py.typed +0 -0
  23. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/src/haystack_integrations/components/embedders/huggingface_api/__init__.py +0 -0
  24. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/src/haystack_integrations/components/embedders/py.typed +0 -0
  25. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/src/haystack_integrations/components/generators/huggingface_api/__init__.py +0 -0
  26. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/src/haystack_integrations/components/generators/huggingface_api/chat/__init__.py +0 -0
  27. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/src/haystack_integrations/components/generators/py.typed +0 -0
  28. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/src/haystack_integrations/components/rankers/huggingface_api/__init__.py +0 -0
  29. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/src/haystack_integrations/components/rankers/py.typed +0 -0
  30. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/tests/__init__.py +0 -0
  31. {huggingface_api_haystack-0.2.0 → huggingface_api_haystack-0.4.0}/tests/test_files/apple.jpg +0 -0
@@ -0,0 +1,35 @@
1
+ # Changelog
2
+
3
+ ## [integrations/huggingface_api-v0.3.0] - 2026-06-26
4
+
5
+ ### 🐛 Bug Fixes
6
+
7
+ - Validate top_k in HuggingFaceTEIRanker (#3503)
8
+
9
+ ### 🧪 Testing
10
+
11
+ - Improve del_hf_env_vars fixture (#3428)
12
+
13
+ ### 🧹 Chores
14
+
15
+ - Improve consistency of integrations folder structure (#3430)
16
+
17
+
18
+ ## [integrations/huggingface_api-v0.2.0] - 2026-06-05
19
+
20
+ ### 🚀 Features
21
+
22
+ - Move HuggingFaceTEIRanker from Haystack (#3407)
23
+
24
+ ### 📚 Documentation
25
+
26
+ - Document raised exceptions in HF API components (#3406)
27
+
28
+
29
+ ## [integrations/huggingface_api-v0.1.0] - 2026-06-05
30
+
31
+ ### 🚀 Features
32
+
33
+ - Move HF API components to core integrations (#3403)
34
+
35
+ <!-- generated by git-cliff -->
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: huggingface-api-haystack
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Haystack integration for Hugging Face API
5
5
  Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/huggingface_api#readme
6
6
  Project-URL: Issues, https://github.com/deepset-ai/haystack-core-integrations/issues
@@ -68,7 +68,7 @@ integration = 'pytest -m "integration" {args:tests}'
68
68
  all = 'pytest {args:tests}'
69
69
  unit-cov-retry = 'pytest --cov=haystack_integrations --reruns 3 --reruns-delay 30 -x -m "not integration" {args:tests}'
70
70
  integration-cov-append-retry = 'pytest --cov=haystack_integrations --cov-append --reruns 3 --reruns-delay 30 -x -m "integration" {args:tests}'
71
- types = """mypy -p haystack_integrations.components.common.huggingface_api \
71
+ types = """mypy -p haystack_integrations.common.huggingface_api \
72
72
  -p haystack_integrations.components.embedders.huggingface_api \
73
73
  -p haystack_integrations.components.generators.huggingface_api \
74
74
  -p haystack_integrations.components.rankers.huggingface_api {args}"""
@@ -14,7 +14,7 @@ from haystack.utils.url_validation import is_valid_http_url
14
14
  from huggingface_hub import AsyncInferenceClient, InferenceClient
15
15
  from tqdm import tqdm
16
16
 
17
- from haystack_integrations.components.common.huggingface_api.utils import (
17
+ from haystack_integrations.common.huggingface_api.utils import (
18
18
  HFEmbeddingAPIType,
19
19
  HFModelType,
20
20
  _check_valid_model,
@@ -9,7 +9,7 @@ from haystack.utils import Secret
9
9
  from haystack.utils.url_validation import is_valid_http_url
10
10
  from huggingface_hub import AsyncInferenceClient, InferenceClient
11
11
 
12
- from haystack_integrations.components.common.huggingface_api.utils import (
12
+ from haystack_integrations.common.huggingface_api.utils import (
13
13
  HFEmbeddingAPIType,
14
14
  HFModelType,
15
15
  _check_valid_model,
@@ -2,6 +2,7 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
+ import inspect
5
6
  import json
6
7
  from collections.abc import AsyncIterable, Iterable
7
8
  from datetime import datetime, timezone
@@ -10,7 +11,6 @@ from typing import Any, Union
10
11
  from haystack import component, default_from_dict, default_to_dict, logging
11
12
  from haystack.components.generators.utils import _convert_streaming_chunks_to_chat_message, _normalize_messages
12
13
  from haystack.dataclasses import (
13
- AsyncStreamingCallbackT,
14
14
  ChatMessage,
15
15
  ComponentInfo,
16
16
  ReasoningContent,
@@ -45,7 +45,7 @@ from huggingface_hub import (
45
45
  InferenceClient,
46
46
  )
47
47
 
48
- from haystack_integrations.components.common.huggingface_api.utils import (
48
+ from haystack_integrations.common.huggingface_api.utils import (
49
49
  HFGenerationAPIType,
50
50
  HFModelType,
51
51
  _check_valid_model,
@@ -270,7 +270,7 @@ class HuggingFaceAPIChatGenerator:
270
270
  from haystack_integrations.components.generators.huggingface_api import HuggingFaceAPIChatGenerator
271
271
  from haystack.dataclasses import ChatMessage
272
272
  from haystack.utils import Secret
273
- from haystack_integrations.components.common.huggingface_api.utils import HFGenerationAPIType
273
+ from haystack_integrations.common.huggingface_api.utils import HFGenerationAPIType
274
274
 
275
275
  messages = [ChatMessage.from_system("\\nYou are a helpful, respectful and honest assistant"),
276
276
  ChatMessage.from_user("What's Natural Language Processing?")]
@@ -294,7 +294,7 @@ class HuggingFaceAPIChatGenerator:
294
294
  from haystack_integrations.components.generators.huggingface_api import HuggingFaceAPIChatGenerator
295
295
  from haystack.dataclasses import ChatMessage, ImageContent
296
296
  from haystack.utils import Secret
297
- from haystack_integrations.components.common.huggingface_api.utils import HFGenerationAPIType
297
+ from haystack_integrations.common.huggingface_api.utils import HFGenerationAPIType
298
298
 
299
299
  # Create an image from file path, URL, or base64
300
300
  image = ImageContent.from_file_path("path/to/your/image.jpg")
@@ -680,7 +680,7 @@ class HuggingFaceAPIChatGenerator:
680
680
  self,
681
681
  messages: list[dict[str, str]],
682
682
  generation_kwargs: dict[str, Any],
683
- streaming_callback: AsyncStreamingCallbackT,
683
+ streaming_callback: StreamingCallbackT,
684
684
  ) -> dict[str, list[ChatMessage]]:
685
685
  api_output: AsyncIterable[ChatCompletionStreamOutput] = await self._async_client.chat_completion(
686
686
  messages,
@@ -696,7 +696,10 @@ class HuggingFaceAPIChatGenerator:
696
696
  chunk=chunk, previous_chunks=streaming_chunks, component_info=component_info
697
697
  )
698
698
  streaming_chunks.append(stream_chunk)
699
- await streaming_callback(stream_chunk)
699
+ # sync callbacks are allowed in async contexts with Haystack >= 3.0, so only await async ones
700
+ callback_result = streaming_callback(stream_chunk)
701
+ if inspect.isawaitable(callback_result):
702
+ await callback_result
700
703
 
701
704
  message = _convert_streaming_chunks_to_chat_message(chunks=streaming_chunks)
702
705
  if message.meta.get("usage") is None:
@@ -93,6 +93,9 @@ class HuggingFaceTEIRanker:
93
93
  self.max_retries = max_retries
94
94
  self.retry_status_codes = retry_status_codes
95
95
  self.raw_scores = raw_scores
96
+ if top_k <= 0:
97
+ msg = f"top_k must be > 0, but got {top_k}"
98
+ raise ValueError(msg)
96
99
 
97
100
  def to_dict(self) -> dict[str, Any]:
98
101
  """
@@ -201,6 +204,9 @@ class HuggingFaceTEIRanker:
201
204
  # Return empty if no documents provided
202
205
  if not documents:
203
206
  return {"documents": []}
207
+ if top_k is not None and top_k <= 0:
208
+ msg = f"top_k must be > 0, but got {top_k}"
209
+ raise ValueError(msg)
204
210
 
205
211
  # Prepare the payload
206
212
  deduplicated_documents = _deduplicate_documents(documents)
@@ -265,6 +271,9 @@ class HuggingFaceTEIRanker:
265
271
  # Return empty if no documents provided
266
272
  if not documents:
267
273
  return {"documents": []}
274
+ if top_k is not None and top_k <= 0:
275
+ msg = f"top_k must be > 0, but got {top_k}"
276
+ raise ValueError(msg)
268
277
 
269
278
  # Prepare the payload
270
279
  deduplicated_documents = _deduplicate_documents(documents)
@@ -0,0 +1,39 @@
1
+ # SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import os
6
+ from pathlib import Path
7
+
8
+ import pytest
9
+
10
+
11
+ @pytest.fixture()
12
+ def test_files_path():
13
+ return Path(__file__).parent / "test_files"
14
+
15
+
16
+ @pytest.fixture()
17
+ def del_hf_env_vars_if_empty(monkeypatch):
18
+ """
19
+ Delete Hugging Face environment variables for tests if empty.
20
+
21
+ Prevents passing empty tokens to Hugging Face, which would cause API calls to fail.
22
+ This is particularly relevant for PRs opened from forks, where secrets are not available
23
+ and empty environment variables might be set instead of being removed.
24
+
25
+ See https://github.com/deepset-ai/haystack/issues/8811 for more details.
26
+ """
27
+ for var in ("HF_API_TOKEN", "HF_TOKEN"):
28
+ if not os.environ.get(var, "").strip():
29
+ monkeypatch.delenv(var, raising=False)
30
+
31
+
32
+ @pytest.fixture(autouse=True)
33
+ def allow_deserialization_of_test_modules(monkeypatch):
34
+ """
35
+ haystack-ai >= 3.0 refuses to deserialize classes and callables from modules outside its
36
+ trusted-module allowlist. Tools and callbacks defined in the test modules live outside that
37
+ allowlist, so trust them explicitly; haystack-ai 2.x ignores this environment variable.
38
+ """
39
+ monkeypatch.setenv("HAYSTACK_DESERIALIZATION_ALLOWLIST", "tests,test_*")
@@ -28,7 +28,7 @@ from huggingface_hub import (
28
28
  )
29
29
  from huggingface_hub.errors import RepositoryNotFoundError
30
30
 
31
- from haystack_integrations.components.common.huggingface_api.utils import HFGenerationAPIType
31
+ from haystack_integrations.common.huggingface_api.utils import HFGenerationAPIType
32
32
  from haystack_integrations.components.generators.huggingface_api.chat.chat_generator import (
33
33
  HuggingFaceAPIChatGenerator,
34
34
  _convert_chat_completion_stream_output_to_streaming_chunk,
@@ -270,20 +270,10 @@ class TestHuggingFaceAPIChatGenerator:
270
270
  assert init_params["token"] == {"env_vars": ["HF_API_TOKEN", "HF_TOKEN"], "strict": False, "type": "env_var"}
271
271
  assert init_params["generation_kwargs"] == {"temperature": 0.6, "stop": ["stop", "words"], "max_tokens": 512}
272
272
  assert init_params["streaming_callback"] is None
273
- assert init_params["tools"] == [
274
- {
275
- "type": "haystack.tools.tool.Tool",
276
- "data": {
277
- "description": "description",
278
- "function": "builtins.print",
279
- "inputs_from_state": None,
280
- "name": "name",
281
- "outputs_to_state": None,
282
- "outputs_to_string": None,
283
- "parameters": {"x": {"type": "string"}},
284
- },
285
- }
286
- ]
273
+
274
+ # deserializing the serialized component must reproduce the original tool
275
+ loaded = HuggingFaceAPIChatGenerator.from_dict(result)
276
+ assert loaded.tools == [tool]
287
277
 
288
278
  def test_from_dict(self, mock_check_valid_model):
289
279
  tool = Tool(name="name", description="description", parameters={"x": {"type": "string"}}, function=print)
@@ -323,6 +313,11 @@ class TestHuggingFaceAPIChatGenerator:
323
313
  pipeline.add_component("generator", generator)
324
314
 
325
315
  pipeline_dict = pipeline.to_dict()
316
+
317
+ # the Tool serialization format is owned by haystack-ai and varies across its versions; the
318
+ # dumps/loads round-trip below covers the tools, so exclude them from the pinned-dict comparison
319
+ tools_entries = pipeline_dict["components"]["generator"]["init_parameters"].pop("tools")
320
+ assert len(tools_entries) == 1
326
321
  assert pipeline_dict == {
327
322
  "metadata": {},
328
323
  "max_runs_per_component": 100,
@@ -337,20 +332,6 @@ class TestHuggingFaceAPIChatGenerator:
337
332
  "token": {"type": "env_var", "env_vars": ["ENV_VAR"], "strict": False},
338
333
  "generation_kwargs": {"temperature": 0.6, "stop": ["stop", "words"], "max_tokens": 512},
339
334
  "streaming_callback": None,
340
- "tools": [
341
- {
342
- "type": "haystack.tools.tool.Tool",
343
- "data": {
344
- "inputs_from_state": None,
345
- "name": "name",
346
- "outputs_to_state": None,
347
- "outputs_to_string": None,
348
- "description": "description",
349
- "parameters": {"x": {"type": "string"}},
350
- "function": "builtins.print",
351
- },
352
- }
353
- ],
354
335
  },
355
336
  }
356
337
  },
@@ -1189,30 +1170,10 @@ class TestHuggingFaceAPIChatGenerator:
1189
1170
  )
1190
1171
  data = generator.to_dict()
1191
1172
 
1192
- expected_tools_data = {
1193
- "type": "haystack.tools.toolset.Toolset",
1194
- "data": {
1195
- "tools": [
1196
- {
1197
- "type": "haystack.tools.tool.Tool",
1198
- "data": {
1199
- "name": "weather",
1200
- "description": "useful to determine the weather in a given location",
1201
- "parameters": {
1202
- "type": "object",
1203
- "properties": {"city": {"type": "string"}},
1204
- "required": ["city"],
1205
- },
1206
- "function": "tests.test_chat_generator.get_weather",
1207
- "outputs_to_string": None,
1208
- "inputs_from_state": None,
1209
- "outputs_to_state": None,
1210
- },
1211
- }
1212
- ]
1213
- },
1214
- }
1215
- assert data["init_parameters"]["tools"] == expected_tools_data
1173
+ # deserializing the serialized component must reproduce the original toolset
1174
+ loaded = HuggingFaceAPIChatGenerator.from_dict(data)
1175
+ assert isinstance(loaded.tools, Toolset)
1176
+ assert list(loaded.tools) == list(toolset)
1216
1177
 
1217
1178
  def test_convert_tools_to_hfapi_tools(self):
1218
1179
  assert _convert_tools_to_hfapi_tools(None) is None
@@ -13,7 +13,7 @@ from haystack.utils.auth import Secret
13
13
  from huggingface_hub.utils import RepositoryNotFoundError
14
14
  from numpy import array
15
15
 
16
- from haystack_integrations.components.common.huggingface_api.utils import HFEmbeddingAPIType
16
+ from haystack_integrations.common.huggingface_api.utils import HFEmbeddingAPIType
17
17
  from haystack_integrations.components.embedders.huggingface_api import HuggingFaceAPIDocumentEmbedder
18
18
 
19
19
 
@@ -13,14 +13,14 @@ from haystack_integrations.components.rankers.huggingface_api import HuggingFace
13
13
 
14
14
 
15
15
  class TestHuggingFaceTEIRanker:
16
- def test_init(self, del_hf_env_vars):
16
+ def test_init(self, del_hf_env_vars_if_empty):
17
17
  """Test initialization with default and custom parameters"""
18
18
  # Default parameters
19
19
  ranker = HuggingFaceTEIRanker(url="https://api.my-tei-service.com")
20
20
  assert ranker.url == "https://api.my-tei-service.com"
21
21
  assert ranker.top_k == 10
22
22
  assert ranker.timeout == 30
23
- assert not ranker.token.resolve_value()
23
+ assert ranker.token == Secret.from_env_var(["HF_API_TOKEN", "HF_TOKEN"], strict=False)
24
24
  assert ranker.max_retries == 3
25
25
  assert ranker.retry_status_codes is None
26
26
 
@@ -41,7 +41,7 @@ class TestHuggingFaceTEIRanker:
41
41
  assert ranker.max_retries == 5
42
42
  assert ranker.retry_status_codes == [500, 502, 503]
43
43
 
44
- def test_to_dict(self, del_hf_env_vars):
44
+ def test_to_dict(self, del_hf_env_vars_if_empty):
45
45
  """Test serialization to dict with Secret token"""
46
46
  component = HuggingFaceTEIRanker(
47
47
  url="https://api.my-tei-service.com", top_k=5, timeout=30, max_retries=4, retry_status_codes=[500, 502]
@@ -60,7 +60,7 @@ class TestHuggingFaceTEIRanker:
60
60
  assert data["init_parameters"]["max_retries"] == 4
61
61
  assert data["init_parameters"]["retry_status_codes"] == [500, 502]
62
62
 
63
- def test_from_dict(self, del_hf_env_vars):
63
+ def test_from_dict(self, del_hf_env_vars_if_empty):
64
64
  """Test deserialization from dict with environment variable token"""
65
65
  data = {
66
66
  "type": "haystack_integrations.components.rankers.huggingface_api.ranker.HuggingFaceTEIRanker",
@@ -82,14 +82,28 @@ class TestHuggingFaceTEIRanker:
82
82
  assert component.max_retries == 4
83
83
  assert component.retry_status_codes == [500, 502]
84
84
 
85
- def test_empty_documents(self, del_hf_env_vars):
85
+ def test_empty_documents(self, del_hf_env_vars_if_empty):
86
86
  """Test that empty documents list returns empty result"""
87
87
  ranker = HuggingFaceTEIRanker(url="https://api.my-tei-service.com")
88
88
  result = ranker.run(query="test query", documents=[])
89
89
  assert result == {"documents": []}
90
90
 
91
+ def test_init_fail_with_zero_top_k(self):
92
+ with pytest.raises(ValueError, match="top_k must be > 0, but got 0"):
93
+ HuggingFaceTEIRanker(url="http://localhost:8080", top_k=0)
94
+
95
+ def test_init_fail_with_negative_top_k(self):
96
+ with pytest.raises(ValueError, match=r"top_k must be > 0, but got -5"):
97
+ HuggingFaceTEIRanker(url="http://localhost:8080", top_k=-5)
98
+
99
+ def test_run_fail_with_invalid_top_k_override(self):
100
+ ranker = HuggingFaceTEIRanker(url="http://localhost:8080")
101
+ docs = [Document(content="doc")]
102
+ with pytest.raises(ValueError, match="top_k must be > 0, but got 0"):
103
+ ranker.run(query="q", documents=docs, top_k=0)
104
+
91
105
  @patch("haystack_integrations.components.rankers.huggingface_api.ranker.request_with_retry")
92
- def test_run_with_mock(self, mock_request, del_hf_env_vars):
106
+ def test_run_with_mock(self, mock_request, del_hf_env_vars_if_empty):
93
107
  """Test run method with mocked API response"""
94
108
  # Setup mock response
95
109
  mock_response = MagicMock(spec=httpx.Response)
@@ -137,7 +151,7 @@ class TestHuggingFaceTEIRanker:
137
151
  assert result["documents"][2].score == 0.75
138
152
 
139
153
  @patch("haystack_integrations.components.rankers.huggingface_api.ranker.request_with_retry")
140
- def test_run_with_truncation_direction(self, mock_request, del_hf_env_vars):
154
+ def test_run_with_truncation_direction(self, mock_request, del_hf_env_vars_if_empty):
141
155
  """Test run method with truncation direction parameter"""
142
156
  # Setup mock response
143
157
  mock_response = MagicMock(spec=httpx.Response)
@@ -170,7 +184,7 @@ class TestHuggingFaceTEIRanker:
170
184
  )
171
185
 
172
186
  @patch("haystack_integrations.components.rankers.huggingface_api.ranker.request_with_retry")
173
- def test_run_with_custom_top_k(self, mock_request, del_hf_env_vars):
187
+ def test_run_with_custom_top_k(self, mock_request, del_hf_env_vars_if_empty):
174
188
  """Test run method with custom top_k parameter"""
175
189
  # Setup mock response with 5 documents
176
190
  mock_response = MagicMock(spec=httpx.Response)
@@ -207,7 +221,7 @@ class TestHuggingFaceTEIRanker:
207
221
  assert result["documents"][1].content == "Document 3"
208
222
 
209
223
  @patch("haystack_integrations.components.rankers.huggingface_api.ranker.request_with_retry")
210
- def test_run_deduplicates_documents(self, mock_request, del_hf_env_vars):
224
+ def test_run_deduplicates_documents(self, mock_request, del_hf_env_vars_if_empty):
211
225
  """Test that duplicate documents are removed before sending to the API."""
212
226
  mock_response = MagicMock(spec=httpx.Response)
213
227
  mock_response.json.return_value = [{"index": 1, "score": 0.9}, {"index": 0, "score": 0.2}]
@@ -228,7 +242,7 @@ class TestHuggingFaceTEIRanker:
228
242
  url="https://api.my-tei-service.com/rerank",
229
243
  json={"query": "test query", "texts": ["keep me", "unique"], "raw_scores": False},
230
244
  timeout=30,
231
- headers={},
245
+ headers={"Authorization": f"Bearer {ranker.token.resolve_value()}"} if ranker.token.resolve_value() else {},
232
246
  attempts=3,
233
247
  status_codes_to_retry=None,
234
248
  )
@@ -237,7 +251,7 @@ class TestHuggingFaceTEIRanker:
237
251
  assert result["documents"][1].content == "keep me"
238
252
 
239
253
  @patch("haystack_integrations.components.rankers.huggingface_api.ranker.request_with_retry")
240
- def test_error_handling(self, mock_request, del_hf_env_vars):
254
+ def test_error_handling(self, mock_request, del_hf_env_vars_if_empty):
241
255
  """Test error handling in the ranker"""
242
256
  # Setup mock response with error
243
257
  mock_response = MagicMock(spec=httpx.Response)
@@ -261,7 +275,7 @@ class TestHuggingFaceTEIRanker:
261
275
 
262
276
  @pytest.mark.asyncio
263
277
  @patch("haystack_integrations.components.rankers.huggingface_api.ranker.async_request_with_retry")
264
- async def test_run_async_with_mock(self, mock_request, del_hf_env_vars):
278
+ async def test_run_async_with_mock(self, mock_request, del_hf_env_vars_if_empty):
265
279
  """Test run_async method with mocked API response"""
266
280
  # Setup mock response
267
281
  mock_response = MagicMock(spec=httpx.Response)
@@ -310,7 +324,7 @@ class TestHuggingFaceTEIRanker:
310
324
 
311
325
  @pytest.mark.asyncio
312
326
  @patch("haystack_integrations.components.rankers.huggingface_api.ranker.async_request_with_retry")
313
- async def test_run_async_deduplicates_documents(self, mock_request, del_hf_env_vars):
327
+ async def test_run_async_deduplicates_documents(self, mock_request, del_hf_env_vars_if_empty):
314
328
  """Test that duplicate documents are removed before sending to the API."""
315
329
  mock_response = MagicMock(spec=httpx.Response)
316
330
  mock_response.json.return_value = [{"index": 1, "score": 0.9}, {"index": 0, "score": 0.2}]
@@ -331,7 +345,7 @@ class TestHuggingFaceTEIRanker:
331
345
  url="https://api.my-tei-service.com/rerank",
332
346
  json={"query": "test query", "texts": ["keep me", "unique"], "raw_scores": False},
333
347
  timeout=30,
334
- headers={},
348
+ headers={"Authorization": f"Bearer {ranker.token.resolve_value()}"} if ranker.token.resolve_value() else {},
335
349
  attempts=3,
336
350
  status_codes_to_retry=None,
337
351
  )
@@ -341,7 +355,7 @@ class TestHuggingFaceTEIRanker:
341
355
 
342
356
  @pytest.mark.asyncio
343
357
  @patch("haystack_integrations.components.rankers.huggingface_api.ranker.async_request_with_retry")
344
- async def test_run_async_empty_documents(self, mock_request, del_hf_env_vars):
358
+ async def test_run_async_empty_documents(self, mock_request, del_hf_env_vars_if_empty):
345
359
  """Test run_async with empty documents list"""
346
360
  ranker = HuggingFaceTEIRanker(url="https://api.my-tei-service.com")
347
361
  result = await ranker.run_async(query="test query", documents=[])
@@ -12,7 +12,7 @@ from haystack.utils.auth import Secret
12
12
  from huggingface_hub.utils import RepositoryNotFoundError
13
13
  from numpy import array
14
14
 
15
- from haystack_integrations.components.common.huggingface_api.utils import HFEmbeddingAPIType
15
+ from haystack_integrations.common.huggingface_api.utils import HFEmbeddingAPIType
16
16
  from haystack_integrations.components.embedders.huggingface_api import HuggingFaceAPITextEmbedder
17
17
 
18
18
 
@@ -7,7 +7,7 @@ from unittest.mock import MagicMock, patch
7
7
  import pytest
8
8
  from huggingface_hub.errors import RepositoryNotFoundError
9
9
 
10
- from haystack_integrations.components.common.huggingface_api.utils import (
10
+ from haystack_integrations.common.huggingface_api.utils import (
11
11
  HFEmbeddingAPIType,
12
12
  HFGenerationAPIType,
13
13
  HFModelType,
@@ -30,19 +30,19 @@ class TestAPITypes:
30
30
 
31
31
 
32
32
  class TestCheckValidModel:
33
- @patch("haystack_integrations.components.common.huggingface_api.utils.HfApi")
33
+ @patch("haystack_integrations.common.huggingface_api.utils.HfApi")
34
34
  def test_valid_model(self, mock_hf_api):
35
35
  mock_hf_api.return_value.model_info.return_value = MagicMock(pipeline_tag="feature-extraction")
36
36
  _check_valid_model("BAAI/bge-small-en-v1.5", HFModelType.EMBEDDING, token=None)
37
37
  mock_hf_api.return_value.model_info.assert_called_once_with("BAAI/bge-small-en-v1.5", token=None)
38
38
 
39
- @patch("haystack_integrations.components.common.huggingface_api.utils.HfApi")
39
+ @patch("haystack_integrations.common.huggingface_api.utils.HfApi")
40
40
  def test_wrong_model_type(self, mock_hf_api):
41
41
  mock_hf_api.return_value.model_info.return_value = MagicMock(pipeline_tag="text-generation")
42
42
  with pytest.raises(ValueError, match="not a embedding model"):
43
43
  _check_valid_model("microsoft/phi-2", HFModelType.EMBEDDING, token=None)
44
44
 
45
- @patch("haystack_integrations.components.common.huggingface_api.utils.HfApi")
45
+ @patch("haystack_integrations.common.huggingface_api.utils.HfApi")
46
46
  def test_model_not_found(self, mock_hf_api):
47
47
  mock_hf_api.return_value.model_info.side_effect = RepositoryNotFoundError("not found", response=MagicMock())
48
48
  with pytest.raises(ValueError, match="not found on HuggingFace Hub"):
@@ -1,9 +0,0 @@
1
- # Changelog
2
-
3
- ## [integrations/huggingface_api-v0.1.0] - 2026-06-05
4
-
5
- ### 🚀 Features
6
-
7
- - Move HF API components to core integrations (#3403)
8
-
9
- <!-- generated by git-cliff -->
@@ -1,27 +0,0 @@
1
- # SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2
- #
3
- # SPDX-License-Identifier: Apache-2.0
4
-
5
- from pathlib import Path
6
-
7
- import pytest
8
-
9
-
10
- @pytest.fixture()
11
- def test_files_path():
12
- return Path(__file__).parent / "test_files"
13
-
14
-
15
- @pytest.fixture()
16
- def del_hf_env_vars(monkeypatch):
17
- """
18
- Delete Hugging Face environment variables for tests.
19
-
20
- Prevents passing empty tokens to Hugging Face, which would cause API calls to fail.
21
- This is particularly relevant for PRs opened from forks, where secrets are not available
22
- and empty environment variables might be set instead of being removed.
23
-
24
- See https://github.com/deepset-ai/haystack/issues/8811 for more details.
25
- """
26
- monkeypatch.delenv("HF_API_TOKEN", raising=False)
27
- monkeypatch.delenv("HF_TOKEN", raising=False)