"""Grok CLI backend.""" import json import os import shutil import tempfile from tai_pkg.clirun import Cli from tai_pkg.errors import BackendError from tai_pkg.opencode import Opencode __all__ = ["Grok"] class Grok: """Grok.""" @staticmethod def grok_binary(): return os.environ.get("GROK_BIN") or shutil.which("grok") or shutil.which("grokcli") @staticmethod def grok_parse_output(stdout): try: data = json.loads(stdout or "") except ValueError: return Opencode.opencode_parse(stdout) if isinstance(data, dict): if data.get("is_error") or data.get("error"): detail = data.get("error") or data.get("result") or data.get("text") or "unknown error" raise BackendError("grok: %s" % str(detail)[:300]) if isinstance(data.get("content"), str) or isinstance(data.get("tool_calls"), list): if "content" in data or "tool_calls" in data: return data for key in ("text", "result", "output", "answer", "content"): value = data.get(key) if isinstance(value, str) and value.strip(): return Opencode.opencode_parse(value) return Opencode.opencode_parse(stdout) @staticmethod def grok_run(prompt, timeout): binary = Grok.grok_binary() if not binary: raise BackendError("grok CLI not found (GROK_BIN or PATH)") with tempfile.TemporaryDirectory(prefix="tai-grok-") as folder: prompt_path = os.path.join(folder, "prompt.txt") with open(prompt_path, "w", encoding="utf-8") as handle: handle.write(prompt) variants = [ [binary, "--prompt-file", prompt_path, "--output-format", "json", "--no-auto-update"], [binary, "--prompt-file", prompt_path, "--output-format", "json"], [binary, "-p", prompt, "--output-format", "json"], [binary, "-p", prompt], ] last_error = "" for argv in variants: done = Cli._cli_run(argv, None, timeout, "grok") if done.returncode != 0 and Cli._cli_unknown_flag(done.stderr) and argv is not variants[-1]: last_error = (done.stderr or "").strip()[:200] continue if done.returncode != 0 and not (done.stdout or "").strip(): blob = ((done.stderr or "") + "\n" + (done.stdout or "")).strip() if Cli._cli_auth_failure(blob): raise BackendError("grok not authenticated (set XAI_API_KEY or run grok login): %s" % blob[:200]) raise BackendError("grok exit %d: %s" % (done.returncode, blob[:300] or last_error or "no output")) if not (done.stdout or "").strip(): blob = (done.stderr or "").strip() raise BackendError("grok returned no output: %s" % (blob[:300] or last_error or "empty stdout")) return done.stdout or "" raise BackendError("grok failed: %s" % (last_error or "no usable output")) @staticmethod def grok_health(): binary = Grok.grok_binary() if not binary: return "missing: grok CLI not found" return Cli._cli_version_only_health(binary, "grok")