docs: add relay README hero

This commit is contained in:
Ishaan Jaff 2026-07-09 17:53:45 -07:00
parent f824783601
commit 2be8e1679e
No known key found for this signature in database
13 changed files with 441 additions and 1 deletions

View file

@ -37,7 +37,9 @@
</a>
</h4>
<img alt="LiteLLM AI Gateway" src="https://github.com/user-attachments/assets/c5ee0412-6fb5-4fb6-ab5b-bafae4209ca6" />
<p align="center">
<img src="./docs/assets/relay-hero.png" alt="LiteLLM Relay + LiteLLM AI Gateway" width="100%">
</p>
---

354
docs/assets/hero.html Normal file
View file

@ -0,0 +1,354 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>LiteLLM Relay + AI Gateway</title>
<link rel="icon" href="data:,">
<style>
:root{
--blue-50:#f2f8ff; --blue-200:#c3ddf7;
--blue-500:#1e88e5; --blue-600:#1565c0; --blue-700:#0d47a1;
--ink:#0b1f33; --muted:#5b7387; --line:#dce9f5;
}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Helvetica,Arial,sans-serif;
background:#eef3f8;display:flex;justify-content:center;padding:24px}
.canvas{width:1240px;height:790px;background:#fff;padding:40px 50px;position:relative}
.eyebrow{text-align:center;font:600 12px ui-monospace,Menlo,monospace;letter-spacing:.22em;color:var(--blue-500)}
h1{text-align:center;font-size:35px;line-height:1.25;font-weight:600;color:var(--ink);margin-top:14px;letter-spacing:-.01em}
h1 .accent{color:var(--blue-600)}
.sub{text-align:center;max-width:680px;margin:14px auto 0;font-size:15px;line-height:1.6;color:var(--muted)}
.stage{margin-top:36px;display:flex;align-items:center;justify-content:center}
.laptop{width:430px;border:1.5px solid var(--blue-200);border-radius:16px;
background:linear-gradient(180deg,#fafcff,#f2f8ff);padding:18px 16px;position:relative}
.zone-tag{position:absolute;top:-9px;left:18px;background:#fff;padding:0 9px;
font:600 9.5px ui-monospace,monospace;letter-spacing:.18em;color:var(--blue-600)}
.apps{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:6px}
.app{display:flex;align-items:center;gap:9px;background:#fff;border:1px solid var(--line);
border-radius:9px;padding:8px 10px}
.app .slot{flex:0 0 22px}
.app span.nm{font-size:12.5px;color:#2c4257;font-weight:500;white-space:nowrap}
/* logo slot: img if it loads, monogram if it doesn't */
.slot{width:22px;height:22px;display:flex;align-items:center;justify-content:center}
.slot img{width:20px;height:20px;object-fit:contain;display:block}
.mono{width:22px;height:22px;border-radius:6px;display:flex;align-items:center;
justify-content:center;font:700 9.5px system-ui;color:#fff;letter-spacing:-.02em;
overflow:hidden;line-height:1}
.mono.emoji{background:none!important;font-size:16px}
.funnel{display:block;margin:4px auto -4px}
.relay{background:linear-gradient(135deg,var(--blue-600),var(--blue-700));border-radius:12px;
padding:13px 15px;color:#fff;box-shadow:0 10px 26px -10px rgba(13,71,161,.6);
display:flex;align-items:center;gap:12px}
.relay .em{font-size:22px;line-height:1}
.relay .txt{flex:1}
.relay .t1{font-size:15.5px;font-weight:650;letter-spacing:-.01em}
.relay .t2{font-size:10.5px;opacity:.82;margin-top:2px}
.relay .badge{font:600 8.5px ui-monospace,monospace;letter-spacing:.12em;
background:rgba(255,255,255,.16);border:1px solid rgba(255,255,255,.25);
padding:5px 8px;border-radius:5px;white-space:nowrap}
.chokenote{text-align:center;margin-top:10px;font:500 9.5px ui-monospace,monospace;
letter-spacing:.06em;color:var(--blue-600)}
.pipe{width:186px;padding:0 4px}
.pipe-lbl{text-align:center;font:600 9.5px ui-monospace,monospace;letter-spacing:.13em;
color:#9db2c4;margin-bottom:8px;line-height:1.7}
.pipe-lbl b{color:var(--blue-600);font-weight:600}
.gw{width:296px;border:1.5px solid var(--blue-200);border-radius:16px;background:#fff;
box-shadow:0 16px 40px -22px rgba(21,101,192,.35);padding:22px 20px 18px;position:relative}
.gw-head{display:flex;align-items:center;justify-content:center;gap:9px}
.gw-em{font-size:25px;line-height:1}
.gw-word{font-size:22px;font-weight:650;color:var(--ink);letter-spacing:-.02em}
.gw-sub{text-align:center;margin-top:5px;font-size:11.5px;color:var(--blue-600);font-weight:500}
.pillars{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:18px}
.pillar{background:var(--blue-50);border:1px solid #e6f1fb;border-radius:9px;padding:13px 4px 10px;text-align:center}
.pillar svg{width:18px;height:18px;stroke:var(--blue-600);fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.pillar span{display:block;margin-top:8px;font:600 8.5px ui-monospace,monospace;letter-spacing:.13em;color:var(--blue-600)}
.meta{margin-top:15px;padding-top:12px;border-top:1px dashed var(--line);text-align:center;font-size:10.5px;color:var(--muted);line-height:1.6}
.provs{width:196px;padding-left:26px}
.ptag{font:600 9.5px ui-monospace,monospace;letter-spacing:.18em;color:#9db2c4;margin-bottom:12px}
.prov{display:flex;align-items:center;gap:10px;height:38px}
.pchip{width:32px;height:32px;border-radius:8px;border:1px solid var(--line);
display:flex;align-items:center;justify-content:center;flex:0 0 32px;background:#fff}
.pchip .slot{width:18px;height:18px}
.pchip .slot img{width:18px;height:18px}
.pchip .mono{width:20px;height:20px;font-size:8.5px}
.prov span.nm{font-size:12.5px;color:#2c4257;font-weight:500}
.footline{position:absolute;left:0;right:0;bottom:30px;text-align:center;font-size:14.5px;color:#3d566c}
.footline b{color:var(--blue-600);font-weight:600}
/* export button, hidden when printing/capturing */
#export{position:fixed;left:12px;bottom:12px;background:#1565c0;color:#fff;border:0;
font:600 12px system-ui;padding:10px 16px;border-radius:8px;cursor:pointer;
box-shadow:0 6px 18px rgba(21,101,192,.35)}
#export:hover{background:#0d47a1}
#export:disabled{opacity:.6;cursor:default}
@media print{#export,#diag{display:none!important}}
/* diagnostics panel */
#diag{position:fixed;right:12px;bottom:12px;background:#0b1f33;color:#cfe4f7;
font:11px ui-monospace,monospace;padding:12px 14px;border-radius:8px;max-width:330px;
line-height:1.6;box-shadow:0 8px 24px rgba(0,0,0,.3);display:none}
#diag b{color:#fff}
#diag .ok{color:#5fd08a}
#diag .bad{color:#ff8f7a}
</style>
</head>
<body>
<div class="canvas">
<div class="eyebrow">[ ONE PLATFORM ]</div>
<h1><span class="accent">LiteLLM Relay</span> + LiteLLM AI Gateway.<br>Coverage for every AI your company uses.</h1>
<p class="sub">Install LiteLLM Relay on employee machines to detect AI usage and route it to LiteLLM AI Gateway, making Gateway your single pane of glass for AI usage across the company.</p>
<div class="stage">
<div class="laptop">
<div class="zone-tag">ON THE LAPTOP</div>
<div class="apps">
<div class="app"><span class="slot" data-name="Claude Desktop" data-src="./logos/claude.svg" data-mono="A" data-bg="#d97757"></span><span class="nm">Claude Desktop</span></div>
<div class="app"><span class="slot" data-name="ChatGPT (browser)" data-src="./logos/openai.svg" data-mono="◎" data-bg="#0f0f0f"></span><span class="nm">ChatGPT (browser)</span></div>
<div class="app"><span class="slot" data-name="Cursor" data-src="./logos/cursor.svg" data-mono="▶" data-bg="#1c1c1c"></span><span class="nm">Cursor</span></div>
<div class="app"><span class="slot" data-name="Notion AI" data-src="./logos/notion.svg" data-mono="N" data-bg="#0f0f0f"></span><span class="nm">Notion AI</span></div>
<div class="app"><span class="slot" data-name="Codex" data-src="./logos/openai.svg" data-mono="&lt;&gt;" data-bg="#3f3f46"></span><span class="nm">Codex</span></div>
<div class="app"><span class="slot" data-name="OpenClaw" data-mono="🦞" data-bg="#e0532c"></span><span class="nm">OpenClaw</span></div>
</div>
<svg class="funnel" width="398" height="46" viewBox="0 0 398 46">
<defs><linearGradient id="fg" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#4da3ff" stop-opacity=".2"/>
<stop offset="1" stop-color="#1565c0" stop-opacity=".6"/>
</linearGradient></defs>
<g fill="none" stroke="url(#fg)" stroke-width="1.4">
<path d="M20 0 C 20 28, 170 22, 178 46"/>
<path d="M120 0 C 120 28, 186 24, 190 46"/>
<path d="M199 0 L 199 46"/>
<path d="M278 0 C 278 28, 212 24, 208 46"/>
<path d="M378 0 C 378 28, 228 22, 220 46"/>
</g>
</svg>
<div class="relay">
<div class="em">🚅</div>
<div class="txt">
<div class="t1">LiteLLM Relay</div>
<div class="t2">installed on every device · routes AI usage</div>
</div>
<div class="badge">CHOKE&nbsp;POINT</div>
</div>
<div class="chokenote">↑ ONE LOCAL EXIT. NOTHING BYPASSES IT.</div>
</div>
<div class="pipe">
<div class="pipe-lbl">FORWARDS TO<br><b>ONE SET OF RULES</b></div>
<svg width="178" height="62" viewBox="0 0 178 62">
<defs>
<linearGradient id="pg" x1="0" x2="1">
<stop offset="0" stop-color="#0d47a1"/><stop offset="1" stop-color="#4da3ff"/>
</linearGradient>
<marker id="ar" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto">
<path d="M0 0 L7 3 L0 6 z" fill="#4da3ff"/>
</marker>
</defs>
<path d="M2 26 H166" stroke="url(#pg)" stroke-width="8" stroke-linecap="round" opacity=".12"/>
<path d="M2 26 H162" stroke="url(#pg)" stroke-width="1.8" marker-end="url(#ar)"/>
<circle cx="46" cy="26" r="2.6" fill="#1e88e5"/>
<circle cx="86" cy="26" r="2.6" fill="#1e88e5" opacity=".55"/>
<circle cx="126" cy="26" r="2.6" fill="#1e88e5" opacity=".28"/>
<text x="84" y="50" text-anchor="middle" font-family="ui-monospace,monospace" font-size="8.5" fill="#9db2c4" letter-spacing="1">virtual key · tagged</text>
</svg>
</div>
<div class="gw">
<div class="zone-tag">CENTRAL</div>
<div class="gw-head">
<div class="gw-em">🚅</div>
<div class="gw-word">LiteLLM</div>
</div>
<div class="gw-sub">AI Gateway</div>
<div class="pillars">
<div class="pillar">
<svg viewBox="0 0 24 24"><path d="M3 7h6M15 7h6M3 17h6M15 17h6"/><circle cx="12" cy="7" r="2.6"/><circle cx="12" cy="17" r="2.6"/></svg>
<span>ROUTE</span>
</div>
<div class="pillar">
<svg viewBox="0 0 24 24"><path d="M12 3l7 3v5.5c0 4.4-3 8-7 9.5-4-1.5-7-5.1-7-9.5V6z"/><path d="M9.2 12.2l2 2 3.6-4"/></svg>
<span>GOVERN</span>
</div>
<div class="pillar">
<svg viewBox="0 0 24 24"><rect x="4.5" y="10.5" width="15" height="10" rx="2.4"/><path d="M8 10.5V7.6a4 4 0 018 0v2.9"/></svg>
<span>SECURE</span>
</div>
</div>
<div class="meta">Virtual keys · Budgets · Guardrails<br>Audit logs · SSO</div>
</div>
<div class="provs">
<div class="ptag">100+ PROVIDERS</div>
<div class="prov"><div class="pchip"><span class="slot" data-name="Anthropic" data-src="./logos/anthropic.svg" data-mono="A" data-bg="#d97757"></span></div><span class="nm">Anthropic</span></div>
<div class="prov"><div class="pchip"><span class="slot" data-name="OpenAI" data-src="./logos/openai.svg" data-mono="◎" data-bg="#0f0f0f"></span></div><span class="nm">OpenAI</span></div>
<div class="prov"><div class="pchip"><span class="slot" data-name="Google Vertex" data-src="./logos/vertexai.svg" data-mono="G" data-bg="#4285f4"></span></div><span class="nm">Google Vertex</span></div>
<div class="prov"><div class="pchip"><span class="slot" data-name="Azure OpenAI" data-src="./logos/azure.svg" data-mono="Az" data-bg="#0078d4"></span></div><span class="nm">Azure OpenAI</span></div>
<div class="prov"><div class="pchip"><span class="slot" data-name="AWS Bedrock" data-src="./logos/bedrock.svg" data-mono="aws" data-bg="#ff9900"></span></div><span class="nm">AWS Bedrock</span></div>
<div class="prov"><div class="pchip"><span class="slot" data-name="Mistral, +95 more" data-src="./logos/mistral.svg" data-mono="M" data-bg="#fa520f"></span></div><span class="nm">Mistral, +95 more</span></div>
</div>
</div>
<div class="footline">Every AI tool your team loves, now in one place. <b>No more shadow AI.</b></div>
</div>
<div id="diag"></div>
<button id="export">Download PNG (2x)</button>
<script>
// Each slot tries its local vendored logo. If the image is missing,
// it silently swaps to an inline monogram. Nothing ever renders broken.
(function () {
var slots = document.querySelectorAll('.slot');
var report = [];
var pending = 0;
function mono(slot) {
var txt = slot.dataset.mono || '?';
var d = document.createElement('div');
// emoji marks (like the OpenClaw lobster) render bare, not in a colored tile
var isEmoji = /\p{Extended_Pictographic}/u.test(txt);
d.className = isEmoji ? 'mono emoji' : 'mono';
if (!isEmoji) d.style.background = slot.dataset.bg;
d.textContent = txt;
slot.innerHTML = '';
slot.appendChild(d);
}
function done() {
if (--pending > 0) return;
var bad = report.filter(function (r) { return !r.ok; });
if (!bad.length) return;
var el = document.getElementById('diag');
el.style.display = 'block';
el.innerHTML = '<b>Logo slugs that failed</b><br>' +
bad.map(function (r) { return '<span class="bad">✗ ' + r.slug + '</span>'; }).join('<br>') +
'<br><br>Using monogram fallback for these.<br>Check real names at lobehub.com/icons';
}
slots.forEach(function (slot) {
if (!slot.dataset.src) { mono(slot); return; }
var urls = slot.dataset.src.split('|');
var label = slot.dataset.name || 'icon';
pending++;
var i = 0;
function attempt() {
if (i >= urls.length) {
mono(slot);
report.push({ slug: label, ok: false });
done();
return;
}
// fetch as text -> data URI, so <canvas> never taints on export
fetch(urls[i], { mode: 'cors' })
.then(function (r) {
if (!r.ok) throw new Error(r.status);
return r.text();
})
.then(function (svg) {
if (svg.indexOf('<svg') === -1) throw new Error('not svg');
var img = new Image();
img.src = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg)));
slot.innerHTML = '';
slot.appendChild(img);
report.push({ slug: label, ok: true });
done();
})
.catch(function () { i++; attempt(); });
}
attempt();
});
if (pending === 0) done();
})();
// --- PNG export -------------------------------------------------------
// Serializes .canvas into an SVG <foreignObject> and rasterizes at 2x.
// Requires icons to be inlined as data URIs (the loader above does this).
document.getElementById('export').addEventListener('click', function () {
var btn = this;
var node = document.querySelector('.canvas');
var W = node.offsetWidth, H = node.offsetHeight, SCALE = 2;
btn.disabled = true;
btn.textContent = 'Rendering...';
// pull every stylesheet rule into an inline <style>
var css = '';
for (var i = 0; i < document.styleSheets.length; i++) {
try {
var rules = document.styleSheets[i].cssRules;
for (var j = 0; j < rules.length; j++) css += rules[j].cssText + '\n';
} catch (e) { /* cross-origin sheet, skip */ }
}
var clone = node.cloneNode(true);
clone.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
var svg =
'<svg xmlns="http://www.w3.org/2000/svg" width="' + W + '" height="' + H + '">' +
'<foreignObject width="100%" height="100%">' +
'<div xmlns="http://www.w3.org/1999/xhtml">' +
'<style>' + css + '</style>' +
clone.outerHTML +
'</div>' +
'</foreignObject>' +
'</svg>';
var img = new Image();
img.onload = function () {
var c = document.createElement('canvas');
c.width = W * SCALE; c.height = H * SCALE;
var ctx = c.getContext('2d');
ctx.scale(SCALE, SCALE);
ctx.drawImage(img, 0, 0);
try {
var a = document.createElement('a');
a.download = 'relay-hero.png';
a.href = c.toDataURL('image/png');
a.click();
btn.textContent = 'Download PNG (2x)';
} catch (e) {
btn.textContent = 'Canvas tainted - screenshot instead';
}
btn.disabled = false;
};
img.onerror = function () {
btn.textContent = 'Export failed - screenshot instead';
btn.disabled = false;
};
img.src = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg)));
});
// file:// blocks fetch(), so warn early rather than silently show monograms
if (location.protocol === 'file:') {
var w = document.getElementById('diag');
w.style.display = 'block';
w.innerHTML = '<b>Opened via file://</b><br>' +
'Browsers block <span class="bad">fetch()</span> on local files, so local ' +
'logos cannot load and you will see monograms.<br><br>' +
'Serve it instead:<br><span class="ok">python3 -m http.server 8000</span><br>' +
'then open <span class="ok">localhost:8000/hero.html</span>';
}
</script>
</body>
</html>

View file

@ -0,0 +1 @@
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Anthropic</title><path d="M13.827 3.52h3.603L24 20h-3.603l-6.57-16.48zm-7.258 0h3.767L16.906 20h-3.674l-1.343-3.461H5.017l-1.344 3.46H0L6.57 3.522zm4.132 9.959L8.453 7.687 6.205 13.48H10.7z"></path></svg>

After

Width:  |  Height:  |  Size: 368 B

View file

@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Azure</title><path d="M7.242 1.613A1.11 1.11 0 018.295.857h6.977L8.03 22.316a1.11 1.11 0 01-1.052.755h-5.43a1.11 1.11 0 01-1.053-1.466L7.242 1.613z" fill="url(#lobe-icons-azure-0-_R_0_)"></path><path d="M18.397 15.296H7.4a.51.51 0 00-.347.882l7.066 6.595c.206.192.477.298.758.298h6.226l-2.706-7.775z" fill="#0078D4"></path><path d="M15.272.857H7.497L0 23.071h7.775l1.596-4.73 5.068 4.73h6.665l-2.707-7.775h-7.998L15.272.857z" fill="url(#lobe-icons-azure-1-_R_0_)"></path><path d="M17.193 1.613a1.11 1.11 0 00-1.052-.756h-7.81.035c.477 0 .9.304 1.052.756l6.748 19.992a1.11 1.11 0 01-1.052 1.466h-.12 7.895a1.11 1.11 0 001.052-1.466L17.193 1.613z" fill="url(#lobe-icons-azure-2-_R_0_)"></path><defs><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-azure-0-_R_0_" x1="8.247" x2="1.002" y1="1.626" y2="23.03"><stop stop-color="#114A8B"></stop><stop offset="1" stop-color="#0669BC"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-azure-1-_R_0_" x1="14.042" x2="12.324" y1="15.302" y2="15.888"><stop stop-opacity=".3"></stop><stop offset=".071" stop-opacity=".2"></stop><stop offset=".321" stop-opacity=".1"></stop><stop offset=".623" stop-opacity=".05"></stop><stop offset="1" stop-opacity="0"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-azure-2-_R_0_" x1="12.841" x2="20.793" y1="1.626" y2="22.814"><stop stop-color="#3CCBF4"></stop><stop offset="1" stop-color="#2892DF"></stop></linearGradient></defs></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Bedrock</title><defs><linearGradient id="lobe-icons-bedrock-_R_0_" x1="80%" x2="20%" y1="20%" y2="80%"><stop offset="0%" stop-color="#6350FB"></stop><stop offset="50%" stop-color="#3D8FFF"></stop><stop offset="100%" stop-color="#9AD8F8"></stop></linearGradient></defs><path d="M13.05 15.513h3.08c.214 0 .389.177.389.394v1.82a1.704 1.704 0 011.296 1.661c0 .943-.755 1.708-1.685 1.708-.931 0-1.686-.765-1.686-1.708 0-.807.554-1.484 1.297-1.662v-1.425h-2.69v4.663a.395.395 0 01-.188.338l-2.69 1.641a.385.385 0 01-.405-.002l-4.926-3.086a.395.395 0 01-.185-.336V16.3L2.196 14.87A.395.395 0 012 14.555L2 14.528V9.406c0-.14.073-.27.192-.34l2.465-1.462V4.448c0-.129.062-.249.165-.322l.021-.014L9.77 1.058a.385.385 0 01.407 0l2.69 1.675a.395.395 0 01.185.336V7.6h3.856V5.683a1.704 1.704 0 01-1.296-1.662c0-.943.755-1.708 1.685-1.708.931 0 1.685.765 1.685 1.708 0 .807-.553 1.484-1.296 1.662v2.311a.391.391 0 01-.389.394h-4.245v1.806h6.624a1.69 1.69 0 011.64-1.313c.93 0 1.685.764 1.685 1.707 0 .943-.754 1.708-1.685 1.708a1.69 1.69 0 01-1.64-1.314H13.05v1.937h4.953l.915 1.18a1.66 1.66 0 01.84-.227c.931 0 1.685.764 1.685 1.707 0 .943-.754 1.708-1.685 1.708-.93 0-1.685-.765-1.685-1.708 0-.346.102-.668.276-.937l-.724-.935H13.05v1.806zM9.973 1.856L7.93 3.122V6.09h-.778V3.604L5.435 4.669v2.945l2.11 1.36L9.712 7.61V5.334h.778V7.83c0 .136-.07.263-.184.335L7.963 9.638v2.081l1.422 1.009-.446.646-1.406-.998-1.53 1.005-.423-.66 1.605-1.055v-1.99L5.038 8.29l-2.26 1.34v1.676l1.972-1.189.398.677-2.37 1.429V14.3l2.166 1.258 2.27-1.368.397.677-2.176 1.311V19.3l1.876 1.175 2.365-1.426.398.678-2.017 1.216 1.918 1.201 2.298-1.403v-5.78l-4.758 2.893-.4-.675 5.158-3.136V3.289L9.972 1.856zM16.13 18.47a.913.913 0 00-.908.92c0 .507.406.918.908.918a.913.913 0 00.907-.919.913.913 0 00-.907-.92zm3.63-3.81a.913.913 0 00-.908.92c0 .508.406.92.907.92a.913.913 0 00.908-.92.913.913 0 00-.908-.92zm1.555-4.99a.913.913 0 00-.908.92c0 .507.407.918.908.918a.913.913 0 00.907-.919.913.913 0 00-.907-.92zM17.296 3.1a.913.913 0 00-.907.92c0 .508.406.92.907.92a.913.913 0 00.908-.92.913.913 0 00-.908-.92z" fill="url(#lobe-icons-bedrock-_R_0_)" fill-rule="nonzero"></path></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Claude</title><path d="M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z" fill="#D97757" fill-rule="nonzero"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1 @@
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Cursor</title><path d="M22.106 5.68L12.5.135a.998.998 0 00-.998 0L1.893 5.68a.84.84 0 00-.419.726v11.186c0 .3.16.577.42.727l9.607 5.547a.999.999 0 00.998 0l9.608-5.547a.84.84 0 00.42-.727V6.407a.84.84 0 00-.42-.726zm-.603 1.176L12.228 22.92c-.063.108-.228.064-.228-.061V12.34a.59.59 0 00-.295-.51l-9.11-5.26c-.107-.062-.063-.228.062-.228h18.55c.264 0 .428.286.296.514z"></path></svg>

After

Width:  |  Height:  |  Size: 547 B

View file

@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Mistral</title><path d="M3.428 3.4h3.429v3.428H3.428V3.4zm13.714 0h3.43v3.428h-3.43V3.4z" fill="gold"></path><path d="M3.428 6.828h6.857v3.429H3.429V6.828zm10.286 0h6.857v3.429h-6.857V6.828z" fill="#FFAF00"></path><path d="M3.428 10.258h17.144v3.428H3.428v-3.428z" fill="#FF8205"></path><path d="M3.428 13.686h3.429v3.428H3.428v-3.428zm6.858 0h3.429v3.428h-3.429v-3.428zm6.856 0h3.43v3.428h-3.43v-3.428z" fill="#FA500F"></path><path d="M0 17.114h10.286v3.429H0v-3.429zm13.714 0H24v3.429H13.714v-3.429z" fill="#E10500"></path></svg>

After

Width:  |  Height:  |  Size: 655 B

View file

@ -0,0 +1 @@
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Notion</title><path clip-rule="evenodd" d="M15.257.055l-13.31.98C.874 1.128.5 1.83.5 2.667v14.559c0 .654.233 1.213.794 1.96l3.129 4.06c.513.653.98.794 1.962.745l15.457-.932c1.307-.093 1.681-.7 1.681-1.727V4.954c0-.53-.21-.684-.829-1.135l-.106-.078L18.34.755c-1.027-.746-1.45-.84-3.083-.7zm-8.521 4.63c-1.263.086-1.549.105-2.266-.477L2.647 2.76c-.186-.187-.092-.42.375-.466l12.796-.933c1.074-.094 1.634.28 2.054.606l2.195 1.587c.093.047.326.326.047.326l-13.216.794-.162.01zM5.263 21.193V7.287c0-.606.187-.886.748-.933l15.176-.886c.515-.047.748.28.748.886v13.81c0 .609-.093 1.122-.934 1.168l-14.523.84c-.842.047-1.215-.232-1.215-.98zm14.338-13.16c.093.422 0 .842-.422.89l-.699.139v10.264c-.608.327-1.168.513-1.635.513-.747 0-.934-.232-1.495-.932l-4.576-7.185v6.952l1.448.327s0 .84-1.169.84l-3.221.186c-.094-.187 0-.654.327-.747l.84-.232V9.853L7.832 9.76c-.093-.42.14-1.026.794-1.073l3.456-.232 4.763 7.279v-6.44l-1.214-.14c-.094-.513.28-.887.747-.933l3.223-.187z"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1 @@
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>OpenAI</title><path d="M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1 @@
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>VertexAI</title><path d="M11.995 20.216a1.892 1.892 0 100 3.785 1.892 1.892 0 000-3.785zm0 2.806a.927.927 0 11.927-.914.914.914 0 01-.927.914z" fill="#4285F4"></path><path clip-rule="evenodd" d="M21.687 14.144c.237.038.452.16.605.344a.978.978 0 01-.18 1.3l-8.24 6.082a1.892 1.892 0 00-1.147-1.508l8.28-6.08a.991.991 0 01.682-.138z" fill="#669DF6" fill-rule="evenodd"></path><path clip-rule="evenodd" d="M10.122 21.842l-8.217-6.066a.952.952 0 01-.206-1.287.978.978 0 011.287-.206l8.28 6.08a1.893 1.893 0 00-1.144 1.479z" fill="#AECBFA" fill-rule="evenodd"></path><path d="M4.273 4.475a.978.978 0 01-.965-.965V1.09a.978.978 0 111.943 0v2.42a.978.978 0 01-.978.965zM4.247 13.034a.978.978 0 100-1.956.978.978 0 000 1.956zM4.247 10.19a.978.978 0 100-1.956.978.978 0 000 1.956zM4.247 7.332a.978.978 0 100-1.956.978.978 0 000 1.956z" fill="#AECBFA"></path><path d="M19.718 7.307a.978.978 0 01-.965-.979v-2.42a.965.965 0 011.93 0v2.42a.964.964 0 01-.965.979zM19.743 13.047a.978.978 0 100-1.956.978.978 0 000 1.956zM19.743 10.151a.978.978 0 100-1.956.978.978 0 000 1.956zM19.743 2.068a.978.978 0 100-1.956.978.978 0 000 1.956z" fill="#4285F4"></path><path d="M11.995 15.917a.978.978 0 01-.965-.965v-2.459a.978.978 0 011.943 0v2.433a.976.976 0 01-.978.991zM11.995 18.762a.978.978 0 100-1.956.978.978 0 000 1.956zM11.995 10.64a.978.978 0 100-1.956.978.978 0 000 1.956zM11.995 7.783a.978.978 0 100-1.956.978.978 0 000 1.956z" fill="#669DF6"></path><path d="M15.856 10.177a.978.978 0 01-.965-.965v-2.42a.977.977 0 011.702-.763.979.979 0 01.241.763v2.42a.978.978 0 01-.978.965zM15.869 4.913a.978.978 0 100-1.956.978.978 0 000 1.956zM15.869 15.853a.978.978 0 100-1.956.978.978 0 000 1.956zM15.869 12.996a.978.978 0 100-1.956.978.978 0 000 1.956z" fill="#4285F4"></path><path d="M8.121 15.853a.978.978 0 100-1.956.978.978 0 000 1.956zM8.121 7.783a.978.978 0 100-1.956.978.978 0 000 1.956zM8.121 4.913a.978.978 0 100-1.957.978.978 0 000 1.957zM8.134 12.996a.978.978 0 01-.978-.94V9.611a.965.965 0 011.93 0v2.445a.966.966 0 01-.952.94z" fill="#AECBFA"></path></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
docs/assets/relay-hero.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

75
fetch-logos.sh Executable file
View file

@ -0,0 +1,75 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
OUT_DIR="$ROOT_DIR/docs/assets/logos"
TMP_DIR="$(mktemp -d)"
cleanup() {
rm -rf "$TMP_DIR"
}
trap cleanup EXIT
mkdir -p "$OUT_DIR"
ICONS=(
"anthropic|anthropic.svg"
"azure|azure-color.svg"
"bedrock|bedrock-color.svg"
"claude|claude-color.svg"
"cursor|cursor.svg"
"mistral|mistral-color.svg"
"notion|notion.svg"
"openai|openai.svg"
"vertexai|vertexai-color.svg"
)
successes=()
misses=()
fetch_icon() {
local name="$1"
local package_file="$2"
local dest="$OUT_DIR/$name.svg"
local tmp="$TMP_DIR/$name.svg"
local urls=(
"https://unpkg.com/@lobehub/icons-static-svg@latest/icons/$package_file"
"https://registry.npmmirror.com/@lobehub/icons-static-svg/latest/files/icons/$package_file"
)
for url in "${urls[@]}"; do
if curl -fsSL "$url" -o "$tmp" && grep -qi '<svg' "$tmp"; then
cp "$tmp" "$dest"
successes+=("$name.svg <= $url")
return 0
fi
done
rm -f "$tmp" "$dest"
misses+=("$name.svg")
return 1
}
for icon in "${ICONS[@]}"; do
name="${icon%%|*}"
package_file="${icon#*|}"
fetch_icon "$name" "$package_file" || true
done
echo "Succeeded:"
if ((${#successes[@]})); then
printf ' - %s\n' "${successes[@]}"
else
echo " - none"
fi
echo "Missed:"
if ((${#misses[@]})); then
printf ' - %s\n' "${misses[@]}"
else
echo " - none"
fi
echo "Known package gaps:"
echo " - codex.svg (using openai.svg in docs/assets/hero.html)"
echo " - openclaw.svg (using emoji fallback in docs/assets/hero.html)"