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

Мост Factory Droid

Factory Droid — среда выполнения агента, а не документированный эндпоинт инференса, совместимый с OpenAI. Если пользовательский провайдер, направленный на внутренний URL Factory LLM, возвращает 403 Forbidden, одна лишь смена адаптера opencodex или добавление заголовков провайдера не превращает этот частный маршрут в поддерживаемый публичный API.

Рабочая интеграция выглядит так:

Text-only Responses client
-> opencodex (http://127.0.0.1:10100/v1/responses)
-> local Responses bridge (http://127.0.0.1:11435/v1/responses)
-> official droid exec command
-> Factory account and selected model

Учётные данные Factory при этом остаются в официальном клиенте Droid. OpenCodex получает отдельный токен моста, действующий только локально.

Симптом Причина Исправление
403 Forbidden от URL Factory LLM URL не является документированным универсальным эндпоинтом OpenAI для сторонних клиентов Вызывайте Factory через официальный CLI или SDK Droid
404 по адресу /models/models Базовый URL провайдера уже заканчивался на /models Используйте корень API как baseUrl; не добавляйте путь обнаружения
Не работает поиск моделей Мост не предоставляет полный актуальный каталог Установите liveModels: false и задайте статический список models
Loopback-провайдер отклонён Доступ к частной сети запрещён по умолчанию Задавайте allowPrivateNetwork: true только для loopback-моста
Не разрешается ${DROID_BRIDGE_TOKEN} Переменная отсутствует в окружении службы opencodex Передайте её процессу службы, а не только интерактивной оболочке
OutputTextDelta without active item Мост отправил текстовый фрагмент до открытия выходного элемента и части содержимого Отправляйте полный жизненный цикл Responses SSE в правильном порядке

Поэтому те же учётные данные Factory могут работать с droid exec, тогда как прямой запрос к недокументированному URL LLM возвращает 403. Эти результаты проверяют разные продукты и не противоречат друг другу.

  1. Установите Droid CLI и войдите в аккаунт.

  2. Убедитесь, что ограниченный запрос без интерфейса работает:

    Terminal window
    droid exec --model glm-5.2 --output-format json "Reply with DROID_OK only."
  3. Запустите локальный мост, вызывающий droid exec (или официальный Droid SDK) и предоставляющий:

    • GET /healthz
    • GET /v1/models
    • POST /v1/responses

Factory документирует droid exec как интерфейс для неинтерактивной автоматизации и рекомендует вывод JSON для скриптов. Для более длительной интеграции Factory также описывает потоковый JSON-RPC и официальные SDK для TypeScript и Python в руководстве Droid Exec.

Привяжите мост к 127.0.0.1, требуйте случайно сгенерированный bearer-токен, ограничьте размер запросов и используйте список разрешённых ID моделей. Минимальный мост принимает лишь следующие формы input в Responses:

  • непустую строку; или
  • массив, содержащий только элементы message. Каждое сообщение должно иметь роль user, developer, system или assistant, а его содержимое должно быть строкой либо состоять только из текстовых частей (input_text для входных ролей и output_text для истории ассистента).

Проверяйте весь запрос до вызова Droid. Если часть входа является изображением или файлом, tools содержит любое определение инструмента либо input содержит вызов инструмента или результат (function_call, function_call_output, custom_tool_call или custom_tool_call_output), верните HTTP 400 с ошибкой invalid_request_error в формате Responses. Используйте стабильный код моста, например unsupported_bridge_input, и укажите в сообщении отклонённое поле. Сделайте это до запуска SSE, даже при stream: true; никогда не отбрасывайте неподдерживаемое содержимое и не превращайте его в строку или плоский текст промпта.

{
"error": {
"type": "invalid_request_error",
"code": "unsupported_bridge_input",
"param": "tools",
"message": "The minimal Droid bridge does not accept tool definitions."
}
}

Для принятого запроса мост должен:

  1. преобразовать принятый Responses input в промпт;
  2. вызвать droid exec --model <id> --output-format json <prompt>;
  3. разобрать итоговые result и session_id;
  4. вернуть оболочку OpenAI Responses; и
  5. сопоставить previous_response_id с ID сессии Droid, когда нужно продолжение.

Для потоковых ответов отправляйте события в таком порядке:

response.created
response.output_item.added
response.content_part.added
response.output_text.delta
response.output_text.done
response.content_part.done
response.output_item.done
response.completed

Не открывайте мост на 0.0.0.0 и не используйте учётные данные Factory повторно как bearer-токен моста.

Создайте пользовательского провайдера с явным ID droid:

Terminal window
ocx provider add droid \
--adapter openai-responses \
--base-url http://127.0.0.1:11435/v1 \
--default-model glm-5.2 \
--allow-private-network

Команда создаёт запись конфигурации providers.droid. В дашборде откройте Providers → droid → Edit JSON и замените значение этого провайдера на:

{
"adapter": "openai-responses",
"baseUrl": "http://127.0.0.1:11435/v1",
"responsesPath": "/responses",
"allowPrivateNetwork": true,
"authMode": "key",
"apiKey": "${DROID_BRIDGE_TOKEN}",
"liveModels": false,
"models": ["glm-5.2", "glm-5.2-fast", "kimi-k3"],
"defaultModel": "glm-5.2"
}

ID моделей приведены для примера. Оставьте только модели, доступные через droid exec для аккаунта Factory, в который выполнен вход. Не добавляйте этому провайдеру заголовки вывода, специфичные для Factory: его вышестоящий сервер — локальный мост, а не HTTP-эндпоинт Factory.

После сохранения провайдера или изменения статического каталога синхронизируйте и перезапустите Codex, чтобы новые сессии прочитали обновлённый каталог:

Terminal window
ocx sync --restart-codex
ocx doctor

--restart-codex перезапускает подходящие app-server и полностью закрывает и заново открывает настольное приложение Codex, завершая активные беседы. Используйте --restart-app-server-only, чтобы оставить настольное приложение работающим. Перезапускайте только после завершения или сохранения этих сессий.

Проверяйте каждую границу отдельно:

Terminal window
curl -fsS http://127.0.0.1:11435/healthz
ocx doctor
ocx access test droid/glm-5.2 --protocol responses

Строка провайдера или запись в списке выбора модели доказывает только видимость в каталоге. Интеграция работает лишь тогда, когда проверочный запрос Responses возвращается через маршрут droid/<model>.

Описанный минимальный мост переводит текст и жизненный цикл Responses SSE. Он не реализует полный двусторонний протокол вызовов функций и инструментов Codex. Codex App и codex exec обычно отправляют определения инструментов, даже если в промпте сказано не вызывать инструменты, а в текущем CLI Codex нет общего флага для их удаления. Минимальный мост должен отклонять такие запросы по описанному выше контракту 400. Для определений, вызовов и результатов инструментов, разрешений, отмены и расширенных событий Droid нужен мост с состоянием на основе потокового JSON-RPC Factory или официального Droid SDK. Успех ocx access test подтверждает текстовый маршрут, а не работу агента Codex или маршрута инструментов.