ziyuzhou02 commited on
Commit
df09e67
·
verified ·
1 Parent(s): b0c146e

Format last eval timestamp as UTC+8

Browse files
Files changed (1) hide show
  1. app.py +82 -155
app.py CHANGED
@@ -3,7 +3,6 @@ import os
3
  from pathlib import Path
4
 
5
  import gradio as gr
6
- import pandas as pd
7
 
8
  from src.about import (
9
  CITATION_BUTTON_LABEL,
@@ -12,196 +11,116 @@ from src.about import (
12
  LLM_BENCHMARKS_TEXT,
13
  TITLE,
14
  )
15
- from src.benchmark_config import BENCHMARK_INTERVAL_SECONDS, EVAL_WATCH_POLL_SECONDS, LIVE_EVAL_ENABLED
16
- from src.display.css_html_js import (
17
- build_custom_css,
18
- eval_countdown_head_js,
19
- eval_countdown_markup,
20
- )
21
- from src.eval_scheduler import get_scheduler, start_live_eval_scheduler
22
  from src.utils import (
23
  get_grouped_dfs,
 
24
  prepare_ranks_table,
25
  prepare_values_table,
26
  )
27
 
28
  RESULTS_PATH = os.getenv("TSFM_RESULTS_PATH", "results")
29
-
30
- start_live_eval_scheduler()
31
-
32
- _last_seen_eval_token: str | None = None
33
 
34
 
35
  def load_online_status(results_path: str = RESULTS_PATH) -> dict:
36
  status_path = Path(results_path) / "online_status.json"
37
  if not status_path.exists():
38
- return {"status": "unknown", "message": "No online_status.json yet."}
39
  return json.loads(status_path.read_text())
40
 
41
 
42
- def build_countdown_html() -> str:
43
- scheduler = get_scheduler()
44
- running = scheduler.is_running()
45
- return eval_countdown_markup(
46
- next_eval_at_unix=scheduler.next_eval_at_unix(),
47
- interval_seconds=BENCHMARK_INTERVAL_SECONDS,
48
- running=running,
 
 
49
  )
50
 
51
 
52
- def build_status_line(status: dict) -> str:
53
- eval_state = get_scheduler().snapshot()
54
- finished = status.get("finished_at", "n/a")
55
- eval_status = eval_state.get("status", "unknown")
56
- eval_message = eval_state.get("message", "")
57
-
58
- if eval_status == "running":
59
- live_hint = "**Evaluating now** — collecting TS-Bench data and calling TSFM.ai…"
60
- elif not LIVE_EVAL_ENABLED:
61
- if eval_status in {"ok", "partial"}:
62
- live_hint = (
63
- f"**Leaderboard** · Last eval `{finished}` · "
64
- f"results updated every **{BENCHMARK_INTERVAL_SECONDS // 60} min** from local runner"
65
- )
66
- else:
67
- live_hint = "**Leaderboard** · waiting for first results push from local runner"
68
- elif eval_status == "disabled":
69
- live_hint = f"**Live eval off** — {eval_message}"
70
- else:
71
- live_hint = (
72
- f"**Live eval** · Last run `{finished}` · "
73
- f"collect + eval every **{BENCHMARK_INTERVAL_SECONDS // 60} min** · "
74
- f"leaderboard refreshes when each run completes"
75
- )
76
-
77
- return live_hint
78
-
79
-
80
  def refresh_leaderboard() -> tuple:
81
  grouped = get_grouped_dfs(RESULTS_PATH)
82
  status = load_online_status(RESULTS_PATH)
 
83
 
84
  outputs: list = [
85
  prepare_values_table(grouped["overall_values"]),
86
  prepare_ranks_table(grouped["overall_ranks"]),
87
- build_status_line(status),
88
- build_countdown_html(),
 
89
  ]
90
- domains: list[str] = grouped["domains"]
91
- for domain in domains:
92
  values_df = grouped["domain_values"].get(domain)
93
- ranks_df = grouped["domain_ranks"].get(domain)
94
  if values_df is not None and not values_df.empty:
95
  outputs.append(prepare_values_table(values_df))
96
  else:
97
  outputs.append(prepare_values_table(grouped["overall_values"].iloc[:0]))
98
- if ranks_df is not None and not ranks_df.empty:
99
- outputs.append(prepare_ranks_table(ranks_df))
100
- else:
101
- outputs.append(prepare_ranks_table(grouped["overall_ranks"].iloc[:0]))
102
  return tuple(outputs)
103
 
104
 
105
- def load_initial_outputs() -> tuple[list[str], tuple]:
106
- try:
107
- grouped = get_grouped_dfs(RESULTS_PATH)
108
- status = load_online_status(RESULTS_PATH)
109
- domains = grouped["domains"]
110
- outputs: list = [
111
- prepare_values_table(grouped["overall_values"]),
112
- prepare_ranks_table(grouped["overall_ranks"]),
113
- build_status_line(status),
114
- build_countdown_html(),
115
- ]
116
- for domain in domains:
117
- values_df = grouped["domain_values"].get(domain)
118
- ranks_df = grouped["domain_ranks"].get(domain)
119
- if values_df is not None and not values_df.empty:
120
- outputs.append(prepare_values_table(values_df))
121
- else:
122
- outputs.append(prepare_values_table(grouped["overall_values"].iloc[:0]))
123
- if ranks_df is not None and not ranks_df.empty:
124
- outputs.append(prepare_ranks_table(ranks_df))
125
- else:
126
- outputs.append(prepare_ranks_table(grouped["overall_ranks"].iloc[:0]))
127
- return domains, tuple(outputs)
128
- except FileNotFoundError:
129
- empty = pd.DataFrame(columns=["Model"])
130
- placeholder = (
131
- "**Leaderboard** · no `results/` data yet — "
132
- "run `python scripts/run_online_daemon.py --once` locally"
133
- )
134
- return [], (
135
- empty,
136
- empty,
137
- placeholder,
138
- eval_countdown_markup(0, BENCHMARK_INTERVAL_SECONDS, running=False),
139
- )
140
-
141
 
142
- DOMAINS, INITIAL_OUTPUTS = load_initial_outputs()
143
 
 
 
 
144
 
145
- def maybe_refresh_after_eval() -> tuple:
146
- """Refresh leaderboard only when eval state changes (start or finish)."""
147
- global _last_seen_eval_token
 
 
148
 
149
- eval_state = get_scheduler().snapshot()
150
- token = "|".join(
151
- [
152
- str(eval_state.get("status", "")),
153
- str(eval_state.get("updated_at", "")),
154
- str(eval_state.get("finished_at", "")),
155
- str(eval_state.get("next_eval_at_unix", "")),
156
- ]
 
 
 
 
157
  )
158
- if token == _last_seen_eval_token:
159
- raise gr.Skip()
160
- _last_seen_eval_token = token
161
- return refresh_leaderboard()
162
 
 
 
163
 
164
- demo = gr.Blocks(
165
- css=build_custom_css(),
166
- head=eval_countdown_head_js(),
167
- )
168
- with demo:
169
- gr.Markdown(f"# {TITLE.strip()}")
170
- gr.Markdown(INTRODUCTION_TEXT, elem_classes="markdown-text")
171
- status = gr.Markdown(INITIAL_OUTPUTS[2], elem_classes="markdown-text")
172
- countdown = gr.HTML(INITIAL_OUTPUTS[3])
 
 
 
 
173
 
174
- domain_tables: list[tuple[gr.Dataframe, gr.Dataframe]] = []
175
- with gr.Tabs():
176
- with gr.Tab("Overall"):
177
- overall_values_df = gr.Dataframe(
178
- value=INITIAL_OUTPUTS[0],
 
179
  interactive=False,
180
  wrap=True,
181
- label="Metric values (geometric mean across datasets, lower is better)",
182
  )
183
- overall_ranks_df = gr.Dataframe(
184
- value=INITIAL_OUTPUTS[1],
185
- interactive=False,
186
- wrap=True,
187
- label="Ranks (average per-dataset rank, lower is better)",
188
- )
189
-
190
- for i, domain in enumerate(DOMAINS):
191
- with gr.Tab(domain):
192
- values_df = gr.Dataframe(
193
- value=INITIAL_OUTPUTS[4 + i * 2],
194
- interactive=False,
195
- wrap=True,
196
- label="Metric values (lower is better)",
197
- )
198
- ranks_df = gr.Dataframe(
199
- value=INITIAL_OUTPUTS[5 + i * 2],
200
- interactive=False,
201
- wrap=True,
202
- label="Ranks (lower is better)",
203
- )
204
- domain_tables.append((values_df, ranks_df))
205
 
206
  with gr.Accordion("About", open=False):
207
  gr.Markdown(LLM_BENCHMARKS_TEXT, elem_classes="markdown-text")
@@ -214,14 +133,22 @@ with demo:
214
  show_copy_button=True,
215
  )
216
 
217
- refresh_outputs = [overall_values_df, overall_ranks_df, status, countdown]
218
- for values_df, ranks_df in domain_tables:
219
- refresh_outputs.extend([values_df, ranks_df])
 
 
 
 
 
220
 
221
- # UI-only: data updates on container rebuild after local push — no load hook or timer.
222
- if LIVE_EVAL_ENABLED:
223
- gr.Timer(value=EVAL_WATCH_POLL_SECONDS).tick(
224
- maybe_refresh_after_eval, inputs=[], outputs=refresh_outputs
225
- )
 
 
 
226
 
227
- demo.launch()
 
3
  from pathlib import Path
4
 
5
  import gradio as gr
 
6
 
7
  from src.about import (
8
  CITATION_BUTTON_LABEL,
 
11
  LLM_BENCHMARKS_TEXT,
12
  TITLE,
13
  )
14
+ from src.display.css_html_js import custom_css
15
+ from src.display.formatting import format_timestamp_utc8
 
 
 
 
 
16
  from src.utils import (
17
  get_grouped_dfs,
18
+ load_baseline_name,
19
  prepare_ranks_table,
20
  prepare_values_table,
21
  )
22
 
23
  RESULTS_PATH = os.getenv("TSFM_RESULTS_PATH", "results")
24
+ REFRESH_SECONDS = int(os.getenv("TSFM_REFRESH_SECONDS", "300"))
 
 
 
25
 
26
 
27
  def load_online_status(results_path: str = RESULTS_PATH) -> dict:
28
  status_path = Path(results_path) / "online_status.json"
29
  if not status_path.exists():
30
+ return {"status": "unknown", "message": "No online_status.json yet. Run run_online_eval.py."}
31
  return json.loads(status_path.read_text())
32
 
33
 
34
+ def build_status_line(status: dict, baseline: str) -> str:
35
+ finished = format_timestamp_utc8(status.get("finished_at", "n/a"))
36
+ data_source = status.get("data_source", "n/a")
37
+ ts_bench_root = status.get("ts_bench_root", "")
38
+ root_hint = f" · TS-Bench: `{ts_bench_root}`" if ts_bench_root else ""
39
+ return (
40
+ f"**Live benchmark** · Data: `{data_source}`{root_hint} · "
41
+ f"Baseline: `{baseline}` · Last eval: **{finished}** · "
42
+ f"Auto-refresh every {REFRESH_SECONDS}s"
43
  )
44
 
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  def refresh_leaderboard() -> tuple:
47
  grouped = get_grouped_dfs(RESULTS_PATH)
48
  status = load_online_status(RESULTS_PATH)
49
+ baseline = load_baseline_name(RESULTS_PATH)
50
 
51
  outputs: list = [
52
  prepare_values_table(grouped["overall_values"]),
53
  prepare_ranks_table(grouped["overall_ranks"]),
54
+ build_status_line(status, baseline),
55
+ REFRESH_SECONDS,
56
+ f"Next auto-refresh in **{REFRESH_SECONDS}s**",
57
  ]
58
+ for domain in DOMAINS:
 
59
  values_df = grouped["domain_values"].get(domain)
 
60
  if values_df is not None and not values_df.empty:
61
  outputs.append(prepare_values_table(values_df))
62
  else:
63
  outputs.append(prepare_values_table(grouped["overall_values"].iloc[:0]))
 
 
 
 
64
  return tuple(outputs)
65
 
66
 
67
+ def tick_refresh_progress(remaining: int) -> tuple[int, str]:
68
+ next_remaining = max(int(remaining) - 1, 0)
69
+ return next_remaining, f"Next auto-refresh in **{next_remaining}s**"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
 
71
 
72
+ grouped_initial = get_grouped_dfs(RESULTS_PATH)
73
+ DOMAINS: list[str] = grouped_initial["domains"]
74
+ INITIAL_OUTPUTS = refresh_leaderboard()
75
 
76
+ demo = gr.Blocks(css=custom_css)
77
+ with demo:
78
+ gr.HTML(TITLE)
79
+ gr.Markdown(INTRODUCTION_TEXT, elem_classes="markdown-text")
80
+ status = gr.Markdown(INITIAL_OUTPUTS[2], elem_classes="markdown-text")
81
 
82
+ refresh_progress = gr.Slider(
83
+ minimum=0,
84
+ maximum=REFRESH_SECONDS,
85
+ value=REFRESH_SECONDS,
86
+ step=1,
87
+ label="Auto-refresh countdown",
88
+ interactive=False,
89
+ elem_id="refresh-progress",
90
+ )
91
+ refresh_countdown = gr.Markdown(
92
+ f"Next auto-refresh in **{REFRESH_SECONDS}s**",
93
+ elem_classes="markdown-text",
94
  )
 
 
 
 
95
 
96
+ with gr.Row():
97
+ refresh_btn = gr.Button("Refresh now", variant="secondary")
98
 
99
+ gr.Markdown("### Overall", elem_classes="domain-heading")
100
+ overall_values_df = gr.Dataframe(
101
+ value=INITIAL_OUTPUTS[0],
102
+ interactive=False,
103
+ wrap=True,
104
+ label="Metric values (normalized vs baseline, lower is better)",
105
+ )
106
+ overall_ranks_df = gr.Dataframe(
107
+ value=INITIAL_OUTPUTS[1],
108
+ interactive=False,
109
+ wrap=True,
110
+ label="Ranks (average per-dataset rank, lower is better)",
111
+ )
112
 
113
+ domain_value_dfs: list[gr.Dataframe] = []
114
+ for i, domain in enumerate(DOMAINS):
115
+ gr.Markdown(f"### {domain}", elem_classes="domain-heading")
116
+ domain_value_dfs.append(
117
+ gr.Dataframe(
118
+ value=INITIAL_OUTPUTS[5 + i],
119
  interactive=False,
120
  wrap=True,
121
+ label=f"{domain} · metric values (lower is better)",
122
  )
123
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
  with gr.Accordion("About", open=False):
126
  gr.Markdown(LLM_BENCHMARKS_TEXT, elem_classes="markdown-text")
 
133
  show_copy_button=True,
134
  )
135
 
136
+ refresh_outputs = [
137
+ overall_values_df,
138
+ overall_ranks_df,
139
+ status,
140
+ refresh_progress,
141
+ refresh_countdown,
142
+ *domain_value_dfs,
143
+ ]
144
 
145
+ refresh_btn.click(refresh_leaderboard, inputs=[], outputs=refresh_outputs)
146
+ demo.load(refresh_leaderboard, inputs=[], outputs=refresh_outputs)
147
+ gr.Timer(value=REFRESH_SECONDS).tick(refresh_leaderboard, inputs=[], outputs=refresh_outputs)
148
+ gr.Timer(value=1).tick(
149
+ tick_refresh_progress,
150
+ inputs=[refresh_progress],
151
+ outputs=[refresh_progress, refresh_countdown],
152
+ )
153
 
154
+ demo.queue(default_concurrency_limit=20).launch()