feat: per-task model/effort/approval + relay models endpoint (#59)
Co-authored-by: host <host@test.local>
This commit is contained in:
1 parent
cff152c5a2
commit
8b2f3968dc
9 files changed
+521
-16
No files matched your search
+51
-11
@@ -133,6 +133,27 @@ def _default_runner(cmd):
|
||||
return subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
|
||||
def _approval_cli_args(approval):
|
||||
"""console 权限三档 → host.py 顶层全局参数。
|
||||
|
||||
请求批准(ask):approvalPolicy=untrusted,敏感操作先问;
|
||||
帮我批准(auto):approvalPolicy=never,自动批准,仍受
|
||||
工作区沙箱限制;完全访问(full):approvalPolicy=never
|
||||
+ sandbox=danger-full-access,不再受工作区限制(危险)。
|
||||
None 或未知值:不传参数,host.py 走配置文件默认值。
|
||||
docker 任务的配置沙箱本身就是 danger-full-access,所以
|
||||
full 档对 docker 任务无变化,变化只在 approvalPolicy。
|
||||
"""
|
||||
if approval == "ask":
|
||||
return ["--approval-policy", "untrusted"]
|
||||
if approval == "auto":
|
||||
return ["--approval-policy", "never"]
|
||||
if approval == "full":
|
||||
return ["--approval-policy", "never",
|
||||
"--sandbox", "danger-full-access"]
|
||||
return []
|
||||
|
||||
|
||||
class LocalExecutor(Executor):
|
||||
"""本地执行器:调 host.py CLI 子进程做执行封装。
|
||||
|
||||
@@ -166,13 +187,16 @@ class LocalExecutor(Executor):
|
||||
# 不改 host.py 的 dispatch/run 签名。
|
||||
self.model = model
|
||||
|
||||
def _base_command(self, model=None):
|
||||
def _base_command(self, model=None, effort=None, approval=None):
|
||||
cmd = [sys.executable, self.host_py_path]
|
||||
if self.config_path is not None:
|
||||
cmd += ["--config", self.config_path]
|
||||
effective = model or self.model
|
||||
if effective:
|
||||
cmd += ["--model", effective]
|
||||
if effort:
|
||||
cmd += ["--effort", effort]
|
||||
cmd += _approval_cli_args(approval)
|
||||
return cmd
|
||||
|
||||
def _command(self, task_id):
|
||||
@@ -256,7 +280,9 @@ class LocalExecutor(Executor):
|
||||
raise ValueError("任务缺少 id")
|
||||
if not task.get("prompt"):
|
||||
raise ValueError("任务缺少 prompt")
|
||||
cmd = self._base_command(model=task.get("model"))
|
||||
cmd = self._base_command(model=task.get("model"),
|
||||
effort=task.get("effort"),
|
||||
approval=task.get("approval"))
|
||||
cmd += ["dispatch", "--name", self._host_name(task),
|
||||
"--task", task["prompt"]]
|
||||
repo = task.get("repo") or self.repo
|
||||
@@ -277,7 +303,9 @@ class LocalExecutor(Executor):
|
||||
raise ValueError("任务缺少 id")
|
||||
if not task.get("prompt"):
|
||||
raise ValueError("任务缺少 prompt")
|
||||
cmd = self._base_command(model=task.get("model"))
|
||||
cmd = self._base_command(model=task.get("model"),
|
||||
effort=task.get("effort"),
|
||||
approval=task.get("approval"))
|
||||
cmd += ["run", "--task", self._host_name(task),
|
||||
"--prompt", task["prompt"]]
|
||||
return self._result_from_runner(
|
||||
@@ -303,7 +331,9 @@ class LocalExecutor(Executor):
|
||||
if not goal_text:
|
||||
raise ValueError("任务缺少 prompt")
|
||||
rounds = task.get("goal_max_rounds") or 3
|
||||
cmd = self._base_command(model=task.get("model"))
|
||||
cmd = self._base_command(model=task.get("model"),
|
||||
effort=task.get("effort"),
|
||||
approval=task.get("approval"))
|
||||
cmd += ["goal", "--name", self._host_name(task),
|
||||
"--task", goal_text, "--verify", verify,
|
||||
"--max-rounds", str(rounds)]
|
||||
@@ -371,7 +401,9 @@ class LocalExecutor(Executor):
|
||||
raise ValueError("任务缺少 id")
|
||||
if not task.get("prompt"):
|
||||
raise ValueError("任务缺少 prompt")
|
||||
cmd = self._base_command(model=task.get("model"))
|
||||
cmd = self._base_command(model=task.get("model"),
|
||||
effort=task.get("effort"),
|
||||
approval=task.get("approval"))
|
||||
if kind == "run":
|
||||
cmd += ["run", "--task", self._host_name(task),
|
||||
"--prompt", task["prompt"]]
|
||||
@@ -636,7 +668,8 @@ class DockerExecutor(Executor):
|
||||
cmd += list(inner_cmd)
|
||||
return cmd
|
||||
|
||||
def _inner_host_command(self, *args, model=None):
|
||||
def _inner_host_command(self, *args, model=None, effort=None,
|
||||
approval=None):
|
||||
"""容器内 host.py 命令:python3 /app/host.py [args...]。
|
||||
|
||||
config_path 若已给,容器内用 /app/<basename> 指代它——
|
||||
@@ -644,8 +677,9 @@ class DockerExecutor(Executor):
|
||||
本就放在项目目录);不在代码目录时不静默猜路径,
|
||||
由 _inner_host_command 的调用方在传 config 前自行确认。
|
||||
这里只做最简单的同名映射,不展开宿主绝对路径进容器。
|
||||
--model 是 host.py 的顶层全局参数,必须放在子命令之前,
|
||||
所以 model 在这里插到 --config 之后、子命令之前。
|
||||
--model/--effort/--approval-policy/--sandbox 是 host.py
|
||||
的顶层全局参数,必须放在子命令之前,所以在这里插到
|
||||
--config 之后、子命令之前。
|
||||
"""
|
||||
cmd = ["python3", "/app/host.py"]
|
||||
if self.config_path is not None:
|
||||
@@ -654,6 +688,9 @@ class DockerExecutor(Executor):
|
||||
effective = model or self.model
|
||||
if effective:
|
||||
cmd += ["--model", effective]
|
||||
if effort:
|
||||
cmd += ["--effort", effort]
|
||||
cmd += _approval_cli_args(approval)
|
||||
cmd += list(args)
|
||||
return cmd
|
||||
|
||||
@@ -790,7 +827,8 @@ class DockerExecutor(Executor):
|
||||
raise ValueError("任务缺少 prompt")
|
||||
inner = self._inner_host_command(
|
||||
"dispatch", "--name", self._host_name(task),
|
||||
"--task", task["prompt"], model=task.get("model"))
|
||||
"--task", task["prompt"], model=task.get("model"),
|
||||
effort=task.get("effort"), approval=task.get("approval"))
|
||||
repo = task.get("repo")
|
||||
if repo:
|
||||
inner += ["--repo", repo]
|
||||
@@ -808,7 +846,8 @@ class DockerExecutor(Executor):
|
||||
raise ValueError("任务缺少 prompt")
|
||||
inner = self._inner_host_command(
|
||||
"run", "--task", self._host_name(task),
|
||||
"--prompt", task["prompt"], model=task.get("model"))
|
||||
"--prompt", task["prompt"], model=task.get("model"),
|
||||
effort=task.get("effort"), approval=task.get("approval"))
|
||||
return self._run_in_container(task, inner, timeout=timeout,
|
||||
extra_mounts=extra_mounts,
|
||||
network=network,
|
||||
@@ -836,7 +875,8 @@ class DockerExecutor(Executor):
|
||||
inner = self._inner_host_command(
|
||||
"goal", "--name", self._host_name(task),
|
||||
"--task", goal_text, "--verify", verify,
|
||||
"--max-rounds", str(rounds), model=task.get("model"))
|
||||
"--max-rounds", str(rounds), model=task.get("model"),
|
||||
effort=task.get("effort"), approval=task.get("approval"))
|
||||
repo = task.get("repo")
|
||||
if repo:
|
||||
inner += ["--repo", repo]
|
||||
|
||||
Reference in new issue
Block a user