feat(opencode): configure vllm-qwen38 provider for local Qwen3.8-27B
This commit is contained in:
@@ -8,3 +8,5 @@ dist/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.worktrees/
|
||||
.claude/
|
||||
.opencode/package-lock.json
|
||||
|
||||
+18
-5
@@ -21,19 +21,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"local": {
|
||||
"vllm-qwen38": {
|
||||
"npm": "@ai-sdk/openai-compatible",
|
||||
"name": "Local AI workstation (qwen3.8-27b)",
|
||||
"name": "Qwen3.8-27B vLLM (RTX 3090, MTP spec-decode)",
|
||||
"options": {
|
||||
"baseURL": "http://192.168.1.79:18020/v1",
|
||||
"apiKey": "PLACEHOLDER_REPLACED_AT_RUNTIME"
|
||||
"apiKey": "PLACEHOLDER_REPLACED_AT_RUNTIME",
|
||||
"timeout": 300000,
|
||||
"chunkTimeout": 30000
|
||||
},
|
||||
"models": {
|
||||
"qwen3.8-27b": {
|
||||
"name": "Qwen 3.8 27B (local)",
|
||||
"name": "Qwen3.8-27B (vLLM, MTP, 150k ctx)",
|
||||
"tools": true,
|
||||
"thinking": true,
|
||||
"attachments": false,
|
||||
"limit": {
|
||||
"context": 57344,
|
||||
"context": 150000,
|
||||
"output": 8192
|
||||
},
|
||||
"options": {
|
||||
"temperature": 0.3,
|
||||
"topP": 0.8,
|
||||
"topK": 20,
|
||||
"repetitionPenalty": 1.05,
|
||||
"frequencyPenalty": 0,
|
||||
"presencePenalty": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -489,7 +489,7 @@ def _resolve_display_model(base_model: str, config: dict | None) -> str:
|
||||
like `claude-sonnet-5` or `qwen3.8-27b` is safe. Re-prefixed with
|
||||
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
|
||||
`qwen3.8-27b` → `vllm-qwen38/qwen3.8-27b` (vLLM on RTX 3090 at
|
||||
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
|
||||
@@ -505,8 +505,8 @@ def _resolve_display_model(base_model: str, config: dict | None) -> str:
|
||||
cfg_model = (config or {}).get("model")
|
||||
if isinstance(cfg_model, str) and 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.
|
||||
# through the right provider block (vllm-qwen38 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:
|
||||
|
||||
+8
-7
@@ -57,7 +57,7 @@ class Price:
|
||||
"""Per-MTok prices. `cache_write` and `cache_read` are absolute rates, not
|
||||
multipliers, so providers with different cache economics stay comparable.
|
||||
|
||||
`provider` is the opencode provider name (`headroom`, `local`, ...). It
|
||||
`provider` is the opencode provider name (`headroom`, `vllm-qwen38`, ...). It
|
||||
doubles as the dispatch key for `.pr-review.json:model` overrides — when
|
||||
a per-repo override is set, `_resolve_display_model` returns
|
||||
`f"{provider}/{key}"` so the opencode subprocess routes correctly.
|
||||
@@ -98,12 +98,13 @@ PRICES: dict[str, Price] = {
|
||||
# xAI Grok — cache_write = input
|
||||
"grok-4.5": Price("Grok 4.5", 2.00, 6.00, 2.00, 0.30),
|
||||
"grok-4.3": Price("Grok 4.3", 1.25, 2.50, 1.25, 0.20),
|
||||
# Self-hosted — local AI workstation, no per-token charge. provider="local"
|
||||
# so the opencode subprocess routes via the `local` provider block in
|
||||
# opencode.json (baseURL=http://192.168.1.79:18020/v1). Equivalent-cost
|
||||
# column will read $0 — the cost-comparison signal is that the same work
|
||||
# would bill $X on a paid model.
|
||||
"qwen3.8-27b": Price("Qwen 3.8 27B (local)", 0.0, 0.0, 0.0, 0.0, provider="local"),
|
||||
# Self-hosted — AI workstation RTX 3090, vLLM + DFlash2 spec-decode, no
|
||||
# per-token charge. provider="vllm-qwen38" so the opencode subprocess
|
||||
# routes via the matching provider block in opencode.json
|
||||
# (baseURL=http://192.168.1.79:18020/v1). Equivalent-cost column reads $0
|
||||
# — the cost-comparison signal is that the same work would bill $X on a
|
||||
# paid model.
|
||||
"qwen3.8-27b": Price("Qwen3.8-27B (vLLM, MTP, 150k ctx)", 0.0, 0.0, 0.0, 0.0, provider="vllm-qwen38"),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -397,7 +397,7 @@ def install_config(src: str, dst: str) -> bool:
|
||||
private-network addresses. Real values are supplied at runtime and patched
|
||||
in here.
|
||||
|
||||
Env var convention (case-sensitive provider name — `headroom`, `local`):
|
||||
Env var convention (case-sensitive provider name — `headroom`, `vllm-qwen38`):
|
||||
|
||||
PRAGENT_<NAME>_BASE_URL — per-provider endpoint override
|
||||
PRAGENT_<NAME>_API_KEY — per-provider API key override
|
||||
@@ -406,9 +406,9 @@ def install_config(src: str, dst: str) -> bool:
|
||||
PRAGENT_MODEL_API_KEY — legacy catchall (same)
|
||||
|
||||
Per-provider wins over the catchall. The first 2 win when the operator
|
||||
needs a different endpoint per upstream (e.g. headroom → MiniMax, local →
|
||||
ai-workstation). The catchall keeps the single-provider deploys from
|
||||
needing any env config.
|
||||
needs a different endpoint per upstream (e.g. headroom → MiniMax,
|
||||
vllm-qwen38 → ai-workstation). The catchall keeps the single-provider
|
||||
deploys from needing any env config.
|
||||
|
||||
This is done in Python rather than with opencode's own `{env:VAR}` config
|
||||
templating because the reviewer subprocess runs with an allow-listed
|
||||
|
||||
@@ -462,11 +462,12 @@ def test_resolve_display_model_precedence(monkeypatch):
|
||||
ai_review._resolve_display_model("MiniMax-M2.7", {"model": "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).
|
||||
# Self-hosted models carry provider="vllm-qwen38" → routes to the
|
||||
# matching 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"
|
||||
== "vllm-qwen38/qwen3.8-27b"
|
||||
)
|
||||
# 3. Env wins over config
|
||||
monkeypatch.setenv("OPENCODE_MODEL", "headroom/MiniMax-M2.7")
|
||||
|
||||
Reference in New Issue
Block a user