0.167.21

Fixed

  • Детект переполнения контекста гейтился на HTTP 400 — Ollama/llama.cpp отдают 500, аварийная компакция не запускалась ( — P2). IsContextOverflowError сканировал тело ошибки только при pe.StatusCode == 400, поэтому переполнение контекста на локальных бэкендах (Ollama/llama.cpp регулярно отвечают 500 с «context window»/«n_ctx» в теле) классифицировалось как транзиентная ошибка: spec.CompactFn не вызывался, а хуже — persistentRetry крутил детерминированный отказ бесконечно. Фикс: единый статус-гейтовый скан classifyContextOverflow (общий для Go-ошибок и error-ответов) с двумя наборами маркеров — strong (глагол+существительное: exceed(s) context window, n_ctx exceeded, maximum context length, prompt (is) too long, exceeds the maximum context, input length exceeds the context, exceeds the available context/exceeds context size (llama.cpp/Ollama), context length exceeded (проза-вариант маркера); доверяют только на явном наборе статусов, несущих решение бэкенда о запросе — {400, 413, 422, 500}: канонический 400, прокси-413, семантическая валидация 422 и прямой 500-ответ Ollama/llama.cpp; 429 и auth/routing 4xx (401/403/404/…) исключены — их тела это rate-limit словарь и отказы аутентификации/маршрутизации, 502/503/504 — прокси-бойлерплейт и эхо логов упавшего бэкенда) и weak (token limit, too many tokens, maximum number of tokens, token_limit, input length exceeds the maximum; только на каноническом 400 — 413 исключён, т.к. прокси отвечают им квотами; плюс reject-проверка co-occurrence rate-limit квалификаторов («per minute/second/hour/day», tpm, rpm) — старые vLLM отвечали 400 на TPM-лимиты, и «input length exceeds the maximum per minute» не должно флипать в overflow). Структурный маркер context_length_exceeded в Type/Code — на любом статусе, кроме 429 (429 дефиниционно rate limit — его backoff не должен skip’аться). Транспортные ошибки (StatusCode==0) body-скан не проходят вовсе. Plain-ошибки (без статуса) — только strong-маркеры. Ретрай-слой: во всех четырёх циклах (standardRetry/persistentRetry + stream-варианты) переполнение не ретраится — возвращается сразу (Go-ошибка как есть; error-ответ конвертируется в *ProviderError с нормализованным Code=context_length_exceeded, canonical-400 при пропавшем статусе, без тела в сообщении — no-leak, ShouldRetry=false), чтобы runner (runner.go emergency-compact) получил детерминированный сигнал вместо сожжённого бюджета попыток; body error-ответов сканируется с капом 4 KiB (обрезка по границе рун). Лог каждой шорткат-ветки (status_synthesized для triage). Тесты: положительные — 500+exceed context window/n_ctx = (Ollama/llama.cpp-стили), 413+strong, 400+context_length_exceeded (регресс), Anthropic «prompt is too long», response-ветка (Content-маркеры, статус 0 + структурный маркер), re-классификация возвращённой ошибки, end-to-end через retry-цикл (Chat + stream, ровно 1 вызов); отрицательные — 429+rate-limit словарь (включая «input length exceeds the maximum per minute»), 500+«internal server error», 500/413/422+weak, plain+weak, транспортный echo (status 0), биллинг-апселл «context window», 502/503/504+strong в теле прокси (эхо логов не флипает классификацию), 429+структурный маркер в Type, 400+«per minute»/tpm (rate-limit квалификаторы), 401/403/404/409+strong (auth/routing), nil; регресс — транзиент 503 по-прежнему ретраится до 3 попыток.