Getting Started
weirding is a production-grade Python library for XML ↔ Pydantic v2 conversion. It compiles
XML schema documents into Pydantic v2 BaseModel classes, validates XML data against those
models, and serializes instances back to XML. The XML schema you author once is a single
source of truth with first-class structured-output interop across the ecosystem — export
provider-ready schemas with to_json_schema() for OpenAI/Azure, Databricks ai_query, and
open-weight runtimes (vLLM/Ollama); drop generated models straight into LangChain/LangGraph;
and drive any provider's retry loop with the prompt utilities, Claude included. See the
integration guides for end-to-end recipes.
Installation
Install the core library:
pip install weirding
For XSD schema support (optional):
pip install "weirding[xsd]"
Core workflow
weirding uses a plain-attribute annotation convention. Define your schema as XML with type,
required, description, and other annotations on each field element:
import weirding
schema_xml = """
<Response>
<name type="string" required="true" description="Full name"/>
<age type="integer" required="true" minimum="0"/>
<bio type="string" required="false"/>
</Response>
"""
# Compile to JSON Schema IR (inspectable dict, follows JSON Schema draft 2020-12)
ir = weirding.compile(schema_xml)
# Compile directly to a Pydantic v2 BaseModel
Model = weirding.define_model(schema_xml)
# Parse XML data into a validated model instance
instance = weirding.parse("""
<Response>
<name>Alice Smith</name>
<age>30</age>
</Response>
""", Model)
# Serialize back to XML
xml_out = weirding.to_xml(instance)
The compile() → define_model() pipeline is idempotent and stateless — call it once at
startup and cache the result.
Reverse edges — full 3-way conversion
The conversion loop is closed: every edge of the XML ↔ JSON Schema ↔ Pydantic triangle has
a function (ADR-0012). Alongside the forward path above, to_schema() derives an IR back
out of a model and dump_xml() re-emits the XML schema document from an IR.
import weirding
Model = weirding.define_model("<Point><x type='number'/><y type='number'/></Point>")
ir = weirding.to_schema(Model) # Pydantic model → JSON Schema IR dict
xml_schema = weirding.dump_xml(ir) # JSON Schema IR → XML schema document
# Model straight to XML schema is the one-liner:
weirding.dump_xml(weirding.to_schema(Model))
dump_xml() serializes a schema — the authoring document, the inverse of compile().
This is distinct from to_xml(), which serializes a model instance into XML data (the
inverse of parse()). One produces a schema you could re-author; the other produces a data
payload.
For ecosystem export, to_json_schema(ir, *, strict=False) turns the IR into a
provider-ready JSON Schema — clean draft 2020-12 by default (vLLM, Ollama, jsonschema), or
the stricter OpenAI ∩ Databricks intersection with strict=True. See the
integration guides for provider-specific recipes.
XSD support
weirding auto-detects XSD schemas from the root element namespace. Install weirding[xsd]
and pass an XSD document to any of the core functions:
import weirding
xsd_xml = """
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Person">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:integer"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
"""
Model = weirding.define_model(xsd_xml)
instance = weirding.parse("<Person><name>Bob</name><age>25</age></Person>", Model)
LLM retry workflow
weirding ships provider-neutral prompt engineering utilities for structured-output retry
loops — they make no API calls and work with any provider (Claude, OpenAI, open-weight, …).
prompt.to_template() turns a model into an XML prompt template, and prompt.RetryContext
manages the retry loop when the model returns invalid XML.
import weirding
from weirding import prompt
schema_xml = """
<Extraction>
<entity type="string" required="true" description="Named entity extracted from the text"/>
<category type="string" required="true" description="Entity category: PERSON, ORG, or LOC"/>
<confidence type="number" required="true" minimum="0" maximum="1"/>
</Extraction>
"""
Model = weirding.define_model(schema_xml)
# Build an XML template to embed in your LLM prompt
template = prompt.to_template(Model)
# => "<Extraction>\n <entity>...</entity>\n <category>...</category>\n ..."
system_prompt = f"""Extract named entities from the user's text.
Respond using this XML format exactly:
{template}"""
# Retry loop — wraps the LLM call, parse, and error formatting
ctx = prompt.RetryContext(Model, max_attempts=3)
result = None
while not ctx.exceeded:
user_message = "Extract entities from: ..."
if ctx.attempt:
# retry_message() is built from format_error() — safe to send back to the
# model: no raw user data or PII is echoed (include_input=False)
user_message += "\n\n" + ctx.retry_message()
llm_response = call_your_llm(system_prompt, user_message)
try:
result = weirding.parse(llm_response, Model)
break # success
except weirding.ParseError as exc:
ctx.record_error(exc)
See the API Reference for full parameter documentation, and the integration guides for provider-specific recipes.