DeepSeek Harness: carga impulsada por dependencias, recarga automática y contexto anidado
Hemos aprendido la máquina de estados Fiber anteriormente; en esta entrada veremos cómo la máquina de estados es impulsada por las dependencias de servicios.
¿Por qué el plugin se desinstala automáticamente cuando el servicio del que depende desaparece? ¿Por qué puede recargarse automáticamente cuando el servicio se restaura?
Carga impulsada por dependencias
Un plugin que declara inject espera a que todos los servicios necesarios estén listos antes de entrar en LOADING y ejecutar apply.
Este mecanismo expresa el orden de carga mediante dependencias de servicios, sin necesidad de orquestar manualmente el orden de inicio.
Ejemplo
// Declara que este plugin depende de los servicios tools y llm
export const inject = ['tools', 'llm']
export function apply(ctx: Context) {
// En este punto ctx.tools y ctx.llm ya están listos y se pueden usar de forma segura
}
Si el servicio del que depende desaparece (por ejemplo, cuando el proveedor es reemplazado), el plugin se desinstala automáticamente, pasando directamente de ACTIVE a DISPOSED.
Cuando el servicio se restaura, el plugin se recarga y recorre nuevamente PENDING → LOADING → ACTIVE.
Las palabras textuales de la documentación oficial: si el servicio del que depende desaparece (por ejemplo, cuando el proveedor es reemplazado), el plugin se desinstala automáticamente (ACTIVE → DISPOSED) y se recarga cuando el servicio se restaura.
Garantías de la recarga automática
La recarga automática es segura porque todos los registros realizados a través de ctx se revierten al desinstalar.
Los listeners, herramientas y adaptadores de la instancia antigua no quedan residuales; la nueva instancia los vuelve a registrar.
Esto evita que el plugin llame a servicios que ya no existen, y también evita escuchas o registros duplicados.
Contexto anidado: ctx.plugin()
ctx.plugin() se usa para registrar un subplugin dentro del plugin actual.
El subplugin crea un Fiber hijo, que hereda el contexto del padre pero tiene un ciclo de vida independiente.
El Fiber hijo se desinstala junto con el plugin padre.
Ejemplo
export function apply(ctx: Context) {
// Registra un subplugin; childPlugin puede ser una función, un objeto o una subclase de Service
ctx.plugin(childPlugin)
// El subplugin tiene su propio Fiber y se desinstala junto con el plugin padre
}
El contexto anidado es muy común en la composición de plugins: un plugin "agregador" organiza varios plugins pequeños y gestiona sus ciclos de vida de manera unificada.
Terminación manual anticipada: fiber.dispose()
En la mayoría de los casos, el plugin se desinstala cuando el contexto se destruye, pero a veces necesitas terminar una instancia de plugin anticipadamente durante la ejecución.
El objeto Fiber devuelto por ctx.plugin() proporciona el métododispose()método.
Ejemplo
import type { Context } from '@deepseek-ai/cordis'
// Declaración de tipos: context y función de plugin (ilustrativo)
declare const ctx: Context
declare function myPlugin(ctx: Context): void
// Crea una instancia del plugin y obtén su Fiber
const fiber = ctx.plugin(myPlugin)
// Cuando necesites terminar manualmente, llama a dispose
// Nota: dispose devuelve una Promise; debes usar await para esperar a que se complete la limpieza asíncrona
await fiber.dispose()
dispose garantiza tres cosas:
| Garantía | Descripción |
|---|---|
| Todos los registros se eliminan | Todos los registros de listeners, herramientas, adaptadores, etc. que posee el plugin se revierten |
| Los subplugins se desinstalan recursivamente | Los Fibers hijos creados mediante ctx.plugin() también se limpian junto con él |
| Espera a que la limpieza asíncrona termine | La Promise devuelta se resuelve solo después de que todos los manejadores asíncronos de disposición hayan terminado |
En otras palabras, cuando await fiber.dispose() retorna, este plugin junto con su subárbol se han detenido por completo.
Resumen y autoevaluación
La carga impulsada por dependencias hace que el plugin se inicie solo cuando el servicio está listo; si el servicio desaparece se desinstala automáticamente, y si se restaura se recarga automáticamente; ctx.plugin() crea Fibers hijos que se pueden desinstalar recursivamente.
Autoevaluación:
- ¿En qué estado se detiene el plugin cuando el servicio declarado por inject no está listo?
- ¿Qué relación hay entre el Fiber hijo creado por ctx.plugin() y el Fiber padre?
- Cuando await fiber.dispose() retorna, ¿qué se garantiza que ya se ha completado?