How GPT-SoVITS WebUI Implements Model Weight Selection and Checkpoint Switching

The GPT-SoVITS WebUI dynamically scans checkpoint directories, populates Gradio dropdown menus with discovered .pth and .ckpt files, and switches models via dedicated change handlers that resolve paths, detect versions, and load weights into global inference objects while yielding UI state updates.

The RVC-Boss/GPT-SoVITS repository provides a Gradio-based WebUI for voice synthesis that requires seamless switching between GPT and SoVITS checkpoints. Understanding how the WebUI implements model weight selection and checkpoint switching reveals a tightly coupled system of filesystem scanning, UI state management, and runtime model instantiation.

How Checkpoint Discovery Works

The foundation of model selection lies in config.py, which defines static maps for pre-trained models and the weight-root directories. The system maintains separate lists for SoVITS and GPT weights:


# config.py – definitions of the weight roots

SoVITS_weight_root = ["SoVITS_weights", "SoVITS_weights_v2", ...]   # ← line 44‑49

GPT_weight_root    = ["GPT_weights", "GPT_weights_v2", ...]          # ← line 52‑58

The get_weights_names() function walks these directories to discover available checkpoints. It scans for files ending in .pth (SoVITS) or .ckpt (GPT), while also checking named entries in name2sovits_path and name2gpt_path:

def get_weights_names():
    SoVITS_names = []
    for key in name2sovits_path:
        if os.path.exists(name2sovits_path[key]):
            SoVITS_names.append(key)                         # ← line 88‑90

    for path in SoVITS_weight_root:
        if not os.path.exists(path):
            continue
        for name in os.listdir(path):
            if name.endswith(".pth"):
                SoVITS_names.append(f"{path}/{name}")       # ← line 95‑97

    # … same logic for GPT (lines 99‑108) …

    SoVITS_names = sorted(SoVITS_names, key=custom_sort_key) # ← line 109‑110

    return SoVITS_names, GPT_names                         # ← line 113‑114

This discovery mechanism ensures that both repository-provided models and user-added checkpoints in custom *_weights folders appear in the UI automatically.

Populating the Gradio Dropdown Menus

In webui.py, the inference tab constructs the GPT_dropdown and SoVITS_dropdown components using the sorted lists returned by config.get_weights_names():


# webui.py – inference tab (excerpt)

GPT_dropdown = gr.Dropdown(
    label=i18n("GPT模型列表"),
    choices=sorted(GPT_names, key=custom_sort_key),   # ← line 1450‑1452

    value=gpt_path,
    interactive=True,
    scale=14,
)

SoVITS_dropdown = gr.Dropdown(
    label=i18n("SoVITS模型列表"),
    choices=sorted(SoVITS_names, key=custom_sort_key), # ← line 1452‑1455

    value=sovits_path,
    interactive=True,
    scale=14,
)

A Refresh button allows users to re-scan the filesystem at runtime without restarting the application:

refresh_button = gr.Button(i18n("刷新模型路径"), variant="primary", scale=14)  # line 1459

refresh_button.click(fn=change_choices, inputs=[], outputs=[SoVITS_dropdown, GPT_dropdown])  # line 1460

The change_choices() function forwards the updated lists back to the dropdown components:

def change_choices():
    SoVITS_names, GPT_names = get_weights_names()   # ← line 116‑117

    return {"choices": SoVITS_names, "__type__": "update"}, {"choices": GPT_names, "__type__": "update"}

Switching Between Checkpoints

When a user selects a new checkpoint from either dropdown, Gradio invokes dedicated change handlers that perform the actual model loading and UI synchronization.

Resolving Model Paths and Versions

Both change_sovits_weights() and change_gpt_weights() first resolve friendly display names (e.g., "v2底模!") into actual filesystem paths. The handlers check for special characters ! or ! in the selection to identify named entries:

if "!" in sovits_path or "!" in sovits_path:
    sovits_path = name2sovits_path[sovits_path]                     # line 30‑31

For SoVITS checkpoints, the system must detect the model version to instantiate the correct class. The get_sovits_version_from_path_fast() function extracts version information (v1, v2, v3, v4) and LoRA flags directly from the filename:

version, model_version, if_lora_v3 = get_sovits_version_from_path_fast(sovits_path)  # line 33‑34

Loading SoVITS Checkpoints

The change_sovits_weights() function in GPT_SoVITS/inference_webui.py implements the complete switching logic as a generator, yielding Gradio update dictionaries to refresh UI elements progressively without blocking the main thread:

def change_sovits_weights(sovits_path, prompt_language=None, text_language=None):
    # ① Resolve named entries

    if "!" in sovits_path or "!" in sovits_path:
        sovits_path = name2sovits_path[sovits_path]                     # line 30‑31

    # ② Determine version, model‑type and LoRA flag from the filename

    version, model_version, if_lora_v3 = get_sovits_version_from_path_fast(sovits_path)  # line 33‑34

    # ③ Prepare UI state (language lists, visibility of sample‑steps, etc.) and **yield**

    #    a series of Gradio update dicts so the UI instantly reflects the new model.

    #    (lines 42‑78 – see the “yield (…)” block)

    # ④ Load the checkpoint (weights + config) and build the model instance

    dict_s2 = load_sovits_new(sovits_path)                 # line 81

    hps = DictToAttrRecursive(dict_s2["config"])           # line 82‑84

    #    – adjust version flags, instantiate the appropriate Synthesizer class

    #      (v1/v2 vs. v3/v4) (lines 92‑110)

    # ⑤ Move model to the selected device & precision

    if is_half:
        vq_model = vq_model.half().to(device)              # line 117‑119

    else:
        vq_model = vq_model.to(device)

Key implementation details include:

  • Path resolution allows readable UI labels while loading the correct file from name2sovits_path
  • Version detection determines whether to instantiate SynthesizerTrn (v1/v2) or SynthesizerTrnV3 (v3/v4)
  • UI synchronization yields update dictionaries that adjust language selectors and reveal version-specific controls like sample_steps for V3/V4 models
  • Device handling moves the loaded vq_model to the configured GPU/CPU and applies float16 precision when is_half is enabled

Loading GPT Checkpoints

The GPT switching logic follows a similar pattern but with less UI complexity. The change_gpt_weights() function updates the global gpt inference object:

def change_gpt_weights(gpt_path):
    global gpt
    # Resolve named entries (same as SoVITS)

    if "!" in gpt_path or "!" in gpt_path:
        gpt_path = name2gpt_path[gpt_path]               # line 376‑377 (in inference_webui.py)

    # Load the checkpoint (the implementation lives in process_ckpt)

    gpt = load_gpt_new(gpt_path)                         # line 381

    # Update UI (show loading indicator, enable inference button, etc.)

    return (
        {"__type__": "update", "value": i18n("模型加载中,请等待"), "interactive": False},
        {"__type__": "update", "value": True, "interactive": True},
    )

Once loaded, the global gpt variable ensures that subsequent calls to get_tts_wav() reference the currently selected checkpoint.

Programmatic Model Switching

You can trigger checkpoint switches programmatically by importing the change handlers directly from GPT_SoVITS/inference_webui.py:

from GPT_SoVITS.inference_webui import change_sovits_weights, change_gpt_weights

# Switch SoVITS checkpoint

list(change_sovits_weights("SoVITS_weights_v2/my_model.pth", prompt_language="中文", text_language="中文"))

# Switch GPT checkpoint

list(change_gpt_weights("GPT_weights_v2/my_gpt.ckpt"))

Each function returns a generator of Gradio update dictionaries; iterating over the generator forces the loading process to execute and apply UI updates.

Key Source Files and Architecture

The checkpoint switching mechanism spans several critical files in the RVC-Boss/GPT-SoVITS repository:

  • config.py – Contains get_weights_names(), SoVITS_weight_root, and GPT_weight_root definitions for filesystem scanning
  • webui.py – Constructs the GPT_dropdown and SoVITS_dropdown Gradio components and wires the refresh button to change_choices()
  • GPT_SoVITS/inference_webui.py – Implements change_sovits_weights() and change_gpt_weights() with version detection and model instantiation
  • GPT_SoVITS/inference_webui_fast.py – Mirrors the switching logic for the low-latency inference path
  • process_ckpt.py – Provides load_sovits_new() and load_gpt_new() for low-level checkpoint deserialization

Summary

  • Filesystem discovery via config.get_weights_names() scans SoVITS_weight_root and GPT_weight_root for .pth and .ckpt files, merging results with named path mappings
  • Dynamic UI population uses change_choices() to refresh Gradio dropdowns without requiring application restarts
  • Path resolution handles both direct file paths and friendly display names containing ! or ! characters through name2sovits_path and name2gpt_path lookups
  • Version-aware loading detects model architecture (v1/v2 vs. v3/v4) from filenames and instantiates the appropriate SynthesizerTrn class
  • Generator-based updates allow change_sovits_weights() to yield progressive UI state changes while loading large models into the global vq_model and gpt objects

Frequently Asked Questions

How does the WebUI detect new checkpoints added after startup?

The Refresh button in the inference tab triggers change_choices(), which re-executes get_weights_names() to scan the filesystem again. This updates both dropdown menus with any new .pth or .ckpt files placed in the weight directories since the last scan, eliminating the need to restart the Gradio server.

What file extensions does GPT-SoVITS expect for model weights?

According to the source code in config.py, SoVITS checkpoints must use the .pth extension while GPT checkpoints use the .ckpt extension. The get_weights_names() function explicitly filters for these extensions when building the dropdown choices lists.

Why does change_sovits_weights() use a generator pattern?

The function yields multiple Gradio update dictionaries (with "__type__": "update") during execution rather than returning a single value. This allows the UI to display loading states, adjust language selectors, and toggle visibility of version-specific controls (like sample_steps for V3/V4) immediately, while the heavyweight model loading continues in the background without blocking the interface.

How does the system handle different model versions (v1, v2, v3, v4)?

The get_sovits_version_from_path_fast() function parses the selected filename to extract version strings. Based on this detection, change_sovits_weights() instantiates the appropriate synthesizer class—SynthesizerTrn for v1/v2 models or SynthesizerTrnV3 for v3/v4 models—and adjusts global configuration flags accordingly.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →