Module: RubyLLM::Text::Base
- Defined in:
- lib/ruby_llm/text/base.rb
Class Method Summary collapse
- .build_schema(schema) ⇒ Object
- .call_llm(prompt, model: nil, temperature: nil, schema: nil, **options) ⇒ Object
- .clean_json_response(response) ⇒ Object
Class Method Details
.build_schema(schema) ⇒ Object
60 61 62 63 64 65 66 67 68 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 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/ruby_llm/text/base.rb', line 60 def self.build_schema(schema) # If already a schema object, return as-is return schema if schema.respond_to?(:schema) return nil unless schema.is_a?(Hash) schema_class = Class.new(RubyLLM::Schema) # Handle JSON Schema-style hashes (e.g., {type: "object", properties: {...}}) # Check both symbol and string keys to handle parsed JSON schema_type = schema[:type] || schema["type"] schema_properties = schema[:properties] || schema["properties"] if schema_type == "object" && schema_properties required_fields = schema[:required] || schema["required"] || [] schema_properties.each do |field, spec| # Build constraint options constraints = {} # Handle required fields constraints[:required] = required_fields.include?(field.to_s) || required_fields.include?(field) # Extract constraints from spec [ :enum, :minimum, :maximum ].each do |constraint| value = spec[constraint] || spec[constraint.to_s] constraints[constraint] = value if value end # Handle oneOf union types - default to string for compatibility # Check both symbol and string keys if spec[:oneOf] || spec["oneOf"] schema_class.string field, **constraints # Use string as most flexible type else case spec[:type] || spec["type"] when "string" schema_class.string field, **constraints when "number", "integer" schema_class.number field, **constraints when "boolean" schema_class.boolean field, **constraints when "array" # Handle array with items specification items_spec = spec[:items] || spec["items"] if items_spec items_type = items_spec[:type] || items_spec["type"] case items_type when "string" schema_class.array field, :string, **constraints when "number", "integer" schema_class.array field, :number, **constraints when "boolean" schema_class.array field, :boolean, **constraints else schema_class.array field, :string, **constraints end else schema_class.array field, :string, **constraints end else schema_class.string field, **constraints # fallback to string end end end else # Handle simple symbol-based schemas (e.g., {name: :string, age: :integer}) schema.each do |field, type| case type when :string schema_class.string field when :integer, :number schema_class.number field when :boolean schema_class.boolean field when :array schema_class.array field else schema_class.string field # fallback to string end end end schema_class end |
.call_llm(prompt, model: nil, temperature: nil, schema: nil, **options) ⇒ Object
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/ruby_llm/text/base.rb', line 4 def self.call_llm(prompt, model: nil, temperature: nil, schema: nil, **) model ||= RubyLLM.config.default_model temperature ||= RubyLLM::Text.config.temperature chat = RubyLLM.chat(model: model) chat = chat.with_temperature(temperature) if schema # Convert plain Hash schemas to RubyLLM::Schema objects if needed schema_obj = build_schema(schema) chat = chat.with_schema(schema_obj) end # Apply any additional options .each do |key, value| method_name = "with_#{key}" chat = chat.send(method_name, value) if chat.respond_to?(method_name) end response = chat.ask(prompt) response.content rescue => e raise RubyLLM::Text::Error, "LLM call failed: #{e.message}" end |
.clean_json_response(response) ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/ruby_llm/text/base.rb', line 28 def self.clean_json_response(response) # Remove markdown code block formatting if present cleaned = response.gsub(/^```json\n/, "").gsub(/\n```$/, "").strip # If still no JSON, try to extract JSON from mixed content if !cleaned.start_with?("{") && cleaned.include?("{") # Find JSON object in the response with proper brace matching brace_count = 0 start_pos = cleaned.index("{") if start_pos end_pos = start_pos cleaned[start_pos..-1].each_char.with_index(start_pos) do |char, i| if char == "{" brace_count += 1 elsif char == "}" brace_count -= 1 if brace_count == 0 end_pos = i break end end end if brace_count == 0 cleaned = cleaned[start_pos..end_pos] end end end cleaned end |