In Format Handling, we learned how langextract cleans up the messy output from a Language Model (LLM) to give us nice, structured data. But before we can parse the output, we need to send the input to the right AI model.
Every AI provider works differently.
client.chat.completions.create().model.generate_content().
If you were writing this from scratch, your code would look like a messy pile of if/else statements:
# The "Messy" Way (Don't do this!)
if model_name == "gpt-4":
import openai
# ... call OpenAI ...
elif model_name == "gemini-1.5":
import google.generativeai
# ... call Google ...
This is hard to maintain. If you want to switch from GPT-4 to Gemini to save money, you have to rewrite your code.
langextract solves this with two concepts:
"gemini-2.5-flash") and decides which code handles it.You want to test if your prompt works better on a local model (Ollama) or a cloud model (Gemini).
With langextract, you just change one string:
import langextract as lx
# Use a local model
lx.extract(
text="Hello",
examples=examples, # Assume examples exist
model_id="ollama/llama3"
)
# Switch to Google Cloud instantly
lx.extract(
text="Hello",
examples=examples,
model_id="gemini-2.5-flash"
)
Explanation: You didn't change any logic. You didn't import different libraries. The Factory handled the switch.
The model_id is a string identifier. It usually looks like "provider-model_name" or just "model_name".
"gpt-4o""gemini-2.0-flash""ollama/mistral"
The router holds a registry of Regex Patterns. When you pass "gemini-2.5-flash", the router checks its list:
^gpt.*? No.^gemini.*? Yes! -> Route to the GeminiProvider class.Once the router picks the class, the Factory instantiates it. It creates the object and finds your API keys in your environment variables so you don't have to pass them around.
Here is what happens when you request a specific model ID:
Let's look at how langextract implements this magic.
langextract/providers/router.py)
The router uses Lazy Loading. It doesn't import the heavy libraries (like google-generativeai or openai) until you actually ask for them. This keeps your application startup fast.
Here is a simplified look at how the router finds the right class:
# From langextract/providers/router.py
def resolve(model_id: str):
# Loop through all registered patterns
for entry in _entries:
# If the regex matches the model_id string...
if any(pattern.search(model_id) for pattern in entry.patterns):
# ... load and return the specific Provider Class
return entry.loader()
langextract/factory.py)The Factory's job is to make things easy for you. One of its best features is Auto-Configuration.
If you use Gemini, it automatically looks for GEMINI_API_KEY or LANGEXTRACT_API_KEY in your environment variables.
# From langextract/factory.py
def _kwargs_with_environment_defaults(model_id, kwargs):
# If the user didn't provide an explicit API key...
if "api_key" not in resolved:
# Check standard environment variables
if "gemini" in model_id.lower():
resolved["api_key"] = os.getenv("GEMINI_API_KEY")
return resolved
Explanation: This means you rarely need to pass api_key="..." in your code. Just set it in your terminal, and the Factory finds it.
What if you want to use a model provider that langextract doesn't support yet? Or a custom internal API?
The system is designed as a Plugin Architecture. You can register your own providers without changing the core library code.
langextract comes with a powerful script to create your own provider plugin in seconds.
# In your terminal
python scripts/create_provider_plugin.py MyCustomLLM --patterns "^custom-.*"
This script generates a full Python package folder structure for you!
provider.py (where you write your API logic).tests.pyproject.toml so langextract can automatically discover your plugin when it is installed.
Sometimes, you might have two providers that claim the same model ID, or you want to pass very specific settings. You can bypass the string magic using ModelConfig.
from langextract import factory
# Precise control over creation
config = factory.ModelConfig(
model_id="my-custom-model",
provider="MyCustomProvider", # Force this specific class
provider_kwargs={"temperature": 0.7}
)
model = factory.create_model(config)
The Provider Routing & Factory system acts as a universal adapter. It decouples what you want to do (extract data) from who is doing it (OpenAI, Google, etc.).
Now that we have a configured model and we know how to talk to it, we face a new problem: Limit Constraints. What happens if the text you want to analyze is larger than the model's context window?
Generated by Code IQ