Precios

Arquitectura general

Archivos fuente

Esta página se genera a partir de los siguientes archivos fuente:

Visión General de la Arquitectura del Sistema

Sim Studio AI es una plataforma de automatización de flujos de trabajo basada en una arquitectura modular construida sobre Next.js con App Router. El sistema implementa un motor de ejecución DAG (Directed Acyclic Graph) que permite orquestar bloques, conectores, proveedores de LLM y triggers de manera extensible y tipada.

La arquitectura sigue principios fundamentales de diseño: responsabilidad única para cada componente, composición sobre complejidad, seguridad de tipos con TypeScript en todas las capas, y estado predecible mediante Zustand para estado global y useState para concerns de UI (.claude/rules/sim-architecture.md:9-13).

正在加载图表渲染器...

Puntos clave del diagrama de arquitectura:

  1. Separación en capas: El sistema divide claramente la capa cliente (UI/estado), el núcleo de ejecución (lógica de workflows), las integraciones externas y la infraestructura de soporte.

  2. Flujo de dependencias: Las dependencias fluyen desde la capa cliente hacia el núcleo, y desde el núcleo hacia las integraciones e infraestructura, nunca en sentido inverso.

  3. Registros centralizados: Blocks, Connectors, Providers y Triggers mantienen registros centralizados que permiten extensibilidad sin modificar el núcleo (apps/sim/blocks/registry.ts:207, apps/sim/connectors/registry.ts:33).

  4. Estado distribuido: TanStack Query gestiona cache del servidor mientras Zustand maneja estado de UI, evitando duplicación y sincronización manual (apps/sim/hooks/queries/general-settings.ts:67-76).


Módulos Core del Sistema

Motor de Ejecución (Executor)

El motor de ejecución es el componente central que procesa los flujos de trabajo. Implementa un ejecutor DAG que coordina la ejecución de bloques en orden topológico, manejando dependencias entre nodos y propagando resultados entre ellos.

Responsabilidades delimitadas:

  • Ejecutar workflows representados como grafos dirigidos acíclicos
  • Coordinar la ejecución paralela de bloques independientes
  • Manejar el ciclo de vida de ejecución (inicio, progreso, error, completado)
  • Gestionar el contexto de ejecución y variables de flujo

API de entrada:

typescript
1// Punto de entrada principal - exporta DAGExecutor como Executor
2export { DAGExecutor as Executor } from '@/executor/execution/executor'

(apps/sim/executor/index.ts:6)

Estructura de datos clave: El executor recibe configuraciones de bloques desde el registry, donde cada bloque define su tipo, herramientas asociadas, parámetros y manejadores:

typescript
1// Estructura del registry de bloques
2export const registry: Record<string, BlockConfig> = {
3  agent: AgentBlock,
4  api: ApiBlock,
5  condition: ConditionBlock,
6  // ... más de 150 bloques
7}

(apps/sim/blocks/registry.ts:207-430)

Cadena de llamada crítica:

  1. El usuario dispara un workflow desde UI o trigger externo
  2. El executor recibe el estado del workflow y valida el DAG
  3. Para cada bloque, obtiene su configuración via getBlock(type) (apps/sim/blocks/registry.ts:432-438)
  4. Ejecuta el bloque con el contexto actual y acumula resultados
  5. Propaga outputs a bloques dependientes según las conexiones del grafo

Manejo de errores: El sistema de herramientas implementa detección de rate limits y errores de cuota, incluyendo casos especiales donde proveedores retornan 401/403 en lugar de 429:

typescript
1function isRateLimitError(error: unknown): boolean {
2  if (error && typeof error === 'object') {
3    const status = (error as { status?: number }).status
4    if (status === 429 || status === 503) return true
5    if (status === 401 || status === 403) {
6      const message = ((error as { message?: string }).message || '').toLowerCase()
7      if (message.includes('quota') || message.includes('rate limit')) {
8        return true
9      }
10    }
11  }
12  return false
13}

(apps/sim/tools/index.ts:145-157)


Sistema de Bloques (Block Registry)

El sistema de bloques define las unidades de procesamiento atómicas que componen los workflows. Cada bloque encapsula una funcionalidad específica: integración con servicio externo, operación de datos, lógica condicional, o generación de contenido con IA.

Responsabilidades delimitadas:

  • Registrar y exponer todas las configuraciones de bloques disponibles
  • Resolver bloques por tipo, incluyendo versionado (v2, v3)
  • Categorizar bloques por función (blocks, tools, triggers)
  • Validar tipos de bloque en tiempo de ejecución

API de entrada principales:

typescript
1// Obtener bloque por tipo con normalización de guiones
2export const getBlock = (type: string): BlockConfig | undefined => {
3  if (registry[type]) return registry[type]
4  const normalized = type.replace(/-/g, '_')
5  return registry[normalized]
6}
7
8// Obtener última versión de un bloque versionado
9export const getLatestBlock = (baseType: string): BlockConfig | undefined => {
10  const normalized = baseType.replace(/-/g, '_')
11  const versionedKeys = Object.keys(registry).filter((key) => {
12    const match = key.match(new RegExp(`^${normalized}_v(\\d+)$`))
13    return match !== null
14  })
15  // Ordena por versión y retorna la más reciente
16  if (versionedKeys.length > 0) {
17    const sorted = versionedKeys.sort((a, b) => {
18      const versionA = Number.parseInt(a.match(/_v(\d+)$/)?.[1] || '0', 10)
19      const versionB = Number.parseInt(b.match(/_v(\d+)$/)?.[1] || '0', 10)
20      return versionB - versionA
21    })
22    return registry[sorted[0]]
23  }
24  return registry[normalized]
25}

(apps/sim/blocks/registry.ts:432-458)

Estructura de datos del BlockConfig: Cada bloque exporta una configuración que incluye:

  • category: 'blocks' | 'tools' | 'triggers'
  • tools: definición de herramientas accesibles
  • inputs: esquema de parámetros de entrada
  • outputs: esquema de resultados
  • handler: función de ejecución

Ejemplos de bloques registrados: El registry incluye más de 150 bloques, desde integraciones (Slack, Salesforce, HubSpot) hasta utilidades (Condition, Router, Response):

typescript
1export const registry: Record<string, BlockConfig> = {
2  slack: SlackBlock,
3  salesforce: SalesforceBlock,
4  hubspot: HubSpotBlock,
5  condition: ConditionBlock,
6  router: RouterBlock,
7  response: ResponseBlock,
8  // ... 150+ bloques más
9}

(apps/sim/blocks/registry.ts:207-430)

Sistema de versionado: Los bloques pueden tener múltiples versiones coexistiendo (e.g., mistral_parse, mistral_parse_v2, mistral_parse_v3), permitiendo migración gradual y backward compatibility (apps/sim/blocks/registry.ts:113-117).


Sistema de Herramientas (Tools)

El sistema de herramientas gestiona la ejecución de operaciones específicas dentro de los bloques, incluyendo manejo de API keys alojadas, rate limiting, y procesamiento de outputs.

Responsabilidades delimitadas:

  • Inyectar API keys alojadas de forma segura
  • Detectar y manejar rate limits y errores de cuota
  • Procesar outputs de archivos antes de retornar al usuario
  • Filtrar campos internos de las respuestas

Manejo de API Keys alojadas: Cuando un usuario no configura su propia API key, el sistema puede usar keys alojadas con rate limiting:

typescript
1// Inyección de API key alojada
2if (!acquireResult.success) {
3  logger.error(`[${requestId}] No hosted keys configured for ${tool.id}: ${acquireResult.error}`)
4  const error = new Error(acquireResult.error || `No hosted keys configured for ${tool.id}`)
5  ;(error as any).status = 503
6  throw error
7}
8params[apiKeyParam] = acquireResult.key

(apps/sim/tools/index.ts:120-128)

Procesamiento de outputs de archivos: El sistema detecta y procesa outputs que contienen archivos antes de retornar resultados:

typescript
1async function processFileOutputs(
2  result: ToolResponse,
3  tool: ToolConfig,
4  executionContext?: ExecutionContext
5): Promise<ToolResponse> {
6  if (!executionContext || !result.success) return result
7  if (typeof window !== 'undefined') return result
8  
9  const { FileToolProcessor } = await import('@/executor/utils/file-tool-processor')
10  if (!FileToolProcessor.hasFileOutputs(tool)) return result
11  
12  const processedOutput = await FileToolProcessor.processToolOutputs(
13    result.output, tool, executionContext
14  )
15  return { ...result, output: processedOutput }
16}

(apps/sim/tools/index.ts:569-602)

Filtrado de campos internos: Los campos con prefijo __ son reservados para datos transitorios y se eliminan antes de retornar:

typescript
1function stripInternalFields(output: Record<string, unknown>): Record<string, unknown> {
2  if (typeof output !== 'object' || output === null || Array.isArray(output)) {
3    return output
4  }
5  const result: Record<string, unknown> = {}
6  for (const [key, value] of Object.entries(output)) {
7    if (!key.startsWith('__')) {
8      result[key] = value
9    }
10  }
11  return result
12}

(apps/sim/tools/index.ts:365-376)

Parámetros del sistema MCP: El sistema filtra parámetros internos antes de extraer argumentos de herramientas:

typescript
1const MCP_SYSTEM_PARAMETERS = new Set([
2  'serverId', 'serverUrl', 'toolName', 'serverName',
3  '_context', 'envVars', 'workflowVariables',
4  'blockData', 'blockNameMapping', '_toolSchema',
5])

(apps/sim/tools/index.ts:537-548)


Sistema de Proveedores (Provider Registry)

El sistema de proveedores gestiona las integraciones con diferentes servicios de LLM (OpenAI, Anthropic, etc.), proporcionando una interfaz unificada para la ejecución de modelos.

Responsabilidades delimitadas:

  • Registrar configuraciones de proveedores LLM
  • Inicializar proveedores al arrancar la aplicación
  • Resolver proveedores por ID para ejecución

API de entrada:

typescript
1export async function getProviderExecutor(
2  providerId: ProviderId
3): Promise<ProviderConfig | undefined> {
4  const provider = providerRegistry[providerId]
5  if (!provider) {
6    logger.error(`Provider not found: ${providerId}`)
7    return undefined
8  }
9  return provider
10}

(apps/sim/providers/registry.ts:39-48)

Inicialización de proveedores: El sistema inicializa todos los proveedores registrados al arrancar:

typescript
1export async function initializeProviders(): Promise&lt;void&gt; {
2  for (const [id, provider] of Object.entries(providerRegistry)) {
3    if (provider.initialize) {
4      try {
5        await provider.initialize()
6        logger.info(`Initialized provider: ${id}`)
7      } catch (error) {
8        logger.error(`Failed to initialize ${id} provider`, {
9          error: error instanceof Error ? error.message : 'Unknown error',
10        })
11      }
12    }
13  }
14}

(apps/sim/providers/registry.ts:50-63)


Sistema de Triggers (Trigger Registry)

El sistema de triggers maneja los eventos que inician la ejecución de workflows, incluyendo webhooks de servicios externos y polling programado.

Responsabilidades delimitadas:

  • Registrar configuraciones de triggers disponibles
  • Manejar webhooks entrantes de servicios externos
  • Gestionar polling para servicios sin webhook
  • Mapear eventos específicos a triggers registrados

Estructura del registry: El registry agrupa triggers por servicio y tipo de evento:

typescript
1export const TRIGGER_REGISTRY: TriggerRegistry = {
2  slack_webhook: slackWebhookTrigger,
3  airtable_webhook: airtableWebhookTrigger,
4  attio_record_created: attioRecordCreatedTrigger,
5  attio_record_updated: attioRecordUpdatedTrigger,
6  calcom_booking_created: calcomBookingCreatedTrigger,
7  calendly_invitee_created: calendlyInviteeCreatedTrigger,
8  confluence_page_created: confluencePageCreatedTrigger,
9  // ... más triggers
10}

(apps/sim/triggers/registry.ts:176-229)

Granularidad de eventos: Los triggers pueden ser genéricos (webhook) o específicos para tipos de eventos:

typescript
1// Triggers específicos de Attio
2attio_record_created: attioRecordCreatedTrigger,
3attio_record_updated: attioRecordUpdatedTrigger,
4attio_record_deleted: attioRecordDeletedTrigger,
5attio_note_created: attioNoteCreatedTrigger,
6attio_task_created: attioTaskCreatedTrigger,
7// Triggers específicos de Confluence
8confluence_page_created: confluencePageCreatedTrigger,
9confluence_page_updated: confluencePageUpdatedTrigger,
10confluence_page_removed: confluencePageRemovedTrigger,

(apps/sim/triggers/registry.ts:186-227)


Sistema de Conectores (Connector Registry)

El sistema de conectores proporciona integraciones preconfiguradas con servicios externos, manejando autenticación OAuth y operaciones comunes.

Responsabilidades delimitadas:

  • Registrar conectores disponibles para servicios externos
  • Proporcionar configuración de autenticación OAuth
  • Estandarizar operaciones comunes entre servicios

Conectores registrados: El sistema incluye más de 30 conectores para servicios populares:

typescript
1export const CONNECTOR_REGISTRY: ConnectorRegistry = {
2  airtable: airtableConnector,
3  asana: asanaConnector,
4  confluence: confluenceConnector,
5  discord: discordConnector,
6  github: githubConnector,
7  gmail: gmailConnector,
8  google_calendar: googleCalendarConnector,
9  google_drive: googleDriveConnector,
10  hubspot: hubspotConnector,
11  jira: jiraConnector,
12  linear: linearConnector,
13  notion: notionConnector,
14  slack: slackConnector,
15  salesforce: salesforceConnector,
16  // ... más conectores
17}

(apps/sim/connectors/registry.ts:33-64)


Flujo de Datos y Cadena de Llamadas

El siguiente diagrama ilustra el flujo de datos end-to-end desde que un usuario dispara un workflow hasta la entrega de resultados:

正在加载图表渲染器...

Puntos clave del flujo de datos:

  1. Orquestación por DAGExecutor: El executor coordina toda la ejecución, respetando dependencias del grafo y ejecutando bloques en paralelo cuando es posible (apps/sim/executor/index.ts:6).

  2. Resolución dinámica de bloques: Cada bloque se resuelve via registry, permitiendo extensibilidad sin modificar el executor (apps/sim/blocks/registry.ts:432).

  3. Inyección de API keys: El sistema de herramientas puede inyectar keys alojadas cuando el usuario no configura las propias (apps/sim/tools/index.ts:120-128).

  4. Filtrado de respuesta: Los campos internos (__) se eliminan antes de retornar resultados al usuario (apps/sim/tools/index.ts:365-376).

  5. Cache con TanStack Query: Los hooks de TanStack Query cachean respuestas con staleTime de 60 minutos, reduciendo llamadas innecesarias (apps/sim/hooks/queries/general-settings.ts:75).


Decisiones de Diseño y Trade-offs

1. Registry Pattern para Extensibilidad

Decisión: Implementar registros centralizados para bloques, conectores, proveedores y triggers.

Razón: Permite añadir nuevas integraciones sin modificar el núcleo del sistema. Cada nuevo bloque simplemente se añade al registry y queda disponible automáticamente.

Trade-off: Aumenta el tamaño del bundle inicial. Mitigación: code splitting y lazy loading de bloques menos usados.

Evidencia: El patrón se aplica consistentemente en blocks/registry.ts, connectors/registry.ts, providers/registry.ts, triggers/registry.ts.

2. TanStack Query como Fuente de Verdad

Decisión: Usar TanStack Query para todo el estado del servidor, eliminando sincronización manual.

Razón: Evita el problema de estado derivado y sincronización. TanStack Query maneja cache, revalidación, y optimistic updates automáticamente.

Trade-off: Curva de aprendizaje para desarrolladores acostumbrados a Redux/Zustand para todo el estado.

Evidencia: apps/sim/hooks/queries/general-settings.ts:67-76 muestra el patrón de uso.

3. Versionado de Bloques Coexistente

Decisión: Permitir múltiples versiones de un bloque en el registry (e.g., mistral_parse, mistral_parse_v2, mistral_parse_v3).

Razón: Permite migración gradual de usuarios sin breaking changes. Los workflows existentes continúan funcionando mientras nuevos usan versiones mejoradas.

Trade-off: Aumenta complejidad del registry y requiere lógica de resolución de versiones.

Evidencia: apps/sim/blocks/registry.ts:440-458 implementa getLatestBlock().

4. API Keys Alojadas con Rate Limiting

Decisión: Proporcionar API keys alojadas para usuarios sin configuración propia, con rate limiting por dimensión.

Razón: Reduce fricción de onboarding. Usuarios pueden probar el sistema sin configurar credenciales.

Trade-off: Costo operativo y complejidad de gestión de keys. Requiere monitoreo de uso y billing.

Evidencia: apps/sim/tools/index.ts:120-128 y apps/sim/tools/index.ts:338-356.

5. Normalización de Identificadores

Decisión: Normalizar identificadores reemplazando guiones por underscores (type.replace(/-/g, '_')).

Razón: Compatible con convenciones de URLs (kebab-case) y nombres de variables (snake_case) sin duplicación.

Trade-off: Añade overhead de procesamiento en cada lookup.

Evidencia: apps/sim/blocks/registry.ts:436-437.

6. Filtrado de Campos Internos con Prefijo __

Decisión: Reservar el prefijo __ para campos internos y eliminarlos antes de retornar respuestas.

Razón: Permite pasar metadatos internos (costos, debugging) sin exponerlos al usuario final.

Trade-off: Requiere convención estricta y documentación clara.

Evidencia: apps/sim/tools/index.ts:365-376.

7. Detección Amplia de Rate Limits

Decisión: Detectar rate limits no solo por status 429, sino también por mensajes en 401/403.

Razón: Algunos proveedores (e.g., Perplexity) retornan 401/403 con "insufficient_quota" en lugar de 429 estándar.

Trade-off: Puede detectar falsos positivos si mensajes coinciden accidentalmente.

Evidencia: apps/sim/tools/index.ts:145-157.


Tabla de Selección Tecnológica

TecnologíaPropósitoRazón de SelecciónAlternativas Consideradas
Next.js App RouterFramework webSSR, API routes, file-based routingRemix, Pages Router
TypeScriptLenguajeType safety, DX, ecosistemaJavaScript, Flow
TanStack QueryEstado servidorCache, revalidación, optimistic updatesSWR, Apollo Client
ZustandEstado global UISimple, sin boilerplate, TypeScriptRedux, Jotai, Recoil
Drizzle ORMBase de datosType-safe, lightweight, SQL-likePrisma, TypeORM
MermaidDiagramasIntegración Markdown, renderizado client-sidePlantUML, D2
Python SDKSDK clienteEcosistema data/ML, requests libraryJavaScript SDK únicamente
CLI PackageHerramientas devAutomatización, scripts de desarrolloShell scripts
Socket ServerReal-timeActualizaciones de ejecución en vivoServer-Sent Events
DAG ExecutorMotor workflowsEjecución ordenada, paralelismoState machine simple

Diagrama de Dependencias entre Módulos

正在加载图表渲染器...

Puntos clave del diagrama de dependencias:

  1. Flujo unidireccional: Las dependencias fluyen de UI hacia motor, y de motor hacia infraestructura, nunca en reversa.

  2. Aislamiento de registries: Los registries son hojas del grafo de dependencias, no dependen de otros módulos de la aplicación.

  3. Hooks como interfaz: TanStack Query hooks actúan como interfaz entre UI y motor de ejecución, encapsulando lógica de fetch y cache.

  4. Executor como hub: El executor es el único componente que interactúa con tools, blocks, database y sockets simultáneamente.


Configuración y Flujo de Inicio

Estructura de Directorios

La aplicación sigue una estructura feature-based bajo apps/sim/:

apps/sim/
├── app/                 # Next.js app router (páginas, API routes)
├── blocks/              # Definiciones de bloques y registry
├── components/          # UI compartida (emcn/, ui/)
├── executor/            # Motor de ejecución de workflows
├── hooks/               # Hooks compartidos (queries/, selectors/)
├── lib/                 # Utilidades de aplicación
├── providers/           # Integraciones de proveedores LLM
├── stores/              # Zustand stores
├── tools/               # Definiciones de herramientas
└── triggers/            # Definiciones de triggers

(.claude/rules/sim-architecture.md:16-28)

Organización de Features

Las features viven bajo app/workspace/[workspaceId]/:

feature/
├── components/          # Componentes de la feature
├── hooks/               # Hooks específicos de feature
├── utils/               # Utilidades específicas (2+ consumidores)
├── feature.tsx          # Componente principal
└── page.tsx             # Entry point de página Next.js

(.claude/rules/sim-architecture.md:34-41)

Convenciones de Nomenclatura

TipoConvenciónEjemplo
ComponentesPascalCaseWorkflowList
HooksPrefijo useuseWorkflowOperations
Archivoskebab-caseworkflow-list.tsx
Storesstores/feature/store.tsstores/workflow/store.ts
ConstantesSCREAMING_SNAKE_CASEMAX_RETRIES
InterfacesPascalCase con sufijoWorkflowListProps
(.claude/rules/sim-architecture.md:43-50)

Reglas de Utilidades

  • Nunca crear utils.ts para un solo consumidor - inline en el componente
  • Crear utils.ts cuando 2+ archivos necesitan el mismo helper
  • Ubicación: lib/ (app-wide) → feature/utils/ (feature-scoped) → inline (uso único) (.claude/rules/sim-architecture.md:51-56)

Flujo de Inicialización

  1. Next.js App Router inicializa la aplicación
  2. Provider Registry inicializa proveedores LLM via initializeProviders() (apps/sim/providers/registry.ts:50-63)
  3. TanStack Query carga settings iniciales via hooks (apps/sim/hooks/queries/general-settings.ts:67-76)
  4. Socket Server establece conexión para actualizaciones real-time
  5. Block/Connector/Trigger Registries cargan configuraciones bajo demanda

SDK de Python

El proyecto incluye un SDK de Python para ejecutar workflows programáticamente:

python
1# setup.py
2setup(
3    name="simstudio-sdk",
4    version="0.1.1",
5    description="Sim SDK - Execute workflows programmatically",
6    python_requires=">=3.8",
7    install_requires=[
8        "requests>=2.25.0",
9        "typing-extensions>=4.0.0; python_version<'3.10'",
10    ],
11)

(packages/python-sdk/setup.py:6-32)

El SDK soporta Python 3.8-3.12 y proporciona una interfaz para ejecutar workflows sin usar la UI web.