mistral_translator 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/README.md +189 -121
- data/README_TESTING.md +33 -0
- data/SECURITY.md +157 -0
- data/docs/.nojekyll +2 -0
- data/docs/404.html +30 -0
- data/docs/README.md +153 -0
- data/docs/advanced-usage/batch-processing.md +158 -0
- data/docs/advanced-usage/error-handling.md +106 -0
- data/docs/advanced-usage/monitoring.md +133 -0
- data/docs/advanced-usage/summarization.md +86 -0
- data/docs/advanced-usage/translations.md +141 -0
- data/docs/api-reference/callbacks.md +231 -0
- data/docs/api-reference/configuration.md +74 -0
- data/docs/api-reference/errors.md +673 -0
- data/docs/api-reference/methods.md +539 -0
- data/docs/getting-started.md +179 -0
- data/docs/index.html +27 -0
- data/docs/installation.md +142 -0
- data/docs/migration-0.1.0-to-0.2.0.md +61 -0
- data/docs/rails-integration/adapters.md +84 -0
- data/docs/rails-integration/controllers.md +107 -0
- data/docs/rails-integration/jobs.md +97 -0
- data/docs/rails-integration/setup.md +339 -0
- data/examples/basic_usage.rb +129 -102
- data/examples/batch-job.rb +511 -0
- data/examples/monitoring-setup.rb +499 -0
- data/examples/rails-model.rb +399 -0
- data/lib/mistral_translator/adapters.rb +261 -0
- data/lib/mistral_translator/client.rb +103 -100
- data/lib/mistral_translator/client_helpers.rb +161 -0
- data/lib/mistral_translator/configuration.rb +171 -1
- data/lib/mistral_translator/errors.rb +16 -0
- data/lib/mistral_translator/helpers.rb +292 -0
- data/lib/mistral_translator/helpers_extensions.rb +150 -0
- data/lib/mistral_translator/levenshtein_helpers.rb +40 -0
- data/lib/mistral_translator/logger.rb +28 -4
- data/lib/mistral_translator/prompt_builder.rb +93 -41
- data/lib/mistral_translator/prompt_helpers.rb +83 -0
- data/lib/mistral_translator/prompt_metadata_helpers.rb +42 -0
- data/lib/mistral_translator/response_parser.rb +194 -23
- data/lib/mistral_translator/security.rb +72 -0
- data/lib/mistral_translator/summarizer.rb +41 -2
- data/lib/mistral_translator/translator.rb +174 -98
- data/lib/mistral_translator/translator_helpers.rb +268 -0
- data/lib/mistral_translator/version.rb +1 -1
- data/lib/mistral_translator.rb +51 -25
- metadata +39 -3
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Module de sécurité optionnel - chargé seulement si nécessaire
|
4
|
+
module MistralTranslator
|
5
|
+
module Security
|
6
|
+
# Validation basique des entrées (version légère)
|
7
|
+
module BasicValidator
|
8
|
+
MAX_TEXT_LENGTH = 50_000
|
9
|
+
MIN_TEXT_LENGTH = 1
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def validate_text!(text)
|
13
|
+
# Accepter nil et texte vide - ce sont des cas d'usage légitimes
|
14
|
+
return "" if text.nil? || text.empty?
|
15
|
+
|
16
|
+
text_str = text.to_s
|
17
|
+
return "" if text_str.strip.empty?
|
18
|
+
|
19
|
+
raise ArgumentError, "Text too long (max #{MAX_TEXT_LENGTH} chars)" if text_str.length > MAX_TEXT_LENGTH
|
20
|
+
|
21
|
+
text_str
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate_batch!(texts)
|
25
|
+
raise ArgumentError, "Batch cannot be nil" if texts.nil?
|
26
|
+
raise ArgumentError, "Batch must be an array" unless texts.is_a?(Array)
|
27
|
+
raise ArgumentError, "Batch cannot be empty" if texts.empty?
|
28
|
+
raise ArgumentError, "Batch too large (max 20 items)" if texts.size > 20
|
29
|
+
|
30
|
+
texts.each { |text| validate_text!(text) }
|
31
|
+
texts
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Rate limiter basique (version légère)
|
37
|
+
class BasicRateLimiter
|
38
|
+
def initialize(max_requests: 50, window_seconds: 60)
|
39
|
+
@max_requests = max_requests
|
40
|
+
@window_seconds = window_seconds
|
41
|
+
@requests = []
|
42
|
+
@mutex = Mutex.new
|
43
|
+
end
|
44
|
+
|
45
|
+
def wait_and_record!
|
46
|
+
@mutex.synchronize do
|
47
|
+
cleanup_old_requests
|
48
|
+
if @requests.size >= @max_requests
|
49
|
+
wait_time = calculate_wait_time
|
50
|
+
sleep(wait_time) if wait_time.positive?
|
51
|
+
end
|
52
|
+
@requests << Time.now
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def cleanup_old_requests
|
59
|
+
cutoff_time = Time.now - @window_seconds
|
60
|
+
@requests.reject! { |request_time| request_time < cutoff_time }
|
61
|
+
end
|
62
|
+
|
63
|
+
def calculate_wait_time
|
64
|
+
return 0 if @requests.empty?
|
65
|
+
|
66
|
+
oldest_request = @requests.min
|
67
|
+
time_until_oldest_expires = (oldest_request + @window_seconds) - Time.now
|
68
|
+
[time_until_oldest_expires, 0].max
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "logger"
|
4
|
+
require_relative "security"
|
4
5
|
|
5
6
|
module MistralTranslator
|
6
7
|
class Summarizer
|
@@ -16,12 +17,18 @@ module MistralTranslator
|
|
16
17
|
# Résumé simple dans une langue donnée
|
17
18
|
def summarize(text, language: "fr", max_words: DEFAULT_MAX_WORDS)
|
18
19
|
log_debug("Starting summarize - language: #{language}, max_words: #{max_words}")
|
19
|
-
|
20
|
+
|
21
|
+
# Validation basique des entrées
|
22
|
+
validated_text = Security::BasicValidator.validate_text!(text)
|
23
|
+
raise ArgumentError, "Max words must be positive" unless max_words.is_a?(Integer) && max_words.positive?
|
24
|
+
|
25
|
+
# Si le texte est vide, retourner directement une chaîne vide
|
26
|
+
return "" if validated_text.empty?
|
20
27
|
|
21
28
|
target_locale = LocaleHelper.validate_locale!(language)
|
22
29
|
log_debug("Target locale validated: #{target_locale}")
|
23
30
|
|
24
|
-
cleaned_text = clean_document_content(
|
31
|
+
cleaned_text = clean_document_content(validated_text)
|
25
32
|
log_debug("Text cleaned, length: #{cleaned_text&.length}")
|
26
33
|
|
27
34
|
result = summarize_with_retry(cleaned_text, target_locale, max_words)
|
@@ -50,6 +57,21 @@ module MistralTranslator
|
|
50
57
|
raw_response = @client.complete(prompt)
|
51
58
|
|
52
59
|
result = ResponseParser.parse_summary_response(raw_response)
|
60
|
+
if result.nil? || result[:summary].nil? || result[:summary].empty?
|
61
|
+
# Fallback: si le JSON est manquant ou vide, on tente un résumé simple puis traduction
|
62
|
+
log_error(
|
63
|
+
"Empty or invalid summary response for summarize_and_translate. Using fallback. " \
|
64
|
+
"Raw length: #{raw_response.to_s.length}"
|
65
|
+
)
|
66
|
+
summary_only = summarize(cleaned_text, language: source_locale, max_words: max_words)
|
67
|
+
begin
|
68
|
+
return MistralTranslator.translate(summary_only, from: source_locale, to: target_locale)
|
69
|
+
rescue EmptyTranslationError, InvalidResponseError, RateLimitError => e
|
70
|
+
log_error("Translation fallback failed: #{e.class.name} - returning source summary")
|
71
|
+
return summary_only
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
53
75
|
result[:summary]
|
54
76
|
end
|
55
77
|
|
@@ -95,14 +117,20 @@ module MistralTranslator
|
|
95
117
|
|
96
118
|
private
|
97
119
|
|
120
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
98
121
|
def summarize_with_retry(text, target_locale, max_words, attempt = 0)
|
99
122
|
log_debug("Summarize attempt #{attempt + 1} for #{target_locale}")
|
100
123
|
|
101
124
|
prompt = PromptBuilder.summary_prompt(text, max_words, target_locale)
|
102
125
|
raw_response = @client.complete(prompt)
|
126
|
+
response_len = raw_response&.length || 0
|
103
127
|
|
104
128
|
result = ResponseParser.parse_summary_response(raw_response)
|
105
129
|
if result.nil? || result[:summary].nil? || result[:summary].empty?
|
130
|
+
log_error(
|
131
|
+
"Empty or invalid summary response for #{target_locale}. " \
|
132
|
+
"Raw response length: #{response_len}"
|
133
|
+
)
|
106
134
|
raise EmptyTranslationError, "Empty summary received"
|
107
135
|
end
|
108
136
|
|
@@ -123,6 +151,7 @@ module MistralTranslator
|
|
123
151
|
sleep(DEFAULT_RETRY_DELAY)
|
124
152
|
retry
|
125
153
|
end
|
154
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
126
155
|
|
127
156
|
def build_summary_translation_prompt(text, source_locale, target_locale, max_words)
|
128
157
|
source_name = LocaleHelper.locale_to_language(source_locale)
|
@@ -222,5 +251,15 @@ module MistralTranslator
|
|
222
251
|
|
223
252
|
puts "[MistralTranslator] #{message}"
|
224
253
|
end
|
254
|
+
|
255
|
+
def log_error(message)
|
256
|
+
# Utiliser le logger centralisé
|
257
|
+
Logger.warn(message, sensitive: false)
|
258
|
+
|
259
|
+
# Pour les tests, optionnellement afficher en stdout
|
260
|
+
return unless ENV["MISTRAL_TRANSLATOR_TEST_OUTPUT"] == "true"
|
261
|
+
|
262
|
+
puts "[MistralTranslator] ERROR: #{message}"
|
263
|
+
end
|
225
264
|
end
|
226
265
|
end
|
@@ -1,9 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "logger"
|
4
|
+
require_relative "translator_helpers"
|
5
|
+
require_relative "security"
|
4
6
|
|
5
7
|
module MistralTranslator
|
6
8
|
class Translator
|
9
|
+
include TranslatorHelpers::InputValidator
|
10
|
+
include TranslatorHelpers::RetryHandler
|
11
|
+
include TranslatorHelpers::LoggingHelper
|
12
|
+
include TranslatorHelpers::PromptHandler
|
13
|
+
include TranslatorHelpers::AnalysisHelper
|
14
|
+
include TranslatorHelpers::RequestHelper
|
15
|
+
include TranslatorHelpers::MultiTargetHelper
|
16
|
+
|
7
17
|
DEFAULT_RETRY_COUNT = 3
|
8
18
|
DEFAULT_RETRY_DELAY = 2
|
9
19
|
|
@@ -12,50 +22,77 @@ module MistralTranslator
|
|
12
22
|
end
|
13
23
|
|
14
24
|
# Traduction simple d'un texte vers une langue
|
15
|
-
def translate(text, from:, to
|
16
|
-
|
25
|
+
def translate(text, from:, to:, **options)
|
26
|
+
# Validation basique des entrées
|
27
|
+
validated_text = Security::BasicValidator.validate_text!(text)
|
28
|
+
|
29
|
+
# Si le texte est vide, retourner directement une chaîne vide
|
30
|
+
return "" if validated_text.empty?
|
17
31
|
|
18
32
|
source_locale = LocaleHelper.validate_locale!(from)
|
19
33
|
target_locale = LocaleHelper.validate_locale!(to)
|
20
34
|
|
21
|
-
|
35
|
+
# Si même langue source et cible, retourner le texte original
|
36
|
+
return validated_text if source_locale == target_locale
|
37
|
+
|
38
|
+
translate_with_retry(validated_text, source_locale, target_locale,
|
39
|
+
context: options[:context],
|
40
|
+
glossary: options[:glossary],
|
41
|
+
preserve_html: options.fetch(:preserve_html, false))
|
42
|
+
end
|
43
|
+
|
44
|
+
# Traduction avec score de confiance (expérimental)
|
45
|
+
def translate_with_confidence(text, from:, to:, context: nil, glossary: nil)
|
46
|
+
result = begin
|
47
|
+
translate(text, from: from, to: to, context: context, glossary: glossary)
|
48
|
+
rescue EmptyTranslationError
|
49
|
+
""
|
50
|
+
end
|
51
|
+
|
52
|
+
# Score de confiance basique basé sur la longueur et la cohérence
|
53
|
+
confidence = calculate_confidence_score(text, result, from, to)
|
54
|
+
|
55
|
+
{
|
56
|
+
translation: result,
|
57
|
+
confidence: confidence,
|
58
|
+
metadata: {
|
59
|
+
source_locale: from,
|
60
|
+
target_locale: to,
|
61
|
+
original_length: text.length,
|
62
|
+
translated_length: result.length
|
63
|
+
}
|
64
|
+
}
|
22
65
|
end
|
23
66
|
|
24
|
-
# Traduction vers plusieurs langues
|
25
|
-
def translate_to_multiple(text, from:, to
|
67
|
+
# Traduction vers plusieurs langues avec support du batch
|
68
|
+
def translate_to_multiple(text, from:, to:, **options)
|
26
69
|
validate_translation_inputs!(text, from, to)
|
27
70
|
|
28
71
|
source_locale = LocaleHelper.validate_locale!(from)
|
29
72
|
target_locales = Array(to).map { |locale| LocaleHelper.validate_locale!(locale) }
|
30
73
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
74
|
+
if options[:use_batch] && target_locales.size > 3
|
75
|
+
translate_to_multiple_batch(text, source_locale, target_locales, context: options[:context],
|
76
|
+
glossary: options[:glossary])
|
77
|
+
else
|
78
|
+
translate_to_multiple_sequential(text, source_locale, target_locales, context: options[:context],
|
79
|
+
glossary: options[:glossary])
|
37
80
|
end
|
38
|
-
|
39
|
-
results
|
40
81
|
end
|
41
82
|
|
42
83
|
# Traduction en lot (plusieurs textes vers une langue)
|
43
|
-
def translate_batch(texts, from:, to:)
|
44
|
-
|
45
|
-
|
84
|
+
def translate_batch(texts, from:, to:, context: nil, glossary: nil)
|
85
|
+
validated_texts = Security::BasicValidator.validate_batch!(texts)
|
46
86
|
source_locale = LocaleHelper.validate_locale!(from)
|
47
87
|
target_locale = LocaleHelper.validate_locale!(to)
|
48
88
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
else
|
53
|
-
translate_small_batch(texts, source_locale, target_locale)
|
54
|
-
end
|
89
|
+
return handle_same_language_batch(validated_texts) if source_locale == target_locale
|
90
|
+
|
91
|
+
process_mixed_batch(validated_texts, source_locale, target_locale, context, glossary)
|
55
92
|
end
|
56
93
|
|
57
|
-
# Auto-détection de la langue source
|
58
|
-
def translate_auto(text, to:)
|
94
|
+
# Auto-détection de la langue source avec support du contexte
|
95
|
+
def translate_auto(text, to:, context: nil, glossary: nil)
|
59
96
|
target_locale = LocaleHelper.validate_locale!(to)
|
60
97
|
|
61
98
|
# Premier appel pour détecter la langue
|
@@ -63,115 +100,154 @@ module MistralTranslator
|
|
63
100
|
detection_response = @client.complete(detection_prompt)
|
64
101
|
detected_language = parse_language_detection(detection_response)
|
65
102
|
|
66
|
-
#
|
67
|
-
|
103
|
+
# Vérifier que la langue détectée est différente de la cible
|
104
|
+
if detected_language == target_locale
|
105
|
+
# Si même langue, retourner le texte original
|
106
|
+
return text
|
107
|
+
end
|
108
|
+
|
109
|
+
# Puis traduction normale avec contexte
|
110
|
+
translate(text, from: detected_language, to: target_locale, context: context, glossary: glossary)
|
68
111
|
end
|
69
112
|
|
70
113
|
private
|
71
114
|
|
72
|
-
|
73
|
-
|
74
|
-
|
115
|
+
# Ces méthodes sont héritées via MultiTargetHelper
|
116
|
+
|
117
|
+
def handle_same_language_batch(validated_texts)
|
118
|
+
validated_texts.each_with_index.to_h { |text, index| [index, text] }
|
119
|
+
end
|
75
120
|
|
76
|
-
|
77
|
-
|
121
|
+
def process_mixed_batch(validated_texts, source_locale, target_locale, context, glossary)
|
122
|
+
results = {}
|
123
|
+
batch_config = { source_locale: source_locale, target_locale: target_locale, context: context,
|
124
|
+
glossary: glossary }
|
125
|
+
requests = build_batch_requests(validated_texts, batch_config, results)
|
78
126
|
|
79
|
-
|
80
|
-
rescue EmptyTranslationError, InvalidResponseError => e
|
81
|
-
raise e unless attempt < DEFAULT_RETRY_COUNT
|
127
|
+
process_batch_requests(requests, results) if requests.any?
|
82
128
|
|
83
|
-
|
84
|
-
log_retry(e, attempt + 1, wait_time)
|
85
|
-
sleep(wait_time)
|
86
|
-
translate_with_retry(text, source_locale, target_locale, attempt + 1)
|
87
|
-
rescue RateLimitError => e
|
88
|
-
log_rate_limit_hit(source_locale, target_locale)
|
89
|
-
sleep(DEFAULT_RETRY_DELAY)
|
90
|
-
retry
|
129
|
+
results
|
91
130
|
end
|
92
131
|
|
93
|
-
def
|
94
|
-
|
95
|
-
|
132
|
+
def build_batch_requests(validated_texts, batch_config, results)
|
133
|
+
requests = []
|
134
|
+
|
135
|
+
validated_texts.each_with_index do |text, index|
|
136
|
+
if text.nil? || text.empty?
|
137
|
+
results[index] = ""
|
138
|
+
else
|
139
|
+
requests << create_batch_request(text, batch_config, index)
|
140
|
+
end
|
141
|
+
end
|
96
142
|
|
97
|
-
|
143
|
+
requests
|
144
|
+
end
|
145
|
+
|
146
|
+
def create_batch_request(text, batch_config, index)
|
147
|
+
{
|
148
|
+
prompt: build_translation_prompt(text, batch_config[:source_locale], batch_config[:target_locale],
|
149
|
+
context: batch_config[:context], glossary: batch_config[:glossary]),
|
150
|
+
from: batch_config[:source_locale],
|
151
|
+
to: batch_config[:target_locale],
|
152
|
+
index: index,
|
153
|
+
original_text: text
|
154
|
+
}
|
155
|
+
end
|
98
156
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
157
|
+
def process_batch_requests(requests, results)
|
158
|
+
batch_results = @client.translate_batch(requests, batch_size: 10)
|
159
|
+
batch_results.each do |result|
|
160
|
+
process_batch_result(result, results)
|
103
161
|
end
|
104
162
|
end
|
105
163
|
|
106
|
-
def
|
107
|
-
|
164
|
+
def process_batch_result(result, results)
|
165
|
+
index = result[:original_request][:index]
|
166
|
+
results[index] = (parse_batch_response(result[:result]) if result[:success])
|
167
|
+
end
|
108
168
|
|
109
|
-
|
110
|
-
|
169
|
+
def parse_batch_response(response)
|
170
|
+
parsed_result = ResponseParser.parse_translation_response(response)
|
171
|
+
parsed_result[:translated] if parsed_result
|
172
|
+
rescue EmptyTranslationError
|
173
|
+
""
|
174
|
+
end
|
111
175
|
|
112
|
-
|
176
|
+
def translate_with_retry(text, source_locale, target_locale, attempt = 0, **options)
|
177
|
+
prompt = build_prompt_for_retry(text, source_locale, target_locale, **options)
|
178
|
+
request_context = build_request_context(source_locale, target_locale, attempt)
|
113
179
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
180
|
+
raw_response = perform_client_request(prompt, request_context)
|
181
|
+
extract_translated_text!(raw_response)
|
182
|
+
rescue EmptyTranslationError, InvalidResponseError => e
|
183
|
+
handle_retryable_error!(e, attempt) do
|
184
|
+
translate_with_retry(text, source_locale, target_locale, attempt + 1, **options)
|
119
185
|
end
|
120
|
-
|
121
|
-
|
186
|
+
rescue RateLimitError
|
187
|
+
log_rate_limit_hit(source_locale, target_locale)
|
188
|
+
sleep(DEFAULT_RETRY_DELAY)
|
189
|
+
retry
|
122
190
|
end
|
123
191
|
|
124
|
-
|
125
|
-
PromptBuilder.language_detection_prompt(text)
|
126
|
-
end
|
192
|
+
# Ces méthodes sont héritées via RequestHelper
|
127
193
|
|
128
|
-
|
129
|
-
json_content = response.match(/\{.*\}/m)&.[](0)
|
130
|
-
return "en" unless json_content # Défaut en anglais si détection échoue
|
194
|
+
# Log via LoggingHelper
|
131
195
|
|
132
|
-
|
133
|
-
|
196
|
+
def build_translation_prompt(text, source_locale, target_locale, **options)
|
197
|
+
base_prompt = PromptBuilder.translation_prompt(text, source_locale, target_locale,
|
198
|
+
preserve_html: options.fetch(:preserve_html, false))
|
134
199
|
|
135
|
-
|
136
|
-
|
137
|
-
|
200
|
+
# Enrichir le prompt avec le contexte et le glossaire
|
201
|
+
context_present = options[:context] && !options[:context].to_s.strip.empty?
|
202
|
+
glossary_present =
|
203
|
+
(options[:glossary].is_a?(Hash) && options[:glossary].any?) ||
|
204
|
+
(options[:glossary].is_a?(String) && !options[:glossary].to_s.strip.empty?)
|
205
|
+
if context_present || glossary_present
|
206
|
+
enrich_prompt_with_context(base_prompt, options[:context], options[:glossary])
|
207
|
+
else
|
208
|
+
base_prompt
|
209
|
+
end
|
138
210
|
end
|
139
211
|
|
140
|
-
def
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
212
|
+
def enrich_prompt_with_context(base_prompt, context, glossary)
|
213
|
+
enriched_parts = []
|
214
|
+
context_part = build_context_enrichment(context)
|
215
|
+
glossary_part = build_glossary_enrichment(glossary)
|
216
|
+
enriched_parts << context_part if context_part
|
217
|
+
enriched_parts << glossary_part if glossary_part
|
218
|
+
|
219
|
+
return base_prompt if enriched_parts.empty?
|
220
|
+
|
221
|
+
enriched_context = enriched_parts.join("\n")
|
222
|
+
base_prompt.sub("RÈGLES :", "#{enriched_context}\n\nRÈGLES :")
|
145
223
|
end
|
146
224
|
|
147
|
-
def
|
148
|
-
|
149
|
-
target_languages = Array(to)
|
150
|
-
raise ArgumentError, "Target languages cannot be empty" if target_languages.empty?
|
225
|
+
def build_context_enrichment(context)
|
226
|
+
return nil if context.nil? || context.to_s.strip.empty?
|
151
227
|
|
152
|
-
|
228
|
+
"CONTEXTE : #{context}"
|
153
229
|
end
|
154
230
|
|
155
|
-
def
|
156
|
-
|
157
|
-
|
158
|
-
|
231
|
+
def build_glossary_enrichment(glossary)
|
232
|
+
return nil if glossary.nil?
|
233
|
+
return build_glossary_hash(glossary) if glossary.is_a?(Hash)
|
234
|
+
return build_glossary_string(glossary) if glossary.is_a?(String)
|
159
235
|
|
160
|
-
|
161
|
-
raise ArgumentError, "Text at index #{index} cannot be nil or empty" if text.nil? || text.empty?
|
162
|
-
end
|
236
|
+
nil
|
163
237
|
end
|
164
238
|
|
165
|
-
def
|
166
|
-
|
167
|
-
|
168
|
-
|
239
|
+
def build_glossary_hash(glossary_hash)
|
240
|
+
return nil if glossary_hash.empty?
|
241
|
+
|
242
|
+
glossary_text = glossary_hash.map { |key, value| "#{key} → #{value}" }.join(", ")
|
243
|
+
"GLOSSAIRE (à respecter strictement) : #{glossary_text}"
|
169
244
|
end
|
170
245
|
|
171
|
-
def
|
172
|
-
|
173
|
-
|
174
|
-
|
246
|
+
def build_glossary_string(glossary_string)
|
247
|
+
value = glossary_string.to_s.strip
|
248
|
+
return nil if value.empty?
|
249
|
+
|
250
|
+
"GLOSSAIRE : #{value}"
|
175
251
|
end
|
176
252
|
end
|
177
253
|
end
|