Pular para o conteúdo
OmniRoute source

open-sse Architecture (Português (Brasil))

open-sse/ é um workspace independente no monorepo do OmniRoute por vários motivos:

  1. Reutilização — open-sse é publicado como @omniroute/open-sse no npm, permitindo que outros projetos o utilizem de forma independente
  2. Limites bem definidos — o mecanismo de streaming é desacoplado da camada de interface/BD específica do OmniRoute
  3. Desempenho — o mecanismo não possui dependências do Next.js, permitindo inicializações a frio mais rápidas em contextos de CLI/serverless
  4. Versionamento — open-sse pode lançar versões em seu próprio ritmo
package.json
"workspaces": ["open-sse"]

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

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


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)

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+model com base em integridade, custo, latência etc.

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 → system para provedores diferentes da OpenAI)

O translator/index.ts expõe:

translateRequest(body, sourceFormat, targetFormat): TranslatedRequest
needsTranslation(source, target): boolean

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

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.

Após a resposta (bem-sucedida ou com falha), o uso é registrado:

  • prompt_tokens, completion_tokens, cached_tokens da resposta
  • cost_usd calculado com base nos dados de preços
  • latency_ms, status, error_class em caso de falha
  • Persistido na tabela usage_history

Os artefatos de log das chamadas (se habilitados) são gravados em ${DATA_DIR}/call_logs/.


O principal manipulador de requisições. Apesar de seu tamanho, ele tem uma estrutura clara:

// Pseudoestrutura de chatCore.ts
export 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.

O mecanismo de roteamento que resolve um combo em destinos ordenados.

services/combo.ts
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)

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ão
  • execute() — o loop HTTP principal com nova tentativa/recuo/disjuntor
open-sse/executors/default.ts
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)`:
```ts
import { 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.


Traduza entre 3 formatos: OpenAI, Anthropic, Gemini, além da nova Responses API.

import { needsTranslation, translateRequest } from "@omniroute/open-sse/translator";
if (needsTranslation(sourceFormat, targetFormat)) {
body = translateRequest(body, sourceFormat, targetFormat);
}

Traduções comuns:

  • OpenAI → Anthropic: campo system separado, cabeçalho x-api-key
  • OpenAI → Gemini: contents em vez de messages, systemInstruction
  • OpenAI → Responses API: array input, estado previous_response_id
  • Função developer → system para provedores que não sejam OpenAI
  • Função system → mesclada à primeira mensagem do usuário para GLM/ERNIE
  • json_schema → responseMimeType + responseSchema do Gemini
  • tools → formato de ferramentas específico do provedor
  • Parâmetros de raciocínio (o1, Claude) → equivalentes específicos do provedor

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

As ferramentas são registradas como arquivos independentes em open-sse/mcp-server/tools/, cada um exportando um nome, esquema, manipulador e escopo:

open-sse/mcp-server/tools/getHealth.ts
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();
},
};
// stdio (uso via CLI)
startMcpStdio(server);
// SSE (streaming baseado em HTTP)
startMcpSse(server, port);
// Streamable HTTP (MCP moderno)
startMcpStreamable(server, port);

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");
}

open-sse/transformer/ converte entre os formatos Chat Completions e Responses API.

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:

  1. Converte Responses → Chat Completions internamente
  2. Envia ao provedor (qualquer provedor compatível com Chat Completions)
  3. Converte a resposta de volta ao formato Responses
  4. Transmite a resposta convertida ao cliente

O transformador (transformer/responsesTransformer.ts) fornece:

createResponsesApiTransformStream(): TransformStream

Isso processa:

  • Eventos response.output_item.added
  • Eventos response.output_text.delta
  • Evento response.completed
  • Mapeamento de chamadas de ferramentas (function_call ↔ tool_calls)

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


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

❌ 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


  1. Crie open-sse/services/[serviceName].ts com uma responsabilidade específica
  2. Exporte a função manipuladora principal e quaisquer constantes
  3. Adicione testes unitários em tests/unit/services/[serviceName].test.mjs
  4. Integre ao pipeline de solicitações em handlers/chatCore.ts (se relacionado ao roteamento)
  5. Atualize a lógica de roteamento em combo.ts se o serviço afetar a seleção de destinos
  6. Documente neste arquivo
  1. Crie open-sse/executors/[provider].ts estendendo BaseExecutor
  2. Registre em config/providerRegistry.ts
  3. Adicione à fábrica em executors/index.ts
  4. Adicione testes unitários para o executor
  5. Documente em docs/architecture/ARCHITECTURE.md
  1. Crie ou atualize open-sse/mcp-server/tools/[category]Tools.ts
  2. Defina o esquema Zod para as entradas
  3. Registre a ferramenta em mcp-server/index.ts
  4. Adicione à matriz de escopos em mcp-server/auth/
  5. Adicione testes unitários


Código-fonte do OmniRoute (a58000c7685f)

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.

Interface principal do HagiCode no tema claro
  • 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.
Acessar HagiCode