refactor: audit batch 3 (I-3 capability verdict, I-4 policy merge)

- I-3: new models.evaluate_capability returning a CapabilityVerdict
  (ok/missing/unknown from check, known/unknown_missing from split,
  status/verb derivations); decide_routing, worker gateway check,
  worker persisted recheck, dry-run tier+gateway checks and server
  shadow endpoint all call it. Blocking rules stay at the call
  sites (gateway has no worker-default fallback, recheck blocks
  only on known, dry-run/shadow never block); all user-visible
  audit details, dry-run notes and shadow notes are byte-identical.
- I-4: new models.merge_policy_documents shared by
  load_routing_policy and dry_run_from_env; the old tiers-update
  block is kept verbatim (it was a no-op after update(): JSON
  tiers replace the whole map, file tiers absent from JSON are
  dropped -- preserved, not fixed, as that would be a behaviour
  change). dropped_tiers/partial_failures remain load-time
  records, not merge products.
- Tests: test_audit_batch3.py, 13 tests (verdict matrix + parity
  with check/split, decide/dry-run wording, merge boundaries +
  300 seeded random pairs + loader/dry-run agreement).
  Full suite: 1694 OK (skipped=11), baseline 1681 + 13, 0 failures.
This commit is contained in:
host committed 2026-11-04 18:02:23 +00:00
1 parent a7f77c8a98
commit 06e4e16bbb
5 files changed
+485 -68

No files matched your search

+17 -21
View File
@@ -165,10 +165,9 @@ from cloud.db import connect as _db_connect
from cloud.goal_command import parse_goal_command as _parse_goal_command
from cloud.logging_config import get_logger, log_event, setup_logging
from cloud.models import ModelRouter, RoutingPolicy, decide_routing, \
check_model_capabilities, classify_task, \
classify_task, evaluate_capability, \
is_fallbackable, load_routing_policy, parse_routing_policy, \
record_fallback, record_routing, \
split_model_capability_missing
record_fallback, record_routing
from cloud.routing_shadow import parse_worker_routing_detail as \
_parse_routing_detail
from cloud.auto_memory import AutoMemoryStore
@@ -7032,15 +7031,15 @@ class Worker:
if mode not in ("active", "shadow"):
return None
model = task.get("model")
ok, missing, unknown = check_model_capabilities(
verdict = evaluate_capability(
self.routing_policy.model_capabilities, model, requires)
if ok:
if verdict.ok:
return None
known, _unk = split_model_capability_missing(
self.routing_policy.model_capabilities, model, requires)
known = verdict.known_missing
detail = ("%s model=%s tier=%s mode=%s missing=%s unknown=%s"
% (task["id"], model, tier, mode,
",".join(missing) or "-", unknown))
",".join(verdict.missing) or "-",
verdict.unknown))
if mode == "shadow":
self._audit(task["project_id"],
"worker_capability_recheck_shadow", detail)
@@ -7444,17 +7443,17 @@ class Worker:
if _gw_requires:
_gw_mode = self.routing_policy.tier_mode(_gw_tier)
if _gw_mode in ("active", "shadow"):
_gw_ok, _gw_missing, _gw_unknown = \
check_model_capabilities(
self.routing_policy.model_capabilities,
task.get("model"), _gw_requires)
if not _gw_ok:
_gw_verdict = evaluate_capability(
self.routing_policy.model_capabilities,
task.get("model"), _gw_requires)
if not _gw_verdict.ok:
_gw_note = ("gateway model %s %s capability:"
" %s" % (
task.get("model"),
"cannot verify"
if _gw_unknown else "lacks",
", ".join(_gw_missing)))
if _gw_verdict.unknown
else "lacks",
", ".join(_gw_verdict.missing)))
# Audit fix 4(第三遍审计分档建议):
# 显式模型按 known/unknown 分档。known
# 是目录明确写了不满足(布尔 False、
@@ -7463,10 +7462,7 @@ class Worker:
# 无该模型、或条目缺字段,目录陈旧
# 不应惩罚用户的正确选择,只审计不拦。
# shadow 档两种情况都只审计。
_gw_known, _gw_unk = \
split_model_capability_missing(
self.routing_policy.model_capabilities,
task.get("model"), _gw_requires)
_gw_known = _gw_verdict.known_missing
_gw_block = False
_gw_error = None
if _explicit_model:
@@ -7483,8 +7479,8 @@ class Worker:
" missing=%s unknown=%s blocked=%s"
% (task_id, task.get("model"),
_gw_tier, _gw_mode,
",".join(_gw_missing) or "-",
_gw_unknown,
",".join(_gw_verdict.missing) or "-",
_gw_verdict.unknown,
_gw_blocked))
if _gw_blocked:
_gw_block = True