Zum Hauptinhalt springen

Cordis-Brücke

Die Cordis-Brücke bettet die Engine von DeepSeek Harness (DSH) in das Backend von Libre WebUI ein. DSH läuft als Plugin-Baum in einer von Libre WebUI bereitgestellten Cordis-Laufzeit. Seine Funktionen werden dadurch als Cordis-Dienste statt als importierte Module verfügbar.

Die Brücke ist standardmäßig deaktiviert. Die hier beschriebenen Vorgänge finden erst statt, wenn ein Betreiber sie aktiviert (siehe Cordis-Konfiguration).

Warum eine Brücke statt einer direkten Integration

DSH-Pakete direkt aus den Diensten von Libre WebUI zu importieren wäre kürzer, aber schlechter. Ein direkter Import macht die Engine zu einer Abhängigkeit beim Kompilieren. Ein anderer Modelladapter, eine ausgetauschte Agentenschleife oder das Entfernen der Engine erfordern dann Änderungen an Libre WebUI und eine erneute Bereitstellung.

Die Brücke kehrt diese Beziehung um. Libre WebUI hängt von genau einem abstrakten Vertrag ab; ein Cordis-Kompositionsdokument legt fest, was ihn erfüllt:

  • Ziel ohne Neubuild ändern. Die Komposition ist eine YAML-Datei. Die Engine auf einen anderen Anbieter auszurichten ist daher eine Konfigurationsänderung.
  • Funktionen konfigurieren. Jede Funktion ist ein Loader-Eintrag. Änderungen an der vom Betreiber gepflegten Komposition gelten beim nächsten Start des Hosts.
  • Sauber entfernen. Jeder von der Engine installierte Dienst, Listener und Effekt gehört der Root-Fiber. Ihr Entsorgen nimmt all diese Änderungen zurück. Die Engine lässt sich somit ohne Neustart von Libre WebUI anhalten.

Schichten

Konkrete DSH-Abhängigkeiten bleiben in backend/src/cordis/dsh/. Routen und Anwendungsdienste verwenden die Verträge der Brücke. Der Work-Treiber hat eine eigene Komposition im Arbeitsspeicher und bindet niemals die Dateisystem-Plugins des Hosts ein.

Verträge

Der Vertrag liegt in backend/src/cordis/contracts.ts. Er umfasst bewusst nur die von der Libre-WebUI-API benötigten Strukturen und verwendet keine internen Engine-Begriffe.

VertragZweck
DshEngine.status()Lebenszyklusstatus jedes Engine-Dienstes (pending / ready / failed)
DshEngine.modelConfiguration()Standardmodell und Standardanbieter der laufenden Komposition
DshEngine.listSessions()Sitzungsübersichten, neueste zuerst
DshEngine.getSession(id)Eine Sitzung mit ihren projizierten Nachrichten
DshEngine.createSession(opts)Sitzungs-ID und Arbeitsverzeichnis reservieren
DshEngine.updateSessionSettings(id, settings)Tatsächliche Modellauswahl und nativen Dateisystem-Berechtigungsmodus im Leerlauf speichern
DshEngine.decideApproval(id, approvalId, decision)Eine ausstehende native Werkzeugfreigabe für ihre zugehörige Sitzung entscheiden
DshEngine.deleteSession(id)Eine Sitzung beenden und ihren Agenten entsorgen
DshEngine.listAgents()Laufende Agenten mit Kennzeichnung als Root oder Kind
DshEngine.listTools()Von der Engine registrierte, dem Modell angebotene Werkzeuge
DshEngine.sendMessage(id, txt)Einen Durchlauf starten und ein Stream-Handle zurückgeben
DshEngine.cancel(id)Den laufenden Durchlauf einer Sitzung abbrechen

Der Vertrag wird als Cordis-Dienst libreDshEngine veröffentlicht. Ein Verbraucher liest ihn mit ctx.get('libreDshEngine'), ohne das Brückenmodul zu importieren.

EngineStreamChunk transportiert text, reasoning, tool-call, tool-result, approval-request, approval-decision, error und done. Live-Frames werden anhand ihres Agenten und ihrer Sitzung weitergeleitet; die entsprechende dauerhaft gespeicherte Assistentennachricht wird nicht erneut ausgegeben. sendMessage liefert ein Handle, dessen subscribe bereits ausgegebene Daten erneut abspielt. Ein schnelles erstes Token kann dadurch zwischen dem Start des Durchlaufs und dem Anhängen des HTTP-Listeners nicht verloren gehen.

Ablauf eines Chat-Durchlaufs

NDJSON wird statt eines WebSockets verwendet, weil ein Durchlauf nach der Anfrage genau eine Sequenz vom Server zum Client bildet. Alles im POST zu belassen vermeidet einen zweiten Handshake, ein Ticket und ein Wiederverbindungsprotokoll. Der gesamte Durchlauf bleibt in einer authentifizierten Anfrage.

DONE und PENDING

Cordis aktiviert ein Plugin, sobald seine deklarierten Dienste verfügbar sind. Ein Eintrag verbringt daher Zeit in Zuständen, in denen er noch nicht läuft. Zwei unterschiedliche Begriffe sind wichtig; ihre Verwechslung ist die häufigste Ursache einer stummen Engine.

Zustand eines Loader-Eintrags. Der Loader verfolgt jeden Eintrag über PENDING → LOADING → ACTIVE oder FAILED. Fehlen deklarierte Dienste, bleibt der Eintrag unbegrenzt ausstehend, statt fehlzuschlagen. Deshalb kann eine unvollständige Komposition eine Engine starten, die nichts bereitstellt.

Verfügbarkeit eines Dienstes. Der Host meldet jeden erwarteten Dienst wie folgt:

StatusBedeutungUrsache
pendingNicht im Kontext registriertDer bereitstellende Eintrag wurde nicht aktiviert oder ist deaktiviert
readyRegistriert und nutzbarDer bereitstellende Eintrag wurde aktiviert
failedDeklariert, aber nicht nutzbarMit einer Zeichenfolge detail gemeldet

host.status() listet alle erwarteten Dienste samt Verfügbarkeit und benennt fehlende Pflichtdienste; GET /api/cordis/health liefert dieselben Informationen. Lässt eine Komposition einen erforderlichen Dienst aus, schlägt der Start mit einem Fehler fehl, statt eine Engine zu veröffentlichen, die leere Listen zurückgibt.

Zwei Abhängigkeitsketten werden leicht falsch zusammengestellt:

  • dsh-tools kann ohne systemPrompt nicht starten.
  • dsh-agent-loop kann erst starten, wenn agents, sessions, llm, tools, systemPrompt und sessionProjections vorhanden sind.

Fehlt einer dieser Dienste, kann der Sitzungsspeicher funktionieren, während die Engine niemals eine Nachricht beantwortet.

Anbieterkonfiguration

Der mitgelieferte Eintrag libre-webui-llm-adapter stellt die in Libre WebUI konfigurierten Modellanbieter bereit. Die Modellauswahl auf der Engine-Seite wählt ein Anbietermodell für eine Sitzung, ohne diesen Eintrag auszutauschen.

Änderungen an Plugin-Einträgen der Komposition gelten beim nächsten Host-Start. Starte das Backend neu oder deaktiviere und aktiviere Cordis erneut, wenn der Administratorschalter entsperrt ist. Gespeicherte Sitzungen verbleiben im konfigurierten Speicher und werden über die aktuelle Komposition fortgesetzt.

Vertrauenswürdiger Integrationscode kann die Lebenszyklus-APIs des Cordis Loaders direkt verwenden. Die Brücke bietet keinen Endpunkt zum Adapterwechsel und stellt einen vorherigen Adapter nicht automatisch wieder her, wenn ein Ersatz fehlschlägt.

Rücknahme

Das Entsorgen der Root-Fiber des Hosts entfernt alles, was die Engine installiert hat. Diese Eigentumsbeziehung bildet die gesamte Garantie, denn:

  • Dienste werden von Plugins registriert und zusammen mit deren Fiber zurückgezogen.
  • Abonnements von session/event werden im eigenen Konstruktor der Brücke registriert und gehören der Fiber ihres Eintrags.
  • Die Brücke verfolgt Agenten-Handles und entsorgt sie in ihrem Aufräumeffekt.
  • Der Host entsorgt den Root-Kontext, dem alle Einträge gehören.

stopCordisHost() ist idempotent und in die Abschaltsequenz des Backends eingebunden. Timer und Datei-Handles der Engine werden dadurch freigegeben, statt bis zum Prozessende erhalten zu bleiben.

Sitzungsidentität und Persistenz

Die Engine-Seite reserviert bei der Erstellung eine undurchsichtige Sitzungs-ID. Bei aktivierter Persistenz wird der Header sofort gespeichert, sodass selbst eine leere Sitzung einen Neustart übersteht. Die Brücke listet gespeicherte und laufende Sitzungen, liest gespeicherte Protokolle über die validierte Persistenz-API von DSH und setzt den Agenten bei einer Folgemeldung unter derselben ID fort. Neue Benutzernachrichten verwenden den Konstruktor für identifizierte Nachrichten von DSH.

Beim Löschen einer Sitzung wird ihr Agent vor dem Entfernen ihres Artefakts abgebrochen und entsorgt. Der lokale JSONL-Löschadapter validiert Speicher- und Sitzungspfade und lehnt symbolische Links ab. Benutzerdefinierte Persistenz-Backends ohne Löschunterstützung liefern einen Fehler, statt die Entfernung von Daten zu behaupten.

Ein Abbruch erreicht den nativen Agenten, die Modellanfrage und die Werkzeugausführung. Trennt sich ein Client, wird sein Durchlauf abgebrochen; abgeschlossene Nachrichten bleiben lesbar. Die gepufferte Stream-Wiedergabe ist begrenzt und erhält schnelle Antworten, die vor dem Anhängen eines Lesers eintreffen.

Die Host-Engine ist eine Solo-Funktion mit genau einer Replik. Team-Bereitstellungen dürfen ihre lokale JSONL-Laufzeit nicht einbinden. Work in der Sandbox verwendet stattdessen seine vorhandenen SQL-Repositories für Aufgaben, Ausführungen, Nachrichten, Freigaben und Ereignisse.

HTTP-Schnittstelle

MethodePfadZweck
GET/api/cordis/healthBrückenstatus; ohne Authentifizierung
GET/api/cordis/sessionsSitzungen auflisten
POST/api/cordis/sessionsEine Sitzung erstellen
GET/api/cordis/sessions/:idEine Sitzung samt Nachrichten lesen
DELETE/api/cordis/sessions/:idEine Sitzung beenden
POST/api/cordis/sessions/:id/messagesNachricht senden und NDJSON streamen
POST/api/cordis/sessions/:id/cancelDen laufenden Durchlauf abbrechen
GET/api/cordis/agentsLaufende Agenten auflisten
GET/api/cordis/toolsRegistrierte Werkzeuge auflisten

Jede Route außer /health benötigt eine authentifizierte Administratorsitzung und antwortet mit 503 und dem code CORDIS_DISABLED, CORDIS_STARTING oder CORDIS_UNAVAILABLE, solange die Brücke keine Anfragen bedienen kann.

Cordis-Engine-Seite von Libre WebUI mit Sitzungsliste, registrierten Engine-Werkzeugen und einer gestreamten Chat-Unterhaltung.

Die Seite befindet sich in frontend/src/pages/CordisPage.tsx und ist über /cordis in der Seitenleiste erreichbar. Sie listet Sitzungen und registrierte Werkzeuge, erstellt Sitzungen und streamt einen Durchlauf in den Gesprächsverlauf. Wenn die Brücke aus ist oder nicht starten kann, zeigt sie den Grund statt einer leeren Liste, weil „keine Sitzungen“ sonst genauso aussähe wie „keine Engine“.

Der Browser-Client liegt in frontend/src/utils/api/cordisApi.ts. Er kommuniziert ausschließlich mit dieser Schnittstelle und importiert weder Backend-Typen noch Pakete aus @deepseek-ai/*. Die Engine bleibt somit ohne Frontend-Änderung austauschbar. Ein Durchlauf wird mit sendMessage(sessionId, text, { onChunk }) verarbeitet; der Client parst das zeilenweise JSON selbst und toleriert über mehrere Netzwerklesevorgänge verteilte Fragmente.

Chat-Steuerelemente der Engine

Die Engine-Seite zeigt Markdown, Tabellen und Codeblöcke mit Syntaxhervorhebung sowie Kopierfunktionen für Antworten und Code. System-Prompts und eingespeister Laufzeitkontext werden im eingeklappten Bereich Sitzungskontext zusammengefasst und nicht als vom Benutzer verfasste Nachrichten dargestellt. Sichtbares Reasoning und Werkzeugaktivität haben getrennte aufklappbare Bereiche. Werkzeugergebnisse bleiben auch nach einem Neuladen der richtigen Operation zugeordnet.

Wähle in der Eingabe einen tatsächlichen Anbieter-Modelltyp. Die Auswahl nutzt verfügbare lokale und Plugin-Modelle des angemeldeten Administrators einschließlich Anbieteridentität. Persona- und Agentenauswahlen aus Chat sind keine Modell-IDs und speisen ihre Anweisungen nicht in ein Engine-Gespräch ein. Alte fehlerhafte Persona-Modell-Header werden als Standardmodellhinweise ignoriert, ohne das gespeicherte Protokoll zu verändern.

Jede Sitzung besitzt die Einstellung Nur lesen oder Schreiben im Arbeitsbereich. Die Dateisystemrichtlinie von DSH und die kanonische Arbeitsbereichsgrenze der Brücke setzen sie durch. Die Eingabe zeigt den Umfang des Arbeitsbereichs. Einstellungen werden als native Sitzungsereignisse gespeichert und überstehen Neustarts; während eines aktiven Durchlaufs werden Änderungen abgelehnt.

Eine native Anforderung zusätzlicher Rechte erscheint als Karte mit Einmal zulassen / Ablehnen bei der Operation. Die Freigabe gilt nur für diese Anfrage und verändert den dauerhaft gewählten Berechtigungsmodus nicht. Veraltete oder abgebrochene Anfragen können nicht genehmigt werden. Chat-Aufrufe ohne Benutzeroberfläche lehnen Fragen ab, die sie nicht anzeigen können. Die Brücke bietet keinen uneingeschränkten Host-Zugriff.

Zusätzliche Administrator-Endpunkte sind:

MethodePfadZweck
GET/api/cordis/modelsVerfügbare Anbietermodelle und aktuelles tatsächliches Standardmodell
PATCH/api/cordis/sessions/:id/settingsModell und/oder Berechtigungsmodus dieser Sitzung festlegen
POST/api/cordis/sessions/:id/approvals/:approvalIdEine ausstehende Anfrage mit allowed-once oder rejected entscheiden

Die Engine in Chat verwenden

Aktiviere Zugriff und Richtlinien → Agent-CLI-Modelle und Cordis-Engine. Administratoren können anschließend DeepSeek Harness in Chat wählen. Jede Anfrage erhält eine neue temporäre Engine-Sitzung mit dem für diese Chat-Anfrage gelieferten Gesprächsverlauf. Die normale Chat-Datenbank bleibt maßgeblich; unabhängige Gespräche, Abzweigungen und Wiederholungsversuche können keinen unsichtbaren Engine-Verlauf teilen. Das temporäre Engine-Protokoll wird nach Abschluss oder Abbruch entfernt und erscheint nicht auf der Engine-Seite.

Die Standard-Anbieterkomposition listet außerdem DeepSeek Harness · Modell (Anbieter) in der Gruppe Agenten. Die gespeicherten IDs kapseln dieselbe qualifizierte Anbieterroute wie die Engine-Seite: dsh:lwui:ollama:<model> oder dsh:lwui:plugin:<plugin>:<model>, wobei jede Anbieterkomponente prozentkodiert ist. Eine optionale lokale native DSH-Verbindung ergänzt dsh:native:<provider>:<model> aus dem aktuellen Modellkatalog dieser Instanz. Diese Einträge verwenden die Konfiguration und Zugangsdaten des nativen Anbieters. Installiere das eigenständige Apache-2.0-Paket von libre-webui/dsh-native-provider oder erstelle ein Bundle aus der Libre-WebUI-Distribution. Beide verwenden den Paketnamen @libre-webui/dsh-native-provider und belassen Anbieterschlüssel in nativem DSH. Die Verbindung benötigt denselben Unix-Host und dasselbe Betriebssystemkonto sowie einen privaten Unix-Socket; Anwendungen mit demselben Konto kann sie nicht voneinander isolieren. Sie stellt nur Modellinferenz bereit, keine nativen Agentensitzungen oder nativen Werkzeugausführungen. Installation, Profilneustarts, Upgrades und Entfernung beschreibt die Konfigurationsanleitung. Fehlende Verbindungen oder ausgewählte Modelle führen zu einem Fehler, ohne den Anbieter zu wechseln. Native Aufrufe erscheinen zudem unter Anbieter-Nutzung mit gewähltem Modell, gemeldeten Tokens, Latenz und Ergebnisstatus. Das Basisprofil dsh behält das Standardmodell der laufenden Komposition. Benutzerdefinierte Adapterkompositionen bieten dieses Basisprofil an, ohne nicht unterstützte Libre-WebUI-Anbieterüberschreibungen anzukündigen.

Titel und Reasoning-Zusammenfassungen lösen eine DSH-Auswahl zum zugrunde liegenden Anbieter auf und stellen eine direkte Textanfrage ohne Werkzeuge oder Agentensitzung. Anfragen an das Basisprofil lesen die Standardwerte der laufenden Engine einschließlich Überschreibungen im Brückeneintrag, statt sie aus dem aktuellen Anbieterkatalog zu erraten. Benutzerdefinierte Adapter benötigen hierfür ein ausdrücklich konfiguriertes Ollama- oder Plugin-Aufgabenmodell. Ein nicht verfügbarer ausgewählter Anbieter führt zum normalen Fehler oder zu einer lokalen Titelvorschau, nicht zu einer Anfrage an einen anderen Anbieter.

Die Anfrage verwendet die Anbietereinstellungen und Zugangsdaten des authentifizierten Administrators. Zugangsdaten eines anderen Administrators werden nicht stillschweigend ausgewählt. Der konfigurierte Cordis-Arbeitsbereich bleibt der Standard; Chat ersetzt ihn nicht durch das Benutzerverzeichnis des Serverkontos.

Work in der Sandbox

Bei aktiviertem Cordis bietet Work eine separate Engine-Auswahl mit Libre WebUI und DeepSeek Harness. Die Modellauswahl behält normale Modellnamen und Anbieteridentitäten bei. Für LWUI-Anbieter wird die DSH-Auswahl intern als dsh:<model> gespeichert. Native DSH-Auswahlen speichern stattdessen providerType: dsh, die exakte native Anbieter-ID und die unveränderte Modell-ID. Die normalen Werkzeugfähigkeits- und Zugriffsprüfungen gelten weiterhin; native Zugangsdaten erfordern zusätzlich einen aktiven Administrator.

Jede Ausführung erstellt eine isolierte DSH-Agentenschleife im Arbeitsspeicher. Ihr Modelladapter erhält den aktuellen Work-Verlauf, Anbietermetadaten, Bilder und Werkzeugschemas. Die Werkzeugimplementierungen warten lediglich auf die von Work gelieferten Ergebnisse; sie können keine Host-Dateien lesen oder Host-Prozesse starten.

Work bleibt für Argumentvalidierung, Freigabeanfragen, Werkzeugausführung in der Arbeitsbereichslaufzeit, Speicherung von Ergebnissen und Anbieter-Wiedergabezustand in SQL, Budgetdurchsetzung und Ereignisveröffentlichung zuständig. Ein abgelehntes Werkzeug erzeugt das normale Ablehnungsergebnis. Beim Abbruch wird DSH entsorgt und der bestehende Work-Containerbereinigungspfad verwendet. Nach einer Worker-Wiederherstellung erhält ein neuer DSH-Treiber den wiederhergestellten Work-Kontext und wiederholt keine bereits abgeschlossenen Werkzeugnebenwirkungen.

Die Work-Integration benötigt weder die Komposition der Host-Engine noch den JSONL-Sitzungsspeicher. Sie folgt den bestehenden Docker-/Kubernetes-Laufzeit- und Bereitstellungsregeln von Work einschließlich der Anforderungen an gemeinsame Persistenz im Team-Modus.

Sicherheitsgrenze

Die Engine-Seite und der hostseitige Chat-Agent sind ausschließlich für Administratoren bestimmt. Engine-Sitzungen sind eine gemeinsame Administratorkonsole einschließlich ihrer System-Prompts, kein benutzerspezifischer Arbeitsbereich. Normale Konten können sie über die API weder lesen, erstellen, ändern noch abbrechen.

Die mitgelieferten Host-Dateisystemwerkzeuge beschränken Lese- und Schreibzugriffe anhand kanonischer Dateisystemziele einschließlich der Auflösung symbolischer Links auf den konfigurierten Arbeitsbereich. Sitzungsspezifische Arbeitsverzeichnisse müssen darin bleiben. Die native DSH-Änderungsrichtlinie und einmalige Engine-Freigaben gelten weiterhin. Vom Betreiber installierte Kompositions-Plugins sind vertrauenswürdiger Servercode und können weitere Fähigkeiten gewähren. Engine-Freigaben sind vom Freigabe- und Container-Ausführungsablauf von Work getrennt.

Der DSH-Treiber von Work ist separat: Er bindet keine Host-Dateisystem-, Shell- oder Persistenz-Plugins ein und kann nur über die vorhandene Autorisierung und Sandbox von Work ausführen. Externe Modellanbieter bleiben ausdrücklich optional und verwenden die konfigurierte Anbieterroute des ausgewählten Kontos.