From d0707433831f23af090a48b7d341c329d7fe4df3 Mon Sep 17 00:00:00 2001 From: Pranav Date: Tue, 14 Jan 2025 16:15:47 -0800 Subject: [PATCH] feat(ee): Add Captain features (#10665) Migration Guide: https://chwt.app/v4/migration This PR imports all the work related to Captain into the EE codebase. Captain represents the AI-based features in Chatwoot and includes the following key components: - Assistant: An assistant has a persona, the product it would be trained on. At the moment, the data at which it is trained is from websites. Future integrations on Notion documents, PDF etc. This PR enables connecting an assistant to an inbox. The assistant would run the conversation every time before transferring it to an agent. - Copilot for Agents: When an agent is supporting a customer, we will be able to offer additional help to lookup some data or fetch information from integrations etc via copilot. - Conversation FAQ generator: When a conversation is resolved, the Captain integration would identify questions which were not in the knowledge base. - CRM memory: Learns from the conversations and identifies important information about the contact. --------- Co-authored-by: Vishnu Narayanan Co-authored-by: Sojan Co-authored-by: iamsivin Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> --- .circleci/config.yml | 10 +- .codeclimate.yml | 1 + .github/workflows/run_foss_spec.yml | 8 +- .github/workflows/run_response_bot_spec.yml | 89 ------ Gemfile | 2 + Gemfile.lock | 6 + .../integrations/captain_controller.rb | 78 ----- .../api/v1/portals/articles_controller.rb | 5 +- app/dispatchers/async_dispatcher.rb | 2 + .../dashboard/api/captain/assistant.js | 19 ++ .../dashboard/api/captain/document.js | 19 ++ .../dashboard/api/captain/inboxes.js | 26 ++ .../dashboard/api/captain/response.js | 21 ++ .../dashboard/api/inbox/conversation.js | 4 + app/javascript/dashboard/api/integrations.js | 8 - .../Contacts/ContactsSidebar/ContactNotes.vue | 2 +- .../components/ContactNoteItem.vue | 4 +- .../components-next/captain/PageLayout.vue | 84 ++++++ .../captain/assistant/AssistantCard.story.vue | 85 ++++++ .../captain/assistant/AssistantCard.vue | 101 +++++++ .../captain/assistant/DocumentCard.story.vue | 171 +++++++++++ .../captain/assistant/DocumentCard.vue | 108 +++++++ .../captain/assistant/InboxCard.story.vue | 86 ++++++ .../captain/assistant/InboxCard.vue | 100 +++++++ .../captain/assistant/ResponseCard.story.vue | 157 ++++++++++ .../captain/assistant/ResponseCard.vue | 118 ++++++++ .../captain/pageComponents/DeleteDialog.vue | 56 ++++ .../assistant/AssistantForm.vue | 174 +++++++++++ .../assistant/CreateAssistantDialog.vue | 87 ++++++ .../document/CreateDocumentDialog.vue | 58 ++++ .../pageComponents/document/DocumentForm.vue | 114 ++++++++ .../document/RelatedResponses.vue | 68 +++++ .../inbox/ConnectInboxDialog.vue | 65 +++++ .../pageComponents/inbox/ConnectInboxForm.vue | 115 ++++++++ .../response/CreateResponseDialog.vue | 84 ++++++ .../pageComponents/response/ResponseForm.vue | 161 +++++++++++ .../components-next/sidebar/Sidebar.vue | 18 +- .../components/copilot/CopilotContainer.vue | 25 +- .../conversation/ConversationSidebar.vue | 18 +- .../i18n/locale/en/integrations.json | 164 +++++++++++ .../dashboard/i18n/locale/en/settings.json | 3 + .../dashboard/routes/dashboard/Captain.vue | 107 ------- .../dashboard/captain/assistants/Index.vue | 114 ++++++++ .../captain/assistants/inboxes/Index.vue | 128 +++++++++ .../dashboard/captain/captain.routes.js | 47 +++ .../dashboard/captain/documents/Index.vue | 124 ++++++++ .../dashboard/captain/responses/Index.vue | 113 ++++++++ .../routes/dashboard/dashboard.routes.js | 16 +- .../dashboard/store/captain/assistant.js | 7 + .../dashboard/store/captain/document.js | 7 + .../dashboard/store/captain/inboxes.js | 22 ++ .../dashboard/store/captain/response.js | 7 + .../dashboard/store/captain/storeFactory.js | 140 +++++++++ app/javascript/dashboard/store/index.js | 9 +- app/jobs/hook_job.rb | 8 - app/models/contact.rb | 1 + app/models/inbox.rb | 10 +- app/models/integrations/app.rb | 2 - app/models/integrations/hook.rb | 25 -- app/models/note.rb | 3 +- app/policies/inbox_policy.rb | 4 - .../llm_formatter/contact_llm_formatter.rb | 35 +++ .../super_admin/application/_icons.html.erb | 5 + .../application/_navigation.html.erb | 6 - config/application.rb | 1 + .../monkey_patches/schema_dumper.rb | 10 +- config/initializers/sidekiq.rb | 9 +- config/installation_config.yml | 18 +- config/integration/apps.yml | 264 ++++++++--------- config/locales/en.yml | 157 +++++----- config/routes.rb | 29 +- config/schedule.yml | 45 ++- .../20250104200055_create_captain_tables.rb | 90 ++++++ .../20250104210328_remove_robin_tables.rb | 10 + ...5001414_add_status_to_captain_documents.rb | 6 + ..._remove_not_null_from_captain_documents.rb | 5 + ...7030743_add_config_to_captain_assistant.rb | 5 + .../20250108031358_create_captain_inbox.rb | 11 + ...41_remove_index_from_captain_assistants.rb | 5 + db/schema.rb | 64 ++++- .../captain/assistant_responses_controller.rb | 68 +++++ .../accounts/captain/assistants_controller.rb | 39 +++ .../accounts/captain/documents_controller.rb | 58 ++++ .../v1/accounts/captain/inboxes_controller.rb | 39 +++ .../accounts/response_sources_controller.rb | 34 --- .../v1/accounts/conversations_controller.rb | 24 ++ .../api/v1/accounts/inboxes_controller.rb | 4 - .../super_admin/app_configs_controller.rb | 2 +- .../webhooks/firecrawl_controller.rb | 30 ++ .../response_sources_controller.rb | 76 ----- .../enterprise/async_dispatcher.rb | 7 + .../app/helpers/super_admin/features.yml | 7 + .../conversation/response_builder_job.rb | 90 ++++++ .../app/jobs/captain/documents/crawl_job.rb | 40 +++ .../captain/documents/response_builder_job.rb | 29 ++ .../jobs/captain/llm/update_embedding_job.rb | 8 + .../captain/tools/firecrawl_parser_job.rb | 20 ++ .../tools/simple_page_crawl_parser_job.rb | 21 ++ .../conversations_resolution_scheduler_job.rb | 22 +- .../app/jobs/response_bot/response_bot_job.rb | 7 - .../jobs/response_bot/response_builder_job.rb | 82 ------ .../response_document_content_job.rb | 10 - enterprise/app/models/article_embedding.rb | 10 +- enterprise/app/models/captain/assistant.rb | 37 +++ .../app/models/captain/assistant_response.rb | 57 ++++ enterprise/app/models/captain/document.rb | 62 ++++ enterprise/app/models/captain_inbox.rb | 22 ++ .../app/models/enterprise/concerns/account.rb | 10 +- .../app/models/enterprise/concerns/article.rb | 4 +- .../app/models/enterprise/concerns/inbox.rb | 12 +- enterprise/app/models/enterprise/inbox.rb | 11 +- .../app/models/inbox_response_source.rb | 21 -- enterprise/app/models/response.rb | 46 --- enterprise/app/models/response_document.rb | 46 --- enterprise/app/models/response_source.rb | 33 --- .../app/policies/captain/assistant_policy.rb | 21 ++ .../services/captain/copilot/chat_service.rb | 77 +++++ .../captain/llm/assistant_chat_service.rb | 101 +++++++ .../captain/llm/base_open_ai_service.rb | 12 + .../captain/llm/contact_attributes_service.rb | 57 ++++ .../captain/llm/contact_notes_service.rb | 58 ++++ .../captain/llm/conversation_faq_service.rb | 105 +++++++ .../services/captain/llm/embedding_service.rb | 20 ++ .../captain/llm/faq_generator_service.rb | 47 +++ .../captain/llm/system_prompts_service.rb | 98 +++++++ .../captain/tools/firecrawl_service.rb | 40 +++ .../tools/simple_page_crawl_service.rb | 38 +++ .../hook_execution_service.rb | 11 +- .../message_templates/response_bot_service.rb | 105 ------- .../app/services/features/base_service.rb | 7 - .../helpcenter_embedding_search_service.rb | 42 --- .../services/features/response_bot_service.rb | 95 ------ .../app/services/openai/embeddings_service.rb | 22 -- .../assistant_responses/create.json.jbuilder | 1 + .../assistant_responses/index.json.jbuilder | 10 + .../assistant_responses/show.json.jbuilder | 1 + .../assistant_responses/update.json.jbuilder | 1 + .../captain/assistants/create.json.jbuilder | 1 + .../captain/assistants/index.json.jbuilder | 10 + .../captain/assistants/show.json.jbuilder | 1 + .../captain/assistants/update.json.jbuilder | 1 + .../captain/documents/create.json.jbuilder | 1 + .../captain/documents/index.json.jbuilder | 10 + .../captain/documents/show.json.jbuilder | 1 + .../captain/inboxes/create.json.jbuilder | 1 + .../captain/inboxes/index.json.jbuilder | 10 + .../models/captain/_assistant.json.jbuilder | 7 + .../captain/_assistant_response.json.jbuilder | 16 ++ .../v1/models/captain/_document.json.jbuilder | 11 + enterprise/config/premium_features.yml | 1 + enterprise/lib/captain/agent.rb | 132 +++++++++ enterprise/lib/captain/llm_service.rb | 63 ++++ enterprise/lib/captain/tool.rb | 66 +++++ .../integrations/openai_processor_service.rb | 18 -- enterprise/listeners/captain_listener.rb | 11 + package.json | 1 - pnpm-lock.yaml | 268 ----------------- .../integrations/captain_controller_spec.rb | 123 -------- .../assistant_responses_controller_spec.rb | 223 ++++++++++++++ .../captain/assistants_controller_spec.rb | 178 ++++++++++++ .../captain/documents_controller_spec.rb | 271 ++++++++++++++++++ .../captain/inboxes_controller_spec.rb | 119 ++++++++ .../response_sources_controller_spec.rb | 133 --------- .../v1/accounts/inboxes_controller_spec.rb | 42 --- .../webooks/firecrawl_controller_spec.rb | 58 ++++ .../jobs/captain/documents/crawl_job_spec.rb | 69 +++++ .../documents/response_builder_job_spec.rb | 47 +++ .../tools/firecrawl_parser_job_spec.rb | 62 ++++ .../simple_page_crawl_parser_job_spec.rb | 97 +++++++ ...ersations_resolution_scheduler_job_spec.rb | 32 +-- .../llm/conversation_faq_service_spec.rb | 138 +++++++++ .../tools/simple_page_crawl_service_spec.rb | 128 +++++++++ .../response_bot_service_spec.rb | 108 ------- .../reconcile_plan_config_service_spec.rb | 20 +- .../services/page_crawler_service_spec.rb | 57 ---- spec/factories/captain/assistant.rb | 7 + spec/factories/captain/assistant_response.rb | 13 + spec/factories/captain/document.rb | 9 + spec/factories/captain/inbox.rb | 6 + spec/factories/response_source.rb | 9 - spec/models/integrations/app_spec.rb | 22 -- spec/models/integrations/hook_spec.rb | 44 --- spec/models/note_spec.rb | 3 +- spec/spec_helper.rb | 10 - 184 files changed, 6666 insertions(+), 2242 deletions(-) delete mode 100644 .github/workflows/run_response_bot_spec.yml delete mode 100644 app/controllers/api/v1/accounts/integrations/captain_controller.rb create mode 100644 app/javascript/dashboard/api/captain/assistant.js create mode 100644 app/javascript/dashboard/api/captain/document.js create mode 100644 app/javascript/dashboard/api/captain/inboxes.js create mode 100644 app/javascript/dashboard/api/captain/response.js create mode 100644 app/javascript/dashboard/components-next/captain/PageLayout.vue create mode 100644 app/javascript/dashboard/components-next/captain/assistant/AssistantCard.story.vue create mode 100644 app/javascript/dashboard/components-next/captain/assistant/AssistantCard.vue create mode 100644 app/javascript/dashboard/components-next/captain/assistant/DocumentCard.story.vue create mode 100644 app/javascript/dashboard/components-next/captain/assistant/DocumentCard.vue create mode 100644 app/javascript/dashboard/components-next/captain/assistant/InboxCard.story.vue create mode 100644 app/javascript/dashboard/components-next/captain/assistant/InboxCard.vue create mode 100644 app/javascript/dashboard/components-next/captain/assistant/ResponseCard.story.vue create mode 100644 app/javascript/dashboard/components-next/captain/assistant/ResponseCard.vue create mode 100644 app/javascript/dashboard/components-next/captain/pageComponents/DeleteDialog.vue create mode 100644 app/javascript/dashboard/components-next/captain/pageComponents/assistant/AssistantForm.vue create mode 100644 app/javascript/dashboard/components-next/captain/pageComponents/assistant/CreateAssistantDialog.vue create mode 100644 app/javascript/dashboard/components-next/captain/pageComponents/document/CreateDocumentDialog.vue create mode 100644 app/javascript/dashboard/components-next/captain/pageComponents/document/DocumentForm.vue create mode 100644 app/javascript/dashboard/components-next/captain/pageComponents/document/RelatedResponses.vue create mode 100644 app/javascript/dashboard/components-next/captain/pageComponents/inbox/ConnectInboxDialog.vue create mode 100644 app/javascript/dashboard/components-next/captain/pageComponents/inbox/ConnectInboxForm.vue create mode 100644 app/javascript/dashboard/components-next/captain/pageComponents/response/CreateResponseDialog.vue create mode 100644 app/javascript/dashboard/components-next/captain/pageComponents/response/ResponseForm.vue delete mode 100644 app/javascript/dashboard/routes/dashboard/Captain.vue create mode 100644 app/javascript/dashboard/routes/dashboard/captain/assistants/Index.vue create mode 100644 app/javascript/dashboard/routes/dashboard/captain/assistants/inboxes/Index.vue create mode 100644 app/javascript/dashboard/routes/dashboard/captain/captain.routes.js create mode 100644 app/javascript/dashboard/routes/dashboard/captain/documents/Index.vue create mode 100644 app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue create mode 100644 app/javascript/dashboard/store/captain/assistant.js create mode 100644 app/javascript/dashboard/store/captain/document.js create mode 100644 app/javascript/dashboard/store/captain/inboxes.js create mode 100644 app/javascript/dashboard/store/captain/response.js create mode 100644 app/javascript/dashboard/store/captain/storeFactory.js create mode 100644 app/services/llm_formatter/contact_llm_formatter.rb create mode 100644 db/migrate/20250104200055_create_captain_tables.rb create mode 100644 db/migrate/20250104210328_remove_robin_tables.rb create mode 100644 db/migrate/20250105001414_add_status_to_captain_documents.rb create mode 100644 db/migrate/20250105005821_remove_not_null_from_captain_documents.rb create mode 100644 db/migrate/20250107030743_add_config_to_captain_assistant.rb create mode 100644 db/migrate/20250108031358_create_captain_inbox.rb create mode 100644 db/migrate/20250108211541_remove_index_from_captain_assistants.rb create mode 100644 enterprise/app/controllers/api/v1/accounts/captain/assistant_responses_controller.rb create mode 100644 enterprise/app/controllers/api/v1/accounts/captain/assistants_controller.rb create mode 100644 enterprise/app/controllers/api/v1/accounts/captain/documents_controller.rb create mode 100644 enterprise/app/controllers/api/v1/accounts/captain/inboxes_controller.rb delete mode 100644 enterprise/app/controllers/api/v1/accounts/response_sources_controller.rb create mode 100644 enterprise/app/controllers/enterprise/webhooks/firecrawl_controller.rb delete mode 100644 enterprise/app/controllers/super_admin/response_sources_controller.rb create mode 100644 enterprise/app/dispatchers/enterprise/async_dispatcher.rb create mode 100644 enterprise/app/jobs/captain/conversation/response_builder_job.rb create mode 100644 enterprise/app/jobs/captain/documents/crawl_job.rb create mode 100644 enterprise/app/jobs/captain/documents/response_builder_job.rb create mode 100644 enterprise/app/jobs/captain/llm/update_embedding_job.rb create mode 100644 enterprise/app/jobs/captain/tools/firecrawl_parser_job.rb create mode 100644 enterprise/app/jobs/captain/tools/simple_page_crawl_parser_job.rb delete mode 100644 enterprise/app/jobs/response_bot/response_bot_job.rb delete mode 100644 enterprise/app/jobs/response_bot/response_builder_job.rb delete mode 100644 enterprise/app/jobs/response_bot/response_document_content_job.rb create mode 100644 enterprise/app/models/captain/assistant.rb create mode 100644 enterprise/app/models/captain/assistant_response.rb create mode 100644 enterprise/app/models/captain/document.rb create mode 100644 enterprise/app/models/captain_inbox.rb delete mode 100644 enterprise/app/models/inbox_response_source.rb delete mode 100644 enterprise/app/models/response.rb delete mode 100644 enterprise/app/models/response_document.rb delete mode 100644 enterprise/app/models/response_source.rb create mode 100644 enterprise/app/policies/captain/assistant_policy.rb create mode 100644 enterprise/app/services/captain/copilot/chat_service.rb create mode 100644 enterprise/app/services/captain/llm/assistant_chat_service.rb create mode 100644 enterprise/app/services/captain/llm/base_open_ai_service.rb create mode 100644 enterprise/app/services/captain/llm/contact_attributes_service.rb create mode 100644 enterprise/app/services/captain/llm/contact_notes_service.rb create mode 100644 enterprise/app/services/captain/llm/conversation_faq_service.rb create mode 100644 enterprise/app/services/captain/llm/embedding_service.rb create mode 100644 enterprise/app/services/captain/llm/faq_generator_service.rb create mode 100644 enterprise/app/services/captain/llm/system_prompts_service.rb create mode 100644 enterprise/app/services/captain/tools/firecrawl_service.rb create mode 100644 enterprise/app/services/captain/tools/simple_page_crawl_service.rb delete mode 100644 enterprise/app/services/enterprise/message_templates/response_bot_service.rb delete mode 100644 enterprise/app/services/features/base_service.rb delete mode 100644 enterprise/app/services/features/helpcenter_embedding_search_service.rb delete mode 100644 enterprise/app/services/features/response_bot_service.rb delete mode 100644 enterprise/app/services/openai/embeddings_service.rb create mode 100644 enterprise/app/views/api/v1/accounts/captain/assistant_responses/create.json.jbuilder create mode 100644 enterprise/app/views/api/v1/accounts/captain/assistant_responses/index.json.jbuilder create mode 100644 enterprise/app/views/api/v1/accounts/captain/assistant_responses/show.json.jbuilder create mode 100644 enterprise/app/views/api/v1/accounts/captain/assistant_responses/update.json.jbuilder create mode 100644 enterprise/app/views/api/v1/accounts/captain/assistants/create.json.jbuilder create mode 100644 enterprise/app/views/api/v1/accounts/captain/assistants/index.json.jbuilder create mode 100644 enterprise/app/views/api/v1/accounts/captain/assistants/show.json.jbuilder create mode 100644 enterprise/app/views/api/v1/accounts/captain/assistants/update.json.jbuilder create mode 100644 enterprise/app/views/api/v1/accounts/captain/documents/create.json.jbuilder create mode 100644 enterprise/app/views/api/v1/accounts/captain/documents/index.json.jbuilder create mode 100644 enterprise/app/views/api/v1/accounts/captain/documents/show.json.jbuilder create mode 100644 enterprise/app/views/api/v1/accounts/captain/inboxes/create.json.jbuilder create mode 100644 enterprise/app/views/api/v1/accounts/captain/inboxes/index.json.jbuilder create mode 100644 enterprise/app/views/api/v1/models/captain/_assistant.json.jbuilder create mode 100644 enterprise/app/views/api/v1/models/captain/_assistant_response.json.jbuilder create mode 100644 enterprise/app/views/api/v1/models/captain/_document.json.jbuilder create mode 100644 enterprise/lib/captain/agent.rb create mode 100644 enterprise/lib/captain/llm_service.rb create mode 100644 enterprise/lib/captain/tool.rb create mode 100644 enterprise/listeners/captain_listener.rb delete mode 100644 spec/controllers/api/v1/accounts/integrations/captain_controller_spec.rb create mode 100644 spec/enterprise/controllers/api/v1/accounts/captain/assistant_responses_controller_spec.rb create mode 100644 spec/enterprise/controllers/api/v1/accounts/captain/assistants_controller_spec.rb create mode 100644 spec/enterprise/controllers/api/v1/accounts/captain/documents_controller_spec.rb create mode 100644 spec/enterprise/controllers/api/v1/accounts/captain/inboxes_controller_spec.rb delete mode 100644 spec/enterprise/controllers/api/v1/accounts/response_sources_controller_spec.rb create mode 100644 spec/enterprise/controllers/enterprise/webooks/firecrawl_controller_spec.rb create mode 100644 spec/enterprise/jobs/captain/documents/crawl_job_spec.rb create mode 100644 spec/enterprise/jobs/captain/documents/response_builder_job_spec.rb create mode 100644 spec/enterprise/jobs/captain/tools/firecrawl_parser_job_spec.rb create mode 100644 spec/enterprise/jobs/captain/tools/simple_page_crawl_parser_job_spec.rb create mode 100644 spec/enterprise/services/captain/llm/conversation_faq_service_spec.rb create mode 100644 spec/enterprise/services/captain/tools/simple_page_crawl_service_spec.rb delete mode 100644 spec/enterprise/services/enterprise/message_templates/response_bot_service_spec.rb delete mode 100644 spec/enterprise/services/page_crawler_service_spec.rb create mode 100644 spec/factories/captain/assistant.rb create mode 100644 spec/factories/captain/assistant_response.rb create mode 100644 spec/factories/captain/document.rb create mode 100644 spec/factories/captain/inbox.rb delete mode 100644 spec/factories/response_source.rb diff --git a/.circleci/config.yml b/.circleci/config.yml index b8a628677..db7f87d5c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -26,6 +26,12 @@ jobs: override-ci-command: pnpm i - run: node --version - run: pnpm --version + - run: + name: Add PostgreSQL repository and update + command: | + sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' + wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - + sudo apt-get update -y - run: name: Install System Dependencies @@ -34,7 +40,9 @@ jobs: DEBIAN_FRONTEND=noninteractive sudo apt-get install -y \ libpq-dev \ redis-server \ - postgresql \ + postgresql-common \ + postgresql-16 \ + postgresql-16-pgvector \ build-essential \ git \ curl \ diff --git a/.codeclimate.yml b/.codeclimate.yml index 213f7d172..c68251f32 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -51,6 +51,7 @@ exclude_patterns: - 'app/javascript/dashboard/routes/dashboard/settings/automation/constants.js' - 'app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.js' - 'app/javascript/dashboard/routes/dashboard/settings/reports/constants.js' + - 'app/javascript/dashboard/store/captain/storeFactory.js' - 'app/javascript/dashboard/i18n/index.js' - 'app/javascript/widget/i18n/index.js' - 'app/javascript/survey/i18n/index.js' diff --git a/.github/workflows/run_foss_spec.yml b/.github/workflows/run_foss_spec.yml index 6a018b314..0af172849 100644 --- a/.github/workflows/run_foss_spec.yml +++ b/.github/workflows/run_foss_spec.yml @@ -1,9 +1,3 @@ -# # -# # This action will strip the enterprise folder -# # and run the spec. -# # This is set to run against every PR. -# # - name: Run Chatwoot CE spec on: push: @@ -18,7 +12,7 @@ jobs: runs-on: ubuntu-20.04 services: postgres: - image: postgres:15.3 + image: pgvector/pgvector:pg15 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: '' diff --git a/.github/workflows/run_response_bot_spec.yml b/.github/workflows/run_response_bot_spec.yml deleted file mode 100644 index c788a0400..000000000 --- a/.github/workflows/run_response_bot_spec.yml +++ /dev/null @@ -1,89 +0,0 @@ -# # -# # This workflow will run specs related to response bot -# # This can only be activated in installations Where vector extension is available. -# # - -name: Run Response Bot spec -on: - push: - branches: - - develop - - master - pull_request: - workflow_dispatch: - -jobs: - test: - runs-on: ubuntu-20.04 - services: - postgres: - image: ankane/pgvector - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: "" - POSTGRES_DB: postgres - POSTGRES_HOST_AUTH_METHOD: trust - ports: - - 5432:5432 - # needed because the postgres container does not provide a healthcheck - # tmpfs makes DB faster by using RAM - options: >- - --mount type=tmpfs,destination=/var/lib/postgresql/data - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - redis: - image: redis - ports: - - 6379:6379 - options: --entrypoint redis-server - - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.ref }} - repository: ${{ github.event.pull_request.head.repo.full_name }} - - - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true # runs 'bundle install' and caches installed gems automatically - - - uses: pnpm/action-setup@v2 - with: - version: 9.3.0 - - - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: pnpm - - - name: pnpm - run: pnpm install - - - name: Create database - run: bundle exec rake db:create - - - name: Seed database - run: bundle exec rake db:schema:load - - - name: Enable ResponseBotService in installation - run: RAILS_ENV=test bundle exec rails runner "Features::ResponseBotService.new.enable_in_installation" - - # Run Response Bot specs - - name: Run backend tests - run: | - bundle exec rspec \ - spec/enterprise/controllers/api/v1/accounts/response_sources_controller_spec.rb \ - spec/enterprise/services/enterprise/message_templates/response_bot_service_spec.rb \ - spec/enterprise/controllers/enterprise/api/v1/accounts/inboxes_controller_spec.rb:47 \ - spec/enterprise/jobs/enterprise/account/conversations_resolution_scheduler_job_spec.rb \ - --profile=10 \ - --format documentation - - - name: Upload rails log folder - uses: actions/upload-artifact@v4 - if: always() - with: - name: rails-log-folder - path: log diff --git a/Gemfile b/Gemfile index 439b0f63e..45e194e13 100644 --- a/Gemfile +++ b/Gemfile @@ -175,6 +175,8 @@ gem 'pgvector' # Convert Website HTML to Markdown gem 'reverse_markdown' +gem 'ruby-openai' + ### Gems required only in specific deployment environments ### ############################################################## diff --git a/Gemfile.lock b/Gemfile.lock index 74cdbeea8..45c54bd46 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -231,6 +231,7 @@ GEM erubi (1.13.0) et-orbi (1.2.11) tzinfo + event_stream_parser (1.0.0) execjs (2.8.1) facebook-messenger (2.0.1) httparty (~> 0.13, >= 0.13.7) @@ -684,6 +685,10 @@ GEM rubocop-rspec (2.21.0) rubocop (~> 1.33) rubocop-capybara (~> 2.17) + ruby-openai (7.3.1) + event_stream_parser (>= 0.3.0, < 2.0.0) + faraday (>= 1) + faraday-multipart (>= 1) ruby-progressbar (1.13.0) ruby-vips (2.1.4) ffi (~> 1.12) @@ -941,6 +946,7 @@ DEPENDENCIES rubocop-performance rubocop-rails rubocop-rspec + ruby-openai scout_apm scss_lint seed_dump diff --git a/app/controllers/api/v1/accounts/integrations/captain_controller.rb b/app/controllers/api/v1/accounts/integrations/captain_controller.rb deleted file mode 100644 index 6813d14b5..000000000 --- a/app/controllers/api/v1/accounts/integrations/captain_controller.rb +++ /dev/null @@ -1,78 +0,0 @@ -class Api::V1::Accounts::Integrations::CaptainController < Api::V1::Accounts::BaseController - before_action :hook - - def proxy - request_url = build_request_url(request_path) - response = HTTParty.send(request_method, request_url, body: permitted_params[:body].to_json, headers: headers) - render plain: response.body, status: response.code - end - - def copilot - request_url = build_request_url(build_request_path("/assistants/#{hook.settings['assistant_id']}/copilot")) - params = { - previous_messages: copilot_params[:previous_messages], - conversation_history: conversation_history, - message: copilot_params[:message] - } - response = HTTParty.send(:post, request_url, body: params.to_json, headers: headers) - render plain: response.body, status: response.code - end - - private - - def headers - { - 'X-User-Email' => hook.settings['account_email'], - 'X-User-Token' => hook.settings['access_token'], - 'Content-Type' => 'application/json', - 'Accept' => '*/*' - } - end - - def build_request_path(route) - "api/accounts/#{hook.settings['account_id']}#{route}" - end - - def request_path - request_route = with_leading_hash_on_route(params[:route]) - - return 'api/sessions/profile' if request_route == '/sessions/profile' - - build_request_path(request_route) - end - - def build_request_url(request_path) - base_url = InstallationConfig.find_by(name: 'CAPTAIN_API_URL').value - URI.join(base_url, request_path).to_s - end - - def hook - @hook ||= Current.account.hooks.find_by!(app_id: 'captain') - end - - def request_method - method = permitted_params[:method].downcase - raise 'Invalid or missing HTTP method' unless %w[get post put patch delete options head].include?(method) - - method - end - - def with_leading_hash_on_route(request_route) - return '' if request_route.blank? - - request_route.start_with?('/') ? request_route : "/#{request_route}" - end - - def conversation_history - conversation = Current.account.conversations.find_by!(display_id: copilot_params[:conversation_id]) - conversation.to_llm_text - end - - def copilot_params - params.permit(:previous_messages, :conversation_id, :message) - end - - def permitted_params - params.permit(:method, :route, body: {}) - end -end diff --git a/app/controllers/public/api/v1/portals/articles_controller.rb b/app/controllers/public/api/v1/portals/articles_controller.rb index 08c62bada..f0fcf403d 100644 --- a/app/controllers/public/api/v1/portals/articles_controller.rb +++ b/app/controllers/public/api/v1/portals/articles_controller.rb @@ -7,9 +7,10 @@ class Public::Api::V1::Portals::ArticlesController < Public::Api::V1::Portals::B def index @articles = @portal.articles.published + @articles_count = @articles.count search_articles order_by_sort_param - @articles.page(list_params[:page]) if list_params[:page].present? + @articles = @articles.page(list_params[:page]) if list_params[:page].present? end def show; end @@ -44,7 +45,7 @@ class Public::Api::V1::Portals::ArticlesController < Public::Api::V1::Portals::B end def list_params - params.permit(:query, :locale, :sort, :status) + params.permit(:query, :locale, :sort, :status, :page) end def permitted_params diff --git a/app/dispatchers/async_dispatcher.rb b/app/dispatchers/async_dispatcher.rb index b582bc5dd..7416b7861 100644 --- a/app/dispatchers/async_dispatcher.rb +++ b/app/dispatchers/async_dispatcher.rb @@ -22,3 +22,5 @@ class AsyncDispatcher < BaseDispatcher ] end end + +AsyncDispatcher.prepend_mod_with('AsyncDispatcher') diff --git a/app/javascript/dashboard/api/captain/assistant.js b/app/javascript/dashboard/api/captain/assistant.js new file mode 100644 index 000000000..ce636e526 --- /dev/null +++ b/app/javascript/dashboard/api/captain/assistant.js @@ -0,0 +1,19 @@ +/* global axios */ +import ApiClient from '../ApiClient'; + +class CaptainAssistant extends ApiClient { + constructor() { + super('captain/assistants', { accountScoped: true }); + } + + get({ page = 1, searchKey } = {}) { + return axios.get(this.url, { + params: { + page, + searchKey, + }, + }); + } +} + +export default new CaptainAssistant(); diff --git a/app/javascript/dashboard/api/captain/document.js b/app/javascript/dashboard/api/captain/document.js new file mode 100644 index 000000000..165fa79b6 --- /dev/null +++ b/app/javascript/dashboard/api/captain/document.js @@ -0,0 +1,19 @@ +/* global axios */ +import ApiClient from '../ApiClient'; + +class CaptainDocument extends ApiClient { + constructor() { + super('captain/documents', { accountScoped: true }); + } + + get({ page = 1, searchKey } = {}) { + return axios.get(this.url, { + params: { + page, + searchKey, + }, + }); + } +} + +export default new CaptainDocument(); diff --git a/app/javascript/dashboard/api/captain/inboxes.js b/app/javascript/dashboard/api/captain/inboxes.js new file mode 100644 index 000000000..e0a1efdfe --- /dev/null +++ b/app/javascript/dashboard/api/captain/inboxes.js @@ -0,0 +1,26 @@ +/* global axios */ +import ApiClient from '../ApiClient'; + +class CaptainInboxes extends ApiClient { + constructor() { + super('captain/assistants', { accountScoped: true }); + } + + get({ assistantId } = {}) { + return axios.get(`${this.url}/${assistantId}/inboxes`); + } + + create(params = {}) { + const { assistantId, inboxId } = params; + return axios.post(`${this.url}/${assistantId}/inboxes`, { + inbox: { inbox_id: inboxId }, + }); + } + + delete(params = {}) { + const { assistantId, inboxId } = params; + return axios.delete(`${this.url}/${assistantId}/inboxes/${inboxId}`); + } +} + +export default new CaptainInboxes(); diff --git a/app/javascript/dashboard/api/captain/response.js b/app/javascript/dashboard/api/captain/response.js new file mode 100644 index 000000000..0cd31fa3c --- /dev/null +++ b/app/javascript/dashboard/api/captain/response.js @@ -0,0 +1,21 @@ +/* global axios */ +import ApiClient from '../ApiClient'; + +class CaptainResponses extends ApiClient { + constructor() { + super('captain/assistant_responses', { accountScoped: true }); + } + + get({ page = 1, searchKey, assistantId, documentId } = {}) { + return axios.get(this.url, { + params: { + page, + searchKey, + assistant_id: assistantId, + document_id: documentId, + }, + }); + } +} + +export default new CaptainResponses(); diff --git a/app/javascript/dashboard/api/inbox/conversation.js b/app/javascript/dashboard/api/inbox/conversation.js index ebc6176e7..8b9eacf3f 100644 --- a/app/javascript/dashboard/api/inbox/conversation.js +++ b/app/javascript/dashboard/api/inbox/conversation.js @@ -133,6 +133,10 @@ class ConversationApi extends ApiClient { getAllAttachments(conversationId) { return axios.get(`${this.url}/${conversationId}/attachments`); } + + requestCopilot(conversationId, body) { + return axios.post(`${this.url}/${conversationId}/copilot`, body); + } } export default new ConversationApi(); diff --git a/app/javascript/dashboard/api/integrations.js b/app/javascript/dashboard/api/integrations.js index b78a2ea7e..2b816e603 100644 --- a/app/javascript/dashboard/api/integrations.js +++ b/app/javascript/dashboard/api/integrations.js @@ -32,14 +32,6 @@ class IntegrationsAPI extends ApiClient { deleteHook(hookId) { return axios.delete(`${this.baseUrl()}/integrations/hooks/${hookId}`); } - - requestCaptain(body) { - return axios.post(`${this.baseUrl()}/integrations/captain/proxy`, body); - } - - requestCaptainCopilot(body) { - return axios.post(`${this.baseUrl()}/integrations/captain/copilot`, body); - } } export default new IntegrationsAPI(); diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactNotes.vue b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactNotes.vue index dfe3f437c..d5586cb12 100644 --- a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactNotes.vue +++ b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactNotes.vue @@ -29,7 +29,7 @@ const getWrittenBy = note => { const isCurrentUser = note?.user?.id === currentUser.value.id; return isCurrentUser ? t('CONTACTS_LAYOUT.SIDEBAR.NOTES.YOU') - : note.user.name; + : note?.user?.name || 'Bot'; }; const onAdd = content => { diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/components/ContactNoteItem.vue b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/components/ContactNoteItem.vue index 504be1547..17367c930 100644 --- a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/components/ContactNoteItem.vue +++ b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/components/ContactNoteItem.vue @@ -33,8 +33,8 @@ const handleDelete = () => {
diff --git a/app/javascript/dashboard/components-next/captain/PageLayout.vue b/app/javascript/dashboard/components-next/captain/PageLayout.vue new file mode 100644 index 000000000..36229d428 --- /dev/null +++ b/app/javascript/dashboard/components-next/captain/PageLayout.vue @@ -0,0 +1,84 @@ + + + diff --git a/app/javascript/dashboard/components-next/captain/assistant/AssistantCard.story.vue b/app/javascript/dashboard/components-next/captain/assistant/AssistantCard.story.vue new file mode 100644 index 000000000..02544439f --- /dev/null +++ b/app/javascript/dashboard/components-next/captain/assistant/AssistantCard.story.vue @@ -0,0 +1,85 @@ + + + diff --git a/app/javascript/dashboard/components-next/captain/assistant/AssistantCard.vue b/app/javascript/dashboard/components-next/captain/assistant/AssistantCard.vue new file mode 100644 index 000000000..8c2b9b749 --- /dev/null +++ b/app/javascript/dashboard/components-next/captain/assistant/AssistantCard.vue @@ -0,0 +1,101 @@ + + + diff --git a/app/javascript/dashboard/components-next/captain/assistant/DocumentCard.story.vue b/app/javascript/dashboard/components-next/captain/assistant/DocumentCard.story.vue new file mode 100644 index 000000000..ec004e6c0 --- /dev/null +++ b/app/javascript/dashboard/components-next/captain/assistant/DocumentCard.story.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/app/javascript/dashboard/components-next/captain/assistant/DocumentCard.vue b/app/javascript/dashboard/components-next/captain/assistant/DocumentCard.vue new file mode 100644 index 000000000..e541ee5cf --- /dev/null +++ b/app/javascript/dashboard/components-next/captain/assistant/DocumentCard.vue @@ -0,0 +1,108 @@ + + + diff --git a/app/javascript/dashboard/components-next/captain/assistant/InboxCard.story.vue b/app/javascript/dashboard/components-next/captain/assistant/InboxCard.story.vue new file mode 100644 index 000000000..34439a135 --- /dev/null +++ b/app/javascript/dashboard/components-next/captain/assistant/InboxCard.story.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/app/javascript/dashboard/components-next/captain/assistant/InboxCard.vue b/app/javascript/dashboard/components-next/captain/assistant/InboxCard.vue new file mode 100644 index 000000000..4e94fffc3 --- /dev/null +++ b/app/javascript/dashboard/components-next/captain/assistant/InboxCard.vue @@ -0,0 +1,100 @@ + + + diff --git a/app/javascript/dashboard/components-next/captain/assistant/ResponseCard.story.vue b/app/javascript/dashboard/components-next/captain/assistant/ResponseCard.story.vue new file mode 100644 index 000000000..2522c113a --- /dev/null +++ b/app/javascript/dashboard/components-next/captain/assistant/ResponseCard.story.vue @@ -0,0 +1,157 @@ + + + + + diff --git a/app/javascript/dashboard/components-next/captain/assistant/ResponseCard.vue b/app/javascript/dashboard/components-next/captain/assistant/ResponseCard.vue new file mode 100644 index 000000000..23ef50a07 --- /dev/null +++ b/app/javascript/dashboard/components-next/captain/assistant/ResponseCard.vue @@ -0,0 +1,118 @@ + + + diff --git a/app/javascript/dashboard/components-next/captain/pageComponents/DeleteDialog.vue b/app/javascript/dashboard/components-next/captain/pageComponents/DeleteDialog.vue new file mode 100644 index 000000000..3301e085d --- /dev/null +++ b/app/javascript/dashboard/components-next/captain/pageComponents/DeleteDialog.vue @@ -0,0 +1,56 @@ + + + diff --git a/app/javascript/dashboard/components-next/captain/pageComponents/assistant/AssistantForm.vue b/app/javascript/dashboard/components-next/captain/pageComponents/assistant/AssistantForm.vue new file mode 100644 index 000000000..3c8afec07 --- /dev/null +++ b/app/javascript/dashboard/components-next/captain/pageComponents/assistant/AssistantForm.vue @@ -0,0 +1,174 @@ + + + diff --git a/app/javascript/dashboard/components-next/captain/pageComponents/assistant/CreateAssistantDialog.vue b/app/javascript/dashboard/components-next/captain/pageComponents/assistant/CreateAssistantDialog.vue new file mode 100644 index 000000000..ac67063ec --- /dev/null +++ b/app/javascript/dashboard/components-next/captain/pageComponents/assistant/CreateAssistantDialog.vue @@ -0,0 +1,87 @@ + + +