API-Überblick
OpenAI-kompatible, EU-souveräne LLM-API von Kasimir: Authentifizierung, Endpunkte, Modelle, Streaming, Fehler und Metering.
Die Kasimir Public API stellt den geregelten, EU-souveränen LLM-Zugang programmatisch bereit. Sie ist drop-in-kompatibel mit der OpenAI-API — bestehende Tools und SDKs funktionieren, indem Sie lediglich base_url und den API-Schlüssel austauschen. Kasimir agiert dabei als schlanker Metering- und Governance-Proxy vor dem internen LiteLLM-Proxy: identisches Wire-Format, plus firmenspezifische Schlüssel-Isolation, ein gebrandeter Modell-Namespace, die EU-/Nicht-EU-Schranke, Nutzungsabrechnung und Durchsetzung des Firmen-Budgets.
Basis-URL
https://kasimir.ai/api/v1Alle Endpunkte sind relativ zu dieser Basis-URL. Sie ist exakt das, was Sie als base_url in einem OpenAI-SDK setzen.
Authentifizierung
Jede Anfrage trägt einen Firmen-API-Schlüssel im Authorization-Header als Bearer-Token:
Authorization: Bearer kasi_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxSchlüssel haben das Präfix kasi_ gefolgt von 32 Zeichen (Base32-Alphabet, ~160 Bit Entropie). Sie werden in /administration/access erzeugt, nur einmal bei der Erstellung im Klartext angezeigt und serverseitig ausschließlich als SHA-256-Digest gespeichert. Zur Identifikation zeigt die Oberfläche dauerhaft nur das Präfix (erste 8 Zeichen).
Behandeln Sie den Schlüssel wie ein Passwort. Er wird nach der Erstellung nie wieder im Klartext angezeigt. Bei Verlust widerrufen Sie ihn und erstellen einen neuen. Der interne LiteLLM-Master-Key verlässt niemals den Server — Sie erhalten ausschließlich kasi_-Schlüssel.
Scopes
Schlüssel tragen Scopes. Die Endpunkte prüfen serverseitig:
Scope | Benötigt für |
|---|---|
|
|
(kein Scope) |
|
Fehlt ein erforderlicher Scope, antwortet die API mit 403. Ein ungültiger oder widerrufener Schlüssel ergibt 401.
Endpunkte im Überblick
Methode | Pfad | Zweck | Scope |
|---|---|---|---|
|
| Für die Firma freigegebene Chat- und Embedding-Modelle auflisten | beliebig gültig |
|
| Chat-Completion (Streaming + Non-Streaming) |
|
|
| Embedding-Vektoren erzeugen |
|
|
| Gespeicherten Assistenten als One-Shot-Funktion ausführen |
|
Die ersten drei sind OpenAI-shaped. assistants/{id}/run ist ein Kasimir-spezifischer Komfort-Endpunkt (siehe unten).
GET /models
Liefert die für Ihre Firma freigegebenen Modelle, OpenAI-shaped. Die Liste wendet bereits die Firmen-Filter an: deaktivierte Modelle (disabled_model_slugs) sind ausgeblendet, und Nicht-EU-Modelle erscheinen nur, wenn Ihre Organisation allow_non_eu_models aktiviert hat.
curl https://kasimir.ai/api/v1/models \
-H "Authorization: Bearer kasi_..."Antwort:
{
"object": "list",
"data": [
{
"id": "self-hosted-llama-3.3-70b",
"object": "model",
"created": 1745971200,
"owned_by": "kasimir"
}
]
}Das Feld id ist der Modell-Slug — genau dieser Wert gehört in das model-Feld der Completion- und Embedding-Aufrufe. Interne model_id-Werte und Upstream-Aliase werden nie nach außen gegeben.
Modell-Slugs sind firmen- und konfigurationsspezifisch. Verlassen Sie sich nicht auf hartkodierte Namen — fragen Sie GET /models ab, um die für Ihren Schlüssel gültigen Slugs zu erhalten.
POST /chat/completions
Akzeptiert einen OpenAI-Chat-Body. Pflichtfelder sind model und messages; alle weiteren Felder (temperature, max_tokens, tools, response_format, …) werden transparent durchgereicht, sodass Function-Calling, strukturierte Ausgaben, finish_reason und multimodale Inhalte so funktionieren, wie der Upstream sie unterstützt.
Parameter
Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| string | ja | Modell-Slug aus |
| array | ja | Mindestens 1 Nachricht im OpenAI-Format ( |
| boolean | nein |
|
| number | nein | Durchgereicht |
| number | nein | Durchgereicht |
| – | nein | Durchgereicht (LiteLLM-Parität) |
Non-Streaming
curl https://kasimir.ai/api/v1/chat/completions \
-H "Authorization: Bearer kasi_..." \
-H "Content-Type: application/json" \
-d '{
"model": "self-hosted-llama-3.3-70b",
"messages": [
{"role": "system", "content": "Du bist ein hilfreicher Assistent."},
{"role": "user", "content": "Erkläre DSGVO in einem Satz."}
]
}'Antwort (OpenAI-shaped; das model-Feld trägt den Slug zurück):
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1745971200,
"model": "self-hosted-llama-3.3-70b",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "Die DSGVO ist..." },
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 24, "completion_tokens": 18, "total_tokens": 42 }
}Streaming
Mit "stream": true liefert die API einen text/event-stream. Die Upstream-SSE-Bytes werden transparent durchgereicht; Kasimir schreibt nur das model-Feld auf den Slug um und greift den finalen usage-Chunk für das Metering ab. Das Ende ist die übliche data: [DONE]-Zeile.
curl -N https://kasimir.ai/api/v1/chat/completions \
-H "Authorization: Bearer kasi_..." \
-H "Content-Type: application/json" \
-d '{
"model": "self-hosted-llama-3.3-70b",
"messages": [{"role": "user", "content": "Zähle bis drei."}],
"stream": true
}'data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"self-hosted-llama-3.3-70b","choices":[{"index":0,"delta":{"content":"Eins"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":12,"completion_tokens":5,"total_tokens":17}}
data: [DONE]POST /embeddings
OpenAI-shaped Embeddings. Pflichtfelder: model (ein Embedding-Slug) und input (String oder String-Array).
Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| string | ja | Embedding-Modell-Slug ( |
| string | string[] | ja | Zu embeddender Text bzw. Texte |
curl https://kasimir.ai/api/v1/embeddings \
-H "Authorization: Bearer kasi_..." \
-H "Content-Type: application/json" \
-d '{
"model": "self-hosted-bge-m3",
"input": "Beispieltext zum Einbetten"
}'{
"object": "list",
"data": [
{ "object": "embedding", "index": 0, "embedding": [0.0123, -0.045, "..."] }
],
"model": "self-hosted-bge-m3",
"usage": { "prompt_tokens": 6, "total_tokens": 6 }
}Die Vektordimension des Standard-Embedding-Modells ist auf 1024 festgelegt (BAAI/bge-m3).
POST /assistants/{id}/run
Komfort-Endpunkt (kein OpenAI-Standard), der einen in Kasimir gespeicherten Assistenten als One-Shot-Funktion ausführt. Der System-Prompt des Assistenten wird automatisch vorangestellt (plus das aktuelle Datum); kein SSE-Streaming, keine Konversations-Persistenz, kein Tool-Calling-Roundtrip.
Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| array | ja | 1–50 Nachrichten mit |
| string | nein | Überschreibt das Standardmodell des Assistenten |
| number (0–2) | nein | Überschreibt die Assistenten-Einstellung |
| integer (1–32000) | nein | Überschreibt die Assistenten-Einstellung |
curl https://kasimir.ai/api/v1/assistants/<assistant-id>/run \
-H "Authorization: Bearer kasi_..." \
-H "Content-Type: application/json" \
-d '{ "messages": [{"role": "user", "content": "Fasse den Quartalsbericht zusammen."}] }'{
"text": "Der Quartalsbericht zeigt...",
"model": "self-hosted-llama-3.3-70b",
"usage": { "prompt_tokens": 312, "completion_tokens": 88 }
}Fehlt sowohl model_slug als auch ein Standardmodell am Assistenten, antwortet der Endpunkt mit 400. Ein nicht gefundener oder fremder Assistent ergibt 404.
Verwendung mit dem OpenAI Python SDK
Da die Endpunkte OpenAI-shaped sind, genügt es, base_url und api_key zu setzen:
from openai import OpenAI
client = OpenAI(
base_url="https://kasimir.ai/api/v1",
api_key="kasi_...",
)
# Modelle auflisten
for m in client.models.list().data:
print(m.id)
# Chat-Completion
resp = client.chat.completions.create(
model="self-hosted-llama-3.3-70b",
messages=[{"role": "user", "content": "Erkläre DSGVO in einem Satz."}],
)
print(resp.choices[0].message.content)
# Streaming
stream = client.chat.completions.create(
model="self-hosted-llama-3.3-70b",
messages=[{"role": "user", "content": "Zähle bis drei."}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="", flush=True)
# Embeddings
emb = client.embeddings.create(
model="self-hosted-bge-m3",
input="Beispieltext zum Einbetten",
)
print(len(emb.data[0].embedding))Fehler
Fehler sind OpenAI-shaped, mit korrektem HTTP-Statuscode, sodass Standard-SDKs sie normal melden:
{ "error": { "message": "Model \"foo\" not found", "type": "not_found_error", "code": null } }HTTP |
| Bedeutung |
|---|---|---|
|
| Ungültiger Request-Body (z. B. fehlendes |
|
| Fehlender, ungültiger oder widerrufener Schlüssel |
|
| Monatsbudget der Firma erreicht |
|
| Fehlender Scope oder durch EU-Schranke gesperrtes Nicht-EU-Modell |
|
| Modell/Assistent nicht gefunden bzw. für die Firma deaktiviert |
|
| Upstream-Fehler (LiteLLM/Modell) |
EU-Governance & Souveränität
Jeder API-Aufruf durchläuft serverseitig dieselbe EU-/Nicht-EU-Schranke wie die In-App-Modellauswahl. Modelle mit eu_hosted=false sind nur erreichbar, wenn die Organisation allow_non_eu_models explizit aktiviert hat — andernfalls 403. Der gebrandete Modell-Namespace (Slug ↔ interne model_id) und der LiteLLM-Master-Key bleiben vollständig serverseitig. So gilt für die API dieselbe DSGVO-Garantie wie für die Anwendung.
Metering & Budget
Jeder erfolgreiche Completion- bzw. Embedding-Aufruf wird im api_usage-Ledger verbucht (Modell-Slug, Endpunkt, Prompt-/Completion-Tokens, Kosten in EUR). Die Kosten folgen der Standardformel:
cost = (prompt_tokens / 1000) * cost_eur_per_1k_input
+ (completion_tokens / 1000) * cost_eur_per_1k_outputBeim Streaming stammt die Nutzung aus dem getappten finalen usage-Chunk (stream_options.include_usage wird automatisch gesetzt). Metering ist best-effort — ein Abrechnungsfehler bricht die bereits gesendete Antwort nie ab.
Budget-Durchsetzung: Vor jedem Forward prüft die API das Firmen-Monatsbudget (companies.monthly_budget_eur). Der Monatsverbrauch umfasst API- und In-App-Chat-Kosten des laufenden Kalendermonats. Ist das Budget erreicht, antwortet die API mit 402. Ein Budget von null/0 bedeutet unbegrenzt. Eine Nutzungsübersicht (Requests, Tokens, EUR diesen Monat) finden Owner/Admins unter /administration/access.
Setzen Sie ein Monatsbudget in der Administration, um Kostenkontrolle zu erzwingen — ohne Budget laufen API-Aufrufe unbegrenzt.