Web, Pipeline, and CLI Architecture

MUDIDI provides a local FastAPI dashboard backend and a CLI. Both resolve into the typed inference configuration and shared extraction pipeline.

Local dashboard backend

mudidi web (web/server.py)
  → create_app (web/app.py)
  → POST /runs/preview
       ├─ copy one uploaded PDF into run-owned storage (web/inputs.py)
       ├─ validate required form fields and PDF page bounds (web/forms.py)
       ├─ build InferenceConfig
       └─ prepare a validated run (web/jobs.py, web/runs.py)
  → POST /runs/{id}/start
       └─ dedicated inference subprocess (web/inference_worker.py)

The dashboard route rejects legacy image and directory upload fields. It requires exactly one dictionary PDF and a dictionary-page specification. Dictionary, introduction, and representative MDF parsing-guide pages share the same positive 1-based grammar and are checked against the PDF page count before a durable run is created. FormFieldError associates failures with controls so the server-rendered form can identify them inline.

Run metadata and events are stored in SQLite. Uploaded inputs, instruction text, presets, and output artifacts are kept in managed local directories. web/jobs.py enforces the single-worker lifecycle, while web/runs.py owns durable state transitions, cancellation, resume, and the MDF parsing-guide approval checkpoint.

CLI Dispatch

mudidi main (cli/main.py)
  ├─ run → typed inference config → extract.py
  ├─ benchmark run → typed benchmark config → extract.py
  ├─ benchmark sweep → expand/validate all runs → sequential extract.py calls
  ├─ benchmark evaluate
  │    ├─ stage1 → evaluate_stage1.py:main
  │    └─ stage2 → evaluate_stage2_mdf.py:main
  └─ config validate → config/yaml_config.py

mudidi run Argument Flow

main.py (single public parser, sparse CLI overrides)
  → load/merge typed YAML (config/yaml_config.py)
  → execution_namespace_from_config (temporary compatibility adapter)
  → extract.py (orchestration driver)

Benchmark Sweep Flow

benchmark_sweep YAML
  → benchmark_sweep.py (axes/explicit expansion, filters, max-runs guard)
  → BenchmarkRunConfig validation + input/prerequisite preview for every run
  → sequential execution (failure_policy: continue|stop)
  → <output>/sweeps/<name>/sweep_manifest.json

Extraction Orchestration (cli/extract.py)

Responsibilities: - Materialize page inputs (snippets dir, PDF split via PyMuPDF, rasterize via PyMuPDF) - Collect intro / alphabet / OCR-hint / neighbor context - Select strategy: TwoStageLLMExtraction, VLM OCR batch, or Mathpix OCR batch - ThreadPoolExecutor page concurrency with rate-limit backoff - Write manifests, usage JSON, stage outputs

Extraction Strategies

Strategy File Stages
TwoStageLLMExtraction extraction/llm_two_stage.py Stage 1 structured transcript → Pass 1 parse rules → Pass 2 MDF
VLM OCR extraction/vlm_ocr.py Stage 1 only via ocr/vlm/runner.py
Mathpix OCR extraction/mathpix_ocr.py Stage 1 Mathpix Convert API → flat text + reusable OCR hints
ExtractionStrategy ABC extraction/base.py Extension point

LLM Pipeline

Stage 1: prompts.py → client.complete_structured(TranscriptionResponse)
Pass 1:  pass_1.py → load_or_discover_parse_rules → mdf_parsing_guide.json
Pass 2:  pass_2.py → extract_direct_mdf → page MDF text
Client:  client.py → litellm.completion (retries, reasoning, cache keys)

Optional agentic loops run after Stage 1/2 output. Stage 1 automatically allows catastrophic recovery (full-page re-transcription); artifacts are written below each page's agentic/ directory.

OCR / VLM Backends

ocr/base.py (OCRBackend ABC)
  ├─ mathpix.py, mathpix_convert.py
  ├─ adapters/ (flat export from backend JSON)
  └─ vlm/
       ├─ registry.py (mineru2.5-pro, paddleocr-vl-1.5, glm-ocr)
       ├─ runner.py, completion.py
       └─ local servers: paddle_genai_server.py, glm_vllm_server.py

Evaluation CLIs

Evaluator Entry Metrics
FlatStage1Evaluator evaluate_stage1.py char quality, markup, read order, per-language spans
MdfEvaluator evaluate_stage2_mdf.py field alignment, marker equiv, similarity, read order

Supporting: mdf_lexical_repair.py, mdf_stage1_projection.py, stage1_task_discovery.py

Key Files (by concern)

Concern Files
Run config config/run_config.py, config/output_paths.py, config/prompt_cache.py
Model args cli/model_args.py
Page I/O utils/pdf_split.py, utils/pdf_render.py, utils/stage1_input.py
Stage 2 I/O utils/stage2_direct_mdf_io.py, utils/parse_rules_pages.py
MDF utils utils/mdf_export.py, utils/mdf_compare.py
Context utils/page_context.py, utils/dictionary_languages.py

Output Artifacts

inference:  <output>/stage-1/<page>/, stage-2/<page>/, mdf_parsing_guide.json
benchmark:  <output>/stage-1/<experiment>/<page>/, stage-2/<experiment>/
per page:   *_usage.json, *_stage2_raw.txt, *.mdf.txt, run_usage.json