-
Notifications
You must be signed in to change notification settings - Fork 10
Open
Labels
core-featureCore functionalityCore functionalityenhancementNew feature or requestNew feature or requesthigh-priorityHigh priority featureHigh priority feature
Description
Overview
Implement typed predictors that provide type-safe field handling with automatic validation and parsing, similar to Pydantic in Python.
Description
Typed predictors ensure type safety at module boundaries by automatically validating and coercing inputs/outputs according to specified types. This reduces runtime errors and improves developer experience with better IDE support.
Key Features to Implement
- Type definitions for signature fields
- Automatic validation on input/output
- Type coercion with clear rules
- Custom type validators
- IDE-friendly type annotations
- Integration with existing Field system
Implementation Requirements
1. Type System Enhancement
module Desiru
module Types
class TypedField < Field
attr_reader :type_class, :validator
def initialize(name:, type:, description: nil, optional: false, validator: nil)
super(name: name, type: type, description: description, optional: optional)
@type_class = resolve_type_class(type)
@validator = validator
end
def validate_and_coerce(value)
# Skip nil for optional fields
return nil if value.nil? && optional
# Type coercion
coerced = coerce_value(value)
# Type validation
unless valid_type?(coerced)
raise ValidationError, "Expected #{type}, got #{coerced.class}"
end
# Custom validation
if @validator && \!@validator.call(coerced)
raise ValidationError, "Custom validation failed for #{name}"
end
coerced
end
private
def coerce_value(value)
case @type
when :integer
Integer(value) rescue raise ValidationError, "Cannot convert to integer"
when :float
Float(value) rescue raise ValidationError, "Cannot convert to float"
when :boolean
to_boolean(value)
when :array
Array(value)
when :hash
value.to_h rescue raise ValidationError, "Cannot convert to hash"
else
value
end
end
end
end
end2. Typed Predictor Module
module Desiru
class TypedPredictor < Module
def initialize(signature:, **options)
super
setup_typed_fields
end
def forward(**inputs)
# Validate and coerce inputs
validated_inputs = validate_inputs(inputs)
# Execute prediction
result = super(**validated_inputs)
# Validate and coerce outputs
validate_outputs(result)
end
private
def setup_typed_fields
@typed_input_fields = {}
@typed_output_fields = {}
@signature.input_fields.each do |name, field|
@typed_input_fields[name] = Types::TypedField.new(
name: name,
type: field.type,
description: field.description,
optional: field.optional
)
end
@signature.output_fields.each do |name, field|
@typed_output_fields[name] = Types::TypedField.new(
name: name,
type: field.type,
description: field.description,
optional: field.optional
)
end
end
def validate_inputs(inputs)
validated = {}
@typed_input_fields.each do |name, field|
value = inputs[name] || inputs[name.to_s]
validated[name] = field.validate_and_coerce(value)
end
validated
end
end
end3. Advanced Type Definitions
# Support for complex types
module Desiru::Types
# Enum type
class Enum < TypedField
def initialize(name:, values:, **options)
super(name: name, type: :enum, **options)
@allowed_values = values
end
def validate_and_coerce(value)
unless @allowed_values.include?(value)
raise ValidationError, "Value must be one of: #{@allowed_values.join(', ')}"
end
value
end
end
# Structured type
class Structured < TypedField
def initialize(name:, schema:, **options)
super(name: name, type: :structured, **options)
@schema = schema
end
def validate_and_coerce(value)
validate_against_schema(value, @schema)
end
end
# List with element type
class TypedList < TypedField
def initialize(name:, element_type:, **options)
super(name: name, type: :array, **options)
@element_type = element_type
end
def validate_and_coerce(value)
array = super(value)
array.map { |elem|
@element_type.validate_and_coerce(elem)
}
end
end
end4. DSL for Typed Signatures
class TypedSignature < Signature
def self.define(&block)
builder = SignatureBuilder.new
builder.instance_eval(&block)
builder.build
end
class SignatureBuilder
def initialize
@input_fields = {}
@output_fields = {}
end
# DSL methods
def input(name, type, **options)
@input_fields[name] = create_typed_field(name, type, **options)
end
def output(name, type, **options)
@output_fields[name] = create_typed_field(name, type, **options)
end
def enum(name, values:, **options)
Types::Enum.new(name: name, values: values, **options)
end
def list_of(type)
Types::TypedList.new(element_type: type)
end
end
endExample Usage
# Define typed signature
signature = Desiru::TypedSignature.define do
input :age, :integer, validator: ->(v) { v >= 0 && v <= 150 }
input :name, :string, description: "Person's full name"
input :interests, list_of(:string), optional: true
output :category, enum(values: ["child", "teen", "adult", "senior"])
output :summary, :string
end
# Create typed predictor
predictor = Desiru::TypedPredictor.new(
signature: signature,
model: "gpt-4"
)
# Type-safe usage
result = predictor.forward(
age: "25", # Automatically converted to integer
name: "Alice Smith"
)
# Result has validated category and summary
# This would raise ValidationError
result = predictor.forward(
age: "invalid", # Cannot convert to integer
name: "Bob"
)Integration with IDE
# Generate RBS type signatures
class TypedPredictor
def generate_rbs
<<~RBS
class #{self.class.name}
def forward: (#{format_input_types}) -> { #{format_output_types} }
end
RBS
end
endTesting Requirements
- Unit tests for each type coercion
- Validation error cases
- Complex type scenarios
- Performance impact measurement
- IDE integration tests
Priority
High - Significantly improves reliability and developer experience
Metadata
Metadata
Assignees
Labels
core-featureCore functionalityCore functionalityenhancementNew feature or requestNew feature or requesthigh-priorityHigh priority featureHigh priority feature