fix(openai-wrapper): Kontext-Ueberlauf als OpenAI-400 context_length_exceeded

Bisher kam context_exceeded als 502 an — OpenAI-Clients (litellm/OpenHands)
hielten das fuer einen Serverfehler statt zu kuerzen/verdichten. Jetzt 400
mit code context_length_exceeded und dem OpenAI-ueblichen Text inkl. Zahlen.

Doku: Abschnitt "Den Verlauf verwaltet das Programm" (Kuerzen, Verdichten,
Gedaechtnis) fuer Drittprogramme.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 15:04:09 +01:00
co-authored by Claude Opus 5.5
parent c1b06aa26a
commit 48fc88d430
2 changed files with 32 additions and 1 deletions
+23 -1
View File
@@ -296,7 +296,10 @@ steht und welche Modelle es gibt. Im Programm eintragen:
- `POST /v1/chat/completions` — auch `stream: true` (kommt derzeit in einem - `POST /v1/chat/completions` — auch `stream: true` (kommt derzeit in einem
Rutsch) und `tools` Rutsch) und `tools`
- Fehler als HTTP-Fehler mit OpenAI-Fehlerobjekt: nicht geladenes Modell → - Fehler als HTTP-Fehler mit OpenAI-Fehlerobjekt: nicht geladenes Modell →
404 `model_not_found`, Cluster nicht erreichbar → 503, Zeitüberschreitung → 504 404 `model_not_found`, Cluster nicht erreichbar → 503, Zeitüberschreitung → 504,
Verlauf zu lang → **400 `context_length_exceeded`** mit dem OpenAI-üblichen Text
(„maximum context length is N tokens …") — daran erkennen OpenAI-Clients
(litellm, OpenHands …) den Überlauf und kürzen/verdichten selbst
- Antwort-Header `X-Aria-Node` / `X-Aria-Gpu` sagen, wer geantwortet hat - Antwort-Header `X-Aria-Node` / `X-Aria-Gpu` sagen, wer geantwortet hat
- **Läuft das Programm selbst in Docker**, ist `127.0.0.1` dort der eigene - **Läuft das Programm selbst in Docker**, ist `127.0.0.1` dort der eigene
Container. Dann das Docker-Netz `aria-llm` anhängen und Container. Dann das Docker-Netz `aria-llm` anhängen und
@@ -333,6 +336,25 @@ Boxen den Wrapper (B) nehmen.
--- ---
## Den Verlauf verwaltet das Programm
Die Box merkt sich **nichts** zwischen zwei Anfragen. Jede Anfrage muss alles
mitbringen, was das Modell sehen soll (Systemprompt, Werkzeuge, Verlauf). Das
Kontextfenster (`ctx`) ist die Obergrenze für **eine** Anfrage inklusive
Antwort. Wächst der Verlauf darüber hinaus, muss das Programm ihn kleiner
machen — drei übliche Wege, auch kombiniert:
| Weg | Was passiert | Gut für |
|---|---|---|
| **Kürzen** | älteste Nachrichten weglassen (Systemprompt behalten) | einfache Chats, Werkzeug-Schleifen |
| **Verdichten** („Compacting") | alte Teile vom Modell zusammenfassen lassen, Zusammenfassung statt Original mitsenden | lange Arbeitssitzungen |
| **Gedächtnis** (RAG) | Wichtiges als Fakten in einer Datenbank ablegen, pro Anfrage nur Passendes dazuholen | dauerhaftes Wissen über Sitzungen hinweg |
Wichtig dabei: Systemprompt **und** Werkzeug-Definitionen zählen mit, Platz
für die Antwort (`max_tokens`) einplanen, Token grob schätzen (Deutsch/Code:
eher 2,5–3 Zeichen pro Token) — und `context_exceeded` trotzdem abfangen:
kürzen, einmal neu senden.
## Was Programme nicht senden ## Was Programme nicht senden
`llm_provision_model`, `llm_unload_model` und `llm_remove_model` sind der `llm_provision_model`, `llm_unload_model` und `llm_remove_model` sind der
+9
View File
@@ -228,6 +228,15 @@ async def handle(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) ->
return await reply(writer, 503, oai_error(_unreachable(e), "cluster_unreachable", "server_error")) return await reply(writer, 503, oai_error(_unreachable(e), "cluster_unreachable", "server_error"))
if not res.get("ok"): if not res.get("ok"):
code = res.get("errorCode") or "model_error" code = res.get("errorCode") or "model_error"
if code == "context_exceeded":
# Wie OpenAI: 400 + context_length_exceeded und die Zahlen im
# Text — daran erkennen OpenAI-Clients (litellm/OpenHands & Co.)
# einen Kontext-Ueberlauf und kuerzen/verdichten selbst.
n_ctx, n_req = res.get("ctx"), res.get("promptTokens")
msg = (f"This model's maximum context length is {n_ctx} tokens. However, your "
f"messages resulted in {n_req} tokens. Please reduce the length of the messages."
if n_ctx and n_req else (res.get("error") or "context length exceeded"))
return await reply(writer, 400, oai_error(msg, "context_length_exceeded"))
status = {"bad_request": 400, "model_unavailable": 404, "timeout": 504}.get(code, 502) status = {"bad_request": 400, "model_unavailable": 404, "timeout": 504}.get(code, 502)
return await reply(writer, status, oai_error( return await reply(writer, status, oai_error(
res.get("error") or "Fehler", "model_not_found" if code == "model_unavailable" else code, res.get("error") or "Fehler", "model_not_found" if code == "model_unavailable" else code,