|
5 | 5 | interface Peer { |
6 | 6 | peer_id?: string; |
7 | 7 | hostname?: string; |
8 | | - version?: string; |
9 | 8 | status?: string; |
10 | 9 | device?: string; |
11 | 10 | launched_by?: string; |
12 | 11 | slurm_job_id?: string; |
13 | 12 | started_at?: string; |
| 13 | + expires_at?: string; |
| 14 | + otela_version?: string; |
14 | 15 | framework?: string; |
15 | 16 | worker_group_id?: string; |
16 | 17 | labels?: Record<string, string>; |
|
55 | 56 | $: firstHead = entry.data.replicas[0]?.head ?? {}; |
56 | 57 | $: framework = firstHead.framework || ""; |
57 | 58 |
|
| 59 | + // "2026-05-17T07:00:00Z" → "2026-05-17T07:00:00Z (11 hours ago)". |
| 60 | + // Returns the iso untouched if it doesn't parse — keeps the row useful even |
| 61 | + // if OpenTela emits something we don't understand. |
| 62 | + function withRelative(iso: string | undefined): string { |
| 63 | + if (!iso) return ""; |
| 64 | + const t = new Date(iso).getTime(); |
| 65 | + if (isNaN(t)) return iso; |
| 66 | + const diffMs = t - Date.now(); |
| 67 | + const abs = Math.abs(diffMs); |
| 68 | + const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); |
| 69 | + let rel: string; |
| 70 | + if (abs < 60_000) rel = rtf.format(Math.round(diffMs / 1000), "second"); |
| 71 | + else if (abs < 3_600_000) rel = rtf.format(Math.round(diffMs / 60_000), "minute"); |
| 72 | + else if (abs < 86_400_000) rel = rtf.format(Math.round(diffMs / 3_600_000), "hour"); |
| 73 | + else rel = rtf.format(Math.round(diffMs / 86_400_000), "day"); |
| 74 | + return `${iso} (${rel})`; |
| 75 | + } |
| 76 | +
|
58 | 77 | // Multi-node topology string: "2 nodes × 4xGH200" for an 8-GPU TP replica. |
59 | 78 | function topologyString(r: Replica): string { |
60 | 79 | const dev = r.devices[0] || "?"; |
|
187 | 206 | <!-- Per-replica detail blocks --> |
188 | 207 | {#each entry.data.replicas as replica, idx (replica.worker_group_id)} |
189 | 208 | {@const head = replica.head} |
190 | | - {@const hasLabels = !!(head.launched_by || head.slurm_job_id || head.started_at || head.framework || head.version || head.status)} |
| 209 | + {@const hasLabels = !!(head.launched_by || head.slurm_job_id || head.started_at || head.expires_at || head.framework || head.otela_version || head.status)} |
191 | 210 | {@const peerLine = (p) => { |
192 | 211 | const hn = p.hostname; |
193 | 212 | const pid = p.peer_id; |
|
198 | 217 | ["model", entry.data.title], |
199 | 218 | ["launched_by", head.launched_by], |
200 | 219 | ["slurm_job_id", head.slurm_job_id], |
201 | | - ["started_at", head.started_at], |
| 220 | + ["started_at", withRelative(head.started_at)], |
| 221 | + ["expires_at", withRelative(head.expires_at)], |
202 | 222 | ["framework", head.framework], |
203 | | - ["version", head.version], |
| 223 | + ["otela_version", head.otela_version], |
204 | 224 | // worker_group_id is omitted when it's a synthesised legacy-N fallback — |
205 | 225 | // it's just noise in that case. |
206 | 226 | ["worker_group_id", replica.worker_group_id.startsWith("legacy-") ? "" : replica.worker_group_id], |
|
227 | 247 |
|
228 | 248 | {#if !hasLabels} |
229 | 249 | <p class="text-xs text-amber-700 dark:text-amber-400 mt-2"> |
230 | | - Launch metadata (launched_by, slurm_job_id, framework, started_at…) requires OpenTela v0.0.6+ on the serving node. |
| 250 | + Launch metadata (launched_by, slurm_job_id, framework, started_at, expires_at…) requires OpenTela v0.0.6+ on the serving node. |
231 | 251 | </p> |
232 | 252 | {/if} |
233 | 253 |
|
234 | 254 | <!-- Topology / extra labels block: framework_args, etc. --> |
235 | 255 | {#if head.labels && Object.keys(head.labels).length > 0} |
236 | 256 | {@const extra = Object.entries(head.labels).filter(([k]) => |
237 | | - !["launched_by","slurm_job_id","worker_group_id","framework","started_at","slurm_partition","served_model_name"].includes(k) |
| 257 | + !["launched_by","slurm_job_id","worker_group_id","framework","started_at","expires_at","slurm_partition","served_model_name"].includes(k) |
238 | 258 | )} |
239 | 259 | {#if extra.length > 0} |
240 | 260 | <div class="text-xs text-slate-500 dark:text-slate-400 mt-2 mb-1">Extra labels</div> |
|
0 commit comments