Princeaka commited on
Commit
43155d4
·
verified ·
1 Parent(s): df538c5

Update brain_lazy.py

Browse files
Files changed (1) hide show
  1. brain_lazy.py +196 -45
brain_lazy.py CHANGED
@@ -1,57 +1,208 @@
1
  """
2
- brain_lazy.py
3
- Lazy loader for multimodular_modul_v7
4
- - Avoids Hugging Face runtime startup timeout
5
- - Loads brain in background while API/CLI starts instantly
 
 
 
 
 
 
 
6
  """
7
 
8
  import os
9
- import importlib
10
- import threading
11
  import time
 
 
 
 
 
 
 
 
 
 
 
12
 
13
- # ------------------------------
14
- # Persistent storage cache setup
15
- # ------------------------------
16
- os.environ["TRANSFORMERS_CACHE"] = "/home/user/app/cache"
17
- os.environ["HF_HOME"] = "/home/user/app/cache"
18
- os.makedirs("/home/user/app/cache", exist_ok=True)
19
-
20
- # ------------------------------
21
- # Brain loader
22
- # ------------------------------
23
- _brain = None
24
  _is_loading = False
25
  _is_ready = False
26
- _lock = threading.Lock()
27
 
28
- def _load_brain():
29
- global _brain, _is_ready, _is_loading
 
 
 
 
 
 
 
 
 
 
 
30
  with _lock:
31
- if _brain is None:
32
- _is_loading = True
33
- print("⏳ Loading multimodular brain (lazy mode)...")
34
- start_time = time.time()
35
- _brain = importlib.import_module("multimodular_modul_v7")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  _is_ready = True
 
 
 
 
 
 
 
37
  _is_loading = False
38
- print(f"✅ Brain loaded in {time.time() - start_time:.2f} seconds.")
39
- return _brain
40
-
41
- def preload_in_background():
42
- threading.Thread(target=_load_brain, daemon=True).start()
43
-
44
- # Start background preload at import
45
- preload_in_background()
46
-
47
- # ------------------------------
48
- # Proxy functions
49
- # ------------------------------
50
- def process_input(text): return _load_brain().process_input(text)
51
- def search_kb(query): return _load_brain().search_kb(query)
52
- def upload_media(path): return _load_brain().upload_media(path)
53
- def backup_brain(): return _load_brain().backup_brain()
54
- def restore_brain(): return _load_brain().restore_brain()
55
- def show_creative_skills(): return _load_brain().show_creative_skills()
56
- def sync_status(): return _load_brain().sync_status()
57
- def is_ready(): return _is_ready
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
+ brain_lazy.py (rewritten)
3
+ Purpose:
4
+ - Start your app FAST (no startup timeout on Hugging Face).
5
+ - Immediately after startup, auto-load the full multimodular brain in the background
6
+ (models, weights, heavy imports) so users don’t hit first-use lag.
7
+ - Keep your original multimodular_modul_v7.py completely untouched.
8
+
9
+ Notes:
10
+ - This does NOT change pip install time for big wheels (e.g., torch/timm). That happens
11
+ during Space build from requirements.txt. This file prevents slow *runtime* model
12
+ initialization by deferring it to a background preload right after boot.
13
  """
14
 
15
  import os
 
 
16
  import time
17
+ import threading
18
+ import importlib
19
+ from typing import Optional, Any
20
+
21
+ # -----------------------------------------------------------------------------
22
+ # Persistent cache (Hugging Face → enable Persistent storage in Space settings)
23
+ # -----------------------------------------------------------------------------
24
+ CACHE_DIR = "/home/user/app/cache"
25
+ os.environ.setdefault("TRANSFORMERS_CACHE", CACHE_DIR)
26
+ os.environ.setdefault("HF_HOME", CACHE_DIR)
27
+ os.makedirs(CACHE_DIR, exist_ok=True)
28
 
29
+ # -----------------------------------------------------------------------------
30
+ # Loader flags
31
+ # -----------------------------------------------------------------------------
32
+ _brain = None # the real multimodular module (multimodular_modul_v7)
33
+ _lock = threading.Lock()
 
 
 
 
 
 
34
  _is_loading = False
35
  _is_ready = False
36
+ _last_error: Optional[str] = None
37
 
38
+ # How long proxies will wait (max) for background preload before returning a
39
+ # graceful “warming up” message. Tweak for your UX.
40
+ PROXY_WAIT_SECONDS = 25
41
+
42
+ # -----------------------------------------------------------------------------
43
+ # Background preload
44
+ # -----------------------------------------------------------------------------
45
+ def _load_brain_blocking() -> Any:
46
+ """
47
+ Imports the heavy brain module and performs a light warm-up.
48
+ This runs either in the background (at startup) or on-demand if a call arrives early.
49
+ """
50
+ global _brain, _is_ready, _is_loading, _last_error
51
  with _lock:
52
+ if _brain is not None:
53
+ return _brain
54
+ if _is_loading:
55
+ # Another thread is loading; just return and let caller wait/poll.
56
+ return None
57
+
58
+ _is_loading = True
59
+ _last_error = None
60
+ start = time.time()
61
+ try:
62
+ print("⏳ [brain_lazy] Importing multimodular_modul_v7 ...")
63
+ brain = importlib.import_module("multimodular_modul_v7")
64
+
65
+ # Optional: if your module exposes an init() or warm_up(), call it.
66
+ # Otherwise, do a tiny no-op inference to trigger weights load.
67
+ warm_started = False
68
+ if hasattr(brain, "init"):
69
+ try:
70
+ brain.init()
71
+ warm_started = True
72
+ print("✅ [brain_lazy] brain.init() finished.")
73
+ except Exception as e:
74
+ print(f"⚠️ [brain_lazy] brain.init() failed: {e}")
75
+
76
+ if hasattr(brain, "warm_up"):
77
+ try:
78
+ brain.warm_up()
79
+ warm_started = True
80
+ print("✅ [brain_lazy] brain.warm_up() finished.")
81
+ except Exception as e:
82
+ print(f"⚠️ [brain_lazy] brain.warm_up() failed: {e}")
83
+
84
+ # Minimal warm-up if none provided
85
+ if not warm_started and hasattr(brain, "process_input"):
86
+ try:
87
+ _ = brain.process_input("ping")
88
+ print("✅ [brain_lazy] minimal warm-up via process_input('ping') done.")
89
+ except Exception as e:
90
+ print(f"⚠️ [brain_lazy] minimal warm-up failed: {e}")
91
+
92
+ _brain = brain
93
  _is_ready = True
94
+ print(f"✅ [brain_lazy] Brain loaded in {time.time() - start:.2f}s")
95
+ return _brain
96
+ except Exception as e:
97
+ _last_error = str(e)
98
+ print(f"❌ [brain_lazy] Brain load failed: {e}")
99
+ return None
100
+ finally:
101
  _is_loading = False
102
+
103
+ def _preload_thread():
104
+ """
105
+ Kicks off immediately at import so HF sees a fast boot,
106
+ then models load in background right away.
107
+ """
108
+ _load_brain_blocking()
109
+
110
+ # Start background preload now (non-blocking)
111
+ threading.Thread(target=_preload_thread, daemon=True).start()
112
+
113
+ # -----------------------------------------------------------------------------
114
+ # Helpers
115
+ # -----------------------------------------------------------------------------
116
+ def is_ready() -> bool:
117
+ return _is_ready
118
+
119
+ def last_error() -> Optional[str]:
120
+ return _last_error
121
+
122
+ def _ensure_loaded_with_wait(timeout_s: float) -> Optional[Any]:
123
+ """
124
+ Ensure the brain is loaded. Wait up to `timeout_s` seconds for background preload.
125
+ If still not ready after timeout, return None (caller can respond with a
126
+ graceful "warming up" message).
127
+ """
128
+ global _brain
129
+ # Fast path
130
+ if _brain is not None and _is_ready:
131
+ return _brain
132
+
133
+ # Trigger on-demand load if background hasn’t started (very rare)
134
+ if not _is_loading and _brain is None:
135
+ # Start a parallel load but do not block the whole timeout here;
136
+ # we will poll below.
137
+ threading.Thread(target=_load_brain_blocking, daemon=True).start()
138
+
139
+ waited = 0.0
140
+ interval = 0.25
141
+ while waited < timeout_s:
142
+ if _brain is not None and _is_ready:
143
+ return _brain
144
+ time.sleep(interval)
145
+ waited += interval
146
+
147
+ return None
148
+
149
+ def _warming_up_message(op: str) -> Any:
150
+ """
151
+ Graceful response while models finish loading.
152
+ You can customize this to fit your API schema/UI.
153
+ """
154
+ msg = {
155
+ "status": "warming_up",
156
+ "operation": op,
157
+ "detail": "CHB is loading models in the background. Please retry in a few seconds.",
158
+ "ready": _is_ready,
159
+ "error": _last_error,
160
+ }
161
+ return msg
162
+
163
+ # -----------------------------------------------------------------------------
164
+ # Public proxy API (mirrors your multimodular_modul_v7 public surface)
165
+ # Each call tries to use the loaded brain; if not ready within PROXY_WAIT_SECONDS,
166
+ # it returns a non-blocking 'warming_up' payload instead of hanging requests.
167
+ # -----------------------------------------------------------------------------
168
+ def process_input(text: str) -> Any:
169
+ brain = _ensure_loaded_with_wait(PROXY_WAIT_SECONDS)
170
+ if brain is None:
171
+ return _warming_up_message("process_input")
172
+ return brain.process_input(text)
173
+
174
+ def search_kb(query: str) -> Any:
175
+ brain = _ensure_loaded_with_wait(PROXY_WAIT_SECONDS)
176
+ if brain is None:
177
+ return _warming_up_message("search_kb")
178
+ return brain.search_kb(query)
179
+
180
+ def upload_media(file_path: str) -> Any:
181
+ brain = _ensure_loaded_with_wait(PROXY_WAIT_SECONDS)
182
+ if brain is None:
183
+ return _warming_up_message("upload_media")
184
+ return brain.upload_media(file_path)
185
+
186
+ def backup_brain() -> Any:
187
+ brain = _ensure_loaded_with_wait(PROXY_WAIT_SECONDS)
188
+ if brain is None:
189
+ return _warming_up_message("backup_brain")
190
+ return brain.backup_brain()
191
+
192
+ def restore_brain() -> Any:
193
+ brain = _ensure_loaded_with_wait(PROXY_WAIT_SECONDS)
194
+ if brain is None:
195
+ return _warming_up_message("restore_brain")
196
+ return brain.restore_brain()
197
+
198
+ def show_creative_skills() -> Any:
199
+ brain = _ensure_loaded_with_wait(PROXY_WAIT_SECONDS)
200
+ if brain is None:
201
+ return _warming_up_message("show_creative_skills")
202
+ return brain.show_creative_skills()
203
+
204
+ def sync_status() -> Any:
205
+ brain = _ensure_loaded_with_wait(PROXY_WAIT_SECONDS)
206
+ if brain is None:
207
+ return _warming_up_message("sync_status")
208
+ return brain.sync_status()