Repository: deepset-ai/haystack

Issue:
__str__ and __repr__ not ideal in MultiLabel dataclass
**Describe the bug**
While running some code using haystack I stumbled upon an issue where I always had a list of seemingly empty dicts when getting aggregated labels from a document store. Turns out the issue is just that __repr__ and __str__ are broken on MultiLabel because the data class is missing annotations.

**Expected behavior**
I'm not sure but I think for `asdict()` to work we need annotations like in the other data classes. So MultiLabel should probably look like this:

```python
@dataclass
class MultiLabel:
    labels: List[Label]
    query: str
    document_ids: List[str]
    gold_offsets_in_contexts: List[Dict]
    gold_offsets_in_documents: List[Dict]
    answers: List[str]
    no_answer: bool
    document_ids: List[str]
    document_contents: List[str]

    def __init__(self,
                 labels: List[Label],
                 drop_negative_labels=False,
                 drop_no_answers=False
                 ):
#...
```

Additionally, I realized there are some inconsistencies between `Label` and `MultiLabel` and other data classes such as `Document` or `Answer`. While those have some nicely formatted output such as `"<Answer ... >"`, `Label` and `MultiLabel` simply have their dict versions as representations. This could be confusing when the underlying data class shows different behaviour from an actual dict.

**Additional context**
Add any other context about the problem here, like document types / preprocessing steps / settings of reader etc.

**To Reproduce**
Steps to reproduce the behavior

**FAQ Check**
- [ x] Have you had a look at [our new FAQ page](https://haystack.deepset.ai/overview/faq)?

**System:**
 - OS: 
 - GPU/CPU:
 - Haystack version (commit or version number): 
 - DocumentStore:
 - Reader:
 - Retriever:


Discussion:

Resolving patch:
diff --git a/haystack/schema.py b/haystack/schema.py
--- a/haystack/schema.py
+++ b/haystack/schema.py
@@ -481,11 +481,20 @@ def __repr__(self):
         return str(self.to_dict())
 
     def __str__(self):
-        return str(self.to_dict())
+        return f"<Label: {self.to_dict()}>"
 
 
 @dataclass
 class MultiLabel:
+    labels: List[Label]
+    query: str
+    answers: List[str]
+    no_answer: bool
+    document_ids: List[str]
+    document_contents: List[str]
+    gold_offsets_in_contexts: List[Dict]
+    gold_offsets_in_documents: List[Dict]
+
     def __init__(self, labels: List[Label], drop_negative_labels=False, drop_no_answers=False):
         """
         There are often multiple `Labels` associated with a single query. For example, there can be multiple annotated
@@ -499,7 +508,6 @@ def __init__(self, labels: List[Label], drop_negative_labels=False, drop_no_answ
         :param drop_negative_labels: Whether to drop negative labels from that group (e.g. thumbs down feedback from UI)
         :param drop_no_answers: Whether to drop labels that specify the answer is impossible
         """
-
         # drop duplicate labels and remove negative labels if needed.
         labels = list(set(labels))
         if drop_negative_labels:
@@ -588,7 +596,7 @@ def __repr__(self):
         return str(self.to_dict())
 
     def __str__(self):
-        return str(self.to_dict())
+        return f"<MultiLabel: {self.to_dict()}>"
 
 
 def _pydantic_dataclass_from_dict(dict: dict, pydantic_dataclass_type) -> Any:


Test patch: