Architecture and Developer Guide
March 27, 2026 · View on GitHub
This document provides a developer-focused overview of the Omni-Eval Kit's architecture. It explains the design principles, codebase structure, and how to extend the framework with new models, datasets, and evaluation metrics.
1. Project Philosophy
The toolkit is designed with three core principles in mind:
- Modularity: Each key component (model handling, data loading, inference, evaluation) is decoupled into its own module. This makes the system easier to understand, debug, and extend.
- Extensibility: Adding new functionality should be straightforward and should not require major refactoring of existing code. Clear extension points are provided for common use cases.
- Clarity over Complexity: The workflows are designed to be explicit and easy to follow. For example, the main evaluation logic resides in
eval_main.py, providing a clear entry point to the entire process.
2. Codebase Structure
The project is organized into the following key directories:
omnievalkit/
├── eval_main.py # The main entry point for all evaluations.
├── setup.py # Package installation config.
├── data/ # Evaluation datasets (downloaded via scripts/hf_download.py)
├── docs/ # Project documentation.
├── scripts/ # Helper scripts (dataset download, format conversion)
├── results/ # Evaluation results output.
├── configs/ # Global configuration files.
└── o_e_Kit/ # Core package directory.
├── datasets/ # Dataset implementations.
├── models/ # Wrappers for different models.
│ ├── minicpm/ # MiniCPM-O series (minicpmo.py)
│ ├── qwen/ # Qwen3-Omni series
│ ├── gemini/ # Gemini API evaluation
│ └── asr/ # ASR baselines (Whisper)
├── utils/ # Core utilities for inference, evaluation, etc.
│ ├── args/ # Modular argument management
│ ├── metrics/ # Evaluation metrics
│ ├── infer.py # Handles the distributed inference loop.
│ ├── model_loader.py # Dynamic model loading.
│ ├── get_args.py # Argument parsing.
│ └── ...
└── configs/ # Dataset generation configs.
eval_main.py: The central orchestrator. It parses arguments, initializes the distributed environment, loads the specified model and dataset, calls the inference utility, and triggers the final evaluation.scripts/: Contains helper scripts for data management (HuggingFace upload/download) and API-based evaluation.models/: Contains wrappers for different models. Each wrapper can implement one or more generation methods (e.g.,generate_batch,generate_duplex), which are dynamically called based on the--generate_methodargument.datasets/: Contains customtorch.utils.data.Datasetimplementations for different evaluation benchmarks.utils/: A collection of helper modules.infer.py: Manages the data loading (DataLoader), distributed sampling (InferenceSampler), and the core inference loop, returning raw predictions.wer_eval.py: Takes the raw predictions and calculates specific metrics (like WER/CER), generating a final score and a detailed report.get_args.py: Defines and parses all command-line arguments.
3. How to Extend the Framework
This is the most powerful aspect of the toolkit. Here’s how to add new components.
How to Add a New Dataset
-
Create a Dataset Class: In the
datasets/directory, create a new Python file (e.g.,my_new_dataset.py). Inside, define a class that inherits fromtorch.utils.data.Dataset. It must implement__len__and__getitem__. The__getitem__method should return a dictionary with consistent keys (e.g.,wav_path,question,gt_answers). -
Add to
get_args.py: Add new command-line arguments to specify the paths for your new dataset's files. Also, add a flag like--eval_my_new_datasetto control when it runs. -
Update
eval_main.py:- In the
load_datasetfunction, add a newelifblock to handle your new dataset's name. - In the
mainfunction, add a newifblock triggered by your--eval_my_new_datasetflag to orchestrate the evaluation flow for this dataset.
- In the
How to Add a New Model
-
Create a Model Wrapper: In the
models/directory, create a new file for your model (e.g.,my_new_model.py). Inside, define a class that wraps your model.- The
__init__method should handle loading the model weights. - Implement one or more generation methods (e.g.,
def my_text_generation(self, **batch)). Each method should accept a batch dictionary and return a list of strings. The method's name should correspond to a value you'll pass to the--generate_methodargument.
- The
-
Register the Generation Method: In
utils/infer.py, in therun_inferencefunction, add anelifblock to call your new method based on thegenerate_methodstring. -
Register the Model Class: In
eval_main.py, in theload_modelfunction, add anelifblock for your model's--model_typestring. This block will import and instantiate your new model wrapper class.
How to Add a New Evaluation Metric
-
Create an Evaluator Class: In the
utils/directory, you can create a new evaluation module (e.g.,vqa_eval.py). This module should contain a class (e.g.,VQA_Eval) with:- An
evaluatemethod that takes the list of predictions. - A
summarymethod that returns a formatted report string and a final score.
- An
-
Update
evaluate_dataset: Ino_e_Kit/utils/wer_eval.py(or a more general dispatch script), add a newelifblock in theevaluate_datasetfunction. This block will be triggered by your new dataset's name and will call your new evaluator class.