jashdoshi77 commited on
Commit
9f74ac7
·
1 Parent(s): 813f54d

Add onnxruntime + tokenizers + caching for fast loading

Browse files
Files changed (2) hide show
  1. requirements.txt +3 -0
  2. server.py +106 -3
requirements.txt CHANGED
@@ -14,8 +14,11 @@ tenacity>=8.2.0
14
  matplotlib>=3.8.0
15
  seaborn>=0.13.0
16
  chromadb-client>=0.5.0
 
 
17
  streamlit-autorefresh>=1.0.1
18
  apscheduler>=3.10.0
19
  flask>=3.0.0
20
  flask-cors>=4.0.0
21
 
 
 
14
  matplotlib>=3.8.0
15
  seaborn>=0.13.0
16
  chromadb-client>=0.5.0
17
+ onnxruntime>=1.16.0
18
+ tokenizers>=0.15.0
19
  streamlit-autorefresh>=1.0.1
20
  apscheduler>=3.10.0
21
  flask>=3.0.0
22
  flask-cors>=4.0.0
23
 
24
+
server.py CHANGED
@@ -6,6 +6,7 @@ Serves the React frontend and Flask API with:
6
  - Continuous learning
7
  - Persistent storage using /data folder
8
  - Model updates
 
9
 
10
  For Hugging Face Spaces deployment.
11
  """
@@ -18,11 +19,20 @@ import os
18
  import shutil
19
  import threading
20
  from pathlib import Path
21
- from datetime import datetime
22
  from apscheduler.schedulers.background import BackgroundScheduler
23
  from apscheduler.triggers.interval import IntervalTrigger
24
  from apscheduler.triggers.cron import CronTrigger
25
 
 
 
 
 
 
 
 
 
 
26
  # Configure logging
27
  logging.basicConfig(
28
  level=logging.INFO,
@@ -503,10 +513,54 @@ def get_accuracy():
503
 
504
  @app.route("/api/mvp")
505
  def get_mvp_race():
506
- """Get current MVP race standings."""
 
 
507
  if not pipeline:
508
  return jsonify({"candidates": [], "error": "Pipeline not ready"})
509
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
  try:
511
  mvp_df = pipeline.get_mvp_race()
512
 
@@ -522,6 +576,10 @@ def get_mvp_race():
522
  "similarity": round(float(row["mvp_similarity"]) * 100, 1)
523
  })
524
 
 
 
 
 
525
  return jsonify({"candidates": candidates})
526
  except Exception as e:
527
  logger.error(f"Error in get_mvp_race: {e}")
@@ -529,10 +587,51 @@ def get_mvp_race():
529
 
530
  @app.route("/api/championship")
531
  def get_championship_odds():
532
- """Get current championship odds."""
 
 
533
  if not pipeline:
534
  return jsonify({"teams": [], "error": "Pipeline not ready"})
535
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
536
  try:
537
  champ_df = pipeline.get_championship_odds()
538
 
@@ -545,6 +644,10 @@ def get_championship_odds():
545
  "win_pct": round(float(row.get("W_PCT", 0.5)) * 100, 1)
546
  })
547
 
 
 
 
 
548
  return jsonify({"teams": teams})
549
  except Exception as e:
550
  logger.error(f"Error in get_championship_odds: {e}")
 
6
  - Continuous learning
7
  - Persistent storage using /data folder
8
  - Model updates
9
+ - Caching for fast responses
10
 
11
  For Hugging Face Spaces deployment.
12
  """
 
19
  import shutil
20
  import threading
21
  from pathlib import Path
22
+ from datetime import datetime, timedelta
23
  from apscheduler.schedulers.background import BackgroundScheduler
24
  from apscheduler.triggers.interval import IntervalTrigger
25
  from apscheduler.triggers.cron import CronTrigger
26
 
27
+ # =============================================================================
28
+ # CACHE CONFIGURATION - For fast responses
29
+ # =============================================================================
30
+ cache = {
31
+ "mvp": {"data": None, "timestamp": None, "ttl": 300}, # 5 min cache
32
+ "championship": {"data": None, "timestamp": None, "ttl": 300}, # 5 min cache
33
+ "teams": {"data": None, "timestamp": None, "ttl": 3600}, # 1 hour cache
34
+ }
35
+
36
  # Configure logging
37
  logging.basicConfig(
38
  level=logging.INFO,
 
513
 
514
  @app.route("/api/mvp")
515
  def get_mvp_race():
516
+ """Get current MVP race standings with caching."""
517
+ global cache
518
+
519
  if not pipeline:
520
  return jsonify({"candidates": [], "error": "Pipeline not ready"})
521
 
522
+ # Check cache
523
+ now = datetime.utcnow()
524
+ mvp_cache = cache.get("mvp", {})
525
+ cache_data = mvp_cache.get("data")
526
+ cache_time = mvp_cache.get("timestamp")
527
+ cache_ttl = mvp_cache.get("ttl", 300)
528
+
529
+ # Return cached data if valid
530
+ if cache_data and cache_time and (now - cache_time).total_seconds() < cache_ttl:
531
+ logger.debug("Returning cached MVP data")
532
+ return jsonify(cache_data)
533
+
534
+ # Fetch fresh data in background thread if cache expired
535
+ def fetch_mvp_data():
536
+ try:
537
+ mvp_df = pipeline.get_mvp_race()
538
+
539
+ candidates = []
540
+ for idx, row in mvp_df.iterrows():
541
+ candidates.append({
542
+ "rank": len(candidates) + 1,
543
+ "name": row["PLAYER_NAME"],
544
+ "ppg": round(float(row["PTS"]), 1),
545
+ "rpg": round(float(row["REB"]), 1),
546
+ "apg": round(float(row["AST"]), 1),
547
+ "mvp_score": round(float(row["mvp_score"]), 1),
548
+ "similarity": round(float(row["mvp_similarity"]) * 100, 1)
549
+ })
550
+
551
+ # Update cache
552
+ cache["mvp"]["data"] = {"candidates": candidates}
553
+ cache["mvp"]["timestamp"] = datetime.utcnow()
554
+ logger.info(f"MVP cache updated with {len(candidates)} candidates")
555
+ except Exception as e:
556
+ logger.error(f"Background MVP fetch error: {e}")
557
+
558
+ # If we have stale cache, return it immediately and refresh in background
559
+ if cache_data:
560
+ threading.Thread(target=fetch_mvp_data, daemon=True).start()
561
+ return jsonify(cache_data)
562
+
563
+ # No cache - fetch synchronously (first request only)
564
  try:
565
  mvp_df = pipeline.get_mvp_race()
566
 
 
576
  "similarity": round(float(row["mvp_similarity"]) * 100, 1)
577
  })
578
 
579
+ # Update cache
580
+ cache["mvp"]["data"] = {"candidates": candidates}
581
+ cache["mvp"]["timestamp"] = datetime.utcnow()
582
+
583
  return jsonify({"candidates": candidates})
584
  except Exception as e:
585
  logger.error(f"Error in get_mvp_race: {e}")
 
587
 
588
  @app.route("/api/championship")
589
  def get_championship_odds():
590
+ """Get current championship odds with caching."""
591
+ global cache
592
+
593
  if not pipeline:
594
  return jsonify({"teams": [], "error": "Pipeline not ready"})
595
 
596
+ # Check cache
597
+ now = datetime.utcnow()
598
+ champ_cache = cache.get("championship", {})
599
+ cache_data = champ_cache.get("data")
600
+ cache_time = champ_cache.get("timestamp")
601
+ cache_ttl = champ_cache.get("ttl", 300)
602
+
603
+ # Return cached data if valid
604
+ if cache_data and cache_time and (now - cache_time).total_seconds() < cache_ttl:
605
+ logger.debug("Returning cached championship data")
606
+ return jsonify(cache_data)
607
+
608
+ # Fetch fresh data in background thread if cache expired
609
+ def fetch_champ_data():
610
+ try:
611
+ champ_df = pipeline.get_championship_odds()
612
+
613
+ teams = []
614
+ for idx, row in champ_df.iterrows():
615
+ teams.append({
616
+ "rank": len(teams) + 1,
617
+ "team": row.get("TEAM_ABBREVIATION", row.get("Team", "N/A")),
618
+ "odds": round(float(row.get("champ_probability", row.get("Championship_Odds", 0))) * 100, 1),
619
+ "win_pct": round(float(row.get("W_PCT", 0.5)) * 100, 1)
620
+ })
621
+
622
+ # Update cache
623
+ cache["championship"]["data"] = {"teams": teams}
624
+ cache["championship"]["timestamp"] = datetime.utcnow()
625
+ logger.info(f"Championship cache updated with {len(teams)} teams")
626
+ except Exception as e:
627
+ logger.error(f"Background championship fetch error: {e}")
628
+
629
+ # If we have stale cache, return it immediately and refresh in background
630
+ if cache_data:
631
+ threading.Thread(target=fetch_champ_data, daemon=True).start()
632
+ return jsonify(cache_data)
633
+
634
+ # No cache - fetch synchronously (first request only)
635
  try:
636
  champ_df = pipeline.get_championship_odds()
637
 
 
644
  "win_pct": round(float(row.get("W_PCT", 0.5)) * 100, 1)
645
  })
646
 
647
+ # Update cache
648
+ cache["championship"]["data"] = {"teams": teams}
649
+ cache["championship"]["timestamp"] = datetime.utcnow()
650
+
651
  return jsonify({"teams": teams})
652
  except Exception as e:
653
  logger.error(f"Error in get_championship_odds: {e}")