Skip to main content
The DataModel system in LarAgent provides a robust foundation for handling structured data. It ensures strict typing, automatic validation, serialization, and OpenAPI schema generation - flexible enough for simple DTOs while powerful enough for complex, nested, and polymorphic data structures. Used in storage, tool arguments and structured output with LLMs, the DataModel system is a core part of LarAgent’s architecture.

Core Features

All data models extend the LarAgent\Core\Abstractions\DataModel abstract class, which provides:

Automatic Hydration

Populate objects from arrays using fill() or fromArray()

Schema Generation

Automatically generate OpenAPI/JSON Schemas using PHP types and attributes

Serialization

Convert objects back to arrays or JSON

Performance

Uses static runtime caching to minimize Reflection overhead

Basic Usage

When creating a DataModel for use with LLMs (structured output or tool arguments), use the #[Desc] attribute to provide context that helps the AI understand the purpose and expected format of each field.
Even when you don’t need LLM integration, you can still use DataModel instead of a plain DTO to benefit from the built-in fromArray() and toArray() methods.

Polymorphic Arrays

To handle lists of different model types (e.g., a message containing both text and images), extend DataModelArray. DataModelArray allows you to define a set of allowed models and a discriminator field to determine which model to instantiate for each item in the array. It’s like a collection, but strictly typed to only allow specific DataModels based on a discriminator field.
You can instantiate polymorphic arrays in multiple ways:

Differentiation

Sometime one discriminator field is not enough to differentiate between multiple models. In such cases, you can override the matchesArray() method in DataModel. For example, in a chat message array where both AssistantMessage and ToolCallMessage share the same role value of assistant, you can implement custom logic in matchesArray to check for the presence of specific fields:
In ToolCallMessage & AssistantMessage, you can implement custom logic:
ToolCallMessage.php
AssistantMessage.php
Where the $data is the raw array being hydrated. This allows you to implement any complex differentiation logic based on the presence/absence or value of certain fields.

Supported Property Types

DataModel supports various types for properties that are automatically mapped to Schema types, automatically validated, and serialized:

Basic Types

string, int, float, bool

Nullable Types

?string, ?int, etc. - marks property as optional in schema

Arrays

array - simple arrays without type hints

Nested Models

Other DataModel classes as properties

Enums

PHP Enum for constrained values

Data Model Arrays

DataModelArray for typed collections

Nested Data Models

DataModels can contain other DataModels as properties, enabling you to build complex, hierarchical data structures. Nested models are automatically hydrated when using fromArray() and properly serialized with toArray().

Enums

PHP backed enums are fully supported and automatically converted to JSON Schema enum constraints. This is useful when a property should only accept specific values.
Use string-backed enums for human-readable values that the LLM can easily understand. Integer-backed enums work well for numeric scales or priority levels.

DataModelArray as Property

A DataModelArray can be used as a property within a DataModel, enabling typed collections of polymorphic items.
When a DataModelArray is used as a property, it behaves like any nested DataModel—automatically hydrated from arrays and serialized back to arrays.

DTO-Style Data Models

For simple data transfer objects that don’t need schema generation (For example, when you use it only in storage), you can use a lightweight DTO approach:
It’s good practice to override fromArray() and toArray() methods for DataModels that are heavily used. Static method resolution is faster than the dynamic reflection-based approach used by default, which can make a significant difference at scale.

Performance Optimization

The DataModel class uses Reflection to inspect properties and types. While LarAgent implements static runtime caching to mitigate the cost, Reflection is still slower than native code.
You are not required to override fromArray() or toArray() methods. The base implementation works perfectly for 90% of use cases.

When to Override

Override fromArray() and toArray() only if:
  1. The model is instantiated frequently (thousands of times in a loop)
  2. The model is part of a core hot path (like MessageContent in a streaming response)
  3. You need custom transformation logic that standard casting doesn’t support

Performance-Optimized Example

Premature optimization can make your code harder to maintain. Start with the default implementation and optimize only when necessary.

Summary