atakan Claude Opus 5 commited on
Commit
098a4cf
·
1 Parent(s): 5388c07

fix: Launch the probe again, with SSR off

Browse files

Mounting the probe instead of launching it failed startup outright: "No
@spaces.GPU function detected during startup". So mounting does not register the
handler the platform's validation looks for, and the original note in this file
was right -- the probe must be launch()ed. My reversal of it was wrong and is
corrected in place, with what was actually observed, so the next person does not
try mounting again.

Keeping the part of that change that stands on its own: ssr_mode=False. Gradio 6
defaults to SSR and spawns a Node subprocess ("Node proxy -> Python :7862" in the
startup log) into the process ZeroGPU forks its CUDA worker from, and a fork
parent holding a subprocess is a plausible cause of the worker's "No CUDA GPUs
are available". That is now the only variable changed against the last build
that reached GPU acquisition.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Files changed (1) hide show
  1. app_space.py +21 -13
app_space.py CHANGED
@@ -10,13 +10,16 @@ ControlAI, and each of the four below was learned by having the Space fail:
10
  function detected". Hence the hidden probe button.
11
  2. That function must be a module-level `def`. Nested inside a `with
12
  gr.Blocks():` block, the same detection fails.
13
- 3. Mount the probe into the FastAPI app; do not `launch()` it. An earlier note
14
- said the opposite -- that mounting collided with "address already in use" --
15
- but that was mounting *and* calling `launch()`, i.e. two servers. Mounting
16
- alone, with one `uvicorn.run`, is a single server and does not collide.
17
- Launching it separately is actively harmful now: Gradio 6 launches with SSR,
18
- which spawns a Node subprocess and a server thread into the very process
19
- ZeroGPU then `fork()`s its CUDA worker from.
 
 
 
20
  4. Never pass the model-holding object as an *argument* to a `@spaces.GPU`
21
  function. ZeroGPU marshals arguments across a process boundary and will try
22
  to share the model's CUDA tensors, failing with `_share_cuda_: only
@@ -217,12 +220,17 @@ with gr.Blocks() as _gpu_demo:
217
 
218
  def main() -> None:
219
  port = int(os.environ.get("PORT", 7860))
220
- # Mounted, not launched: one server, one process, no Node SSR subprocess and
221
- # no extra server thread in the process ZeroGPU forks its CUDA worker from.
222
- # The mount still counts as wiring _gpu_probe to a Gradio event handler,
223
- # which is what the platform's startup validation looks for.
224
- merged = gr.mount_gradio_app(app, _gpu_demo, path="/_gpu")
225
- uvicorn.run(merged, host="0.0.0.0", port=port, log_level="info")
 
 
 
 
 
226
 
227
 
228
  if __name__ == "__main__":
 
10
  function detected". Hence the hidden probe button.
11
  2. That function must be a module-level `def`. Nested inside a `with
12
  gr.Blocks():` block, the same detection fails.
13
+ 3. The probe must be `launch()`ed, on its own port, and **not** merely mounted.
14
+ Mounting it with `gr.mount_gradio_app` was tried: the Space fails to start
15
+ with "No @spaces.GPU function detected during startup", so mounting does not
16
+ register the handler the way the platform's validation looks for. Do not
17
+ mount it into the FastAPI app and *also* run uvicorn on the same port,
18
+ though -- that is two servers and collides with "address already in use".
19
+ Launch with `ssr_mode=False`: Gradio 6 defaults to SSR, which spawns a Node
20
+ subprocess into the very process ZeroGPU then `fork()`s its CUDA worker
21
+ from, and a fork parent holding a subprocess is a plausible cause of the
22
+ worker's "No CUDA GPUs are available".
23
  4. Never pass the model-holding object as an *argument* to a `@spaces.GPU`
24
  function. ZeroGPU marshals arguments across a process boundary and will try
25
  to share the model's CUDA tensors, failing with `_share_cuda_: only
 
220
 
221
  def main() -> None:
222
  port = int(os.environ.get("PORT", 7860))
223
+ # Launched, not mounted: mounting fails startup validation ("No @spaces.GPU
224
+ # function detected"). Separate port, non-blocking, and ssr_mode=False so no
225
+ # Node subprocess lands in the process ZeroGPU forks its CUDA worker from.
226
+ _gpu_demo.launch(
227
+ server_name="0.0.0.0",
228
+ server_port=port + 1,
229
+ prevent_thread_lock=True,
230
+ share=False,
231
+ ssr_mode=False,
232
+ )
233
+ uvicorn.run(app, host="0.0.0.0", port=port, log_level="info")
234
 
235
 
236
  if __name__ == "__main__":