Perceptron supports constrained decoding so replies always adhere to the shape you expect. Provide a Pydantic class, JSON Schema or regex pattern and the inference server ensures the outputs adhere to the structure without additional prompt engineering.
Helpers overview
pydantic_format(MyModel, name=None, strict=None): Generate the schema from a Pydantic v2 model; optionally override the schema name; set strict=True for strict enforcement.
json_schema_format(schema, name="response", strict=None): Wrap a JSON Schema dict to enforce keys, enums, and structure—set strict=True for strict enforcement.
regex_format(pattern): Constrain short outputs (yes/no, IDs, emails) with a regex instead of a full schema.
All helpers feed into the same response_format argument available on perceive, async_perceive, Client.generate, and Client.stream.
Pydantic-backed responses
Use Pydantic models to define the target shape, then pass pydantic_format so the response is guaranteed to match. You can parse the result directly into the model for type-safe handling.
from perceptron import image, perceive, pydantic_format, text
from pydantic import BaseModel, Field
from typing import Literal
class SceneAnalysis(BaseModel):
scene_type: str = Field(description="outdoor, indoor, urban, nature")
main_subjects: list[str]
mood: Literal["calm", "energetic", "dramatic", "peaceful", "tense"]
time_of_day: Literal["morning", "afternoon", "evening", "night", "unknown"]
@perceive(response_format=pydantic_format(SceneAnalysis))
def analyze_scene(path: str):
return image(path) + text("Analyze this scene and return JSON.")
result = analyze_scene("scene.jpg")
analysis = SceneAnalysis.model_validate_json(result.text)
print(analysis.time_of_day)
Strict mode (strict=True) pushes providers to reject extra fields and invalid enums. When omitted, provider defaults apply.
Raw JSON Schema
If you already have a schema, pass it directly via json_schema_format.
from perceptron import image, json_schema_format, perceive, text
schema = {
"type": "object",
"properties": {
"title": {"type": "string"},
"keywords": {"type": "array", "items": {"type": "string"}},
"confidence": {"type": "number"}
},
"required": ["title", "keywords", "confidence"],
"additionalProperties": False
}
@perceive(response_format=json_schema_format(schema, name="summary", strict=True))
def summarize(path: str):
return image(path) + text("Summarize the image and include keywords.")
resp = summarize("frame.png")
print(resp.text) # Valid JSON matching the schema
Regex constraints for short answers
Use regex_format when a compact pattern is enough (e.g., binary decisions, IDs, or numeric ranges).
from perceptron import image, perceive, regex_format, text
@perceive(response_format=regex_format(r"yes|no"))
def quick_check(path: str):
return image(path) + text("Is there a stop sign in this photo? Respond yes or no.")
print(quick_check("frame.jpg").text)
Streaming with structure
Client.stream and async_perceive(stream=True) support the same response_format. Streamed deltas arrive as text; the final event still respects your schema so you can parse once the stream completes.
Schema compilation can add latency the first time you use a new shape. Reuse the same schema object or Pydantic model to benefit from provider-side caching.