DeepSeek Harness: Declarando Dependências: inject
Última atualização: 2026-08-31
Plugins não são ilhas — a maioria dos plugins precisa de serviços fornecidos por outros plugins. O array inject é o mecanismo de declaração de dependências do Cordis, garantindo que dependências carreguem antes de seus consumidores e eliminando erros de runtime "serviço não existe."
📋 Pré-requisitos: Ter completado 11-first-plugin.md, entender apply e Context
1. O Que Você Vai Aprender
- Array
injectpara declarar dependências - Lista de serviços built-in: tools, llm, sessions, fs, shell, etc.
- Garantias de ordem de carregamento de dependências
- Dependências opcionais
inject: ['tools', 'llm?'] - Detecção de dependências circulares
- Mecanismo de injeção de dependência subjacente
2. Array inject para Declarar Dependências
(1) Forma Função
▶ Exemplo 1: Declarando Dependências na Forma Função
import { Context } from '@deepseek-ai/cordis'
export const name = 'my-tool'
export const inject = ['tools', 'llm']
export function apply(ctx: Context) {
// ctx.tools e ctx.llm são garantidos como prontos quando apply é chamado
ctx.logger.info('tools ready:', !!ctx.tools)
ctx.logger.info('llm ready:', !!ctx.llm)
}
O array inject lista todos os nomes de serviços que o plugin precisa. O framework garante que esses serviços estejam registrados antes de apply ser chamado.
(2) Forma Objeto
export default {
name: 'my-tool',
inject: ['tools', 'llm'],
apply(ctx: Context) {
// ...
}
}
(3) Forma Classe
export default class MyPlugin {
static name = 'my-tool'
static inject = ['tools', 'llm']
constructor(private ctx: Context) {
// ...
}
}
(4) Consequências de Não Declarar inject
// ❌ Não declarar inject, usar serviços diretamente
export function apply(ctx: Context) {
ctx.tools.register(...) // Erro de runtime: ctx.tools pode não existir
}
Sem declarar inject, o plugin pode carregar antes que os serviços dependentes sejam registrados, causando ctx.tools como undefined.
3. Lista de Serviços Built-in
(1) Serviços Centrais
O DSH fornece os seguintes serviços através de plugins built-in:
| Nome do Serviço | Provedor | Função |
|---|---|---|
tools |
dsh-core | Registro e execução de ferramentas |
llm |
dsh-plugin-llm | Adaptador LLM |
sessions |
dsh-core | Gerenciamento de sessões |
fs |
dsh-plugin-fs | Operações no sistema de arquivos |
shell |
dsh-plugin-shell | Execução de comandos Shell |
sandbox |
dsh-plugin-sandbox | Ambiente sandbox |
search |
dsh-plugin-search | Busca de código |
trajectory |
dsh-core | Registro de logs |
(2) Método de Acesso a Serviços
Após declarar inject, acesse serviços via ctx.serviceName:
export const inject = ['tools', 'llm']
export function apply(ctx: Context) {
// ctx.tools — Serviço de ferramentas
ctx.tools.register({
name: 'my_tool',
// ...
})
// ctx.llm — Serviço LLM
const response = await ctx.llm.complete({
messages: [{ role: 'user', content: 'hello' }]
})
}
(3) Inferência de Tipo de Serviço
TypeScript automaticamente infere tipos de serviço em ctx baseado no inject:
// inject = ['tools'] → ctx.tools: ToolsService
// inject = ['llm'] → ctx.llm: LLMService
// inject = ['tools', 'llm'] → ambos ctx.tools + ctx.llm são tipados
4. Garantia de Ordem de Carregamento de Dependências
(1) Ordenação Topológica
Cordis constrói um grafo de dependências a partir das declarações inject de todos os plugins, depois carrega em ordem topológica:
graph LR
A[plugin-a<br/>inject: []] --> B[plugin-b<br/>inject: ['a']]
B --> C[plugin-c<br/>inject: ['a', 'b']]
Ordem de carregamento: A → B → C
(2) Ordenação Automática
Você não precisa controlar manualmente a ordem de carregamento. Mesmo se C aparecer antes de A no cordis.yml:
plugins:
plugin-c: ...
plugin-a: ...
plugin-b: ...
O framework ainda carrega na ordem A → B → C.
(3) Carregamento Paralelo
Plugins sem relações de dependência podem carregar em paralelo:
graph TB
A[plugin-a] --> C[plugin-c<br/>inject: a, b]
B[plugin-b] --> C
A e B podem carregar simultaneamente; C apenas carrega após ambos completarem.
(4) Fases de Carregamento
Fase 1: Carregar plugins sem dependências → [core, logger]
Fase 2: Carregar plugins dependendo da Fase 1 → [tools, sessions]
Fase 3: Carregar plugins dependendo da Fase 2 → [my-plugin, other-plugin]
...
5. Dependências Opcionais
(1) Sintaxe
Acrescente ? ao nome da dependência para torná-la opcional:
export const inject = ['tools', 'llm?']
Significado: tools é uma dependência obrigatória (ausência causa falha no carregamento), llm é opcional (ausência ainda carrega normalmente).
(2) Acessando Dependências Opcionais
▶ Exemplo 2: Dependência Opcional com Fallback
export const inject = ['tools', 'llm?']
export function apply(ctx: Context) {
// tools sempre existe
ctx.tools.register(...)
// llm pode não existir
if (ctx.llm) {
ctx.llm.complete(...)
} else {
ctx.logger.warn('llm não disponível, pulando recursos LLM')
}
}
(3) Casos de Uso de Dependências Opcionais
| Cenário | Obrigatória/Opcional | Motivo |
|---|---|---|
| Registro de ferramenta deve usar tools | Obrigatória | Funcionalidade central |
| Aprimoramento com capacidade LLM | Opcional | Funciona sem ela |
| Funcionalidade de Sandbox | Opcional | Nem todos os ambientes têm sandbox |
| Serviço de logging | Obrigatória | Infraestrutura |
(4) Detecção em Runtime
export const inject = ['tools', 'search?']
export function apply(ctx: Context) {
ctx.tools.register({
name: 'smart_search',
async execute(params) {
if (ctx.search) {
return ctx.search.query(params.query)
}
return 'serviço de busca não disponível'
}
})
}
6. Detecção de Dependências Circulares
(1) O Que É uma Dependência Circular
A depende de B, e B depende de A:
A inject: ['B']
B inject: ['A']
Isso cria um deadlock: A espera por B, B espera por A — nenhum pode carregar.
(2) Mecanismo de Detecção do Cordis
O framework verifica o grafo de dependências na inicialização e reporta imediatamente dependências circulares:
Error: Circular dependency detected:
plugin-a → plugin-b → plugin-a
Please review your inject declarations.
(3) Resolvendo Dependências Circulares
Solução 1: Extrair Dependências Compartilhadas
Antes: A → B → A
Depois: A → C, B → C
Extraia a lógica que tanto A quanto B precisam para C.
Solução 2: Desacoplar com Eventos
▶ Exemplo 3: Desacoplamento de Dependência Circular via Eventos
// A não depende diretamente de B, mas escuta eventos
export const inject = []
export function apply(ctx: Context) {
ctx.on('b/ready', (bService) => {
// A usa capacidades de B sem declarar dependência
})
}
Solução 3: Usar Dependências Opcionais
// A depende opcionalmente de B
export const inject = ['B?']
export function apply(ctx: Context) {
if (ctx.B) {
// Usar B
}
}
(4) Ciclos de Três Nós
A → B → C → A
Cordis também pode detectar ciclos de múltiplos nós. A mensagem de erro mostra a cadeia completa.
7. Mecanismo de Injeção de Dependência Subjacente
(1) Registro e Descoberta de Serviços
// Plugin provedor registra serviço
ctx.provide('tools', toolsInstance)
// Plugin consumidor descobre serviço
const tools = ctx.get('tools')
(2) Timing do inject e apply
sequenceDiagram
participant F as Framework
participant P as Plugin Provedor
participant C as Plugin Consumidor
F->>P: Carregar Provedor
P->>F: apply() → Registrar serviço 'tools'
F->>C: Verificar inject ['tools'] ✅ Pronto
F->>C: Chamar apply()
C->>F: ctx.tools disponível
(3) Quando Dependências Não Estão Prontas
sequenceDiagram
participant F as Framework
participant C as Plugin Consumidor
F->>F: Verificar inject ['tools'] ❌ Não pronto
F->>C: Plugin entra em estado pendente
Note over F: Aguardando registro do serviço tools
F->>F: Serviço tools registrado
F->>C: Re-verificar ✅ → Chamar apply()
Plugins não são descartados quando dependências estão ausentes — entram em estado pendente e se ativam automaticamente quando dependências ficam prontas.
(4) Injeção de Dependência com Tipagem Segura
// Mapeamento de tipos interno do framework
interface Context {
tools: ToolsService // Quando inject inclui 'tools'
llm: LLMService // Quando inject inclui 'llm'
sessions: SessionService // Quando inject inclui 'sessions'
// ...
}
O mecanismo de tipos condicionais do TypeScript automaticamente estende a definição de tipo de ctx baseado no array inject, garantindo segurança de tipo em tempo de compilação.
❓ Perguntas Frequentes
bash pnpm dsh web --patch --dump-config # Ou em runtime: ctx.logger.info(Object.keys(ctx.services)) import é a referência estática de módulo do TypeScript, determinada em tempo de compilação. inject é dependência de serviço em runtime, resolvida pelo framework Cordis durante o carregamento do plugin. Eles se complementam: import traz tipos e funções utilitárias, inject declara dependências de serviço em runtime.📖 Resumo
- O array
injectdeclara dependências de runtime de um plugin; o framework garante que dependências carreguem antes de apply - Serviços built-in: tools, llm, sessions, fs, shell, sandbox, search, trajectory
- Acrescente
?ao nome da dependência para dependências opcionais; plugins carregam normalmente mesmo se ausentes - Cordis detecta automaticamente dependências circulares e reporta erros; resolva extraindo dependências compartilhadas / desacoplando com eventos / usando dependências opcionais
- Quando dependências estão ausentes, plugins entram em estado pendente e se ativam automaticamente quando prontas
- inject é dependência declarativa — nunca controle manualmente a ordem de carregamento
📝 Exercícios
1. ⭐ Básico: Escreva um plugin declarando inject: ['tools'], registre uma ferramenta simples usando ctx.tools.register em apply. Inicie e verifique que a ferramenta está disponível.
2. ⭐⭐ Intermediário: Escreva um plugin declarando inject: ['tools', 'llm?']. Quando llm está disponível, a ferramenta chama LLM para aprimoramento; quando indisponível, retorna resultados degradados. Teste ambos os cenários.
3. ⭐⭐⭐ Desafio: Deliberadamente crie dois plugins mutuamente dependentes A (inject: ['B']) e B (inject: ['A']), observe o erro de dependência circular do Cordis. Depois reescreva usando desacoplamento por eventos para eliminar a dependência circular, verificando que ambos os plugins carregam normalmente.