# What Is the Role of Pydantic in Soup CLI's Configuration?

> Discover how Pydantic enhances Soup CLI's configuration with type safety and validation for soup.yaml files, ensuring robust and error-free execution.

- Repository: [Alpamys Makazhan/Soup](https://github.com/MakazhanAlpamys/Soup)
- Tags: internals
- Published: 2026-09-06

---

**Pydantic v2 provides Soup CLI with a declarative, strongly-typed configuration layer that validates [`soup.yaml`](https://github.com/MakazhanAlpamys/Soup/blob/main/soup.yaml) files through hierarchical BaseModel schemas, ensuring type safety and domain-specific constraints before any training code executes.**

Soup CLI, an open-source training framework hosted at `MakazhanAlpamys/Soup`, relies on **Pydantic** to manage its entire configuration pipeline. Instead of manual dictionary parsing, the CLI uses Pydantic v2 models to define, validate, and enforce the structure of user-defined settings stored in [`soup.yaml`](https://github.com/MakazhanAlpamys/Soup/blob/main/soup.yaml).

## Declarative Schema Definition in schema.py

The file [`src/soup_cli/config/schema.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/config/schema.py) serves as the single source of truth for all configuration sections. Each component inherits from **Pydantic's `BaseModel`**:

- **DataConfig** handles dataset parameters
- **TrainingConfig** manages hyperparameters and quantization settings  
- **LoraConfig** controls Low-Rank Adaptation options

This hierarchical inheritance creates a strongly-typed object graph. When nested models compose the top-level **SoupConfig**, the entire configuration tree becomes type-safe and self-documenting.

## Automatic Type Coercion and Field Validation

When the CLI loads [`soup.yaml`](https://github.com/MakazhanAlpamys/Soup/blob/main/soup.yaml), it passes the raw dictionary to `SoupConfig.model_validate()`. Pydantic automatically:

1. **Coerces values** to declared types (`int`, `float`, `Literal[...]`, `Union[...]`)
2. **Enforces constraints** defined via `Field()` parameters such as `ge=0`, `le=1.0`, and `max_length=4096`
3. **Validates nested structures** recursively through the model hierarchy

This eliminates boilerplate conversion code and ensures that downstream training modules receive properly typed values.

## Custom Cross-Field Validation

Beyond type checking, Pydantic's **`@field_validator`** and **`@model_validator`** decorators enforce complex business rules. According to the source code in [`src/soup_cli/config/schema.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/config/schema.py), these validators handle:

- **Mutually exclusive LoRA methods** (preventing simultaneous activation of incompatible options like `use_dora` and `use_vera`)
- **Stream-buffer consistency** checks
- **Exclusivity constraints** between `train_on_responses_only` and `train_on_messages_with_train_field`

These domain-specific validations run automatically during model instantiation, catching logical errors before training begins.

## Fail-Fast Error Handling

If a user supplies an out-of-range value or an illegal combination, Pydantic raises a **`ValidationError`** with a clear, human-readable message. This **fail-fast** approach prevents obscure runtime crashes deep in the training loop. Instead of debugging GPU memory errors caused by invalid batch sizes, developers receive immediate feedback about which configuration key violated which constraint.

## Lazy Dependency Management

The schema module intentionally imports only lightweight utilities, keeping validation isolated from heavy dependencies like `torch` and `transformers`. This architectural decision means:

- The CLI starts instantly even on systems without GPUs
- Configuration files can be validated and inspected without loading CUDA libraries  
- Training dependencies are deferred until the validated config object actually reaches the training routine

The separation between [`src/soup_cli/config/schema.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/config/schema.py) and [`src/soup_cli/commands/run.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/commands/run.py) enforces this boundary.

## Practical Configuration Loading

Here is how Soup CLI loads and validates a [`soup.yaml`](https://github.com/MakazhanAlpamys/Soup/blob/main/soup.yaml) file:

```python
from pathlib import Path
import yaml
from soup_cli.config.schema import SoupConfig

# Load raw YAML

yaml_path = Path("soup.yaml")
raw_cfg = yaml.safe_load(yaml_path.read_text())

# Pydantic validates and returns a strongly-typed config instance

config: SoupConfig = SoupConfig.model_validate(raw_cfg)

# Access validated fields with proper types

print("Training epochs:", config.training.epochs)
print("Quantization mode:", config.training.quantization)

```

Attempting to set mutually exclusive LoRA options triggers an immediate validation error:

```python

# This raises ValidationError:

config.training.lora.use_dora = True
config.training.lora.use_vera = True  # Mutually exclusive

```

## Summary

- **Pydantic** provides the declarative foundation for Soup CLI's configuration system in [`src/soup_cli/config/schema.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/config/schema.py)
- **BaseModel** inheritance creates a hierarchical, strongly-typed schema covering `DataConfig`, `TrainingConfig`, and `LoraConfig`
- **Automatic validation** via `model_validate()` enforces type coercion, field constraints, and custom business rules through `@field_validator` and `@model_validator`
- **Fail-fast behavior** ensures invalid configurations trigger clear `ValidationError` messages before training code executes
- **Dependency isolation** keeps the validation layer lightweight, avoiding heavy imports like `torch` until absolutely necessary

## Frequently Asked Questions

### How does Soup CLI handle invalid configuration values?

Soup CLI uses Pydantic's validation engine to catch invalid values immediately upon loading [`soup.yaml`](https://github.com/MakazhanAlpamys/Soup/blob/main/soup.yaml). When `SoupConfig.model_validate()` encounters a type mismatch, constraint violation, or illegal parameter combination, it raises a `ValidationError` with a specific message indicating which field failed and why. This prevents the training process from starting with bad parameters.

### Where are the Pydantic models for Soup CLI defined?

All Pydantic models reside in [`src/soup_cli/config/schema.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/config/schema.py). This file defines the top-level `SoupConfig` class along with nested configuration sections like `DataConfig`, `TrainingConfig`, and `LoraConfig`. Each class inherits from Pydantic's `BaseModel` and uses `Field()` specifications to declare constraints and documentation.

### Can I validate a Soup configuration without installing PyTorch?

Yes. The schema module is designed to import only lightweight standard library and utility modules, avoiding heavy dependencies like `torch` or `transformers`. This allows you to validate [`soup.yaml`](https://github.com/MakazhanAlpamys/Soup/blob/main/soup.yaml) files and inspect configuration objects on any system, regardless of GPU availability or deep learning framework installation.

### What types of cross-field validations does Soup CLI enforce?

According to the source code, validators enforce domain-specific rules such as mutual exclusivity between LoRA methods (e.g., preventing simultaneous use of `use_dora` and `use_vera`), consistency between streaming and buffer settings, and logical constraints between training modes like `train_on_responses_only` versus `train_on_messages_with_train_field`.