typesafe-ai-ruby 0.1.0 → 0.2.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/CHANGELOG.md +12 -1
- data/README.md +62 -19
- data/lib/typesafe/assets.rb +64 -0
- data/lib/typesafe/client.rb +33 -6
- data/lib/typesafe/configuration.rb +13 -2
- data/lib/typesafe/constants.rb +8 -0
- data/lib/typesafe/errors.rb +6 -0
- data/lib/typesafe/http/multipart_encoder.rb +65 -0
- data/lib/typesafe/http/request.rb +6 -2
- data/lib/typesafe/http/requestor.rb +26 -9
- data/lib/typesafe/image.rb +123 -0
- data/lib/typesafe/questions/box.rb +19 -0
- data/lib/typesafe/questions/choice.rb +6 -3
- data/lib/typesafe/questions/normalizer.rb +18 -11
- data/lib/typesafe/questions/noul.rb +6 -3
- data/lib/typesafe/questions/question.rb +7 -2
- data/lib/typesafe/questions/score.rb +6 -3
- data/lib/typesafe/request_options.rb +20 -9
- data/lib/typesafe/responses/answer.rb +74 -1
- data/lib/typesafe/responses/system_one_response.rb +4 -0
- data/lib/typesafe/util.rb +29 -9
- data/lib/typesafe/version.rb +1 -1
- data/lib/typesafe.rb +13 -0
- data/sig/typesafe/http.rbs +27 -2
- data/sig/typesafe/questions.rbs +15 -8
- data/sig/typesafe/responses.rbs +27 -0
- data/sig/typesafe.rbs +57 -5
- metadata +5 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module TypeSafe
|
|
4
|
+
module Questions
|
|
5
|
+
# Locates what its instructions describe in an image. The answer is a bounding box.
|
|
6
|
+
# Box questions need an image in the request, the :images danger zone, and Jev 1.14
|
|
7
|
+
# (model "jev-preview" or "jev-1.14.0"). Box takes no criteria.
|
|
8
|
+
#
|
|
9
|
+
# For example:
|
|
10
|
+
# TypeSafe::Box.new(instructions: "the coffee mug in `photo`")
|
|
11
|
+
class Box < Question
|
|
12
|
+
TYPE = "box"
|
|
13
|
+
|
|
14
|
+
def initialize(instructions: nil)
|
|
15
|
+
super(TYPE, instructions: instructions, criteria: nil)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -14,10 +14,13 @@ module TypeSafe
|
|
|
14
14
|
TYPE = "choice"
|
|
15
15
|
|
|
16
16
|
def initialize(criteria:, instructions: nil)
|
|
17
|
-
|
|
17
|
+
assets = []
|
|
18
|
+
instructions = Util.deep_stringify(instructions, assets: assets) # collect instruction images first
|
|
19
|
+
super(TYPE, instructions: instructions, criteria: self.class.normalize_criteria(criteria, assets: assets),
|
|
20
|
+
assets: assets)
|
|
18
21
|
end
|
|
19
22
|
|
|
20
|
-
def self.normalize_criteria(criteria)
|
|
23
|
+
def self.normalize_criteria(criteria, assets: nil)
|
|
21
24
|
raise ValidationError, "Choice criteria must be a non-empty Hash of option => description" unless
|
|
22
25
|
criteria.is_a?(Hash) && !criteria.empty?
|
|
23
26
|
|
|
@@ -25,7 +28,7 @@ module TypeSafe
|
|
|
25
28
|
name = Util.stringify_key(key)
|
|
26
29
|
raise ValidationError, "Choice option names must not be blank" if name.strip.empty?
|
|
27
30
|
|
|
28
|
-
result[name] = value.nil? ? nil : Util.deep_stringify(value)
|
|
31
|
+
result[name] = value.nil? ? nil : Util.deep_stringify(value, assets: assets)
|
|
29
32
|
end
|
|
30
33
|
end
|
|
31
34
|
end
|
|
@@ -6,36 +6,39 @@ module TypeSafe
|
|
|
6
6
|
# mistakes before a request is sent.
|
|
7
7
|
module Normalizer
|
|
8
8
|
TYPES = {
|
|
9
|
-
Noul::TYPE => Noul, Choice::TYPE => Choice, Score::TYPE => Score
|
|
9
|
+
Noul::TYPE => Noul, Choice::TYPE => Choice, Score::TYPE => Score, Box::TYPE => Box
|
|
10
10
|
}.freeze
|
|
11
11
|
|
|
12
12
|
module_function
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
# Images found in the state are appended to +assets+ and replaced by their references.
|
|
15
|
+
def state(state, assets: nil)
|
|
15
16
|
raise ValidationError, "state must not be nil; pass a String, Hash, or Array" if state.nil?
|
|
16
17
|
|
|
17
|
-
value = Util.deep_stringify(state)
|
|
18
|
+
value = Util.deep_stringify(state, assets: assets)
|
|
18
19
|
return value if value.is_a?(String) || value.is_a?(Hash) || value.is_a?(Array)
|
|
19
20
|
|
|
20
21
|
raise ValidationError, "state must be a String, Hash, or Array, got #{state.class}"
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
# Questions in wire format.
|
|
24
|
-
def questions(questions)
|
|
25
|
+
def questions(questions, assets: nil)
|
|
25
26
|
raise ValidationError, "questions must be a non-empty Hash" unless questions.is_a?(Hash) && !questions.empty?
|
|
26
27
|
|
|
27
28
|
questions.each_with_object({}) do |(name, question), result|
|
|
28
29
|
key = Util.stringify_key(name)
|
|
29
30
|
raise ValidationError, "question names must not be blank" if key.strip.empty?
|
|
30
31
|
|
|
31
|
-
result[key] = question(question, key)
|
|
32
|
+
result[key] = question(question, key, assets: assets)
|
|
32
33
|
end
|
|
33
34
|
end
|
|
34
35
|
|
|
35
|
-
def question(question, name)
|
|
36
|
+
def question(question, name, assets: nil)
|
|
36
37
|
case question
|
|
37
|
-
when Question
|
|
38
|
-
|
|
38
|
+
when Question
|
|
39
|
+
assets&.concat(question.assets)
|
|
40
|
+
question.to_h
|
|
41
|
+
when Hash then raw_question(question, name, assets: assets)
|
|
39
42
|
else
|
|
40
43
|
raise ValidationError,
|
|
41
44
|
"question #{name.inspect} must be a TypeSafe::Noul, Choice, Score, or a Hash with a \"type\" key"
|
|
@@ -44,8 +47,8 @@ module TypeSafe
|
|
|
44
47
|
|
|
45
48
|
# Validate a raw Hash question. Known types get the same checks as the typed classes;
|
|
46
49
|
# unknown types and extra keys pass through untouched so new API fields work immediately.
|
|
47
|
-
def raw_question(hash, name)
|
|
48
|
-
wire = Util.deep_stringify(hash)
|
|
50
|
+
def raw_question(hash, name, assets: nil)
|
|
51
|
+
wire = Util.deep_stringify(hash, assets: assets)
|
|
49
52
|
type = wire["type"]
|
|
50
53
|
unless type.is_a?(String) && !type.empty?
|
|
51
54
|
raise ValidationError,
|
|
@@ -53,7 +56,11 @@ module TypeSafe
|
|
|
53
56
|
end
|
|
54
57
|
|
|
55
58
|
klass = TYPES[type]
|
|
56
|
-
|
|
59
|
+
if klass == Box
|
|
60
|
+
raise ValidationError, "question #{name.inspect}: box questions take no criteria" if wire.key?("criteria")
|
|
61
|
+
elsif klass && requires_criteria?(klass, wire)
|
|
62
|
+
wire["criteria"] = klass.normalize_criteria(wire["criteria"])
|
|
63
|
+
end
|
|
57
64
|
wire
|
|
58
65
|
end
|
|
59
66
|
|
|
@@ -14,10 +14,13 @@ module TypeSafe
|
|
|
14
14
|
KEYS = %w[true false].freeze
|
|
15
15
|
|
|
16
16
|
def initialize(instructions: nil, criteria: nil)
|
|
17
|
-
|
|
17
|
+
assets = []
|
|
18
|
+
instructions = Util.deep_stringify(instructions, assets: assets) # collect instruction images first
|
|
19
|
+
super(TYPE, instructions: instructions, criteria: self.class.normalize_criteria(criteria, assets: assets),
|
|
20
|
+
assets: assets)
|
|
18
21
|
end
|
|
19
22
|
|
|
20
|
-
def self.normalize_criteria(criteria)
|
|
23
|
+
def self.normalize_criteria(criteria, assets: nil)
|
|
21
24
|
return nil if criteria.nil?
|
|
22
25
|
raise ValidationError, "Noul criteria must be a Hash with true/false keys" unless criteria.is_a?(Hash)
|
|
23
26
|
|
|
@@ -28,7 +31,7 @@ module TypeSafe
|
|
|
28
31
|
"Noul criteria keys must be true or false, got #{key.inspect}"
|
|
29
32
|
end
|
|
30
33
|
|
|
31
|
-
result[name] = value.nil? ? nil : Util.deep_stringify(value)
|
|
34
|
+
result[name] = value.nil? ? nil : Util.deep_stringify(value, assets: assets)
|
|
32
35
|
end
|
|
33
36
|
end
|
|
34
37
|
end
|
|
@@ -11,11 +11,16 @@ module TypeSafe
|
|
|
11
11
|
attr_reader :instructions
|
|
12
12
|
# Type-specific rubric.
|
|
13
13
|
attr_reader :criteria
|
|
14
|
+
# Images placed in the instructions or criteria, sent as assets with the request.
|
|
15
|
+
attr_reader :assets
|
|
14
16
|
|
|
15
|
-
def initialize(type, instructions:, criteria:)
|
|
17
|
+
def initialize(type, instructions:, criteria:, assets: [])
|
|
16
18
|
@type = type
|
|
17
|
-
@
|
|
19
|
+
@assets = assets
|
|
20
|
+
@instructions = instructions.nil? ? nil : Util.deep_stringify(instructions, assets: @assets)
|
|
18
21
|
@criteria = criteria
|
|
22
|
+
@assets.uniq!
|
|
23
|
+
@assets.freeze
|
|
19
24
|
freeze
|
|
20
25
|
end
|
|
21
26
|
|
|
@@ -14,17 +14,20 @@ module TypeSafe
|
|
|
14
14
|
MIN_LEVELS = 2
|
|
15
15
|
|
|
16
16
|
def initialize(criteria:, instructions: nil)
|
|
17
|
-
|
|
17
|
+
assets = []
|
|
18
|
+
instructions = Util.deep_stringify(instructions, assets: assets) # collect instruction images first
|
|
19
|
+
super(TYPE, instructions: instructions, criteria: self.class.normalize_criteria(criteria, assets: assets),
|
|
20
|
+
assets: assets)
|
|
18
21
|
end
|
|
19
22
|
|
|
20
|
-
def self.normalize_criteria(criteria)
|
|
23
|
+
def self.normalize_criteria(criteria, assets: nil)
|
|
21
24
|
raise ValidationError, "Score criteria must be an Array of at least #{MIN_LEVELS} levels" unless
|
|
22
25
|
criteria.is_a?(Array) && criteria.length >= MIN_LEVELS
|
|
23
26
|
|
|
24
27
|
criteria.map do |level|
|
|
25
28
|
raise ValidationError, "Score levels must not be nil" if level.nil?
|
|
26
29
|
|
|
27
|
-
Util.deep_stringify(level)
|
|
30
|
+
Util.deep_stringify(level, assets: assets)
|
|
28
31
|
end
|
|
29
32
|
end
|
|
30
33
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module TypeSafe
|
|
4
4
|
# Per-call overrides accepted by Client#system_one and Resources::Models#list.
|
|
5
5
|
class RequestOptions
|
|
6
|
-
KEYS = %i[timeout headers extra_body retry_policy].freeze
|
|
6
|
+
KEYS = %i[timeout headers extra_body retry_policy danger_zone].freeze
|
|
7
7
|
|
|
8
8
|
# Timeout per attempt in seconds for this call only.
|
|
9
9
|
attr_reader :timeout
|
|
@@ -13,21 +13,32 @@ module TypeSafe
|
|
|
13
13
|
attr_reader :extra_body
|
|
14
14
|
# Retry policy override for this call only: a RetryPolicy, or a Hash applied on the client policy.
|
|
15
15
|
attr_reader :retry_policy
|
|
16
|
+
# Experimental features to turn on for this call: :images and/or :long_context.
|
|
17
|
+
attr_reader :danger_zone
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
# Coerce nil, a Hash, or a RequestOptions. A danger_zone given separately is merged in.
|
|
20
|
+
def self.from(options, danger_zone: nil)
|
|
21
|
+
result = case options
|
|
22
|
+
when RequestOptions then options
|
|
23
|
+
when nil then new
|
|
24
|
+
when Hash then new(**options.transform_keys(&:to_sym))
|
|
25
|
+
else raise ArgumentError, "request_options must be a Hash, got #{options.class}"
|
|
26
|
+
end
|
|
27
|
+
danger_zone.nil? ? result : result.with_danger_zone(danger_zone)
|
|
24
28
|
end
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
# A copy with additional danger zones turned on.
|
|
31
|
+
def with_danger_zone(zones)
|
|
32
|
+
self.class.new(timeout: timeout, headers: headers, extra_body: extra_body, retry_policy: retry_policy,
|
|
33
|
+
danger_zone: danger_zone + Util.danger_zones(zones))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def initialize(timeout: nil, headers: nil, extra_body: nil, retry_policy: nil, danger_zone: nil)
|
|
27
37
|
@timeout = validate_timeout(timeout)
|
|
28
38
|
@headers = (headers || {}).each_with_object({}) { |(name, value), result| result[name.to_s] = value.to_s }
|
|
29
39
|
@extra_body = validate_extra_body(extra_body)
|
|
30
40
|
@retry_policy = validate_retry_policy(retry_policy)
|
|
41
|
+
@danger_zone = Util.danger_zones(danger_zone).freeze
|
|
31
42
|
freeze
|
|
32
43
|
end
|
|
33
44
|
|
|
@@ -129,6 +129,79 @@ module TypeSafe
|
|
|
129
129
|
end
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
+
# The four edges of a box as fractions of the image's width (left, right) and height
|
|
133
|
+
# (top, bottom), measured from the top-left corner.
|
|
134
|
+
class BoxCoordinates
|
|
135
|
+
attr_reader :top, :bottom, :left, :right
|
|
136
|
+
|
|
137
|
+
def self.parse(data, path)
|
|
138
|
+
box = Reader.hash!(data, path)
|
|
139
|
+
new(**%i[top bottom left right].to_h { |edge| [edge, Reader.number!(box[edge.to_s], "#{path}.#{edge}")] })
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def initialize(top:, bottom:, left:, right:)
|
|
143
|
+
@top = top
|
|
144
|
+
@bottom = bottom
|
|
145
|
+
@left = left
|
|
146
|
+
@right = right
|
|
147
|
+
freeze
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def width
|
|
151
|
+
right - left
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def height
|
|
155
|
+
bottom - top
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# [left, top, right, bottom], the order most image libraries use for a crop box.
|
|
159
|
+
def to_a
|
|
160
|
+
[left, top, right, bottom]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# [left, top, right, bottom] in integer pixels for an image of the given size.
|
|
164
|
+
def to_pixels(width:, height:)
|
|
165
|
+
[(left * width).round, (top * height).round, (right * width).round, (bottom * height).round]
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def to_h
|
|
169
|
+
{ "top" => top, "bottom" => bottom, "left" => left, "right" => right }
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def ==(other)
|
|
173
|
+
other.is_a?(BoxCoordinates) && to_a == other.to_a
|
|
174
|
+
end
|
|
175
|
+
alias eql? ==
|
|
176
|
+
|
|
177
|
+
def hash
|
|
178
|
+
to_a.hash
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# A located box with its confidence.
|
|
183
|
+
class BoxAnswer < Answer
|
|
184
|
+
attr_reader :box
|
|
185
|
+
# Confidence from 0 to 1, or nil when the API did not report it.
|
|
186
|
+
attr_reader :confidence
|
|
187
|
+
|
|
188
|
+
def self.parse(data, path)
|
|
189
|
+
confidence = data["confidence"]
|
|
190
|
+
new(
|
|
191
|
+
data,
|
|
192
|
+
box: BoxCoordinates.parse(data["box"], "#{path}.box"),
|
|
193
|
+
confidence: confidence.nil? ? nil : Reader.number!(confidence, "#{path}.confidence")
|
|
194
|
+
)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def initialize(raw, box:, confidence:)
|
|
198
|
+
@box = box
|
|
199
|
+
@confidence = confidence
|
|
200
|
+
super(raw)
|
|
201
|
+
freeze
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
132
205
|
# An answer whose type this gem does not recognize. Only #raw is available.
|
|
133
206
|
class UnknownAnswer < Answer
|
|
134
207
|
def self.parse(data, _path)
|
|
@@ -137,7 +210,7 @@ module TypeSafe
|
|
|
137
210
|
end
|
|
138
211
|
|
|
139
212
|
Answer::TYPES = {
|
|
140
|
-
"noul" => NoulAnswer, "choice" => ChoiceAnswer, "score" => ScoreAnswer
|
|
213
|
+
"noul" => NoulAnswer, "choice" => ChoiceAnswer, "score" => ScoreAnswer, "box" => BoxAnswer
|
|
141
214
|
}.freeze
|
|
142
215
|
end
|
|
143
216
|
end
|
data/lib/typesafe/util.rb
CHANGED
|
@@ -30,21 +30,30 @@ module TypeSafe
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
# Convert a value into plain JSON-compatible Ruby: string keys, no symbols, finite floats.
|
|
33
|
-
# Objects responding to as_json are converted through it.
|
|
34
|
-
|
|
33
|
+
# Objects responding to as_json are converted through it. Images become their reference and
|
|
34
|
+
# are appended to +assets+; without an +assets+ collector an Image raises. Anything else raises.
|
|
35
|
+
def deep_stringify(value, assets: nil)
|
|
35
36
|
case value
|
|
36
37
|
when String, Integer, true, false, nil then value
|
|
37
38
|
when Symbol then value.to_s
|
|
38
39
|
when Float then finite_float(value)
|
|
39
|
-
when
|
|
40
|
-
|
|
40
|
+
when Image then image_reference(value, assets)
|
|
41
|
+
when Hash, Array then stringify_container(value, assets)
|
|
42
|
+
else convert_object(value, assets)
|
|
41
43
|
end
|
|
42
44
|
end
|
|
43
45
|
|
|
44
|
-
def
|
|
45
|
-
|
|
46
|
+
def image_reference(image, assets)
|
|
47
|
+
raise ValidationError, "images can only be placed in state, instructions and criteria" if assets.nil?
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
assets << image
|
|
50
|
+
image.reference
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def stringify_container(value, assets)
|
|
54
|
+
return value.map { |v| deep_stringify(v, assets: assets) } if value.is_a?(Array)
|
|
55
|
+
|
|
56
|
+
value.each_with_object({}) { |(k, v), h| h[stringify_key(k)] = deep_stringify(v, assets: assets) }
|
|
48
57
|
end
|
|
49
58
|
|
|
50
59
|
def finite_float(value)
|
|
@@ -61,7 +70,7 @@ module TypeSafe
|
|
|
61
70
|
end
|
|
62
71
|
end
|
|
63
72
|
|
|
64
|
-
def convert_object(value)
|
|
73
|
+
def convert_object(value, assets)
|
|
65
74
|
unless value.respond_to?(:as_json)
|
|
66
75
|
raise ValidationError,
|
|
67
76
|
"cannot encode #{value.class} as JSON; pass a String, Hash, Array, or an object responding to as_json"
|
|
@@ -70,7 +79,7 @@ module TypeSafe
|
|
|
70
79
|
converted = value.as_json
|
|
71
80
|
raise ValidationError, "#{value.class}#as_json returned itself and cannot be encoded" if converted.equal?(value)
|
|
72
81
|
|
|
73
|
-
deep_stringify(converted)
|
|
82
|
+
deep_stringify(converted, assets: assets)
|
|
74
83
|
end
|
|
75
84
|
|
|
76
85
|
# Parse retry-after-ms (preferred) or Retry-After (seconds or HTTP-date) into seconds.
|
|
@@ -112,6 +121,17 @@ module TypeSafe
|
|
|
112
121
|
JSON.generate(body)
|
|
113
122
|
end
|
|
114
123
|
|
|
124
|
+
# Normalize a danger_zone option into an Array of known Symbols.
|
|
125
|
+
def danger_zones(value)
|
|
126
|
+
zones = Array(value).map { |zone| zone.to_s.to_sym }.uniq
|
|
127
|
+
unknown = zones - Constants::DANGER_ZONES.keys
|
|
128
|
+
return zones if unknown.empty?
|
|
129
|
+
|
|
130
|
+
raise ArgumentError,
|
|
131
|
+
"unknown danger_zone #{unknown.map(&:inspect).join(", ")}; " \
|
|
132
|
+
"expected #{Constants::DANGER_ZONES.keys.map(&:inspect).join(", ")}"
|
|
133
|
+
end
|
|
134
|
+
|
|
115
135
|
# The runtime identifier sent in X-TypeSafe-Runtime.
|
|
116
136
|
def runtime
|
|
117
137
|
"ruby/#{RUBY_VERSION} (#{RUBY_PLATFORM})"
|
data/lib/typesafe/version.rb
CHANGED
data/lib/typesafe.rb
CHANGED
|
@@ -14,14 +14,18 @@ require_relative "typesafe/logging"
|
|
|
14
14
|
require_relative "typesafe/instrumentation"
|
|
15
15
|
require_relative "typesafe/configuration"
|
|
16
16
|
require_relative "typesafe/retry_policy"
|
|
17
|
+
require_relative "typesafe/image"
|
|
18
|
+
require_relative "typesafe/assets"
|
|
17
19
|
require_relative "typesafe/request_options"
|
|
18
20
|
require_relative "typesafe/questions/question"
|
|
19
21
|
require_relative "typesafe/questions/noul"
|
|
20
22
|
require_relative "typesafe/questions/choice"
|
|
21
23
|
require_relative "typesafe/questions/score"
|
|
24
|
+
require_relative "typesafe/questions/box"
|
|
22
25
|
require_relative "typesafe/questions/normalizer"
|
|
23
26
|
require_relative "typesafe/http/request"
|
|
24
27
|
require_relative "typesafe/http/response"
|
|
28
|
+
require_relative "typesafe/http/multipart_encoder"
|
|
25
29
|
require_relative "typesafe/http/connection_manager"
|
|
26
30
|
require_relative "typesafe/http/net_http_transport"
|
|
27
31
|
require_relative "typesafe/http/retrier"
|
|
@@ -41,6 +45,7 @@ module TypeSafe
|
|
|
41
45
|
Noul = Questions::Noul
|
|
42
46
|
Choice = Questions::Choice
|
|
43
47
|
Score = Questions::Score
|
|
48
|
+
Box = Questions::Box
|
|
44
49
|
|
|
45
50
|
class << self
|
|
46
51
|
# Global settings used by .client. Mutate through .configure.
|
|
@@ -102,6 +107,14 @@ module TypeSafe
|
|
|
102
107
|
Score.new(instructions: instructions, criteria: criteria)
|
|
103
108
|
end
|
|
104
109
|
|
|
110
|
+
# Build a box question.
|
|
111
|
+
#
|
|
112
|
+
# For example:
|
|
113
|
+
# TypeSafe.box("the coffee mug in `photo`")
|
|
114
|
+
def box(instructions = nil)
|
|
115
|
+
Box.new(instructions: instructions)
|
|
116
|
+
end
|
|
117
|
+
|
|
105
118
|
private
|
|
106
119
|
|
|
107
120
|
def merge_criteria(positional, named)
|
data/sig/typesafe/http.rbs
CHANGED
|
@@ -11,8 +11,10 @@ module TypeSafe
|
|
|
11
11
|
attr_reader headers: Hash[String, String]
|
|
12
12
|
attr_reader body: String?
|
|
13
13
|
attr_reader timeout: Numeric
|
|
14
|
+
attr_reader body_summary: String?
|
|
14
15
|
|
|
15
|
-
def initialize: (method: Symbol, uri: URI::HTTP, headers: Hash[String, String], body: String?, timeout: Numeric
|
|
16
|
+
def initialize: (method: Symbol, uri: URI::HTTP, headers: Hash[String, String], body: String?, timeout: Numeric,
|
|
17
|
+
?body_summary: String?) -> void
|
|
16
18
|
def endpoint: () -> String
|
|
17
19
|
def with_headers: (Hash[String, String] extra) -> Request
|
|
18
20
|
end
|
|
@@ -31,6 +33,29 @@ module TypeSafe
|
|
|
31
33
|
def retry_after: () -> Float?
|
|
32
34
|
end
|
|
33
35
|
|
|
36
|
+
class MultipartEncoder
|
|
37
|
+
class Part
|
|
38
|
+
attr_accessor name: String
|
|
39
|
+
attr_accessor content_type: String
|
|
40
|
+
attr_accessor content: String
|
|
41
|
+
|
|
42
|
+
def initialize: (name: String, content_type: String, content: String) -> void
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class Encoded
|
|
46
|
+
attr_accessor body: String
|
|
47
|
+
attr_accessor content_type: String
|
|
48
|
+
attr_accessor summary: String
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
attr_reader boundary: String
|
|
52
|
+
|
|
53
|
+
def self.build: (Hash[String, untyped] body, Hash[String, Image] assets) -> Encoded
|
|
54
|
+
def initialize: (?boundary: String) -> void
|
|
55
|
+
def content_type: () -> String
|
|
56
|
+
def encode: (String json, Array[Part] parts) -> String
|
|
57
|
+
end
|
|
58
|
+
|
|
34
59
|
class ConnectionManager
|
|
35
60
|
IDLE_TIMEOUT: Integer
|
|
36
61
|
|
|
@@ -69,7 +94,7 @@ module TypeSafe
|
|
|
69
94
|
|
|
70
95
|
def initialize: (Configuration config, ?transport: _Transport?, ?sleeper: (^(Numeric) -> void)?,
|
|
71
96
|
?clock: (^() -> Float)?) -> void
|
|
72
|
-
def post: (String path, body: Hash[String, untyped], ?options: RequestOptions) -> Response
|
|
97
|
+
def post: (String path, body: Hash[String, untyped], ?options: RequestOptions, ?assets: Hash[String, Image]) -> Response
|
|
73
98
|
def get: (String path, ?options: RequestOptions) -> Response
|
|
74
99
|
def close: () -> void
|
|
75
100
|
end
|
data/sig/typesafe/questions.rbs
CHANGED
|
@@ -4,8 +4,9 @@ module TypeSafe
|
|
|
4
4
|
attr_reader type: String
|
|
5
5
|
attr_reader instructions: json_content?
|
|
6
6
|
attr_reader criteria: untyped
|
|
7
|
+
attr_reader assets: Array[Image]
|
|
7
8
|
|
|
8
|
-
def initialize: (String type, instructions: json_input?, criteria: untyped) -> void
|
|
9
|
+
def initialize: (String type, instructions: json_input?, criteria: untyped, ?assets: Array[Image]) -> void
|
|
9
10
|
def to_h: () -> Hash[String, untyped]
|
|
10
11
|
def to_json: (*untyped args) -> String
|
|
11
12
|
def ==: (untyped other) -> bool
|
|
@@ -18,14 +19,14 @@ module TypeSafe
|
|
|
18
19
|
KEYS: Array[String]
|
|
19
20
|
|
|
20
21
|
def initialize: (?instructions: json_input?, ?criteria: Hash[untyped, untyped]?) -> void
|
|
21
|
-
def self.normalize_criteria: (Hash[untyped, untyped]? criteria) -> Hash[String, json_content?]?
|
|
22
|
+
def self.normalize_criteria: (Hash[untyped, untyped]? criteria, ?assets: Array[Image]?) -> Hash[String, json_content?]?
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
class Choice < Question
|
|
25
26
|
TYPE: String
|
|
26
27
|
|
|
27
28
|
def initialize: (?instructions: json_input?, criteria: Hash[untyped, untyped]) -> void
|
|
28
|
-
def self.normalize_criteria: (Hash[untyped, untyped] criteria) -> Hash[String, json_content?]
|
|
29
|
+
def self.normalize_criteria: (Hash[untyped, untyped] criteria, ?assets: Array[Image]?) -> Hash[String, json_content?]
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
class Score < Question
|
|
@@ -33,16 +34,22 @@ module TypeSafe
|
|
|
33
34
|
MIN_LEVELS: Integer
|
|
34
35
|
|
|
35
36
|
def initialize: (?instructions: json_input?, criteria: Array[untyped]) -> void
|
|
36
|
-
def self.normalize_criteria: (Array[untyped] criteria) -> Array[json_content]
|
|
37
|
+
def self.normalize_criteria: (Array[untyped] criteria, ?assets: Array[Image]?) -> Array[json_content]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Box < Question
|
|
41
|
+
TYPE: String
|
|
42
|
+
|
|
43
|
+
def initialize: (?instructions: json_input?) -> void
|
|
37
44
|
end
|
|
38
45
|
|
|
39
46
|
module Normalizer
|
|
40
47
|
TYPES: Hash[String, singleton(Question)]
|
|
41
48
|
|
|
42
|
-
def self?.state: (json_input state) -> json_content
|
|
43
|
-
def self?.questions: (Hash[String | Symbol, question_input] questions) -> Hash[String, Hash[String, untyped]]
|
|
44
|
-
def self?.question: (question_input question, String name) -> Hash[String, untyped]
|
|
45
|
-
def self?.raw_question: (Hash[untyped, untyped] hash, String name) -> Hash[String, untyped]
|
|
49
|
+
def self?.state: (json_input | Image state, ?assets: Array[Image]?) -> json_content
|
|
50
|
+
def self?.questions: (Hash[String | Symbol, question_input] questions, ?assets: Array[Image]?) -> Hash[String, Hash[String, untyped]]
|
|
51
|
+
def self?.question: (question_input question, String name, ?assets: Array[Image]?) -> Hash[String, untyped]
|
|
52
|
+
def self?.raw_question: (Hash[untyped, untyped] hash, String name, ?assets: Array[Image]?) -> Hash[String, untyped]
|
|
46
53
|
end
|
|
47
54
|
end
|
|
48
55
|
end
|
data/sig/typesafe/responses.rbs
CHANGED
|
@@ -73,6 +73,32 @@ module TypeSafe
|
|
|
73
73
|
def raw_probabilities: () -> Hash[String, Numeric]
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
+
class BoxCoordinates
|
|
77
|
+
attr_reader top: Float
|
|
78
|
+
attr_reader bottom: Float
|
|
79
|
+
attr_reader left: Float
|
|
80
|
+
attr_reader right: Float
|
|
81
|
+
|
|
82
|
+
def self.parse: (untyped data, String path) -> BoxCoordinates
|
|
83
|
+
def initialize: (top: Float, bottom: Float, left: Float, right: Float) -> void
|
|
84
|
+
def width: () -> Float
|
|
85
|
+
def height: () -> Float
|
|
86
|
+
def to_a: () -> [Float, Float, Float, Float]
|
|
87
|
+
def to_pixels: (width: Numeric, height: Numeric) -> [Integer, Integer, Integer, Integer]
|
|
88
|
+
def to_h: () -> Hash[String, Float]
|
|
89
|
+
def ==: (untyped other) -> bool
|
|
90
|
+
alias eql? ==
|
|
91
|
+
def hash: () -> Integer
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
class BoxAnswer < Answer
|
|
95
|
+
attr_reader box: BoxCoordinates
|
|
96
|
+
attr_reader confidence: Float?
|
|
97
|
+
|
|
98
|
+
def self.parse: (Hash[String, untyped] data, String path) -> BoxAnswer
|
|
99
|
+
def initialize: (Hash[String, untyped] raw, box: BoxCoordinates, confidence: Float?) -> void
|
|
100
|
+
end
|
|
101
|
+
|
|
76
102
|
class UnknownAnswer < Answer
|
|
77
103
|
def self.parse: (Hash[String, untyped] data, String path) -> UnknownAnswer
|
|
78
104
|
end
|
|
@@ -94,6 +120,7 @@ module TypeSafe
|
|
|
94
120
|
def nouls: () -> IndifferentHash
|
|
95
121
|
def choices: () -> IndifferentHash
|
|
96
122
|
def scores: () -> IndifferentHash
|
|
123
|
+
def boxes: () -> IndifferentHash
|
|
97
124
|
def to_h: () -> Hash[String, untyped]
|
|
98
125
|
def to_json: (*untyped args) -> String
|
|
99
126
|
end
|