Class: ReviewHeadlineGenerator

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

Overview

Generates a punchy, card-ready headline from a review's comment body.

Uses a fast LLM (Gemini Flash) to extract the most compelling insight
from the review text — something specific and quote-like that works as
the bold opener on a review card.

Usage:
headline = ReviewHeadlineGenerator.call("Installed in 2 hours, floors warm...")

=> "Installed in 2 hours and our floors were warm by morning"

Constant Summary collapse

SYSTEM_PROMPT =
<<~PROMPT.squish.freeze
  You are a marketing copywriter specializing in authentic customer testimonials.
  Your task: extract or craft a punchy, specific headline from a customer review.

  Rules:
  - 8 to 14 words maximum
  - Capture the single most compelling or emotional insight from the review
  - Be specific — avoid generic phrases like "Great product!" or "Excellent service!"
  - Use the customer's own words or tone where possible
  - Do NOT add a trailing period; a ! or ? is fine if it fits naturally
  - Reply with ONLY the headline text — no quotes, no extra commentary
PROMPT

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(review_text) ⇒ ReviewHeadlineGenerator

Returns a new instance of ReviewHeadlineGenerator.



32
33
34
# File 'app/services/review_headline_generator.rb', line 32

def initialize(review_text)
  @review_text = review_text.to_s.strip
end

Class Method Details

.call(review_text) ⇒ String

Returns the generated headline.

Parameters:

  • review_text (String)

    the raw review comment body

Returns:

  • (String)

    the generated headline



28
29
30
# File 'app/services/review_headline_generator.rb', line 28

def self.call(review_text)
  new(review_text).call
end

Instance Method Details

#callObject



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'app/services/review_headline_generator.rb', line 36

def call
  model_id = AiModelConstants.id(:redactor_gemini_flash)

  chat = RubyLLM.chat(model: model_id, provider: :gemini, assume_model_exists: true)
  chat.with_temperature(0.6)

  prompt = "#{SYSTEM_PROMPT}\n\nReview text:\n#{@review_text}"

  result = RubyLLM::Instrumentation.with(feature: 'review_headline') do
    chat.ask(prompt)
  end

  # Strip straight and curly quotes the LLM sometimes wraps the headline in.
  result.content.to_s.strip.gsub(/\A["\u201C\u2018]+|["\u201D\u2019]+\z/, '')
end