Bridge Cordis
Il bridge Cordis integra DeepSeek Harness (DSH) nel backend di Libre WebUI. DSH funziona come albero di plugin in un runtime Cordis ospitato da Libre WebUI: le funzionalità arrivano come servizi Cordis anziché moduli importati.
Il bridge è disattivato per impostazione predefinita. Quanto descritto qui avviene solo dopo l’abilitazione da parte dell’operatore; vedi Configurazione Cordis.
Perché un bridge anziché un’integrazione diretta
Importare i pacchetti DSH dai servizi Libre WebUI sarebbe più breve ma meno adatto. Renderebbe il motore una dipendenza in fase di compilazione: cambiare adattatore del modello, sostituire il ciclo agente o rimuovere il motore richiederebbe modificare e ridistribuire Libre WebUI.
Il bridge inverte la dipendenza. Libre WebUI usa un contratto astratto; un documento di composizione Cordis decide quale implementazione lo soddisfa:
- Cambiare destinazione senza ricompilare. La composizione è YAML: scegliere un altro provider è una modifica di configurazione.
- Configurare le funzionalità. Ogni funzionalità è una riga del Loader. Le modifiche alla composizione gestita dall’operatore valgono al successivo avvio dell’host.
- Rimuovere senza residui. La fibra radice possiede servizi, listener ed effetti installati dal motore. Il suo rilascio li ritira tutti, permettendo di arrestare il motore senza riavviare Libre WebUI.
Livelli
Le dipendenze DSH concrete restano in backend/src/cordis/dsh/. Route e servizi applicativi usano i contratti del bridge. Il driver Work ha una composizione separata in memoria e non monta mai plugin del filesystem host.
Contratti
Il contratto si trova in backend/src/cordis/contracts.ts. È volutamente limitato ai dati necessari all’API Libre WebUI, senza terminologia interna del motore.
| Contratto | Scopo |
|---|---|
DshEngine.status() | Stato del ciclo di vita di ogni servizio (pending / ready / failed) |
DshEngine.modelConfiguration() | Modello e provider predefiniti della composizione attiva |
DshEngine.listSessions() | Riepiloghi delle sessioni, dalla più recente |
DshEngine.getSession(id) | Una sessione con i messaggi proiettati |
DshEngine.createSession(opts) | Riservare ID e directory di lavoro della sessione |
DshEngine.updateSessionSettings(id, settings) | Salvare il modello reale e la modalità di permessi nativi del filesystem quando inattiva |
DshEngine.decideApproval(id, approvalId, decision) | Risolvere un’approvazione nativa in attesa per la sessione proprietaria |
DshEngine.deleteSession(id) | Terminare una sessione e rilasciare l’agente |
DshEngine.listAgents() | Agenti attivi, distinti in radice o figli |
DshEngine.listTools() | Strumenti registrati dal motore e visibili al modello |
DshEngine.sendMessage(id, txt) | Avviare un turno e restituire un handle di streaming |
DshEngine.cancel(id) | Annullare il turno attivo della sessione |
Il contratto viene pubblicato come servizio Cordis libreDshEngine: i consumatori lo leggono con ctx.get('libreDshEngine'), senza importare il modulo del bridge.
EngineStreamChunk trasporta text, reasoning, tool-call, tool-result, approval-request, approval-decision, error e done. I frame in tempo reale seguono l’agente e la sessione proprietari; il corrispondente messaggio persistente non viene emesso due volte. sendMessage restituisce un handle il cui subscribe riproduce quanto già emesso: un primo token veloce non si perde tra l’avvio del turno e il collegamento del listener HTTP.
Sequenza di un turno di chat
Si usa NDJSON anziché WebSocket perché un turno è una sequenza dal server al client dopo la richiesta. Mantenerla sul POST evita un secondo handshake, ticket e protocollo di riconnessione e conserva l’intero turno dentro una richiesta autenticata.
DONE e PENDING
Cordis attiva un plugin quando i servizi dichiarati sono disponibili; una riga attraversa quindi stati precedenti all’esecuzione. Distinguere i due concetti seguenti evita la causa più comune di un motore silenzioso.
Stato della voce Loader. Il Loader segue ogni riga attraverso PENDING → LOADING → ACTIVE oppure FAILED. Se mancano i servizi dichiarati, la riga resta in attesa indefinitamente anziché fallire: una composizione incompleta può quindi avviarsi senza servire nulla.
Disponibilità del servizio. L’host descrive ciascun servizio previsto così:
| Stato | Significato | Causa |
|---|---|---|
pending | Non registrato nel contesto | La riga che lo fornisce non è attiva oppure è disabilitata |
ready | Registrato e utilizzabile | La riga che lo fornisce è attiva |
failed | Dichiarato ma inutilizzabile | Segnalato con una stringa detail |
host.status() elenca disponibilità e servizi obbligatori mancanti; GET /api/cordis/health espone gli stessi dati. Omettere un servizio necessario genera un errore all’avvio, anziché pubblicare un motore che restituisce liste vuote.
Due catene di dipendenze sono facili da sbagliare:
dsh-toolsnon parte senzasystemPrompt.dsh-agent-loopparte solo quando esistonoagents,sessions,llm,tools,systemPromptesessionProjections.
La mancanza di uno di questi servizi produce un archivio di sessioni funzionante ma nessuna risposta ai messaggi.
Configurazione del provider
La riga fornita libre-webui-llm-adapter serve i provider configurati in Libre WebUI. Il selettore della pagina Motore sceglie il modello per una sessione senza sostituire quella riga.
Le modifiche alle righe della composizione valgono al successivo avvio dell’host. Riavvia il backend oppure disabilita e riabilita Cordis se l’interruttore amministrativo è sbloccato. Le sessioni persistenti restano nell’archivio configurato e riprendono tramite la composizione attuale.
Il codice di integrazione fidato può usare direttamente le API del ciclo di vita del Loader. Il bridge non espone un endpoint per sostituire l’adattatore né ripristina automaticamente quello precedente se il nuovo fallisce.
Ripristino
Il rilascio della fibra radice dell’host rimuove tutto ciò che il motore ha installato. La garanzia dipende da questa singola relazione di proprietà:
- I plugin registrano i servizi, ritirati insieme alla loro fibra.
- Le sottoscrizioni
session/eventnascono nel costruttore del bridge e appartengono alla fibra della riga. - Il bridge traccia gli handle degli agenti e li dispone nel proprio effetto di rilascio.
- L’host dispone il contesto radice, proprietario di tutte le righe.
stopCordisHost() è idempotente ed è collegato alla sequenza di arresto del backend: timer e handle di file vengono liberati esplicitamente, non lasciati all’uscita del processo.
Identità e persistenza delle sessioni
La pagina Motore riserva un ID opaco alla creazione. Con la persistenza attiva, l’intestazione viene salvata subito: anche una sessione vuota sopravvive al riavvio. Il bridge elenca sessioni salvate e attive, legge i log tramite l’API di persistenza convalidata di DSH e riprende l’agente sullo stesso ID per il seguito. I nuovi messaggi utente usano il costruttore DSH per messaggi identificati.
L’eliminazione annulla e dispone l’agente prima di rimuovere il file della sessione. L’adattatore JSONL locale verifica percorsi di archivio e sessione e rifiuta i collegamenti simbolici. Backend personalizzati senza supporto all’eliminazione restituiscono un errore, senza dichiarare falsamente la rimozione.
L’annullamento raggiunge agente nativo, richiesta al modello e lavoro degli strumenti. La disconnessione del client annulla il turno; i messaggi completati restano leggibili. Il buffer di riproduzione dello stream ha limiti e conserva le risposte veloci precedenti al collegamento del lettore.
Il Motore host è una funzione della modalità solo con una singola replica. Le distribuzioni team non possono montare il runtime JSONL locale. Work isolato usa invece gli archivi SQL esistenti per attività, esecuzioni, messaggi, approvazioni ed eventi.
Interfaccia HTTP
| Metodo | Percorso | Scopo |
|---|---|---|
GET | /api/cordis/health | Stato del bridge; senza autenticazione |
GET | /api/cordis/sessions | Elencare le sessioni |
POST | /api/cordis/sessions | Creare una sessione |
GET | /api/cordis/sessions/:id | Leggere una sessione e i messaggi |
DELETE | /api/cordis/sessions/:id | Terminare una sessione |
POST | /api/cordis/sessions/:id/messages | Inviare un messaggio con streaming NDJSON |
POST | /api/cordis/sessions/:id/cancel | Annullare il turno attivo |
GET | /api/cordis/agents | Elencare gli agenti attivi |
GET | /api/cordis/tools | Elencare gli strumenti registrati |
Tutte le route tranne /health richiedono una sessione autenticata di amministratore e, quando il bridge non può servire richieste, rispondono 503 con code pari a CORDIS_DISABLED, CORDIS_STARTING o CORDIS_UNAVAILABLE.

La pagina è frontend/src/pages/CordisPage.tsx, disponibile in /cordis dalla barra laterale. Elenca sessioni e strumenti, crea sessioni e aggiunge i turni in streaming alla trascrizione. Se il bridge è spento o non parte mostra il motivo invece di una lista vuota: altrimenti “nessuna sessione” e “nessun motore” sembrerebbero uguali.
Il client browser è frontend/src/utils/api/cordisApi.ts. Usa soltanto questa interfaccia, senza importare tipi backend o pacchetti @deepseek-ai/*, così il motore resta sostituibile senza modifiche frontend. I turni si consumano con sendMessage(sessionId, text, { onChunk }); il client analizza il JSON delimitato da nuove righe e gestisce i frammenti divisi fra letture di rete.
Controlli della chat del Motore
La pagina visualizza Markdown, tabelle e codice con evidenziazione sintattica, con comandi per copiare risposte e codice. Prompt di sistema e contesto runtime iniettato sono raccolti in Contesto della sessione, chiuso inizialmente, anziché apparire come messaggi dell’utente. Ragionamento esposto e attività degli strumenti hanno sezioni separate; i risultati restano associati alla corretta operazione dopo il ricaricamento.
Scegli un modello provider reale nel compositore. Il selettore usa i modelli locali e plugin disponibili all’amministratore connesso, con la relativa identità del provider. Le selezioni di persone e agenti di Chat non sono ID di modelli e non inseriscono le loro istruzioni nelle conversazioni del Motore. Le vecchie intestazioni errate persona-modello vengono ignorate come indicazioni predefinite, senza cambiare il log salvato.
Ogni sessione ha Sola lettura oppure Scrittura nello spazio di lavoro, applicato dalla politica filesystem DSH e dal confine canonico del bridge. Il compositore mostra l’area autorizzata. Le impostazioni sono eventi nativi persistenti e sopravvivono al riavvio; le modifiche durante un turno vengono rifiutate.
Una richiesta nativa di ampliamento dei permessi appare come scheda Consenti una volta / Rifiuta collegata all’operazione. L’approvazione vale solo per quella richiesta e non cambia i permessi permanenti. Non si possono approvare richieste scadute o annullate; Chat senza interfaccia rifiuta le domande che non può mostrare. Il bridge non offre accesso illimitato all’host.
Endpoint amministrativi aggiuntivi:
| Metodo | Percorso | Scopo |
|---|---|---|
GET | /api/cordis/models | Modelli provider disponibili e valore predefinito del modello reale |
PATCH | /api/cordis/sessions/:id/settings | Impostare modello e/o modalità di permessi della sessione |
POST | /api/cordis/sessions/:id/approvals/:approvalId | Decidere una richiesta in attesa con allowed-once o rejected |
Usare il motore in Chat
Abilita Accesso e criteri → Modelli di agenti CLI e Motore Cordis. Gli amministratori possono quindi scegliere DeepSeek Harness in Chat. Ogni richiesta riceve una sessione motore transitoria nuova contenente la trascrizione fornita da quella richiesta. Il normale database Chat resta autorevole: conversazioni indipendenti, fork e tentativi ripetuti non condividono una cronologia motore invisibile. Il log transitorio viene eliminato alla conclusione o all’annullamento e non appare nella pagina Motore.
La composizione provider standard elenca anche DeepSeek Harness · modello (provider) nel gruppo Agenti. Gli ID salvati racchiudono la stessa route qualificata della pagina Motore: dsh:lwui:ollama:<model> o dsh:lwui:plugin:<plugin>:<model>, con ogni componente provider codificato percentualmente. La connessione DSH nativa locale opzionale aggiunge voci dsh:native:<provider>:<model> dal catalogo attuale dell’istanza, riutilizzandone configurazione e credenziali.
Installa il pacchetto autonomo Apache-2.0 da libre-webui/dsh-native-provider, oppure prepara un bundle dalla distribuzione Libre WebUI. Entrambi usano @libre-webui/dsh-native-provider e conservano le chiavi in DSH. Servono lo stesso host Unix e account del sistema operativo, con socket Unix privato; la connessione non isola applicazioni dello stesso account. Espone soltanto inferenza, senza sessioni agente o esecuzione strumenti nativi. La guida alla configurazione descrive installazione, riavvii dei profili, aggiornamenti e rimozione. Connessioni o modelli mancanti falliscono senza cambiare provider. Le chiamate native compaiono anche in Utilizzo provider con modello, token dichiarati, latenza ed esito.
Il profilo base dsh mantiene il modello predefinito della composizione attiva. Le composizioni con adattatori personalizzati espongono questo profilo senza pubblicizzare sostituzioni provider LWUI non supportate.
Titoli e riepiloghi del ragionamento risolvono la selezione DSH nel provider sottostante e inviano testo direttamente, senza strumenti o sessione agente. Il profilo base legge i valori della composizione attiva, incluse le sostituzioni della riga bridge, senza dedurli dal catalogo. Gli adattatori personalizzati richiedono un modello per le attività Ollama o plugin configurato esplicitamente. Un provider indisponibile produce il normale errore o l’anteprima locale del titolo, senza inviare richieste a un altro provider.
Si usano impostazioni e credenziali del provider dell’amministratore autenticato; quelle di altri amministratori non vengono scelte implicitamente. L’area Cordis configurata resta quella predefinita; Chat non la sostituisce con la home dell’utente server.
Work in sandbox
Quando Cordis è attivo, Work offre un controllo Motore separato con Libre WebUI e DeepSeek Harness. Il selettore conserva nomi e identità dei provider. Internamente, DSH è salvato come dsh:<model> per provider LWUI; le scelte native salvano invece providerType: dsh, ID provider nativo esatto e ID modello grezzo. Restano validi i controlli di accesso e capacità strumenti; le credenziali native richiedono inoltre un amministratore attivo.
Ogni esecuzione crea un ciclo DSH isolato in memoria. L’adattatore riceve trascrizione Work attuale, metadati del provider, immagini e schemi strumenti. I corpi degli strumenti attendono soltanto i risultati restituiti da Work: non possono leggere file host né avviare processi host.
Work resta responsabile di convalidare argomenti, chiedere approvazioni, eseguire nel runtime dell’area di lavoro, salvare risultati e stato di riproduzione provider in SQL, applicare budget e pubblicare eventi. Uno strumento negato produce il risultato normale di rifiuto. L’annullamento dispone DSH e segue la pulizia del container Work. Dopo il recupero del worker, un nuovo driver riceve il contesto ripristinato senza ripetere gli effetti già completati.
L’integrazione non richiede composizione del Motore host o archivio JSONL. Segue le regole Docker/Kubernetes e di distribuzione Work, inclusa la persistenza condivisa della modalità team.
Confine di sicurezza
Pagina Motore e agente Chat lato host sono riservati agli amministratori. Le sessioni sono una console amministrativa condivisa, inclusi i prompt di sistema, non un’area per utente. Gli account ordinari non possono leggerle, crearle, modificarle o annullarle tramite API.
Gli strumenti filesystem forniti confinano letture e scritture all’area configurata tramite percorsi canonici, risolvendo anche i collegamenti simbolici. Le directory di sessione alternative devono restare in quest’area. Restano valide la politica DSH sulle modifiche e le approvazioni singole del Motore. I plugin di composizione installati dall’operatore sono codice server fidato e possono concedere capacità aggiuntive. Le approvazioni del Motore sono separate da quelle di Work e dalla sua esecuzione nei container.
Il driver DSH di Work è distinto: non monta plugin filesystem, shell o persistenza host ed esegue solo attraverso autorizzazione e sandbox Work. I provider remoti restano opzionali e usano la route configurata dell’account selezionato.