Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
550bea0
commit 10dd0ba
Showing
13 changed files
with
256 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class SentimentColumnToMessages < ActiveRecord::Migration[7.0] | ||
def change | ||
add_column :messages, :sentiment, :jsonb, default: {} | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
class Enterprise::SentimentAnalysisJob < ApplicationJob | ||
queue_as :default | ||
|
||
def perform(message) | ||
return if message.account.locale != 'en' | ||
return if valid_incoming_message?(message) | ||
|
||
save_message_sentiment(message) | ||
rescue StandardError => e | ||
Rails.logger.error("Sentiment Analysis Error for message #{message.id}: #{e}") | ||
ChatwootExceptionTracker.new(e, account: message.account).capture_exception | ||
end | ||
|
||
def save_message_sentiment(message) | ||
# We are truncating the data here to avoind the OnnxRuntime::Error | ||
# Indices element out of data bounds, idx=512 must be within the inclusive range [-512,511] | ||
# While gathering the maningfull node the Array/tensor index is going out of bound | ||
|
||
text = message.content&.truncate(2900) | ||
sentiment = model.predict(text) | ||
message.sentiment = sentiment.merge(value: label_val(sentiment)) | ||
|
||
message.save! | ||
end | ||
|
||
# Model initializes OnnxRuntime::Model, with given file for inference session and to create the tensor | ||
def model | ||
model_path = ENV.fetch('SENTIMENT_FILE_PATH', nil) | ||
Informers::SentimentAnalysis.new(model_path) if model_path.present? | ||
end | ||
|
||
def label_val(sentiment) | ||
sentiment[:label] == 'positive' ? 1 : -1 | ||
end | ||
|
||
def valid_incoming_message?(message) | ||
!message.incoming? || message.private? | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module Enterprise::Message | ||
def update_message_sentiments | ||
::Enterprise::SentimentAnalysisJob.perform_later(self) | ||
end | ||
end |
45 changes: 45 additions & 0 deletions
45
enterprise/app/models/enterprise/sentiment_analysis_helper.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
module Enterprise::SentimentAnalysisHelper | ||
extend ActiveSupport::Concern | ||
|
||
included do | ||
def opening_sentiments | ||
records = incoming_messages.first(average_message_count) | ||
average_sentiment(records) | ||
end | ||
|
||
def closing_sentiments | ||
return unless resolved? | ||
|
||
records = incoming_messages.last(average_message_count) | ||
average_sentiment(records) | ||
end | ||
|
||
def average_sentiment(records) | ||
{ | ||
label: average_sentiment_label(records), | ||
score: average_sentiment_score(records) | ||
} | ||
end | ||
|
||
private | ||
|
||
def average_sentiment_label(records) | ||
value = records.pluck(:sentiment).sum { |a| a['value'].to_i } | ||
value.negative? ? 'negative' : 'positive' | ||
end | ||
|
||
def average_sentiment_score(records) | ||
total = records.pluck(:sentiment).sum { |a| a['score'].to_f } | ||
total / average_message_count | ||
end | ||
|
||
def average_message_count | ||
# incoming_messages.count >= 10 ? 5 : ((incoming_messages.count / 2) - 1) | ||
5 | ||
end | ||
|
||
def incoming_messages | ||
messages.incoming.where(private: false) | ||
end | ||
end | ||
end |
82 changes: 82 additions & 0 deletions
82
spec/enterprise/jobs/enterprise/sentiment_analysis_job_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
require 'rails_helper' | ||
|
||
RSpec.describe Enterprise::SentimentAnalysisJob do | ||
context 'when account locale set to english language' do | ||
let(:account) { create(:account, locale: 'en') } | ||
let(:message) { build(:message, content_type: nil, account: account) } | ||
|
||
context 'when update the message sentiments' do | ||
let(:model_path) { 'sentiment-analysis.onnx' } | ||
let(:model) { double } | ||
|
||
before do | ||
allow(Informers::SentimentAnalysis).to receive(:new).with(model_path).and_return(model) | ||
allow(model).to receive(:predict).and_return({ label: 'positive', score: '0.6' }) | ||
end | ||
|
||
it 'with incoming message' do | ||
with_modified_env SENTIMENT_FILE_PATH: 'sentiment-analysis.onnx' do | ||
message.update(message_type: :incoming) | ||
|
||
described_class.perform_now(message) | ||
|
||
expect(message.sentiment).not_to be_empty | ||
end | ||
end | ||
|
||
it 'update sentiment label for positive message' do | ||
with_modified_env SENTIMENT_FILE_PATH: 'sentiment-analysis.onnx' do | ||
message.update(message_type: :incoming, content: 'I like your product') | ||
|
||
described_class.perform_now(message) | ||
|
||
expect(message.sentiment).not_to be_empty | ||
expect(message.sentiment['label']).to eq('positive') | ||
expect(message.sentiment['value']).to eq(1) | ||
end | ||
end | ||
|
||
it 'update sentiment label for negative message' do | ||
with_modified_env SENTIMENT_FILE_PATH: 'sentiment-analysis.onnx' do | ||
message.update(message_type: :incoming, content: 'I did not like your product') | ||
allow(model).to receive(:predict).and_return({ label: 'negative', score: '0.6' }) | ||
|
||
described_class.perform_now(message) | ||
|
||
expect(message.sentiment).not_to be_empty | ||
expect(message.sentiment['label']).to eq('negative') | ||
expect(message.sentiment['value']).to eq(-1) | ||
end | ||
end | ||
end | ||
|
||
context 'when does not update the message sentiments' do | ||
it 'with outgoing message' do | ||
message.update(message_type: :outgoing) | ||
|
||
described_class.perform_now(message) | ||
|
||
expect(message.sentiment).to be_empty | ||
end | ||
|
||
it 'with private message' do | ||
message.update(private: true) | ||
|
||
described_class.perform_now(message) | ||
|
||
expect(message.sentiment).to be_empty | ||
end | ||
end | ||
end | ||
|
||
context 'when account locale is not set to english language' do | ||
let(:account) { create(:account, locale: 'es') } | ||
let(:message) { build(:message, content_type: nil, account: account) } | ||
|
||
it 'does not update the message sentiments' do | ||
described_class.perform_now(message) | ||
|
||
expect(message.sentiment).to be_empty | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'rails_helper' | ||
require Rails.root.join 'spec/models/concerns/liquidable_shared.rb' | ||
|
||
RSpec.describe Message do | ||
context 'with sentiment analysis' do | ||
let(:message) { build(:message, message_type: :incoming, content_type: nil, account: create(:account)) } | ||
|
||
it 'calls SentimentAnalysisJob' do | ||
allow(Enterprise::SentimentAnalysisJob).to receive(:perform_later).and_return(:perform_later).with(message) | ||
|
||
message.save! | ||
|
||
expect(Enterprise::SentimentAnalysisJob).to have_received(:perform_later) | ||
end | ||
end | ||
end |