Repository: All-Hands-AI/OpenHands
File: enterprise/integrations/utils.py (Python)

Code before:
    mention_lower = mention.lower()

    pattern = re.escape(mention_lower)
    # Match mention that is not part of a larger word
    return bool(re.search(rf'(?:^|[^\w@]){pattern}(?![\w-])', text_lower))


def github_webhook_has_openhands_mention(text: str, inline_oh_label: str) -> bool:
    """Check if text contains an @openhands mention for GitHub webhook filtering.

    This is a convenience wrapper around has_exact_mention specifically for
    GitHub webhook early filtering.

    Args:
        text: The text to check (e.g., comment body)
        inline_oh_label: The inline OpenHands label (e.g., "@openhands" or "@openhands-exp")

    Returns:
        bool: True if the exact mention is found, False otherwise
    """
    if not text:
        return False
    return has_exact_mention(text, inline_oh_label)


def github_webhook_is_potentially_relevant(
    payload: dict,
    oh_label: str,
    inline_oh_label: str,
    enable_proactive_conversations: bool = False,
) -> bool:
    """Lightweight check to filter out irrelevant GitHub webhook events early.

    This function performs quick payload inspection WITHOUT any API calls
    or database access to filter out events that cannot possibly trigger
    OpenHands actions. Use this at the webhook handler level to avoid
    expensive downstream processing.

    Supported GitHub webhook events:
    - pull_request (action: closed) - for data collection
    - issues (action: labeled) - when labeled with openhands label
    - issue_comment (action: created) - when comment contains @openhands mention
    - pull_request_review_comment (action: created) - when comment contains @openhands mention
    - workflow_run (action: completed) - only if proactive conversations enabled

    Args:
        payload: The GitHub webhook payload dictionary
        oh_label: The OpenHands label for issues (e.g., "openhands" or "openhands-exp")
        inline_oh_label: The inline OpenHands mention (e.g., "@openhands" or "@openhands-exp")
        enable_proactive_conversations: Whether proactive conversation starters are enabled


Review comment (suggestion):
All these checks could be done early in `receive_message` method 

I'd like to avoid this since 

1. the match conditions should be defined in one place
2. the early return check can happen in one place


Right now it seems like we're duplicating logic, unless I'm missing something

Code after:
    mention_lower = mention.lower()

    pattern = re.escape(mention_lower)
    # Match mention that is not part of a larger word
    return bool(re.search(rf'(?:^|[^\w@]){pattern}(?![\w-])', text_lower))


def confirm_event_type(event: Event):
    return isinstance(event, AgentStateChangedObservation) and not (
        event.agent_state == AgentState.REJECTED
        or event.agent_state == AgentState.USER_CONFIRMED
        or event.agent_state == AgentState.USER_REJECTED
        or event.agent_state == AgentState.LOADING
        or event.agent_state == AgentState.RUNNING
    )


def get_readable_error_reason(reason: str):
    if reason == 'STATUS$ERROR_LLM_AUTHENTICATION':
        reason = 'Authentication with the LLM provider failed. Please check your API key or credentials'
    elif reason == 'STATUS$ERROR_LLM_SERVICE_UNAVAILABLE':
        reason = 'The LLM service is temporarily unavailable. Please try again later'
    elif reason == 'STATUS$ERROR_LLM_INTERNAL_SERVER_ERROR':
        reason = 'The LLM provider encountered an internal error. Please try again soon'
    elif reason == 'STATUS$ERROR_LLM_OUT_OF_CREDITS':
        reason = "You've run out of credits. Please top up to continue"
    elif reason == 'STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION':
        reason = 'Content policy violation. The output was blocked by content filtering policy'
    return reason


def get_summary_for_agent_state(
    observations: list[AgentStateChangedObservation], conversation_link: str
) -> str:
    unknown_error_msg = f'OpenHands encountered an unknown error. [See the conversation]({conversation_link}) for more information, or try again'

    if len(observations) == 0:
        logger.error(
            'Unknown error: No agent state observations found',
            extra={'conversation_link': conversation_link},
        )
        return unknown_error_msg

    observation: AgentStateChangedObservation = observations[0]
    state = observation.agent_state

    if state == AgentState.RATE_LIMITED:
        logger.warning(
            'Agent was rate limited',
            extra={
                'agent_state': state.value,