feat: cloud 门禁/超时/成本/沙箱收敛为单一判定源
- 新增标准库叶子层 timeouts/gates/quota/sandbox_policy: 审批 3600 与单轮 600 各有名常量与环境覆盖层,已建项 expires_at 永不重算;目录/控制/合并状态门禁统一判定, 目录违规无任何覆盖参数;配额取数与比较唯一来源 (USD 先于无价、命中均为 >=、无价只计 actual 行); /tmp 隔离矩阵与失联清理判定纯函数化。 - 调用点委托:server/worker/automations/auto_memory 的 配额 SQL 与比较、server/tree/marker 的目录解析、 marker 的控制门、sandbox 的任务容器名正则、两处 300 秒 陈旧阈值默认值均收敛,别名与错误文案不变。 - 统一两个 P0 口径分歧:服务端无价计数由全部 usd IS NULL 行改为 actual-only;显式不限额用户在服务端准入亦不受 无价上限约束(与 worker 运行时一致)。 - 不新增门禁种类与 record 模式(主线无此实现);不改 skill_seed/skills/SKILL.md;不改 v1.1 契约与分支保护。 - 新增 test_arch_converge.py 18 项。
This commit is contained in:
1 parent
17fa493da7
commit
facc2a249a
16 files changed
+1070
-301
No files matched your search
@@ -0,0 +1,124 @@
|
||||
"""超时默认值的单一配置源。
|
||||
|
||||
背景:主线(origin/main)上同一个“3600 秒”以字面量散落在
|
||||
多处——审批队列(cloud/approvals.py)、协调员
|
||||
(cloud/coordinator.py)、接线层任务超时
|
||||
(cloud/harness2_tasks.py)、标记轮询上限
|
||||
(cloud/marker_tools.py)、服务端审批入口(cloud/server.py),
|
||||
另有单轮护栏 600 秒(host.py 的 turn_timeout 默认值,见下)
|
||||
与陈旧回收 300 秒(server.sweep_stale_tasks /
|
||||
worker._reap_kill_requests)。改一处漏一处是真实风险:
|
||||
本次收敛前,审批入口与队列模块的上下界就各写了一套。
|
||||
|
||||
本模块只依赖标准库,是叶子模块:任何 cloud 模块都可
|
||||
import 它而不引入循环。每个用途各有名常量,值即使相同
|
||||
也不共用一个名字——改审批默认绝不能顺带改掉轮询上限。
|
||||
|
||||
各常量与现状(改值前必须先读本段):
|
||||
- APPROVAL_DEFAULT_SECONDS = 3600:新审批项的默认等待
|
||||
秒数。覆盖层:环境变量 CLOUD_APPROVAL_TIMEOUT_SECONDS,
|
||||
仅当解析为 60..86400 的整数时生效,否则回落默认值。
|
||||
- APPROVAL_MIN_SECONDS / APPROVAL_MAX_SECONDS = 60 / 86400:
|
||||
服务端审批入口的校验区间(保持与旧行为一致)。
|
||||
- LONG_TIMEOUT_SECONDS = 3600:协调员、接线层任务超时、
|
||||
标记轮询上限三处共用的数值,各模块仍保留自己的常量名
|
||||
作为别名(调用点与测试 import 的名字不变)。
|
||||
- TURN_SECONDS = 600:host.py 单轮护栏的默认值。host.py
|
||||
不在 cloud/ 内,本轨文件边界不改它;这里只登记数值与
|
||||
含义,杜绝“审批 600 秒”的误读——主线上不存在 600 秒的
|
||||
审批超时,600 秒是单轮护栏,不是审批等待。
|
||||
- STALE_SWEEP_SECONDS = 300:陈旧回收与失联收殓共用的
|
||||
阈值默认值(server 与 worker 各保留自己的参数名)。
|
||||
- WORKER_ALIVE_SECONDS = 120:worker 注册表存活阈值。
|
||||
|
||||
已建审批项的迁移语义(冻结,不可悄悄改):
|
||||
- 本模块没有任何回填、改写 expires_at 的函数。默认值或
|
||||
环境变量变化只影响之后新建的审批项;已建项的 expires_at
|
||||
在创建时已落库,之后一律不动。过期扫荡仍按行上的
|
||||
expires_at 判定,绝不按当前默认值重算。
|
||||
- 服务端入口的校验与队列模块的创建语义各有分工,不可
|
||||
混用:服务端入口只接受 60..86400 的整数(见
|
||||
validate_server_timeout,显式 null、布尔值、浮点数、
|
||||
越界一律拒绝,与旧行为一致);队列模块级创建允许
|
||||
timeout_seconds=None(用默认值)与 0(不过期,见
|
||||
resolve_module_timeout),服务端入口永远传不出 0。
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
APPROVAL_DEFAULT_SECONDS = 3600
|
||||
APPROVAL_MIN_SECONDS = 60
|
||||
APPROVAL_MAX_SECONDS = 86400
|
||||
LONG_TIMEOUT_SECONDS = 3600
|
||||
TURN_SECONDS = 600
|
||||
STALE_SWEEP_SECONDS = 300
|
||||
WORKER_ALIVE_SECONDS = 120
|
||||
|
||||
|
||||
def approval_default_seconds(override=None):
|
||||
"""新审批项的默认等待秒数:显式参数 > 环境变量 > 默认值。
|
||||
|
||||
环境变量 CLOUD_APPROVAL_TIMEOUT_SECONDS 只有解析为
|
||||
60..86400 的整数时才生效;非法、越界、布尔语义一律
|
||||
回落默认值,绝不抛异常(默认值解析失败不能炸掉建项)。
|
||||
"""
|
||||
if override is not None:
|
||||
try:
|
||||
value = int(override)
|
||||
except (TypeError, ValueError):
|
||||
return APPROVAL_DEFAULT_SECONDS
|
||||
if isinstance(override, bool):
|
||||
return APPROVAL_DEFAULT_SECONDS
|
||||
if APPROVAL_MIN_SECONDS <= value <= APPROVAL_MAX_SECONDS:
|
||||
return value
|
||||
return APPROVAL_DEFAULT_SECONDS
|
||||
raw = os.environ.get("CLOUD_APPROVAL_TIMEOUT_SECONDS") or ""
|
||||
try:
|
||||
value = int(raw)
|
||||
except (TypeError, ValueError):
|
||||
return APPROVAL_DEFAULT_SECONDS
|
||||
if APPROVAL_MIN_SECONDS <= value <= APPROVAL_MAX_SECONDS:
|
||||
return value
|
||||
return APPROVAL_DEFAULT_SECONDS
|
||||
|
||||
|
||||
def validate_server_timeout(value):
|
||||
"""服务端审批入口的超时校验:返回合法整数,否则 ValueError。
|
||||
|
||||
与旧入口行为逐字一致:缺省由调用方先填默认值;显式
|
||||
null、布尔值、浮点数、非整数、越界一律拒绝,错误文案
|
||||
保持 "timeout_seconds must be 60..86400" 不变。
|
||||
"""
|
||||
if isinstance(value, bool) or not isinstance(value, int):
|
||||
raise ValueError("timeout_seconds must be 60..86400")
|
||||
if value < APPROVAL_MIN_SECONDS or value > APPROVAL_MAX_SECONDS:
|
||||
raise ValueError("timeout_seconds must be 60..86400")
|
||||
return value
|
||||
|
||||
|
||||
def resolve_module_timeout(value, default=None):
|
||||
"""队列模块级创建的超时解析:返回秒数或 None(不过期)。
|
||||
|
||||
None 用默认值;0 表示不过期(只允许模块级调用,服务
|
||||
端入口另有限制,永远传不出 0);负数、布尔值、非数字
|
||||
一律 ValueError。浮点数沿用旧行为:接受并按原值参与
|
||||
expires_at 计算(服务端入口不走这条路,不受影响)。
|
||||
"""
|
||||
if default is None:
|
||||
default = approval_default_seconds()
|
||||
if value is None:
|
||||
return default
|
||||
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
||||
raise ValueError("timeout_seconds invalid")
|
||||
if value < 0:
|
||||
raise ValueError("timeout_seconds invalid")
|
||||
if not value:
|
||||
return None
|
||||
return value
|
||||
|
||||
|
||||
def expires_at_for(now, timeout_seconds):
|
||||
"""由创建时刻与解析后的超时算 expires_at:不过期回 None。"""
|
||||
if not timeout_seconds:
|
||||
return None
|
||||
return now + timeout_seconds
|
||||
Reference in new issue
Block a user