# LTX-2 Model Version Detection Rules and Checkpoint Format Differences Explained

> Understand LTX-2 model version detection rules and checkpoint format differences between LTX-2.3 and LTX-2.5. Learn how generation is determined and key format variations.

- Repository: [Lightricks/LTX-2](https://github.com/Lightricks/LTX-2)
- Tags: deep-dive
- Published: 2026-08-18

---

**LTX-2 determines a checkpoint's generation by reading the `model_version` metadata field from its `.safetensors` file, normalizing separators, and parsing leading numeric components into a comparable tuple, while LTX-2.3 and LTX-2.S (original) checkpoints share a unified file layout but differ in STG block indices, step counts, and Gemma versions.**

This guide examines the version detection algorithm and checkpoint format distinctions between **LTX-2.3** and **LTX-2.S** (the original "S" family, including LTX-2.5) based on the official Lightricks/LTX-2 source code. Understanding these differences ensures you load the correct pipeline parameters for each checkpoint generation.

---

## How LTX-2 Detects Model Versions from Checkpoints

The **model version detection** system reads checkpoint metadata and converts version strings into comparable tuples. This process flows through two core functions in the pipeline utilities.

### Step 1: Extract and Normalize Version Metadata

The detection begins in **[`ltx-pipelines/src/ltx_pipelines/utils/constants.py`](https://github.com/Lightricks/LTX-2/blob/main/ltx-pipelines/src/ltx_pipelines/utils/constants.py)** with `detect_model_version`:

```python
def detect_model_version(checkpoint_path: Path) -> tuple[int, ...]:
    """
    Reads the metadata of the checkpoint and returns the model version as a tuple.
    Tuples of the form `(2, 3)`, `(2, 5)` are returned for valid versions.
    """
    with safe_open(checkpoint_path, framework="pt") as f:
        metadata = f.metadata()
        version_string = metadata.get("model_version", None)

    if version_string is None:
        return ()

    # Normalize separators: "2.4-rc2" → "2.4.rc2"

    version_string = version_string.replace("-", ".")
    return parse_model_version(version_string)

```

This function performs **three critical normalizations**:

- Replaces hyphens with dots so pre-release tags like `2.4-rc2` become `2.4.rc2`
- Delegates parsing to `parse_model_version` for numeric extraction
- Returns an **empty tuple `()`** when the field is missing or unreadable

### Step 2: Parse Numeric Components

The parsing logic lives in **[`ltx-core/src/ltx_core/loader/helpers.py`](https://github.com/Lightricks/LTX-2/blob/main/ltx-core/src/ltx_core/loader/helpers.py)**:

```python
def parse_model_version(version_string: str) -> tuple[int, ...]:
    """
    Parse a model version string into a tuple of integers.
    
    Only leading numeric components are kept; suffixes like 'rc' or 'beta' are discarded
    """
    components = version_string.split(".")
    version_tuple = []
    for component in components:
        if component.isdigit():
            version_tuple.append(int(component))
        else:
            break
    return tuple(version_tuple)

```

This parser **stops at the first non-numeric component**, discarding tags like `rc`, `beta`, or `alpha`. The result is a clean tuple such as `(2, 3)` or `(2, 5)` that supports direct comparison operations.

### Step 3: Map Version to Pipeline Parameters

The `detect_params` function (also in [`constants.py`](https://github.com/Lightricks/LTX-2/blob/main/constants.py)) uses tuple comparison against `_PARAMS_SINCE_VERSION`:

```python
def detect_params(checkpoint_path: Path) -> LTXParams:
    """Detect the appropriate LTXParams for a given checkpoint."""
    version_tuple = detect_model_version(checkpoint_path)
    
    # Iterate from newest to oldest, return first match

    for since_version, params in reversed(_PARAMS_SINCE_VERSION):
        if version_tuple >= since_version:
            return params
    return LTX_2_PARAMS  # Oldest fallback

```

The version tuple `(2, 3)` selects `LTX_2_3_PARAMS`, while `(2, 5)` selects `LTX_2_5_PARAMS`. The **empty tuple `()` compares below all real versions**, triggering the legacy `LTX_2_PARAMS` fallback for checkpoints without metadata.

---

## Checkpoint Format Differences: LTX-2.3 vs LTX-2.S

Despite sharing a **unified file structure**, these generations differ in generation-specific defaults, Gemma versions, and pipeline configuration.

### Unified Checkpoint Layout (Both Generations)

Both LTX-2.3 and LTX-2.S use the same physical format as documented in **[`packages/ltx-trainer/README.md`](https://github.com/Lightricks/LTX-2/blob/main/packages/ltx-trainer/README.md)**:

| Component | Location |
|-----------|----------|
| Transformer weights | Single `.safetensors` file |
| Video VAE | Embedded in same `.safetensors` |
| Audio VAE | Embedded in same `.safetensors` |
| Vocoder | Embedded in same `.safetensors` |
| Text encoder (Gemma) | Separate folder alongside `.safetensors` |

The [`README.md`](https://github.com/Lightricks/LTX-2/blob/main/README.md) states: *"The unified checkpoint format works for LTX-2, LTX-2.3, and LTX-2.5—all model weights live in a single file with the Gemma folder containing text encoder weights."*

### Generation-Specific Differences

| Feature | LTX-2.3 | LTX-2.S (Original/2.5) |
|---------|---------|------------------------|
| **`model_version` field** | `"2.3"` or `"2.3.rcX"` | `"2"`, `"2.0"`, or **omitted** |
| **Detection result** | `(2, 3)` | `()` (empty tuple → fallback) |
| **Gemma version** | Gemma 3 | Gemma 3 (2.0) / Gemma 4 (2.5) |
| **STG block index** | Block **28** | Block **29** |
| **Default steps (full model)** | 30 | 40 |
| **Default image CRF** | 33 | 33 (2.0) / 18 (2.4+) |

These defaults are resolved through `detect_params` and applied in **[`packages/ltx-pipelines/CLAUDE.md`](https://github.com/Lightricks/LTX-2/blob/main/packages/ltx-pipelines/CLAUDE.md)**, which documents: *"STG is applied to block 29 for LTX-2 and LTX-2.S, versus block 28 for LTX-2.3."*

### Gemma Version Validation

The loader validates Gemma compatibility in **[`ltx_core/loader/module_ops.py`](https://github.com/Lightricks/LTX-2/blob/main/ltx_core/loader/module_ops.py)** via `_check_gemma_version`. Both generations require matching between:

- The `gemma_version` metadata entry in the checkpoint
- The actual Gemma folder contents

LTX-2.5 checkpoints specify Gemma 4, while LTX-2.3 and early LTX-2 use Gemma 3.

---

## Practical Code Examples

### Detect Version and Load Correct Parameters

```python
from ltx_pipelines.utils.constants import detect_model_version, detect_params
from pathlib import Path

# Path to any LTX-2 family checkpoint

ckpt_path = Path("checkpoints/ltx-2.3-22b.safetensors")

# Extract version tuple

version = detect_model_version(ckpt_path)
print(f"Detected version: {version}")  # → (2, 3)

# Automatically select generation-specific defaults

params = detect_params(ckpt_path)
print(f"Steps: {params.steps}")        # → 30

print(f"STG blocks: {params.stg_blocks}")  # → [28]

```

### Conditional Logic Based on Version

```python
from ltx_pipelines.utils.constants import LTX_2_3_PARAMS, LTX_2_5_PARAMS

version = detect_model_version(ckpt_path)

if version >= (2, 5):
    print("LTX-2.5+ detected: using 20 steps, Gemma 4, CRF 18")
    # Override with 2.5-specific behavior

elif version >= (2, 3):
    print("LTX-2.3 detected: 30 steps, Gemma 3, STG block 28")
    params = LTX_2_3_PARAMS
elif version >= (2,):
    print("LTX-2.0-2.1 detected: 40 steps, Gemma 3, STG block 29")
else:
    print("Unknown/legacy checkpoint: using safest fallback defaults")

```

### Inspect Checkpoint Metadata Directly

```python
from safetensors import safe_open

with safe_open(ckpt_path, framework="pt") as f:
    metadata = f.metadata()
    print("Raw model_version:", metadata.get("model_version", "NOT FOUND"))
    print("Gemma version:", metadata.get("gemma_version", "NOT FOUND"))
    
    # All available metadata keys

    print("All keys:", list(metadata.keys()))

```

---

## Summary

- **Version detection** reads `model_version` from `.safetensors` metadata, normalizes hyphens to dots, and parses leading numeric components into comparable tuples like `(2, 3)` or `()`.
- **Empty tuple `()`** indicates missing or unparsable versions, triggering the oldest parameter fallback.
- **Unified checkpoint format** bundles transformer, VAEs, and vocoder in one file with a separate Gemma folder for both generations.
- **Key differences**: LTX-2.3 uses STG block 28 and 30 steps; LTX-2.S uses STG block 29 and 40 steps (original) or switches to Gemma 4 (2.5).
- **Source files**: [`constants.py`](https://github.com/Lightricks/LTX-2/blob/main/constants.py) for detection logic, [`helpers.py`](https://github.com/Lightricks/LTX-2/blob/main/helpers.py) for parsing, [`CLAUDE.md`](https://github.com/Lightricks/LTX-2/blob/main/CLAUDE.md) for parameter defaults, and [`README.md`](https://github.com/Lightricks/LTX-2/blob/main/README.md) for checkpoint layout.

---

## Frequently Asked Questions

### What happens if a checkpoint has no `model_version` metadata?

The detection returns an empty tuple `()`, which compares below all real versions. This triggers `LTX_2_PARAMS`—the oldest fallback defaults with 40 steps, STG block 29, and CRF 33. This behavior ensures backward compatibility with early LTX-2 checkpoints created before version metadata was standardized.

### How does the hyphen normalization affect version comparison?

Hyphens are replaced with dots before parsing, so `2.4-rc2` becomes `2.4.rc2`, then parses to `(2, 4)`. The `rc2` suffix is discarded because `parse_model_version` stops at the first non-numeric component. This means pre-release builds map to their base version's parameter set.

### Can I force specific parameters regardless of detected version?

Yes—`detect_params` is a convenience helper. You can import and instantiate parameter classes directly: `from ltx_pipelines.utils.constants import LTX_2_3_PARAMS` and use `LTX_2_3_PARAMS` explicitly. The detection system only automates what you could configure manually.

### Why do LTX-2.3 and LTX-2.5 share the "S" designation in some documentation?

The "S" (Standard) family originally referred to the base LTX-2 architecture. LTX-2.3 represents a significant generation with its own parameter set, while LTX-2.5 returns to the S lineage with upgraded components (Gemma 4, refined defaults). The unified checkpoint format masks these differences at the file level—the detection system resolves them at runtime.