Configuration is passed as Action inputs in GitHub Action mode and as environment variables in server modes.

LLM

Env var Action input Default Description
LLM_API_KEY llm_api_key required Bearer token for the LLM endpoint. In web mode, provider configs can supply per-repo keys.
LLM_API_BASE llm_api_base https://api.openai.com/v1 OpenAI-compatible API base. LLM_BASE_URL is also accepted as an env alias.
LLM_MODEL llm_model first model from /models Model identifier.
LLM_BILL_TO llm_bill_to unset Optional routing slug, used for Hugging Face Router requests.
LLM_MAX_TOKENS llm_max_tokens 4096 Maximum completion tokens.
LLM_STREAM llm_stream env default true, Action default false Consume streaming SSE responses.
LLM_REASONING_EFFORT none unset Optional reasoning_effort value passed through to providers that support it.
LLM_MAX_INPUT_TOKENS none 2000000 Hard cap on cumulative input tokens for a review. Set 0 to disable.

Review Behavior

Env var Action input Default Description
MENTION_TRIGGER mention_trigger @askserge Trigger phrase; must be the first word of the comment.
REVIEW_EVENT review_event COMMENT Fallback review event when the model omits one.
MAX_DIFF_CHARS max_diff_chars 200000 Maximum diff characters sent to the LLM.
REVIEW_RULES_PATH review_rules_path .ai/review-rules.md Rules file read from the target repo default branch.
DEFAULT_REVIEW_RULES default_review_rules general Python correctness and security rules Fallback when no rules file exists.
ALLOW_APPROVE none false Allows publishing APPROVE events in App/web mode.
PERSONA_HEADER none 🤗 **Serge** says: Prefix for failure comments and bot messages.
STAGING staging false Marks a non-production deployment. Published reviews then carry a note that they were posted from staging.

Context Compression

Opt-in compression of token-heavy context (tool outputs, older turns) before each LLM call, via the headroom-ai package. Install the extra with pip install '.[headroom]' (the Action pulls it in automatically when headroom_compress is on). It is a no-op if the package is missing or a compression call fails, so a review never breaks on it.

Env var Action input Default Description
HEADROOM_COMPRESS headroom_compress false Master switch.
HEADROOM_TARGET_RATIO headroom_target_ratio unset Keep-ratio for text compression (e.g. 0.5). Empty lets headroom decide.
HEADROOM_COMPRESS_USER_MESSAGES headroom_compress_user_messages false Also compress user messages (the annotated diff). Off keeps cited lines intact.
HEADROOM_COMPRESS_SYSTEM_MESSAGES headroom_compress_system_messages true Compress system messages.
HEADROOM_PROTECT_RECENT headroom_protect_recent 4 Never compress the last N messages.
HEADROOM_MIN_TOKENS headroom_min_tokens 250 Skip messages shorter than this many tokens.
HEADROOM_KOMPRESS_MODEL headroom_kompress_model unset Kompress model id, or disabled to skip ML compression.
HEADROOM_MODEL_LIMIT headroom_model_limit 200000 Model context window (tokens) used for sizing.

Repository Context and Tools

Env var Action input Default Description
CONTEXT_SCRIPT_PATH context_script_path .ai/context-script Optional executable context script.
CONTEXT_SCRIPT_TIMEOUT context_script_timeout 30 Seconds before the context script is ignored.
HELPER_TOOLS_PATH helper_tools_path .ai/review-tools.json Optional helper tool config.
REPO_CHECKOUT_PATH repo_checkout_path Action: github.workspace; env: empty Local checkout root for read-only tools. Empty disables tools.
TOOL_MAX_ITERATIONS tool_max_iterations env default 30, Action default 8 Maximum tool-calling rounds. Set 0 to disable the cap.
TOOL_REPEAT_LIMIT tool_repeat_limit 3 Byte-identical tool calls tolerated before the loop is cut off. Each repeat is told it is repeating, and a repeat that returned the same bytes is served as a pointer to the copy already in the transcript rather than a second copy. 0 disables.
TOOL_PATH_REVISIT_LIMIT tool_path_revisit_limit 3 Visits to one file/directory before every further visit is told what it already has. Catches the re-read-at-a-different-line-range shape TOOL_REPEAT_LIMIT cannot see. 0 disables the nudge.
TOOL_PATH_TRIP_AFTER tool_path_trip_after 40 Total re-opens across all paths before the loop is cut off. Set well above a healthy session. 0 keeps the nudges but never cuts off.

Project history (relore)

relore indexes a repository’s issue and pull-request history — issue bodies, PR descriptions, reviews and inline review comments — and serves it ranked, trust-tiered and aged. When it is configured, both the review loop and the task (ITF) loop gain five read-only tools: history_search, history_thread, history_why, history_inflight and history_copies. They answer the questions a checkout cannot: is this intentional, has anyone hit this, why is this line here, and — before a task writes anything — is somebody already fixing this. history_copies is the odd one out: it reads code rather than history, from the repository’s default branch, grouping every definition of a symbol by what the body does. It is for repositories that duplicate code on purpose — transformers’ per-model modeling_*.py files — where the question is never where a function is but which copy diverged.

serge shells out to the pinned relore client rather than calling its HTTP API, because the client carries three things worth not re-implementing: the client/daemon version handshake (a mismatch is refused, not answered), the server-side untrusted-content envelope around retrieved text, and the error vocabulary that tells a daemon that is down apart from an empty index. See reviewbot/relore_tool.py.

Env var Default Description
RELORE_API unset Base URL of the relore daemon. Unset = the history tools are not offered at all.
RELORE_REPOS unset Comma-separated OWNER/NAME list of the repositories that daemon indexes. The tools appear in the model’s schema only for a PR or task on one of these — on an unindexed repo every call returns empty and the agent spends turns finding that out. Keep it in step with what relore actually polls.
RELORE_TIMEOUT 45 Per-call wall clock (seconds) for the client subprocess.

Three things the deployment must get right:

  • The client is installed at image build time, pinned by the RELORE_REF build arg in Dockerfile and docker/Dockerfile.task-runner. It must name the commit the deployed daemon was built from: client and daemon must be the exact same version or every call is refused with 426. A task pod’s egress allowlist has no PyPI, so this can never be a runtime install.
  • Task pods reach the daemon through the egress proxy. Their NetworkPolicy allows egress only to serge-egress, serge’s callback and kube-dns, so the daemon’s host must be in taskExecution.kubernetes.egress.allowDomains. Without it every history call from a task pod times out and the agent silently loses the lens.
  • --repo is serge’s fact, not the model’s. It is set from the pull request or task being worked on and is not reachable from the tool schema, so a review of one repo can never quote decisions from another.

Retrieved text arrives inside relore’s <<<RELORE-UNTRUSTED>>> envelope with each quoted line prefixed >. serge relays it verbatim — it is data, never instructions, and the system prompt says so.

GitHub App

Env var Required for Description
GITHUB_APP_ID App/web publish Numeric GitHub App ID.
GITHUB_PRIVATE_KEY App/web publish Inline PEM private key. Literal \n sequences are expanded.
GITHUB_PRIVATE_KEY_PATH App/web publish Path to the PEM private key.
GITHUB_WEBHOOK_SECRET Webhook mode Webhook signing secret.
WEBHOOK_MAX_WORKERS reviewbot-app Concurrent webhook review workers. Default 2.

Web App

Env var Default Description
GITHUB_OAUTH_CLIENT_ID required unless DEV_NO_AUTH=1 GitHub OAuth client ID.
GITHUB_OAUTH_CLIENT_SECRET required unless DEV_NO_AUTH=1 GitHub OAuth client secret.
GITHUB_OAUTH_CALLBACK_URL optional Callback URL registered on the OAuth App.
WEB_SESSION_SECRET required unless DEV_NO_AUTH=1 Secret for signed session cookies.
WEB_ALLOWED_USERS unset Comma-separated GitHub logins allowed into the UI.
WEB_ALLOWED_ORG unset Comma-separated GitHub orgs allowed into the UI.
WEB_STORE_PATH jobs.db SQLite path.
WEB_JOB_RETENTION 25 Number of recent jobs to retain.
DEV_NO_AUTH false Disables OAuth for local development only.
WEB_INSECURE_COOKIES false Drops the Secure flag from session cookies.
WEB_CLONE_CACHE_DIR temp directory Shared clone cache path.
WEB_CLONE_CACHE_TTL_SECONDS 604800 Clone cache TTL.
WEB_CLONE_DEPTH 50 Shallow fetch depth.
WEB_GITHUB_APP_URL project default Install/configure URL shown in the web help page. Set this to your GitHub App URL for public deployments.

Tasks (write-capable)

The tasks flow is off by default. When enabled, it also needs the GitHub App to hold Contents: write + Pull Requests: write and a per-repo opt-in flag on the provider config.

Env var Default Description
TASK_API_ENABLED false Master switch for POST /tasks.
TASK_OIDC_ISSUER https://token.actions.githubusercontent.com OIDC issuer (override for GHES / self-hosted).
TASK_OIDC_AUDIENCE serge aud value the OIDC token must carry.
TASK_LLM_MAX_TOKENS unset Task-only completion-token cap. Unset means tasks use LLM_MAX_TOKENS; normal reviews are unchanged.
TASK_LLM_MAX_INPUT_TOKENS unset Task-only cumulative input-token cap. Unset means tasks use LLM_MAX_INPUT_TOKENS; normal reviews are unchanged.
TASK_TOOL_MAX_ITERATIONS unset Task-only tool-loop cap. Unset means tasks use TOOL_MAX_ITERATIONS; normal reviews are unchanged.
TASK_MAX_FOLLOWUPS 5 Max serge-authored commits per fix branch. 0 disables the cap.
TASK_MAX_WORKERS 2 Concurrent task workers (separate pool from reviews).

Normalize validation (in-loop)

Optionally validate each patch against the target repo’s own normalizer (e.g. make style && make fix-repo) inside the LLM loop: serge applies the patch to the worktree and runs the normalizer; if it fails, the error is fed back to the model so it corrects the patch (up to TASK_NORMALIZE_MAX_RETRIES times). On success the worktree already holds the applied + normalized result, so the opened PR is conformant at creation (no red repo-consistency CI, no follow-up commit). Opt-in — unset TASK_NORMALIZE_COMMAND and serge behaves exactly as before. See normalize validation for the full setup.

Env var Default Description
TASK_NORMALIZE_COMMAND unset Argv to run (shell-quoted, e.g. bash -lc 'make style && make fix-repo'). Unset disables validation. Operator/repo config — never request-supplied.
TASK_NORMALIZE_IMAGE unset Docker image (repo toolchain baked in) for the docker backend.
TASK_SANDBOX_BACKEND auto bwrap | docker | kubernetes | auto. auto = docker when an image is set and the docker CLI is present, else bwrap.
TASK_NORMALIZE_TIMEOUT 1800 Per-run timeout (seconds).
TASK_NORMALIZE_MEMORY unset Optional docker --memory cap (e.g. 4g).
TASK_NORMALIZE_MAX_RETRIES 2 How many times a normalizer rejection is fed back to the model for correction. 0 = validate once, no corrective re-prompts.
TASK_PREFLIGHT_COMMAND unset Argv run on the pristine checkout before any LLM work, to prove the gate is passable at all (e.g. bash -lc 'uv pip install -e . --system --no-deps && python -c "import transformers"'). A non-zero exit fails the task immediately as a broken gate. Unset disables the probe. Operator/repo config — never request-supplied.
TASK_PREFLIGHT_TIMEOUT 300 Per-run timeout (seconds) for the preflight probe.
TASK_RUNNER_TIMEOUT 3600 Wall-clock budget for one task pod. In kubernetes this is the Job’s activeDeadlineSeconds — a hard kill, not a request.
TASK_TAIL_RESERVE 0 Seconds of that budget held back so a finished patch can still be normalized and pushed; the agent loop stops when less is left (stop_reason="deadline"). 0 derives it as TASK_NORMALIZE_TIMEOUT + 180. See the runner’s wall-clock budget.
TASK_NORMALIZE_GUIDANCE unset Free-text policy injected into the task system prompt and the normalize-failure feedback (e.g. “prefer root-cause fixes over # noqa”). For anything the command itself can’t express.

Task fixes also read the repo’s own conventions file (REVIEW_RULES_PATH, default .ai/review-rules.md) straight from the checked-out branch and inject it into the patch-writing prompt — the same file the review flow uses. Point it at AGENTS.md (or any committed path) if that’s where your conventions live. The model is told, regardless of config, to fix root causes and use suppressions (# noqa, # type: ignore) only as a last resort.

Server

Env var Default Description
PORT 8080 Development server port.
LOG_LEVEL INFO Logging level.