mirror of
https://github.com/Sea-Haven-Industries/pr-reviewer.git
synced 2026-10-03 11:33:28 +00:00
Pre-compute PR reviews so they are ready the moment a PR is opened in the queue, instead of waiting on an on-demand Fireworks call each time. A daemon worker polls the queue every POLL_INTERVAL and reviews new or changed non-draft PRs into a local SQLite cache (gitignored). Change detection is two-level: skip when the PR's updated_at is unchanged, and even when it moved, skip the model call when the diff's SHA-256 matches, so comment-only bumps do not burn tokens. Failed reviews retry up to MAX_REVIEW_ATTEMPTS with 429 backoff; departed PRs are closed with a grace window before purge. Singletons are initialized eagerly in the FastAPI lifespan before the worker thread starts to avoid an init race; all cache writes are serialized. New endpoints GET /api/reviews and POST /api/refresh back a dashboard that polls for status (reviewing/ready/error) and opens a ready review instantly. Nothing is posted automatically; the human still decides. Stdlib-only, so no new runtime or test dependencies.
77 lines
3.2 KiB
Python
77 lines
3.2 KiB
Python
"""Configuration loaded from environment / .env.
|
|
|
|
Secrets never reach the browser. The frontend talks only to this local
|
|
backend; the backend holds the GitHub PAT and Fireworks key.
|
|
"""
|
|
|
|
import os
|
|
from functools import lru_cache
|
|
from pathlib import Path
|
|
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv()
|
|
|
|
_REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
|
|
class Config:
|
|
# --- GitHub ---
|
|
# Either provide GITHUB_TOKEN (a PAT) here, or leave it blank to fall
|
|
# back to `gh auth token` from your local gh CLI (see github_client.py).
|
|
GITHUB_TOKEN: str = os.getenv("GITHUB_TOKEN", "").strip()
|
|
GITHUB_ORG: str = os.getenv("GITHUB_ORG", "Sea-Haven-Industries").strip()
|
|
# The search filter that defines the review queue.
|
|
PR_SEARCH_FILTER: str = os.getenv(
|
|
"PR_SEARCH_FILTER",
|
|
"is:pr state:open archived:false sort:updated-desc org:Sea-Haven-Industries",
|
|
).strip()
|
|
# Cap how many PRs we pull per refresh. GitHub search returns at most 100
|
|
# per page; PRs beyond that are not paginated (fine at current org volume).
|
|
MAX_PRS: int = int(os.getenv("MAX_PRS", "100"))
|
|
# Skip diffs larger than this many bytes (keeps token cost sane).
|
|
MAX_DIFF_BYTES: int = int(os.getenv("MAX_DIFF_BYTES", "120000"))
|
|
|
|
# --- Fireworks (OpenAI-compatible endpoint) ---
|
|
FIREWORKS_API_KEY: str = os.getenv("FIREWORKS_API_KEY", "").strip()
|
|
FIREWORKS_BASE_URL: str = os.getenv(
|
|
"FIREWORKS_BASE_URL", "https://api.fireworks.ai/inference/v1"
|
|
).strip()
|
|
# Swap this to any Fireworks model id you like. Coding-strong defaults:
|
|
# accounts/fireworks/models/deepseek-v4-pro
|
|
# accounts/fireworks/models/kimi-k2p6
|
|
FIREWORKS_MODEL: str = os.getenv(
|
|
"FIREWORKS_MODEL", "accounts/fireworks/models/deepseek-v4-pro"
|
|
).strip()
|
|
FIREWORKS_TEMPERATURE: float = float(os.getenv("FIREWORKS_TEMPERATURE", "0.2"))
|
|
FIREWORKS_MAX_TOKENS: int = int(os.getenv("FIREWORKS_MAX_TOKENS", "4000"))
|
|
|
|
# --- Review behavior ---
|
|
# If a PR author's login is in this list (case-insensitive), the review
|
|
# body will @-mention them. Handles the @openswe bot case.
|
|
MENTION_AUTHORS: list[str] = [
|
|
a.strip().lower()
|
|
for a in os.getenv("MENTION_AUTHORS", "openswe").split(",")
|
|
if a.strip()
|
|
]
|
|
|
|
HOST: str = os.getenv("HOST", "127.0.0.1")
|
|
PORT: int = int(os.getenv("PORT", "8765"))
|
|
|
|
# --- Background auto-review worker ---
|
|
# Seconds between poll cycles. The worker fetches the queue, then pre-reviews
|
|
# any new or changed non-draft PR so results are ready before you open them.
|
|
POLL_INTERVAL: int = int(os.getenv("POLL_INTERVAL", "300"))
|
|
# How many PRs to review in parallel per cycle. Kept low for a single-user
|
|
# tool to stay well under Fireworks rate limits.
|
|
WORKER_CONCURRENCY: int = int(os.getenv("WORKER_CONCURRENCY", "2"))
|
|
# Give up auto-retrying a PR that keeps failing until its diff changes.
|
|
MAX_REVIEW_ATTEMPTS: int = int(os.getenv("MAX_REVIEW_ATTEMPTS", "3"))
|
|
# SQLite cache of pre-computed reviews. Absolute path resolved from the repo
|
|
# root so it lands in the same place regardless of the working directory.
|
|
CACHE_DB: str = os.getenv("CACHE_DB", str(_REPO_ROOT / "pr_cache.db"))
|
|
|
|
|
|
@lru_cache
|
|
def get_config() -> Config:
|
|
return Config()
|