LLM Cluster — API

NoReason · GPU inference přes router
cluster online

Samohostovaný LLM cluster: pošleš prompt na jeden endpoint a router ho rozhodí na volnou GPU. Kompatibilní s Ollama i OpenAI API — tvoje aplikace ho může používat jako běžnou LLM službu (chat, JSON výstupy, embeddingy, dávky).

Veřejně  https://llmrouter.pigsn.cz Interně (Docker na stejném serveru)  http://llmrouter:8020

Za 30 sekundRychlý start

Jediný povinný požadavek: hlavička s API klíčem. Model neuváděj — router pošle úlohu na nejméně vytížený node a ten použije svůj lokální chat model.

curl
curl https://llmrouter.pigsn.cz/api/chat \
  -H "X-API-Key: $LLM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"Řekni jedním slovem: funguje?"}]}'
odpověď
{
  "model": "gemma4:12b",
  "done": true,
  "message": { "role": "assistant", "content": "Funguje." },
  "_router_node": "SERVER-BRNO@192.168.10.200"
}

KlíčAutentizace

Každý požadavek (kromě /health) musí nést API klíč. Router přijímá obě běžné formy — použij tu, kterou tvoje knihovna posílá:

X-API-Key: <klíč>
Ollama-styl a přímé volání (fetch, curl, requests).
Authorization: Bearer <klíč>
Standard OpenAI SDK a nástroje kolem OpenAI API — fungují out-of-box.

Každá aplikace má mít svůj vlastní klíč — vytvoříš ho v dashboardu v sekci Přístup („Vytvořit klíč"). U klíče pak vidíš, kolik požadavků a tokenů spotřeboval a kolik by to stálo komerčně; jde ho kdykoli pozastavit nebo smazat, aniž bys sahal na ostatní aplikace. Klíč začíná sk-….

Existuje i jeden hlavní klíč (ROUTER_API_KEY v ~/llmrouter/.env) s plným přístvem vč. administrace — ten drž jen pro sebe, do aplikací dávej per-app klíče. Bez klíče nebo se špatným → 401.

Klíč nedávej do frontendu

Klíč = přístup k clusteru na tvůj účet. Drž ho na backendu / v proměnné prostředí, nikdy ne v prohlížeči nebo v repozitáři.

ModelJak cluster routuje

Router je model-agnostický: u běžného chatu ignoruje požadovaný název modelu a pošle úlohu na nejméně vytížený node, který ji spočítá na svém preferovaném modelu (dnes gemma4:12b na 8 GB nodech, gemma4:26b na RTX 4090). Tím se práce rozloží na celý cluster a škáluje s počtem GPU.

Když potřebuješ konkrétní model (např. qwen3.6:35b-a3b pro těžší úlohu), přidej "strict": true a "model" (v /v1 stačí vyplnit model). Router pak úlohu pošle jen na node, který ten model má nainstalovaný. Když ho žádný nemá, dostaneš čitelnou chybu; když jsou nody plné, požadavek čeká ve frontě (viz limity níže).

Thinking modely (gemma4, qwen3.x): router má reasoning implicitně vypnutý (think:false) — u extrakcí by jinak spálil tisíce skrytých tokenů a vracel prázdný obsah. Potřebuješ-li reasoning, pošli explicitně "think": true.

Souběžná kapacita

Cluster zpracovává desítky požadavků najednou (kapacita = součet slotů online nodů; přes den běží část nodů podle rozvrhu, večer typicky celá flotila ~50+ slotů). Když jsou sloty plné, požadavky čekají ve frontě (chat má přednost před dávkami); když se slot neuvolní do 180 s, dostaneš 502 — počítej s retry. Aktuální kapacitu a frontu vidíš v /api/nodes (capacity, queued) a /health.

EndpointChat

POST/api/chat Ollama-styl · JSON in/out

Tělo požadavku:

PoleTypPopis
messagespovinnéPole zpráv {role, content} (role: system / user / assistant).
formatvolitelné"json" → model vrátí validní JSON (JSON mode).
optionsvolitelnéParametry modelu, např. {"temperature":0,"num_ctx":4096}.
strict+modelvolitelnéCíleně na node s daným modelem (viz routing).

Volitelná hlavička X-Task: <název> seskupí práci pod pojmenovanou úlohu ve statistikách (užitečné pro měření spotřeby na dashboardu).

JSON výstup + parametry
curl https://llmrouter.pigsn.cz/api/chat \
  -H "X-API-Key: $LLM_KEY" -H "X-Task: extrakce-adres" \
  -d '{
    "messages":[
      {"role":"system","content":"Vrať JSON {mesto, psc}."},
      {"role":"user","content":"Firma sídlí na Náměstí 5, 60200 Brno."}
    ],
    "format":"json",
    "options":{"temperature":0}
  }'

EndpointChat — OpenAI-kompatibilní

POST/v1/chat/completions drop-in pro OpenAI SDK

Stejná práce, ale ve formátu OpenAI Chat Completions. Nastav SDK base_url na …/v1 a klíč projde jako Bearer. Mapují se temperature, top_p, max_tokens/max_completion_tokens, stop, response_format {"type":"json_object"} i {"type":"json_schema", "json_schema":{"schema":{…}}} (grammar-constrained výstup — model nemůže vrátit nevalidní JSON). Vyplněný model = cílené routování na node s tím modelem; prázdný / default / gpt-* = výchozí model nodu. stream:true není podporován (dostaneš 400) — výsledek přijde vcelku.

python · openai sdk
from openai import OpenAI

client = OpenAI(
    base_url="https://llmrouter.pigsn.cz/v1",
    api_key=os.environ["LLM_KEY"],        # jde jako Authorization: Bearer
)
resp = client.chat.completions.create(
    model="gemma4:26b",               # konkrétní model = cílené routování; "default" = model nodu
    messages=[{"role":"user","content":"Ahoj!"}],
)
print(resp.choices[0].message.content)

EndpointEmbeddingy

POST/api/embed vektory pro vyhledávání / RAG

Vrací vektory pro pole textů. Výchozí model bge-m3 (1024 dim, vícejazyčný); volitelně nomic-embed-text. Router pošle úlohu jen na node, který embed model má.

curl
curl https://llmrouter.pigsn.cz/api/embed \
  -H "X-API-Key: $LLM_KEY" \
  -d '{"model":"bge-m3","input":["první text","druhý text"]}'

# → {"model":"bge-m3","embeddings":[[0.01,...],[...]], "_router_node":"..."}

EndpointDávkové úlohy

Na velké dávky (tisíce položek) nedělej tisíce HTTP volání — pošli jednu úlohu. Router ji rozdělí na položky, rozhodí přes celý cluster a ty si průběžně bereš výsledky.

POST/api/jobszaloží dávku
PolePopis
itemsPole textů ke zpracování (povinné).
systemSystem prompt (stejný pro všechny položky).
templateŠablona user zprávy; {input} se nahradí položkou. Výchozí "{input}".
json_formattrue (výchozí) → JSON výstupy.
optionsParametry modelu (temperature, num_ctx, num_predict…).
modelVolitelné: konkrétní model — položky poběží jen na nodech, které ho mají (neznámý model → 400). Bez něj výchozí model nodu.
založení + průběh
# 1) založ dávku → vrátí {"job_id":"ab12…","total":3}
curl …/api/jobs -H "X-API-Key: $LLM_KEY" -d '{
  "name":"kategorizace",
  "system":"Zařaď inzerát. Vrať JSON {kategorie}.",
  "template":"Text: {input}",
  "items":["…inzerát 1…","…inzerát 2…","…inzerát 3…"]
}'

# 2) stav (kolik hotovo)      GET /api/jobs/{job_id}
# 3) všechny výsledky         GET /api/jobs/{job_id}/results
curl …/api/jobs/ab12…/results -H "X-API-Key: $LLM_KEY"
GET/api/jobs/{id}stav + vzorek
GET/api/jobs/{id}/resultsvšechny výsledky

Výsledek každé položky: {idx, input, status, result, error}. status jde pending → done / error; dokud done < total, dávka běží.

EndpointStav & zdraví

GET/healthbez klíče · pro monitoring
odpověď
{ "ok": true, "queued": 3, "running": 19 }   # fronta / právě zpracovává
GET/api/nodesnody, kapacita, příkon, modely
GET/api/statshistorie práce a spotřeby (Europe/Prague)
GET/api/tagsOllama-kompat seznam modelů

/api/nodes vrací pole nodů s poli online, routable, inflight, num_parallel, models, power_w, disk_free_gb… + souhrn capacity (souběžná kapacita) a power_w. Živý přehled je i na dashboardu /ui/.

AdminSpráva modelů

Modely se instalují/spravují přes API i přes dashboard (záložka Modely). Node stáhne model na vyžádání a router pak na něj umí cílit.

POST/api/pull_modelstáhnout na všechny nody, kterým chybí
POST/api/node/{id}/model{model, action:"pull"|"remove"}
POST/api/node/{id}/prefmodel{model} — na čem node počítá

Node id je z /api/nodes (např. SERVER-BRNO@192.168.10.200); v URL ho URL-enkóduj. Dále lze node vypnout z routingu, nastavit časové okno a preferovaný model — vše na dashboardu na kartě nodu.

Copy & pastePříklady v kódu

Python — přímé volání (requests)

python
import os, requests

BASE = "https://llmrouter.pigsn.cz"   # nebo http://llmrouter:8020 zevnitř Dockeru
H = {"X-API-Key": os.environ["LLM_KEY"]}

r = requests.post(f"{BASE}/api/chat", headers={**H, "X-Task":"muj-ukol"}, json={
    "messages": [{"role":"user","content":"Shrň jednou větou: …"}],
    "options": {"temperature": 0},
}, timeout=120)
print(r.json()["message"]["content"])

JavaScript / Node — fetch

javascript
const res = await fetch("https://llmrouter.pigsn.cz/api/chat", {
  method: "POST",
  headers: { "X-API-Key": process.env.LLM_KEY, "Content-Type": "application/json" },
  body: JSON.stringify({ messages: [{ role: "user", content: "Ahoj" }] }),
});
const data = await res.json();
console.log(data.message.content);

Druhý názor na silnějším modelu (strict)

python
# pošli TĚŽKÝ / nejistý případ cíleně na velký MoE model (musí být na některém nodu)
requests.post(f"{BASE}/api/chat", headers=H, json={
    "messages": msgs,
    "model": "qwen3.6:35b-a3b",
    "strict": True,          # → jen node s tímto modelem; jinak chyba (ošetři fallback)
})

DostupnéModely v clusteru

ModelK čemuPoznámka
gemma4:12bchat / JSON extrakce (výchozí bulk)Na většině nodů — nejlepší poměr česká přesnost × rychlost (slepé A/B na reálných datech).
gemma4:26bchat — rychlý velký modelMoE (~4B aktivních). Celý ve VRAM na RTX 4090 (~1 s/dotaz), hybridně jinde.
qwen3.6:35b-a3barbitráž / těžké úlohyNejpřesnější na číslech (10/10 mzdový golden set). Hybrid GPU+DDR5, ~20 s/dotaz — jen přes strict.
bge-m3embeddingy1024 dim, vícejazyčný. Výchozí pro /api/embed.
nomic-embed-textembeddingyLehčí alternativa.
qwen2.5:7b/14blegacyPonechány jako pojistka, v Ollamě deprecated — nové integrace je nemají používat.

Nový model přidáš přes správu modelů (dashboard → Modely) — jakýkoli z ollama.com/library.

ProdukceDobrá praxe & limity

Interní vs. veřejná URL
Aplikace na stejném serveru volej přes http://llmrouter:8020 (Docker síť llmrouter_default) — ušetříš ~0,5–1,5 s režie oproti veřejnému HTTPS přes nginx.
Znovupoužívej spojení
Drž jednoho HTTP klienta s keep-alive a retry (2–3×). Router se občas restartuje (deploy); keep-alive spojení pak jednou selže a retry to nezvratně dorovná.
Timeouty
Dej klientu velkorysý timeout (60–120 s). Při plném clusteru požadavek chvíli čeká na volný slot.
JSON výstupy
format:"json" (nebo OpenAI response_format) + temperature:0 = stabilní strojově parsovatelný výstup.
Označuj práci (X-Task)
Hlavička X-Task seskupí tvoje volání do pojmenované úlohy — pak v dashboardu vidíš, kolik práce a energie tvoje appka spotřebovala.
Klíče per aplikace & férovost

Každá aplikace má vlastní klíč (dashboard → Přístup) s účtováním požadavků i tokenů — sdílený master klíč do aplikací nedávej. Interaktivní chat má přednost před dávkami; dávky ale nikdy nevyhladoví — každý ~8. slot patří frontě dávek, takže tečou i pod plným chat náporem. Jedna aplikace přesto umí cluster na čas zabrat velkým náporem chatů — u hromadné práce používej dávky (/api/jobs), ne smyčku chat volání.

Odolnost, na kterou se dá spolehnout

Nody se po výpadku samy znovupřipojí (WS reconnect), rozdělané úlohy se po timeoutu vrátí do fronty a přeberou jinde, a když je GPU offline, práce počká — nic se neztratí. Zdraví sleduj přes /health a /api/nodes.