Spaces:
Running
Running
main: background self-warm thread keeps the model hot 24/7
Browse filesUser is on a paid always-on (never-sleep) Space, yet the first request after a
day idle still took ~30s. Cause: the model path goes cold during idle (memory
paged out / CPU throttled) and /warmup never runs the model, so it can't keep it
warm. Added a daemon thread that runs a tiny LOCAL Wav2Vec2 inference every
SELF_WARM_INTERVAL_SEC (default 240s) — forward pass only, NO STT API calls
(those cost money) — so the model never goes cold regardless of app activity.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
main.py
CHANGED
|
@@ -13,6 +13,7 @@ import sys
|
|
| 13 |
import uuid
|
| 14 |
import time
|
| 15 |
import tempfile
|
|
|
|
| 16 |
from contextlib import asynccontextmanager
|
| 17 |
|
| 18 |
from fastapi import FastAPI, File, Form, UploadFile, HTTPException, Header
|
|
@@ -80,6 +81,14 @@ VALID_WORDS = {
|
|
| 80 |
}
|
| 81 |
|
| 82 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
@asynccontextmanager
|
| 84 |
async def lifespan(app: FastAPI):
|
| 85 |
"""Pre-load Wav2Vec2 model AND Supabase client before accepting requests.
|
|
@@ -122,6 +131,34 @@ async def lifespan(app: FastAPI):
|
|
| 122 |
except Exception as e:
|
| 123 |
print(f"[WARMUP] dummy scoring setup failed: {e}")
|
| 124 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
yield
|
| 126 |
|
| 127 |
|
|
|
|
| 13 |
import uuid
|
| 14 |
import time
|
| 15 |
import tempfile
|
| 16 |
+
import threading
|
| 17 |
from contextlib import asynccontextmanager
|
| 18 |
|
| 19 |
from fastapi import FastAPI, File, Form, UploadFile, HTTPException, Header
|
|
|
|
| 81 |
}
|
| 82 |
|
| 83 |
|
| 84 |
+
# How often the background thread re-runs a tiny LOCAL inference to keep the
|
| 85 |
+
# Wav2Vec2 model hot. The server is always-on, but after a day idle the model
|
| 86 |
+
# path goes cold (memory paged out / CPU throttled) and the first real request
|
| 87 |
+
# paid ~30s. /warmup never runs the model, so it can't prevent this. 240s keeps
|
| 88 |
+
# the model warm with negligible cost. Override via env if needed.
|
| 89 |
+
SELF_WARM_INTERVAL_SEC = int(os.environ.get("SELF_WARM_INTERVAL_SEC", "240"))
|
| 90 |
+
|
| 91 |
+
|
| 92 |
@asynccontextmanager
|
| 93 |
async def lifespan(app: FastAPI):
|
| 94 |
"""Pre-load Wav2Vec2 model AND Supabase client before accepting requests.
|
|
|
|
| 131 |
except Exception as e:
|
| 132 |
print(f"[WARMUP] dummy scoring setup failed: {e}")
|
| 133 |
|
| 134 |
+
# Background self-warm: keep the local Wav2Vec2 model HOT 24/7. The server is
|
| 135 |
+
# always-on (paid, never-sleep), but after a day idle the model path goes cold
|
| 136 |
+
# and the first real request paid ~30s. This daemon thread runs a tiny LOCAL
|
| 137 |
+
# inference every few minutes — Wav2Vec2 forward pass only, NO STT API calls
|
| 138 |
+
# (those cost money) — so the model never goes cold, independent of the app.
|
| 139 |
+
def _self_warm_loop():
|
| 140 |
+
import time as _time
|
| 141 |
+
import numpy as np
|
| 142 |
+
import soundfile as sf
|
| 143 |
+
warm_wav = os.path.join(tempfile.gettempdir(), "selfwarm.wav")
|
| 144 |
+
try:
|
| 145 |
+
rng = np.random.default_rng(1)
|
| 146 |
+
sf.write(warm_wav, (rng.standard_normal(8000) * 0.01).astype("float32"), 16000)
|
| 147 |
+
except Exception as e:
|
| 148 |
+
print(f"[SELF-WARM] setup failed, warmer not started: {e}")
|
| 149 |
+
return
|
| 150 |
+
from phoneme_extractor import shin_vs_samekh
|
| 151 |
+
while True:
|
| 152 |
+
_time.sleep(SELF_WARM_INTERVAL_SEC)
|
| 153 |
+
try:
|
| 154 |
+
shin_vs_samekh(warm_wav) # local Wav2Vec2 forward pass, no network
|
| 155 |
+
print("[SELF-WARM] model kept hot")
|
| 156 |
+
except Exception as e:
|
| 157 |
+
print(f"[SELF-WARM] tick failed: {e}")
|
| 158 |
+
|
| 159 |
+
threading.Thread(target=_self_warm_loop, daemon=True).start()
|
| 160 |
+
print(f"[SELF-WARM] background warmer started (every {SELF_WARM_INTERVAL_SEC}s)")
|
| 161 |
+
|
| 162 |
yield
|
| 163 |
|
| 164 |
|