Repository: deepset-ai/haystack
Pull request: feat: allow `Generators` to run with a system prompt defined at run time
### Related Issues

- fixes [#8394](https://github.com/deepset-ai/haystack/issues/8394)

### Proposed Changes:

- added an extra optional parameter to the run method, `system_prompt`, which overwrites the value of `system_prompt` if set during initialisation
- changed a few methods in OpenGenerator to static methods, since they are not attached to any class properties
- removed an unused test, duplicate test function

### How did you test it?

- manual verification, integration tests and CI tests

### 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:
feat: Make system_prompt in Generators a runtime parameter as well
It'd be nice to be able to change the system prompt at run time to allow for easier experimentation when working with pipelines defined in yaml files. Otherwise it requires changing the yaml file to test changes rather than being able to change the call to the pipeline run method to insert a new system prompt at run time. 

I don't think this would be a breaking change since it would just add a new optional parameter to the run method of our generators.

Hints from issue discussion:
Hi @sjrl - here's a draft PR: https://github.com/deepset-ai/haystack/pull/8423/files

Let me know if this is what you are after. We then need to add it to every possible generator, to make it coherent.

Commit message: supporting the three use cases: no system prompt, using system prompt defined at init, using system prompt defined at run time

Patch to review:
diff --git a/haystack/components/generators/openai.py b/haystack/components/generators/openai.py
index 83f1a02d96..927dc3b80a 100644
--- a/haystack/components/generators/openai.py
+++ b/haystack/components/generators/openai.py
@@ -170,6 +170,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "OpenAIGenerator":
     def run(
         self,
         prompt: str,
+        run_time_system_prompt: Optional[str] = None,
         streaming_callback: Optional[Callable[[StreamingChunk], None]] = None,
         generation_kwargs: Optional[Dict[str, Any]] = None,
     ):
@@ -178,6 +179,9 @@ def run(
 
         :param prompt:
             The string prompt to use for text generation.
+        :param run_time_system_prompt:
+            The system prompt to use for text generation. If this run time system prompt is omitted, the system
+            prompt, if defined at initialisation time, is used.
         :param streaming_callback:
             A callback function that is called when a new token is received from the stream.
         :param generation_kwargs:
@@ -189,7 +193,9 @@ def run(
         for each response.
         """
         message = ChatMessage.from_user(prompt)
-        if self.system_prompt:
+        if run_time_system_prompt is not None:
+            messages = [ChatMessage.from_system(run_time_system_prompt), message]
+        elif self.system_prompt:
             messages = [ChatMessage.from_system(self.system_prompt), message]
         else:
             messages = [message]


Code review comments:
diff_hunk: @@ -170,6 +170,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "OpenAIGenerator":
     def run(
         self,
         prompt: str,
+        run_time_system_prompt: Optional[str] = None,
path: haystack/components/generators/openai.py
text: @user1:
hmm is it possible for us to leave the name as `system_prompt` and still keep the behavior you mention in the docs? 

I think it lines up better with our design of other components whose runtime variables optionally override the init ones. E.g. we do this regularly in our retrievers with the `top_k` parameter, but we still use the same name `top_k` both in the init and run methods.