llm

Models and your own key

ReviewGate runs on your model. The provider sits behind an abstraction: Anthropic's cloud, any OpenAI-compatible endpoint, a cheaper model, or a local one inside your own network.

bring-your-own-keyThe key is yours. The bot starts without one (health works), but a review job fails with a clear error until the key is set. That is the economics of a free product: the marginal cost is your own request to the model.

Yandex AI Studio

One of the OpenAI-compatible providers, useful when your data must stay inside a particular jurisdiction. You need an API key of a service account (the ai.languageModels.user role) and a folder (folder_id). The default model is Qwen3-235B and the endpoint is filled in automatically.

.env — Yandex AI Studio
LLM_PROVIDER=yandex
LLM_API_KEY=<the API key of a YC service account>
LLM_FOLDER_ID=b1g...                       # the Yandex Cloud folder
# the Qwen3-235B model and the llm.api.cloud.yandex.net endpoint are the defaults
no request logging on the provider sideThe bot sends x-data-logging-enabled: false, so the requests are not logged on Yandex's side. For a strict «the code never leaves our network», use a local model, below.

Anthropic's cloud

.env — the cloud
LLM_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-...
ANTHROPIC_MODEL=claude-opus-4-8     # cheaper: claude-sonnet-4-6 / claude-haiku-4-5
modelwhen
claude-opus-4-8maximum quality (the default; code review is sensitive to how good the model is)
claude-sonnet-4-6a balance of price and quality
claude-haiku-4-5cheap and fast for large volumes. This model does not support the reasoning mode — ReviewGate repeats the request in a simplified form by itself and says so in the summary: there will be fewer findings and they will be shallower

A bonus of the Anthropic path is prompt caching: the system block of the prompt is cached, and repeat reviews in the same repository read it from the cache for roughly a tenth of the input price; the judge reuses the same prefix.

A local model

In an air-gapped deployment nothing may leave at all. Ollama and vLLM expose an OpenAI-compatible API, and the bot talks to it directly, without a separate gateway. A quick start with Ollama: ollama pull qwen3-coder:30b-a3b-q8_0, then point the bot at the endpoint:

.env — a local model
LLM_PROVIDER=ollama                       # an OpenAI-compatible provider
LLM_BASE_URL=http://localhost:11434/v1    # endpoint Ollama (или vLLM)
LLM_MODEL=qwen3-coder:30b-a3b-q8_0        # the model tag
# LLM_API_KEY=                            # Ollama needs none; OpenRouter/OpenAI require it
LLM_TIMEOUT_MS=900000                     # a very slow local model (the default is 600000)
REVIEW_CONCURRENCY=1                      # SET IT EXPLICITLY: the default is 3, but Ollama serves one at a time (vLLM more)
strict JSONvLLM enforces the JSON schema (guided decoding); Ollama holds it more loosely, so the bot backs itself up with a resilient parser and a repeat request. A local 30B model is noticeably weaker than cloud Claude on subtle findings — a deliberate trade-off for «the code never leaves our network».
concurrency follows your hardwareWhen several developers open requests at once, some reviews queue up: the bot runs them in parallel up to REVIEW_CONCURRENCY (3 by default). With a local model this value must be lowered to what your inference actually sustains: Ollama serves requests one at a time out of the box, so set 1 for it (parallelism is enabled separately, see OLLAMA_NUM_PARALLEL); vLLM can carry several requests on one card (continuous batching), so go by its real throughput and your spare VRAM. A number above what the hardware can do will not speed reviews up — the queue simply moves from the bot to the GPU. On a cloud provider the guide is your RPM/TPM limits; the parameter itself tops out at 64. The cheat sheet for cloud providers is below.
the code does not go outWith a local model the entire review flow stays inside your network. More about that on the Security page.

Reaching the provider through a proxy

If your network cannot reach the provider directly — a corporate egress policy, a regional block — give the bot an HTTP(S) CONNECT proxy: it will route only the model requests through it. The tunnel is blind: TLS to the provider stays end to end, and the proxy sees the host, never the code or the diff.

.env — a proxy
LLM_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-...
LLM_PROXY=http://user:pass@proxy.example.com:8888   # слепой CONNECT-туннель
each deployment has its own proxyThe proxy is set by the deployment itself: the vendor never becomes a bottleneck, and if access is blocked you solve it on your side without affecting anyone else. A local model needs no proxy at all, and neither does a provider your network already reaches.

Several providers (multi-vendor)

An ensemble review (llm.generators / llm.judges / llm.arbiter) can spread the roles across different vendors: the main generator on Claude, the second on DeepSeek, the arbiter on Anthropic's strongest model. Different models see different problems, which raises the recall of the review, while the arbiter holds precision.

An extra vendor is declared as a named backend in the deployment environment: LLM_BACKEND_<NAME>_BASE_URL / _API_KEY / _MODEL / _PROVIDER / _PROXY / _JSON_MODE / _MAX_TOKENS / _TIMEOUT_MS / _BUDGET_TOKENS (NAME is latin letters and digits without underscores; the name default is reserved — it always addresses the deployment's main provider, configured with the flat LLM_* variables). In .reviewgate/config.yml a role references a backend by name: backend: deepseek — the keys and addresses stay with the operator and cannot be overridden from a repository; backend: default addresses the main provider explicitly.

.env — a DeepSeek backend, and the roles in config.yml
# .env — a named backend (the keys stay with the operator)
LLM_BACKEND_DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
LLM_BACKEND_DEEPSEEK_API_KEY=sk-...
LLM_BACKEND_DEEPSEEK_MODEL=deepseek-v4-flash   # or deepseek-v4-pro
LLM_BACKEND_DEEPSEEK_JSON_MODE=none            # rejects json_schema; json_object returns empty content on long prompts
LLM_BACKEND_DEEPSEEK_MAX_TOKENS=64000          # the reasoning eats the same ceiling as the answer

# .reviewgate/config.yml — review roles across different vendors
llm:
  generators:
    - model: claude-sonnet-4-6      # pass 1 — the main vendor
    - backend: deepseek             # pass 2 — independent, another vendor
  judges:
    - model: claude-opus-4-8        # the judge: weighs the union and collapses duplicates
the spending cap is a column of the catalog_BUDGET_TOKENS is the token budget of a run for that backend (input + output + cache). A company policy limits the expensive cloud vendors; there is no point limiting local ones — no variable, no limit. The thresholds are fixed (80% a notice · 100% a loud notice, with the review still completing · 300% a stop that publishes what was collected) and cannot be overridden by users; the limits are unavailable from a repository config.yml — this is an operator knob. More in what to do when a run stops on budget.
network and privacy are per vendorLLM_PROXY does not extend to named backends: if one of them needs a proxy, set the backend's own _PROXY. And remember that the diff goes to every vendor you choose — assess their data processing policy exactly as you did for the main one. If a provider does not accept a strict JSON schema (response_format: json_schema), set _JSON_MODE=none: the answer shape is then held by the prompt and the parser. DeepSeek is the live example — it rejects json_schema, and its json_object returns an empty answer on a real review run (a long prompt — the JSON lands in the reasoning). Reasoning models also spend the output ceiling on their thinking — give the backend its own _MAX_TOKENS (64000 for DeepSeek v4; the same number stays within the Sonnet 5 ceiling).

Parallel reviews (REVIEW_CONCURRENCY)

Several requests opened at once are carried in parallel — up to three by default (REVIEW_CONCURRENCY=3, the allowed range is 1–64). A team of about ten developers or more should raise the value at installation time, otherwise requests wait for each other at peak hours. Within that range the ceiling is set not by the bot but by your model: a review is a token-heavy request (the input is often 50–150 thousand tokens, and an ensemble makes several calls per request), so with cloud providers you usually hit the tokens per minute limit rather than the request limit.

cloud providerwhat limits the parallelismwhere to look
Anthropicusage tiers (they grow with what you have paid so far): separate limits on requests (RPM) and on input/output tokens per minute (ITPM/OTPM). On the lower tiers the tokens may not even cover one large review without pauses — workable parallelism starts around tier 2–3. On current models cache reads do not count towards ITPM, and the bot caches the system prompt automatically, which noticeably raises the real throughputConsole → Limits
OpenAIthe same mechanics: tier RPM/TPM limits per modelPlatform → Limits
DeepSeekpublishes no hard limits — under load it slows answers down dynamically: parallelism works, but the response time driftsdocs API
Yandex AI Studiocloud quotas per folder (requests per second, concurrent generations); they are raised through a quota increase requestthe YC console → Quotas
OpenRouterthe limits depend on the key and balance and on the provider the model is routed tothe OpenRouter dashboard
hitting the limit is survivable, but slowWhen there are more concurrent reviews than the provider will take, the extra requests get 429s or overload responses. The bot does not drop such reviews: it retries with pauses and, if the overload drags on, writes an honest notice into the request. The queue does stretch out, though, so raise the value gradually and watch the timings in the «🔬 Run diagnostics» block. For a local model see the note above: go by what your inference can do, not by a number in the config.

Tuning

.env — tuning
LLM_EFFORT=high              # low | medium | high | xhigh | max
LLM_MAX_TOKENS=16000         # the cap on output tokens
LLM_DIFF_CHAR_BUDGET=400000  # the character budget for the diff in the prompt (~100K tokens)
LLM_CACHE_TTL=1h             # the prompt cache TTL (Anthropic only): 1h | 5m | off
the config outranks the environmentThe roles in .reviewgate/config.yml override these variables, so the model setup can live next to the project code.