Repository: deepset-ai/haystack
Pull request: feat: Configure max_retries and timeout for AzureOpenAIDocumentEmbedder
max_retries: if not set is read from the OPENAI_MAX_RETRIES env variable or set to 5.

timeout: if not set is read from the OPENAI_TIMEOUT env variable or set to 30.

### Related Issues

- fixes #7945 

### Proposed Changes:

 <!--- In case of a bug: Describe what caused the issue and how you solved it -->
 <!--- In case of a feature: Describe what did you add and how it works -->
 AzureOpenAIDocumentEmbedder now supports max_retries and timeout for initialization.
timeout can also be set using the env variable OPENAI_TIMEOUT. Default: 30
max_retries can also be set using the env variable OPENAI_MAX_RETRIES. Default: 5

### How did you test it?

<!-- unit tests, integration tests, manual verification, instructions for manual tests -->
Unit tests


### Notes for the reviewer

<!-- E.g. point out section where the reviewer  -->
There are a few other similar PRs for the issue. This is the 4/4 PR.

### Checklist

- I have read the [contributors guidelines](https://github.com/deepset-ai/haystack/blob/main/CONTRIBUTING.md) and the [code of conduct](https://github.com/deepset-ai/haystack/blob/main/code_of_conduct.txt)
- I have updated the related issue with new insights and changes
- I added unit tests and updated the docstrings
- I've used one of the [conventional commit types](https://www.conventionalcommits.org/en/v1.0.0/) for my PR title: `fix:`, `feat:`, `build:`, `chore:`, `ci:`, `docs:`, `style:`, `refactor:`, `perf:`, `test:`.
- I documented my code
- I ran [pre-commit hooks](https://github.com/deepset-ai/haystack/blob/main/CONTRIBUTING.md#installation) and fixed any issue


Problem statement:
Add `max_retries` and `timeout` params to all `AzureOpenAI` classes
**Is your feature request related to a problem? Please describe.**

Currently all `OpenAI` related classes (e.g. `OpenAIDocumentEmbedder`, `OpenAIChatGenerator`) can be initialised by setting `max_retries` and `timeout` params.

The corresponding `AzureOpenAI` don't always have the same params.

**Describe the solution you'd like**

It would be nice to have these params in the `AzureOpenAI` classes

**Describe alternatives you've considered**

Subclass `AzureOpenAI` and create custom components.

**Additional context**

cc @anakin87 :)

Hints from issue discussion:
Thanks @EdoardoAbatiTR!

I'm also opening this for contributions. In case anyone is interested, let's discuss it here.
Hey, 

I am new here. Can I assign this to myself? 
@nvzard feel free to work on this. Take a look at the [guidelines for contributors](https://github.com/deepset-ai/haystack/blob/main/CONTRIBUTING.md).

I would suggest creating one PR for each component to be changed.
E.g., start with [`AzureOpenAIGenerator`](https://github.com/deepset-ai/haystack/blob/main/haystack/components/generators/azure.py) and use [`OpenAIGenerator`](https://github.com/deepset-ai/haystack/blob/main/haystack/components/generators/openai.py) as an inspiration.
@anakin87 thanks for the references 😄 

I've made the [first PR](https://github.com/deepset-ai/haystack/pull/7983) for `AzureOpenAIGenerator`, let me know if I missed anything. 

Pending: `AzureOpenAIDocumentEmbedder`, `AzureOpenAITextEmbedder`, `AzureOpenAIChatGenerator`
Hi all, thank you for working on this :) 

I think this got closed automatically because there was the "Fixes" keyword the previous PR, but it should stay open until the other classes are also updated
Ok np @EdoardoAbatiTR,  what other classes need to be updated?
@vblagoje `AzureOpenAIDocumentEmbedder, AzureOpenAITextEmbedder, AzureOpenAIChatGenerator` are left. I'll open a separate PR for each.

I already have a PR for `AzureOpenAIChatGenerator` https://github.com/deepset-ai/haystack/pull/7988 and will open one for the rest of the classes as well.
If it's okay for everyone. Can I take a couple of classes?
I'm new to open-source, and would love to contribute.
@kanjikinsmoke I already have started work for the other classes. I think there are other issues available to work on: https://github.com/deepset-ai/haystack/issues?q=is%3Aopen+is%3Aissue+label%3A%22Contributions+wanted%21%22
@kanjikinsmoke we just talked this morning internally about few easy issues that you could potentially work on. I'll ping @julian-risch to get back to you soon

Commit message: feat: Configure max_retries & timeout for AzureOpenAIDocumentEmbedder

max_retries: if not set is read from the OPENAI_MAX_RETRIES
env variable or set to 5.

timeout: if not set is read from the OPENAI_TIMEOUT
env variable or set to 30.

Signed-off-by: Nitanshu Vashistha <nitanshu.vzard@gmail.com>

Patch to review:
diff --git a/haystack/components/embedders/azure_document_embedder.py b/haystack/components/embedders/azure_document_embedder.py
index 5488087832..c694775e77 100644
--- a/haystack/components/embedders/azure_document_embedder.py
+++ b/haystack/components/embedders/azure_document_embedder.py
@@ -33,7 +33,7 @@ class AzureOpenAIDocumentEmbedder:
     ```
     """
 
-    def __init__(
+    def __init__(  # noqa: PLR0913 (too-many-arguments)
         self,
         azure_endpoint: Optional[str] = None,
         api_version: Optional[str] = "2023-05-15",
@@ -48,6 +48,8 @@ def __init__(
         progress_bar: bool = True,
         meta_fields_to_embed: Optional[List[str]] = None,
         embedding_separator: str = "\n",
+        timeout: Optional[float] = None,
+        max_retries: Optional[int] = None,
     ):
         """
         Create an AzureOpenAIDocumentEmbedder component.
@@ -84,6 +86,10 @@ def __init__(
             List of meta fields that will be embedded along with the Document text.
         :param embedding_separator:
             Separator used to concatenate the meta fields to the Document text.
+        :param timeout: The timeout to be passed to the underlying `AzureOpenAI` client, if not set it is
+            inferred from the `OPENAI_TIMEOUT` environment variable or set to 30.
+        :param max_retries: Maximum retries to establish contact with AzureOpenAI if it returns an internal error,
+            if not set it is inferred from the `OPENAI_MAX_RETRIES` environment variable or set to 5.
         """
         # if not provided as a parameter, azure_endpoint is read from the env var AZURE_OPENAI_ENDPOINT
         azure_endpoint = azure_endpoint or os.environ.get("AZURE_OPENAI_ENDPOINT")
@@ -106,6 +112,8 @@ def __init__(
         self.progress_bar = progress_bar
         self.meta_fields_to_embed = meta_fields_to_embed or []
         self.embedding_separator = embedding_separator
+        self.timeout = timeout or float(os.environ.get("OPENAI_TIMEOUT", 30.0))
+        self.max_retries = max_retries or int(os.environ.get("OPENAI_MAX_RETRIES", 5))
 
         self._client = AzureOpenAI(
             api_version=api_version,
@@ -114,6 +122,8 @@ def __init__(
             api_key=api_key.resolve_value() if api_key is not None else None,
             azure_ad_token=azure_ad_token.resolve_value() if azure_ad_token is not None else None,
             organization=organization,
+            timeout=self.timeout,
+            max_retries=self.max_retries,
         )
 
     def _get_telemetry_data(self) -> Dict[str, Any]:
@@ -144,6 +154,8 @@ def to_dict(self) -> Dict[str, Any]:
             embedding_separator=self.embedding_separator,
             api_key=self.api_key.to_dict() if self.api_key is not None else None,
             azure_ad_token=self.azure_ad_token.to_dict() if self.azure_ad_token is not None else None,
+            timeout=self.timeout,
+            max_retries=self.max_retries,
         )
 
     @classmethod
diff --git a/releasenotes/notes/retries-and-timeout-for-AzureOpenAIDocumentEmbedder-006fd84204942e43.yaml b/releasenotes/notes/retries-and-timeout-for-AzureOpenAIDocumentEmbedder-006fd84204942e43.yaml
new file mode 100644
index 0000000000..b7b30002d4
--- /dev/null
+++ b/releasenotes/notes/retries-and-timeout-for-AzureOpenAIDocumentEmbedder-006fd84204942e43.yaml
@@ -0,0 +1,10 @@
+---
+enhancements:
+  - |
+    Add max_retries, timeout to AzureOpenAIDocumentEmbedder init.
+    AzureOpenAIDocumentEmbedder can now be initialised by setting max_retries.
+    If max_retries is not set, it is inferred from the `OPENAI_MAX_RETRIES`
+    environment variable or set to 5.
+    AzureOpenAIDocumentEmbedder can now be initialised by setting timeout.
+    If timeout is not set, it is inferred from the `OPENAI_TIMEOUT`
+    environment variable or set to 30.
diff --git a/test/components/embedders/test_azure_document_embedder.py b/test/components/embedders/test_azure_document_embedder.py
index 05f04bea69..f9663e612f 100644
--- a/test/components/embedders/test_azure_document_embedder.py
+++ b/test/components/embedders/test_azure_document_embedder.py
@@ -43,6 +43,8 @@ def test_to_dict(self, monkeypatch):
                 "progress_bar": True,
                 "meta_fields_to_embed": [],
                 "embedding_separator": "\n",
+                "max_retries": 5,
+                "timeout": 30.0,
             },
         }
 


Code review comments:
diff_hunk: @@ -84,6 +86,10 @@ def __init__(
             List of meta fields that will be embedded along with the Document text.
         :param embedding_separator:
             Separator used to concatenate the meta fields to the Document text.
+        :param timeout: The timeout to be passed to the underlying `AzureOpenAI` client, if not set it is
path: haystack/components/embedders/azure_document_embedder.py
text: @user1:
```suggestion
        :param timeout: The timeout in seconds to be passed to the underlying `AzureOpenAI` client, if not set it is
```

diff_hunk: @@ -84,6 +86,10 @@ def __init__(
             List of meta fields that will be embedded along with the Document text.
         :param embedding_separator:
             Separator used to concatenate the meta fields to the Document text.
+        :param timeout: The timeout to be passed to the underlying `AzureOpenAI` client, if not set it is
+            inferred from the `OPENAI_TIMEOUT` environment variable or set to 30.
+        :param max_retries: Maximum retries to establish contact with AzureOpenAI if it returns an internal error,
path: haystack/components/embedders/azure_document_embedder.py
text: @user1:
```suggestion
        :param max_retries: Maximum retries to establish a connection with AzureOpenAI if it returns an internal error,
```