Перейти до основного вмісту

Міст Cordis

Міст Cordis вбудовує рушій DeepSeek Harness (DSH) у backend Libre WebUI. DSH працює як дерево плагінів у середовищі Cordis, розміщеному в Libre WebUI, тому його можливості надаються як сервіси Cordis, а не безпосередньо імпортовані модулі.

Міст типово вимкнено. Описані тут дії не виконуються, доки оператор його не ввімкне. Дивіться Налаштування Cordis.

Чому міст, а не пряма інтеграція

Прямий імпорт пакетів DSH у сервіси Libre WebUI скоротив би код, але зробив би рушій залежністю часу компіляції. Тоді заміна адаптера моделі чи циклу агента або видалення рушія вимагала б змін і повторного розгортання Libre WebUI.

Міст змінює напрям залежності. Libre WebUI залежить від одного абстрактного контракту, а документ композиції Cordis визначає його реалізацію:

  • Зміна провайдера без перебудови. Композиція є YAML-файлом, тому вибір іншого провайдера є зміною налаштувань.
  • Налаштування можливостей. Кожна можливість є рядком Loader. Зміни операторської композиції набувають чинності при наступному запуску хоста.
  • Чисте видалення. Усі сервіси, обробники й ефекти рушія належать кореневому файберу. Його звільнення відкочує все, дозволяючи зупинити рушій без перезапуску 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. 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. Вибір моделі на сторінці рушія задає модель провайдера для сеансу, не замінюючи цей рядок.

Зміни рядків композиції діють після наступного запуску хоста. Перезапустіть backend або вимкніть і знову ввімкніть Cordis, якщо адміністративний перемикач не заблокований. Збережені сеанси залишаються в налаштованому сховищі та поновлюються через поточну композицію.

Довірений код інтеграції може напряму використовувати API життєвого циклу Cordis Loader. Міст не має endpoint для заміни адаптера й не відновлює старий адаптер автоматично після невдалої заміни.

Відкат

Звільнення кореневого файбера хоста видаляє все, встановлене рушієм. Гарантію забезпечує спільний власник ресурсів:

  • Сервіси реєструють плагіни, тому вони вилучаються разом зі своїм файбером.
  • Підписки session/event реєструються в конструкторі моста й належать файберу його рядка.
  • Міст відстежує дескриптори агентів і звільняє їх у своєму ефекті очищення.
  • Хост звільняє кореневий контекст, якому належать усі рядки.

stopCordisHost() є ідемпотентним і входить до процедури завершення backend. Таймери й файлові дескриптори рушія звільняються явно, а не залишаються до виходу процесу.

Ідентичність і збереження сеансів

Під час створення сторінка рушія резервує непрозорий 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.

Сторінка рушія Cordis у Libre WebUI зі списком сеансів, зареєстрованими інструментами та потоковою розмовою.

Сторінка реалізована в frontend/src/pages/CordisPage.tsx і доступна з бічної панелі за /cordis. Вона показує сеанси та інструменти, створює сеанси й передає хід у розмову потоком. Якщо міст вимкнено або не запускається, замість порожнього списку показується причина, щоб відрізняти відсутність сеансів від відсутності рушія.

Клієнт браузера міститься в frontend/src/utils/api/cordisApi.ts. Він використовує лише цей інтерфейс, не імпортує типів backend чи пакетів @deepseek-ai/*, тому заміна рушія не потребує змін frontend. Хід читається через sendMessage(sessionId, text, { onChunk }); клієнт сам розбирає JSON, розділений новими рядками, і підтримує розбиття частин між мережевими читаннями.

Керування чатом рушія

Сторінка рушія відображає Markdown, таблиці та код із підсвічуванням, а також кнопки копіювання відповідей і коду. Системні промпти та доданий контекст runtime зібрано у згорнутій секції Контекст сеансу, а не показано як повідомлення користувача. Відкрите міркування й дії інструментів мають окремі секції; після перезавантаження результати інструментів залишаються пов’язаними з правильними діями.

Вибирайте реальну модель провайдера в полі введення. Список показує локальні та плагінні моделі доступні адміністратору, включно з ідентичністю провайдера. Персони й агенти Chat не є ID моделей і не додають своїх інструкцій у розмову рушія. Старі заголовки невдалих моделей-персон не використовуються як підказки типової моделі, а збережений журнал не змінюється.

Кожен сеанс має режим Лише читання або Запис у робочому просторі. Його забезпечують файлова політика DSH і канонічна межа робочого простору. Поле введення показує цю область. Налаштування зберігаються як нативні події сеансу й переживають перезапуски; під час активного ходу зміни заборонені.

Нативний запит підвищення дозволів показується карткою Дозволити один раз / Відхилити біля відповідної дії. Погодження діє лише для цього запиту й не змінює постійний режим. Застарілі чи скасовані запити погодити не можна, а Chat без інтерфейсу відхиляє питання, які не може показати. Міст не надає необмеженого доступу до хоста.

Додаткові адміністративні endpoints:

МетодШляхПризначення
GET/api/cordis/modelsДоступні моделі провайдерів і поточна реальна типова модель
PATCH/api/cordis/sessions/:id/settingsВибір моделі та/або режиму дозволів цього сеансу
POST/api/cordis/sessions/:id/approvals/:approvalIdРішення щодо одного запиту: allowed-once або rejected

Використання рушія в Chat

Увімкніть Доступ і політики → Моделі агентів CLI та Рушій Cordis. Тоді адміністратори можуть обрати DeepSeek Harness у Chat. Кожен запит отримує новий тимчасовий сеанс рушія з переданою історією цього запиту. Звичайна база Chat залишається авторитетним джерелом: незалежні розмови, відгалуження та повтори не ділять приховану історію рушія. Тимчасовий журнал видаляється після завершення або скасування й не відображається на сторінці рушія.

Стандартна композиція також показує 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; Chat не підміняє його домашнім каталогом користувача сервера.

Work у пісочниці

За ввімкненого Cordis у Work є окремий Рушій із вибором Libre WebUI або DeepSeek Harness. Селектор зберігає звичні назви моделей та ідентичності провайдерів. Для провайдерів LWUI вибір DSH внутрішньо записується як dsh:<model>. Нативний вибір зберігає providerType: dsh, точний ID нативного провайдера й оригінальний ID моделі. Звичайні перевірки доступу й підтримки інструментів залишаються, а нативні облікові дані додатково вимагають активного адміністратора.

Кожен запуск створює ізольований цикл DSH-агента в пам’яті. Адаптер отримує поточну історію Work, метадані провайдера, зображення та схеми інструментів. Реалізації інструментів лише чекають результатів від Work; вони не читають файли хоста й не запускають його процеси.

Work продовжує перевіряти аргументи, запитувати погодження, виконувати інструменти в робочому runtime, записувати результати й стан повторного використання провайдера в SQL, забезпечувати бюджети та публікувати події. Відхилений інструмент дає звичайний результат відмови. Скасування звільняє DSH і використовує наявне очищення контейнерів Work. Після відновлення worker новий драйвер отримує відновлений контекст Work, не повторюючи завершених побічних ефектів.

Інтеграція Work не потребує композиції хостового рушія чи сховища JSONL. Вона дотримується наявних правил Docker/Kubernetes і розгортання Work, зокрема вимог спільного збереження team.

Межа безпеки

Сторінка рушія та хостовий агент Chat доступні лише адміністраторам. Сеанси рушія разом із системними промптами є спільною адміністративною консоллю, а не особистим простором користувача. Звичайні облікові записи не можуть читати, створювати, змінювати чи скасовувати їх через API.

Комплектні файлові інструменти хоста обмежують читання й запис налаштованим простором за канонічними шляхами, включно з розв’язанням символічних посилань. Перевизначений робочий каталог сеансу теж має залишатися всередині. Нативна політика змін DSH і разові погодження рушія діють і надалі. Операторські плагіни є довіреним серверним кодом і можуть додавати можливості. Погодження рушія відокремлені від погоджень та контейнерного виконання Work.

Драйвер DSH для Work окремий: не монтує файлові, shell чи persistence-плагіни хоста й виконує дії лише через наявну авторизацію та пісочницю Work. Віддалені моделі залишаються добровільним вибором і використовують маршрут налаштованого провайдера вибраного облікового запису.