128 lines
4.7 KiB
Python
128 lines
4.7 KiB
Python
"""ReinOpenWeights — glas-harness adapter around the rein-openweights CLI.
|
|
|
|
Same shape as reins/rein_aharness.py: rein-openweights's `run` command
|
|
performs an entire bounded agentic session (credential acquisition,
|
|
OpenRouter tool-calling loop, commit verification) as one opaque unit —
|
|
no per-tool-call hooks exposed yet. dispatch_tool collapses the whole
|
|
run into a single call.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import shutil
|
|
import subprocess
|
|
from typing import Any
|
|
|
|
from glas_harness.contract import (
|
|
ExecutionSummary,
|
|
HarnessProfile,
|
|
Rein,
|
|
SandboxHandle,
|
|
ToolCall,
|
|
ToolResult,
|
|
)
|
|
from glas_harness.reins._shared import git_head, parse_json_object, write_task_file
|
|
|
|
|
|
class ReinOpenWeightsNotInstalled(RuntimeError):
|
|
pass
|
|
|
|
|
|
class ReinOpenWeights(Rein):
|
|
def __init__(
|
|
self,
|
|
cli_bin: str = "rein-openweights",
|
|
model: str | None = None,
|
|
max_turns: int | None = None,
|
|
budget_tokens: int | None = None,
|
|
tool_profile: str | None = None,
|
|
) -> None:
|
|
self.cli_bin = cli_bin
|
|
self.model = model
|
|
self.max_turns = max_turns
|
|
self.budget_tokens = budget_tokens
|
|
self.tool_profile = tool_profile
|
|
self._last_result: dict[str, Any] = {}
|
|
|
|
def _bin(self) -> str:
|
|
resolved = shutil.which(self.cli_bin)
|
|
if not resolved:
|
|
raise ReinOpenWeightsNotInstalled(
|
|
f"'{self.cli_bin}' not found on PATH — install rein-openweights first"
|
|
)
|
|
return resolved
|
|
|
|
def start_session(
|
|
self, profile: HarnessProfile, inputs: dict[str, str], sandbox: SandboxHandle
|
|
) -> dict[str, str]:
|
|
target_repo = (
|
|
inputs.get("target_repo")
|
|
or sandbox.reachability.get("workspace_dir")
|
|
or sandbox.reachability.get("remote_dir")
|
|
)
|
|
if not target_repo:
|
|
raise ValueError("no target_repo resolvable from inputs or sandbox reachability")
|
|
|
|
task_file = inputs.get("task_file") or write_task_file(
|
|
inputs["title"],
|
|
inputs["description"],
|
|
target_repo,
|
|
timeout_seconds=profile.limits.timeout_seconds or 600,
|
|
)
|
|
|
|
return {
|
|
"sandbox_id": sandbox.sandbox_id,
|
|
"task_file": task_file,
|
|
"target_repo": target_repo,
|
|
"head_before": git_head(target_repo),
|
|
}
|
|
|
|
def dispatch_tool(self, session: dict[str, str], tool_call: ToolCall) -> ToolResult:
|
|
argv = [self._bin(), "run", "--task-file", session["task_file"], "--no-hub"]
|
|
if self.model:
|
|
argv += ["--model", self.model]
|
|
if self.max_turns:
|
|
argv += ["--max-turns", str(self.max_turns)]
|
|
if self.budget_tokens:
|
|
argv += ["--budget-tokens", str(self.budget_tokens)]
|
|
if self.tool_profile:
|
|
argv += ["--tool-profile", self.tool_profile]
|
|
proc = subprocess.run(argv, capture_output=True, text=True)
|
|
ok = proc.returncode == 0
|
|
self._last_result = parse_json_object(proc.stdout)
|
|
return ToolResult(
|
|
ok=ok,
|
|
output=proc.stdout,
|
|
error=None if ok else (proc.stderr or self._last_result.get("reason")),
|
|
events_completeness="unavailable",
|
|
tokens_spent=self._last_result.get("tokens_spent"),
|
|
duration_s=self._last_result.get("execution_time_s"),
|
|
resolved_model=self._last_result.get("model") or self.model,
|
|
metadata={
|
|
"turns": self._last_result.get("turns"),
|
|
"tool_profile": self._last_result.get("tool_profile") or self.tool_profile,
|
|
},
|
|
)
|
|
|
|
def end_session(self, session: dict[str, str]) -> ExecutionSummary:
|
|
head_after = git_head(session["target_repo"])
|
|
committed = bool(head_after) and head_after != session.get("head_before")
|
|
reported_ok = bool(self._last_result.get("ok", committed))
|
|
reason = self._last_result.get("reason") or None
|
|
outcome = "succeeded" if committed and reported_ok else (
|
|
"refused" if reason == "no OpenRouter credential resolved" else "failed"
|
|
)
|
|
return ExecutionSummary(
|
|
commit_sha=head_after or None,
|
|
committed=committed,
|
|
outcome=outcome,
|
|
reason=reason,
|
|
tokens_spent=self._last_result.get("tokens_spent"),
|
|
duration_s=self._last_result.get("execution_time_s"),
|
|
resolved_model=self._last_result.get("model") or self.model,
|
|
artifacts=[head_after] if committed and head_after else [],
|
|
metadata={
|
|
"turns": self._last_result.get("turns"),
|
|
"tool_profile": self._last_result.get("tool_profile") or self.tool_profile,
|
|
},
|
|
)
|