From 29338dfbd39ae83832ae3fd84f642de890b9a3c8 Mon Sep 17 00:00:00 2001 From: "Claude (backend session)" Date: Sat, 22 Aug 2026 01:03:20 +0300 Subject: [PATCH] Correct the Gemini thinking-knob and billing notes against vendor docs and ping the orchestrator about the engine losing the glossary on Gemini hops --- docs/PROGRESS.md | 30 ++++++++++++++++++++++++++ docs/experiments/00-provider-quirks.md | 9 ++++---- eval/dovodka/rol.py | 21 ++++++++++++++---- eval/providers.json | 2 +- 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index e64c3811..1a06d989 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -280,6 +280,36 @@ ## Полигон +**⛔⛔ ПИНГ ОРКЕСТРАТОРА №19 — 22.08. ЖИВОЙ ДЕФЕКТ ДВИЖКА: эскалация на Gemini ТИХО ТЕРЯЕТ ГЛОССАРИЙ.** +Найдено полигоном живой пробой, подтверждено вендор-докой. **OpenAI-совместимый слой Gemini принимает +РОВНО ОДНО системное сообщение; лишние выбрасываются молча, без ошибки.** Механизм назван вендором: +в нативном API `systemInstruction` — один объект `Content` (`ai.google.dev/api/generate-content`), +поэтому уход на нативный API проблемы НЕ решает — склеивать надо в любом случае. +Замер (один и тот же системный текст 7970 знаков): одним сообщением → `prompt_tokens` **2994**, маркерная +инструкция ИСПОЛНЕНА; двумя → **535** токенов и инструкция НЕ исполнена, при `finish=stop` и внешне +правильном переводе. Отказа нет — есть тихая потеря промта, невидимая гейтам годности. + +**Почему это ваша зона, а не наша.** `backend/internal/pipeline/render.go:256-258` штатно строит ВТОРОЕ +системное сообщение — инъекцию банка памяти (глоссарий/STM); `backend/internal/llm/httpllm.go:517-522` +(`toOpenAIMessages`) копирует сообщения дословно; склейки под Gemini нет нигде. При этом +`gemini-3.1-pro-preview` стоит в ратифицированной цепочке эскалации во ВСЕХ арм-конфигах +(`backend/configs/pipeline-c1.yaml:129`, `pipeline-arm-glm.yaml:61`, `pipeline-arm-deepseek-pro.yaml:62`, +`pipeline-arm-mistral.yaml:61`). ⇒ **любой эскалационный хоп на Gemini при непустой инъекции теряет +глоссарий, и книга едет без канона имён.** Оговорка D22.3 «экспозиция Ф1 нулевая» держится только на том, +что до Gemini в дефолте не доходят — то есть на маршруте, а не на коде. +⚠ Класс в проекте УЖЕ решён рядом: `backend/internal/llm/provider_anthropic.go:169-179` сворачивает +ведущий system-префикс в одно поле и падает громко на system посреди диалога. Не обобщено на Gemini. +Полигон у себя починил склейкой по вендору (`eval/dovodka/rol.py` `ONE_SYSTEM`/`fit_msgs`); в бэкенд +рукой не лезу. Квирк записан в `00-provider-quirks.md` (наша зона) с механизмом и ссылками. + +**Тем же касанием — две поправки в квирках, обе наши прежние ошибки, уже внесены нами:** +(1) форма `extra_body.google.thinking_config...` в бюллетене НЕ доезжает через openai-SDK: вендорская +форма несёт ключ `extra_body` на верхнем уровне тела (`extra_body={'extra_body': {'google': …}}`), а один +уровень даёт HTTP 400 «Unknown name "google"». Из-за этой ошибки `eval/providers.json:59` объявил рабочую +форму багом, и проект три недели считал, что ручки размышления у Gemini нет. (2) `reasoning_tokens=0` у +Gemini — НОРМА слоя, а не измерение: вывод «размышление выключено» из этого поля недействителен, считать +надо `total − prompt − completion`. У серии Gemini 3 размышление вообще невыключаемо (вендор, 17.08.2026). + **⚠ Пинг оркестратора №18 — 20.08 (протухшие цены в квирках, чинить вашей рукой).** `docs/experiments/00-provider-quirks.md` до сих пор утверждает, что цены после катовера НЕ изменились («flash $0.14/$0.28»). Это опровергнуто пере-пином 13–15.08 (D39.137): таблица запинена ПИКОМ — flash 0.44/1.32 при кэш-хите **0.014**, pro 1.32/3.96 при кэш-хите **0.044** за 1M (⚠ испр. 20.08: первая редакция пинга приписала flash кэш-хит pro — ошибка ×3.1 ровно в той клетке, ради которой пинг и писался; сверено с `backend/configs/models.yaml:167,183` и телом D39.137 §2а), и счёт шиппинга = 100% DeepSeek ⇒ ×4.2–4.4 в пике. Квирки — ваша зона, рукой не трогаю; поправьте при ближайшем касании, потому что по ним считают деньги проб. Тем же касанием сверьте пункт «возражение Sol по проходу `tier`»: шапка держит его на владельце, а ваша секция пишет, что оно снято и причина названа — один из двух носителей неверен. **ФАЗА Д — СТАТУС НА ВЕЧЕР 10.08 (сессия передана, хендофф `/home/ubuntu/CONTINUATION_PROMPT.md`).** ⚠ **Запись ПРОТУХЛА трижды и её надо срезать при лендинге петель** (свип доков 20.08): статус «на вечер 10.08» лежит поверх записей от 15.08, хендофф указывает ВНЕ репозитория (файл на месте, но от 10.08 и вне git — то есть не переживёт машину), а в дереве с 16.08 живёт незадокументированная работа (`eval/dovodka/PLAN-16-08.md`, `PLAN-17-08.md`, `HANDOFF-21-08.md`, `ja6.py`, `naklon.py`), которой в этой секции нет вовсе. Три поколения записей в одном месте — именно то, из-за чего состояние фазы Д сегодня нельзя прочитать. diff --git a/docs/experiments/00-provider-quirks.md b/docs/experiments/00-provider-quirks.md index 9bd8854c..550a096b 100644 --- a/docs/experiments/00-provider-quirks.md +++ b/docs/experiments/00-provider-quirks.md @@ -51,8 +51,8 @@ | **DeepSeek v4-flash** | гибридная reasoning-модель, thinking ON по умолчанию | **Управление (официальные доки, api-docs.deepseek.com/guides/thinking_mode, проверено 04.07): в body `extra_body={"thinking":{"type":"enabled"\|"disabled"}}`** (дефолт enabled). Т.е. выключить МОЖНО, параметр есть. НО для роли ПЕРЕВОДЧИКА не выключать: `disabled`→non-thinking режим, который на плотном CJK **ЭХАЕТ исходник**. Оставлять thinking ВКЛ; reasoning→`reasoning_content`, `content` чистый; ~~`max_tokens` ≥8000~~ **→ см. строку 0731 ниже: флор 8000 ПРОБИТ**. ⚠ **thinking-режим НЕ поддерживает `temperature`/`top_p`/penalties** (шлём — молча игнорируются). ~~`reasoning_effort`: только `high`/`max` (low/medium→high, xhigh→max)~~ **→ УСТАРЕЛО, см. строку 0731**; `none` в OpenAI-формате НЕ существует → 400. Для дешёвой роли ЭКСТРАКЦИИ (не перевод) thinking можно выключить — echo бьёт по переводу, не по JSON-выводу (перепроверить). | | GLM-4.5-air / 4.6 | thinking ON, не влезал в 180с | отключить: `extra_body: {"thinking": {"type": "disabled"}}` | | **glm-5** | thinking ON по умолчанию | гасится тем же `extra_body: {"thinking": {"type": "disabled"}}` — подтверждено живыми армами эксп-18/21 (арм B шёл thinking-OFF; провод-факт эксп-21 §8) | -| Gemini 2.5 Flash | thinking ON, съедал `max_tokens`, **молча обрезал перевод** | отключить: `extra_body.google.thinking_config.thinking_budget: 0` | -| **Gemini 3.1 Pro** | thinking **обязателен** | `thinking_budget:0` → HTTP 400 «only works in thinking mode». НЕ отключать; дать большой `max_tokens` (≥8000) | +| Gemini 2.5 Flash | thinking ON, съедал `max_tokens`, **молча обрезал перевод** | ⚠ **ФОРМА, КАК ОНА ЗДЕСЬ СТОЯЛА, НА ПРОВОД НЕ ДОЕЗЖАЕТ** (поймано полигоном 22.08). Проводное тело несёт ключ `extra_body` **НА ВЕРХНЕМ УРОВНЕ**: `{"extra_body":{"google":{"thinking_config":{…}}}}`; в Python-SDK это `extra_body={'extra_body': {'google': {…}}}` (вендор-сэмпл ai.google.dev/gemini-api/docs/openai, 17.08.2026). Один уровень вложенности → HTTP 400 «Unknown name "google"» — это НАША ошибка, а не отказ вендора. Штатный off-switch для серии 2.5 — плоский `reasoning_effort:"none"` | +| **Gemini 3.x и 2.5 Pro** | thinking **невыключаем У ВСЕЙ СЕРИИ** | дословно вендор (17.08.2026): «Reasoning cannot be turned off for Gemini 2.5 Pro or 3 models». Дефолт `gemini-3.1-pro-preview` — **On (high)**. У 3.x ручка — `thinking_level` (minimal/low/medium/high), у 2.5 — `thinking_budget`; через OpenAI-слой обе задаются как `reasoning_effort`, и **вместе с `thinking_level`/`thinking_budget` слать нельзя**. ⚠ Для `gemini-3.1-flash-lite` строки в вендорской таблице дефолтов НЕТ — **дефолтный уровень НЕ УСТАНОВЛЕН**, писать «не установлено», а не «выключено». НЕ отключать; дать большой `max_tokens` (≥8000) | | gpt-5-mini (reasoning) | reasoning ON, выжигал бюджет → пустой ответ | `reasoning_effort: "minimal"`; **без `temperature`**; `max_completion_tokens` вместо `max_tokens` | | **gpt-5.4 (reasoning)** | ⚠ **`reasoning_effort:"medium"` РЕПРОДУЦИРУЕМО ОБРЕЗАЕТ ВЫВОД на длинной редактуре** (exp14b: reasoning съел бюджет → `content` 223 out-ток ≈ 602 симв из ~5000, `finish=stop` — выглядит как валидный короткий ответ, НЕ length-обрыв → тихо портит результат) | В роли РЕДАКТОРА/переводчика (длинный выход) слать **`reasoning_effort:"low"`** (exp14b: тот же чанк → 5137 симв, полный); `max_completion_tokens` (не `max_tokens`), без `temperature`. reasoning⊆completion (subset-биллинг). Слаг live-фактчек 2026-07-11: `gpt-5.4-2026-03-05`. *(Верифицировано span-читкой D38; self-review полигона поймал живьём.)* | | **gpt-5.6-luna (reasoning)** | reasoning ON; **без явной ручки эффорта выжигает бюджет на рассуждение и отдаёт ПУСТОЙ `content`** (провод-факт эксп-21 §8) | слать явный `reasoning_effort` (`low` для перевода/редактуры); `max_completion_tokens`; пустой `content` при `finish=length` — оплаченный брак, гейтить приёмом ответа | @@ -154,8 +154,9 @@ ## Контент-фильтры / safety -- ⛔⛔ **Gemini, ДВА СИСТЕМНЫХ СООБЩЕНИЯ = ПРОМТ ТЕРЯЕТСЯ МОЛЧА (полигон 22.08, живая проба `eval/dovodka/proba_gemini.py`).** Слой OpenAI у Gemini принимает ОДНО системное сообщение; при двух и более лишние выбрасываются без ошибки. Замер на одном и том же системном тексте в 7970 знаков: ОДНИМ сообщением → `prompt_tokens` 2994 и маркерная инструкция ИСПОЛНЕНА; ДВУМЯ → **535 токенов (это размер одного user-сообщения) и инструкция НЕ исполнена**. Ответ приходит с `finish=stop` и выглядит правильным переводом — отказа нет, есть тихая потеря инструкции, невидимая всем гейтам годности. ⚠ Так были куплены 16 клеток арма `RE` (промт-роль на gemini): они переведены ВООБЩЕ БЕЗ ИНСТРУКЦИИ, и вердикт о переносимости из них был бы ложным. Лечение: склеивать системные в одно ДЛЯ ЭТОГО ВЕНДОРА (`rol.ONE_SYSTEM`/`fit_msgs`) — общая склейка сменила бы форму запроса у всех моделей и рассогласовала бы уже купленные клетки. ⚠ Проверено, что у `deepseek-v4-pro`, `gpt-5.6-luna`, `glm-5` и `grok-4.3` два системных доезжают целиком. -- **Gemini**: `safety_settings` через OpenAI-совместимый слой **не передаются** (HTTP 400 «Unknown name safety_settings»). Дефолт фильтров у 2.5/3 — OFF (gap-2). Для явного контроля фильтров (роль судьи 18+) нужен **нативный** Gemini API, не OpenAI-слой. ⚠ **D22.6: на ГРАФИЧНОЙ эротике Gemini 3.x fail-closed `PROHIBITED_CONTENT` (8× wire-verified, exp11) — фильтр НЕконфигурируем, нативный API не спасает; судья эротики — только Grok. На violence/SFW Gemini-судья жив.** `finish_reason` приходит СОСТАВНОЙ строкой `'content_filter: PROHIBITED_CONTENT'` (точные матчеры мертвы — D22.8а). Биллинг Gemini: thinking ТОЛЬКО в `total_tokens` → `reasoning: additive_total`, иначе недоучёт 146×/вызов (D22.3). ⚠ **В роли редактора Gemini течёт сервис-преамбулой в выход («Вот отредактированный…», 6/6 чанков — exp12) → за output-санитайзером D30.3.** ⚠ **Дополнение эксп-21 (08.08):** fail-closed ответ OpenAI-слоя приходит вовсе БЕЗ объекта `message` (не с пустым `content`) — парсер обязан переживать его отсутствие; на вебновелльном violence-материале срабатывания массовые (10 клеток судейского прогона с `content_filter: PROHIBITED_CONTENT`) ⇒ оговорка «на violence/SFW Gemini-судья жив» целиком НЕ держится — Gemini непригоден как ЕДИНСТВЕННЫЙ второй контур на этом материале (эксп-21 §1/§8). +- ⛔⛔ **Gemini, ДВА СИСТЕМНЫХ СООБЩЕНИЯ = ПРОМТ ТЕРЯЕТСЯ МОЛЧА (полигон 22.08, живая проба `eval/dovodka/proba_gemini.py`).** **МЕХАНИЗМ НАЗВАН** (вендор-дока `ai.google.dev/api/generate-content`): в нативном API `systemInstruction` — это ОДИН объект `Content`, поэтому слой ФИЗИЧЕСКИ не может пронести больше одного системного, и уход на нативный API проблемы НЕ решает — там пришлось бы склеивать так же. При двух и более лишние выбрасываются без ошибки. ⚠ КАКОЕ именно выживает — НЕ УСТАНОВЛЕНО: форум Google (discuss.ai.google.dev/t/86097, 30.05.2025) сообщает «выживает последнее», а наш замер даёт 535 токенов ≈ размер одного user-сообщения, то есть не выжило НИ ОДНО. Расхождение открыто. Документированного вендором ограничения в разделе «Current limitations» НЕТ. Замер на одном и том же системном тексте в 7970 знаков: ОДНИМ сообщением → `prompt_tokens` 2994 и маркерная инструкция ИСПОЛНЕНА; ДВУМЯ → **535 токенов (это размер одного user-сообщения) и инструкция НЕ исполнена**. Ответ приходит с `finish=stop` и выглядит правильным переводом — отказа нет, есть тихая потеря инструкции, невидимая всем гейтам годности. ⚠ Так были куплены 16 клеток арма `RE` (промт-роль на gemini): они переведены ВООБЩЕ БЕЗ ИНСТРУКЦИИ, и вердикт о переносимости из них был бы ложным. Лечение: склеивать системные в одно ДЛЯ ЭТОГО ВЕНДОРА (`rol.ONE_SYSTEM`/`fit_msgs`) — общая склейка сменила бы форму запроса у всех моделей и рассогласовала бы уже купленные клетки. ⚠ Проверено, что у `deepseek-v4-pro`, `gpt-5.6-luna`, `glm-5` и `grok-4.3` два системных доезжают целиком. +- ⛔⛔ **ТОТ ЖЕ ДЕФЕКТ ЖИВЁТ В ДВИЖКЕ, А НЕ ТОЛЬКО В ПОЛИГОНЕ (аудит 22.08).** `backend/internal/pipeline/render.go:256-258` штатно строит ВТОРОЕ системное сообщение — инъекцию банка памяти (глоссарий/STM), `backend/internal/llm/httpllm.go:517-522` копирует сообщения дословно, склейки под Gemini нет нигде, а `gemini-3.1-pro-preview` стоит в ратифицированной цепочке эскалации во ВСЕХ арм-конфигах (`pipeline-c1.yaml:129`, `pipeline-arm-glm.yaml:61`, `pipeline-arm-deepseek-pro.yaml:62`, `pipeline-arm-mistral.yaml:61`). ⇒ **любой эскалационный хоп на Gemini при непустой инъекции ТИХО теряет глоссарий.** Оговорка D22.3 «экспозиция Ф1 нулевая» держится только на том, что до Gemini в дефолте не доходят. ⚠ Класс в проекте уже был решён рядом: `backend/internal/llm/provider_anthropic.go:169-179` сворачивает ведущий system-префикс в одно поле и падает громко на system посреди диалога — просто не обобщён на Gemini. **Зона бэкенда, пинг оркестратору отправлен.** +- **Gemini**: `safety_settings` через OpenAI-совместимый слой **не передаются** (HTTP 400 «Unknown name safety_settings»). Дефолт фильтров у 2.5/3 — OFF (gap-2). Для явного контроля фильтров (роль судьи 18+) нужен **нативный** Gemini API, не OpenAI-слой. ⚠ **D22.6: на ГРАФИЧНОЙ эротике Gemini 3.x fail-closed `PROHIBITED_CONTENT` (8× wire-verified, exp11) — фильтр НЕконфигурируем, нативный API не спасает; судья эротики — только Grok. На violence/SFW Gemini-судья жив.** `finish_reason` приходит СОСТАВНОЙ строкой `'content_filter: PROHIBITED_CONTENT'` (точные матчеры мертвы — D22.8а). Биллинг Gemini: thinking ТОЛЬКО в `total_tokens` → `reasoning: additive_total`, иначе недоучёт 146×/вызов (D22.3). Нативное имя поля — `usageMetadata.thoughtsTokenCount`; слой AI-Studio `completion_tokens_details.reasoning_tokens` не отдаёт ВОВСЕ, тогда как Vertex AI то же поле отдаёт (форум 15.01.2026) — при переезде на Vertex учёт менять. ⚠⚠ **`reasoning_tokens=0` у Gemini — НОРМА, а не измерение: вывод «размышление выключено» из этого поля НЕДЕЙСТВИТЕЛЕН.** Полигон 22.08 на этом и поскользнулся, объявив «вендор-дефолт flash-lite = размышление ВЫКЛ (замерено)»; считать надо `total − prompt − completion`. ⚠ **В роли редактора Gemini течёт сервис-преамбулой в выход («Вот отредактированный…», 6/6 чанков — exp12) → за output-санитайзером D30.3.** ⚠ **Дополнение эксп-21 (08.08):** fail-closed ответ OpenAI-слоя приходит вовсе БЕЗ объекта `message` (не с пустым `content`) — парсер обязан переживать его отсутствие; на вебновелльном violence-материале срабатывания массовые (10 клеток судейского прогона с `content_filter: PROHIBITED_CONTENT`) ⇒ оговорка «на violence/SFW Gemini-судья жив» целиком НЕ держится — Gemini непригоден как ЕДИНСТВЕННЫЙ второй контур на этом материале (эксп-21 §1/§8). - **Qwen Model Studio**: внешний фильтр `data_inspection_failed` на вход/выход, неотключаем (риск молчаливых вырезаний) — не тестировано (нет ключа). - Детект отказов/вырезаний — см. `eval/refusal_bench.py` (паттерны soft-отказа + coverage-гейт по len_ratio/sent_cov, пороги в эксп. 02). diff --git a/eval/dovodka/rol.py b/eval/dovodka/rol.py index 74190d09..112f2650 100644 --- a/eval/dovodka/rol.py +++ b/eval/dovodka/rol.py @@ -463,7 +463,11 @@ THINK_LEVEL: dict = { "RK": "low", # grok на вендор-дефолте — ровно так и куплен, теперь это НАПИСАНО "RKT": "high", # grok с настоящим размышлением — клетка Б6, не куплена "RL": "vendor", # gpt-5.6-luna: ростер пинит `low`, снимаем (В22) - "RE": "vendor", # gemini-3.1-flash-lite: вендор-дефолт = размышление ВЫКЛ (замерено, 0 ток.) + "RE": "vendor", # gemini-3.1-flash-lite: ВЕНДОР-ДЕФОЛТ. ⛔ НЕ «выключено»: у серии Gemini 3 + # размышление вообще невыключаемо (вендор-дока 17.08.2026), а поле + # `reasoning_tokens` у Gemini ВСЕГДА 0 по построению слоя — считать надо + # `total − prompt − completion`. Прежний комментарий «замерено, 0 ток.» + # читал ровно то поле, которое наш же бюллетень объявил всегда нулевым. "RET": "budget", # он же с размышлением: у Gemini своя ручка `thinking_budget`, не `effort` } @@ -508,9 +512,18 @@ def call_kwargs(model: str, msgs: list[dict], arm: str = "") -> dict: if lvl == "budget": # ⚠ У Gemini уровень задаётся БЮДЖЕТОМ, а не словом: `thinking_budget` в google-секции # (квирк-бюллетень :54). `-1` — динамический бюджет, то есть «думай сколько нужно». - # ⚠ Форма `extra_body.google.thinking_config.thinking_budget` (квирк-бюллетень :54) вендором - # ОТВЕРГНУТА 22.08: HTTP 400 «Unknown name "google"» — тот же класс, что отказ на - # `safety_settings`. Пробуем стандартную для OpenAI-слоя ручку. + # ⛔ ПОПРАВКА 22.08 ПОСЛЕ СВЕРКИ С ВЕНДОР-ДОКОЙ. Прежний комментарий здесь утверждал, что + # вендор ОТВЕРГ google-секцию. Неверно: секция работает, а 400 «Unknown name "google"» был + # НАШЕЙ ошибкой вложенности — Python-SDK раскладывает `extra_body` в верхний уровень тела, + # поэтому вендорская форма требует ДВОЙНОГО ключа: `extra_body={'extra_body': {'google': …}}`. + # Рабочая форма лежала в репозитории (`eval/adaptive_probe.py:71`), но была объявлена багом + # в `eval/providers.json:59`, и ошибка размножилась оттуда. + # ⇒ Здесь оставлена ПЛОСКАЯ `reasoning_effort` — она документирована вендором для 3.x + # (маппится в `thinking_level`) и проверена живой пробой: 4068 токенов размышления в + # `total_tokens`. Слать её ВМЕСТЕ с `thinking_level`/`thinking_budget` вендор запрещает. + # ⚠ `high` — ВЕРХНЯЯ ступень. Для роли переводчика аналог того, что мы шлём другим вендорам, + # это `low`/`minimal`; контраст `RE`/`RET` читать как «вендор-дефолт против high», НЕ как + # «выключено против включено». eb["reasoning_effort"] = "high" elif lvl == "vendor": # снимаем ЛЮБОЕ наше подавление: у Z.ai это `thinking.disabled`, у OpenAI — `effort: low` diff --git a/eval/providers.json b/eval/providers.json index 06665067..acf35e6d 100644 --- a/eval/providers.json +++ b/eval/providers.json @@ -56,7 +56,7 @@ "api_key_env": "GEMINI_API_KEY", "rps_delay": 1.0, "max_tokens": 8000, - "_comment": "МИГРИРОВАН 2026-07-10 (D21.9): gemini-2.5-flash EOL 16.10.2026 + интермиттентный 404-флейк → gemini-3.1-flash-lite (вендор-замена 2.5-flash-lite; НЕ 3.5-flash — та даёт 503 high-demand, проба research/15 + live-смоук 07-10). Live-смоук 07-10: finish=stop, thinking не съедает бюджет на коротком выходе → thinking_config НЕ нужен (прежний extra_body был ДВОЙНО ВЛОЖЕН extra_body.extra_body.google — на провод не уходил, баг убран). safety_settings через OpenAI-слой НЕ передаются (400 Unknown name); дефолт фильтров 3.x зависит от класса: SAFETY конфигурируем, PROHIBITED_CONTENT — НЕТ (00-provider-quirks). Для явного контроля фильтров судьи 18+ → нативный Gemini API." + "_comment": "МИГРИРОВАН 2026-07-10 (D21.9): gemini-2.5-flash EOL 16.10.2026 + интермиттентный 404-флейк → gemini-3.1-flash-lite (вендор-замена 2.5-flash-lite; НЕ 3.5-flash — та даёт 503 high-demand, проба research/15 + live-смоук 07-10). Live-смоук 07-10: finish=stop, thinking не съедает бюджет на коротком выходе → thinking_config НЕ нужен ⛔ ПОПРАВКА 22.08: фраза «прежний extra_body был ДВОЙНО ВЛОЖЕН … баг убран» БЫЛА НЕВЕРНА — двойной ключ это и есть ВЕНДОРСКАЯ форма (ai.google.dev/gemini-api/docs/openai, 17.08.2026): Python-SDK раскладывает extra_body в верхний уровень тела, поэтому google-секция требует extra_body={'extra_body': {'google': …}}. Из этой ошибки выросло убеждение, что ручки размышления у Gemini нет; она есть. Штатная форма через слой — плоский reasoning_effort (у 3.x маппится в thinking_level). safety_settings через OpenAI-слой НЕ передаются (400 Unknown name); дефолт фильтров 3.x зависит от класса: SAFETY конфигурируем, PROHIBITED_CONTENT — НЕТ (00-provider-quirks). Для явного контроля фильтров судьи 18+ → нативный Gemini API." }, { "name": "openai",