Перейти к содержимому

Сайдкары: веб-поиск и vision

Не все маршрутизируемые модели предоставляют hosted веб-поиск или нативный ввод изображений. opencodex восполняет эти возможности двумя сайдкарами. Каждый может работать через провайдера входа ChatGPT (forward) или через сохранённого OAuth-провайдера Anthropic; веб-поиск также может использовать сохранённый Grok OAuth через явно выбранный бэкенд xai. Ошибки сайдкара превращаются в ограниченные по размеру результаты инструментов или маркеры изображений, а не приводят к сбою всего хода.

Когда Codex запрашивает hosted web_search для маршрутизируемой модели вне сквозного режима, opencodex:

  1. Убирает hosted-инструмент web_search и вместо него предоставляет маршрутизируемой модели синтетический функциональный инструмент web_search(query). Исходные опции hosted-инструмента сохраняются для вызова сайдкара.
  2. Запускает маршрутизируемую модель в небольшом агентном цикле. Когда она вызывает web_search, opencodex использует выбранный бэкенд сайдкара: OpenAI выполняет hosted web_search по умолчанию с gpt-5.6-luna; Anthropic выполняет web_search_20250305 по умолчанию с claude-sonnet-5. xAI выполняет hosted web_search по умолчанию с grok-4.6 и добавляет x_search в тот же запрос, когда xSearch.enabled равно true. Потоковый ответ и цитаты становятся результатом инструмента.
  3. Повторяет цикл, пока модель не ответит или суммарный бюджет реальных запросов не достигнет maxSearchesPerTurn (по умолчанию 3), после чего убирает инструмент поиска и принуждает к финальному ответу. Настоящие клиентские инструменты вроде apply_patch или shell завершают ход, чтобы эти вызовы дошли до Codex.

Каждая итерация маршрутизируемой модели запрашивает у вышестоящего провайдера stream: true, но opencodex полностью буферизует семантические события внутри, прежде чем решить, искать дальше или вернуть финальный ответ. Заранее получаются только финальные заголовки/статус первой итерации и ротации ключей по 429. Поэтому синтетические поисковые вызовы и промежуточный вывод никогда не попадают к клиенту как видимый вывод модели.

Внедряемый результат оборачивается в границу недоверенных данных, ограничивается по длине и дедуплицируется по URL источника. В ходах со структурированным выводом (json_schema / json_object) он передаётся компактным JSON, а не прозой. Для текстовых маршрутизируемых моделей поисковой модели дополнительно поручается описывать релевантные изображения словами и указывать их исходные URL.

{
"webSearchSidecar": {
"enabled": true,
"backend": "anthropic",
"model": "claude-sonnet-5",
"reasoning": "low",
"maxSearchesPerTurn": 3,
"routedModelStallTimeoutMs": 200000,
"timeoutMs": 200000
}
}

Уровень рассуждений minimal не используется, потому что hosted-бэкенд отклоняет инструменты на этом уровне. Неудачный поиск возвращается маршрутизируемой модели как ограниченный по размеру результат с ошибкой, позволяя ей ответить на основе уже имеющегося контекста.

Действуют четыре отдельных таймера. stallTimeoutSec — базовый бюджет простоя событий моста. connectTimeoutMs (по умолчанию 200000) покрывает только DNS/TCP/TLS и финальные заголовки ответа. Задаваемый только в файле конфигурации webSearchSidecar.routedModelStallTimeoutMs (по умолчанию 200000, целое 1..2147483647) ограничивает непрерывное отсутствие сырых байтов ответа для каждой итерации маршрутизируемой модели и сбрасывается при каждом непустом байте. webSearchSidecar.timeoutMs отдельно ограничивает один hosted-запрос поиска. Итоговый сторожевой таймер моста — max(base stall, connect timeout, routed-model stall, sidecar timeout) + 30 seconds. Простой маршрутизируемой модели не является общим таймаутом генерации. Сбои до начала SSE возвращают JSON с кодом, отличным от 2xx; сбои генерации после отправки заголовков ответа доставляются как SSE-событие response.failed.

Когда маршрутизируемая модель указана в noVisionModels своего провайдера — либо для неё в modelInputModalities явно указана только текстовая модальность — и запрос содержит изображение, opencodex описывает каждое изображение до основного вызова и заменяет его текстом, если доступен план vision-сайдкара. Без доступного плана исходное изображение удаляется, а не передаётся текстовому бэкенду. Каталог моделей объявляет вход изображений для каждой модели, покрытой сайдкаром. Комбо объявляют вход изображений только если каждый участник принимает изображения нативно или через сайдкар и параметр комбо imageInput не отключён; поэтому такие клиенты, как приложение Codex, разрешают вложения вместо их блокировки до запуска сайдкара. Если visionSidecar.model отсутствует или пуст, путь выполнения OpenAI, дашборд и API управления используют фолбэк gpt-5.4-mini. При запуске явно сохранённое устаревшее значение gpt-5.4-mini по-прежнему мигрирует на gpt-5.6-luna; миграция применяется только к сохранённому значению, а не к отсутствующему полю модели.

  • Изображения могут приходить из сообщений пользователя, разработчика и результатов инструментов, включая view_image из Codex.
  • На пути OpenAI (passthrough с логином ChatGPT) каждое изображение отправляется в настроенную vision-модель через endpoint Responses с выбранным reasoning.effort (по умолчанию low), и полученное описание заменяет часть с изображением на месте. Путь Anthropic использует endpoint Messages со своим mapping’ом thinking-бюджета и игнорирует эту специфичную для OpenAI настройку.
  • Для нативных моделей с надёжными метаданными возможностей неподдерживаемый уровень нормализуется к самому высокому поддерживаемому уровню, не превышающему запрошенный; если такого нет, используется самый низкий поддерживаемый уровень. Неизвестные и пользовательские модели без надёжных метаданных остаются без ограничений.
  • Описания выполняются с ограниченной параллельностью (по 3 одновременно, порядок входа сохраняется). Пользовательский контекст, передаваемый описывающей модели, ограничен 800 символами, а каждое внедряемое описание — 2 000 символами. Запрос не отправляет max_output_tokens, который бэкенд ChatGPT отклоняет.
  • URL изображений проверяются перед пересылкой: data-URL должны использовать png / jpeg / jpg / webp / gif, а объём base64-данных ограничен примерно 20 МБ. Принимаются только схемы data: и https:; удалённые https-изображения загружает бэкенд OpenAI, а не прокси.
  • Сопоставление noVisionModels игнорирует суффикс :size в стиле Ollama, поэтому запись gpt-oss покрывает и gpt-oss:120b.
  • Если описание не удалось, модель получает короткий маркер ошибки обработки. (Если доступного плана сайдкара нет, описание не запускается, а исходное изображение удаляется, как описано выше.)
  • maxDescriptionsPerTurn (по умолчанию 8) ограничивает число новых описаний за один ход основной модели. Попадания в кэш и дубликаты в рамках того же хода лимит не расходуют. Успешные описания data:-изображений кэшируются по бэкенду, модели, детализации, байтам изображения и контексту сообщения; в ключи OpenAI дополнительно входит уровень рассуждений (в ключи Anthropic — нет, поскольку там это поле игнорируется). Изменяемые https:-изображения не кэшируются.
{
"visionSidecar": {
"enabled": true,
"backend": "openai",
"model": "gpt-5.6-luna",
"reasoning": "medium",
"maxDescriptionsPerTurn": 8,
"timeoutMs": 45000
}
}

Модель помечается как текстовая для конкретного провайдера:

{
"providers": {
"ollama-cloud": {
"baseUrl": "https://ollama.com/v1",
"noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"]
}
}
}

Карточка Vision sidecar на дашборде позволяет включать и выключать сайдкар, задавать maxDescriptionsPerTurn и timeoutMs, не убирая уже существующие элементы модели, бэкенда и рассуждения. Выключение не удаляет эти настройки; повторное включение сохраняет прежние значения модели, бэкенда, reasoning, таймаута и лимита.

PUT /api/sidecar-settings принимает те же поля. Частичное обновление оставляет непереданные ключи без изменений. timeoutMs использует целочисленные границы рантайма (1–2147483647 мс).

Если удобнее править файл, по-прежнему можно поставить enabled: false в config.json. Поиск и описание изображений через Anthropic OAuth переиспользуют существующий прецедент OAuth-отпечатка Claude Code, но их стоит обкатать с целевым аккаунтом и нагрузкой.

Все поля описаны в справочнике по конфигурации.