crypto-forecasting-ai / scripts /check_space_bundle.py
chonthichar's picture
Claude Opus 5
Serve jump risk and volatility-rises on the volatility panel
d32c7e6
Raw History Blame Contribute Delete
6.07 kB
"""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])