Class: ImageAnalysis::VisionAnalyzer

Inherits:
Object
  • Object
show all
Defined in:
app/services/image_analysis/vision_analyzer.rb

Overview

Service for analyzing images using Gemini Flash.
Generates AI descriptions of product images for metadata suggestions and CRM display.

Examples:

Analyze an image

result = ImageAnalysis::VisionAnalyzer.call(image)
result.description # => "A towel warmer mounted on a bathroom wall..."

Force regeneration

ImageAnalysis::VisionAnalyzer.call(image, force: true)

Defined Under Namespace

Classes: Result

Constant Summary collapse

MODEL =
AiModelConstants.id(:vision_analysis)
MAX_TOKENS =
500
PROMPT =
<<~PROMPT
  Describe EXACTLY what you see in this image.

  CRITICAL: Base your description ONLY on what is visually present.

  Describe:
  1. PEOPLE: Are there people? What are they doing?
  2. OBJECTS: What products, materials, or objects are visible?
  3. SETTING: What type of room/location? Indoor or outdoor?
  4. CONTEXT: Professional photo, installation, product shot, or diagram?

  For radiant heating products, look for:
  - Orange heating cables or mesh rolls
  - Black rubber mats with cables
  - Thermostats or control boxes
  - Towel warmers (metal bar racks)

  Be accurate. Write in plain text, no markdown.
PROMPT

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(image, force: false) ⇒ VisionAnalyzer

Returns a new instance of VisionAnalyzer.



46
47
48
49
# File 'app/services/image_analysis/vision_analyzer.rb', line 46

def initialize(image, force: false)
  @image = image
  @force = force
end

Class Method Details

.call(image, force: false) ⇒ Object



41
42
43
# File 'app/services/image_analysis/vision_analyzer.rb', line 41

def call(image, force: false)
  new(image, force: force).call
end

Instance Method Details

#callObject



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'app/services/image_analysis/vision_analyzer.rb', line 51

def call
  return Result.new(success?: false, error: 'Image not found') unless @image
  return Result.new(success?: false, error: 'Image is inactive') if @image.inactive?
  return Result.new(success?: true, description: @image.ai_visual_description) if already_analyzed?

  image_url = @image.ik_raw_url
  return Result.new(success?: false, error: 'No image URL available') unless image_url

  prompt = build_prompt
  description = describe_with_gemini(image_url, prompt)

  if description.present?
    save_description(description)
    Result.new(success?: true, description: description)
  else
    Result.new(success?: false, error: 'Empty description returned')
  end
rescue RubyLLM::Error => e
  Result.new(success?: false, error: "Vision error: #{e.message}")
rescue StandardError => e
  Result.new(success?: false, error: "Error: #{e.message}")
end