active_model-validations-ai 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1aa5446b42e458800e2e8a8dcc6c867b4c2cfef578dd51a6e846701a12864b1d
4
- data.tar.gz: 00eae2e2a14357ca82b77590ca69d1486ad1a8e31abee028ebcb4961cd7b7dfa
3
+ metadata.gz: d44eee74a5bd787b4ef3fa979b836d38ab849934a6c475c511a05d21c3d2568b
4
+ data.tar.gz: da9537a5b8c063d64377f8a9fe6f774903a413833b5201c95e03990e6c762a3e
5
5
  SHA512:
6
- metadata.gz: 1362deb8b5d7481610d53776755b53ef353848329250b7c050e9caf0701bb89a1ed9e0262985a9d45c83eab847e87da4d59cfe02e207c72056307b4636e57069
7
- data.tar.gz: 0f22e4bb56974895367cd70687b52801712b86b46be7abbf256eb4e41f02a8da132e164b602ccf35740a9d23f014e92e7782e12ac274d4b3853da7312f167b0b
6
+ metadata.gz: 85afbbd6f52c16ed8d61eca71acfaa3f1aa02e3587126e7c1245304f3718e9f4b99572c7c233da1a8d1c3bf9210be61820c5f561b839e5a16d7f820c62a615d5
7
+ data.tar.gz: b44e93ca478d587b154377bb93ded494c2ab787e681e119b6065eed2e1980324581c388a729c00ec14c45af2c0dba1faa58522e8855198fd82eb54c412c2a969
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2023 Kasper Timm Hansen
3
+ Copyright (c) 2023 Bullet Train, Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -24,17 +24,54 @@ end
24
24
 
25
25
  The policy and attribute value is sent to OpenAI with our prompt to be validated with GPT. AI generally isn't near zero-cost like other APIs, so this can be expensive to run. Use with some caution.
26
26
 
27
- ## Installation
27
+ ## Configuration
28
+
29
+ We're using `ruby-openai` as the OpenAI client under the hood, see the documentation here for how to configure the access token.
30
+
31
+ https://github.com/alexrudall/ruby-openai#with-config
32
+
33
+ Note: if you're using [Bullet Train](bullettrain.co), fill in
34
+ `ENV["OPENAI_ACCESS_TOKEN"]` and OpenAI will already be configured for you.
35
+
36
+ ### Setting request timeout
37
+
38
+ For validations we know we're not waiting for a complicated chat response, so we timeout requests after 3 seconds instead of `ruby-openai`'s 120 second default. If you need to, you can change the timeout with this:
39
+
40
+ ```ruby
41
+ ActiveModel::Validations::AI.request_timeout = 5 # seconds
42
+ ```
43
+
44
+ ### Handling errors
45
+
46
+ We capture any `Faraday::Error` exceptions and report them. If we're in a Rails app we report via `Rails.error` otherwise it's via `ActiveSupport::ErrorReporter.new`. This gives you access to report to your existing error reporting service automatically.
47
+
48
+ You can also change the error reporter if need be:
28
49
 
29
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
50
+ ```ruby
51
+ ActiveModel::Validations::AI.error_reporter = ActiveSupport::ErrorReporter.new
52
+ ```
53
+
54
+ ### Testing
55
+
56
+ We've tested that our requests to OpenAI work so you don't have to pay that cost in your apps tests.
57
+
58
+ Add this in your `test/test_helper.rb` or `spec/spec_helper.rb`:
59
+
60
+ ```ruby
61
+ ActiveModel::Validations::AI.stub_with valid: true
62
+ ```
63
+
64
+ Which prevent any requests from firing and assumes that any validation from this gem is valid.
65
+
66
+ ## Installation
30
67
 
31
68
  Install the gem and add to the application's Gemfile by executing:
32
69
 
33
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
70
+ $ bundle add active_model-validations-ai
34
71
 
35
72
  If bundler is not being used to manage dependencies, install the gem by executing:
36
73
 
37
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
74
+ $ gem install active_model-validations-ai
38
75
 
39
76
  ## Development
40
77
 
@@ -3,3 +3,4 @@ en:
3
3
  # The values :model, :attribute and :value are always available for interpolation
4
4
  messages:
5
5
  noncompliant: "does not comply with our policy '%{policy}'"
6
+ errored: "validation encountered an error and has been reported"
@@ -3,7 +3,7 @@
3
3
  module ActiveModel
4
4
  module Validations
5
5
  module AI
6
- VERSION = "0.1.0"
6
+ VERSION = "0.2.0"
7
7
  end
8
8
  end
9
9
  end
@@ -9,20 +9,64 @@ require_relative "ai/version"
9
9
  module ActiveModel
10
10
  module Validations
11
11
  module AI
12
+ singleton_class.attr_accessor :error_reporter
13
+ @error_reporter = ActiveSupport::ErrorReporter.new
14
+
15
+ singleton_class.attr_accessor :request_timeout
16
+ @request_timeout = 3 # seconds
17
+
12
18
  def self.client
13
- @client ||= OpenAI::Client.new
19
+ @client ||= OpenAI::Client.new(request_timeout: request_timeout)
20
+ end
21
+
22
+ def self.stub_with(valid:)
23
+ old_client, @client = @client, MockGPT.new(request_timeout: request_timeout, valid: valid)
24
+ yield if block_given?
25
+ ensure
26
+ @client = old_client if block_given?
27
+ end
28
+
29
+ class MockGPT
30
+ singleton_class.attr_reader :response
31
+
32
+ def self.set(**response)
33
+ old_response, @response = @response, response
34
+ yield
35
+ ensure
36
+ @response = old_response
37
+ end
38
+
39
+ def initialize(request_timeout:, **response)
40
+ @request_timeout, @response = request_timeout, response
41
+ end
42
+ attr_reader :request_timeout
43
+
44
+ def chat(parameters:)
45
+ raise Faraday::Error if request_timeout&.zero?
46
+ # parameters => { model:, messages: [{content:}] } # TODO: Replace with this pattern matching on Ruby 3.0+.
47
+ parameters.dig(:model) or raise ArgumentError
48
+ parameters.dig(:messages, 0, :content) or raise ArgumentError
49
+ { "choices" => [{ "message" => { "content" => response.to_json } }] }
50
+ end
51
+
52
+ private
53
+
54
+ def response
55
+ self.class.response || @response
56
+ end
14
57
  end
15
58
  end
16
59
 
17
60
  class ComplianceValidator < ::ActiveModel::EachValidator
18
61
  def validate_each(record, name, value)
19
- record.errors.add(name, :noncompliant, **options) unless valid?(value)
62
+ AI.error_reporter.handle(Faraday::Error, fallback: -> { record.errors.add(name, :errored, **options) }) do
63
+ record.errors.add(name, :noncompliant, **options) unless valid?(value)
64
+ end
20
65
  end
21
66
 
22
67
  private
23
68
 
24
69
  def valid?(value)
25
- # TODO: Figure out how to timeout a request or handle downtime, and invalidate the attribute.
26
70
  response = AI.client.chat(
27
71
  parameters: {
28
72
  model: "gpt-3.5-turbo",
@@ -34,7 +78,7 @@ module ActiveModel
34
78
  end
35
79
 
36
80
  def prompt_for(value)
37
- "Validate this input '#{value}' against this policy '#{policy}'. Respod only in JSON formatted like this '{ \"valid\": true }'."
81
+ "Validate this input '#{value}' against this policy '#{policy}'. Respond only in JSON formatted like this '{ \"valid\": true }'."
38
82
  end
39
83
 
40
84
  def policy
@@ -53,3 +97,11 @@ end
53
97
  ActiveSupport.on_load :i18n do
54
98
  I18n.load_path << File.expand_path("ai/locale/en.yml", __dir__)
55
99
  end
100
+
101
+ if defined?(Rails::Railtie)
102
+ class ActiveModel::Validations::AI::Railtie < Rails::Railtie
103
+ initializer "error_reporter.setup" do
104
+ ActiveModel::Validations::AI.error_reporter = Rails.error
105
+ end
106
+ end
107
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_model-validations-ai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kasper Timm Hansen
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2023-10-06 00:00:00.000000000 Z
12
+ date: 2023-11-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
@@ -17,14 +17,14 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '6.1'
20
+ version: '7.0'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
- version: '6.1'
27
+ version: '7.0'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: ruby-openai
30
30
  requirement: !ruby/object:Gem::Requirement
@@ -71,7 +71,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - ">="
73
73
  - !ruby/object:Gem::Version
74
- version: 2.7.0
74
+ version: 3.0.0
75
75
  required_rubygems_version: !ruby/object:Gem::Requirement
76
76
  requirements:
77
77
  - - ">="