Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Claude Code tasks running in an OpenYuanrong sandbox. The task image is
# remapped to the internal registry and the prebuilt Claude Code sidecar is
# mounted at /opt/claude-code. Its bin directory is placed on the sandbox PATH.
- name: swe_bench
sandbox:
provider: openyuanrong
runtime_timeout: 7200
image_map:
- from: "swebench/**"
to: "swr.cn-east-3.myhuaweicloud.com/openyuanrong/swe-bench-verified/**:v2"
sandbox_kwargs:
env:
PATH: /opt/claude-code/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
proxy_port: 38197
mounts:
- target: /opt/claude-code
image_url: swr.cn-east-3.myhuaweicloud.com/openyuanrong/claude-code-tool:latest
agent:
name: claude_code
max_turns: 200
run_timeout: 4800
model:
temperature: 1.0
top_p: 1.0
max_total_tokens: 131072
prompt_template: &swe_claude_prompt
- role: user
content: |-
Read the following task description and resolve the issue in the current directory.

Task description:
{problem_statement}

Inspect the relevant code, make the minimal correct changes, and verify the result with appropriate tests or checks. Do not modify tests or commit changes. When finished, briefly summarize what changed and how it was verified.

- name: swe_rebench
sandbox:
provider: openyuanrong
runtime_timeout: 7200
image_map:
- from: "swerebench/sweb.eval.x86_64.**"
to: "swr.cn-east-3.myhuaweicloud.com/openyuanrong/swe-rebench/**:latest"
sandbox_kwargs:
env:
PATH: /opt/claude-code/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
proxy_port: 38197
mounts:
- target: /opt/claude-code
image_url: swr.cn-east-3.myhuaweicloud.com/openyuanrong/claude-code-tool:latest
agent:
name: claude_code
max_turns: 200
run_timeout: 4800
model:
temperature: 1.0
top_p: 1.0
max_total_tokens: 131072
prompt_template: *swe_claude_prompt
64 changes: 64 additions & 0 deletions tests/uni_agent/sandbox/test_exec_error_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from __future__ import annotations

import asyncio
from types import SimpleNamespace

import pytest

Expand Down Expand Up @@ -240,6 +241,69 @@ def test_openyuanrong_recognizes_its_timeout():
assert sb._is_timeout_error(RuntimeError("other")) is False


@pytest.mark.cpu
@pytest.mark.level0
def test_openyuanrong_exec_merges_sandbox_and_command_environments():
from uni_agent.sandbox.openyuanrong import OpenyuanrongSandbox

class _Commands:
def __init__(self):
self.call = None

def run(self, command, *, envs, cwd, timeout):
self.call = {"command": command, "envs": envs, "cwd": cwd, "timeout": timeout}
return SimpleNamespace(exit_code=0, stdout="ok", stderr="")

commands = _Commands()
sb = OpenyuanrongSandbox(
image="python:3.12",
env={"PATH": "/opt/claude-code/bin:/usr/bin", "SHARED": "sandbox"},
)
sb._sandbox = SimpleNamespace(commands=commands)

result = asyncio.run(
sb._exec(
["claude", "--version"],
timeout=42,
workdir="/testbed",
env={"ANTHROPIC_BASE_URL": "http://gateway", "SHARED": "command"},
)
)

assert result == ExecResult(exit_code=0, stdout="ok", stderr="")
assert commands.call == {
"command": "claude --version",
"envs": {
"PATH": "/opt/claude-code/bin:/usr/bin",
"SHARED": "command",
"ANTHROPIC_BASE_URL": "http://gateway",
},
"cwd": "/testbed",
"timeout": 42,
}


@pytest.mark.cpu
@pytest.mark.level0
def test_openyuanrong_exec_preserves_nonzero_status_and_output():
from uni_agent.sandbox.openyuanrong import OpenyuanrongSandbox

class _Commands:
def run(self, command, *, envs, cwd, timeout):
assert command == "claude -p fix"
assert envs is None
assert cwd == "/testbed"
assert timeout == 60
return SimpleNamespace(exit_code=1, stdout="partial output", stderr="Claude Code failed")

sb = OpenyuanrongSandbox(image="python:3.12")
sb._sandbox = SimpleNamespace(commands=_Commands())

result = asyncio.run(sb._exec(["claude", "-p", "fix"], timeout=60, workdir="/testbed"))

assert result == ExecResult(exit_code=1, stdout="partial output", stderr="Claude Code failed")


# --------------------------- provider is_alive() liveness ---------------------------


Expand Down
23 changes: 19 additions & 4 deletions uni_agent/sandbox/openyuanrong.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,26 @@ async def _exec(
"""Run ``argv`` once via akernel ``Commands.run``."""
sb = self._require()
timeout_i = int(timeout) if timeout else 60
# ``Sandbox(env=...)`` configures the created container, but the
# akernel Commands API does not inherit those values for individual
# commands. Replay the sandbox defaults here so a sidecar-mounted
# executable (for example /opt/claude-code/bin/claude) is on PATH.
# Per-command values take precedence, matching the other providers.
command_env = dict(self.env or {})
if env:
command_env.update(env)
# commands.run is a blocking SDK poll; run it off the event loop.
result = await asyncio.to_thread(sb.commands.run, shlex.join(argv), envs=env, cwd=workdir, timeout=timeout_i)
result = await asyncio.to_thread(
sb.commands.run,
shlex.join(argv),
envs=command_env or None,
cwd=workdir,
timeout=timeout_i,
)
exit_code = int(result.exit_code)
stdout = _to_str(getattr(result, "stdout", ""))
stderr = _to_str(getattr(result, "stderr", ""))
if exit_code != 0:
raise RuntimeError(stderr or f"command exited with {exit_code}")
return ExecResult(exit_code=0, stdout=stdout, stderr=stderr)
# A command failure is a normal data-plane result. Returning it keeps
# the original status and output rather than turning it into Sandbox
# exec()'s generic alive-sandbox error (exit code 127).
return ExecResult(exit_code=exit_code, stdout=stdout, stderr=stderr)
Loading