diff --git a/frontend/src/mastra/index.ts b/frontend/src/mastra/index.ts index dcbf07b0..d827359b 100644 --- a/frontend/src/mastra/index.ts +++ b/frontend/src/mastra/index.ts @@ -88,6 +88,7 @@ export function toAgentConsultationContext(data: JsonRecord) { lagna_boundary: rectification.lagna_boundary, }, thematic_evidence: selectedTheme, + vedastro_gateway: record(data.vedastro_gateway), reference_transparency: record(data.reference_transparency), }; } diff --git a/frontend/tests/consultation-context.test.ts b/frontend/tests/consultation-context.test.ts index eac83739..52605011 100644 --- a/frontend/tests/consultation-context.test.ts +++ b/frontend/tests/consultation-context.test.ts @@ -6,6 +6,7 @@ test("passes transparent public-case references into the agent context", () => { const source = readFileSync(new URL("../src/mastra/index.ts", import.meta.url), "utf8"); assert.match(source, /reference_transparency:\s*record\(data\.reference_transparency\)/); + assert.match(source, /vedastro_gateway:\s*record\(data\.vedastro_gateway\)/); assert.match(source, /high_similarity_public_references_available/); assert.match(source, /requested_uncovered_domains/); assert.match(source, /public_context_only/); diff --git a/scripts/diagnose_vedastro_mode.py b/scripts/diagnose_vedastro_mode.py index bd3a6c43..3c485218 100644 --- a/scripts/diagnose_vedastro_mode.py +++ b/scripts/diagnose_vedastro_mode.py @@ -25,7 +25,7 @@ def _bool_env(name: str) -> bool: def _timeout_seconds() -> float: raw = os.environ.get("VEDASTRO_TIMEOUT_SECONDS", "").strip() if not raw: - return 4.0 + return 20.0 try: return float(raw) except ValueError: @@ -34,8 +34,8 @@ def _timeout_seconds() -> float: def build_report() -> dict: load_local_env(ROOT) - endpoint = os.environ.get("VEDASTRO_API_ENDPOINT", "").strip() - network_enabled = _bool_env("VEDASTRO_ENABLE_NETWORK") + endpoint = os.environ.get("VEDASTRO_API_ENDPOINT", "https://api.vedastro.org/api").strip() + network_enabled = os.environ.get("VEDASTRO_ENABLE_NETWORK", "1").strip().lower() in {"1", "true", "yes", "on"} timeout_seconds = _timeout_seconds() has_api_key = bool(os.environ.get("VEDASTRO_API_KEY", "").strip()) free_tier_queue_enabled = _bool_env("VEDASTRO_FREE_TIER_QUEUE") or _bool_env("VEDASTRO_FREE_TIER_QUEUE_ENABLED") or _bool_env("VEDASTRO_ENABLE_FREE_TIER_QUEUE") diff --git a/scripts/jyotish_api_server.py b/scripts/jyotish_api_server.py index 6ee62ed4..b5bce213 100644 --- a/scripts/jyotish_api_server.py +++ b/scripts/jyotish_api_server.py @@ -648,6 +648,18 @@ def execute_consultation_workflow( blind=bool(body.get('blind') or body.get('blind_technical_mode')), ) + vedastro_gateway = rectification.get('vedastro_gateway') if isinstance(rectification, dict) else None + if not isinstance(vedastro_gateway, dict): + try: + vedastro_gateway = handler._compute_vedastro_gateway_run(body) + except Exception as exc: # Gateway evidence must not block the local chart result. + vedastro_gateway = { + 'scope': 'vedastro_gateway_run', + 'status': 'official_blocked', + 'official_closure_reason': 'gateway_invocation_error', + 'error_type': type(exc).__name__, + } + result = { 'success': True, 'endpoint': 'consultation_workflow', @@ -688,6 +700,7 @@ def execute_consultation_workflow( 'muhurta_panchanga': muhurta_panchanga, 'audited_remedies': audited_remedies, 'vedastro_official': vedastro_official, + 'vedastro_gateway': vedastro_gateway, 'runtime_truth': runtime_truth, 'external_parity_gate': external_parity_gate, 'interpretation_source_runtime_coverage': interpretation_source_runtime_coverage, @@ -6707,9 +6720,19 @@ class JyotishAPIHandler(BaseHTTPRequestHandler): headline = '出生时间风险较低,可进入完整解盘' next_action = '保留原始出生记录来源;重要预测仍建议用 Dasha/Transit/案例验证交叉确认。' + try: + vedastro_gateway = self._compute_vedastro_gateway_run(body) + except Exception as exc: # Keep rectification available when the external observation is down. + vedastro_gateway = { + 'scope': 'vedastro_gateway_run', + 'status': 'official_blocked', + 'official_closure_reason': 'gateway_invocation_error', + 'error_type': type(exc).__name__, + } return { 'success': True, 'endpoint': 'rectification_gate', + 'vedastro_gateway': vedastro_gateway, 'ascendant': { 'lon': asc_lon, 'sign': SIGNS[int(asc_lon / 30) % 12], diff --git a/scripts/vedastro_gateway.py b/scripts/vedastro_gateway.py index 382a71b0..5fccd7d7 100644 --- a/scripts/vedastro_gateway.py +++ b/scripts/vedastro_gateway.py @@ -15,12 +15,17 @@ from typing import Any 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" def _bool_env(name: str) -> bool: return os.environ.get(name, "").strip().lower() in {"1", "true", "yes", "on"} +def _official_network_enabled() -> bool: + return os.environ.get("VEDASTRO_ENABLE_NETWORK", "1").strip().lower() in {"1", "true", "yes", "on"} + + def _int_env(name: str, default: int = 0) -> int: raw = os.environ.get(name, "").strip() if not raw: @@ -32,9 +37,9 @@ def _int_env(name: str, default: int = 0) -> int: def build_gateway_config() -> dict[str, Any]: - mode = os.environ.get("VEDASTRO_GATEWAY_MODE", "local_first").strip() or "local_first" + mode = os.environ.get("VEDASTRO_GATEWAY_MODE", "official_first").strip() or "official_first" self_host = os.environ.get("VEDASTRO_SELF_HOST_ENDPOINT", "").strip() - official = os.environ.get("VEDASTRO_API_ENDPOINT", "").strip() + official = os.environ.get("VEDASTRO_API_ENDPOINT", OFFICIAL_ENDPOINT).strip() return { "mode": mode, "self_host_endpoint_configured": bool(self_host), @@ -49,7 +54,7 @@ def build_gateway_config() -> dict[str, Any]: def _active_backend(config: dict[str, Any]) -> str: if config["self_host_endpoint_configured"]: return "self_host" - if config["official_endpoint_configured"] and _bool_env("VEDASTRO_ENABLE_NETWORK"): + if config["official_endpoint_configured"] and _official_network_enabled(): return "official" if config["cache_ttl_seconds"] > 0: return "cache" diff --git a/scripts/vedastro_user_entrypoint.py b/scripts/vedastro_user_entrypoint.py index 6b19201b..3c4f618c 100644 --- a/scripts/vedastro_user_entrypoint.py +++ b/scripts/vedastro_user_entrypoint.py @@ -109,7 +109,7 @@ def _run_capability_catalog(case: dict[str, Any]) -> dict[str, Any]: cwd=ROOT, text=True, capture_output=True, - timeout=max(5.0, float(os.environ.get("VEDASTRO_TIMEOUT_SECONDS", "5") or 5)), + timeout=max(5.0, float(os.environ.get("VEDASTRO_TIMEOUT_SECONDS", "20") or 20)), check=False, env=os.environ.copy(), ) diff --git a/tests/test_api_server_security.py b/tests/test_api_server_security.py index a0176d8c..8b67dad0 100644 --- a/tests/test_api_server_security.py +++ b/tests/test_api_server_security.py @@ -2033,6 +2033,10 @@ def test_panchanga_range_rejects_large_ranges() -> None: def test_rectification_gate_returns_varga_risk_summary() -> None: handler = _handler() + handler._compute_vedastro_gateway_run = lambda body: { # type: ignore[method-assign] + 'status': 'official_verified', + 'official_raw_response': {'request_id': 'rectification-live-call'}, + } result = handler._compute_rectification_gate({ 'planets': _sample_planets(), 'ascendant': {'lon': 92.0}, @@ -2042,6 +2046,7 @@ def test_rectification_gate_returns_varga_risk_summary() -> None: assert result['success'] is True assert result['endpoint'] == 'rectification_gate' + assert result['vedastro_gateway']['official_raw_response']['request_id'] == 'rectification-live-call' assert result['effective_accuracy'] == '15min' assert 'headline' in result['summary'] assert result['summary']['recommended_events'] @@ -2261,6 +2266,11 @@ def test_consultation_workflow_uses_unified_orchestrator_contract(monkeypatch) - 'endpoint': 'rectification_gate', 'summary': {'recommended_events': ['career_change']}, }) + monkeypatch.setattr(handler, '_compute_vedastro_gateway_run', lambda body: { + 'scope': 'vedastro_gateway_run', + 'status': 'official_verified', + 'official_raw_response': {'request_id': 'test-live-call'}, + }) monkeypatch.setattr(handler, '_compute_thematic_report', lambda body: { 'success': True, 'endpoint': 'thematic_report', @@ -2324,6 +2334,8 @@ def test_consultation_workflow_uses_unified_orchestrator_contract(monkeypatch) - 'official_blocked', 'local_fallback', } + assert result['vedastro_gateway']['status'] == 'official_verified' + assert result['vedastro_gateway']['official_raw_response']['request_id'] == 'test-live-call' assert result['runtime_evidence_log']['quality_gate']['technique_audit_table_required'] is True assert result['runtime_evidence_log']['quality_gate']['technique_audit_table'][0]['technique'] == 'VedAstro Cloud State' assert result['machine_evidence_packet']['status'] == 'partial' diff --git a/tests/test_vedastro_gateway.py b/tests/test_vedastro_gateway.py index 5c27498c..02f829ea 100644 --- a/tests/test_vedastro_gateway.py +++ b/tests/test_vedastro_gateway.py @@ -3,7 +3,7 @@ from __future__ import annotations import json -def test_gateway_status_defaults_to_local_first_cn_safe(monkeypatch): +def test_gateway_status_defaults_to_official_first_cn_safe(monkeypatch): from scripts import vedastro_gateway monkeypatch.delenv("VEDASTRO_GATEWAY_MODE", raising=False) @@ -17,13 +17,12 @@ def test_gateway_status_defaults_to_local_first_cn_safe(monkeypatch): status = vedastro_gateway.gateway_status() assert status["scope"] == "vedastro_gateway" - assert status["mode"] == "local_first" + assert status["mode"] == "official_first" assert status["direct_browser_access_allowed"] is False assert status["frontend_secret_safe"] is True assert status["backend_priority"] == ["self_host", "official", "cache", "queue", "local_fallback"] - assert status["active_backend"] == "local_fallback" - assert status["official_readiness"]["official_ready"] is False - assert "missing_endpoint" in status["official_readiness"]["readiness_blockers"] + assert status["active_backend"] == "official" + assert status["official_readiness"]["official_ready"] is True assert status["boundary"] == "Users never call VedAstro directly; backend gateway owns cache, queue, and fallback."