SovrGPT Dokumentation
API

POST /v1/chat/completions

Der Hauptendpoint — identisches Schema wie OpenAI.

Erzeugt eine Modell-Antwort auf eine Chat-Konversation.

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

FeldTypDefaultBeschreibung
modelstringModell-ID aus GET /v1/models.
messagesarrayKonversation. Roles: system, user, assistant, tool.
temperaturenumber0.70.0 – 2.0. Höher = kreativer.
top_pnumber1.0Nucleus-Sampling.
max_tokensintmodel-defaultMaximale Antwort-Länge in Tokens.
streamboolfalseTrue → SSE-Stream.
stopstring|arrayStop-Sequenzen.
toolsarrayOpenAI-Tool-Schema (Function-Calling).
tool_choicestring|object"auto""none", "auto", "required", oder spezifisches Tool.
response_formatobject{ "type": "json_object" } für JSON-Modus.
reasoning_effortstringDenk-Aufwand. Welche Modelle das auswerten, sagt effort_levels in GET /v1/models. Siehe Denk-Level.
chat_template_kwargsobjectvLLM-Chat-Template-Argumente, verbatim durchgereicht (z. B. { "enable_thinking": false } für Qwen, { "thinking": true, "reasoning_effort": "max" } für coder-max). Gewinnt gegen reasoning_effort.

Ignoriert (nicht crashend): seed, logit_bias, user, n (>1 nicht unterstützt), presence_penalty, frequency_penalty.

Denk-Level (reasoning_effort)

Viele Modelle können pro Request unterschiedlich tief „denken". Steuerung OpenAI-idiomatisch über reasoning_effort (im Python-SDK via extra_body):

curl https://sovrgpt.com/api/v1/chat/completions \
  -H "Authorization: Bearer $SOVR_KEY" -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3.8-27b",
    "messages": [{ "role": "user", "content": "Finde und behebe den Bug in diesem Stacktrace …" }],
    "reasoning_effort": "high"
  }'

Welche Modelle das auswerten

GET /v1/models weist je Modell effort_levels aus — die Stufen, die für dieses Modell gemessen wurden. Ein leeres Array heißt: dieses Modell hat keinen wirksamen Regler, ein trotzdem gesendeter Wert wird nicht übersetzt.

Wir bieten nur Stufen an, deren Wirkung wir nachgemessen haben. Ein Modell, das einen Wert zwar annimmt (HTTP 200) ihn aber ignoriert, führt hier bewusst keine Stufen — eine Zusage, die nicht eintritt, ist schlechter als keine.

WertBedeutung
weggelassenVoreinstellung des Modells.
noneNicht denken — schnellste Antwort.
minimal, lowSparsam.
mediumNormal.
high, max, xhighGründlich.

Führt ein Modell die gewünschte Stufe nicht (manche haben nur low und high), wird auf die nächstniedrigere vorhandene abgebildet — nie auf eine höhere.

Was intern damit passiert

Die Namen oben sind unsere; was beim Modell ankommt, ist verschieden und für Aufrufende bewusst unsichtbar. Beispiele aus dem laufenden Betrieb: balanced und coder-mini erwarten kein reasoning_effort, sondern einen Token-Deckel (thinking_token_budget, eine Zahl); bei einem der Fremdbetriebs-Modelle heißt die oberste Stufe xhigh und ein high würde dort mit HTTP 400 abgelehnt; bei einem anderen läuft die Skala umgekehrt. Diese Übersetzung übernimmt die Route — Sie schicken low, medium oder high.

high kann das Antwortbudget aufbrauchen. Der Gedankengang zählt gegen max_tokens. Gemessen an gpt-oss-120b: bei high und knappem max_tokens bleibt der Antworttext leer. Wer die oberste Stufe nutzt, sollte max_tokens mitziehen.

Ein explizit mitgegebenes chat_template_kwargs wird verbatim durchgereicht und gewinnt gegen reasoning_effort — wer das Feld setzt, bekommt genau das.

Antwort (non-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 hat geantwortet? Die drei Kopfzeilen

Jede Antwort — streamend, nicht streamend und auch das 503 unten — trägt:

KopfzeileImmer?Bedeutung
x-sovrgpt-model-served✅ immerDie Katalog-Id, die tatsächlich geantwortet hat.
x-sovrgpt-model-requestednur bei ErsetzungDie Id, die Sie geschickt haben.
x-sovrgpt-model-substitutednur bei Ersetzungunknown_model (die Id kennen wir nicht) oder unavailable (die Id ist uns bekannt, aber gerade nicht bedienbar — zurückgezogen, oder ein Anbieter-Schlüssel fehlt).

🔴 Warum das wichtig ist: eine unbekannte oder zurückgezogene Modell-Id scheitert NICHT, sie wird ersetzt. Sie bekommen HTTP 200 und eine brauchbare Antwort vom souveränen Standard-Modell. Das ist Absicht — eine Integration, die gestern lief, soll nicht heute an einer Katalogänderung sterben —, aber Sie sollten es merken:

curl -sD- -o/dev/null -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"}]}'

# x-sovrgpt-model-served: gemma-4-12b
# x-sovrgpt-model-requested: gpt-4o
# x-sovrgpt-model-substituted: unknown_model

⚠️ Ein Alias ist keine Ersetzung. coding löst auf qwen3.6-35b-a3b auf; dann steht nur x-sovrgpt-model-served da. Die Ersetzungs-Kopfzeilen erscheinen ausschliesslich, wenn Sie ein anderes Modell bekommen haben, als Sie bestellt haben.

🔑 Genau darin liegt der Unterschied zwischen einem Rollennamen und einer Modell-ID, und er ist am 11.09.2026 praktisch geworden: mit der Rücknahme von qwen3-coder-next-fp8 wurde der Rollenname coder auf den Nachfolger umgehängt — wer ihn schickt, bekommt weiterhin ein Coding-Modell und keine Ersetzungs-Kopfzeile. ⇒ Wenn Ihnen die Rolle wichtiger ist als das konkrete Gewicht, schicken Sie den Rollennamen.

Ein zurückgezogenes Modell: 404 model_withdrawn

Eine unbekannte ID wird ersetzt (siehe oben). Eine ID, die es bei uns einmal gab und die wir zurückgezogen haben, wird nicht ersetzt — sie scheitert, und zwar mit einem eigenen Code und dem Nachfolger im Klartext:

{
  "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.

🔑 Warum hier ein Fehler und keine Ersetzung: eine stille Ersetzung ist richtig, solange wir den Zweck der Anfrage noch treffen — bei einem Rollennamen tun wir das. Bei einer zurückgezogenen ID wüssten wir es nicht: wer ausdrücklich ein Coding-Modell benannt hat, wäre mit einem allgemeinen Modell schlechter bedient als mit einem klaren Fehler.

⚠️ Nicht mit model_not_found verwechseln. Die beiden Codes verlangen verschiedene Reaktionen:

CodeBedeutungWas hilft
model_not_foundIhre Organisation hat dieses Modell nicht freigeschaltetein Org-Admin kann es unter Einstellungen → Modelle aktivieren
model_withdrawnDas Modell gibt es nicht mehrniemand kann es aktivieren — tragen Sie den genannten Nachfolger ein

📌 Wer nur eine Zeile Absicherung einbauen will: auf die Anwesenheit von x-sovrgpt-model-substituted prüfen und protokollieren. Das Feld model im Rumpf trägt dieselbe Wahrheit, aber Sie müssten von sich aus auf die Idee kommen, es mit dem Gesendeten zu vergleichen.

Streaming (SSE)

Mit "stream": true oder Accept: text/event-stream:

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]

Format ist mit OpenAI identisch — der OpenAI-SDK-stream: true-Modus funktioniert ohne Änderungen.

Wenn das Modell erst hochfahren muss: 503

Unsere selbst betriebenen Modelle laufen auf Null herunter, wenn sie niemand benutzt. Fragen Sie eines an, für das gerade nachweislich kein Arbeiter bereit ist, bekommen Sie 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 wurde für Sie bereits angestoßen. Warten Sie Retry-After Sekunden und schicken Sie dieselbe Anfrage erneut. Je nach Modell dauert ein Kaltstart mehrere Minuten — es können also mehrere Versuche nötig sein. Die Größenordnung je Modell steht in der Meldung und in Modelle.

Läuft die Antwort erst einmal, wird sie nicht abgebrochen. Wir prüfen nur vor dem Absenden. Ein einmal begonnener Lauf darf so lange dauern, wie er braucht — auch wenn das Minuten sind.

Modelle im Partner-Betrieb (tensorx-…, stackit-…) und Ihre eigenen Endpunkte 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 Fehler danach kann deshalb nur noch im Strom gemeldet werden. Sie bekommen dann 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 ist ein eigener Rahmen und trägt die vier bekannten Felder message, type, param, code.
  • [DONE] schließt den Strom 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> — der Status ist damit maschinenlesbar und steckt nicht im Fließtext.

Warum error und choices getrennt kommen. Gängige Client-Bibliotheken prüfen jeden Rahmen gegen ein Schema mit mehreren Zweigen. Steht error im selben Rahmen wie choices, greift der choices-Zweig zuerst und das error-Feld wird verworfen — der Fehler verschwindet dann spurlos. Getrennte Rahmen vermeiden das. Wenn Sie selbst parsen: behandeln Sie jeden Rahmen einzeln und brechen Sie bei finish_reason: "error" ab.

Vision-Input

Nur bei Modellen mit accepts_vision: true (vision-Tier, default-Tier ab 3.5):

{
  "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 Public-URL oder ein Base64-Data-URL sein (data:image/png;base64,…).

Function-Calling / Tools

Vollständig OpenAI-kompatibel. 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 analog zu OpenAI. Der Client führt die Funktion aus und schickt das Ergebnis als role: "tool"-Message zurück.

Reasoning-Blöcke

Bei reasoning-Tier-Modellen enthält die Antwort zusätzlich <think>…</think>- Blöcke vor der finalen Antwort. UIs können das ein- oder ausblenden — das SovrGPT-UI klappt sie standardmäßig zu.

Die Überschrift hieß bis 2026-08 „Fehler & Reasoning", beschrieb aber nur die Reasoning-Blöcke und kein einziges Fehlerverhalten. Fehler stehen jetzt dort, wo sie auftreten: im Stream und als regulärer HTTP-Status davor.

POST /v1/chat/completions