Restructure omni services and add Chatwoot research snapshot

This commit is contained in:
Ruslan Bakiev
2026-02-21 11:11:27 +07:00
parent edea7a0034
commit b73babbbf6
7732 changed files with 978203 additions and 32 deletions

View File

@@ -0,0 +1,44 @@
require 'rails_helper'
RSpec.describe LlmFormatter::ArticleLlmFormatter do
let(:account) { create(:account) }
let(:portal) { create(:portal, account: account) }
let(:category) { create(:category, slug: 'test_category', portal: portal, account: account) }
let(:author) { create(:user, account: account) }
let(:formatter) { described_class.new(article) }
describe '#format' do
context 'when article has all details' do
let(:article) do
create(:article,
slug: 'test_article',
portal: portal, category: category, author: author, views: 100, account: account)
end
it 'formats article details correctly' do
expected_output = <<~TEXT
Title: #{article.title}
ID: #{article.id}
Status: #{article.status}
Category: #{category.name}
Author: #{author.name}
Views: #{article.views}
Created At: #{article.created_at}
Updated At: #{article.updated_at}
Content:
#{article.content}
TEXT
expect(formatter.format).to eq(expected_output)
end
end
context 'when article has no category' do
let(:article) { create(:article, portal: portal, category: nil, author: author, account: account) }
it 'shows Uncategorized for category' do
expect(formatter.format).to include('Category: Uncategorized')
end
end
end
end

View File

@@ -0,0 +1,78 @@
require 'rails_helper'
RSpec.describe LlmFormatter::ContactLlmFormatter do
let(:account) { create(:account) }
let(:contact) { create(:contact, account: account, name: 'John Doe', email: 'john@example.com', phone_number: '+1234567890') }
let(:formatter) { described_class.new(contact) }
describe '#format' do
context 'when contact has no notes' do
it 'formats contact details correctly' do
expected_output = [
"Contact ID: ##{contact.id}",
'Contact Attributes:',
'Name: John Doe',
'Email: john@example.com',
'Phone: +1234567890',
'Location: ',
'Country Code: ',
'Contact Notes:',
'No notes for this contact'
].join("\n")
expect(formatter.format).to eq(expected_output)
end
end
context 'when contact has notes' do
before do
create(:note, account: account, contact: contact, content: 'First interaction')
create(:note, account: account, contact: contact, content: 'Follow up needed')
end
it 'includes notes in the output' do
expected_output = [
"Contact ID: ##{contact.id}",
'Contact Attributes:',
'Name: John Doe',
'Email: john@example.com',
'Phone: +1234567890',
'Location: ',
'Country Code: ',
'Contact Notes:',
' - First interaction',
' - Follow up needed'
].join("\n")
expect(formatter.format).to eq(expected_output)
end
end
context 'when contact has custom attributes' do
let!(:custom_attribute) do
create(:custom_attribute_definition, account: account, attribute_model: 'contact_attribute', attribute_display_name: 'Company')
end
before do
contact.update(custom_attributes: { custom_attribute.attribute_key => 'Acme Inc' })
end
it 'includes custom attributes in the output' do
expected_output = [
"Contact ID: ##{contact.id}",
'Contact Attributes:',
'Name: John Doe',
'Email: john@example.com',
'Phone: +1234567890',
'Location: ',
'Country Code: ',
'Company: Acme Inc',
'Contact Notes:',
'No notes for this contact'
].join("\n")
expect(formatter.format).to eq(expected_output)
end
end
end
end

View File

@@ -0,0 +1,99 @@
require 'rails_helper'
RSpec.describe LlmFormatter::ConversationLlmFormatter do
let(:account) { create(:account) }
let(:conversation) { create(:conversation, account: account) }
let(:formatter) { described_class.new(conversation) }
describe '#format' do
context 'when conversation has no messages' do
it 'returns basic conversation info with no messages' do
expected_output = [
"Conversation ID: ##{conversation.display_id}",
"Channel: #{conversation.inbox.channel.name}",
'Message History:',
'No messages in this conversation'
].join("\n")
expect(formatter.format).to eq(expected_output)
end
end
context 'when conversation has messages' do
it 'formats messages in chronological order with sender labels' do
create(
:message,
conversation: conversation,
message_type: 'incoming',
content: 'Hello, I need help'
)
create(
:message,
:bot_message,
conversation: conversation,
message_type: 'outgoing',
content: 'Thanks for reaching out, an agent will reach out to you soon'
)
create(
:message,
conversation: conversation,
message_type: 'outgoing',
content: 'How can I assist you today?'
)
expected_output = [
"Conversation ID: ##{conversation.display_id}",
"Channel: #{conversation.inbox.channel.name}",
'Message History:',
'User: Hello, I need help',
'Bot: Thanks for reaching out, an agent will reach out to you soon',
'Support Agent: How can I assist you today?',
''
].join("\n")
expect(formatter.format).to eq(expected_output)
end
end
context 'when include_contact_details is true' do
it 'includes contact details' do
expected_output = [
"Conversation ID: ##{conversation.display_id}",
"Channel: #{conversation.inbox.channel.name}",
'Message History:',
'No messages in this conversation',
"Contact Details: #{conversation.contact.to_llm_text}"
].join("\n")
expect(formatter.format(include_contact_details: true)).to eq(expected_output)
end
end
context 'when conversation has custom attributes' do
it 'includes formatted custom attributes in the output' do
create(
:custom_attribute_definition,
account: account,
attribute_display_name: 'Order ID',
attribute_key: 'order_id',
attribute_model: :conversation_attribute
)
conversation.update(custom_attributes: { 'order_id' => '12345' })
expected_output = [
"Conversation ID: ##{conversation.display_id}",
"Channel: #{conversation.inbox.channel.name}",
'Message History:',
'No messages in this conversation',
'Conversation Attributes:',
'Order ID: 12345'
].join("\n")
expect(formatter.format).to eq(expected_output)
end
end
end
end