Wire VedAstro status and live quality gate

This commit is contained in:
732642856
2026-06-29 03:59:30 +08:00
parent 8466e3a71c
commit a07f91c88b
11 changed files with 693 additions and 25 deletions
+52 -1
View File
@@ -98,6 +98,7 @@ QUALITY_GATE_PROFILES = {
"skip_dasha_audit": True,
"skip_oracle_audit": True,
"skip_local_accuracy_report": True,
"skip_vedastro_live": True,
},
"browser": {
"skip_slow": True,
@@ -110,6 +111,7 @@ QUALITY_GATE_PROFILES = {
"skip_dasha_audit": True,
"skip_oracle_audit": True,
"skip_local_accuracy_report": True,
"skip_vedastro_live": True,
},
"release": {
"skip_slow": False,
@@ -122,6 +124,7 @@ QUALITY_GATE_PROFILES = {
"skip_dasha_audit": False,
"skip_oracle_audit": False,
"skip_local_accuracy_report": False,
"skip_vedastro_live": True,
},
"accuracy": {
"skip_slow": True,
@@ -134,6 +137,20 @@ QUALITY_GATE_PROFILES = {
"skip_dasha_audit": False,
"skip_oracle_audit": False,
"skip_local_accuracy_report": False,
"skip_vedastro_live": True,
},
"vedastro-live": {
"skip_slow": True,
"skip_yoga_logic": True,
"skip_frontend_runtime": True,
"skip_frontend_click": True,
"frontend_click_mode": "core",
"check_release_hygiene": False,
"skip_real_cases": True,
"skip_dasha_audit": True,
"skip_oracle_audit": True,
"skip_local_accuracy_report": True,
"skip_vedastro_live": False,
},
}
@@ -341,6 +358,36 @@ def release_hygiene_check() -> None:
print("release_hygiene_check ok: no release-critical product files are untracked")
def run_vedastro_live_smoke() -> None:
print("\n== VedAstro live adapter smoke ==")
endpoint = os.environ.get("VEDASTRO_API_ENDPOINT", "").strip()
network_enabled = os.environ.get("VEDASTRO_ENABLE_NETWORK", "").strip().lower() in {"1", "true", "yes"}
if not endpoint or not network_enabled:
print(json.dumps({
"status": "blocked",
"reason": "vedastro_live_endpoint_or_network_flag_missing",
"required_env": {
"endpoint": "VEDASTRO_API_ENDPOINT",
"network": "VEDASTRO_ENABLE_NETWORK",
},
"boundary": "Default CI stays deterministic; configure both env vars to run a real VedAstro live smoke.",
}, ensure_ascii=False, indent=2))
return
run([
PYTHON,
"scripts/vedastro_service_adapter.py",
"--range-scan",
"--domain",
"career",
"--case",
"beijing_first_use_demo",
"--start-date",
"2026-01-01",
"--end-date",
"2026-12-31",
])
def compile_targets() -> None:
print("\n== Compile core Python files ==")
targets: list[Path] = []
@@ -386,6 +433,7 @@ def run_profile(args: argparse.Namespace) -> dict:
"skip_dasha_audit",
"skip_oracle_audit",
"skip_local_accuracy_report",
"skip_vedastro_live",
]:
if getattr(args, key):
profile[key] = True
@@ -396,7 +444,7 @@ def run_profile(args: argparse.Namespace) -> dict:
def main() -> int:
parser = argparse.ArgumentParser(description="Run Jyotish skill quality gate")
parser.add_argument("--profile", choices=["quick", "browser", "release", "accuracy"], default="browser", help="Quality gate profile: quick, browser, release, or accuracy")
parser.add_argument("--profile", choices=["quick", "browser", "release", "accuracy", "vedastro-live"], default="browser", help="Quality gate profile: quick, browser, release, accuracy, or vedastro-live")
parser.add_argument("--skip-slow", action="store_true", help="Skip slow golden-case regressions")
parser.add_argument("--skip-yoga-logic", action="store_true", help="Skip Yoga logic comparison report refresh")
parser.add_argument("--skip-frontend-runtime", action="store_true", help="Skip frontend build and runtime smoke")
@@ -405,6 +453,7 @@ def main() -> int:
parser.add_argument("--skip-dasha-audit", action="store_true", help="Skip Dasha reference-drift audit")
parser.add_argument("--skip-oracle-audit", action="store_true", help="Skip combined Dasha/Shadbala external oracle boundary audit")
parser.add_argument("--skip-local-accuracy-report", action="store_true", help="Skip consolidated local accuracy report")
parser.add_argument("--skip-vedastro-live", action="store_true", help="Skip optional VedAstro live endpoint smoke")
parser.add_argument("--frontend-click-mode", choices=["core", "mobile", "offline", "pdf", "workspace", "mobile-trust", "import-files", "all"], default=None, help="Browser click smoke mode for browser/release profiles")
parser.add_argument("--frontend-click-timeout", type=int, default=240, help="Timeout seconds for browser click smoke")
parser.add_argument("--all-tests", action="store_true", help="Run every pytest file, including optional-dependency suites")
@@ -449,6 +498,8 @@ def main() -> int:
run([PYTHON, "scripts/validate_logic_v2.py"], optional=True)
if not profile["skip_local_accuracy_report"]:
run([PYTHON, "scripts/local_accuracy_report.py", "--format", "json"])
if not profile["skip_vedastro_live"]:
run_vedastro_live_smoke()
print("\nQuality gate passed.")
return 0
+150 -22
View File
@@ -9,12 +9,15 @@ workspace can evolve from research notes to an executable adapter contract.
from __future__ import annotations
import argparse
import hashlib
import json
import os
import socket
import time
from pathlib import Path
from typing import Any
from urllib import request, error
from urllib.parse import urlparse
ROOT = Path(__file__).resolve().parents[1]
@@ -195,12 +198,14 @@ RANGE_SCAN_SIGNAL_METADATA = {
}
DEFAULT_TIMEOUT_SECONDS = 120
TIMEOUT_ENV = "VEDASTRO_TIMEOUT_SECONDS"
BACKOFF_ENV = "VEDASTRO_RETRY_BACKOFF_SECONDS"
RETRY_POLICY = {
"max_attempts": 2,
"backoff_seconds": 1,
"retry_on": ["timeout", "429", "502", "503", "504"],
}
ALLOW_NETWORK_ENV = "VEDASTRO_ENABLE_NETWORK"
ARTIFACT_DIR = ROOT / "scratch" / "local" / "vedastro_adapter"
def _timeout_seconds() -> float:
@@ -213,6 +218,57 @@ def _timeout_seconds() -> float:
return DEFAULT_TIMEOUT_SECONDS
def _backoff_seconds() -> float:
raw = os.environ.get(BACKOFF_ENV, "").strip()
if not raw:
return float(RETRY_POLICY["backoff_seconds"])
try:
return max(0.0, float(raw))
except ValueError:
return float(RETRY_POLICY["backoff_seconds"])
def _json_bytes(payload: dict[str, Any]) -> bytes:
return json.dumps(payload, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
def _hash_payload(payload: dict[str, Any]) -> str:
return hashlib.sha256(_json_bytes(payload)).hexdigest()
def _utc_timestamp() -> str:
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
def _endpoint_host(endpoint: str) -> str:
parsed = urlparse(endpoint)
return parsed.netloc or endpoint
def _artifact_path(operation: str, request_hash: str, response_hash: str) -> Path:
ARTIFACT_DIR.mkdir(parents=True, exist_ok=True)
filename = f"{operation}-{request_hash[:12]}-{response_hash[:12]}.json"
return ARTIFACT_DIR / filename
def _repo_relative(path: Path) -> str:
try:
return str(path.relative_to(ROOT))
except ValueError:
return str(path)
def _write_artifact(result: dict[str, Any]) -> str:
metadata = result.get("source_metadata") or {}
artifact = _artifact_path(
str(metadata.get("operation") or result.get("operation") or "calculation"),
str(metadata.get("request_hash") or "no-request-hash"),
str(metadata.get("response_hash") or "no-response-hash"),
)
artifact.write_text(json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True), encoding="utf-8")
return _repo_relative(artifact)
def schema() -> dict[str, Any]:
request_example = {
**PARITY_CASES["beijing_first_use_demo"],
@@ -321,10 +377,15 @@ def schema() -> dict[str, Any]:
"external_service": True,
"required_fields": [
"endpoint",
"endpoint_host",
"transport",
"provenance_mode",
"retry_policy",
"timeout_seconds",
"request_hash",
"response_hash",
"called_at",
"artifact_path",
],
},
}
@@ -382,8 +443,44 @@ def _external_technique_preview(
}
def _normalize_success(payload: dict[str, Any], endpoint: str) -> dict[str, Any]:
def _base_live_metadata(
endpoint: str,
request_preview: dict[str, Any],
payload: dict[str, Any],
operation: str,
attempt_count: int = 1,
retry_error_codes: list[int] | None = None,
) -> dict[str, Any]:
return {
"transport": "http_json_service_boundary",
"endpoint": endpoint,
"endpoint_host": _endpoint_host(endpoint),
"method": "POST",
"operation": operation,
"provenance_mode": "external_service_candidate",
"timeout_seconds": _timeout_seconds(),
"retry_policy": {**RETRY_POLICY, "backoff_seconds": _backoff_seconds()},
"network_execution_env": ALLOW_NETWORK_ENV,
"called_at": _utc_timestamp(),
"request_hash": _hash_payload(request_preview),
"response_hash": _hash_payload(payload),
"attempt_count": attempt_count,
"retry_error_codes": retry_error_codes or [],
}
def _normalize_success(
payload: dict[str, Any],
endpoint: str,
request_preview: dict[str, Any],
attempt_count: int = 1,
retry_error_codes: list[int] | None = None,
) -> dict[str, Any]:
metadata = {
**_base_live_metadata(endpoint, request_preview, payload, "calculation", attempt_count, retry_error_codes),
**(payload.get("source_metadata") or {}),
}
result = {
"backend": "vedastro_service_adapter_candidate",
"available": True,
"status": "ok",
@@ -391,15 +488,10 @@ def _normalize_success(payload: dict[str, Any], endpoint: str) -> dict[str, Any]
"node_policy": payload.get("node_policy"),
"body_list": payload.get("body_list"),
"bodies": payload.get("bodies"),
"source_metadata": {
"transport": "http_json_service_boundary",
"endpoint": endpoint,
"provenance_mode": "external_service_candidate",
"timeout_seconds": _timeout_seconds(),
"retry_policy": RETRY_POLICY,
**(payload.get("source_metadata") or {}),
},
"source_metadata": metadata,
}
result["source_metadata"]["artifact_path"] = _write_artifact(result)
return result
def _normalize_external_technique_success(
@@ -445,6 +537,8 @@ def _normalize_range_scan_success(
payload: dict[str, Any],
endpoint: str,
request_preview: dict[str, Any],
attempt_count: int = 1,
retry_error_codes: list[int] | None = None,
) -> dict[str, Any]:
# Handle actual VedAstro response format: {"Status": "Pass", "Payload": [...]}
if payload.get("Status") == "Pass":
@@ -461,6 +555,7 @@ def _normalize_range_scan_success(
allowed_ids = allowlist.get("event_ids", set())
allowed_tags = allowlist.get("tags", set())
original_event_count = len(events)
evidence_ledger = []
for index, event in enumerate(events, start=1):
if not isinstance(event, dict):
@@ -508,7 +603,16 @@ def _normalize_range_scan_success(
"tags": top.get("tags") or [],
}
return {
metadata = {
**_base_live_metadata(endpoint, request_preview, payload, "range_scan", attempt_count, retry_error_codes),
"vedastro_event_method": request_preview.get("vedastro_event_method"),
"allowlist_domain": domain,
"allowlist_event_count": len(evidence_ledger),
"filtered_event_count": len(evidence_ledger),
"raw_event_count": original_event_count,
**(payload.get("source_metadata") or {}),
}
result = {
"backend": "vedastro_service_adapter_candidate",
"available": True,
"status": "ok",
@@ -518,15 +622,10 @@ def _normalize_range_scan_success(
"event_count": len(evidence_ledger),
"top_event": top_event,
"evidence_ledger": evidence_ledger,
"source_metadata": {
"transport": "http_json_service_boundary",
"endpoint": endpoint,
"provenance_mode": "external_service_candidate",
"timeout_seconds": _timeout_seconds(),
"retry_policy": RETRY_POLICY,
**(payload.get("source_metadata") or {}),
},
"source_metadata": metadata,
}
result["source_metadata"]["artifact_path"] = _write_artifact(result)
return result
def _source_metadata(endpoint: str) -> dict[str, Any]:
@@ -553,6 +652,35 @@ def _post_json(endpoint: str, request_preview: dict[str, Any]) -> dict[str, Any]
return json.loads(raw)
def _retry_status_codes() -> set[int]:
codes = set()
for value in RETRY_POLICY.get("retry_on", []):
try:
codes.add(int(str(value)))
except ValueError:
continue
return codes
def _post_json_with_retry(endpoint: str, request_preview: dict[str, Any]) -> tuple[dict[str, Any], int, list[int]]:
retry_codes = _retry_status_codes()
retry_error_codes: list[int] = []
max_attempts = int(RETRY_POLICY["max_attempts"])
for attempt in range(1, max_attempts + 1):
try:
payload = _post_json(endpoint, request_preview)
if not isinstance(payload, dict):
return {}, attempt, retry_error_codes
return payload, attempt, retry_error_codes
except error.HTTPError as exc:
if attempt >= max_attempts or exc.code not in retry_codes:
raise
retry_error_codes.append(exc.code)
if _backoff_seconds():
time.sleep(_backoff_seconds())
return {}, max_attempts, retry_error_codes
def run_case(case_id: str) -> dict[str, Any]:
if case_id not in PARITY_CASES:
return {
@@ -578,7 +706,7 @@ def run_case(case_id: str) -> dict[str, Any]:
"source_metadata": _source_metadata(endpoint),
}
try:
payload = _post_json(endpoint, request_preview)
payload, attempt_count, retry_error_codes = _post_json_with_retry(endpoint, request_preview)
except error.HTTPError as exc:
return {
"backend": "vedastro_service_adapter_candidate",
@@ -616,7 +744,7 @@ def run_case(case_id: str) -> dict[str, Any]:
"source_metadata": _source_metadata(endpoint),
}
return _normalize_success(payload, endpoint)
return _normalize_success(payload, endpoint, request_preview)
def run_range_scan(case_id: str, domain: str, start_date: str, end_date: str) -> dict[str, Any]:
@@ -655,7 +783,7 @@ def run_range_scan(case_id: str, domain: str, start_date: str, end_date: str) ->
}
try:
payload = _post_json(endpoint, request_preview)
payload, attempt_count, retry_error_codes = _post_json_with_retry(endpoint, request_preview)
except error.HTTPError as exc:
return {
"backend": "vedastro_service_adapter_candidate",
@@ -693,7 +821,7 @@ def run_range_scan(case_id: str, domain: str, start_date: str, end_date: str) ->
"source_metadata": _source_metadata(endpoint),
}
return _normalize_range_scan_success(payload, endpoint, request_preview)
return _normalize_range_scan_success(payload, endpoint, request_preview, attempt_count, retry_error_codes)
def run_external_technique(case_id: str, domain: str, method: str, api_endpoint: str) -> dict[str, Any]: