def from_tags(self, tags: List[str]) -> List[Tuple[str, int, int]]:
        """Convert IOB or BILOU tags to spans.

        Overlapping spans are NOT supported!

        Args:
            tags: The IOB or BILOU tags.

        Returns:
            A list of spans.

        Raises:
            ValueError: If the list of tags has not the same length as the list of tokens,
                or tags are not in the IOB or BILOU format.
        """

        def get_prefix_and_entity(tag_str: str) -> Tuple[str, Optional[str]]:
            if tag_str == "O":
                return tag_str, None
            splits = tag_str.split("-")
            return splits[0], "-".join(splits[1:])

        if len(tags) != len(self.tokens):
            raise ValueError("The list of tags must have the same length as the list of tokens!")

        spans, start_idx = [], None
        for idx, tag in enumerate(tags):
            prefix, entity = get_prefix_and_entity(tag)

            if prefix == "O":
                continue

            if prefix == "U":
                start_idx, end_idx = self._token_to_char_idx[idx]
                spans.append((entity, start_idx, end_idx))
                start_idx = None
                continue

            if prefix == "L":
                # If no start prefix, we just assume "L" == "U":
                if start_idx is None:
                    start_idx, end_idx = self._token_to_char_idx[idx]
                else:
                    _, end_idx = self._token_to_char_idx[idx]
                spans.append((entity, start_idx, end_idx))
                start_idx = None
                continue

            if prefix == "B":
                start_idx, end_idx = self._token_to_char_idx[idx]
            elif prefix == "I":
                # If "B" is missing, we just assume "I" starts the span
                if start_idx is None:
                    start_idx = self._token_to_char_idx[idx][0]
                end_idx = self._token_to_char_idx[idx][1]
            else:
                raise ValueError("Tags are not in the IOB or BILOU format!")

            try:
                next_tag = tags[idx + 1]
            # Reached last tag, add span
            except IndexError:
                spans.append((entity, start_idx, end_idx))
                break

            next_prefix, next_entity = get_prefix_and_entity(next_tag)
            # span continues
            if next_prefix in ["I", "L"] and next_entity == entity:
                continue
            # span ends
            spans.append((entity, start_idx, end_idx))
            start_idx = None

        return spans
Convert IOB or BILOU tags to spans.

        Overlapping spans are NOT supported!

        Args:
            tags: The IOB or BILOU tags.

        Returns:
            A list of spans.

        Raises:
            ValueError: If the list of tags has not the same length as the list of tokens,
                or tags are not in the IOB or BILOU format.
        
argilla-v1/src/argilla_v1/utils/span_utils.py