feat(upstream): snapshot a6f47abd, REST VedAstro path, and 116-technique truth layer

Advance the one-way import to git commit a6f47abd with consultation keypath golden, switch official VedAstro comparison to the REST Calculate bridge, and receive the 25 new registry entries behind research_only_blocked.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-03 17:35:26 +08:00
parent 01a4536333
commit 45d132588f
1184 changed files with 464058 additions and 70 deletions
+26
View File
@@ -28,6 +28,32 @@ TECHNIQUE_TRUTH_IDS = (
"tajika_yogas",
"conception_chart",
"relationship_combinations",
"ashtottari_dasha",
"classical_astrology_core",
"classical_event_chart",
"classical_real_case_calibration",
"classical_usable_loop_audit",
"western_draconic_chart",
"western_profection",
"western_relocation_chart",
"western_solar_arc_directions",
"western_zodiacal_release",
"ziwei_app_js",
"ziwei_bio_core_js",
"ziwei_bridge_script_pack",
"ziwei_character_bio_enhanced_js",
"ziwei_character_bio_generator_js",
"ziwei_complete_js",
"ziwei_doushu_bridge",
"ziwei_face_writing_bundle",
"ziwei_i18n_bridge_bundle",
"ziwei_i18n_complete_js",
"ziwei_mainline_packet",
"ziwei_patterns_js",
"ziwei_personal_report_export",
"ziwei_psychology_js",
"ziwei_runtime_bridge_scripts",
"ziwei_star_details_js",
)
_BLOCKED_STATUSES = {"blocked", "research_only_blocked"}
+6 -1
View File
@@ -70,6 +70,10 @@ def build_report() -> dict:
if official_ready
else "official_snapshot_budget_exhausted_or_endpoint_blocked"
),
"official_transport": "rest",
"official_calculate_endpoint": "https://api.vedastro.org/api/Calculate",
"rate_limit_per_minute": 5,
"mcp_bridge_role": "protocol_probe_only",
"official_closure_plan": {
"required_env": {
"VEDASTRO_API_ENDPOINT": endpoint or "https://api.vedastro.org/api",
@@ -77,9 +81,10 @@ def build_report() -> dict:
"VEDASTRO_TIMEOUT_SECONDS": "20",
"VEDASTRO_API_KEY": "optional_but_recommended_for_stable_full_snapshot",
},
"free_tier_policy": "Queue/cache can reduce throttling, but free tier may still return blocked or partial snapshots.",
"free_tier_policy": "Official REST Calculate is limited to 5 requests/minute. Queue/cache can reduce throttling, but free tier may still return blocked or partial snapshots.",
"premium_key_policy": "API key recommended for stable official full snapshot; free tier may still block or throttle.",
"raw_response_acceptance": "vedastro_official.raw_response must be present before claiming official cloud closure.",
"default_path": "REST bridge scripts/vedastro_rest_bridge.py against /api/Calculate. Official MCP tools/call is protocol-probe only.",
},
"next_step": (
"Run full-reading or strict_workflow; verify vedastro_official.status is ok/partial."
+4 -4
View File
@@ -4,10 +4,10 @@
This artifact turns the parity matrix plus live official catalog snapshot into
an execution-friendly view:
- what should go through official MCP
- what should go through the official REST Calculate bridge (default)
- what should go through the official Python bridge
- what should go through the REST adapter
- what should stay local because the repo is already stronger there
- official MCP remains protocol-probe only after tools/call regression
"""
from __future__ import annotations
@@ -27,9 +27,9 @@ DEFAULT_MARKDOWN_PATH = ROOT / "docs" / "research" / "vedastro_fast_path_checkli
CATALOG_PATH = ROOT / "scratch" / "local" / "vedastro_adapter" / "method_catalog_snapshot.json"
LANE_TITLES = {
"official_mcp": "Direct Official MCP",
"official_mcp": "Official MCP protocol probe only",
"official_python_bridge": "Official Python Bridge",
"rest_adapter": "REST Adapter",
"rest_adapter": "Official REST Calculate bridge",
"local_native_preferred": "Local Native Preferred",
"hybrid_router": "Hybrid Router",
"external_evidence_only": "External Evidence Only",
+31
View File
@@ -16,6 +16,9 @@ BACKEND_PRIORITY = ["self_host", "official", "cache", "queue", "local_fallback"]
BOUNDARY_TEXT = "Users never call VedAstro directly; backend gateway owns cache, queue, and fallback."
ROOT = Path(__file__).resolve().parents[1]
OFFICIAL_ENDPOINT = "https://api.vedastro.org/api"
OFFICIAL_CALCULATE_ENDPOINT = "https://api.vedastro.org/api/Calculate"
REST_RATE_LIMIT_PER_MINUTE = 5
MCP_BRIDGE_ROLE = "protocol_probe_only"
def _bool_env(name: str) -> bool:
@@ -229,16 +232,42 @@ def run_gateway_job(job_id: str) -> dict[str, Any] | None:
return complete_gateway_job(job_id, result)
def probe_official_rest_health() -> dict[str, Any]:
"""Health of the default official path: REST ``/api/Calculate`` Status Pass."""
base = {
"transport": "rest",
"endpoint": OFFICIAL_CALCULATE_ENDPOINT,
"rate_limit_per_minute": REST_RATE_LIMIT_PER_MINUTE,
"mcp_bridge_role": MCP_BRIDGE_ROLE,
}
if os.environ.get("JYOTISH_SKIP_LOCAL_ENV", "").strip().lower() in {"1", "true", "yes", "on"}:
return {**base, "status": "not_probed", "reason": "jyotish_skip_local_env"}
if not _official_network_enabled():
return {**base, "status": "official_blocked", "reason": "network_disabled"}
try:
from scripts.vedastro_rest_bridge import probe_calculate_health
except ModuleNotFoundError: # pragma: no cover - script execution
from vedastro_rest_bridge import probe_calculate_health
result = probe_calculate_health()
return {**base, **result}
def gateway_status() -> dict[str, Any]:
from scripts.diagnose_vedastro_mode import build_report as build_vedastro_mode_report
readiness = build_vedastro_mode_report()
config = build_gateway_config()
rest_health = probe_official_rest_health()
return {
"scope": "vedastro_gateway",
"mode": config["mode"],
"backend_priority": BACKEND_PRIORITY,
"active_backend": _active_backend(config),
"official_transport": "rest",
"official_calculate_endpoint": OFFICIAL_CALCULATE_ENDPOINT,
"rate_limit_per_minute": REST_RATE_LIMIT_PER_MINUTE,
"mcp_bridge_role": MCP_BRIDGE_ROLE,
"official_calculate_health": rest_health,
"self_host_configured": config["self_host_endpoint_configured"],
"official_configured": config["official_endpoint_configured"],
"credential_configured": bool(os.environ.get("VEDASTRO_API_KEY", "").strip()),
@@ -251,6 +280,8 @@ def gateway_status() -> dict[str, Any]:
"readiness_blockers": list(readiness.get("readiness_blockers") or []),
"free_tier_possible_with_cache_queue": bool(readiness.get("free_tier_possible_with_cache_queue")),
"official_closure_plan": readiness.get("official_closure_plan") or {},
"official_transport": "rest",
"rate_limit_per_minute": REST_RATE_LIMIT_PER_MINUTE,
},
"direct_browser_access_allowed": False,
"frontend_secret_safe": True,
+5 -4
View File
@@ -1,9 +1,10 @@
#!/usr/bin/env python3
"""Thin bridge to the official public VedAstro MCP endpoint.
"""Protocol-probe-only bridge to the official public VedAstro MCP endpoint.
This bridge is intentionally narrow. It proves that the official public MCP
surface is reachable and callable from the local workspace without letting
external responses silently override local adjudication.
The 2026-09-01 probe found ``tools/call`` returning "Invalid or Outdated Call".
Default official comparison now goes through ``scripts/vedastro_rest_bridge.py``.
This module stays for initialize/tools-list protocol checks and must not be
treated as the production comparison path.
"""
from __future__ import annotations
+4 -3
View File
@@ -189,16 +189,17 @@ VEDASTRO_CAPABILITY_SEEDS: list[dict[str, Any]] = [
"jyotish_api_server.py",
"strict workflows",
"vedastro_service_adapter.py",
"vedastro_rest_bridge.py",
"vedastro_official_mcp_bridge.py",
],
"can_call_vedastro": True,
"recommended_path": "hybrid_local_plus_vedastro",
"fastest_path_lane": "official_mcp",
"fastest_path_lane": "rest_adapter",
"priority": "P0",
"license_boundary": "external_service_or_local_native",
"adjudicator_use": "primary",
"gap_notes": "Local API/MCP surfaces exist and the official public MCP bridge is live; REST adapter official endpoint smoke still depends on configured endpoint-backed execution.",
"route_notes": "If official MCP is available, that is the fastest direct agent path; otherwise fall back to local REST adapter.",
"gap_notes": "Official MCP tools/call remains Invalid or Outdated Call; default external path is the REST Calculate bridge. MCP bridge is protocol-probe only. match/synastry stays official_blocked.",
"route_notes": "Default official comparison uses scripts/vedastro_rest_bridge.py (5 req/min). Keep vedastro_official_mcp_bridge.py for protocol probing only.",
},
{
"vedastro_capability": "Numerology / Non-Jyotish Tools",
+217
View File
@@ -0,0 +1,217 @@
#!/usr/bin/env python3
"""VedAstro official REST bridge.
The official MCP ``tools/call`` resolver returned "Invalid or Outdated Call"
in the 2026-09-01 probe. This bridge calls ``https://api.vedastro.org/api/Calculate``
directly for external comparison data.
Birth arguments default to a fictional smoke chart (1990-01-01 Beijing).
They are never a real person. The match/synastry subcommand is not implemented
and remains ``official_blocked``.
"""
from __future__ import annotations
import argparse
import json
import os
import sys
import threading
import time
import urllib.error
import urllib.request
from pathlib import Path
from typing import Any
API = "https://api.vedastro.org/api/Calculate"
RATE_LIMIT_PER_MINUTE = 5
# Fictional smoke only. Do not replace with a real birth record.
SMOKE_DEFAULT = dict(
name="Beijing, China",
lat=39.9042,
lon=116.4074,
birth="12:00 01/01/1990 +08:00",
ayanamsa="LAHIRI",
)
_RATE_LOCK = threading.Lock()
_RATE_WINDOW = 0.0
_RATE_COUNT = 0
class RestRateLimited(RuntimeError):
"""Raised when the local 5/min guard trips before a network call."""
def _time(birth: str, name: str, lat: float, lon: float) -> dict[str, Any]:
return {"StdTime": birth, "Location": {"Name": name, "Longitude": lon, "Latitude": lat}}
def _date_to_vedastro_std(value: str) -> str:
parts = value.split()
if len(parts) != 3:
raise ValueError("time must use 'YYYY-MM-DD HH:MM +08:00' format")
date_part, time_part, tz = parts
y, m, d = date_part.split("-")
return f"{time_part} {d}/{m}/{y} {tz}"
def consume_rate_token(*, now: float | None = None) -> None:
global _RATE_WINDOW, _RATE_COUNT
current = time.time() if now is None else now
with _RATE_LOCK:
if current - _RATE_WINDOW >= 60:
_RATE_WINDOW = current
_RATE_COUNT = 0
if _RATE_COUNT >= RATE_LIMIT_PER_MINUTE:
raise RestRateLimited("vedastro_rest_rate_limited")
_RATE_COUNT += 1
def reset_rate_limiter() -> None:
global _RATE_WINDOW, _RATE_COUNT
with _RATE_LOCK:
_RATE_WINDOW = 0.0
_RATE_COUNT = 0
def build_horoscope_payload(args: argparse.Namespace) -> dict[str, Any]:
return {
"Ayanamsa": args.ayanamsa,
"BirthTime": _time(args.birth, args.name, args.lat, args.lon),
"SortByWeight": False,
}
def build_dasa_payload(args: argparse.Namespace) -> dict[str, Any]:
return {
"Ayanamsa": args.ayanamsa,
"BirthTime": _time(args.birth, args.name, args.lat, args.lon),
"StartTime": _time(_date_to_vedastro_std(args.start), args.name, args.lat, args.lon),
"EndTime": _time(_date_to_vedastro_std(args.end), args.name, args.lat, args.lon),
"Levels": 3,
"PrecisionHours": 100,
}
def match_not_implemented() -> dict[str, Any]:
return {
"Status": "official_blocked",
"official_closure_state": "official_blocked",
"official_closure_reason": "match_subcommand_not_implemented",
"boundary": "VedAstro REST match/synastry is not implemented; do not fabricate a comparison.",
}
def call(
method: str,
payload: dict[str, Any],
out: str | None = None,
*,
opener: Any | None = None,
timeout: float = 90,
skip_rate_limit: bool = False,
) -> dict[str, Any]:
if not skip_rate_limit:
consume_rate_token()
req = urllib.request.Request(
f"{API}/{method}",
data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json"},
method="POST",
)
try:
if opener is None:
with urllib.request.urlopen(req, timeout=timeout) as response:
data = json.loads(response.read().decode())
else:
with opener(req, timeout=timeout) as response:
data = json.loads(response.read().decode())
except urllib.error.HTTPError as error:
if error.code == 429:
return {
"Status": "official_blocked",
"official_closure_state": "official_blocked",
"official_closure_reason": "rate_limited",
"http_status": 429,
}
raise
if out:
Path(out).write_text(json.dumps(data, ensure_ascii=False, indent=1) + "\n", encoding="utf-8")
print(f"[已落盘] {out}")
return data
def probe_calculate_health(*, opener: Any | None = None, timeout: float = 8) -> dict[str, Any]:
"""POST a fictional smoke horoscope and report whether Status is Pass."""
args = argparse.Namespace(**SMOKE_DEFAULT)
try:
payload = build_horoscope_payload(args)
data = call("HoroscopePredictions", payload, opener=opener, timeout=timeout)
except RestRateLimited:
return {
"status": "official_blocked",
"reason": "rate_limited",
"endpoint": API,
"transport": "rest",
}
except (urllib.error.URLError, TimeoutError, json.JSONDecodeError, OSError) as error:
return {
"status": "official_blocked",
"reason": type(error).__name__,
"endpoint": API,
"transport": "rest",
}
status = str(data.get("Status") or "")
if status.lower() == "pass":
return {"status": "official_verified", "reason": "status_pass", "endpoint": API, "transport": "rest"}
if str(data.get("official_closure_reason") or "") == "rate_limited":
return {"status": "official_blocked", "reason": "rate_limited", "endpoint": API, "transport": "rest"}
return {
"status": "official_blocked",
"reason": f"status_{status or 'missing'}",
"endpoint": API,
"transport": "rest",
}
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="VedAstro official REST bridge (fictional smoke defaults)")
sub = parser.add_subparsers(dest="cmd", required=True)
for command in ("horoscope", "dasa"):
item = sub.add_parser(command)
item.add_argument("--birth", default=SMOKE_DEFAULT["birth"], help="VedAstro StdTime; default is fictional smoke")
item.add_argument("--name", default=SMOKE_DEFAULT["name"])
item.add_argument("--lat", type=float, default=SMOKE_DEFAULT["lat"])
item.add_argument("--lon", type=float, default=SMOKE_DEFAULT["lon"])
item.add_argument("--ayanamsa", default=SMOKE_DEFAULT["ayanamsa"])
item.add_argument("--out")
sub._name_parser_map["dasa"].add_argument("--start", default="2026-01-01 00:00 +08:00")
sub._name_parser_map["dasa"].add_argument("--end", default="2032-12-31 00:00 +08:00")
match = sub.add_parser("match", help="Not implemented; remains official_blocked")
match.add_argument("--out")
return parser
def main(argv: list[str] | None = None) -> int:
args = build_parser().parse_args(argv)
if args.cmd == "match":
result = match_not_implemented()
if args.out:
Path(args.out).write_text(json.dumps(result, ensure_ascii=False, indent=1) + "\n", encoding="utf-8")
print(json.dumps(result, ensure_ascii=False))
return 0
if args.cmd == "horoscope":
result = call("HoroscopePredictions", build_horoscope_payload(args), args.out)
elif args.cmd == "dasa":
result = call("DasaAtRange", build_dasa_payload(args), args.out)
else:
sys.exit("未知子命令")
if not args.out:
print(json.dumps(result, ensure_ascii=False)[:2000])
return 0
if __name__ == "__main__":
os.environ.setdefault("PYTHONDONTWRITEBYTECODE", "1")
raise SystemExit(main())