tangram 0.1.0.pre.alpha.7 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c4cbfd5a24c6561fe11c81ae785d47bc4f4e16a0b11c10f079c63d863c6cb15
4
- data.tar.gz: 29d6160096b0ca3d692781d07f4dffb971ab9563c9a6bd1c7911a1676e161806
3
+ metadata.gz: e622eeb9d9cd659b75acbf6dc167a0685a5111ab35a434c888c497de99faf742
4
+ data.tar.gz: 91c21be6670a9e3d3072648dba57b570a4cc313958af8a5a16ee7588f461c9b9
5
5
  SHA512:
6
- metadata.gz: d8e03fe9d77eedf28dc4ab6b6cbf17f6c5274479b1a0324f7071d5e5f608a0fc17bb312b46d995c6873b8b7ef8fac81f8b02490ca06038e936c61a07ac19f38b
7
- data.tar.gz: 8c47ffbf42a08c613e3e6bcfdcefc396d5db0d2288a85b52f3bf766f30f9a0bff685d04c282a837b693e2c959e4037713f655a3e07a01e4fe1bc04077ee997df
6
+ metadata.gz: 5079553215b584e04826f4e522e5b47587711e7493c389f587ec2202acc33a72a9f769d1155e11f746bbb69320a4d2a3bee587701b33877a558ddf467d7d967c
7
+ data.tar.gz: 0d86be9c1a11e988cbcd6549ae9eb55c0cde9126ab85f1f99f23f796100c158ee32401fe92e0a63b28bacee114cf3a15f2cf0af6b265bf3dbd03717ed44bd86d
data/README.md CHANGED
@@ -1 +1,52 @@
1
- # tangram-ruby
1
+ # Tangram + Ruby
2
+
3
+ - [Watch the Video](https://www.tangramhq.com)
4
+ - [Follow the Getting Started guide](https://www.tangramhq.com/docs/getting-started)
5
+
6
+ The Tangram Ruby library makes it easy to make predictions with your Tangram machine learning model from Ruby.
7
+
8
+ ## Usage
9
+
10
+ ```
11
+ $ gem install tangram
12
+ ```
13
+
14
+ ```ruby
15
+ require 'tangram'
16
+
17
+ model = Tangram::Model.from_file('./heart-disease.tangram')
18
+
19
+ input = {
20
+ age: 63,
21
+ gender: 'male',
22
+ # ...
23
+ }
24
+
25
+ output = model.predict(input)
26
+
27
+ puts(output)
28
+ ```
29
+
30
+ For more information, follow the [Getting Started guide](https://www.tangramhq.com/docs/getting-started).
31
+
32
+ ## Examples
33
+
34
+ This repo contains two examples, [examples/predict]([examples/predict]) and [examples/monitor](examples/monitor).
35
+
36
+ ### Predict
37
+
38
+ This example demonstrates loading a model from a `.tangram` file and making a prediction.
39
+
40
+ ### Monitor
41
+
42
+ This example demonstrates logging predictions and true values to the Tangram reporting and monitoring app. Before running the example, upload the `heart-disease.tangram` file in [examples/monitor/heart-disease.tangram](examples/monitor/heart-disease.tangram) to either https://app.tangramhq.com or your private instance. Then generate a token by clicking the "Tokens" link on the right side of the top bar.
43
+
44
+ Then you can run the example:
45
+
46
+ ```
47
+ $ TANGRAM_TOKEN=<YOUR TOKEN HERE> ruby examples/monitor/main.rb
48
+ ```
49
+
50
+ Now if you refresh the production stats or production metrics tabs for the model you uploaded, you should see predictions and true values being reported.
51
+
52
+ For more information on reporting and monitoring, follow the [Getting Started guide](https://www.tangramhq.com/docs/getting-started).
@@ -0,0 +1,52 @@
1
+ require 'tangram'
2
+
3
+ # Go to https://app.tangramhq.com or your private instance, upload the `heart-disease.tangram` file adjacent to this file, generate a token, and pass it as an environment variable to this script.
4
+ token = ENV['TANGRAM_TOKEN']
5
+ if token.nil?
6
+ raise 'TANGRAM_TOKEN environment variable not set'
7
+ end
8
+ base_url = ENV['TANGRAM_BASE_URL'] || 'https://app.tangramhq.com'
9
+
10
+ # Get the path to the `.tangram` file.
11
+ model_path = File.join(File.dirname(__FILE__), 'heart-disease.tangram')
12
+ # Load the model from the file and set the Tangram API token and base url.
13
+ model = Tangram::Model.from_file(model_path, token: token, base_url: base_url)
14
+
15
+ # Create an example input matching the schema of the CSV file the model was trained on. Here the data is just hard-coded, but in your application you will probably get this from a database or user input.
16
+ input = {
17
+ age: 63,
18
+ gender: 'male',
19
+ chest_pain: 'typical angina',
20
+ resting_blood_pressure: 145,
21
+ cholesterol: 233,
22
+ fasting_blood_sugar_greater_than_120: 'true',
23
+ resting_ecg_result: 'probable or definite left ventricular hypertrophy',
24
+ exercise_max_heart_rate: 150,
25
+ exercise_induced_angina: 'no',
26
+ exercise_st_depression: 2.3,
27
+ exercise_st_slope: 'downsloping',
28
+ fluoroscopy_vessels_colored: 0,
29
+ thallium_stress_test: 'fixed defect',
30
+ }
31
+
32
+ # Make the prediction using a custom threshold chosen on the "Tuning" page of the Tangram reporting and monitoring app.
33
+ options = { threshold: 0.25 }
34
+ output = model.predict(input, options: options)
35
+
36
+ # Make the prediction using a custom threshold chosen on the "Tuning" page of the Tangram reporting and monitoring app.
37
+ puts('Input:', input)
38
+ puts('Output:', output)
39
+
40
+ # Log the prediction. This will allow us to view production stats in the Tangram reporting and monitoring app.
41
+ model.log_prediction(
42
+ identifier: 'John Doe',
43
+ options: options,
44
+ input: input,
45
+ output: output,
46
+ )
47
+
48
+ # Later on, if we get an official diagnosis for the patient, we can log the true value for the prior prediction. Make sure to match the `identifier` from the former prediction.
49
+ model.log_true_value(
50
+ identifier: 'John Doe',
51
+ true_value: 'Positive',
52
+ )
@@ -0,0 +1,30 @@
1
+ require 'tangram'
2
+
3
+ # Get the path to the .tangram file.
4
+ model_path = File.join(File.dirname(__FILE__), 'heart-disease.tangram')
5
+ # Load the model from the file.
6
+ model = Tangram::Model.from_file(model_path)
7
+
8
+ # Create an example input matching the schema of the CSV file the model was trained on. Here the data is just hard-coded, but in your application you will probably get this from a database or user input.
9
+ input = {
10
+ age: 63,
11
+ gender: 'male',
12
+ chest_pain: 'typical angina',
13
+ resting_blood_pressure: 145,
14
+ cholesterol: 233,
15
+ fasting_blood_sugar_greater_than_120: 'true',
16
+ resting_ecg_result: 'probable or definite left ventricular hypertrophy',
17
+ exercise_max_heart_rate: 150,
18
+ exercise_induced_angina: 'no',
19
+ exercise_st_depression: 2.3,
20
+ exercise_st_slope: 'downsloping',
21
+ fluoroscopy_vessels_colored: 0,
22
+ thallium_stress_test: 'fixed defect',
23
+ }
24
+
25
+ # Make the prediction!
26
+ output = model.predict(input)
27
+
28
+ # Print out the input and output.
29
+ puts('Input:', input)
30
+ puts('Output:', output)
@@ -5,13 +5,13 @@ module LibTangram
5
5
  cpu = RbConfig::CONFIG['host_cpu']
6
6
  os = RbConfig::CONFIG['host_os']
7
7
  if cpu == 'x86_64' and os =~ /linux/
8
- library_name = 'libtangram-0.1.0-alpha.4-linux-x86_64.so'
8
+ library_name = 'libtangram-0.1.0-linux-x86_64.so'
9
9
  elsif cpu == 'x86_64' and os =~ /darwin/
10
- library_name = 'libtangram-0.1.0-alpha.4-macos-x86_64.dylib'
10
+ library_name = 'libtangram-0.1.0-macos-x86_64.dylib'
11
11
  elsif cpu == 'x86_64' and os =~ /mingw/
12
- library_name = 'tangram-0.1.0-alpha.4-windows-x86_64.dll'
12
+ library_name = 'tangram-0.1.0-windows-x86_64.dll'
13
13
  else
14
- raise 'tangram-ruby does not yet support your combination of operating system and CPU architecture.'
14
+ raise 'tangram-ruby does not yet support your combination of operating system and CPU architecture. Want support for your platform? Get in touch at help@tangramhq.com.'
15
15
  end
16
16
  extend FFI::Library
17
17
  ffi_lib File.expand_path("#{library_name}", __dir__)
data/lib/tangram/model.rb CHANGED
@@ -1,18 +1,23 @@
1
- require 'tangram/libtangram'
2
1
  require 'json'
2
+ require 'net/http'
3
+ require 'tangram/libtangram'
3
4
 
4
5
  module Tangram
5
6
  class Model
6
- def self.from_file(model_path)
7
+
8
+ def self.from_file(model_path, base_url: nil, token: nil)
7
9
  model_data = IO.binread(model_path)
8
- self.from_data(model_data)
10
+ self.from_data(model_data, base_url: base_url, token: token)
9
11
  end
10
12
 
11
- def self.from_data(model_data)
12
- self.new(model_data)
13
+ def self.from_data(model_data, base_url: nil, token: nil)
14
+ self.new(model_data, base_url: base_url, token: token)
13
15
  end
14
16
 
15
- def initialize(model_data)
17
+ def initialize(model_data, base_url: nil, token: nil)
18
+ @base_url = base_url.nil? ? 'https://app.tangramhq.com' : base_url
19
+ @token = token
20
+ @log_queue = []
16
21
  model_ptr = FFI::MemoryPointer.new(:pointer)
17
22
  result = LibTangram.tangram_model_load(model_data, model_data.size, model_ptr)
18
23
  if result != 0
@@ -21,7 +26,18 @@ module Tangram
21
26
  @model = FFI::AutoPointer.new(model_ptr.read_pointer, LibTangram.method(:tangram_model_free))
22
27
  end
23
28
 
24
- def task
29
+ def id()
30
+ id_ptr = FFI::MemoryPointer.new(:pointer)
31
+ result = LibTangram.tangram_model_id(@model, id_ptr)
32
+ if result != 0
33
+ raise 'tangram error'
34
+ end
35
+ id = id_ptr.read_pointer.read_string.force_encoding('utf-8')
36
+ LibTangram.tangram_string_free(id_ptr.read_pointer)
37
+ id
38
+ end
39
+
40
+ def task()
25
41
  task_ptr = FFI::MemoryPointer.new(:pointer)
26
42
  result = LibTangram.tangram_model_task(@model, task_ptr)
27
43
  if result != 0
@@ -32,7 +48,7 @@ module Tangram
32
48
  task
33
49
  end
34
50
 
35
- def predict(input, options)
51
+ def predict(input, options: nil)
36
52
  input = JSON.unparse(input)
37
53
  unless options.nil?
38
54
  options = JSON.unparse(options)
@@ -49,5 +65,73 @@ module Tangram
49
65
  LibTangram.tangram_string_free(output_ptr.read_pointer)
50
66
  output
51
67
  end
68
+
69
+ def log_prediction(identifier:, options: nil, input:, output:)
70
+ self.log_event({
71
+ type: 'prediction',
72
+ modelId: self.id,
73
+ identifier: identifier,
74
+ options: options,
75
+ input: input,
76
+ output: output,
77
+ })
78
+ end
79
+
80
+ def enqueue_log_prediction(identifier:, options: nil, input:, output:)
81
+ self.log_queue.push({
82
+ type: 'prediction',
83
+ modelId: self.id,
84
+ identifier: identifier,
85
+ options: options,
86
+ input: input,
87
+ output: output,
88
+ })
89
+ end
90
+
91
+ def log_true_value(identifier:, true_value:)
92
+ self.log_event({
93
+ type: 'true_value',
94
+ modelId: self.id,
95
+ identifier: identifier,
96
+ trueValue: true_value,
97
+ })
98
+ end
99
+
100
+ def enqueue_log_true_value(identifier:, true_value:)
101
+ self.log_queue.push({
102
+ type: 'true_value',
103
+ modelId: self.id,
104
+ identifier: identifier,
105
+ trueValue: true_value,
106
+ })
107
+ end
108
+
109
+ def flush_log_queue()
110
+ self.log_events(@log_queue)
111
+ @log_queue = []
112
+ end
113
+
114
+ def log_event(event)
115
+ self.log_events([event])
116
+ end
117
+
118
+ def log_events(events)
119
+ if @token.nil?
120
+ raise 'Tangram cannot log events without a token. Make sure to pass a token to the model constructor.'
121
+ end
122
+ headers = {
123
+ 'Content-Type': 'application/json',
124
+ 'Authorization': 'Bearer ' + @token,
125
+ }
126
+ uri = URI(@base_url + '/api/track')
127
+ http = Net::HTTP.new(uri.host, uri.port)
128
+ request = Net::HTTP::Post.new(uri.request_uri, headers)
129
+ request.body = events.to_json
130
+ response = http.request(request)
131
+ unless response.kind_of? Net::HTTPSuccess
132
+ raise response
133
+ end
134
+ end
135
+
52
136
  end
53
137
  end
data/tangram.gemspec CHANGED
@@ -1,7 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "tangram"
3
- s.version = "0.1.0-alpha.7"
4
- s.date = "2019-11-22"
3
+ s.version = "0.1.1"
5
4
  s.summary = "Tangram for Ruby"
6
5
  s.description = "Make predictions with a Tangram model from your Ruby app. Learn more at https://www.tangramhq.com/."
7
6
  s.authors = ["Tangram"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tangram
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.pre.alpha.7
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tangram
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-22 00:00:00.000000000 Z
11
+ date: 2020-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -32,13 +32,16 @@ extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
34
  - README.md
35
- - examples/heart-disease/main.rb
35
+ - examples/monitor/heart-disease.tangram
36
+ - examples/monitor/main.rb
37
+ - examples/predict/heart-disease.tangram
38
+ - examples/predict/main.rb
36
39
  - lib/tangram.rb
37
- - lib/tangram/libtangram-0.1.0-alpha.4-linux-x86_64.so
38
- - lib/tangram/libtangram-0.1.0-alpha.4-macos-x86_64.dylib
40
+ - lib/tangram/libtangram-0.1.0-linux-x86_64.so
41
+ - lib/tangram/libtangram-0.1.0-macos-x86_64.dylib
39
42
  - lib/tangram/libtangram.rb
40
43
  - lib/tangram/model.rb
41
- - lib/tangram/tangram-0.1.0-alpha.4-windows-x86_64.dll
44
+ - lib/tangram/tangram-0.1.0-windows-x86_64.dll
42
45
  - scripts/build
43
46
  - tangram.gemspec
44
47
  homepage: http://rubygems.org/gems/tangram
@@ -56,9 +59,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
56
59
  version: '0'
57
60
  required_rubygems_version: !ruby/object:Gem::Requirement
58
61
  requirements:
59
- - - ">"
62
+ - - ">="
60
63
  - !ruby/object:Gem::Version
61
- version: 1.3.1
64
+ version: '0'
62
65
  requirements: []
63
66
  rubygems_version: 3.0.6
64
67
  signing_key:
@@ -1,25 +0,0 @@
1
- require 'tangram'
2
-
3
- model = Tangram::Model.from_file('./heart-disease.tangram')
4
-
5
- puts model.task
6
-
7
- input = {
8
- age: 63,
9
- gender: 'male',
10
- chest_pain: 'typical angina',
11
- resting_blood_pressure: 145,
12
- cholesterol: 233,
13
- fasting_blood_sugar_greater_than_120: 'true',
14
- resting_ecg_result: 'probable or definite left ventricular hypertrophy',
15
- exercise_max_heart_rate: 150,
16
- exercise_induced_angina: 'no',
17
- exercise_st_depression: 2.3,
18
- exercise_st_slope: 'downsloping',
19
- fluoroscopy_vessels_colored: 0,
20
- thallium_stress_test: 'fixed defect',
21
- }
22
-
23
- output = model.predict(input)
24
-
25
- puts('Prediction:', output['className'])