lara-sdk 1.7.0 → 1.9.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 +4 -4
- data/lib/lara/client.rb +12 -1
- data/lib/lara/glossaries.rb +34 -28
- data/lib/lara/images.rb +26 -2
- data/lib/lara/memories.rb +6 -12
- data/lib/lara/models/images.rb +89 -6
- data/lib/lara/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6441d4e8ad5cc6064ed3367644ed969d3c91e2dc5d5bcf2645680b2389d491b6
|
|
4
|
+
data.tar.gz: 1bbdb83ca6fb7f330ebb3adf69b8126facb9c1045875f083196580a2915d8b67
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c8b12451198b5a52a076766e310e4622cab58c1a1ce6173d6f3852291c183835b3111fcded6aee2ce60b0631727066d9b3dd443644ea03d12856f2db01314f07
|
|
7
|
+
data.tar.gz: b7dd274d9cdf562a91d0fcb496c9ce2f4053d9be3a99bb4fe5bfdb9d09d93d271ef46e82e2f8538b9b4fb24da8489401ef40bf79f6eeb29c1bc68bbbce3add2f
|
data/lib/lara/client.rb
CHANGED
|
@@ -92,6 +92,15 @@ module Lara
|
|
|
92
92
|
|
|
93
93
|
def request(method, path, body: nil, files: nil, headers: nil, params: nil,
|
|
94
94
|
raw_response: false, &callback)
|
|
95
|
+
file_positions = (files || {}).each_with_object({}) do |(name, file), positions|
|
|
96
|
+
next unless file.respond_to?(:pos) && file.respond_to?(:seek)
|
|
97
|
+
|
|
98
|
+
begin
|
|
99
|
+
positions[name] = file.pos
|
|
100
|
+
rescue IOError, SystemCallError
|
|
101
|
+
# Non-seekable uploads can still be sent once.
|
|
102
|
+
end
|
|
103
|
+
end
|
|
95
104
|
ensure_valid_token
|
|
96
105
|
|
|
97
106
|
make_request(method, path, body: body, files: files, headers: headers, params: params,
|
|
@@ -100,6 +109,8 @@ module Lara
|
|
|
100
109
|
raise unless e.status_code == 401
|
|
101
110
|
|
|
102
111
|
@auth_mutex.synchronize { refresh_or_reauthenticate }
|
|
112
|
+
# Multipart encoding consumed the files; replay their original bytes.
|
|
113
|
+
file_positions.each { |name, position| files[name].seek(position) }
|
|
103
114
|
make_request(method, path, body: body, files: files, headers: headers, params: params,
|
|
104
115
|
raw_response: raw_response, &callback)
|
|
105
116
|
end
|
|
@@ -218,7 +229,7 @@ module Lara
|
|
|
218
229
|
return response.body if raw_response
|
|
219
230
|
|
|
220
231
|
content_type = response.headers["content-type"] || response.headers["Content-Type"]
|
|
221
|
-
return response.body if content_type&.include?("text/csv")
|
|
232
|
+
return response.body if content_type&.include?("text/csv") || content_type&.include?("application/xml")
|
|
222
233
|
|
|
223
234
|
if callback || (body && body[:reasoning])
|
|
224
235
|
parse_stream_response(response.body, &callback)
|
data/lib/lara/glossaries.rb
CHANGED
|
@@ -6,10 +6,11 @@ module Lara
|
|
|
6
6
|
module FileFormat
|
|
7
7
|
UNIDIRECTIONAL = "csv/table-uni"
|
|
8
8
|
MULTIDIRECTIONAL = "csv/table-multi"
|
|
9
|
+
TBX = "tbx"
|
|
9
10
|
|
|
10
11
|
# @return [Array<String>] All supported formats
|
|
11
12
|
def self.all
|
|
12
|
-
[UNIDIRECTIONAL, MULTIDIRECTIONAL]
|
|
13
|
+
[UNIDIRECTIONAL, MULTIDIRECTIONAL, TBX]
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
# @param format [String] The format to validate
|
|
@@ -91,33 +92,28 @@ module Lara
|
|
|
91
92
|
Lara::Models::GlossaryCounts.new(**@client.get("/v2/glossaries/#{id}/counts").transform_keys(&:to_sym))
|
|
92
93
|
end
|
|
93
94
|
|
|
94
|
-
#
|
|
95
|
-
# @param
|
|
95
|
+
# Import options are independent keyword arguments; omitted options use their defaults.
|
|
96
|
+
# @param content_type [String] One of FileFormat.all; defaults to UNIDIRECTIONAL
|
|
97
|
+
# @param gzip [Boolean] Whether the supplied file is already gzip-compressed; defaults to false
|
|
96
98
|
# @param callback_url [String,nil] Optional URL notified when the import completes
|
|
97
99
|
# @return [Lara::Models::GlossaryImport]
|
|
98
|
-
def
|
|
99
|
-
|
|
100
|
+
def import_file(id, file_path, content_type: FileFormat::UNIDIRECTIONAL, gzip: false,
|
|
101
|
+
callback_url: nil)
|
|
100
102
|
unless FileFormat.valid?(content_type)
|
|
101
103
|
raise ArgumentError, "Invalid content_type. Supported formats: #{FileFormat.all.join(', ')}"
|
|
102
104
|
end
|
|
103
105
|
|
|
104
|
-
basename = File.basename(
|
|
105
|
-
if gzip
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
body = { "compression" => "gzip" }
|
|
116
|
-
files = { "csv" => Faraday::UploadIO.new(buffer, "application/gzip", "#{basename}.gz") }
|
|
117
|
-
else
|
|
118
|
-
body = {}
|
|
119
|
-
files = { "csv" => Faraday::UploadIO.new(csv_path, "text/csv", basename) }
|
|
120
|
-
end
|
|
106
|
+
basename = File.basename(file_path)
|
|
107
|
+
mime_type = if gzip
|
|
108
|
+
"application/gzip"
|
|
109
|
+
elsif content_type == FileFormat::TBX
|
|
110
|
+
"application/xml"
|
|
111
|
+
else
|
|
112
|
+
"text/csv"
|
|
113
|
+
end
|
|
114
|
+
files = { "csv" => Faraday::UploadIO.new(file_path, mime_type, basename) }
|
|
115
|
+
body = {}
|
|
116
|
+
body["compression"] = "gzip" if gzip
|
|
121
117
|
|
|
122
118
|
body["content_type"] = content_type unless content_type == FileFormat::UNIDIRECTIONAL
|
|
123
119
|
body["callback_url"] = callback_url if callback_url
|
|
@@ -125,6 +121,16 @@ module Lara
|
|
|
125
121
|
body: body, files: files).transform_keys(&:to_sym))
|
|
126
122
|
end
|
|
127
123
|
|
|
124
|
+
# @deprecated Use #import_file instead.
|
|
125
|
+
def import_csv(id, csv_path, content_type: FileFormat::UNIDIRECTIONAL, gzip: false,
|
|
126
|
+
callback_url: nil)
|
|
127
|
+
warn "[DEPRECATION] import_csv is deprecated; use import_file instead.", uplevel: 1
|
|
128
|
+
unless [FileFormat::UNIDIRECTIONAL, FileFormat::MULTIDIRECTIONAL].include?(content_type)
|
|
129
|
+
raise ArgumentError, "import_csv only supports CSV formats; use import_file for TBX files."
|
|
130
|
+
end
|
|
131
|
+
import_file(id, csv_path, content_type: content_type, gzip: gzip, callback_url: callback_url)
|
|
132
|
+
end
|
|
133
|
+
|
|
128
134
|
# @return [Lara::Models::GlossaryImport]
|
|
129
135
|
def get_import_status(import_id)
|
|
130
136
|
Lara::Models::GlossaryImport.new(**@client.get("/v2/glossaries/imports/#{import_id}").transform_keys(&:to_sym))
|
|
@@ -146,10 +152,10 @@ module Lara
|
|
|
146
152
|
current
|
|
147
153
|
end
|
|
148
154
|
|
|
149
|
-
# Exports
|
|
150
|
-
# @param content_type [String]
|
|
151
|
-
# @param source [String, nil]
|
|
152
|
-
# @return [String]
|
|
155
|
+
# Exports the glossary in the requested format.
|
|
156
|
+
# @param content_type [String] One of the formats returned by FileFormat.all
|
|
157
|
+
# @param source [String, nil] Required for unidirectional CSV; omit for multidirectional CSV and TBX
|
|
158
|
+
# @return [String] Raw CSV or TBX content
|
|
153
159
|
def export(id, content_type: FileFormat::UNIDIRECTIONAL, source: nil)
|
|
154
160
|
unless FileFormat.valid?(content_type)
|
|
155
161
|
raise ArgumentError, "Invalid content_type. Supported formats: #{FileFormat.all.join(', ')}"
|
|
@@ -160,8 +166,8 @@ module Lara
|
|
|
160
166
|
end
|
|
161
167
|
|
|
162
168
|
# @param callback_url [String] URL notified when the export is ready
|
|
163
|
-
# @param content_type [String]
|
|
164
|
-
# @param source [String, nil]
|
|
169
|
+
# @param content_type [String] One of the formats returned by FileFormat.all
|
|
170
|
+
# @param source [String, nil] Required for unidirectional CSV; omit for multidirectional CSV and TBX
|
|
165
171
|
# @return [Lara::Models::GlossaryExport]
|
|
166
172
|
def export_async(id, callback_url:, content_type: FileFormat::UNIDIRECTIONAL, source: nil)
|
|
167
173
|
unless FileFormat.valid?(content_type)
|
data/lib/lara/images.rb
CHANGED
|
@@ -46,10 +46,12 @@ module Lara
|
|
|
46
46
|
# @param glossaries [Array<String>, nil] Glossary IDs to apply.
|
|
47
47
|
# @param style [String, nil] Translation style ("faithful", "fluid", "creative").
|
|
48
48
|
# @param verbose [Boolean] If true, includes match details in the response.
|
|
49
|
+
# @param include_layout [Boolean] When true, includes complete geometry and styling on every
|
|
50
|
+
# paragraph independently of verbose.
|
|
49
51
|
# @param no_trace [Boolean] If true, disables request tracing.
|
|
50
52
|
# @return [Lara::Models::ImageTextResult]
|
|
51
53
|
def translate_text(file_path:, target:, source: nil, adapt_to: nil, glossaries: nil,
|
|
52
|
-
style: nil, verbose: false, no_trace: false)
|
|
54
|
+
style: nil, verbose: false, no_trace: false, include_layout: false)
|
|
53
55
|
image_upload = Faraday::Multipart::FilePart.new(file_path, mime_type_for(file_path))
|
|
54
56
|
|
|
55
57
|
body = {
|
|
@@ -58,7 +60,8 @@ module Lara
|
|
|
58
60
|
adapt_to: adapt_to&.to_json,
|
|
59
61
|
glossaries: glossaries&.to_json,
|
|
60
62
|
style: style,
|
|
61
|
-
verbose: verbose.to_s
|
|
63
|
+
verbose: verbose.to_s,
|
|
64
|
+
include_layout: include_layout.to_s
|
|
62
65
|
}.compact
|
|
63
66
|
|
|
64
67
|
headers = {}
|
|
@@ -69,6 +72,27 @@ module Lara
|
|
|
69
72
|
Lara::Models::ImageTextResult.from_hash(result)
|
|
70
73
|
end
|
|
71
74
|
|
|
75
|
+
# Renders supplied translations onto the original image without translating again.
|
|
76
|
+
# Overlay and inpainting require ImageLayoutParagraph instances. Generative models
|
|
77
|
+
# accept either ImageParagraph or ImageLayoutParagraph. An omitted model defaults
|
|
78
|
+
# to generative_fast.
|
|
79
|
+
# @param paragraphs [Array<Lara::Models::ImageParagraph>] Text and translations to render.
|
|
80
|
+
# @return [String] Binary image data.
|
|
81
|
+
def render_translated(file_path:, target:, paragraphs:, source: nil, model: nil, no_trace: false)
|
|
82
|
+
image_upload = Faraday::Multipart::FilePart.new(file_path, mime_type_for(file_path))
|
|
83
|
+
body = {
|
|
84
|
+
source: source,
|
|
85
|
+
target: target,
|
|
86
|
+
paragraphs: paragraphs.map(&:to_render_hash).to_json,
|
|
87
|
+
model: model
|
|
88
|
+
}.compact
|
|
89
|
+
headers = {}
|
|
90
|
+
headers["X-No-Trace"] = "true" if no_trace
|
|
91
|
+
|
|
92
|
+
@client.post("/v2/images/render-translated", body: body, files: { image: image_upload },
|
|
93
|
+
headers: headers, raw_response: true)
|
|
94
|
+
end
|
|
95
|
+
|
|
72
96
|
private
|
|
73
97
|
|
|
74
98
|
def mime_type_for(file_path)
|
data/lib/lara/memories.rb
CHANGED
|
@@ -144,20 +144,14 @@ module Lara
|
|
|
144
144
|
end
|
|
145
145
|
end
|
|
146
146
|
|
|
147
|
+
# @param gzip [Boolean] Whether the supplied file is already gzip-compressed; defaults to false
|
|
147
148
|
# @return [Lara::Models::MemoryImport]
|
|
148
|
-
def import_tmx(id, tmx_path, callback_url: nil)
|
|
149
|
-
require "stringio"
|
|
150
|
-
require "zlib"
|
|
149
|
+
def import_tmx(id, tmx_path, gzip: false, callback_url: nil)
|
|
151
150
|
basename = File.basename(tmx_path)
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
gz.finish
|
|
157
|
-
buffer.rewind
|
|
158
|
-
|
|
159
|
-
files = { "tmx" => Faraday::UploadIO.new(buffer, "application/gzip", "#{basename}.gz") }
|
|
160
|
-
body = { "compression" => "gzip" }
|
|
151
|
+
mime_type = gzip ? "application/gzip" : "application/xml"
|
|
152
|
+
files = { "tmx" => Faraday::UploadIO.new(tmx_path, mime_type, basename) }
|
|
153
|
+
body = {}
|
|
154
|
+
body["compression"] = "gzip" if gzip
|
|
161
155
|
body["callback_url"] = callback_url if callback_url
|
|
162
156
|
Lara::Models::MemoryImport.new(**@client.post("/v2/memories/#{id}/import",
|
|
163
157
|
body: body, files: files).transform_keys(&:to_sym))
|
data/lib/lara/models/images.rb
CHANGED
|
@@ -18,6 +18,51 @@ module Lara
|
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
# Four corners, each an [x, y] pair of pixel coordinates.
|
|
22
|
+
class ImageBBox < Base
|
|
23
|
+
attr_reader :top_left, :top_right, :bottom_right, :bottom_left
|
|
24
|
+
|
|
25
|
+
def initialize(top_left:, top_right:, bottom_right:, bottom_left:)
|
|
26
|
+
super()
|
|
27
|
+
@top_left = top_left
|
|
28
|
+
@top_right = top_right
|
|
29
|
+
@bottom_right = bottom_right
|
|
30
|
+
@bottom_left = bottom_left
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.from_hash(hash)
|
|
34
|
+
new(top_left: hash["top_left"] || hash["topLeft"],
|
|
35
|
+
top_right: hash["top_right"] || hash["topRight"],
|
|
36
|
+
bottom_right: hash["bottom_right"] || hash["bottomRight"],
|
|
37
|
+
bottom_left: hash["bottom_left"] || hash["bottomLeft"])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def to_render_hash
|
|
41
|
+
{ top_left: top_left, top_right: top_right, bottom_right: bottom_right, bottom_left: bottom_left }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Original text direction (ltr, rtl, or ttb) and colors.
|
|
46
|
+
class ImageTextInfo < Base
|
|
47
|
+
attr_reader :direction, :text_color, :background_color
|
|
48
|
+
|
|
49
|
+
def initialize(direction:, text_color:, background_color:)
|
|
50
|
+
super()
|
|
51
|
+
@direction = direction
|
|
52
|
+
@text_color = text_color
|
|
53
|
+
@background_color = background_color
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.from_hash(hash)
|
|
57
|
+
new(direction: hash["direction"], text_color: hash["text_color"] || hash["textColor"],
|
|
58
|
+
background_color: hash["background_color"] || hash["backgroundColor"])
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def to_render_hash
|
|
62
|
+
{ direction: direction, text_color: text_color, background_color: background_color }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
21
66
|
class ImageParagraph < Base
|
|
22
67
|
attr_reader :text, :translation, :adapted_to_matches, :glossaries_matches
|
|
23
68
|
|
|
@@ -28,6 +73,29 @@ module Lara
|
|
|
28
73
|
@adapted_to_matches = adapted_to_matches
|
|
29
74
|
@glossaries_matches = glossaries_matches
|
|
30
75
|
end
|
|
76
|
+
|
|
77
|
+
def to_render_hash
|
|
78
|
+
{ text: text, translation: translation }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class ImageLayoutParagraph < ImageParagraph
|
|
83
|
+
attr_reader :bbox, :lines_bboxes, :text_info, :alignment
|
|
84
|
+
|
|
85
|
+
def initialize(text:, translation:, bbox:, lines_bboxes:, text_info:, alignment:,
|
|
86
|
+
adapted_to_matches: nil, glossaries_matches: nil)
|
|
87
|
+
super(text: text, translation: translation, adapted_to_matches: adapted_to_matches,
|
|
88
|
+
glossaries_matches: glossaries_matches)
|
|
89
|
+
@bbox = bbox
|
|
90
|
+
@lines_bboxes = lines_bboxes
|
|
91
|
+
@text_info = text_info
|
|
92
|
+
@alignment = alignment
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def to_render_hash
|
|
96
|
+
super.merge(bbox: bbox.to_render_hash, lines_bboxes: lines_bboxes.map(&:to_render_hash),
|
|
97
|
+
text_info: text_info.to_render_hash, alignment: alignment)
|
|
98
|
+
end
|
|
31
99
|
end
|
|
32
100
|
|
|
33
101
|
class ImageTextResult < Base
|
|
@@ -39,8 +107,8 @@ module Lara
|
|
|
39
107
|
paragraphs = (hash["paragraphs"] || []).map { |p| build_paragraph(p) }
|
|
40
108
|
|
|
41
109
|
new(
|
|
42
|
-
source_language: hash["sourceLanguage"],
|
|
43
|
-
adapted_to: hash["adaptedTo"],
|
|
110
|
+
source_language: hash["source_language"] || hash["sourceLanguage"],
|
|
111
|
+
adapted_to: hash["adapted_to"] || hash["adaptedTo"],
|
|
44
112
|
glossaries: hash["glossaries"],
|
|
45
113
|
paragraphs: paragraphs
|
|
46
114
|
)
|
|
@@ -50,15 +118,30 @@ module Lara
|
|
|
50
118
|
private
|
|
51
119
|
|
|
52
120
|
def build_paragraph(paragraph_hash)
|
|
53
|
-
adapted_to_matches = convert_matches(
|
|
54
|
-
|
|
121
|
+
adapted_to_matches = convert_matches(
|
|
122
|
+
paragraph_hash["adapted_to_matches"] || paragraph_hash["adaptedToMatches"], NGMemoryMatch
|
|
123
|
+
)
|
|
124
|
+
glossaries_matches = convert_matches(
|
|
125
|
+
paragraph_hash["glossaries_matches"] || paragraph_hash["glossariesMatches"], NGGlossaryMatch
|
|
126
|
+
)
|
|
55
127
|
|
|
56
|
-
|
|
128
|
+
attributes = {
|
|
57
129
|
text: paragraph_hash["text"],
|
|
58
130
|
translation: paragraph_hash["translation"],
|
|
59
131
|
adapted_to_matches: adapted_to_matches,
|
|
60
132
|
glossaries_matches: glossaries_matches
|
|
61
|
-
|
|
133
|
+
}
|
|
134
|
+
bbox = paragraph_hash["bbox"]
|
|
135
|
+
lines_bboxes = paragraph_hash["lines_bboxes"] || paragraph_hash["linesBboxes"]
|
|
136
|
+
text_info = paragraph_hash["text_info"] || paragraph_hash["textInfo"]
|
|
137
|
+
alignment = paragraph_hash["alignment"]
|
|
138
|
+
if bbox && lines_bboxes && text_info && alignment
|
|
139
|
+
ImageLayoutParagraph.new(**attributes, bbox: ImageBBox.from_hash(bbox),
|
|
140
|
+
lines_bboxes: lines_bboxes.map { |box| ImageBBox.from_hash(box) },
|
|
141
|
+
text_info: ImageTextInfo.from_hash(text_info), alignment: alignment)
|
|
142
|
+
else
|
|
143
|
+
ImageParagraph.new(**attributes)
|
|
144
|
+
end
|
|
62
145
|
end
|
|
63
146
|
|
|
64
147
|
def convert_matches(value, klass)
|
data/lib/lara/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lara-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Translated
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|