Merge branch 'develop' into feat/log-captain

This commit is contained in:
Shivam Mishra
2025-07-30 17:45:36 +04:00
committed by GitHub
9 changed files with 81 additions and 74 deletions
@@ -372,6 +372,7 @@ export default {
const variables = getMessageVariables({
conversation: this.currentChat,
contact: this.currentContact,
inbox: this.inbox,
});
return variables;
},
@@ -2,7 +2,7 @@
"AGENT_BOTS": {
"HEADER": "Boty",
"LOADING_EDITOR": "Ładowanie edytora...",
"DESCRIPTION": "Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try. You can manage your bots from this page or create new ones using the 'Add Bot' button.",
"DESCRIPTION": "Boty agentów są jak najbardziej fantastyczni członkowie Twojego zespołu. Mogą zajmować się drobnymi sprawami, dzięki czemu Ty możesz skupić się na tym, co naprawdę ważne. Wypróbuj je! Możesz zarządzać swoimi botami z tej strony lub tworzyć nowe za pomocą przycisku 'Dodaj bota'.",
"LEARN_MORE": "Learn about agent bots",
"GLOBAL_BOT": "System bot",
"GLOBAL_BOT_BADGE": "System",
@@ -30,10 +30,10 @@
}
},
"LIST": {
"404": "No bots found. You can create a bot by clicking the 'Add Bot' button.",
"404": "Nie znaleziono botów. Możesz utworzyć bota klikając przycisk 'Dodaj bota'.",
"LOADING": "Pobieranie botów...",
"TABLE_HEADER": {
"DETAILS": "Bot Details",
"DETAILS": "Szczegóły bota",
"URL": "Adres URL webhooka"
}
},
@@ -157,6 +157,14 @@ export const MESSAGE_VARIABLES = [
label: 'Agent email',
key: 'agent.email',
},
{
key: 'inbox.name',
label: 'Inbox name',
},
{
label: 'Inbox id',
key: 'inbox.id',
},
];
export const ATTACHMENT_ICONS = {
@@ -68,8 +68,16 @@ module Enterprise::Concerns::Article
headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{ENV.fetch('OPENAI_API_KEY', nil)}" }
body = { model: 'gpt-4o', messages: messages, response_format: { type: 'json_object' } }.to_json
Rails.logger.info "Requesting Chat GPT with body: #{body}"
response = HTTParty.post('https://api.openai.com/v1/chat/completions', headers: headers, body: body)
response = HTTParty.post(openai_api_url, headers: headers, body: body)
Rails.logger.info "Chat GPT response: #{response.body}"
JSON.parse(response.parsed_response['choices'][0]['message']['content'])['search_terms']
end
private
def openai_api_url
endpoint = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value || 'https://api.openai.com/'
endpoint = endpoint.chomp('/')
"#{endpoint}/v1/chat/completions"
end
end
-62
View File
@@ -1,62 +0,0 @@
class ChatGpt
def self.base_uri
'https://api.openai.com'
end
def initialize(context_sections = '')
@model = 'gpt-4o'
@messages = [system_message(context_sections)]
end
def generate_response(input, previous_messages = [], role = 'user')
@messages += previous_messages
@messages << { 'role': role, 'content': input } if input.present?
response = request_gpt
JSON.parse(response['choices'][0]['message']['content'].strip)
end
private
def system_message(context_sections)
{
'role': 'system',
'content': system_content(context_sections)
}
end
def system_content(context_sections)
<<~SYSTEM_PROMPT_MESSAGE
You are a very enthusiastic customer support representative who loves to help people.
Your answers will always be formatted in valid JSON hash, as shown below. Never respond in non JSON format.
```
{
response: '' ,
context_ids: [ids],
}
```
response: will be the next response to the conversation
context_ids: will be an array of unique context IDs that were used to generate the answer. choose top 3.
The answers will be generated using the information provided at the end of the prompt under the context sections. You will not respond outside the context of the information provided in context sections.
If the answer is not provided in context sections, Respond to the customer and ask whether they want to talk to another support agent . If they ask to Chat with another agent, return `conversation_handoff' as the response in JSON response
----------------------------------
Context sections:
#{context_sections}
SYSTEM_PROMPT_MESSAGE
end
def request_gpt
headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{ENV.fetch('OPENAI_API_KEY')}" }
body = { model: @model, messages: @messages, response_format: { type: 'json_object' } }.to_json
Rails.logger.info "Requesting Chat GPT with body: #{body}"
response = HTTParty.post("#{self.class.base_uri}/v1/chat/completions", headers: headers, body: body)
Rails.logger.info "Chat GPT response: #{response.body}"
JSON.parse(response.body)
end
end
+7 -2
View File
@@ -4,7 +4,6 @@ class Integrations::OpenaiBaseService
# sticking with 120000 to be safe
# 120000 * 4 = 480,000 characters (rounding off downwards to 400,000 to be safe)
TOKEN_LIMIT = 400_000
API_URL = 'https://api.openai.com/v1/chat/completions'.freeze
GPT_MODEL = ENV.fetch('OPENAI_GPT_MODEL', 'gpt-4o-mini').freeze
ALLOWED_EVENT_NAMES = %w[rephrase summarize reply_suggestion fix_spelling_grammar shorten expand make_friendly make_formal simplify].freeze
@@ -81,6 +80,12 @@ class Integrations::OpenaiBaseService
self.class::CACHEABLE_EVENTS.include?(event_name)
end
def api_url
endpoint = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value || 'https://api.openai.com/'
endpoint = endpoint.chomp('/')
"#{endpoint}/v1/chat/completions"
end
def make_api_call(body)
headers = {
'Content-Type' => 'application/json',
@@ -88,7 +93,7 @@ class Integrations::OpenaiBaseService
}
Rails.logger.info("OpenAI API request: #{body}")
response = HTTParty.post(API_URL, headers: headers, body: body)
response = HTTParty.post(api_url, headers: headers, body: body)
Rails.logger.info("OpenAI API response: #{response.body}")
return { error: response.parsed_response, error_code: response.code } unless response.success?
+1 -1
View File
@@ -34,7 +34,7 @@
"@breezystack/lamejs": "^1.2.7",
"@chatwoot/ninja-keys": "1.2.3",
"@chatwoot/prosemirror-schema": "1.1.6-next",
"@chatwoot/utils": "^0.0.47",
"@chatwoot/utils": "^0.0.48",
"@formkit/core": "^1.6.7",
"@formkit/vue": "^1.6.7",
"@hcaptcha/vue3-hcaptcha": "^1.3.0",
+5 -5
View File
@@ -23,8 +23,8 @@ importers:
specifier: 1.1.6-next
version: 1.1.6-next
'@chatwoot/utils':
specifier: ^0.0.47
version: 0.0.47
specifier: ^0.0.48
version: 0.0.48
'@formkit/core':
specifier: ^1.6.7
version: 1.6.7
@@ -406,8 +406,8 @@ packages:
'@chatwoot/prosemirror-schema@1.1.6-next':
resolution: {integrity: sha512-9lf7FrcED/B5oyGrMmIkbegkhlC/P0NrtXoX8k94YWRosZcx0hGVGhpTud+0Mhm7saAfGerKIwTRVDmmnxPuCA==}
'@chatwoot/utils@0.0.47':
resolution: {integrity: sha512-0z/MY+rBjDnf6zuWbMdzexH+zFDXU/g5fPr/kcUxnqtvPsZIQpL8PvwSPBW0+wS6R7LChndNkdviV1e9H8Yp+Q==}
'@chatwoot/utils@0.0.48':
resolution: {integrity: sha512-67M2lvpBp0Ciczv1uRzabOXSCGiEeJE3wYVoPAxkqI35CJSkotu4tSX2TFOwagUQoRyU6F8YV3xXGfCpDN9WAA==}
engines: {node: '>=10'}
'@codemirror/commands@6.7.0':
@@ -5255,7 +5255,7 @@ snapshots:
prosemirror-utils: 1.2.2(prosemirror-model@1.22.3)(prosemirror-state@1.4.3)
prosemirror-view: 1.34.1
'@chatwoot/utils@0.0.47':
'@chatwoot/utils@0.0.48':
dependencies:
date-fns: 2.30.0
@@ -253,5 +253,52 @@ RSpec.describe Integrations::Openai::ProcessorService do
expect(result).to eq({ :message => 'This is a reply from openai.' })
end
end
context 'when testing endpoint configuration' do
let(:event) { { 'name' => 'rephrase', 'data' => { 'content' => 'test message' } } }
context 'when CAPTAIN_OPEN_AI_ENDPOINT is not configured' do
it 'uses default OpenAI endpoint' do
InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.destroy
stub_request(:post, 'https://api.openai.com/v1/chat/completions')
.with(body: anything, headers: expected_headers)
.to_return(status: 200, body: openai_response, headers: {})
result = subject.perform
expect(result).to eq({ :message => 'This is a reply from openai.' })
end
end
context 'when CAPTAIN_OPEN_AI_ENDPOINT is configured' do
before do
create(:installation_config, name: 'CAPTAIN_OPEN_AI_ENDPOINT', value: 'https://custom.azure.com/')
end
it 'uses custom endpoint' do
stub_request(:post, 'https://custom.azure.com/v1/chat/completions')
.with(body: anything, headers: expected_headers)
.to_return(status: 200, body: openai_response, headers: {})
result = subject.perform
expect(result).to eq({ :message => 'This is a reply from openai.' })
end
end
context 'when CAPTAIN_OPEN_AI_ENDPOINT has trailing slash' do
before do
create(:installation_config, name: 'CAPTAIN_OPEN_AI_ENDPOINT', value: 'https://custom.azure.com/')
end
it 'properly handles trailing slash' do
stub_request(:post, 'https://custom.azure.com/v1/chat/completions')
.with(body: anything, headers: expected_headers)
.to_return(status: 200, body: openai_response, headers: {})
result = subject.perform
expect(result).to eq({ :message => 'This is a reply from openai.' })
end
end
end
end
end