from abc import ABC, abstractmethod
from dataclasses import dataclass

from harbor.models.metric import UsageInfo


@dataclass
class LLMResponse:
    """Response from an LLM call containing the generated content and metadata.

    Attributes:
        content: The generated text response
        usage: Token usage and cost information
        prompt_token_ids: Full prompt token IDs including conversation history (if collect_rollout_details=True)
        completion_token_ids: Token IDs for the generated completion (if collect_rollout_details=True)
        logprobs: Log probabilities for each completion token (if collect_rollout_details=True)
    """
    content: str
    usage: UsageInfo | None = None
    prompt_token_ids: list[int] | None = None
    completion_token_ids: list[int] | None = None
    logprobs: list[float] | None = None


class ContextLengthExceededError(Exception):
    """Raised when the LLM response indicates the context length was exceeded."""

    pass


class OutputLengthExceededError(Exception):
    """Raised when the LLM response was truncated due to max_tokens limit."""

    def __init__(self, message: str, truncated_response: str | None = None):
        super().__init__(message)
        self.truncated_response = truncated_response


class ParseError(Exception):
    """Raised when the LLM response cannot be parsed into the expected format."""

    pass


class BaseLLM(ABC):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    @abstractmethod
    async def call(self, prompt: str, **kwargs) -> LLMResponse:
        pass
