lara-sdk 1.0.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.
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Lara
6
+ module Models
7
+ class TextBlock < Base
8
+ attr_reader :text, :translatable
9
+
10
+ def initialize(text:, translatable: true)
11
+ super()
12
+ @text = text
13
+ @translatable = !!translatable
14
+ end
15
+ end
16
+
17
+ class NGMemoryMatch < Base
18
+ attr_reader :memory, :tuid, :language, :sentence, :translation
19
+
20
+ def initialize(memory:, language:, sentence:, translation:, tuid: nil)
21
+ super()
22
+ @memory = memory
23
+ @tuid = tuid
24
+ @language = language
25
+ @sentence = sentence
26
+ @translation = translation
27
+ end
28
+ end
29
+
30
+ class NGGlossaryMatch < Base
31
+ attr_reader :glossary, :language, :term, :translation
32
+
33
+ def initialize(glossary:, language:, term:, translation:)
34
+ super()
35
+ @glossary = glossary
36
+ @language = language
37
+ @term = term
38
+ @translation = translation
39
+ end
40
+ end
41
+
42
+ class DetectPrediction < Base
43
+ attr_reader :language, :confidence
44
+
45
+ def initialize(language:, confidence:)
46
+ super()
47
+ @language = language
48
+ @confidence = confidence
49
+ end
50
+ end
51
+
52
+ class DetectResult < Base
53
+ attr_reader :language, :content_type, :predictions
54
+
55
+ def initialize(language:, content_type:, predictions: [])
56
+ super()
57
+ @language = language
58
+ @content_type = content_type
59
+ @predictions = predictions.map { |p| DetectPrediction.new(**p.transform_keys(&:to_sym)) }
60
+ end
61
+ end
62
+
63
+ class TextResult < Base
64
+ attr_reader :content_type, :source_language, :translation,
65
+ :adapted_to, :glossaries,
66
+ :adapted_to_matches, :glossaries_matches
67
+
68
+ def self.from_hash(hash)
69
+ return nil unless hash.is_a?(Hash)
70
+
71
+ translation = hash["translation"]
72
+ if translation.is_a?(Array) && translation.is_a?(Array) && !translation.all?(String)
73
+ translation = translation.map do |e|
74
+ TextBlock.new(text: e["text"],
75
+ translatable: e["translatable"])
76
+ end
77
+ end
78
+
79
+ adapted_to_matches = convert_matches(hash["adapted_to_matches"], NGMemoryMatch)
80
+ glossaries_matches = convert_matches(hash["glossaries_matches"], NGGlossaryMatch)
81
+
82
+ new(
83
+ content_type: hash["content_type"],
84
+ source_language: hash["source_language"],
85
+ translation: translation,
86
+ adapted_to: hash["adapted_to"],
87
+ glossaries: hash["glossaries"],
88
+ adapted_to_matches: adapted_to_matches,
89
+ glossaries_matches: glossaries_matches
90
+ )
91
+ end
92
+
93
+ def initialize(content_type:, source_language:, translation:, adapted_to: nil, glossaries: nil,
94
+ adapted_to_matches: nil, glossaries_matches: nil)
95
+ super()
96
+ @content_type = content_type
97
+ @source_language = source_language
98
+ @translation = translation
99
+ @adapted_to = adapted_to
100
+ @glossaries = glossaries
101
+ @adapted_to_matches = adapted_to_matches
102
+ @glossaries_matches = glossaries_matches
103
+ end
104
+
105
+ class << self
106
+ private
107
+
108
+ def convert_matches(value, klass)
109
+ return nil if value.nil?
110
+
111
+ return unless value.is_a?(Array)
112
+
113
+ if value.empty?
114
+ []
115
+ elsif value.first.is_a?(Array)
116
+ value.map { |arr| arr.map { |h| build_match(klass, h) } }
117
+ else
118
+ value.map { |h| build_match(klass, h) }
119
+ end
120
+ end
121
+
122
+ def build_match(klass, h)
123
+ case klass.name.split("::").last
124
+ when "NGMemoryMatch"
125
+ NGMemoryMatch.new(
126
+ memory: h["memory"],
127
+ tuid: h["tuid"],
128
+ language: h["language"],
129
+ sentence: h["sentence"],
130
+ translation: h["translation"]
131
+ )
132
+ when "NGGlossaryMatch"
133
+ NGGlossaryMatch.new(
134
+ glossary: h["glossary"],
135
+ language: h["language"],
136
+ term: h["term"],
137
+ translation: h["translation"]
138
+ )
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "faraday/multipart"
5
+
6
+ module Lara
7
+ class S3Client
8
+ def upload(url:, fields:, io:)
9
+ payload_io = io.is_a?(String) ? File.open(io, "rb") : io
10
+
11
+ begin
12
+ filename = if io.is_a?(String)
13
+ File.basename(io)
14
+ else
15
+ io.respond_to?(:path) ? File.basename(io.path) : "upload.bin"
16
+ end
17
+
18
+ conn = Faraday.new(url: url) do |f|
19
+ f.request(:multipart)
20
+ f.request(:url_encoded)
21
+ f.adapter(Faraday.default_adapter)
22
+ end
23
+
24
+ file_part = Faraday::Multipart::FilePart.new(payload_io, "application/octet-stream",
25
+ filename)
26
+
27
+ response = conn.post(nil) do |req|
28
+ req.body = fields.transform_values(&:to_s).merge("file" => file_part)
29
+ end
30
+
31
+ raise Lara::LaraError, "S3 upload failed: HTTP #{response.status}" unless response.success?
32
+
33
+ nil
34
+ ensure
35
+ payload_io.close if io.is_a?(String) && payload_io && !payload_io.closed?
36
+ end
37
+ rescue ArgumentError => e
38
+ warn "[S3Client] upload ArgumentError: #{e.message}"
39
+ raise
40
+ rescue Faraday::Error => e
41
+ warn "[S3Client] upload Faraday error: #{e.class} #{e.message}"
42
+ raise Lara::LaraError, "S3 upload failed: #{e.message}"
43
+ end
44
+
45
+ def download(url:)
46
+ conn = Faraday.new(url: url) { |f| f.adapter Faraday.default_adapter }
47
+ response = conn.get
48
+ raise Lara::LaraError, "S3 download failed: HTTP #{response.status}" unless response.success?
49
+
50
+ response.body
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "client"
4
+
5
+ module Lara
6
+ class Translator
7
+ # @param credentials [Lara::Credentials,nil]
8
+ # @param auth_token [Lara::AuthToken,nil]
9
+ # @param access_key_id [String,nil]
10
+ # @param access_key_secret [String,nil]
11
+ # @param base_url [String,nil]
12
+ # @param connection_timeout [Integer,nil]
13
+ # @param read_timeout [Integer,nil]
14
+ def initialize(credentials: nil, auth_token: nil, access_key_id: nil, access_key_secret: nil,
15
+ base_url: nil, connection_timeout: nil, read_timeout: nil)
16
+ auth_method = if auth_token
17
+ auth_token
18
+ elsif credentials
19
+ credentials
20
+ elsif access_key_id && access_key_secret
21
+ Credentials.new(access_key_id, access_key_secret)
22
+ else
23
+ raise ArgumentError,
24
+ "either credentials, auth_token, or access_key_id and access_key_secret must be provided"
25
+ end
26
+
27
+ @client = Client.new(auth_method, base_url: base_url,
28
+ connection_timeout: connection_timeout, read_timeout: read_timeout)
29
+ @memories = Memories.new(@client)
30
+ @glossaries = Glossaries.new(@client)
31
+ @documents = Documents.new(@client)
32
+ @images = Images.new(@client)
33
+ @audio = AudioTranslator.new(@client)
34
+ end
35
+
36
+ attr_reader :client, :memories, :glossaries, :documents, :images, :audio
37
+
38
+ # Translates text with optional tuning parameters.
39
+ # @param text [String, Array<String>, Array<Lara::Models::TextBlock>]
40
+ # @param source [String,nil]
41
+ # @param source_hint [String,nil]
42
+ # @param target [String]
43
+ # @param adapt_to [Array<String>,nil]
44
+ # @param glossaries [Array<String>,nil]
45
+ # @param instructions [Array<String>,nil]
46
+ # @param content_type [String,nil]
47
+ # @param multiline [Boolean]
48
+ # @param timeout_ms [Integer,nil]
49
+ # @param priority [String,nil]
50
+ # @param use_cache [String,Boolean,nil]
51
+ # @param cache_ttl_s [Integer,nil]
52
+ # @param no_trace [Boolean]
53
+ # @param verbose [Boolean]
54
+ # @param style [String,nil]
55
+ # @param reasoning [Boolean] When true with a block, yields partial results during reasoning
56
+ # @param headers [Hash,nil]
57
+ # @yield [Lara::Models::TextResult] Partial translation result (only when reasoning is true)
58
+ # @return [Lara::Models::TextResult] Final translation result
59
+ def translate(text, target:, source: nil, source_hint: nil, adapt_to: nil, glossaries: nil,
60
+ instructions: nil, content_type: nil, multiline: true, timeout_ms: nil,
61
+ priority: nil, use_cache: nil, cache_ttl_s: nil, no_trace: false, verbose: false,
62
+ style: nil, reasoning: false, headers: nil, &callback)
63
+ q = normalize_text_input(text)
64
+
65
+ use_cache_value = case use_cache
66
+ when true then "yes"
67
+ when false then "no"
68
+ else use_cache
69
+ end
70
+
71
+ body = {
72
+ q: q,
73
+ source: source,
74
+ target: target,
75
+ source_hint: source_hint,
76
+ content_type: content_type,
77
+ multiline: multiline,
78
+ adapt_to: adapt_to,
79
+ glossaries: glossaries,
80
+ instructions: instructions,
81
+ timeout: timeout_ms,
82
+ priority: priority,
83
+ use_cache: use_cache_value,
84
+ cache_ttl: cache_ttl_s,
85
+ verbose: verbose,
86
+ style: style,
87
+ reasoning: reasoning
88
+ }.compact
89
+
90
+ request_headers = {}
91
+ request_headers.merge!(headers) if headers.is_a?(Hash)
92
+ request_headers["X-No-Trace"] = "true" if no_trace
93
+
94
+ stream_callback = if callback && reasoning
95
+ ->(partial) { callback.call(Lara::Models::TextResult.from_hash(partial)) }
96
+ end
97
+
98
+ result = @client.post("/translate", body: body, headers: request_headers, &stream_callback)
99
+ Lara::Models::TextResult.from_hash(result) if result
100
+ end
101
+
102
+ # Detects the language of the given text.
103
+ # @param text [String, Array<String>] Text to detect language for
104
+ # @param hint [String, nil] Language hint
105
+ # @param passlist [Array<String>, nil] List of allowed languages
106
+ # @return [Lara::Models::DetectResult]
107
+ def detect(text, hint: nil, passlist: nil)
108
+ body = { q: text }
109
+ body[:hint] = hint if hint
110
+ body[:passlist] = passlist if passlist&.any?
111
+ body = body.compact
112
+
113
+ result = @client.post("/v2/detect", body: body)
114
+ Lara::Models::DetectResult.new(
115
+ language: result["language"],
116
+ content_type: result["content_type"],
117
+ predictions: result["predictions"] || []
118
+ )
119
+ end
120
+
121
+ # Lists supported language codes.
122
+ def get_languages
123
+ @client.get("/v2/languages")
124
+ end
125
+
126
+ private
127
+
128
+ def normalize_text_input(text)
129
+ case text
130
+ when String
131
+ text
132
+ when Array
133
+ if text.all?(String)
134
+ text
135
+ elsif text.all?(Lara::Models::TextBlock)
136
+ text.map { |tb| { text: tb.text, translatable: tb.translatable } }
137
+ else
138
+ raise ArgumentError, "text must be an iterable of strings or TextBlock objects"
139
+ end
140
+ else
141
+ raise ArgumentError, "text must be a string or an iterable"
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lara
4
+ VERSION = "1.0.0"
5
+ end
data/lib/lara.rb ADDED
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lara/version"
4
+ require_relative "lara/credentials"
5
+ require_relative "lara/auth_token"
6
+ require_relative "lara/errors"
7
+ require_relative "lara/client"
8
+ require_relative "lara/translator"
9
+ require_relative "lara/memories"
10
+ require_relative "lara/glossaries"
11
+ require_relative "lara/s3_client"
12
+ require_relative "lara/documents"
13
+ require_relative "lara/images"
14
+ require_relative "lara/audio"
15
+
16
+ # Models
17
+ require_relative "lara/models/base"
18
+ require_relative "lara/models/text"
19
+ require_relative "lara/models/memories"
20
+ require_relative "lara/models/glossaries"
21
+ require_relative "lara/models/documents"
22
+ require_relative "lara/models/images"
23
+ require_relative "lara/models/audio"
24
+
25
+ module Lara
26
+ # Ruby SDK for Lara AI-powered translation services
27
+ end
metadata ADDED
@@ -0,0 +1,198 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lara-sdk
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Translated
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-03-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday-multipart
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mime-types
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.4'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: multipart-post
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '2.3'
62
+ - - "<"
63
+ - !ruby/object:Gem::Version
64
+ version: '2.4'
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '2.3'
72
+ - - "<"
73
+ - !ruby/object:Gem::Version
74
+ version: '2.4'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rake
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '13.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '13.0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rspec
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '3.0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: simplecov
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '0.22'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.22'
117
+ - !ruby/object:Gem::Dependency
118
+ name: webmock
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '3.18'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '3.18'
131
+ - !ruby/object:Gem::Dependency
132
+ name: yard
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '0.9'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '0.9'
145
+ description: A Ruby library for Lara's API - AI-powered translation services
146
+ email:
147
+ - support@laratranslate.com
148
+ executables: []
149
+ extensions: []
150
+ extra_rdoc_files: []
151
+ files:
152
+ - lib/lara.rb
153
+ - lib/lara/audio.rb
154
+ - lib/lara/auth_token.rb
155
+ - lib/lara/client.rb
156
+ - lib/lara/credentials.rb
157
+ - lib/lara/documents.rb
158
+ - lib/lara/errors.rb
159
+ - lib/lara/glossaries.rb
160
+ - lib/lara/images.rb
161
+ - lib/lara/memories.rb
162
+ - lib/lara/models/audio.rb
163
+ - lib/lara/models/base.rb
164
+ - lib/lara/models/documents.rb
165
+ - lib/lara/models/glossaries.rb
166
+ - lib/lara/models/images.rb
167
+ - lib/lara/models/memories.rb
168
+ - lib/lara/models/text.rb
169
+ - lib/lara/s3_client.rb
170
+ - lib/lara/translator.rb
171
+ - lib/lara/version.rb
172
+ homepage: https://laratranslate.com
173
+ licenses:
174
+ - MIT
175
+ metadata:
176
+ homepage_uri: https://laratranslate.com
177
+ source_code_uri: https://github.com/translated/lara-ruby
178
+ changelog_uri: https://github.com/translated/lara-ruby/blob/main/CHANGELOG.md
179
+ post_install_message:
180
+ rdoc_options: []
181
+ require_paths:
182
+ - lib
183
+ required_ruby_version: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: 2.6.0
188
+ required_rubygems_version: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: '0'
193
+ requirements: []
194
+ rubygems_version: 3.4.19
195
+ signing_key:
196
+ specification_version: 4
197
+ summary: Official Lara SDK for Ruby
198
+ test_files: []