| name | neuron-structured-output |
| description | Design and implement structured output classes for Neuron AI agents using SchemaProperty attributes and validation rules. Use this skill when the user mentions structured output, JSON schema extraction, data validation, output classes, DTOs for AI responses, extracting structured data from LLM, or configuring property schemas. Also trigger for any task involving SchemaProperty attribute, validation rules like NotBlank/Email/Url, nested objects, arrays of objects, enums, polymorphic types with anyOf, or the Validator class. |
Neuron AI Structured Output
This skill helps you create structured output classes for extracting typed data from LLM responses in Neuron AI applications.
Core Concept
Neuron AI uses a two-layer approach for structured output:
- SchemaProperty - Controls the JSON schema sent to the LLM to guide generation
- Validation Rules - Verifies the LLM response meets your requirements
use NeuronAI\StructuredOutput\SchemaProperty;
use NeuronAI\StructuredOutput\Validation\Rules\NotBlank;
class Person
{
#[SchemaProperty(description: 'The user name.', required: true)]
#[NotBlank]
public string $name;
}
SchemaProperty Attribute
The SchemaProperty attribute defines how properties are represented in the JSON schema sent to the LLM.
Constructor Arguments
#[SchemaProperty(
string $title = null, // Property title in JSON schema
string $description = null, // Property description (guides LLM)
bool $required = null, // Override required status
int $min = null, // Minimum value (numbers) or items (arrays)
int $max = null, // Maximum value (numbers) or items (arrays)
int $minLength = null, // Minimum string length
int $maxLength = null, // Maximum string length
array $anyOf = null, // Array of allowed class/enum types
)]
Argument Implications
| Argument | JSON Schema Output | Usage |
|---|
title | title: "..." | Human-readable property title |
description | description: "..." | Critical: Guides LLM on what to generate |
required: true | Adds to required array | Property must be present |
required: false | Excludes from required | Property is optional |
min | minimum (numbers) or minItems (arrays) | Lower bound constraint |
max | maximum (numbers) or maxItems (arrays) | Upper bound constraint |
minLength | minLength | Minimum string characters |
maxLength | maxLength | Maximum string characters |
anyOf | anyOf: [...] | Polymorphic types (multiple possible classes) |
Required Property Logic
Properties are required by default unless:
required: false is explicitly set in SchemaProperty
- Property is nullable (
?string $name)
- Property has a default value (
string $name = 'default')
class Example
{
public string $firstName;
#[SchemaProperty(required: false)]
public string $middleName;
public ?string $lastName;
public string $country = 'US';
}
Basic Property Types
String Properties
class Article
{
#[SchemaProperty(
description: 'The article title',
minLength: 10,
maxLength: 200
)]
public string $title;
#[SchemaProperty(description: 'Article content')]
public string $content;
}
Generated schema:
{
"title": {
"description": "The article title",
"type": "string",
"minLength": 10,
"maxLength": 200
},
"content": {
"description": "Article content",
"type": "string"
}
}
Numeric Properties
class Rating
{
#[SchemaProperty(
description: 'Rating from 1 to 5 stars',
min: 1,
max: 5
)]
public int $stars;
#[SchemaProperty(description: 'Price in dollars')]
public float $price;
}
Generated schema:
{
"stars": {
"description": "Rating from 1 to 5 stars",
"type": "integer",
"minimum": 1,
"maximum": 5
},
"price": {
"description": "Price in dollars",
"type": "number"
}
}
Boolean Properties
class Settings
{
#[SchemaProperty(description: 'Whether notifications are enabled')]
public bool $notificationsEnabled;
}
Nested Objects
Define nested classes to create complex structures:
use NeuronAI\StructuredOutput\SchemaProperty;
use NeuronAI\StructuredOutput\Validation\Rules\NotBlank;
class Address
{
#[SchemaProperty(description: 'The street name')]
#[NotBlank]
public string $street;
public string $city;
#[SchemaProperty(description: 'Postal/ZIP code')]
public string $zip;
}
class Person
{
#[NotBlank]
public string $firstName;
public string $lastName;
public Address $address;
}
Usage:
$person = $agent->structured(
new UserMessage('John Doe lives at 123 Main St, New York, 10001'),
Person::class
);
echo $person->address->city;
Arrays
Array of Strings (Simple)
class TagList
{
#[SchemaProperty(description: 'List of tags', min: 1, max: 10)]
public array $tags;
}
Array of Objects
Use anyOf to specify the object type for arrays:
use NeuronAI\StructuredOutput\Validation\Rules\ArrayOf;
class Tag
{
#[SchemaProperty(description: 'The tag name')]
public string $name;
}
class Article
{
#[SchemaProperty(
description: 'Article tags',
anyOf: [Tag::class]
)]
#[ArrayOf(Tag::class)]
public array $tags;
}
Important: Use both:
SchemaProperty(anyOf: [Class::class]) - For JSON schema generation
#[ArrayOf(Class::class)] - For validation
Nested Arrays (Deep Structures)
class TagProperty
{
#[SchemaProperty(description: 'The property value')]
public string $value;
}
class Tag
{
#[SchemaProperty(description: 'Tag name')]
public string $name;
#[SchemaProperty(
description: 'Additional tag properties',
anyOf: [TagProperty::class]
)]
#[ArrayOf(TagProperty::class)]
public array $properties;
}
class Person
{
public string $name;
#[SchemaProperty(anyOf: [Tag::class])]
#[ArrayOf(Tag::class)]
public array $tags;
}
Enums
Backed Enums (String or Int)
enum Status: string
{
case ACTIVE = 'active';
case INACTIVE = 'inactive';
case PENDING = 'pending';
}
class User
{
public string $name;
public Status $status;
}
Generated schema:
{
"status": {
"type": "string",
"enum": ["active", "inactive", "pending"]
}
}
Integer Enums
enum Priority: int
{
case LOW = 1;
case MEDIUM = 2;
case HIGH = 3;
}
class Task
{
public string $title;
public Priority $priority;
}
Polymorphic Types (anyOf)
Use anyOf when a property can be one of several types:
class FtpMode
{
public string $mode;
public string $account;
}
class EmailMode
{
public string $mode;
public string $mailingList;
}
class NotificationConfig
{
#[SchemaProperty(anyOf: [FtpMode::class, EmailMode::class])]
public array $modes;
}
Discriminator Field
For polymorphic arrays, Neuron uses a __classname__ discriminator field:
Generated schema includes:
{
"modes": {
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"properties": {
"__classname__": {
"type": "string",
"enum": ["ftpmode"],
"description": "This property is mandatory..."
},
"mode": {"type": "string"},
"account": {"type": "string"}
}
},
{
"type": "object",
"properties": {
"__classname__": {
"type": "string",
"enum": ["emailmode"]
},
"mode": {"type": "string"},
"mailingList": {"type": "string"}
}
}
]
}
}
}
The LLM must include __classname__ in responses for proper deserialization.
Validation Rules
Validation rules verify LLM output. If validation fails, Neuron can retry the request.
String Validation
use NeuronAI\StructuredOutput\Validation\Rules\NotBlank;
use NeuronAI\StructuredOutput\Validation\Rules\Length;
use NeuronAI\StructuredOutput\Validation\Rules\Email;
use NeuronAI\StructuredOutput\Validation\Rules\Url;
use NeuronAI\StructuredOutput\Validation\Rules\WordsCount;
class UserProfile
{
#[NotBlank]
public string $username;
#[Email]
public string $email;
#[Url]
public string $website;
#[Length(min: 10, max: 500)]
public string $bio;
#[WordsCount(min: 5, max: 100)]
public string $summary;
#[Length(exactly: 10)]
public string $code;
}
Numeric Validation
use NeuronAI\StructuredOutput\Validation\Rules\GreaterThan;
use NeuronAI\StructuredOutput\Validation\Rules\LowerThan;
use NeuronAI\StructuredOutput\Validation\Rules\OutOfRange;
use NeuronAI\StructuredOutput\Validation\Rules\EqualTo;
class Product
{
#[GreaterThan(0)]
public float $price;
#[LowerThan(1000)]
public int $stock;
#[OutOfRange(min: 0, max: 100)]
public int $discountPercentage;
#[EqualTo(42)]
public int $answer;
}
Array Validation
use NeuronAI\StructuredOutput\Validation\Rules\ArrayOf;
use NeuronAI\StructuredOutput\Validation\Rules\Count;
class Team
{
#[ArrayOf(User::class)]
public array $members;
#[Count(min: 1, max: 10)]
public array $tags;
#[Count(exactly: 3)]
public array $topThree;
#[ArrayOf('string')]
public array $categories;
}
Boolean & Null Validation
use NeuronAI\StructuredOutput\Validation\Rules\IsTrue;
use NeuronAI\StructuredOutput\Validation\Rules\IsFalse;
use NeuronAI\StructuredOutput\Validation\Rules\IsNull;
use NeuronAI\StructuredOutput\Validation\Rules\IsNotNull;
class Settings
{
#[IsTrue]
public bool $agreedToTerms;
#[IsFalse]
public bool $isBlocked;
#[IsNotNull]
public ?string $optionalValue;
}
Enum Validation
use NeuronAI\StructuredOutput\Validation\Rules\Enum;
class Order
{
#[Enum(class: Status::class)]
public string $status;
#[Enum(values: ['urgent', 'normal', 'low'])]
public string $priority;
}
Comparison Validation
use NeuronAI\StructuredOutput\Validation\Rules\EqualTo;
use NeuronAI\StructuredOutput\Validation\Rules\NotEqualTo;
use NeuronAI\StructuredOutput\Validation\Rules\GreaterThanEqual;
use NeuronAI\StructuredOutput\Validation\Rules\LowerThanEqual;
class Comparison
{
#[EqualTo(100)]
public int $exactValue;