0848e9581f
This commit adjusts the configuration for the `gpt-oss-120b` model to run in plain-text mode. This is necessary because when `reasoning_effort` is set to "medium", the reasoning tokens combined with schema-constrained decoding can exhaust the `max_completion_tokens` budget, leading to an empty content response. The change restores the previous behavior where `gpt-oss-120b` was configured without `response_format` or `top_p`, ensuring the request body sent to Ionos remains byte-identical to the pre-multi-backend state. This prevents regressions and maintains stability for existing production workflows.
390 lines
18 KiB
Markdown
390 lines
18 KiB
Markdown
# Ionos LLM API: Quirks und Workarounds
|
||
|
||
**Stand:** 2026-05-03 (zweite Session, in Doctate umgesetzt) — empirisch
|
||
ermittelt gegen `https://openai.inference.de-txl.ionos.com/v1/chat/completions`.
|
||
Verhalten kann sich aendern; Datum oben mit dem aktuellen Test-Befund
|
||
abgleichen, bevor man auf diese Notizen baut.
|
||
|
||
## TL;DR
|
||
|
||
- Die **Marketing-Doku** unter `docs.ionos.com/cloud/ai/...` ist
|
||
unvollstaendig. Die dort gezeigten Beispiele fuer Llama 3.1 405B FP8
|
||
laufen in der Praxis in einen Gateway-Timeout, weil sie wesentliche
|
||
Parameter weglassen.
|
||
- Die **echte aktuelle API-Spezifikation** liegt unter
|
||
`https://api.ionos.com/docs/inference-openai/v1/`. Das ist eine
|
||
Redoc-Seite mit eingebetteter OpenAPI-3.0.3-Spec — die einzige
|
||
verlaessliche Quelle fuer unterstuetzte Parameter, Defaults und Limits.
|
||
- Llama 3.1 405B FP8 hat ein bekanntes **End-of-Turn-Termination-Problem**:
|
||
in der Sandbox kam `<|eot_id|>` als ASCII-`assistant\n\n` zurueck, was
|
||
Quelle der ersten Diagnose war. Aber das eigentliche Problem ist breiter
|
||
und modell-inhaerent: Llama 3.1 verfaellt mit Greedy-Decoding und/oder
|
||
konflikthaltigen Prompts in Endlos-Schleifen (bestaetigt von Ionos selbst,
|
||
HuggingFace-Discussion #32, vLLM-Issues #13530, #13828).
|
||
- **Empfohlene Loesung fuer Doctate (Drei-Komponenten-Kombi):**
|
||
`temperature: 0.6 + top_p: 0.9` (Sampling) + `response_format:
|
||
json_schema` (Form-Garantie) + expliziter Format-Hinweis als zweite
|
||
`system`-Message (sagt dem Modell WAS in `document` zu schreiben ist).
|
||
Alle drei sind notwendig; jede einzelne weggelassen kippt in einen
|
||
anderen Failure-Mode (Timeout, Endlos-Schleife, oder silent
|
||
`{"document": ""}`).
|
||
- **Wichtig**: gpt-oss-120b laeuft mit `reasoning_effort: medium`
|
||
am besten im **plain-text mode** (kein `response_format`, kein
|
||
`top_p`, keine zweite system-Message). Reasoning-Tokens zaehlen gegen
|
||
`max_completion_tokens`, und schema-constrained decoding zusaetzlich
|
||
zum Reasoning kann das Budget so leersaugen, dass `content: null`
|
||
zurueckkommt (Test T7 / Production-Failure auf Case c414cf52,
|
||
6 recordings, ~3.8k chars). Doctate setzt deshalb fuer gpt-oss
|
||
`use_json_schema: false` — Body ist byte-identisch zum
|
||
pre-multi-backend-Stand (commit bf6464d).
|
||
- Die Drei-Komponenten-Loesung ist also **Llama-spezifisch**, nicht
|
||
universell. Backends mit Reasoning brauchen das Gegenteil: schema OFF.
|
||
|
||
## So kommt man an die richtige API-Info
|
||
|
||
### Verfuegbare Modelle abfragen
|
||
|
||
Die Marketing-Doku listet Modelle, ist aber nicht immer synchron mit dem
|
||
Backend. Authoritative Liste:
|
||
|
||
```bash
|
||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||
https://openai.inference.de-txl.ionos.com/v1/models | jq .
|
||
```
|
||
|
||
Aktuell (2026-05-03) verfuegbar:
|
||
|
||
| Modell | Kategorie | Anmerkung |
|
||
|---|---|---|
|
||
| `meta-llama/Meta-Llama-3.1-8B-Instruct` | Chat | schnell, klein |
|
||
| `meta-llama/Meta-Llama-3.1-405B-Instruct-FP8` | Chat | siehe Quirks unten |
|
||
| `meta-llama/Llama-3.3-70B-Instruct` | Chat | stoppt sauber, <1s |
|
||
| `mistralai/Mistral-Nemo-Instruct-2407` | Chat | ungetestet |
|
||
| `mistralai/Mistral-Small-24B-Instruct` | Chat | siehe Doctate-Memory |
|
||
| `openai/gpt-oss-120b` | Reasoning | stoppt sauber, vorherige Doctate-Wahl |
|
||
| `BAAI/bge-m3`, `bge-large-en-v1.5` | Embedding | – |
|
||
| `sentence-transformers/paraphrase-multilingual-mpnet-base-v2` | Embedding | – |
|
||
| `meta-llama/CodeLlama-13b-Instruct-hf` | Code | – |
|
||
| `Qwen/Qwen3-Coder-Next` | Code | – |
|
||
| `lightonai/LightOnOCR-2-1B` | OCR | – |
|
||
| `black-forest-labs/FLUX.1-schnell` | Bild | – |
|
||
|
||
### Aktuelle OpenAPI-Spec extrahieren
|
||
|
||
Die Redoc-Seite rendert die Spec clientseitig — `openapi.json` ist nicht
|
||
unter einer stabilen URL erreichbar, sondern liegt eingebettet im HTML
|
||
als JavaScript-Variable `__redoc_state`. Extraktion mit Brace-Balancing,
|
||
weil ein Regex auf `</script>` zu viel mitnimmt:
|
||
|
||
```python
|
||
import json, re
|
||
import urllib.request
|
||
|
||
html = urllib.request.urlopen(
|
||
"https://api.ionos.com/docs/inference-openai/v1/").read().decode()
|
||
start = html.find('{"menu":{"activeItemIdx":-1}')
|
||
depth, in_str, esc = 0, False, False
|
||
for i, c in enumerate(html[start:], start):
|
||
if in_str:
|
||
if esc: esc = False
|
||
elif c == '\\': esc = True
|
||
elif c == '"': in_str = False
|
||
else:
|
||
if c == '"': in_str = True
|
||
elif c == '{': depth += 1
|
||
elif c == '}':
|
||
depth -= 1
|
||
if depth == 0:
|
||
end = i + 1; break
|
||
|
||
spec = json.loads(html[start:end])['spec']['data']
|
||
print(json.dumps(spec['paths']['/v1/chat/completions']['post'], indent=2))
|
||
```
|
||
|
||
### Parameter-Schema fuer `chat/completions` (Stand 2026-05-03)
|
||
|
||
Wichtige Defaults und Limits aus der Spec (Auszug, fett = abweichend von
|
||
OpenAI):
|
||
|
||
| Parameter | Typ | Default | Anmerkung |
|
||
|---|---|---|---|
|
||
| `model` | string | required | – |
|
||
| `messages` | array | required | – |
|
||
| `temperature` | number | **1** | – |
|
||
| `top_p` | number | **-1** | Sentinel fuer "deaktiviert" |
|
||
| `n` | int | 1 | – |
|
||
| `stream` | bool | false | – |
|
||
| `stop` | array | – | **max 4 Sequenzen** |
|
||
| `max_tokens` | int | **16** | **deprecated** zugunsten `max_completion_tokens` |
|
||
| `max_completion_tokens` | int | **16** | – |
|
||
| `presence_penalty` | number | 0 | – |
|
||
| `frequency_penalty` | number | 0 | – |
|
||
| `logit_bias` | object | – | Token-ID -> Bias |
|
||
| `response_format` | object | – | `text` / `json_object` / `json_schema` |
|
||
| `tools`, `tool_choice` | – | – | Function Calling |
|
||
|
||
**Nicht exposed** (wuerde es mit anderen vLLM-Backends geben):
|
||
`repetition_penalty`, `min_p`, `top_k`, `skip_special_tokens`,
|
||
`ignore_eos`, `stop_token_ids`. Wer diese vLLM-Backdoors braucht, ist
|
||
auf Ionos falsch.
|
||
|
||
### Warum die Marketing-Doku nicht reicht
|
||
|
||
`https://docs.ionos.com/cloud/ai/ai-model-hub/models/llms/meta-llama-3-1-405b`
|
||
zeigt das Beispiel:
|
||
|
||
```json
|
||
{ "model": "...405B-FP8", "messages": [...],
|
||
"temperature": 0.7, "max_tokens": 100 }
|
||
```
|
||
|
||
Das funktioniert nur deshalb, weil `max_tokens: 100` zufaellig klein
|
||
genug ist, um vor Quirk B (siehe unten) zurueckzukommen. Sobald man
|
||
realistische Ausgabelaengen will (z.B. 2k+ Tokens fuer einen Arztbrief),
|
||
laeuft die Combo in den Gateway-Timeout, weil die Doku den Tokenizer-Bug
|
||
nicht erwaehnt und die Empfehlungen nicht praxistauglich validiert sind.
|
||
|
||
## Llama 3.1 405B FP8: Drei Quirks
|
||
|
||
### Quirk A — `<|eot_id|>` wird als ASCII geleakt
|
||
|
||
Das Llama-3.1-Chat-Template nutzt `<|eot_id|>` (End-of-Turn) als
|
||
Stop-Marker. Auf Ionos's vLLM-Deployment ist dieses Token nicht
|
||
korrekt im Tokenizer als Special-Token registriert. Folge: das Modell
|
||
emittiert es als die einzelnen ASCII-Zeichen `a s s i s t a n t \n \n`
|
||
und labert weiter. Beobachtbar an Outputs der Form:
|
||
|
||
```
|
||
Hallo! Wie kann ich helfen?assistant\n\n
|
||
Oder moechtest du einfach plaudern?assistant\n\n
|
||
Entschuldigung, ich habe mich wiederholt...assistant\n\n
|
||
```
|
||
|
||
Konsequenz: der `stop`-Parameter mit `["<|eot_id|>"]` greift nie, weil
|
||
diese String-Sequenz nicht im Output auftaucht. Workarounds:
|
||
|
||
| Workaround | Funktioniert | Sauber? |
|
||
|---|---|---|
|
||
| `stop: ["assistant"]` zusaetzlich | ja, 0.79 s fuer "Sag hallo." | unsauber, Provider-Bug-Patch |
|
||
| System-Prompt instruiert eigenen End-Marker, `stop` matcht ihn | ja, 0.89 s | mittel, Instruction-Drift moeglich |
|
||
| `response_format: json_schema` | ja, 0.84 s | sauber, offizielles Feature |
|
||
|
||
### Quirk B — Ohne `max_tokens` laeuft das Modell bis Context-Ceiling
|
||
|
||
Da das Modell selbst keinen Stop-Token emittiert (Quirk A) und
|
||
`max_tokens` Default = 16 nur theoretisch greift, generiert FP8-405B
|
||
auf vielen Pfaden bis zum Kontext-Limit (128k Tokens) weiter. Bei
|
||
~20 tok/s Generierungsrate sind das ~107 Minuten — laenger als jeder
|
||
Gateway-Timeout. Beobachtung: HTTP 504 "stream timeout" nach ~3
|
||
Minuten, oder schon `HTTP 000` clientseitig.
|
||
|
||
Konsequenz: **immer** `max_tokens` (oder `max_completion_tokens`)
|
||
explizit setzen. Realistische Werte fuer Doctate-Arztbriefe: 2048-4096.
|
||
|
||
### Quirk C — Generierungsrate ~20-23 tok/s
|
||
|
||
Empirisch ueber 6 Runs ermittelt (siehe Tabelle unten). Das ist deutlich
|
||
langsamer als `Llama-3.3-70B-Instruct` (~1 s fuer 10 Tokens) und
|
||
`gpt-oss-120b` (~0.5 s fuer 10 Tokens) auf demselben Endpoint.
|
||
Konsequenz: praktischer Hardlimit bei ~3000 Output-Tokens, sonst
|
||
Gateway-Timeout.
|
||
|
||
### Empirische Datentabelle
|
||
|
||
#### Session 1 (Diagnose, Sandbox-Inputs)
|
||
|
||
Alle Tests mit `meta-llama/Meta-Llama-3.1-405B-Instruct-FP8`,
|
||
Input `"Sag hallo."` (ausser T22 = realer Doctate-Body, ~1.5k Prompt-Tokens):
|
||
|
||
| Test | Parameter | Ergebnis | Tok/s |
|
||
|---|---|---|---|
|
||
| T1 | `temp=0`, `max_tokens=8192` | Timeout 25 s | – |
|
||
| T3 | `temp=0`, `max_tokens=100` | 5.1 s, finish=length, 100 Tokens | 19.6 |
|
||
| T8 | `temp=0`, `max_tokens=500` | 23 s, finish=length, 500 Tokens | 21.7 |
|
||
| T9 | `temp=0`, `max_tokens=1500` | 114 s, finish=length, 1500 Tokens | 13.2 |
|
||
| T10 | `temp=0`, `max_tokens=3000` | 183 s, finish=length, 3000 Tokens | 16.4 |
|
||
| T11 | `temp=0`, `max_tokens=4500` | Gateway-Timeout (curl 200 s) | – |
|
||
| T17 | Doku-Combo + `stop:["...","assistant"]` | **0.79 s, finish=stop, 11 Tokens** | – |
|
||
| T19 | Doku-Combo + System-Marker `<|12345|>` | **0.89 s, finish=stop, 14 Tokens** | – |
|
||
| T21 | `response_format: json_schema` (nur `document:string`) | **0.84 s, finish=stop, 8 Tokens** | – |
|
||
| T22 | T21 + realer Doctate-Body (1539 Prompt-Tokens) | **22.77 s, finish=stop, 482 Tokens** | 22.7 |
|
||
|
||
Konsistenz: **`finish_reason: "length"` heisst Modell wollte
|
||
weiter** — kein sauberer Stop.
|
||
|
||
#### Session 2 (Reproduktion gegen Production-Pipeline, 30 s Timeout)
|
||
|
||
Alle Tests gegen den realen Doctate-Body von Case `aecf5890` (236
|
||
char user_content, ~1040-1115 Prompt-Tokens). Skripte unter
|
||
`/tmp/doctate-llama-test/run{1..6}.py`. Production-`LLAMA_SYSTEM_PROMPT`
|
||
verbatim (2819 chars).
|
||
|
||
| Test | Sampling | json_schema | Format-Hinweis | Wallclock | finish | Output |
|
||
|---|---|---|---|---|---|---|
|
||
| T1 | nein (`temp=0.5`) | ja | nein | **30 s Timeout** | – | nichts geliefert |
|
||
| T2 | nein (`temp=0.5`) | ja | im Prompt inline | 3.26 s | stop | sauber, 61 Tokens |
|
||
| T3 | ja (`0.6 + 0.9 + freq=0.1 + stop`) | nein (Ionos-Doku verbatim) | nein | **30 s Timeout** | – | nichts geliefert |
|
||
| T4 | ja (`0.6 + 0.9`) | ja | nein | 0.98 s | stop | **`{"document":""}` (silent deletion)** |
|
||
| T5 | ja (`0.6 + 0.9`) | ja | im Prompt inline | 3× ⌀ 3.05 s | stop | sauber, 61 Tokens, 3× stabil |
|
||
| T6 | ja (`0.6 + 0.9`) | ja | als zweite `system`-Message | 3× ⌀ 3.13 s | stop | sauber, 61 Tokens, 3× stabil — **Production-Konfig** |
|
||
|
||
**Wichtige Befunde aus Session 2:**
|
||
|
||
1. **json_schema allein reicht nicht (T1)**: Production-Pfad mit
|
||
`temperature: 0.5` ohne `top_p` timeoutet, weil Llama Schema-Resampling
|
||
als Endlos-Schleife verkraftet.
|
||
2. **Sampling allein reicht nicht (T3)**: Selbst die exakte Ionos-Doku-
|
||
Empfehlung mit `stop: ["<|eot_id|>", "<|end_of_text|>"]` timeoutet
|
||
gegen unseren 2.8k-Char-Prompt. Die Doku-Empfehlung ist fuer kuerzere
|
||
Prompts validiert.
|
||
3. **Sampling + Schema ohne Hinweis kippt in silent deletion (T4)**:
|
||
Modell respektiert die Form, weiss aber inhaltlich nicht was tun.
|
||
`{"document": ""}` waere fuer Doctate als leere Notiz im UI gelandet
|
||
— gefaehrlicher als Timeout, weil der Worker `remove_failure_marker`
|
||
aufruft. **Worker.rs hat seither einen Empty-Guard.**
|
||
4. **Drei-Komponenten-Kombi laeuft 3x stabil (T5, T6)**: Sampling +
|
||
Schema + Format-Hinweis. T6 (separate `system`-Message) ist
|
||
architektonisch sauberer und gewaehlt fuer die Doctate-Implementation.
|
||
|
||
#### Session 3 (gpt-oss-Regression vom Multi-Backend-Default, 30 s Timeout)
|
||
|
||
Modell `openai/gpt-oss-120b` mit `reasoning_effort: "medium"`,
|
||
`max_completion_tokens: 4096`. Input: realer Doctate-Body Case
|
||
`c414cf52` (6 Aufnahmen, 3851 char user_content, ~1900 prompt_tokens
|
||
nach DEFAULT_SYSTEM_PROMPT).
|
||
|
||
| Test | response_format | Wallclock | finish | content | completion_tokens |
|
||
|---|---|---|---|---|---|
|
||
| Production 14:23 | `json_schema` (DEFAULT_USE_JSON_SCHEMA) | 58 s | – | **null** (`LlmError::Parse`) | – |
|
||
| T7 (nach Fix) | weggelassen | 15.68 s | stop | sauberer Arztbrief, 3685 chars | 1692 |
|
||
|
||
**Ursache**: Mit `reasoning_effort: medium` zaehlen Reasoning-Tokens
|
||
gegen `max_completion_tokens`. Schema-constrained decoding (vLLM
|
||
grammar) belastet zusaetzlich das CPU-Budget. Bei Bodies dieser
|
||
Groesse fressen Reasoning + Schema-Resampling die 4096 Tokens auf,
|
||
bevor das Modell zur Inhalts-Generierung kommt — Resultat:
|
||
`choices[0].message.content == null`.
|
||
|
||
**Pre-Multi-Backend** (commit bf6464d) lief gpt-oss ohne
|
||
`response_format`, also reasoning + plain text → content kam immer
|
||
zurueck. Der Multi-Backend-Layer hat `DEFAULT_USE_JSON_SCHEMA = true`
|
||
universell aktiviert, was fuer Llama noetig, fuer gpt-oss aber
|
||
schaedlich war. Fix: `gpt_oss_120b` setzt jetzt explizit
|
||
`with_use_json_schema(false)` — Body byte-identisch zu pre-bf6464d.
|
||
|
||
## Loesungswege im Vergleich
|
||
|
||
| Loesung | Speed | Sauber | Modell-agnostisch | Kommentar |
|
||
|---|---|---|---|---|
|
||
| `stop: ["assistant"]` | sehr schnell | nein | nein | Patch fuer Provider-Bug; bricht im englischen Text |
|
||
| Eigener End-Marker via System-Prompt | sehr schnell | mittel | ja | Modell kann Marker bei langem Output vergessen |
|
||
| `response_format: json_schema` allein | – | nein | nein | **In Production NICHT ausreichend** (T1, T4) — kippt entweder in Timeout oder silent deletion |
|
||
| **Drei-Komponenten-Kombi** (Sampling + Schema + Format-Hinweis) | schnell (3 s) | ja | – | **In Doctate umgesetzt fuer Llama** (T5, T6) |
|
||
| Modellwechsel auf `gpt-oss-120b` / `Llama-3.3-70B` | sehr schnell | ja | – | Schema-aware, brauchen die Drei-Komponenten-Kombi nicht |
|
||
|
||
**Empfehlung pro Modell-Familie:**
|
||
- **Reasoning-Modelle mit `reasoning_effort != null`** (gpt-oss-120b
|
||
und Verwandte): **plain-text mode** — kein `response_format`, kein
|
||
`top_p`. Reasoning + Schema kollidieren am Token-Budget, Resultat
|
||
ist `content: null`. Doctate-Default fuer dieses Profil:
|
||
`temperature: 0.5 + reasoning_effort: medium + max_completion_tokens:
|
||
4096`.
|
||
- **Llama 3.1 405B FP8**: nur als **Drei-Komponenten-Kombi** brauchbar
|
||
— `temperature: 0.6 + top_p: 0.9 + response_format: json_schema +
|
||
expliziter Format-Hinweis als zweite system-Message`. Wer eine
|
||
Komponente weglaesst, faengt sich Timeout (T1, T3) oder silent
|
||
deletion (T4) ein.
|
||
- **Schema-aware Nicht-Reasoning-Modelle** (vermutlich Llama 3.3 70B,
|
||
Mistral Nemo): Default plain-text + `response_format: json_schema`
|
||
ist der Erwartungswert, aber **noch nicht empirisch verifiziert**
|
||
— vor Aktivierung gegen den Doctate-Body testen.
|
||
|
||
## Doctate-Implementation (`server/src/analyze/backend.rs`)
|
||
|
||
Pro-Backend-Konfiguration mit zwei verschiedenen Profilen:
|
||
|
||
```rust
|
||
// gpt-oss: schema OFF, weil Reasoning + Schema das Token-Budget aufessen.
|
||
ionos_backend("gpt_oss_120b", "GPT OSS 120b", "openai/gpt-oss-120b")
|
||
.with_reasoning_effort("medium")
|
||
.with_use_json_schema(false),
|
||
|
||
// Llama 3.1 405B FP8: Drei-Komponenten-Kombi.
|
||
const LLAMA_TEMPERATURE: f32 = 0.6;
|
||
const LLAMA_TOP_P: f32 = 0.9;
|
||
const LLAMA_FORMAT_INSTRUCTION: &str = "AUSGABEFORMAT: ...";
|
||
|
||
ionos_backend("llama_3_1_405b", "Llama 3.1 405B", "...405B-Instruct-FP8")
|
||
.with_system_prompt(LLAMA_SYSTEM_PROMPT)
|
||
.with_temperature(LLAMA_TEMPERATURE)
|
||
.with_top_p(LLAMA_TOP_P)
|
||
.with_format_instruction(LLAMA_FORMAT_INSTRUCTION),
|
||
```
|
||
|
||
`chat_once` baut den Body dynamisch:
|
||
- `response_format: json_schema` wird gesendet **gdw**
|
||
`backend.use_json_schema == true` (Llama: ja, gpt-oss: nein).
|
||
- `top_p` wird per `Option::is_none`-Skip nur gesendet wenn `Some`
|
||
(Llama: ja, gpt-oss: nein).
|
||
- Eine zweite `system`-Message wird ans `messages`-Array gehaengt,
|
||
falls `format_instruction.is_some()` (Llama: ja, gpt-oss: nein).
|
||
|
||
Damit ist der gpt-oss-Body **byte-identisch** zum pre-Multi-Backend-
|
||
Stand (commit bf6464d). Gepinnt durch
|
||
`gpt_oss_runs_in_plain_text_mode_no_schema_no_top_p_no_format_hint`-Test.
|
||
|
||
`worker.rs` hat einen **Empty-Guard** nach `vocab.replace`: leere
|
||
`document`-Strings (Failure-Mode T4 fuer Llama) loesen einen
|
||
Failure-Marker statt einer leeren `document.md` aus.
|
||
|
||
## Entscheidungsbaum
|
||
|
||
```mermaid
|
||
graph TD
|
||
A[Neuer Backend-Profil-Eintrag in backend.rs] --> B{Reasoning-Modell?<br/>reasoning_effort != null?}
|
||
B -->|Ja, gpt-oss-120b| C[plain-text mode:<br/>with_use_json_schema false,<br/>kein top_p, keine format_instruction<br/>Body byte-stabil zu pre-bf6464d]
|
||
B -->|Nein| D{Schema-aware trainiert?}
|
||
D -->|Ja, vermutlich Llama 3.3 70B,<br/>Mistral Nemo| E[Default: temp 0.5,<br/>use_json_schema=true<br/>VOR Aktivierung gegen<br/>Doctate-Body testen]
|
||
D -->|Nein, Llama 3.1 405B FP8| F[Drei-Komponenten-Kombi:<br/>temp 0.6 + top_p 0.9 + schema<br/>+ format_instruction]
|
||
F --> G[Worker-Empty-Guard schuetzt<br/>vor silent deletion]
|
||
```
|
||
|
||
## Quellen und Verweise
|
||
|
||
- API-Endpoint: `https://openai.inference.de-txl.ionos.com/v1/chat/completions`
|
||
- Authoritative API-Doku (Redoc):
|
||
`https://api.ionos.com/docs/inference-openai/v1/`
|
||
- Marketing-Doku (oft veraltet, mit Vorsicht geniessen):
|
||
`https://docs.ionos.com/cloud/ai/ai-model-hub`
|
||
- vLLM-Tokenizer-Hintergrund (warum `<|eot_id|>` als ASCII leakt):
|
||
Llama-3.1-Chat-Template + `add_special_tokens=False`-Detokenization-Pfad.
|
||
- Verwandte Doctate-Memories: `project_canary_ollama_vram_conflict`,
|
||
`project_mistral_24b_silent_deletion`,
|
||
`feedback_no_inline_llm_in_handlers`.
|
||
|
||
## Wartungshinweis
|
||
|
||
Diese Datei dokumentiert Verhalten zu einem Zeitpunkt. Bevor man bei
|
||
einem neuen Bug auf diese Workarounds setzt, immer zuerst pruefen, ob:
|
||
|
||
1. Die Modellliste sich geaendert hat (`/v1/models`).
|
||
2. `<|eot_id|>` mittlerweile im Output sauber als Token statt als
|
||
`assistant\n\n` ankommt — dann ist Quirk A behoben und die
|
||
String-Hacks koennen weg.
|
||
3. Die OpenAPI-Spec neue/geaenderte Parameter zeigt.
|
||
4. Die Drei-Komponenten-Kombi fuer Llama 3.1 weiterhin noetig ist:
|
||
schnellster Check ist, das `format_instruction`-Feld testweise auf
|
||
`None` zu setzen und einen Doctate-Case neu zu triggern. Wenn der
|
||
Output sauber kommt, kann der Format-Hinweis weg. Wenn er
|
||
`{"document":""}` ist, gilt diese Doku noch.
|
||
5. Der gpt-oss-Token-Budget-Konflikt mit `response_format: json_schema`
|
||
weiterhin existiert: testweise `with_use_json_schema(true)` fuer
|
||
gpt-oss setzen, einen 6-Aufnahmen-Case (~3.8k chars) triggern. Wenn
|
||
`LlmError::Parse: missing choices[0].message.content` im Log
|
||
erscheint, gilt diese Doku noch. Wenn der Output sauber durchkommt,
|
||
hat Ionos das Reasoning/Schema-Budget-Verhalten geaendert und
|
||
`use_json_schema=true` koennte fuer gpt-oss reaktiviert werden.
|
||
|
||
Datum oben aktualisieren, wenn man verifiziert hat, dass diese
|
||
Dokumentation noch dem realen Backend-Verhalten entspricht.
|