Repository: deepset-ai/haystack

Issue:
Unexpected HuggingFaceTEITextEmbedder behaviour
**Unexpected HuggingFaceTEITextEmbedder behaviour**

When using a local docker container running TEI the repsonse of HuggingFaceTEITextEmbedder is unexpected

**Unexpected behaviour**
The response is a List of List of float
```
text_to_embed = "I love pizza!"
text_embedder = HuggingFaceTEITextEmbedder(
model="intfloat/multilingual-e5-large-instruct",
   url=http://localhost:8080,
   token=<my-token>,
  )
print(text_embedder.run(text_to_embed))
# {'embedding': [ [ 0.017020374536514282, -0.023255806416273117, ... ] ]
 ```
This trigger error if connected to a retriever 
E.g. InMemoryEmbeddingRetriever accepts a query_embedding params as input.
Passing a List of List of float trigger this condition
```   
if len(query_embedding) == 0 or not isinstance(query_embedding[0], float):
            raise ValueError("query_embedding should be a non-empty list of floats.")

```

**Expected behavior**
The response should be a single List of float and not a List of List of float
```
text_to_embed = "I love pizza!"
text_embedder = HuggingFaceTEITextEmbedder(
model="intfloat/multilingual-e5-large-instruct",
   url=http://localhost:8080,
   token=<my-token>,
  )
print(text_embedder.run(text_to_embed))
# {'embedding': [ 0.017020374536514282, -0.023255806416273117, ... ] 
 ```

**Additional context**
Embedding model info
[Link to HF model repo](https://huggingface.co/intfloat/multilingual-e5-large-instruct)
```
{
  "model_id": "intfloat/multilingual-e5-large-instruct",
  "model_sha": null,
  "model_dtype": "float16",
  "model_type": {
    "embedding": {
      "pooling": "mean"
    }
  },
  "max_concurrent_requests": 512,
  "max_input_length": 512,
  "max_batch_tokens": 16384,
  "max_batch_requests": null,
  "max_client_batch_size": 32,
  "tokenization_workers": 64,
  "version": "1.1.0",
  "sha": "7b85d8c041dcd8d961b51c4d578a1da5639b712b",
  "docker_label": "sha-7b85d8c"
}
```

**Help request**
I don't know how to fix this. It is a problem of the model, TEI or Haystack?

I see that Haystack TEI call the HF client feature_extraction method
```
   embedding = self.client.feature_extraction(text=text_to_embed)
```
Here the docs Link of HF [docs](https://huggingface.co/docs/api-inference/detailed_parameters#feature-extraction-task)
I do not know if it is related to the issue or not

Hope to be detailed enough in the description of the problem



Discussion:

Resolving patch:
diff --git a/haystack/components/embedders/hugging_face_tei_document_embedder.py b/haystack/components/embedders/hugging_face_tei_document_embedder.py
--- a/haystack/components/embedders/hugging_face_tei_document_embedder.py
+++ b/haystack/components/embedders/hugging_face_tei_document_embedder.py
@@ -9,7 +9,7 @@
 from haystack.utils import Secret, deserialize_secrets_inplace
 from haystack.utils.hf import HFModelType, check_valid_model
 
-with LazyImport(message="Run 'pip install transformers'") as transformers_import:
+with LazyImport(message="Run 'pip install huggingface_hub'") as huggingface_hub_import:
     from huggingface_hub import InferenceClient
 
 logger = logging.getLogger(__name__)
@@ -79,7 +79,7 @@ def __init__(
         :param embedding_separator:
             Separator used to concatenate the meta fields to the Document text.
         """
-        transformers_import.check()
+        huggingface_hub_import.check()
 
         if url:
             r = urlparse(url)
diff --git a/haystack/components/embedders/hugging_face_tei_text_embedder.py b/haystack/components/embedders/hugging_face_tei_text_embedder.py
--- a/haystack/components/embedders/hugging_face_tei_text_embedder.py
+++ b/haystack/components/embedders/hugging_face_tei_text_embedder.py
@@ -6,7 +6,7 @@
 from haystack.utils import Secret, deserialize_secrets_inplace
 from haystack.utils.hf import HFModelType, check_valid_model
 
-with LazyImport(message="Run 'pip install transformers'") as transformers_import:
+with LazyImport(message="Run 'pip install huggingface_hub'") as huggingface_hub_import:
     from huggingface_hub import InferenceClient
 
 logger = logging.getLogger(__name__)
@@ -62,7 +62,7 @@ def __init__(
         :param suffix:
             A string to add at the end of each text.
         """
-        transformers_import.check()
+        huggingface_hub_import.check()
 
         if url:
             r = urlparse(url)
@@ -135,8 +135,8 @@ def run(self, text: str):
 
         text_to_embed = self.prefix + text + self.suffix
 
-        embedding = self.client.feature_extraction(text=text_to_embed)
+        embeddings = self.client.feature_extraction(text=[text_to_embed])
         # The client returns a numpy array
-        embedding = embedding.tolist()
+        embedding = embeddings.tolist()[0]
 
         return {"embedding": embedding}


Test patch:
diff --git a/test/components/embedders/test_hugging_face_tei_document_embedder.py b/test/components/embedders/test_hugging_face_tei_document_embedder.py
--- a/test/components/embedders/test_hugging_face_tei_document_embedder.py
+++ b/test/components/embedders/test_hugging_face_tei_document_embedder.py
@@ -3,10 +3,10 @@
 import numpy as np
 import pytest
 from huggingface_hub.utils import RepositoryNotFoundError
-from haystack.utils.auth import Secret
 
 from haystack.components.embedders.hugging_face_tei_document_embedder import HuggingFaceTEIDocumentEmbedder
 from haystack.dataclasses import Document
+from haystack.utils.auth import Secret
 
 
 @pytest.fixture
@@ -222,6 +222,29 @@ def test_run(self, mock_check_valid_model):
             assert len(doc.embedding) == 384
             assert all(isinstance(x, float) for x in doc.embedding)
 
+    @pytest.mark.flaky(reruns=5, reruns_delay=5)
+    @pytest.mark.integration
+    def test_run_inference_api_endpoint(self):
+        docs = [
+            Document(content="I love cheese", meta={"topic": "Cuisine"}),
+            Document(content="A transformer is a deep learning architecture", meta={"topic": "ML"}),
+        ]
+
+        embedder = HuggingFaceTEIDocumentEmbedder(
+            model="sentence-transformers/all-MiniLM-L6-v2", meta_fields_to_embed=["topic"], embedding_separator=" | "
+        )
+
+        result = embedder.run(documents=docs)
+        documents_with_embeddings = result["documents"]
+
+        assert isinstance(documents_with_embeddings, list)
+        assert len(documents_with_embeddings) == len(docs)
+        for doc in documents_with_embeddings:
+            assert isinstance(doc, Document)
+            assert isinstance(doc.embedding, list)
+            assert len(doc.embedding) == 384
+            assert all(isinstance(x, float) for x in doc.embedding)
+
     def test_run_custom_batch_size(self, mock_check_valid_model):
         docs = [
             Document(content="I love cheese", meta={"topic": "Cuisine"}),
diff --git a/test/components/embedders/test_hugging_face_tei_text_embedder.py b/test/components/embedders/test_hugging_face_tei_text_embedder.py
--- a/test/components/embedders/test_hugging_face_tei_text_embedder.py
+++ b/test/components/embedders/test_hugging_face_tei_text_embedder.py
@@ -3,9 +3,9 @@
 import numpy as np
 import pytest
 from huggingface_hub.utils import RepositoryNotFoundError
-from haystack.utils.auth import Secret
 
 from haystack.components.embedders.hugging_face_tei_text_embedder import HuggingFaceTEITextEmbedder
+from haystack.utils.auth import Secret
 
 
 @pytest.fixture
@@ -17,7 +17,7 @@ def mock_check_valid_model():
 
 
 def mock_embedding_generation(text, **kwargs):
-    response = np.random.rand(384)
+    response = np.array([np.random.rand(384) for i in range(len(text))])
     return response
 
 
@@ -107,7 +107,16 @@ def test_run(self, mock_check_valid_model):
 
             result = embedder.run(text="The food was delicious")
 
-            mock_embedding_patch.assert_called_once_with(text="prefix The food was delicious suffix")
+            mock_embedding_patch.assert_called_once_with(text=["prefix The food was delicious suffix"])
+
+        assert len(result["embedding"]) == 384
+        assert all(isinstance(x, float) for x in result["embedding"])
+
+    @pytest.mark.flaky(reruns=5, reruns_delay=5)
+    @pytest.mark.integration
+    def test_run_inference_api_endpoint(self):
+        embedder = HuggingFaceTEITextEmbedder(model="sentence-transformers/all-MiniLM-L6-v2")
+        result = embedder.run(text="The food was delicious")
 
         assert len(result["embedding"]) == 384
         assert all(isinstance(x, float) for x in result["embedding"])