Class: ActiveAgent::Providers::Common::BaseModel
- Inherits:
-
Object
- Object
- ActiveAgent::Providers::Common::BaseModel
- Includes:
- ActiveModel::Attributes, ActiveModel::Model
- Defined in:
- lib/active_agent/providers/common/model.rb
Overview
BaseModel provides a foundation for structured data models with compressed serialization support.
This class extends ActiveModel functionality to provide:
-
Attribute definition with default values
-
Compressed hash serialization (excludes default values)
-
Required attribute tracking
-
Deep compaction of nested structures
Example
class Message < BaseModel
attribute :role, :string, as: "user"
attribute :type, :string, fallback: "plain/text"
attribute :content, :string
end
= Message.new(content: "Hello")
.to_h #=> { role: "user", type: "plain/text", content: "Hello" }
.serialize #=> { role: "user", type: "plain/text", content: "Hello" }
Direct Known Subclasses
Anthropic::Options, Messages::Base, Responses::Base, Responses::Format, Usage, Mock::EmbeddingRequest, Mock::Messages::Base, Mock::Options, Mock::Request, OpenAI::Options, OpenRouter::Requests::MaxPrice, OpenRouter::Requests::Plugin, OpenRouter::Requests::Plugins::PdfConfig, OpenRouter::Requests::Prediction, OpenRouter::Requests::ProviderPreferences, OpenRouter::Requests::ResponseFormat
Class Method Summary collapse
-
.attribute(name, type = nil, **options) ⇒ Object
Defines an attribute on the model with special handling for default values.
-
.delegate_attributes(*attributes, to:) ⇒ Object
Delegates attribute accessors to another object.
-
.drop_attributes(*attributes) ⇒ void
Drops specified attributes by defining no-op setters.
-
.inherited(subclass) ⇒ void
Ensures subclasses get their own required_attributes set.
-
.keys ⇒ Array<Symbol>
Returns all attribute keys including aliases.
-
.required_attributes ⇒ Set<String>
Returns the set of required attribute names that must be included in compressed output.
Instance Method Summary collapse
-
#<=>(other) ⇒ Integer?
Compares two models based on their serialized representations.
-
#==(other) ⇒ Boolean
Compares equality based on serialized representation.
-
#deep_compact(kwargs = {}) ⇒ Hash
Recursively removes nil values and empty collections from a hash.
-
#deep_dup ⇒ BaseModel
Creates a deep duplicate of the model.
-
#initialize(kwargs = {}) ⇒ BaseModel
constructor
Initializes a new instance with the given attributes.
-
#inspect ⇒ String
(also: #to_s)
Returns a string representation for inspection.
-
#merge!(kwargs = {}) ⇒ BaseModel
Merges the given attributes into the current instance.
-
#serialize ⇒ Hash
Serializes the model using attribute type serializers.
-
#to_h ⇒ Hash
Alias for #to_hash.
-
#to_hash ⇒ Hash
Converts the model to a hash representation.
Constructor Details
#initialize(kwargs = {}) ⇒ BaseModel
Initializes a new instance with the given attributes.
Attributes can be provided as a hash. Hash keys are sorted to prioritize nested objects during initialization for backwards compatibility. A special internal key ‘__default_values` can be passed to get an instance with only default values without any overrides.
161 162 163 164 165 166 167 168 169 170 |
# File 'lib/active_agent/providers/common/model.rb', line 161 def initialize(kwargs = {}) # To allow us to get a list of attribute defaults without initialized overrides return super(nil) if kwargs.key?(:'__default_values') # Backwards Compatibility: This sorts object construction to the top to protect the assignment # of backward compatibility assignments. kwargs = kwargs.sort_by { |k, v| v.is_a?(Hash) ? 0 : 1 }.to_h if kwargs.is_a?(Hash) super(kwargs) end |
Class Method Details
.attribute(name, type = nil, **options) ⇒ Object
Defines an attribute on the model with special handling for default values.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/active_agent/providers/common/model.rb', line 69 def self.attribute(name, type = nil, **) if .key?(:as) default_value = .delete(:as) super(name, type, default: default_value, **) # Track this attribute as required (must be included in compressed hash) required_attributes << name.to_s define_method("#{name}=") do |value| normalized_value = value.is_a?(String) ? value.to_sym : value normalized_default = default_value.is_a?(String) ? default_value.to_sym : default_value next if normalized_value == normalized_default raise ArgumentError, "Cannot set '#{name}' attribute (read-only with default value)" end elsif .key?(:fallback) default_value = .delete(:fallback) super(name, type, default: default_value, **) # Track this attribute as required (must be included in compressed hash) required_attributes << name.to_s else super(name, type, **) end end |
.delegate_attributes(*attributes, to:) ⇒ Object
Delegates attribute accessors to another object.
Creates getter and setter methods that forward to the specified target object. If the target is nil when setting, an empty hash is initialized.
106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/active_agent/providers/common/model.rb', line 106 def self.delegate_attributes(*attributes, to:) attributes.each do |attribute| define_method(attribute) do public_send(to)&.public_send(attribute) end define_method("#{attribute}=") do |value| public_send("#{to}=", {}) if public_send(to).nil? public_send(to).public_send("#{attribute}=", value) end end end |
.drop_attributes(*attributes) ⇒ void
This method returns an undefined value.
Drops specified attributes by defining no-op setters.
This is useful when converting between providers that support different attributes or when dropping attributes during message response to request construction. The attributes can still be read if defined elsewhere, but setting them has no effect.
131 132 133 134 135 136 137 |
# File 'lib/active_agent/providers/common/model.rb', line 131 def self.drop_attributes(*attributes) attributes.each do |attribute| define_method("#{attribute}=") do |value| # No-Op: Drop the value end end end |
.inherited(subclass) ⇒ void
This method returns an undefined value.
Ensures subclasses get their own required_attributes set.
43 44 45 46 |
# File 'lib/active_agent/providers/common/model.rb', line 43 def self.inherited(subclass) super subclass.instance_variable_set(:@required_attributes, required_attributes.dup) end |
.keys ⇒ Array<Symbol>
Returns all attribute keys including aliases.
Combines both the main attribute type keys and any attribute aliases, ensuring all possible attribute names are represented as symbols.
145 146 147 |
# File 'lib/active_agent/providers/common/model.rb', line 145 def self.keys (attribute_types.keys.map(&:to_sym) | attribute_aliases.keys.map(&:to_sym)) end |
.required_attributes ⇒ Set<String>
Returns the set of required attribute names that must be included in compressed output.
Required attributes are those defined with the as option, which establishes a default value that should always be serialized.
35 36 37 |
# File 'lib/active_agent/providers/common/model.rb', line 35 def self.required_attributes @required_attributes ||= Set.new end |
Instance Method Details
#<=>(other) ⇒ Integer?
Compares two models based on their serialized representations.
Uses the serialized hash to compare models, allowing for sorting and equality comparisons based on attribute values rather than object identity.
339 340 341 |
# File 'lib/active_agent/providers/common/model.rb', line 339 def <=>(other) serialize <=> other&.serialize end |
#==(other) ⇒ Boolean
Compares equality based on serialized representation.
Two models are equal if their serialized hashes are equal, regardless of object identity. This allows value-based equality comparisons.
355 356 357 |
# File 'lib/active_agent/providers/common/model.rb', line 355 def ==(other) serialize == other&.serialize end |
#deep_compact(kwargs = {}) ⇒ Hash
Recursively removes nil values and empty collections from a hash.
Nested hashes and arrays are processed recursively. Empty hashes and arrays after compaction are also removed.
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/active_agent/providers/common/model.rb', line 198 def deep_compact(kwargs = {}) kwargs.each_with_object({}) do |(key, value), result| compacted_value = case value when Hash deep_compacted = deep_compact(value) deep_compacted unless deep_compacted.empty? when Array compacted_array = value.map { |v| v.is_a?(Hash) ? deep_compact(v) : v }.compact compacted_array unless compacted_array.empty? else value end result[key] = compacted_value unless compacted_value.nil? end end |
#deep_dup ⇒ BaseModel
Creates a deep duplicate of the model.
Duplicates the model instance and recursively duplicates any array or hash attributes to ensure complete independence from the original object.
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/active_agent/providers/common/model.rb', line 250 def deep_dup dup.tap do |duplicated| attribute_names.each do |name| value = public_send(name) next if value.nil? duplicated.public_send("#{name}=", case value when Array value.map { |v| v.respond_to?(:deep_dup) ? v.deep_dup : v.dup rescue v } when Hash value.deep_dup when BaseModel value.deep_dup else value.dup rescue value end) end end end |
#inspect ⇒ String Also known as: to_s
Returns a string representation for inspection.
Provides a readable view of the model showing the class name and non-default attributes in a format similar to standard Ruby object inspection.
311 312 313 314 315 316 317 318 319 320 321 322 |
# File 'lib/active_agent/providers/common/model.rb', line 311 def inspect attrs = JSON.pretty_generate(serialize, { space: " ", indent: " ", object_nl: "\n", array_nl: "\n" }).lines.drop(1).join.chomp.sub(/\}\z/, "").strip return "#<#{self.class.name}>" if attrs.empty? "#<#{self.class.name} {\n #{attrs}\n}>" end |
#merge!(kwargs = {}) ⇒ BaseModel
Merges the given attributes into the current instance.
Only attributes with corresponding setter methods are updated. Keys are symbolized before merging.
179 180 181 182 183 184 185 |
# File 'lib/active_agent/providers/common/model.rb', line 179 def merge!(kwargs = {}) kwargs.deep_symbolize_keys.each do |key, value| public_send("#{key}=", value) if respond_to?("#{key}=") end self end |
#serialize ⇒ Hash
Serializes the model using attribute type serializers.
Iterates through each attribute and uses its ActiveModel::Type serializer to convert the value to its serialized form. Only non-default values are included, except for required attributes (those defined with ‘:as` or `:fallback` options). This provides a compressed serialization that respects custom type logic.
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/active_agent/providers/common/model.rb', line 282 def serialize default_values = self.class.new(__default_values: true).attributes required_attrs = self.class.required_attributes deep_compact(attribute_names.each_with_object({}) do |name, hash| value = public_send(name) # Always include required attributes (those defined with 'as' option) # or attributes that differ from their default value next if value == default_values[name] && !required_attrs.include?(name) # Use the attribute's type serializer attr_type = self.class.attribute_types[name] hash[name.to_sym] = attr_type.serialize(value) end) end |
#to_h ⇒ Hash
Alias for #to_hash.
242 |
# File 'lib/active_agent/providers/common/model.rb', line 242 def to_h = to_hash |
#to_hash ⇒ Hash
Converts the model to a hash representation.
Recursively converts nested BaseModel instances and arrays to hashes. Nil values and empty collections are removed via deep_compact.
225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'lib/active_agent/providers/common/model.rb', line 225 def to_hash deep_compact(attribute_names.each_with_object({}) do |name, hash| value = public_send(name) hash[name.to_sym] = case value when BaseModel then value.to_hash when Array then value.map { _1.is_a?(BaseModel) ? _1.to_hash : _1 } else value end end) end |