Spaces:
Running on Zero
Running on Zero
Commit ·
b36328a
1
Parent(s): c1f861c
Add user-adjustable GPU Duration slider to Advanced Settings
Browse filesExposes the spaces.GPU duration as a UI slider (10–120s, step 5s) so
users can tune quota usage per request. Switching modes auto-resets the
slider to its default (fast=20s, high_detail=60s).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- app.py +6 -5
- static/gallery.js +1 -0
- static/mode_toggle.js +8 -0
- static/run_preprocess.js +4 -2
- templates/app.html +5 -0
app.py
CHANGED
|
@@ -332,7 +332,7 @@ with open("templates/app.html") as _f:
|
|
| 332 |
|
| 333 |
# ── Gradio blocks ──────────────────────────────────────────────────────────────
|
| 334 |
|
| 335 |
-
def infer(images_b64_json, prompt, seed, randomize_seed, guidance_scale, steps, mode, progress=gr.Progress(track_tqdm=True)):
|
| 336 |
# CPU-only preprocessing — GPU not yet allocated
|
| 337 |
gc.collect()
|
| 338 |
pil_images = b64_to_pil_list(images_b64_json)
|
|
@@ -341,7 +341,7 @@ def infer(images_b64_json, prompt, seed, randomize_seed, guidance_scale, steps,
|
|
| 341 |
width, height = update_dimensions_on_upload(pil_images[0], max_dim_for_mode(mode))
|
| 342 |
t0 = time.perf_counter()
|
| 343 |
try:
|
| 344 |
-
result_image, seed, duration = _infer_gpu(pil_images, prompt, seed, guidance_scale, steps, width, height, mode)
|
| 345 |
# _spawn_log is called here (main process) so the thread survives after _infer_gpu's
|
| 346 |
# @spaces.GPU subprocess exits — previously the daemon thread was killed on subprocess exit.
|
| 347 |
_spawn_log(pil_images, result_image, prompt, seed, steps, guidance_scale, width, height, duration, True)
|
|
@@ -352,8 +352,8 @@ def infer(images_b64_json, prompt, seed, randomize_seed, guidance_scale, steps,
|
|
| 352 |
raise
|
| 353 |
|
| 354 |
|
| 355 |
-
@spaces.GPU(duration=lambda *a, **kw:
|
| 356 |
-
def _infer_gpu(pil_images, prompt, seed, guidance_scale, steps, width, height, mode):
|
| 357 |
_cuda_ok = torch.cuda.is_available()
|
| 358 |
timer = _InferTimer(_cuda_ok)
|
| 359 |
t0 = time.perf_counter()
|
|
@@ -427,6 +427,7 @@ with gr.Blocks() as demo:
|
|
| 427 |
guidance_scale = gr.Slider(minimum=1.0, maximum=10.0, step=0.1, value=1.0, elem_id="gradio-guidance", elem_classes="hidden-input", container=False)
|
| 428 |
steps = gr.Slider(minimum=1, maximum=50, step=1, value=4, elem_id="gradio-steps", elem_classes="hidden-input", container=False)
|
| 429 |
mode = gr.Textbox(value="fast", elem_id="gradio-mode", elem_classes="hidden-input", container=False)
|
|
|
|
| 430 |
result = gr.Image(elem_id="gradio-result", elem_classes="hidden-input", container=False, format="png")
|
| 431 |
|
| 432 |
example_idx = gr.Textbox(value="", elem_id="example-idx-input", elem_classes="hidden-input", container=False)
|
|
@@ -443,7 +444,7 @@ with gr.Blocks() as demo:
|
|
| 443 |
|
| 444 |
run_btn.click(
|
| 445 |
fn=infer,
|
| 446 |
-
inputs=[hidden_images_b64, prompt, seed, randomize_seed, guidance_scale, steps, mode],
|
| 447 |
outputs=[result, seed],
|
| 448 |
js=run_preprocess_js,
|
| 449 |
)
|
|
|
|
| 332 |
|
| 333 |
# ── Gradio blocks ──────────────────────────────────────────────────────────────
|
| 334 |
|
| 335 |
+
def infer(images_b64_json, prompt, seed, randomize_seed, guidance_scale, steps, mode, gpu_duration=20, progress=gr.Progress(track_tqdm=True)):
|
| 336 |
# CPU-only preprocessing — GPU not yet allocated
|
| 337 |
gc.collect()
|
| 338 |
pil_images = b64_to_pil_list(images_b64_json)
|
|
|
|
| 341 |
width, height = update_dimensions_on_upload(pil_images[0], max_dim_for_mode(mode))
|
| 342 |
t0 = time.perf_counter()
|
| 343 |
try:
|
| 344 |
+
result_image, seed, duration = _infer_gpu(pil_images, prompt, seed, guidance_scale, steps, width, height, mode, int(gpu_duration))
|
| 345 |
# _spawn_log is called here (main process) so the thread survives after _infer_gpu's
|
| 346 |
# @spaces.GPU subprocess exits — previously the daemon thread was killed on subprocess exit.
|
| 347 |
_spawn_log(pil_images, result_image, prompt, seed, steps, guidance_scale, width, height, duration, True)
|
|
|
|
| 352 |
raise
|
| 353 |
|
| 354 |
|
| 355 |
+
@spaces.GPU(duration=lambda *a, **kw: int(a[8]) if len(a) > 8 else 60)
|
| 356 |
+
def _infer_gpu(pil_images, prompt, seed, guidance_scale, steps, width, height, mode, gpu_duration=20):
|
| 357 |
_cuda_ok = torch.cuda.is_available()
|
| 358 |
timer = _InferTimer(_cuda_ok)
|
| 359 |
t0 = time.perf_counter()
|
|
|
|
| 427 |
guidance_scale = gr.Slider(minimum=1.0, maximum=10.0, step=0.1, value=1.0, elem_id="gradio-guidance", elem_classes="hidden-input", container=False)
|
| 428 |
steps = gr.Slider(minimum=1, maximum=50, step=1, value=4, elem_id="gradio-steps", elem_classes="hidden-input", container=False)
|
| 429 |
mode = gr.Textbox(value="fast", elem_id="gradio-mode", elem_classes="hidden-input", container=False)
|
| 430 |
+
gpu_duration = gr.Slider(minimum=10, maximum=120, step=5, value=20, elem_id="gradio-gpu-duration", elem_classes="hidden-input", container=False)
|
| 431 |
result = gr.Image(elem_id="gradio-result", elem_classes="hidden-input", container=False, format="png")
|
| 432 |
|
| 433 |
example_idx = gr.Textbox(value="", elem_id="example-idx-input", elem_classes="hidden-input", container=False)
|
|
|
|
| 444 |
|
| 445 |
run_btn.click(
|
| 446 |
fn=infer,
|
| 447 |
+
inputs=[hidden_images_b64, prompt, seed, randomize_seed, guidance_scale, steps, mode, gpu_duration],
|
| 448 |
outputs=[result, seed],
|
| 449 |
js=run_preprocess_js,
|
| 450 |
)
|
static/gallery.js
CHANGED
|
@@ -234,6 +234,7 @@ function init() {
|
|
| 234 |
syncSlider('custom-seed', 'gradio-seed');
|
| 235 |
syncSlider('custom-guidance', 'gradio-guidance');
|
| 236 |
syncSlider('custom-steps', 'gradio-steps');
|
|
|
|
| 237 |
|
| 238 |
const randCheck = document.getElementById('custom-randomize');
|
| 239 |
if (randCheck) {
|
|
|
|
| 234 |
syncSlider('custom-seed', 'gradio-seed');
|
| 235 |
syncSlider('custom-guidance', 'gradio-guidance');
|
| 236 |
syncSlider('custom-steps', 'gradio-steps');
|
| 237 |
+
syncSlider('custom-gpu-duration', 'gradio-gpu-duration');
|
| 238 |
|
| 239 |
const randCheck = document.getElementById('custom-randomize');
|
| 240 |
if (randCheck) {
|
static/mode_toggle.js
CHANGED
|
@@ -1,10 +1,18 @@
|
|
| 1 |
() => {
|
| 2 |
window.__selectedMode = 'fast';
|
|
|
|
| 3 |
window.__setMode = function(m) {
|
| 4 |
window.__selectedMode = m;
|
| 5 |
var fast = document.getElementById('mode-btn-fast');
|
| 6 |
var hd = document.getElementById('mode-btn-hd');
|
| 7 |
if (fast) fast.classList.toggle('mode-btn-active', m === 'fast');
|
| 8 |
if (hd) hd.classList.toggle('mode-btn-active', m === 'high_detail');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
};
|
| 10 |
}
|
|
|
|
| 1 |
() => {
|
| 2 |
window.__selectedMode = 'fast';
|
| 3 |
+
var MODE_GPU_DURATION = { fast: 20, high_detail: 60 };
|
| 4 |
window.__setMode = function(m) {
|
| 5 |
window.__selectedMode = m;
|
| 6 |
var fast = document.getElementById('mode-btn-fast');
|
| 7 |
var hd = document.getElementById('mode-btn-hd');
|
| 8 |
if (fast) fast.classList.toggle('mode-btn-active', m === 'fast');
|
| 9 |
if (hd) hd.classList.toggle('mode-btn-active', m === 'high_detail');
|
| 10 |
+
var dur = MODE_GPU_DURATION[m];
|
| 11 |
+
if (dur !== undefined) {
|
| 12 |
+
var sl = document.getElementById('custom-gpu-duration');
|
| 13 |
+
var vl = document.getElementById('custom-gpu-duration-val');
|
| 14 |
+
if (sl) { sl.value = dur; sl.dispatchEvent(new Event('input', {bubbles: true})); }
|
| 15 |
+
if (vl) vl.textContent = dur;
|
| 16 |
+
}
|
| 17 |
};
|
| 18 |
}
|
static/run_preprocess.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
| 1 |
-
(imgs, p, s, rs, gs, st, m) => {
|
| 2 |
const images = window.__uploadedImages || [];
|
| 3 |
const b64Array = images.map(img => img.b64);
|
| 4 |
const imgsJson = JSON.stringify(b64Array);
|
| 5 |
const promptEl = document.getElementById('custom-prompt-input');
|
| 6 |
const promptVal = promptEl ? promptEl.value : p;
|
| 7 |
const mode = window.__selectedMode || 'fast';
|
| 8 |
-
|
|
|
|
|
|
|
| 9 |
}
|
|
|
|
| 1 |
+
(imgs, p, s, rs, gs, st, m, gd) => {
|
| 2 |
const images = window.__uploadedImages || [];
|
| 3 |
const b64Array = images.map(img => img.b64);
|
| 4 |
const imgsJson = JSON.stringify(b64Array);
|
| 5 |
const promptEl = document.getElementById('custom-prompt-input');
|
| 6 |
const promptVal = promptEl ? promptEl.value : p;
|
| 7 |
const mode = window.__selectedMode || 'fast';
|
| 8 |
+
const gpuDurEl = document.getElementById('custom-gpu-duration');
|
| 9 |
+
const gpuDuration = gpuDurEl ? parseInt(gpuDurEl.value, 10) : gd;
|
| 10 |
+
return [imgsJson, promptVal, s, rs, gs, st, mode, gpuDuration];
|
| 11 |
}
|
templates/app.html
CHANGED
|
@@ -133,6 +133,11 @@
|
|
| 133 |
<input type="range" id="custom-steps" min="1" max="50" step="1" value="4">
|
| 134 |
<span class="slider-val" id="custom-steps-val">4</span>
|
| 135 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
</div>
|
| 137 |
</div>
|
| 138 |
</div>
|
|
|
|
| 133 |
<input type="range" id="custom-steps" min="1" max="50" step="1" value="4">
|
| 134 |
<span class="slider-val" id="custom-steps-val">4</span>
|
| 135 |
</div>
|
| 136 |
+
<div class="slider-row">
|
| 137 |
+
<label>GPU Duration (s)</label>
|
| 138 |
+
<input type="range" id="custom-gpu-duration" min="10" max="120" step="5" value="20">
|
| 139 |
+
<span class="slider-val" id="custom-gpu-duration-val">20</span>
|
| 140 |
+
</div>
|
| 141 |
</div>
|
| 142 |
</div>
|
| 143 |
</div>
|