Dokumentation

HTTP-API

laya-serve stellt Laya über das TypeSafe-Jev-Wire-Protokoll /v1/systemone bereit. Ein gegen Jev geschriebener Client – hs-jev, typesafe-sdk oder dein eigener – kann seine Basis-URL auf diesen Server richten und weiterarbeiten: Die Ausgabe von predict() aus Laya ist bereits schema-kompatibel, und der Server fügt nur die HTTP-Oberfläche hinzu: eine Entscheidungsroute, eine Health-Sonde, eine optionale Bearer-Prüfung und Anfragegrenzen.

Ein Client, der sich an laya-serve statt an die Jev-API richtet, existiert für PHP: marcreichel/laya-php ist ein Composer-SDK (PHP 8.4+), das eine Klasse von Enums und Attributen auf Fragen abbildet und eine Instanz zurückgibt, GET /health für eine Deploy-Prüfung liest und ein Test-Fake mitliefert, damit Aufrufer ohne laufenden Server unit-testen können.

pip install "laya[serve]"
laya-serve            # http://0.0.0.0:8000

Derselbe Einstiegspunkt läuft eingebettet in jedem ASGI-Server: laya.serve.create_app() baut die FastAPI-App, optional mit einem Router, den du injizierst (create_app(router)), statt einem aus der Umgebung gebauten.

Konfiguration

Alles sind Umgebungsvariablen, sodass ein Image sowohl einen Entwicklungsstart auf dem Laptop als auch eine systemd-Unit bedient.

Umgebungsvariable Bedeutung Standard
LAYA_HOST Bind-Adresse 0.0.0.0
LAYA_PORT Bind-Port 8000
LAYA_ROOT_PATH öffentlicher URL-Präfix, wenn hinter einem Reverse-Proxy bereitgestellt leer
LAYA_DEVICE torch-Gerät für jeden Checkpoint auto
LAYA_PRELOAD baut die Checkpoints beim Start, nicht lazy 1
LAYA_MODELS Kommaliste zum Vorladen (english,multilingual,typed-decisions); leer = alle alle
LAYA_THREADS begrenzt die Intra-Op-Threads von torch auf der CPU; halte es <= den physischen Kernen – die logischen Kerne zu überbuchen ist eine große Regression torch-Standard
LAYA_AUTO_TASK automatisch zum Checkpoint typed-decisions routen 0
LAYA_IDLE_UNLOAD_SECONDS entlädt residente Checkpoints nach so vielen Idle-Sekunden; die nächste Anfrage lädt ihren Checkpoint erneut. Null deaktiviert das Entladen 0
LAYA_DEFAULT_MODEL Checkpoint, auf den ein Zustand ohne Sprachhinweis zurückfällt; Aliase wie ml werden so aufgelöst, wie core sie auflöst, und ein nicht auflösbarer Name stoppt den Server beim Start english
LAYA_API_KEY wenn gesetzt, erfordert Authorization: Bearer <key> keiner
LAYA_LOG_LEVEL uvicorn-Loglevel info
LAYA_MAX_CONCURRENT gleichzeitig nach der Authentifizierung zugelassene Anfragen; Überschuss bekommt 503 16
LAYA_MAX_BATCH_TOKENS Token, die ein FORWARD PASS eines /v1/systemone/batch zusammenfassen darf (states x Fragen x Zeilenbreite); ein größerer Batch wird auf mehrere Pässe aufgeteilt, nicht abgelehnt 131072
LAYA_JEV_STRICT liefert den strikten Jev-Wire-Vertrag: kein Root-routing, kein action / answer_confidence pro Antwort, kein confidence bei noul-Antworten und usage reduziert auf input_tokens + output_tokens. Für Clients, die die Antwort gegen den Jev-Vertrag ohne Zusatzfelder validieren 0

Für ein Deployment, das unter einem Präfix wie /laya veröffentlicht wird, setze LAYA_ROOT_PATH=/laya. FastAPI nutzt es beim Generieren der OpenAPI- und Swagger-UI-URLs. Konfiguriere den Reverse-Proxy so, dass er /laya entfernt, bevor er Anfragen an Laya weiterleitet; die Routen der App bleiben intern /health und /v1/systemone.

Für Container, einschließlich CUDA- und ARM64-Images, siehe Docker-Schnellstart.

Für stoßweisen lokalen Einsatz setze LAYA_IDLE_UNLOAD_SECONDS=300. Inferenz und Entladen laufen auf demselben Worker, und das Idle-Fenster beginnt erneut, wenn ein einzelner oder Batch-Forward-Pass endet, auch bei fehlgeschlagenen Anfragen. Die nächste Vorhersage zahlt einen Cold Load. Das Entladen gibt Modellreferenzen und Gerätecaches frei, einschließlich Metal; der Prozess-Allocator kann RAM-Seiten behalten, sodass der Prozess-RSS nicht um die Größe des Checkpoints fallen muss.

Endpunkte

GET /health

Immer offen (keine Authentifizierung), und bleibt während der Inferenz ansprechbar, weil der CPU-gebundene Forward-Pass in seinem eigenen Worker läuft, nicht in der Event-Loop. Die Felder unterhalb von liveness sind auf einem Deployment, das LAYA_API_KEY gesetzt hat, nicht offen: ohne den Bearer antwortet /health nur mit {"status": "ok"} und sonst nichts, weil der Rest residente Checkpoints, ihre genauen Revisions-SHAs, den Gerätezustand und den letzten Fallback-Grund jedes Checkpoints nennt, was Host-Hardware zitiert. Eine Sonde braucht nur die 200, also ist ein Healthcheck nicht betroffen und ein falscher Bearer ist weiterhin eine 200 statt einer 401. Ohne gesetztes LAYA_API_KEY bekommt jeder Aufrufer die hier gezeigte vollständige Nutzlast.

{"status": "ok", "loaded": ["english", "multilingual"], "revisions": {"english": "...", "multilingual": "..."},
 "device": "cuda", "device_is_preference": false,
 "checkpoint_devices": {"english": "cuda", "multilingual": "cuda"},
 "cpu_fallbacks": {"english": {"count": 0, "last_reason": null}, "multilingual": {"count": 0, "last_reason": null}}}

Die Antwort eines Servers, sodass die Blöcke zueinander passen: jeder Schlüssel von revisions, checkpoint_devices und cpu_fallbacks ist ein Name in loaded. tests/test_serve.py hält dieses Beispiel Feld für Feld gegen den Handler, der es erzeugt.

Bei aktiviertem Idle-Entladen enthalten authentifizierte Health-Antworten außerdem idle_unload_seconds (das konfigurierte Fenster) und idle_seconds (Zeit seit der letzten Inferenzanfrage oder deren Abschluss). Health-Sonden setzen diese Uhr nicht zurück. Eine leere loaded-Liste ist nach einem Idle-Entladen normal.

  • status ist ok, wann immer der Prozess überhaupt antwortet. Über die Checkpoints sagt es nichts.
  • loaded listet die im Speicher residenten Checkpoints. Es ist leer, bis eine Anfrage einen baut, was LAYA_PRELOAD=0 den Prozess tun lässt.
  • revisions ist die Artefakt-Revision, aus der jeder residente Checkpoint geladen wurde, mit denselben Namen wie loaded als Schlüssel, sodass ein Deployment bestätigen kann, was es tatsächlich ausliefert.
  • device ist das Gerät, auf dem ein residenter Checkpoint wirklich rechnet, was nicht immer das ist, was LAYA_DEVICE verlangt hat: ein Checkpoint, der eine GPU will, die er nicht bekommen kann, fällt still auf CPU zurück und antwortet trotzdem korrekt. Ohne residenten Checkpoint ist es stattdessen die konfigurierte Präferenz.
  • device_is_preference ist true genau solange nichts resident ist, und false, sobald der Handler messen kann. Das ist der Unterschied zwischen einem Server, der seine Konfiguration meldet, und einem, der meldet, wo seine Arbeit stattfindet: einer, der seine GPU still verloren hat, sagt false mit device cpu, statt weiter cuda zu antworten.
  • checkpoint_devices gibt die Messung pro Checkpoint, mit den Namen in loaded als Schlüssel; device ist der erste dieser Werte.
  • cpu_fallbacks zählt pro residentem Checkpoint die Anfragen, die den GPU-Speicher erschöpft haben und einmal auf CPU wiederholt wurden: count seit Prozessstart und last_reason mit dem Fehlertext der letzten. Die Herabstufung ist auf die fehlgeschlagene Anfrage beschränkt, sodass ein Checkpoint, der auf CPU gebaut wurde, weil die GPU nie verfügbar war, kein Fallback ist und hier 0 zählt – das zeigt sich in device.

POST /v1/systemone

Eine Anfrage trägt einen state und beliebig viele Fragen darüber:

curl -s localhost:8000/v1/systemone -H 'content-type: application/json' -d '{
  "state": "I was charged twice this month, I want my money back",
  "questions": {
    "queue":   {"type": "choice", "instructions": "Which team?",
                "criteria": {"billing": "billing and refunds", "tech": "login and app issues",
                             "other": "everything else"}},
    "urgency": {"type": "score",  "instructions": "How urgent?",
                "criteria": ["calm", "firm", "angry", "furious"]}
  }
}'
Feld erforderlich Bedeutung
state ja Text, E-Mail, Ticket oder JSON-Dokument, über das entschieden wird; ein fehlender oder auf null gesetzter Zustand ist ein 400
questions ja Objekt mit Frage-IDs als Schlüssel; jede Frage ist choice / score / noul mit instructions und criteria
model nein benennt einen Checkpoint; ein Pfad oder eine nicht veröffentlichte Hub-ID ist ein 422, alles andere wird ignoriert (siehe unten)
task nein erzwingt einen Checkpoint über den Workflow-Namen, statt das Routing entscheiden zu lassen; ein unbekannter Name ist ein 422, der ihn benennt
lang nein ein Sprachcode (de, en-US), der die Erkennung überspringt, wenn er eine Sprache benennt; ein leerer oder nicht erkannter Code fällt auf die Erkennung zurück
lang_guess nein ein Sprachcode aus dem eigenen Identifikator des Clients, der nach lang und vor der Erkennung konsultiert wird; jeder nicht englische Code routet zum mehrsprachigen Checkpoint
max_len nein gesamtes Token-Fenster für diese Anfrage, begrenzt durch LAYA_MAX_TOKEN_BUDGET
head_max_len nein Token-Fenster, das sich der Options-Prompt teilt, dieselbe Obergrenze; siehe Das Token-Budget erweitern, wann eine Frage es braucht
min_confidence nein Enthaltungsschwellenwert in [0.0, 1.0]; eine Antwort, deren answer_confidence darunter fällt, kommt als low_confidence markiert zurück, und die Antwort selbst bleibt erhalten

model, task, lang, lang_guess, max_len, head_max_len und min_confidence sind die Argumente, die Router.predict annimmt und die ein JSON-Body angeben kann; jedes wird nur weitergeleitet, wenn die Anfrage es sendet, sodass ein fehlendes die eigene Router(...)-Einstellung des Deployments zuständig lässt. Die fünf Hook-Argumente, die predict ebenfalls annimmt – hooks, on_predict_start, on_predict_end, hooks_raise, hooks_timeout – werden mit einem 422 abgelehnt statt verworfen: Ein Hook ist ein Callable, das innerhalb des Serverprozesses läuft, und die letzten beiden sagen, wie die Hooks ausgeführt werden, die ein Deployment installiert hat, sodass kein Wert, den ein Aufrufer sendet, hier eine Bedeutung hat. Dieselben fünf werden clientseitig von einem LangChain-Knoten mit einer base_url (laya.integrations.langchain) abgelehnt, sodass eine Kette und ein roher HTTP-Client jetzt dieselbe Antwort bekommen.

model wird akzeptiert, damit ein Jev-Client weiterhin eines senden kann. Die öffentlichen Hugging-Face-IDs (convaiinnovations/laya-multilingual, convaiinnovations/laya-typed-decisions), die Checkpoint-Namen (english, multilingual, typed-decisions) und ihre Aliase wählen einen Checkpoint. convaiinnovations/laya und jeder andere Wert, der kein Pfad und keine Hub-Repo-ID ist – einschließlich einer Jev-ID wie jev-1 – bedeutet „lass den Router wählen”, und der routing-Block der Antwort zeichnet auf, was gewählt wurde und warum. Ein Wert, der wie ein Dateisystempfad oder eine nicht veröffentlichte Hub-ID aussieht (/path/to/checkpoint, org/repo, ~/ckpt, .\ckpt), ist ein 422 auf sowohl /v1/systemone als auch /v1/systemone/batch: Dieser Server kann ihn nicht laden, und mit einem anderen Checkpoint zu antworten würde das verbergen. Das detail ist derselbe unknown model-Text, den core auslöst, plus der Hinweis, model wegzulassen, damit der Router wählt.

Antwort

{
  "model": "laya-rl-agent",
  "answers": {
    "queue": {"type": "choice", "choice": "billing",
              "probabilities": {"billing": 0.9519, "tech": 0.0327, "other": 0.0154},
              "confidence": 0.797, "answer_confidence": 0.9519,
              "action": {"act_probability": 1.0}},
    "urgency": {"type": "score", "score": 1.6994,
                "legend": {"0": "calm", "1": "firm", "2": "angry", "3": "furious"},
                "probabilities": {"0": 0.0249, "1": 0.4136, "2": 0.3985, "3": 0.1629},
                "confidence": 0.1925, "answer_confidence": 0.4136,
                "action": {"act_probability": 1.0}}
  },
  "usage": {"input_tokens": 83, "output_tokens": 0, "state_tokens": 12,
            "state_tokens_dropped": 0, "truncated": false, "truncated_questions": []},
  "routing": {"model": "english", "repo": "convaiinnovations/laya", "reason": "English Latin text",
              "detection": {"script": "latin", "script_profile": {"latin": 1.0}, "language": "en",
                            "is_english": true, "language_undecided": false, "diacritic_rate": 0.0,
                            "non_latin_fraction": 0.0, "mixed_segment": null},
              "workflow": null}
}

Das Beispiel ist eine Antwort, die dieser Server gegeben hat, wörtlich: die obige Anfrage, der zwischengespeicherte english-Checkpoint auf CPU. answers und usage sind die Schlüssel, die Jev-Clients dekodieren; model ist der konstante Name des Entscheidungskopfs, und der Checkpoint, der geantwortet hat, steht in routing.

Antworttyp Schlüssel
choice choice (die Argmax-Option), probabilities pro Option
score score (erwarteter Stufenindex, kann zwischen Stufen fallen), probabilities mit Schlüsseln "0".. "k-1", legend, das Index auf den Stufentext abbildet
noul noul, die Wahrscheinlichkeit der Ja-Option
alle confidence, answer_confidence und action.act_probability
gate abstention, abstention_threshold und low_confidence, geschrieben vom Enthaltungs-Gate – siehe unten

Die Gate-Zeile ist der Enthaltungsbericht (#361) und die einzige Möglichkeit, wie ein Aufrufer sehen kann, dass das Gate, für das er bezahlt hat, gelaufen ist. Eine Anfrage, die min_confidence setzt, bekommt ihn; eine, die das nicht tut, bekommt keinen der drei Schlüssel. abstention ist einer von drei Zuständen und wird auf jede Antwort einer gegateten Anfrage geschrieben: passed (seine Konfidenz hat den Schwellenwert überschritten), abstained (sie fiel darunter, und low_confidence ist true genau auf diesen Antworten) oder unevaluated (die Antwort trug keine brauchbare Konfidenz, sodass das Gate nicht entscheiden konnte – das als Bestehen zu melden wäre dieselbe Lüge wie das Melden als Flag). abstention_threshold gibt den Schwellenwert zurück, gegen den diese Zustände gemessen wurden, was einen Batch-Lauf mit Schwellenwerten pro Klasse nachträglich wieder aufteilbar macht. Ohne gesetztes min_confidence erscheint keiner der drei Schlüssel auf irgendeiner Antwort: die Abwesenheit ist der Bericht, kein vierter Zustand, und so unterscheidet ein Aufrufer einen ungegateten Lauf von einem bestandenen Gate. min_confidence von genau 0.0 wurde gesetzt, also werden Zustände gemeldet, und nichts kann darunter fallen, also liest jede Antwort passed – die zurückgegebene 0.0 ist das, was das von einem Bestehen bei einem echten Schwellenwert unterscheidet. Die Antwort selbst bleibt in jedem Zustand erhalten; das Gate markiert, es verwirft nicht.

usage berichtet, woraus der Forward-Pass gebaut wurde. Wie viel eines Zustands das Modell liest, ist ein Token-Budget, keine Zeichenzahl, und das Budget bewegt sich mit max_len, head_max_len und dem Options-Prompt jeder Frage selbst (#174), sodass diese Schlüssel der einzige Ort sind, an dem diese Tatsache sichtbar ist:

usage-Schlüssel Bedeutung
input_tokens Nicht-Pad-Token der Zeilen des Zustands – eine Zeile pro Frage, sodass es mit den Fragen wächst, statt eine Kontextlänge zu sein
output_tokens immer 0 – der Kopf antwortet in einem Durchgang, er generiert nichts
state_tokens Token, die der gesamte serialisierte Zustand braucht
state_tokens_dropped Token davon, die mindestens eine Frage nicht bekam: der schlimmste Fall über die Fragen, da jede dem Zustand einen anderen Raum lässt
truncated true, wenn dieser schlimmste Fall etwas verworfen hat
truncated_questions die IDs der Fragen, deren eigenes Fenster beschnitten wurde, [] wenn keine
options nur vorhanden, wenn einige Fragenoptionen nicht mehr je eine Token-Spanne haben: mit der Frage-ID als Schlüssel, mit total (den Optionen, die diese Frage definiert), distinct (den Spannen, die die Sequenz erreicht haben) und tokens_per_option

Eine abgeschnittene Antwort ist immer noch eine Antwort – der Kopf entscheidet über die ihm gegebene Evidenz –, aber ein Aufrufer, der Zustände nach Zeichenzahl bemisst, kann den Schnitt nirgendwo sonst in der Antwort sehen.

routing zeichnet auf, welcher Checkpoint geantwortet hat und warum:

routing-Schlüssel Bedeutung
model der Checkpoint, der geantwortet hat: english, multilingual oder typed-decisions
repo seine öffentliche Hugging-Face-ID
reason der Satz für die Wahl, der die Evidenz nennt, auf die er gehandelt hat
detection laya.lang.analyse() auf dem Zustand – script, script_profile, language, is_english, language_undecided, diacritic_rate, non_latin_fraction, mixed_segment – oder null, wenn die Route entschieden hat, bevor sie den Text las
workflow der typed-decisions-Workflow, zu dem die Frage-IDs passen, oder null

detection ist null auf jedem Pfad, der entscheidet, ohne den Zustand zu lesen: einer, der durch model oder task erzwungen wurde, einer, der durch lang oder lang_guess beantwortet wurde, oder einer, der aus den Frage-IDs einem typed-decisions-Workflow entsprach. Ein lang_guess hinterlässt keinen eigenen Schlüssel – der Hinweis, auf den er gehandelt hat, ist in reason genannt. Die Zweige model und task melden workflow ebenfalls als null, weil sie antworten, bevor die Frage-IDs gelesen werden.

Konfidenz: zwei Zahlen, nicht austauschbar

  • answer_confidence ist die Wahrscheinlichkeitsmasse auf der gemeldeten Antwort (max(p)). Es ist die Größe, die das Temperatur-Scaling anpasst, und diejenige, auf der die ECE-Zahlen dieses Repos berechnet werden, sodass sie die Gating-Eigenschaft trägt, auf die sich die Seite Benchmarks und bekannte Grenzen stützt – aber nur für einen Checkpoint, dessen Temperatur-Anpassung an deinem Verkehr validiert wurde.
  • confidence bedeutet je Typ etwas anderes: normalisierte Entropie 1 - H(p)/log(k) bei choice und score und max(p_yes, p_no) bei noul (wo sie gleich answer_confidence ist).

Vergleiche die beiden niemals gegen einen einzigen Schwellenwert. Beachte auch den Unterschied beim Portieren von Jev: TypeSafe definiert Konfidenz als (n*p_max - 1)/(n - 1), sodass ein aus einem Jev-Deployment übernommener Schwellenwert bei Layas Entropiewert anders gatet.

Strikter Jev-Vertrag: LAYA_JEV_STRICT

Die obige Nutzlast ist die vollständige Laya-Nutzlast. Der Jev-Vertrag, an den ein Client sie halten mag, definiert weniger: drei Top-Level-Felder (model, answers, usage), die vertraglichen Schlüssel auf jeder Antwort und sonst nichts, und ein usage aus den zwei Token-Zahlen. Ein Client, der die Antwort gegen diesen Vertrag ohne Zusatzfelder validiert – OpenClaws TypeSafe-Provider-Plugin ist einer – lehnt die vollständige Nutzlast ab, also projiziert LAYA_JEV_STRICT=1 die Antwort vor dem Antworten auf den Vertrag, auf sowohl /v1/systemone als auch /v1/systemone/batch:

  • der Root behält nur model, answers und usage; routing wird nicht gesendet;
  • eine choice-Antwort behält choice, probabilities und confidence;
  • eine score-Antwort behält score, probabilities, confidence und legend;
  • eine noul-Antwort behält nur noul;
  • usage behält input_tokens und output_tokens; die Truncation-Fakten und die Obergrenze der zusammengefassten Optionen werden nicht gesendet.

Die Projektion behält nur die vertraglichen Schlüssel und berechnet nichts neu: jeder Wert ist der, den das Ergebnis bereits trägt, sodass die Wahrscheinlichkeiten und Scores, die ein strikter Client liest, identisch mit denen sind, die die vollständige Nutzlast meldet. Der Standard bleibt die vollständige Nutzlast, und ein Deployment, das das Flag einschaltet, verliert die Truncation-Sichtbarkeit, die usage bietet – ein beschnittener Zustand ist dann in den Logs sichtbar, nicht in der Antwort. Score criteria sollte unter dem strikten Vertrag einfache Strings bleiben: ein strikter Client vergleicht das zurückgegebene legend mit den Kriterien, die er gesendet hat, und Laya rendert ein strukturiertes Kriterium mit Pythons JSON, was ein JavaScript-Aufrufer, der seine eigenen Kriterien stringifiziert, nicht Byte für Byte treffen muss.

Erfolgreiche Antworten tragen außerdem Server-Timing: inference;dur=<ms> und X-Inference-Time-Ms.

Grenzen

Anfrage-Leitplanken werden vor der Tokenisierung geprüft, sodass eine übergroße Anfrage den Server nichts außer den gelesenen Bytes kostet. Jede von ihnen ist ein 413; das detail sagt, welches Limit erreicht wurde.

Limit Wert
Anfrage-Body 2 MiB, während des Streamings erzwungen – ein gechunkter oder zu niedrig angegebener Content-Length kann es nicht umgehen
state 50.000 Zeichen des Textes, den das Modell erhält – der String selbst bei einem String-Zustand, json.dumps(state, ensure_ascii=False) bei einem Objekt oder Array
Fragen pro Anfrage 64
states pro Batch-Anfrage 64
Optionen pro choice-Frage 100
Stufen pro score-Frage 32
Optionen über alle Fragen hinweg 512
gleichzeitig zugelassene Anfragen LAYA_MAX_CONCURRENT (16)

/v1/systemone/batch ist anders begrenzt, und nicht durch eine Ablehnung. Es tokenisiert jeden Zustand einmal pro Frage und fasst jede Zeile zu einem einzigen Tensor zusammen, sodass sich die Feldgrenzen multiplizieren: 64 Zustände mit 64 Fragen sind 4096 Zeilen, was jede andere Grenze auf dieser Seite erlaubt. Was eine Zeile kostet, ist ihre Breite, und max_len ist selbst ein Anfragefeld, sodass die Kosten eines Batches states x questions x width sind.

Statt einen großen Batch abzulehnen, teilt der Endpunkt ihn: Wenn dieses Produkt LAYA_MAX_BATCH_TOKENS (standardmäßig 131.072) überschreitet, wählt er eine batch_size, sodass jeder Forward-Pass innerhalb des Budgets bleibt, und Router.predict_batch führt den Batch in mehreren Pässen aus. Jeder Zustand wird weiterhin beantwortet und die Antwort bleibt unverändert. Eine Anfrage, deren Zeilen bereits passen, bekommt überhaupt keine batch_size übergeben, verhält sich also genau wie zuvor – was wichtig ist, weil die Batch-Form Fließkommaergebnisse verschieben kann. Eine batch_size, die der Aufrufer sendet, gewinnt immer: er hat eine Form verlangt.

Standardmäßig gehen 256 Zeilen in einem Pass durch – 64 Zustände mit 4 Fragen oder 8 mit 32. Größere Batches werden geteilt, und ein höheres max_len macht jeden Pass schmaler, statt das 16-Fache an Arbeit zu kosten. Was das nicht begrenzt, ist, wie lange eine Anfrage den Server belegt; das sind LAYA_MAX_CONCURRENT und der einzelne Inferenz-Worker, und das gilt bereits für eine /v1/systemone-Anfrage über einen Zustand mit 50.000 Zeichen.

Die Options-Obergrenzen sind reine HTTP-Schutzmechanismen gegen Amplifikation; das Modell selbst passt Optionstoken in ein Fenster head_max_len=192 ein, sodass eine Frage innerhalb der HTTP-Grenzen trotzdem als 422 abgelehnt werden kann, wenn die Optionstexte zusammen dieses Budget überschreiten. Das Evaluierungs-Harness führt dieselben Anfragen in-process ohne die HTTP-Schicht aus.

Fehler

Status wann detail des Bodys
400 der Body ist kein gültiges JSON, kein Objekt, hat keine questions, state fehlt oder ist null, questions ist kein Objekt, oder ein String irgendwo im Body enthält ein ungepaartes \udXXX-Surrogat-Escape was falsch ist
401 LAYA_API_KEY ist gesetzt und der Bearer-Token fehlt oder ist falsch invalid or missing bearer token
413 irgendein Limit von oben welches Limit und um wie viel
422 die Frage ist wohlgeformtes JSON, aber für Laya ungültig (unbekannter Typ, Optionen über dem Kopf-Budget), oder eine Anfrage-Steuerung (lang, min_confidence, ein Hook-Argument) hat nicht die Form, die dieser Endpunkt akzeptiert benennt die Frage oder das Feld und was zu korrigieren ist
500 die Inferenz ist aus einem anderen Grund fehlgeschlagen inference failed – immer dieser String, damit Pfade, Gewichte und Speicherzustand nie preisgegeben werden; die Ursache steht im Server-Log
503 bereits LAYA_MAX_CONCURRENT Anfragen sind in Bearbeitung server busy, try again later

Der 400 für ungepaarte Surrogate ist der, der ungewöhnlich aussieht. \udXXX ohne Paar ist gültiges JSON, aber das Zeichen, das es nennt, kann nicht UTF-8-kodiert werden, sodass der Tokenizer einen TypeError auslöst – der eigene String des Aufrufers, der als Serverfehler ankommt, mit einem Traceback pro Anfrage. Beide Entscheidungsrouten durchlaufen daher den geparsten Body nach einzelnen Surrogaten und lehnen eines ab, bevor es die Inferenz erreicht. Der Durchlauf läuft nach den Größenprüfungen, sodass ein übergroßer Body weiterhin zuerst abgelehnt wird und die Zeichen- und Fragegrenzen begrenzen, was er erreichen kann. Ein gepaartes Surrogat ist, wenn der Parser fertig ist, ein gewöhnliches Astralzeichen, sodass ein Emoji in einem Zustand nicht betroffen ist.

Über der Obergrenze liegende Last wird abgelehnt, nicht in eine Warteschlange gestellt: Clients, die einen Zulassungsplatz halten, während sie einen langsamen Body streamen, können /health nicht aushungern, und ein erneuter Versuch kann den Platz einnehmen, den ein abgelehnter Client hinterlassen hat.

Nebenläufigkeitsmodell

Die Inferenz ist ein synchroner torch-Aufruf, der auf der CPU Hunderte von Millisekunden bis Sekunden dauert, sodass sie nie in der Event-Loop läuft: Anfragen werden an einen Executor mit einem einzigen Worker übergeben, was einen Forward-Pass auf einmal bedeutet – die Form, die ein einzelner Checkpoint auf einem Gerät will. Die Zulassung (der Semaphor LAYA_MAX_CONCURRENT) wird geprüft, bevor ein Body-Byte gelesen wird, und über die Inferenz hinweg gehalten; das Inferenz-Gate wird erst betreten, nachdem der Body vollständig ist, sodass ein langsamer Client einen Zulassungsplatz hält, aber nie einen Inferenzplatz.

(Noch) nicht vorhanden

Dieser Server spricht bewusst nur ein Protokoll. Es gibt keinen OpenAI-kompatiblen Endpunkt; führe stattdessen mehrere Fragen in einer Anfrage aus, da sie sich einen einzigen Forward-Pass pro Fragenset teilen. Die eine andere Route ist POST /v1/systemone/batch, die ein questions-Set über ein Array von states beantwortet. Sie hat noch keinen Abschnitt auf dieser Seite – ihre Anfrageform steht im Self-Hosting-Abschnitt des README – und jede Prüfung oben gilt für sie wie für POST /v1/systemone: dieselben Form-400s, dieselbe Unpaired-Surrogate-Ablehnung, dieselbe Auth, Admission, Größenlimits, Body-Control-Validierung und 500-Zuordnung. Die laya-CLI und der MCP-Server decken die lokale Nutzung ab – siehe das README.