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. |