restokes92's picture
Upload Kaiju Coder 7 OpenCode helper package
c75f885 verified
#!/usr/bin/env python3
"""OpenAI-compatible JSON-spec extraction for Kaiju harnesses."""
from __future__ import annotations
import json
import os
import re
import urllib.error
import urllib.request
from pathlib import Path
from typing import Any
FAST_JSON_CONTRACT = """Planner contract:
- Return one minified JSON object only.
- No markdown, no prose, no reasoning, no comments, no HTML, no code fences.
- Keep the whole answer compact, ideally under 150 tokens.
- Use short strings and short arrays. End immediately after the final }.
"""
DEFAULT_SYSTEM_PROMPT = """You are the Kaiju website spec planner.
Return strict JSON only. Do not return HTML. Do not use markdown fences.
The JSON keys must be:
business_name, business_type, location, headline, subheadline, cta,
services, sections, testimonials, hours, contact_phone, contact_email,
palette, image_urls.
Keep services to 3-5 short items. Keep sections to practical identifiers.
If images are needed, use only real https URLs you are confident exist.
"""
def with_fast_json_contract(system_prompt: str) -> str:
if "Planner contract:" in system_prompt:
return system_prompt
return FAST_JSON_CONTRACT + "\n" + system_prompt.strip() + "\n"
def extract_json_object(text: str) -> dict[str, Any]:
cleaned = text.strip()
if cleaned.startswith("```"):
cleaned = re.sub(r"^```(?:json)?", "", cleaned).strip()
cleaned = re.sub(r"```$", "", cleaned).strip()
try:
value = json.loads(cleaned)
if isinstance(value, dict):
return value
except json.JSONDecodeError:
pass
start = cleaned.find("{")
end = cleaned.rfind("}")
if start == -1 or end == -1 or end <= start:
raise ValueError("model did not return a JSON object")
value = json.loads(cleaned[start : end + 1])
if not isinstance(value, dict):
raise ValueError("model JSON was not an object")
return value
def request_json_spec(
*,
base_url: str,
model: str,
prompt: str,
api_key_env: str = "KAIJU_EVAL_API_KEY",
system_prompt_file: Path | None = None,
default_system_prompt: str = DEFAULT_SYSTEM_PROMPT,
timeout: int = 90,
max_tokens: int = 224,
temperature: float = 0.0,
disable_thinking: bool = True,
) -> dict[str, Any]:
system_prompt = default_system_prompt
if system_prompt_file:
system_prompt = system_prompt_file.read_text(encoding="utf-8")
system_prompt = with_fast_json_contract(system_prompt)
body = {
"model": model,
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt},
],
"temperature": temperature,
"max_tokens": max_tokens,
"response_format": {"type": "json_object"},
}
if disable_thinking:
# SGLang/Qwen reasoning models otherwise spend the entire planner budget
# in hidden reasoning_content and return no parseable JSON content.
body["chat_template_kwargs"] = {"enable_thinking": False, "thinking": False}
data = json.dumps(body).encode("utf-8")
headers = {"Content-Type": "application/json", "User-Agent": "kaiju-website-harness/0.1"}
api_key = os.environ.get(api_key_env)
if api_key:
headers["Authorization"] = f"Bearer {api_key}"
request = urllib.request.Request(
base_url.rstrip("/") + "/chat/completions",
data=data,
headers=headers,
method="POST",
)
try:
with urllib.request.urlopen(request, timeout=timeout) as response:
payload = json.loads(response.read().decode("utf-8", errors="replace"))
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", errors="replace")
raise RuntimeError(f"spec model HTTP {exc.code}: {detail[:1000]}") from exc
content = payload["choices"][0]["message"]["content"] or ""
return extract_json_object(content)
def request_website_spec(
*,
base_url: str,
model: str,
prompt: str,
api_key_env: str = "KAIJU_EVAL_API_KEY",
system_prompt_file: Path | None = None,
timeout: int = 90,
max_tokens: int = 224,
temperature: float = 0.0,
disable_thinking: bool = True,
) -> dict[str, Any]:
return request_json_spec(
base_url=base_url,
model=model,
prompt=prompt,
api_key_env=api_key_env,
system_prompt_file=system_prompt_file,
default_system_prompt=DEFAULT_SYSTEM_PROMPT,
timeout=timeout,
max_tokens=max_tokens,
temperature=temperature,
disable_thinking=disable_thinking,
)