inference_activity 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 930b5b9c1533a9da594668af59131f20b9d670b621e6e1da59e9bbb1cab08617
4
+ data.tar.gz: 47f37d2e37c9bdf70d1cb5b906b0e6f143bca9be4fed5f8dc6169fc43e64c6e7
5
+ SHA512:
6
+ metadata.gz: 27ea85c2efcc589f767b4c23bb14038f52c1809b96e0488ebef08e5ad780c6d9a47fbcf394ba6f20f8ab28d1cc0f9a1dc411dcb34ba237dd0c9a28f22b3c6df0
7
+ data.tar.gz: 9763eca818ca551b36b9d94706d36ef7864d4c16ecaea00f85b1a503a758fc33ee28359a5dcd14e03fb7178f242dc92760d11c2f2cc7737e87ccbd5de69418c9
data/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # Inference Activity for Ruby
2
+
3
+ A Ruby gem that provides Net::HTTP extensions for tracking inference activities on Heroku AI. This gem is the Ruby equivalent of `inference-activity-axios`.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'inference_activity'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ bundle install
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```bash
22
+ gem install inference_activity
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ The gem automatically extends Net::HTTP to track inference activities. Simply require it in your code:
28
+
29
+ ```ruby
30
+ require 'inference_activity'
31
+ require 'net/http'
32
+ require 'json'
33
+ require 'uri'
34
+
35
+ # Set up your environment variables
36
+ ENV['INFERENCE_ACTIVITY_URL'] = 'your-activity-logging-endpoint'
37
+ ENV['INFERENCE_ACTIVITY_KEY'] = 'your-api-key'
38
+
39
+ # Make requests as usual with Net::HTTP
40
+ uri = URI('https://api.heroku.ai/v1/chat/completions')
41
+ request = Net::HTTP::Post.new(uri)
42
+ request['Authorization'] = "Bearer #{ENV['API_KEY']}"
43
+ request['Content-Type'] = 'application/json'
44
+ request.body = {
45
+ messages: [
46
+ { role: 'user', content: 'Hello!' }
47
+ ],
48
+ model: 'gpt-3.5-turbo'
49
+ }.to_json
50
+
51
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
52
+ http.request(request)
53
+ end
54
+ ```
55
+
56
+ The gem will automatically:
57
+ - Track the request/response
58
+ - Measure response time
59
+ - Redact sensitive information
60
+ - Send activity logs to your specified endpoint
61
+
62
+ ## Features
63
+
64
+ - Automatically tracks inference activities for Heroku AI endpoints
65
+ - Redacts sensitive information from requests and responses
66
+ - Tracks response times and logs activity data
67
+ - Handles the following endpoints:
68
+ - `/v1/chat/completions`
69
+ - `/v1/embeddings`
70
+ - `/v1/images/generations`
71
+
72
+ ## Environment Variables
73
+
74
+ - `INFERENCE_ACTIVITY_URL`: The endpoint where activity logs will be sent
75
+ - `INFERENCE_ACTIVITY_KEY`: The API key for authentication when sending activity logs
76
+
77
+ ## Activity Data Format
78
+
79
+ The gem sends the following data structure to your activity logging endpoint:
80
+
81
+ ```ruby
82
+ {
83
+ timestamp: 1234567890123, # Unix timestamp in milliseconds
84
+ response_time: 500, # Response time in milliseconds
85
+ status_code: 200, # HTTP status code
86
+ status_message: "OK", # HTTP status message
87
+ request: {
88
+ method: "POST",
89
+ url: "https://api.heroku.ai/v1/chat/completions",
90
+ params: {},
91
+ body: {
92
+ messages: "[REDACTED]",
93
+ model: "gpt-3.5-turbo"
94
+ }
95
+ },
96
+ response: {
97
+ headers: {
98
+ "content-type" => "application/json",
99
+ # ...
100
+ },
101
+ data: {
102
+ choices: [{
103
+ message: {
104
+ content: "[REDACTED]",
105
+ role: "assistant"
106
+ }
107
+ }],
108
+ # ...
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ ## Differences from inference-activity-axios
115
+
116
+ This gem provides the same functionality as `inference-activity-axios` but is adapted for Ruby's Net::HTTP:
117
+
118
+ 1. Uses Ruby's Net::HTTP extension system instead of Axios interceptors
119
+ 2. Automatically applies to all Net::HTTP requests in the application
120
+ 3. Uses Ruby's Time class for timestamps
121
+ 4. Maintains consistent millisecond-precision timestamps for compatibility
122
+
123
+ ## Development
124
+
125
+ After checking out the repo, run `bundle install` to install dependencies. Then, run `rake spec` to run the tests.
126
+
127
+ ## Contributing
128
+
129
+ Bug reports and pull requests are welcome on GitHub at https://github.com/add-co/inference-activity-ruby.
130
+
131
+ ## License
132
+
133
+ The gem is available as open source under the terms of the MIT License.
@@ -0,0 +1,3 @@
1
+ module InferenceActivity
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,150 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ require 'uri'
4
+ require 'time'
5
+
6
+ module InferenceActivity
7
+ class << self
8
+ def wrap_request(request, &block)
9
+ start_time = Time.now
10
+
11
+ # Execute the original request
12
+ response = yield
13
+
14
+ # Track activity if environment variables are set
15
+ track_activity(request, response, start_time) if tracking_enabled?
16
+
17
+ response
18
+ end
19
+
20
+ private
21
+
22
+ def tracking_enabled?
23
+ !ENV['INFERENCE_ACTIVITY_URL'].nil? && !ENV['INFERENCE_ACTIVITY_KEY'].nil?
24
+ end
25
+
26
+ def track_activity(request, response, start_time)
27
+ duration = ((Time.now - start_time) * 1000).to_i # Convert to milliseconds
28
+
29
+ activity_data = {
30
+ timestamp: (Time.now.to_f * 1000).to_i, # Unix timestamp in milliseconds
31
+ response_time: duration,
32
+ status_code: response.code.to_i,
33
+ status_message: response.message,
34
+ request: redact_request(request),
35
+ response: redact_response(response)
36
+ }
37
+
38
+ send_activity_data(activity_data)
39
+ rescue => e
40
+ warn "Failed to send inference activity: #{e.message}"
41
+ end
42
+
43
+ def redact_request(request)
44
+ uri = URI(request.uri)
45
+ body = parse_request_body(request)
46
+
47
+ if body.is_a?(Hash)
48
+ if uri.path.end_with?('/v1/chat/completions') && body['messages']
49
+ body = body.merge('messages' => '[REDACTED]')
50
+ end
51
+
52
+ if uri.path.end_with?('/v1/embeddings') && body['input']
53
+ body = body.merge('input' => '[REDACTED]')
54
+ end
55
+
56
+ if uri.path.end_with?('/v1/images/generations')
57
+ body = body.merge('prompt' => '[REDACTED]') if body['prompt']
58
+ body = body.merge('negative_prompt' => '[REDACTED]') if body['negative_prompt']
59
+ end
60
+ end
61
+
62
+ {
63
+ method: request.method,
64
+ url: request.uri.to_s,
65
+ params: URI.decode_www_form(uri.query || '').to_h,
66
+ body: body
67
+ }
68
+ end
69
+
70
+ def redact_response(response)
71
+ data = parse_response_body(response)
72
+ uri = URI(response.uri)
73
+
74
+ if data.is_a?(Hash)
75
+ if uri.path.end_with?('/v1/chat/completions') && data['choices']
76
+ data['choices'] = data['choices'].map do |choice|
77
+ if choice['message']
78
+ choice.merge('message' => choice['message'].merge('content' => '[REDACTED]'))
79
+ else
80
+ choice
81
+ end
82
+ end
83
+ end
84
+
85
+ if uri.path.end_with?('/v1/embeddings') && data['data']
86
+ data['data'] = data['data'].map do |item|
87
+ item.merge('embedding' => '[REDACTED]')
88
+ end
89
+ end
90
+
91
+ if uri.path.end_with?('/v1/images/generations') && data['data']
92
+ data['data'] = data['data'].map do |item|
93
+ redacted = item.dup
94
+ redacted['b64_json'] = '[REDACTED]' if item.key?('b64_json')
95
+ redacted['revised_prompt'] = '[REDACTED]' if item.key?('revised_prompt')
96
+ redacted
97
+ end
98
+ end
99
+ end
100
+
101
+ {
102
+ headers: response.each_header.to_h,
103
+ data: data
104
+ }
105
+ end
106
+
107
+ def parse_request_body(request)
108
+ return {} unless request.body
109
+
110
+ JSON.parse(request.body)
111
+ rescue JSON::ParserError
112
+ request.body
113
+ end
114
+
115
+ def parse_response_body(response)
116
+ JSON.parse(response.body)
117
+ rescue JSON::ParserError
118
+ response.body
119
+ end
120
+
121
+ def send_activity_data(activity_data)
122
+ uri = URI(ENV['INFERENCE_ACTIVITY_URL'])
123
+ request = Net::HTTP::Post.new(uri)
124
+ request['Authorization'] = "Bearer #{ENV['INFERENCE_ACTIVITY_KEY']}"
125
+ request['Content-Type'] = 'application/json'
126
+ request.body = activity_data.to_json
127
+
128
+ Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
129
+ http.request(request)
130
+ end
131
+ end
132
+ end
133
+
134
+ module RequestExtensions
135
+ def self.included(base)
136
+ base.class_eval do
137
+ alias_method :original_exec, :exec
138
+
139
+ def exec(sock, ver, path)
140
+ InferenceActivity.wrap_request(self) do
141
+ original_exec(sock, ver, path)
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ # Extend Net::HTTP::Request with our tracking functionality
150
+ Net::HTTPRequest.include(InferenceActivity::RequestExtensions)
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: inference_activity
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Add Co
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-03-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '13.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '13.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ description: Provides request/response tracking and activity logging for Heroku AI
42
+ endpoints with sensitive data redaction
43
+ email:
44
+ - dev@add.co
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - README.md
50
+ - lib/inference_activity.rb
51
+ - lib/inference_activity/version.rb
52
+ homepage: https://github.com/add-co/inference-activity-ruby
53
+ licenses:
54
+ - MIT
55
+ metadata: {}
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 2.7.0
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubygems_version: 3.3.15
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: Net::HTTP extensions for tracking inference activities on Heroku AI
75
+ test_files: []