Dokumentation

Docker-Schnellstart

Führe das SDK aus, ohne Python oder PyTorch auf deinem Host zu installieren. Für den CPU-Schnellstart brauchst du 8 GB RAM und 10 GB freien Speicher, dazu Docker Engine oder Docker Desktop und Compose v2 oder neuer.

Aus dem Repository-Stamm:

docker compose run --build --rm laya

Das baut den Checkout, führt die Beispielanfrage auf der CPU aus und gibt JSON aus, das choice, score und noul abdeckt. Die erste Anfrage lädt den ausgewählten öffentlichen Hugging-Face-Checkpoint herunter; ein Konto ist nicht nötig. Rechne mit mehreren Minuten für den ersten Download. Die Gewichte bleiben in einem benannten Volume. Spätere Läufe nutzen docker compose run --rm laya.

Vorhersagen und Konfidenz brauchen weiterhin eine Evaluierung an deiner Arbeitslast. Sieh dir die Benchmark-Grenzen an.

Für ARM64-Hosts, DGX Spark und Apple Silicon siehe ARM64- und DGX-Spark-Container.

NVIDIA-GPU / CUDA

Installiere einen kompatiblen NVIDIA-Treiber und konfiguriere Docker mit dem NVIDIA Container Toolkit. Das GPU-Image nutzt PyTorch-CUDA-12.8-Wheels. Prüfe die Compute-Capability und den Treiber deiner GPU gegen PyTorchs unterstützte Builds; ältere Karten brauchen möglicherweise einen anderen Build. Plane zusätzlichen Speicherplatz für die CUDA-Schichten ein. Der VRAM-Bedarf hängt vom Checkpoint, der Batch-Größe und der Eingabelänge ab.

docker compose -f compose.yaml -f compose.cuda.yaml run --build --rm laya

Das Override wählt GPU 0 und verwendet standardmäßig LAYA_DEVICE=cuda. Setze LAYA_GPU_ID auf einen anderen Host-Index oder eine UUID. Diese GPU erscheint im Container als Gerät 0. Prüfe den Zugriff, ohne Gewichte herunterzuladen:

docker compose -f compose.yaml -f compose.cuda.yaml run --rm laya python -c \
  'import torch; assert torch.cuda.is_available(); print(torch.cuda.get_device_name(0)); print(torch.ones(1, device="cuda").cpu())'

Das Beispiel lehnt nicht verfügbares CUDA ab, bevor es einen Checkpoint lädt. Laya kann nach einem Speicher- oder Inferenzfehler dennoch auf die CPU zurückfallen, also prüfe seine Warnungen. Baue neu, wenn du zwischen CPU- und CUDA-Konfiguration wechselst.

Das Image setzt TORCH_DISABLE_NATIVE_JIT=1. Andernfalls ersetzt PyTorch 2.14 einige Eager-CUDA-Ops durch Triton-Kernel, die es bei der ersten Inferenz kompiliert, was einen C-Compiler braucht, den das Slim-Image nicht enthält: Der Container meldet sich als gesund und scheitert dann bei jeder Anfrage (#365). Die Standard-Kernel liefern dieselben Antworten bei derselben Latenz. Setze dieselbe Variable auch bei einer Bare-Metal-Installation, wenn predict mit Failed to find C compiler scheitert.

Das nutzt die GPU-Reservierungen von Compose. Windows erfordert das von Docker Desktop unterstützte WSL2-GPU-Setup. Container für Apple MPS, AMD/ROCm und Intel GPU liegen außerhalb dieses Schnellstarts; nutze die CPU, es sei denn, du konfigurierst und validierst ein anderes Backend.

Konfiguration

Setze Compose-Variablen in deiner Shell, in einer lokalen .env-Datei oder im environment-Block des Dienstes. Committe keine Geheimnisse in .env. Laufzeitvariablen funktionieren auch mit docker run -e; reine Compose-Einstellungen sind unten gekennzeichnet.

Variable Standard Zweck
LAYA_DEVICE cpu / cuda Gerät, das von der Basis- / GPU-Konfiguration gewählt wird
LAYA_CUDA_AMP nicht gesetzt (amp_dtype des Checkpoints) fp16/float16 oder bf16/bfloat16 für den CUDA-Forward; alles andere wird ignoriert. Nicht kosmetisch: Der Schwellenwert-Abschnitt des README misst, dass bf16 3 von 864 Argmaxes auf dem Paritätsset umdreht, wo fp16 keines umdreht
LAYA_CPU_AMP nicht gesetzt bf16 oder bfloat16 versetzt den CPU-Forward in bf16; alles andere lässt ihn in fp32. Auch keine fp16-Schreibweise schaltet ihn ein: CPU-Autocast hat keinen fp16-Schnellpfad, der fp32 schlägt, also ist bf16 die einzige reduzierte Präzision, die core auf diesem Gerät bietet
LAYA_MODEL auto Router-Alias: auto, english, multilingual, typed-decisions
LAYA_MODEL_PATH nicht gesetzt Pfad eines kompatiblen Checkpoints im Container
LAYA_REVISION nicht gesetzt Hub-Commit, -Branch oder -Tag für jeden Checkpoint-Download, oder reviewed für die geprüften SHAs in laya/revisions.py; ein revision=-Argument gewinnt weiterhin
LAYA_REQUEST_FILE mitgelieferte Anfrage Pfad der JSON-Anfrage im Container
OMP_NUM_THREADS 4 CPU-Threads; bleib innerhalb der verfügbaren Kerne
HF_TOKEN / HF_TOKEN_FILE nicht gesetzt Optionale Hugging-Face-Zugangsdaten
LAYA_API_KEY / LAYA_API_KEY_FILE nicht gesetzt nur laya-serve: erfordert Authorization: Bearer <key>
LAYA_PORT 8000 nur laya-serve: Container-Port und der dafür veröffentlichte Host-Port
HF_HUB_OFFLINE 0 1 nutzt nur zwischengespeicherte Checkpoints
HF_HOME /home/laya/.cache/huggingface Cache-Pfad; siehe Mount-Anforderung unten
LAYA_CACHE_VOLUME Modell-Cache des Projekts nur Compose: benanntes Cache-Volume
LAYA_GPU_ID 0 nur Compose: NVIDIA-Geräteindex oder UUID
LAYA_TORCH_INDEX cpu / cu128 / cu130 Compose-Build: PyTorch-Wheel-Index
LAYA_TORCH_VERSION 2.14.0 Compose-Build: fixierte PyTorch-Version

Compose reicht die Laufzeitvariablen weiter, außer HF_HOME, das mit seinem festen Cache-Mount abgestimmt bleibt, und außer LAYA_MPS_AMP_MIN_ROWS, dem MPS-Zeilengate, das kein Image hier erreichen kann, weil kein Container hier MPS auswählen kann. Wenn du HF_HOME in docker run oder deiner eigenen Compose-Datei überschreibst, sorge für einen passenden Mount, der für UID 10001 beschreibbar ist. Direkte Docker-Builds wählen PyTorch mit --build-arg TORCH_INDEX=cu128; -e zur Laufzeit kann das installierte Wheel nicht ändern.

LAYA_MODEL=english OMP_NUM_THREADS=2 docker compose run --build --rm laya

docker build -t laya:local .
docker run --rm -e LAYA_MODEL=english -e OMP_NUM_THREADS=2 \
  -v laya-model-cache:/home/laya/.cache/huggingface laya:local

Für deine eigene Anfrage:

docker compose run --rm --volume "$PWD/request.json:/inputs/request.json:ro" \
  --env LAYA_REQUEST_FILE=/inputs/request.json laya

Für eine kommentierte Konfiguration mit Mounts für Anfrage, Checkpoint und Secret-Datei siehe compose.example.yml:

docker compose -f compose.yaml -f compose.example.yml run --build --rm laya

Füge -f compose.cuda.yaml vor run für NVIDIA-GPUs hinzu. Das Beispiel ist ein Override von compose.yaml, sodass Cache- und Image-Einstellungen an einer Stelle bleiben.

Secret-Dateien

HF_TOKEN_FILE liest beim Start eine gemountete UTF-8-Datei, entfernt umgebende Leerzeichen und hat Vorrang vor HF_TOKEN. Unlesbare, leere oder ungültige Dateien stoppen den Start, ohne ihren Inhalt auszugeben. Die Datei muss für UID 10001 lesbar sein. _FILE gilt nur für unterstützte Geheimnisse, nicht für jede Einstellung.

Mit HF_TOKEN_PATH, das auf eine vorhandene Host-Datei außerhalb des Checkouts zeigt:

docker compose run --rm --volume "$HF_TOKEN_PATH:/run/secrets/hf_token:ro" \
  --env HF_TOKEN_FILE=/run/secrets/hf_token laya

Docker-Secrets oder Kubernetes-Secret-Volumes können dieselbe Datei bereitstellen. Die Werte werden beim Start in die Prozessumgebung geladen; starte nach dem Ändern einer Datei neu. Verwende Tokens niemals als Build-Argumente und backe sie nicht in Images. Öffentliche Checkpoints brauchen keinen Token.

Feinabgestimmte Checkpoints

Dieses Image führt Inferenz aus. Fine-Tuning passiert außerhalb davon — das Fine-Tuning-Notebook führt die ganze Schleife auf Kaggles kostenlosen 2xT4-GPUs aus und exportiert einen Checkpoint, den dieses Image ausliefern kann. Hintergrund und offene Fragen zur Trainingsschnittstelle bleiben in #4 und #26.

Richte LAYA_CHECKPOINT_PATH auf ein absolutes Host-Verzeichnis, das rl_agent_config.json, model.safetensors und passende Tokenizer-Dateien enthält:

docker compose run --rm --volume "$LAYA_CHECKPOINT_PATH:/models/custom" \
  --env LAYA_MODEL_PATH=/models/custom laya

Nutze eine Arbeitskopie, die für UID 10001 beschreibbar ist, weil der Loader die Tokenizer-Konfiguration aktualisieren kann. Ein LoRA-Adapter allein ist kein vollständiger Checkpoint. Lass LAYA_MODEL=auto, wenn du LAYA_MODEL_PATH setzt; ein expliziter Alias und ein lokaler Pfad schließen sich gegenseitig aus. Die Antwort für den lokalen Pfad kommt vom Agent und hat keine routing-Metadaten des Router. Diese Einstellungen funktionieren auch mit dem CUDA-Override. Evaluiere feinabgestimmte Checkpoints an zurückgehaltenen Beispielen, bevor du dich auf sie verlässt.

Modelle von ModelScope

Wenn ein Host huggingface.co nicht erreichen kann, kann der Checkpoint von ModelScope kommen und zur Build-Zeit ins Image gebacken werden. Ein Argument wählt den Checkpoint aus, und es ist standardmäßig der mehrsprachige. Das Compose-Override fügt die Prefetch-Argumente beiden Diensten hinzu und hält den Container vom Hub fern:

docker compose -f compose.yaml -f compose.http.yaml -f compose.modelscope.yaml up --build laya-serve

Für NVIDIA füge -f compose.cuda.yaml vor up hinzu; es wiederholt seine eigenen Argumente für beide Dienste, sodass die Reihenfolge der beiden Overrides keine Rolle spielt. Reines Docker nimmt die Argumente direkt:

docker build --build-arg MODELSCOPE_MODEL=multilingual \
  -t laya:local .
docker run --rm -e HF_HUB_OFFLINE=1 -p 127.0.0.1:8000:8000 laya:local laya-serve

Eine Voraussetzung auf einem Host, der das schon einmal ausgeführt hat. Die eingebackenen Gewichte landen in $HF_HOME/hub im Image, unter dem Cache-Verzeichnis, auf das compose.yaml das Volume model-cache mountet (/home/laya/.cache/huggingface), und Docker befüllt ein benanntes Volume nur aus dem Image, solange dieses Volume leer ist. Ein Volume, das vom Hub-basierten Schnellstart übrig geblieben ist, enthält den älteren Hub-Snapshot, wird nie neu befüllt, und die eingebackenen Gewichte bleiben dahinter unsichtbar: Der Loader löst refs/main zum alten Hub-Commit auf, und der Container antwortet mit den Gewichten, die bereits heruntergeladen wurden, als hätte der Neu-Build nichts geändert. Richte das Deployment auf ein leeres Cache-Volume — docker compose down --volumes mit denselben Compose-Dateien und derselben LAYA_CACHE_VOLUME, oder LAYA_CACHE_VOLUME=<name> für ein frisches. Mit HF_HUB_OFFLINE=1 ist ein fehlender oder abweichender Ref ein Ladefehler ohne Netz, auf das man zurückfallen könnte, aber die Voraussetzung ist dieselbe.

docker/prefetch_modelscope.py listet das Repository auf modelscope.cn auf, lädt die eigenen Dateien des Checkpoints herunter — dieselbe Menge, die laya/agent.py vom Hub verlangt, sodass kein Geschwister-Checkpoint gezogen wird — und schreibt sie so in den Hub-Cache des Images, wie snapshot_download einen Snapshot auslegt. Sonst ändert sich nichts: Agent, der Router, den laya-serve baut, laya.cli und die Integrationen behalten ihre Repo-IDs und lösen sie zum eingebackenen Snapshot auf, sodass ein so gebauter Container gar kein Netz braucht. Die Größe jeder Datei wird gegen das geprüft, was das Repository meldet, bevor der Snapshot veröffentlicht wird, und ihr SHA-256 ebenso, wenn das Repository einen veröffentlicht. Eine Größen- oder Digest-Abweichung lässt den Build scheitern. Ein Repository, das keinen Digest veröffentlicht, lässt nur die Größe prüfen, was eine Ersetzung gleicher Größe nicht erkennen kann.

Variable Standard Zweck
MODELSCOPE_MODEL multilingual (Compose); im Dockerfile leer Einzubackender Checkpoint: multilingual, english, typed-decisions oder all. Leer bedeutet kein Prefetch und das unveränderte Image
MODELSCOPE_REVISION master Einzubackender ModelScope-Branch, -Tag oder -Commit
HF_HUB_OFFLINE 1 im Compose-Override 1 kontaktiert den Hub nie, sodass die eingebackene Kopie ausgeliefert wird

Ein Typ expandiert zum Pfad dieses Checkpoints im gebündelten Repository, den der Router und der einmalige Schnellstart standardmäßig laden, sodass ein Build, der einen Typ nennt, ihn ohne weitere Änderung ausliefert:

MODELSCOPE_MODEL=english docker compose -f compose.yaml -f compose.http.yaml -f compose.modelscope.yaml up --build laya-serve
MODELSCOPE_MODEL=all docker compose -f compose.yaml -f compose.http.yaml -f compose.modelscope.yaml up --build laya-serve

all ist die ganze Familie, etwa 2.4 GB Gewichte. Das Override setzt außerdem LAYA_MODELS=multilingual, weil LAYA_PRELOAD=1 mit der Standardliste versuchen würde, jeden Checkpoint zu bauen, und am ersten scheitern würde, der nicht eingebacken wurde; setze LAYA_MODELS auf die Liste, die du eingebacken hast, wenn du mehr einbackst, und MODELSCOPE_MODEL=all, wenn ein Deployment wirklich die Familie ausliefert.

Mehrere Typen können gleichzeitig genannt werden — MODELSCOPE_MODEL="english multilingual" backt beide ein, etwa 1.5 GB — was ein Serving-Image meist will: Der Router wählt selbst zwischen dem englischen und dem mehrsprachigen Checkpoint, und einer, den er nicht bekommen hat, antwortet 500 inference failed mit does not contain 'rl_agent_config.json' im Log. Checkpoints, die sich ein Repository teilen, werden immer in einen Snapshot eingebacken, weil eine gecachte Revision sich zu einem einzigen Verzeichnis auflöst; der Root-Checkpoint und jeder Unterordner sind beide darin.

Über die Typen hinaus nimmt das Argument auch repo[:subfolder]-Spezifikationen an, komma- oder leerzeichengetrennt, womit die eigenständigen Repositories des Mirrors (laya, laya-multilingual, laya-typed-decisions) oder ein feinabgestimmter Checkpoint eingebacken wird. Ein eigenständiges Repository ist das, was Agent("convaiinnovations/laya-multilingual") direkt lädt; der Standard des Router ist der gebündelte Pfad, sodass ein Serving-Image normalerweise einen Typ will.

Zwei Details zu Pins und Provenienz. Der Build druckt den Commit, auf den der Snapshot geschlüsselt ist, also die Spitze der eingebackenen Revision — übergib diesen SHA als revision= oder LAYA_REVISION, um ein Laden genau auf das Eingebackene festzulegen. Ein Mirror-Repository kann Dateien aus mehreren Uploads enthalten, also ist diese Spitze der einzige repository-weite Schlüssel, den es gibt. Hub-seitige Pins beschreiben keinen Mirror-Snapshot: reviewed benennt Hugging-Face-Commits, und die SHA-256-Digest-Map ist auf Hub-Artefakt-Hashes geschlüsselt, sodass keiner hier greift und es auch keinen Digest-Pin für einen Mirror-Snapshot gibt. Der Build verweigert bereits einen Download, der nicht zur vom Mirror gemeldeten Größe passt — und zu dessen Digest, wenn der Mirror einen veröffentlicht — aber das prüft Konsistenz mit den Mirror-Metadaten, nicht einen unabhängig festgelegten Digest. Die Gewichte kommen von dem Mirror-Konto, das das Argument nennt, was eine eigene Supply-Chain-Entscheidung ist, die das Deployment treffen muss.

Entwicklung und Aufräumen

Öffne eine Python-Eingabeaufforderung mit docker compose run --rm laya python. Um die vorhandenen Routing-/Kriterien-Prüfungen und die Secret-Datei-Tests gegen deinen Checkout auszuführen, ohne Gewichte herunterzuladen:

docker compose run --rm --volume "$PWD:/workspace:ro" --workdir /workspace laya \
  sh -ec 'python tests/test_router.py; python tests/test_criteria.py; python tests/test_docker_entrypoint.py'

Baue mit --build neu, nachdem du den Quellcode oder das mitgelieferte Beispiel geändert hast. Das Image läuft als UID/GID 10001. Neue benannte Volumes erben die Eigentümerschaft des Image-Cache-Verzeichnisses; Host-Verzeichnisse müssen für diese UID beschreibbar sein. Halte Modell-Caches beschreibbar für Tokenizer-Kompatibilitätsaktualisierungen.

--rm entfernt abgeschlossene Container. docker compose down behält den Cache. Um heruntergeladene Gewichte zu löschen, führe docker compose down --volumes mit denselben Compose-Dateien und derselben Einstellung LAYA_CACHE_VOLUME aus. Die nächste Anfrage lädt sie erneut herunter; entferne keinen Cache, den du mit einem anderen Projekt teilst.

HTTP-Bereitstellung

Das Image enthält laya-serve, sodass derselbe Build, der den einmaligen Schnellstart ausführt, die Jev-kompatible API bereitstellen kann. compose.http.yaml fügt ihn als zweiten Dienst hinzu und lässt laya unangetastet:

docker compose -f compose.yaml -f compose.http.yaml up --build laya-serve
curl -s localhost:8000/health
curl -s localhost:8000/v1/systemone -H 'content-type: application/json' \
  --data @examples/docker/request.json

Für NVIDIA füge das CUDA-Override hinzu. Es wiederholt die Build-Argumente und die Gerätereservierung für laya-serve, weil laya-serve ein eigener Dienst ist und Overrides für laya ihn nie erreichen:

docker compose -f compose.yaml -f compose.http.yaml -f compose.cuda.yaml up --build laya-serve

up hält den Dienst im Vordergrund am Laufen; -d löst ihn ab. Die Gewichte gehen in dasselbe benannte Volume model-cache wie beim Schnellstart, sodass das Ausliefern nach einem Schnellstart-Lauf mit den Checkpoints auf der Festplatte beginnt. Stoppe mit docker compose ... down und denselben Compose-Dateien.

Der Port wird nur auf 127.0.0.1 veröffentlicht. Die API hat keine Authentifizierung, bis LAYA_API_KEY gesetzt ist, also setze einen Schlüssel, bevor du sie mit LAYA_BIND_ADDRESS=0.0.0.0 exponierst, und stelle einen TLS-Reverse-Proxy für entfernte Clients davor. /health erfordert in beiden Fällen keine Authentifizierung, also funktioniert der Healthcheck unten weiter; mit gesetztem Schlüssel antwortet es einem unauthentifizierten Aufrufer mit {"status": "ok"} und hält die Felder checkpoint, revision und device zurück, die den Bearer brauchen.

Der Dienst hat einen Healthcheck auf /health. Der Server lädt vor, bevor er zu lauschen beginnt, sodass ein gesunder Container mit LAYA_PRELOAD=1 seine Checkpoints geladen hat. docker compose ... up -d --wait laya-serve kehrt zurück, sobald er gesund ist.

/health meldet device als das Gerät, auf dem ein residenter Checkpoint tatsächlich 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 die CPU zurück und antwortet trotzdem korrekt. checkpoint_devices benennt jeden geladenen Checkpoint, und device_is_preference ist nur dann true, solange nichts residiert, sodass ein Deployment, das still seine GPU verloren hat, das sagt, statt seine eigene Konfiguration zurückzugeben.

Server-Konfiguration

Diese gelten nur für den Dienst laya-serve.

Variable Standard Wirkung
LAYA_HOST 0.0.0.0 Bind-Adresse im Container
LAYA_PORT 8000 Container-Port und der dafür veröffentlichte Host-Port
LAYA_BIND_ADDRESS 127.0.0.1 Host-Adresse, auf der der Port veröffentlicht wird
LAYA_PRELOAD 0 1 baut jeden Checkpoint beim Start statt bei der ersten Anfrage
LAYA_MODELS (alle) Kommaliste zum Vorladen: english,multilingual,typed-decisions
LAYA_THREADS OMP_NUM_THREADS begrenzt die Intra-Op-Threads von torch; halte sie auf oder unter den physischen Kernen
LAYA_AUTO_TASK 0 1 lässt den Router typed-decisions automatisch erreichen
LAYA_DEFAULT_MODEL english Checkpoint, auf den ein Zustand ohne Sprachhinweise zurückfällt (keine Buchstaben oder zu kurzer lateinischer Text zur Identifikation). Setze multilingual für überwiegend nicht-englischen Verkehr; ein nicht auflösbarer Name stoppt den Container beim Start, statt eine Konfiguration auszuliefern, die niemand verlangt hat
LAYA_MAX_LOADED 2 Residente Checkpoints; LAYA_AUTO_TASK macht einen dritten bei Bedarf erreichbar, und eine Obergrenze unter dem, was das Routing wählt, baut bei jedem Wechsel einen neu auf
LAYA_MAX_CONCURRENT 16 gleichzeitig zugelassene Anfragen; spätere bekommen 503 (ein Wert, der nicht geparst werden kann oder nicht positiv ist, fällt auf 16 zurück)
LAYA_LOG_LEVEL info uvicorn-Loglevel
LAYA_API_KEY (keiner) wenn gesetzt, erfordert Authorization: Bearer <key>
LAYA_ROOT_PATH (leer) öffentlicher URL-Präfix für FastAPI hinter einem Reverse-Proxy; der Proxy sollte ihn vor dem Weiterleiten entfernen
LAYA_MAX_TOKEN_BUDGET 8192 Obergrenze für die Overrides max_len und head_max_len pro Anfrage
LAYA_SHA256_DIGESTS (keine) JSON-Digests, die geprüft werden, bevor ein Checkpoint geparst wird: {artifact: digest} für jeden Checkpoint oder {model: {artifact: digest}} pro Checkpoint. Siehe Sicherheit

Setze zum Beispiel LAYA_ROOT_PATH=/laya, wenn du die API unter /laya veröffentlichst. Der Proxy muss diesen Präfix entfernen, bevor er an den Container weiterleitet; diese Einstellung aktualisiert die von FastAPI generierten URLs und ändert die internen Routen /health und /v1/systemone nicht.

LAYA_PRELOAD verwendet hier 0 statt der Paketvorgabe 1, weil das Vorladen den ersten Start alle drei Checkpoints herunterladen lässt. Setze es auf 1 für ein langlebiges Deployment, damit die erste Anfrage nicht für den Aufbau bezahlt.

LAYA_PORT legt sowohl den veröffentlichten Host-Port als auch den Port fest, an den der Server bindet, sodass die beiden nicht auseinanderdriften können. Ändere eine Stelle, um den Dienst zu verschieben:

LAYA_PORT=9000 docker compose -f compose.yaml -f compose.http.yaml up --build laya-serve

Bearer-Token aus einer Datei

LAYA_API_KEY_FILE wird einmal beim Start gelesen, nach LAYA_API_KEY verschoben, und die Variable _FILE wird entfernt, bevor der Server per exec startet. Bevorzuge dies gegenüber dem Ablegen des Schlüssels in der Umgebung:

docker compose -f compose.yaml -f compose.http.yaml run --rm \
  --volume "$PWD/laya_api_key:/run/secrets/laya_api_key:ro" \
  -e LAYA_API_KEY_FILE=/run/secrets/laya_api_key \
  --service-ports laya-serve