labs / static /docs.html
3v324v23's picture
deploy: unified router + dreamy website (2026-06-16T09:46:52Z)
c1a683f
Raw
History Blame Contribute Delete
14.1 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#FFD6E8" />
<!-- CRITICAL: paint the dawn sky on the first byte, before any external CSS/JS
loads. background-attachment:fixed locks it to the viewport AND is the only
background iOS Safari renders in the overscroll/rubber-band region — so
scrolling never exposes a bare strip. Literal hex because tokens.css
hasn't loaded yet. -->
<style>
html{background:linear-gradient(180deg,#E8E0FF 0%,#FFD6E8 52%,#FFE5D9 100%) fixed;background-color:#FFD6E8;}
</style>
<title>Docs — DreamRouter</title>
<meta name="description" content="DreamRouter API reference: authenticate, list models, send chat completions, stream responses, and handle errors across one OpenAI-compatible endpoint." />
<link rel="canonical" href="https://apiarium-labs.hf.space/docs.html" />
<meta property="og:type" content="website" />
<meta property="og:title" content="DreamRouter Docs" />
<meta property="og:description" content="One OpenAI-compatible endpoint. Auth, models, chat, streaming, errors." />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Comfortaa:wght@400;500;700&family=Nunito:wght@400;600;700;800&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet" />
<script src="https://unpkg.com/lenis@1.1.13/dist/lenis.min.js" defer></script>
<link rel="stylesheet" href="css/tokens.css?v=20260620b" />
<link rel="stylesheet" href="css/base.css?v=20260620b" />
<link rel="stylesheet" href="css/scene.css?v=20260620b" />
<link rel="stylesheet" href="css/nav.css?v=20260620b" />
<link rel="stylesheet" href="css/sections.css?v=20260620b" />
<link rel="stylesheet" href="css/docs.css?v=20260620b" />
</head>
<body data-page="docs">
<!-- dream overlays -->
<div class="blur-overlay" aria-hidden="true"></div>
<div class="grain-overlay" aria-hidden="true"></div>
<!-- a lighter scene for inner pages: far clouds + moon only (no whale) -->
<div class="scene" aria-hidden="true">
<div class="layer layer-far-clouds" data-speed="0.06" data-mouse="6">
<svg class="cloud-far c1" viewBox="0 0 200 100"><use href="#cloud"/></svg>
<svg class="cloud-far c3" viewBox="0 0 200 100"><use href="#cloud"/></svg>
</div>
<div class="layer layer-moon" data-speed="0.10" data-mouse="10">
<div class="moon"><span class="moon-disc"></span></div>
</div>
</div>
<!-- shared nav + footer -->
<div data-site-nav></div>
<main id="main">
<header class="doc-hero">
<div class="container">
<h1 data-split>docs</h1>
<p data-reveal>One OpenAI-compatible endpoint. Authenticate, send a request, stream the reply.</p>
</div>
</header>
<div class="container">
<div class="doc-layout">
<!-- sticky TOC -->
<aside class="doc-toc" aria-label="On this page">
<h2>On this page</h2>
<ul>
<li><a href="#quickstart">Quickstart</a></li>
<li><a href="#auth">Authentication</a></li>
<li><a href="#base-url">Base URL</a></li>
<li><a href="#models">List models</a></li>
<li><a href="#chat">Chat completions</a></li>
<li><a href="#streaming">Streaming</a></li>
<li><a href="#parameters">Parameters</a></li>
<li><a href="#errors">Errors</a></li>
<li><a href="#retries">Retries &amp; limits</a></li>
</ul>
</aside>
<!-- content -->
<div class="doc-content">
<section id="quickstart">
<h2>Quickstart</h2>
<p data-reveal>Send a standard OpenAI-style chat completion. Swap the base URL and the model name — that's the whole integration.</p>
<div class="bezel" data-code-tabs>
<div class="code-tabs" role="tablist">
<button data-tab="curl" aria-selected="true">cURL</button>
<button data-tab="py" aria-selected="false">Python</button>
<button data-tab="js" aria-selected="false">JavaScript</button>
</div>
<div class="code-wrap">
<pre class="code-panel" data-tab="curl" data-active="true"><span class="tok-com"># one endpoint, every model</span>
curl https://apiarium-labs.hf.space/v1/chat/completions \
-H <span class="tok-str">"Authorization: Bearer $DREAMROUTER_KEY"</span> \
-H <span class="tok-str">"Content-Type: application/json"</span> \
-d <span class="tok-str">'{
"model": "claude-opus-4.8",
"messages": [{"role":"user","content":"Hello, whale."}]
}'</span></pre>
<pre class="code-panel" data-tab="py"><span class="tok-key">from</span> openai <span class="tok-key">import</span> OpenAI
client = OpenAI(
base_url=<span class="tok-str">"https://apiarium-labs.hf.space/v1"</span>,
api_key=<span class="tok-str">"dr_u_..."</span>, <span class="tok-com"># your personal key from the dashboard</span>
)
resp = client.chat.completions.create(
model=<span class="tok-str">"claude-opus-4.8"</span>,
messages=[{<span class="tok-str">"role"</span>: <span class="tok-str">"user"</span>, <span class="tok-str">"content"</span>: <span class="tok-str">"Hello, whale."</span>}],
)
<span class="tok-key">print</span>(resp.choices[<span class="tok-num">0</span>].message.content)</pre>
<pre class="code-panel" data-tab="js"><span class="tok-key">import</span> OpenAI <span class="tok-key">from</span> <span class="tok-str">"openai"</span>;
<span class="tok-key">const</span> client = <span class="tok-key">new</span> OpenAI({
baseURL: <span class="tok-str">"https://apiarium-labs.hf.space/v1"</span>,
apiKey: <span class="tok-str">"dr_u_..."</span>, <span class="tok-com">// your personal key from the dashboard</span>
});
<span class="tok-key">const</span> resp = <span class="tok-key">await</span> client.chat.completions.create({
model: <span class="tok-str">"claude-opus-4.8"</span>,
messages: [{ role: <span class="tok-str">"user"</span>, content: <span class="tok-str">"Hello, whale."</span> }],
});
console.log(resp.choices[<span class="tok-num">0</span>].message.content);</pre>
</div>
</div>
<div class="callout note">
<span class="ico">i</span>
<div>The router rewrites your model name to each target's upstream id automatically. You send one name; it resolves to a healthy pool behind the scenes. <a href="models.html">Browse the 374 available models →</a></div>
</div>
</section>
<section id="auth">
<h2>Authentication</h2>
<p data-reveal>Proxy endpoints require a Bearer token in the <code>Authorization</code> header.</p>
<pre class="code-panel" style="background:var(--ink);padding:1rem 1.2rem;border-radius:14px;"><span class="tok-key">Authorization</span>: Bearer sk-your-DreamRouter-key</pre>
<div class="callout warn">
<span class="ico">!</span>
<div>Public endpoints (<code>/</code>, <code>/health</code>, <code>/v1/models</code>) need no auth. Everything else returns <code>403</code> without a valid key. An optional IP allowlist can further lock access.</div>
</div>
</section>
<section id="base-url">
<h2>Base URL</h2>
<p data-reveal>All requests are made against:</p>
<pre class="code-panel" style="background:var(--ink);padding:1rem 1.2rem;border-radius:14px;">https://apiarium-labs.hf.space/v1</pre>
<p>Point any OpenAI-compatible client at this base URL and you are done.</p>
</section>
<section id="models">
<h2>List models</h2>
<p data-reveal><code>GET /v1/models</code> returns every model the router can resolve. Public, no auth required.</p>
<div class="bezel" data-code-tabs>
<div class="code-tabs" role="tablist">
<button data-tab="curl" aria-selected="true">cURL</button>
<button data-tab="resp" aria-selected="false">Response</button>
</div>
<div class="code-wrap">
<pre class="code-panel" data-tab="curl" data-active="true">curl https://apiarium-labs.hf.space/v1/models</pre>
<pre class="code-panel" data-tab="resp">{
<span class="tok-key">"object"</span>: <span class="tok-str">"list"</span>,
<span class="tok-key">"data"</span>: [
{ <span class="tok-key">"id"</span>: <span class="tok-str">"claude-opus-4.8"</span>, <span class="tok-key">"object"</span>: <span class="tok-str">"model"</span>, <span class="tok-key">"owned_by"</span>: <span class="tok-str">"claude-subnet"</span> },
{ <span class="tok-key">"id"</span>: <span class="tok-str">"deepseek-v4-pro"</span>, <span class="tok-key">"object"</span>: <span class="tok-str">"model"</span>, <span class="tok-key">"owned_by"</span>: <span class="tok-str">"deepseek"</span> }
<span class="tok-com">// ...374 models</span>
]
}</pre>
</div>
</div>
</section>
<section id="chat">
<h2>Chat completions</h2>
<p data-reveal><code>POST /v1/chat/completions</code> accepts the standard OpenAI request body. The <code>model</code> field selects the pool; everything else passes through.</p>
<table class="doc-table">
<thead><tr><th>Field</th><th>Type</th><th>Notes</th></tr></thead>
<tbody>
<tr><td><code>model</code></td><td>string</td><td>Required. Any name from <code>/v1/models</code>.</td></tr>
<tr><td><code>messages</code></td><td>array</td><td>OpenAI message format.</td></tr>
<tr><td><code>stream</code></td><td>boolean</td><td>SSE stream when <code>true</code>.</td></tr>
<tr><td><code>temperature</code></td><td>number</td><td>Forwarded upstream.</td></tr>
<tr><td><code>max_tokens</code></td><td>number</td><td>Forwarded. GPT-5.x maps to <code>max_completion_tokens</code> automatically.</td></tr>
<tr><td><code>tools</code></td><td>array</td><td>Tool/function calling supported where the upstream allows it.</td></tr>
</tbody>
</table>
</section>
<section id="streaming">
<h2>Streaming</h2>
<p data-reveal>Set <code>"stream": true</code>. The router forwards the upstream SSE stream and filters non-<code>data:</code> lines so strict parsers stay happy. Each chunk is a standard <code>chat.completion.chunk</code>.</p>
<div class="bezel">
<div class="code-wrap">
<pre class="code-panel" data-active="true" style="display:block;">data: {<span class="tok-key">"choices"</span>:[{<span class="tok-key">"delta"</span>:{<span class="tok-key">"content"</span>:<span class="tok-str">"Hello"</span>}}]}
data: {<span class="tok-key">"choices"</span>:[{<span class="tok-key">"delta"</span>:{<span class="tok-key">"content"</span>:<span class="tok-str">", whale."</span>}}]}
data: [DONE]</pre>
</div>
</div>
</section>
<section id="parameters">
<h2>Parameters</h2>
<p data-reveal>Unsupported parameters are silently dropped before forwarding. The router never invents upstream behavior — it only translates model names and injects keys.</p>
</section>
<section id="errors">
<h2>Errors</h2>
<table class="doc-table">
<thead><tr><th>Status</th><th>Meaning</th></tr></thead>
<tbody>
<tr><td><code>401</code></td><td>Missing or invalid bearer token.</td></tr>
<tr><td><code>403</code></td><td>Endpoint blocked, or IP not on the allowlist.</td></tr>
<tr><td><code>404</code></td><td>Model not found. Returned before any upstream call.</td></tr>
<tr><td><code>429</code></td><td>All targets rate-limited after retries.</td></tr>
<tr><td><code>502</code></td><td>Every target in the pool failed its attempts.</td></tr>
<tr><td><code>503</code></td><td>Pool exhausted — every target cooling down.</td></tr>
</tbody>
</table>
</section>
<section id="retries">
<h2>Retries &amp; limits</h2>
<p data-reveal>Each request gets up to <strong>five attempts</strong>. On <code>401</code>, <code>402</code>, <code>403</code>, <code>429</code>, or <code>5xx</code>, the failing target cools for <strong>sixty seconds</strong> and the router tries the next healthy one. Cooldowns are idempotent — re-failing a target never resets its timer, so flapping endpoints recover cleanly.</p>
<div class="callout note">
<span class="ico"></span>
<div>Load is spread by a thread-safe round-robin cursor, so traffic distributes evenly across the whole pool — not hammering the first target.</div>
</div>
</section>
</div><!-- /.doc-content -->
</div><!-- /.doc-layout -->
</div><!-- /.container -->
<div data-site-footer></div>
</main>
<!-- SVG sprite (cloud symbol) -->
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
<symbol id="cloud" viewBox="0 0 200 100">
<path fill="currentColor" d="M44,82 C24,82 18,58 38,53 C32,32 62,26 74,42 C80,22 116,22 122,44 C142,32 168,48 158,68 C172,72 172,90 152,90 L54,90 C40,92 35,88 44,82 Z" />
</symbol>
</svg>
<script src="js/layout.js?v=20260620b"></script>
<script src="js/motion.js?v=20260620b"></script>
<script src="js/parallax.js?v=20260620b"></script>
<script src="js/docs.js?v=20260620b"></script>
</body>
</html>