File size: 6,066 Bytes
c2769af
 
 
 
 
 
 
 
 
 
 
 
 
811c110
6f8f877
 
 
 
c2769af
0a03c3d
c2769af
a304696
 
598e0d0
a442066
 
6b72f32
 
 
 
 
 
 
 
 
 
a442066
 
 
 
 
 
 
6b72f32
 
 
 
 
e9beb74
 
 
 
 
 
 
 
468b9b2
 
 
 
 
 
c2769af
766d18e
 
 
 
 
 
 
 
 
d32c7e6
 
 
 
 
 
 
 
 
 
 
c2769af
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
"""Fail a Space build if the dashboard or registered model files were omitted."""
from pathlib import Path
import yaml


def check_bundle(root):
    required = [
        "app/space_server.py", "app/api.py", "app/analyst_api.py",
        "app/agents/orchestrator.py", "app/agents/decision_agent.py",
        "app/agents/forecast_context_agent.py", "app/agents/market_research_agent.py",
        "app/agents/validation_agent.py", "app/agents/answer_guard.py",
        "app/services/llm_service.py", "app/services/agent_config.py",
        "app/services/tavily_service.py", "app/schemas/agent_models.py",
        "app/services/reasoning_agents.py", "app/services/research_workflow.py", "app/services/market_decision.py",
        "app/services/research_triggers.py", "app/services/market_monitoring.py",
        "app/services/monitoring_events_store.py", "app/services/monitoring_backup.py",
        "app/services/monitor_schedule.py", "app/services/monitor_worker_runtime.py",
        "app/static/monitor/monitoring-events.js",
        "app/static/monitor/index.html", "app/static/monitor/analyst.js",
        "app/static/monitor/market-overview.js", "app/static/monitor/light-overrides.css",
        "app/static/monitor/analyst.css", "app/static/monitor/dashboard.js",
        "app/static/monitor/styles.css", "app/static/monitor/store.js",
        "app/static/monitor/sidebar.js", "app/static/monitor/views.css",
        "app/static/monitor/workspace.css", "app/asset_catalog.yaml",
        "app/static/monitor/deployment.js", "app/static/monitor/volatility.js",
        "requirements-agents.txt",
    ]
    # Frozen deployment models. A missing artifact here means /predictions
    # returns 503 in production, so fail the build instead.
    for horizon in (1, 6, 24):
        required += [
            f"btc_deployment_models/risk/risk_{horizon}h_logistic.joblib",
            f"btc_deployment_models/risk/risk_{horizon}h_scaler.joblib",
            f"btc_deployment_models/direction/direction_{horizon}h_transformer.pt",
            f"btc_deployment_models/direction/direction_{horizon}h_scaler.joblib",
        ]
    # Volatility regime artifacts. Missing here means /volatility returns 503.
    required += [
        "btc_deployment_models/volatility/volatility_24h_model.joblib",
        "btc_deployment_models/volatility/volatility_24h_metadata.json",
        "btc_deployment_models/volatility/volatility_feature_list.csv",
        "src/volatility.py",
    ]
    required += [
        "btc_deployment_models/risk/risk_feature_list.csv",
        "btc_deployment_models/risk/risk_band_empirical_results.csv",
        "btc_deployment_models/risk/provisional_risk_band_thresholds.json",
        "btc_deployment_models/direction/direction_stationary_feature_list.csv",
        # Volatility: the classifier and the magnitude regressor. The regressor
        # is torch rather than a scikit-learn pickle because the Space pins
        # 1.6.1 and models are trained on a host running 1.9.1.
        "btc_deployment_models/volatility/volatility_24h_model.joblib",
        "btc_deployment_models/volatility/volatility_24h_metadata.json",
        "btc_deployment_models/volatility/volatility_feature_list.csv",
        "btc_deployment_models/volatility/volatility_24h_regressor.json",
        "btc_deployment_models/volatility/volatility_24h_regressor.pt",
        # ETH and ADA volatility. Torch, because a scikit-learn pickle trained
        # on the host that produced them cannot load under the pinned version.
        "btc_deployment_models/volatility/volatility_eth_24h_classifier.pt",
        "btc_deployment_models/volatility/volatility_eth_24h_metadata.json",
        "btc_deployment_models/volatility/volatility_ada_24h_classifier.pt",
        "btc_deployment_models/volatility/volatility_ada_24h_metadata.json",
    ]
    # The VAE+Transformer regime models, which now serve all three assets and
    # supersede the per-row artifacts above. Both files per asset are needed:
    # the weights hold every seed's state_dict, the JSON holds the feature
    # order, sequence length, scaler and decision threshold, which differ by
    # asset and are not recoverable from the weights alone.
    required += [
        f"btc_deployment_models/volatility/volatility_{code}_24h_vaetransformer.{ext}"
        for code in ("btc", "eth", "ada") for ext in ("pt", "json")
    ]
    # Jump risk and volatility-rises. Horizons differ per asset for
    # volatility-rises (BTC and ETH 12h, ADA 1h) because the horizon sweep
    # chose them, so those filenames are listed rather than generated.
    required += [
        f"btc_deployment_models/jump/jump_{code}_24h_vaetransformer.{ext}"
        for code in ("btc", "eth", "ada") for ext in ("pt", "json")
    ]
    required += [
        f"btc_deployment_models/volatility_rises/volrises_{code}_{hours}h_vaetransformer.{ext}"
        for code, hours in (("btc", 12), ("eth", 12), ("ada", 1)) for ext in ("pt", "json")
    ]
    registry = yaml.safe_load((root / "app/deployment_registry.yaml").read_text(encoding="utf-8"))
    for spec in registry.values():
        directory = Path(spec["output_subdir"])
        required.extend([spec["config"], directory / "model_state.pt"])
        if spec["kind"] in {"lstm", "gru"}:
            required.append(directory / "scaler.joblib")
        elif spec["kind"] == "vae_transformer_v2":
            required.append(directory / "market_scaler.joblib")
            if spec["feature_set"] == "market_plus_sentiment":
                required.append(directory / "sentiment_scaler.joblib")
        else:
            raise SystemExit("Unsupported model kind in deployment registry")
    missing = [str(item) for item in required if not (root / item).is_file() or (root / item).stat().st_size == 0]
    if missing:
        raise SystemExit("Incomplete Space upload. Missing required files:\n" + "\n".join(missing))
    print(f"Space bundle complete: dashboard and {len(registry)} trained horizon models.")


if __name__ == "__main__":
    check_bundle(Path(__file__).resolve().parents[1])