From a457802a96cd5b217143372d24b489f5f0eff7c7 Mon Sep 17 00:00:00 2001 From: BlazingRockStorm Date: Tue, 19 Nov 2024 10:20:03 +0900 Subject: [PATCH 1/5] change model into provider --- README.md | 6 +++--- lib/sentiment_ai.rb | 4 ++-- spec/ai_models/gemini_pro_spec.rb | 6 +++--- spec/ai_models/openai_spec.rb | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ee2b573..322c470 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ require "sentiment_ai" Then use it like this: ```ruby -sentiment = SentimentAI.new(YOUR_MODEL,YOUR_API_KEY) +sentiment = SentimentAI.new(YOUR_PROVIDER,YOUR_API_KEY) ``` For example: @@ -38,7 +38,7 @@ sentiment = SentimentAI.new(:open_ai, OPEN_AI_KEY) For the current version, the gem supports only OpenAI and Google Gemini. -After calling the model, use: +After calling the provider, use: ```ruby sentiment.analyze_sentence("I Love Ruby") # => { :sentence => "I Love Ruby", :sentiment => "positive" } @@ -71,7 +71,7 @@ sentiment = SentimentAI.new(:open_ai, OPEN_AI_KEY, :ja) sentiment.analyze_sentence("Rubyは世界一プログラミング言語") # => { :sentence => "Rubyは世界一プログラミング言語", :sentiment => "肯定的" } ``` -### Supported GenAI models +### Supported GenAI providers | Language | Code | |----------|------| diff --git a/lib/sentiment_ai.rb b/lib/sentiment_ai.rb index 6fcc8b4..66f8751 100644 --- a/lib/sentiment_ai.rb +++ b/lib/sentiment_ai.rb @@ -19,9 +19,9 @@ def self.new(*args) end class Base - def initialize(model, api_key, language = :en) + def initialize(provider, api_key, language = :en) I18n.locale = language - @generative_ai = case model + @generative_ai = case provider when :open_ai Core::OpenAIDriver.new(api_key) when :gemini_ai_pro diff --git a/spec/ai_models/gemini_pro_spec.rb b/spec/ai_models/gemini_pro_spec.rb index 8d25353..06b25b7 100644 --- a/spec/ai_models/gemini_pro_spec.rb +++ b/spec/ai_models/gemini_pro_spec.rb @@ -3,12 +3,12 @@ require 'spec_helper' require 'dotenv/load' -RSpec.describe 'Using Gemini Pro model' do +RSpec.describe 'Using Gemini Pro provider' do let(:sentiment) { SentimentAI.new(:gemini_ai_pro, ENV['GEMINI_API']) } let(:japanese_sentiment) { SentimentAI.new(:gemini_ai_pro, ENV['GEMINI_API'], :ja) } - describe 'new model behaviours' do - it 'model being called correctly' do + describe 'new provider behaviours' do + it 'provider being called correctly' do expect(sentiment).to be_truthy end end diff --git a/spec/ai_models/openai_spec.rb b/spec/ai_models/openai_spec.rb index a83f554..08f3252 100644 --- a/spec/ai_models/openai_spec.rb +++ b/spec/ai_models/openai_spec.rb @@ -3,12 +3,12 @@ require 'spec_helper' require 'dotenv/load' -RSpec.describe 'Using OpenAI model' do +RSpec.describe 'Using OpenAI provider' do let(:sentiment) { SentimentAI.new(:open_ai, ENV['OPENAI_KEY']) } let(:japanese_sentiment) { SentimentAI.new(:gemini_ai_pro, ENV['OPENAI_KEY'], :ja) } - describe 'new model behaviours' do - it 'model being called correctly' do + describe 'new provider behaviours' do + it 'provider being called correctly' do expect(sentiment).to be_truthy end end From 99e4dd11dda68aea4bf17232803be3569d2e42a9 Mon Sep 17 00:00:00 2001 From: BlazingRockStorm Date: Thu, 21 Nov 2024 01:07:13 +0900 Subject: [PATCH 2/5] add code and tests --- README.md | 6 ++++++ config/locales/en.yml | 3 ++- config/locales/ja.yml | 3 ++- config/locales/vi.yml | 3 ++- lib/sentiment_ai.rb | 5 +++++ lib/sentiment_ai/core/gemini_driver.rb | 10 ++++++++++ lib/sentiment_ai/core/openai_driver.rb | 15 +++++++++++++++ spec/ai_models/gemini_pro_spec.rb | 8 ++++++++ spec/ai_models/openai_spec.rb | 8 ++++++++ 9 files changed, 58 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 322c470..bf1b0cf 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,12 @@ sentiment = SentimentAI.new(:open_ai, OPEN_AI_KEY, :ja) sentiment.analyze_sentence("Rubyは世界一プログラミング言語") # => { :sentence => "Rubyは世界一プログラミング言語", :sentiment => "肯定的" } ``` +If you want to only choose positive, use: +```ruby +sentiment.positive_check("I Love Ruby") +# => { :sentence => "I Love Ruby", :positive => true } +# => { :sentence => "It was never love for me", :positive => false } +``` ### Supported GenAI providers | Language | Code | diff --git a/config/locales/en.yml b/config/locales/en.yml index 877d689..647372e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,3 +1,4 @@ en: prompt: - sentence: "Analyze the sentiment of the sentence given below.\n%{sentence}\nThe output should be in the format- value" \ No newline at end of file + sentence: "Analyze the sentiment of the sentence given below.\n%{sentence}\nThe output should be in the format- value" + positive_check: "Is the sentence given below positive?\n%{sentence}\nThe output should be true or false" \ No newline at end of file diff --git a/config/locales/ja.yml b/config/locales/ja.yml index e4f90f8..236f30a 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -1,3 +1,4 @@ ja: prompt: - sentence: "テキストを「中立」、「否定的」、または「肯定的」に分類してください。 テキスト:%{sentence}\n結果の形式- 値" \ No newline at end of file + sentence: "テキストを「中立」、「否定的」、または「肯定的」に分類してください。 テキスト:%{sentence}\n結果の形式- 値" + positive_check: "テキストは肯定的ですか?\n%{sentence}\n「はい」はtrue、 「いいえ」は false" \ No newline at end of file diff --git a/config/locales/vi.yml b/config/locales/vi.yml index a59238e..532a9fc 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -1,3 +1,4 @@ vi: prompt: - sentence: "Phân tích sắc thái của câu dưới đây là tích cực, tiêu cực hay trung lập.\n%{sentence}\nKết quả trả về dưới dạng- Gía trị" \ No newline at end of file + sentence: "Phân tích sắc thái của câu dưới đây là tích cực, tiêu cực hay trung lập.\n%{sentence}\nKết quả trả về dưới dạng- Gía trị" + positive_check: "Câu sau đây có tích cực hay không?\n%{sentence}\nkết quả trả về là true hoặc false" \ No newline at end of file diff --git a/lib/sentiment_ai.rb b/lib/sentiment_ai.rb index 66f8751..0bf5a42 100644 --- a/lib/sentiment_ai.rb +++ b/lib/sentiment_ai.rb @@ -36,6 +36,11 @@ def analyze_sentence(sentence) { sentence: sentence, sentiment: sentiment } end + def positive_check(sentence) + sentiment_bool = @generative_ai.positive_check(sentence) + { sentence: sentence, positive: sentiment_bool == 'true' } + end + def analyze_array(array) array.map { |sentence| analyze_sentence(sentence) } end diff --git a/lib/sentiment_ai/core/gemini_driver.rb b/lib/sentiment_ai/core/gemini_driver.rb index 9a326b0..7f8aa6b 100644 --- a/lib/sentiment_ai/core/gemini_driver.rb +++ b/lib/sentiment_ai/core/gemini_driver.rb @@ -28,6 +28,16 @@ def analyze_sentence(sentence) extract_candidates(response) end + def positive_check(sentence) + text_request = I18n.t('prompt.positive_check', sentence: sentence) + + response = @sentiment_ai.stream_generate_content({ + contents: { role: 'user', parts: { text: text_request } }, + generationConfig: { temperature: 0 } + }) + extract_candidates(response) + end + private def extract_candidates(candidates) diff --git a/lib/sentiment_ai/core/openai_driver.rb b/lib/sentiment_ai/core/openai_driver.rb index c11494e..12daa50 100644 --- a/lib/sentiment_ai/core/openai_driver.rb +++ b/lib/sentiment_ai/core/openai_driver.rb @@ -23,6 +23,21 @@ def analyze_sentence(sentence) } ) end + + def positive_check(sentence) + text_request = I18n.t('prompt.positive_check', sentence: sentence) + + @sentiment_ai.chat( + parameters: { + model: 'gpt-4o', + messages: [{ role: 'user', content: text_request }], + temperature: 0.7, + stream: proc do |chunk, _bytesize| + print chunk.dig('choices', 0, 'delta', 'content') + end + } + ) + end end end end diff --git a/spec/ai_models/gemini_pro_spec.rb b/spec/ai_models/gemini_pro_spec.rb index 06b25b7..2bc4d25 100644 --- a/spec/ai_models/gemini_pro_spec.rb +++ b/spec/ai_models/gemini_pro_spec.rb @@ -35,6 +35,14 @@ end end + describe '#positive_check' do + it 'return true or false' do + expect(sentiment.positive_check('Delicious food')).to eq({ sentence: 'Delicious food', + positive: true }) + expect(sentiment.positive_check('Too noisy!!!')).to eq({ sentence: 'Too noisy!!!', positive: false }) + end + end + describe '#analyze_array' do let(:array) { ['Delicious food', 'Too noisy!!!', "I really don't know how to feel about Pokemon"] } let(:result_array) do diff --git a/spec/ai_models/openai_spec.rb b/spec/ai_models/openai_spec.rb index 08f3252..5a52207 100644 --- a/spec/ai_models/openai_spec.rb +++ b/spec/ai_models/openai_spec.rb @@ -35,6 +35,14 @@ end end + describe '#positive_check' do + it 'return true or false' do + expect(sentiment.positive_check('Delicious food')).to eq({ sentence: 'Delicious food', + positive: true }) + expect(sentiment.positive_check('Too noisy!!!')).to eq({ sentence: 'Too noisy!!!', positive: false }) + end + end + describe '#analyze_array' do let(:array) { ['Delicious food', 'Too noisy!!!', "I really don't know how to feel about Pokemon"] } let(:result_array) do From df4106fafa57601e5bd3947f9c8a02381396cba5 Mon Sep 17 00:00:00 2001 From: BlazingRockStorm Date: Thu, 21 Nov 2024 20:47:32 +0900 Subject: [PATCH 3/5] add Anthropic provider and model claude --- Gemfile.lock | 5 ++ README.md | 1 + lib/sentiment_ai.rb | 3 ++ lib/sentiment_ai/core/anthropic_driver.rb | 42 ++++++++++++++++ sentiment-ai.gemspec | 1 + spec/ai_models/anthropic_spec.rb | 59 +++++++++++++++++++++++ spec/ai_models/gemini_pro_spec.rb | 4 +- spec/ai_models/openai_spec.rb | 4 +- 8 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 lib/sentiment_ai/core/anthropic_driver.rb create mode 100644 spec/ai_models/anthropic_spec.rb diff --git a/Gemfile.lock b/Gemfile.lock index e41d09c..9f3b3df 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,6 +8,10 @@ GEM specs: addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) + anthropic (0.3.2) + event_stream_parser (>= 0.3.0, < 2.0.0) + faraday (>= 1) + faraday-multipart (>= 1) base64 (0.2.0) bigdecimal (3.1.8) concurrent-ruby (1.3.4) @@ -98,6 +102,7 @@ PLATFORMS x86_64-darwin-22 DEPENDENCIES + anthropic (~> 0.3.2) csv (~> 3.3) dotenv gemini-ai (~> 4.2) diff --git a/README.md b/README.md index bf1b0cf..a376acd 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ sentiment.positive_check("I Love Ruby") |----------|------| | OpenAI(GPT) | `:open_ai` | | Google Gemini | `:gemini_ai_pro` | +| Anthropic | `:anthropic` | ### Supported languages diff --git a/lib/sentiment_ai.rb b/lib/sentiment_ai.rb index 0bf5a42..e72169f 100644 --- a/lib/sentiment_ai.rb +++ b/lib/sentiment_ai.rb @@ -3,6 +3,7 @@ require 'sentiment_ai/version' require 'sentiment_ai/core/gemini_driver' require 'sentiment_ai/core/openai_driver' +require 'sentiment_ai/core/anthropic_driver' require 'i18n' require 'csv' @@ -26,6 +27,8 @@ def initialize(provider, api_key, language = :en) Core::OpenAIDriver.new(api_key) when :gemini_ai_pro Core::GeminiDriver.new(api_key) + when :anthropic + Core::AnthropicDriver.new(api_key) else raise ArgumentError end diff --git a/lib/sentiment_ai/core/anthropic_driver.rb b/lib/sentiment_ai/core/anthropic_driver.rb new file mode 100644 index 0000000..8ec6318 --- /dev/null +++ b/lib/sentiment_ai/core/anthropic_driver.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'anthropic' + +module SentimentAI + module Core + class AnthropicDriver + def initialize(api_key) + @sentiment_ai = Anthropic::Client.new(access_token: api_key) + end + + def analyze_sentence(sentence) + text_request = I18n.t('prompt.sentence', sentence: sentence) + + @sentiment_ai.messages( + parameters: { + model: 'claude-3-haiku-20240307', + messages: [ + { 'role': 'user', 'content': text_request } + ], + max_tokens: 1000 + } + ) + end + + def positive_check(sentence) + text_request = I18n.t('prompt.positive_check', sentence: sentence) + + @sentiment_ai.messages( + parameters: { + model: 'claude-3-haiku-20240307', + system: "Respond only in English.", + messages: [ + { 'role': 'user', 'content': text_request } + ], + max_tokens: 1000 + } + ) + end + end + end +end diff --git a/sentiment-ai.gemspec b/sentiment-ai.gemspec index b016248..486fa2a 100644 --- a/sentiment-ai.gemspec +++ b/sentiment-ai.gemspec @@ -19,6 +19,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'csv', '~> 3.3' spec.add_development_dependency 'i18n', '~> 1.14' + spec.add_development_dependency 'anthropic', '~> 0.3.2' spec.add_development_dependency 'gemini-ai', '~> 4.2' spec.add_development_dependency 'ruby-openai', '~> 6.0' end diff --git a/spec/ai_models/anthropic_spec.rb b/spec/ai_models/anthropic_spec.rb new file mode 100644 index 0000000..c432870 --- /dev/null +++ b/spec/ai_models/anthropic_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'dotenv/load' + +RSpec.describe 'Using Anthropic provider' do + let(:sentiment) { SentimentAI.new(:anthropic, ENV['ANTHROPIC_KEY']) } + let(:japanese_sentiment) { SentimentAI.new(:anthropic, ENV['ANTHROPIC_KEY'], :ja) } + + describe 'new provider behaviours' do + it 'provider being called correctly' do + expect(sentiment).to be_truthy + end + end + + # describe 'analyze sentence in another language' do + # describe '#analyze_sentence' do + # it 'return the sentiment of the sentence' do + # expect(japanese_sentiment.analyze_sentence('うまい!')).to eq({ sentence: 'うまい!', sentiment: '肯定的' }) + # expect(japanese_sentiment.analyze_sentence('不愉快')).to eq({ sentence: '不愉快', sentiment: '否定的' }) + # expect(japanese_sentiment.analyze_sentence('休暇はまずまずでした。')).to eq({ sentence: '休暇はまずまずでした。', sentiment: '中立' }) + # end + # end + # end + + describe 'analyze feature' do + describe '#analyze_sentence' do + it 'return the sentiment of the sentence' do + expect(sentiment.analyze_sentence('Delicious food')).to eq({ sentence: 'Delicious food', + sentiment: 'positive' }) + expect(sentiment.analyze_sentence('Too noisy!!!')).to eq({ sentence: 'Too noisy!!!', sentiment: 'negative' }) + expect(sentiment.analyze_sentence("I really don't know how to feel about Pokemon")).to eq({ + sentence: "I really don't know how to feel about Pokemon", sentiment: 'neutral' + }) + end + end + + describe '#positive_check' do + it 'return true or false' do + expect(sentiment.positive_check('Delicious food')).to eq({ sentence: 'Delicious food', + positive: true }) + expect(sentiment.positive_check('Too noisy!!!')).to eq({ sentence: 'Too noisy!!!', positive: false }) + end + end + + describe '#analyze_array' do + let(:array) { ['Delicious food', 'Too noisy!!!', "I really don't know how to feel about Pokemon"] } + let(:result_array) do + [{ sentence: 'Delicious food', sentiment: 'positive' }, + { sentence: 'Too noisy!!!', sentiment: 'negative' }, + { sentence: "I really don't know how to feel about Pokemon", sentiment: 'neutral' }] + end + + it 'return the sentiments of all sentences in the array' do + expect(sentiment.analyze_array(array)).to eq(result_array) + end + end + end +end diff --git a/spec/ai_models/gemini_pro_spec.rb b/spec/ai_models/gemini_pro_spec.rb index 2bc4d25..5bacd32 100644 --- a/spec/ai_models/gemini_pro_spec.rb +++ b/spec/ai_models/gemini_pro_spec.rb @@ -38,11 +38,11 @@ describe '#positive_check' do it 'return true or false' do expect(sentiment.positive_check('Delicious food')).to eq({ sentence: 'Delicious food', - positive: true }) + positive: true }) expect(sentiment.positive_check('Too noisy!!!')).to eq({ sentence: 'Too noisy!!!', positive: false }) end end - + describe '#analyze_array' do let(:array) { ['Delicious food', 'Too noisy!!!', "I really don't know how to feel about Pokemon"] } let(:result_array) do diff --git a/spec/ai_models/openai_spec.rb b/spec/ai_models/openai_spec.rb index 5a52207..1231719 100644 --- a/spec/ai_models/openai_spec.rb +++ b/spec/ai_models/openai_spec.rb @@ -5,7 +5,7 @@ RSpec.describe 'Using OpenAI provider' do let(:sentiment) { SentimentAI.new(:open_ai, ENV['OPENAI_KEY']) } - let(:japanese_sentiment) { SentimentAI.new(:gemini_ai_pro, ENV['OPENAI_KEY'], :ja) } + let(:japanese_sentiment) { SentimentAI.new(:open_ai, ENV['OPENAI_KEY'], :ja) } describe 'new provider behaviours' do it 'provider being called correctly' do @@ -38,7 +38,7 @@ describe '#positive_check' do it 'return true or false' do expect(sentiment.positive_check('Delicious food')).to eq({ sentence: 'Delicious food', - positive: true }) + positive: true }) expect(sentiment.positive_check('Too noisy!!!')).to eq({ sentence: 'Too noisy!!!', positive: false }) end end From 1b91fb41e9ad1c88969647c648a83d5e25e34d6e Mon Sep 17 00:00:00 2001 From: BlazingRockStorm Date: Fri, 22 Nov 2024 15:33:58 +0900 Subject: [PATCH 4/5] fix in anthropic --- lib/sentiment_ai/core/anthropic_driver.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/sentiment_ai/core/anthropic_driver.rb b/lib/sentiment_ai/core/anthropic_driver.rb index 8ec6318..ad93de7 100644 --- a/lib/sentiment_ai/core/anthropic_driver.rb +++ b/lib/sentiment_ai/core/anthropic_driver.rb @@ -29,7 +29,6 @@ def positive_check(sentence) @sentiment_ai.messages( parameters: { model: 'claude-3-haiku-20240307', - system: "Respond only in English.", messages: [ { 'role': 'user', 'content': text_request } ], From 801f29264c530c97e17b6e3ec956bb7d07fc38cf Mon Sep 17 00:00:00 2001 From: BlazingRockStorm Date: Fri, 22 Nov 2024 15:36:46 +0900 Subject: [PATCH 5/5] bump version --- CHANGELOG.md | 5 +++++ Gemfile.lock | 2 +- lib/sentiment_ai/version.rb | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4159cf3..24c5b0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [0.1.0] - 2024-11-22 +All by [@BlazingRockStorm](https://github.com/BlazingRockStorm): +- Add `positive_check` +- Add Anthropic + ## [0.0.5] - 2024-11-13 All by [@BlazingRockStorm](https://github.com/BlazingRockStorm): - Rewrite the `analyze_sentence` method to have the output in form of Hash diff --git a/Gemfile.lock b/Gemfile.lock index 9f3b3df..1069792 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - sentiment-ai (0.0.5) + sentiment-ai (0.1.0) GEM remote: https://rubygems.org/ diff --git a/lib/sentiment_ai/version.rb b/lib/sentiment_ai/version.rb index aaafe65..ca55e1d 100644 --- a/lib/sentiment_ai/version.rb +++ b/lib/sentiment_ai/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module SentimentAI - VERSION = '0.0.5' + VERSION = '0.1.0' end