Class: Ragdoll::ImageToTextService

Inherits:
Object
  • Object
show all
Defined in:
app/services/ragdoll/image_to_text_service.rb

Defined Under Namespace

Classes: DescriptionError

Constant Summary collapse

DEFAULT_OPTIONS =
{
  model: "gemma3",
  provider: :ollama,
  assume_model_exists: true,
  temperature: 0.2,
  detail_level: :comprehensive
}.freeze
DEFAULT_FALLBACK_OPTIONS =
{
  model: "smollm2",
  provider: :ollama,
  assume_model_exists: true,
  temperature: 0.4
}.freeze
DETAIL_LEVELS =
{
  minimal: "Provide a brief, one-sentence description of the image.",
  standard: "Describe the main elements, objects, and overall composition of the image.",
  comprehensive: "Provide a detailed description including objects, people, settings, colors, mood, style, and any text visible in the image.",
  analytical: "Analyze the image thoroughly, describing composition, lighting, subjects, background, context, and any symbolic or artistic elements."
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(primary: DEFAULT_OPTIONS, fallback: DEFAULT_FALLBACK_OPTIONS, **options) ⇒ ImageToTextService

Returns a new instance of ImageToTextService.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'app/services/ragdoll/image_to_text_service.rb', line 37

def initialize(primary: DEFAULT_OPTIONS, fallback: DEFAULT_FALLBACK_OPTIONS, **options)
  @options = DEFAULT_OPTIONS.merge(options)
  @detail_level = @options[:detail_level] || :comprehensive

  configure_ruby_llm_globally

  # Setup primary model
  primary_opts = primary.dup
  primary_temp = primary_opts.delete(:temperature) || 0.2
  @primary_prompt = build_prompt(@detail_level)

  begin
    @primary = RubyLLM.chat(**primary_opts).with_temperature(primary_temp)
  rescue StandardError => e
    puts "❌ ImageToTextService: Primary model creation failed: #{e.message}"
    @primary = nil
  end

  # Setup fallback model
  fallback_opts = fallback.dup
  fallback_temp = fallback_opts.delete(:temperature) || 0.4

  begin
    @fallback = RubyLLM.chat(**fallback_opts).with_temperature(fallback_temp)
  rescue StandardError => e
    puts "❌ ImageToTextService: Fallback model creation failed: #{e.message}"
    @fallback = nil
  end

  if @primary.nil? && @fallback.nil?
    puts "⚠️  ImageToTextService: WARNING - No models available! Service will return metadata-based descriptions only."
  end
end

Class Method Details

.convert(file_path, **options) ⇒ Object



33
34
35
# File 'app/services/ragdoll/image_to_text_service.rb', line 33

def self.convert(file_path, **options)
  new(**options).convert(file_path)
end

Instance Method Details

#convert(file_path) ⇒ Object



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
# File 'app/services/ragdoll/image_to_text_service.rb', line 71

def convert(file_path)
  return "" unless File.exist?(file_path)
  return "" unless image_file?(file_path)

  start_time = Time.now
  @image_path = file_path

  # Try to read image and prepare data
  begin
    @image = Magick::Image.read(@image_path).first
    image_data = prepare_image_data
    return generate_fallback_description unless image_data
  rescue StandardError => e
    puts "❌ ImageToTextService: Failed to read image: #{e.message}"
    return generate_fallback_description
  end

  # Attempt vision model description
  if @primary
    description = attempt_vision_description(image_data)
    if description && !description.empty?
      elapsed = Time.now - start_time
      puts "✅ ImageToTextService: Vision description generated (#{elapsed.round(2)}s)"
      return description
    end
  end

  # Attempt fallback model with metadata
  if @fallback
    description = attempt_fallback_description
    if description && !description.empty?
      elapsed = Time.now - start_time
      puts "✅ ImageToTextService: Fallback description generated (#{elapsed.round(2)}s)"
      return description
    end
  end

  # Final fallback to metadata-based description
  elapsed = Time.now - start_time
  puts "🔚 ImageToTextService: Using metadata-based description (#{elapsed.round(2)}s)"
  generate_fallback_description
end

#supported_formatsObject



114
115
116
# File 'app/services/ragdoll/image_to_text_service.rb', line 114

def supported_formats
  %w[.jpg .jpeg .png .gif .bmp .webp .svg .ico .tiff .tif]
end