def parse_arxiv_id(arxiv_id: str) -> tuple[str, str]:
    """Parse arxiv identifier into article_id and version_id.

    Args:
        arxiv_id: String containing arxiv identifier (with or without version)

    Returns:
        Tuple of (article_id, version_id), where version_id is empty string if not present

    Examples:
        >>> parse_arxiv_id("hep-th/9901001v1")
        ('hep-th/9901001', 'v1')
        >>> parse_arxiv_id("2308.07124v2")
        ('2308.07124', 'v2')
        >>> parse_arxiv_id("math.GT/0309136")
        ('math.GT/0309136', '')
    """
    pattern = r"""
        ^                   # Start of string
        (                   # Group 1 - article id
            (?:            # Non-capturing group for old/new style
                [\w.-]+/   # Old style: category (including dots) + /
                \d{7}      # Old style: 7 digits
                |          # OR
                \d{4}      # New style: 4 digits
                \.         # New style: decimal point
                \d{4,5}    # New style: 4-5 digits
            )
        )
        (?:               # Optional non-capturing group for version
            (v\d+)        # Group 2 - version number including 'v'
        )?                # Version is optional
        $                 # End of string
    """
    match = re.match(pattern, arxiv_id, re.VERBOSE)
    if not match:
        raise ValueError(f"Invalid arxiv identifier format: {arxiv_id}")

    article_id, version = match.groups()
    return article_id, version or ""


if __name__ == "__main__":
    print(parse_arxiv_id("1806.10215v4"))
    print(parse_arxiv_id("1501.00001v1"))
    print(parse_arxiv_id("math.GT/0309136"))
    print(parse_arxiv_id("cmp-lg/9404001"))
    print(parse_arxiv_id("cs/0411052"))
