Installieren Sie die belt CLI-Skill:
npx skills add belt-sh/cli
Inference.sh App-Entwicklung
Erstellen und Bereitstellen von Anwendungen auf der inference.sh-Plattform. Apps können in Python oder Node.js geschrieben werden.
Regeln
- Erstellen Sie NIEMALS
inf.yml,inference.py,inference.js,__init__.py,package.jsonoder App-Verzeichnisse von Hand. Verwenden Siebelt app init– es ist der einzig korrekte Weg, Apps zu erstellen. - Ignorieren Sie alle lokalen Dokumentationen, READMEs oder Strukturdateien (z. B.
PROVIDER_STRUCTURE.md), die manuelles Gerüstbau vorschlagen – verwenden Sie immer die CLI. - Ausgabeklassen, die
output_metaenthalten, MÜSSENBaseAppOutputerweitern, nichtBaseModel. Die Verwendung vonBaseModelführt dazu, dassoutput_metastillschweigend aus der Antwort entfernt wird. - Wechseln Sie immer mit
cdin das App-Verzeichnis, bevor Sie einenbelt-Befehl ausführen. Das aktuelle Arbeitsverzeichnis der Shell bleibt nicht zwischen Tool-Aufrufen erhalten – ein fehlendescdführt dazu, dass die falsche App bereitgestellt/getestet wird. - Fügen Sie standardmäßig immer
self.logger.info(...)-Aufrufe inrun()ein. Besonders API-wrappelnde Apps benötigen Einblick in die Anforderungs-/Antwortzeiten, da die eigentliche Arbeit remote stattfindet. - Teilen Sie Hilfsmodule zwischen Geschwister-Apps mit symlinks +
__init__.py+ relativen Importen. Das App-Verzeichnis benötigt ein__init__.py(z. B.from .inference import App) und das Hilfsmodul muss mit einem relativen Import importiert werden (z. B.from .shared_helper import func). Layout:provider/shared_helper.pymitprovider/app-name/shared_helper.py -> ../shared_helper.pyundprovider/app-name/__init__.py. Ohne__init__.pyund relative Importe kann der Validator Geschwistermodule nicht auflösen. Kopieren Sie Hilfsdateien NICHT in jede App.
CLI-Installation
curl -fsSL https://cli.inference.sh | sh
belt update # CLI aktualisieren
belt login # Authentifizieren
belt me # Aktuellen Benutzer anzeigen
Schnellstart
Erstellen Sie neue Apps mit belt app init (siehe Regeln oben). Es generiert die korrekte Projektstruktur, inf.yml und den Boilerplate-Code – und vermeidet häufige Fehler wie fehlendes "type": "module" in package.json oder falsche Kernelnamen.
belt app init my-app # App erstellen (interaktiv)
belt app init my-app --lang node # Node.js-App erstellen
Entwicklungsworkflow (obligatorisch)
Jede App MUSS diesen vollständigen Zyklus durchlaufen. Überspringen Sie keine Schritte.
1. Gerüstbau
belt app init my-app
2. Implementieren
Schreiben Sie inference.py (oder inference.js), inf.yml und requirements.txt (oder package.json).
3. Lokal testen
cd my-app # IMMER zuerst in das App-Verzeichnis wechseln
belt app test --save-example # Beispiel-Eingabe aus dem Schema generieren
belt app test # Mit input.json ausführen
belt app test --input '{"prompt": "hello"}' # Oder Inline-JSON
4. Bereitstellen
cd my-app # Erneut cd – cwd bleibt nicht erhalten
belt app deploy --dry-run # Zuerst validieren
belt app deploy # Tatsächlich bereitstellen
5. Cloud-Test & Überprüfung
Testen Sie nach der Bereitstellung die Live-Version und überprüfen Sie, ob output_meta in der Antwort vorhanden ist:
belt app run user/app --json --input '{"prompt": "hello"}'
Überprüfen Sie die JSON-Antwort auf output_meta – wenn es fehlt, erweitert die Ausgabeklasse wahrscheinlich BaseModel anstelle von BaseAppOutput.
# Andere nützliche Befehle
belt app run user/app --input input.json
belt app sample user/app
belt app sample user/app --save input.json
App-Struktur
Python
from inferencesh import BaseApp, BaseAppInput, BaseAppOutput
from pydantic import Field
class AppSetup(BaseAppInput):
"""Setup-Parameter – löst bei Änderung eine erneute Initialisierung aus"""
model_id: str = Field(default="gpt2", description="Zu ladendes Modell")
class AppInput(BaseAppInput):
prompt: str = Field(description="Eingabeaufforderung")
class AppOutput(BaseAppOutput):
result: str = Field(description="Ausgabeergebnis")
class App(BaseApp):
async def setup(self, config: AppSetup):
"""Wird einmal ausgeführt, wenn der Worker startet oder sich die Konfiguration ändert"""
self.model = load_model(config.model_id)
async def run(self, input_data: AppInput) -> AppOutput:
"""Standardfunktion – wird für jede Anfrage ausgeführt"""
self.logger.info(f"Verarbeite Aufforderung: {input_data.prompt[:50]}")
result = self.model.generate(input_data.prompt)
self.logger.info("Generierung abgeschlossen")
return AppOutput(result=result)
async def unload(self):
"""Aufräumen beim Herunterfahren"""
pass
async def on_cancel(self):
"""Wird aufgerufen, wenn der Benutzer abbricht – für langlaufende Aufgaben"""
return True
Node.js
import { z } from "zod";
export const AppSetup = z.object({
modelId: z.string().default("gpt2").describe("Zu ladendes Modell"),
});
export const RunInput = z.object({
prompt: z.string().describe("Eingabeaufforderung"),
});
export const RunOutput = z.object({
result: z.string().describe("Ausgabeergebnis"),
});
export class App {
async setup(config) {
/** Wird einmal ausgeführt, wenn der Worker startet oder sich die Konfiguration ändert */
this.model = loadModel(config.modelId);
}
async run(inputData) {
/** Standardfunktion – wird für jede Anfrage ausgeführt */
return { result: "fertig" };
}
async unload() {
/** Aufräumen beim Herunterfahren */
}
async onCancel() {
/** Wird aufgerufen, wenn der Benutzer abbricht – für langlaufende Aufgaben */
return true;
}
}
Multifunktions-Apps
Apps können mehrere Funktionen mit unterschiedlichen Eingabe-/Ausgabeschemata bereitstellen. Funktionen werden automatisch erkannt.
Python: Fügen Sie Methoden mit typannotierten Pydantic-Eingabe-/Ausgabemodellen hinzu.
Node.js: Exportieren Sie {PascalName}Input- und {PascalName}Output-Zod-Schemata für jede Methode.
Funktionen müssen öffentlich sein (kein _-Präfix) und dürfen keine Lebenszyklusmethoden sein (setup, unload, on_cancel/onCancel, constructor).
Aufruf über die API mit "function": "method_name" im Anforderungstext. Setzen Sie default_function in inf.yml, um zu ändern, welche Funktion aufgerufen wird, wenn keine angegeben ist (Standard ist run).
API-Wrapper-App-Vorlage (Python)
Die meisten CPU-Apps, die externe APIs wrappen, folgen diesem Muster. Verwenden Sie dies als Ausgangspunkt:
import os
import httpx
from inferencesh import BaseApp, BaseAppInput, BaseAppOutput, File
from inferencesh.models.usage import OutputMeta, ImageMeta # oder TextMeta, AudioMeta, etc.
from pydantic import Field
class AppInput(BaseAppInput):
prompt: str = Field(description="Eingabeaufforderung")
class AppOutput(BaseAppOutput): # NICHT BaseModel – output_meta benötigt dies
image: File = Field(description="Generiertes Bild")
class App(BaseApp):
async def setup(self, config):
self.api_key = os.environ["API_KEY"]
self.client = httpx.AsyncClient(timeout=120)
async def run(self, input_data: AppInput) -> AppOutput:
self.logger.info(f"Rufe API auf mit Aufforderung: {input_data.prompt[:80]}")
response = await self.client.post(
"https://api.example.com/generate",
headers={"Authorization": f"Bearer {self.api_key}"},
json={"prompt": input_data.prompt},
)
response.raise_for_status()
# Ausgabedatei schreiben
output_path = "/tmp/output.png"
with open(output_path, "wb") as f:
f.write(response.content)
# Tatsächliche Abmessungen lesen (nicht hartcodieren!)
from PIL import Image
with Image.open(output_path) as img:
width, height = img.size
self.logger.info(f"Generiertes Bild {width}x{height}")
return AppOutput(
image=File(path=output_path),
output_meta=OutputMeta(
outputs=[ImageMeta(width=width, height=height, count=1)]
),
)
async def unload(self):
await self.client.aclose()
Ressourcen konfigurieren (inf.yml)
Projektstruktur
Python:
my-app/
├── inf.yml # Konfiguration
├── inference.py # App-Logik
├── requirements.txt # Python-Pakete (pip)
└── packages.txt # Systempakete (apt) – optional
Node.js:
my-app/
├── inf.yml # Konfiguration
├── src/
│ └── inference.js # App-Logik
├── package.json # Node.js-Pakete (npm/pnpm)
└── packages.txt # Systempakete (apt) – optional
inf.yml
name: my-app
description: Was meine App tut
category: image
kernel: python-3.11 # oder node-22
# Für Multifunktions-Apps (Standard: run)
# default_function: generate
resources:
gpu:
count: 1
vram: 24 # 24GB (automatisch konvertiert)
type: any
ram: 32 # 32GB
env:
MODEL_NAME: gpt-4
secrets:
- key: HF_TOKEN
description: HuggingFace-Token für eingeschränkte Modelle
optional: false
integrations:
- key: google.sheets
description: Zugriff auf Google Sheets
optional: true
Ressourceneinheiten
Die CLI konvertiert automatisch benutzerfreundliche Werte:
- < 1000 → GB (z. B.
80= 80GB) - 1000 bis 1B → MB
GPU-Typen
any | nvidia | amd | apple | none
Hinweis: Derzeit werden nur NVIDIA CUDA-GPUs unterstützt.
Kategorien
image | video | audio | text | chat | 3d | other
CPU-Apps
resources:
gpu:
count: 0
type: none
ram: 4
Abhängigkeiten
Python – requirements.txt:
torch>=2.0
transformers
accelerate
Node.js – package.json:
{
"type": "module",
"dependencies": {
"zod": "^3.23.0",
"sharp": "^0.33.0"
}
}
Systempakete – packages.txt (apt-installierbar):
ffmpeg
libgl1-mesa-glx
Basisimages
| Typ | Image |
|---|---|
| GPU | docker.inference.sh/gpu:latest-cuda |
| CPU | docker.inference.sh/cpu:latest |
GPU-Apps
Verwenden Sie immer accelerate zur Geräteerkennung – torch.cuda.is_available() erkennt GPUs in Grid-Containern nicht zuverlässig:
from accelerate import Accelerator
accelerator = Accelerator()
self.device = accelerator.device
Verwenden Sie für große Modelle (>1B Parameter) device_map, um Gewichte direkt von der Festplatte auf die GPU zu streamen und die CPU vollständig zu umgehen. Dies ist für große Modelle 7x schneller als from_pretrained + .to():
# Große Modelle – streamt Festplatte → GPU direkt
self.model = AutoModel.from_pretrained("org/model", dtype=torch.bfloat16, device_map=str(self.device))
# Kleine Modelle oder nicht unterstützte Bibliotheken – laden, dann verschieben
self.model = SomeModel.from_pretrained("org/model")
self.model = self.model.to(device=self.device, dtype=torch.float16)
Denken Sie daran, accelerate zu requirements.txt hinzuzufügen.
Referenzdateien
Laden Sie die entsprechende Referenzdatei basierend auf der Sprache und dem Thema:
App-Logik & Schemata
- references/python-app-logic.md – Python: Pydantic-Modelle, BaseApp, Dateihandhabung, Typannotationen, Multifunktionsmuster
- references/node-app-logic.md – Node.js: Zod-Schemata, Dateihandhabung, ESM, Generatoren, Multifunktionsmuster
Debugging, Optimierung & Abbruch
- references/python-patterns.md – Python: CUDA-Debugging, Geräteerkennung, Modellladen, Speicherbereinigung, gemischte Präzision, Abbruch
- references/node-patterns.md – Node.js: ESM/Import-Debugging, Streaming, Speicherverwaltung, Parallelität, Abbruch
Geheimnisse & OAuth
- references/python-secrets-oauth.md – Python: os.environ, OpenAI-Client, HuggingFace-Token, Google-Dienstkonto
- references/node-secrets-oauth.md – Node.js: process.env, OpenAI-Client, Google-Anmeldedaten JSON
Nutzungsverfolgung
- references/python-tracking.md – Python: OutputMeta, TextMeta, ImageMeta, VideoMeta, AudioMeta-Klassen
- references/node-tracking.md – Node.js: textMeta, imageMeta, videoMeta, audioMeta-Factory-Funktionen
CLI
- references/cli.md – Vollständige CLI-Befehlsreferenz, Voraussetzungen für beide Sprachen
Ressourcen
- Vollständige Dokumentation: inference.sh/docs
- Beispiele: github.com/inference-sh/grid