# How to Configure DSPy, Outlines, LangChain, and GLiNER 2 Backends in Sieves

> Easily configure DSPy Outlines LangChain and GLiNER 2 backends in Sieves. Swap model providers with one line of code and maintain identical task logic. Explore flexible LLM integration.

- Repository: [Mantis/sieves](https://github.com/mantisai/sieves)
- Tags: how-to-guide
- Published: 2026-03-06

---

**Sieves provides a unified `ModelWrapper` abstraction that lets you swap between DSPy, Outlines, LangChain, GLiNER 2, and HuggingFace backends by changing a single model instantiation line while keeping your task logic identical.**

The `mantisai/sieves` library decouples task definitions from model implementations through a consistent configuration API. Whether you need **DSPy** for chain-of-thought reasoning, **Outlines** for structured JSON generation, or **GLiNER 2** for zero-shot NER, you configure each backend using the `ModelSettings` class and `ModelType` enum system implemented in the model wrapper layer.

## Core Architecture

The backend configuration system in Sieves rests on three foundational components defined in `sieves/model_wrappers/`:

### ModelWrapper Base Class

The abstract **`ModelWrapper`** class in [`sieves/model_wrappers/core.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/core.py) handles initialization, inference-mode selection, few-shot support, token-usage tracking, and strict parsing across all backends. Every concrete wrapper inherits from this base, ensuring consistent behavior for batching, error handling, and metadata extraction.

### ModelSettings Configuration

The **`ModelSettings`** dataclass in [`sieves/model_wrappers/types.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/types.py) (lines 8-25) acts as the configuration container for any backend. It accepts:

- `inference_mode`: String matching the backend's supported modes (e.g., `"chain_of_thought"`, `"json"`)
- `strict`: Boolean controlling whether parsing failures raise errors or return `None`
- `batch_size`: Integer for controlling execution batching
- `inference_kwargs`: Dictionary of backend-specific generation parameters
- `init_kwargs`: Dictionary of backend-specific initialization parameters

### ModelType Enumeration

The **`ModelType`** enum in [`sieves/model_wrappers/model_type.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/model_type.py) (lines 21-29) maps symbolic names to concrete wrapper implementations:

- `dspy` → [`sieves/model_wrappers/dspy_.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/dspy_.py)
- `outlines` → [`sieves/model_wrappers/outlines_.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/outlines_.py)
- `langchain` → [`sieves/model_wrappers/langchain_.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/langchain_.py)
- `gliner` → [`sieves/model_wrappers/gliner_.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/gliner_.py)
- `huggingface` → [`sieves/model_wrappers/huggingface_.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/huggingface_.py)

Sieves determines the appropriate wrapper automatically via `ModelType.get_model_type()` when you pass a model instance to a task.

## Backend Configuration Examples

Each backend follows the same three-step pattern: instantiate the native model, optionally configure `ModelSettings`, and pass both to a Sieves task.

### DSPy Backend

Configure DSPy for chain-of-thought reasoning or standard completion via `inference_mode`:

```python
import dspy
from sieves import tasks, ModelSettings

# 1. Create native DSPy model

dspy_model = dspy.LM("openai/gpt-4o-mini", api_key="your-key")

# 2. Configure settings for chain-of-thought with non-strict parsing

dspy_settings = ModelSettings(
    inference_mode="chain_of_thought",  # Maps to DSPy.InferenceMode.chain_of_thought

    strict=False,
    inference_kwargs={"max_new_tokens": 256}
)

# 3. Initialize task

sentiment_task = tasks.SentimentAnalysis(model=dspy_model, model_settings=dspy_settings)

# Execute

from sieves import Doc
doc = Doc(text="I love this product!")
result = sentiment_task([doc])[0]
print(result.results[sentiment_task.id])

```

The `DSPy` wrapper in [`sieves/model_wrappers/dspy_.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/dspy_.py) implements `build_executable()` to handle async execution and token usage extraction from DSPy completions.

### Outlines Backend

Outlines excels at structured generation with JSON, regex, or choice constraints:

```python
import outlines
from transformers import AutoModelForCausalLM, AutoTokenizer
from sieves import tasks, ModelSettings

# 1. Load HuggingFace checkpoint into Outlines

model_name = "HuggingFaceTB/SmolLM2-135M-Instruct"
outlines_model = outlines.models.from_transformers(
    AutoModelForCausalLM.from_pretrained(model_name),
    AutoTokenizer.from_pretrained(model_name),
)

# 2. Request JSON output mode

outlines_settings = ModelSettings(inference_mode="json", strict=True)

# 3. Build task

sentiment_task = tasks.SentimentAnalysis(
    model=outlines_model, 
    model_settings=outlines_settings
)

# Execute

doc = Doc(text="The movie was boring.")
sentiment_task([doc])
print(doc.results[sentiment_task.id])

```

The `Outlines` wrapper in [`sieves/model_wrappers/outlines_.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/outlines_.py) supports `InferenceMode` values including `"text"`, `"choice"`, `"regex"`, and `"json"`.

### LangChain Backend

LangChain integration works with any chat model following the LangChain interface:

```python
from langchain_openai import ChatOpenAI
from sieves import tasks, ModelSettings

# 1. Initialize LangChain model

langchain_model = ChatOpenAI(model="gpt-4o-mini", api_key="your-key")

# 2. Default settings work for most tasks

langchain_settings = ModelSettings(strict=True)

# 3. Build task

sentiment_task = tasks.SentimentAnalysis(
    model=langchain_model, 
    model_settings=langchain_settings
)

# Execute

doc = Doc(text="What a wonderful day!")
sentiment_task([doc])
print(doc.results[sentiment_task.id])

```

Implementation details reside in [`sieves/model_wrappers/langchain_.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/langchain_.py).

### GLiNER 2 Backend

GLiNER 2 specializes in zero-shot named entity recognition and classification:

```python
import gliner2
from sieves import tasks, ModelSettings

# 1. Load GLiNER2 model

gliner_model = gliner2.GLiNER2.from_pretrained("fastino/gliner2-base-v1")

# 2. Standard settings (extraction mode only)

gliner_settings = ModelSettings(strict=True)

# 3. Build NER task with custom entity types

ner_task = tasks.NER(
    entities=["PERSON", "LOC"],
    model=gliner_model,
    model_settings=gliner_settings,
)

# Execute

doc = Doc(text="Barack Obama was born in Hawaii.")
ner_task([doc])
print(doc.results[ner_task.id])

```

The wrapper in [`sieves/model_wrappers/gliner_.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/gliner_.py) handles GLiNER 2's specific extraction interface.

### HuggingFace Backend

For zero-shot classification using HuggingFace pipelines:

```python
import transformers
from sieves import tasks, ModelSettings

# 1. Build zero-shot pipeline

hf_pipeline = transformers.pipeline(
    "zero-shot-classification",
    model="MoritzLaurer/xtremedistil-l6-h256-zeroshot-v1.1-all-33",
)

# 2. Configure (pipelines never raise parsing errors)

hf_settings = ModelSettings(strict=False)

# 3. Create classification task

clf_task = tasks.Classification(
    labels=["positive", "negative"],
    model=hf_pipeline,
    model_settings=hf_settings,
)

# Execute

doc = Doc(text="The service was terrible.")
clf_task([doc])
print(doc.results[clf_task.id])

```

See [`sieves/model_wrappers/huggingface_.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/huggingface_.py) for the pipeline integration logic.

## Advanced Configuration Patterns

### Switching Inference Modes

Each wrapper exposes specific `InferenceMode` enums. For example, DSPy supports `"chain_of_thought"` while Outlines supports `"json"`. Pass these via `ModelSettings.inference_mode`:

```python
settings = ModelSettings(
    inference_mode="json",  # Valid for Outlines, invalid for GLiNER

    strict=True,
    batch_size=4
)

```

### Backend Agnostic Task Definitions

Because tasks accept any `ModelWrapper` compatible instance, you can swap backends without touching task logic:

```python

# Simply change the model line; task code remains identical

# model = dspy.LM(...)           # For DSPy

# model = outlines.models...     # For Outlines

# model = ChatOpenAI(...)        # For LangChain

task = tasks.SentimentAnalysis(model=model, model_settings=settings)

```

### Token Usage and Metadata

All wrappers automatically extract token usage and raw outputs, storing them in `Doc.meta` after execution. The `ModelWrapper` base class in [`sieves/model_wrappers/core.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/core.py) standardizes this metadata collection across DSPy, LangChain, and other backends.

## Summary

- **Unified API**: The `ModelWrapper` abstraction in [`sieves/model_wrappers/core.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/core.py) provides consistent initialization, execution, and error handling across all backends.
- **Configuration Layer**: Use `ModelSettings` (defined in [`sieves/model_wrappers/types.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/types.py)) to control `inference_mode`, `strict` parsing, `batch_size`, and backend-specific kwargs.
- **Automatic Detection**: The `ModelType` enum in [`sieves/model_wrappers/model_type.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/model_type.py) maps model instances to correct wrappers automatically via `get_model_type()`.
- **Backend Files**: Concrete implementations reside in [`dspy_.py`](https://github.com/mantisai/sieves/blob/main/dspy_.py), [`outlines_.py`](https://github.com/mantisai/sieves/blob/main/outlines_.py), [`langchain_.py`](https://github.com/mantisai/sieves/blob/main/langchain_.py), [`gliner_.py`](https://github.com/mantisai/sieves/blob/main/gliner_.py), and [`huggingface_.py`](https://github.com/mantisai/sieves/blob/main/huggingface_.py) under `sieves/model_wrappers/`.
- **Zero Code Changes**: Swap between DSPy, Outlines, LangChain, GLiNER 2, and HuggingFace by changing only the model instantiation line; task definitions remain unchanged.

## Frequently Asked Questions

### What is the difference between strict and non-strict mode in Sieves?

**Strict mode** (`strict=True`) forces the wrapper to raise an exception when structured output parsing fails, while **non-strict mode** returns `None` for failed parses and continues processing. Configure this in `ModelSettings.strict` when initializing any task. This setting is particularly important for production pipelines where you want to ensure data quality (strict) versus exploratory workflows where partial failures are acceptable.

### Can I use different inference modes for the same backend?

Yes. Each wrapper defines its supported `InferenceMode` enum in its implementation file. For example, the Outlines wrapper supports `"text"`, `"choice"`, `"regex"`, and `"json"` modes, while the DSPy wrapper supports standard generation and `"chain_of_thought"`. Pass the desired mode as a string to `ModelSettings.inference_mode`, and the wrapper's `build_executable()` method configures the backend accordingly.

### How does Sieves handle token usage tracking across backends?

The abstract `ModelWrapper` class in [`sieves/model_wrappers/core.py`](https://github.com/mantisai/sieves/blob/main/sieves/model_wrappers/core.py) defines token extraction methods that each concrete wrapper implements for its specific backend. After execution, token counts appear in `Doc.meta` alongside raw model outputs. This works uniformly whether you're using DSPy, LangChain, or HuggingFace pipelines, allowing consistent cost monitoring across different model providers.

### Is it possible to mix multiple backends in the same pipeline?

Yes. Because each task carries its own `model` and `model_settings`, you can chain tasks using different backends in a single pipeline. For example, use GLiNER 2 for NER extraction in one task, then pass the documents to a DSPy-powered classification task. Each task independently manages its wrapper initialization through `ModelType.get_model_type()`, keeping the backends isolated despite operating on the same `Doc` objects.