Compare commits

...
Author SHA1 Message Date
Shivam MishraandGitHub 97987dcda2 Merge branch 'develop' into feature/cw-2248 2023-07-20 08:41:03 +05:30
Shivam MishraandGitHub bda0234618 refactor: don't save summary to the conversations model 2023-07-20 08:40:42 +05:30
Shivam MishraandGitHub 1083d0e974 chore: Update summary.txt 2023-07-20 08:37:37 +05:30
Shivam MishraandGitHub 9f879c440e chore: revert to old prompt response template 2023-07-20 08:36:44 +05:30
Shivam Mishra 811dc077fc fix: schema changes 2023-07-17 21:46:33 +05:30
Shivam Mishra 445d948af0 refactor: move event data fns to separate concern 2023-07-17 21:06:32 +05:30
Shivam Mishra d99eb145f2 refactor: update empty header method 2023-07-17 21:03:59 +05:30
Shivam Mishra 76820e2834 Merge branch 'feature/cw-2248' of github.com:chatwoot/chatwoot into feature/cw-2248 2023-07-17 17:00:28 +05:30
Shivam Mishra c862f6d419 test: remove_empty_headers 2023-07-17 16:59:52 +05:30
Shivam Mishra 16b7cabcc8 feat: strip data before sending 2023-07-17 16:51:19 +05:30
Shivam Mishra 95a547d717 feat: clean summary before rendering 2023-07-17 16:37:52 +05:30
Shivam Mishra e387f54130 feat: add method to clean empty headers 2023-07-17 16:37:26 +05:30
Shivam Mishra 1ccb4e1cc2 refactor: update format for summary 2023-07-17 16:24:36 +05:30
Shivam Mishra 23e840dabe feat: show rendered summary 2023-07-17 16:23:20 +05:30
Shivam MishraandGitHub 4b30abf44c Merge branch 'develop' into feature/cw-2248 2023-07-17 15:31:15 +05:30
Shivam Mishra b65a2f7ebd feat: update summary to be a text field 2023-07-17 14:00:36 +05:30
Shivam Mishra babe1028e2 feat: add summary to conversation json 2023-07-17 13:43:41 +05:30
Shivam Mishra 8b27123f8c feat: save conversation summary to db 2023-07-17 13:41:00 +05:30
Shivam Mishra 0b37e11ebf feat: add conversation summary fields 2023-07-17 13:38:09 +05:30
10 changed files with 115 additions and 46 deletions
@@ -0,0 +1,15 @@
module ConversationPresentationHelper
extend ActiveSupport::Concern
def push_event_data
Conversations::EventDataPresenter.new(self).push_data
end
def lock_event_data
Conversations::EventDataPresenter.new(self).lock_data
end
def webhook_data
Conversations::EventDataPresenter.new(self).push_data
end
end
+11 -12
View File
@@ -14,6 +14,8 @@
# priority :integer
# snoozed_until :datetime
# status :integer default("open"), not null
# summary :text
# summary_generated_at :datetime
# uuid :uuid not null
# waiting_since :datetime
# created_at :datetime not null
@@ -57,6 +59,7 @@ class Conversation < ApplicationRecord
include UrlHelper
include SortHandler
include ConversationMuteHelpers
include ConversationPresentationHelper
validates :account_id, presence: true
validates :inbox_id, presence: true
@@ -122,6 +125,14 @@ class Conversation < ApplicationRecord
last_message_in_messaging_window?(messaging_window)
end
def rendered_summary
return nil if summary.nil?
render_engine = ChatwootMarkdownRenderer.new(summary)
render_engine.remove_empty_headers
render_engine.render_message
end
def last_incoming_message
messages&.incoming&.last
end
@@ -173,18 +184,6 @@ class Conversation < ApplicationRecord
messages.incoming.unread_since(agent_last_seen_at)
end
def push_event_data
Conversations::EventDataPresenter.new(self).push_data
end
def lock_event_data
Conversations::EventDataPresenter.new(self).lock_data
end
def webhook_data
Conversations::EventDataPresenter.new(self).push_data
end
def notifiable_assignee_change?
return false unless saved_change_to_assignee_id?
return false if assignee_id.blank?
@@ -45,4 +45,6 @@ json.unread_count conversation.unread_incoming_messages.count
json.last_non_activity_message conversation.messages.non_activity_messages.first.try(:push_event_data)
json.last_activity_at conversation.last_activity_at.to_i
json.priority conversation.priority
json.summary conversation.rendered_summary
json.summary_generated_at conversation.summary_generated_at.to_i
json.waiting_since conversation.waiting_since.to_i.to_i
@@ -0,0 +1,6 @@
class AddSummaryFieldsToConversations < ActiveRecord::Migration[7.0]
def change
add_column :conversations, :summary, :text
add_column :conversations, :summary_generated_at, :datetime
end
end
+3 -1
View File
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_07_14_054138) do
ActiveRecord::Schema[7.0].define(version: 2023_07_17_075819) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
enable_extension "pg_trgm"
@@ -451,6 +451,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_07_14_054138) do
t.integer "priority"
t.bigint "sla_policy_id"
t.datetime "waiting_since"
t.text "summary"
t.datetime "summary_generated_at"
t.index ["account_id", "display_id"], name: "index_conversations_on_account_id_and_display_id", unique: true
t.index ["account_id", "id"], name: "index_conversations_on_id_and_account_id"
t.index ["account_id", "inbox_id", "status", "assignee_id"], name: "conv_acid_inbid_stat_asgnid_idx"
@@ -25,4 +25,4 @@ Reply in the user's language, as a markdown of the following format.
**Action Items**
**Follow-up Items**
**Follow-up Items**
+17
View File
@@ -8,6 +8,19 @@ class ChatwootMarkdownRenderer
render_as_html_safe(html)
end
def remove_empty_headers
doc = CommonMarker.render_doc(@content, :DEFAULT)
doc.walk do |node|
if node.type == :header
next_node = node.next
node.delete if node_removable?(node, next_node)
end
end
@content = doc.to_commonmark.strip
end
def render_article
markdown_renderer = CustomMarkdownRenderer.new
doc = CommonMarker.render_doc(@content, :DEFAULT)
@@ -18,6 +31,10 @@ class ChatwootMarkdownRenderer
private
def node_removable?(node, next_node)
next_node.nil? || (next_node.type == :header && next_node.header_level <= node.header_level)
end
def render_as_html_safe(html)
# rubocop:disable Rails/OutputSafety
html.html_safe
@@ -69,16 +69,6 @@ class Integrations::Openai::ProcessorService < Integrations::OpenaiBaseService
build_api_call_body("#{AGENT_INSTRUCTION} Please simplify the following response. #{LANGUAGE_INSTRUCTION}")
end
def build_api_call_body(system_content, user_content = event['data']['content'])
{
model: GPT_MODEL,
messages: [
{ role: 'system', content: system_content },
{ role: 'user', content: user_content }
]
}.to_json
end
def conversation_messages(in_array_format: false)
conversation = find_conversation
messages = init_messages_body(in_array_format)
+10
View File
@@ -24,6 +24,16 @@ class Integrations::OpenaiBaseService
private
def build_api_call_body(system_content, user_content = event['data']['content'])
{
model: GPT_MODEL,
messages: [
{ role: 'system', content: system_content },
{ role: 'user', content: user_content }
]
}.to_json
end
def event_name
event['name']
end
+50 -22
View File
@@ -7,38 +7,66 @@ RSpec.describe ChatwootMarkdownRenderer do
let(:markdown_renderer) { instance_double(CustomMarkdownRenderer) }
let(:html_content) { '<p>This is a <em>test</em> content with <sup>markdown</sup></p>' }
before do
allow(CommonMarker).to receive(:render_doc).with(markdown_content, :DEFAULT).and_return(doc)
allow(CustomMarkdownRenderer).to receive(:new).and_return(markdown_renderer)
allow(markdown_renderer).to receive(:render).with(doc).and_return(html_content)
end
describe '#render_article' do
let(:rendered_content) { renderer.render_article }
it 'renders the markdown content to html' do
expect(rendered_content.to_s).to eq(html_content)
before do
allow(CommonMarker).to receive(:render_doc).with(markdown_content, :DEFAULT).and_return(doc)
allow(CustomMarkdownRenderer).to receive(:new).and_return(markdown_renderer)
allow(markdown_renderer).to receive(:render).with(doc).and_return(html_content)
end
it 'returns an html safe string' do
expect(rendered_content).to be_html_safe
context 'when rendering a article' do
let(:rendered_content) { renderer.render_article }
it 'renders the markdown content to html' do
expect(rendered_content.to_s).to eq(html_content)
end
it 'returns an html safe string' do
expect(rendered_content).to be_html_safe
end
end
context 'when rendering a message' do
let(:message_html_content) { '<p>This is a <em>test</em> content with ^markdown^</p>' }
let(:rendered_message) { renderer.render_message }
before do
allow(CommonMarker).to receive(:render_html).with(markdown_content).and_return(message_html_content)
end
it 'renders the markdown message to html' do
expect(rendered_message.to_s).to eq(message_html_content)
end
it 'returns an html safe string' do
expect(rendered_message).to be_html_safe
end
end
end
describe '#render_message' do
let(:message_html_content) { '<p>This is a <em>test</em> content with ^markdown^</p>' }
let(:rendered_message) { renderer.render_message }
before do
allow(CommonMarker).to receive(:render_html).with(markdown_content).and_return(message_html_content)
describe '#remove_empty_headers' do
it 'removes headers with no subheaders or content' do
payload = "# Empty Header\n\n# Header 1\n\nSome content\n\n# Header 2\n\nSome more content"
renderer = described_class.new(payload)
expect(renderer.remove_empty_headers).to eq("# Header 1\n\nSome content\n\n# Header 2\n\nSome more content")
end
it 'renders the markdown message to html' do
expect(rendered_message.to_s).to eq(message_html_content)
it 'does not remove headers with subheaders' do
payload = "# Header 1\n\n## Subheader\n\nSome content"
renderer = described_class.new(payload)
expect(renderer.remove_empty_headers).to eq(payload)
end
it 'returns an html safe string' do
expect(rendered_message).to be_html_safe
it 'does not remove headers with content' do
payload = "# Header 1\n\nSome content\n\n# Header 2\n\nSome more content"
renderer = described_class.new(payload)
expect(renderer.remove_empty_headers).to eq(payload)
end
it 'does not remove headers with subheaders and content' do
payload = "# Header 1\n\n## Subheader\n\nSome content\n\n# Header 2\n\nSome more content"
renderer = described_class.new(payload)
expect(renderer.remove_empty_headers).to eq(payload)
end
end
end