mistral_translator 0.2.1 → 0.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1736f848f5877e51c319bb4584e110e863a6e44464af7f05133d3ea2e2f96fd6
4
- data.tar.gz: bd06e5e900f129e662fb2ca90b5c241e55ac86511710d7c4f0cc373fca6c4c2c
3
+ metadata.gz: 5e52138d192d693042abbc570da3a9ed1f7f56acaed881c46a59df100dc476a0
4
+ data.tar.gz: 74a169d74c5248dc7ec1cab84c5892018eeacf3ba398bfb0b0a3c41a98d87ae9
5
5
  SHA512:
6
- metadata.gz: 3f57b5f9652c5b8327de801d5e391eb41f3c6915a9d4551e4ee57a2a61369ead60cffe1c72a5a35ccf089f23401d41d5685480331fe97786d9af7a661c303349
7
- data.tar.gz: 672f5aa7932401c2bae6c2ced94058d60ffb6dfab3fb5d6760896f61ed26e5f4eef7b2491697d6680e544b97612f774b4f6fed95b66d0127984d1863592e1509
6
+ metadata.gz: 3f324d7cb87b75c340d1875fc6189c13c39dfe26c1878ff187c30c04674b88549c813be6f73c644b2b01a5f28ed5e88da55351c86b37800d9648835113f58b3e
7
+ data.tar.gz: 0d9ac452969b457ed81cd34295e564d869c3e8f254e0807a8d473af8c99fbcb10f0d2ea5bc5abea518a0f7bd59d978428dac8e5c25864c63a4fa61d0dfb1617a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2025-12-09
4
+
5
+ ### Added
6
+
7
+ - Thread-safe metrics with Mutex protection for concurrent usage
8
+ - Connection pooling with net-http-persistent (automatic connection reuse)
9
+ - SSL/TLS configuration options: `ssl_verify_mode`, `ssl_ca_file`, `ssl_ca_path`, `ssl_timeout`
10
+ - Thread-safe logger cache for `warn_once` method
11
+ - Comprehensive concurrent/async documentation with SolidQueue, Sidekiq, Concurrent Ruby examples
12
+ - 39 new tests for thread-safety, SSL configuration, and connection pooling
13
+
14
+ ### Changed
15
+
16
+ - HTTP client now uses Net::HTTP::Persistent for better performance
17
+ - Configuration metrics now thread-safe across concurrent requests
18
+ - Documentation reorganized with SolidQueue as recommended background job backend
19
+
20
+ ### Performance
21
+
22
+ - Connection pooling reduces TCP handshake overhead
23
+ - Thread-safe implementation enables safe concurrent translations
24
+ - Configurable SSL timeout for production environments
25
+
3
26
  ## [0.2.0] - 2025-09-09
4
27
 
5
28
  ### Added
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,70 @@
1
+ # Contributing to MistralTranslator
2
+
3
+ Thank you for your interest in contributing to MistralTranslator! We welcome contributions of all kinds.
4
+
5
+ ## Development Setup
6
+
7
+ ### Prerequisites
8
+
9
+ - Ruby 3.2+
10
+ - Bundler
11
+
12
+ ### Installation
13
+
14
+ ```bash
15
+ git clone https://github.com/your-username/mistral_translator.git
16
+ cd mistral_translator
17
+ bundle install
18
+ ```
19
+
20
+ ## Running Tests
21
+
22
+ ```bash
23
+ # Unit tests (no API key required)
24
+ bundle exec rspec
25
+
26
+ # Integration tests (requires real API key)
27
+ export MISTRAL_API_KEY=your_key && bundle exec rspec spec/integration/mistral_api_integration_spec.rb
28
+
29
+ # All tests with coverage
30
+ bundle exec rspec --format documentation
31
+ ```
32
+
33
+ ## Contributing Guidelines
34
+
35
+ ### Reporting Issues
36
+
37
+ - Use GitHub Issues for bug reports and feature requests
38
+ - Include Ruby version, gem version, and relevant code snippets
39
+ - For API-related issues, include sanitized request/response examples
40
+
41
+ ### Pull Requests
42
+
43
+ 1. Fork the repository
44
+ 2. Create a feature branch: `git checkout -b feature/your-feature`
45
+ 3. Make your changes with tests
46
+ 4. Ensure all tests pass: `bundle exec rspec`
47
+ 5. Follow Ruby style conventions (we use RuboCop - no error) : `bundle exec rubocop`
48
+ 6. Submit a pull request with a clear description
49
+
50
+ ### Code Standards
51
+
52
+ - Write tests for new features and bug fixes
53
+ - Follow existing code patterns and architecture
54
+ - Update documentation for public API changes
55
+ - Keep commits atomic and well-described
56
+
57
+ ### Testing
58
+
59
+ - Unit tests should not require API calls (use mocks/stubs)
60
+ - Integration tests should use VCR cassettes when possible
61
+ - Test edge cases and error conditions
62
+ - Test coverage must be at 100% (for now)
63
+
64
+ ## Release Process
65
+
66
+ Releases are handled by maintainers following semantic versioning.
67
+
68
+ ## Questions?
69
+
70
+ Feel free to open an issue for any questions about contributing.
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
- The MIT License (MIT)
1
+ MIT License
2
2
 
3
- Copyright (c) 2025 PierreArko
3
+ Copyright (c) 2024 Pierre Cherbero (peyochanchan)
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
9
  copies of the Software, and to permit persons to whom the Software is
10
10
  furnished to do so, subject to the following conditions:
11
11
 
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
14
 
15
15
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
16
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
17
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -2,8 +2,10 @@
2
2
 
3
3
  Ruby gem for AI-powered translation and text summarization using Mistral AI API, with advanced Rails support.
4
4
 
5
+ [![Ruby](https://img.shields.io/badge/ruby-%23CC342D.svg?style=flat&logo=ruby&logoColor=white)](https://www.ruby-lang.org/)
5
6
  [![Gem Version](https://badge.fury.io/rb/mistral_translator.svg)](https://badge.fury.io/rb/mistral_translator)
6
-
7
+ [![RSpec](https://img.shields.io/badge/tested%20with-RSpec-red.svg)](https://rspec.info/)
8
+ [![RuboCop](https://img.shields.io/badge/code%20style-RuboCop-brightgreen.svg)](https://rubocop.org/)
7
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
10
 
9
11
  ## Quick Start
@@ -30,7 +32,7 @@ MistralTranslator.translate("Bonjour le monde", from: "fr", to: "en")
30
32
  - **Multi-level summarization** with translation
31
33
  - **Robust error handling** with automatic retry and fallback
32
34
  - **Complete monitoring** with metrics and callbacks
33
- - **Asynchronous jobs** for background processing
35
+ - **Thread-safe & concurrent** with connection pooling ([docs](https://peyochanchan.github.io/mistral_translator/#/advanced-usage/concurrent-async))
34
36
  - **Built-in rate limiting** and security
35
37
 
36
38
  ## Supported Languages
@@ -222,11 +224,11 @@ The complete documentation includes:
222
224
 
223
225
  ### Quick Links
224
226
 
225
- - [Installation & Setup](https://peyochanchan.github.io/mistral_translator/installation)
226
- - [Rails Integration Guide](https://peyochanchan.github.io/mistral_translator/rails-integration/)
227
- - [API Methods Reference](https://peyochanchan.github.io/mistral_translator/api-reference/methods)
228
- - [Error Handling Guide](https://peyochanchan.github.io/mistral_translator/api-reference/errors)
229
- - [Monitoring Setup](https://peyochanchan.github.io/mistral_translator/advanced-usage/monitoring)
227
+ - [Installation & Setup](https://peyochanchan.github.io/mistral_translator/#/installation)
228
+ - [Rails Integration Guide](https://peyochanchan.github.io/mistral_translator/#/rails-integration/setup)
229
+ - [API Methods Reference](https://peyochanchan.github.io/mistral_translator/#/api-reference/methods)
230
+ - [Error Handling Guide](https://peyochanchan.github.io/mistral_translator/#/api-reference/errors)
231
+ - [Monitoring Setup](https://peyochanchan.github.io/mistral_translator/#/advanced-usage/monitoring)
230
232
 
231
233
  ## Requirements
232
234
 
@@ -237,14 +239,28 @@ The complete documentation includes:
237
239
  ## Testing
238
240
 
239
241
  ```bash
240
- # Run tests
241
- bundle exec rspec
242
+ # Install dependencies
243
+ bundle install
242
244
 
243
- # Run with coverage
244
- COVERAGE=true bundle exec rspec
245
+ # Run all unit tests (no API key required)
246
+ bundle exec rspec spec/mistral_translator/
245
247
 
246
- # Integration tests (requires API key)
247
- MISTRAL_API_KEY=your_key bundle exec rspec --tag integration
248
+ # Run all tests with documentation format
249
+ bundle exec rspec --format documentation
250
+ ```
251
+
252
+ ### Integration Tests
253
+
254
+ Integration tests require a real Mistral API key. **Never commit API keys to version control.**
255
+
256
+ ```bash
257
+ # Set API key via environment variable (recommended)
258
+ export MISTRAL_API_KEY="your_api_key_here"
259
+
260
+ # Run integration tests
261
+ bundle exec rspec spec/integration/
262
+
263
+ **Security Note:** Use a dedicated test API key with limited quotas for integration testing.
248
264
  ```
249
265
 
250
266
  ## Contributing
@@ -259,15 +275,24 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines.
259
275
 
260
276
  ## License
261
277
 
262
- MIT License. See [LICENSE](LICENSE) for details.
278
+ MIT License. See [LICENSE](LICENSE.txt) for details.
263
279
 
264
280
  ## Support
265
281
 
266
282
  - 📖 [Documentation](https://peyochanchan.github.io/mistral_translator/)
267
283
  - 🐛 [Issues](../../issues)
268
- - 💬 [Discussions](../../discussions)
269
284
  - 📧 Support: Create an issue for help
270
285
 
286
+ ### ☕ Support the Project
287
+
288
+ If this gem helps you, consider supporting its development:
289
+
290
+ [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-FFDD00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://buymeacoffee.com/peyochanchan)
291
+
271
292
  ---
272
293
 
273
294
  Built with Ruby ❤️ by [@peyochanchan](https://github.com/peyochanchan)
295
+
296
+ ```
297
+
298
+ ```
@@ -0,0 +1,270 @@
1
+ # Concurrent & Asynchronous Processing
2
+
3
+ MistralTranslator is thread-safe and optimized for concurrent usage with protected metrics, connection pooling, and built-in rate limiting.
4
+
5
+ ## Thread Safety
6
+
7
+ **Protected components:**
8
+ - Metrics tracking with Mutex
9
+ - Logger cache with concurrent access control
10
+ - Rate limiter with thread synchronization
11
+ - HTTP connection pooling (Net::HTTP::Persistent)
12
+
13
+ ## Ruby Threads
14
+
15
+ ### Basic Usage
16
+
17
+ ```ruby
18
+ languages = ['fr', 'es', 'de', 'it']
19
+ threads = languages.map do |lang|
20
+ Thread.new do
21
+ MistralTranslator.translate("Hello", from: 'en', to: lang)
22
+ rescue MistralTranslator::Error => e
23
+ { error: e.message }
24
+ end
25
+ end
26
+
27
+ results = threads.map(&:value)
28
+ ```
29
+
30
+ ### Thread Pool Pattern
31
+
32
+ ```ruby
33
+ # Safe: Limited threads
34
+ texts.each_slice(5) do |batch|
35
+ threads = batch.map { |t| Thread.new { translate(t) } }
36
+ threads.each(&:join)
37
+ end
38
+
39
+ # Dangerous: Unlimited threads can exhaust memory
40
+ texts.map { |t| Thread.new { translate(t) } } # Avoid this
41
+ ```
42
+
43
+ ## Concurrent Ruby (Recommended)
44
+
45
+ ### Installation
46
+
47
+ ```ruby
48
+ # Gemfile
49
+ gem 'concurrent-ruby', '~> 1.2'
50
+ ```
51
+
52
+ ### Fixed Thread Pool
53
+
54
+ ```ruby
55
+ require 'concurrent'
56
+
57
+ pool = Concurrent::FixedThreadPool.new(5)
58
+
59
+ futures = languages.map do |lang|
60
+ Concurrent::Future.execute(executor: pool) do
61
+ MistralTranslator.translate(text, from: 'en', to: lang)
62
+ end
63
+ end
64
+
65
+ results = futures.map(&:value)
66
+
67
+ pool.shutdown
68
+ pool.wait_for_termination
69
+ ```
70
+
71
+ ### Promise Chains
72
+
73
+ ```ruby
74
+ promise = Concurrent::Promise.execute do
75
+ MistralTranslator.translate_auto(text, to: 'en')
76
+ end.then do |english|
77
+ target_langs.map do |lang|
78
+ MistralTranslator.translate(english, from: 'en', to: lang)
79
+ end
80
+ end.rescue do |error|
81
+ Rails.logger.error "Pipeline failed: #{error}"
82
+ []
83
+ end
84
+
85
+ results = promise.value
86
+ ```
87
+
88
+ ## Background Jobs
89
+
90
+ ### SolidQueue (Rails 8+, Recommended)
91
+
92
+ SolidQueue is the default ActiveJob backend in Rails 8, database-backed and Redis-free.
93
+
94
+ ```ruby
95
+ # config/database.yml - SolidQueue uses your existing database
96
+ production:
97
+ primary:
98
+ <<: *default
99
+ queue: # Separate DB for jobs (optional)
100
+ <<: *default
101
+ database: app_queue
102
+ migrations_paths: db/queue_migrate
103
+
104
+ # app/jobs/translation_job.rb
105
+ class TranslationJob < ApplicationJob
106
+ queue_as :translations
107
+
108
+ retry_on MistralTranslator::RateLimitError, wait: :polynomially_longer
109
+ retry_on MistralTranslator::ApiError, wait: 5.seconds, attempts: 3
110
+
111
+ def perform(text, from_locale, to_locale)
112
+ MistralTranslator.translate(text, from: from_locale, to: to_locale)
113
+ end
114
+ end
115
+
116
+ # Usage
117
+ languages.each { |lang| TranslationJob.perform_later(text, 'en', lang) }
118
+ ```
119
+
120
+ **Configuration:**
121
+ ```ruby
122
+ # config/recurring.yml - Schedule periodic translations
123
+ production:
124
+ sync_translations:
125
+ class: TranslationSyncJob
126
+ schedule: every day at 3am
127
+ queue: translations
128
+ ```
129
+
130
+ ### Sidekiq / Other Backends
131
+
132
+ Compatible with any ActiveJob backend:
133
+
134
+ ```ruby
135
+ class TranslationJob < ApplicationJob
136
+ queue_as :translations
137
+ retry_on MistralTranslator::RateLimitError, wait: :exponentially_longer
138
+
139
+ def perform(text, from, to)
140
+ MistralTranslator.translate(text, from: from, to: to)
141
+ end
142
+ end
143
+ ```
144
+
145
+ ## Performance Best Practices
146
+
147
+ ### Configuration for Concurrent Use
148
+
149
+ ```ruby
150
+ MistralTranslator.configure do |config|
151
+ config.enable_metrics = true
152
+ config.retry_delays = [1, 2, 4, 8] # Shorter for concurrent use
153
+
154
+ config.on_rate_limit = ->(from, to, wait, attempt, ts) {
155
+ Rails.logger.warn "Rate limit: #{wait}s wait"
156
+ }
157
+ end
158
+ ```
159
+
160
+ ### Recommendations
161
+
162
+ **Do:**
163
+ - Use fixed thread pools (5-10 threads)
164
+ - Enable metrics for monitoring
165
+ - Use background jobs for non-critical tasks
166
+ - Batch similar requests
167
+
168
+ **Avoid:**
169
+ - Unlimited thread creation
170
+ - Blocking user requests with translations
171
+ - Sharing API keys across environments
172
+
173
+ ## Rate Limiting
174
+
175
+ The built-in rate limiter (50 requests/60s) is thread-safe but per-process.
176
+
177
+ **Multiple processes:** Each has its own limiter. With 4 Puma workers = 200 requests/min total.
178
+
179
+ ### Custom Rate Limiter
180
+
181
+ ```ruby
182
+ class RedisRateLimiter
183
+ def wait_and_record!
184
+ key = "mistral:#{Time.now.to_i / 60}"
185
+ count = REDIS.incr(key)
186
+ REDIS.expire(key, 60) if count == 1
187
+
188
+ sleep(60 - Time.now.to_i % 60) if count > 50
189
+ end
190
+ end
191
+
192
+ client = MistralTranslator::Client.new(
193
+ rate_limiter: RedisRateLimiter.new
194
+ )
195
+ ```
196
+
197
+ ## Monitoring
198
+
199
+ ```ruby
200
+ MistralTranslator.configure do |config|
201
+ config.enable_metrics = true
202
+
203
+ config.on_translation_complete = ->(from, to, orig, trans, duration) {
204
+ Rails.logger.info "[#{Thread.current.object_id}] #{from}→#{to}: #{duration.round(2)}s"
205
+ }
206
+ end
207
+
208
+ # Thread-safe metrics
209
+ metrics = MistralTranslator.metrics
210
+ puts "Total: #{metrics[:total_translations]}"
211
+ puts "Avg time: #{metrics[:average_translation_time]}s"
212
+ puts "Error rate: #{metrics[:error_rate]}%"
213
+ ```
214
+
215
+ ## Example: High-Performance Service
216
+
217
+ ```ruby
218
+ class TranslationService
219
+ def initialize(max_threads: 5)
220
+ @pool = Concurrent::FixedThreadPool.new(max_threads)
221
+ end
222
+
223
+ def translate_all(texts, from:, to_languages:)
224
+ futures = texts.flat_map do |text|
225
+ to_languages.map do |lang|
226
+ Concurrent::Future.execute(executor: @pool) do
227
+ MistralTranslator.translate(text, from: from, to: lang)
228
+ end
229
+ end
230
+ end
231
+ futures.map(&:value)
232
+ ensure
233
+ @pool.shutdown
234
+ @pool.wait_for_termination(30)
235
+ end
236
+ end
237
+ ```
238
+
239
+ ## Troubleshooting
240
+
241
+ **"Too many open files"**
242
+ ```bash
243
+ ulimit -n 4096
244
+ ```
245
+
246
+ **Rate limits despite low volume**
247
+ Check if multiple processes share the API key. Consider Redis-based rate limiting.
248
+
249
+ **Memory growth**
250
+ ```ruby
251
+ # Join threads periodically
252
+ threads = []
253
+ texts.each do |text|
254
+ threads << Thread.new { translate(text) }
255
+
256
+ if threads.size >= 10
257
+ threads.each(&:join)
258
+ threads.clear
259
+ end
260
+ end
261
+ ```
262
+
263
+ ## Summary
264
+
265
+ - Thread-safe with protected metrics and configuration
266
+ - Connection pooling automatic with net-http-persistent
267
+ - Use fixed-size thread pools (5-10 recommended)
268
+ - Built-in rate limiting works across threads
269
+ - Background jobs recommended for non-critical tasks
270
+ - Separate API keys per environment
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "net/http/persistent"
4
+
3
5
  module MistralTranslator
4
6
  module ClientHelpers
5
7
  # Helper pour la gestion des requêtes HTTP
@@ -9,14 +11,10 @@ module MistralTranslator
9
11
 
10
12
  request_body = build_request_body(prompt, max_tokens, temperature)
11
13
 
12
- http = Net::HTTP.new(uri.host, uri.port)
13
- http.use_ssl = (uri.scheme == "https")
14
- http.read_timeout = 60 # 60 secondes de timeout
15
-
16
14
  request = Net::HTTP::Post.new(uri.path, headers)
17
15
  request.body = request_body.to_json
18
16
 
19
- response = http.request(request)
17
+ response = http_pool.request(uri, request)
20
18
  log_request_response(request_body, response)
21
19
 
22
20
  response
@@ -26,6 +24,38 @@ module MistralTranslator
26
24
  raise ApiError, "HTTP error: #{e.message}"
27
25
  end
28
26
 
27
+ def http_pool
28
+ @http_pool ||= begin
29
+ pool = Net::HTTP::Persistent.new(name: "mistral_translator")
30
+ pool.read_timeout = 60
31
+ pool.idle_timeout = 30
32
+ pool.open_timeout = MistralTranslator.configuration.ssl_timeout
33
+ pool.max_requests = 100
34
+
35
+ # SSL/TLS configuration
36
+ configure_ssl(pool)
37
+
38
+ pool
39
+ end
40
+ end
41
+
42
+ def configure_ssl(pool)
43
+ config = MistralTranslator.configuration
44
+
45
+ # SSL verify mode
46
+ pool.verify_mode = case config.ssl_verify_mode
47
+ when :peer then OpenSSL::SSL::VERIFY_PEER
48
+ when :none then OpenSSL::SSL::VERIFY_NONE
49
+ else config.ssl_verify_mode
50
+ end
51
+
52
+ # Custom CA certificate file
53
+ pool.ca_file = config.ssl_ca_file if config.ssl_ca_file
54
+
55
+ # Custom CA certificates directory
56
+ pool.ca_path = config.ssl_ca_path if config.ssl_ca_path
57
+ end
58
+
29
59
  def build_request_body(prompt, max_tokens, temperature)
30
60
  body = {
31
61
  model: @model,
@@ -4,7 +4,8 @@ module MistralTranslator
4
4
  class Configuration
5
5
  attr_accessor :api_key, :model, :default_max_tokens, :default_temperature, :retry_delays,
6
6
  :on_translation_start, :on_translation_complete, :on_translation_error,
7
- :on_rate_limit, :on_batch_complete, :enable_metrics
7
+ :on_rate_limit, :on_batch_complete, :enable_metrics,
8
+ :ssl_verify_mode, :ssl_ca_file, :ssl_ca_path, :ssl_timeout
8
9
  attr_reader :api_url
9
10
 
10
11
  def initialize
@@ -23,7 +24,14 @@ module MistralTranslator
23
24
  @on_batch_complete = nil
24
25
  @enable_metrics = false
25
26
 
26
- # Métriques intégrées
27
+ # SSL/TLS configuration
28
+ @ssl_verify_mode = :peer # :peer, :none, or OpenSSL constant
29
+ @ssl_ca_file = nil # Path to CA certificate file
30
+ @ssl_ca_path = nil # Path to CA certificates directory
31
+ @ssl_timeout = 60 # SSL handshake timeout in seconds
32
+
33
+ # Métriques intégrées (thread-safe avec mutex)
34
+ @metrics_mutex = Mutex.new
27
35
  @metrics = {
28
36
  total_translations: 0,
29
37
  total_characters: 0,
@@ -54,9 +62,11 @@ module MistralTranslator
54
62
 
55
63
  return unless @enable_metrics
56
64
 
57
- @metrics[:total_translations] += 1
58
- @metrics[:total_characters] += text_length
59
- @metrics[:translations_by_language]["#{from_locale}->#{to_locale}"] += 1
65
+ @metrics_mutex.synchronize do
66
+ @metrics[:total_translations] += 1
67
+ @metrics[:total_characters] += text_length
68
+ @metrics[:translations_by_language]["#{from_locale}->#{to_locale}"] += 1
69
+ end
60
70
  end
61
71
 
62
72
  def trigger_translation_complete(from_locale, to_locale, original_length, translated_length, duration)
@@ -64,7 +74,9 @@ module MistralTranslator
64
74
 
65
75
  return unless @enable_metrics
66
76
 
67
- @metrics[:total_duration] += duration
77
+ @metrics_mutex.synchronize do
78
+ @metrics[:total_duration] += duration
79
+ end
68
80
  end
69
81
 
70
82
  def trigger_translation_error(from_locale, to_locale, error, attempt)
@@ -72,7 +84,9 @@ module MistralTranslator
72
84
 
73
85
  return unless @enable_metrics
74
86
 
75
- @metrics[:errors_count] += 1
87
+ @metrics_mutex.synchronize do
88
+ @metrics[:errors_count] += 1
89
+ end
76
90
  end
77
91
 
78
92
  def trigger_rate_limit(from_locale, to_locale, wait_time, attempt)
@@ -80,7 +94,9 @@ module MistralTranslator
80
94
 
81
95
  return unless @enable_metrics
82
96
 
83
- @metrics[:rate_limits_hit] += 1
97
+ @metrics_mutex.synchronize do
98
+ @metrics[:rate_limits_hit] += 1
99
+ end
84
100
  end
85
101
 
86
102
  def trigger_batch_complete(batch_size, total_duration, success_count, error_count)
@@ -91,35 +107,39 @@ module MistralTranslator
91
107
  def metrics
92
108
  return {} unless @enable_metrics
93
109
 
94
- @metrics.merge({
95
- average_translation_time: if @metrics[:total_translations].positive?
96
- (@metrics[:total_duration] / @metrics[:total_translations]).round(3)
97
- else
98
- 0
99
- end,
100
- average_characters_per_translation: if @metrics[:total_translations].positive?
101
- (@metrics[:total_characters] /
102
- @metrics[:total_translations]).round(0)
103
- else
104
- 0
105
- end,
106
- error_rate: if @metrics[:total_translations].positive?
107
- ((@metrics[:errors_count].to_f / @metrics[:total_translations]) * 100).round(2)
108
- else
109
- 0
110
- end
111
- })
110
+ @metrics_mutex.synchronize do
111
+ @metrics.merge({
112
+ average_translation_time: if @metrics[:total_translations].positive?
113
+ (@metrics[:total_duration] / @metrics[:total_translations]).round(3)
114
+ else
115
+ 0
116
+ end,
117
+ average_characters_per_translation: if @metrics[:total_translations].positive?
118
+ (@metrics[:total_characters] /
119
+ @metrics[:total_translations]).round(0)
120
+ else
121
+ 0
122
+ end,
123
+ error_rate: if @metrics[:total_translations].positive?
124
+ ((@metrics[:errors_count].to_f / @metrics[:total_translations]) * 100).round(2)
125
+ else
126
+ 0
127
+ end
128
+ })
129
+ end
112
130
  end
113
131
 
114
132
  def reset_metrics!
115
- @metrics = {
116
- total_translations: 0,
117
- total_characters: 0,
118
- total_duration: 0.0,
119
- rate_limits_hit: 0,
120
- errors_count: 0,
121
- translations_by_language: Hash.new(0)
122
- }
133
+ @metrics_mutex.synchronize do
134
+ @metrics = {
135
+ total_translations: 0,
136
+ total_characters: 0,
137
+ total_duration: 0.0,
138
+ rate_limits_hit: 0,
139
+ errors_count: 0,
140
+ translations_by_language: Hash.new(0)
141
+ }
142
+ end
123
143
  end
124
144
 
125
145
  # Configuration helpers pour les callbacks les plus communs
@@ -17,12 +17,15 @@ module MistralTranslator
17
17
 
18
18
  # Log seulement si pas déjà loggé récemment (évite la spam)
19
19
  def warn_once(message, key: nil, sensitive: false, ttl: 300)
20
- @warn_cache ||= {}
21
- cache_key = key || message
20
+ cache_mutex.synchronize do
21
+ @warn_cache ||= {}
22
+ cache_key = key || message
22
23
 
23
- return unless should_log_warning?(cache_key, ttl)
24
+ return unless should_log_warning?(cache_key, ttl)
25
+
26
+ @warn_cache[cache_key] = Time.now
27
+ end
24
28
 
25
- @warn_cache[cache_key] = Time.now
26
29
  log(:warn, message, sensitive)
27
30
  end
28
31
 
@@ -74,6 +77,10 @@ module MistralTranslator
74
77
 
75
78
  Time.now - @warn_cache[key] > ttl
76
79
  end
80
+
81
+ def cache_mutex
82
+ @cache_mutex ||= Mutex.new
83
+ end
77
84
  end
78
85
  end
79
86
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MistralTranslator
4
- VERSION = "0.2.1"
4
+ VERSION = "0.3.0"
5
5
 
6
6
  # Informations additionnelles sur la gem
7
7
  API_VERSION = "v1"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mistral_translator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peyochanchan
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0.4'
26
+ - !ruby/object:Gem::Dependency
27
+ name: net-http-persistent
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '4.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '4.0'
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: rake
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +122,7 @@ files:
108
122
  - ".ruby-version"
109
123
  - CHANGELOG.md
110
124
  - CODE_OF_CONDUCT.md
125
+ - CONTRIBUTING.md
111
126
  - LICENSE.txt
112
127
  - README.md
113
128
  - README_TESTING.md
@@ -117,6 +132,7 @@ files:
117
132
  - docs/404.html
118
133
  - docs/README.md
119
134
  - docs/advanced-usage/batch-processing.md
135
+ - docs/advanced-usage/concurrent-async.md
120
136
  - docs/advanced-usage/error-handling.md
121
137
  - docs/advanced-usage/monitoring.md
122
138
  - docs/advanced-usage/summarization.md