승격과 복구 경로에 구체 모델이 하드코딩되어 카탈로그 변경에도 dispatcher 수정이 필요했다. 런타임 capability와 promotion을 카탈로그로 이동해 모델 교체를 설정 변경으로 한정한다.
72 lines
3 KiB
Python
72 lines
3 KiB
Python
#!/usr/bin/env python3
|
|
"""Driver-level validation for operator-defined execution targets."""
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
VALID_ADAPTERS = frozenset(
|
|
{"pi", "agy", "opencode", "claude-glm", "claude", "codex"}
|
|
)
|
|
|
|
|
|
def validate_target_contract(target, path: str, error_type) -> None:
|
|
if target.adapter not in VALID_ADAPTERS:
|
|
raise error_type(
|
|
f"{path}.adapter must be one of {sorted(VALID_ADAPTERS)}; "
|
|
"a new adapter requires dispatcher driver support"
|
|
)
|
|
local = target.adapter == "pi"
|
|
expected_class = "local_model" if local else "cloud_model"
|
|
if target.execution_class != expected_class:
|
|
raise error_type(
|
|
f"{path}: {target.adapter} targets must use {expected_class}"
|
|
)
|
|
if target.selfcheck_required != local:
|
|
raise error_type(
|
|
f"{path}: selfcheck_required must be {str(local).lower()} "
|
|
f"for {target.adapter} as the persisted-decision compatibility field"
|
|
)
|
|
if not isinstance(target.selfcheck_full_review, bool):
|
|
raise error_type(f"{path}: selfcheck.full_review must be a boolean")
|
|
if not isinstance(target.selfcheck_checklist_review, bool):
|
|
raise error_type(f"{path}: selfcheck.checklist_review must be a boolean")
|
|
if target.adapter == "pi":
|
|
if not target.target.startswith("iop/"):
|
|
raise error_type(f"{path}: pi target must start with iop/")
|
|
if target.thinking_level is None:
|
|
raise error_type(f"{path}: pi target requires thinking_level")
|
|
if target.reasoning_effort is not None or target.command_model is not None:
|
|
raise error_type(
|
|
f"{path}: pi target cannot set reasoning_effort or command_model"
|
|
)
|
|
elif target.native_session_resume:
|
|
raise error_type(
|
|
f"{path}: native_session_resume is only valid for pi"
|
|
)
|
|
elif target.thinking_level is not None:
|
|
raise error_type(f"{path}: thinking_level is only valid for pi")
|
|
if target.adapter == "agy" and (
|
|
target.reasoning_effort is not None or target.command_model is not None
|
|
):
|
|
raise error_type(
|
|
f"{path}: agy target cannot set reasoning_effort or command_model"
|
|
)
|
|
if target.adapter == "opencode" and (
|
|
target.reasoning_effort not in {"medium", "high", "max"}
|
|
or target.command_model is None
|
|
):
|
|
raise error_type(
|
|
f"{path}: opencode target requires command_model and "
|
|
"medium|high|max reasoning_effort"
|
|
)
|
|
if target.adapter == "claude-glm" and (
|
|
target.command_model is None or target.reasoning_effort != "xhigh"
|
|
):
|
|
raise error_type(
|
|
f"{path}: claude-glm target requires command_model and "
|
|
"xhigh reasoning_effort"
|
|
)
|
|
if target.adapter in {"claude", "codex"} and target.reasoning_effort is None:
|
|
raise error_type(
|
|
f"{path}: {target.adapter} target requires reasoning_effort"
|
|
)
|