class Structify::SchemaSerializer
Handles serialization of schema definitions to different formats
def format_version_range(versions)
-
(String)
- A human-readable version range
Parameters:
-
versions
(Range, Array, Integer
) -- The version range to format
def format_version_range(versions) if versions.is_a?(Range) if versions.end.nil? "#{versions.begin} and above" else "#{versions.begin} to #{versions.end}#{versions.exclude_end? ? ' (exclusive)' : ''}" end elsif versions.is_a?(Array) versions.join(", ") else "#{versions} and above" # Single integer means this version and onwards end end
def initialize(schema_builder)
-
schema_builder
(Structify::SchemaBuilder
) -- The schema builder to serialize
def initialize(schema_builder) @schema_builder = schema_builder end
def to_json_schema
-
(Hash)
- The JSON schema
def to_json_schema # Get current schema version current_version = schema_builder.version_number # Get fields that are applicable to the current schema version fields = schema_builder.fields.select do |f| # Check if the field has a version_range if f[:version_range] version_in_range?(current_version, f[:version_range]) # Legacy check for removed_in elsif f[:removed_in] f[:removed_in] > current_version else true end end # Get required fields (excluding fields not in the current version) required_fields = fields.select { |f| f[:required] }.map { |f| f[:name].to_s } # Start with chain_of_thought if thinking mode is enabled properties_hash = {} if schema_builder.thinking_enabled properties_hash["chain_of_thought"] = { type: "string", description: "Explain your thought process step by step before determining the final values." } end # Add all other fields fields.each_with_object(properties_hash) do |f, hash| # Start with the basic type prop = { type: f[:type].to_s } # Add description if available prop[:description] = f[:description] if f[:description] # Add enum if available prop[:enum] = f[:enum] if f[:enum] # Handle array specific properties if f[:type] == :array # Add items schema prop[:items] = f[:items] if f[:items] # Add array constraints prop[:minItems] = f[:min_items] if f[:min_items] prop[:maxItems] = f[:max_items] if f[:max_items] prop[:uniqueItems] = f[:unique_items] if f[:unique_items] end # Handle object specific properties if f[:type] == :object && f[:properties] prop[:properties] = {} required_props = [] # Process each property f[:properties].each do |prop_name, prop_def| prop[:properties][prop_name] = prop_def.dup # If a property is marked as required, add it to required list and remove from property definition if prop_def[:required] required_props << prop_name prop[:properties][prop_name].delete(:required) end end # Add required array if we have required properties prop[:required] = required_props unless required_props.empty? end # Add version info to description only if requested by environment variable # This allows for backward compatibility with existing tests if ENV["STRUCTIFY_SHOW_VERSION_INFO"] && f[:version_range] && prop[:description] version_info = format_version_range(f[:version_range]) prop[:description] = "#{prop[:description]} (Available in versions: #{version_info})" elsif ENV["STRUCTIFY_SHOW_VERSION_INFO"] && f[:version_range] prop[:description] = "Available in versions: #{format_version_range(f[:version_range])}" end # Legacy: Add a deprecation notice to description if f[:deprecated_in] && f[:deprecated_in] <= current_version deprecation_note = "Deprecated in v#{f[:deprecated_in]}. " prop[:description] = if prop[:description] "#{deprecation_note}#{prop[:description]}" else deprecation_note end end hash[f[:name].to_s] = prop end { name: schema_builder.title_str, description: schema_builder.description_str, parameters: { type: "object", required: required_fields, properties: properties_hash } } end
def version_in_range?(version, range)
-
(Boolean)
- Whether the version is within the range
Parameters:
-
range
(Range, Array, Integer
) -- The range, array, or single version to check against -
version
(Integer
) -- The version to check
def version_in_range?(version, range) case range when Range # Handle endless ranges (Ruby 2.6+): 2.. means 2 and above if range.end.nil? version >= range.begin else range.cover?(version) end when Array range.include?(version) else # A single integer means "this version and onwards" version >= range end end