fix: Docker 断点续跑透传密钥环境(SUB2API_API_KEY 缺失空转修法)
Docker goal_resume_breakpoint_task 是唯一漏传 env=self._docker_cli_env(extra_env) 的 goal 方法:密钥只按名 进 docker run,值靠 CLI 环境带;漏传导致容器内 key 为空, 断点后每轮瞬间报 Missing SUB2API_API_KEY、无限空转 (2026-10-29 长任务实弹:274 轮全失败)。 单测:断言断点方法调用 runner 时 CLI 环境含密钥值, 命令里密钥只按名出现不带值。
This commit is contained in:
1 parent
c757810097
commit
5b2bdc9bbf
2 files changed
+43
-1
No files matched your search
+8
-1
@@ -1440,6 +1440,11 @@ class DockerExecutor(Executor):
|
||||
创建,续跑容器不带同一 --model-provider 时 Server 的
|
||||
provider 表里没有它,thread/resume 报 provider not found
|
||||
(2026-10-29 probe15 实弹)。
|
||||
密钥环境也必须透传:密钥类变量只按名进 docker run
|
||||
命令(-e SUB2API_API_KEY 不带值),值靠 _docker_cli_env
|
||||
放进 CLI 子进程环境;本方法此前漏传 env,容器内 key
|
||||
为空,每轮瞬间报 Missing SUB2API_API_KEY、无限空转
|
||||
(2026-10-29 长任务实弹:断点后 274 轮全失败)。
|
||||
"""
|
||||
if not isinstance(task, dict) or task.get("id") is None:
|
||||
raise ValueError("任务缺少 id")
|
||||
@@ -1453,7 +1458,9 @@ class DockerExecutor(Executor):
|
||||
network=network,
|
||||
extra_env=extra_env)
|
||||
try:
|
||||
result = self._call_runner(cmd, timeout=timeout)
|
||||
result = self._call_runner(
|
||||
cmd, timeout=timeout,
|
||||
env=self._docker_cli_env(extra_env))
|
||||
except Exception as exc:
|
||||
if exc.__class__.__name__ == "TimeoutExpired":
|
||||
self.kill_container(task)
|
||||
|
||||
@@ -490,6 +490,41 @@ class P1WorkerTestCase(unittest.TestCase):
|
||||
"--model-provider" in cmd and "sub2api" in cmd
|
||||
for cmd in self.calls))
|
||||
|
||||
# 12d. Docker 断点续跑必须把密钥环境透传给 runner:
|
||||
# 密钥类变量只按名进 docker run(-e SUB2API_API_KEY 不带值),
|
||||
# 值靠 _docker_cli_env 放进 CLI 子进程环境;漏传 env 时容器
|
||||
# 内 key 为空,每轮瞬间报 Missing SUB2API_API_KEY(2026-10-29
|
||||
# 长任务实弹:断点后 274 轮全失败、无限空转)。
|
||||
def test_docker_breakpoint_forwards_secret_env_to_runner(self):
|
||||
seen = {}
|
||||
|
||||
def dfake(cmd, timeout=None, env=None):
|
||||
seen["cmd"] = list(cmd)
|
||||
seen["env"] = env
|
||||
return SimpleNamespace(
|
||||
returncode=0,
|
||||
stdout="goal met after rounds\n"
|
||||
"usage input=10 cached=0 cache_write=0 output=5 "
|
||||
"reasoning=0 total=15 completions=1\n",
|
||||
stderr="")
|
||||
|
||||
task = {"id": "T1", "prompt": "orig goal"}
|
||||
docker = DockerExecutor(runner=dfake, env=FAKE_ENV)
|
||||
with mock.patch("cloud.sandbox.shutil.which",
|
||||
return_value="/usr/bin/docker"):
|
||||
docker.goal_resume_breakpoint_task(
|
||||
task, model_provider="sub2api",
|
||||
extra_env={"SUB2API_API_KEY": "secret-placeholder",
|
||||
"SUB2API_BASE_URL": "https://example.test"})
|
||||
# 命令里密钥只按名出现,不带值(不进 argv)。
|
||||
self.assertIn("SUB2API_API_KEY", seen["cmd"])
|
||||
self.assertNotIn("SUB2API_API_KEY=secret-placeholder",
|
||||
seen["cmd"])
|
||||
# runner 的 CLI 环境里必须有值,否则容器内 key 为空。
|
||||
self.assertIsNotNone(seen["env"])
|
||||
self.assertEqual(seen["env"].get("SUB2API_API_KEY"),
|
||||
"secret-placeholder")
|
||||
|
||||
# 13. 开新目标:goal-resume 带 --goal-text(旧行为保留)
|
||||
def test_goal_resume_new_goal_keeps_goal_text(self):
|
||||
calls = []
|
||||
|
||||
Reference in new issue
Block a user