Files
llmgw/internal/web/templates/about.html
T
Ilker ManapandClaude Opus 5.5 fea5774fe5 The install guide shows every screen of a real install
/install takes a reader from three bare Ubuntu machines to a first answer:
what each machine needs and the commands that check it, the release fetched
and its checksum checked, the eight setup screens, the system's state and
the console, and a first request with a key from the account page. Every
screen is from an install of v0.14.9 on three fresh VMs, recorded with tmux
and kept as terminal frames: the page shows them as text, and plays a
session back when asked, fetching it compressed from /install/cast/. The
administrator's password and the API key were masked before the recording
was kept, and a test fails if either shape reaches the page.

/about gains what llmgw now does beyond routing, and its install steps are
today's: the setup screens and node add, not onboard-node.sh and a model
file, with a link into the guide.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 01:35:08 +02:00

340 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "about"}}{{if not .Site}}<!doctype html>
<html lang="{{lang}}"><head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="{{t "about.meta.description"}}">
{{end}}<title>llmgw</title>
{{template "sitestyle" .}}
{{if not .Site}}</head><body>{{end}}
<div class="wrap">
<header class="bar">
<a class="mark" href="#top" aria-label="{{t "about.home_label"}}">
<svg width="26" height="26" viewBox="0 0 26 26" aria-hidden="true">
<rect x="1" y="1" width="24" height="24" rx="6" fill="none" stroke="currentColor" stroke-width="2"/>
<path d="M7 9h12M7 13h12M7 17h12" stroke="var(--fiber)" stroke-width="2" stroke-linecap="round"/>
</svg>
llmgw
</a>
<nav aria-label="{{t "about.nav.label"}}">
<a href="#what">{{t "about.nav.what"}}</a>
<a href="#fabrics">{{t "about.nav.fabrics"}}</a>
<a href="#install">{{t "about.nav.install"}}</a>
<a href="#proxy">{{t "about.nav.proxy"}}</a>
<a href="#download">{{t "about.nav.download"}}</a>
<a href="https://gitea.mynodes.xyz/ilker/llmgw">{{t "about.nav.source"}}</a>
{{if not .Site}}<a href="/">{{t "about.nav.gateway"}}</a>{{end}}
</nav>
</header>
<div class="hero" id="top">
<div>
<p class="eyebrow">{{t "about.hero.eyebrow" .Version}}</p>
<h1 style="margin-top:14px">{{th "about.hero.title"}}</h1>
<p class="lede">{{t "about.hero.lede"}}</p>
<div class="cta">
<a class="btn" href="#download">{{t "about.hero.download"}}{{with .Release}} {{.}}{{end}}</a>
<a class="btn ghost" href="{{.InstallURL}}">{{t "about.hero.guide"}}</a>
</div>
<div class="oneline" aria-label="{{t "about.hero.oneline_label"}}">
<code><span class="p">$ </span>sudo llmgw server init --admin you@example.com</code>
<button class="copy" style="position:static" data-copy="sudo llmgw server init --admin you@example.com" type="button">{{t "about.copy"}}</button>
</div>
</div>
<figure class="schem">
<svg viewBox="0 0 440 250" role="img" aria-labelledby="schem-t">
<title id="schem-t">{{t "about.schem.title"}}</title>
<!-- clients -->
<g>
<rect class="s-box" x="4" y="58" width="92" height="30" rx="6"/>
<text class="s-label" x="50" y="77" text-anchor="middle" translate="no">openai sdk</text>
<rect class="s-box" x="4" y="110" width="92" height="30" rx="6"/>
<text class="s-label" x="50" y="129" text-anchor="middle">{{t "about.schem.editor"}}</text>
<rect class="s-box" x="4" y="162" width="92" height="30" rx="6"/>
<text class="s-label" x="50" y="181" text-anchor="middle" translate="no">curl</text>
</g>
<path id="c1" class="s-link" d="M96 73 C130 73 130 118 160 118"/>
<path id="c2" class="s-link" d="M96 125 H160"/>
<path id="c3" class="s-link" d="M96 177 C130 177 130 132 160 132"/>
<!-- gateway -->
<rect class="s-gw" x="160" y="96" width="84" height="58" rx="8"/>
<text class="s-strong" x="202" y="122" text-anchor="middle">llmgw</text>
<text class="s-label" x="202" y="139" text-anchor="middle">:8080</text>
<path id="fa" class="s-link" d="M244 114 C272 114 272 62 300 62"/>
<path id="fb" class="s-link" d="M244 136 C272 136 272 188 300 188"/>
<!-- fabric a -->
<rect class="s-fab" x="300" y="14" width="136" height="98" rx="10"/>
<text class="s-label" x="312" y="32" translate="no">nvl72 · gb200</text>
<g transform="translate(312 42)">
<rect class="s-gpu-a" width="22" height="22" rx="3"/><rect class="s-gpu-a" x="28" width="22" height="22" rx="3"/>
<rect class="s-gpu-a" x="56" width="22" height="22" rx="3"/><rect class="s-gpu-a" x="84" width="22" height="22" rx="3"/>
<rect class="s-gpu-a" y="28" width="22" height="22" rx="3"/><rect class="s-gpu-a" x="28" y="28" width="22" height="22" rx="3"/>
<rect class="s-gpu-idle" x="56" y="28" width="22" height="22" rx="3"/><rect class="s-gpu-idle" x="84" y="28" width="22" height="22" rx="3"/>
</g>
<!-- fabric b -->
<rect class="s-fab" x="300" y="138" width="136" height="98" rx="10"/>
<text class="s-label" x="312" y="156" translate="no">ib-b · h100-80g</text>
<g transform="translate(312 166)">
<rect class="s-gpu-b" width="22" height="22" rx="3"/><rect class="s-gpu-b" x="28" width="22" height="22" rx="3"/>
<rect class="s-gpu-idle" x="56" width="22" height="22" rx="3"/><rect class="s-gpu-idle" x="84" width="22" height="22" rx="3"/>
<rect class="s-gpu-idle" y="28" width="22" height="22" rx="3"/><rect class="s-gpu-idle" x="28" y="28" width="22" height="22" rx="3"/>
<rect class="s-gpu-idle" x="56" y="28" width="22" height="22" rx="3"/><rect class="s-gpu-idle" x="84" y="28" width="22" height="22" rx="3"/>
</g>
<!-- requests in flight -->
<circle class="pulse" r="4"><animateMotion dur="2.4s" repeatCount="indefinite"><mpath href="#c1"/></animateMotion></circle>
<circle class="pulse" r="4"><animateMotion dur="2.4s" begin="0.8s" repeatCount="indefinite"><mpath href="#c2"/></animateMotion></circle>
<circle class="pulse" r="4"><animateMotion dur="2.4s" begin="1.6s" repeatCount="indefinite"><mpath href="#c3"/></animateMotion></circle>
<circle class="pulse" r="4"><animateMotion dur="2.4s" begin="1.2s" repeatCount="indefinite"><mpath href="#fa"/></animateMotion></circle>
<circle class="pulse b" r="4"><animateMotion dur="2.4s" begin="2s" repeatCount="indefinite"><mpath href="#fb"/></animateMotion></circle>
</svg>
<figcaption>{{t "about.schem.caption"}}</figcaption>
</figure>
</div>
<section id="what">
<div class="sec-head">
<div>
<h2>{{t "about.what.title"}}</h2>
<p class="intro">{{t "about.what.intro"}}</p>
</div>
<div class="facts">
<div class="fact"><h3>{{t "about.what.api.title"}}</h3>
<p>{{th "about.what.api.text"}}</p></div>
<div class="fact"><h3>{{t "about.what.placement.title"}}</h3>
<p>{{t "about.what.placement.text"}}</p></div>
<div class="fact"><h3>{{t "about.what.ondemand.title"}}</h3>
<p>{{t "about.what.ondemand.text"}}</p></div>
<div class="fact"><h3>{{t "about.what.keys.title"}}</h3>
<p>{{t "about.what.keys.text"}}</p></div>
<div class="fact"><h3>{{t "about.what.editors.title"}}</h3>
<p>{{t "about.what.editors.text"}}</p></div>
<div class="fact"><h3>{{t "about.what.chat.title"}}</h3>
<p>{{t "about.what.chat.text"}}</p></div>
<div class="fact"><h3>{{t "about.what.mirror.title"}}</h3>
<p>{{t "about.what.mirror.text"}}</p></div>
<div class="fact"><h3>{{t "about.what.engines.title"}}</h3>
<p>{{t "about.what.engines.text"}}</p></div>
<div class="fact"><h3>{{t "about.what.upgrades.title"}}</h3>
<p>{{t "about.what.upgrades.text"}}</p></div>
<div class="fact"><h3>{{t "about.what.dashboard.title"}}</h3>
<p>{{th "about.what.dashboard.text"}}</p></div>
<div class="fact"><h3>{{t "about.what.knowledge.title"}}</h3>
<p>{{th "about.what.knowledge.text"}}</p></div>
<div class="fact"><h3>{{t "about.what.guard.title"}}</h3>
<p>{{t "about.what.guard.text"}}</p></div>
<div class="fact"><h3>{{t "about.what.signin.title"}}</h3>
<p>{{t "about.what.signin.text"}}</p></div>
<div class="fact"><h3>{{t "about.what.ha.title"}}</h3>
<p>{{t "about.what.ha.text"}}</p></div>
<div class="fact"><h3>{{t "about.what.media.title"}}</h3>
<p>{{t "about.what.media.text"}}</p></div>
</div>
</div>
</section>
<section id="fabrics">
<div class="sec-head">
<div>
<h2>{{t "about.fabrics.title"}}</h2>
<p class="intro">{{t "about.fabrics.intro"}}</p>
<p class="intro"><a href="https://gitea.mynodes.xyz/ilker/llmgw/src/branch/main/docs/fabrics.md">{{t "about.fabrics.guide"}}</a></p>
</div>
<div class="fab-grid">
<div class="order" aria-label="{{t "about.fabrics.order_label"}}">
<div><b>1</b><h3>{{t "about.fabrics.nvlink.title"}}</h3><p>{{th "about.fabrics.nvlink.text"}}</p></div>
<div><b>2</b><h3>{{t "about.fabrics.ib.title"}}</h3><p>{{t "about.fabrics.ib.text"}}</p></div>
<div><b class="x">×</b><h3>{{t "about.fabrics.never.title"}}</h3><p>{{t "about.fabrics.never.text"}}</p></div>
</div>
<div>
<h3>{{t "about.fabrics.doctor"}}</h3>
<div class="term" aria-label="{{t "about.fabrics.doctor_label"}}"><pre>$ sudo llmgw doctor
<span class="fail">FAIL</span> nvlink this machine has NVSwitch but nvidia-fabricmanager is inactive
<span class="dim">→ systemctl enable --now nvidia-fabricmanager</span>
<span class="warn">WARN</span> fabric nvidia_peermem is not loaded: GPUDirect RDMA is off
<span class="dim">→ modprobe nvidia_peermem</span>
<span class="ok">OK</span> gpu 8 GPUs: 8× NVIDIA H100 80GB HBM3
<span class="ok">OK</span> nvlink NVLink groups: [0 1 2 3 4 5 6 7]
<span class="ok">OK</span> fabric mlx5_0/1 ACTIVE at 400 Gb/s (InfiniBand), subnet manager 0x248a07030030f0c0
<span class="ok">OK</span> fabric subnet managers: 0x…f0c0 (master, priority 15), 0x…f1d4 (standby, priority 14)
<span class="ok">OK</span> agent NCCL bootstraps over ens3 (10.1.0.21)</pre></div>
</div>
<ul class="watch">
<li><strong>{{t "about.fabrics.sm.title"}}</strong>{{t "about.fabrics.sm.text"}}</li>
<li><strong>{{t "about.fabrics.map.title"}}</strong>{{th "about.fabrics.map.text"}}</li>
<li><strong>{{t "about.fabrics.racks.title"}}</strong>{{th "about.fabrics.racks.text"}}</li>
<li><strong>{{t "about.fabrics.ports.title"}}</strong>{{t "about.fabrics.ports.text"}}</li>
</ul>
</div>
</div>
</section>
<section id="install">
<div class="sec-head">
<div>
<h2>{{t "about.install.title"}}</h2>
<p class="intro">{{t "about.install.intro"}}</p>
<p class="intro">{{th "about.install.guide" .InstallURL}}</p>
<p class="cta" style="margin-top:18px"><a class="btn" href="{{.InstallURL}}">{{t "about.install.cta"}} →</a></p>
</div>
<ol class="steps">
<li>
<h3>{{t "about.install.binary.title"}}</h3>
<p>{{t "about.install.binary.text"}}</p>
<div class="code"><button class="copy" type="button">{{t "about.copy"}}</button><pre><code>VERSION={{or .Release "vX.Y.Z"}}{{if not .Release}} <span class="c"># from the releases page</span>{{end}}
U=https://gitea.mynodes.xyz/ilker/llmgw/releases/download/$VERSION
curl -fsSLO $U/llmgw-$VERSION-linux-amd64
curl -fsSL $U/checksums.txt | sha256sum -c --ignore-missing
sudo install -m0755 llmgw-$VERSION-linux-amd64 /usr/local/bin/llmgw</code></pre></div>
</li>
<li>
<h3>{{t "about.install.server.title"}}</h3>
<p>{{th "about.install.server.text"}}</p>
<div class="code"><button class="copy" type="button">{{t "about.copy"}}</button><pre><code>sudo llmgw</code></pre></div>
<a href="{{.InstallURL}}#setup" style="text-decoration:none"><div class="term" aria-label="{{t "install.tty_label"}}"><pre translate="no" style="font-size:clamp(4px,.62vw,.5rem);line-height:1.25">{{.Still "setup-checks"}}</pre></div></a>
</li>
<li>
<h3>{{t "about.install.node.title"}}</h3>
<p>{{th "about.install.node.text"}}</p>
<div class="code"><button class="copy" type="button">{{t "about.copy"}}</button><pre><code>sudo llmgw node add ubuntu@10.0.0.21 --fabric ib-a:mlx5_0,mlx5_1</code></pre></div>
<div class="term" aria-label="{{t "about.install.node.output_label"}}"><pre>$ llmgw ctl nodes
NODE STATE GPUS MEM CLASSES FABRICS REPLICAS LAST SEEN
gpu01 <span class="ok">ready</span> 8/8 free 503G h100-80g ib-a 0 2s</pre></div>
</li>
<li>
<h3>{{t "about.install.model.title"}}</h3>
<p>{{th "about.install.model.text"}}</p>
<div class="code"><button class="copy" type="button">{{t "about.copy"}}</button><pre><code>curl https://llm.example.com/v1/chat/completions \
-H "Authorization: Bearer $LLMGW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "coder", "messages": [{"role": "user", "content": "hello"}]}'</code></pre></div>
</li>
</ol>
</div>
</section>
<section id="proxy">
<div class="sec-head">
<div>
<h2>{{t "about.proxy.title"}}</h2>
<p class="intro">{{t "about.proxy.intro"}}</p>
<p class="intro">{{th "about.proxy.loopback"}}</p>
</div>
<div class="tabs">
<input type="radio" name="proxy" id="t-caddy" checked>
<input type="radio" name="proxy" id="t-nginx">
<div class="tablist" role="presentation" translate="no">
<label for="t-caddy">Caddy</label>
<label for="t-nginx">nginx</label>
</div>
<div class="panel p-caddy">
<p class="meta"><span>{{th "about.proxy.caddy"}}</span>
<a href="{{.Download}}/Caddyfile" translate="no">Caddyfile</a></p>
<div class="code"><button class="copy" type="button">{{t "about.copy"}}</button><pre><code>{{.Caddy}}</code></pre></div>
</div>
<div class="panel p-nginx">
<p class="meta"><span>{{th "about.proxy.nginx"}}</span>
<a href="{{.Download}}/nginx.conf" translate="no">nginx.conf</a></p>
<div class="code"><button class="copy" type="button">{{t "about.copy"}}</button><pre><code>{{.Nginx}}</code></pre></div>
</div>
<ul class="rules">
<li><strong>{{t "about.proxy.stream.title"}}</strong>{{t "about.proxy.stream.text"}}</li>
<li><strong>{{t "about.proxy.timeout.title"}}</strong>{{t "about.proxy.timeout.text"}}</li>
<li><strong>{{t "about.proxy.headers.title"}}</strong>{{th "about.proxy.headers.text"}}</li>
<li><strong>{{t "about.proxy.body.title"}}</strong>{{t "about.proxy.body.text"}}</li>
</ul>
</div>
</div>
</section>
<section id="download">
<div class="sec-head">
<div>
<h2>{{t "about.download.title"}}</h2>
<p class="intro">{{th "about.download.intro"}}</p>
</div>
<div>
{{if .Release}}
<div class="ver"><span class="tag">{{.Release}}</span>{{with .Date}}<span class="eyebrow">{{t "about.download.released" .}}</span>{{end}}</div>
<div class="dl-wrap">
<table class="dl">
<thead><tr><th>{{t "about.download.col.for"}}</th><th>{{t "about.download.col.file"}}</th><th>{{t "about.download.col.notes"}}</th></tr></thead>
<tbody>
<tr><td translate="no">Linux x86-64</td><td><a href="{{$.Download}}/llmgw-{{$.Release}}-linux-amd64"><code>llmgw-{{$.Release}}-linux-amd64</code></a></td><td class="note">{{t "about.download.static_any"}}</td></tr>
<tr><td translate="no">Linux ARM64</td><td><a href="{{$.Download}}/llmgw-{{$.Release}}-linux-arm64"><code>llmgw-{{$.Release}}-linux-arm64</code></a></td><td class="note">{{t "about.download.static"}}</td></tr>
<tr><td translate="no">Linux x86-64, NVLink</td><td><a href="{{$.Download}}/llmgw-{{$.Release}}-linux-amd64-nvml"><code>llmgw-{{$.Release}}-linux-amd64-nvml</code></a></td><td class="note">{{t "about.download.nvml"}}</td></tr>
<tr><td>{{t "about.download.mac_arm"}}</td><td><a href="{{$.Download}}/llmgw-{{$.Release}}-darwin-arm64"><code>llmgw-{{$.Release}}-darwin-arm64</code></a></td><td class="note">{{th "about.download.mac_note"}}</td></tr>
<tr><td translate="no">macOS Intel</td><td><a href="{{$.Download}}/llmgw-{{$.Release}}-darwin-amd64"><code>llmgw-{{$.Release}}-darwin-amd64</code></a></td><td class="note"></td></tr>
<tr><td translate="no">Caddy</td><td><a href="{{$.Download}}/Caddyfile"><code>Caddyfile</code></a></td><td class="note">{{t "about.download.proxy_note"}}</td></tr>
<tr><td translate="no">nginx</td><td><a href="{{$.Download}}/nginx.conf"><code>nginx.conf</code></a></td><td class="note">{{t "about.download.proxy_note"}}</td></tr>
<tr><td>{{t "about.download.checksums"}}</td><td><a href="{{$.Download}}/checksums.txt"><code>checksums.txt</code></a></td><td class="note">{{t "about.download.checksums_note"}}</td></tr>
</tbody>
</table>
</div>
{{else}}
<p class="intro" style="margin-top:0">{{th "about.download.dev" .Version}}</p>
{{end}}
</div>
</div>
</section>
<section id="status">
<div class="sec-head">
<div>
<h2>{{t "about.status.title"}}</h2>
<p class="intro">{{t "about.status.intro"}}</p>
</div>
<div class="status">
<div class="proven">
<h3>{{t "about.status.proven"}}</h3>
<ul>
<li>{{t "about.status.proven.single"}}</li>
<li>{{t "about.status.proven.ondemand"}}</li>
<li>{{t "about.status.proven.keys"}}</li>
<li>{{t "about.status.proven.upgrades"}}</li>
<li>{{t "about.status.proven.stores"}}</li>
<li>{{t "about.status.proven.chat"}}</li>
<li>{{t "about.status.proven.ha"}}</li>
<li>{{t "about.status.proven.signin"}}</li>
<li>{{t "about.status.proven.kb"}}</li>
</ul>
</div>
<div class="open">
<h3>{{t "about.status.open"}}</h3>
<ul>
<li>{{th "about.status.open.fabrics"}}</li>
<li>{{t "about.status.open.planes"}}</li>
<li>{{t "about.status.open.weights"}}</li>
</ul>
</div>
</div>
</div>
</section>
<footer>
<span>llmgw {{.Version}}</span>
<span><a href="https://gitea.mynodes.xyz/ilker/llmgw">{{t "about.nav.source"}}</a> ·
<a href="https://gitea.mynodes.xyz/ilker/llmgw/src/branch/main/CHANGELOG.md">{{t "about.footer.changelog"}}</a> ·
<a href="https://gitea.mynodes.xyz/ilker/llmgw/src/branch/main/docs/reference.md">{{t "about.footer.reference"}}</a></span>{{if not .Site}}{{template "langs"}}{{end}}
</footer>
</div>
<script>
var say = { copy: {{t "about.copy"}}, copied: {{t "about.copied"}}, selected: {{t "about.selected"}} };
document.querySelectorAll('.copy').forEach(function (b) {
b.addEventListener('click', function () {
var text = b.dataset.copy || b.parentElement.querySelector('pre').innerText;
var done = function (label) { b.textContent = label; setTimeout(function () { b.textContent = say.copy; }, 1600); };
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(function () { done(say.copied); }, function () { select(); });
} else { select(); }
function select() {
var el = b.dataset.copy ? b.parentElement.querySelector('code') : b.parentElement.querySelector('pre');
var r = document.createRange(); r.selectNodeContents(el);
var s = window.getSelection(); s.removeAllRanges(); s.addRange(r);
done(say.selected);
}
});
});
</script>
{{if not .Site}}</body></html>{{end}}{{end}}