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,6 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm:
4
+ - 2.3
5
+ notifications:
6
+ email: false
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ # A sample Gemfile
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ group :development do
8
+ gem 'yard'
9
+ end
10
+
11
+ group :development, :test do
12
+ gem 'rake'
13
+ end
14
+
15
+ group :test do
16
+ gem 'mocha', '1.2.1'
17
+ gem 'rspec', '3.6.0'
18
+ gem 'simplecov', '0.15.0', require: false
19
+ gem 'webmock', '3.0.1'
20
+ end
data/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2016-, Jaakko Rinta-Filppula
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
@@ -0,0 +1,29 @@
1
+ Clarinet [![Build Status](https://travis-ci.org/tophattom/clarinet.svg?branch=master)](https://travis-ci.org/tophattom/clarinet)
2
+ ===
3
+
4
+ Ruby client for [Clarifai](https://clarifai.com) API v2.
5
+
6
+ Clarinet matches Clarifai's official JavaScript SDK almost 1:1.
7
+ Method names are mostly the same expect in `snake_case` as opposed
8
+ to `camelCase` in the JS library.
9
+
10
+ ## Dependencies
11
+
12
+ * `addressable`
13
+ * `httparty`
14
+
15
+ ## Install
16
+
17
+ ## Usage
18
+
19
+ ```ruby
20
+ # Your client ID and secret
21
+ api_key = '...'
22
+
23
+ # Initialize
24
+ client = Clarinet::App.new api_key
25
+
26
+ # Get predictions for image with URL.
27
+ # Response keys are symbolized.
28
+ outputs = client.models.predict(Clarinet::Model::GENERAL, 'https://samples.clarifai.com/metro-north.jpg')
29
+ ```
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler'
4
+
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ task default: :spec
8
+
9
+ require 'rspec/core/rake_task'
10
+ RSpec::Core::RakeTask.new do |t|
11
+ t.rspec_opts = ["--color", '--format doc']
12
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/clarinet/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'clarinet'
7
+ s.version = Clarinet::VERSION
8
+ s.summary = 'Clarifai API client'
9
+ s.description = 'Simple client to interface with the Clarifai API v2'
10
+ s.authors = ['Jaakko Rinta-Filppula']
11
+ s.email = 'jaakko.rf@gmail.com'
12
+ s.homepage = 'https://github.com/tophattom/clarinet'
13
+ s.license = 'ISC'
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+
17
+ s.add_dependency 'httparty', ['~> 0.14']
18
+ s.add_dependency 'addressable', ['~> 2.5']
19
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'clarinet/status'
4
+ require 'clarinet/error/error'
5
+ require 'clarinet/utils'
6
+
7
+ require 'clarinet/client'
8
+
9
+ require 'clarinet/concept'
10
+ require 'clarinet/concepts'
11
+ require 'clarinet/input'
12
+ require 'clarinet/inputs'
13
+
14
+ require 'clarinet/model'
15
+ require 'clarinet/models'
16
+
17
+ require 'clarinet/app'
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Clarinet
4
+ class App
5
+
6
+ # @!visibility private
7
+ attr_reader :client
8
+
9
+ # @return [Clarinet::Concepts]
10
+ attr_reader :concepts
11
+
12
+ # @return [Clarinet::Inputs]
13
+ attr_reader :inputs
14
+
15
+ # @return [Clarinet::Models]
16
+ attr_reader :models
17
+
18
+ # @param api_key [String] Clarifai API key
19
+ def initialize(api_key)
20
+ @client = Clarinet::Client.new api_key
21
+
22
+ @concepts = Clarinet::Concepts.new self
23
+ @inputs = Clarinet::Inputs.new self
24
+ @models = Clarinet::Models.new self
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'httparty'
4
+ require 'json'
5
+
6
+ module Clarinet
7
+ # @!visibility private
8
+ class Client
9
+ include HTTParty
10
+
11
+ base_uri 'https://api.clarifai.com/v2'
12
+ format :plain
13
+ headers 'Content-Type' => 'application/json'
14
+
15
+ def initialize(api_key)
16
+ @auth_headers = {
17
+ 'Authorization' => "Key #{api_key}"
18
+ }
19
+ end
20
+
21
+ def models(options = {})
22
+ with_response_parsing do
23
+ self.class.get '/models', headers: @auth_headers, query: options
24
+ end
25
+ end
26
+
27
+ def model(id)
28
+ with_response_parsing do
29
+ self.class.get "/models/#{id}", headers: @auth_headers
30
+ end
31
+ end
32
+
33
+ def model_output_info(id)
34
+ with_response_parsing do
35
+ self.class.get "/models/#{id}/output_info", headers: @auth_headers
36
+ end
37
+ end
38
+
39
+ def model_train(id)
40
+ with_response_parsing do
41
+ self.class.post "/models/#{id}/versions", header: @auth_headers
42
+ end
43
+ end
44
+
45
+ def model_versions(id, options = {})
46
+ with_response_parsing do
47
+ self.class.get "/models/#{id}/versions", headers: @auth_headers, query: options
48
+ end
49
+ end
50
+
51
+ def models_search(query)
52
+ body = { model_query: query }
53
+
54
+ with_response_parsing do
55
+ self.class.post '/models/searches', headers: @auth_headers, body: body.to_json
56
+ end
57
+ end
58
+
59
+ def models_update(data)
60
+ with_response_parsing do
61
+ self.class.patch '/models', headers: @auth_headers, body: data.to_json
62
+ end
63
+ end
64
+
65
+ def outputs(model_id, inputs, config = {})
66
+ body = { inputs: inputs }
67
+ body[:model] = { output_info: { output_config: config } } unless config.empty?
68
+
69
+ with_response_parsing do
70
+ self.class.post "/models/#{model_id}/outputs", headers: @auth_headers, body: body.to_json
71
+ end
72
+ end
73
+
74
+ def concepts(options = {})
75
+ with_response_parsing do
76
+ self.class.get '/concepts', headers: @auth_headers, query: options
77
+ end
78
+ end
79
+
80
+ def concept(id)
81
+ with_response_parsing do
82
+ self.class.get "/concepts/#{id}", headers: @auth_headers
83
+ end
84
+ end
85
+
86
+ def concepts_search(query)
87
+ body = { concept_query: query }
88
+
89
+ with_response_parsing do
90
+ self.class.post '/concepts/searches', headers: @auth_headers, body: body.to_json
91
+ end
92
+ end
93
+
94
+ def inputs_create(concepts)
95
+ body = { concepts: concepts }
96
+
97
+ with_response_parsing do
98
+ self.class.post '/concepts', headers: @auth_headers, body: body.to_json
99
+ end
100
+ end
101
+
102
+ def inputs(options = {})
103
+ with_response_parsing do
104
+ self.class.get '/inputs', headers: @auth_headers, query: options
105
+ end
106
+ end
107
+
108
+ def inputs_create(inputs)
109
+ body = { inputs: inputs }
110
+
111
+ with_response_parsing do
112
+ self.class.post '/inputs', headers: @auth_headers, body: body.to_json
113
+ end
114
+ end
115
+
116
+ def input_delete(id)
117
+ with_response_parsing do
118
+ self.class.delete "/inputs/#{id}", headers: @auth_headers
119
+ end
120
+ end
121
+
122
+ def inputs_delete(ids)
123
+ body = { ids: ids }
124
+
125
+ with_response_parsing do
126
+ self.class.delete '/inputs', headers: @auth_headers, body: body.to_json
127
+ end
128
+ end
129
+
130
+ def inputs_delete_all
131
+ body = { delete_all: true }
132
+
133
+ with_response_parsing do
134
+ self.class.delete '/inputs', headers: @auth_headers, body: body.to_json
135
+ end
136
+ end
137
+
138
+ def inputs_status
139
+ with_response_parsing do
140
+ self.class.get '/inputs/status', headers: @auth_headers
141
+ end
142
+ end
143
+
144
+ def inputs_update(data)
145
+ with_response_parsing do
146
+ self.class.patch '/inputs', headers: @auth_headers, body: data.to_json
147
+ end
148
+ end
149
+
150
+
151
+ private
152
+
153
+ def with_response_parsing(&block)
154
+ response = yield
155
+ data = JSON.parse response.parsed_response, symbolize_names: true
156
+ Clarinet::Utils.check_response_status data[:status]
157
+ data
158
+ end
159
+
160
+ end
161
+ end
@@ -0,0 +1,22 @@
1
+ module Clarinet
2
+ class Concept
3
+
4
+ attr_reader :id
5
+ attr_reader :name
6
+ attr_reader :app_id
7
+ attr_reader :value
8
+ attr_reader :created_at
9
+ attr_reader :raw_data
10
+
11
+ def initialize(raw_data = {})
12
+ @raw_data = raw_data
13
+
14
+ @id = raw_data[:id]
15
+ @name = raw_data[:name]
16
+ @created_at = raw_data[:created_at]
17
+ @app_id = raw_data[:app_id]
18
+ @value = raw_data[:value]
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Clarinet
4
+ class Concepts
5
+ extend Forwardable
6
+
7
+ delegate [:[], :each, :map, :find, :select, :reject] => :@concepts
8
+
9
+ def initialize(app, raw_data = [])
10
+ @app = app
11
+ @raw_data = raw_data
12
+
13
+ @concepts = raw_data.map do |concept_data|
14
+ Clarinet::Concept.new concept_data
15
+ end
16
+ end
17
+
18
+ def create(concepts)
19
+ concepts = [concepts] unless concepts.is_a? Array
20
+ concepts = concepts.map { |concept| format_concept(concept) }
21
+
22
+ data = @app.client.concepts.create concepts
23
+ Clarinet::Concepts.new @app, data[:concepts]
24
+ end
25
+
26
+ def list(options = { page: 1, per_page: 20 })
27
+ data = @app.client.concepts options
28
+ Clarinet::Concepts.new @app, data[:concepts]
29
+ end
30
+
31
+ def get(id)
32
+ data = @app.client.concept id
33
+ Clarinet::Concept.new data[:concept]
34
+ end
35
+
36
+ def search(name, language = nil)
37
+ query = {
38
+ name: name,
39
+ language: language
40
+ }
41
+
42
+ data = @app.client.concepts_search query
43
+ Clarinet::Concepts.new @app, data[:concepts]
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,29 @@
1
+ module Clarinet
2
+ module Error
3
+ class ApiError < StandardError
4
+ attr_accessor :code
5
+ attr_accessor :description
6
+ attr_accessor :details
7
+ end
8
+
9
+ class InvalidAuthTokenError < ApiError
10
+
11
+ end
12
+
13
+ class ApiKeyNotFoundError < ApiError
14
+
15
+ end
16
+
17
+ class BadRequestFormatError < ApiError
18
+
19
+ end
20
+
21
+ class InvalidRequestError < ApiError
22
+
23
+ end
24
+
25
+ class ImageDecodingError < ApiError
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Clarinet
4
+ class Input
5
+
6
+ attr_reader :id
7
+ attr_reader :created_at
8
+ attr_reader :image_url
9
+ attr_reader :concepts
10
+ attr_reader :score
11
+ attr_reader :metadata
12
+ attr_reader :raw_data
13
+
14
+ def initialize(app, raw_data)
15
+ @app = app
16
+
17
+ @id = raw_data[:id]
18
+ @created_at = raw_data[:created_at]
19
+ @image_url = raw_data[:data][:image_url]
20
+
21
+ @concepts = Clarinet::Concepts.new app, raw_data[:data][:concepts]
22
+
23
+ @score = raw_data[:score]
24
+ @metadata = raw_data[:data][:metadata]
25
+
26
+ @raw_data = raw_data
27
+ end
28
+
29
+ def merge_concepts(concepts, metadata = nil)
30
+ update 'merge', concepts: concepts, metadata: metadata
31
+ end
32
+
33
+ def delete_concepts(concepts, metadata = nil)
34
+ update 'remove', concepts: concepts, metadata: metadata
35
+ end
36
+
37
+ def overwrite_concepts(concepts, metadata = nil)
38
+ update 'overwrite', concepts: concepts, metadata: metadata
39
+ end
40
+
41
+ private
42
+
43
+ def update(action, concepts: [], metadata: nil)
44
+ input_data = {}
45
+ input_data[:concepts] = concepts unless concepts.empty?
46
+ input_data[:metadata] = metadata unless metadata.nil?
47
+
48
+ data = {
49
+ action: action,
50
+ inputs: [
51
+ {
52
+ id: id,
53
+ data: input_data
54
+ }
55
+ ]
56
+ }
57
+
58
+ response_data = @app.client.inputs_update data
59
+ Clarinet::Input.new response_data[:input]
60
+ end
61
+
62
+ end
63
+ end