-
Notifications
You must be signed in to change notification settings - Fork 415
Add HFSandboxBackend for the OpenCode harness #998
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
a5af5cb
451480a
70a061f
0702e0f
8cf9941
d749de5
764e02e
97c8ce0
00f5485
4e26cb6
f9fb122
2eab5d7
114f419
86bd067
968485d
acf3143
880ce5e
75f8824
1c46658
edfab8a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,21 +49,22 @@ | |
| build_opencode_json, | ||
| build_run_cmd, | ||
| instruction_path, | ||
| opencode_bin_path, | ||
| opencode_config_path, | ||
| proxy_dir, | ||
| proxy_log_path, | ||
| proxy_source_path, | ||
| proxy_trace_path, | ||
| system_prompt_path, | ||
| ) | ||
| from .sandbox.base import BgJob, SandboxBackend, SandboxHandle | ||
| from .task import OpenCodeTask | ||
|
|
||
|
|
||
| # Inside-sandbox proxy paths (Mode B). | ||
| # Mode B proxy port. In-sandbox paths derive from config.sandbox_home (opencode_runtime). | ||
| _PROXY_PORT = 7000 | ||
| _PROXY_TRACE_PATH = "/home/user/logs/agent/proxy_trace.jsonl" | ||
| _PROXY_LOG_PATH = "/home/user/logs/agent/proxy.log" | ||
|
|
||
| # Where the proxy source lives on disk (in this repo). Uploaded into the | ||
| # sandbox at /home/user/proxy/interception.py before each rollout, unless | ||
| # the sandbox was created from a template that already has it baked in. | ||
| # Local proxy source, uploaded to proxy_source_path(config) unless already baked in. | ||
| _PROXY_SOURCE_PATH = Path(__file__).parent / "sandbox" / "interception.py" | ||
|
|
||
|
|
||
|
|
@@ -240,54 +241,55 @@ def create( | |
| or getattr(getattr(sandbox, "raw", None), "sandbox_id", "?") | ||
| ) | ||
| _log.info("factory.create: sandbox=%s — bootstrapping…", sid) | ||
| # Any failure past here (bootstrap/proxy/agent) must tear the sandbox down. | ||
| try: | ||
| self._bootstrap_sandbox(sandbox, oc_task) | ||
|
Comment on lines
+244
to
246
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added in |
||
|
|
||
| base_url_override: str | None = None | ||
| proxy_trace_path: str | None = None | ||
| proxy_bg_job: BgJob | None = None | ||
| if self._mode == "transparent_proxy": | ||
| _log.info( | ||
| "factory.create: starting interception proxy on :%d → %s", | ||
| _PROXY_PORT, self._config.base_url, | ||
| ) | ||
| proxy_bg_job, base_url_override, proxy_trace_path = self._start_proxy( | ||
| sandbox | ||
| ) | ||
| _log.info("factory.create: proxy up at %s", base_url_override) | ||
| # Rewrite opencode.json so opencode points at the proxy. Force | ||
| # ``openai_compatible`` so opencode hits ``/v1/chat/completions`` | ||
| # (which the proxy serves) rather than provider-specific paths. | ||
| from .config import OpenCodeConfig as _OCC | ||
|
|
||
| proxy_cfg = _OCC( | ||
| **{ | ||
| **self._config.model_dump(), | ||
| "provider": "openai_compatible", | ||
| "base_url": base_url_override, | ||
| } | ||
| ) | ||
| sandbox.write_text( | ||
| opencode_config_path(self._config), | ||
| build_opencode_json(proxy_cfg), | ||
| ) | ||
|
|
||
| session = OpenCodeSession( | ||
| sandbox=sandbox, | ||
| config=self._config, | ||
| task=oc_task, | ||
| verifier=self._verifier, | ||
| base_url_override=base_url_override, | ||
| proxy_trace_path=proxy_trace_path, | ||
| proxy_bg_job=proxy_bg_job, | ||
| ) | ||
| session.start_agent() | ||
| return session | ||
| except Exception as exc: | ||
| _log.error("factory.create: bootstrap failed: %r", exc) | ||
| _log.error("factory.create: setup failed, killing sandbox: %r", exc) | ||
| sandbox.kill() | ||
| raise | ||
|
Comment on lines
288
to
294
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in |
||
|
|
||
| base_url_override: str | None = None | ||
| proxy_trace_path: str | None = None | ||
| proxy_bg_job: BgJob | None = None | ||
| if self._mode == "transparent_proxy": | ||
| _log.info( | ||
| "factory.create: starting interception proxy on :%d → %s", | ||
| _PROXY_PORT, self._config.base_url, | ||
| ) | ||
| proxy_bg_job, base_url_override, proxy_trace_path = self._start_proxy( | ||
| sandbox | ||
| ) | ||
| _log.info("factory.create: proxy up at %s", base_url_override) | ||
| # Rewrite opencode.json so opencode points at the proxy. Force | ||
| # ``openai_compatible`` so opencode hits ``/v1/chat/completions`` | ||
| # (which the proxy serves) rather than provider-specific paths. | ||
| from .config import OpenCodeConfig as _OCC | ||
|
|
||
| proxy_cfg = _OCC( | ||
| **{ | ||
| **self._config.model_dump(), | ||
| "provider": "openai_compatible", | ||
| "base_url": base_url_override, | ||
| } | ||
| ) | ||
| sandbox.write_text( | ||
| opencode_config_path(self._config), | ||
| build_opencode_json(proxy_cfg), | ||
| ) | ||
|
|
||
| session = OpenCodeSession( | ||
| sandbox=sandbox, | ||
| config=self._config, | ||
| task=oc_task, | ||
| verifier=self._verifier, | ||
| base_url_override=base_url_override, | ||
| proxy_trace_path=proxy_trace_path, | ||
| proxy_bg_job=proxy_bg_job, | ||
| ) | ||
| session.start_agent() | ||
| return session | ||
|
|
||
| # ------------------------------------------------------------------ | ||
| def _wait_for_sandbox_ready( | ||
| self, | ||
|
|
@@ -369,7 +371,7 @@ def _opencode_already_installed(self, sandbox: SandboxHandle) -> bool: | |
| """ | ||
| try: | ||
| r = sandbox.exec( | ||
| "/home/user/.opencode/bin/opencode --version", | ||
| f"{opencode_bin_path(self._config)} --version", | ||
| timeout=10, | ||
| ) | ||
| return r.exit_code == 0 | ||
|
|
@@ -440,9 +442,7 @@ def _start_proxy( | |
| Skips the pip install + source-upload steps when the prebaked | ||
| template already has them in place. | ||
| """ | ||
| proxy_already_present = sandbox.exists( | ||
| "/home/user/proxy/interception.py" | ||
| ) | ||
| proxy_already_present = sandbox.exists(proxy_source_path(self._config)) | ||
|
|
||
| if not proxy_already_present: | ||
| # Install proxy deps (idempotent on retries). | ||
|
|
@@ -457,18 +457,18 @@ def _start_proxy( | |
| ) | ||
| # Upload the proxy module into the sandbox. | ||
| sandbox.write_text( | ||
| "/home/user/proxy/interception.py", | ||
| proxy_source_path(self._config), | ||
| _PROXY_SOURCE_PATH.read_text(), | ||
| ) | ||
| sandbox.write_text("/home/user/proxy/__init__.py", "") | ||
| sandbox.write_text(f"{proxy_dir(self._config)}/__init__.py", "") | ||
|
|
||
| proxy_args = [ | ||
| "python", | ||
| "interception.py", | ||
| "--upstream-url", | ||
| self._config.base_url, | ||
| "--trace", | ||
| _PROXY_TRACE_PATH, | ||
| proxy_trace_path(self._config), | ||
| "--port", | ||
| str(_PROXY_PORT), | ||
| "--top-logprobs", | ||
|
|
@@ -487,9 +487,9 @@ def _start_proxy( | |
|
|
||
| quoted_proxy_args = " ".join(shlex.quote(arg) for arg in proxy_args) | ||
| proxy_cmd = ( | ||
| "cd /home/user/proxy && " | ||
| f"cd {shlex.quote(proxy_dir(self._config))} && " | ||
| f"{quoted_proxy_args} " | ||
| f"> {shlex.quote(_PROXY_LOG_PATH)} 2>&1" | ||
| f"> {shlex.quote(proxy_log_path(self._config))} 2>&1" | ||
| ) | ||
| proxy_env = {"OPENCODE_UPSTREAM_API_KEY": self._config.api_key} | ||
| proxy_job = sandbox.start_bg(proxy_cmd, envs=proxy_env) | ||
|
|
@@ -511,7 +511,7 @@ def _start_proxy( | |
| else: | ||
| log = "" | ||
| try: | ||
| log = sandbox.read_text(_PROXY_LOG_PATH) | ||
| log = sandbox.read_text(proxy_log_path(self._config)) | ||
| except Exception: | ||
| pass | ||
| proxy_job.kill() | ||
|
|
@@ -521,7 +521,7 @@ def _start_proxy( | |
| ) | ||
|
|
||
| base_url_override = f"http://127.0.0.1:{_PROXY_PORT}/v1" | ||
| return proxy_job, base_url_override, _PROXY_TRACE_PATH | ||
| return proxy_job, base_url_override, proxy_trace_path(self._config) | ||
|
|
||
|
|
||
| __all__ = [ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
black_boxis the intended default (safe minimal path; the GRPO/training path passestransparent_proxyexplicitly). The misleading(default)annotation ontransparent_proxyin the module docstring was removed in880ce5e8.