# How to Configure Custom Model Providers in LangExtract: A Complete Guide

> Configure custom model providers in LangExtract by implementing BaseLanguageModel, registering with lx.providers.registry.register, and exposing via pyproject.toml entry points. Learn more!

- Repository: [Google/langextract](https://github.com/google/langextract)
- Tags: how-to-guide
- Published: 2026-02-16

---

**You configure custom model providers in LangExtract by implementing the `BaseLanguageModel` interface, registering your provider with the `@lx.providers.registry.register` decorator, and exposing it via a [`pyproject.toml`](https://github.com/google/langextract/blob/main/pyproject.toml) entry point under the `langextract.providers` group.**

LangExtract, Google's extensible extraction framework, uses a **plugin-based architecture** to support diverse model backends. Whether you need to integrate a proprietary API or a local inference server, understanding how to configure custom model providers lets you extend LangExtract without modifying core source code.

## Understanding LangExtract's Provider Architecture

LangExtract discovers and loads model backends through two distinct layers that work together to resolve model IDs at runtime.

### Provider Discovery Layer

The **discovery layer** builds a registry mapping provider names to import specifications. Located in [`langextract/plugins.py`](https://github.com/google/langextract/blob/main/langextract/plugins.py), this module exposes `available_providers()` and `get_provider_class()` to enumerate built-in, optional, and third-party providers.

Built-in providers like `gemini` and `ollama` reside in the `_BUILTINS` dictionary, while optional providers (e.g., `openai`) live in `_OPTIONAL_BUILTINS` and load only when dependencies are present. Third-party packages expose providers via entry points under the `langextract.providers` group, which `_discovered()` merges into the registry. The `allow_override` flag controls whether third-party plugins can replace built-in implementations.

### Runtime Registration Layer

The **runtime layer** in [`langextract/providers/router.py`](https://github.com/google/langextract/blob/main/langextract/providers/router.py) handles lazy resolution of model IDs to concrete provider classes. This layer maintains a list of `_Entry` objects that map regex patterns to loader functions, avoiding circular imports by deferring class imports until needed.

Key functions include `register()` for immediate registration, `register_lazy()` for deferred loading via import-path strings, `resolve()` for pattern-based matching, and `resolve_provider()` for name-based lookup. When `resolve()` scans entries sorted by priority, it returns the first provider whose regex matches the supplied model ID.

## Step-by-Step Guide to Configure Custom Model Providers

Follow this workflow to integrate a custom backend into LangExtract.

### Step 1: Scaffold Your Provider Package

Use the provided generator script to create a compliant package structure:

```bash
python scripts/create_provider_plugin.py MyProvider --with-schema

```

This creates `langextract_myprovider/` with [`provider.py`](https://github.com/google/langextract/blob/main/provider.py), [`schema.py`](https://github.com/google/langextract/blob/main/schema.py), and test files, plus a [`pyproject.toml`](https://github.com/google/langextract/blob/main/pyproject.toml) template.

### Step 2: Implement the Provider Class

Inherit from `lx.inference.BaseLanguageModel` and implement the `infer` method. Store configuration in `__init__`:

```python

# langextract_myprovider/provider.py

import os
import langextract as lx

@lx.providers.registry.register(r'^mycorp', priority=10)
class MyProviderLanguageModel(lx.inference.BaseLanguageModel):
    """Custom provider integrating MyCorp's inference API."""

    def __init__(self, model_id: str, api_key: str = None, **kwargs):
        super().__init__()
        self.model_id = model_id
        self.api_key = api_key or os.getenv("MYCORP_API_KEY")
        
    def infer(self, batch_prompts, **kwargs):
        for prompt in batch_prompts:
            # Replace with actual API integration

            result = f"Processed: {prompt[:50]}..."
            yield [lx.inference.ScoredOutput(score=1.0, output=result)]

```

### Step 3: Register Model ID Patterns

The `@lx.providers.registry.register` decorator accepts a regex pattern and priority. The router uses this to match model IDs starting with your prefix. Higher priority values take precedence when multiple patterns match.

For deferred loading to avoid heavy imports at startup, use `register_lazy`:

```python
from langextract import providers

providers.registry.register_lazy(
    r'^mycorp',
    target="langextract_myprovider.provider:MyProviderLanguageModel",
    priority=10,
)

```

### Step 4: Configure the Entry Point

Expose your provider via [`pyproject.toml`](https://github.com/google/langextract/blob/main/pyproject.toml) so `langextract.plugins` can discover it:

```toml
[project.entry-points."langextract.providers"]
myprovider = "langextract_myprovider.provider:MyProviderLanguageModel"

```

Install in editable mode to test changes:

```bash
pip install -e .

```

### Step 5: Load and Test Your Provider

Explicitly load plugins before use (automatic in production via `factory.create_model`, but manual loading helps in tests):

```python
import langextract as lx

lx.providers.load_plugins_once()

config = lx.factory.ModelConfig(
    model_id="mycorp-xyz-001",
    provider="MyProviderLanguageModel",
    provider_kwargs={"api_key": "YOUR_API_KEY"},
)

model = lx.factory.create_model(config)

for output in model.infer(["Extract entities from this text"]):
    print(output[0].output)

```

## Implementing Schema Support for Structured Output

To enable structured extraction, subclass `lx.schema.BaseSchema` and expose it via `get_schema_class()`:

```python

# langextract_myprovider/schema.py

import langextract as lx

class MyProviderSchema(lx.schema.BaseSchema):
    """Schema defining structured output format."""

    @classmethod
    def from_examples(cls, examples_data, attribute_suffix="_attributes"):
        return cls({"type": "object", "properties": {}})

    def to_provider_config(self):
        return {"response_schema": self._schema_dict}

    @property
    def supports_strict_mode(self) -> bool:
        return True

```

Update the provider to reference this class:

```python
class MyProviderLanguageModel(lx.inference.BaseLanguageModel):
    @classmethod
    def get_schema_class(cls):
        return MyProviderSchema

```

LangExtract forwards schema configuration through `provider_kwargs` when instantiating the model.

## Resolving Providers at Runtime

The router resolves providers through two mechanisms:

**Pattern Matching**: `resolve(model_id)` scans registered entries by priority, returning the first provider whose regex matches the model ID. For example, `mycorp-xyz-001` matches the pattern `^mycorp`.

**Name Matching**: `resolve_provider(provider_name)` searches for exact matches or class name containment, allowing you to specify `myprovider` or `MyProviderLanguageModel` interchangeably.

Both functions raise `InferenceConfigError` when resolution fails, indicating whether you need to install a missing plugin or correct the provider name.

## Summary

- **LangExtract uses a two-layer plugin system**: discovery via [`langextract/plugins.py`](https://github.com/google/langextract/blob/main/langextract/plugins.py) and runtime resolution via [`langextract/providers/router.py`](https://github.com/google/langextract/blob/main/langextract/providers/router.py).
- **Register providers** using `@lx.providers.registry.register` with regex patterns and priorities, or `register_lazy` for deferred loading.
- **Expose providers** via [`pyproject.toml`](https://github.com/google/langextract/blob/main/pyproject.toml) entry points under the `langextract.providers` group.
- **Implement structured output** by subclassing `lx.schema.BaseSchema` and exposing it through `get_schema_class()`.
- **Test your integration** by calling `lx.providers.load_plugins_once()` and creating models via `lx.factory.ModelConfig` and `lx.factory.create_model`.

## Frequently Asked Questions

### What is the difference between provider discovery and runtime registration?

Provider discovery, handled in [`langextract/plugins.py`](https://github.com/google/langextract/blob/main/langextract/plugins.py), builds a static map of provider names to import specifications using entry points and built-in dictionaries. Runtime registration, managed in [`langextract/providers/router.py`](https://github.com/google/langextract/blob/main/langextract/providers/router.py), maintains a dynamic registry of regex patterns to loader functions that resolve specific model IDs to provider classes lazily, avoiding circular imports and heavy dependencies until actually needed.

### How do I avoid circular imports when registering my provider?

Use the `register_lazy` function instead of the `@register` decorator. This accepts a string import path (e.g., `"langextract_myprovider.provider:MyProviderLanguageModel"`) and defers the actual import until the router first attempts to resolve a matching model ID. This pattern keeps your provider module lightweight and prevents import cycles during LangExtract initialization.

### Can I override built-in providers like Gemini or Ollama with my custom implementation?

By default, LangExtract prevents third-party plugins from overriding built-in providers to maintain backwards compatibility. The `allow_override` parameter in `available_providers()` controls this behavior. While the default setting protects built-ins, you can technically enable overrides if you modify the discovery logic, though this is generally discouraged to avoid breaking core functionality.

### How does LangExtract match a model ID to my custom provider?

The router in [`langextract/providers/router.py`](https://github.com/google/langextract/blob/main/langextract/providers/router.py) matches model IDs using the regex pattern you supplied during registration. When you call `resolve(model_id)`, the router iterates through registered entries sorted by priority (highest first) and returns the first provider whose pattern regex matches the supplied model ID. For example, if you register with `r'^mycorp'`, any model ID starting with "mycorp" will resolve to your provider.