rosdiff / api /zen.py
Chandrakiran-08
Deploy to HF Spaces
8501103
Raw History Blame Contribute Delete
4.03 kB
"""Free models through OpenCode Zen, used as a chat model (for debugging repairs) without OpenRouter.
`ZenLLM` speaks the same `complete()` interface as the OpenRouter client, but runs `opencode run` with only the
OpenCode Zen provider configured and every tool denied: it reads the conversation and answers, nothing else.
`WithFreeModels` sends calls for `opencode/...` models to it and everything else to the paid client.
"""
from __future__ import annotations
import time
from collections.abc import Sequence
from .llm import LLM, Completion, LLMError, Message
from .opencode_runner import OpenCodeError, OpenCodeWorkspace, find_opencode
from .routing import is_free
def zen_config(settings, model_id: str) -> dict:
"""An OpenCode config with only the Zen provider and one free model, and no tools."""
provider, _, model = model_id.partition("/")
options = {"apiKey": "{env:ZEN_API_KEY}"} if settings.zen_api_key else {}
return {
"$schema": "https://opencode.ai/config.json",
"model": model_id,
"provider": {provider: {"options": options, "models": {model: {"name": model}}}},
"permission": {
"edit": "deny",
"bash": "deny",
"webfetch": "deny",
"websearch": "deny",
"external_directory": "deny",
},
"autoupdate": False,
"share": "disabled",
}
def zen_text(events: list[dict]) -> str:
texts = []
for e in events:
part = e.get("part") or {}
if (e.get("type") == "text" or part.get("type") == "text") and isinstance(part.get("text"), str):
texts.append(part["text"])
return "\n".join(texts).strip()
def _transcript(messages: Sequence[Message]) -> str:
"""OpenCode takes one message per run, so the conversation is passed as a labelled transcript."""
parts = []
for m in messages:
label = {"system": "INSTRUCTIONS", "user": "USER", "assistant": "YOUR EARLIER ANSWER"}.get(m.role, m.role)
parts.append(f"### {label}\n{m.content}")
parts.append("### Reply to the last USER message now, following the INSTRUCTIONS exactly.")
return "\n\n".join(parts)
class ZenLLM:
"""A free OpenCode Zen model as a chat model. Raises LLMError on any failure, so callers can fall back."""
def __init__(self, settings, model: str | None = None):
self.settings = settings
self.model = model or settings.debug_model
def complete(self, messages: Sequence[Message], temperature: float = 0.2, model: str | None = None) -> Completion:
model_id = model or self.model
if not find_opencode(self.settings):
raise LLMError("OpenCode is not installed, so the free model is unavailable")
provider, _, name = model_id.partition("/")
start = time.monotonic()
try:
with OpenCodeWorkspace(self.settings, config=zen_config(self.settings, model_id)) as ws:
run = ws.run(_transcript(messages), context="", model=name, provider=provider)
except OpenCodeError as e:
raise LLMError(f"free model: {e}") from e
if run.error:
raise LLMError(f"free model: {run.error}")
text = zen_text(run.events)
if not text:
raise LLMError("free model returned no text")
usage = {**run.usage(), "cost": 0.0}
return Completion(text, model_id, int((time.monotonic() - start) * 1000), usage)
class WithFreeModels:
"""Routes `opencode/...` models to the free client and everything else to the paid one."""
def __init__(self, paid: LLM, free: LLM):
self.paid, self.free = paid, free
self.model = paid.model
def complete(self, messages: Sequence[Message], temperature: float = 0.2, model: str | None = None) -> Completion:
if is_free(model):
return self.free.complete(messages, temperature, model=model)
return self.paid.complete(messages, temperature, model=model) if model else self.paid.complete(messages, temperature)