feat(ai_review): route per-repo model override through PRICES provider field
This commit is contained in:
+17
-4
@@ -483,11 +483,15 @@ def _resolve_display_model(base_model: str, config: dict | None) -> str:
|
|||||||
|
|
||||||
Precedence (highest first):
|
Precedence (highest first):
|
||||||
1. `OPENCODE_MODEL` env var — operator override, used as-is (already a
|
1. `OPENCODE_MODEL` env var — operator override, used as-is (already a
|
||||||
provider-prefixed opencode ref).
|
provider-prefixed opencode ref like `headroom/MiniMax-M2.7`).
|
||||||
2. `.pr-review.json:model` — per-repo override. Already validated
|
2. `.pr-review.json:model` — per-repo override. Already validated
|
||||||
against `cost_model.PRICES` by `parse_repo_config`, so a bare key
|
against `cost_model.PRICES` by `parse_repo_config`, so a bare key
|
||||||
like `claude-sonnet-5` is safe to use as the opencode ref AND the
|
like `claude-sonnet-5` or `qwen3.8-27b` is safe. Re-prefixed with
|
||||||
REVIEW_HEADER label.
|
the model's `provider` field from `cost_model.Price` (default
|
||||||
|
`headroom`) so the opencode subprocess routes correctly — e.g.
|
||||||
|
`qwen3.8-27b` → `local/qwen3.8-27b` (local AI workstation on
|
||||||
|
192.168.1.79:18020), `claude-sonnet-5` → `headroom/claude-sonnet-5`
|
||||||
|
(Anthropic pricing proxy).
|
||||||
3. Default — `f"headroom/{base_model}"` where `base_model` is the bare
|
3. Default — `f"headroom/{base_model}"` where `base_model` is the bare
|
||||||
`OLLAMA_MODEL` (e.g. `"MiniMax-M2.7" → "headroom/MiniMax-M2.7"`).
|
`OLLAMA_MODEL` (e.g. `"MiniMax-M2.7" → "headroom/MiniMax-M2.7"`).
|
||||||
|
|
||||||
@@ -500,7 +504,16 @@ def _resolve_display_model(base_model: str, config: dict | None) -> str:
|
|||||||
return env
|
return env
|
||||||
cfg_model = (config or {}).get("model")
|
cfg_model = (config or {}).get("model")
|
||||||
if isinstance(cfg_model, str) and cfg_model.strip():
|
if isinstance(cfg_model, str) and cfg_model.strip():
|
||||||
return cfg_model.strip()
|
# Look up the provider from PRICES so the opencode subprocess routes
|
||||||
|
# through the right provider block (local vs headroom). Lazy import —
|
||||||
|
# the ollama path doesn't touch cost_model.
|
||||||
|
from cost_model import PRICES
|
||||||
|
provider = PRICES.get(cfg_model.strip())
|
||||||
|
if provider is not None:
|
||||||
|
return f"{provider.provider}/{cfg_model.strip()}"
|
||||||
|
# parse_repo_config already drops unknowns, but stay defensive: fall
|
||||||
|
# back to headroom so the review still runs rather than crash.
|
||||||
|
return f"headroom/{cfg_model.strip()}"
|
||||||
return f"headroom/{base_model}"
|
return f"headroom/{base_model}"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -451,15 +451,22 @@ def test_parse_repo_config_model_must_be_string():
|
|||||||
|
|
||||||
|
|
||||||
def test_resolve_display_model_precedence(monkeypatch):
|
def test_resolve_display_model_precedence(monkeypatch):
|
||||||
# Order is OPENCODE_MODEL env > config['model'] > headroom/{base}.
|
# Order is OPENCODE_MODEL env > config['model'] (re-prefixed by provider) > headroom/{base}.
|
||||||
monkeypatch.delenv("OPENCODE_MODEL", raising=False)
|
monkeypatch.delenv("OPENCODE_MODEL", raising=False)
|
||||||
# 1. No env, no config → headroom/<base>
|
# 1. No env, no config → headroom/<base>
|
||||||
assert ai_review._resolve_display_model("MiniMax-M2.7", None) == "headroom/MiniMax-M2.7"
|
assert ai_review._resolve_display_model("MiniMax-M2.7", None) == "headroom/MiniMax-M2.7"
|
||||||
assert ai_review._resolve_display_model("MiniMax-M2.7", {}) == "headroom/MiniMax-M2.7"
|
assert ai_review._resolve_display_model("MiniMax-M2.7", {}) == "headroom/MiniMax-M2.7"
|
||||||
# 2. No env, config has model → use config model as-is (already a known key)
|
# 2. No env, config has a PRICES key → re-prefixed with that model's provider.
|
||||||
|
# headroom-hosted models default to provider="headroom".
|
||||||
assert (
|
assert (
|
||||||
ai_review._resolve_display_model("MiniMax-M2.7", {"model": "claude-sonnet-5"})
|
ai_review._resolve_display_model("MiniMax-M2.7", {"model": "claude-sonnet-5"})
|
||||||
== "claude-sonnet-5"
|
== "headroom/claude-sonnet-5"
|
||||||
|
)
|
||||||
|
# Self-hosted models carry provider="local" → routes to the `local`
|
||||||
|
# provider block in opencode.json (AI workstation on 192.168.1.79:18020).
|
||||||
|
assert (
|
||||||
|
ai_review._resolve_display_model("MiniMax-M2.7", {"model": "qwen3.8-27b"})
|
||||||
|
== "local/qwen3.8-27b"
|
||||||
)
|
)
|
||||||
# 3. Env wins over config
|
# 3. Env wins over config
|
||||||
monkeypatch.setenv("OPENCODE_MODEL", "headroom/MiniMax-M2.7")
|
monkeypatch.setenv("OPENCODE_MODEL", "headroom/MiniMax-M2.7")
|
||||||
|
|||||||
Reference in New Issue
Block a user