clarinet 0.5.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.
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Clarinet
4
+ class Inputs
5
+ extend Forwardable
6
+
7
+ # @!method []
8
+ # @see Array#[]
9
+ # @return [Clarinet::Model]
10
+ # @!method each
11
+ # @see Array#each
12
+ # @!method map
13
+ # @see Array#map
14
+ # @!method find
15
+ # @see Array#find
16
+ # @!method select
17
+ # @see Array#select
18
+ # @!method select
19
+ # @see Array#select
20
+ delegate [:[], :each, :map, :find, :select, :reject] => :@inputs
21
+
22
+ # @!visibility private
23
+ def initialize(app, raw_data = [])
24
+ @app = app
25
+
26
+ @raw_data = raw_data
27
+
28
+ @inputs = raw_data.map do |input_data|
29
+ Clarinet::Input.new app, input_data
30
+ end
31
+ end
32
+
33
+ # Adds an input or multiple inputs
34
+ # @return [Clarinet::Inputs] Instance of Inputs
35
+ def create(inputs)
36
+ inputs = [inputs] unless inputs.is_a? Array
37
+ inputs = inputs.map { |input| Clarinet::Utils.format_input(input) }
38
+
39
+ data = @app.client.inputs_create inputs
40
+ Clarinet::Inputs.new data[:inputs]
41
+ end
42
+
43
+ # Delete an input or a list of inputs by id
44
+ # @return [Hash] API response
45
+ def delete(id)
46
+ @app.client.input_delete id if id.is_a? String
47
+ @app.client.inputs_delete id if id.is_a? Array
48
+ end
49
+
50
+ # Delete all inputs
51
+ # @return [Hash] API response
52
+ def delete_all
53
+ @app.client.inputs_delete_all
54
+ end
55
+
56
+ # Get all inputs in app
57
+ # @param options [Hash] Listing options
58
+ # @option options [Int] :page (1) The page number
59
+ # @option options [Int] :per_page (20) Number of models to return per page
60
+ # @return [Clarinet::Inputs] Inputs instance
61
+ def list(options = { page: 1, per_page: 20 })
62
+ data = @app.client.inputs options
63
+ Clarinet::Inputs.new @app, data[:inputs]
64
+ end
65
+
66
+ # Get input by id
67
+ # @param id [String] The input id
68
+ # @return [Clarinet::Input] Input instance
69
+ def get(id)
70
+ data = @app.client.input id
71
+ Clarinet::Input.new @app, data[:input]
72
+ end
73
+
74
+ # Get inputs status (number of uploaded, in process or failed inputs)
75
+ # @return [Hash] API response
76
+ def status
77
+ @app.client.inputs_status
78
+ end
79
+
80
+ def merge_concepts(inputs)
81
+ update 'merge', inputs
82
+ end
83
+
84
+ def overwrite_concepts(inputs)
85
+ update 'overwrite', inputs
86
+ end
87
+
88
+ def delete_concepts(inputs)
89
+ update 'remove', inputs
90
+ end
91
+
92
+ private
93
+
94
+ def update(action, inputs)
95
+ inputs = [inputs] unless inputs.is_a? Array
96
+ inputs = inputs.map { |input| Clarinet::Utils.format_input(input) }
97
+
98
+ data = {
99
+ action: action,
100
+ inputs: inputs
101
+ }
102
+
103
+ response_data = @app.client.inputs_update data
104
+ Clarinet::Inputs.new @app, response_data[:inputs]
105
+ end
106
+
107
+ end
108
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'httparty'
4
+
5
+ module Clarinet
6
+ class Model
7
+
8
+ GENERAL = 'aaa03c23b3724a16a56b629203edc62c'
9
+ FOOD = 'bd367be194cf45149e75f01d59f77ba7'
10
+ TRAVEL = 'eee28c313d69466f836ab83287a54ed9'
11
+ NSFW = 'e9576d86d2004ed1a38ba0cf39ecb4b1'
12
+ WEDDINGS = 'c386b7a870114f4a87477c0824499348'
13
+ COLOR = 'eeed0b6733a644cea07cf4c60f87ebb7'
14
+
15
+ MAX_INPUT_COUNT = 128
16
+
17
+ # @return [Hash] Raw API data used to construct this instance
18
+ attr_reader :raw_data
19
+
20
+ # @return [String] Model id
21
+ attr_reader :id
22
+
23
+ # @return [String] Model name
24
+ attr_reader :name
25
+
26
+ # @return [String] Created at timestamp
27
+ attr_reader :created_at
28
+
29
+ # @return [String]
30
+ attr_reader :app_id
31
+
32
+ # @return [Hash]
33
+ attr_reader :output_info
34
+
35
+ # @return [String]
36
+ attr_reader :model_version
37
+
38
+ # @!visibility private
39
+ def initialize(app, raw_data)
40
+ @app = app
41
+
42
+ @raw_data = raw_data
43
+
44
+ @id = raw_data[:id]
45
+ @name = raw_data[:name]
46
+ @created_at = raw_data[:created_at]
47
+ @app_id = raw_data[:app_id]
48
+ @output_info = raw_data[:output_info]
49
+
50
+ @model_version = raw_data[:model_version]
51
+ end
52
+
53
+ # Returns all the model's output info
54
+ # @return [Clarinet::Model] Model instance with complete output_info data
55
+ def get_output_info
56
+ response_data = @app.client.model_output_info @id
57
+ Clarinet::Model.new @app, response_data[:model]
58
+ end
59
+
60
+ # Returns model ouputs according to inputs
61
+ # @param inputs [String, Hash, Array<String>, Array<Hash>] An array of objects/object/string pointing to
62
+ # an image resource. A string can either be a url or base64 image bytes. Object keys explained below:
63
+ # @!macro predict_inputs
64
+ # @option inputs [Hash] :image Object with at least +:url+ or +:base64+ key as explained below:
65
+ # * +:url+ (String) A publicly accessibly
66
+ # * +:base64+ (String) Base64 string representing image bytes
67
+ # * +:crop+ (Array<Float>) An array containing the percent to be cropped from top, left, bottom and right
68
+ # @return [Hash] API response
69
+ def predict(inputs, config = {})
70
+ video = config[:video] || false
71
+ config.delete :video
72
+
73
+ inputs = [inputs] unless inputs.is_a? Array
74
+ inputs = inputs.map { |input| Clarinet::Utils.format_media_predict(input) }
75
+
76
+ @app.client.outputs id, inputs, config
77
+ end
78
+
79
+ # Returns a list of versions of the model
80
+ # @param options [Hash] Listing options
81
+ # @option options [Int] :page (1) The page number
82
+ # @option options [Int] :per_page (20) Number of models to return per page
83
+ # @return [Hash] API response
84
+ def versions(options = { page: 1, per_page: 20 })
85
+ @app.client.model_versions @id, options
86
+ end
87
+
88
+ # Remove concepts from a model
89
+ # @param concepts [Array<Hash>] List of concept hashes with id
90
+ # @return [Clarinet::Model] Model instance
91
+ def delete_concepts(concepts)
92
+ concepts = [concepts] unless concepts.is_a? Array
93
+ update 'remove', { concepts: concepts }
94
+ end
95
+
96
+ # Merge concepts to a model
97
+ # @param (see #delete_concepts)
98
+ # @return (see #delete_concepts)
99
+ def merge_concepts(concepts)
100
+ concepts = [concepts] unless concepts.is_a? Array
101
+ update 'merge', { concepts: concepts }
102
+ end
103
+
104
+ # Overwrite concepts in a model
105
+ # @param (see #delete_concepts)
106
+ # @return (see #delete_concepts)
107
+ def overwrite_concepts(concepts)
108
+ concepts = [concepts] unless concepts.is_a? Array
109
+ update 'merge', { concepts: concepts }
110
+ end
111
+
112
+ # Create a new model version
113
+ # @note Training takes some time and the new version will not be immediately available.
114
+ # @return [Clarinet::Model] Model instance
115
+ def train
116
+ response_data = @app.client.model_train @id
117
+ Clarinet::Model.new @app, response_data[:model]
118
+ end
119
+
120
+ private
121
+
122
+ def update(action, obj)
123
+ model_data = obj.merge id: @id
124
+ data = {
125
+ models: [Clarinet::Utils.format_model(model_data)]
126
+ }
127
+
128
+ response_data = @app.client.models_update data
129
+ Clarinet::Model.new @app, response_data[:models].first
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Clarinet
4
+ class Models
5
+ extend Forwardable
6
+
7
+ # @!method []
8
+ # @see Array#[]
9
+ # @return [Clarinet::Model]
10
+ # @!method each
11
+ # @see Array#each
12
+ # @!method map
13
+ # @see Array#map
14
+ # @!method find
15
+ # @see Array#find
16
+ # @!method first
17
+ # @see Array#first
18
+ # @!method last
19
+ # @see Array#last
20
+ # @!method select
21
+ # @see Array#select
22
+ # @!method select
23
+ # @see Array#select
24
+ delegate [:[], :each, :map, :find, :first, :last, :select, :reject, :size] => :@models
25
+
26
+ # @return [Hash] Raw API data used to construct this instance
27
+ attr_reader :raw_data
28
+
29
+ # @!visibility private
30
+ def initialize(app, raw_data = [])
31
+ @app = app
32
+ @raw_data = raw_data
33
+
34
+ @models = raw_data.map do |model_data|
35
+ Clarinet::Model.new app, model_data
36
+ end
37
+ end
38
+
39
+ # Returns a Model instance given model id or name. It will call search if name is given.
40
+ # @param model [String, Hash, Clarinet::Model]
41
+ # If String, it is assumed to be model id. Otherwise, if Hash is given, it can have any of the following keys:
42
+ # @option model [String] :id Model id
43
+ # @option model [String] :name Model name
44
+ # @option model [String] :version Model version
45
+ # @option model [String] :type (nil) This can be "concept", "color", "embed", "facedetect", "cluster" or "blur"
46
+ # @return [Clarinet::Model] Model instance corresponding to the given id
47
+ # or the first search result
48
+ def init_model(model)
49
+ model_data = {}
50
+
51
+ model_data[:id] = model if model.is_a? String
52
+ model_data = model if model.is_a? Hash
53
+ model_data = model.raw_data if model.is_a? Clarinet::Model
54
+
55
+ return Clarinet::Model.new @app, model_data if model_data[:id]
56
+
57
+ search_results = search model_data[:name], model_data[:type]
58
+
59
+ return search_results.find { |result| result.model_version.id == model_data[:version] }.first if model_data[:version]
60
+
61
+ search_results.first
62
+ end
63
+
64
+ # Predict using a specific model
65
+ # @param model (see #init_model)
66
+ # @param inputs (see Clarinet::Model#predict)
67
+ # @macro predict_inputs
68
+ # @return [Hash] Data returned by the API with symbolized keys
69
+ def predict(model, inputs)
70
+ init_model(model).predict(inputs)
71
+ end
72
+
73
+ # Return all the models
74
+ # @param options [Hash] Listing options
75
+ # @option options [Int] :page (1) The page number
76
+ # @option options [Int] :per_page (20) Number of models to return per page
77
+ # @return [Clarinet::Models]
78
+ def list(options = { page: 1, per_page: 20 })
79
+ data = @app.client.models options
80
+ Clarinet::Models.new @app, data[:models]
81
+ end
82
+
83
+ # Returns a model specified by ID
84
+ # @param id [String] The model's id
85
+ # @return [Clarinet::Model] Model instance
86
+ def get(id)
87
+ data = @app.client.model id
88
+ Clarinet::Model.new @app, data[:model]
89
+ end
90
+
91
+ # Search for models by name or type
92
+ # @param name [String] The model name
93
+ # @param type [String] This can be "concept", "color", "embed", "facedetect", "cluster" or "blur"
94
+ # @return [Clarinet::Models] Models instance
95
+ def search(name, type = nil)
96
+ query = {
97
+ name: name,
98
+ type: type
99
+ }
100
+
101
+ data = @app.client.models_search query
102
+ Clarinet::Models.new @app, data[:models]
103
+ end
104
+
105
+ end
106
+ end
@@ -0,0 +1,13 @@
1
+ module Clarinet
2
+ module Status
3
+ SUCCESS = 10000
4
+
5
+ INVALID_AUTH_TOKEN = 11001
6
+ API_KEY_NOT_FOUND = 11009
7
+
8
+ BAD_REQUEST_FORMAT = 11100
9
+ INVALID_REQUEST = 11102
10
+
11
+ IMAGE_DECODING_FAILED = 30300
12
+ end
13
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'addressable/uri'
4
+
5
+ module Clarinet
6
+ # @!visibility private
7
+ class Utils
8
+
9
+ def self.check_response_status(status)
10
+ status_code = status[:code]
11
+
12
+ return if status_code == Clarinet::Status::SUCCESS
13
+
14
+ error_class = Clarinet::Error::ApiError
15
+ error_class = Clarinet::Error::InvalidAuthTokenError if status_code == Clarinet::Status::INVALID_AUTH_TOKEN
16
+ error_class = Clarinet::Error::ApiKeyNotFoundError if status_code == Clarinet::Status::API_KEY_NOT_FOUND
17
+ error_class = Clarinet::Error::BadRequestFormatError if status_code == Clarinet::Status::BAD_REQUEST_FORMAT
18
+ error_class = Clarinet::Error::InvalidRequestError if status_code == Clarinet::Status::INVALID_REQUEST
19
+ error_class = Clarinet::Error::ImageDecodingError if status_code == Clarinet::Status::IMAGE_DECODING_FAILED
20
+
21
+ new_error = error_class.new status[:description]
22
+ new_error.code = status_code
23
+ new_error.description = status[:description]
24
+ new_error.details = status[:details]
25
+ raise new_error
26
+ end
27
+
28
+ def self.format_model(model_data)
29
+ formatted = {
30
+ id: model_data[:id]
31
+ }
32
+
33
+ formatted[:name] = model_data[:name] if model_data.key? :name
34
+
35
+ output_info = {}
36
+ if model_data.key? :concepts_mutually_exclusive
37
+ output_info[:output_config] = output_info[:output_config] || {}
38
+ output_info[:output_config][:concepts_mutually_exclusive] = model_data[:concepts_mutually_exclusive]
39
+ end
40
+
41
+ if model_data.key? :closed_environment
42
+ output_info[:output_config] = output_info[:output_config] || {}
43
+ output_info[:output_config][:closed_environment] = model_data[:closed_environment]
44
+ end
45
+
46
+ if model_data.key? :concepts
47
+ output_info[:data] = {
48
+ concepts: model_data[:concepts].map { |c| format_concept(c) }
49
+ }
50
+ end
51
+
52
+ formatted[:output_info] = output_info
53
+ formatted
54
+ end
55
+
56
+ def self.format_concept(concept_data)
57
+ return { id: concept_data } if concept_data.is_a? String
58
+ concept_data
59
+ end
60
+
61
+ def self.format_input(input_data, include_image = true)
62
+ input_data = { url: input_data } if input_data.is_a? String
63
+
64
+ formatted = {
65
+ id: input_data[:id],
66
+ data: {}
67
+ }
68
+
69
+ formatted[:data][:concepts] = input_data.concepts if input_data.key? :concepts
70
+ formatted[:data][:metadata] = input_data.metadata if input_data.key? :metadata
71
+ formatted[:data][:geo] = { geo_point: input_data.geo } if input_data.key? :geo
72
+
73
+ if include_image
74
+ formatted[:data][:image] = {
75
+ url: input_data[:url],
76
+ base64: input_data[:base64],
77
+ crop: input_data[:crop],
78
+ allow_duplicate_url: input_data.fetch(:allow_duplicate_url, false)
79
+ }
80
+ end
81
+
82
+ formatted
83
+ end
84
+
85
+ def self.format_media_predict(input_data, type = :image)
86
+ if input_data.is_a? String
87
+ input_data = { base64: input_data } unless valid_url? input_data
88
+ input_data = { url: input_data } if valid_url? input_data
89
+ end
90
+
91
+ data = {}
92
+ data[type] = input_data
93
+ { data: data }
94
+ end
95
+
96
+ private_class_method def self.valid_url?(url)
97
+ uri = Addressable::URI.parse url
98
+ uri.scheme == 'http' || uri.scheme == 'https'
99
+ end
100
+
101
+ end
102
+ end