Skip to content

Model Export

Stickler provides three methods for exporting model schemas. Two support round-trip serialization (export then re-import); the third is for runtime introspection only.

Export Methods at a Glance

Method Format Round-trip Import via Best for
to_json_schema() JSON Schema + x-aws-stickler-* Yes from_json_schema() Interoperability, OpenAPI
to_stickler_config() Custom Stickler JSON Yes model_from_json() Hand-editing, version control
model_json_schema() Pydantic JSON Schema No N/A API docs, LLM tool specs, runtime inspection

Common Workflow: Export, Customize, Re-import

from stickler import StructuredModel, ComparableField

class Product(StructuredModel):
    name: str = ComparableField(default=...)
    price: float = ComparableField(default=...)

# Export defaults
config = Product.to_stickler_config()

# Customize
config["fields"]["name"]["threshold"] = 0.9
config["fields"]["name"]["weight"] = 2.0

# Re-import
CustomProduct = StructuredModel.model_from_json(config)

This is useful for starting with sensible defaults and then tuning thresholds, sharing configurations across teams, or A/B testing comparison strategies.

to_json_schema()

Exports a standard JSON Schema document with x-aws-stickler-* extensions. Compatible with JSON Schema tooling and validators.

schema = Product.to_json_schema()

Output:

{
  "type": "object",
  "x-aws-stickler-model-name": "Product",
  "properties": {
    "name": {
      "type": "string",
      "x-aws-stickler-comparator": "LevenshteinComparator",
      "x-aws-stickler-threshold": 0.8,
      "x-aws-stickler-weight": 2.0
    },
    "price": {
      "type": "number",
      "x-aws-stickler-comparator": "NumericComparator",
      "x-aws-stickler-threshold": 0.95
    }
  },
  "required": ["name", "price"]
}

to_stickler_config()

Exports a concise Stickler-specific format that is easy to read and edit by hand.

config = Product.to_stickler_config()

Output:

{
  "model_name": "Product",
  "fields": {
    "name": {
      "type": "str",
      "comparator": "LevenshteinComparator",
      "threshold": 0.8,
      "weight": 2.0,
      "required": true
    },
    "price": {
      "type": "float",
      "comparator": "NumericComparator",
      "threshold": 0.95,
      "required": true
    }
  }
}

model_json_schema()

Inherited from Pydantic. Describes the model's shape only: no comparison configuration is emitted, so the output matches what an equivalent plain BaseModel would produce. That is what makes a configured StructuredModel usable directly as an LLM structured-output schema.

This method is not round-trip compatible. Use to_json_schema() or to_stickler_config() when you need the configuration back.

Feeding its output to from_json_schema() fails or misleads depending on the model. A model whose fields are all required parses, but the rebuilt model carries default thresholds, weights and comparators, because the shape alone does not describe them (#214). A model with any Optional field raises ValueError: Unsupported JSON Schema type: None, because Optional renders as anyOf: [{"type": ...}, {"type": "null"}] with no top-level type (#198 addresses that gap).

schema = Product.model_json_schema()
# schema["properties"]["name"]["type"]  -> "string"

# For comparison configuration, read the field or use the export methods:
Product.model_fields["name"].json_schema_extra  # carries the config
Product.to_json_schema()                        # x-aws-stickler-* extensions

Nested Models and Lists

Both round-trip methods handle nested StructuredModel classes and List[StructuredModel] fields recursively:

from typing import List

class LineItem(StructuredModel):
    match_threshold = 0.8
    product: str = ComparableField(default=...)
    quantity: int = ComparableField(default=...)

class Order(StructuredModel):
    order_id: str = ComparableField(threshold=1.0, default=...)
    items: List[LineItem] = ComparableField(default=...)

# JSON Schema export includes the LineItem schema under "items"
schema = Order.to_json_schema()

# Stickler config includes LineItem fields under items.fields
config = Order.to_stickler_config()

Round-Trip Examples

JSON Schema

schema = Product.to_json_schema()
Reconstructed = StructuredModel.from_json_schema(schema)

p1 = Product(name="Laptop", price=999.99)
p2 = Reconstructed(name="Laptop", price=999.99)

r1 = p1.compare_with(p1)
r2 = p2.compare_with(p2)
assert r1["overall_score"] == r2["overall_score"]

Stickler Config

config = Product.to_stickler_config()
Reconstructed = StructuredModel.model_from_json(config)

# Comparison behavior is identical

Saving and Loading

import json

# Save
with open('product_schema.json', 'w') as f:
    json.dump(Product.to_json_schema(), f, indent=2)

# Load
with open('product_schema.json') as f:
    schema = json.load(f)
Product = StructuredModel.from_json_schema(schema)

See Also