ποΈ ArchitectureΒΆ
This page covers MMIRAGEβs internal module layout and the key design decisions behind each subsystem. It is aimed at contributors and developers who want to understand or modify the codebase.
If you are looking for a user-facing explanation of what happens when you run mmirage run,
read Pipeline instead.
High-Level OverviewΒΆ
Each shard follows the same three-stage pipeline:
At the orchestration level, the CLI manages shard dispatch, retry logic, and optional SLURM submission:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β mmirage CLI β
β run / submit / check / retry / merge / stats β
βββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββΌββββββββββββββββββββββββ
β launch_pipeline β
β (local loop or SLURM array submission) β
ββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β spawns
ββββββββββββββββΌββββββββββββββββββββββββββββββ
β shard_process.py β
β (one process per shard) β
ββββ¬βββββββββββ¬βββββββββββββββ¬ββββββββββββββββ
β β β
ββββββββββΌβββ ββββββΌβββββ ββββββΌβββββββ
β Loader β β Mapper β β Renderer β
β(Dataset) β β(Compute)β β(Jinja2) β
ββββββββββ¬βββ ββββββ¬βββββ ββββββ¬βββββββ
β β β
ββββββββββββ΄βββββββββββββ
β
βββββββββββββββΌββββββββββββββ
β shard_utils.py β
β atomic save + state mgmt β
βββββββββββββββββββββββββββββ
Package LayoutΒΆ
src/mmirage/
βββ __init__.py Public API surface (MMirageConfig, load_mmirage_config β¦)
βββ cli.py CLI entry point and subcommand handlers
βββ shard_process.py Single-shard processing script
βββ shard_utils.py Shard state, atomic saves, GPU polling, benchmarking
βββ merge_shards.py Post-processing: merge shard_* dirs into one dataset
β
βββ config/ Configuration layer (pure dataclasses, no heavy deps)
β βββ config.py MMirageConfig, ExecutionParams, ProcessingParams
β βββ loading.py LoadingParams, env-var resolution
β βββ batch_provider.py Provider-neutral BatchProviderConfig
β βββ openai_batch.py OpenAIBatchConfig (extends BatchProviderConfig)
β βββ anthropic_batch.py AnthropicBatchConfig (extends BatchProviderConfig)
β βββ utils.py YAML loader, env-var expansion, dacite wiring
β
βββ cli_utils/ CLI helpers
β βββ runtime.py Path expansion, file logging, setup_runtime
β βββ slurm.py sbatch script generation, job submission, polling
β βββ status.py Shard status reads, retry budget, check_failed_shards
β
βββ core/
βββ loader/ Dataset loading
β βββ base.py BaseDataLoader, DataLoaderRegistry
β βββ jsonl.py JSONL loader (type: "JSONL")
β βββ local_hf.py HuggingFace load_from_disk (type: "loadable")
β βββ utils.py load_datasets_from_configs helper
β
βββ process/ Data transformation
β βββ variables.py InputVar, OutputVar, VariableEnvironment, JMESPath cache
β βββ base.py BaseProcessor, ProcessorRegistry, TokenCounts
β βββ mapper.py MMIRAGEMapper β orchestrates variables through processors
β βββ processors/
β β βββ llm/
β β β βββ config.py SGLangLLMConfig, SGLangServerArgs, LLMOutputVar
β β β βββ llm_processor.py LLMProcessor β SGLang engine wrapper
β β βββ custom/
β β β βββ config.py CustomProcessorConfig, CustomOutputVar
β β β βββ custom_processor.py CustomProcessor β pebble pool, circuit breaker
β β β βββ worker.py Spawned-worker script loading and execution
β β βββ batch_api/
β β βββ config.py BatchApiProcessorConfig
β β βββ batch_api_processor.py BatchApiProcessor β provider batch submission
β βββ batch/ Async/batch inference subsystem
β βββ orchestrator.py End-to-end batch pipeline
β βββ adapter.py Provider-neutral batch adapter interface
β βββ openai_adapter.py OpenAI Batch API adapter
β βββ anthropic_adapter.py Anthropic Messages Batches adapter
β βββ chunking.py Request chunking (byte/count limits)
β βββ collector.py Response collection and result joining
β βββ status_checker.py Batch job polling
β βββ registry.py Adapter registry
β
βββ writer/
βββ renderer.py TemplateRenderer β Jinja2 output_schema rendering
Data FlowΒΆ
Single Shard (local mode)ΒΆ
Config loading β
load_mmirage_configreads the YAML, expands${ENV_VAR}references, and constructs a typedMMirageConfigviadacite.Dataset loading β
load_datasets_from_configscalls the appropriateDataLoader(JSONL orloadable), returning a HuggingFaceDataset.Sharding β The dataset is split into
num_shardsslices; this shard processes sliceshard_id.Mapping β
MMIRAGEMapper.rewrite_batchiterates over batches:Extracts
InputVarvalues from each sample using cached JMESPath expressions.Resolves image inputs to PIL Images or absolute paths.
Calls the registered
Processor(e.g.LLMProcessor) for eachOutputVar.
Rendering β
TemplateRenderer.batch_renderapplies theoutput_schemaJinja2 templates, substituting variable values. Simple{{ var }}references bypass Jinja2 to preserve non-string types (e.g. PIL Images).Atomic save β The processed shard is written to
shard_<id>/underoutput_dirusing a temp-then-rename pattern with hostname + PID + UUID to avoid cross-host collisions on shared filesystems.State marker β A
status.jsonfile is written to the state directory recordingsuccessorfailureand the attempt count.
SLURM modeΒΆ
launch_pipeline generates and submits an sbatch array script. Each array task runs shard_process.py with SLURM_ARRAY_TASK_ID as the shard ID. The orchestrator polls job status via squeue, waits for the settle_time_seconds, checks status.json for each shard, and retries failed shards up to max_retries.
Batch API mode (OpenAI, Anthropic)ΒΆ
The BatchApiProcessor delegates request submission to the batch orchestrator:
Requests are serialized to JSONL chunks respecting
max_chunk_bytes/max_requests_per_chunk.Each chunk is uploaded/submitted and a metadata receipt is written.
The mapper writes
__BATCH_SUBMITTED__:<custom_id>placeholders into the output shards.Results are later checked/collected via
mmirage.core.process.batch.status_checkerandmmirage.core.process.batch.collector.
Key Design DecisionsΒΆ
Registry pattern for loaders and processorsΒΆ
Both DataLoaderRegistry and ProcessorRegistry use a decorator-based registry. New loaders/processors self-register at import time, keeping the core pipeline agnostic of concrete implementations.
Dacite + dataclasses for configΒΆ
All configuration is expressed as plain Python dataclasses. dacite converts the raw YAML dict into typed objects, providing structural validation without a heavy schema library at runtime.
JMESPath cachingΒΆ
Compiled JMESPath expressions are cached in a module-level dict to avoid recompilation on every sample β important for high-throughput processing.
Atomic shard savesΒΆ
Output shards are first written to a temporary directory with a host+PID+UUID suffix, then renamed. This guarantees crash-safe writes and avoids collisions on SLURM shared filesystems where multiple nodes may share a PID space.
Separation of config and heavy depsΒΆ
The config/ package has minimal imports (no torch, sglang, transformers). The core/process/processors/llm/config.py module is also lightweight β it registers the processor configuration without importing the SGLang engine. The engine is only imported when a shard actually processes data, enabling fast CLI startup and documentation builds.
See alsoΒΆ
Pipeline β user-facing walkthrough of the data flow
Concepts β vocabulary used throughout the codebase
Developer Guide β adding loaders and processors, running tests