Exdantic.Runtime (exdantic v0.0.2)

View Source

Runtime schema generation and validation capabilities.

This module enables dynamic schema creation from field definitions at runtime, supporting the DSPy pattern of pydantic.create_model("DSPyProgramOutputs", **fields).

Phase 5 Enhancement: Enhanced Runtime Schemas

Added support for enhanced runtime schemas with model validators and computed fields:

# Create enhanced schema with full validation pipeline
fields = [{:name, :string, [required: true]}, {:age, :integer, [optional: true]}]

# Model validators for cross-field validation
validators = [
  fn data -> {:ok, %{data | name: String.trim(data.name)}} end,
  {MyModule, :validate_age}
]

# Computed fields for derived values
computed_fields = [
  {:display_name, :string, fn data -> {:ok, String.upcase(data.name)} end},
  {:age_group, :string, {MyModule, :compute_age_group}}
]

# Create enhanced schema
schema = Runtime.create_enhanced_schema(fields,
  title: "User Schema",
  model_validators: validators,
  computed_fields: computed_fields
)

# Validate with full pipeline
{:ok, result} = Runtime.validate_enhanced(%{name: "  john  ", age: 25}, schema)
# Result: %{name: "john", age: 25, display_name: "JOHN", age_group: "adult"}

Enhanced schemas support:

  • Model validators (both named functions and anonymous functions)
  • Computed fields (both named functions and anonymous functions)
  • Full validation pipeline execution (field → model → computed)
  • JSON Schema generation with enhanced metadata
  • Integration with existing validation infrastructure

Basic Runtime Schemas

For simple use cases without enhanced features:

# Basic runtime schema
fields = [{:name, :string, [required: true]}, {:age, :integer, [optional: true]}]
schema = Runtime.create_schema(fields, title: "Basic User Schema")

{:ok, validated} = Runtime.validate(%{name: "John", age: 30}, schema)
# Result: %{name: "John", age: 30}

Schema Types

  • DynamicSchema - Basic runtime schema with field validation
  • EnhancedSchema - Advanced runtime schema with model validators and computed fields

Phase 5 Migration Guide

Upgrading to Enhanced Runtime Schemas

Phase 5 adds enhanced runtime schemas while maintaining 100% backward compatibility with existing DynamicSchema usage.

Existing Code (Still Works)

# All existing runtime schema code continues to work unchanged
fields = [{:name, :string, [required: true]}]
schema = Exdantic.Runtime.create_schema(fields)
{:ok, result} = Exdantic.Runtime.validate(data, schema)

New Enhanced Features

# Create enhanced schema with model validators and computed fields
fields = [{:name, :string, [required: true]}]

validators = [fn data -> {:ok, %{data | name: String.trim(data.name)}} end]
computed = [{:display_name, :string, fn data -> {:ok, String.upcase(data.name)} end}]

enhanced_schema = Exdantic.Runtime.create_enhanced_schema(fields,
  model_validators: validators,
  computed_fields: computed
)

{:ok, result} = Exdantic.Runtime.validate_enhanced(data, enhanced_schema)

Unified Validation Interface

# Use Exdantic.Runtime.Validator for unified interface
alias Exdantic.Runtime.Validator

# Works with both DynamicSchema and EnhancedSchema
{:ok, result} = Validator.validate(data, any_runtime_schema)
json_schema = Validator.to_json_schema(any_runtime_schema)
info = Validator.schema_info(any_runtime_schema)

Breaking Changes

None. All existing code continues to work without modification.

New Dependencies

None. Phase 5 uses only existing Exdantic modules and standard library functions.

Performance Impact

  • DynamicSchema validation performance unchanged
  • EnhancedSchema adds minimal overhead for model validator and computed field execution
  • JSON schema generation includes computed field metadata with negligible performance impact

Implementation Notes

Function Storage

Enhanced schemas store anonymous functions in a runtime function registry, ensuring they can be executed during validation while maintaining clean serialization for schema metadata.

Error Handling

Enhanced schemas provide comprehensive error handling:

  • Field validation errors maintain existing behavior
  • Model validator errors include clear context and function references
  • Computed field errors specify which computation failed and why
  • Type validation errors for computed field return values

JSON Schema Integration

Enhanced schemas generate JSON schemas that include:

  • All regular fields with their types and constraints
  • Computed fields marked as readOnly: true
  • Enhanced metadata (x-enhanced-schema, x-model-validators, x-computed-fields)
  • Full compatibility with existing JSON schema tooling

Memory Management

Runtime functions are stored efficiently with unique generated names to prevent conflicts. The function registry is cleaned up when the schema is garbage collected.

Testing Strategy

Phase 5 includes comprehensive tests covering:

  • Basic enhanced schema creation and validation
  • Model validator execution (named and anonymous functions)
  • Computed field execution (named and anonymous functions)
  • Error handling at each pipeline stage
  • JSON schema generation with enhanced features
  • Integration with existing validation infrastructure
  • Performance benchmarks for enhanced vs basic schemas

All existing tests continue to pass, ensuring backward compatibility.

Summary

Functions

Creates an enhanced runtime schema with model validators and computed fields.

Creates an enhanced schema with Phase 6 integration features.

Creates a schema at runtime from field definitions.

Generates JSON Schema for enhanced runtime schemas.

Generates JSON Schema from a runtime schema.

Validates data against a runtime-created schema.

Validates data against an enhanced runtime schema.

Validates an enhanced schema with complete pipeline testing.

Types

field_definition()

@type field_definition() :: {atom(), type_spec()} | {atom(), type_spec(), keyword()}

schema_option()

@type schema_option() ::
  {:title, String.t()} | {:description, String.t()} | {:strict, boolean()}

type_spec()

@type type_spec() :: Exdantic.Types.type_definition() | atom() | module()

Functions

create_enhanced_schema(field_definitions, opts \\ [])

@spec create_enhanced_schema([field_definition()], [schema_option()]) ::
  Exdantic.Runtime.EnhancedSchema.t()

Creates an enhanced runtime schema with model validators and computed fields.

This function provides a convenient way to create schemas with enhanced features similar to compile-time schemas but generated at runtime.

Parameters

  • field_definitions - List of field definitions
  • opts - Enhanced schema options

Options

  • :model_validators - List of model validator functions
  • :computed_fields - List of computed field specifications
  • Standard options: :title, :description, :strict, :name

Examples

iex> fields = [{:name, :string, [required: true]}, {:age, :integer, [optional: true]}]
iex> validators = [fn data -> {:ok, %{data | name: String.trim(data.name)}} end]
iex> computed = [{:display_name, :string, fn data -> {:ok, String.upcase(data.name)} end}]
iex> schema = Exdantic.Runtime.create_enhanced_schema(fields,
...>   model_validators: validators,
...>   computed_fields: computed
...> )
%Exdantic.Runtime.EnhancedSchema{...}

create_enhanced_schema_v6(field_definitions, opts \\ [])

@spec create_enhanced_schema_v6([field_definition()], [schema_option()]) ::
  Exdantic.Runtime.EnhancedSchema.t()

Creates an enhanced schema with Phase 6 integration features.

Phase 6 Enhancement: Complete integration with EnhancedResolver and validation pipeline.

Parameters

  • field_definitions - Field definitions
  • opts - Enhanced schema options with Phase 6 features

Phase 6 Options

  • :auto_optimize_for_provider - Automatically optimize for LLM provider (default: nil)
  • :include_validation_metadata - Include validation metadata in schema (default: false)
  • :dspy_compatible - Ensure DSPy compatibility (default: false)
  • All existing options from previous phases

Examples

iex> schema = Exdantic.Runtime.create_enhanced_schema_v6(fields,
...>   model_validators: validators,
...>   computed_fields: computed,
...>   auto_optimize_for_provider: :openai,
...>   dspy_compatible: true
...> )

create_schema(field_definitions, opts \\ [])

Creates a schema at runtime from field definitions.

Parameters

  • field_definitions - List of field definitions in the format:
    • {field_name, type}
    • {field_name, type, options}
  • opts - Schema configuration options

Options

  • :title - Schema title
  • :description - Schema description
  • :strict - Enable strict validation (default: false)
  • :name - Schema name for references

Examples

iex> fields = [
...>   {:name, :string, [required: true, min_length: 2]},
...>   {:age, :integer, [optional: true, gt: 0]},
...>   {:email, :string, [required: true, format: ~r/@/]}
...> ]
iex> schema = Exdantic.Runtime.create_schema(fields, title: "User Schema")
%Exdantic.Runtime.DynamicSchema{...}

enhanced_to_json_schema(enhanced_schema, opts \\ [])

@spec enhanced_to_json_schema(
  Exdantic.Runtime.EnhancedSchema.t(),
  keyword()
) :: map()

Generates JSON Schema for enhanced runtime schemas.

Parameters

  • enhanced_schema - An EnhancedSchema struct
  • opts - JSON Schema generation options

Returns

  • JSON Schema map including computed field metadata

to_json_schema(schema, opts \\ [])

@spec to_json_schema(
  Exdantic.Runtime.DynamicSchema.t(),
  keyword()
) :: map()

Generates JSON Schema from a runtime schema.

Parameters

  • dynamic_schema - A DynamicSchema struct
  • opts - JSON Schema generation options

Returns

  • JSON Schema map

Examples

iex> json_schema = Exdantic.Runtime.to_json_schema(schema)
%{"type" => "object", "properties" => %{...}}

validate(data, schema, opts \\ [])

@spec validate(map(), Exdantic.Runtime.DynamicSchema.t(), keyword()) ::
  {:ok, map()} | {:error, [Exdantic.Error.t()]}

Validates data against a runtime-created schema.

Parameters

  • data - The data to validate (map)
  • dynamic_schema - A DynamicSchema struct
  • opts - Validation options

Returns

  • {:ok, validated_data} on success
  • {:error, errors} on validation failure

Examples

iex> data = %{name: "John", age: 30}
iex> Exdantic.Runtime.validate(data, schema)
{:ok, %{name: "John", age: 30}}

validate_enhanced(data, enhanced_schema, opts \\ [])

@spec validate_enhanced(map(), Exdantic.Runtime.EnhancedSchema.t(), keyword()) ::
  {:ok, map()} | {:error, [Exdantic.Error.t()]}

Validates data against an enhanced runtime schema.

Parameters

  • data - The data to validate (map)
  • enhanced_schema - An EnhancedSchema struct
  • opts - Validation options

Returns

  • {:ok, validated_data} on success (includes computed fields)
  • {:error, errors} on validation failure

Examples

iex> data = %{name: "  John  ", age: 30}
iex> Exdantic.Runtime.validate_enhanced(data, schema)
{:ok, %{name: "John", age: 30, display_name: "JOHN"}}

validate_enhanced_v6(data, enhanced_schema, opts \\ [])

@spec validate_enhanced_v6(map(), Exdantic.Runtime.EnhancedSchema.t(), keyword()) ::
  {:ok, map()} | {:ok, map(), map()} | {:error, [Exdantic.Error.t()]}

Validates an enhanced schema with complete pipeline testing.

Phase 6 Enhancement: Comprehensive validation testing including all features.

Parameters

  • data - Data to validate
  • enhanced_schema - Enhanced schema
  • opts - Validation options with Phase 6 features

Phase 6 Options

  • :test_all_providers - Test compatibility with all LLM providers (default: false)
  • :generate_performance_report - Include performance metrics (default: false)
  • :validate_json_schema - Validate generated JSON schema (default: false)

Returns

  • Enhanced validation result with optional additional information