Use Schema Patterns¶
lm_schema_call returns a structured value whose
shape can be transformed like any other pytree. Dataclass examples use Optree’s dataclass integration.
Concept
Schemas · Pytrees · Transforms · Primitives
Route with Enum¶
import optree
import autoform as af
@optree.dataclasses.dataclass(namespace=af.PYTREE_NAMESPACE)
class Route:
tool: str
answer: str
route_schema = Route(tool=af.Enum("search", "done"), answer=af.Str())
def choose_route(question: str) -> Route:
prompt = af.format("Choose search or done for this question:\n{}", question)
msg = dict(role="user", content=prompt)
return af.lm_schema_call([msg], model="gpt-5.5", schema=route_schema)
Use Enum for finite decisions that should feed switch, status fields, or
other control values.
Use Nested Arguments¶
import optree
import autoform as af
@optree.dataclasses.dataclass(namespace=af.PYTREE_NAMESPACE)
class SearchArgs:
query: str
limit: int
@optree.dataclasses.dataclass(namespace=af.PYTREE_NAMESPACE)
class SearchDecision:
tool: str
args: SearchArgs
decision_schema = SearchDecision(
tool=af.Enum("search", "done"), args=SearchArgs(query=af.Str(), limit=af.Int(min=1, max=5))
)
def choose_search(question: str) -> SearchDecision:
prompt = af.format("Choose the next tool call for:\n{}", question)
msg = dict(role="user", content=prompt)
return af.lm_schema_call([msg], model="gpt-5.5", schema=decision_schema)
Nested dataclasses keep related fields together and still register as one pytree-shaped schema.
Build Schema Variants¶
Schemas are pytrees, so regular pytree utilities can derive call-specific variants before a call.
import optree
import autoform as af
schema = {"answer": af.Str(), "confidence": af.Float(min=0, max=1)}
extract_schema = optree.tree_map(
lambda leaf: leaf @ af.Doc("Extract only facts explicitly present in the input."),
schema,
namespace=af.PYTREE_NAMESPACE,
)
critique_schema = optree.tree_map(
lambda leaf: leaf @ af.Doc("Critique the draft and report uncertainty conservatively."),
schema,
namespace=af.PYTREE_NAMESPACE,
)
Both schemas return {"answer": ..., "confidence": ...}. The downstream code stays fixed while each LM call receives different field guidance.
For a one-off call, the schema can stay inline:
messages = [dict(role="user", content="Classify this request.")]
result = af.lm_schema_call(
messages,
model="gpt-5.5",
schema={
"kind": af.Enum("question", "request") @ af.Doc("Request type."),
"reply": af.Str() @ af.Doc("Short reply."),
},
)
Represent Optional-Like Fields¶
There is no special optional schema node. Model presence explicitly.
import optree
import autoform as af
@optree.dataclasses.dataclass(namespace=af.PYTREE_NAMESPACE)
class MaybeAnswer:
state: str
text: str
maybe_schema = MaybeAnswer(state=af.Enum("present", "absent"), text=af.Str())
This keeps the output shape stable for batch, pullback, and switch.
Send Field Feedback¶
Structured outputs also work with pullback.
import optree
import autoform as af
@optree.dataclasses.dataclass(namespace=af.PYTREE_NAMESPACE)
class Summary:
title: str
score: float
summary_schema = Summary(title=af.Str(max=80), score=af.Float(min=0, max=1))
def summarize(topic: str) -> Summary:
prompt = af.format("Summarize {}.", topic)
msg = dict(role="user", content=prompt)
return af.lm_schema_call([msg], model="gpt-5.5", schema=summary_schema)
ir = af.trace(summarize)("recursion")
feedback = Summary(title="too vague", score="overconfident")
output, (topic_hint,) = af.pullback(ir).call(("recursion",), feedback)
print(output)
print(topic_hint)
The feedback value has the same dataclass shape as the output. Each field can carry its own critique, and the backward rule summarizes that critique for the inputs that produced the structured response.
Malformed provider output raises a parsing error during execution. Keep schemas
small and concrete: finite choices with Enum, bounded numbers with Int or
Float, and field descriptions with Doc.