# How to Contribute to the Soup CLI Project: A Complete Guide for Developers

> Learn how to contribute to the Soup CLI project. This guide covers forking, setting up your environment, running tests, linting with ruff, and submitting pull requests for developers.

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

---

**To contribute to the Soup CLI project, fork the repository, set up a Python 3.10–3.12 virtual environment, install with `pip install -e ".[dev]"`, run tests with `pytest`, and follow the linting standards enforced by ruff before opening a pull request.**

Contributing to the **Soup CLI** means joining a modular, Typer‑based command‑line application for LLM fine‑tuning, evaluation, and deployment according to the MakazhanAlpamys/Soup source code. This guide walks you through the architecture, setup steps, and submission process so you can make effective, well‑tested contributions.

---

## Understanding the Soup CLI Architecture

Before writing code, familiarize yourself with how components connect. The project uses a **centralized command registry** with **lazy imports** to keep startup times fast.

### Entry Point and Command Registration

In [`src/soup_cli/cli.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/cli.py), the `run()` function drives the entire Typer application. It handles global options, audit logging, and optional telemetry. The [`cli.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/cli.py) file also serves as the **single registry** where all sub‑commands are attached:

```python

# src/soup_cli/cli.py (lines 98–146)

# Commands are registered via app.command() or app.add_typer()

```

Each sub‑command—`train`, `chat`, `serve`, and others—is imported and bound to the main `app`. This design makes the CLI **extensible**: adding a new command requires only a new module and a single registration line.

### Lazy Imports for Performance

Heavy ML dependencies (`torch`, `transformers`, `peft`, `trl`) are **imported inside command handlers**, not at module load time. See the comment at line 84 in [`cli.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/cli.py) and the implementation pattern in individual command modules. This keeps `soup --help` responsive even without GPU libraries installed.

### Configuration and Data Handling

| Component | Location | Purpose |
|-----------|----------|---------|
| **Configuration schema** | [`src/soup_cli/config/schema.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/config/schema.py) | Pydantic v2 models defining every YAML field—validation, defaults, and type safety in one place. |
| **Data format normalization** | [`src/soup_cli/data/formats.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/data/formats.py) | Converts Alpaca, ShareGPT, ChatML, vision, and audio formats into a unified `{"messages": [...]}` structure. |
| **Data loading** | [`src/soup_cli/data/loader.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/data/loader.py) | Auto‑detects format and prepares data for trainers. |

### Trainer Wrappers

Trainer modules in `src/soup_cli/trainer/` (e.g., [`sft.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/sft.py), [`dpo.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/dpo.py)) wrap HuggingFace TRL trainers. They automatically apply **LoRA**, **quantization**, **batch‑size estimation**, and **Rich progress bars** to streamline fine‑tuning workflows.

---

## Setting Up Your Development Environment

Follow these validated steps from [`CONTRIBUTING.md`](https://github.com/MakazhanAlpamys/Soup/blob/main/CONTRIBUTING.md) to prepare your workspace:

```bash

# 1. Fork the repository on GitHub and clone your fork locally

git clone https://github.com/<your-username>/Soup.git
cd Soup

# 2. Create an isolated Python environment (Python 3.10–3.12)

python3 -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate

# 3. Install the project in editable mode with development dependencies

pip install -e ".[dev]"

# 4. Verify the setup by running the test suite

pytest tests/ -v --tb=short

# 5. Run the linter and automatically fix simple violations

ruff check --fix src/soup_cli/ scripts/ tests/

```

The `pip install -e ".[dev]"` command installs the package in **editable mode** plus development tools like `pytest` and `ruff`.

---

## Making and Testing Your Changes

### Adding a New Sub‑Command

The Soup CLI follows a predictable pattern for command extensions:

1. Create your command module at [`src/soup_cli/commands/mycmd.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/commands/mycmd.py).
2. Register it in [`src/soup_cli/cli.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/cli.py) using `app.command()` or `app.add_typer()`.
3. Use **lazy imports** inside your command handler for any heavy dependencies.

```python

# Example structure for src/soup_cli/commands/mycmd.py

import typer

app = typer.Typer()

@app.command()
def mycmd(
    config: str = typer.Option(..., help="Path to config YAML"),
    verbose: bool = typer.Option(False, "--verbose", "-v"),
):
    """Brief description of what mycmd does."""
    # Lazy import: only load when command is invoked

    import torch
    # ... implementation ...

```

### Running Targeted Tests

After changes, validate with focused test runs:

```bash

# Run tests for your specific command

pytest tests/ -k mycmd -v

# Full test suite before final commit

pytest tests/ -v

```

The `tests/` directory covers CLI commands, trainers, data pipelines, and edge cases according to the repository structure.

### Code Quality Standards

The project enforces **ruff** with specific rules:

- Line length: **100 characters**
- Import ordering enforced
- No bare `print` statements (use logging)

Run the linter before every commit:

```bash
ruff check --fix src/soup_cli/ scripts/ tests/

```

---

## Documentation and Pull Request Process

### Updating Documentation

All contributions must include documentation updates. The project maintains guides under `docs/`:

| Document | Content |
|----------|---------|
| [`docs/commands.md`](https://github.com/MakazhanAlpamys/Soup/blob/main/docs/commands.md) | Reference for all CLI commands |
| [`docs/training.md`](https://github.com/MakazhanAlpamys/Soup/blob/main/docs/training.md) | Training workflows and configuration |

Update both the relevant doc page and the **README.md** for user‑facing features.

### Submitting Your Contribution

```bash

# 1. Stage your changes

git add src/soup_cli/commands/mycmd.py src/soup_cli/cli.py docs/commands.md

# 2. Commit with conventional commit format

git commit -m "feat: add mycmd sub‑command for custom model evaluation"

# 3. Push and open a Pull Request

git push origin my-feature-branch

```

Pull requests are reviewed against:

- Test coverage for new functionality
- ruff compliance with zero warnings
- Documentation accuracy and completeness
- Adherence to lazy import patterns for performance

---

## Key Files for Contributors

| File | When to Modify |
|------|----------------|
| [`src/soup_cli/cli.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/cli.py) | Adding or wiring new commands; global option changes |
| [`src/soup_cli/config/schema.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/config/schema.py) | New configuration fields or validation rules |
| [`src/soup_cli/data/formats.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/data/formats.py) | Additional data format support |
| `src/soup_cli/commands/` | New command implementations |
| `tests/` | Corresponding test coverage for any change |
| [`CONTRIBUTING.md`](https://github.com/MakazhanAlpamys/Soup/blob/main/CONTRIBUTING.md) | Rarely—only for process improvements |

---

## Summary

- **Fork and clone** the MakazhanAlpamys/Soup repository, then install with `pip install -e ".[dev]"`.
- **Understand the architecture**: centralized command registry in [`cli.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/cli.py), lazy imports for performance, Pydantic schemas for configuration.
- **Extend via `src/soup_cli/commands/`**, register in [`cli.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/cli.py), and maintain lazy import patterns.
- **Validate with `pytest`** and **lint with `ruff`** before submitting.
- **Document changes** in `docs/` and [`README.md`](https://github.com/MakazhanAlpamys/Soup/blob/main/README.md), using conventional commits for clear history.

---

## Frequently Asked Questions

### What Python versions are supported for contributing to Soup CLI?

The project supports **Python 3.10 through 3.12**. These are the versions validated in CI and tested against all dependencies including `torch`, `transformers`, and `trl`. Using a version outside this range may cause compatibility issues with the ML stack.

### Why does Soup CLI use lazy imports instead of top‑level imports?

Lazy imports keep the CLI **fast for lightweight operations** like `soup --help` or `soup --version`. Heavy ML libraries (`torch`, `transformers`, `peft`, `trl`) are only loaded when a specific command needs them. This pattern is implemented throughout `src/soup_cli/commands/` and noted in the comment at line 84 of [`cli.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/cli.py).

### How do I add a new configuration option to Soup CLI?

Define the field in [`src/soup_cli/config/schema.py`](https://github.com/MakazhanAlpamys/Soup/blob/main/src/soup_cli/config/schema.py) using **Pydantic v2**. This file is the **single source of truth**—all validation, defaults, and type safety derive from here. The rest of the codebase references these models, so changes propagate automatically to command handlers and YAML loading.

### What should I do if ruff reports errors I don't understand?

Run `ruff check --fix src/soup_cli/ scripts/ tests/` first—this auto‑corrects many issues. For remaining errors, consult the [ruff documentation](https://docs.astral.sh/ruff/) or check existing code in the repository for compliant patterns. The project uses **100‑character line length** and prohibits bare `print` statements in favor of structured logging.