Spaces:
Runtime error
Runtime error
Update
Browse files- app.py +21 -73
- constants.py +47 -0
- demo_list.py +63 -30
- restart_scheduler.py +25 -0
- settings.py +5 -0
app.py
CHANGED
@@ -2,79 +2,22 @@
|
|
2 |
|
3 |
from __future__ import annotations
|
4 |
|
5 |
-
import os
|
6 |
-
|
7 |
import gradio as gr
|
8 |
-
import pandas as pd
|
9 |
-
from apscheduler.schedulers.background import BackgroundScheduler
|
10 |
-
from huggingface_hub import HfApi
|
11 |
|
|
|
|
|
12 |
from demo_list import DemoList
|
|
|
|
|
13 |
|
14 |
demo_list = DemoList()
|
15 |
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
scheduler = BackgroundScheduler()
|
21 |
-
scheduler.add_job(func=lambda: api.restart_space(SPACE_ID),
|
22 |
-
trigger='interval',
|
23 |
-
seconds=60 * INTERVAL_MIN)
|
24 |
scheduler.start()
|
25 |
|
26 |
-
STATUS_CHOICES = [
|
27 |
-
'RUNNING',
|
28 |
-
'PAUSED',
|
29 |
-
'STOPPED',
|
30 |
-
'RUNTIME_ERROR',
|
31 |
-
'BUILD_ERROR',
|
32 |
-
'BUILDING',
|
33 |
-
]
|
34 |
-
HARDWARE_CHOICES = [
|
35 |
-
'cpu-basic',
|
36 |
-
'cpu-upgrade',
|
37 |
-
't4-small',
|
38 |
-
't4-medium',
|
39 |
-
'zero-a10g',
|
40 |
-
'a10g-small',
|
41 |
-
'a10g-large',
|
42 |
-
'a100-large',
|
43 |
-
]
|
44 |
-
SDK_CHOICES = [
|
45 |
-
'gradio',
|
46 |
-
'streamlit',
|
47 |
-
'docker',
|
48 |
-
]
|
49 |
-
SLEEP_TIME_CHOICES = list(demo_list.TO_TIME_STR.values())
|
50 |
-
SLEEP_TIME_STR_TO_INT = {v: k for k, v in demo_list.TO_TIME_STR.items()}
|
51 |
-
OWNER_CHOICES = [WHOAMI, 'other organizations']
|
52 |
-
|
53 |
-
|
54 |
-
def update_df(status: list[str], hardware: list[str], sdk: list[str],
|
55 |
-
sleep_time: list[str], owner: list[str],
|
56 |
-
multiple_replicas: bool) -> pd.DataFrame:
|
57 |
-
df_raw = demo_list.df_raw
|
58 |
-
df = demo_list.df
|
59 |
-
|
60 |
-
if multiple_replicas:
|
61 |
-
df = df[df_raw.replicas > 1]
|
62 |
-
|
63 |
-
df = df[(df_raw.status.isin(status)) & (df_raw.hardware.isin(hardware)) &
|
64 |
-
(df_raw.sdk.isin(sdk))]
|
65 |
-
|
66 |
-
sleep_time_int = [SLEEP_TIME_STR_TO_INT[s] for s in sleep_time]
|
67 |
-
df = df[df_raw.sleep_time.isin(sleep_time_int)]
|
68 |
-
|
69 |
-
if set(owner) == set(OWNER_CHOICES):
|
70 |
-
pass
|
71 |
-
elif WHOAMI in owner:
|
72 |
-
df = df[df_raw.owner == WHOAMI]
|
73 |
-
else:
|
74 |
-
df = df[df_raw.owner != WHOAMI]
|
75 |
-
|
76 |
-
return df
|
77 |
-
|
78 |
|
79 |
def update_status_checkboxes(choices: list[str]) -> list[str]:
|
80 |
if '(ALL)' in choices:
|
@@ -123,20 +66,24 @@ with gr.Blocks(css='style.css') as demo:
|
|
123 |
HARDWARE_CHOICES,
|
124 |
value=HARDWARE_CHOICES,
|
125 |
type='value')
|
126 |
-
sdk = gr.CheckboxGroup(label='SDK',
|
127 |
-
choices=['(ALL)', '(NONE)'] + SDK_CHOICES,
|
128 |
-
value=SDK_CHOICES,
|
129 |
-
type='value')
|
130 |
sleep_time = gr.CheckboxGroup(label='Sleep time',
|
131 |
choices=['(ALL)', '(NONE)'] +
|
132 |
SLEEP_TIME_CHOICES,
|
133 |
value=SLEEP_TIME_CHOICES,
|
134 |
type='value')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
135 |
owner = gr.CheckboxGroup(label='Owner',
|
136 |
choices=OWNER_CHOICES,
|
137 |
value=OWNER_CHOICES,
|
138 |
type='value')
|
139 |
-
multiple_replicas = gr.Checkbox(label='Multiple replicas', value=False)
|
140 |
apply_button = gr.Button('Apply')
|
141 |
df = gr.Dataframe(value=demo_list.df,
|
142 |
datatype=demo_list.column_datatype,
|
@@ -166,14 +113,15 @@ with gr.Blocks(css='style.css') as demo:
|
|
166 |
queue=False,
|
167 |
show_progress=False,
|
168 |
api_name=False)
|
169 |
-
apply_button.click(fn=
|
170 |
inputs=[
|
171 |
status,
|
172 |
hardware,
|
173 |
-
sdk,
|
174 |
sleep_time,
|
175 |
-
owner,
|
176 |
multiple_replicas,
|
|
|
|
|
|
|
177 |
],
|
178 |
outputs=df,
|
179 |
api_name=False)
|
|
|
2 |
|
3 |
from __future__ import annotations
|
4 |
|
|
|
|
|
5 |
import gradio as gr
|
|
|
|
|
|
|
6 |
|
7 |
+
from constants import (HARDWARE_CHOICES, OWNER_CHOICES, SDK_CHOICES,
|
8 |
+
SLEEP_TIME_CHOICES, STATUS_CHOICES, VISIBILITY_CHOICES)
|
9 |
from demo_list import DemoList
|
10 |
+
from restart_scheduler import RestartScheduler
|
11 |
+
from settings import HF_TOKEN, INTERVAL_MINUTES, SPACE_ID
|
12 |
|
13 |
demo_list = DemoList()
|
14 |
|
15 |
+
if SPACE_ID is not None and INTERVAL_MINUTES > 0:
|
16 |
+
scheduler = RestartScheduler(space_id=SPACE_ID,
|
17 |
+
interval_minutes=INTERVAL_MINUTES,
|
18 |
+
hf_token=HF_TOKEN)
|
|
|
|
|
|
|
|
|
19 |
scheduler.start()
|
20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
def update_status_checkboxes(choices: list[str]) -> list[str]:
|
23 |
if '(ALL)' in choices:
|
|
|
66 |
HARDWARE_CHOICES,
|
67 |
value=HARDWARE_CHOICES,
|
68 |
type='value')
|
|
|
|
|
|
|
|
|
69 |
sleep_time = gr.CheckboxGroup(label='Sleep time',
|
70 |
choices=['(ALL)', '(NONE)'] +
|
71 |
SLEEP_TIME_CHOICES,
|
72 |
value=SLEEP_TIME_CHOICES,
|
73 |
type='value')
|
74 |
+
multiple_replicas = gr.Checkbox(label='Multiple replicas', value=False)
|
75 |
+
sdk = gr.CheckboxGroup(label='SDK',
|
76 |
+
choices=['(ALL)', '(NONE)'] + SDK_CHOICES,
|
77 |
+
value=SDK_CHOICES,
|
78 |
+
type='value')
|
79 |
+
visibility = gr.CheckboxGroup(label='Visibility',
|
80 |
+
choices=VISIBILITY_CHOICES,
|
81 |
+
value=VISIBILITY_CHOICES,
|
82 |
+
type='value')
|
83 |
owner = gr.CheckboxGroup(label='Owner',
|
84 |
choices=OWNER_CHOICES,
|
85 |
value=OWNER_CHOICES,
|
86 |
type='value')
|
|
|
87 |
apply_button = gr.Button('Apply')
|
88 |
df = gr.Dataframe(value=demo_list.df,
|
89 |
datatype=demo_list.column_datatype,
|
|
|
113 |
queue=False,
|
114 |
show_progress=False,
|
115 |
api_name=False)
|
116 |
+
apply_button.click(fn=demo_list.apply_filter,
|
117 |
inputs=[
|
118 |
status,
|
119 |
hardware,
|
|
|
120 |
sleep_time,
|
|
|
121 |
multiple_replicas,
|
122 |
+
sdk,
|
123 |
+
visibility,
|
124 |
+
owner,
|
125 |
],
|
126 |
outputs=df,
|
127 |
api_name=False)
|
constants.py
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from huggingface_hub import HfApi
|
2 |
+
|
3 |
+
STATUS_CHOICES = [
|
4 |
+
'RUNNING',
|
5 |
+
'PAUSED',
|
6 |
+
'STOPPED',
|
7 |
+
'RUNTIME_ERROR',
|
8 |
+
'BUILD_ERROR',
|
9 |
+
'BUILDING',
|
10 |
+
'RUNNING_BUILDING',
|
11 |
+
'NO_APP_FILE',
|
12 |
+
]
|
13 |
+
HARDWARE_CHOICES = [
|
14 |
+
'cpu-basic',
|
15 |
+
'cpu-upgrade',
|
16 |
+
't4-small',
|
17 |
+
't4-medium',
|
18 |
+
'zero-a10g',
|
19 |
+
'a10g-small',
|
20 |
+
'a10g-large',
|
21 |
+
'a100-large',
|
22 |
+
]
|
23 |
+
SDK_CHOICES = [
|
24 |
+
'gradio',
|
25 |
+
'streamlit',
|
26 |
+
'docker',
|
27 |
+
]
|
28 |
+
SLEEP_TIME_INT_TO_STR = {
|
29 |
+
-1: 'null',
|
30 |
+
300: '5 minutes',
|
31 |
+
900: '15 minutes',
|
32 |
+
1800: '30 minutes',
|
33 |
+
3600: '1 hour',
|
34 |
+
36000: '10 hours',
|
35 |
+
86400: '24 hours',
|
36 |
+
172800: '48 hours',
|
37 |
+
259200: '72 hours',
|
38 |
+
604800: '1 week',
|
39 |
+
}
|
40 |
+
SLEEP_TIME_CHOICES = list(SLEEP_TIME_INT_TO_STR.values())
|
41 |
+
SLEEP_TIME_STR_TO_INT = {v: k for k, v in SLEEP_TIME_INT_TO_STR.items()}
|
42 |
+
|
43 |
+
VISIBILITY_CHOICES = ['public', 'private']
|
44 |
+
|
45 |
+
api = HfApi()
|
46 |
+
WHOAMI = api.whoami()['name']
|
47 |
+
OWNER_CHOICES = [WHOAMI, 'other organizations']
|
demo_list.py
CHANGED
@@ -7,6 +7,9 @@ import tqdm.auto
|
|
7 |
import yaml
|
8 |
from huggingface_hub import HfApi
|
9 |
|
|
|
|
|
|
|
10 |
repo_dir = pathlib.Path(__file__).parent
|
11 |
|
12 |
|
@@ -29,20 +32,6 @@ class DemoList:
|
|
29 |
['replicas', 'markdown'],
|
30 |
]
|
31 |
|
32 |
-
TO_TIME_STR = {
|
33 |
-
-1: 'null',
|
34 |
-
300: '5 minutes',
|
35 |
-
600: '10 minutes',
|
36 |
-
900: '15 minutes',
|
37 |
-
1800: '30 minutes',
|
38 |
-
3600: '1 hour',
|
39 |
-
36000: '10 hours',
|
40 |
-
86400: '24 hours',
|
41 |
-
172800: '48 hours',
|
42 |
-
259200: '72 hours',
|
43 |
-
604800: '1 week',
|
44 |
-
}
|
45 |
-
|
46 |
def __init__(self):
|
47 |
self.api = HfApi()
|
48 |
self._raw_data = self.load_data()
|
@@ -69,31 +58,39 @@ class DemoList:
|
|
69 |
space_id = self.get_space_id(url)
|
70 |
space_info = self.api.space_info(repo_id=space_id)
|
71 |
card = space_info.cardData
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
|
74 |
for tag in ['arxiv', 'github', 'tags']:
|
75 |
if tag not in info:
|
76 |
info[tag] = []
|
77 |
|
78 |
-
|
79 |
-
info['owner'] = space_id.split('/')[0]
|
80 |
-
info['title'] = card['title']
|
81 |
-
info['sdk'] = card['sdk']
|
82 |
-
info['sdk_version'] = card.get('sdk_version', '')
|
83 |
-
info['likes'] = space_info.likes
|
84 |
-
info['last_modified'] = space_info.lastModified
|
85 |
-
info['status'] = space_info.runtime['stage']
|
86 |
-
|
87 |
-
info['suggested_hardware'] = card.get('suggested_hardware', '')
|
88 |
info['hardware'] = space_info.runtime['hardware']['current']
|
89 |
if info['hardware'] is None:
|
90 |
info['hardware'] = space_info.runtime['hardware']['requested']
|
91 |
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
|
|
|
|
|
|
|
|
96 |
|
|
|
97 |
resources = space_info.runtime['resources']
|
98 |
info['replicas'] = -1 if resources is None else resources[
|
99 |
'replicas']
|
@@ -175,10 +172,46 @@ class DemoList:
|
|
175 |
row.sdk_version,
|
176 |
'sleep_time':
|
177 |
self.add_div_tag_to_sleep_time(
|
178 |
-
|
179 |
'replicas':
|
180 |
self.add_div_tag_to_replicas(row.replicas),
|
181 |
}
|
182 |
new_rows.append(new_row)
|
183 |
df = pd.DataFrame(new_rows).loc[:, self.column_names]
|
184 |
return df
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
import yaml
|
8 |
from huggingface_hub import HfApi
|
9 |
|
10 |
+
from constants import (OWNER_CHOICES, SLEEP_TIME_INT_TO_STR,
|
11 |
+
SLEEP_TIME_STR_TO_INT, WHOAMI)
|
12 |
+
|
13 |
repo_dir = pathlib.Path(__file__).parent
|
14 |
|
15 |
|
|
|
32 |
['replicas', 'markdown'],
|
33 |
]
|
34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
def __init__(self):
|
36 |
self.api = HfApi()
|
37 |
self._raw_data = self.load_data()
|
|
|
58 |
space_id = self.get_space_id(url)
|
59 |
space_info = self.api.space_info(repo_id=space_id)
|
60 |
card = space_info.cardData
|
61 |
+
|
62 |
+
info: dict = data[url] | {
|
63 |
+
'url': url,
|
64 |
+
'title': card['title'] if 'title' in card else space_id,
|
65 |
+
'owner': space_id.split('/')[0],
|
66 |
+
'sdk': card['sdk'],
|
67 |
+
'sdk_version': card.get('sdk_version', ''),
|
68 |
+
'likes': space_info.likes,
|
69 |
+
'private': space_info.private,
|
70 |
+
'last_modified': space_info.lastModified,
|
71 |
+
'status': space_info.runtime['stage'],
|
72 |
+
'suggested_hardware': card.get('suggested_hardware', ''),
|
73 |
+
}
|
74 |
|
75 |
for tag in ['arxiv', 'github', 'tags']:
|
76 |
if tag not in info:
|
77 |
info[tag] = []
|
78 |
|
79 |
+
# `current` of paused Spaces is `None`, but `requested` is not
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
info['hardware'] = space_info.runtime['hardware']['current']
|
81 |
if info['hardware'] is None:
|
82 |
info['hardware'] = space_info.runtime['hardware']['requested']
|
83 |
|
84 |
+
# `gcTimeout` is `None` for `cpu-basic` Spaces and Spaces
|
85 |
+
# with "Don't sleep" sleep time.
|
86 |
+
# We use `-1` to represent it.
|
87 |
+
info['sleep_time'] = space_info.runtime['gcTimeout'] or -1
|
88 |
+
if info['sleep_time'] not in SLEEP_TIME_INT_TO_STR:
|
89 |
+
print(space_id)
|
90 |
+
print(f'Unknown sleep time: {info["sleep_time"]}')
|
91 |
+
continue
|
92 |
|
93 |
+
# `resources` of paused Spaces is `None`
|
94 |
resources = space_info.runtime['resources']
|
95 |
info['replicas'] = -1 if resources is None else resources[
|
96 |
'replicas']
|
|
|
172 |
row.sdk_version,
|
173 |
'sleep_time':
|
174 |
self.add_div_tag_to_sleep_time(
|
175 |
+
SLEEP_TIME_INT_TO_STR[row.sleep_time], row.hardware),
|
176 |
'replicas':
|
177 |
self.add_div_tag_to_replicas(row.replicas),
|
178 |
}
|
179 |
new_rows.append(new_row)
|
180 |
df = pd.DataFrame(new_rows).loc[:, self.column_names]
|
181 |
return df
|
182 |
+
|
183 |
+
def apply_filter(
|
184 |
+
self,
|
185 |
+
status: list[str],
|
186 |
+
hardware: list[str],
|
187 |
+
sleep_time: list[str],
|
188 |
+
multiple_replicas: bool,
|
189 |
+
sdk: list[str],
|
190 |
+
visibility: list[str],
|
191 |
+
owner: list[str],
|
192 |
+
) -> pd.DataFrame:
|
193 |
+
df_raw = self.df_raw
|
194 |
+
df = self.df
|
195 |
+
|
196 |
+
if multiple_replicas:
|
197 |
+
df = df[df_raw.replicas > 1]
|
198 |
+
|
199 |
+
if visibility == ['public']:
|
200 |
+
df = df[~df_raw.private]
|
201 |
+
elif visibility == ['private']:
|
202 |
+
df = df[df_raw.private]
|
203 |
+
|
204 |
+
df = df[(df_raw.status.isin(status)) & (df_raw.hardware.isin(hardware))
|
205 |
+
& (df_raw.sdk.isin(sdk))]
|
206 |
+
|
207 |
+
sleep_time_int = [SLEEP_TIME_STR_TO_INT[s] for s in sleep_time]
|
208 |
+
df = df[df_raw.sleep_time.isin(sleep_time_int)]
|
209 |
+
|
210 |
+
if set(owner) == set(OWNER_CHOICES):
|
211 |
+
pass
|
212 |
+
elif WHOAMI in owner:
|
213 |
+
df = df[df_raw.owner == WHOAMI]
|
214 |
+
else:
|
215 |
+
df = df[df_raw.owner != WHOAMI]
|
216 |
+
|
217 |
+
return df
|
restart_scheduler.py
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from apscheduler.schedulers.background import BackgroundScheduler
|
2 |
+
from huggingface_hub import HfApi
|
3 |
+
from huggingface_hub.utils import RepositoryNotFoundError
|
4 |
+
|
5 |
+
|
6 |
+
class RestartScheduler:
|
7 |
+
def __init__(self, space_id: str, interval_minutes: int,
|
8 |
+
hf_token: str | None):
|
9 |
+
api = HfApi(token=hf_token)
|
10 |
+
if api.get_token_permission() != 'write':
|
11 |
+
raise ValueError('The HF token must have write permission.')
|
12 |
+
try:
|
13 |
+
api.space_info(repo_id=space_id)
|
14 |
+
except RepositoryNotFoundError:
|
15 |
+
raise ValueError('The Space ID does not exist.')
|
16 |
+
if interval_minutes <= 0:
|
17 |
+
raise ValueError('The interval must be positive.')
|
18 |
+
|
19 |
+
self.scheduler = BackgroundScheduler()
|
20 |
+
self.scheduler.add_job(func=lambda: api.restart_space(space_id),
|
21 |
+
trigger='interval',
|
22 |
+
seconds=60 * interval_minutes)
|
23 |
+
|
24 |
+
def start(self):
|
25 |
+
self.scheduler.start()
|
settings.py
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
|
3 |
+
HF_TOKEN = os.getenv('HUGGING_FACE_HUB_TOKEN')
|
4 |
+
SPACE_ID = os.getenv('SPACE_ID')
|
5 |
+
INTERVAL_MINUTES = int(os.getenv('INTERVAL_MINUTES', '30'))
|