CC: prepare generic VISION package candidate
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate the child-local VISION package candidate manifest and docs."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
CURRENT_TOOLS = [
|
||||
"vision.image_analyze",
|
||||
"vision.image_generate",
|
||||
"vision.palette_extract",
|
||||
"vision.background_cutout",
|
||||
]
|
||||
|
||||
PLANNED_TOOLS = [
|
||||
"vision.ocr_read",
|
||||
"vision.screenshot_observe",
|
||||
"vision.browser_observe",
|
||||
"vision.document_layout_read",
|
||||
"vision.chart_read",
|
||||
"vision.table_read",
|
||||
"vision.diagram_read",
|
||||
"vision.object_detect",
|
||||
"vision.visual_ground",
|
||||
"vision.segment",
|
||||
"vision.video_read",
|
||||
"vision.image_edit",
|
||||
]
|
||||
|
||||
ROUTE_TOOLS = {
|
||||
"POST /vlm/analyze": "vision.image_analyze",
|
||||
"POST /flux/render": "vision.image_generate",
|
||||
"POST /palette/extract": "vision.palette_extract",
|
||||
"POST /rembg/cutout": "vision.background_cutout",
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
errors: list[str] = []
|
||||
required = [
|
||||
"CONTEXT.md",
|
||||
"docs/VISION-PACKAGE-CANDIDATE.md",
|
||||
"candidate-manifests/vision-package-candidate.json",
|
||||
]
|
||||
for rel in required:
|
||||
if not (ROOT / rel).exists():
|
||||
errors.append(f"missing:{rel}")
|
||||
|
||||
manifest_path = ROOT / "candidate-manifests/vision-package-candidate.json"
|
||||
manifest = _read_json(manifest_path, errors)
|
||||
if manifest:
|
||||
_expect(manifest.get("canonical_sense") == "VISION", "canonical_sense_not_vision", errors)
|
||||
_expect(
|
||||
manifest.get("status") == "candidate_only_not_seed_installed",
|
||||
"status_not_candidate_only",
|
||||
errors,
|
||||
)
|
||||
_expect(manifest.get("current_tool_candidates") == CURRENT_TOOLS, "current_tools_mismatch", errors)
|
||||
_expect(manifest.get("planned_tool_candidates") == PLANNED_TOOLS, "planned_tools_mismatch", errors)
|
||||
route_map = {
|
||||
item.get("route"): item.get("tool_id")
|
||||
for item in manifest.get("current_route_adapters", [])
|
||||
}
|
||||
_expect(route_map == ROUTE_TOOLS, "route_tool_map_mismatch", errors)
|
||||
grant_policy = manifest.get("grant_policy", {})
|
||||
_expect(grant_policy.get("wildcard_grant_allowed") is False, "wildcard_grant_allowed", errors)
|
||||
_expect(
|
||||
grant_policy.get("package_default_grants_all_tools") is False,
|
||||
"package_default_grants_all_tools",
|
||||
errors,
|
||||
)
|
||||
_expect(grant_policy.get("tool_grants_are_granular") is True, "tool_grants_not_granular", errors)
|
||||
for key, value in manifest.get("non_authorization", {}).items():
|
||||
if value is not False:
|
||||
errors.append(f"non_authorization_enabled:{key}")
|
||||
forbidden = set(manifest.get("forbidden_research_owned_capabilities", []))
|
||||
for capability in ("web_search", "fetch_page", "extract_pdf", "deep_research", "research_synthesis"):
|
||||
if capability not in forbidden:
|
||||
errors.append(f"missing_forbidden_research_capability:{capability}")
|
||||
|
||||
_check_snippets(
|
||||
"docs/VISION-PACKAGE-CANDIDATE.md",
|
||||
[
|
||||
"Research reads sources; Vision sees media",
|
||||
"No Core promotion",
|
||||
"wildcard grant",
|
||||
"`vision.image_analyze`",
|
||||
"`vision.video_read`",
|
||||
],
|
||||
errors,
|
||||
)
|
||||
_check_snippets(
|
||||
"CONTEXT.md",
|
||||
["Visual Evidence", "Research Handoff", "BTE Compatibility Surface"],
|
||||
errors,
|
||||
)
|
||||
|
||||
result = {
|
||||
"ok": not errors,
|
||||
"validator": "vision-package-candidate-v1",
|
||||
"checked": required,
|
||||
"errors": errors,
|
||||
"warnings": [],
|
||||
}
|
||||
print(json.dumps(result, indent=2, sort_keys=True))
|
||||
return 0 if result["ok"] else 1
|
||||
|
||||
|
||||
def _read_json(path: Path, errors: list[str]) -> dict | None:
|
||||
if not path.exists():
|
||||
return None
|
||||
try:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
except json.JSONDecodeError as exc:
|
||||
errors.append(f"{path.relative_to(ROOT)}:json:{exc}")
|
||||
return None
|
||||
|
||||
|
||||
def _check_snippets(rel: str, snippets: list[str], errors: list[str]) -> None:
|
||||
path = ROOT / rel
|
||||
if not path.exists():
|
||||
return
|
||||
text = path.read_text(encoding="utf-8")
|
||||
for snippet in snippets:
|
||||
if snippet not in text:
|
||||
errors.append(f"{rel}:missing:{snippet}")
|
||||
|
||||
|
||||
def _expect(condition: bool, error: str, errors: list[str]) -> None:
|
||||
if not condition:
|
||||
errors.append(error)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user