squid-main-space / codex_harness.py
mengxaingshuo's picture
fix: call OpenCode Go API directly
13bbb15
Raw
History Blame
3.17 kB
from __future__ import annotations
import json
import os
import urllib.error
import urllib.request
import uuid
from dataclasses import dataclass
@dataclass
class CodexResult:
final_response: str
finish_reason: str = "completed"
class CodexHarness:
"""Small non-interactive adapter for OpenCode Go's Responses API."""
def __init__(self, model=None, api_key=None, base_url=None,
session_root=None, system_prompt=None, **kwargs):
self.model = model or os.environ.get("OPENCODE_GO_MODEL", "gpt-5.6-luna")
self.api_key = (
api_key
or os.environ.get("OPENCODE_GO_API_KEY")
or os.environ.get("OPENAI_API_KEY")
or ""
).strip()
self.base_url = (
base_url
or os.environ.get("OPENCODE_GO_BASE_URL")
or os.environ.get("OPENAI_BASE_URL")
or "https://opencode.ai/zen/go/v1"
).rstrip("/")
self.system_prompt = system_prompt or ""
@staticmethod
def _text(payload: dict) -> str:
text = str(payload.get("output_text") or "").strip()
if text:
return text
parts = []
for item in payload.get("output") or []:
for content in item.get("content") or []:
if content.get("type") in {"output_text", "text"}:
value = content.get("text")
if isinstance(value, str):
parts.append(value)
elif isinstance(value, dict) and isinstance(value.get("value"), str):
parts.append(value["value"])
return "".join(parts).strip()
def run(self, prompt, session_id=None):
if not self.api_key:
raise RuntimeError("OPENCODE_GO_API_KEY is not configured")
full_prompt = (self.system_prompt + "\n\n" + prompt).strip()
body = json.dumps({"model": self.model, "input": full_prompt}, ensure_ascii=False).encode("utf-8")
request = urllib.request.Request(
self.base_url + "/responses",
data=body,
method="POST",
headers={
"Authorization": "Bearer " + self.api_key,
"Content-Type": "application/json",
"Accept": "application/json",
"User-Agent": "Global-Marine-Foundation/4.4",
"x-opencode-session": str(session_id or uuid.uuid4()),
},
)
try:
with urllib.request.urlopen(request, timeout=600) as response:
payload = json.loads(response.read().decode("utf-8"))
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", errors="replace")[:1000]
raise RuntimeError(f"OpenCode Go API {exc.code}: {detail}") from exc
except urllib.error.URLError as exc:
raise RuntimeError(f"OpenCode Go connection failed: {exc.reason}") from exc
answer = self._text(payload)
if not answer:
raise RuntimeError("OpenCode Go returned no text: " + json.dumps(payload, ensure_ascii=False)[:800])
return CodexResult(final_response=answer)