fix: third-audit gate findings (explicit split, shadow explicit failures, N2-N5, N1 note)
Explicit gateway capability now splits known/unknown: known-missing explicit models fail-fast under active (same shape as the gateway default gate), unknown explicit models stay audit-only, shadow never blocks; explicit priority now protects which model the user chose, not that the chosen model must run. Shadow report reads worker_capability_gateway_explicit rows as explicit_capability_failures with a field note distinguishing the three explicit counts. N2: coordinator requeue CAS rowcount=0 branch covered via a proxy connection test (no refactor needed). N3: shadow endpoint test on the create_server + real HTTP skeleton (capability, truncated, and explicit-failure fields). N4: creation-layer rejection tests for tools/instructions/tool_approvals on goal/coordinator tasks. N5: model writeback failure logs a warning (best-effort kept). N1: writeback capability-recheck trade-off documented in comment only; recheck itself is a later batch.
This commit is contained in:
1 parent
621c08d10f
commit
18e3f1c82d
5 files changed
+660
-21
No files matched your search
@@ -246,6 +246,49 @@ def check_model_capabilities(capabilities, model, requires):
|
||||
return (not missing), sorted(missing), unknown
|
||||
|
||||
|
||||
def split_model_capability_missing(capabilities, model, requires):
|
||||
"""把 check_model_capabilities 的缺项拆成已知 / 未知两档。
|
||||
|
||||
Audit fix 4(第三遍审计分档建议):显式模型的网关检查不能
|
||||
只用 (missing, unknown) 的总口径——unknown 为真时可能是
|
||||
“目录里根本没有该模型”,也可能是“条目里某项能力没填”,
|
||||
两者决策不同。返回 (known_missing, unknown_missing):
|
||||
|
||||
- 目录里没有该模型:全部需求都算 unknown,known 为空;
|
||||
- 布尔能力明确为 False、或 context_tokens 明确低于需求:
|
||||
known(目录明确写了不满足);
|
||||
- 布尔能力未填(None)、或 context_tokens 未填:unknown。
|
||||
|
||||
与 check_model_capabilities 的口径一致:requires 里非布尔/
|
||||
非整数键被忽略,不计入任何一档。
|
||||
"""
|
||||
requires = requires or {}
|
||||
model = _clean_model_name(model)
|
||||
entry = (capabilities or {}).get(model) if model else None
|
||||
if entry is None:
|
||||
return [], sorted(
|
||||
k for k in requires
|
||||
if k in REQUIREMENT_BOOL_KEYS or k in REQUIREMENT_INT_KEYS)
|
||||
known = []
|
||||
unknown = []
|
||||
for key, value in requires.items():
|
||||
if key in REQUIREMENT_BOOL_KEYS:
|
||||
flag = entry.get(key)
|
||||
if flag is True:
|
||||
continue
|
||||
if flag is None:
|
||||
unknown.append(key)
|
||||
else:
|
||||
known.append(key)
|
||||
elif key in REQUIREMENT_INT_KEYS:
|
||||
context = entry.get("context_tokens")
|
||||
if context is None:
|
||||
unknown.append(key)
|
||||
elif context < value:
|
||||
known.append(key)
|
||||
return sorted(known), sorted(unknown)
|
||||
|
||||
|
||||
def prompt_len_bucket(prompt) -> str:
|
||||
"""Bucket a prompt by length for logging only, never for routing.
|
||||
|
||||
|
||||
Reference in new issue
Block a user