Here is the refactored package structure. Each module adheres to the Single Responsibility Principle, uses relative imports, and maintains a clean public API.

### `queue/__init__.py`
```python
"""
Job Queue Package

Provides a modular job processing system with FIFO, priority, and retry capabilities.
"""

from .core import Job, JobQueue
from .priority import PriorityQueue
from .retry import process_with_retry

# Public API surface
__all__ = ["Job", "JobQueue", "PriorityQueue", "process_with_retry"]
```

### `queue/core.py`
```python
"""Core data structures and base queue implementation."""

from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Callable, Optional
from datetime import datetime
import uuid


@dataclass
class Job:
    """Represents a unit of work to be executed."""
    id: str = field(default_factory=lambda: uuid.uuid4().hex)
    task: Callable[..., Any] | None = None
    args: tuple = ()
    kwargs: dict[str, Any] = field(default_factory=dict)
    priority: int = 0
    status: str = "pending"
    retry_count: int = 0
    max_retries: int = 3
    created_at: datetime = field(default_factory=datetime.now)


class JobQueue:
    """Basic FIFO job queue implementation."""

    def __init__(self) -> None:
        self._queue: list[Job] = []

    def enqueue(self, job: Job) -> None:
        """Add a job to the end of the queue."""
        self._queue.append(job)

    def dequeue(self) -> Optional[Job]:
        """Remove and return the first job from the queue."""
        if not self._queue:
            return None
        return self._queue.pop(0)

    def size(self) -> int:
        """Return the number of jobs in the queue."""
        return len(self._queue)

    def is_empty(self) -> bool:
        """Check if the queue is empty."""
        return len(self._queue) == 0
```

### `queue/retry.py`
```python
"""Retry logic with exponential backoff."""

import time
from typing import Any
from .core import Job


def process_with_retry(job: Job) -> Any:
    """
    Execute a job with exponential backoff retry logic.
    
    Args:
        job: The Job instance to execute.
        
    Returns:
        The result of the job's task execution.
        
    Raises:
        Exception: The last exception encountered if all retries are exhausted.
    """
    if job.task is None:
        raise ValueError("Job must have a valid task to execute.")
        
    last_exception: Exception | None = None

    for attempt in range(job.max_retries + 1):
        try:
            job.status = "running"
            result = job.task(*job.args, **job.kwargs)
            job.status = "completed"
            return result
        except Exception as e:
            last_exception = e
            job.retry_count += 1
            job.status = "retrying"
            
            if attempt < job.max_retries:
                # Exponential backoff: 1s, 2s, 4s, 8s, ...
                wait_time = 2 ** attempt
                time.sleep(wait_time)
            else:
                job.status = "failed"
                raise last_exception
```

### `queue/priority.py`
```python
"""Priority queue implementation using a min-heap."""

import heapq
from typing import Optional
from .core import Job, JobQueue


class PriorityQueue(JobQueue):
    """Priority-based job queue using a min-heap."""

    def __init__(self) -> None:
        super().__init__()
        # Heap stores tuples of (priority, job_id, job)
        # job_id acts as a stable tie-breaker to preserve insertion order
        self._heap: list[tuple[int, str, Job]] = []

    def enqueue(self, job: Job) -> None:
        """Add a job to the heap based on its priority."""
        heapq.heappush(self._heap, (job.priority, job.id, job))

    def dequeue(self) -> Optional[Job]:
        """Remove and return the job with the highest priority (lowest number)."""
        if not self._heap:
            return None
        _, _, job = heapq.heappop(self._heap)
        return job

    def size(self) -> int:
        """Return the number of jobs in the queue."""
        return len(self._heap)

    def is_empty(self) -> bool:
        """Check if the queue is empty."""
        return len(self._heap) == 0
```

### Key Design Decisions:
1. **Single Responsibility**: `core.py` handles data modeling and the base FIFO queue. `retry.py` isolates execution & backoff logic. `priority.py` handles heap-based ordering.
2. **Type Safety**: Modern type hints (`|` union syntax, `Optional`, `Callable`) are used throughout with `from __future__ import annotations` for forward compatibility.
3. **Heap Tie-Breaking**: `PriorityQueue` uses `job.id` as a secondary sort key in the heap tuple to prevent `TypeError` when comparing jobs with equal priorities.
4. **Clean Public API**: `__init__.py` re-exports all public components while keeping implementation details encapsulated. Relative imports ensure the package works regardless of installation context.