open-sse Architecture (Español)
¿Por qué un paquete de workspace independiente?
Sección titulada «¿Por qué un paquete de workspace independiente?»open-sse/ es un workspace independiente dentro del monorepo de OmniRoute por varias razones:
- Reutilización —
open-ssese publica como@omniroute/open-sseen npm, por lo que otros proyectos pueden usarlo de forma independiente - Límites claros — el motor de streaming está desacoplado de la capa de interfaz de usuario/base de datos específica de OmniRoute
- Rendimiento — el motor no tiene dependencias de Next.js, lo que permite arranques en frío más rápidos en contextos de CLI/serverless
- Versionado —
open-ssepuede publicar versiones siguiendo su propio calendario
"workspaces": ["open-sse"]Estructura de nivel superior
Sección titulada «Estructura de nivel superior»open-sse/├── index.ts # Punto de entrada público├── types.d.ts # Exportaciones públicas de tipos├── package.json # @omniroute/open-sse├── config/ # Configuraciones de proveedores, constantes y registros├── executors/ # Ejecutores HTTP por proveedor (67 + base.ts/index.ts)├── handlers/ # Controladores de solicitudes (chatCore, responses, etc.)├── lib/ # Utilidades internas├── mcp-server/ # Servidor Model Context Protocol├── services/ # ~298 módulos de servicio├── transformer/ # Transformador de formato de Responses API├── translator/ # Traducción de formatos (OpenAI ↔ Claude ↔ Gemini)└── utils/ # Utilidades compartidas (registros, errores, streaming, etc.)Cantidad de módulos
Sección titulada «Cantidad de módulos»| Directorio | Archivos | Propósito |
| executors/ | 167 | Ejecutores HTTP por proveedor (unificados mediante la factoría DefaultExecutor) |
| handlers/ | 157 | Puntos de entrada de solicitudes (chatCore, responses, embeddings) |
| services/ | ~536 | Enrutamiento, almacenamiento en caché, limitación de frecuencia, actualización, etc. |
| translator/ | 56 | Conversión de formatos (OpenAI ↔ Claude ↔ Gemini) |
| mcp-server/ | 44 | Herramientas y transportes MCP |
| utils/ | ~108 | Utilidades transversales (registros, errores, streaming) |
| config/ | ~339 | Configuraciones de proveedores, constantes y registros |
El flujo de solicitudes
Sección titulada «El flujo de solicitudes»Cada solicitud a un LLM atraviesa un flujo de 5 etapas:
┌──────────────┐ Solicitud HTTP │ 1. ENRUTAR │ resolución de combos, selección de modelo (ruta de Next.js) └──────┬───────┘ │ ▼ ┌──────────────┐ │ 2. TRADUCIR │ conversión de formato (OpenAI ↔ Claude ↔ Gemini) └──────┬───────┘ │ ▼ ┌──────────────┐ │ 3. EJECUTAR │ ejecutor del proveedor, HTTP, reintentos, disyuntor └──────┬───────┘ │ ▼ ┌──────────────┐ │ 4. STREAMING│ transformación SSE, contrapresión └──────┬───────┘ │ ▼ ┌──────────────┐ │ 5. REGISTRAR│ seguimiento de uso, registro de llamadas, clasificación de errores └──────┬───────┘ │ ▼ Respuesta HTTP (SSE o JSON)Etapa 1: Enrutamiento (services/combo.ts)
Sección titulada «Etapa 1: Enrutamiento (services/combo.ts)»Punto de entrada: handleComboChat() en services/combo.ts
Resuelve la solicitud en una tupla concreta (provider, model, account, credentials):
- Busca el combo por ID (o crea un combo virtual para los modelos
auto/*) - Aplica la estrategia de enrutamiento (prioridad, ponderada, round-robin, etc.)
- Excluye los proveedores con problemas (disyuntor)
- Selecciona el siguiente destino viable
Para los modelos auto/*, esta etapa también:
- Ejecuta el algoritmo de puntuación de 16 factores (
services/autoCombo/) - Selecciona un par
provider+modelsegún el estado, el coste, la latencia, etc.
Etapa 2: Traducción (translator/)
Sección titulada «Etapa 2: Traducción (translator/)»Si el formato de origen (p. ej., OpenAI) difiere del formato de destino (p. ej., Claude), la solicitud se traduce:
- Prompt del sistema → mensaje del sistema
- Definiciones de herramientas → formato de herramientas específico del proveedor
- Parámetros de razonamiento/pensamiento → equivalentes específicos del proveedor
- Normalización del rol de los mensajes (
developer→systempara proveedores distintos de OpenAI)
translator/index.ts expone:
translateRequest(body, sourceFormat, targetFormat): TranslatedRequestneedsTranslation(source, target): booleanEtapa 3: Ejecución (executors/)
Sección titulada «Etapa 3: Ejecución (executors/)»Punto de entrada: getExecutor(providerId).execute(request, options)
Todos los proveedores utilizan DefaultExecutor (executors/default.ts) mediante el mecanismo de reserva de la factoría getExecutor(). El ejecutor:
- Construye la URL ascendente (
buildUrl()) - Añade encabezados específicos del proveedor (
buildHeaders()) - Transforma el cuerpo de la solicitud (
transformRequest()) - Envía la solicitud HTTP con reintentos y retroceso exponencial
- Gestiona la renovación de la autenticación cuando es necesario (proveedores OAuth)
Todos los ejecutores extienden BaseExecutor (executors/base.ts, 1170 líneas de código), que proporciona:
- Lógica común de reintentos
- Integración con proxy
- Integración con disyuntor
- Hooks de registro de uso
Etapa 4: Streaming (utils/stream.ts)
Sección titulada «Etapa 4: Streaming (utils/stream.ts)»Para las respuestas en streaming, el ejecutor devuelve un ReadableStream. El controlador:
- Pasa el flujo por una transformación SSE (
createSSETransformStreamWithLogger) - Aplica señales periódicas de actividad para detectar conexiones inactivas
- Gestiona correctamente la desconexión del cliente (
pipeWithDisconnect) - Transforma SSE → JSON para clientes sin streaming
Para las respuestas sin streaming, el ejecutor devuelve un objeto JSON analizado que se transmite sin cambios.
Etapa 5: Registro (services/usage.ts)
Sección titulada «Etapa 5: Registro (services/usage.ts)»Después de la respuesta (correcta o fallida), se registra el uso:
prompt_tokens,completion_tokens,cached_tokensde la respuestacost_usdcalculado a partir de los datos de precioslatency_ms,status,error_classsi se produce un error- Se conserva en la tabla
usage_history
Los artefactos del registro de llamadas (si están habilitados) se escriben en ${DATA_DIR}/call_logs/.
Análisis detallado de archivos clave
Sección titulada «Análisis detallado de archivos clave»chatCore.ts (5977 líneas)
Sección titulada «chatCore.ts (5977 líneas)»El controlador principal de solicitudes. A pesar de su tamaño, tiene una estructura clara:
// Pseudoestructura de chatCore.tsexport async function handleChat(request: NextRequest) { // 1. Autenticación + CORS await authenticateRequest(request); applyCorsHeaders(response);
// 2. Validación del cuerpo const body = await parseRequestBody(request);
// 3. Detección de formato + traducción const sourceFormat = detectFormat(request); const targetFormat = getTargetFormat(providerId); if (needsTranslation(sourceFormat, targetFormat)) { body = translateRequest(body, sourceFormat, targetFormat); }
// 4. Enrutamiento de combos 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 con el siguiente destino } }
// 5. Alternativa de emergencia return await emergencyFallback(body);}A pesar de ser una única función gigantesca, está organizada en secciones comentadas que corresponden al proceso de 5 etapas.
combo.ts (4456 LOC)
Sección titulada «combo.ts (4456 LOC)»El motor de enrutamiento que resuelve un combo en 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");}Admite 19 estrategias de enrutamiento (consulta src/shared/constants/routingStrategies.ts):
| Estrategia | Comportamiento |
|---|---|
priority |
Lista ordenada con prioridad para el primer destino |
weighted |
Selección probabilística según el peso de cada destino |
round-robin |
Recorre los destinos en orden de forma cíclica |
context-relay |
Transfiere el contexto entre destinos |
fill-first |
Agota la cuota antes de pasar al siguiente |
p2c |
Potencia de dos opciones |
random |
Selección aleatoria uniforme |
least-used |
Elige el destino con menos usos recientes |
cost-optimized |
Primero, el destino operativo más barato |
reset-aware |
Tiene en cuenta los períodos de restablecimiento del proveedor |
reset-window |
Enrutamiento basado en el período de restablecimiento |
headroom |
Primero, el destino con mayor margen de cuota restante |
strict-random |
Selección verdaderamente uniforme (sin ponderación por calidad) |
auto |
Usa una puntuación de 16 factores (autoCombo/) |
lkgp |
Primero, el último proveedor conocido que funcionaba |
context-optimized |
El más adecuado para solicitudes con contexto largo |
fusion |
Distribuye en paralelo a un panel y luego sintetiza mediante un juez (fusion.ts) |
base.ts (1170 LOC)
Sección titulada «base.ts (1170 LOC)»El ejecutor abstracto que extienden los 107 ejecutores. Contiene:
buildUrl()— construcción predeterminada de la URL (las subclases la sobrescriben para personalizarla)buildHeaders()— encabezados predeterminados (autenticación, tipo de contenido)transformRequest()— transferencia directa de forma predeterminadaexecute()— el bucle HTTP principal con reintentos, espera exponencial y disyuntor
export class DefaultExecutor extends BaseExecutor { // Gestiona todos los proveedores compatibles con OpenAI/Anthropic // Los proveedores registran configuraciones (URL, autenticación, encabezados), pero comparten la lógica del ejecutor}El comportamiento específico de cada proveedor (encabezados de autenticación, URL base y encabezados de versión) se configura mediante el registro de proveedores, no mediante clases de ejecutor separadas.
---
## Servicios (117 módulos)
Los servicios son **módulos específicos y de propósito único** que los manejadores combinan. Las principales categorías son:
### Enrutamiento y combinación
- `combo.ts` — punto de entrada para solicitudes con enrutamiento combinado- `services/autoCombo/` — puntuación de 16 factores, 8 estrategias de enrutamiento automático- `wildcardRouter.ts` — busca coincidencias con rutas comodín (`gpt-*`)- `modelFamilyFallback.ts` — respaldo intrafamilia T5
### Limitación de tasa y cuota
- `rateLimitManager.ts` — depósito de tokens por clave+proveedor- `usage.ts` — registro de uso- `quotaCache.ts` — instantáneas de cuota en memoria
### Cuenta y token
- `tokenRefresh.ts` — renovación de OAuth al recibir 401- `accountFallback.ts` — cambio a una cuenta alternativa- `sessionManager.ts` — estado de sesión multiconversación
### Inteligencia
- `intentClassifier.ts` — clasifica la intención de la solicitud- `taskAwareRouter.ts` — enruta según el tipo de tarea- `thinkingBudget.ts` — asigna tokens de razonamiento- `contextManager.ts` — inyecta contexto de enrutamiento
### Resiliencia
- `resilience.ts` — orquestación de reintentos, espera incremental y disyuntores- `emergencyFallback.ts` — respaldo de último recurso- `modelDeprecation.ts` — enrutamiento automático a modelos sucesores
### Estado
- `signatureCache.ts` — deduplicación por firma de solicitud- `volumeDetector.ts` — reducción de carga- `contextHandoff.ts` — serialización de sesiones
### Compresión
- `compression/` (subdirectorio) — canalización completa de compresión- 39 archivos que abarcan motores, paquetes de reglas y adaptadores
### Habilidades
- (descrito en [SKILLS.md](./SKILLS.md))
### Memoria
- (descrito en [MEMORY.md](./MEMORY.md))
---
## Ejecutores (más de 75 archivos)
Un archivo por proveedor. Todos extienden `BaseExecutor` y sobrescriben lo que difiere.
### Patrones comunes
Los proveedores se resuelven mediante `getExecutor(providerId)`, que devuelve el ejecutor configurado. Los proveedores compatibles con OpenAI/Anthropic utilizan `DefaultExecutor` (`executors/default.ts`). El comportamiento específico de cada proveedor (URL base, cabeceras de autenticación, versión de la API) se configura en `open-sse/config/providers/`, mientras que las transformaciones del cuerpo de la solicitud se gestionan en `open-sse/translator/`.
La **URL personalizada** se establece mediante la configuración del proveedor:
```ts// Configuración del proveedor en open-sse/config/providers/export default { id: "together", baseURL: "https://api.together.xyz/v1/chat/completions",}La autenticación personalizada se gestiona mediante la configuración de autenticación del registro de proveedores (clave de API, OAuth, perfiles de cabeceras).
Las transformaciones personalizadas del cuerpo de la solicitud (por ejemplo, cuando Anthropic separa system de messages) se registran por proveedor en open-sse/translator/.
### La fábrica de ejecutores
`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: [...],});La resolución se realiza mediante ExecutorRegistry (executors/registry.ts): cada ejecutor especializado se declara en la tabla integrada de executors/index.ts y se registra mediante registerExecutor(alias, instance) al cargar el módulo; getExecutor() consulta el registro y recurre a un DefaultExecutor memoizado para cualquier proveedor sin una entrada especializada. La asignación completa de alias → ejecutor se especifica mediante la prueba de referencia tests/unit/executor-map-golden.test.ts.
Traductores
Sección titulada «Traductores»Traduce entre 3 formatos: OpenAI, Anthropic y Gemini, además de la nueva Responses API.
Cuándo se realiza la traducción
Sección titulada «Cuándo se realiza la traducción»import { needsTranslation, translateRequest } from "@omniroute/open-sse/translator";
if (needsTranslation(sourceFormat, targetFormat)) { body = translateRequest(body, sourceFormat, targetFormat);}Traducciones habituales:
OpenAI → Anthropic: camposystemindependiente, encabezadox-api-keyOpenAI → Gemini:contentsen lugar demessages,systemInstructionOpenAI → Responses API: matrizinput, estadoprevious_response_id
Casos especiales contemplados
Sección titulada «Casos especiales contemplados»- Rol
developer→systempara proveedores distintos de OpenAI - Rol
system→ combinado con el primer mensaje del usuario para GLM/ERNIE json_schema→responseMimeType+responseSchemade Geminitools→ formato de herramientas específico del proveedor- Parámetros de razonamiento (o1, Claude) → equivalentes específicos del proveedor
Servidor MCP
Sección titulada «Servidor MCP»open-sse/mcp-server/ implementa el servidor del Model Context Protocol:
- 110 herramientas (gestión de proveedores, combinaciones, memoria, caché, compresión, proxy, habilidades, gamificación, complementos, Notion, Obsidian y corpus local)
- 3 transportes: stdio, SSE y Streamable HTTP
- 33 ámbitos para una autorización detallada
Registro de herramientas
Sección titulada «Registro de herramientas»Las herramientas se registran como archivos independientes en open-sse/mcp-server/tools/; cada uno exporta un nombre, un esquema, un controlador y un ámbito:
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
Sección titulada «Transportes»// stdio (uso de la CLI)startMcpStdio(server);
// SSE (transmisión basada en HTTP)startMcpSse(server, port);
// Streamable HTTP (MCP moderno)startMcpStreamable(server, port);Autorización
Sección titulada «Autorización»Cada llamada a una herramienta pasa por comprobaciones de ámbito (open-sse/mcp-server/auth/):
if (!hasScope(apiKey, "providers:read")) { throw new Error("Insufficient scope");}Transformadores
Sección titulada «Transformadores»open-sse/transformer/ convierte entre los formatos Chat Completions y Responses API.
¿Por qué un transformador independiente?
Sección titulada «¿Por qué un transformador independiente?»Responses API es el nuevo formato de OpenAI con conversaciones con estado (previous_response_id). Cuando un cliente envía una solicitud de Responses, OmniRoute:
- Convierte internamente Responses → Chat Completions
- La envía al proveedor (cualquier proveedor compatible con Chat Completions)
- Vuelve a convertir la respuesta al formato de Responses
- Transmite la respuesta convertida al cliente
El transformador (transformer/responsesTransformer.ts) proporciona:
createResponsesApiTransformStream(): TransformStreamEsto gestiona:
- Eventos
response.output_item.added - Eventos
response.output_text.delta - Evento
response.completed - Asignación de llamadas a herramientas (
function_call↔tool_calls)
Configuración
Sección titulada «Configuración»open-sse/config/ contiene la capa de configuración:
| Archivo | Propósito |
|---|---|
providerRegistry.ts |
Registro de modelos de chat basado en el catálogo de 352 proveedores |
providerModels.ts |
Alias de modelos, asignación de formatos |
constants.ts |
Tiempos de espera, límites y códigos de estado |
defaultThinkingSignature.ts |
Firma de razonamiento predeterminada de Claude |
modelStrip.ts (en services) |
Eliminación de campos específica para cada proveedor |
Esquema del registro de proveedores
Sección titulada «Esquema del registro de proveedores»interface ProviderConfig { id: string; name: string; baseUrl: string; authType: "bearer" | "api-key" | "oauth" | "cookie"; executorClass: string; defaultModel: string; capabilities: ProviderCapabilities; models: ModelDefinition[];}La validación de Zod durante la carga del módulo garantiza que todas las configuraciones de proveedores sean válidas.
Restricciones de rendimiento
Sección titulada «Restricciones de rendimiento»El motor de enrutamiento tiene presupuestos de rendimiento estrictos:
| Operación | Objetivo | Medición |
|---|---|---|
| Resolución de combinaciones | <10ms | Para 50 destinos |
| Comprobación del límite de solicitudes | <1ms | Token bucket en memoria |
| Fallback de familia de modelos | <5ms | Definiciones de familia en caché |
| Despacho de enrutamiento de solicitudes | <2ms | Ruta crítica |
| Sin E/S bloqueante en la ruta crítica de enrutamiento | — | Todo asíncrono |
Antipatrones
Sección titulada «Antipatrones»❌ Llamadas síncronas a la BD en combo.ts — precalcular y almacenar en caché
❌ Lógica de reintentos en los manejadores — usar retry() del servicio de resiliencia
❌ Acceso directo a la configuración del proveedor — usar los getters de providerRegistry
❌ Cadenas de fallback codificadas de forma rígida — definirlas en modelFamilyFallback.ts
❌ Mutaciones de estado entre solicitudes simultáneas — usar únicamente contexto con ámbito de solicitud
Añadir un componente nuevo
Sección titulada «Añadir un componente nuevo»Añadir un servicio nuevo
Sección titulada «Añadir un servicio nuevo»- Crear
open-sse/services/[serviceName].tscon una responsabilidad específica - Exportar la función principal del manejador y cualquier constante
- Añadir pruebas unitarias en
tests/unit/services/[serviceName].test.mjs - Integrarlo en el flujo de procesamiento de solicitudes en
handlers/chatCore.ts(si está relacionado con el enrutamiento) - Actualizar la lógica de enrutamiento en
combo.tssi el servicio afecta a la selección de destinos - Documentarlo en este archivo
Añadir un ejecutor nuevo
Sección titulada «Añadir un ejecutor nuevo»- Crear
open-sse/executors/[provider].tsque extiendaBaseExecutor - Registrarlo en
config/providerRegistry.ts - Añadirlo a la factoría de
executors/index.ts - Añadir pruebas unitarias para el ejecutor
- Documentarlo en
docs/architecture/ARCHITECTURE.md
Añadir una herramienta MCP nueva
Sección titulada «Añadir una herramienta MCP nueva»- Crear o actualizar
open-sse/mcp-server/tools/[category]Tools.ts - Definir el esquema Zod para las entradas
- Registrar la herramienta en
mcp-server/index.ts - Añadirla a la matriz de ámbitos en
mcp-server/auth/ - Añadir pruebas unitarias
Véase también
Sección titulada «Véase también»- ARCHITECTURE.md — arquitectura de alto nivel
- CODEBASE_DOCUMENTATION.md — referencia de ingeniería
- REPOSITORY_MAP.md — descripción directorio por directorio
- AUTO-COMBO.md — puntuación de 16 factores
- MCP-SERVER.md — servidor MCP
- A2A-SERVER.md — servidor A2A
- Fuente:
open-sse/(más de 400 archivos, ~143K líneas de código)
HagiCode
HagiCode es un espacio de trabajo de programación con agentes, flujos estructurados, ejecución multiagente y vistas de Hero Dungeon.
Convierte ideas en software útil con un flujo de trabajo con agentes más inteligente, rápido y ameno.

- SmartLos flujos estructurados convierten la intención en un itinerario ejecutable desde la idea hasta la entrega.
- EfficientLos flujos multiagente permiten avanzar en paralelo con la investigación, implementación y revisión.
- FunHero Dungeon hace que las largas sesiones de programación sean visuales y colaborativas.