Skip to content

fix(server): webhook reviews fall back to hot-applied server LLM configs (0.10.1) - #152

Merged
Liewzheng merged 2 commits into
mainfrom
fix/webhook-llm-fallback
Sep 8, 2026
Merged

fix(server): webhook reviews fall back to hot-applied server LLM configs (0.10.1)#152
Liewzheng merged 2 commits into
mainfrom
fix/webhook-llm-fallback

Conversation

@Liewzheng

Copy link
Copy Markdown
Owner

Root cause

The webhook review path (run_review_common in src/server/mod.rs, shared by the GitLab handler src/server/gitlab/hooks.rs and the GitHub handler src/server/github.rs) resolved LLM providers from only config::resolve_config(None) (the config file [[llm]] section) and llm_configs_from_env() (LLM_CONFIG). It never read the hot-applied state.llm_configs (populated from the DB llm_providers table at startup and updated live by the WebUI / PUT /api/v1/config).

Result: providers configured in the WebUI had no effect on webhook-dispatched reviews — e.g. NAS deployments where every MR review failed with all experts failed: ... LLM config 'default' has no api_base set.

Fix

  • run_review_common gains a trailing server_llm_configs: Option<Vec<LLMConfig>> parameter; resolution extracted into a pure helper resolve_webhook_llm_configs with precedence config file [[llm]] > server (hot-applied) > LLM_CONFIG env. An empty server vec counts as "not provided" (same semantics as the 0.9.1 API-path fix); None (CLI/legacy, no AppState) keeps the exact pre-0.10.1 behavior.
  • Both webhook handlers snapshot state.llm_configs (via the existing Weak<AppState> upgrade pattern) and thread it down to the review task. The GitHub handler previously kept only the task store from with_app_state; it now also holds a Weak<AppState>, mirroring the GitLab handler. The snapshot is taken lazily inside the review-dispatching arms (GitLab: Merge Request / Note / System Hook; GitHub: pull_request / issue_comment) so ping/push/ignored events pay zero cost.
  • When all three sources are empty, a clear ERROR is logged before the task fails, with caller-appropriate guidance: server-state callers are pointed at the WebUI (Configuration → LLM), stateless callers at the config file / LLM_CONFIG env. Log-only — failure semantics unchanged.

Tests

  • 4 new unit tests lock the precedence: file wins over server; server used when the file is empty; Some(vec![]) falls through to env; None keeps config-file → env behavior.
  • New integration test llm_configs::webhook_review_uses_hot_applied_server_llm_configs (tests/server/llm_configs.rs) reproduces the reported scenario end-to-end: real spawned server with no LLM_CONFIG env and no config file, provider added via POST /api/v1/llm/providers (the WebUI flow) pointing at a wiremock LLM, a GitLab MR webhook fired against a wiremock GitLab instance. Asserts the webhook-dispatched review settles completed and the hot-applied provider was actually called (/chat/completions hits ≥ 1). Pre-fix this task failed with "no api_base".

Verification (run in the fix worktree)

  • cargo fmt --check — clean
  • cargo clippy --all-targets --all-features — 0 warnings
  • cargo test — all green: lib 1525 passed / 0 failed (2 ignored), bin 58, cli 31, server integration 49 (incl. the new webhook test), doc 4

Version

Cargo.toml bumped to 0.10.1; CHANGELOG.md gains [0.10.1] - 2026-09-07. No CLI behavior change; no API/schema change.

…igs (0.10.1)

The webhook review path (run_review_common, shared by the GitLab and
GitHub handlers) resolved LLM providers from only the config file [[llm]]
section and the LLM_CONFIG env, never the hot-applied state.llm_configs
(DB llm_providers / WebUI). Providers configured in the WebUI had no
effect on webhook-dispatched reviews, which failed with
"LLM config 'default' has no api_base set".

Both webhook handlers now snapshot state.llm_configs at dispatch time and
pass it down to run_review_common. Precedence: config file [[llm]] >
server (hot-applied) > LLM_CONFIG env; an empty server list counts as
not provided (0.9.1 API-path semantics); the CLI path (no state) is
unchanged. When all three sources are empty a clear ERROR is logged
pointing at the WebUI provider configuration before the task fails.

Tests: 4 unit tests for the resolution precedence plus a full-stack
integration test (mock GitLab + mock LLM, provider added via
POST /api/v1/llm/providers, no LLM_CONFIG env / config file) asserting
the webhook-dispatched review completes via the hot-applied provider.
… lazy config snapshot

Self-review follow-ups to the 0.10.1 fix:

- The all-sources-empty ERROR log in run_review_common pointed at the
  WebUI unconditionally, which is misleading on the CLI/legacy path
  (server_llm_configs=None, no WebUI exists). The guidance now matches
  the caller: server-state callers are pointed at the WebUI, stateless
  callers at the config file [[llm]] / LLM_CONFIG env. Log-only; the
  failure semantics are unchanged.
- Both webhook handlers snapshotted state.llm_configs (RwLock read +
  deep Vec clone including API keys) at handle_event entry, even for
  ping/push/ignored events. The snapshot now happens lazily inside the
  review-dispatching arms only (gitlab: Merge Request/Note/System Hook;
  github: pull_request/issue_comment), so non-review events pay zero
  cost.
@Liewzheng
Liewzheng merged commit 142d95f into main Sep 8, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant