This commit is contained in:
2026-04-21 16:53:56 +03:00
parent 1346924387
commit 2ccd6831eb
26 changed files with 430403 additions and 3 deletions
@@ -15,6 +15,7 @@ import json
import re
import time
import math
import os
import pandas as pd
import numpy as np
from collections import defaultdict
@@ -33,6 +34,7 @@ from models.basketball_v25 import (
)
from core.engines.player_predictor import PlayerPrediction, get_player_predictor
from services.feature_enrichment import FeatureEnrichmentService
from services.v26_shadow_engine import V26ShadowEngine, get_v26_shadow_engine
from utils.top_leagues import load_top_league_ids
from utils.league_reliability import load_league_reliability
@@ -137,8 +139,10 @@ class SingleMatchOrchestrator:
def __init__(self) -> None:
self.v25_predictor: Optional[V25Predictor] = None
self.v26_shadow_engine: Optional[V26ShadowEngine] = None
self.basketball_predictor: Optional[Any] = None
self.dsn = get_clean_dsn()
self.engine_mode = str(os.getenv("AI_ENGINE_MODE", "v25")).strip().lower()
self.top_league_ids = load_top_league_ids()
self.league_reliability = load_league_reliability()
self.enrichment = FeatureEnrichmentService()
@@ -212,6 +216,11 @@ class SingleMatchOrchestrator:
self.v25_predictor = get_v25_predictor()
return self.v25_predictor
def _get_v26_shadow_engine(self) -> V26ShadowEngine:
if getattr(self, "v26_shadow_engine", None) is None:
self.v26_shadow_engine = get_v26_shadow_engine()
return self.v26_shadow_engine
def _build_v25_features(self, data: MatchData) -> Dict[str, float]:
"""
Build the single authoritative V25 pre-match feature vector.
@@ -676,7 +685,35 @@ class SingleMatchOrchestrator:
features = self._build_v25_features(data)
v25_signal = self._get_v25_signal(data, features)
prediction = self._build_v25_prediction(data, features, v25_signal)
return self._build_prediction_package(data, prediction, v25_signal)
base_package = self._build_prediction_package(data, prediction, v25_signal)
mode = str(getattr(self, "engine_mode", "v25") or "v25").lower()
if mode not in {"v25", "v26", "dual"}:
mode = "v25"
quality = base_package.get("data_quality", self._compute_data_quality(data))
shadow_package = self._get_v26_shadow_engine().build_package(
data=data,
prediction=prediction,
v25_signal=v25_signal,
quality=quality,
)
if mode == "v26":
return shadow_package
if mode == "dual":
merged = dict(base_package)
merged.update(
{
"shadow_engine": shadow_package,
"shadow_engine_version": shadow_package.get("model_version"),
"calibration_version": shadow_package.get("calibration_version"),
"decision_trace_id": shadow_package.get("decision_trace_id"),
"market_reliability": shadow_package.get("market_reliability", {}),
}
)
return merged
return base_package
def analyze_match_htms(self, match_id: str) -> Optional[Dict[str, Any]]:
"""