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

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 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 (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 (lines 21-29) maps symbolic names to concrete wrapper implementations:

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:

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 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:

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 supports InferenceMode values including "text", "choice", "regex", and "json".

LangChain Backend

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

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.

GLiNER 2 Backend

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

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 handles GLiNER 2's specific extraction interface.

HuggingFace Backend

For zero-shot classification using HuggingFace pipelines:

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 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:

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:


# 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 standardizes this metadata collection across DSPy, LangChain, and other backends.

Summary

  • Unified API: The ModelWrapper abstraction in 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) to control inference_mode, strict parsing, batch_size, and backend-specific kwargs.
  • Automatic Detection: The ModelType enum in 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, outlines_.py, langchain_.py, gliner_.py, and 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 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.

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 →