Skip to content

Commit 2d3940d

Browse files
committed
use updated url, add time info in replica-panel for info on node
1 parent 1f65ebf commit 2d3940d

11 files changed

Lines changed: 42 additions & 30 deletions

File tree

Makefile

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,14 @@ test:
2828

2929
_ensure-env:
3030
@if [ ! -f .env ]; then \
31-
echo "DATABASE_URL=$(DATABASE_URL)" > .env; \
32-
echo "wrote default .env (DATABASE_URL -> local docker postgres on :$(PG_PORT))"; \
31+
cp .env.example .env; \
32+
echo "copied .env.example -> .env"; \
3333
fi
3434

3535
_ensure-frontend-env:
3636
@if [ ! -f frontend/.env ]; then \
37-
secret=$$(openssl rand -hex 32); \
38-
{ \
39-
echo "AUTH_SECRET=$$secret"; \
40-
echo "AUTH_TRUST_HOST=true"; \
41-
echo "AUTH0_CLIENT_ID="; \
42-
echo "AUTH0_CLIENT_SECRET="; \
43-
echo "AUTH0_ISSUER="; \
44-
} > frontend/.env; \
45-
echo "wrote default frontend/.env (AUTH_SECRET generated; fill in AUTH0_* to enable login)"; \
37+
cp frontend/.env.example frontend/.env; \
38+
echo "copied frontend/.env.example -> frontend/.env (fill in AUTH0_* to enable login)"; \
4639
fi
4740

4841
db-up:

backend/services/model_service.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def _peer_metadata(node_info: dict) -> dict:
1818
return {
1919
"peer_id": node_info.get("id", ""),
2020
"hostname": node_info.get("hostname", ""),
21-
"version": node_info.get("version", ""),
21+
"otela_version": node_info.get("version", ""),
2222
"status": node_info.get("status", ""),
2323
"labels": labels,
2424
# Convenience pulls — frontends can just read these directly
@@ -28,6 +28,7 @@ def _peer_metadata(node_info: dict) -> dict:
2828
"slurm_job_id": labels.get("slurm_job_id", ""),
2929
"framework": labels.get("framework", ""),
3030
"started_at": labels.get("started_at", ""),
31+
"expires_at": labels.get("expires_at", ""),
3132
}
3233

3334

frontend/src/components/ui/ModelCard.svelte

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
interface Peer {
66
peer_id?: string;
77
hostname?: string;
8-
version?: string;
98
status?: string;
109
device?: string;
1110
launched_by?: string;
1211
slurm_job_id?: string;
1312
started_at?: string;
13+
expires_at?: string;
14+
otela_version?: string;
1415
framework?: string;
1516
worker_group_id?: string;
1617
labels?: Record<string, string>;
@@ -55,6 +56,24 @@
5556
$: firstHead = entry.data.replicas[0]?.head ?? {};
5657
$: framework = firstHead.framework || "";
5758
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+
5877
// Multi-node topology string: "2 nodes × 4xGH200" for an 8-GPU TP replica.
5978
function topologyString(r: Replica): string {
6079
const dev = r.devices[0] || "?";
@@ -187,7 +206,7 @@
187206
<!-- Per-replica detail blocks -->
188207
{#each entry.data.replicas as replica, idx (replica.worker_group_id)}
189208
{@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)}
191210
{@const peerLine = (p) => {
192211
const hn = p.hostname;
193212
const pid = p.peer_id;
@@ -198,9 +217,10 @@
198217
["model", entry.data.title],
199218
["launched_by", head.launched_by],
200219
["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)],
202222
["framework", head.framework],
203-
["version", head.version],
223+
["otela_version", head.otela_version],
204224
// worker_group_id is omitted when it's a synthesised legacy-N fallback —
205225
// it's just noise in that case.
206226
["worker_group_id", replica.worker_group_id.startsWith("legacy-") ? "" : replica.worker_group_id],
@@ -227,14 +247,14 @@
227247

228248
{#if !hasLabels}
229249
<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.
231251
</p>
232252
{/if}
233253

234254
<!-- Topology / extra labels block: framework_args, etc. -->
235255
{#if head.labels && Object.keys(head.labels).length > 0}
236256
{@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)
238258
)}
239259
{#if extra.length > 0}
240260
<div class="text-xs text-slate-500 dark:text-slate-400 mt-2 mb-1">Extra labels</div>

frontend/src/components/ui/ModelList.svelte

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,9 @@
112112
<h2 class="text-3xl font-bold text-slate-900 dark:text-white mb-4">
113113
Available Models
114114
{#if !loading && !error}
115-
<span style="display:inline-flex;align-items:center;justify-content:center;font-size:0.65em;font-weight:bold;line-height:1;padding:0.15em 0.5em;border-radius:4px;background-color:#6366f1;color:#fff;vertical-align:middle;margin-left:0.3em">{modelCount}</span>
115+
<span style="display:inline-flex;align-items:center;justify-content:center;font-size:0.65em;font-weight:bold;line-height:1;min-width:2em;padding:0.15em 0.5em;border-radius:4px;background-color:#6366f1;color:#fff;vertical-align:middle;margin-left:0.3em">{modelCount}</span>
116116
{#if replicaCount !== modelCount}
117-
<span class="ml-2 text-base font-normal text-slate-500 dark:text-slate-400" title="Total replicas (separately-launched instances) across all models">
118-
, Replicas
119-
<span style="display:inline-flex;align-items:center;justify-content:center;font-size:0.75em;font-weight:bold;line-height:1;padding:0.15em 0.5em;border-radius:4px;background-color:#64748b;color:#fff;vertical-align:middle;margin-left:0.25em">{replicaCount}</span>
117+
<span class="ml-2" title="Total replicas (separately-launched instances) across all models"><span style="display:inline-flex;align-items:center;justify-content:center;font-size:0.65em;font-weight:bold;line-height:1;min-width:2em;padding:0.15em 0.5em;border-radius:4px;background-color:red;color:#fff;vertical-align:middle;margin-left:0.3em">{replicaCount}</span>
120118
</span>
121119
{/if}
122120
{/if}

frontend/src/content/articles/03-opentela/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,4 @@ By using OpenTela, SwissAI enables:
5959

6060
## Conclusion
6161

62-
The Swiss AI Initiative's integration of OpenTela represents a significant shift toward a more sovereign and collaborative AI infrastructure. By leveraging OpenTela's decentralized architecture, SwissAI built a platform where traditional HPC clusters can be used as a shared pool where every researchers can benefit from and contribute to. It also effectively converts fragmented, idle GPU capacity into a unified, accessible resource for the research community. You can view the real-time status of the models served by SwissAI on the [Swiss AI Research Platform](https://serving.swissai.cscs.ch/). If you are interested in learning more about how SwissAI uses OpenTela, please feel free to reach out to us!
62+
The Swiss AI Initiative's integration of OpenTela represents a significant shift toward a more sovereign and collaborative AI infrastructure. By leveraging OpenTela's decentralized architecture, SwissAI built a platform where traditional HPC clusters can be used as a shared pool where every researchers can benefit from and contribute to. It also effectively converts fragmented, idle GPU capacity into a unified, accessible resource for the research community. You can view the real-time status of the models served by SwissAI on the [Swiss AI Research Platform](https://serving.swissai.svc.cscs.ch/). If you are interested in learning more about how SwissAI uses OpenTela, please feel free to reach out to us!

frontend/src/content/guides/01-getting-started/model-launch.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ date: "December 22 2025"
66

77
## Recommended: Use model-launch
88

9-
_[**model-launch**](https://github.com/swiss-ai/model-launch) is the recommended tool for getting models on [serving.swissai.cscs.ch](https://serving.swissai.cscs.ch)!_
9+
_[**model-launch**](https://github.com/swiss-ai/model-launch) is the recommended tool for getting models on [serving.swissai.svc.cscs.ch](https://serving.swissai.svc.cscs.ch)!_
1010

1111
It provides a framework-agnostic approach to submitting SLURM jobs for distributed inference using SGLang or vLLM. The tool handles single-node and multi-node deployments, automatically integrates with OCF (Open Compute Framework) for service discovery, and makes your models accessible externally from outside the cluster. It includes ready-to-use examples for popular models like Swiss AI Apertus, DeepSeek-V3, Kimi-K2, and many others, with support for advanced features like multi-worker routing, pre-launch commands, and interactive debugging modes.
1212

frontend/src/layouts/PageLayout.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type Props = {
1010
};
1111
1212
const { title, description } = Astro.props;
13-
const apiUrl = process.env.VITE_API_URL || 'https://api.swissai.cscs.ch';
13+
const apiUrl = import.meta.env.VITE_API_URL || 'https://api.swissai.svc.cscs.ch';
1414
---
1515

1616
<!doctype html>

frontend/src/lib/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
const DEFAULT_API_URL = 'https://api.swissai.cscs.ch';
1+
const DEFAULT_API_URL = 'https://api.swissai.svc.cscs.ch';
22

33
export function getApiUrl(): string {
44
if (typeof window !== 'undefined') {
55
return (window as any).__API_URL__ || DEFAULT_API_URL;
66
}
7-
return process.env.VITE_API_URL || DEFAULT_API_URL;
7+
return import.meta.env.VITE_API_URL || DEFAULT_API_URL;
88
}

frontend/src/pages/api_key.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ if (isDev) {
2828
2929
// API key will be fetched client-side to avoid exposing it in the HTML
3030
let apiKey = "Loading...";
31-
const apiUrl = process.env.VITE_API_URL || 'https://api.swissai.cscs.ch';
31+
const apiUrl = import.meta.env.VITE_API_URL || 'https://api.swissai.svc.cscs.ch';
3232
---
3333

3434
<PageLayout title="API Key" description="Your personal API key for accessing Research Computer services">

frontend/src/pages/index.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const articles = (await getCollection("articles"))
2626
2727
const session = import.meta.env.DEV ? null : await getSession(Astro.request);
2828
const isSessionValid = session && session.user;
29-
const chatAppUrl = process.env.CHAT_APP_URL || "https://chat.swissai.cscs.ch";
29+
const chatAppUrl = process.env.CHAT_APP_URL || "https://chat.swissai.svc.cscs.ch";
3030
3131
const contributors = [
3232
{

0 commit comments

Comments
 (0)