From 34e179ecce9215f68b768d17953e66cb8b8c92b3 Mon Sep 17 00:00:00 2001 From: Sergio Bayona Date: Sat, 20 Apr 2024 11:25:14 -0500 Subject: [PATCH 01/13] github actions for building and releasing the gem --- .github/workflows/dev-build.yml | 22 ++++++++++++++++++++++ .github/workflows/gem-release.yml | 28 ++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 .github/workflows/dev-build.yml create mode 100644 .github/workflows/gem-release.yml diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml new file mode 100644 index 0000000..a2f30d8 --- /dev/null +++ b/.github/workflows/dev-build.yml @@ -0,0 +1,22 @@ +# .github/workflows/push_gem.yml +jobs: + push: + name: Push gem to RubyGems.org + runs-on: ubuntu-latest + + permissions: + id-token: write + contents: write + + steps: + # Set up + - uses: actions/checkout@v4 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + ruby-version: ruby + + # Release + - uses: rubygems/release-gem@v1 + \ No newline at end of file diff --git a/.github/workflows/gem-release.yml b/.github/workflows/gem-release.yml new file mode 100644 index 0000000..038be7b --- /dev/null +++ b/.github/workflows/gem-release.yml @@ -0,0 +1,28 @@ +name: Gem Release + +on: + pull_request: + branches: [ "main" ] + types: + - closed +jobs: + if_merged: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + + permissions: + contents: write + id-token: write + + environment: release + + steps: + - uses: actions/checkout@v4 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + ruby-version: ruby + + # Release + - uses: rubygems/release-gem@v1 \ No newline at end of file From e99c32399f56b62d2229a31bba69f077bb5ab4d5 Mon Sep 17 00:00:00 2001 From: Sergio Bayona Date: Sat, 20 Apr 2024 18:29:03 -0500 Subject: [PATCH 02/13] fixing github action --- .github/workflows/dev-build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml index a2f30d8..9f68a07 100644 --- a/.github/workflows/dev-build.yml +++ b/.github/workflows/dev-build.yml @@ -1,4 +1,5 @@ -# .github/workflows/push_gem.yml +name: Dev Build +on: [push] jobs: push: name: Push gem to RubyGems.org From ab0f047ca8f1e91a71342ad478db3f55c2c3afbf Mon Sep 17 00:00:00 2001 From: Sergio Bayona Date: Sat, 20 Apr 2024 18:36:24 -0500 Subject: [PATCH 03/13] dev build --- .github/workflows/dev-build.yml | 41 ++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml index 9f68a07..d31ff38 100644 --- a/.github/workflows/dev-build.yml +++ b/.github/workflows/dev-build.yml @@ -1,23 +1,28 @@ name: Dev Build -on: [push] -jobs: + +on: push: - name: Push gem to RubyGems.org - runs-on: ubuntu-latest + branches: [ "main" ] + pull_request: + branches: [ "main" ] - permissions: - id-token: write - contents: write +permissions: + contents: read - steps: - # Set up - - uses: actions/checkout@v4 - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true - ruby-version: ruby +jobs: + test: + + runs-on: ubuntu-latest + strategy: + matrix: + ruby-version: ['3.3.0'] - # Release - - uses: rubygems/release-gem@v1 - \ No newline at end of file + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - name: Run tests + run: bundle exec rake From 9e8b01e7be0fe066df892d15d7ea3add8885b997 Mon Sep 17 00:00:00 2001 From: Sergio Bayona Date: Sat, 20 Apr 2024 20:22:18 -0500 Subject: [PATCH 04/13] github action fixes --- .github/workflows/dev-build.yml | 1 + .github/workflows/gem-release.yml | 56 +++++++++++++++++++++---------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml index d31ff38..78ae283 100644 --- a/.github/workflows/dev-build.yml +++ b/.github/workflows/dev-build.yml @@ -20,6 +20,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up Ruby + uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 with: ruby-version: ${{ matrix.ruby-version }} diff --git a/.github/workflows/gem-release.yml b/.github/workflows/gem-release.yml index 038be7b..92d96a2 100644 --- a/.github/workflows/gem-release.yml +++ b/.github/workflows/gem-release.yml @@ -1,28 +1,48 @@ -name: Gem Release +name: Ruby Gem on: + push: + branches: [ "main" ] pull_request: branches: [ "main" ] - types: - - closed + jobs: - if_merged: - if: github.event.pull_request.merged == true + build: + name: Build + Publish runs-on: ubuntu-latest - permissions: - contents: write - id-token: write - - environment: release + contents: read + packages: write steps: - - uses: actions/checkout@v4 - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true - ruby-version: ruby + - uses: actions/checkout@v3 + - name: Set up Ruby 3.3.0 + # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, + # change this to (see https://github.com/ruby/setup-ruby#versioning): + # uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 + with: + ruby-version: 3.3.0 + + - name: Publish to GPR + run: | + mkdir -p $HOME/.gem + touch $HOME/.gem/credentials + chmod 0600 $HOME/.gem/credentials + printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials + gem build *.gemspec + gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem + env: + GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}" + OWNER: ${{ github.repository_owner }} - # Release - - uses: rubygems/release-gem@v1 \ No newline at end of file + - name: Publish to RubyGems + run: | + mkdir -p $HOME/.gem + touch $HOME/.gem/credentials + chmod 0600 $HOME/.gem/credentials + printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials + gem build *.gemspec + gem push *.gem + env: + GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}" From 127618a2adea1536a391b3a1bbefcfbbf4978fbe Mon Sep 17 00:00:00 2001 From: Sergio Bayona Date: Sat, 20 Apr 2024 20:22:18 -0500 Subject: [PATCH 05/13] github action fixes --- .github/workflows/dev-build.yml | 7 ++-- .github/workflows/gem-release.yml | 56 +++++++++++++++++++++---------- Gemfile.lock | 2 ++ instructor-rb.gemspec | 1 + 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml index d31ff38..f7c443c 100644 --- a/.github/workflows/dev-build.yml +++ b/.github/workflows/dev-build.yml @@ -15,12 +15,13 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['3.3.0'] + ruby-version: ['3.3'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Ruby - uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 + + uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true # runs 'bundle install' and caches installed gems automatically diff --git a/.github/workflows/gem-release.yml b/.github/workflows/gem-release.yml index 038be7b..92d96a2 100644 --- a/.github/workflows/gem-release.yml +++ b/.github/workflows/gem-release.yml @@ -1,28 +1,48 @@ -name: Gem Release +name: Ruby Gem on: + push: + branches: [ "main" ] pull_request: branches: [ "main" ] - types: - - closed + jobs: - if_merged: - if: github.event.pull_request.merged == true + build: + name: Build + Publish runs-on: ubuntu-latest - permissions: - contents: write - id-token: write - - environment: release + contents: read + packages: write steps: - - uses: actions/checkout@v4 - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true - ruby-version: ruby + - uses: actions/checkout@v3 + - name: Set up Ruby 3.3.0 + # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, + # change this to (see https://github.com/ruby/setup-ruby#versioning): + # uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 + with: + ruby-version: 3.3.0 + + - name: Publish to GPR + run: | + mkdir -p $HOME/.gem + touch $HOME/.gem/credentials + chmod 0600 $HOME/.gem/credentials + printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials + gem build *.gemspec + gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem + env: + GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}" + OWNER: ${{ github.repository_owner }} - # Release - - uses: rubygems/release-gem@v1 \ No newline at end of file + - name: Publish to RubyGems + run: | + mkdir -p $HOME/.gem + touch $HOME/.gem/credentials + chmod 0600 $HOME/.gem/credentials + printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials + gem build *.gemspec + gem push *.gem + env: + GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}" diff --git a/Gemfile.lock b/Gemfile.lock index 5defc8c..9cc83eb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -66,6 +66,7 @@ GEM public_suffix (5.0.5) racc (1.7.3) rainbow (3.1.1) + rake (13.2.1) regexp_parser (2.9.0) rexml (3.2.6) rspec (3.13.0) @@ -118,6 +119,7 @@ PLATFORMS DEPENDENCIES instructor-rb! pry (~> 0.13) + rake (~> 13.1) rspec (~> 3.0) rspec-json_expectations (~> 2.0) rubocop (~> 1.21) diff --git a/instructor-rb.gemspec b/instructor-rb.gemspec index ea07a43..2a73c08 100644 --- a/instructor-rb.gemspec +++ b/instructor-rb.gemspec @@ -34,4 +34,5 @@ Gem::Specification.new do |spec| spec.add_dependency 'activesupport', '~> 7.0' spec.add_dependency 'easy_talk', '~> 0.1.7' spec.add_dependency 'ruby-openai', '~> 6' + spec.add_development_dependency 'rake', '~> 13.1' end From 150593e9df72c68165ad01ffc4f97b584d989486 Mon Sep 17 00:00:00 2001 From: Sergio Bayona Date: Mon, 22 Apr 2024 10:09:27 -0500 Subject: [PATCH 06/13] Rakefile --- Rakefile | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 Rakefile diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..05fde3d --- /dev/null +++ b/Rakefile @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'bundler/gem_tasks' +require 'rspec/core/rake_task' +require 'rubocop/rake_task' + +RSpec::Core::RakeTask.new(:spec) + +RuboCop::RakeTask.new + +task default: %i[spec rubocop] From 1d02ccf40d0c9bbf48c2b7e9fd972cbcbe253f2a Mon Sep 17 00:00:00 2001 From: Sergio Bayona Date: Mon, 22 Apr 2024 10:09:58 -0500 Subject: [PATCH 07/13] rubocop run --- lib/instructor/openai/patch.rb | 2 ++ lib/instructor/openai/response.rb | 12 +++++++----- spec/iterable_spec.rb | 2 +- spec/openai/response_spec.rb | 2 ++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/instructor/openai/patch.rb b/lib/instructor/openai/patch.rb index b3d9869..f8a0c51 100644 --- a/lib/instructor/openai/patch.rb +++ b/lib/instructor/openai/patch.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Instructor module OpenAI module Patch diff --git a/lib/instructor/openai/response.rb b/lib/instructor/openai/response.rb index 59b3b07..a339bce 100644 --- a/lib/instructor/openai/response.rb +++ b/lib/instructor/openai/response.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Instructor module OpenAI class Response @@ -6,7 +8,7 @@ def initialize(response) end def chat_completions - @response.dig('choices') + @response['choices'] end def tool_calls @@ -14,7 +16,7 @@ def tool_calls end def function_responses - tool_calls&.map { |tool_call| tool_call.dig('function') } + tool_calls&.map { |tool_call| tool_call['function'] } end def function_response @@ -27,14 +29,14 @@ def single_response? def parse if single_response? - JSON.parse(function_response.dig('arguments')) + JSON.parse(function_response['arguments']) else - function_responses.map { |res| JSON.parse(res.dig('arguments')) } + function_responses.map { |res| JSON.parse(res['arguments']) } end end def by_function_name(function_name) - function_responses&.find { |res| res.dig('name') == function_name }&.dig('arguments') + function_responses&.find { |res| res['name'] == function_name }&.dig('arguments') end end end diff --git a/spec/iterable_spec.rb b/spec/iterable_spec.rb index 111bbc8..ab43636 100644 --- a/spec/iterable_spec.rb +++ b/spec/iterable_spec.rb @@ -25,7 +25,7 @@ class UserDetail response_model: T::Array[UserDetail] ) - for user in users + users.each do |user| expect(user.valid?).to eq(true) expect(user.name).to be_a(String) expect(user.age).to be_a(Integer) diff --git a/spec/openai/response_spec.rb b/spec/openai/response_spec.rb index fd8ea4e..6ff636e 100644 --- a/spec/openai/response_spec.rb +++ b/spec/openai/response_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' RSpec.describe Instructor::OpenAI::Response do From ebc392375ad8cec6874d000b79a8bcae75746ae9 Mon Sep 17 00:00:00 2001 From: Sergio Bayona Date: Mon, 22 Apr 2024 10:24:32 -0500 Subject: [PATCH 08/13] gem fixes --- .gitignore | 4 +- .rubocop.yml | 18 +++++ .ruby-version | 1 - Gemfile | 9 --- Gemfile.lock | 130 --------------------------------- instructor-rb.gemspec | 10 ++- lib/instructor/openai/patch.rb | 2 +- spec/openai/patching_spec.rb | 2 +- spec/openai/response_spec.rb | 3 +- 9 files changed, 33 insertions(+), 146 deletions(-) create mode 100644 .rubocop.yml delete mode 100644 .ruby-version delete mode 100644 Gemfile.lock diff --git a/.gitignore b/.gitignore index 18eda07..7915ff7 100644 --- a/.gitignore +++ b/.gitignore @@ -46,8 +46,8 @@ build-iPhoneSimulator/ # for a library or gem, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: -# Gemfile.lock -# .ruby-version +Gemfile.lock +.ruby-version # .ruby-gemset # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..dbbf2ff --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,18 @@ +require: + - rubocop-rake + - rubocop-rspec + +AllCops: + TargetRubyVersion: 3.1 + +Metrics/BlockLength: + Exclude: + - 'spec/**/*' + +Lint/ConstantDefinitionInBlock: + Exclude: + - 'spec/**/*' + +Layout/LineLength: + Exclude: + - 'spec/**/*' diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 15a2799..0000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -3.3.0 diff --git a/Gemfile b/Gemfile index 087f98c..5f10ba8 100644 --- a/Gemfile +++ b/Gemfile @@ -2,12 +2,3 @@ source 'https://rubygems.org' gemspec - -group :tests do - gem 'pry', '~> 0.13' - gem 'rspec', '~> 3.0' - gem 'rspec-json_expectations', '~> 2.0' - gem 'rubocop', '~> 1.21' - gem 'vcr', '~> 6.0' - gem 'webmock', '~> 3.13' -end diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 9cc83eb..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,130 +0,0 @@ -PATH - remote: . - specs: - instructor-rb (0.1.0) - activesupport (~> 7.0) - easy_talk (~> 0.1.7) - ruby-openai (~> 6) - -GEM - remote: https://rubygems.org/ - specs: - activesupport (7.1.3.2) - base64 - bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) - connection_pool (>= 2.2.5) - drb - i18n (>= 1.6, < 2) - minitest (>= 5.1) - mutex_m - tzinfo (~> 2.0) - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) - ast (2.4.2) - base64 (0.2.0) - bigdecimal (3.1.7) - coderay (1.1.3) - concurrent-ruby (1.2.3) - connection_pool (2.4.1) - crack (1.0.0) - bigdecimal - rexml - diff-lcs (1.5.1) - drb (2.2.1) - easy_talk (0.1.7) - activesupport (~> 7.0) - json-schema (~> 4) - sorbet-runtime (~> 0.5) - event_stream_parser (1.0.0) - faraday (2.9.0) - faraday-net_http (>= 2.0, < 3.2) - faraday-multipart (1.0.4) - multipart-post (~> 2) - faraday-net_http (3.1.0) - net-http - hashdiff (1.1.0) - i18n (1.14.4) - concurrent-ruby (~> 1.0) - json (2.7.2) - json-schema (4.3.0) - addressable (>= 2.8) - language_server-protocol (3.17.0.3) - method_source (1.0.0) - minitest (5.22.3) - multipart-post (2.4.0) - mutex_m (0.2.0) - net-http (0.4.1) - uri - parallel (1.24.0) - parser (3.3.0.5) - ast (~> 2.4.1) - racc - pry (0.14.2) - coderay (~> 1.1) - method_source (~> 1.0) - public_suffix (5.0.5) - racc (1.7.3) - rainbow (3.1.1) - rake (13.2.1) - regexp_parser (2.9.0) - rexml (3.2.6) - rspec (3.13.0) - rspec-core (~> 3.13.0) - rspec-expectations (~> 3.13.0) - rspec-mocks (~> 3.13.0) - rspec-core (3.13.0) - rspec-support (~> 3.13.0) - rspec-expectations (3.13.0) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.13.0) - rspec-json_expectations (2.2.0) - rspec-mocks (3.13.0) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.13.0) - rspec-support (3.13.1) - rubocop (1.63.1) - json (~> 2.3) - language_server-protocol (>= 3.17.0) - parallel (~> 1.10) - parser (>= 3.3.0.2) - rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.31.1, < 2.0) - ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.31.2) - parser (>= 3.3.0.4) - ruby-openai (6.5.0) - event_stream_parser (>= 0.3.0, < 2.0.0) - faraday (>= 1) - faraday-multipart (>= 1) - ruby-progressbar (1.13.0) - sorbet-runtime (0.5.11347) - tzinfo (2.0.6) - concurrent-ruby (~> 1.0) - unicode-display_width (2.5.0) - uri (0.13.0) - vcr (6.2.0) - webmock (3.23.0) - addressable (>= 2.8.0) - crack (>= 0.3.2) - hashdiff (>= 0.4.0, < 2.0.0) - -PLATFORMS - arm64-darwin-22 - ruby - -DEPENDENCIES - instructor-rb! - pry (~> 0.13) - rake (~> 13.1) - rspec (~> 3.0) - rspec-json_expectations (~> 2.0) - rubocop (~> 1.21) - vcr (~> 6.0) - webmock (~> 3.13) - -BUNDLED WITH - 2.5.4 diff --git a/instructor-rb.gemspec b/instructor-rb.gemspec index 2a73c08..bc56409 100644 --- a/instructor-rb.gemspec +++ b/instructor-rb.gemspec @@ -12,7 +12,7 @@ Gem::Specification.new do |spec| spec.description = "Explore the power of structured extraction in Ruby with the Instructor gem. Leveraging OpenAI's function calling API." spec.homepage = 'https://github.com/instructor-ai/instructor-rb' spec.license = 'MIT' - spec.required_ruby_version = '>= 2.7.0' + spec.required_ruby_version = '>= 3.1.0' spec.metadata['allowed_push_host'] = 'https://rubygems.pkg.github.com/instructor-ai' @@ -34,5 +34,13 @@ Gem::Specification.new do |spec| spec.add_dependency 'activesupport', '~> 7.0' spec.add_dependency 'easy_talk', '~> 0.1.7' spec.add_dependency 'ruby-openai', '~> 6' + spec.add_development_dependency 'pry-byebug', '~> 3.10' spec.add_development_dependency 'rake', '~> 13.1' + spec.add_development_dependency 'rspec', '~> 3.0' + spec.add_development_dependency 'rspec-json_expectations', '~> 2.0' + spec.add_development_dependency 'rubocop', '~> 1.21' + spec.add_development_dependency 'rubocop-rake', '~> 0.6' + spec.add_development_dependency 'rubocop-rspec', '~> 2.29' + spec.add_development_dependency 'vcr', '~> 6.0' + spec.add_development_dependency 'webmock', '~> 3.13' end diff --git a/lib/instructor/openai/patch.rb b/lib/instructor/openai/patch.rb index f8a0c51..5c98316 100644 --- a/lib/instructor/openai/patch.rb +++ b/lib/instructor/openai/patch.rb @@ -19,7 +19,7 @@ def chat(parameters:, response_model: nil, max_retries: 0, validation_context: n model = determine_model(response_model) function = build_function(model) parameters = prepare_parameters(parameters, validation_context, function) - response = json_post(path: '/chat/completions', parameters: parameters) + response = json_post(path: '/chat/completions', parameters:) process_response(response, model) end end diff --git a/spec/openai/patching_spec.rb b/spec/openai/patching_spec.rb index 90cf876..e8be373 100644 --- a/spec/openai/patching_spec.rb +++ b/spec/openai/patching_spec.rb @@ -78,7 +78,7 @@ def self.name it 'retries the chat method when parsing fails' do expect(client).to receive(:json_post).exactly(max_retries).times expect do - client.chat(parameters: parameters, response_model: user_model, max_retries: max_retries) + client.chat(parameters:, response_model: user_model, max_retries:) end.to raise_error(JSON::ParserError) end end diff --git a/spec/openai/response_spec.rb b/spec/openai/response_spec.rb index 6ff636e..fa1a5ac 100644 --- a/spec/openai/response_spec.rb +++ b/spec/openai/response_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Instructor::OpenAI::Response do + subject(:response_object) { described_class.new(response) } + let(:response) do { 'id' => 'chatcmpl-9DEGpBfHqcS17uJtx1vxpRMEb4DtK', 'object' => 'chat.completion', @@ -30,7 +32,6 @@ }, 'system_fingerprint' => 'fp_c2295e73ad' } end - subject(:response_object) { described_class.new(response) } it 'returns a chat completion' do expect(response_object.chat_completions).to eq(response['choices']) From e44a86c2e2254c42a4425d373b020eddfe8c0a95 Mon Sep 17 00:00:00 2001 From: Sergio Bayona Date: Mon, 22 Apr 2024 11:04:06 -0500 Subject: [PATCH 09/13] various modifications in preparation for initial release --- .rubocop.yml | 20 +++++ instructor-rb.gemspec | 4 +- lib/instructor.rb | 8 +- lib/instructor/openai/patch.rb | 55 +++++++++++++ lib/instructor/openai/response.rb | 28 +++++++ spec/basic_spec.rb | 28 ------- spec/{ => examples}/autoticketer_spec.rb | 82 +++++++------------ spec/features/basic_use_spec.rb | 38 +++++++++ spec/features/iterable_spec.rb | 56 +++++++++++++ spec/helpers/autoticketer_models.rb | 37 +++++++++ spec/iterable_spec.rb | 39 --------- .../{patching_spec.rb => patch_spec.rb} | 49 ++++++----- spec/spec_helper.rb | 2 +- 13 files changed, 301 insertions(+), 145 deletions(-) delete mode 100644 spec/basic_spec.rb rename spec/{ => examples}/autoticketer_spec.rb (74%) create mode 100644 spec/features/basic_use_spec.rb create mode 100644 spec/features/iterable_spec.rb create mode 100644 spec/helpers/autoticketer_models.rb delete mode 100644 spec/iterable_spec.rb rename spec/openai/{patching_spec.rb => patch_spec.rb} (77%) diff --git a/.rubocop.yml b/.rubocop.yml index dbbf2ff..953ee26 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -16,3 +16,23 @@ Lint/ConstantDefinitionInBlock: Layout/LineLength: Exclude: - 'spec/**/*' + +RSpec/FilePath: + SpecSuffixOnly: true + +RSpec/MultipleExpectations: + Max: 4 + +RSpec/ExampleLength: + Max: 10 + Exclude: + - spec/examples/* + +RSpec/DescribeClass: + Exclude: + - spec/examples/* + - spec/features/* + +RSpec/MethodLength: + Exclude: + - spec/examples/* \ No newline at end of file diff --git a/instructor-rb.gemspec b/instructor-rb.gemspec index bc56409..7a9261c 100644 --- a/instructor-rb.gemspec +++ b/instructor-rb.gemspec @@ -9,12 +9,12 @@ Gem::Specification.new do |spec| spec.email = ['bayona.sergio@gmail.com', 'jason@jxnl.co'] spec.summary = 'Structured extraction in Ruby, powered by llms.' - spec.description = "Explore the power of structured extraction in Ruby with the Instructor gem. Leveraging OpenAI's function calling API." + spec.description = 'Explore the power of LLM structured extraction in Ruby with the Instructor gem.' spec.homepage = 'https://github.com/instructor-ai/instructor-rb' spec.license = 'MIT' spec.required_ruby_version = '>= 3.1.0' - spec.metadata['allowed_push_host'] = 'https://rubygems.pkg.github.com/instructor-ai' + spec.metadata['allowed_push_host'] = 'https://rubygems.pkg.github.com' spec.metadata['homepage_uri'] = spec.homepage spec.metadata['source_code_uri'] = 'https://github.com/instructor-ai/instructor-rb' diff --git a/lib/instructor.rb b/lib/instructor.rb index 18255cd..e820e9d 100644 --- a/lib/instructor.rb +++ b/lib/instructor.rb @@ -7,15 +7,21 @@ require_relative 'instructor/openai/patch' require_relative 'instructor/openai/response' +# Instructor makes it easy to reliably get structured data like JSON from Large Language Models (LLMs) +# like GPT-3.5, GPT-4, GPT-4-Vision module Instructor class Error < ::StandardError; end + # The ValidationError class represents an error that occurs during validation. class ValidationError < ::StandardError; end - # patches the OpenAI client to add the following functionality: + # Patches the OpenAI client to add the following functionality: # - Retries on exceptions # - Accepts and validates a response model # - Accepts a validation_context argument + # + # @param openai_client [OpenAI::Client] The OpenAI client to be patched. + # @return [OpenAI::Client] The patched OpenAI client. def self.patch(openai_client) openai_client.prepend(Instructor::OpenAI::Patch) end diff --git a/lib/instructor/openai/patch.rb b/lib/instructor/openai/patch.rb index 5c98316..927a624 100644 --- a/lib/instructor/openai/patch.rb +++ b/lib/instructor/openai/patch.rb @@ -1,8 +1,15 @@ # frozen_string_literal: true +# The Instructor module provides functionality for interacting with OpenAI's chat API. module Instructor module OpenAI + # The `Patch` module provides methods for patching and modifying the OpenAI client behavior. module Patch + # Executes a block of code with retries in case of specific exceptions. + # + # @param max_retries [Integer] The maximum number of retries. + # @param exceptions [Array] The exceptions to catch and retry. + # @yield The block of code to execute. def with_retries(max_retries, exceptions, &block) attempts = 0 begin @@ -14,6 +21,13 @@ def with_retries(max_retries, exceptions, &block) end end + # Sends a chat request to the API and processes the response. + # + # @param parameters [Hash] The parameters for the chat request as expected by the OpenAI client. + # @param response_model [Class] The response model class. + # @param max_retries [Integer] The maximum number of retries. Default is 0. + # @param validation_context [Hash] The validation context for the parameters. Optional. + # @return [Object] The processed response. def chat(parameters:, response_model: nil, max_retries: 0, validation_context: nil) with_retries(max_retries, [JSON::ParserError, Instructor::ValidationError, Faraday::ParsingError]) do model = determine_model(response_model) @@ -24,16 +38,32 @@ def chat(parameters:, response_model: nil, max_retries: 0, validation_context: n end end + # Prepares the parameters for the chat request. + # + # @param parameters [Hash] The original parameters. + # @param validation_context [Hash] The validation context for the parameters. + # @param function [Hash] The function details. + # @return [Hash] The prepared parameters. def prepare_parameters(parameters, validation_context, function) parameters = apply_validation_context(parameters, validation_context) parameters.merge(tools: [function]) end + # Processes the API response. + # + # @param response [Hash] The API response. + # @param model [Class] The response model class. + # @return [Object] The processed response. def process_response(response, model) parsed_response = Response.new(response).parse iterable? ? process_multiple_responses(parsed_response, model) : process_single_response(parsed_response, model) end + # Processes multiple responses from the API. + # + # @param parsed_response [Array] The parsed API responses. + # @param model [Class] The response model class. + # @return [Array] The processed responses. def process_multiple_responses(parsed_response, model) parsed_response.map do |response| instance = model.new(response) @@ -41,11 +71,20 @@ def process_multiple_responses(parsed_response, model) end end + # Processes a single response from the API. + # + # @param parsed_response [Hash] The parsed API response. + # @param model [Class] The response model class. + # @return [Object] The processed response. def process_single_response(parsed_response, model) instance = model.new(parsed_response) instance.valid? ? instance : raise(Instructor::ValidationError) end + # Determines the response model based on the provided value. + # + # @param response_model [Class] The response model class or typed array. + # @return [Class] The determined response model class. def determine_model(response_model) if response_model.is_a?(T::Types::TypedArray) @iterable = true @@ -56,6 +95,11 @@ def determine_model(response_model) end end + # Applies the validation context to the parameters. + # + # @param parameters [Hash] The original parameters. + # @param validation_context [Hash] The validation context. + # @return [Hash] The parameters with applied validation context. def apply_validation_context(parameters, validation_context) return parameters unless validation_context.is_a?(Hash) @@ -66,6 +110,10 @@ def apply_validation_context(parameters, validation_context) parameters end + # Builds the function details for the API request. + # + # @param model [Class] The response model class. + # @return [Hash] The function details. def build_function(model) { type: 'function', @@ -77,10 +125,17 @@ def build_function(model) } end + # Generates the description for the function. + # + # @param model [Class] The response model class. + # @return [String] The generated description. def generate_description(model) "Correctly extracted `#{model.name}` with all the required parameters with correct types" end + # Checks if the response is iterable. + # + # @return [Boolean] `true` if the response is iterable, `false` otherwise. def iterable? @iterable end diff --git a/lib/instructor/openai/response.rb b/lib/instructor/openai/response.rb index a339bce..12c4bd4 100644 --- a/lib/instructor/openai/response.rb +++ b/lib/instructor/openai/response.rb @@ -2,31 +2,55 @@ module Instructor module OpenAI + # The Response class represents the response received from the OpenAI API. + # It takes the raw response and provides convenience methods to access the chat completions, + # tool calls, function responses, and parsed arguments. class Response + # Initializes a new instance of the Response class. + # + # @param response [Hash] The response received from the OpenAI API. def initialize(response) @response = response end + # Returns the chat completions from the response. + # + # @return [Array] An array of chat completions. def chat_completions @response['choices'] end + # Returns the tool calls from the chat completions. + # + # @return [Hash, nil] The tool calls or nil if not found. def tool_calls chat_completions&.dig(0, 'message', 'tool_calls') end + # Returns the function responses from the tool calls. + # + # @return [Array, nil] An array of function responses or nil if not found. def function_responses tool_calls&.map { |tool_call| tool_call['function'] } end + # Returns the first function response. + # + # @return [Hash, nil] The first function response or nil if not found. def function_response function_responses&.first end + # Checks if there is only a single function response. + # + # @return [Boolean] True if there is only a single function response, false otherwise. def single_response? function_responses&.size == 1 end + # Parses the function response(s) and returns the parsed arguments. + # + # @return [Array, Hash] The parsed arguments. def parse if single_response? JSON.parse(function_response['arguments']) @@ -35,6 +59,10 @@ def parse end end + # Returns the arguments of the function with the specified name. + # + # @param function_name [String] The name of the function. + # @return [Hash, nil] The arguments of the function or nil if not found. def by_function_name(function_name) function_responses&.find { |res| res['name'] == function_name }&.dig('arguments') end diff --git a/spec/basic_spec.rb b/spec/basic_spec.rb deleted file mode 100644 index 9868529..0000000 --- a/spec/basic_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'running an OpenAI function call' do - class UserDetail - include EasyTalk::Model - define_schema do - property :name, String - property :age, Integer - end - end - - it 'returns an object with the expected valid attribute values', vcr: 'basic_spec/valid_response' do - client = Instructor.patch(OpenAI::Client).new - - user = client.chat( - parameters: { - model: 'gpt-3.5-turbo', - messages: [{ role: 'user', content: 'Extract Jason is 25 years old' }] - }, - response_model: UserDetail - ) - - expect(user.name).to eq('Jason') - expect(user.age).to eq(25) - end -end diff --git a/spec/autoticketer_spec.rb b/spec/examples/autoticketer_spec.rb similarity index 74% rename from spec/autoticketer_spec.rb rename to spec/examples/autoticketer_spec.rb index 80603ce..8546779 100644 --- a/spec/autoticketer_spec.rb +++ b/spec/examples/autoticketer_spec.rb @@ -1,65 +1,17 @@ # frozen_string_literal: true require 'spec_helper' +require_relative '../helpers/autoticketer_models' RSpec.describe 'Auto-ticketer' do - class Subtask - include EasyTalk::Model - - define_schema do - property :id, Integer, description: 'Unique identifier for the subtask' - property :name, String, description: 'Informative title of the subtask' - end - end - - class Ticket - include EasyTalk::Model - - PRIORITY = %w[low medium high].freeze - - define_schema do - property :id, Integer, description: 'Unique identifier for the ticket' - property :name, String, description: 'Title of the ticket' - property :description, String, description: 'Detailed description of the ticket' - property :priority, String, description: 'Priority level' - property :assignees, T::Array[String], description: 'List of users assigned to the ticket' - property :subtasks, T.nilable(T::Array[Subtask]), description: 'List of subtasks associated with the ticket' - property :dependencies, T.nilable(T::Array[Integer]), - description: 'List of ticket IDs that this ticket depends on' - end - end - - class ActionItems - include EasyTalk::Model - - define_schema do - property :items, T::Array[Ticket] - end + RSpec.configure do |c| + c.include AutoticketerModels end - def generate(data) - client = Instructor.patch(OpenAI::Client).new - - client.chat( - parameters: { - model: 'gpt-3.5-turbo', - messages: [ - { - role: 'system', - "content": 'The following is a transcript of a meeting between a manager and their team. The manager is assigning tasks to their team members and creating action items for them to complete.' - }, - { - "role": 'user', - "content": "Create the action items for the following transcript: #{data}" - } - ] - }, - response_model: ActionItems - ) - end + let(:client) { Instructor.patch(OpenAI::Client).new } - it 'generates the proper json-schema', vcr: 'autoticketer/generate' do - data = <<~DATA + let(:data) do + <<~DATA Alice: Hey team, we have several critical tasks we need to tackle for the upcoming release. First, we need to work on improving the authentication system. It's a top priority. Bob: Got it, Alice. I can take the lead on the authentication improvements. Are there any specific areas you want me to focus on? @@ -84,6 +36,28 @@ def generate(data) Alice: Sounds like a plan. Let's get these tasks modeled out and get started. DATA + end + + def generate(data) + client.chat( + parameters: { + model: 'gpt-3.5-turbo', + messages: [ + { + role: 'system', + "content": 'The following is a transcript of a meeting between a manager and their team. The manager is assigning tasks to their team members and creating action items for them to complete.' + }, + { + "role": 'user', + "content": "Create the action items for the following transcript: #{data}" + } + ] + }, + response_model: AutoticketerModels::ActionItems + ) + end + + it 'generates the proper json-schema', vcr: 'autoticketer/generate' do result = generate(data) expect(result.as_json).to include_json( diff --git a/spec/features/basic_use_spec.rb b/spec/features/basic_use_spec.rb new file mode 100644 index 0000000..6a0b63c --- /dev/null +++ b/spec/features/basic_use_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'running an OpenAI function call' do + let(:user_model) do + Class.new do + include EasyTalk::Model + + def self.name + 'User' + end + + define_schema do + property :name, String + property :age, Integer + end + end + end + + let(:client) { Instructor.patch(OpenAI::Client).new } + + let(:parameters) do + { + model: 'gpt-3.5-turbo', + messages: [{ role: 'user', content: 'Extract Jason is 25 years old' }] + } + end + + let(:response_model) { user_model } + + it 'returns a single object with the expected valid attribute values', vcr: 'basic_spec/valid_response' do + user = client.chat(parameters:, response_model:) + + expect(user.name).to eq('Jason') + expect(user.age).to eq(25) + end +end diff --git a/spec/features/iterable_spec.rb b/spec/features/iterable_spec.rb new file mode 100644 index 0000000..639ac3b --- /dev/null +++ b/spec/features/iterable_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'running an OpenAI function with a multiple object response' do + let(:user_model) do + Class.new do + include EasyTalk::Model + + def self.name + 'User' + end + + define_schema do + property :name, String + property :age, Integer + end + end + end + + let(:client) { Instructor.patch(OpenAI::Client).new } + + let(:parameters) do + { + model: 'gpt-3.5-turbo', + messages: [ + { role: 'system', content: 'Extract the names and ages of the users' }, + { role: 'user', content: 'Extract `Jason is 25 and Peter is 32`' } + ] + } + end + + let(:response_model) { T::Array[user_model] } + + let(:users) { client.chat(parameters:, response_model:) } + + it 'returns valid objects', vcr: 'iterable_spec/valid_response' do + users.each do |user| + expect(user.valid?).to eq(true) + end + end + + it 'returns objects with valid types', vcr: 'iterable_spec/valid_response' do + users.each do |user| + expect(user.name).to be_a(String) + expect(user.age).to be_a(Integer) + end + end + + it 'returns objects with the expected attribute values', vcr: 'iterable_spec/valid_response' do + expect(users[0].name).to eq('Jason') + expect(users[0].age).to eq(25) + expect(users[1].name).to eq('Peter') + expect(users[1].age).to eq(32) + end +end diff --git a/spec/helpers/autoticketer_models.rb b/spec/helpers/autoticketer_models.rb new file mode 100644 index 0000000..e4e6d34 --- /dev/null +++ b/spec/helpers/autoticketer_models.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module AutoticketerModels + class Subtask + include EasyTalk::Model + + define_schema do + property :id, Integer, description: 'Unique identifier for the subtask' + property :name, String, description: 'Informative title of the subtask' + end + end + + class Ticket + include EasyTalk::Model + + PRIORITY = %w[low medium high].freeze + + define_schema do + property :id, Integer, description: 'Unique identifier for the ticket' + property :name, String, description: 'Title of the ticket' + property :description, String, description: 'Detailed description of the ticket' + property :priority, String, description: 'Priority level' + property :assignees, T::Array[String], description: 'List of users assigned to the ticket' + property :subtasks, T.nilable(T::Array[Subtask]), description: 'List of subtasks associated with the ticket' + property :dependencies, T.nilable(T::Array[Integer]), + description: 'List of ticket IDs that this ticket depends on' + end + end + + class ActionItems + include EasyTalk::Model + + define_schema do + property :items, T::Array[Ticket] + end + end +end diff --git a/spec/iterable_spec.rb b/spec/iterable_spec.rb deleted file mode 100644 index ab43636..0000000 --- a/spec/iterable_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'running an OpenAI function with an interable response model' do - class UserDetail - include EasyTalk::Model - define_schema do - property :name, String - property :age, Integer - end - end - - it 'returns an object with the expected valid attribute values', vcr: 'iterable_spec/valid_response' do - client = Instructor.patch(OpenAI::Client).new - - users = client.chat( - parameters: { - model: 'gpt-3.5-turbo', - messages: [ - { role: 'system', content: 'Extract the names and ages of the users' }, - { role: 'user', content: 'Extract `Jason is 25 and Peter is 32`' } - ] - }, - response_model: T::Array[UserDetail] - ) - - users.each do |user| - expect(user.valid?).to eq(true) - expect(user.name).to be_a(String) - expect(user.age).to be_a(Integer) - end - - expect(users[0].name).to eq('Jason') - expect(users[0].age).to eq(25) - expect(users[1].name).to eq('Peter') - expect(users[1].age).to eq(32) - end -end diff --git a/spec/openai/patching_spec.rb b/spec/openai/patch_spec.rb similarity index 77% rename from spec/openai/patching_spec.rb rename to spec/openai/patch_spec.rb index e8be373..c9a099f 100644 --- a/spec/openai/patching_spec.rb +++ b/spec/openai/patch_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'patching the OpenAI client' do +RSpec.describe Instructor::OpenAI::Patch do subject(:patched_client) { Instructor.patch(OpenAI::Client) } let(:user_model) do @@ -24,15 +24,15 @@ def self.name expect(patched_client).to eq(OpenAI::Client) end - context 'a new instance of the patched client' do + context 'with a new instance of the patched client' do it 'returns an instance of the patched client' do expect(patched_client.new).to be_an_instance_of(OpenAI::Client) end - it 'receives the chat method with the expected arguments' do + pending 'receives the chat method with the expected arguments' do client = patched_client.new - expect(client).to receive(:chat).with(parameters: {}, response_model: nil) client.chat(parameters: {}, response_model: nil) + expect(client).to have_received(:chat).with(parameters: {}, response_model: nil) end it 'does not require the response model argument' do @@ -76,22 +76,31 @@ def self.name end it 'retries the chat method when parsing fails' do - expect(client).to receive(:json_post).exactly(max_retries).times expect do client.chat(parameters:, response_model: user_model, max_retries:) end.to raise_error(JSON::ParserError) + + expect(client).to have_received(:json_post).exactly(max_retries).times end end context 'with validation context' do - it 'returns an object with the expected valid attribute values', vcr: 'patching_spec/with_validation_context' do - client = patched_client.new + let(:client) { patched_client.new } + let(:parameters) do + { + model: 'gpt-3.5-turbo', + messages: [ + { + role: 'user', + content: 'Answer the question: %s with the text chunk: %s' + } + ] + } + end + it 'returns an object with the expected valid attribute values', vcr: 'patching_spec/with_validation_context' do user = client.chat( - parameters: { - model: 'gpt-3.5-turbo', - messages: [{ role: 'user', content: 'Answer the question: %s with the text chunk: %s' }] - }, + parameters:, response_model: user_model, validation_context: { question: 'What is your name and age?', text_chunk: 'my name is Jason and I turned 25 years old yesterday' } @@ -118,17 +127,17 @@ def self.name end end - it 'raises an error when the response model is invalid', vcr: 'patching_spec/invalid_response' do - client = patched_client.new + let(:client) { patched_client.new } + let(:parameters) do + { + model: 'gpt-3.5-turbo', + messages: [{ role: 'user', content: 'Extract Jason is 25 years old' }] + } + end + it 'raises an error when the response model is invalid', vcr: 'patching_spec/invalid_response' do expect do - client.chat( - parameters: { - model: 'gpt-3.5-turbo', - messages: [{ role: 'user', content: 'Extract Jason is 25 years old' }] - }, - response_model: invalid_model - ) + client.chat(parameters:, response_model: invalid_model) end.to raise_error(Instructor::ValidationError) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 241d1d5..2210f40 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'instructor' -require 'pry' +require 'pry-byebug' require 'vcr' require 'rspec/json_expectations' From df43eaba595f59cae7caab345c60c5372491a3cf Mon Sep 17 00:00:00 2001 From: Sergio Bayona Date: Mon, 22 Apr 2024 13:16:32 -0500 Subject: [PATCH 10/13] default to some string --- spec/spec_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2210f40..b844c5f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,7 +9,7 @@ config.cassette_library_dir = 'spec/vcr_cassettes' config.hook_into :webmock config.configure_rspec_metadata! - config.filter_sensitive_data('') { ENV.fetch('OPENAI_API_KEY') } + config.filter_sensitive_data('') { ENV.fetch('OPENAI_API_KEY', 'XXXXX') } end RSpec.configure do |config| @@ -19,5 +19,5 @@ end OpenAI.configure do |config| - config.access_token = ENV.fetch('OPENAI_API_KEY') + config.access_token = ENV.fetch('OPENAI_API_KEY', 'XXXXX') end From 13600a302701237480c6b32c2f0aad8ca2d67dbe Mon Sep 17 00:00:00 2001 From: Sergio Bayona Date: Mon, 22 Apr 2024 14:05:03 -0500 Subject: [PATCH 11/13] gem release --- .github/workflows/gem-release.yml | 52 ++++++++----------------------- 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/.github/workflows/gem-release.yml b/.github/workflows/gem-release.yml index 92d96a2..52e4f72 100644 --- a/.github/workflows/gem-release.yml +++ b/.github/workflows/gem-release.yml @@ -1,48 +1,22 @@ -name: Ruby Gem +name: Publish Gem on: push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - + branches: + - "main" + tags: + - v* jobs: build: - name: Build + Publish runs-on: ubuntu-latest - permissions: - contents: read - packages: write steps: - - uses: actions/checkout@v3 - - name: Set up Ruby 3.3.0 - # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, - # change this to (see https://github.com/ruby/setup-ruby#versioning): - # uses: ruby/setup-ruby@v1 - uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 - with: - ruby-version: 3.3.0 - - - name: Publish to GPR - run: | - mkdir -p $HOME/.gem - touch $HOME/.gem/credentials - chmod 0600 $HOME/.gem/credentials - printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials - gem build *.gemspec - gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem - env: - GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}" - OWNER: ${{ github.repository_owner }} + - uses: actions/checkout@v3 - - name: Publish to RubyGems - run: | - mkdir -p $HOME/.gem - touch $HOME/.gem/credentials - chmod 0600 $HOME/.gem/credentials - printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials - gem build *.gemspec - gem push *.gem - env: - GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}" + - name: Release Gem + if: contains(github.ref, 'refs/tags/v') + uses: cadwallion/publish-rubygems-action@master + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}} + RELEASE_COMMAND: rake release \ No newline at end of file From c8fcdf88ab646cc67e6a5ba3e191faa4b57b829a Mon Sep 17 00:00:00 2001 From: "ellipsis-dev[bot]" <65095814+ellipsis-dev[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 03:26:37 +0000 Subject: [PATCH 12/13] Add ellipsis.yaml config file --- ellipsis.Dockerfile | 12 ++++++++++++ ellipsis.yaml | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 ellipsis.Dockerfile create mode 100644 ellipsis.yaml diff --git a/ellipsis.Dockerfile b/ellipsis.Dockerfile new file mode 100644 index 0000000..6878f5c --- /dev/null +++ b/ellipsis.Dockerfile @@ -0,0 +1,12 @@ +#====================================== +# This Dockerfile was generated by Ellipsis. +# It should be referenced from your `ellipsis.yaml` config file. +# For more details, see documentation: https://docs.ellipsis.dev +# Test with: $ docker build -f ellipsis.Dockerfile . +#====================================== + +FROM ubuntu:20.04 +RUN apt-get update && apt-get install -y git build-essential + +WORKDIR /app +COPY . . diff --git a/ellipsis.yaml b/ellipsis.yaml new file mode 100644 index 0000000..acfaf0e --- /dev/null +++ b/ellipsis.yaml @@ -0,0 +1,38 @@ +# See https://docs.ellipsis.dev for all available configurations. + +version: 1.1 +pr_review: + auto_review_enabled: true # enable auto-review of PRs + auto_summarize_pr: true # enable auto-summary of PRs + confidence_threshold: 0.5 # Threshold for how confident Ellipsis needs to be in order to leave a comment, in range [0.0-1.0] + rules: # customize behavior + - "Code should be DRY (Don't Repeat Yourself)" + - "There should no secrets or credentials in the code" + + # users can customize their own behavior + user_overrides: + # @hbrooks has disabled auto-summary and added a custom rule + - usernames: ["hbrooks"] + auto_summarize_pr: false + rules: + - "Code should be DRY (Don't Repeat Yourself)" + + +# Below is an example of how to configure Ellipsis to build and run your repo. +# Uncomment and replace with your own Dockerfile and commands. + +dockerfile: "ellipsis.Dockerfile" # this will be used to build your repo + +#======================= +# commands: +# - name: "build" +# description: "This command compiles the code and builds the project" +# command: "yarn build" +# return_output_on_success: false # If output isn't useful when the command succeeds +# auto_repair: true # Run this after every code change +# - name: "lint_fix" +# description: "Lints the code in fix mode, which will fix some errors." +# command: "yarn lint:fix" +# return_output_on_success: false +# auto_repair: true +#======================= From 8915e48559e3363cc3a361c1957e32b21489c687 Mon Sep 17 00:00:00 2001 From: Sergio Bayona Date: Wed, 24 Apr 2024 11:21:23 -0500 Subject: [PATCH 13/13] bump easy_talk version --- instructor-rb.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instructor-rb.gemspec b/instructor-rb.gemspec index 7a9261c..42adeee 100644 --- a/instructor-rb.gemspec +++ b/instructor-rb.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.add_dependency 'activesupport', '~> 7.0' - spec.add_dependency 'easy_talk', '~> 0.1.7' + spec.add_dependency 'easy_talk', '~> 0.1.8' spec.add_dependency 'ruby-openai', '~> 6' spec.add_development_dependency 'pry-byebug', '~> 3.10' spec.add_development_dependency 'rake', '~> 13.1'