# How nGPT Manages and Switches Between Multiple LLM Provider Configurations Dynamically

> Discover how nGPT dynamically manages and switches LLM provider configurations via CLI flags environment variables or config indices Seamlessly integrate different LLM providers without code changes

- Repository: [nazDridoy/ngpt](https://github.com/nazdridoy/ngpt)
- Tags: internals
- Published: 2026-03-07

---

**nGPT stores multiple LLM provider configurations in a JSON file and dynamically selects between them at runtime using CLI flags, environment variables, or config indices, enabling seamless provider switching without code changes.**

The open-source project `nazdridoy/ngpt` implements a flexible configuration system that allows users to manage and switch between multiple LLM provider configurations dynamically. Rather than hardcoding provider details, nGPT maintains a portable configuration file that supports multiple provider entries, enabling users to switch between OpenAI, local Ollama instances, or other compatible APIs through simple command-line arguments.

## Storing Multiple LLM Provider Configurations

nGPT persists provider settings in a JSON configuration file located at `~/.config/ngpt/ngpt.conf` (or a custom path specified via `--config`). The file accepts either a single configuration object or an array of provider configurations, making it possible to define multiple LLM backends simultaneously.

Each provider entry requires four key fields:

```json
[
  {
    "api_key": "sk-openai-123",
    "base_url": "https://api.openai.com/v1/",
    "provider": "OpenAI",
    "model": "gpt-4o"
  },
  {
    "api_key": "local-key",
    "base_url": "http://127.0.0.1:11434/v1/",
    "provider": "Ollama",
    "model": "llama3"
  }
]

```

This structure allows nGPT to manage and switch between multiple LLM provider configurations dynamically by treating each array element as a distinct, selectable backend.

## Loading and Selecting Configurations Dynamically

The configuration management logic resides in [`ngpt/core/config.py`](https://github.com/nazdridoy/ngpt/blob/main/ngpt/core/config.py), which exposes two primary functions for handling provider selection at runtime.

### Loading All Provider Configurations

The `load_configs()` function (lines 22-38) reads the JSON configuration file and normalizes the output to a list of dictionaries. This function handles both single-object and array-based configurations, ensuring backward compatibility while enabling multi-provider setups.

```python
def load_configs(custom_path: Optional[str] = None) -> List[Dict[str, Any]]:
    config_path = get_config_path(custom_path)
    
    if config_path.exists():
        with open(config_path, "r") as f:
            file_configs = json.load(f)
            # Accept both a single dict and a list of dicts

            if isinstance(file_configs, dict):
                configs = [file_configs]
            else:
                configs = file_configs
    return configs

```

### Selecting a Specific Provider by Name or Index

The `load_config()` function (lines 22-62) implements the dynamic selection logic. It accepts either a `provider` name or a `config_index` parameter to determine which configuration entry to activate.

When `--provider` is specified, the function performs a case-insensitive search through the configuration list. If multiple entries match the provider name, nGPT prompts the user to disambiguate. If no match exists, it falls back to the entry at the specified index (defaulting to 0).

```python
def load_config(..., config_index: int = 0, provider: Optional[str] = None) -> Dict[str, Any]:
    configs = load_configs(custom_path)
    
    if provider:
        matching = [c for c in configs if c.get("provider", "").lower() == provider.lower()]
        # Handle no match / multiple matches / single match logic

        ...
    
    if config_index < 0 or config_index >= len(configs):
        config_index = 0
        
    config = configs[config_index]
    return config

```

### Environment Variable Overrides

After selecting a configuration, `load_config()` applies environment variable overrides to enable dynamic credential management without modifying the configuration file. The function checks for `OPENAI_API_KEY`, `OPENAI_BASE_URL`, and `OPENAI_MODEL`, updating the selected configuration dictionary with any present values.

```python

# Environment-variable overrides

for env_var, key in env_mapping.items():
    if env_var in os.environ:
        if key == "api_key" or os.environ[env_var]:
            config[key] = os.environ[env_var]

```

## CLI Priority Resolution and Client Initialization

The [`ngpt/cli/handlers/client_handler.py`](https://github.com/nazdridoy/ngpt/blob/main/ngpt/cli/handlers/client_handler.py) file orchestrates the final configuration resolution and client instantiation, ensuring that user preferences from various sources merge correctly.

### Resolving Configuration Priority

The `process_config_selection()` function (lines 17-45) establishes a clear precedence hierarchy for configuration sources:

1. **Explicit CLI flags** (`--provider`, `--config-index`) take highest priority
2. **CLI configuration file** ([`ngpt-cli.conf`](https://github.com/nazdridoy/ngpt/blob/main/ngpt-cli.conf)) values apply when CLI flags are absent
3. **Environment variables** override file-based settings
4. **Main configuration file** ([`ngpt.conf`](https://github.com/nazdridoy/ngpt/blob/main/ngpt.conf)) provides defaults

This function also enforces mutual exclusivity between `--provider` and `--config-index`, exiting with an error if both are specified simultaneously.

```python
def process_config_selection(args, cli_config):
    effective_provider = args.provider
    effective_config_index = args.config_index
    
    # CLI-config is used only when the user didn't set the flag explicitly

    if '--provider' not in sys.argv and 'provider' in cli_config:
        effective_provider = cli_config['provider']
    if '--config-index' not in sys.argv and 'config-index' in cli_config:
        effective_config_index = cli_config['config-index']
    
    # Mutually exclusive check

    if effective_config_index != 0 and effective_provider:
        sys.exit(2)   # error reported to user

    return effective_provider, effective_config_index

```

### Runtime Overrides and Client Instantiation

The `initialize_client()` function (lines 49-80) completes the dynamic provider switching workflow. It loads the selected configuration, applies any runtime CLI overrides (`--api-key`, `--base-url`, `--model`), and instantiates the `NGPTClient` class from [`ngpt/api/client.py`](https://github.com/nazdridoy/ngpt/blob/main/ngpt/api/client.py).

Because `load_config()` returns a fresh dictionary on each invocation, the CLI can switch providers dynamically between commands without restarting the process or modifying persistent files.

```python
def initialize_client(args, cli_config):
    eff_provider, eff_index = process_config_selection(args, cli_config)
    active_config = load_config(args.config, eff_index, eff_provider)
    
    # CLI overrides

    if args.api_key is not None:
        active_config["api_key"] = args.api_key
    if args.base_url is not None:
        active_config["base_url"] = args.base_url
    if args.model is not None:
        active_config["model"] = args.model
        
    client = NGPTClient(
        api_key=active_config.get("api_key"),
        base_url=active_config.get("base_url"),
        provider=active_config.get("provider"),
        model=active_config.get("model")
    )
    return client, active_config

```

## Practical Examples for Dynamic Provider Switching

The following examples demonstrate how to manage and switch between multiple LLM provider configurations dynamically using nGPT's CLI interface.

### Define Multiple Providers in the Configuration File

Create or edit `~/.config/ngpt/ngpt.conf` to include multiple provider entries:

```json
[
  {
    "api_key": "sk-openai-abc123",
    "base_url": "https://api.openai.com/v1/",
    "provider": "OpenAI",
    "model": "gpt-4o"
  },
  {
    "api_key": "ollama-local",
    "base_url": "http://localhost:11434/v1/",
    "provider": "Ollama",
    "model": "llama3.1"
  },
  {
    "api_key": "azure-key",
    "base_url": "https://my-resource.openai.azure.com/openai/deployments/gpt-4/",
    "provider": "Azure",
    "model": "gpt-4"
  }
]

```

### Switch Providers by Name

Use the `--provider` flag to dynamically select a specific configuration without modifying the config file:

```bash
ngpt --provider Ollama "Explain quantum computing in simple terms"

```

nGPT searches the configuration array for a matching provider name (case-insensitive) and instantiates the client with the corresponding base URL and API key.

### Switch Providers by Index

Reference configurations directly by their position in the array using `--config-index`:

```bash
ngpt --config-index 2 "Generate a SQL query for user analytics"

```

This selects the third entry (index 2) from the configuration list, useful when provider names are ambiguous or when scripting nGPT with numeric identifiers.

### Override Specific Fields at Runtime

Combine provider selection with field-level overrides to modify behavior without editing persistent files:

```bash
ngpt --provider OpenAI --model gpt-3.5-turbo "Summarize this article" < article.txt

```

The `--api-key`, `--base-url`, and `--model` flags override values from the selected configuration, enabling temporary switches to different models within the same provider.

## Summary

nGPT manages and switches between multiple LLM provider configurations dynamically through a layered configuration system:

- **JSON-based storage**: Provider credentials and endpoints are stored as an array in [`ngpt.conf`](https://github.com/nazdridoy/ngpt/blob/main/ngpt.conf), supporting multiple simultaneous configurations
- **Flexible selection**: The `load_config()` function in [`ngpt/core/config.py`](https://github.com/nazdridoy/ngpt/blob/main/ngpt/core/config.py) enables selection by provider name (`--provider`) or array index (`--config-index`)
- **Priority resolution**: `process_config_selection()` in [`ngpt/cli/handlers/client_handler.py`](https://github.com/nazdridoy/ngpt/blob/main/ngpt/cli/handlers/client_handler.py) implements a clear precedence hierarchy: CLI flags > CLI config > environment variables > main config
- **Runtime overrides**: Individual fields (API key, base URL, model) can be overridden via CLI arguments or environment variables without modifying configuration files
- **Dynamic instantiation**: The `initialize_client()` function creates fresh `NGPTClient` instances for each invocation, enabling provider switching without process restarts

## Frequently Asked Questions

### How does nGPT handle multiple providers with the same name?

When multiple configurations share the same provider name and you select via `--provider <name>`, nGPT identifies all matching entries and prompts you to choose the specific instance you want to use. If you prefer non-interactive selection, use `--config-index` to specify the exact array position instead of relying on name matching.

### Can I switch providers without editing the configuration file?

Yes. nGPT supports complete dynamic provider switching through command-line flags. Use `--provider` to select by name or `--config-index` to select by position. Additionally, you can override specific connection parameters using `--api-key`, `--base-url`, and `--model` flags to temporarily redirect to different endpoints without modifying [`ngpt.conf`](https://github.com/nazdridoy/ngpt/blob/main/ngpt.conf).

### What happens if both `--provider` and `--config-index` are specified?

nGPT treats these flags as mutually exclusive. The `process_config_selection()` function in [`ngpt/cli/handlers/client_handler.py`](https://github.com/nazdridoy/ngpt/blob/main/ngpt/cli/handlers/client_handler.py) explicitly checks for simultaneous usage of both flags and exits with error code 2 if detected. This prevents ambiguity in configuration selection and ensures predictable behavior.

### How do environment variables interact with the configuration file?

Environment variables act as overrides to the selected configuration. After `load_config()` selects a provider entry (by name or index), it checks for `OPENAI_API_KEY`, `OPENAI_BASE_URL`, and `OPENAI_MODEL` environment variables. If present, these values replace the corresponding fields in the configuration dictionary before the client is instantiated, allowing sensitive credentials to be injected securely without storing them in files.