Repository: deepset-ai/haystack

Issue:
StopWordsCriteria doesn't compare the stop word token ids with the input ids in a continuous and sequential order 
**Describe the bug**
This is my code:
```python
from transformers import AutoTokenizer

stop_word = 'unambiguously'
default_model = "google/flan-t5-base"
tokenizer = AutoTokenizer.from_pretrained(default_model)
layer = HFLocalInvocationLayer(model_name_or_path=default_model)
result = layer.invoke(prompt="Generate a sentence `This is unrelated, but is ambiguously. So unambiguously.`", stop_words=[stop_word])
print(result)
```
And this is the output of the code above: 
```bash 
['This is unrelated, but is ambiguously']
```

This output is not correct, because it should stop at the place of my stop word `unambiguously`, meaning that the correct result should be `['This is unrelated, but is ambiguously. So']`

The reason is that `unambiguously`  has the tokens of  ['▁un', 'ambigu', 'ously'] and they do appear in the tokens of `This is unrelated, but is ambiguously" as can be seen below:

```python
for word in ['unambiguously', 'unrelated', 'ambiguously']:
    tokens = tokenizer.tokenize(word)
    print(tokens)
```

```bash
['▁un', 'ambigu', 'ously']
['▁un', 'related']
['▁', 'ambigu', 'ously']
```

If we look at the code, the bug is caused by [this ](https://github.com/deepset-ai/haystack/blob/main/haystack/nodes/prompt/invocation_layer/hugging_face.py#L54) line, because `torch.isin()` doesn't check whether the stop word token ids appear in a sequential order in the input ids. 


**To Reproduce**
use the script above

**FAQ Check**
- [ x ] Have you had a look at [our new FAQ page](https://docs.haystack.deepset.ai/docs/faq)?

**System:**
 - OS: 
 - GPU/CPU:
 - Haystack version (commit or version number): 
 - DocumentStore:
 - Reader:
 - Retriever:



Discussion:

Resolving patch:
diff --git a/haystack/nodes/prompt/invocation_layer/hugging_face.py b/haystack/nodes/prompt/invocation_layer/hugging_face.py
--- a/haystack/nodes/prompt/invocation_layer/hugging_face.py
+++ b/haystack/nodes/prompt/invocation_layer/hugging_face.py
@@ -51,8 +51,18 @@ def __init__(
             self.stop_words = encoded_stop_words.input_ids.to(device)
 
         def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor, **kwargs) -> bool:
-            stop_result = torch.isin(self.stop_words, input_ids[-1])
-            return any(all(stop_word) for stop_word in stop_result)
+            for stop_word in self.stop_words:
+                found_stop_word = self.is_stop_word_found(input_ids, stop_word)
+                if found_stop_word:
+                    return True
+            return False
+
+        def is_stop_word_found(self, generated_text_ids: torch.Tensor, stop_word: torch.Tensor) -> bool:
+            generated_text_ids = generated_text_ids[-1]
+            len_generated_text_ids = generated_text_ids.size(0)
+            len_stop_word = stop_word.size(0)
+            result = all(generated_text_ids[len_generated_text_ids - len_stop_word :].eq(stop_word))
+            return result
 
 
 class HFLocalInvocationLayer(PromptModelInvocationLayer):


Test patch:
diff --git a/test/prompt/invocation_layer/test_hugging_face.py b/test/prompt/invocation_layer/test_hugging_face.py
--- a/test/prompt/invocation_layer/test_hugging_face.py
+++ b/test/prompt/invocation_layer/test_hugging_face.py
@@ -468,6 +468,44 @@ def test_stop_words_multiple_token(stop_words: List[str]):
     assert "health" not in result[0]
 
 
+@pytest.mark.unit
+def test_stop_words_criteria():
+    """
+    Test that StopWordsCriteria will check stop word tokens in a continuous and sequential order
+    """
+    # input ids for "unambiguously"
+    stop_words_id = torch.tensor([[73, 24621, 11937]])
+
+    # input ids for "This is ambiguously, but is unrelated."
+    input_ids1 = torch.tensor([[100, 19, 24621, 11937, 6, 68, 19, 73, 3897, 5]])
+    # input ids for "This is unambiguously"
+    input_ids2 = torch.tensor([[100, 19, 73, 24621, 11937]])
+
+    # We used to implement stop words algorithm using the torch.isin function like this:
+    # `all(torch.isin(stop_words_id, input_ids1)[0])`
+    # However, this algorithm is not correct as it will return True for presence of "unambiguously" in input_ids1
+    # and True for presence of "unambiguously" in input_ids2. This is because the algorithm will check
+    # if the stop word tokens are present in the input_ids, but it does not check if the stop word tokens are
+    # present in a continuous/sequential order.
+
+    # In "This is ambiguously, but is unrelated." sentence the "un" token comes from "unrelated" and the
+    # "ambiguously" token comes from "ambiguously". The algorithm will return True for presence of
+    # "unambiguously" in input_ids1 which is not correct.
+
+    stop_words_criteria = StopWordsCriteria(tokenizer=Mock(), stop_words=["mock data"])
+    # because we are mocking the tokenizer, we need to set the stop words manually
+    stop_words_criteria.stop_words = stop_words_id
+
+    # this is the correct algorithm to check if the stop word tokens are present in a continuous and sequential order
+    # For the input_ids1, the stop word tokens are present BUT not in a continuous order
+    present_and_continuous = stop_words_criteria(input_ids1, scores=None)
+    assert not present_and_continuous
+
+    # For the input_ids2, the stop word tokens are both present and in a continuous order
+    present_and_continuous = stop_words_criteria(input_ids2, scores=None)
+    assert present_and_continuous
+
+
 @pytest.mark.integration
 @pytest.mark.parametrize("stop_words", [["Berlin"], ["Berlin", "Brandenburg"], ["Berlin", "Brandenburg", "Germany"]])
 def test_stop_words_not_being_found(stop_words: List[str]):