# How LangExtract Handles Schema Constraints with Gemini Models: A Deep Dive into Structured Output

> Discover how LangExtract enforces schema constraints with Gemini models. Learn to automatically generate JSON schemas and validate Gemini's structured output for cleaner data extraction.

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

---

**LangExtract automatically generates JSON schemas from example extractions and injects them into Gemini API calls via `response_schema` and `response_mime_type="application/json"`, while validating that output formatting options remain compatible with Gemini's native structured output capabilities.**

When working with structured data extraction using Google's LangExtract library, understanding how the framework enforces **LangExtract schema constraints with Gemini models** ensures your LLM outputs adhere to predictable, type-safe formats. The library bridges the gap between Python type definitions and Gemini's native JSON schema support through a three-stage pipeline involving schema generation, provider configuration, and runtime validation.

## Architecture Overview

The constraint handling system consists of three integrated components that work together to ensure schema compliance:

| Component | Role | Key Implementation |
|---|---|---|
| **`GeminiSchema`** | Generates JSON schemas from example data and validates format compatibility. | [`langextract/providers/schemas/gemini.py`](https://github.com/google/langextract/blob/main/langextract/providers/schemas/gemini.py) |
| **`GeminiLanguageModel`** | Injects schemas into API requests and enforces JSON output requirements. | [`langextract/providers/gemini.py`](https://github.com/google/langextract/blob/main/langextract/providers/gemini.py) |
| **`BaseSchema` / `FormatHandler`** | Define shared contracts for schema conversion and output formatting. | [`langextract/core/schema.py`](https://github.com/google/langextract/blob/main/langextract/core/schema.py) and [`langextract/core/format_handler.py`](https://github.com/google/langextract/blob/main/langextract/core/format_handler.py) |

## Building Gemini-Compatible Schemas

### Generating Schemas from Examples

The `GeminiSchema.from_examples` classmethod in [`langextract/providers/schemas/gemini.py`](https://github.com/google/langextract/blob/main/langextract/providers/schemas/gemini.py) (lines 98-123) analyzes your example extractions to build a JSON schema that mirrors your data structure:

```python
@classmethod
def from_examples(cls, examples_data, attribute_suffix=data.ATTRIBUTE_SUFFIX) -> GeminiSchema:
    # Collect attribute types per extraction class

    extraction_categories = {}
    for example in examples_data:
        for extraction in example.extractions:
            category = extraction.extraction_class
            extraction_categories.setdefault(category, {})
            if extraction.attributes:
                for attr_name, attr_value in extraction.attributes.items():
                    extraction_categories[category].setdefault(attr_name, set()).add(type(attr_value))

    # Build JSON schema objects for each class and its attribute sub-object

    extraction_properties = {}
    for category, attrs in extraction_categories.items():
        extraction_properties[category] = {"type": "string"}
        attr_field = f"{category}{attribute_suffix}"
        # Schema construction continues...

        extraction_properties[attr_field] = {"type": "object", "properties": attr_properties, "nullable": True}

```

This method inspects Python types from your examples and maps them to JSON schema types, ensuring the Gemini model understands the expected output structure.

### Provider Configuration Conversion

Once built, the `to_provider_config` method (lines 50-58) converts the internal schema representation into Gemini-specific API parameters:

```python
def to_provider_config(self) -> dict[str, Any]:
    """Return Gemini-specific kwargs."""
    return {
        "response_schema": self._schema_dict,
        "response_mime_type": "application/json",
    }

```

These parameters tell the Gemini API to constrain its output to valid JSON matching the provided schema.

### Format Validation

The `validate_format` method (lines 66-96) ensures your output formatting choices don't conflict with Gemini's native JSON capabilities:

```python
def validate_format(self, format_handler: fh.FormatHandler) -> None:
    # Gemini must not use fences because it already outputs raw JSON

    if format_handler.use_fences:
        warnings.warn(
            "Gemini outputs native JSON via response_mime_type='application/json'. "
            "Using fence_output=True may cause parsing issues. Set fence_output=False.",
            UserWarning,
        )
    # Gemini expects the wrapper key to be the library-wide EXTRACTIONS_KEY

    if (not format_handler.use_wrapper
        or format_handler.wrapper_key != data.EXTRACTIONS_KEY):
        warnings.warn(
            f"Gemini's response_schema expects wrapper_key='{data.EXTRACTIONS_KEY}'. "
            f"Current settings: use_wrapper={format_handler.use_wrapper}, "
            f"wrapper_key='{format_handler.wrapper_key}'",
            UserWarning,
        )

```

This validation prevents common configuration errors that would cause parsing failures.

## Provider-Side Schema Injection

### Request Configuration

The `GeminiLanguageModel` class in [`langextract/providers/gemini.py`](https://github.com/google/langextract/blob/main/langextract/providers/gemini.py) handles the actual API integration. During each request in `_process_single_prompt` (lines 12-16), it injects the schema:

```python
def _process_single_prompt(self, prompt: str, config: dict) -> core_types.ScoredOutput:
    # Attach any extra kwargs that weren't already in the config

    for key, value in self._extra_kwargs.items():
        if key not in config and value is not None:
            config[key] = value

    if self.gemini_schema:
        self._validate_schema_config()                     # ensure JSON format

        config.setdefault('response_mime_type', 'application/json')
        config.setdefault('response_schema', self.gemini_schema.schema_dict)

```

This ensures every request includes the schema constraints when `gemini_schema` is present.

### Format-Type Guard

The `_validate_schema_config` method (lines 90-99) enforces that structured output only works with JSON:

```python
def _validate_schema_config(self) -> None:
    if self.gemini_schema and self.format_type != data.FormatType.JSON:
        raise exceptions.InferenceConfigError(
            'Gemini structured output only supports JSON format. '
            'Set format_type=JSON or use_schema_constraints=False.'
        )

```

Attempting to use YAML or other formats with schema constraints triggers an immediate `InferenceConfigError`.

### Batch API Handling

When using the Gemini Batch API, the provider strips schema fields from the generation config because the batch helper injects them separately:

```python

# langextract/providers/gemini.py#L67-L71

batch_config = dict(config)
batch_config.pop('response_mime_type', None)
batch_config.pop('response_schema', None)

# …later pass schema_dict to gemini_batch.infer_batch(...)

```

This maintains compatibility with the batch processing workflow while preserving schema constraints.

## Core Abstractions

`GeminiSchema` inherits from `BaseSchema` defined in [`langextract/core/schema.py`](https://github.com/google/langextract/blob/main/langextract/core/schema.py). The abstract base enforces three methods that every provider-specific schema must implement:

* `from_examples` – create a schema from example extractions.
* `to_provider_config` – turn the internal representation into provider-specific kwargs.
* `requires_raw_output` – indicate whether the provider outputs raw JSON/YAML (Gemini returns `True`).

The `FormatHandler` (in [`langextract/core/format_handler.py`](https://github.com/google/langextract/blob/main/langextract/core/format_handler.py)) reads the `requires_raw_output` flag to decide whether to wrap model output in markdown fences. Because Gemini's schema sets `requires_raw_output=True`, the library automatically omits fences, matching Gemini's native JSON response format.

## Practical Implementation Example

```python
import langextract as le

# 1️⃣ Define example data (normally produced by le.Extractions objects)

examples = le.load_examples("my_examples.json")   # ← any source that yields ExampleData

# 2️⃣ Create a Gemini-compatible schema from the examples

schema = le.providers.schemas.gemini.GeminiSchema.from_examples(examples)

# 3️⃣ Build a Gemini model that will respect the schema

gemini = le.providers.gemini.GeminiLanguageModel(
    api_key="YOUR_API_KEY",               # (or set LANGEXTRACT_API_KEY env var)

    gemini_schema=schema,                 # ← enable structured output

    format_type=le.data.FormatType.JSON,  # required for Gemini

    fence_output=False,                   # optional – warnings if True

)

# 4️⃣ Run inference

for result in gemini.infer(["Extract entities from the following text: …"]):
    print(result[0].output)   # → JSON adhering to the schema

```

If you accidentally set `format_type=le.data.FormatType.YAML` while passing `gemini_schema`, the constructor raises an `InferenceConfigError` to prevent invalid configurations.

## Summary

- **Schema Generation**: `GeminiSchema.from_examples` in [`langextract/providers/schemas/gemini.py`](https://github.com/google/langextract/blob/main/langextract/providers/schemas/gemini.py) analyzes example extractions to build JSON schemas that map Python types to JSON schema definitions.
- **API Integration**: The `GeminiLanguageModel` class injects schemas via `response_schema` and `response_mime_type="application/json"` parameters, ensuring Gemini returns strictly conformant JSON.
- **Validation Guards**: Runtime checks in `validate_format` and `_validate_schema_config` prevent incompatible settings like YAML output or markdown fences when using structured output.
- **Raw Output Handling**: Because Gemini returns native JSON, `requires_raw_output=True` ensures the `FormatHandler` skips markdown fencing and uses the correct wrapper key.

## Frequently Asked Questions

### How does LangExtract convert Python types to JSON schema definitions?

The `GeminiSchema.from_examples` method inspects the `extraction_class` and `attributes` of your example data, collecting Python types (like `str`, `int`, `float`) into sets. It then maps these to JSON schema type strings (e.g., `"type": "string"`, `"type": "number"`) when constructing the `extraction_properties` dictionary, ensuring the generated schema accurately reflects your data structure.

### What happens if I try to use YAML format with Gemini schema constraints?

The library raises an `InferenceConfigError` with the message: *"Gemini structured output only supports JSON format. Set format_type=JSON or use_schema_constraints=False."* This validation occurs in `GeminiLanguageModel._validate_schema_config` within [`langextract/providers/gemini.py`](https://github.com/google/langextract/blob/main/langextract/providers/gemini.py), preventing API calls that would fail or return malformed data.

### Why does LangExtract warn about markdown fences when using Gemini?

Because Gemini's API returns raw JSON when `response_mime_type="application/json"` is set, wrapping the output in markdown fences (like ```json) would cause parsing failures. The `GeminiSchema.validate_format` method checks `format_handler.use_fences` and emits a `UserWarning` if fences are enabled, recommending `fence_output=False` to ensure clean JSON parsing.

### Can I use schema constraints with the Gemini Batch API?

Yes, but with a specific implementation detail. When using batch processing, the `GeminiLanguageModel` strips `response_mime_type` and `response_schema` from the generation config before sending the batch request, as shown in lines 67-71 of [`langextract/providers/gemini.py`](https://github.com/google/langextract/blob/main/langextract/providers/gemini.py). The schema constraints are instead passed directly to the batch helper (`gemini_batch.infer_batch`), ensuring structured output compliance without breaking the batch API payload structure.