feat(opencode): multi-provider support with local provider for qwen3.8-27b
This commit is contained in:
@@ -20,6 +20,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"local": {
|
||||||
|
"npm": "@ai-sdk/openai-compatible",
|
||||||
|
"name": "Local AI workstation (qwen3.8-27b)",
|
||||||
|
"options": {
|
||||||
|
"baseURL": "http://192.168.1.79:18020/v1",
|
||||||
|
"apiKey": "PLACEHOLDER_REPLACED_AT_RUNTIME"
|
||||||
|
},
|
||||||
|
"models": {
|
||||||
|
"qwen3.8-27b": {
|
||||||
|
"name": "Qwen 3.8 27B (local)",
|
||||||
|
"limit": {
|
||||||
|
"context": 32768,
|
||||||
|
"output": 8192
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lsp": {},
|
"lsp": {},
|
||||||
|
|||||||
+34
-14
@@ -389,12 +389,26 @@ def sanitize_workdir(workdir: str) -> list[str]:
|
|||||||
|
|
||||||
|
|
||||||
def install_config(src: str, dst: str) -> bool:
|
def install_config(src: str, dst: str) -> bool:
|
||||||
"""Copy `opencode.json` from src to dst, substituting the model endpoint.
|
"""Copy `opencode.json` from src to dst, substituting per-provider endpoint
|
||||||
|
+ API key.
|
||||||
|
|
||||||
The committed `opencode.json` carries a neutral placeholder for the model
|
The committed `opencode.json` carries neutral placeholders for every
|
||||||
provider's `baseURL`, so the repo can be public without publishing the
|
provider's `baseURL`/`apiKey` so the repo can be public without leaking
|
||||||
address of a private network. The real endpoint is supplied at runtime by
|
private-network addresses. Real values are supplied at runtime and patched
|
||||||
`PRAGENT_MODEL_BASE_URL` and patched in here.
|
in here.
|
||||||
|
|
||||||
|
Env var convention (case-sensitive provider name — `headroom`, `local`):
|
||||||
|
|
||||||
|
PRAGENT_<NAME>_BASE_URL — per-provider endpoint override
|
||||||
|
PRAGENT_<NAME>_API_KEY — per-provider API key override
|
||||||
|
PRAGENT_MODEL_BASE_URL — legacy catchall, applies to every provider
|
||||||
|
when the per-provider var is unset
|
||||||
|
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.
|
||||||
|
|
||||||
This is done in Python rather than with opencode's own `{env:VAR}` 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
|
templating because the reviewer subprocess runs with an allow-listed
|
||||||
@@ -405,20 +419,26 @@ def install_config(src: str, dst: str) -> bool:
|
|||||||
"""
|
"""
|
||||||
if not os.path.isfile(src):
|
if not os.path.isfile(src):
|
||||||
return False
|
return False
|
||||||
base_url = os.environ.get("PRAGENT_MODEL_BASE_URL", "").strip()
|
default_url = os.environ.get("PRAGENT_MODEL_BASE_URL", "").strip()
|
||||||
api_key = os.environ.get("PRAGENT_MODEL_API_KEY", "").strip()
|
default_key = os.environ.get("PRAGENT_MODEL_API_KEY", "").strip()
|
||||||
if not base_url and not api_key:
|
if not default_url and not default_key:
|
||||||
|
# Fast path: no env at all → commit copy is fine, no rewrite needed.
|
||||||
shutil.copy2(src, dst)
|
shutil.copy2(src, dst)
|
||||||
return True
|
return True
|
||||||
try:
|
try:
|
||||||
with open(src, encoding="utf-8") as f:
|
with open(src, encoding="utf-8") as f:
|
||||||
cfg = json.load(f)
|
cfg = json.load(f)
|
||||||
for prov in (cfg.get("provider") or {}).values():
|
for name, prov in (cfg.get("provider") or {}).items():
|
||||||
if isinstance(prov, dict) and isinstance(prov.get("options"), dict):
|
if not isinstance(prov, dict) or not isinstance(prov.get("options"), dict):
|
||||||
if base_url:
|
continue
|
||||||
prov["options"]["baseURL"] = base_url
|
per_url = os.environ.get(f"PRAGENT_{name.upper()}_BASE_URL", "").strip()
|
||||||
if api_key:
|
per_key = os.environ.get(f"PRAGENT_{name.upper()}_API_KEY", "").strip()
|
||||||
prov["options"]["apiKey"] = api_key
|
url = per_url or default_url
|
||||||
|
key = per_key or default_key
|
||||||
|
if url:
|
||||||
|
prov["options"]["baseURL"] = url
|
||||||
|
if key:
|
||||||
|
prov["options"]["apiKey"] = key
|
||||||
with open(dst, "w", encoding="utf-8") as f:
|
with open(dst, "w", encoding="utf-8") as f:
|
||||||
json.dump(cfg, f, indent=2)
|
json.dump(cfg, f, indent=2)
|
||||||
except (OSError, ValueError, AttributeError):
|
except (OSError, ValueError, AttributeError):
|
||||||
|
|||||||
Reference in New Issue
Block a user