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 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?"}]}'
{
"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íč = 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.
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
Tělo požadavku:
| Pole | Typ | Popis |
|---|---|---|
messages | povinné | Pole zpráv {role, content} (role: system / user / assistant). |
format | volitelné | "json" → model vrátí validní JSON (JSON mode). |
options | volitelné | Parametry modelu, např. {"temperature":0,"num_ctx":4096}. |
strict+model | volitelné | 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).
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í
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.
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
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 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.
| Pole | Popis |
|---|---|
items | Pole textů ke zpracování (povinné). |
system | System prompt (stejný pro všechny položky). |
template | Šablona user zprávy; {input} se nahradí položkou. Výchozí "{input}". |
json_format | true (výchozí) → JSON výstupy. |
options | Parametry modelu (temperature, num_ctx, num_predict…). |
model | Volitelné: konkrétní model — položky poběží jen na nodech, které ho mají (neznámý model → 400). Bez něj výchozí model nodu. |
# 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"
Výsledek každé položky: {idx, input, status, result, error}.
status jde pending → done / error; dokud done < total, dávka běží.
EndpointStav & zdraví
{ "ok": true, "queued": 3, "running": 19 } # fronta / právě zpracovává/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.
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)
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
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)
# 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
| Model | K čemu | Poznámka |
|---|---|---|
gemma4:12b | chat / 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:26b | chat — rychlý velký model | MoE (~4B aktivních). Celý ve VRAM na RTX 4090 (~1 s/dotaz), hybridně jinde. |
qwen3.6:35b-a3b | arbitráž / těžké úlohy | Nejpřesnější na číslech (10/10 mzdový golden set). Hybrid GPU+DDR5, ~20 s/dotaz — jen přes strict. |
bge-m3 | embeddingy | 1024 dim, vícejazyčný. Výchozí pro /api/embed. |
nomic-embed-text | embeddingy | Lehčí alternativa. |
qwen2.5:7b/14b | legacy | Ponechá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 OpenAIresponse_format) +temperature:0= stabilní strojově parsovatelný výstup.- Označuj práci (X-Task)
- Hlavička
X-Taskseskupí tvoje volání do pojmenované úlohy — pak v dashboardu vidíš, kolik práce a energie tvoje appka spotřebovala.
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í.
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.