Support local OpenCode Sol provider
This commit is contained in:
parent
d123659bd2
commit
e11bcd1e59
2 changed files with 50 additions and 29 deletions
|
|
@ -27,7 +27,12 @@ class Command(BaseCommand):
|
|||
"provider": "opencode",
|
||||
"compute": spark,
|
||||
"roles": ["PROJECT_BRAIN", "PLANNING", "ARCHAEOLOGY_INTERPRETATION"],
|
||||
"config": {"command": os.environ.get("ARTIFEX_SOL_OPENCODE_COMMAND", "opencode run --json --no-repo --stdin")},
|
||||
"config": {
|
||||
"transport": os.environ.get("ARTIFEX_SOL_TRANSPORT", "local"),
|
||||
"command": os.environ.get("ARTIFEX_SOL_OPENCODE_COMMAND", "/home/daniel/.opencode/bin/opencode run"),
|
||||
"use_pty": os.environ.get("ARTIFEX_SOL_USE_PTY", "1") == "1",
|
||||
"timeout_seconds": int(os.environ.get("ARTIFEX_SOL_TIMEOUT_SECONDS", "120")),
|
||||
},
|
||||
},
|
||||
)
|
||||
Resource.objects.update_or_create(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
|
@ -36,6 +38,17 @@ def extract_json_object(text: str) -> dict[str, Any]:
|
|||
return _extract_json(text[start : end + 1])
|
||||
|
||||
|
||||
def _clean_opencode_output(text: str) -> str:
|
||||
text = re.sub(r"\x1b\[[0-9;?]*[ -/]*[@-~]", "", text)
|
||||
lines = []
|
||||
for line in text.splitlines():
|
||||
stripped = line.strip()
|
||||
if not stripped or stripped.startswith("> build"):
|
||||
continue
|
||||
lines.append(line)
|
||||
return "\n".join(lines).strip()
|
||||
|
||||
|
||||
@dataclass
|
||||
class SolProvider:
|
||||
resource: Resource
|
||||
|
|
@ -44,44 +57,47 @@ class SolProvider:
|
|||
def complete(self, request: ModelRequestContract) -> ModelResponseContract:
|
||||
config = self.resource.config
|
||||
compute = self.resource.compute
|
||||
ssh_alias = (compute.config if compute else {}).get("ssh_alias", config.get("ssh_alias", "spark"))
|
||||
timeout = int(config.get("timeout_seconds", 120))
|
||||
remote_command = config.get("command", "opencode run --json --no-repo --stdin")
|
||||
payload = {
|
||||
"mode": "reasoning_only",
|
||||
"output_schema": "json_object",
|
||||
"prompt": request.prompt,
|
||||
"token_budget": request.token_budget,
|
||||
}
|
||||
completed = subprocess.run(
|
||||
["ssh", str(ssh_alias), str(remote_command)],
|
||||
input=json.dumps(payload),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
check=False,
|
||||
)
|
||||
command = str(config.get("command", "opencode run"))
|
||||
transport = str(config.get("transport", "ssh"))
|
||||
use_pty = bool(config.get("use_pty", False))
|
||||
if transport == "local":
|
||||
argv = [*shlex.split(command), request.prompt]
|
||||
if use_pty:
|
||||
shell_command = " ".join(shlex.quote(part) for part in argv)
|
||||
argv = ["script", "-q", "-e", "-c", shell_command, "/dev/null"]
|
||||
completed = subprocess.run(argv, capture_output=True, text=True, timeout=timeout, check=False)
|
||||
else:
|
||||
ssh_alias = (compute.config if compute else {}).get("ssh_alias", config.get("ssh_alias", "spark"))
|
||||
remote_command = " ".join(shlex.quote(part) for part in [*shlex.split(command), request.prompt])
|
||||
completed = subprocess.run(
|
||||
["ssh", "-tt", str(ssh_alias), remote_command],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
check=False,
|
||||
)
|
||||
if completed.returncode != 0:
|
||||
raise ProviderError(completed.stderr.strip() or "Sol provider failed")
|
||||
data = _extract_json(completed.stdout)
|
||||
content = data.get("content") or data.get("response") or data.get("plan")
|
||||
if isinstance(content, (dict, list)):
|
||||
content = json.dumps(content)
|
||||
if not isinstance(content, str):
|
||||
raise ProviderError("Sol provider response missing string content")
|
||||
content = _clean_opencode_output(completed.stdout)
|
||||
if not content:
|
||||
raise ProviderError("Sol provider response missing content")
|
||||
return ModelResponseContract(
|
||||
model=self.resource.name,
|
||||
content=content,
|
||||
metadata={"provider": self.provider_name, "usage": data.get("usage", {})},
|
||||
metadata={"provider": self.provider_name, "usage": {}},
|
||||
)
|
||||
|
||||
def health(self) -> str:
|
||||
compute = self.resource.compute
|
||||
ssh_alias = (compute.config if compute else {}).get("ssh_alias", self.resource.config.get("ssh_alias", "spark"))
|
||||
config = self.resource.config
|
||||
try:
|
||||
completed = subprocess.run(
|
||||
["ssh", str(ssh_alias), "true"], capture_output=True, text=True, timeout=10, check=False
|
||||
)
|
||||
if config.get("transport") == "local":
|
||||
command = shlex.split(str(config.get("command", "opencode")))[0]
|
||||
completed = subprocess.run([command, "--version"], capture_output=True, text=True, timeout=10, check=False)
|
||||
else:
|
||||
compute = self.resource.compute
|
||||
ssh_alias = (compute.config if compute else {}).get("ssh_alias", self.resource.config.get("ssh_alias", "spark"))
|
||||
completed = subprocess.run(["ssh", str(ssh_alias), "true"], capture_output=True, text=True, timeout=10, check=False)
|
||||
except Exception:
|
||||
return "UNAVAILABLE"
|
||||
return "AVAILABLE" if completed.returncode == 0 else "UNAVAILABLE"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue