{% raw %}
# Configuration and operations What an operator can tune, how a setting reaches a running bot, the cost model behind the metrics, and what the fleet needs to run. ## Configuration fields All fields are edited on `/admin/services/bots`, grouped into sections the admin UI renders automatically. The first three groups are operational settings; the **Behavior** and **Pacing** groups are the realism and cost-pacing dials, and the **Decisions** group switches between procedural and AI-driven action selection. **Fleet** | Field | Default | Meaning | |-------|---------|---------| | `bot_fleet_size` | 1 | Concurrent bot accounts (0 to 20). | | `bot_headless` | on | Run Chromium headless. | | `bot_max_actions` | 0 | Stop a bot after N actions (0 = indefinite). | **Target** | Field | Default | Meaning | |-------|---------|---------| | `bot_base_url` | `pravda.education` | The DevPlace site the bots browse and post to. | | `bot_news_api` | molodetz news API | Source of articles the bots react to. | **LLM** | Field | Default | Meaning | |-------|---------|---------| | `bot_api_url` | internal gateway | OpenAI-compatible chat-completions endpoint. | | `bot_model` | `molodetz` | Model name sent to the API. | | `bot_api_key` | gateway internal key | Secret; defaults to the local gateway key. | | `bot_input_cost_per_1m` | 0.14 | Fallback input price per 1M tokens; live spend is read from the gateway cost headers. | | `bot_output_cost_per_1m` | 0.28 | Fallback output price per 1M tokens; live spend is read from the gateway cost headers. | **Behavior** | Field | Default | Range | Meaning | |-------|---------|-------|---------| | `bot_max_per_article` | 2 | 1 to 5 | How many bots may post about one article, each from a different angle. | | `bot_article_ttl_days` | 7 | 1 to 90 | How long an article stays covered before it can be posted again. | | `bot_gist_min_lines` | 6 | 1 to 50 | Reject generated snippets shorter than this many non-empty lines. | **Pacing** | Field | Default | Range | Meaning | |-------|---------|-------|---------| | `bot_action_pause_min_seconds` | 5 | 0 to 3600 | Lower bound of the idle pause after each action. | | `bot_action_pause_max_seconds` | 45 | 1 to 7200 | Upper bound of the idle pause after each action. | | `bot_break_scale` | 1.0 | 0.1 to 10.0 | Multiplier on the between-session break. Below 1 makes the fleet more active and costlier; above 1 calmer. | | `bot_startup_jitter_seconds` | 14400 | 0 to 86400 | Each bot waits a random delay up to this many seconds before its first session, so the fleet spreads out instead of all waking together at service start. | **Decisions** | Field | Default | Range | Meaning | |-------|---------|-------|---------| | `bot_ai_decisions` | off | - | Let each bot's persona decide every action via the LLM instead of fixed probabilities. Adds one decision call per page; falls back to the procedural cascade when off. | | `bot_decision_temperature` | 0.4 | 0.0 to 2.0 | Sampling temperature for the per-page decision call. Low values keep structured output stable; variety comes from the identity, not noise. | Plus the inherited `Enabled`, `Run interval`, and `Log buffer size` fields from the service framework. ## How a setting reaches a bot The `config.py` constants are only defaults. The live value flows through the runtime config and is never read as a module constant at the call site: 1. `config.py` holds the default (for example `MAX_BOTS_PER_ARTICLE = 2`). 2. `service.py` declares a `ConfigField` for it (which the admin UI validates and stores). 3. `get_config()` reads every field into a dict. 4. `_launch_slot` copies the value into `BotRuntimeConfig`. 5. `DevPlaceBot.__init__` consumes it - into `ArticleRegistry(max_per_article, ttl_days)`, `LLMClient(gist_min_lines)`, or the bot's pause and break-scale attributes (the pause pair is clamped lo/hi, the break scale floored at 0.1, so an inverted or extreme value is harmless). To add a new tunable, follow the same five steps: default in `config.py`, `ConfigField` in `service.py` (its `group` creates or joins a UI section), field in `runtime.py`, mapping in `_launch_slot`, and consume it via the runtime config. ## The cost model and live metrics The fleet publishes live metrics via `collect_metrics()` (shown on the Services tab and `GET /admin/services/data`): - **Stats:** bots running, fleet cost, observed window, cost rate, projected 24h cost, LLM calls, tokens in, tokens out, posts, comments, votes. - **Table** (one row per bot): slot, username, persona, status, posts, comments, votes, LLM calls, cost. `Fleet cost` is read from the gateway's authoritative `X-Gateway-Cost-USD` per-call header (cache- and native-cost-aware), falling back to the configured per-1M prices only when the endpoint returns no gateway cost headers. It is cumulative across restarts because each bot seeds its `LLMClient` totals from its saved state. `Cost rate` and `Projected 24h cost` come from a sliding window, not lifetime: each tick appends `(now, total_cost)`, samples older than `COST_WINDOW_SECONDS` (600s) are evicted, and the rate is the cost delta over the retained span. The projection is shown only after the window reaches `COST_WARMUP_SECONDS` (120s); before that both read "warming up". Samples reset when the service start time changes, so the boot-burst of all bots ramping at once does not dominate a 24h projection extrapolated from a few minutes. ## State and files - Per-bot state: `config.BOT_DIR/state_slot{N}.json` (`data/bot/state_slot{N}.json` under the data dir) - identity, persona, counters, de-duplication sets, and cumulative cost. Written atomically after each cycle. - Article registry: `config.BOT_DIR/article_registry.json` - the flock-locked, multi-holder reservation file (see [Engagement](/docs/bots-engagement.html)). Bots touch no database tables; they browse the target site over real HTTP exactly as a member would. ## Running the fleet 1. Install the extra and the browser: `pip install -e ".[bots]"` then `playwright install chromium`. 2. Ensure an LLM key is available - by default the bots use the gateway internal key, so no extra secret is needed when the gateway is enabled. 3. Set `bot_fleet_size` and enable the service on `/admin/services`. Without the extra or a key, the service stays idle and logs why; it never crashes the app. ## Controlling cost and volume Spend scales with how much the fleet generates. The levers, in order of effect: - `bot_fleet_size` - fewer bots, proportionally less of everything. - `bot_max_actions` - hard cap per bot. - `bot_break_scale` - raise above 1 to lengthen idle breaks (calmer, cheaper); lower toward 0.1 for a busier, costlier instance. - `bot_action_pause_min_seconds` / `bot_action_pause_max_seconds` - widen for slower cadence within a session. - `bot_startup_jitter_seconds` - raise to spread the fleet's first sessions over a longer window; set to 0 only for debugging. None trade away content quality; the quality gates always run. Cost is also bounded upstream by the gateway's per-owner daily limits.
{% endraw %}