Class: RestaurantTags::AiHelpTitleTagWorker

Inherits:
ApplicationWorker show all
Defined in:
app/workers/restaurant_tags/ai_help_title_tag_worker.rb

Instance Method Summary collapse

Methods inherited from ApplicationWorker

unlimited_retry

Instance Method Details

#perform(restaurant_tag_id, translation_id = nil) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
59
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'app/workers/restaurant_tags/ai_help_title_tag_worker.rb', line 5

def perform(restaurant_tag_id, translation_id = nil)
  # Store processing status in Redis if translation_id provided
  store_processing_status(translation_id) if translation_id.present?

  restaurant_tag = RestaurantTag.find_by(id: restaurant_tag_id)

  if restaurant_tag.nil?
    error_msg = "RestaurantTag with ID #{restaurant_tag_id} not found."
    store_error_status(translation_id, error_msg) if translation_id.present?
    raise error_msg
  elsif restaurant_tag.title_en.blank?
    error_msg = "RestaurantTag with ID #{restaurant_tag_id} does not have a valid title_en."
    store_error_status(translation_id, error_msg) if translation_id.present?
    raise error_msg
  end

  prefix = restaurant_tag.title_en.split(':').first.strip
  last_word = restaurant_tag.title_en.split(':').last.strip

  # Get all available locales except English (source language)
  target_locales = MyLocaleManager::AVAILABLE_LOCALES.reject { |locale| locale == :en }

  # Build language names for the prompt
  language_map = {
    th: 'Thai',
    cn: 'Traditional Chinese (Taiwan/Hong Kong)',
    es: 'Spanish',
    fr: 'French',
    de: 'German',
    ru: 'Russian',
    ms: 'Malay',
    ko: 'Korean',
    ja: 'Japanese',
    id: 'Indonesian',
    vi: 'Vietnamese',
    zh: 'Simplified Chinese (Mainland China)',
  }

  language_list = target_locales.map { |locale| "#{locale}: #{language_map[locale]}" }.join("\n        ")
  json_keys = target_locales.map do |locale|
    "\"title_#{locale}\": \"Translated #{language_map[locale]} title\""
  end.join(",\n          ")

  prompt = <<~PROMPT
    Hungry Hub is an online reservation application for the best restaurant offers in Thailand.
    Hungry Hub has restaurant tags, which are keywords or short labels used to describe or categorize the type of cuisine, dining style, popular zone, shopping mall, BTS, MRT, Location, Facilities, Admin, Hashtag, Occasion, and Offer.
    These tags help customers quickly understand the restaurant's concept or theme and make it easier to find.

    Context: A single keyword label used to classify restaurants (e.g., "Seafood", "Vegan", "Fine Dining"). Appears in filters and search tags.

    You are a language specialist helping translate and generate titles for restaurant tags.

    Please help translate the following title into ALL these languages, using appropriate, natural phrasing for each:
    #{language_list}

    The text "#{last_word}" is the text I need you to translate.

    Provide translations for ALL languages listed above.

    Ensure that your response follows this JSON format exactly:

    {
      #{json_keys}
    }

    Important:
    - Provide ALL translations in a single JSON response
    - Use natural, culturally appropriate phrasing for each language
    - Maintain the same meaning and tone across all translations
    - For location names (BTS, MRT, Shopping Mall, Popular Zone), you may transliterate or keep the original name if more appropriate
  PROMPT

  faraday = OpenrouterClient.create_connection

  response = Retriable.retriable(
    tries: 3,
    base_interval: 1,
    multiplier: 2,
    on: [Faraday::TimeoutError, Faraday::ConnectionFailed],
  ) do
    faraday.post('/api/v1/chat/completions') do |req|
      req.headers['HTTP-Referer'] = 'https://hungryhub.com'
      req.headers['X-Title'] = 'HungryHub Restaurant Tag Translation'

      # Calculate required tokens based on text length and target languages
      estimated_tokens = (last_word.length / 2) + (last_word.length * target_locales.size * 2)
      max_tokens = [estimated_tokens, 8000].max.to_i

      req.body = {
        model: 'anthropic/claude-3.5-sonnet',
        messages: [
          { role: 'user', content: prompt },
        ],
        temperature: 0.3,
        max_tokens: max_tokens,
      }.to_json
    end
  end

  if response.success?
    response_data = JSON.parse(response.body)
    json_string = response_data['choices'][0]['message']['content']

    json_match = json_string.match(/\{.*\}/m)

    if json_match
      json_string = json_match[0]
      json_data = JSON.parse(json_string)

      # Add prefix to all translations and prepare update hash
      update_hash = {}
      target_locales.each do |locale|
        key = "title_#{locale}"
        if json_data[key].present?
          update_hash[key.to_sym] = "#{prefix}:#{json_data[key]}"
        end
      end

      # Update restaurant tag with all translations at once
      if update_hash.present?
        restaurant_tag.update(update_hash)

        BUSINESS_LOGGER.info(
          'AI translation completed for restaurant tag',
          {
            restaurant_tag_id: restaurant_tag_id,
            translated_locales: update_hash.keys,
            source_text: last_word,
            translation_id: translation_id,
          },
        )

        # Store success status in Redis
        store_success_status(translation_id, update_hash) if translation_id.present?
      else
        error_msg = 'Error: No valid translations found in the response'
        store_error_status(translation_id, error_msg) if translation_id.present?
        raise error_msg
      end
    else
      error_msg = "Error: No valid JSON found in the response, response: #{json_string}"
      store_error_status(translation_id, error_msg) if translation_id.present?
      raise error_msg
    end
  else
    error_msg = "HTTP error occurred: #{response.status} - #{response.body}"
    store_error_status(translation_id, error_msg) if translation_id.present?
    raise error_msg
  end
rescue StandardError => e
  APMErrorHandler.report(e, context: { restaurant_tag_id: restaurant_tag_id, translation_id: translation_id })
  store_error_status(translation_id, e.message) if translation_id.present?
  raise
end