Skip to content

Architecture Overview

!!! note "Work in progress" Architecture documentation is being migrated from internal developer docs. For now, see dev_docs/ in the repository for architecture decision logs and solution patterns.

Components at a Glance

ComponentTechnologyPurpose
API layerFastAPIREST endpoints, SSE streaming, webhook ingestion
LLM integrationLiteLLM, Claude SDK, OpenAI SDKMulti-provider chat completions, function calling, and structured outputs
Workflow engineTemporalDurable workflow execution with composable nodes (choice, LLM routing, sub-workflows, parallel, iteration)
DatabasePostgreSQL + SQLAlchemyDefinitions, sessions, messages, access control
StreamingRedis PubSubReal-time token delivery from LLM to client
StorageS3-compatible (R2, MinIO)Document and file storage with tenant isolation
IngestionDocling, Chonkie, LiteLLM, pgvectorDocument parsing, chunking, embedding, and hybrid search

Workflow Engine

The workflow engine uses Temporal for durable execution. Each workflow is a directed graph of nodes connected by edges, executed via BFS traversal.

Execution Model

API Request → DynamicWorkflowExecutor (Temporal workflow)
                → BFS over graph nodes
                → Each node dispatches to an activity based on its type
                → Edge routing determines next nodes (conditional, parallel, or all)

Key classes:

ClassFilePurpose
DynamicWorkflowExecutortemporal/workflows.pyMain BFS executor — processes nodes, evaluates choices, routes edges
SubWorkflowWrappertemporal/workflows.pyExecutes child workflows with input/output mapping and cycle detection
Activitiestemporal/activities.pyOne per node type: transform, LLM router, function call, parallel

Node Types

Node types are data-driven — defined in the database via seed_node_types.py and centralized in core/workflow/node_types.py. Each type has an execution_handler that maps to a Temporal activity.

Node TypeHandlerDescription
START / ENDpassthroughGraph entry/exit points
TRANSFORMactivity:execute_transform_node_activityData transformation via templates
FUNCTIONactivity:execute_function_node_activityCall a platform or HTTP function
LLMactivity:execute_llm_node_activityLLM completion
CHOICEworkflow:evaluate_choiceRule-based conditional routing
CHOICE_LLMactivity:execute_llm_router_activityAI classification routing
SUB_WORKFLOWworkflow:execute_childChild workflow composition
PARALLELworkflow:execute_parallelConcurrent branch execution
WAITtemporal:wait_for_eventPause until external signal
APPROVAL_GATEtemporal:approval_gateHuman approval before continuing
FOR_EACHworkflow_code:for_eachIterate over arrays with concurrent batching
FILTERactivity:execute_filter_node_activityFilter array items by conditions
REDUCEactivity:execute_reduce_node_activityAggregate array to single value

Edge Routing

After each node executes, the executor determines which edges to follow:

  • Normal nodes: Follow all outgoing edges
  • Choice / Choice LLM: Follow only the edge whose source_handle matches the chosen branch (branch-0, branch-1, or default)
  • Parallel: Follow edges with source_handle: parallel-* concurrently
  • For-each: Follow the source_handle: foreach-body edge for each item in the array

Workflows without source_handle on edges fall back to legacy behavior (follow all edges).

Safety Features

  • Cycle detection: Sub-workflows track a _nesting_path to prevent recursive loops (max depth: 3)
  • ReDoS protection: Regex patterns in conditions are length-capped
  • Parallel branch limit: Maximum 20 concurrent branches per parallel node
  • Template injection guard: Condition values cannot reference {{nodes.*}}
  • Fail-fast on missing types: Unknown node types raise immediately instead of silently skipping

Martha is built by aiaiai-pt.