API-Referenz
ollaya serve stellt zwei APIs auf http://localhost:11435 bereit:
- die native API unter
/api/*, modelliert nach Ollamas API, für Entscheidungen und Modellverwaltung; - die TypeSafe-kompatible API unter
/v1/*, wire-identisch mit TypeSafe, sodass vorhandene TypeSafe-SDKs unverändert funktionieren. Siehe TypeSafe-Kompatibilität.
| Methode | Pfad | Zweck |
|---|---|---|
GET, HEAD |
/ |
Lebendigkeit: Ollaya is running |
GET |
/api/version |
Serverversion |
POST |
/api/decide |
Beantwortet typisierte Fragen zu einem Zustand; lädt und entlädt außerdem ein Modell |
GET |
/api/tags |
Modelle auf diesem Rechner |
POST |
/api/show |
Details zu einem Modell |
GET |
/api/ps |
In den Speicher geladene Modelle |
POST |
/api/pull |
Lädt ein Modell herunter (streamt den Fortschritt) |
DELETE |
/api/delete |
Entfernt ein Modell |
POST |
/api/copy |
Kopiert ein Modell unter einen neuen Namen |
POST |
/api/create |
Erstellt ein Modell aus einem anderen (streamt den Fortschritt) |
POST |
/v1/systemone |
TypeSafe System One |
POST |
/v1/decisions |
Alias von /v1/systemone |
GET |
/v1/models |
TypeSafe-Modellliste |
/api/push und /api/blobs/:digest sind reserviert und antworten 501 NOT_IMPLEMENTED. Ollamas Text-Endpunkte (/api/generate, /api/chat, /api/embed) antworten 404: Entscheidungsmodelle erzeugen nie Text.
Konventionen
- JSON. Anfrage- und Antwortkörper sind JSON-Objekte. Der Körper wird unabhängig von seinem
Content-Typeals JSON geparst,curl -dfunktioniert also unverändert. Anfragen sind höchstens 8 MiB groß. - Feldnamen sind
snake_case. Unbekannte Anfragefelder werden ignoriert;nullbedeutet abwesend. - Modellnamen sind
[host/][namespace/]model[:tag], ohne Beachtung der Groß-/Kleinschreibung. Ein fehlender Tag bedeutetlatest. Antworten verwenden immer die kanonische Form, etwalaya:latest. - Zahlen. Wahrscheinlichkeiten, Konfidenzen,
scoreundnoulwerden auf 4 Nachkommastellen gerundet. Dauern sind ganze Zahlen in Nanosekunden; Zeitstempel sind RFC 3339 in UTC. - Streaming.
/api/pullund/api/createstreamen zeilenweise getrenntes JSON, ein Objekt pro Zeile, und enden mit genau einem{"status":"success"}oder einer Fehlerzeile. Sende"stream": falsefür eine einzelne Antwort. - Request-IDs. Jede Antwort trägt
X-Request-Id, und/v1/*-Antworten zusätzlichx-typesafe-request-id. Eine gültige, vom Client gesendeteX-Request-Idwird zurückgegeben. - Nebenläufigkeit. Ein geladenes Modell führt jeweils eine Anfrage aus, und jede Anfrage beantwortet alle ihre Fragen in einem Durchlauf. Anfragen an dasselbe Modell werden in eine Warteschlange gestellt, mehr gleichzeitig zu senden wird also nicht schneller fertig; die Umlaufzeit jeder einzelnen enthält dann die Wartezeit. Stelle jede Frage zu einem Zustand in einer Anfrage. Verschiedene geladene Modelle laufen parallel.
- Keine impliziten Pulls. Kein Endpunkt lädt ein Modell als Nebenwirkung herunter.
ollaya runlädt zuerst herunter; Anwendungen rufen/api/pullauf.
Fehler
Jeder Fehler hat an jedem Endpunkt diesen Körper:
{
"error": "model \"laya:xl\" not found, try pulling it first",
"code": "MODEL_NOT_FOUND"
}
| Feld | Bedeutung |
|---|---|
error |
Für Menschen lesbare Meldung. Parse sie nicht; die eine eingefrorene Meldung ist model "<name>" not found, try pulling it first, wie in Ollama. |
code |
Maschinenlesbarer Code. Verzweige danach. |
detail |
Nur für INVALID_REQUEST, TOO_MANY_OPTIONS, INPUT_TOO_LONG und STATE_TRUNCATED: jedes Validierungsproblem in der ValidationError-Form von TypeSafe (FastAPI): loc, msg, type und manchmal ctx. |
| Code | HTTP | Wann | Wiederholen |
|---|---|---|---|
INVALID_JSON |
400 | Körper fehlt, ist kein JSON oder kein Objekt | nein |
INVALID_REQUEST |
422 | Körper besteht die Validierung nicht; detail listet jedes Problem auf |
nein |
TOO_MANY_OPTIONS |
422 | Die Optionen einer Frage passen nicht in das Optionsbudget des Modells | nein |
INPUT_TOO_LONG |
422 | state ist länger als 65.536 Token |
nein |
STATE_TRUNCATED |
422 | /v1/systemone oder /v1/decisions würde einen Teil von state verwerfen, um in den Kontext des Modells zu passen |
nein |
UNAUTHORIZED |
401 | OLLAYA_API_KEY ist gesetzt und der Anfrage fehlt der Schlüssel |
nein |
FORBIDDEN |
403 | Browser-Header Origin oder Host nicht erlaubt |
nein |
MODEL_NOT_FOUND |
404 | Modell (oder das Ziel eines Routers) nicht auf diesem Rechner; bei einem Pull nicht in der Registry | nein |
NOT_FOUND |
404 | Kein solcher Endpunkt | nein |
METHOD_NOT_ALLOWED |
405 | Endpunkt existiert, Methode nicht | nein |
OPERATION_IN_PROGRESS |
409 | Ein Pull oder Create schreibt denselben Modellnamen | danach |
REQUEST_TOO_LARGE |
413 | Körper über 8 MiB | nein |
QUEUE_FULL |
503 | OLLAYA_MAX_QUEUE Anfragen warten bereits; gesendet mit Retry-After: 1 |
ja |
MODEL_LOAD_FAILED |
500 | Das Modell konnte nicht geladen werden (beschädigte Dateien, Speicher, OLLAYA_LOAD_TIMEOUT) |
selten |
INFERENCE_FAILED |
500 | Der Runner ist während einer Entscheidung fehlgeschlagen | ja |
STORAGE_ERROR |
500 | Datenträger voll, Berechtigungen oder E/A | nein |
INTERNAL |
500 | Ein Bug; das Serverprotokoll hat Details unter der Request-ID | ja |
UNSUPPORTED_MODEL |
501 | Dieser Build kann das Format des Modells nicht ausführen | nein |
NOT_IMPLEMENTED |
501 | Reservierter Endpunkt | nein |
REGISTRY_ERROR |
502 | Registry nicht erreichbar oder ungültig | ja |
DIGEST_MISMATCH |
502 | Ein Download passte nicht zu seinem sha256 und wurde verworfen | ja |
Die Menge der Codes ist offen: Behandle einen unbekannten anhand seines HTTP-Status. Ein Validierungsfehler listet jedes Problem auf einmal auf:
{
"error": "state: Field required; questions.urgency.score.criteria: List should have at least 2 items after validation, not 1",
"code": "INVALID_REQUEST",
"detail": [
{"loc": ["body", "state"], "msg": "Field required", "type": "missing"},
{
"loc": ["body", "questions", "urgency", "score", "criteria"],
"msg": "List should have at least 2 items after validation, not 1",
"type": "too_short",
"ctx": {"field_type": "List", "min_length": 2, "actual_length": 1}
}
]
}
Sobald ein Stream begonnen hat, kommt ein Fehler als letzte Zeile in derselben Form, etwa {"error": "…", "code": "DIGEST_MISMATCH"}. Prüfe jede Zeile auf error, bevor du sie als Fortschritt liest.
Fragen
/api/decide, /v1/systemone und /api/create teilen sich ein Fragenschema, das von TypeSafe. Eine Anfrage hat 1–256 Fragen, mit einer beliebigen ID als Schlüssel; die Antworten kommen in derselben Reihenfolge zurück.
type |
instructions |
criteria |
Antwort |
|---|---|---|---|
choice |
optional | erforderlich: Objektlabel → Beschreibung oder ein Array von Labels; 2–255 Optionen | choice, confidence, probabilities |
score |
optional | erforderlich: Array von Stufenbeschreibungen, Stufe 0 zuerst; 2–10 Stufen | score, confidence, legend, probabilities |
noul |
optional | optional: {"true": "…", "false": "…"} |
noul |
instructionskann ein String, ein Objekt, ein Array odernullsein. Fehlt es oder ist esnull, liest das Modell stattdessen die Frage-ID, sodass eine beschreibende ID wieis_spamfür sich allein funktioniert.stateist ein String, ein Objekt oder ein Array, bis zu 65.536 Token. Überschreitet er den verfügbaren Kontext des Modells, kürzt/api/decideihn und meldetstate_truncated: true./v1/systemoneund/v1/decisionsgeben422 STATE_TRUNCATEDzurück, mit dem antwortenden Modell indetail[0].ctx.model.- Modellgrenzen. Jede Option braucht Platz im Kontext des Modells: etwa 125 Optionen für
laya:en(512 Token) und 250 fürlaya:multilingual(1.024). Mehr ergibt422 TOO_MANY_OPTIONS. Bei einem Router gelten die Grenzen des Ziels.
Antworten sind TypeSafe-Formen, in dieser Feldreihenfolge:
type |
Felder |
|---|---|
choice |
choice: das wahrscheinlichste Label. confidence. probabilities: Label → Wahrscheinlichkeit, in der Reihenfolge der criteria. |
score |
score: die erwartete Stufe Σ i·pᵢ, die zwischen Stufen liegen kann. confidence. legend: "0"… → die Beschreibung der Stufe. probabilities: "0"… → Wahrscheinlichkeit. |
noul |
noul: die Wahrscheinlichkeit, dass die Aussage zutrifft. Keine confidence, wie in TypeSafe. |
confidence ist die von TypeSafe normalisierte Spitzenwahrscheinlichkeit, (K · pmax − 1) / (K − 1) für K Optionen: 0, wenn jede Option gleich wahrscheinlich ist, 1, wenn eine Option die ganze Wahrscheinlichkeit auf sich vereint. Die Formel ist für jedes Modell dieselbe, was eine gegebene Konfidenz bedeutet, aber nicht: Modelle sind unterschiedlich kalibriert, stimme einen Schwellenwert also pro Modell auf deinen eigenen Daten ab. Wahrscheinlichkeiten werden mit den Temperaturen jedes Modells kalibriert. Auf einer CUDA-GPU läuft der fp16-Graph, dessen Antworten bei knappen Ausgängen von fp32 abweichen können.
keep_alive
Wie lange ein Modell nach dem Ende einer Anfrage geladen bleibt, mit Ollamas Semantik:
| Wert | Bedeutung |
|---|---|
"5m", "1h30m", "300ms", 300, "300" |
Bleibt so lange nach der Anfrage geladen |
0, "0", "0s" |
Entlädt, sobald die Anfrage beendet ist |
-1, "-5m", jeder negative Wert |
Bleibt geladen, bis der Server stoppt oder ein explizites Entladen erfolgt |
fehlend oder null |
OLLAYA_KEEP_ALIVE, Standard 5m |
Der Timer startet, wenn eine Anfrage endet, und der Wert der letzten Anfrage gewinnt. Bei einem Router gilt er für das Ziel, das geantwortet hat. /v1/* ignoriert keep_alive.
Decide
POST /api/decide
Beantwortet typisierte Fragen zu einem Zustand in einem Vorwärtsdurchlauf. Der Körper ist der Körper von /v1/systemone plus native Optionen; die Antwort ist TypeSafes Antwort plus native Felder, sodass auch ein TypeSafe-Client sie parsen kann.
| Feld | Typ | Erforderlich | Hinweise |
|---|---|---|---|
model |
string | ja | Modellname |
state |
string, object or array | ja, um zu entscheiden | Ohne ihn lädt oder entlädt die Anfrage das Modell (unten) |
questions |
object | ja, außer das Modell hat eingebaute Fragen | Ersetzt die eingebauten Fragen des Modells vollständig |
preset |
string | nein | Der Name eines Presets, eingebaut oder eigen, anstelle von questions |
images |
array of strings | nein | Für ein Vision-Modell: PNG-Bilder, base64 oder base64-data:-URLs. Decider nimmt eines; winnow:e4b-vision nimmt bis zu 16. Siehe Bilder |
keep_alive |
string or number | nein | Siehe keep_alive |
extras |
array of strings | nein | ["laya"] fügt jeder Antwort layas eigene Konfidenz und Act-Wahrscheinlichkeit hinzu |
stream |
boolean | nein | Reserviert; true wird abgelehnt |
curl http://localhost:11435/api/decide -d '{
"model": "laya",
"state": "I was charged twice for my subscription this month. Please refund the second charge.",
"questions": {
"department": {
"type": "choice",
"instructions": "Which team should handle this ticket?",
"criteria": {
"billing": "Payments, invoices and refunds",
"technical": "Bugs, errors and outages",
"account": "Login, profile and settings"
}
},
"urgency": {
"type": "score",
"instructions": "How urgent is this ticket?",
"criteria": ["Can wait", "Needs attention this week", "Needs attention today"]
},
"refund": {
"type": "noul",
"instructions": "The customer asks for money back.",
"criteria": {"true": "Asks for a refund", "false": "Does not ask for a refund"}
}
},
"keep_alive": "10m"
}'
{
"model": "laya:en",
"answers": {
"department": {
"type": "choice",
"choice": "billing",
"confidence": 0.7781,
"probabilities": {"billing": 0.8521, "technical": 0.0611, "account": 0.0868}
},
"urgency": {
"type": "score",
"score": 1.1982,
"confidence": 0.3418,
"legend": {"0": "Can wait", "1": "Needs attention this week", "2": "Needs attention today"},
"probabilities": {"0": 0.1203, "1": 0.5612, "2": 0.3185}
},
"refund": {"type": "noul", "noul": 0.9127}
},
"usage": {"input_tokens": 118, "output_tokens": 0},
"routing": {
"router": "laya:latest",
"model": "laya:en",
"route": "english",
"reason": "English Latin text"
},
"state_truncated": false,
"done_reason": "decide",
"created_at": "2026-09-24T09:30:12.418Z",
"total_duration": 18734512,
"load_duration": 0,
"eval_duration": 16302117
}
| Feld | Bedeutung |
|---|---|
model |
Das Modell, das geantwortet hat: bei einem Router sein Ziel (laya:en für eine laya-Anfrage) |
answers |
Frage-ID → Antwort, in der Reihenfolge der Fragen |
usage |
Gelesene input_tokens; output_tokens ist immer 0 |
routing |
Bei einem Router: router, das gewählte model, ein stabiler route-Schlüssel und ein informativer reason. Sonst null. |
state_truncated |
true, wenn ein Teil des Zustands verworfen wurde, um in den Kontext des Modells zu passen |
done_reason |
"decide", "load" oder "unload" |
created_at |
Wann die Antwort erzeugt wurde |
total_duration |
Nanosekunden vom Empfang der Anfrage bis zur Antwort, Warteschlange inbegriffen |
load_duration |
Nanosekunden, die darauf gewartet wurde, dass das Modell lädt; 0, wenn es warm war |
eval_duration |
Nanosekunden im Runner: Tokenisierung, Vorwärtsdurchlauf, Kalibrierung |
Mit "extras": ["laya"] hat jede Antwort außerdem ein laya-Objekt: confidence (layas entropiebasierte Konfidenz) und act_probability (aus dem Act-Head des Modells oder null).
Bilder
Ein Vision-Modell (decider:2b-vision oder winnow:e4b-vision) beantwortet Fragen zu einem Bild ebenso wie zum Zustand. Sende das Bild in images, base64-kodiert, so wie Ollamas images funktioniert:
curl http://localhost:11435/api/decide -d '{
"model": "decider:2b-vision",
"state": "A photo from the warehouse camera.",
"images": ["'"$(base64 -w0 shelf.png)"'"],
"questions": {
"blocked": {"type": "noul", "instructions": "Is the aisle blocked?"},
"fill": {"type": "score", "instructions": "How full is the shelf?", "criteria": ["empty", "half full", "full"]}
}
}'
- Decider: Ein Bild pro Anfrage, nur PNG. Die Vorverarbeitung des Modells wird Wert für Wert reproduziert, die Pixel müssen also zu dem passen, was die Autoren des Modells dekodieren. Rusts JPEG-Decoder weichen bei manchen Pixeln um bis zu 4 Stufen von libjpeg-turbo ab, JPEG wird deshalb noch nicht akzeptiert: Konvertiere es zuerst nach PNG.
- Decider: Das Bild wird auf Vielfache von 32 Pixeln skaliert, wie das Modell es erwartet, und kann danach höchstens 4.096 Patches von 16x16 Pixeln haben, etwa ein Megapixel (1024x1024). Ein größeres Bild erhält eine 422, die das sagt; skaliere es zuerst herunter.
- Decider: Fragen haben höchstens 10 Optionen. Dasselbe Modell beantwortet auch reine Textanfragen.
- Winnow E4B vision: bis zu 16 geordnete PNGs, 2–64 Optionen pro Frage, im kombinierten Bild-/Zustands-/Fragekontext. Der passende Projektor wird separat aus derselben Autoren-Revision heruntergeladen. Bestehende Winnow-Text-Tags laden ihn nicht.
- Ein Modell, das keine Bilder liest, beantwortet eine Anfrage mit
imagesmit einer 422.
/v1/systemone und /v1/decisions bleiben identisch mit der API von TypeSafe, die kein Bildfeld hat.
Laden und Entladen. Eine Anfrage ohne state und questions entscheidet nie. Ohne keep_alive oder mit einem positiven oder negativen lädt sie das Modell (bei einem Router jedes Ziel) und gibt done_reason: "load" zurück. Mit keep_alive: 0 entlädt sie es ("unload"). ollaya run lädt auf diese Weise vor, und ollaya stop entlädt.
curl http://localhost:11435/api/decide -d '{"model": "laya:en", "keep_alive": -1}'
curl http://localhost:11435/api/decide -d '{"model": "laya:en", "keep_alive": 0}'
Eine Entscheidung hat keine Nebenwirkung auf gespeicherte Daten, ein erneuter Versuch ist also sicher.
Presets
Ein Preset ist ein benannter Fragensatz. Sechs sind eingebaut (triage, email, guard, moderation, router, agent), und du kannst eigene speichern. Sende "preset": "NAME" an /api/decide anstelle von questions.
curl http://localhost:11435/api/presets/create -d '{
"name": "billing-check",
"description": "Billing, and how upset the customer is",
"questions": {
"billing": {"type": "noul", "instructions": "The message is about a charge, an invoice or a refund."},
"tone": {"type": "choice", "instructions": "How does the customer sound?", "criteria": {"calm": null, "annoyed": null, "angry": null}}
}
}'
curl http://localhost:11435/api/decide -d '{"model": "winnow:e4b", "state": "I was charged twice this month.", "preset": "billing-check"}'
| Endpunkt | Körper | Wirkung |
|---|---|---|
GET /api/presets |
– | Eingebaute Presets, dann eigene: name, builtin, description, Frage-IDs, modified_at |
POST /api/presets/create |
name, questions, description (optional) |
Speichert ein eigenes Preset und ersetzt eines mit demselben Namen |
POST /api/presets/show |
name |
Ein Preset mit seinen Fragen |
DELETE /api/presets/delete |
name |
Löscht ein eigenes Preset |
Namen bestehen aus 1 bis 64 Zeichen aus Kleinbuchstaben, Ziffern, - und _. Ein eingebauter Name kann nicht wiederverwendet (422) oder gelöscht (403) werden, und ein unbekannter Name ist ein 404. Eigene Presets werden neben den Modellen gespeichert, sodass jeder Client des Servers dieselben sieht.
Router
Ein Router wie laya (laya:latest) hat keine Gewichte: Für jede Anfrage wählt er eines seiner Ziele, das dann antwortet. laya liest nur den state:
| Zustand | route |
Beantwortet von |
|---|---|---|
| Englisch | english |
laya:en |
| Überwiegend nicht-lateinische Schrift (Arabisch, Kyrillisch, CJK, …) | multilingual |
laya:multilingual |
| Lateinische Schrift, aber nicht Englisch (Türkisch, Deutsch, …) | multilingual |
laya:multilingual |
| Überhaupt keine Buchstaben | english (der Standard) |
laya:en |
Kurzer Text in Großbuchstaben ohne akzentuierte Buchstaben, etwa Händlernamen auf einem Kreditkartenauszug (MIGROS KADIKOY ISTANBUL TR), SKUs oder Benutzernamen, lässt sich meist nicht identifizieren und geht an laya:en. Wenn du die Sprache kennst, fordere laya:multilingual oder laya:en direkt an; das model der Antwort sagt, welcher Checkpoint geantwortet hat.
Routing kostet Mikrosekunden. Verzweige nach route, niemals nach reason, dessen Formulierung sich ändern kann. laya:typed-decisions wird vom Router nie gewählt; fordere es direkt an.
Lokale Modelle auflisten
GET /api/tags
Die Modelle auf diesem Rechner, neueste zuerst. Jeder Eintrag hat name, model (denselben), modified_at, size in Bytes, digest (sha256 des Manifests, reines Hex) und details: parent_model, format (onnx, gguf oder router), family, families, parameter_size und quantization_level (die mitgeführten Genauigkeiten wie F16/F32 oder die Quantisierung eines GGUF-Modells wie Q8_0).
{
"models": [
{
"name": "laya:en",
"model": "laya:en",
"modified_at": "2026-09-24T08:11:02.117Z",
"size": 853634822,
"digest": "bf30e4654e9483ff1e6a4fe6fb21b8a71baff6c8a01013046e7d13339020efd7",
"details": {
"parent_model": "",
"format": "onnx",
"family": "laya",
"families": ["laya"],
"parameter_size": "421M",
"quantization_level": "F16/F32"
}
}
]
}
Modelldetails anzeigen
POST /api/show
curl http://localhost:11435/api/show -d '{"model": "laya:en"}'
| Feld | Bedeutung |
|---|---|
license |
Lizenztext |
modelfile |
Ein Modelfile, das das Modell neu erstellt |
parameters |
Am Modell gesetzte Parameter, ein name value pro Zeile, etwa precision fp32 |
questions |
Eingebaute Fragen oder null |
router |
Bei einem Router: strategy, default und routes (Route → Modell). Sonst null. |
details |
Wie in /api/tags |
model_info |
general.architecture, general.languages, general.source (das festgeschriebene Hugging-Face-Repository) plus familienspezifische Schlüssel wie laya.context_length. general.languages listet die Sprachen, für die das Modell trainiert und evaluiert wurde (multilingual für viele); ein Modell auf einer mehrsprachigen Basis kann trotzdem andere Sprachen lesen, miss das also an deinen eigenen Daten. |
capabilities |
Fragetypen, die es beantwortet (choice, score, noul), plus act, wenn es einen Act-Head hat |
modified_at |
Wie in /api/tags |
Ein Router wird als er selbst angezeigt, nicht zu einem Ziel aufgelöst.
Laufende Modelle auflisten
GET /api/ps
Die geladenen Modelle, nach Namen sortiert. Router erscheinen nie; ihre geladenen Ziele schon. Jeder Eintrag hat name, model, size (Speicher, RAM plus VRAM), digest, details (mit der tatsächlich geladenen Genauigkeit: F16 oder F32 oder die Quantisierung eines GGUF-Modells), expires_at (wann es entladen wird, oder null, wenn geladen gehalten), size_vram, context_length und device (cpu, cuda:0, metal, …).
Ein Modell herunterladen
POST /api/pull
{"model": "laya:en"}
Lädt das Modell in den lokalen Speicher herunter und prüft jeden Blob gegen seinen sha256. Ein Router lädt beim Pull auch jedes Modell herunter, an das er weiterleitet. Nur die Ebenen, die dieser Rechner braucht, werden heruntergeladen, zwischen Modellen geteilte Blobs werden einmal heruntergeladen, und unterbrochene Downloads werden fortgesetzt.
Die Antwort streamt den Fortschritt, mit Ollamas Status-Strings:
{"status":"pulling manifest"}
{"status":"pulling 891102d37268","digest":"sha256:891102d372688fc2a094dac56a384bc537b87c63f21f9f3dac0be2b7cbc8d86c","total":842609210,"completed":420557117}
{"status":"pulling 891102d37268","digest":"sha256:891102d372688fc2a094dac56a384bc537b87c63f21f9f3dac0be2b7cbc8d86c","total":842609210,"completed":842609210}
{"status":"verifying sha256 digest"}
{"status":"writing manifest"}
{"status":"success"}
Ein Modell erscheint erst nach writing manifest in /api/tags. Bei einem Router gibt es ein success, ganz am Ende. Ein Name, der sich nicht parsen lässt, ein Modell, das nicht in der Registry ist, und eine nicht erreichbare Registry sind gewöhnliche HTTP-Fehler (422, 404, 502), bevor der Stream beginnt, curl --fail funktioniert also. Mit "stream": false ist die Antwort nach Abschluss {"status": "success"}. Ein zweiter Pull desselben Namens schließt sich dem laufenden an. Ein erneuter Versuch ist sicher.
Ein Modell löschen
DELETE /api/delete
{"model": "triage"}
Entfernt den Namen und die Blobs, die kein anderes Modell verwendet. Ein geladenes Modell wird entladen, sobald seine Anfragen beendet sind; das Löschen eines Routers behält seine Ziele. Die Antwort ist 200 mit leerem Körper und 404 MODEL_NOT_FOUND, wenn der Name nicht existiert; behandle das nach einem Timeout als Erfolg.
Ein Modell kopieren
POST /api/copy
{"source": "laya:en", "destination": "my-guardrail"}
Kopiert ein Modell unter einen neuen Namen und überschreibt ein vorhandenes Ziel. Die Antwort ist 200 mit leerem Körper.
Ein Modell erstellen
POST /api/create
Die API hinter ollaya create -f Modelfile: Die CLI liest das Modelfile und die darin genannten Dateien und sendet ihren Inhalt als JSON.
| Feld | Typ | Erforderlich | Hinweise |
|---|---|---|---|
model |
string | ja | Name zum Erstellen |
from |
string | ja | Ein lokales Modell, möglicherweise ein Router. Es wird nie heruntergeladen. |
questions |
object | nein | Eingebaute Fragen, validiert wie eine Entscheidungsanfrage |
calibration |
object | nein | temperature: bis zu 3 Zahlen (choice, score, noul). temperature_by_options: "<type>:<2|3-5|6-10|11+>" → Zahl. |
parameters |
object | nein | precision: "fp16" oder "fp32", um einen Graphen festzulegen |
license |
string or array | nein | Lizenztext(e) |
description |
string | nein | Eine Zeile, angezeigt von /v1/models und ollaya show |
stream |
boolean | nein | Standard true |
curl http://localhost:11435/api/create -d '{
"model": "triage",
"from": "laya:en",
"questions": {
"department": {
"type": "choice",
"instructions": "Which team should handle this ticket?",
"criteria": ["billing", "technical", "account"]
}
},
"parameters": {"precision": "fp32"},
"description": "Support ticket triage"
}'
Der Stream meldet using existing layer sha256:… für jede geerbte Ebene, creating new layer sha256:… für jede neue, dann writing manifest und success. Ebenen sind inhaltsadressiert, ein wiederholtes Create ergibt also dasselbe Modell.
Version
GET /api/version
{"version": "0.1.0"}
TypeSafe-kompatible Endpunkte
| Endpunkt | Beschreibung |
|---|---|
POST /v1/systemone |
Anfrage: model, state (erforderlich) und questions. Antwort: genau model, answers und usage. |
POST /v1/decisions |
Alias von /v1/systemone |
GET /v1/models |
Die lokalen Modelle als {"models": [{"name", "description", "release_date"}]} |
/v1/* ignoriert native Felder wie keep_alive und extras und fügt seinen Antworten nie native Felder hinzu. Fehler verwenden denselben Körper wie /api/*, den das TypeSafe-SDK korrekt liest. Siehe TypeSafe-Kompatibilität.
Sicherheit
Der Server bindet an 127.0.0.1:11435 und vertraut wie Ollama lokalen Aufrufern. Bindest du ihn an eine andere Adresse (OLLAYA_HOST=0.0.0.0), kann jeder, der den Port erreicht, Entscheidungen ausführen sowie Modelle herunterladen, löschen und erstellen, also:
OLLAYA_API_KEYlässt jede Anfrage außerGET /,HEAD /und CORS-PreflightAuthorization: Bearer <key>erfordern; andernfalls lautet die Antwort401 UNAUTHORIZED. Das TypeSafe-SDK sendet seinen Schlüssel so, und dieollaya-CLI sendet$OLLAYA_API_KEY. Der Server protokolliert eine Warnung, wenn er über Loopback hinaus ohne Schlüssel lauscht.- TLS wird nicht vom Server terminiert; stelle für den Fernzugriff einen Reverse-Proxy davor.
- Browser. Anfragen mit einem
Origin-Header sind nur vonlocalhost,127.0.0.1,0.0.0.0und[::1](beliebiger Port), App- und Editor-Webviews und den Ursprüngen inOLLAYA_ORIGINS(durch Kommas getrennt,*-Platzhalter) erlaubt. Ein Loopback-Server lehnt außerdem unerwarteteHost-Header ab, was DNS-Rebinding blockiert. - Deine Daten. Zustände und Fragen werden nie protokolliert und nie in Fehlern zurückgegeben.
| Variable | Standard | Wirkung |
|---|---|---|
OLLAYA_HOST |
127.0.0.1:11435 |
Bind-Adresse; das Ziel des Clients. Eine Loopback-Adresse lauscht auch auf [::1], sodass Windows-Programme einen Server in WSL unter localhost ohne Verzögerung erreichen |
OLLAYA_API_KEY |
nicht gesetzt | Erfordert Authorization: Bearer <key> |
OLLAYA_ORIGINS |
nicht gesetzt | Zusätzliche erlaubte Browser-Ursprünge |
OLLAYA_KEEP_ALIVE |
5m |
Standard-keep_alive |
OLLAYA_MAX_LOADED_MODELS |
3 |
Limit geladener Modelle |
OLLAYA_MAX_QUEUE |
512 |
Anfragen in Bearbeitung, bevor 503 QUEUE_FULL kommt |
OLLAYA_LOAD_TIMEOUT |
5m |
Ladefrist, bevor 500 MODEL_LOAD_FAILED kommt |
OLLAYA_DEVICE |
auto |
auto, cpu, cuda oder cuda:<n> |
OLLAYA_MODELS |
~/.ollaya/models |
Modellablage |
OLLAYA_REGISTRY |
ollaya.dev |
Standard-Registry-Host in Namen |