hysts HF staff commited on
Commit
6e67bdd
1 Parent(s): cc8cf29
Files changed (5) hide show
  1. app.py +21 -73
  2. constants.py +47 -0
  3. demo_list.py +63 -30
  4. restart_scheduler.py +25 -0
  5. 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
- api = HfApi()
17
- WHOAMI = api.whoami()['name']
18
- if (SPACE_ID := os.getenv('SPACE_ID')) is not None:
19
- INTERVAL_MIN = int(os.getenv('INTERVAL_MIN', '10'))
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=update_df,
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
- info = data[url]
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
  for tag in ['arxiv', 'github', 'tags']:
75
  if tag not in info:
76
  info[tag] = []
77
 
78
- info['url'] = url
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
- if info['hardware'] == 'cpu-basic':
93
- info['sleep_time'] = 172800
94
- else:
95
- info['sleep_time'] = space_info.runtime['gcTimeout'] or -1
 
 
 
 
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
- self.TO_TIME_STR[row.sleep_time], row.hardware),
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'))