Skip to content

Commit 0b785f2

Browse files
committed
fix(agent-challenge): load file token and host-local NO_PHALA bench
Production mounts the challenge token via shared_token_file; raw-weight push now resolves it the same way auth does so the loop does not skip. NO_PHALA + cli docker backend runs own_runner in-process to avoid nested DooD path mismatch on master embed. Default epoch_seconds matches master sealer (360s).
1 parent 2c19a91 commit 0b785f2

4 files changed

Lines changed: 194 additions & 32 deletions

File tree

packages/challenges/agent-challenge/src/agent_challenge/evaluation/raw_weight_push.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,9 +635,30 @@ def maybe_build_push_client_from_settings(
635635
settings, "challenge_shared_token", None
636636
)
637637
token = str(shared) if shared else None
638+
if not token:
639+
# Production mounts the challenge token via shared_token_file; auth
640+
# already resolves that path — reuse the same loader so raw-weight push
641+
# does not silently skip when only the file is configured.
642+
try:
643+
from agent_challenge.sdk.auth import load_internal_token
644+
645+
loaded = load_internal_token(settings)
646+
token = str(loaded) if loaded else None
647+
except Exception:
648+
token = None
649+
if not token:
650+
token_file = getattr(settings, "shared_token_file", None)
651+
if token_file:
652+
from pathlib import Path as _Path
653+
654+
try:
655+
raw = _Path(str(token_file)).expanduser().read_text(encoding="utf-8").strip()
656+
except OSError:
657+
raw = ""
658+
token = raw or None
638659
if not token:
639660
return None
640-
epoch_seconds = int(getattr(settings, "epoch_seconds", 3600) or 3600)
661+
epoch_seconds = int(getattr(settings, "epoch_seconds", 360) or 360)
641662
interval_hint = float(getattr(settings, "raw_weight_push_interval_seconds", 30.0))
642663
slug = str(
643664
getattr(settings, "slug", None)

packages/challenges/agent-challenge/src/agent_challenge/evaluation/runner.py

Lines changed: 145 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,38 +1396,53 @@ async def _run_terminal_bench_task_durable(
13961396
# is held across the container execution await below.
13971397
await session.commit()
13981398
with _evaluation_workspace(submission, isolate=True) as agent_workspace:
1399-
spec = DockerRunSpec(
1400-
image=runner_image,
1401-
command=(
1402-
"bash",
1403-
"-lc",
1404-
_terminal_bench_script(job, task, plan=plan, backend=execution_backend),
1405-
),
1406-
mounts=(
1407-
DockerMount(
1408-
source=agent_workspace,
1409-
target="/workspace/agent",
1410-
read_only=False,
1399+
from agent_challenge.evaluation.no_phala import is_no_phala_enabled as _np
1400+
1401+
if _np() and settings.docker_backend in {"cli", "docker"}:
1402+
run = await asyncio.to_thread(
1403+
_run_no_phala_host_terminal_bench,
1404+
job=job,
1405+
task=task,
1406+
plan=plan,
1407+
agent_workspace=agent_workspace,
1408+
miner_env=miner_env,
1409+
gateway=gateway,
1410+
execution_backend=execution_backend,
1411+
timeout_seconds=settings.evaluation_timeout_seconds,
1412+
)
1413+
else:
1414+
spec = DockerRunSpec(
1415+
image=runner_image,
1416+
command=(
1417+
"bash",
1418+
"-lc",
1419+
_terminal_bench_script(job, task, plan=plan, backend=execution_backend),
14111420
),
1412-
DockerMount(
1413-
source=plan.jobs_dir,
1414-
target=str(plan.jobs_dir),
1415-
read_only=False,
1421+
mounts=(
1422+
DockerMount(
1423+
source=agent_workspace,
1424+
target="/workspace/agent",
1425+
read_only=False,
1426+
),
1427+
DockerMount(
1428+
source=plan.jobs_dir,
1429+
target=str(plan.jobs_dir),
1430+
read_only=False,
1431+
),
14161432
),
1417-
),
1418-
workdir="/workspace",
1419-
env={
1420-
**_terminal_bench_env(miner_env, gateway),
1421-
**_terminal_bench_stream_env(plan.attempt_id),
1422-
},
1423-
labels=_labels(job, submission, task),
1424-
limits=_terminal_bench_limits(),
1425-
)
1426-
run = await asyncio.to_thread(
1427-
executor.run,
1428-
spec,
1429-
timeout_seconds=settings.evaluation_timeout_seconds,
1430-
)
1433+
workdir="/workspace",
1434+
env={
1435+
**_terminal_bench_env(miner_env, gateway),
1436+
**_terminal_bench_stream_env(plan.attempt_id),
1437+
},
1438+
labels=_labels(job, submission, task),
1439+
limits=_terminal_bench_limits(),
1440+
)
1441+
run = await asyncio.to_thread(
1442+
executor.run,
1443+
spec,
1444+
timeout_seconds=settings.evaluation_timeout_seconds,
1445+
)
14311446
except Exception as exc:
14321447
# The attempt was committed ``running`` before the container await, so a
14331448
# failure here (executor/broker error, ``database is locked``, unexpected
@@ -1997,6 +2012,106 @@ def _own_runner_script(
19972012
""".strip()
19982013

19992014

2015+
2016+
def _run_no_phala_host_terminal_bench(
2017+
*,
2018+
job: EvaluationJob,
2019+
task: BenchmarkTask,
2020+
plan: TerminalBenchAttemptPlan,
2021+
agent_workspace: Path,
2022+
miner_env: Mapping[str, str] | None,
2023+
gateway: GatewayExecutionConfig | None,
2024+
execution_backend: str,
2025+
timeout_seconds: int,
2026+
) -> DockerRunResult:
2027+
"""NO_PHALA host-local path: run own_runner in this process namespace.
2028+
2029+
Avoids nested harbor-runner DooD under master embed (container bind paths are
2030+
not host paths for ``docker -v``). The validator already has docker.sock and
2031+
task images; own_runner spawns sibling task containers directly.
2032+
"""
2033+
import subprocess
2034+
2035+
from agent_challenge.evaluation.no_phala import is_no_phala_enabled
2036+
2037+
if not is_no_phala_enabled():
2038+
raise RuntimeError("no_phala host runner invoked while NO_PHALA is off")
2039+
if execution_backend != "own_runner":
2040+
raise ValueError(f"unsupported backend for no_phala host path: {execution_backend}")
2041+
2042+
task_id = str(task.metadata.get("task_id") or task.task_id)
2043+
bare = task_id.rsplit("/", 1)[-1]
2044+
cache_root = Path(
2045+
settings.own_runner_cache_root
2046+
or "/app/packages/challenges/agent-challenge/docker/canonical/live-task-cache"
2047+
)
2048+
digest = Path(
2049+
settings.own_runner_digest_manifest or "/app/golden/dataset-digest.json"
2050+
)
2051+
plan.jobs_dir.mkdir(parents=True, exist_ok=True)
2052+
plan.job_dir.mkdir(parents=True, exist_ok=True)
2053+
2054+
cmd = [
2055+
"python",
2056+
"-m",
2057+
"agent_challenge.evaluation.own_runner_backend",
2058+
"run",
2059+
"--job-dir",
2060+
str(plan.job_dir),
2061+
"--job-name",
2062+
plan.job_name,
2063+
"--jobs-dir",
2064+
str(plan.jobs_dir),
2065+
"--n-concurrent",
2066+
str(max(1, int(settings.harbor_n_concurrent or 1))),
2067+
"--agent-import-path",
2068+
settings.harbor_agent_import_path,
2069+
"--n-attempts",
2070+
"1",
2071+
"--task",
2072+
bare,
2073+
"--cache-root",
2074+
str(cache_root),
2075+
"--digest-manifest",
2076+
str(digest),
2077+
]
2078+
env = {
2079+
**dict(os.environ),
2080+
**_terminal_bench_env(miner_env, gateway),
2081+
"PYTHONPATH": f"{agent_workspace}:{os.environ.get('PYTHONPATH', '')}".rstrip(":"),
2082+
"DOCKER_HOST": os.environ.get("DOCKER_HOST") or "unix:///var/run/docker.sock",
2083+
"BASE_AGENT_PATH": str(agent_workspace),
2084+
}
2085+
if env["PYTHONPATH"].endswith(":"):
2086+
env["PYTHONPATH"] = env["PYTHONPATH"][:-1]
2087+
2088+
try:
2089+
proc = subprocess.run(
2090+
cmd,
2091+
cwd=str(agent_workspace),
2092+
capture_output=True,
2093+
text=True,
2094+
timeout=timeout_seconds,
2095+
check=False,
2096+
env=env,
2097+
)
2098+
return DockerRunResult(
2099+
container_name=f"no-phala-host-{plan.job_name}"[:120],
2100+
stdout=proc.stdout or "",
2101+
stderr=proc.stderr or "",
2102+
returncode=proc.returncode,
2103+
timed_out=False,
2104+
)
2105+
except subprocess.TimeoutExpired as exc:
2106+
return DockerRunResult(
2107+
container_name=f"no-phala-host-{plan.job_name}"[:120],
2108+
stdout=(exc.stdout or "") if isinstance(exc.stdout, str) else "",
2109+
stderr=(exc.stderr or "") if isinstance(exc.stderr, str) else "timed_out",
2110+
returncode=124,
2111+
timed_out=True,
2112+
)
2113+
2114+
20002115
def _terminal_bench_script(
20012116
job: EvaluationJob,
20022117
task: BenchmarkTask,

packages/challenges/agent-challenge/src/agent_challenge/sdk/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class ChallengeSettings(BaseSettings):
9898
raw_weight_push_freshness_seconds: int = Field(default=300, ge=30)
9999
raw_weight_push_timeout_seconds: float = Field(default=10.0, gt=0.0)
100100
# Epoch bucket size for push revision identity (seconds).
101-
epoch_seconds: int = Field(default=3600, ge=1)
101+
epoch_seconds: int = Field(default=360, ge=1)
102102

103103

104104
# Root stdlib logging level applied at every process entrypoint (the API app

packages/challenges/agent-challenge/tests/test_raw_weight_push_lifespan.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,29 @@ def test_raw_weight_push_interval_rejects_below_minimum() -> None:
191191
shared_token_file=None,
192192
raw_weight_push_interval_seconds=0.05,
193193
)
194+
195+
196+
def test_maybe_build_push_client_loads_token_from_shared_token_file(
197+
tmp_path: Path,
198+
) -> None:
199+
"""File-backed challenge token alone is enough to construct the push client."""
200+
201+
token_path = tmp_path / "challenge_token"
202+
token_path.write_text("file-backed-ac-token-value", encoding="utf-8")
203+
settings = ChallengeSettings(
204+
database_url=f"sqlite+aiosqlite:///{tmp_path / 'ac-push-file-token.sqlite3'}",
205+
shared_token=None,
206+
shared_token_file=str(token_path),
207+
raw_weight_push_enabled=True,
208+
master_base_url="http://master.test",
209+
epoch_seconds=360,
210+
)
211+
db = Database(settings.database_url)
212+
client = push_module.maybe_build_push_client_from_settings(
213+
settings=settings,
214+
database=db,
215+
)
216+
assert client is not None
217+
assert client.shared_token == "file-backed-ac-token-value"
218+
assert client.master_base_url == "http://master.test"
219+
assert client.challenge_slug == "agent-challenge"

0 commit comments

Comments
 (0)