Spaces:
Sleeping
Sleeping
Deploy HARP wrapper via model agent
Browse files
README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
---
|
| 2 |
-
title: "
|
| 3 |
colorFrom: indigo
|
| 4 |
colorTo: gray
|
| 5 |
sdk: gradio
|
|
@@ -9,7 +9,9 @@ pinned: false
|
|
| 9 |
license: "other"
|
| 10 |
---
|
| 11 |
|
| 12 |
-
#
|
|
|
|
|
|
|
| 13 |
|
| 14 |
- Inputs: textbox, slider, slider, checkbox, slider, slider, audio
|
| 15 |
- Outputs: audio, audio, audio
|
|
|
|
| 1 |
---
|
| 2 |
+
title: "Melodyflow"
|
| 3 |
colorFrom: indigo
|
| 4 |
colorTo: gray
|
| 5 |
sdk: gradio
|
|
|
|
| 9 |
license: "other"
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# Melodyflow
|
| 13 |
+
|
| 14 |
+
TODO: describe this model.
|
| 15 |
|
| 16 |
- Inputs: textbox, slider, slider, checkbox, slider, slider, audio
|
| 17 |
- Outputs: audio, audio, audio
|
app.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
import os
|
|
|
|
|
|
|
| 4 |
|
| 5 |
import gradio as gr
|
| 6 |
|
|
@@ -12,6 +14,10 @@ _BACKEND_SPACE = "facebook/MelodyFlow"
|
|
| 12 |
_BACKEND_API_NAME = "/predict"
|
| 13 |
_BACKEND_TOKEN_ENV = "HF_TOKEN"
|
| 14 |
_ACCEPT_USER_TOKEN = True
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
_client = None
|
| 16 |
|
| 17 |
|
|
@@ -26,6 +32,54 @@ def _backend_client():
|
|
| 26 |
return _client
|
| 27 |
|
| 28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
def _quota_hint(message):
|
| 30 |
# Turn a backend ZeroGPU quota error into an actionable message.
|
| 31 |
# NOTE: 'message' is the backend's error text; it never contains our token.
|
|
@@ -55,25 +109,31 @@ model_card = ModelCard(
|
|
| 55 |
|
| 56 |
def process_fn(text, steps, target_flowstep, regularize, regularization_strength, duration, melody, _hf_user_token=''):
|
| 57 |
_tok = (_hf_user_token or '').strip()
|
| 58 |
-
if
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
_values = list(_raw) if isinstance(_raw, (list, tuple)) else [_raw]
|
| 78 |
_detail = " | ".join(str(_v) for _v in _values if isinstance(_v, str) and _v.strip())
|
| 79 |
_out_generated_audio_variation_1 = _values[0] if len(_values) > 0 else None
|
|
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
import os
|
| 4 |
+
import time
|
| 5 |
+
import urllib.request
|
| 6 |
|
| 7 |
import gradio as gr
|
| 8 |
|
|
|
|
| 14 |
_BACKEND_API_NAME = "/predict"
|
| 15 |
_BACKEND_TOKEN_ENV = "HF_TOKEN"
|
| 16 |
_ACCEPT_USER_TOKEN = True
|
| 17 |
+
# How many times to wake+retry a sleeping backend, and how long to wait for
|
| 18 |
+
# it to boot (a free Space cold start can take a few minutes).
|
| 19 |
+
_CALL_RETRIES = int(os.environ.get("BACKEND_CALL_RETRIES", "4"))
|
| 20 |
+
_WAKE_TIMEOUT = float(os.environ.get("BACKEND_WAKE_TIMEOUT", "420"))
|
| 21 |
_client = None
|
| 22 |
|
| 23 |
|
|
|
|
| 32 |
return _client
|
| 33 |
|
| 34 |
|
| 35 |
+
def _reset_client():
|
| 36 |
+
# Drop the cached connection so the next attempt reconnects to a Space
|
| 37 |
+
# that has since finished waking.
|
| 38 |
+
global _client
|
| 39 |
+
_client = None
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def _make_conn(tok):
|
| 43 |
+
tok = (tok or '').strip()
|
| 44 |
+
if tok:
|
| 45 |
+
return Client(_BACKEND_SPACE, hf_token=tok)
|
| 46 |
+
return _backend_client()
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def _space_url(space):
|
| 50 |
+
slug = space.strip().lower().replace('/', '-').replace('_', '-')
|
| 51 |
+
return f'https://{slug}.hf.space/'
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def _is_cold_start(message):
|
| 55 |
+
# Errors that mean 'the backend was asleep/booting', worth waking+retrying
|
| 56 |
+
# (vs. a real application error, which we surface immediately).
|
| 57 |
+
_low = (message or '').lower()
|
| 58 |
+
return any(s in _low for s in (
|
| 59 |
+
'read operation timed out', 'timed out', 'timeout', 'starting',
|
| 60 |
+
'building', 'not ready', 'no application', 'connection', '503', '502',
|
| 61 |
+
))
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def _wake_backend():
|
| 65 |
+
# A sleeping Space boots when its URL is hit; poll until it answers (or
|
| 66 |
+
# the budget expires) so the retried call lands on a running backend.
|
| 67 |
+
_url = _space_url(_BACKEND_SPACE)
|
| 68 |
+
_deadline = time.time() + _WAKE_TIMEOUT
|
| 69 |
+
_delay = 5.0
|
| 70 |
+
while time.time() < _deadline:
|
| 71 |
+
try:
|
| 72 |
+
_req = urllib.request.Request(_url, headers={'User-Agent': 'harp-frontend'})
|
| 73 |
+
with urllib.request.urlopen(_req, timeout=30) as _resp:
|
| 74 |
+
if getattr(_resp, 'status', 200) < 500:
|
| 75 |
+
return True
|
| 76 |
+
except Exception:
|
| 77 |
+
pass
|
| 78 |
+
time.sleep(_delay)
|
| 79 |
+
_delay = min(_delay * 1.5, 30.0)
|
| 80 |
+
return False
|
| 81 |
+
|
| 82 |
+
|
| 83 |
def _quota_hint(message):
|
| 84 |
# Turn a backend ZeroGPU quota error into an actionable message.
|
| 85 |
# NOTE: 'message' is the backend's error text; it never contains our token.
|
|
|
|
| 109 |
|
| 110 |
def process_fn(text, steps, target_flowstep, regularize, regularization_strength, duration, melody, _hf_user_token=''):
|
| 111 |
_tok = (_hf_user_token or '').strip()
|
| 112 |
+
# Call the backend, waking it and retrying if it was asleep (a cold
|
| 113 |
+
# start otherwise fails the first hit with 'read operation timed out').
|
| 114 |
+
_raw = None
|
| 115 |
+
for _attempt in range(_CALL_RETRIES + 1):
|
| 116 |
+
try:
|
| 117 |
+
_conn = _make_conn(_tok)
|
| 118 |
+
_raw = _conn.predict(
|
| 119 |
+
'facebook/melodyflow-t24-30secs',
|
| 120 |
+
text,
|
| 121 |
+
'midpoint',
|
| 122 |
+
steps,
|
| 123 |
+
target_flowstep,
|
| 124 |
+
regularize,
|
| 125 |
+
regularization_strength,
|
| 126 |
+
duration,
|
| 127 |
+
(handle_file(melody) if melody else None),
|
| 128 |
+
api_name="/predict",
|
| 129 |
+
)
|
| 130 |
+
break
|
| 131 |
+
except Exception as _exc: # never surfaces the token
|
| 132 |
+
if _attempt < _CALL_RETRIES and _is_cold_start(str(_exc)):
|
| 133 |
+
_reset_client()
|
| 134 |
+
_wake_backend()
|
| 135 |
+
continue
|
| 136 |
+
raise gr.Error(_quota_hint(str(_exc)))
|
| 137 |
_values = list(_raw) if isinstance(_raw, (list, tuple)) else [_raw]
|
| 138 |
_detail = " | ".join(str(_v) for _v in _values if isinstance(_v, str) and _v.strip())
|
| 139 |
_out_generated_audio_variation_1 = _values[0] if len(_values) > 0 else None
|