Мост Cordis
Мост Cordis встраивает DeepSeek Harness (DSH) в бэкенд Libre WebUI. DSH работает как дерево плагинов в среде Cordis, размещённой Libre WebUI, поэтому возможности предоставляются сервисами Cordis, а не импортируемыми модулями.
Мост по умолчанию выключен. Описанное здесь действует только после включения оператором; см. Конфигурацию Cordis.
Почему мост, а не прямая интеграция
Импорт пакетов DSH из сервисов Libre WebUI был бы короче, но хуже: движок стал бы зависимостью времени компиляции. Смена адаптера модели, цикла агента или удаление движка требовали бы изменения и повторного развёртывания Libre WebUI.
Мост обращает зависимость. Libre WebUI использует один абстрактный контракт, а документ композиции Cordis выбирает его реализацию:
- Смена провайдера без пересборки. Композиция — YAML-файл, поэтому другая модель провайдера выбирается настройкой.
- Настройка возможностей. Каждая возможность — строка Loader. Изменения композиции оператора вступают в силу при следующем запуске хоста.
- Удаление без следов. Все сервисы, обработчики и эффекты движка принадлежат корневому fiber. Его освобождение откатывает их, позволяя остановить движок без перезапуска Libre WebUI.
Слои
Конкретные зависимости DSH остаются внутри backend/src/cordis/dsh/. Маршруты и сервисы приложения используют контракты моста. У драйвера Work отдельная композиция в памяти, которая никогда не загружает плагины файловой системы хоста.
Контракты
Контракт находится в backend/src/cordis/contracts.ts и намеренно ограничен структурами, нужными API Libre WebUI, без внутренней терминологии движка.
| Контракт | Назначение |
|---|---|
DshEngine.status() | Состояние жизненного цикла сервисов (pending / ready / failed) |
DshEngine.modelConfiguration() | Модель и провайдер по умолчанию работающей композиции |
DshEngine.listSessions() | Сводки сеансов от новых к старым |
DshEngine.getSession(id) | Сеанс с проекцией сообщений |
DshEngine.createSession(opts) | Резервирование ID сеанса и рабочего каталога |
DshEngine.updateSessionSettings(id, settings) | Сохранение реальной модели и нативного режима файловых разрешений в простое |
DshEngine.decideApproval(id, approvalId, decision) | Решение ожидающего согласования инструмента для его сеанса |
DshEngine.deleteSession(id) | Завершение сеанса и освобождение агента |
DshEngine.listAgents() | Активные агенты с пометкой корневого или дочернего |
DshEngine.listTools() | Зарегистрированные инструменты, видимые модели |
DshEngine.sendMessage(id, txt) | Начало хода и получение дескриптора потока |
DshEngine.cancel(id) | Отмена активного хода сеанса |
Контракт публикуется как сервис Cordis libreDshEngine; потребитель читает его через ctx.get('libreDshEngine'), не импортируя модуль моста.
EngineStreamChunk передаёт text, reasoning, tool-call, tool-result, approval-request, approval-decision, error и done. Живые кадры маршрутизируются по владеющим агенту и сеансу; соответствующее сохранённое сообщение ассистента не отправляется повторно. sendMessage возвращает дескриптор, чей subscribe воспроизводит уже выданное. Поэтому быстрый первый токен не теряется между началом хода и подключением HTTP-обработчика.
Последовательность одного хода чата
Используется NDJSON, поскольку ход после запроса — единая последовательность от сервера к клиенту. Сохранение её в POST исключает второе рукопожатие, билет и протокол переподключения и удерживает весь ход в одном аутентифицированном запросе.
DONE и PENDING
Cordis активирует плагин после появления объявленных сервисов, поэтому строка некоторое время находится в состояниях до запуска. Путаница между следующими понятиями — наиболее частая причина молчащего движка.
Состояние записи Loader. Каждая строка проходит PENDING → LOADING → ACTIVE либо FAILED. Без объявленных сервисов она бесконечно ожидает вместо ошибки, поэтому неполная композиция может запуститься, но ничего не обслуживать.
Доступность сервиса. Хост сообщает каждый ожидаемый сервис так:
| Состояние | Значение | Причина |
|---|---|---|
pending | Не зарегистрирован в контексте | Предоставляющая строка не активирована или выключена |
ready | Зарегистрирован и доступен | Предоставляющая строка активирована |
failed | Объявлен, но недоступен | Сообщается со строкой detail |
host.status() показывает доступность и отсутствующие обязательные сервисы; GET /api/cordis/health возвращает те же сведения. Отсутствие обязательного сервиса прерывает запуск, а не публикует движок с пустыми ответами.
В двух цепочках легко ошибиться:
dsh-toolsне запускается безsystemPrompt.dsh-agent-loopждётagents,sessions,llm,tools,systemPromptиsessionProjections.
Если чего-то не хватает, хранилище сеансов может работать, но движок никогда не отвечает на сообщения.
Конфигурация провайдера
Поставляемая строка libre-webui-llm-adapter обслуживает провайдеров, настроенных в Libre WebUI. Селектор страницы Движка выбирает модель для сеанса, не заменяя строку.
Изменения строк композиции применяются при следующем старте хоста. Перезапустите бэкенд либо выключите и включите Cordis, если переключатель администратора разблокирован. Сохранённые сеансы остаются в настроенном хранилище и возобновляются через текущую композицию.
Доверенный код интеграции может напрямую использовать API жизненного цикла Loader. Мост не предоставляет конечной точки замены адаптера и автоматически не восстанавливает предыдущий адаптер при сбое нового.
Откат
Освобождение корневого fiber хоста удаляет всё установленное движком. Эта связь владения и составляет гарантию:
- Сервисы регистрируют плагины, и они отзываются вместе с их fiber.
- Подписки
session/eventсоздаются в конструкторе моста и принадлежат fiber его строки. - Мост отслеживает дескрипторы агентов и освобождает их своим завершающим эффектом.
- Хост освобождает корневой контекст, владеющий всеми строками.
stopCordisHost() идемпотентен и включён в завершение бэкенда: таймеры и файловые дескрипторы освобождаются явно, а не остаются до выхода процесса.
Идентичность и сохранение сеансов
Страница Движка резервирует непрозрачный ID при создании. При включённом сохранении заголовок сразу записывается, поэтому даже пустой сеанс переживает перезапуск. Мост перечисляет сохранённые и активные сеансы, читает логи через проверяемый API хранения DSH и возобновляет агента на том же ID для следующего сообщения. Новые сообщения пользователя используют конструктор DSH с идентификатором.
Удаление сеанса сначала отменяет и освобождает агента, затем удаляет его файл. Локальный адаптер JSONL проверяет пути хранилища и сеанса и отвергает символьные ссылки. Пользовательские хранилища без поддержки удаления возвращают ошибку, а не объявляют данные удалёнными.
Отмена достигает нативного агента, запроса модели и инструментов. Отключение клиента отменяет ход; готовые сообщения остаются читаемыми. Повторная выдача буфера потока ограничена и сохраняет быстрый ответ до подключения читателя.
Движок хоста — функция solo с одной репликой. Развёртывания team не могут загрузить локальный JSONL-runtime. Work в песочнице использует существующие SQL-репозитории задач, запусков, сообщений, согласований и событий.
HTTP-интерфейс
| Метод | Путь | Назначение |
|---|---|---|
GET | /api/cordis/health | Состояние моста; без аутентификации |
GET | /api/cordis/sessions | Список сеансов |
POST | /api/cordis/sessions | Создание сеанса |
GET | /api/cordis/sessions/:id | Чтение сеанса с сообщениями |
DELETE | /api/cordis/sessions/:id | Завершение сеанса |
POST | /api/cordis/sessions/:id/messages | Отправка сообщения и поток NDJSON |
POST | /api/cordis/sessions/:id/cancel | Отмена активного хода |
GET | /api/cordis/agents | Список активных агентов |
GET | /api/cordis/tools | Список зарегистрированных инструментов |
Все маршруты, кроме /health, требуют аутентифицированный сеанс администратора. Пока мост не может обслуживать запросы, они отвечают 503 с code равным CORDIS_DISABLED, CORDIS_STARTING или CORDIS_UNAVAILABLE.

Страница — frontend/src/pages/CordisPage.tsx, доступна по /cordis из боковой панели. Она перечисляет сеансы и инструменты, создаёт сеансы и добавляет потоковый ход в историю. Если мост выключен или не запускается, показывается причина, а не пустой список: иначе «нет сеансов» неотличимо от «нет движка».
Клиент браузера — frontend/src/utils/api/cordisApi.ts. Он обращается только к этому интерфейсу, не импортируя типы бэкенда или пакеты @deepseek-ai/*; движок можно заменить без изменения фронтенда. Ход читается через sendMessage(sessionId, text, { onChunk }); клиент сам разбирает JSON по строкам и поддерживает фрагменты, разделённые между сетевыми чтениями.
Управление чатом Движка
Страница показывает Markdown, таблицы и код с подсветкой, с кнопками копирования ответов и кода. Системные промпты и внедрённый контекст среды находятся в свёрнутом разделе Контекст сеанса, а не изображаются сообщениями пользователя. Доступные рассуждения и активность инструментов имеют отдельные раскрываемые секции; результаты после перезагрузки остаются связаны с правильными операциями.
Выберите настоящую модель провайдера. Селектор показывает доступные локальные и плагинные модели вошедшего администратора вместе с провайдером. Персоны и агенты Чата не являются ID моделей и не внедряют инструкции в беседу Движка. Старые ошибочные заголовки с моделью персоны игнорируются как подсказки по умолчанию без изменения сохранённого лога.
У каждого сеанса свой режим Только чтение или Запись в рабочей области, обеспечиваемый файловой политикой DSH и канонической границей моста. Поле ввода показывает область действия. Настройки сохраняются нативными событиями и переживают рестарт; во время хода изменения отклоняются.
Нативный запрос расширения прав появляется карточкой Разрешить один раз / Отклонить у операции. Согласие действует только для этого запроса и не меняет постоянный режим. Устаревшие или отменённые запросы нельзя одобрить; headless-Чат отклоняет вопросы, которые не может показать. Мост не предлагает неограниченный доступ к хосту.
Дополнительные конечные точки администратора:
| Метод | Путь | Назначение |
|---|---|---|
GET | /api/cordis/models | Доступные модели провайдеров и текущая реальная модель по умолчанию |
PATCH | /api/cordis/sessions/:id/settings | Выбор модели или режима разрешений сеанса |
POST | /api/cordis/sessions/:id/approvals/:approvalId | Решение ожидающего запроса через allowed-once или rejected |
Использование движка в Чате
Включите Доступ и политики → Модели агентов CLI и Движок Cordis. Администраторы смогут выбрать DeepSeek Harness в Чате. Каждый запрос получает новый временный сеанс движка с переданной этим запросом историей. Обычная база Чата остаётся источником истины: независимые разговоры, ветки и повторы не делят невидимую историю движка. Временный лог удаляется после завершения или отмены и не отображается на странице Движка.
Стандартная композиция провайдеров также показывает DeepSeek Harness · модель (провайдер) в Агентах. Сохранённые ID оборачивают тот же квалифицированный маршрут, что у страницы Движка: dsh:lwui:ollama:<model> или dsh:lwui:plugin:<plugin>:<model>, с процентным кодированием компонентов. Необязательное локальное подключение нативного DSH добавляет dsh:native:<provider>:<model> из актуального каталога инстанции, повторно используя её настройки и учётные данные.
Установите отдельный пакет Apache-2.0 из libre-webui/dsh-native-provider либо подготовьте bundle из дистрибутива Libre WebUI. Оба используют имя @libre-webui/dsh-native-provider и хранят ключи в нативном DSH. Требуются один Unix-хост и одна учётная запись ОС с приватным Unix-сокетом; приложения под одной учётной записью не изолируются. Доступен только инференс модели, без нативных сеансов агента и выполнения инструментов. Установка, перезапуски профиля, обновление и удаление описаны в руководстве конфигурации. Недоступное соединение или выбранная модель вызывают ошибку без смены провайдера. Нативные вызовы также видны в Использование провайдера с моделью, сообщёнными токенами, задержкой и результатом.
Базовый профиль dsh сохраняет модель по умолчанию работающей композиции. Композиции с собственным адаптером предоставляют этот профиль, не предлагая неподдерживаемые переопределения провайдеров Libre WebUI.
Заголовки и итоги размышлений разрешают выбор DSH в лежащий под ним провайдер и напрямую запрашивают текст без инструментов или сеанса агента. Базовый профиль читает настройки работающего движка, включая переопределения строки моста, а не угадывает их по каталогу. Собственным адаптерам нужна явно выбранная модель задач Ollama или плагина. Недоступный провайдер даёт обычную ошибку либо локальное превью заголовка, не запрос другому провайдеру.
Используются настройки и учётные данные аутентифицированного администратора. Данные других администраторов не выбираются неявно. Настроенная область Cordis остаётся стандартной; Чат не подменяет её домашним каталогом пользователя сервера.
Work в песочнице
При включённом Cordis Work предлагает отдельный Движок: Libre WebUI или DeepSeek Harness. Селектор сохраняет обычные имена моделей и провайдеров. Для провайдеров LWUI выбор DSH хранится как dsh:<model>; для нативных — как providerType: dsh, точный ID провайдера и исходный ID модели. Обычные проверки доступа и поддержки инструментов остаются; нативные данные дополнительно требуют активного администратора.
Каждый запуск создаёт отдельный цикл DSH в памяти. Адаптер получает текущую историю Work, метаданные провайдера, изображения и схемы инструментов. Тела инструментов только ждут результатов Work; они не читают файлы хоста и не запускают его процессы.
Work проверяет аргументы, запрашивает согласия, выполняет инструменты в рабочей среде, сохраняет результаты и состояние воспроизведения провайдера в SQL, применяет бюджеты и публикует события. Отказ даёт обычный результат отказа. Отмена освобождает DSH и следует обычной очистке контейнера Work. После восстановления воркера новый драйвер получает восстановленный контекст без повторения завершённых эффектов инструментов.
Интеграции не нужны композиция Движка хоста и хранилище JSONL. Действуют правила Work для Docker/Kubernetes и развёртывания, включая общую постоянную память режима team.
Граница безопасности
Страница Движка и агент Чата на хосте доступны только администраторам. Сеансы — общая административная консоль, включая системные промпты, а не личные рабочие области. Обычные аккаунты не могут читать, создавать, менять или отменять их через API.
Поставляемые файловые инструменты ограничивают чтение и запись настроенной областью с каноническими путями и разрешением символьных ссылок. Переопределённые рабочие каталоги сеансов обязаны оставаться внутри. Действуют нативная политика изменений DSH и одноразовые согласия Движка. Плагины композиции оператора — доверенный серверный код, способный предоставлять дополнительные возможности. Согласования Движка отделены от согласований Work и выполнения в контейнерах.
Драйвер Work отдельный: он не загружает плагины файлов, оболочки или хранения хоста и выполняет действия только через авторизацию и песочницу Work. Удалённые модели остаются необязательными и используют настроенный маршрут выбранного аккаунта.