Repository: ErikBjare/gptme
File: gptme/tools/autocompact.py (Python)

Code before:
    """
    Get a unique backup conversation name, stripping any existing -before-compact suffixes.

    This ensures:
    - Backup names don't grow indefinitely with repeated compactions
    - Each backup has a unique suffix to prevent folder collisions

    Examples:
    - "my-conversation" -> "my-conversation-before-compact-a7x9"
    - "my-conversation-before-compact" -> "my-conversation-before-compact-k2p5"

    Args:
        conversation_name: The current conversation directory name

    Returns:
        The backup name with exactly one -before-compact suffix and a unique random suffix

    Raises:
        ValueError: If conversation_name is empty
    """
    if not conversation_name:
        raise ValueError("conversation name cannot be empty")

    base_name = conversation_name
    while base_name.endswith("-before-compact"):
        base_name = base_name.removesuffix("-before-compact")

    # Add unique suffix to prevent collisions
    import secrets

    random_suffix = secrets.token_hex(2)  # 4 hex chars
    return f"{base_name}-before-compact-{random_suffix}"


def autocompact_hook(log: list[Message], workspace: Path | None, manager=None):
    """
    Hook that checks if auto-compacting is needed and applies it.

    Runs after each message is processed to check if the conversation
    has grown too large with massive tool results.

    If compacting is needed:
    1. Forks the conversation to preserve original state
    2. Applies auto-compacting to current conversation
    3. Persists the compacted log
    """

    from ..llm.models import get_default_model
    from ..logmanager import Log
    from ..message import len_tokens


Review comment (suggestion):
This won't work, will lead to:

`"my-conversation-before-compact-a7x9" -> "my-conversation-before-compact-a7x9-before-compact"`

Code after:
    """
    Get a unique backup conversation name, stripping any existing -before-compact suffixes.

    This ensures:
    - Backup names don't grow indefinitely with repeated compactions
    - Each backup has a unique suffix to prevent folder collisions

    Examples:
    - "my-conversation" -> "my-conversation-before-compact-a7x9"
    - "my-conversation-before-compact" -> "my-conversation-before-compact-k2p5"

    Args:
        conversation_name: The current conversation directory name

    Returns:
        The backup name with exactly one -before-compact suffix and a unique random suffix

    Raises:
        ValueError: If conversation_name is empty
    """
    if not conversation_name:
        raise ValueError("conversation name cannot be empty")

    # Strip any existing backup suffixes: -before-compact or -before-compact-XXXX
    # Repeatedly strip from the end until no more suffixes found
    # This handles cases like:
    # - "conv-before-compact" -> "conv"
    # - "conv-before-compact-a7x9" -> "conv"
    # - "conv-before-compact-before-compact-before-compact" -> "conv"
    # But preserves names like "test-before-compaction" (not a backup suffix)
    import re

    base_name = conversation_name
    while True:
        new_name = re.sub(r"-before-compact(-[0-9a-f]{4})?$", "", base_name)
        if new_name == base_name:  # No more changes
            break
        base_name = new_name

    if not base_name:  # Safety: if entire name was the suffix (shouldn't happen)
        base_name = conversation_name

    # Add unique suffix to prevent collisions
    import secrets

    random_suffix = secrets.token_hex(2)  # 4 hex chars
    return f"{base_name}-before-compact-{random_suffix}"


def autocompact_hook(log: list[Message], workspace: Path | None, manager=None):
    """