feat-chv-muse-p9: 推理与遥测受限通道(Sentinel 预审 + 遥测最小化 + 训练退出开关)
- 受限推理通道:cloud/chv/inference_channel.py(origin 解析、 allowlist、上限、Sentinel 预审),执行器 _run_with_vm 起 VM 前 必过 _inference_gate(allow 才继续,deny/ask 审计 + fail-closed, 任务级 extra_env 合并进预审);host.py 第二道防线(_server_command 的 origin 结构校验 + Server.request 超时/重试走 env)。 origin 规则:https 公网网关(只 443);http 只接受本地中继 (字面环回/私网 IP,如 http://172.17.0.1:28084,端口保留, 无 DNS、无重绑定,allowlist 与单次能力照常);未知 provider 标签走默认网关(与 _server_command 一致)。 - sentinel.py:从 P5(25bcd35)取的超集版本 + 推理连接器条目 (POST /v1/responses、GET /v1/models;scheme/端口只 https/443, 本地中继不走 evaluate,不把 http 加进表)。 - 遥测最小化:cloud/chv/telemetry.py(字段白名单、确定性采样、 落盘失败不影响任务、token 未回传记 null)+ 训练退出开关 (默认开,本项目决定;关后轨迹不再产生;损坏 fail-closed;变更审计)。 - 访问边界:docs/chv-access-boundary.md(运营访问口径 + 审计钩子, 不冒充技术不可访问;Confidential VM 明确未做)。 - 测试:cloud/tests/test_chv_muse_p9.py 51 组全过;5 个旧文件 的 make_exec 配假 origin + 假解析器(新 fail-closed 门要求的 测试配置);全量 cloud 套件 1918 OK(skipped=8)。 详见 docs/chv-muse-p9-gap.md(含自定清单与真机待验证清单)。
This commit is contained in:
1 parent
06c3e172fe
commit
c06ba50983
13 files changed
+1922
-11
No files matched your search
@@ -45,6 +45,7 @@ CODEX_BIN=.../bin/codex(start.sh 已自动优先用 bin/codex)。
|
||||
"""
|
||||
import argparse
|
||||
import contextlib
|
||||
import ipaddress
|
||||
import json
|
||||
import os
|
||||
import queue
|
||||
@@ -56,6 +57,7 @@ import threading
|
||||
import time
|
||||
import uuid
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from urllib.parse import urlparse
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
DEFAULT_STATE = os.path.join(HERE, "host-state.json")
|
||||
@@ -338,6 +340,81 @@ def _append_trace_marker(state_path, task_name, entry):
|
||||
pass
|
||||
|
||||
|
||||
def _inference_request_timeout(timeout):
|
||||
"""单次请求超时(秒):显式传参优先;否则读
|
||||
`CHV_INFERENCE_TIMEOUT`(默认 120,与旧默认值一致),
|
||||
非整数或越界(10–600)即 ValueError。"""
|
||||
if timeout is not None:
|
||||
return timeout
|
||||
raw = os.environ.get("CHV_INFERENCE_TIMEOUT", "").strip()
|
||||
if not raw:
|
||||
return 120
|
||||
try:
|
||||
value = int(raw)
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
"CHV_INFERENCE_TIMEOUT 必须是整数: %r" % raw)
|
||||
if not 10 <= value <= 600:
|
||||
raise ValueError(
|
||||
"CHV_INFERENCE_TIMEOUT 必须在 10-600 之间: %s" % value)
|
||||
return value
|
||||
|
||||
|
||||
def _inference_max_retries():
|
||||
"""BrokenPipe 重连上限(次):读 `CHV_INFERENCE_MAX_RETRIES`,
|
||||
默认 1(即现有行为 `range(2)`),非整数或越界(0–5)即
|
||||
ValueError。模型回合级重试仍是 worker 的 max_retries 语义,
|
||||
这里只 bound 传输层重连。"""
|
||||
raw = os.environ.get("CHV_INFERENCE_MAX_RETRIES", "").strip()
|
||||
if not raw:
|
||||
return 1
|
||||
try:
|
||||
value = int(raw)
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
"CHV_INFERENCE_MAX_RETRIES 必须是整数: %r" % raw)
|
||||
if not 0 <= value <= 5:
|
||||
raise ValueError(
|
||||
"CHV_INFERENCE_MAX_RETRIES 必须在 0-5 之间: %s" % value)
|
||||
return value
|
||||
|
||||
|
||||
def _validate_inference_origin(origin):
|
||||
"""sub2api origin 结构校验(feat-chv-muse-p9,第二道防线,
|
||||
只用标准库):https 公网网关(端口只 443)或 http 本地中继
|
||||
(字面环回/私网 IP,如 http://172.17.0.1:28084,见
|
||||
cloud/README.md 的 sub2api 网关一节);无 userinfo、有主机
|
||||
名;若 `CHV_INFERENCE_ALLOW_HOSTS` 已配,主机必须在其中。
|
||||
第一道防线是执行器的 Sentinel 预审(起 VM 前);这里拦的
|
||||
是“执行器没审就直接起 codex”的旁路(配置错了 loud fail,
|
||||
不静默跑向别处)。报错不带 origin 原值。"""
|
||||
parsed = urlparse(origin)
|
||||
scheme = parsed.scheme.lower()
|
||||
if "@" in parsed.netloc:
|
||||
raise ValueError("推理 origin 不得带 userinfo(凭证禁进 URL)")
|
||||
host = (parsed.hostname or "").lower()
|
||||
if not host:
|
||||
raise ValueError("推理 origin 无主机名")
|
||||
if scheme == "https":
|
||||
if parsed.port is not None and parsed.port != 443:
|
||||
raise ValueError("推理 origin 端口只接受 443")
|
||||
elif scheme == "http":
|
||||
try:
|
||||
addr = ipaddress.ip_address(host)
|
||||
except ValueError:
|
||||
raise ValueError("http 推理 origin 只接受本地中继"
|
||||
"(环回/私网 IP)")
|
||||
if not (addr.is_loopback or addr.is_private):
|
||||
raise ValueError("http 推理 origin 只接受本地中继"
|
||||
"(环回/私网 IP)")
|
||||
else:
|
||||
raise ValueError("推理 origin 必须 https(或本地中继 http)")
|
||||
allowed = os.environ.get("CHV_INFERENCE_ALLOW_HOSTS", "")
|
||||
hosts = {h.strip().lower() for h in allowed.split(",") if h.strip()}
|
||||
if hosts and host not in hosts:
|
||||
raise ValueError("推理 origin 主机不在 allowlist 内: %s" % host)
|
||||
|
||||
|
||||
def _server_command(codex_bin, provider=None):
|
||||
"""app-server 启动命令:-c 必须放在子命令之前。
|
||||
|
||||
@@ -361,6 +438,9 @@ def _server_command(codex_bin, provider=None):
|
||||
if not origin:
|
||||
raise RuntimeError(
|
||||
"provider=sub2api 但 SUB2API_BASE_URL 未配置")
|
||||
# feat-chv-muse-p9:origin 结构校验(第二道防线;第一道
|
||||
# 是执行器起 VM 前的 Sentinel 预审)。
|
||||
_validate_inference_origin(origin)
|
||||
cmd += [
|
||||
"-c", 'model_provider="sub2api"',
|
||||
"-c", 'model_providers.sub2api.name="Sub2API"',
|
||||
@@ -519,14 +599,19 @@ class Server:
|
||||
self.proc.stdin.write((json.dumps(obj) + "\n").encode())
|
||||
self.proc.stdin.flush()
|
||||
|
||||
def request(self, method, params=None, timeout=120):
|
||||
# BrokenPipe / 进程已死时重拉一次再重试(只重试一次,
|
||||
def request(self, method, params=None, timeout=None):
|
||||
# BrokenPipe / 进程已死时重拉再重试(重试上限见
|
||||
# `_inference_max_retries`,默认 1 次即现有行为;
|
||||
# turn/start 这类有副作用的调用由上层 resume 后再跑,
|
||||
# 这里重试的是 initialize/list/resume 等幂等请求为主;
|
||||
# 即使是 turn/start,重试也只发生在“还没收到任何响应”
|
||||
# 的 BrokenPipe 场景,server 死在 turn 中途时 _read_loop
|
||||
# 已退出、q.get 会超时而不是走这条路)。
|
||||
for attempt in range(2):
|
||||
# feat-chv-muse-p9:默认超时走 CHV_INFERENCE_TIMEOUT
|
||||
# (默认 120 秒,即旧默认值),显式传参优先。
|
||||
timeout = _inference_request_timeout(timeout)
|
||||
max_retries = _inference_max_retries()
|
||||
for attempt in range(1 + max_retries):
|
||||
rid = self.next_id
|
||||
self.next_id += 1
|
||||
q = queue.Queue()
|
||||
@@ -539,7 +624,7 @@ class Server:
|
||||
except (BrokenPipeError, OSError):
|
||||
with self.pending_lock:
|
||||
self.pending.pop(rid, None)
|
||||
if attempt == 0 and self.ensure_alive():
|
||||
if attempt < max_retries and self.ensure_alive():
|
||||
continue
|
||||
raise
|
||||
finally:
|
||||
|
||||
Reference in new issue
Block a user