Skip to content

tot_agent.config

Configuration dataclasses, environment variable loading, and runtime knobs.

Environment variables

Variable Default Description
ANTHROPIC_API_KEY Required. Your Anthropic API key
SITE_URL http://localhost:4321 Base URL of the target application
AGENT_MODEL claude-opus-4-5 Claude model ID
MAX_AGENT_STEPS 40 Per-run tool-call iteration ceiling
TOT_LOG_LEVEL INFO CLI default log level
TOT_LOG_FILE CLI default log file path

Browser timeout variables

Variable Default Description
NAVIGATION_TIMEOUT_MS 30000 Playwright page navigation timeout (ms)
ACTION_TIMEOUT_MS 10000 Playwright element action timeout (ms)
WAIT_FOR_ELEMENT_TIMEOUT_MS 15000 wait_for_selector timeout (ms)
PAGE_READY_TIMEOUT_MS 10000 wait_for_load_state timeout (ms)

Module reference

tot_agent.config

config.py — Configuration dataclasses, environment loading, and tuning knobs.

All runtime configuration is resolved once at import time from environment variables (via :mod:dotenv) with sensible defaults so the package works out-of-the-box for a standard local development setup.

Environment variables

ANTHROPIC_API_KEY Required. Your Anthropic API key. SITE_URL Base URL of the target web application. Defaults to http://localhost:4321. AGENT_MODEL Claude model identifier. Defaults to claude-opus-4-5. MAX_AGENT_STEPS Hard ceiling on tool-call iterations per agent run. Defaults to 40. NAVIGATION_TIMEOUT_MS Timeout for page navigation and route transitions. ACTION_TIMEOUT_MS Timeout for click/fill/select interactions. WAIT_FOR_ELEMENT_TIMEOUT_MS Timeout for explicit selector waits. PAGE_READY_TIMEOUT_MS Best-effort timeout for waiting on a new DOM-ready state after an action.

SIM_USERS = [SimUser('admin', 'admin123', 'Admin', voting_bias='random'), SimUser('alice', 'password1', 'Alice', voting_bias='prefers_illustrated'), SimUser('bob', 'password2', 'Bob', voting_bias='prefers_dark'), SimUser('carol', 'password3', 'Carol', voting_bias='prefers_bright'), SimUser('dave', 'password4', 'Dave', voting_bias='random')] module-attribute

Default roster of simulated users. Edit this list to match the accounts that exist (or that you will create) on your dev site.

SITE_URL = os.getenv('SITE_URL', 'http://localhost:4321') module-attribute

Base URL of the target application (e.g. http://localhost:4321).

ROUTES = {'login': '/login', 'tests': '/tests', 'create_test': '/tests/new', 'dashboard': '/'} module-attribute

Relative route paths. Override in a subclass or at runtime to match your application's URL structure.

ANTHROPIC_API_KEY = os.getenv('ANTHROPIC_API_KEY') module-attribute

Anthropic API key. Loaded from the ANTHROPIC_API_KEY environment variable. Raises :class:RuntimeError at agent construction time if unset.

AGENT_MODEL = os.getenv('AGENT_MODEL', 'claude-opus-4-5') module-attribute

Claude model ID used for the agentic loop.

MAX_AGENT_STEPS = int(os.getenv('MAX_AGENT_STEPS', '40')) module-attribute

Maximum number of tool-call iterations before the agent gives up.

NAVIGATION_TIMEOUT_MS = int(os.getenv('NAVIGATION_TIMEOUT_MS', '15000')) module-attribute

Navigation timeout in milliseconds.

ACTION_TIMEOUT_MS = int(os.getenv('ACTION_TIMEOUT_MS', '5000')) module-attribute

Default timeout for click/fill/select interactions in milliseconds.

WAIT_FOR_ELEMENT_TIMEOUT_MS = int(os.getenv('WAIT_FOR_ELEMENT_TIMEOUT_MS', '8000')) module-attribute

Default timeout for explicit selector waits in milliseconds.

PAGE_READY_TIMEOUT_MS = int(os.getenv('PAGE_READY_TIMEOUT_MS', '4000')) module-attribute

Best-effort wait time for DOM readiness after a form submission or click.

SCREENSHOT_WIDTH = 1280 module-attribute

Browser viewport width in pixels.

SCREENSHOT_HEIGHT = 900 module-attribute

Browser viewport height in pixels.

OPEN_LIBRARY_SEARCH_URL = 'https://openlibrary.org/search.json' module-attribute

Open Library JSON search endpoint.

OPEN_LIBRARY_COVER_URL = 'https://covers.openlibrary.org/b/id/{cover_id}-L.jpg' module-attribute

Template URL for Open Library cover images.

GOOGLE_BOOKS_URL = 'https://www.googleapis.com/books/v1/volumes' module-attribute

Google Books API volumes endpoint (no key required for low-volume search).

COVER_SEARCH_QUERIES = ['science fiction novel', 'fantasy epic', 'literary fiction', 'mystery thriller', 'horror', 'romance', 'historical fiction', 'biography'] module-attribute

Genre queries cycled when seeding test data with random book covers.

SimUser dataclass

A simulated user account used during automated testing.

Parameters:

Name Type Description Default
username str

Login username.

required
password str

Login password.

required
display_name str

Human-readable name shown in logs. Defaults to username when blank.

''
voting_bias str

Personality hint fed to the agent when casting votes. One of "random", "prefers_dark", "prefers_bright", or "prefers_illustrated".

'random'
Source code in src/tot_agent/config.py
@dataclass
class SimUser:
    """A simulated user account used during automated testing.

    :param str username: Login username.
    :param str password: Login password.
    :param str display_name: Human-readable name shown in logs.  Defaults to
        *username* when blank.
    :param str voting_bias: Personality hint fed to the agent when casting votes.
        One of ``"random"``, ``"prefers_dark"``, ``"prefers_bright"``, or
        ``"prefers_illustrated"``.
    """

    username: str
    password: str
    display_name: str = ""
    voting_bias: str = "random"

    def __post_init__(self) -> None:
        """Ensure *display_name* falls back to *username* when left blank."""
        if not self.display_name:
            self.display_name = self.username
        logger.debug("SimUser created: %s (bias=%s)", self.username, self.voting_bias)

    def __repr__(self) -> str:
        return (
            f"SimUser(username={self.username!r}, display_name={self.display_name!r}, "
            f"voting_bias={self.voting_bias!r})"
        )

__post_init__()

Ensure display_name falls back to username when left blank.

Source code in src/tot_agent/config.py
def __post_init__(self) -> None:
    """Ensure *display_name* falls back to *username* when left blank."""
    if not self.display_name:
        self.display_name = self.username
    logger.debug("SimUser created: %s (bias=%s)", self.username, self.voting_bias)

get_user(username)

Look up a :class:SimUser by username.

Parameters:

Name Type Description Default
username str

The username to search for.

required

Returns:

Type Description
SimUser | None

The matching :class:SimUser, or None if not found.

Source code in src/tot_agent/config.py
def get_user(username: str) -> SimUser | None:
    """Look up a :class:`SimUser` by username.

    :param str username: The username to search for.
    :returns: The matching :class:`SimUser`, or ``None`` if not found.
    :rtype: SimUser or None
    """
    for user in SIM_USERS:
        if user.username == username:
            return user
    logger.warning("User %r not found in SIM_USERS", username)
    return None