+
diff --git a/app/javascript/superadmin_pages/components/playground/assets/typing.gif b/app/javascript/superadmin_pages/components/playground/assets/typing.gif
new file mode 100644
index 000000000..b288d2559
Binary files /dev/null and b/app/javascript/superadmin_pages/components/playground/assets/typing.gif differ
diff --git a/app/javascript/superadmin_pages/views/playground/Index.vue b/app/javascript/superadmin_pages/views/playground/Index.vue
new file mode 100644
index 000000000..136a2b481
--- /dev/null
+++ b/app/javascript/superadmin_pages/views/playground/Index.vue
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/views/super_admin/accounts/show.html.erb b/app/views/super_admin/accounts/show.html.erb
index fb8947f69..04d92c9d9 100644
--- a/app/views/super_admin/accounts/show.html.erb
+++ b/app/views/super_admin/accounts/show.html.erb
@@ -25,7 +25,7 @@ as well as a link to its edit page.
<%= link_to(
- t("administrate.actions.edit_resource", name: page.page_title),
+ "Edit",
[:edit, namespace, page.resource],
class: "button",
) if accessible_action?(page.resource, :edit) %>
diff --git a/app/views/super_admin/application/show.html.erb b/app/views/super_admin/application/show.html.erb
new file mode 100644
index 000000000..be51219da
--- /dev/null
+++ b/app/views/super_admin/application/show.html.erb
@@ -0,0 +1,65 @@
+<%#
+# Show
+
+This view is the template for the show page.
+It renders the attributes of a resource,
+as well as a link to its edit page.
+
+## Local variables:
+
+- `page`:
+ An instance of [Administrate::Page::Show][1].
+ Contains methods for accessing the resource to be displayed on the page,
+ as well as helpers for describing how each attribute of the resource
+ should be displayed.
+
+[1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Show
+%>
+
+<% content_for(:title) { t("administrate.actions.show_resource", name: page.page_title) } %>
+
+
+
+ <% page.attributes.each do |title, attributes| %>
+
+ <% end %>
+
+
diff --git a/app/views/super_admin/users/show.html.erb b/app/views/super_admin/users/show.html.erb
index a5bc2bf39..622f95724 100644
--- a/app/views/super_admin/users/show.html.erb
+++ b/app/views/super_admin/users/show.html.erb
@@ -25,10 +25,10 @@ as well as a link to its edit page.
diff --git a/config/routes.rb b/config/routes.rb
index 1ee74145f..36b7f46a0 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -453,7 +453,10 @@ Rails.application.routes.draw do
end
resources :access_tokens, only: [:index, :show]
- resources :response_sources, only: [:index, :show, :new, :create, :edit, :update, :destroy]
+ resources :response_sources, only: [:index, :show, :new, :create, :edit, :update, :destroy] do
+ get :chat, on: :member
+ post :chat, on: :member, action: :process_chat
+ end
resources :response_documents, only: [:index, :show, :new, :create, :edit, :update, :destroy]
resources :responses, only: [:index, :show, :new, :create, :edit, :update, :destroy]
resources :installation_configs, only: [:index, :new, :create, :show, :edit, :update]
diff --git a/enterprise/app/controllers/super_admin/enterprise_base_controller.rb b/enterprise/app/controllers/super_admin/enterprise_base_controller.rb
new file mode 100644
index 000000000..b2108332a
--- /dev/null
+++ b/enterprise/app/controllers/super_admin/enterprise_base_controller.rb
@@ -0,0 +1,8 @@
+class SuperAdmin::EnterpriseBaseController < SuperAdmin::ApplicationController
+ before_action :prepend_view_paths
+
+ # Prepend the view path to the enterprise/app/views won't be available by default
+ def prepend_view_paths
+ prepend_view_path 'enterprise/app/views/'
+ end
+end
diff --git a/enterprise/app/controllers/super_admin/response_documents_controller.rb b/enterprise/app/controllers/super_admin/response_documents_controller.rb
index f1317785a..3702261cf 100644
--- a/enterprise/app/controllers/super_admin/response_documents_controller.rb
+++ b/enterprise/app/controllers/super_admin/response_documents_controller.rb
@@ -1,4 +1,4 @@
-class SuperAdmin::ResponseDocumentsController < SuperAdmin::ApplicationController
+class SuperAdmin::ResponseDocumentsController < SuperAdmin::EnterpriseBaseController
# Overwrite any of the RESTful controller actions to implement custom behavior
# For example, you may want to send an email after a foo is updated.
#
diff --git a/enterprise/app/controllers/super_admin/response_sources_controller.rb b/enterprise/app/controllers/super_admin/response_sources_controller.rb
index a0e939913..045dd5958 100644
--- a/enterprise/app/controllers/super_admin/response_sources_controller.rb
+++ b/enterprise/app/controllers/super_admin/response_sources_controller.rb
@@ -1,4 +1,4 @@
-class SuperAdmin::ResponseSourcesController < SuperAdmin::ApplicationController
+class SuperAdmin::ResponseSourcesController < SuperAdmin::EnterpriseBaseController
# Overwrite any of the RESTful controller actions to implement custom behavior
# For example, you may want to send an email after a foo is updated.
#
@@ -41,4 +41,36 @@ class SuperAdmin::ResponseSourcesController < SuperAdmin::ApplicationController
# See https://administrate-demo.herokuapp.com/customizing_controller_actions
# for more information
+
+ before_action :set_response_source, only: %i[chat process_chat]
+
+ def chat; end
+
+ def process_chat
+ previous_messages = []
+ get_previous_messages(previous_messages)
+ robin_response = ChatGpt.new(
+ Enterprise::MessageTemplates::ResponseBotService.response_sections(params[:message], @response_source)
+ ).generate_response(
+ params[:message], previous_messages
+ )
+ message_content = robin_response['response']
+ if robin_response['context_ids'].present?
+ message_content += Enterprise::MessageTemplates::ResponseBotService.generate_sources_section(robin_response['context_ids'])
+ end
+ render json: { message: message_content }
+ end
+
+ private
+
+ def get_previous_messages(previous_messages)
+ params[:previous_messages].each do |message|
+ role = message['type'] == 'user' ? 'user' : 'system'
+ previous_messages << { content: message['message'], role: role }
+ end
+ end
+
+ def set_response_source
+ @response_source = requested_resource
+ end
end
diff --git a/enterprise/app/controllers/super_admin/responses_controller.rb b/enterprise/app/controllers/super_admin/responses_controller.rb
index 7566c8df3..4eb5db097 100644
--- a/enterprise/app/controllers/super_admin/responses_controller.rb
+++ b/enterprise/app/controllers/super_admin/responses_controller.rb
@@ -1,4 +1,4 @@
-class SuperAdmin::ResponsesController < SuperAdmin::ApplicationController
+class SuperAdmin::ResponsesController < SuperAdmin::EnterpriseBaseController
# Overwrite any of the RESTful controller actions to implement custom behavior
# For example, you may want to send an email after a foo is updated.
#
diff --git a/enterprise/app/jobs/response_builder_job.rb b/enterprise/app/jobs/response_builder_job.rb
index f30f97d4b..536cdef5a 100644
--- a/enterprise/app/jobs/response_builder_job.rb
+++ b/enterprise/app/jobs/response_builder_job.rb
@@ -17,6 +17,7 @@ class ResponseBuilderJob < ApplicationJob
def prepare_data(response_document)
{
model: 'gpt-3.5-turbo',
+ response_format: { type: 'json_object' },
messages: [
{
role: 'system',
@@ -32,16 +33,15 @@ class ResponseBuilderJob < ApplicationJob
def system_message_content
<<~SYSTEM_MESSAGE_CONTENT
- You are a content writer looking to convert user content into short FAQs which can be added to your website's helper centre.
- Format the webpage content provided in the message to FAQ format like the following example.#{' '}
- Ensure that you only generate faqs from the information provider in the message.#{' '}
- Ensure that output is always valid json.#{' '}
- If no match is available, return an empty JSON.
- ```
- [ { "question": "What is the pricing?",
- "answer" : " There are different pricing tiers available."
- }]
- ```
+ You are a content writer looking to convert user content into short FAQs which can be added to your website's helper centre.
+ Format the webpage content provided in the message to FAQ format mentioned below in the json
+ Ensure that you only generate faqs from the information provider in the message.
+ Ensure that output is always valid json.
+ If no match is available, return an empty JSON.
+
+ ```json
+ {faqs: [{question: '', answer: ''}]
+ ```
SYSTEM_MESSAGE_CONTENT
end
@@ -67,7 +67,7 @@ class ResponseBuilderJob < ApplicationJob
return if content.nil?
- faqs = JSON.parse(content.strip)
+ faqs = JSON.parse(content.strip).fetch('faqs', [])
faqs.each do |faq|
response_document.responses.create!(
diff --git a/enterprise/app/models/response_source.rb b/enterprise/app/models/response_source.rb
index cc5890a19..cbbd26289 100644
--- a/enterprise/app/models/response_source.rb
+++ b/enterprise/app/models/response_source.rb
@@ -25,4 +25,9 @@ class ResponseSource < ApplicationRecord
has_many :responses, dependent: :destroy_async
accepts_nested_attributes_for :response_documents
+
+ def get_responses(query)
+ embedding = Openai::EmbeddingsService.new.get_embedding(query)
+ responses.active.nearest_neighbors(:embedding, embedding, distance: 'cosine').first(5)
+ end
end
diff --git a/enterprise/app/services/enterprise/message_templates/response_bot_service.rb b/enterprise/app/services/enterprise/message_templates/response_bot_service.rb
index 214b33788..9df63f7e4 100644
--- a/enterprise/app/services/enterprise/message_templates/response_bot_service.rb
+++ b/enterprise/app/services/enterprise/message_templates/response_bot_service.rb
@@ -1,6 +1,39 @@
class Enterprise::MessageTemplates::ResponseBotService
pattr_initialize [:conversation!]
+ def self.generate_sources_section(article_ids)
+ sources_content = ''
+
+ articles_hash = get_article_hash(article_ids.uniq)
+
+ articles_hash.first(3).each do |article_hash|
+ sources_content += " - [#{article_hash[:response].question}](#{article_hash[:response_document].document_link}) \n"
+ end
+ sources_content = "\n \n \n **Sources** \n#{sources_content}" if sources_content.present?
+ sources_content
+ end
+
+ def self.get_article_hash(article_ids)
+ seen_documents = Set.new
+ article_ids.uniq.filter_map do |article_id|
+ response = Response.find(article_id)
+ response_document = response.response_document
+ next if response_document.blank? || seen_documents.include?(response_document)
+
+ seen_documents << response_document
+ { response: response, response_document: response_document }
+ end
+ end
+
+ def self.response_sections(content, response_source)
+ sections = ''
+
+ response_source.get_responses(content).each do |response|
+ sections += "{context_id: #{response.id}, context: #{response.question} ? #{response.answer}},"
+ end
+ sections
+ end
+
def perform
ActiveRecord::Base.transaction do
@response = get_response(conversation.messages.incoming.last.content)
@@ -12,15 +45,6 @@ class Enterprise::MessageTemplates::ResponseBotService
true
end
- def response_sections(content)
- sections = ''
-
- inbox.get_responses(content).each do |response|
- sections += "{context_id: #{response.id}, context: #{response.question} ? #{response.answer}},"
- end
- sections
- end
-
private
delegate :contact, :account, :inbox, to: :conversation
@@ -28,7 +52,7 @@ class Enterprise::MessageTemplates::ResponseBotService
def get_response(content)
previous_messages = []
get_previous_messages(previous_messages)
- ChatGpt.new(response_sections(content)).generate_response('', previous_messages)
+ ChatGpt.new(self.class.response_sections(content, inbox)).generate_response('', previous_messages)
end
def get_previous_messages(previous_messages)
@@ -63,24 +87,11 @@ class Enterprise::MessageTemplates::ResponseBotService
def create_messages
message_content = @response['response']
- message_content += generate_sources_section if @response['context_ids'].present?
+ message_content += self.class.generate_sources_section(@response['context_ids']) if @response['context_ids'].present?
create_outgoing_message(message_content)
end
- def generate_sources_section
- article_ids = @response['context_ids']
- sources_content = ''
-
- articles_hash = get_article_hash(article_ids.uniq)
-
- articles_hash.first(3).each do |article_hash|
- sources_content += " - [#{article_hash[:response].question}](#{article_hash[:response_document].document_link}) \n"
- end
- sources_content = "\n \n \n **Sources** \n#{sources_content}" if sources_content.present?
- sources_content
- end
-
def create_outgoing_message(message_content)
conversation.messages.create!(
{
@@ -91,16 +102,4 @@ class Enterprise::MessageTemplates::ResponseBotService
}
)
end
-
- def get_article_hash(article_ids)
- seen_documents = Set.new
- article_ids.uniq.filter_map do |article_id|
- response = Response.find(article_id)
- response_document = response.response_document
- next if response_document.blank? || seen_documents.include?(response_document)
-
- seen_documents << response_document
- { response: response, response_document: response_document }
- end
- end
end
diff --git a/enterprise/app/views/super_admin/response_sources/chat.html.erb b/enterprise/app/views/super_admin/response_sources/chat.html.erb
new file mode 100644
index 000000000..ea2147ea4
--- /dev/null
+++ b/enterprise/app/views/super_admin/response_sources/chat.html.erb
@@ -0,0 +1,5 @@
+<% content_for :title, "Robin AI playground: #{@response_source.name}" %>
+<%= render_vue_component('PlaygroundIndex', {
+ responseSourceName: @response_source.name,
+ responseSourcePath: super_admin_response_source_path(@response_source)
+}) %>
diff --git a/enterprise/app/views/super_admin/response_sources/show.html.erb b/enterprise/app/views/super_admin/response_sources/show.html.erb
new file mode 100644
index 000000000..60cbda605
--- /dev/null
+++ b/enterprise/app/views/super_admin/response_sources/show.html.erb
@@ -0,0 +1,71 @@
+<%#
+# Show
+
+This view is the template for the show page.
+It renders the attributes of a resource,
+as well as a link to its edit page.
+
+## Local variables:
+
+- `page`:
+ An instance of [Administrate::Page::Show][1].
+ Contains methods for accessing the resource to be displayed on the page,
+ as well as helpers for describing how each attribute of the resource
+ should be displayed.
+
+[1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Show
+%>
+
+<% content_for(:title) { t("administrate.actions.show_resource", name: page.page_title) } %>
+
+
+