open-sse Architecture (Português (Brasil))
Por que um pacote de workspace separado?
Seção intitulada “Por que um pacote de workspace separado?”open-sse/ é um workspace independente no monorepo do OmniRoute por vários motivos:
- Reutilização —
open-sseé publicado como@omniroute/open-sseno npm, permitindo que outros projetos o utilizem de forma independente - Limites bem definidos — o mecanismo de streaming é desacoplado da camada de interface/BD específica do OmniRoute
- Desempenho — o mecanismo não possui dependências do Next.js, permitindo inicializações a frio mais rápidas em contextos de CLI/serverless
- Versionamento —
open-ssepode lançar versões em seu próprio ritmo
"workspaces": ["open-sse"]Estrutura de nível superior
Seção intitulada “Estrutura de nível superior”open-sse/├── index.ts # Ponto de entrada público├── types.d.ts # Exportações públicas de tipos├── package.json # @omniroute/open-sse├── config/ # Configurações de provedores, constantes e registros├── executors/ # Executores HTTP por provedor (67 + base.ts/index.ts)├── handlers/ # Manipuladores de solicitações (chatCore, responses etc.)├── lib/ # Utilitários internos├── mcp-server/ # Servidor Model Context Protocol├── services/ # Cerca de 298 módulos de serviço├── transformer/ # Transformador de formato da Responses API├── translator/ # Tradução de formatos (OpenAI ↔ Claude ↔ Gemini)└── utils/ # Utilitários compartilhados (logs, erros, streams etc.)Quantidade de módulos
Seção intitulada “Quantidade de módulos”| Diretório | Arquivos | Finalidade |
| executors/ | 167 | Executores HTTP por provedor (unificados por meio da factory DefaultExecutor) |
| handlers/ | 157 | Pontos de entrada de solicitações (chatCore, responses, embeddings) |
| services/ | ~536 | Roteamento, cache, limitação de taxa, atualização etc. |
| translator/ | 56 | Conversão de formatos (OpenAI ↔ Claude ↔ Gemini) |
| mcp-server/ | 44 | Ferramentas e transportes MCP |
| utils/ | ~108 | Utilitários transversais (logs, erros, streams) |
| config/ | ~339 | Configurações de provedores, constantes e registros |
O pipeline de solicitações
Seção intitulada “O pipeline de solicitações”Todas as solicitações a LLMs passam por um pipeline de 5 etapas:
┌──────────────┐ Solicitação HTTP │ 1. ROTEAR │ resolução de combos, seleção de modelo (rota do Next.js) └──────┬───────┘ │ ▼ ┌──────────────┐ │ 2. TRADUZIR │ conversão de formatos (OpenAI ↔ Claude ↔ Gemini) └──────┬───────┘ │ ▼ ┌──────────────┐ │ 3. EXECUTAR │ executor do provedor, HTTP, repetição, circuit breaker └──────┬───────┘ │ ▼ ┌──────────────┐ │ 4. STREAM │ transformação SSE, backpressure └──────┬───────┘ │ ▼ ┌──────────────┐ │ 5. REGISTRAR│ rastreamento de uso, log de chamadas, classificação de erros └──────┬───────┘ │ ▼ Resposta HTTP (SSE ou JSON)Etapa 1: Roteamento (services/combo.ts)
Seção intitulada “Etapa 1: Roteamento (services/combo.ts)”Ponto de entrada: handleComboChat() em services/combo.ts
Resolve a solicitação em uma tupla concreta (provider, model, account, credentials):
- Busca o combo pelo ID (ou cria um combo virtual para modelos
auto/*) - Aplica a estratégia de roteamento (prioridade, ponderada, round-robin etc.)
- Filtra provedores não íntegros (circuit breaker)
- Escolhe o próximo destino viável
Para modelos auto/*, esta etapa também:
- Executa o algoritmo de pontuação com 16 fatores (
services/autoCombo/) - Seleciona um par
provider+modelcom base em integridade, custo, latência etc.
Etapa 2: Tradução (translator/)
Seção intitulada “Etapa 2: Tradução (translator/)”Se o formato de origem (por exemplo, OpenAI) for diferente do formato de destino (por exemplo, Claude), a solicitação será traduzida:
- Prompt do sistema → mensagem do sistema
- Definições de ferramentas → formato de ferramentas específico do provedor
- Parâmetros de raciocínio/pensamento → equivalentes específicos do provedor
- Normalização das funções das mensagens (
developer→systempara provedores diferentes da OpenAI)
O translator/index.ts expõe:
translateRequest(body, sourceFormat, targetFormat): TranslatedRequestneedsTranslation(source, target): booleanEtapa 3: Execução (executors/)
Seção intitulada “Etapa 3: Execução (executors/)”Ponto de entrada: getExecutor(providerId).execute(request, options)
Todos os provedores utilizam DefaultExecutor (executors/default.ts) por meio do fallback da factory getExecutor(). O executor:
- Cria a URL upstream (
buildUrl()) - Adiciona cabeçalhos específicos do provedor (
buildHeaders()) - Transforma o corpo da solicitação (
transformRequest()) - Envia a solicitação HTTP com novas tentativas + backoff exponencial
- Processa a renovação da autenticação quando necessário (provedores OAuth)
Todos os executores estendem BaseExecutor (executors/base.ts, 1170 linhas de código), que fornece:
- Lógica comum de novas tentativas
- Integração com proxy
- Integração com circuit breaker
- Hooks de registro de uso
Etapa 4: Streaming (utils/stream.ts)
Seção intitulada “Etapa 4: Streaming (utils/stream.ts)”Para respostas em streaming, o executor retorna um ReadableStream. O manipulador:
- Encaminha o fluxo por meio de uma transformação SSE (
createSSETransformStreamWithLogger) - Aplica pings de heartbeat para detectar conexões inativas
- Processa de forma adequada a desconexão do cliente (
pipeWithDisconnect) - Transforma SSE → JSON para clientes sem streaming
Para respostas sem streaming, o executor retorna um objeto JSON analisado, que é repassado sem alterações.
Etapa 5: Registro (services/usage.ts)
Seção intitulada “Etapa 5: Registro (services/usage.ts)”Após a resposta (bem-sucedida ou com falha), o uso é registrado:
prompt_tokens,completion_tokens,cached_tokensda respostacost_usdcalculado com base nos dados de preçoslatency_ms,status,error_classem caso de falha- Persistido na tabela
usage_history
Os artefatos de log das chamadas (se habilitados) são gravados em ${DATA_DIR}/call_logs/.
Análise Detalhada dos Principais Arquivos
Seção intitulada “Análise Detalhada dos Principais Arquivos”chatCore.ts (5977 linhas)
Seção intitulada “chatCore.ts (5977 linhas)”O principal manipulador de requisições. Apesar de seu tamanho, ele tem uma estrutura clara:
// Pseudoestrutura de chatCore.tsexport async function handleChat(request: NextRequest) { // 1. Autenticação + CORS await authenticateRequest(request); applyCorsHeaders(response);
// 2. Validação do corpo const body = await parseRequestBody(request);
// 3. Detecção de formato + tradução const sourceFormat = detectFormat(request); const targetFormat = getTargetFormat(providerId); if (needsTranslation(sourceFormat, targetFormat)) { body = translateRequest(body, sourceFormat, targetFormat); }
// 4. Roteamento de combo const targets = await resolveComboTargets(comboId, body); for (const target of targets) { try { const result = await executeOnTarget(target, body); await recordUsage(result); return result; } catch (err) { // Continuar para o próximo destino } }
// 5. Fallback de emergência return await emergencyFallback(body);}Apesar de ser uma única função gigantesca, ela está organizada em seções comentadas que correspondem ao pipeline de 5 etapas.
combo.ts (4456 linhas de código)
Seção intitulada “combo.ts (4456 linhas de código)”O mecanismo de roteamento que resolve um combo em destinos ordenados.
export async function handleComboChat(body, comboId): Promise<ChatResult> { const targets = await resolveComboTargets(comboId, body); for (const target of targets) { try { return await handleSingleModel(target, body); } catch (err) { log.warn("target failed, trying next", { target, err }); } } throw new ComboExhaustedError("All targets failed");}Oferece suporte a 19 estratégias de roteamento (consulte src/shared/constants/routingStrategies.ts):
| Estratégia | Comportamento |
|---|---|
priority |
Lista ordenada com o primeiro destino em primeiro lugar |
weighted |
Probabilístico por peso de cada destino |
round-robin |
Percorre os destinos em ordem |
context-relay |
Transfere o contexto entre os destinos |
fill-first |
Preenche a cota antes de passar para o próximo |
p2c |
Poder de duas escolhas |
random |
Aleatório uniforme |
least-used |
Escolhe aquele com menos usos recentes |
cost-optimized |
Destino íntegro mais barato primeiro |
reset-aware |
Considera as janelas de redefinição do provedor |
reset-window |
Roteamento baseado na janela de redefinição |
headroom |
Maior margem de cota restante primeiro |
strict-random |
Verdadeiramente uniforme (sem ponderação por qualidade) |
auto |
Usa pontuação de 16 fatores (autoCombo/) |
lkgp |
Último provedor conhecido como funcional primeiro |
context-optimized |
Melhor para requisições de contexto longo |
fusion |
Distribui para um painel em paralelo e depois sintetiza via juiz (fusion.ts) |
base.ts (1170 linhas de código)
Seção intitulada “base.ts (1170 linhas de código)”O executor abstrato que todos os 107 executores estendem. Ele contém:
buildUrl()— construção padrão da URL (as subclasses a substituem para personalização)buildHeaders()— cabeçalhos padrão (autenticação, tipo de conteúdo)transformRequest()— passagem direta por padrãoexecute()— o loop HTTP principal com nova tentativa/recuo/disjuntor
export class DefaultExecutor extends BaseExecutor { // Lida com todos os provedores compatíveis com OpenAI/Anthropic // Os provedores registram configurações (URL, autenticação, cabeçalhos), mas compartilham a lógica do executor}O comportamento específico de cada provedor (cabeçalhos de autenticação, URL base, cabeçalhos de versão) é configurado por meio do registro de provedores, não por classes de executor separadas.
---
## Serviços (117 módulos)
Os serviços são **módulos focados e de propósito único** que os handlers compõem. As principais categorias:
### Roteamento e Combinação
- `combo.ts` — ponto de entrada para solicitações roteadas por combinação- `services/autoCombo/` — pontuação de 16 fatores, 8 estratégias de roteamento automático- `wildcardRouter.ts` — corresponde a rotas com curingas (`gpt-*`)- `modelFamilyFallback.ts` — fallback intrafamília T5
### Limitação de Taxa e Cota
- `rateLimitManager.ts` — bucket de tokens por chave+provedor- `usage.ts` — registro de uso- `quotaCache.ts` — snapshots de cota em memória
### Conta e Token
- `tokenRefresh.ts` — renovação OAuth em respostas 401- `accountFallback.ts` — alterna para uma conta alternativa- `sessionManager.ts` — estado de sessão com múltiplos turnos
### Inteligência
- `intentClassifier.ts` — classifica a intenção da solicitação- `taskAwareRouter.ts` — roteia por tipo de tarefa- `thinkingBudget.ts` — aloca tokens de raciocínio- `contextManager.ts` — injeta contexto de roteamento
### Resiliência
- `resilience.ts` — orquestração de repetição, backoff e circuit breaker- `emergencyFallback.ts` — fallback de último recurso- `modelDeprecation.ts` — roteamento automático para modelos sucessores
### Estado
- `signatureCache.ts` — desduplicação pela assinatura da solicitação- `volumeDetector.ts` — redução de carga- `contextHandoff.ts` — serialização da sessão
### Compressão
- `compression/` (subdiretório) — pipeline completo de compressão- 39 arquivos abrangendo mecanismos, pacotes de regras e adaptadores
### Habilidades
- (abordado em [SKILLS.md](./SKILLS.md))
### Memória
- (abordado em [MEMORY.md](./MEMORY.md))
---
## Executores (mais de 75 arquivos)
Um arquivo por provedor. Todos estendem `BaseExecutor` e sobrescrevem o que for diferente.
### Padrões Comuns
Os provedores são resolvidos por meio de `getExecutor(providerId)`, que retorna o executor configurado. Provedores compatíveis com OpenAI/Anthropic usam `DefaultExecutor` (`executors/default.ts`). O comportamento específico de cada provedor (URL base, cabeçalhos de autenticação, versão da API) é configurado em `open-sse/config/providers/`, enquanto as transformações do corpo da solicitação são processadas em `open-sse/translator/`.
A **URL personalizada** é definida por meio da configuração do provedor:
```ts// Configuração do provedor em open-sse/config/providers/export default { id: "together", baseURL: "https://api.together.xyz/v1/chat/completions",}A autenticação personalizada é processada por meio da configuração de autenticação do registro de provedores (chave de API, OAuth, perfis de cabeçalho).
As transformações personalizadas do corpo da solicitação (por exemplo, a separação de system de messages pela Anthropic) são registradas por provedor em open-sse/translator/.
### A Fábrica de Executores
`executors/index.ts` exporta `getExecutor(providerId)`:
```tsimport { getExecutor } from "@omniroute/open-sse/executors";
const executor = getExecutor("anthropic");const result = await executor.execute({ model: "claude-sonnet-4-5", messages: [...],});A resolução passa pelo ExecutorRegistry (executors/registry.ts): cada executor especializado é declarado na tabela integrada de executors/index.ts e registrado por meio de registerExecutor(alias, instance) no carregamento do módulo; getExecutor() consulta o registro e recorre a um DefaultExecutor memoizado para qualquer provedor sem uma entrada especializada. O mapeamento completo de alias → executor é caracterizado pelo teste golden tests/unit/executor-map-golden.test.ts.
Tradutores
Seção intitulada “Tradutores”Traduza entre 3 formatos: OpenAI, Anthropic, Gemini, além da nova Responses API.
Quando a tradução ocorre
Seção intitulada “Quando a tradução ocorre”import { needsTranslation, translateRequest } from "@omniroute/open-sse/translator";
if (needsTranslation(sourceFormat, targetFormat)) { body = translateRequest(body, sourceFormat, targetFormat);}Traduções comuns:
OpenAI → Anthropic: camposystemseparado, cabeçalhox-api-keyOpenAI → Gemini:contentsem vez demessages,systemInstructionOpenAI → Responses API: arrayinput, estadoprevious_response_id
Casos extremos tratados
Seção intitulada “Casos extremos tratados”- Função
developer→systempara provedores que não sejam OpenAI - Função
system→ mesclada à primeira mensagem do usuário para GLM/ERNIE json_schema→responseMimeType+responseSchemado Geminitools→ formato de ferramentas específico do provedor- Parâmetros de raciocínio (o1, Claude) → equivalentes específicos do provedor
Servidor MCP
Seção intitulada “Servidor MCP”open-sse/mcp-server/ implementa o servidor do Model Context Protocol:
- 110 ferramentas (gerenciamento de provedores, combinações, memória, cache, compactação, proxy, habilidades, gamificação, plugins, Notion, Obsidian, corpus local)
- 3 transportes: stdio, SSE, Streamable HTTP
- 33 escopos para autorização granular
Registro de ferramentas
Seção intitulada “Registro de ferramentas”As ferramentas são registradas como arquivos independentes em open-sse/mcp-server/tools/, cada um exportando um nome, esquema, manipulador e escopo:
import { z } from "zod";export default { name: "omniroute_get_health", description: "Get system health snapshot", scope: "read:health", inputSchema: z.object({}), handler: async (_args, ctx) => { return await getSystemHealth(); },};Transportes
Seção intitulada “Transportes”// stdio (uso via CLI)startMcpStdio(server);
// SSE (streaming baseado em HTTP)startMcpSse(server, port);
// Streamable HTTP (MCP moderno)startMcpStreamable(server, port);Autorização
Seção intitulada “Autorização”Toda chamada de ferramenta passa por verificações de escopo (open-sse/mcp-server/auth/):
if (!hasScope(apiKey, "providers:read")) { throw new Error("Insufficient scope");}Transformadores
Seção intitulada “Transformadores”open-sse/transformer/ converte entre os formatos Chat Completions e Responses API.
Por que um transformador separado?
Seção intitulada “Por que um transformador separado?”A Responses API é o novo formato da OpenAI com conversas com estado (previous_response_id). Quando um cliente envia uma solicitação de Responses, o OmniRoute:
- Converte Responses → Chat Completions internamente
- Envia ao provedor (qualquer provedor compatível com Chat Completions)
- Converte a resposta de volta ao formato Responses
- Transmite a resposta convertida ao cliente
O transformador (transformer/responsesTransformer.ts) fornece:
createResponsesApiTransformStream(): TransformStreamIsso processa:
- Eventos
response.output_item.added - Eventos
response.output_text.delta - Evento
response.completed - Mapeamento de chamadas de ferramentas (
function_call↔tool_calls)
Configuração
Seção intitulada “Configuração”open-sse/config/ contém a camada de configuração:
| Arquivo | Finalidade |
|---|---|
providerRegistry.ts |
Registro de modelos de chat no catálogo de 352 provedores |
providerModels.ts |
Aliases de modelos, mapeamento de formatos |
constants.ts |
Tempos limite, limites, códigos de status |
defaultThinkingSignature.ts |
Assinatura de raciocínio padrão do Claude |
modelStrip.ts (em services) |
Remoção de campos por provedor |
Esquema do registro de provedores
Seção intitulada “Esquema do registro de provedores”interface ProviderConfig { id: string; name: string; baseUrl: string; authType: "bearer" | "api-key" | "oauth" | "cookie"; executorClass: string; defaultModel: string; capabilities: ProviderCapabilities; models: ModelDefinition[];}A validação com Zod durante o carregamento do módulo garante que todas as configurações de provedores sejam válidas.
Restrições de Desempenho
Seção intitulada “Restrições de Desempenho”O mecanismo de roteamento tem orçamentos de desempenho rigorosos:
| Operação | Meta | Medição |
|---|---|---|
| Resolução de combos | <10ms | Para 50 destinos |
| Verificação de limite de taxa | <1ms | Token bucket em memória |
| Fallback de família de modelos | <5ms | Definições de famílias em cache |
| Despacho de roteamento de solicitações | <2ms | Caminho crítico |
| Nenhuma E/S bloqueante no caminho crítico de roteamento | — | Tudo assíncrono |
Antipadrões
Seção intitulada “Antipadrões”❌ Chamadas síncronas ao banco de dados em combo.ts — pré-calcule e armazene em cache
❌ Lógica de nova tentativa nos manipuladores — use retry() do serviço de resiliência
❌ Acesso direto à configuração do provedor — use os getters de providerRegistry
❌ Cadeias de fallback codificadas diretamente — defina-as em modelFamilyFallback.ts
❌ Mutações de estado entre solicitações simultâneas — use apenas contexto com escopo de solicitação
Adicionando um Novo Componente
Seção intitulada “Adicionando um Novo Componente”Adicionando um Novo Serviço
Seção intitulada “Adicionando um Novo Serviço”- Crie
open-sse/services/[serviceName].tscom uma responsabilidade específica - Exporte a função manipuladora principal e quaisquer constantes
- Adicione testes unitários em
tests/unit/services/[serviceName].test.mjs - Integre ao pipeline de solicitações em
handlers/chatCore.ts(se relacionado ao roteamento) - Atualize a lógica de roteamento em
combo.tsse o serviço afetar a seleção de destinos - Documente neste arquivo
Adicionando um Novo Executor
Seção intitulada “Adicionando um Novo Executor”- Crie
open-sse/executors/[provider].tsestendendoBaseExecutor - Registre em
config/providerRegistry.ts - Adicione à fábrica em
executors/index.ts - Adicione testes unitários para o executor
- Documente em
docs/architecture/ARCHITECTURE.md
Adicionando uma Nova Ferramenta MCP
Seção intitulada “Adicionando uma Nova Ferramenta MCP”- Crie ou atualize
open-sse/mcp-server/tools/[category]Tools.ts - Defina o esquema Zod para as entradas
- Registre a ferramenta em
mcp-server/index.ts - Adicione à matriz de escopos em
mcp-server/auth/ - Adicione testes unitários
Veja Também
Seção intitulada “Veja Também”- ARCHITECTURE.md — arquitetura de alto nível
- CODEBASE_DOCUMENTATION.md — referência de engenharia
- REPOSITORY_MAP.md — detalhamento diretório por diretório
- AUTO-COMBO.md — pontuação de 16 fatores
- MCP-SERVER.md — servidor MCP
- A2A-SERVER.md — servidor A2A
- Código-fonte:
open-sse/(mais de 400 arquivos, ~143 mil linhas de código)
HagiCode
HagiCode é um ambiente de programação com agentes, fluxos estruturados, execução multiagente e visualizações Hero Dungeon.
Transforme ideias em software útil com um fluxo de trabalho com agentes mais inteligente, rápido e agradável.

- SmartFluxos estruturados transformam intenções em um caminho executável da ideia à entrega.
- EfficientFluxos multiagente mantêm pesquisa, implementação e revisão em andamento simultaneamente.
- FunO Hero Dungeon torna longas sessões de programação mais visuais e colaborativas.