Update API documentation with Llama 3.1 quirks
This commit is contained in:
+108
-34
@@ -1,7 +1,7 @@
|
|||||||
# Ionos LLM API: Quirks und Workarounds
|
# Ionos LLM API: Quirks und Workarounds
|
||||||
|
|
||||||
**Stand:** 2026-05-03 — empirisch ermittelt gegen
|
**Stand:** 2026-05-03 (zweite Session, in Doctate umgesetzt) — empirisch
|
||||||
`https://openai.inference.de-txl.ionos.com/v1/chat/completions`.
|
ermittelt gegen `https://openai.inference.de-txl.ionos.com/v1/chat/completions`.
|
||||||
Verhalten kann sich aendern; Datum oben mit dem aktuellen Test-Befund
|
Verhalten kann sich aendern; Datum oben mit dem aktuellen Test-Befund
|
||||||
abgleichen, bevor man auf diese Notizen baut.
|
abgleichen, bevor man auf diese Notizen baut.
|
||||||
|
|
||||||
@@ -15,15 +15,22 @@ abgleichen, bevor man auf diese Notizen baut.
|
|||||||
`https://api.ionos.com/docs/inference-openai/v1/`. Das ist eine
|
`https://api.ionos.com/docs/inference-openai/v1/`. Das ist eine
|
||||||
Redoc-Seite mit eingebetteter OpenAPI-3.0.3-Spec — die einzige
|
Redoc-Seite mit eingebetteter OpenAPI-3.0.3-Spec — die einzige
|
||||||
verlaessliche Quelle fuer unterstuetzte Parameter, Defaults und Limits.
|
verlaessliche Quelle fuer unterstuetzte Parameter, Defaults und Limits.
|
||||||
- Llama 3.1 405B FP8 hat einen **vLLM-Tokenizer-Bug**: das
|
- Llama 3.1 405B FP8 hat ein bekanntes **End-of-Turn-Termination-Problem**:
|
||||||
End-of-Turn-Token `<|eot_id|>` wird als Klartext-String
|
in der Sandbox kam `<|eot_id|>` als ASCII-`assistant\n\n` zurueck, was
|
||||||
`assistant\n\n` ausgegeben statt als Special-Token. Folge: der
|
Quelle der ersten Diagnose war. Aber das eigentliche Problem ist breiter
|
||||||
offizielle `stop`-Parameter mit `<|eot_id|>` feuert nie, das Modell
|
und modell-inhaerent: Llama 3.1 verfaellt mit Greedy-Decoding und/oder
|
||||||
laeuft bis `max_tokens` voll.
|
konflikthaltigen Prompts in Endlos-Schleifen (bestaetigt von Ionos selbst,
|
||||||
- **Empfohlene Loesung fuer Doctate**: `response_format: json_schema`.
|
HuggingFace-Discussion #32, vLLM-Issues #13530, #13828).
|
||||||
Schema-erzwungenes Decoding stoppt natuerlich am schliessenden `}`,
|
- **Empfohlene Loesung fuer Doctate (Drei-Komponenten-Kombi):**
|
||||||
funktioniert modell-agnostisch und braucht keine Provider-spezifischen
|
`temperature: 0.6 + top_p: 0.9` (Sampling) + `response_format:
|
||||||
Workarounds.
|
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 braucht weder Sampling noch Format-Hinweis —
|
||||||
|
es ist schema-aware trainiert und laeuft mit dem Default-Body sauber.
|
||||||
|
Die Drei-Komponenten-Loesung ist Llama-spezifisch, nicht universell.
|
||||||
|
|
||||||
## So kommt man an die richtige API-Info
|
## So kommt man an die richtige API-Info
|
||||||
|
|
||||||
@@ -176,6 +183,8 @@ Gateway-Timeout.
|
|||||||
|
|
||||||
### Empirische Datentabelle
|
### Empirische Datentabelle
|
||||||
|
|
||||||
|
#### Session 1 (Diagnose, Sandbox-Inputs)
|
||||||
|
|
||||||
Alle Tests mit `meta-llama/Meta-Llama-3.1-405B-Instruct-FP8`,
|
Alle Tests mit `meta-llama/Meta-Llama-3.1-405B-Instruct-FP8`,
|
||||||
Input `"Sag hallo."` (ausser T22 = realer Doctate-Body, ~1.5k Prompt-Tokens):
|
Input `"Sag hallo."` (ausser T22 = realer Doctate-Body, ~1.5k Prompt-Tokens):
|
||||||
|
|
||||||
@@ -193,8 +202,41 @@ Input `"Sag hallo."` (ausser T22 = realer Doctate-Body, ~1.5k Prompt-Tokens):
|
|||||||
| T22 | T21 + realer Doctate-Body (1539 Prompt-Tokens) | **22.77 s, finish=stop, 482 Tokens** | 22.7 |
|
| T22 | T21 + realer Doctate-Body (1539 Prompt-Tokens) | **22.77 s, finish=stop, 482 Tokens** | 22.7 |
|
||||||
|
|
||||||
Konsistenz: **`finish_reason: "length"` heisst Modell wollte
|
Konsistenz: **`finish_reason: "length"` heisst Modell wollte
|
||||||
weiter** — kein sauberer Stop. Nur die letzten vier Loesungswege
|
weiter** — kein sauberer Stop.
|
||||||
bekommen sauberes `"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.
|
||||||
|
|
||||||
## Loesungswege im Vergleich
|
## Loesungswege im Vergleich
|
||||||
|
|
||||||
@@ -202,33 +244,60 @@ bekommen sauberes `"stop"`.
|
|||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| `stop: ["assistant"]` | sehr schnell | nein | nein | Patch fuer Provider-Bug; bricht im englischen Text |
|
| `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 |
|
| Eigener End-Marker via System-Prompt | sehr schnell | mittel | ja | Modell kann Marker bei langem Output vergessen |
|
||||||
| `response_format: json_schema` | schnell | ja | **ja** | Server erzwingt Stop ueber Grammar-Decoding |
|
| `response_format: json_schema` allein | – | nein | nein | **In Production NICHT ausreichend** (T1, T4) — kippt entweder in Timeout oder silent deletion |
|
||||||
| Modellwechsel auf `gpt-oss-120b` / `Llama-3.3-70B` | sehr schnell | ja | – | Quirks A+B betreffen diese Modelle nicht |
|
| **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: **`response_format: json_schema`** als Default-Pfad fuer
|
**Empfehlung pro Modell-Familie:**
|
||||||
Doctate, weil derselbe Code-Pfad auf gpt-oss-120b und Llama 3.3 70B
|
- Schema-aware Modelle (gpt-oss-120b, vermutlich Llama 3.3 70B):
|
||||||
unveraendert weiter funktioniert. Wenn das Schema-Decoding bei
|
`temperature: 0.5 + response_format: json_schema`. Body bleibt klein
|
||||||
zukuenftigen, viel groesseren Outputs Latenzprobleme macht, wechselt
|
und stabil.
|
||||||
man auf einen schnelleren Modell-Endpoint, nicht zurueck auf
|
- Llama 3.1 405B FP8: nur als **Drei-Komponenten-Kombi** brauchbar —
|
||||||
String-Hacks.
|
`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.
|
||||||
|
|
||||||
|
## Doctate-Implementation (`server/src/analyze/backend.rs`)
|
||||||
|
|
||||||
|
Die Drei-Komponenten-Kombi lebt als Per-Backend-Konfiguration:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
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` injiziert `format_instruction` als zweite `system`-Message
|
||||||
|
zwischen `system_prompt` und `user`-Content, falls `Some`. Backends
|
||||||
|
ohne `format_instruction` (gpt-oss) erzeugen den exakten Body wie vor
|
||||||
|
dem Llama-Fix — byte-stabil, gepinnt durch
|
||||||
|
`gpt_oss_has_no_format_instruction_and_no_top_p`-Test.
|
||||||
|
|
||||||
|
`worker.rs` hat einen **Empty-Guard** nach `vocab.replace`: leere
|
||||||
|
`document`-Strings (Failure-Mode T4) loesen einen Failure-Marker statt
|
||||||
|
einer leeren `document.md` aus.
|
||||||
|
|
||||||
## Entscheidungsbaum
|
## Entscheidungsbaum
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TD
|
graph TD
|
||||||
A[Neuer Aufruf gegen Ionos] --> B{Brauche ich strukturierten Output?}
|
A[Neuer Backend-Profil-Eintrag in backend.rs] --> B{Modell?}
|
||||||
B -->|Ja oder unsicher| C[response_format: json_schema setzen]
|
B -->|gpt-oss-120b<br/>schema-aware| C[Default: temp 0.5,<br/>use_json_schema=true,<br/>kein top_p, keine format_instruction]
|
||||||
B -->|Nein, freier Text| D{Modell?}
|
B -->|Llama 3.1 405B FP8| D[Drei-Komponenten-Kombi:<br/>temp 0.6 + top_p 0.9 + schema<br/>+ format_instruction]
|
||||||
D -->|gpt-oss-120b oder Llama 3.3 70B| E[Standard-Aufruf reicht]
|
B -->|Llama 3.3 70B<br/>noch nicht getestet| E[Erst gegen Doctate-Body testen,<br/>vermutlich wie gpt-oss]
|
||||||
D -->|Llama 3.1 405B FP8| F[max_tokens MUSS gesetzt sein]
|
B -->|Anderes neues Modell| F{Schema-aware trainiert?}
|
||||||
F --> G{Wie soll gestoppt werden?}
|
F -->|Ja| C
|
||||||
G -->|Sauber via Schema| C
|
F -->|Nein| D
|
||||||
G -->|String-Hack ok| H["stop: array enthaelt 'assistant'"]
|
C --> G[Body byte-stabil zu pre-Llama-Fix]
|
||||||
G -->|Eigener Marker| I[System-Prompt instruiert + stop matcht]
|
D --> H[Worker-Empty-Guard schuetzt vor silent deletion]
|
||||||
C --> J[Output ist JSON, server-seitig parsen]
|
E --> G
|
||||||
E --> K[Output direkt nutzen]
|
|
||||||
H --> K
|
|
||||||
I --> K
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quellen und Verweise
|
## Quellen und Verweise
|
||||||
@@ -254,6 +323,11 @@ einem neuen Bug auf diese Workarounds setzt, immer zuerst pruefen, ob:
|
|||||||
`assistant\n\n` ankommt — dann ist Quirk A behoben und die
|
`assistant\n\n` ankommt — dann ist Quirk A behoben und die
|
||||||
String-Hacks koennen weg.
|
String-Hacks koennen weg.
|
||||||
3. Die OpenAPI-Spec neue/geaenderte Parameter zeigt.
|
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.
|
||||||
|
|
||||||
Datum oben aktualisieren, wenn man verifiziert hat, dass diese
|
Datum oben aktualisieren, wenn man verifiziert hat, dass diese
|
||||||
Dokumentation noch dem realen Backend-Verhalten entspricht.
|
Dokumentation noch dem realen Backend-Verhalten entspricht.
|
||||||
|
|||||||
Reference in New Issue
Block a user