POST /v1/chat/completions
Chat-Completions der SovrGPT-API im OpenAI-Schema: Request-Felder, Streaming, Denk-Aufwand, Werkzeuge, Bildeingabe, Modell-Kopfzeilen und Fehlercodes.
Erzeugt eine Modellantwort auf einen Chatverlauf. Schema und Streaming entsprechen OpenAI; zusätzlich nennt jede Antwort in einer Kopfzeile das Modell, das tatsächlich geantwortet hat.
curl https://sovrgpt.com/api/v1/chat/completions \
-H "Authorization: Bearer $SOVR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemma-4-12b",
"messages": [
{ "role": "system", "content": "Du bist ein hilfreicher Assistent." },
{ "role": "user", "content": "Erkläre Diffusionsmodelle in zwei Sätzen." }
],
"temperature": 0.5,
"max_tokens": 400
}'Request-Body
| Feld | Typ | Default | Beschreibung |
|---|---|---|---|
model | string | – | Modell-ID aus GET /v1/models. |
messages | array | – | Chatverlauf. Rollen: system, user, assistant, tool. |
temperature | number | 0.7 | 0.0 – 2.0. Höher = kreativer. |
top_p | number | 1.0 | Nucleus-Sampling. |
max_tokens | int | Modell-Default | Maximale Antwortlänge in Token. |
stream | bool | false | true → SSE-Stream. |
stop | string|array | – | Stop-Sequenzen. |
tools | array | – | OpenAI-Tool-Schema (Function-Calling). |
tool_choice | string|object | "auto" | "none", "auto", "required" oder ein bestimmtes Tool. |
response_format | object | – | { "type": "json_object" } für den JSON-Modus. |
reasoning_effort | string | – | Denk-Aufwand. Welche Modelle ihn auswerten, steht in effort_levels von GET /v1/models. Siehe Denk-Level. |
chat_template_kwargs | object | – | Argumente für die Chat-Vorlage des Modellservers, unverändert durchgereicht (z. B. { "enable_thinking": false } für Qwen). Hat Vorrang vor reasoning_effort. |
Weitere OpenAI-Felder (seed, logit_bias, user, presence_penalty,
frequency_penalty) reichen wir unverändert an den Modellserver weiter; ob sie wirken, hängt
vom Modell ab. n > 1 wird nicht unterstützt.
Denk-Level (reasoning_effort)
Viele Modelle können je Anfrage unterschiedlich gründlich „denken“. Sie steuern das wie bei
OpenAI über reasoning_effort (im Python-SDK über extra_body):
curl https://sovrgpt.com/api/v1/chat/completions \
-H "Authorization: Bearer $SOVR_KEY" -H "Content-Type: application/json" \
-d '{
"model": "stackit-qwen3.8-27b",
"messages": [{ "role": "user", "content": "Finde und behebe den Bug in diesem Stacktrace …" }],
"reasoning_effort": "high"
}'GET /v1/models nennt je Modell in effort_levels die Stufen, deren Wirkung wir gemessen
haben. Ein leeres Array heißt: kein wirksamer Regler, ein trotzdem gesendeter Wert wird nicht
übersetzt. Modelle, die einen Wert zwar annehmen, aber ignorieren, führen keine Stufen.
| Wert | Bedeutung |
|---|---|
| weggelassen | Voreinstellung des Modells. |
none | Nicht denken, schnellste Antwort. |
minimal, low | Sparsam. |
medium | Normal. |
high, max, xhigh | Gründlich. |
Führt ein Modell die gewünschte Stufe nicht (manche haben nur low und high), nehmen wir
die nächstniedrigere vorhandene, nie eine höhere.
Beim Modell kommt je nach Anbieter etwas anderes an: ein Token-Deckel statt einer Stufe,
andere Stufennamen oder eine umgekehrte Skala. Diese Übersetzung übernimmt der Server. Sie
schicken immer low, medium oder high.
high kann das Antwortbudget aufbrauchen. Der Gedankengang zählt gegen max_tokens.
Bei stackit-gpt-oss-120b bleibt mit high und knappem max_tokens der Antworttext leer
(gemessen). Heben Sie max_tokens mit an.
Antwort (ohne Streaming)
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1715520000,
"model": "gemma-4-12b",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Diffusionsmodelle …"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 42,
"completion_tokens": 87,
"total_tokens": 129
}
}Welches Modell geantwortet hat
Jede Antwort trägt diese Kopfzeilen, mit und ohne Streaming und auch beim 503 unten:
| Kopfzeile | Wann | Bedeutung |
|---|---|---|
x-sovrgpt-model-served | immer | Die Katalog-ID, die tatsächlich geantwortet hat. |
x-sovrgpt-model-requested | nur bei Ersetzung | Die ID, die Sie geschickt haben — ohne Leerraum am Rand und höchstens 200 Zeichen; Zeichen außerhalb von druckbarem ASCII erscheinen als ?. |
x-sovrgpt-model-substituted | nur bei Ersetzung | unavailable: die ID ist bekannt, aber gerade nicht bedienbar, z. B. ein externes Modell ohne hinterlegten Anbieter-Schlüssel. |
Unbekannte Modell-IDs: 404
Eine unbekannte Modell-ID (ein Tippfehler, gpt-4o, eine Embedding-ID wie bge-m3) scheitert
mit HTTP 404 und dem Code model_not_found. Tragen Sie dann eine ID aus GET /v1/models ein.
curl -s -X POST https://sovrgpt.com/api/v1/chat/completions \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"model":"gpt-4o","messages":[{"role":"user","content":"Hallo"}]}'
# {"error":{"message":"The model 'gpt-4o' does not exist. Call GET /v1/models for the list you may use.","type":"invalid_request_error","param":"model","code":"model_not_found"}}Bis zum 14.10.2026 hat SovrGPT eine unbekannte ID stattdessen durch das Standardmodell ersetzt.
Nicht betroffen: Aliase und Rollennamen (unten), eine Anfrage ganz ohne model (sie bekommt
wie bisher das Standardmodell) und unavailable (eine bekannte ID, die gerade nicht bedienbar
ist).
Ein Alias ist keine Ersetzung. coding löst auf qwen3.6-35b-a3b auf; dann steht nur
x-sovrgpt-model-served da. Rollennamen wie coder zeigen nach einem Modellwechsel auf den
Nachfolger, ebenfalls ohne Ersetzungs-Kopfzeile. Ist Ihnen die Rolle wichtiger als das
konkrete Modell, schicken Sie den Rollennamen.
Ein zurückgezogenes Modell: 404 model_withdrawn
Eine ID, die es bei uns gab und die wir zurückgezogen haben, wird nicht ersetzt. Sie scheitert mit eigenem Code und nennt den Nachfolger:
{
"error": {
"message": "The model 'qwen3-coder-next-fp8' has been withdrawn and is no longer served. Zurückgezogen am 2026-09-11 (32 768 Token Kontext, von 'qwen3.6-35b-a3b' überholt). Nachfolger: 'qwen3.6-35b-a3b' … Call GET /v1/models for the list you may use.",
"type": "invalid_request_error",
"param": "model",
"code": "model_withdrawn"
}
}HTTP 404, dazu die Kopfzeile x-sovrgpt-model-withdrawn mit der betroffenen ID. Wer
ausdrücklich ein bestimmtes Modell benannt hat, ist mit einem klaren Fehler besser bedient als
mit einem allgemeinen Ersatz.
Die beiden 404-Codes verlangen verschiedene Reaktionen:
| Code | Bedeutung | Was hilft |
|---|---|---|
model_not_found | Ihre Organisation hat dieses Modell nicht freigeschaltet, oder ein eigenes Modell (org-model:…) ist pausiert oder gelöscht. Oder die ID gibt es gar nicht (Meldung „does not exist“). | Ein Org-Admin kann es unter Einstellungen → Modelle aktivieren. Bei „does not exist“ eine ID aus GET /v1/models eintragen. |
model_withdrawn | Das Modell gibt es nicht mehr. | Niemand kann es aktivieren. Tragen Sie den genannten Nachfolger ein. |
Streaming (SSE)
Mit "stream": true:
data: {"id":"chatcmpl-abc","choices":[{"delta":{"role":"assistant"},"index":0}]}
data: {"id":"chatcmpl-abc","choices":[{"delta":{"content":"Diff"},"index":0}]}
data: {"id":"chatcmpl-abc","choices":[{"delta":{"content":"usions"},"index":0}]}
…
data: {"id":"chatcmpl-abc","choices":[{"delta":{},"finish_reason":"stop","index":0}]}
data: [DONE]Das Format entspricht OpenAI; der Streaming-Modus der OpenAI-SDKs funktioniert unverändert.
Kommt länger als zehn Sekunden kein Token, etwa während das Modell denkt, sendet der Server
die Kommentarzeile : keep-alive, damit die Verbindung offen bleibt. SSE-Clients ignorieren
sie.
Wenn das Modell erst hochfahren muss: 503
Selbst betriebene Modelle fahren auf null herunter, wenn niemand sie nutzt. Erkennen wir vor dem Absenden, dass nachweislich kein Worker bereit ist, antworten wir sofort:
HTTP/1.1 503 Service Unavailable
Retry-After: 60
{"error":{"message":"The model 'qwen3.8-27b' is scaled to zero and has no worker ready. A warm-up has been started for you — retry in about a minute. Typical cold start: …","type":"api_error","param":null,"code":"model_cold_start"}}Das Hochfahren ist dann bereits angestoßen. Warten Sie Retry-After Sekunden und schicken
Sie dieselbe Anfrage erneut. Ein Kaltstart dauert je nach Modell mehrere Minuten, oft sind
also mehrere Versuche nötig. Die Größenordnung je Modell steht in der Meldung und unter
Modelle.
Eine laufende Antwort brechen wir nicht ab. Wir prüfen nur vor dem Absenden. Ist der Zustand dann nicht sicher erkennbar, wartet die Anfrage, bis das Modell bereit ist. Die Obergrenze je Anfrage liegt bei 780 Sekunden; setzen Sie Ihr Client-Timeout entsprechend.
Partner-Modelle (tensorx-…, stackit-…) und die eigenen Endpunkte Ihrer Organisation
haben keinen Kaltstart und liefern dieses 503 nie.
Wenn mitten im Stream etwas schiefgeht
Sobald das erste Byte gesendet ist, steht der HTTP-Status fest (200). Ein späterer Fehler kommt deshalb im Stream, als drei Rahmen in dieser Reihenfolge:
data: {"id":"chatcmpl-err-…","object":"chat.completion.chunk","created":1700000000,"model":"gemma-4-12b","choices":[{"index":0,"delta":{},"finish_reason":"error"}]}
event: error
data: {"error":{"message":"upstream error","type":"api_error","param":null,"code":"upstream_502"}}
data: [DONE]- Der Abschluss-Rahmen kommt zuerst und setzt
finish_reason: "error". Daran erkennen Sie, dass die Antwort unvollständig ist. - Der Fehler-Rahmen trägt die Felder
message,type,paramundcode. [DONE]schließt den Stream wie im Erfolgsfall.
type ist eine der üblichen Kategorien (authentication_error, permission_error,
rate_limit_error, invalid_request_error, api_error). code trägt bei einem
durchgereichten Anbieterfehler dessen eigenen Code, sonst upstream_<HTTP-Status>.
Werten Sie jeden Rahmen einzeln aus und brechen Sie bei finish_reason: "error" ab.
error kommt bewusst in einem eigenen Rahmen: Viele Client-Bibliotheken verwerfen das Feld,
wenn es im selben Rahmen wie choices steht, und der Fehler ginge verloren.
Bildeingabe (Vision)
Nur bei Modellen mit accepts_vision: true, z. B. gemma-4-12b und gemma-4-26b-a4b:
{
"model": "gemma-4-26b-a4b",
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Was ist auf dem Bild?" },
{ "type": "image_url", "image_url": { "url": "https://…/foto.jpg" } }
]
}
]
}image_url.url kann eine öffentliche URL oder eine Base64-Data-URL sein
(data:image/png;base64,…).
Function-Calling (Tools)
Wie bei OpenAI. Ob ein Modell Werkzeuge beherrscht, steht in accepts_tools von
GET /v1/models. Beispiel:
{
"model": "gemma-4-12b",
"messages": [{ "role": "user", "content": "Was kostet ein Bitcoin?" }],
"tools": [{
"type": "function",
"function": {
"name": "get_price",
"description": "Holt aktuellen Preis",
"parameters": {
"type": "object",
"properties": { "symbol": { "type": "string" } },
"required": ["symbol"]
}
}
}]
}Die Antwort enthält tool_calls wie bei OpenAI. Ihr Client führt die Funktion aus und schickt
das Ergebnis als Nachricht mit role: "tool" zurück.
Gedankengang (reasoning_content)
Modelle mit sichtbarem Gedankengang, etwa qwen3.5-9b-deepseek-v4 (reasoning-Tier), liefern
ihn im eigenen Feld reasoning_content, getrennt vom Antworttext in content. Im Stream steht
er im delta. Der Gedankengang zählt gegen max_tokens.