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."

💡 Dica: inject é dependência declarativa — você apenas diz ao framework "do que preciso," e ele trata de carregar na ordem correta. Nunca controle manualmente a ordem de carregamento.

📋 Pré-requisitos: Ter completado 11-first-plugin.md, entender apply e Context

1. O Que Você Vai Aprender

Mecanismo Inject Ready


2. Array inject para Declarar Dependências

(1) Forma Função

▶ Exemplo 1: Declarando Dependências na Forma Função

TYPESCRIPT
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

TYPESCRIPT
export default {
  name: 'my-tool',
  inject: ['tools', 'llm'],
  apply(ctx: Context) {
    // ...
  }
}

(3) Forma Classe

TYPESCRIPT
export default class MyPlugin {
  static name = 'my-tool'
  static inject = ['tools', 'llm']
  
  constructor(private ctx: Context) {
    // ...
  }
}

(4) Consequências de Não Declarar inject

TYPESCRIPT
// ❌ 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:

TYPESCRIPT
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:

TYPESCRIPT
// 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:

100%
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:

YAML
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:

100%
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

TEXT 📖 Somente leitura
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:

TYPESCRIPT
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

TYPESCRIPT
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

TYPESCRIPT
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:

TEXT 📖 Somente leitura
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:

TEXT 📖 Somente leitura
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

TEXT 📖 Somente leitura
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

TYPESCRIPT
// 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

TYPESCRIPT
// 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

TEXT 📖 Somente leitura
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

TYPESCRIPT
// 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

100%
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

100%
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

TYPESCRIPT
// 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

P A ordem do array inject importa?
R Não. inject apenas declara "preciso destes serviços"; o framework determina a ordem de carregamento com base no grafo global de dependências.
P O que acontece se eu esquecer de declarar inject mas usar um serviço?
R Sem erro em tempo de compilação (TypeScript pode alertar), mas em runtime a propriedade correspondente em ctx pode ser undefined, causando TypeError. Sempre declare os serviços que você usa.
P Quantos serviços um plugin pode depender?
R Sem limite rígido. Mas muitas dependências geralmente significa que as responsabilidades do plugin não estão claras — considere dividi-lo.
P Se o serviço de uma dependência opcional for registrado mais tarde, o plugin pendente se ativará automaticamente?
R Sim. Cordis monitora eventos de registro de serviço e automaticamente transiciona plugins pendentes para ativos quando dependências ficam prontas.
P Como ver todos os serviços atualmente registrados?
R bash pnpm dsh web --patch --dump-config # Ou em runtime: ctx.logger.info(Object.keys(ctx.services))
P Qual a diferença entre inject e import?
R 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


📝 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.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%